90 lines
2.2 KiB
Go
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
|
|
}
|
|
|