Files

90 lines
2.2 KiB
Go

package ledger
import (
"context"
"fmt"
"time"
"github.com/jackc/pgx/v5/pgxpool"
)
// Ledger handles double-entry accounting
type Ledger struct {
db *pgxpool.Pool
}
// NewLedger creates a new ledger
func NewLedger(db *pgxpool.Pool) *Ledger {
return &Ledger{db: db}
}
// Entry represents a ledger entry
type Entry struct {
ID string
CustomerID string
AccountType string // "asset", "liability", "equity"
Amount string
Currency string
Description string
Reference string
CreatedAt time.Time
}
// CreateEntry creates a double-entry ledger entry
func (l *Ledger) CreateEntry(ctx context.Context, debit, credit *Entry) error {
tx, err := l.db.Begin(ctx)
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback(ctx)
// Insert debit entry
debitQuery := `
INSERT INTO ledger_entries (
customer_id, account_type, amount, currency, description, reference, side, created_at
) VALUES ($1, $2, $3, $4, $5, $6, 'debit', NOW())
`
_, err = tx.Exec(ctx, debitQuery,
debit.CustomerID, debit.AccountType, debit.Amount, debit.Currency,
debit.Description, debit.Reference,
)
if err != nil {
return fmt.Errorf("failed to create debit entry: %w", err)
}
// Insert credit entry
creditQuery := `
INSERT INTO ledger_entries (
customer_id, account_type, amount, currency, description, reference, side, created_at
) VALUES ($1, $2, $3, $4, $5, $6, 'credit', NOW())
`
_, err = tx.Exec(ctx, creditQuery,
credit.CustomerID, credit.AccountType, credit.Amount, credit.Currency,
credit.Description, credit.Reference,
)
if err != nil {
return fmt.Errorf("failed to create credit entry: %w", err)
}
return tx.Commit(ctx)
}
// GetBalance gets account balance for a customer
func (l *Ledger) GetBalance(ctx context.Context, customerID, accountType string) (string, error) {
query := `
SELECT
SUM(CASE WHEN side = 'debit' THEN amount::numeric ELSE -amount::numeric END) as balance
FROM ledger_entries
WHERE customer_id = $1 AND account_type = $2
`
var balance string
err := l.db.QueryRow(ctx, query, customerID, accountType).Scan(&balance)
if err != nil {
return "0", nil
}
return balance, nil
}