package repository import ( "context" "github.com/jackc/pgx/v5/pgxpool" "financeiro-carvalho/internal/model" ) // AccountRepoWithYield extends the basic account repo with CDI-related operations. type AccountRepoWithYield interface { List(ctx context.Context) ([]model.Account, error) GetByID(ctx context.Context, id int) (*model.Account, error) Create(ctx context.Context, in model.AccountInput) (*model.Account, error) Update(ctx context.Context, id int, in model.AccountInput) (*model.Account, error) Delete(ctx context.Context, id int) error TotalPatrimony(ctx context.Context) (float64, error) UpdateLastYieldDate(ctx context.Context, id int, date string) error } type AccountRepository struct { pool *pgxpool.Pool } func NewAccountRepository(pool *pgxpool.Pool) *AccountRepository { return &AccountRepository{pool: pool} } func (r *AccountRepository) List(ctx context.Context) ([]model.Account, error) { rows, err := r.pool.Query(ctx, ` SELECT a.id, a.name, a.type, a.initial_balance, a.yield_type, a.last_yield_date::text, a.closing_day, a.due_day, a.created_at::text, a.updated_at::text, a.initial_balance + COALESCE(SUM(CASE WHEN t.type = 'income' THEN t.amount ELSE 0 END), 0) - COALESCE(SUM(CASE WHEN t.type = 'expense' THEN t.amount ELSE 0 END), 0) AS balance FROM accounts a LEFT JOIN transactions t ON t.account_id = a.id GROUP BY a.id ORDER BY a.created_at `) if err != nil { return nil, err } defer rows.Close() var out []model.Account for rows.Next() { var a model.Account if err := rows.Scan(&a.ID, &a.Name, &a.Type, &a.InitialBalance, &a.YieldType, &a.LastYieldDate, &a.ClosingDay, &a.DueDay, &a.CreatedAt, &a.UpdatedAt, &a.Balance); err != nil { return nil, err } out = append(out, a) } return out, rows.Err() } func (r *AccountRepository) GetByID(ctx context.Context, id int) (*model.Account, error) { var a model.Account err := r.pool.QueryRow(ctx, ` SELECT a.id, a.name, a.type, a.initial_balance, a.yield_type, a.last_yield_date::text, a.closing_day, a.due_day, a.created_at::text, a.updated_at::text, a.initial_balance + COALESCE(SUM(CASE WHEN t.type = 'income' THEN t.amount ELSE 0 END), 0) - COALESCE(SUM(CASE WHEN t.type = 'expense' THEN t.amount ELSE 0 END), 0) AS balance FROM accounts a LEFT JOIN transactions t ON t.account_id = a.id WHERE a.id = $1 GROUP BY a.id `, id).Scan(&a.ID, &a.Name, &a.Type, &a.InitialBalance, &a.YieldType, &a.LastYieldDate, &a.ClosingDay, &a.DueDay, &a.CreatedAt, &a.UpdatedAt, &a.Balance) if err != nil { return nil, err } return &a, nil } func (r *AccountRepository) Create(ctx context.Context, in model.AccountInput) (*model.Account, error) { yt := in.YieldType if yt == "" { yt = "none" } var a model.Account err := r.pool.QueryRow(ctx, ` INSERT INTO accounts (name, type, initial_balance, yield_type, closing_day, due_day) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, name, type, initial_balance, yield_type, last_yield_date::text, closing_day, due_day, created_at::text, updated_at::text `, in.Name, in.Type, in.InitialBalance, yt, in.ClosingDay, in.DueDay). Scan(&a.ID, &a.Name, &a.Type, &a.InitialBalance, &a.YieldType, &a.LastYieldDate, &a.ClosingDay, &a.DueDay, &a.CreatedAt, &a.UpdatedAt) if err != nil { return nil, err } a.Balance = a.InitialBalance return &a, nil } func (r *AccountRepository) Update(ctx context.Context, id int, in model.AccountInput) (*model.Account, error) { yt := in.YieldType if yt == "" { yt = "none" } row := r.pool.QueryRow(ctx, ` UPDATE accounts SET name=$1, type=$2, initial_balance=$3, yield_type=$4, closing_day=$5, due_day=$6, updated_at=NOW() WHERE id=$7 RETURNING id, name, type, initial_balance, yield_type, last_yield_date::text, closing_day, due_day, created_at::text, updated_at::text `, in.Name, in.Type, in.InitialBalance, yt, in.ClosingDay, in.DueDay, id) var a model.Account if err := row.Scan(&a.ID, &a.Name, &a.Type, &a.InitialBalance, &a.YieldType, &a.LastYieldDate, &a.ClosingDay, &a.DueDay, &a.CreatedAt, &a.UpdatedAt); err != nil { return nil, err } full, err := r.GetByID(ctx, a.ID) if err != nil { return nil, err } return full, nil } func (r *AccountRepository) Delete(ctx context.Context, id int) error { _, err := r.pool.Exec(ctx, `DELETE FROM accounts WHERE id = $1`, id) return err } func (r *AccountRepository) TotalPatrimony(ctx context.Context) (float64, error) { var total float64 err := r.pool.QueryRow(ctx, ` SELECT COALESCE(SUM( a.initial_balance + COALESCE(( SELECT SUM(CASE WHEN type='income' THEN amount ELSE -amount END) FROM transactions WHERE account_id = a.id ), 0) ), 0) FROM accounts a `).Scan(&total) return total, err } func (r *AccountRepository) UpdateLastYieldDate(ctx context.Context, id int, date string) error { _, err := r.pool.Exec(ctx, `UPDATE accounts SET last_yield_date = $1 WHERE id = $2`, date, id) return err }