From eb461aa7fb8d668028e83618a9175a9bd527640f Mon Sep 17 00:00:00 2001 From: Tim Wundenberg Date: Fri, 16 May 2025 22:26:41 +0200 Subject: [PATCH] feat(transaction): #80 calculate balances --- handler/transaction.go | 38 +- service/transaction.go | 116 ++++- template/transaction/transaction.templ | 6 +- test.log | 572 ------------------------- 4 files changed, 121 insertions(+), 611 deletions(-) delete mode 100644 test.log diff --git a/handler/transaction.go b/handler/transaction.go index 8bb27a0..53d07d8 100644 --- a/handler/transaction.go +++ b/handler/transaction.go @@ -3,7 +3,6 @@ package handler import ( "net/http" "spend-sparrow/handler/middleware" - "spend-sparrow/log" "spend-sparrow/service" t "spend-sparrow/template/transaction" "spend-sparrow/types" @@ -43,24 +42,6 @@ func (h TransactionImpl) Handle(r *http.ServeMux) { 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 - } - - utils.TriggerToastWithStatus(w, r, "success", "Balances recalculated", http.StatusOK) - } -} - func (h TransactionImpl) handleTransactionPage() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { user := middleware.GetUser(r) @@ -192,6 +173,24 @@ func (h TransactionImpl) handleUpdateTransaction() http.HandlerFunc { } } +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 + } + + utils.TriggerToastWithStatus(w, r, "success", "Balances recalculated, please refresh", http.StatusOK) + } +} + func (h TransactionImpl) handleDeleteTransaction() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { user := middleware.GetUser(r) @@ -225,6 +224,5 @@ func (h TransactionImpl) getTransactionData(accounts []*types.Account, treasureC treasureChestMap[treasureChest.Id] = root + treasureChest.Name } } - log.Info("treasureChestMap: %v", treasureChestMap) return accountMap, treasureChestMap } diff --git a/service/transaction.go b/service/transaction.go index 69b60d7..c48fbdc 100644 --- a/service/transaction.go +++ b/service/transaction.go @@ -76,7 +76,18 @@ func (s TransactionImpl) Add(user *types.User, transactionInput types.Transactio 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) + err = db.TransformAndLogDbError("transaction Update", r, err) + if err != nil { + return nil, 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 Update", r, err) if err != nil { return nil, err } @@ -111,7 +122,17 @@ func (s TransactionImpl) Update(user *types.User, input types.TransactionInput) 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) + err = db.TransformAndLogDbError("transaction Update", r, err) + if err != nil { + return nil, 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 Update", r, err) if err != nil { return nil, err } @@ -127,7 +148,17 @@ func (s TransactionImpl) Update(user *types.User, input types.TransactionInput) 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) + err = db.TransformAndLogDbError("transaction Update", r, err) + if err != nil { + return nil, 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 Update", r, err) if err != nil { return nil, err } @@ -211,7 +242,16 @@ func (s TransactionImpl) Delete(user *types.User, id string) error { 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 { + if err != nil && err != db.ErrNotFound { + return err + } + r, err = s.db.Exec(` + UPDATE treasure_chest + SET current_balance = current_balance - (SELECT value FROM "transaction" WHERE id = ? AND user_id = ?) + WHERE id = (SELECT treasure_chest_id FROM "transaction" WHERE id = ? AND user_id = ?) + `, uuid, user.Id, uuid, user.Id) + err = db.TransformAndLogDbError("transaction Delete", r, err) + if err != nil && err != db.ErrNotFound { return err } @@ -230,7 +270,14 @@ func (s TransactionImpl) RecalculateBalances(user *types.User) error { return ErrUnauthorized } - r, err := s.db.Exec(` + tx, err := s.db.Beginx() + err = db.TransformAndLogDbError("transaction RecalculateBalances", nil, err) + if err != nil { + return err + } + defer tx.Rollback() + + r, err := tx.Exec(` UPDATE account SET current_balance = 0 WHERE user_id = ?`, user.Id) @@ -239,7 +286,7 @@ func (s TransactionImpl) RecalculateBalances(user *types.User) error { return err } - r, err = s.db.Exec(` + r, err = tx.Exec(` UPDATE treasure_chest SET current_balance = 0 WHERE user_id = ?`, user.Id) @@ -248,7 +295,10 @@ func (s TransactionImpl) RecalculateBalances(user *types.User) error { return err } - rows, err := s.db.Queryx(`SELECT account_id, treasure_chest_id, value FROM "transaction" WHERE user_id = ?`, user.Id) + rows, err := tx.Queryx(` + SELECT * + FROM "transaction" + WHERE user_id = ?`, user.Id) err = db.TransformAndLogDbError("transaction RecalculateBalances", nil, err) if err != nil && err != db.ErrNotFound { return err @@ -268,8 +318,22 @@ func (s TransactionImpl) RecalculateBalances(user *types.User) error { return err } + updateErrors(transaction) + r, err = tx.Exec(` + UPDATE "transaction" + SET error = ? + WHERE user_id = ? + AND id = ?`, transaction.Error, user.Id, transaction.Id) + err = db.TransformAndLogDbError("transaction RecalculateBalances", r, err) + if err != nil { + return err + } + if transaction.Error != nil { + log.Info("err: %s", *transaction.Error) + } + if transaction.AccountId != nil { - r, err = s.db.Exec(` + r, err = tx.Exec(` UPDATE account SET current_balance = current_balance + ? WHERE id = ? AND user_id = ?`, transaction.Value, transaction.AccountId, user.Id) @@ -279,7 +343,7 @@ func (s TransactionImpl) RecalculateBalances(user *types.User) error { } } if transaction.TreasureChestId != nil { - r, err = s.db.Exec(` + r, err = tx.Exec(` UPDATE treasure_chest SET current_balance = current_balance + ? WHERE id = ? AND user_id = ?`, transaction.Value, transaction.TreasureChestId, user.Id) @@ -290,6 +354,8 @@ func (s TransactionImpl) RecalculateBalances(user *types.User) error { } } + tx.Commit() + return nil } @@ -404,20 +470,34 @@ func (s TransactionImpl) validateAndEnrichTransaction(oldTransaction *types.Tran UpdatedAt: updatedAt, UpdatedBy: &updatedBy, } - error := getErrors(transaction) - if error != "" { - transaction.Error = &error - } + + updateErrors(&transaction) + return &transaction, nil } -func getErrors(transaction types.Transaction) string { +func updateErrors(transaction *types.Transaction) { + error := "" + if transaction.Value < 0 { - // panic("unimplemented") + if transaction.AccountId == nil { + error = "no account specified" + } else if transaction.TreasureChestId == nil { + error = "no treasure chest specified" + } } else if transaction.Value > 0 { - // panic("unimplemented") + if transaction.AccountId == nil && transaction.TreasureChestId == nil { + error = "either an account or a treasure chest needs to be specified" + } else if transaction.AccountId != nil && transaction.TreasureChestId != nil { + error = "positive amounts can only be applied to either an account or a treasure chest" + } } else { - return "\"value\" needs to be positive or negative" + error = "\"value\" needs to be specified" + } + + if error == "" { + transaction.Error = nil + } else { + transaction.Error = &error } - return "" } diff --git a/template/transaction/transaction.templ b/template/transaction/transaction.templ index 59965a5..1e93f54 100644 --- a/template/transaction/transaction.templ +++ b/template/transaction/transaction.templ @@ -174,7 +174,11 @@ templ TransactionItem(transaction *types.Transaction, accounts, treasureChests m }

-

{ displayBalance(transaction.Value)+" €" }

+ if transaction.Value < 0 { +

{ displayBalance(transaction.Value)+" €" }

+ } else { +

{ displayBalance(transaction.Value)+" €" }

+ }