package database import ( "context" "fmt" ) // ── Domain types ────────────────────────────────────────────────────────────── type Department struct { ID int `json:"id"` Code string `json:"code"` Name string `json:"name"` CostCenter string `json:"cost_center"` Active bool `json:"active"` CreatedAt string `json:"created_at"` } type GLAccount struct { ID int `json:"id"` Code string `json:"code"` Description string `json:"description"` Type string `json:"type"` // revenue | cogs | opex | capex | headcount FavourHigh bool `json:"favour_high"` // true = over-budget is good (revenue accounts) Active bool `json:"active"` } // ── ReferenceRepo ───────────────────────────────────────────────────────────── type ReferenceRepo struct { db *DB } func NewReferenceRepo(db *DB) *ReferenceRepo { return &ReferenceRepo{db: db} } // ── Department operations ───────────────────────────────────────────────────── func (r *ReferenceRepo) CreateDepartment(ctx context.Context, d Department) (*Department, error) { res, err := r.db.ExecContext(ctx, ` INSERT INTO departments (code, name, cost_center, active) VALUES (?, ?, ?, ?) ON CONFLICT(code) DO UPDATE SET name = excluded.name, cost_center = excluded.cost_center, active = excluded.active`, d.Code, d.Name, d.CostCenter, boolToInt(d.Active), ) if err != nil { return nil, fmt.Errorf("upsert department: %w", err) } id, err := res.LastInsertId() if err != nil || id == 0 { // ON CONFLICT branch — fetch existing row return r.getDepartmentByCode(ctx, d.Code) } d.ID = int(id) return &d, nil } func (r *ReferenceRepo) getDepartmentByCode(ctx context.Context, code string) (*Department, error) { var d Department var active int err := r.db.QueryRowContext(ctx, `SELECT id, code, name, cost_center, active, created_at FROM departments WHERE code = ?`, code, ).Scan(&d.ID, &d.Code, &d.Name, &d.CostCenter, &active, &d.CreatedAt) if err != nil { return nil, fmt.Errorf("fetch department by code: %w", err) } d.Active = active == 1 return &d, nil } func (r *ReferenceRepo) ListDepartments(ctx context.Context) ([]Department, error) { rows, err := r.db.QueryContext(ctx, `SELECT id, code, name, cost_center, active, created_at FROM departments ORDER BY code`) if err != nil { return nil, err } defer rows.Close() var depts []Department for rows.Next() { var d Department var active int if err := rows.Scan(&d.ID, &d.Code, &d.Name, &d.CostCenter, &active, &d.CreatedAt); err != nil { return nil, err } d.Active = active == 1 depts = append(depts, d) } return depts, rows.Err() } func (r *ReferenceRepo) DeleteDepartment(ctx context.Context, id int) error { _, err := r.db.ExecContext(ctx, `DELETE FROM departments WHERE id = ?`, id) return err } // ── GL Account operations ───────────────────────────────────────────────────── func (r *ReferenceRepo) CreateGLAccount(ctx context.Context, a GLAccount) (*GLAccount, error) { res, err := r.db.ExecContext(ctx, ` INSERT INTO gl_accounts (code, description, type, favour_high, active) VALUES (?, ?, ?, ?, ?) ON CONFLICT(code) DO UPDATE SET description = excluded.description, type = excluded.type, favour_high = excluded.favour_high, active = excluded.active`, a.Code, a.Description, a.Type, boolToInt(a.FavourHigh), boolToInt(a.Active), ) if err != nil { return nil, fmt.Errorf("upsert gl_account: %w", err) } id, err := res.LastInsertId() if err != nil || id == 0 { return r.getGLAccountByCode(ctx, a.Code) } a.ID = int(id) return &a, nil } func (r *ReferenceRepo) getGLAccountByCode(ctx context.Context, code string) (*GLAccount, error) { var a GLAccount var favourHigh, active int err := r.db.QueryRowContext(ctx, `SELECT id, code, description, type, favour_high, active FROM gl_accounts WHERE code = ?`, code, ).Scan(&a.ID, &a.Code, &a.Description, &a.Type, &favourHigh, &active) if err != nil { return nil, fmt.Errorf("fetch gl_account by code: %w", err) } a.FavourHigh = favourHigh == 1 a.Active = active == 1 return &a, nil } func (r *ReferenceRepo) ListGLAccounts(ctx context.Context) ([]GLAccount, error) { rows, err := r.db.QueryContext(ctx, `SELECT id, code, description, type, favour_high, active FROM gl_accounts ORDER BY code`) if err != nil { return nil, err } defer rows.Close() var accts []GLAccount for rows.Next() { var a GLAccount var favourHigh, active int if err := rows.Scan(&a.ID, &a.Code, &a.Description, &a.Type, &favourHigh, &active); err != nil { return nil, err } a.FavourHigh = favourHigh == 1 a.Active = active == 1 accts = append(accts, a) } return accts, rows.Err() } func (r *ReferenceRepo) DeleteGLAccount(ctx context.Context, id int) error { _, err := r.db.ExecContext(ctx, `DELETE FROM gl_accounts WHERE id = ?`, id) return err } // ── helpers ─────────────────────────────────────────────────────────────────── func boolToInt(b bool) int { if b { return 1 } return 0 }