Commit Graph
2 Commits
Author SHA1 Message Date
james d7fddf53ab fix(api): record an elapsed group as expired when its opener cancels
Cancelling the opening unpaid order ran the expiry sweep only implicitly, so a
group whose lifetime had already ended was relabelled cancelled instead of
expired. Both conditions hold in that case; sweep first so the lifetime ending
wins. The behaviour was invisible to shoppers (neither state is joinable) but it
matters to a future refund flow reading expired paid groups.

Add an integration test that reproduces the precedence.
2026-09-18 13:44:02 +00:00
james a7bc476251 feat(api): group buying with payment-time seat claims
Shop-owned activities on one SKU, concrete groups with an
open/successful/expired/cancelled lifecycle, and one paid membership row per
paid order. Checkout accepts a single-SKU quantity-1 intent, snapshots the group
price and identity on the pending order, and opens or references a group; a paid
seat is claimed only at payment, which locks the group and fills it exactly at
capacity.

Pending-payment cancellation restores SKU stock only and never rolls back paid
seats; an unpaid opener cancelling closes a still-empty group. Coupons are
refused on a group shop order, and the flash-sale exclusion is now enforced in
both directions because the activity table exists, which activates the guard
add-flash-sales shipped dormant. The activity column names follow the contract
recorded in this change's design.

Surfaces (shop-admin, mall) and seeding follow.
2026-09-18 13:16:28 +00:00