Skip to content

Phase 03: Supply Chain & Orders

Duration: 1 week Goal: Implement order flow from Outlets to Distribution Centers to Suppliers


class MaterialRequest(BaseDocType):
"""Outlet request for goods from Distribution Center."""
# Identity
request_no: str = Field(max_length=50, unique=True)
# Type
request_type: str = "Material Request" # Material Request, Transfer
# Parties
outlet: str # FK to Outlet (requesting)
distribution_center: str # FK to Distribution Center (fulfilling)
# Dates
request_date: date
required_by_date: date | None = None
# Status
status: str = "Draft" # Draft, Pending, Approved, Fulfilled, Rejected
docstatus: int = 0
# Items
items: list["MaterialRequestItem"] = []
class Meta:
table_name = "material_request"
indexes = ["outlet", "distribution_center", "status"]
class MaterialRequestItem(BaseDocType):
"""Line item in Material Request."""
parent: str # FK to MaterialRequest
item: str # FK to Item
item_name: str
qty: Decimal
uom: str = "Nos"
# Fulfillment tracking
ordered_qty: Decimal = Decimal("0")
received_qty: Decimal = Decimal("0")
class Meta:
table_name = "material_request_item"
  • Create MaterialRequest DocType
  • Create MaterialRequestItem child
  • Outlet submission workflow
  • Distribution Center approval workflow
  • Fulfillment tracking

2. Purchase Order (Distribution Center β†’ Supplier)

Section titled β€œ2. Purchase Order (Distribution Center β†’ Supplier)”
class PurchaseOrder(BaseDocType):
"""Order placed with Supplier by Distribution Center."""
# Identity
po_no: str = Field(max_length=50, unique=True)
# Parties
supplier: str # FK to Supplier
distribution_center: str # FK to Distribution Center (receiving)
# Dates
order_date: date
expected_delivery_date: date | None = None
# Reference
material_request: str | None = None # FK if created from MR
# Totals
total_qty: Decimal = Decimal("0")
total_amount: Decimal = Decimal("0")
# Status
status: str = "Draft" # Draft, Submitted, Partially Received, Completed, Cancelled
docstatus: int = 0
# Items
items: list["PurchaseOrderItem"] = []
class Meta:
table_name = "purchase_order"
indexes = ["supplier", "distribution_center", "status"]
class PurchaseOrderItem(BaseDocType):
"""Line item in Purchase Order."""
parent: str
item: str
item_name: str
qty: Decimal
uom: str = "Nos"
rate: Decimal
amount: Decimal
# Receipt tracking
received_qty: Decimal = Decimal("0")
# Warehouse
target_warehouse: str
class Meta:
table_name = "purchase_order_item"
  • Create PurchaseOrder DocType
  • Create from Material Request
  • Supplier notification (webhook/email)
  • PO approval workflow

class PurchaseReceipt(BaseDocType):
"""Receipt of goods from Supplier."""
# Identity
receipt_no: str = Field(max_length=50, unique=True)
# Reference
purchase_order: str # FK to PurchaseOrder
supplier: str
# Warehouse
warehouse: str # Where goods are received
# Dates
posting_date: date
posting_time: time | None = None
# Totals
total_qty: Decimal = Decimal("0")
total_amount: Decimal = Decimal("0")
# Status
docstatus: int = 0
# Items
items: list["PurchaseReceiptItem"] = []
class Meta:
table_name = "purchase_receipt"
class PurchaseReceiptController(BaseController[PurchaseReceipt]):
async def on_submit(self, doc: PurchaseReceipt) -> None:
"""Create Stock Entry on GRN submission."""
stock_entry = await self.create_stock_entry(
entry_type="Receipt",
target_warehouse=doc.warehouse,
items=doc.items,
reference_doc_type="PurchaseReceipt",
reference_doc_id=doc.id
)
await stock_entry.submit()
# Update PO received quantities
await self.update_purchase_order(doc)
  • Create PurchaseReceipt DocType
  • Auto-create Stock Entry on submit
  • Update PO received quantities
  • Quality check integration (optional)

class DeliveryNote(BaseDocType):
"""Goods delivered from Distribution Center to Outlet."""
# Identity
delivery_no: str = Field(max_length=50, unique=True)
# Parties
distribution_center: str
outlet: str
# Reference
material_request: str | None = None
# Warehouse
source_warehouse: str
# Dates
posting_date: date
# Transport
vehicle_no: str | None = None
driver_name: str | None = None
# Status
status: str = "Draft" # Draft, Dispatched, Delivered
docstatus: int = 0
items: list["DeliveryNoteItem"] = []
class Meta:
table_name = "delivery_note"
class DeliveryNoteController(BaseController[DeliveryNote]):
async def on_submit(self, doc: DeliveryNote) -> None:
"""Create Stock Issue on delivery."""
stock_entry = await self.create_stock_entry(
entry_type="Issue",
source_warehouse=doc.source_warehouse,
items=doc.items,
reference_doc_type="DeliveryNote",
reference_doc_id=doc.id
)
await stock_entry.submit()
  • Create DeliveryNote DocType
  • Auto-create Stock Issue on submit
  • Update Material Request fulfilled qty
  • Delivery confirmation from URC

class DemandGenerationService:
"""Aggregate Outlet demands for Distribution Center ordering."""
async def generate_distribution_center_demand(
self,
distribution_center: str,
from_date: date,
to_date: date
) -> list[dict]:
"""
Aggregate all pending Material Requests for a Distribution Center.
Returns consolidated item-wise demand.
"""
requests = await self.get_pending_requests(distribution_center, from_date, to_date)
demand = defaultdict(Decimal)
for req in requests:
for item in req.items:
demand[item.item] += item.qty - item.ordered_qty
return [
{"item": item, "total_demand": qty}
for item, qty in demand.items()
]
  • Implement demand aggregation service
  • Create consolidated PO from demand
  • Dashboard for demand visibility

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Outlet β”‚ ──MR──▢ β”‚ Distribution Center β”‚ ──PO──▢ β”‚ Supplier β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β–² β”‚ β”‚
β”‚ β”‚ β”‚
β”‚ β–Ό β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
│◀──Delivery── β”‚ DC Warehouse β”‚ ◀──GRNβ”€β”€β”€β”€β”€β”€β”˜
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ β”‚
β”‚ β–Ό
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
└───────────────│ Stock Ledger β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  • Outlet can submit Material Request
  • Distribution Center can approve/reject MR
  • Distribution Center can create PO from MR
  • Supplier receives PO notification
  • GRN creates stock receipt
  • Delivery Note creates stock issue
  • Full flow from MR β†’ PO β†’ GRN β†’ Delivery works
  • Stock balances accurate after full cycle

Goal: Verify that the domain models are correctly augmented by the MetadataDecoratorRegistry during the bootstrap cycle.

  • Verify Model Baking: Ensure StockEntry and DeliveryNote are baked with the required regional mixins at startup.
  • Refactor Delivery Compliance: Implement the transition from local dispatch to RegionalComplianceRegistry.register_transaction.
  • Verify Persistence: Confirm that regional fields are correctly routed to the overflow JSON column or dedicated SQL columns as configured in the Registry.