Order Line Management
This page covers the operations for managing lines within a Purchase Order: adding a single line, adding lines from Kanban Cards (to an existing order), adding lines from items, and removing lines.
Use case references:
PRO::PO::0003::0006— Add Single LinePRO::PO::0003::0001andPRO::PO::0003::0004— Add Lines from CardsPRO::PO::0001::0006andPRO::PO::0001::0007— Add Lines from ItemsPRO::PO::0003::0005— Remove Line
Add a Single Line to an Order
Section titled “Add a Single Line to an Order”Use case: PRO::PO::0003::0006 — Priority: MVP2-B
A single order line can be added to an existing order in NEW state in two ways:
- From a Kanban Card: The line takes the values of the current version of the item referenced by the card (title, quantity, unit cost from the order’s supplier name).
- As a blank line: The UI presents an item selector filtered to items that share the order’s vendor.
- Alphabetically ordered by item name for the tenant.
- The user can start typing to filter by name prefix (case-insensitive match at start of name).
- The user selects from the list or presses Enter after typing a complete name.
- If the item name matches an existing item, the line is filled with the item’s current values.
- If the item name does not match, a modal allows the user to confirm creation of a new item. Canceling the modal reverts to the field being edited.
Bulk line addition from a CSV file is also supported: if the item name in the CSV matches an existing item (case-insensitively), that item is used; otherwise the line is marked INVALID and must be corrected before the order can be submitted.
Implementation Entry Point
Section titled “Implementation Entry Point”OrderRecordService.addLine at operations/src/main/kotlin/cards/arda/operations/procurement/orders/service/OrderRecordService.kt
Preconditions
Section titled “Preconditions”- The target order (identified by
eId+asOforrId) must exist; otherwise the service replies withArgumentValidation(OrderRecordService.kt:152-167,173-186). - When the incoming line references an Item (
linePayload.item.eId), the item lookup must succeed at the requested coordinates; if it no longer exists the system keeps the original payload without failing (OrderRecordService.kt:156-164). - Supplier compatibility is not explicitly validated by this operation; incompatible data is left to downstream validation.
Scenario Overview
Section titled “Scenario Overview”- The service loads the parent order snapshot for the provided coordinates.
- If the incoming line references an Item, the latest version is fetched and
OrderHelper.adjustLineenriches missing fields (title,quantity,unitCost,received) using the order’ssupplierName(OrderHelper.kt:354-374). - The adjusted (or original) payload is inserted through
OrderLineUniverse.add, respecting the optionalinsertionindex (OrderHelper.kt:381-394). - The caller-provided
postProcesscallback is invoked; its failure halts the transaction (OrderHelper.kt:392-393).
Postconditions
Section titled “Postconditions”- The new
OrderLineversion is persisted and returned to the caller wrapped in anEntityRecord. - The parent
OrderHeaderis not automatically rebalanced (goods value, supplier); additional orchestration viaOrderHelper.adjustHeaderis required when needed. - If
postProcessraises an error, the transaction aborts and the error is propagated.
Note on Line Aggregation
Section titled “Note on Line Aggregation”Bulk CSV handling and item name matching described in the UI flow are not implemented at the service layer. Lines are persisted exactly as supplied (after optional enrichment via adjustLine).
API Route: POST /v1/order/order/{eId}/lines
Add Lines from Kanban Cards (to Existing Order)
Section titled “Add Lines from Kanban Cards (to Existing Order)”Use cases: PRO::PO::0003::0001 and PRO::PO::0003::0004 — Priority: MVP3
Given an existing Draft Purchase Order, the user can add new lines by selecting additional Kanban Cards from the Order Queue.
- User selects a set of Cards from the Order Queue.
- User clicks Order and selects “Add to existing order.”
- The system presents a selector of orders in
NEWstatus, ordered most-recently-updated first. - User selects an order and clicks Add.
- The system validates the selected cards for compatibility with the selected order.
- The system adds the new lines to the order and displays the updated order.
- Added cards are transitioned to
REQUESTEDstatus by the backend.
- Added cards are transitioned to
Card-to-Line Logic
Section titled “Card-to-Line Logic”The logic is the same as Create from Kanban Cards with these exceptions:
- If any selected card does not belong to an item with a compatible supplier for the selected order, the system shows an error.
- Cards compatible with pre-existing lines (same item and unit of measure) are aggregated into those existing lines, increasing their quantity.
- Cards not compatible with any existing line are added as new lines.
Sequence Diagram
Section titled “Sequence Diagram”API Routes
Section titled “API Routes”| Step | Route | Notes |
|---|---|---|
List Order Headers with Status NEW | POST /v1/order/order/query | Filter for status: NEW, sorted by updated descending |
| Add Cards to Order | PUT /v1/order/order/from-kanban-cards/{eId} | Body is a list of Card EIds; returns the Updated Full Order Record |
List Cards in Status REQUESTING | POST /v1/kanban/kanban-card/query | Filter for status: REQUESTING |
Add Lines from Items
Section titled “Add Lines from Items”Use cases: PRO::PO::0001::0006 and PRO::PO::0001::0007 — Priority: MVP3
The user selects a set of Items from the Item List and the system creates a Purchase Order with one line per selected item.
Implementation Entry Point
Section titled “Implementation Entry Point”OrderFromItemsService.addOrderFromItems at operations/src/main/kotlin/cards/arda/operations/procurement/orders/service/OrderFromItemsService.kt
Preconditions
Section titled “Preconditions”- The
itemscollection must contain at least one entry and cannot exceedLineUniverse.MAX_PAGE_SIZE(currently 1000); otherwise anArgumentValidationerror is returned (OrderFromItemsService.kt:29-33,60-73). - Every requested Item must exist at
TimeCoordinates.now(effectiveTime); missing items result in aNotFounderror before any persistence (OrderFromItemsService.kt:74-84). - Items must share a compatible supplier according to
OrderHelper.selectCompatibleSupply; if no shared supplier exists among items that have a preferred supply, the call fails withIllegalArgumentException(OrderHelper.kt:134-147). - Creation fails if the selected items do not have a shared vendor (blank primary and secondary suppliers is considered a match with any other item).
Scenario Overview
Section titled “Scenario Overview”- Load all requested Items via
ItemService.listEntitiesas of the effective timestamp (OrderFromItemsService.kt:74-84). - Build draft order lines by calling
OrderHelper.composeLinefor each item (OrderFromItemsService.kt:85-87). - Compose a provisional header with defaults (
OrderStatus.NEW,allowPartial=true,deliverBy = now + 7 days) viaOrderHelper.composeHeader(OrderHelper.kt:261-283). - Enrich every line with supplier-driven details (title, quantity, unit cost) via
OrderHelper.adjustLine, and recompute the header (supplier, goods value) throughOrderHelper.adjustHeader(OrderFromItemsService.kt:88-91). - Persist the header and lines in a single transaction with
OrderHelper.addOrderWithLines, invoking any suppliedpostProcesscallback after persistence (OrderFromItemsService.kt:92-93).
Postconditions
Section titled “Postconditions”- A new
OrderRecord(header plus paginated lines) is stored and returned on success. - Goods value is derived from enriched line costs when supplier data is available; otherwise it remains
null. - No Kanban Card associations are created by this flow.
Line and Header Derivation Notes
Section titled “Line and Header Derivation Notes”- Each line references the selected item through
ItemReference.Value.fromItem. - Quantities default to the supplier’s reorder quantity; they remain
nullwhen not available. The implementation does not useItem.minQuantity, contrary to earlier plans. supplierNameis derived from the compatible supplier returned byselectCompatibleSupply; if none is found, the field remainsnull.orderMethodreflects the shared effective supply method when consistent, orOrderMethod.UNKNOWNotherwise (OrderHelper.kt:165-176).goodsValueis recomputed from enriched lines duringadjustHeader; if any line references an item missing supply data, goods value may remainnull.
Remove a Line from an Order
Section titled “Remove a Line from an Order”Use case: PRO::PO::0003::0005 — Priority: MVP3
Given an existing Draft Purchase Order, the user can remove lines.
- User selects an Order from the Draft Orders list and selects the Edit Lines action.
- The system opens and displays the list of lines in the Order.
- The user selects a line and clicks Remove.
- The system removes the line from the Order and displays the updated list.
If multiple lines need to be removed, the frontend loops through them and calls the API for each one. A batch remove endpoint may be provided in future.
Postcondition: The selected line is removed from the PO. Cards previously associated only with the removed line are returned to REQUESTING via shelve events.
Sequence Diagram
Section titled “Sequence Diagram”API Routes
Section titled “API Routes”| Step | Route | Notes |
|---|---|---|
| List Draft Orders | POST /v1/order/order/query | Filter for status: NEW |
| List Lines for Order | GET /v1/order/order/full/{eId} | Returns full Order Record (header + lines) |
| Remove Line from Order | DELETE /v1/order/order/{eId}/lines/{lineEId} | Removes a line from the order |
See the OpenAPI Specification for full route details.
Copyright: © Arda Systems 2025-2026, All rights reserved