package postgres import ( "context" "time" "github.com/gofrs/uuid/v5" "github.com/saveinmed/backend-go/internal/domain" ) // CreateDocument persists a KYC document. func (r *Repository) CreateDocument(ctx context.Context, doc *domain.CompanyDocument) error { now := time.Now().UTC() doc.CreatedAt = now doc.UpdatedAt = now query := `INSERT INTO company_documents (id, company_id, type, url, status, rejection_reason, created_at, updated_at) VALUES (:id, :company_id, :type, :url, :status, :rejection_reason, :created_at, :updated_at)` _, err := r.db.NamedExecContext(ctx, query, doc) return err } // ListDocuments retrieves documents for a company. func (r *Repository) ListDocuments(ctx context.Context, companyID uuid.UUID) ([]domain.CompanyDocument, error) { var docs []domain.CompanyDocument query := `SELECT id, company_id, type, url, status, rejection_reason, created_at, updated_at FROM company_documents WHERE company_id = $1 ORDER BY created_at DESC` if err := r.db.SelectContext(ctx, &docs, query, companyID); err != nil { return nil, err } return docs, nil } // RecordLedgerEntry inserts an immutable financial record. func (r *Repository) RecordLedgerEntry(ctx context.Context, entry *domain.LedgerEntry) error { entry.CreatedAt = time.Now().UTC() query := `INSERT INTO ledger_entries (id, company_id, amount_cents, type, description, reference_id, created_at) VALUES (:id, :company_id, :amount_cents, :type, :description, :reference_id, :created_at)` _, err := r.db.NamedExecContext(ctx, query, entry) return err } // GetLedger returns transaction history for a company. func (r *Repository) GetLedger(ctx context.Context, companyID uuid.UUID, limit, offset int) ([]domain.LedgerEntry, int64, error) { var entries []domain.LedgerEntry // Get total count var total int64 if err := r.db.GetContext(ctx, &total, "SELECT count(*) FROM ledger_entries WHERE company_id = $1", companyID); err != nil { return nil, 0, err } // Get paginated entries query := `SELECT id, company_id, amount_cents, type, description, reference_id, created_at FROM ledger_entries WHERE company_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3` if err := r.db.SelectContext(ctx, &entries, query, companyID, limit, offset); err != nil { return nil, 0, err } return entries, total, nil } // GetBalance calculates the current balance based on ledger entries. func (r *Repository) GetBalance(ctx context.Context, companyID uuid.UUID) (int64, error) { var balance int64 // COALESCE to handle case with no entries returning NULL query := `SELECT COALESCE(SUM(amount_cents), 0) FROM ledger_entries WHERE company_id = $1` if err := r.db.GetContext(ctx, &balance, query, companyID); err != nil { return 0, err } return balance, nil } // CreateWithdrawal requests a payout. func (r *Repository) CreateWithdrawal(ctx context.Context, withdrawal *domain.Withdrawal) error { now := time.Now().UTC() withdrawal.CreatedAt = now withdrawal.UpdatedAt = now // Transaction to ensure balance check? // In a real system, we reserved balance via ledger entry first. // The Service layer should call RecordLedgerEntry(WITHDRAWAL) before calling CreateWithdrawal. // So here we just insert the request. query := `INSERT INTO withdrawals (id, company_id, amount_cents, status, bank_account_info, created_at, updated_at) VALUES (:id, :company_id, :amount_cents, :status, :bank_account_info, :created_at, :updated_at)` _, err := r.db.NamedExecContext(ctx, query, withdrawal) return err } // ListWithdrawals retrieves payout requests. func (r *Repository) ListWithdrawals(ctx context.Context, companyID uuid.UUID) ([]domain.Withdrawal, error) { var list []domain.Withdrawal query := `SELECT id, company_id, amount_cents, status, bank_account_info, transaction_id, rejection_reason, created_at, updated_at FROM withdrawals WHERE company_id = $1 ORDER BY created_at DESC` if err := r.db.SelectContext(ctx, &list, query, companyID); err != nil { return nil, err } return list, nil }