The product page loads a shop's claimable coupons through the shared client and
claims them in place; the buyer coupon list reads owned snapshots with their
claimed/redeemed/expired state. Checkout offers each shop order at most one
owned coupon and submits only the choice; the pay page and order detail render
the server-persisted discount and reduced total, never a client figure.
Coupon fixtures leave the pages; the fixed-data adapter still serves the coupon
domain as the rollback path. The demo seed creates two deterministic templates
and claims them for the demo customer.
Live addresses domain for the mall: shared contract (AddressBookEntry +
five client methods), mock adapter state v3, live domain pick, addresses
page CRUD, checkout saved-address picker with manual fallback, demo seed,
and the archived change plus address-book capability spec.
Wave 3 of replacing the fixed-data mock adapter: cart, orders, shipments and
invoices flip together, so one purchase runs end to end against the backend.
- cart: CartItemView carries the line's shop and the SKU's stock, so the cart
keeps grouping per shop and the quantity stepper caps at real stock instead
of a hard-coded 999
- contract: Shipment.items is optional and Invoice.invoice_no nullable, both
matching what the API actually returns. Invoice was declared twice in
types.ts and TypeScript merges duplicate interfaces, so the duplicate had to
go for the change to take effect at all
- an anonymous add-to-cart redirects to /login?redirect=..., and sign-in
honours only same-origin paths
- the fixed-data adapter learns the new cart fields, and its persisted state
key moves to v2 because a cart saved by an older build is no longer valid
- order surfaces drop their storeById lookups and keep the generic store label
until the public store read arrives
Verified end to end: two-shop cart grouping with live shop names, stock caps
read from the API, checkout, payment, shipment, delivery confirmation and an
issued invoice. Rollback re-verified with every domain on fixed data and the
backend stopped.
Also checks off Wave 3 in docs/TBD-migrate-wave.md and re-points that file at
the mock content that remains.
OpenSpec change: openspec/changes/replace-mock-api-wave-3