From d3064eaef4c75b27667252a422376eb11b07e869 Mon Sep 17 00:00:00 2001 From: Tim Wundenberg Date: Fri, 16 May 2025 15:45:52 +0200 Subject: [PATCH] feat(transaction): #80 calculate account balances --- handler/transaction.go | 17 +++ service/transaction.go | 109 +++++++++++++++++++- template/account/account.templ | 6 +- template/auth/user.templ | 14 ++- template/treasurechest/treasure_chest.templ | 6 +- 5 files changed, 146 insertions(+), 6 deletions(-) diff --git a/handler/transaction.go b/handler/transaction.go index 089b2a2..7a93c92 100644 --- a/handler/transaction.go +++ b/handler/transaction.go @@ -37,9 +37,26 @@ func (h TransactionImpl) Handle(r *http.ServeMux) { r.Handle("GET /transaction", h.handleTransactionPage()) r.Handle("GET /transaction/{id}", h.handleTransactionItemComp()) r.Handle("POST /transaction/{id}", h.handleUpdateTransaction()) + r.Handle("POST /transaction/recalculate", h.handleRecalculate()) r.Handle("DELETE /transaction/{id}", h.handleDeleteTransaction()) } +func (h TransactionImpl) handleRecalculate() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + user := middleware.GetUser(r) + if user == nil { + utils.DoRedirect(w, r, "/auth/signin") + return + } + + err := h.s.RecalculateBalances(user) + if err != nil { + handleError(w, r, err) + return + } + } +} + func (h TransactionImpl) handleTransactionPage() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { user := middleware.GetUser(r) diff --git a/service/transaction.go b/service/transaction.go index 5baeb23..8e45acf 100644 --- a/service/transaction.go +++ b/service/transaction.go @@ -31,6 +31,8 @@ type Transaction interface { Get(user *types.User, id string) (*types.Transaction, error) GetAll(user *types.User) ([]*types.Transaction, error) Delete(user *types.User, id string) error + + RecalculateBalances(user *types.User) error } type TransactionImpl struct { @@ -69,6 +71,15 @@ func (s TransactionImpl) Add(user *types.User, transactionInput types.Transactio return nil, err } + r, err = s.db.Exec(` + UPDATE account + SET current_balance = current_balance + ? + WHERE id = ? AND user_id = ?`, transaction.Value, transaction.AccountId, user.Id) + err = db.TransformAndLogDbError("transaction UpdateAccount", r, err) + if err != nil { + return nil, err + } + return transaction, nil } @@ -93,11 +104,33 @@ func (s TransactionImpl) Update(user *types.User, input types.TransactionInput) return nil, types.ErrInternal } + if transaction.AccountId != nil { + r, err := s.db.Exec(` + UPDATE account + SET current_balance = current_balance - ? + WHERE id = ? AND user_id = ?`, transaction.Value, transaction.AccountId, user.Id) + err = db.TransformAndLogDbError("transaction UpdateAccount", r, err) + if err != nil { + return nil, err + } + } + transaction, err = s.validateAndEnrichTransaction(transaction, user.Id, input) if err != nil { return nil, err } + if transaction.AccountId != nil { + r, err := s.db.Exec(` + UPDATE account + SET current_balance = current_balance + ? + WHERE id = ? AND user_id = ?`, transaction.Value, transaction.AccountId, user.Id) + err = db.TransformAndLogDbError("transaction UpdateAccount", r, err) + if err != nil { + return nil, err + } + } + r, err := s.db.NamedExec(` UPDATE "transaction" SET @@ -169,7 +202,17 @@ func (s TransactionImpl) Delete(user *types.User, id string) error { return fmt.Errorf("could not parse Id: %w", ErrBadRequest) } - r, err := s.db.Exec("DELETE FROM \"transaction\" WHERE id = ? AND user_id = ?", uuid, user.Id) + r, err := s.db.Exec(` + UPDATE account + SET current_balance = current_balance - (SELECT value FROM "transaction" WHERE id = ? AND user_id = ?) + WHERE id = (select account_id from "transaction" where id = ? AND user_id = ?) + `, uuid, user.Id, uuid, user.Id) + err = db.TransformAndLogDbError("transaction Delete", r, err) + if err != nil { + return err + } + + r, err = s.db.Exec("DELETE FROM \"transaction\" WHERE id = ? AND user_id = ?", uuid, user.Id) err = db.TransformAndLogDbError("transaction Delete", r, err) if err != nil { return err @@ -290,3 +333,67 @@ func (s TransactionImpl) validateAndEnrichTransaction(transaction *types.Transac UpdatedBy: &updatedBy, }, nil } + +func (s TransactionImpl) RecalculateBalances(user *types.User) error { + transactionMetric.WithLabelValues("recalculate").Inc() + if user == nil { + return ErrUnauthorized + } + + r, err := s.db.Exec(` + UPDATE account + SET current_balance = 0 + WHERE user_id = ?`, user.Id) + err = db.TransformAndLogDbError("transaction RecalculateBalances", r, err) + if err != nil && err != db.ErrNotFound { + return err + } + + r, err = s.db.Exec(` + UPDATE treasure_chest + SET current_balance = 0 + WHERE user_id = ?`, user.Id) + err = db.TransformAndLogDbError("transaction RecalculateBalances", r, err) + if err != nil && err != db.ErrNotFound { + return err + } + + rows, err := s.db.Queryx(`SELECT account_id, treasure_chest_id, value FROM "transaction" WHERE user_id = ?`, user.Id) + err = db.TransformAndLogDbError("transaction RecalculateBalances", nil, err) + if err != nil && err != db.ErrNotFound { + return err + } + defer rows.Close() + + transaction := &types.Transaction{} + for rows.Next() { + err = rows.StructScan(transaction) + err = db.TransformAndLogDbError("transaction RecalculateBalances", nil, err) + if err != nil { + return err + } + } + + if transaction.AccountId != nil { + r, err = s.db.Exec(` + UPDATE account + SET current_balance = current_balance + ? + WHERE id = ? AND user_id = ?`, transaction.Value, transaction.AccountId, user.Id) + err = db.TransformAndLogDbError("transaction RecalculateBalances", r, err) + if err != nil { + return err + } + } + if transaction.TreasureChestId != nil { + r, err = s.db.Exec(` + UPDATE treasure_chest + SET current_balance = current_balance + ? + WHERE id = ? AND user_id = ?`, transaction.Value, transaction.TreasureChestId, user.Id) + err = db.TransformAndLogDbError("transaction RecalculateBalances", r, err) + if err != nil { + return err + } + } + + return nil +} diff --git a/template/account/account.templ b/template/account/account.templ index 5d9c595..4a4dfdc 100644 --- a/template/account/account.templ +++ b/template/account/account.templ @@ -80,7 +80,11 @@ templ AccountItem(account *types.Account) {

{ account.Name }

-

{ displayBalance(account.CurrentBalance) }

+ if account.CurrentBalance < 0 { +

{ displayBalance(account.CurrentBalance) }

+ } else { +

{ displayBalance(account.CurrentBalance) }

+ } -