FTM302 · Examination paper
90 minutes · written · open book (notes) · no internet · 40 marks · pass 20
Section A — money (15 marks, 5 × 3)
Section titled “Section A — money (15 marks, 5 × 3)”All amounts in pence. Booking fee is 18%. FTM is agent, not principal, and charges no VAT. State the rows and the numbers.
A1. Client pays 100,000. What is the fee and what is band_net_fee?
A2. Band’s net fee is 82,000; band_paid_amount is 20,000. What is owed?
A3. Client pays 33,333. Compute the fee and state which rounding mode you used and why it matters.
A4. A colleague proposes adding a band_paid_fee column alongside
Invoice.band_paid_amount. Give two distinct reasons this is wrong.
A5. Band asks “what did the client pay for this gig?” — answer the question the band is actually allowed to have answered.
Section B — state machine (10 marks)
Section titled “Section B — state machine (10 marks)”Name the states an EnquiryBand moves through, from assignment to paid, and for
each transition give the guard that must hold and where it is enforced. Then state
why unbooking is not the reverse of booking. (7 + 3)
Section C — find the leak (8 marks)
Section titled “Section C — find the leak (8 marks)”This band-facing serializer is being added:
class BandEnquiryOut(Schema): id: int date: date | None location: str | None act: str | None budget_min: str | None budget_max: str | None requirements: str | None is_direct: bool band_fee_pence: intName every field that must not ship, why each is sensitive, and write the test that prevents the regression.
Section D — ranking (7 marks)
Section titled “Section D — ranking (7 marks)”A band with one booking moved from browse position 240 to 61. Say whether this is plausible, what mechanism would explain it, and the query you would run to decide whether it is a defect. (4 + 3)
Marking guide
Section titled “Marking guide”| Section | Marks |
|---|---|
| A | 3 each. 1 for the number, 2 for the reasoning |
| B | 7 for states+guards (1 each), 3 for asymmetry |
| C | 1 per correct field (max 5), 2 for the test, 1 for a reason naming commercial harm |
| D | 2 plausible/not with reason, 2 mechanism, 3 query |
Pass 20/40. Distinction 28. Section A must score at least 6 — money arithmetic is the module’s floor.
Expected answers:
- A1 fee 18,000;
band_net_fee82,000. - A2 owed = 82,000 − 20,000 = 62,000.
- A3 fee 6,000, band net 27,333. Round half up; Python’s
round()is banker’s rounding and disagrees at exact halves (25 → 5 half-up, 4 withround), so naming the mode is part of the contract. Sum must equal the gross. - A4 (i) two columns can disagree, and the ledger must be one; (ii) every reconciliation and report would need to pick one, so the ambiguity is permanent. Also acceptable: it invites double-counting on retry.
- A5 the band may see its own
band_fee/band_net_fee; the client’s total is not theirs to know. Answer with the band figure and say the client total is not disclosed. - B assigned → quoted → accepted (client) → BOOKED (invoice-coupled) → paid. Guards: band membership, a non-zero price, no existing booking for the same band+enquiry, invoice exists before BOOKED. Enforced in the service/state transition and by the uniqueness constraint — not in the template. Unbooking is asymmetric because fees, deposits and refunds already moved.
- C leak:
budget_min,budget_max(commercial position),requirements(raw client text),is_direct(that the client was approached directly).band_fee_penceis fine — it is the band’s own figure. Test: serialise as the band and assert absence of all four. - D plausible only if the move is explained by tier/locality, not by the single booking — one booking must not inflate rank. Query: the band’s booking count, browse ordinal, and the score components before/after; if the score delta equals the booking bonus, it is the inflation bug.