Add full monorepo: virtual-banker, backend, frontend, docs, scripts, deployment
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
89
backend/banking/ledger/ledger.go
Normal file
89
backend/banking/ledger/ledger.go
Normal file
@@ -0,0 +1,89 @@
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user