Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Business modules

Reference

Purchase orders

Reference for purchase_orders in procurement, with order and item fields, statuses, actions, goods receipt into inventory, reversal, and delivery address.

This page describes the purchase_orders sub-module in src/cluster_erp/modules/procurement/purchase_orders/ and the shared helper src/cluster_erp/modules/procurement/delivery_address.py. A purchase order (PurchaseOrder) is an order placed with a vendor. Receiving an order line posts a goods receipt in the inventory module through the EventBus.

Scope and source

  • Defined in: models.py (PurchaseOrder, PurchaseOrderItem), services.py (PurchaseOrderService), routes.py, metadata.py, and item_metadata.py of the sub-module.
  • Count: 2 tables, 18 routes (@router. decorators in routes.py), 6 ActionMeta entries (5 on the order, 1 on the item).

Order fields

Name Meaning Source
po_no Business key from the yearly sequence purchase_order src/cluster_erp/modules/procurement/purchase_orders/models.py:31, src/cluster_erp/modules/procurement/purchase_orders/services.py:245
vendor_id, vendor_name_cache Vendor party and its cached name src/cluster_erp/modules/procurement/purchase_orders/models.py:37-42
purchase_request_id, project_id Source request and project src/cluster_erp/modules/procurement/purchase_orders/models.py:46-52
status Default draft src/cluster_erp/modules/procurement/purchase_orders/models.py:56-62
order_date, eta_date, actual_arrival_date Dates src/cluster_erp/modules/procurement/purchase_orders/models.py:63-71
currency, total_amount Currency and total Numeric(19, 4) src/cluster_erp/modules/procurement/purchase_orders/models.py:75-82
incoterm, payment_terms Commercial terms src/cluster_erp/modules/procurement/purchase_orders/models.py:85-89
delivery_party_id, delivery_address_id, delivery_address Structured delivery address and its text snapshot src/cluster_erp/modules/procurement/purchase_orders/models.py:97-107
sent_at, sent_by, acknowledged_at Lifecycle record src/cluster_erp/modules/procurement/purchase_orders/models.py:111-119
has_technical_attachment, cancel_reason, notes Send gate flag, reason, notes src/cluster_erp/modules/procurement/purchase_orders/models.py:123-133

Item fields

Name Meaning Source
product_id, product_model_id, product_version_id, description Catalogue item with breakdown, or a text item with a description only src/cluster_erp/modules/procurement/purchase_orders/models.py:159-174, src/cluster_erp/modules/procurement/purchase_orders/schemas.py:20-37
vendor_part_number Vendor's part number src/cluster_erp/modules/procurement/purchase_orders/models.py:175
qty_ordered, qty_received, qty_rejected Quantities Numeric(14, 4) src/cluster_erp/modules/procurement/purchase_orders/models.py:179-190
unit_price, line_total, unit_id Price Numeric(18, 4), total Numeric(19, 4), unit src/cluster_erp/modules/procurement/purchase_orders/models.py:195-203
expected_date, actual_received_date, batch_id, heat_lot_id Receipt data src/cluster_erp/modules/procurement/purchase_orders/models.py:207-219
status Default open; then partial or received src/cluster_erp/modules/procurement/purchase_orders/models.py:223-228, src/cluster_erp/modules/procurement/purchase_orders/services.py:759-762

Statuses and actions

The service allows these transitions (src/cluster_erp/modules/procurement/purchase_orders/services.py:67-75):

stateDiagram-v2
 [*] --> draft
 draft --> sent: send
 sent --> acknowledged: acknowledge
 acknowledged --> in_transit: in_transit
 acknowledged --> received: last line received
 in_transit --> received: last line received
 received --> in_transit: receipt reversed
 received --> closed: close
 draft --> cancelled: cancel
 sent --> cancelled: cancel
 acknowledged --> cancelled: cancel
 in_transit --> cancelled: cancel

In prose: an order starts as draft. send needs an order date and the flag has_technical_attachment (src/cluster_erp/modules/procurement/purchase_orders/services.py:620-641). Receiving is allowed in acknowledged and in_transit; when every live line is received, the order becomes received (src/cluster_erp/modules/procurement/purchase_orders/services.py:705-774). A receipt reversal can move a received order back to in_transit (src/cluster_erp/modules/procurement/purchase_orders/services.py:854-891). close follows received; cancel is open until receipt (src/cluster_erp/modules/procurement/purchase_orders/services.py:892-929).

Action Label Condition Endpoint Permission Source
send Send status is draft POST /purchase-orders/{id}/send purchase_order.send src/cluster_erp/modules/procurement/purchase_orders/metadata.py:92-108, src/cluster_erp/modules/procurement/purchase_orders/routes.py:201
acknowledge Acknowledge status is sent POST /purchase-orders/{id}/acknowledge purchase_order.update src/cluster_erp/modules/procurement/purchase_orders/metadata.py:109-125, src/cluster_erp/modules/procurement/purchase_orders/routes.py:213
in_transit Mark In Transit status is acknowledged POST /purchase-orders/{id}/in-transit purchase_order.update src/cluster_erp/modules/procurement/purchase_orders/metadata.py:126-143, src/cluster_erp/modules/procurement/purchase_orders/routes.py:225
close Close status is received POST /purchase-orders/{id}/close purchase_order.update src/cluster_erp/modules/procurement/purchase_orders/metadata.py:144-158, src/cluster_erp/modules/procurement/purchase_orders/routes.py:269
cancel Cancel status is draft, sent, acknowledged, or in_transit POST /purchase-orders/{id}/cancel purchase_order.update src/cluster_erp/modules/procurement/purchase_orders/metadata.py:159-192, src/cluster_erp/modules/procurement/purchase_orders/routes.py:281
receive (item) Receive Goods item status is open or partial POST /purchase-orders/items/{id}/receive purchase_order.receive src/cluster_erp/modules/procurement/purchase_orders/item_metadata.py:57-125, src/cluster_erp/modules/procurement/purchase_orders/routes.py:254

Order routes

Method and path Permission Source
GET /purchase-orders/, GET /purchase-orders/{po_id} purchase_order.read src/cluster_erp/modules/procurement/purchase_orders/routes.py:58, src/cluster_erp/modules/procurement/purchase_orders/routes.py:147
POST /purchase-orders/, POST /purchase-orders/from-pr/{pr_id} purchase_order.create src/cluster_erp/modules/procurement/purchase_orders/routes.py:98-110
GET /purchase-orders/receivable-lines purchase_order.read src/cluster_erp/modules/procurement/purchase_orders/routes.py:129
PUT /purchase-orders/{po_id} purchase_order.update src/cluster_erp/modules/procurement/purchase_orders/routes.py:161
DELETE /purchase-orders/{po_id}, POST /purchase-orders/bulk-delete purchase_order.delete, purchase_order.bulk_delete src/cluster_erp/modules/procurement/purchase_orders/routes.py:174-186
POST /purchase-orders/{po_id}/items/{item_id}/receive purchase_order.receive src/cluster_erp/modules/procurement/purchase_orders/routes.py:237
POST /purchase-orders/{po_id}/items, PUT and DELETE /purchase-orders/{po_id}/items/{item_id} purchase_order.update src/cluster_erp/modules/procurement/purchase_orders/routes.py:297-331

Goods receipt

The diagram shows a receipt of one line.

sequenceDiagram
 participant U as receive route
 participant S as PurchaseOrderService
 participant B as EventBus
 participant I as inventory
 U->>S: receive_item
 S->>S: check status, breakdown, quantity
 S->>B: emit PoReceivedFull or PoReceivedPartial
 S->>B: query PostGoodsReceipt
 B->>I: _handle_post_goods_receipt
 I-->>S: movement id

In prose: the service refuses a text item and an item without model and version, because stock is posted per breakdown (src/cluster_erp/modules/procurement/purchase_orders/services.py:718-731). The received quantity may not exceed the ordered quantity (src/cluster_erp/modules/procurement/purchase_orders/services.py:738-749). The service updates the line and order status, emits PoReceivedFull or PoReceivedPartial, and then queries PostGoodsReceipt with the order as reference, the line id, and the order's project as stock owner (src/cluster_erp/modules/procurement/purchase_orders/services.py:750-822). An error from inventory fails the receipt with PO_GOODS_RECEIPT_FAILED; a missing inventory handler is logged and the receipt stays a purchasing record only (src/cluster_erp/modules/procurement/purchase_orders/services.py:823-837). A reversed goods movement gives the open quantity back through the query ReconcilePoReceiptReversal (src/cluster_erp/modules/procurement/module.py:192-202, src/cluster_erp/modules/procurement/module.py:885-916).

Constraints and validation

  • On create, the vendor is read with GetPartyById; a blacklisted vendor is refused (src/cluster_erp/modules/procurement/purchase_orders/services.py:215-243).
  • Each catalogue item's breakdown is checked with ensure_product_breakdown (src/cluster_erp/modules/procurement/purchase_orders/services.py:279, src/cluster_erp/modules/procurement/purchase_orders/services.py:507).
  • Unit price is rounded to four places and the line total to the column scale, both with ROUND_HALF_EVEN; the header total is the sum (src/cluster_erp/modules/procurement/purchase_orders/services.py:112-123, src/cluster_erp/modules/procurement/purchase_orders/services.py:307-312).
  • The header and items change only in draft; only a draft can be deleted (src/cluster_erp/modules/procurement/purchase_orders/services.py:415-468).
  • The delivery address must be a shipping address of the delivery party; its text is stored as a snapshot (src/cluster_erp/modules/procurement/delivery_address.py:24-28, src/cluster_erp/modules/procurement/delivery_address.py:78, src/cluster_erp/modules/procurement/purchase_orders/services.py:251).
  • Events: PoCreated, PoSent, PoAcknowledged, PoReceivedFull, PoReceivedPartial, PoCancelled (src/cluster_erp/modules/procurement/purchase_orders/services.py:338, src/cluster_erp/modules/procurement/purchase_orders/services.py:648, src/cluster_erp/modules/procurement/purchase_orders/services.py:672, src/cluster_erp/modules/procurement/purchase_orders/services.py:779-797, src/cluster_erp/modules/procurement/purchase_orders/services.py:921).

Terms introduced

Term Code identifier Definition Turkish term Source
purchase order PurchaseOrder An order placed with a vendor. Satın alma siparişi src/cluster_erp/modules/procurement/purchase_orders/models.py:24
text item PurchaseOrderItem without product_id An order line with a description and no product; it is never goods-received. Metin kalemi src/cluster_erp/modules/procurement/purchase_orders/services.py:718-724

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13