- Nova tabela pending_bill_imports (migration 016) - ImportHandler.Confirm: quando is_credit_card=true e payment_date > hoje, salva como pending em vez de inserir transações - Novos endpoints: GET /pending-bills, POST /pending-bills/:id/confirm, DELETE /pending-bills/:id - Dashboard inclui pending_bill_imports no payload - Frontend: resultado "fatura salva como pendente" no ImportView - AccountsView exibe widget de faturas pendentes com ações de confirmar/descartar - dashboard_test: mock de PendingBillRepo + TransactionRepository Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
94 lines
2.9 KiB
Go
94 lines
2.9 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"financeiro-carvalho/internal/middleware"
|
|
"financeiro-carvalho/internal/model"
|
|
)
|
|
|
|
type PendingBillRepository interface {
|
|
List(ctx context.Context) ([]model.PendingBillImport, error)
|
|
Create(ctx context.Context, filename, paymentDate string, total float64, rows []model.ImportRow) (*model.PendingBillImport, error)
|
|
GetByID(ctx context.Context, id int) (*model.PendingBillImport, error)
|
|
Delete(ctx context.Context, id int) error
|
|
}
|
|
|
|
type pendingBillRepo struct{ pool *pgxpool.Pool }
|
|
|
|
func NewPendingBillRepository(pool *pgxpool.Pool) PendingBillRepository {
|
|
return &pendingBillRepo{pool: pool}
|
|
}
|
|
|
|
func (r *pendingBillRepo) List(ctx context.Context) ([]model.PendingBillImport, error) {
|
|
pid := middleware.ProfileIDFromCtx(ctx)
|
|
rows, err := r.pool.Query(ctx, `
|
|
SELECT id, filename, payment_date::text, total, rows, created_at::text
|
|
FROM pending_bill_imports
|
|
WHERE profile_id = $1
|
|
ORDER BY payment_date
|
|
`, pid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var out []model.PendingBillImport
|
|
for rows.Next() {
|
|
var p model.PendingBillImport
|
|
var rowsJSON []byte
|
|
if err := rows.Scan(&p.ID, &p.Filename, &p.PaymentDate, &p.Total, &rowsJSON, &p.CreatedAt); err != nil {
|
|
return nil, err
|
|
}
|
|
_ = json.Unmarshal(rowsJSON, &p.Rows)
|
|
out = append(out, p)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
func (r *pendingBillRepo) Create(ctx context.Context, filename, paymentDate string, total float64, rows []model.ImportRow) (*model.PendingBillImport, error) {
|
|
pid := middleware.ProfileIDFromCtx(ctx)
|
|
rowsJSON, err := json.Marshal(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var p model.PendingBillImport
|
|
var rowsBack []byte
|
|
err = r.pool.QueryRow(ctx, `
|
|
INSERT INTO pending_bill_imports (profile_id, filename, payment_date, total, rows)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
RETURNING id, filename, payment_date::text, total, rows, created_at::text
|
|
`, pid, filename, paymentDate, total, rowsJSON).
|
|
Scan(&p.ID, &p.Filename, &p.PaymentDate, &p.Total, &rowsBack, &p.CreatedAt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_ = json.Unmarshal(rowsBack, &p.Rows)
|
|
return &p, nil
|
|
}
|
|
|
|
func (r *pendingBillRepo) GetByID(ctx context.Context, id int) (*model.PendingBillImport, error) {
|
|
pid := middleware.ProfileIDFromCtx(ctx)
|
|
var p model.PendingBillImport
|
|
var rowsJSON []byte
|
|
err := r.pool.QueryRow(ctx, `
|
|
SELECT id, filename, payment_date::text, total, rows, created_at::text
|
|
FROM pending_bill_imports
|
|
WHERE id = $1 AND profile_id = $2
|
|
`, id, pid).Scan(&p.ID, &p.Filename, &p.PaymentDate, &p.Total, &rowsJSON, &p.CreatedAt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_ = json.Unmarshal(rowsJSON, &p.Rows)
|
|
return &p, nil
|
|
}
|
|
|
|
func (r *pendingBillRepo) Delete(ctx context.Context, id int) error {
|
|
pid := middleware.ProfileIDFromCtx(ctx)
|
|
_, err := r.pool.Exec(ctx, `DELETE FROM pending_bill_imports WHERE id = $1 AND profile_id = $2`, id, pid)
|
|
return err
|
|
}
|