Repository navigation
feat: enforce order status transitions, restock/reverse on cancel - #82
Merged
Merged
Conversation
Two gaps found while diffing the Orders module flow (Notion) against
create_order()/update_order_status():
1. No status state machine. PUT /{order_id}/status only checked that
the target string was one of VALID_STATUSES, with no check on
whether the *current* status could legally move there — a store
could jump straight from "confirmed" to "delivered", or "unmark"
a delivered/cancelled order. Added ORDER_STATUS_TRANSITIONS
(order_validation.py) matching the diagram's confirmed -> ready ->
delivered path, with cancellation reachable any time before
delivery and delivered/cancelled as terminal states.
2. Cancelling an order had no side effects beyond flipping the status
string: stock decremented at order-create time was never restocked,
and loyalty points earned/redeemed on the order were never
reversed. Added OrderService._cancel_order(), invoked automatically
when update_order_status() transitions to "cancelled" — restocks
each OrderItem's inventory, reverses the order's earned/redeemed
LoyaltyTransaction effects (logged as new "adjusted" transactions
for audit trail), and frees up one coupon use if a coupon was
applied.
api/order_api.py's PUT /{order_id}/status now surfaces invalid
transitions as 400 (previously only 404-for-not-found and
400-for-unrecognized-status-string existed).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t1eBFvEm5tiPyqQiQ3DNM
3 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two gaps surfaced while diffing the "Orders module" flow (Notion) against the actual
create_order()/update_order_status()code:PUT /order/{order_id}/statusonly validated that the target string was a recognized status, not that the current status could legally move there — a store could jumpconfirmed → delivereddirectly, or "revive" adelivered/cancelledorder. AddedORDER_STATUS_TRANSITIONS(api/validators/order_validation.py) matching the diagram'sconfirmed → ready → deliveredpath; cancellation is reachable any time before delivery,delivered/cancelledare terminal. Self-transitions (same status twice) are a no-op, not an error.OrderService._cancel_order(), invoked automatically whenupdate_order_status()transitions to"cancelled"— restocks everyOrderItem's inventory, reverses the order's earned/redeemedLoyaltyTransactioneffects (new"adjusted"-type transactions for audit trail), and frees up one coupon use if a coupon was applied.PUT /{order_id}/statusnow returns 400 for an illegal transition (previously only 404-not-found and 400-unrecognized-status-string existed).Related: the Notion "Orders module" flowchart's delivery-dispatch step has also been updated to make clear that's a manual store action ("Request Delivery"), not automatic — per
SPEC_DELIVERY_DISPATCH.md§3.4.Test plan
update_order_statusstill 200s on a legal transition (tests/unit/test_store_orders_service_extra.py)tests/integration/test_order_promo_extra.py::TestUpdateOrderStatus):confirmed → delivereddirect jump rejected (400)cancelledis terminal —cancelled → confirmedrejected (400)pytest tests/ -v— 610/610 pass🤖 Generated with Claude Code
https://claude.ai/code/session_014t1eBFvEm5tiPyqQiQ3DNM