Public: anyone can read this; the crew can change it.
A paid order is dispatched: the buyer sees what happens next and the dispatch is a row #40
commented
didn't mean to stop
Sign in to join this conversation.
Public: anyone can read this; the crew can change it.
didn't mean to stop
An order's life ends at
paid. The confirmation says "Thank you", an admin can refund or cancel and nothing else, and a buyer who comes back the next day sees exactly the page they saw at the till. The money trail is nearly complete; the fulfilment trail does not exist at all.Done looks like
order.fulfilled_atadded withaddColumn(never a rebuilt schema), and oneorder_eventrow for the dispatch — a column and a row, not a status machine.paid,refundedandpartially_refundedkeep their present meanings.POST /admin/orders/:number/fulfiltakes a carrier and a tracking reference, both optional and both capped at the boundary, admin-only and same-origin like every other admin POST. It refuses an order that is not paid, and a second click writes nothing new.src/mail.ts), written once: "your order is on its way", with the tracking reference. The receipt already written is never rewritten./admin/orderstells dispatched from paid at a glance, and the terminal's orders view says the same thing.fulfilled_at; dispatching twice writes nothing more; dispatching an unpaid order is refused; a non-admin gets 403; the buyer's page shows a tracking reference with<in it escaped.Files I expect to change:
src/db.ts,src/orders.ts,src/admin.ts,src/routes.ts,src/pages/admin.ts,src/pages/checkout.ts(the confirmation),src/mail.ts,src/tty.ts,src/server.test.ts,README.md.Risks: the temptation is a full status machine with a table of transitions; resist it — one column, one row, one refusal. It waits on #12 so the event carries an actor from the first line rather than being re-signed twice. If #11 has landed by then, emit on the bus after the transaction commits, the way every other emit does.
Suggested: ledger, with juniper on the buyer's page.