package repository import ( "context" "errors" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" "financeiro-carvalho/internal/middleware" "financeiro-carvalho/internal/model" ) type CreditBillRepository interface { ListByAccount(ctx context.Context, accountID int) ([]model.CreditBill, error) GetCurrent(ctx context.Context, accountID int) (*model.CreditBill, error) Upsert(ctx context.Context, in model.CreditBillInput) (*model.CreditBill, error) MarkPaid(ctx context.Context, id int, paymentAccountID *int) error } type creditBillRepo struct{ db *pgxpool.Pool } func NewCreditBillRepository(db *pgxpool.Pool) CreditBillRepository { return &creditBillRepo{db: db} } func (r *creditBillRepo) ListByAccount(ctx context.Context, accountID int) ([]model.CreditBill, error) { pid := middleware.ProfileIDFromCtx(ctx) rows, err := r.db.Query(ctx, ` SELECT cb.id, cb.account_id, a.name, cb.period_start::text, cb.period_end::text, cb.due_date::text, COALESCE(SUM(t.amount) FILTER (WHERE t.type = 'expense'), 0) AS total, cb.paid, cb.paid_at::text, cb.payment_account_id FROM credit_bills cb JOIN accounts a ON a.id = cb.account_id AND a.profile_id = $2 LEFT JOIN transactions t ON t.account_id = cb.account_id AND t.date >= cb.period_start AND t.date <= cb.period_end AND t.type = 'expense' WHERE cb.account_id = $1 GROUP BY cb.id, a.name ORDER BY cb.period_start DESC `, accountID, pid) if err != nil { return nil, err } defer rows.Close() var out []model.CreditBill for rows.Next() { var b model.CreditBill if err := rows.Scan(&b.ID, &b.AccountID, &b.AccountName, &b.PeriodStart, &b.PeriodEnd, &b.DueDate, &b.Total, &b.Paid, &b.PaidAt, &b.PaymentAccountID); err != nil { return nil, err } out = append(out, b) } return out, rows.Err() } func (r *creditBillRepo) GetCurrent(ctx context.Context, accountID int) (*model.CreditBill, error) { pid := middleware.ProfileIDFromCtx(ctx) var b model.CreditBill err := r.db.QueryRow(ctx, ` SELECT cb.id, cb.account_id, a.name, cb.period_start::text, cb.period_end::text, cb.due_date::text, COALESCE(SUM(t.amount) FILTER (WHERE t.type = 'expense'), 0) AS total, cb.paid, cb.paid_at::text, cb.payment_account_id FROM credit_bills cb JOIN accounts a ON a.id = cb.account_id AND a.profile_id = $2 LEFT JOIN transactions t ON t.account_id = cb.account_id AND t.date >= cb.period_start AND t.date <= cb.period_end AND t.type = 'expense' WHERE cb.account_id = $1 AND cb.paid = FALSE GROUP BY cb.id, a.name ORDER BY cb.period_start DESC LIMIT 1 `, accountID, pid).Scan(&b.ID, &b.AccountID, &b.AccountName, &b.PeriodStart, &b.PeriodEnd, &b.DueDate, &b.Total, &b.Paid, &b.PaidAt, &b.PaymentAccountID) if errors.Is(err, pgx.ErrNoRows) { return nil, nil } return &b, err } func (r *creditBillRepo) Upsert(ctx context.Context, in model.CreditBillInput) (*model.CreditBill, error) { var b model.CreditBill err := r.db.QueryRow(ctx, ` INSERT INTO credit_bills (account_id, period_start, period_end, due_date, payment_account_id) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (account_id, period_start) DO UPDATE SET period_end = EXCLUDED.period_end, due_date = EXCLUDED.due_date, payment_account_id = EXCLUDED.payment_account_id RETURNING id, account_id, period_start::text, period_end::text, due_date::text, paid, paid_at::text, payment_account_id `, in.AccountID, in.PeriodStart, in.PeriodEnd, in.DueDate, in.PaymentAccountID). Scan(&b.ID, &b.AccountID, &b.PeriodStart, &b.PeriodEnd, &b.DueDate, &b.Paid, &b.PaidAt, &b.PaymentAccountID) return &b, err } func (r *creditBillRepo) MarkPaid(ctx context.Context, id int, paymentAccountID *int) error { pid := middleware.ProfileIDFromCtx(ctx) _, err := r.db.Exec(ctx, ` UPDATE credit_bills cb SET paid = TRUE, paid_at = NOW(), payment_account_id = $2 FROM accounts a WHERE cb.id = $1 AND cb.account_id = a.id AND a.profile_id = $3 `, id, paymentAccountID, pid) return err }