Skip to content

Order Creation

This page documents the ways a purchase order can be created in the system. The primary path for MVP2 is creation from Kanban Cards in the Order Queue. Additional creation paths (empty order, copy, header+lines) are specified here and represent MVP3 scope or future work.

Use case reference: PRO::PO::0001


Use case: PRO::PO::0001::0001 and PRO::PO::0001::0002

In MVP2, the primary way to create a Purchase Order is from a set of Cards in the Order Queue.

  1. User selects a set of Cards from the Order Queue.
  2. User clicks the Order button and selects “New Purchase Order” (see Order Line Management for the alternative “Add to existing order” path).
  3. The system validates the selected cards for compatibility and creates a new Purchase Order.
    • The system transitions the cards to the REQUESTED state in the backend.
  4. The system opens the Purchase Order for editing, allowing the user to edit the Header properties of the Purchase Order.
  1. All cards must belong to items that have a compatible supplier, or no supplier defined.
  2. If the cards have a compatible, non-null supplier, that supplier becomes the supplier of the Purchase Order.
  3. Cards are grouped by item and unit of measure into individual Order Lines.
  4. Each order line has a quantity equal to the sum of reorder quantities for the card group.
  5. Unit costs, line costs, and goods value are calculated from the total quantity of the card group and the item’s price from the supplier.

Postcondition: Cards transition from REQUESTING to REQUESTED. The created PO is displayed in edit mode for header review.

PlantUML diagram

StepRouteNotes
Create OrderPOST /v1/order/order/from-kanban-cardsBody is a list of Card EIds; returns the created Order Record
Update HeaderPUT /v1/order/order/{eId}Body is the updated Order Header as OrderHeaderInput; returns the Updated OrderHeader Record

See the OpenAPI Specification for full route details.


Use case: PRO::PO::0001::0003 — Priority: MVP3

The system presents a blank form so the user can fill in header information and add lines manually.

  1. Edit the order header fields.
  2. Save the order, keeping it in NEW state.
  3. Approve the order, triggering validation and placing it in the APPROVED state. The order becomes read-only.
  4. Add order lines to the order.
  • OrderHelper.addEmptyOrder(header: OrderHeader, ...) at operations/src/main/kotlin/cards/arda/operations/procurement/orders/service/OrderHelper.kt:421-436
  • OrderHelper.addEmptyOrder(headerEid: UUID, orderNumber: String, ...) at OrderHelper.kt:442-451 for flows that only supply an identifier and order number.
  • Caller must provide an OrderMetadata payload and author identifier.
  • When supplying a fully formed OrderHeader, the caller ensures field compliance with domain rules.
  • When only headerEid and orderNumber are supplied, the helper composes a header with defaults.
  1. Compose (or accept) an OrderHeader, defaulting to OrderStatus.NEW, allowPartial = true, and deliverBy = now + 7 days (OrderHelper.kt:261-283).
  2. Persist the header through DataAuthorityService.add, obtaining an EntityRecord snapshot (OrderHelper.kt:430-435).
  3. Evaluate OrderHelper.linesQuery to build an empty PageResult, producing an OrderRecord with zero lines (OrderHelper.kt:431-433).
  4. Invoke the supplied postProcess callback with the freshly created OrderRecord (OrderHelper.kt:434-435).
  • A new order header version exists in the database with no lines attached.
  • The returned OrderRecord includes pagination metadata pointing to the header’s line universe.
  • No automatic notifications or workflow transitions are triggered.
FieldDefault Value
statusNEW
allowPartialtrue
deliverByorderDate + 7 days
goodsValuenull (no lines to aggregate)
supplierNamewhatever the caller supplied (typically null)

Use case: PRO::PO::0001::0004 — Priority: MVP3 — Not yet implemented

The user selects an order from the list and requests a copy. The system would make a complete copy with the following modifications:

  1. History starts fresh at the time of the copy.
  2. Attachments to Kanban Cards are not copied.
  3. Item associations on each order line are reset to the current item version. If a referenced item has been deleted, the line is marked invalid and the PO cannot be submitted until corrected or removed.

There is no service in operations/src/main/kotlin/cards/arda/operations/procurement/orders/service that clones an existing order. Callers needing copy semantics must orchestrate the process manually by reading the order through OrderRecordService.getParentChild and re-submitting adjusted payloads. A dedicated copy utility must be introduced before this capability is exposed in the product UI.


Use case: PRO::PO::0001::0005 — Priority: MVP3

The user directly provides OrderHeader and OrderLines data. The system validates and creates the full order structure. This path supports programmatic or import-driven order creation where the full order structure is known upfront.

  • The provided order lines do not reference Kanban Cards (future enhancement).
  • If item references are provided in order lines, the items must exist and be current in the system as of TimeCoordinates.now().
  1. User provides the OrderHeader information and a list of OrderLines.
  2. System validates the information; reports errors if invalid.
  3. System retrieves reference information (e.g., Items) from other services as needed.
  4. System adds default values to OrderHeader and OrderLines from system settings and item information where not provided.
  5. System persists the OrderHeader and associated OrderLines and returns them to the user.

All fields take the value provided by the user if not null. Calculations are only performed when the user-provided value is null.

FieldDerivation
eIdUUID.randomUUID()
titleFirst non-null of: effectiveSupply.sku, item.name, or OrderLineInput.title
descriptionOrderLineInput.item.description
statusOrderLineStatus.BLANK
itemOrderLineInput.item
supplierSkueffectiveSupply.sku
quantityOrderLineInput.quantity if not null, else effectiveSupply.orderQuantity
unitCostOrderLineInput.unitCost if not null, else effectiveSupply.unitCost
costquantity * unitCost
received0.0 in same unit as quantity; null if quantity is null
notesnull
privateNotesnull
FieldValue
eIdUUID.randomUUID()
statusOrderStatus.NEW
orderNumberGenerated by the OrderService
orderDateCurrent datetime in user timezone (or system timezone)
allowPartialtrue
deliverByCurrent datetime + 7 days
deliveryAddressnull
procurementnull
supplierNamenull
supplierAddressnull
orderMethodOrderMethod.UNKNOWN
salesnull
goodsValueSum of cost fields of all associated lines (null if multiple currencies)
taxesAndFeesEmpty map
totalCostgoodsValue + sum of taxesAndFees values (null if multiple currencies)
termsAndConditionsnull
notesnull
privateNotesnull
  • Additions to Item and ItemSupply: GitHub #406
  • Additions to OrderService and associated persistence layer.