Order Fulfillment: Reservations, Split Shipments and Cancellations
MediumAI-assisted coding · AI-Assisted Debugging
Order Fulfillment: Reservations, Split Shipments and Cancellations
You've joined the commerce platform team that owns order fulfillment. Customers place orders, the service picks which warehouse(s) supply them and reserves the stock, and later the order ships or is cancelled. Support has reported two inventory bugs, and two features are queued behind them.
Codebase
| File | What it does |
|---|---|
fulfillment/api.py | run(operations) entry point and the Dispatcher that routes each operation |
fulfillment/service.py | FulfillmentService: warehouses, stock, placing, shipping and cancelling orders |
fulfillment/allocator.py | Allocator.plan: decides which warehouse supplies which line (planning only, no stock changes) |
fulfillment/inventory.py | Warehouse (on-hand and reserved counts per SKU) and Inventory |
fulfillment/reservations.py | ReservationBook: the holds each order has on warehouse stock |
fulfillment/orders.py | OrderBook storage |
fulfillment/models.py | Allocation and Order dataclasses |
fulfillment/geometry.py | Manhattan distance |
fulfillment/validation.py | Argument-shape helpers |
fulfillment/config.py | Order limits and status names |
fulfillment/errors.py | FulfillmentError and every error code |
tests/ | Existing unit and end-to-end tests (some currently fail) |
How your code is run
run(operations: list[list]) -> list executes the operations in order against a fresh system and returns one result per operation. Every operation is [name, *args]. Errors are returned as "error: <code>" strings, never raised. Locations are integer (x, y) grid points; distance is Manhattan distance |x1-x2| + |y1-y2|. Ids and SKUs are case-sensitive non-empty strings.
| Operation | Arguments | Returns | Errors (in this order) |
|---|---|---|---|
add_warehouse | wh_id, x, y | true | invalid arguments, warehouse exists |
stock | wh_id, sku, qty | that warehouse's new on-hand count for the SKU | invalid arguments, invalid quantity (qty not an integer > 0), warehouse not found |
available | sku | units that can still be promised: on-hand minus reserved, summed over all warehouses (0 for an unknown SKU) | invalid arguments |
place_order | order_id, x, y, lines where lines is [[sku, qty], ...] | {"status": "reserved", "allocations": [[wh_id, sku, qty], ...]} | invalid arguments, order exists, insufficient stock |
ship | order_id | {"status": "shipped", "allocations": [...]} | order not found, already shipped, already cancelled |
order_status | order_id | {"status": "reserved" | "shipped" | "cancelled", "allocations": [...]} | order not found |
cancel | order_id | Part 3 | Part 3 |
cancel_line | order_id, sku | Part 3 | Part 3 |
- An unknown operation returns
"error: unknown operation"; a wrong number of arguments returns"error: invalid arguments". place_orderneeds 1 to 50 lines, each[sku, qty]with a valid SKU andqtyan integer> 0, and no SKU listed twice; otherwiseinvalid arguments.- Placing an order reserves its units: a reserved unit is still on hand, but it is no longer available to any other order. A rejected order reserves nothing.
- Single-warehouse rule: an order is supplied by the nearest warehouse that can cover every line from its available units. Nearest means smallest distance to the order's
(x, y); equal distances go to the smallerwh_id(plain string order).allocationslists one entry per line, in the order the lines were given. If no single warehouse can cover the order the result iserror: insufficient stock. shipmoves an open (reserved) order's units out of the warehouses: on-hand and reserved both drop by the allocated quantity.allocationsis the order's allocations.order_statusshows the order's current allocations; a shipped order keeps its allocations.
Worked example
[["add_warehouse", "east", 10, 0],
["add_warehouse", "west", 0, 0],
["stock", "east", "mug", 5],
["stock", "west", "mug", 3],
["place_order", "o1", 9, 1, [["mug", 2]]],
["available", "mug"],
["ship", "o1"],
["available", "mug"]]returns
[true, true, 5, 3,
{"status": "reserved", "allocations": [["east", "mug", 2]]},
6,
{"status": "shipped", "allocations": [["east", "mug", 2]]},
6]East is 2 away from (9, 1) and west is 10 away, so east supplies the order. Reserving 2 mugs leaves 6 available; shipping them removes the 2 units from on-hand, so 6 remain.
Part 1 — Fix the inventory bugs (OPS-1204, OPS-1219)
Goal: orders must be planned and reserved independently of each other.
Reports from the warehouse floor:
- "The confirmation for my second order lists items from my first order too, and our stock count drops twice for the items of the first one."
- "Two customers both got the last 3 T-shirts. Reserved stock seems to be ignored when we decide whether a warehouse can fulfill an order."
Rules
- The
allocationsof aplace_orderresult contain only that order's own lines, and only those lines are reserved. - A warehouse can supply a line only if its available units (on hand minus reserved) are at least the quantity requested.
tests/test_service.py::test_second_order_only_reports_its_own_allocations and ::test_reserved_units_are_not_available_to_other_orders currently fail.
Part 2 — Split shipments (OPS-1233)
Goal: when no single warehouse can cover an order, fulfil it from several warehouses instead of rejecting it.
Rules
- The single-warehouse rule still has priority: if any warehouse can cover the whole order, use the nearest one exactly as in Part 1.
- Otherwise split: handle the lines in the order given. For each line, go through the warehouses nearest first (ties by
wh_id, all warehouses are considered, even ones that cannot cover the whole order) and takemin(available there, still needed)from each until the line is complete. allocationslists the entries in that order: line by line, and within a line nearest warehouse first. A warehouse that contributes nothing to a line has no entry. A line may be split across warehouses; a warehouse may supply some lines and not others.- If any line cannot be completed from the combined available stock, the result is
error: insufficient stockand nothing is reserved (the order id is not used up andavailableis unchanged).
Example (east (10, 0) has 2 mugs; west (0, 0) has 3 mugs and 1 tee)
["place_order", "o1", 9, 1, [["mug", 4], ["tee", 1]]]returns
{"status": "reserved", "allocations": [["east", "mug", 2], ["west", "mug", 2], ["west", "tee", 1]]}Asking for 6 mugs instead returns "error: insufficient stock" and leaves available("mug") at 5.
Part 3 — Cancellations (OPS-1250)
Goal: customers can cancel a whole order or a single line, and the reserved stock goes back on sale.
Rules
cancel(order_id)cancels an open order. It releases every one of its reservations (reserved drops; on-hand is unchanged), sets the status tocancelled, and returns{"status": "cancelled", "released": [[wh_id, sku, qty], ...]}listing the order's allocations in the order they were made.cancel_line(order_id, sku)cancels the lines for one SKU (including every warehouse that was supplying it), releases just those reservations, and returns{"status": <order status afterwards>, "released": [...]}. The order staysreservedwith its other lines. If that was the last line, the order becomescancelled.- Errors for both, in this order:
order not found,already shipped,already cancelled; and forcancel_linealsoline not found(the order has no remaining line for that SKU, e.g. it was already cancelled). - After a cancel,
order_statusshows only the remaining allocations (none once cancelled),shiponly ships what is still allocated, and the freed units can be used by later orders. A cancelled order id still counts as used (order exists). - Earlier behavior must not change.
Example (continuing the Part 2 order o1)
[["cancel_line", "o1", "mug"], ["order_status", "o1"], ["available", "mug"], ["cancel", "o1"], ["cancel", "o1"]]returns
[{"status": "reserved", "released": [["east", "mug", 2], ["west", "mug", 2]]},
{"status": "reserved", "allocations": [["west", "tee", 1]]},
5,
{"status": "cancelled", "released": [["west", "tee", 1]]},
"error: already cancelled"]Notes
geometry.py,reservations.py,orders.pyandvalidation.pyare known-good.- You may use the AI assistant freely. Later parts must not break earlier ones; the hidden tests re-check earlier behavior as well.
- Parts 1 and 2 done cleanly is a solid result; Part 3 shows depth.
Example 1
- Input:
- [["add_warehouse","east",10,0],["add_warehouse","west",0,0],["stock","east","mug",5],["stock","west","mug",3],["place_order","o1",9,1,[["mug",2]]],["available","mug"],["ship","o1"],["available","mug"]]
- Output:
- [true,true,5,3,{"status":"reserved","allocations":[["east","mug",2]]},6,{"status":"shipped","allocations":[["east","mug",2]]},6]
- Explanation
- East is the nearest warehouse to (9, 1). Reserving 2 mugs leaves 6 available; shipping removes them from stock, so 6 remain.
Example 2
- Input:
- [... east has 2 mugs, west has 3 mugs and 1 tee ..., ["place_order","o1",9,1,[["mug",4],["tee",1]]]]
- Output:
- {"status":"reserved","allocations":[["east","mug",2],["west","mug",2],["west","tee",1]]}
- Explanation
- No single warehouse can cover the order, so each line is filled nearest-first: 2 mugs from east, 2 from west, and the tee from west.
Example 3
- Input:
- [... the order above ..., ["cancel_line","o1","mug"],["order_status","o1"],["available","mug"]]
- Output:
- [{"status":"reserved","released":[["east","mug",2],["west","mug",2]]},{"status":"reserved","allocations":[["west","tee",1]]},5]
- Explanation
- Cancelling the mug line releases both mug holds; the tee stays reserved and all 5 mugs are available again.
Guidelines
- Python 3 standard library only
- Quantities are positive integers; locations are integer grid points with Manhattan distance
- Ties between equally distant warehouses are broken by the smaller warehouse id (string order)
- Every operation returns exactly one value; errors are returned as "error: <code>" strings, never raised
- A rejected order must not leave any reservation behind