Sharpen ordering domain model and close a cancellation race
CONTEXT.md described Order Item cancellability and Order completion in ways the code didn't actually implement (cancellability only covered the first Preparation Step; completion required pickup, not any terminal state). Corrected the glossary to match intended behavior, added Guest Access Token and Charge as formal terms, and recorded the single-Charge-per-Order design as an ADR. Also closes a real race in cancelOrderItem: a Machine could claim a queued Step between the cancellability check and the delete, letting an already-cooking item get cancelled and refunded. The delete now atomically re-checks status = 'queued'. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+16
-2
@@ -413,9 +413,23 @@ export const cancelOrderItem = async (
|
||||
throw new OrderItemNotCancellableError('it has already started cooking')
|
||||
}
|
||||
|
||||
await tx
|
||||
// Re-check status = 'queued' as part of the delete itself, atomically:
|
||||
// a Machine may have claimed this Step (assignQueuedStepsForTag) between
|
||||
// the read above and this statement. If so, zero rows match and we must
|
||||
// not refund a Step that's already cooking.
|
||||
const deletedSteps = await tx
|
||||
.delete(schema.preparationStepInstances)
|
||||
.where(eq(schema.preparationStepInstances.id, currentStep.id))
|
||||
.where(
|
||||
and(
|
||||
eq(schema.preparationStepInstances.id, currentStep.id),
|
||||
eq(schema.preparationStepInstances.status, 'queued'),
|
||||
),
|
||||
)
|
||||
.returning({ id: schema.preparationStepInstances.id })
|
||||
|
||||
if (deletedSteps.length === 0) {
|
||||
throw new OrderItemNotCancellableError('it has already started cooking')
|
||||
}
|
||||
|
||||
await tx
|
||||
.update(schema.orderItems)
|
||||
|
||||
Reference in New Issue
Block a user