source: docs/P4-Prototype/wiki/UseCase0003Implementation.md

main
Last change on this file was ef1c1c7, checked in by Stefan <trsunovstefan@…>, 6 days ago

Wiki docs, phase 6 and phase 7 added

  • Property mode set to 100644
File size: 5.8 KB
Line 
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}}}
Note: See TracBrowser for help on using the repository browser.