feat: create accounts to partition money

This commit is contained in:
juancwu 2026-02-13 00:56:07 +00:00
commit d6f6790c4d
11 changed files with 1026 additions and 4 deletions

View file

@ -0,0 +1,137 @@
package repository
import (
"database/sql"
"errors"
"time"
"git.juancwu.dev/juancwu/budgit/internal/model"
"github.com/jmoiron/sqlx"
)
var (
ErrMoneyAccountNotFound = errors.New("money account not found")
ErrTransferNotFound = errors.New("account transfer not found")
)
type MoneyAccountRepository interface {
Create(account *model.MoneyAccount) error
GetByID(id string) (*model.MoneyAccount, error)
GetBySpaceID(spaceID string) ([]*model.MoneyAccount, error)
Update(account *model.MoneyAccount) error
Delete(id string) error
CreateTransfer(transfer *model.AccountTransfer) error
GetTransfersByAccountID(accountID string) ([]*model.AccountTransfer, error)
DeleteTransfer(id string) error
GetAccountBalance(accountID string) (int, error)
GetTotalAllocatedForSpace(spaceID string) (int, error)
}
type moneyAccountRepository struct {
db *sqlx.DB
}
func NewMoneyAccountRepository(db *sqlx.DB) MoneyAccountRepository {
return &moneyAccountRepository{db: db}
}
func (r *moneyAccountRepository) Create(account *model.MoneyAccount) error {
query := `INSERT INTO money_accounts (id, space_id, name, created_by, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6);`
_, err := r.db.Exec(query, account.ID, account.SpaceID, account.Name, account.CreatedBy, account.CreatedAt, account.UpdatedAt)
return err
}
func (r *moneyAccountRepository) GetByID(id string) (*model.MoneyAccount, error) {
account := &model.MoneyAccount{}
query := `SELECT * FROM money_accounts WHERE id = $1;`
err := r.db.Get(account, query, id)
if err == sql.ErrNoRows {
return nil, ErrMoneyAccountNotFound
}
return account, err
}
func (r *moneyAccountRepository) GetBySpaceID(spaceID string) ([]*model.MoneyAccount, error) {
var accounts []*model.MoneyAccount
query := `SELECT * FROM money_accounts WHERE space_id = $1 ORDER BY created_at DESC;`
err := r.db.Select(&accounts, query, spaceID)
if err != nil {
return nil, err
}
return accounts, nil
}
func (r *moneyAccountRepository) Update(account *model.MoneyAccount) error {
account.UpdatedAt = time.Now()
query := `UPDATE money_accounts SET name = $1, updated_at = $2 WHERE id = $3;`
result, err := r.db.Exec(query, account.Name, account.UpdatedAt, account.ID)
if err != nil {
return err
}
rows, err := result.RowsAffected()
if err == nil && rows == 0 {
return ErrMoneyAccountNotFound
}
return err
}
func (r *moneyAccountRepository) Delete(id string) error {
query := `DELETE FROM money_accounts WHERE id = $1;`
result, err := r.db.Exec(query, id)
if err != nil {
return err
}
rows, err := result.RowsAffected()
if err == nil && rows == 0 {
return ErrMoneyAccountNotFound
}
return err
}
func (r *moneyAccountRepository) CreateTransfer(transfer *model.AccountTransfer) error {
query := `INSERT INTO account_transfers (id, account_id, amount_cents, direction, note, created_by, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7);`
_, err := r.db.Exec(query, transfer.ID, transfer.AccountID, transfer.AmountCents, transfer.Direction, transfer.Note, transfer.CreatedBy, transfer.CreatedAt)
return err
}
func (r *moneyAccountRepository) GetTransfersByAccountID(accountID string) ([]*model.AccountTransfer, error) {
var transfers []*model.AccountTransfer
query := `SELECT * FROM account_transfers WHERE account_id = $1 ORDER BY created_at DESC;`
err := r.db.Select(&transfers, query, accountID)
if err != nil {
return nil, err
}
return transfers, nil
}
func (r *moneyAccountRepository) DeleteTransfer(id string) error {
query := `DELETE FROM account_transfers WHERE id = $1;`
result, err := r.db.Exec(query, id)
if err != nil {
return err
}
rows, err := result.RowsAffected()
if err == nil && rows == 0 {
return ErrTransferNotFound
}
return err
}
func (r *moneyAccountRepository) GetAccountBalance(accountID string) (int, error) {
var balance int
query := `SELECT COALESCE(SUM(CASE WHEN direction = 'deposit' THEN amount_cents ELSE -amount_cents END), 0) FROM account_transfers WHERE account_id = $1;`
err := r.db.Get(&balance, query, accountID)
return balance, err
}
func (r *moneyAccountRepository) GetTotalAllocatedForSpace(spaceID string) (int, error) {
var total int
query := `SELECT COALESCE(SUM(CASE WHEN t.direction = 'deposit' THEN t.amount_cents ELSE -t.amount_cents END), 0)
FROM account_transfers t
JOIN money_accounts a ON t.account_id = a.id
WHERE a.space_id = $1;`
err := r.db.Get(&total, query, spaceID)
return total, err
}