| | 1 | = Use-case UC0003 – Admin issues an invoice with line items = |
| | 2 | |
| | 3 | '''Initiating actor:''' Admin |
| | 4 | |
| | 5 | '''Other actors:''' Client (the invoice recipient) |
| | 6 | |
| | 7 | '''Description:''' The Admin issues an invoice to a client. The Admin selects the client from a list, adds one or more itemized line items, and the system stores the invoice header together with its line items and computed totals. |
| | 8 | |
| | 9 | == Scenario == |
| | 10 | |
| | 11 | 1. '''Admin''' chooses "Create Invoice". To populate the client selector, the '''system''' lists the clients: |
| | 12 | {{{ |
| | 13 | #!sql |
| | 14 | SELECT id, name, company, email |
| | 15 | FROM project.users |
| | 16 | WHERE role = 'CLIENT' |
| | 17 | ORDER BY name; |
| | 18 | }}} |
| | 19 | 2. '''Admin''' selects the client, enters the invoice details and the line items (name, quantity, unit price). The system computes the subtotal, tax, and total. |
| | 20 | 3. '''System''' inserts the invoice header: |
| | 21 | {{{ |
| | 22 | #!sql |
| | 23 | INSERT INTO project.invoices |
| | 24 | (invoice_number, client_id, amount, subtotal, tax_rate, tax_amount, |
| | 25 | due_date, issue_date, status, payment_terms, notes) |
| | 26 | VALUES |
| | 27 | ('INV-2026-005', 4, 2360.00, 2000.00, 18, 360.00, |
| | 28 | TIMESTAMP '2026-08-01', TIMESTAMP '2026-07-01', 'PENDING', 'Net 14', |
| | 29 | 'Website maintenance retainer') |
| | 30 | RETURNING id, invoice_number, subtotal, tax_rate, tax_amount, amount, status; |
| | 31 | }}} |
| | 32 | 4. '''System''' inserts the line items (one row per line): |
| | 33 | {{{ |
| | 34 | #!sql |
| | 35 | INSERT INTO project.invoice_line_items (invoice_id, name, description, quantity, unit_price, sort_order) |
| | 36 | VALUES |
| | 37 | (5, 'Monthly maintenance', 'Retainer - July', 1, 1500.00, 0), |
| | 38 | (5, 'Extra dev hours', '10h @ 50/h', 10, 50.00, 1) |
| | 39 | RETURNING invoice_id, name, quantity, unit_price, (quantity * unit_price) AS line_total, sort_order; |
| | 40 | }}} |
| | 41 | 5. '''System''' shows the finished invoice with its line items and total for confirmation: |
| | 42 | {{{ |
| | 43 | #!sql |
| | 44 | SELECT i.invoice_number, i.status, u.name AS client, |
| | 45 | json_agg(json_build_object( |
| | 46 | 'name', li.name, 'qty', li.quantity, |
| | 47 | 'unit_price', li.unit_price, 'line_total', li.quantity * li.unit_price |
| | 48 | ) ORDER BY li.sort_order) AS line_items, |
| | 49 | SUM(li.quantity * li.unit_price) AS subtotal_from_lines, |
| | 50 | i.subtotal, i.tax_amount, |
| | 51 | i.amount AS invoice_total |
| | 52 | FROM project.invoices i |
| | 53 | JOIN project.users u ON u.id = i.client_id |
| | 54 | JOIN project.invoice_line_items li ON li.invoice_id = i.id |
| | 55 | WHERE i.id = 1 |
| | 56 | GROUP BY i.invoice_number, i.status, u.name, i.subtotal, i.tax_amount, i.amount; |
| | 57 | }}} |
| | 58 | |
| | 59 | The invoice header and line items are inserted in one transaction so an invoice is never stored without its lines. The final SELECT confirms the stored total matches the sum of the line items plus tax. |