feat: add currency to accounts

This commit is contained in:
juancwu 2026-05-04 04:24:08 +00:00
commit ca0fec563e
21 changed files with 627 additions and 63 deletions

View file

@ -3,11 +3,21 @@ package repository
import (
"database/sql"
"errors"
"time"
"git.juancwu.dev/juancwu/budgit/internal/model"
"github.com/jmoiron/sqlx"
"github.com/shopspring/decimal"
)
// AllocationConversion describes the converted amount/target for a single
// allocation row when an account's currency is changed.
type AllocationConversion struct {
ID string
Amount decimal.Decimal
TargetAmount *decimal.Decimal
}
var ErrAccountNotFound = errors.New("account not found")
type AccountRepository interface {
@ -16,6 +26,9 @@ type AccountRepository interface {
BySpaceID(spaceID string) ([]*model.Account, error)
Rename(id, name string) error
Delete(id string) error
// ChangeCurrency atomically updates an account's currency and balance and
// rewrites each provided allocation's amount/target in the new currency.
ChangeCurrency(accountID, newCurrency string, newBalance decimal.Decimal, allocationConversions []AllocationConversion) error
}
type accountRepository struct {
@ -27,9 +40,9 @@ func NewAccountRepository(db *sqlx.DB) AccountRepository {
}
func (r *accountRepository) Create(account *model.Account) error {
query := `INSERT INTO accounts (id, name, space_id, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5);`
_, err := r.db.Exec(query, account.ID, account.Name, account.SpaceID, account.CreatedAt, account.UpdatedAt)
query := `INSERT INTO accounts (id, name, space_id, currency, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6);`
_, err := r.db.Exec(query, account.ID, account.Name, account.SpaceID, account.Currency, account.CreatedAt, account.UpdatedAt)
return err
}
@ -64,3 +77,24 @@ func (r *accountRepository) Delete(id string) error {
_, err := r.db.Exec(query, id)
return err
}
func (r *accountRepository) ChangeCurrency(accountID, newCurrency string, newBalance decimal.Decimal, allocationConversions []AllocationConversion) error {
return WithTx(r.db, func(tx *sqlx.Tx) error {
now := time.Now()
if _, err := tx.Exec(
`UPDATE accounts SET currency = $1, balance = $2, updated_at = $3 WHERE id = $4;`,
newCurrency, newBalance, now, accountID,
); err != nil {
return err
}
for _, c := range allocationConversions {
if _, err := tx.Exec(
`UPDATE allocations SET amount = $1, target_amount = $2, updated_at = $3 WHERE id = $4;`,
c.Amount, c.TargetAmount, now, c.ID,
); err != nil {
return err
}
}
return nil
})
}