Changes between Initial Version and Version 1 of UseCase0003Implementation


Ignore:
Timestamp:
09/24/26 14:02:09 (4 days ago)
Author:
231285
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • UseCase0003Implementation

    v1 v1  
     1= Use-case 0003 Implementation - Deposit virtual funds =
     2
     3'''Initiating actor:''' Trader
     4
     5'''Other actors:''' —
     6
     7A logged-in Trader tops up their virtual cash balance in USD. This is a
     8simulation-only operation: no real money changes hands, the amount is simply added to
     9the Trader's `available_balance`. Every deposit is also recorded in the ledger
     10(`transactions`) so that it appears in the transaction history
     11([wiki:UseCase0006Implementation UseCase0006]). The operation writes to two tables —
     12the user row and the ledger — inside a single database transaction, so either both
     13changes are stored or neither is. Non-numeric, zero or negative amounts are rejected
     14before the database is touched.
     15
     16Original use-case description (P3): [wiki:UseCase0003].
     17Implementation: `server/account.go`, function `Deposit`; the verification uses
     18`ShowBalance` from the same file (the code is shown at the end of this page).
     19
     20Precondition: the Trader is logged in ([wiki:UseCase0002Implementation UseCase0002]);
     21in the run below as `alice`, who starts from the seed state (available 8250.00 USD,
     22invested 1750.00 USD).
     23
     24== Scenario ==
     25
     26 1. '''Trader''' chooses `[2] Deposit virtual funds` in the authenticated menu
     27    (types `2`).
     28 2. '''System''' prints `-- Deposit virtual funds --` and asks `Amount (USD):`.
     29
     30The screenshot shows steps 1–2: the login as alice, the authenticated menu, the
     31choice `2` and the amount prompt waiting for input.
     32
     33[[Image(uc0003_1_2_deposit.png)]]
     34
     35 3. '''Trader''' enters an amount: `500`.
     36 4. '''System''' validates the input in Go, without accessing the database: the text
     37    must parse as a number (`strconv.ParseFloat`) and be greater than 0 (see
     38    alternate flow 4a).
     39 5. '''System''' opens one database transaction (`db.DB.Begin()`), increments the
     40    balance, writes the ledger row and commits. Both statements run in this single
     41    transaction; if either fails, the deferred `tx.Rollback()` undoes everything.
     42    `BEGIN` and `COMMIT` are issued by Go's `Begin()` / `Commit()`; the two
     43    statements are sent exactly as in the code:
     44
     45{{{
     46BEGIN;
     47
     48UPDATE users
     49    SET available_balance = available_balance + $1,
     50        updated_at        = now()
     51  WHERE id = $2;
     52
     53INSERT INTO transactions (user_id, type, amount, currency, description)
     54VALUES ($1, 'deposit', $2, 'USD', 'Virtual deposit');
     55
     56COMMIT;
     57}}}
     58
     59Parameters: placeholders are numbered per statement. In the `UPDATE`, `$1` is the
     60amount (`500`) and `$2` the user id (`amt, s.UserID`); in the `INSERT` it is the
     61other way round, `$1` is the user id and `$2` the amount (`s.UserID, amt`),
     62matching the column order.
     63
     64 6. '''System''' confirms `Deposited 500.0000 USD.` and returns to the authenticated
     65    menu.
     66
     67The screenshot shows steps 3–6: the entered amount `500`, the confirmation and the
     68authenticated menu again.
     69
     70[[Image(uc0003_3_6_deposited.png)]]
     71
     72The statements run on the `project` schema (the connection sets
     73`search_path=project,public`), so `users` and `transactions` mean `project.users`
     74and `project.transactions`.
     75
     76=== Alternate flow 4a — invalid amount ===
     77
     78If the amount is not a number, or is zero or negative, the system prints
     79`Invalid amount.`; no transaction is started and nothing is written. In the run the
     80Trader first entered `-50`. In the prototype the system then shows the
     81authenticated menu again and the Trader chooses `[2] Deposit virtual funds` once
     82more, which returns the scenario to step 2.
     83
     84[[Image(uc0003_4a_invalid.png)]]
     85
     86== Verification ==
     87
     88Right after the deposit the Trader chooses `[1] View balance` (function
     89`ShowBalance`), which runs (`$1` = user id):
     90
     91{{{
     92SELECT available_balance, invested_balance FROM users WHERE id = $1
     93}}}
     94
     95It prints `Available: 8750.0000 USD`, `Invested : 1750.0000 USD`,
     96`Total    : 10500.0000 USD`. The available balance grew from the seed value 8250.00
     97by exactly the 500.00 deposited (the rejected `-50` changed nothing), and
     98`invested_balance` is untouched.
     99
     100[[Image(uc0003_verify_balance.png)]]
     101
     102== How to reproduce ==
     103
     104{{{
     105./eduberza -init
     106./eduberza
     107# [2] Login: alice / test123
     108# [2] Deposit virtual funds: -50   -> Invalid amount.
     109# [2] Deposit virtual funds: 500   -> Deposited 500.0000 USD.
     110# [1] View balance                 -> Available: 8750.0000 USD
     111}}}
     112
     113All four screenshots come from one real run of exactly these inputs.
     114
     115== Source code ==
     116
     117`server/account.go` — `Deposit`, and `ShowBalance` used for the verification:
     118
     119{{{
     120// ShowBalance prints the logged-in user's balances.
     121func ShowBalance(s *Session) {
     122        var avail, invested float64
     123        err := db.DB.QueryRow(
     124                `SELECT available_balance, invested_balance FROM users WHERE id = $1`,
     125                s.UserID,
     126        ).Scan(&avail, &invested)
     127        if err != nil {
     128                fmt.Println("Error:", err)
     129                return
     130        }
     131        fmt.Printf("\n  Available: %.4f USD\n", avail)
     132        fmt.Printf("  Invested : %.4f USD\n", invested)
     133        fmt.Printf("  Total    : %.4f USD\n", avail+invested)
     134}
     135
     136// Deposit - UC0003
     137// Transactional: updates users.available_balance and inserts a ledger row.
     138func Deposit(s *Session) {
     139        fmt.Println("\n-- Deposit virtual funds --")
     140        amtStr := prompt("Amount (USD): ")
     141        amt, err := strconv.ParseFloat(amtStr, 64)
     142        if err != nil || amt <= 0 {
     143                fmt.Println("Invalid amount.")
     144                return
     145        }
     146
     147        tx, err := db.DB.Begin()
     148        if err != nil {
     149                fmt.Println("Error:", err)
     150                return
     151        }
     152        defer tx.Rollback()
     153
     154        if _, err := tx.Exec(
     155                `UPDATE users
     156                    SET available_balance = available_balance + $1,
     157                        updated_at        = now()
     158                  WHERE id = $2`,
     159                amt, s.UserID,
     160        ); err != nil {
     161                fmt.Println("Error:", err)
     162                return
     163        }
     164        if _, err := tx.Exec(
     165                `INSERT INTO transactions (user_id, type, amount, currency, description)
     166                 VALUES ($1, 'deposit', $2, 'USD', 'Virtual deposit')`,
     167                s.UserID, amt,
     168        ); err != nil {
     169                fmt.Println("Error:", err)
     170                return
     171        }
     172        if err := tx.Commit(); err != nil {
     173                fmt.Println("Error:", err)
     174                return
     175        }
     176        fmt.Printf("Deposited %.4f USD.\n", amt)
     177}
     178}}}