Procurement - Supplier & Purchase Order Management
Domain: Procurement
Location: api/nextango/apps/procurement/
Last Updated: 2026-07-09
Overview
The Procurement app manages the full purchase order lifecycle for inventory replenishment. It provides a supplier/vendor directory and structured purchase order management with status tracking from draft through to receipt.
Procurement data is bidirectionally synced with Sanity CMS via webhook handlers. The Django models mirror the Sanity supplier.js and purchaseOrder.js schemas, and serializers handle camelCase ↔ snake_case transformation automatically.
Documentation Quick Links
Core Documentation
| File | Description |
|---|---|
| models.md | Supplier, PurchaseOrder, PurchaseOrderLineItem models |
| serializers.md | Procurement serializers and camelCase transformation |
| views.md | All procurement API endpoints |
| examples.md | Request/response examples |
Key Concepts
Supplier Directory
- Supplier records hold contact info, payment terms, account numbers, and lead times
is_active=Falsesuppliers remain in history but cannot receive new purchase orderssupplier_codeis a unique internal reference (e.g.,SUP-001)
Purchase Order Lifecycle
status is a plain field with six choices; the model does not enforce transition order. The typical flow is:
Pending → Submitted → Partially Received → Awaiting Compliance Verification → Received
↘ Cancelled
Status values match the Sanity schema exactly (capitalized strings).
Dual Storage of Line Items
POs store line items in two ways:
PurchaseOrder.items(JSONField): raw SanitypurchaseOrderItemarray for full Sanity structure preservationPurchaseOrderLineItemmodel: relational counterpart for Django-side querying and inventory link tracking
Sanity Integration
Supplier,PurchaseOrder, andPurchaseOrderLineItemextendSanityIntegratedModel(carriessanity_idfield)- Serializers resolve Sanity
_refreferences to Django FK IDs at write time PurchaseOrderSerializer.to_internal_value()resolvessupplier._ref→Supplier.pk
API Endpoints
Suppliers
| Endpoint | Method | Description |
|---|---|---|
/suppliers/ | GET | List all suppliers |
/suppliers/ | POST | Create a supplier |
/suppliers/{id}/ | GET | Retrieve supplier |
/suppliers/{id}/ | PUT / PATCH | Update supplier |
/suppliers/{id}/ | DELETE | Delete supplier |
/suppliers/analytics/performance/ | GET | Supplier performance analytics |
Purchase Orders
| Endpoint | Method | Description |
|---|---|---|
/purchase-orders/ | GET | List purchase orders |
/purchase-orders/ | POST | Create a purchase order |
/purchase-orders/{id}/ | GET | Retrieve PO with line items |
/purchase-orders/{id}/ | PUT / PATCH | Update PO |
/purchase-orders/{id}/ | DELETE | Delete PO |
/purchase-orders/analytics/summary/ | GET | Procurement summary analytics |
Full endpoint documentation: views.md
Automated Reorder Monitoring
The check_reorder_levels Celery task runs daily at 6 AM (configured in Celery beat schedule) and scans all active InventoryLevel records where reorder_point > 0, stock_on_hand is at or below that reorder point, and the linked product variant has custom_inventory_management enabled. For each matching SKU, it emits a WARNING log containing SKU, store name, current quantity, reorder point, and suggested order quantity.
Deployment requirement: Celery beat must be running for the task to fire automatically. Manual execution is also possible via check_reorder_levels.delay().
Note: The task surfaces alerts for operations staff to act on: it does not automatically create purchase orders. PO creation remains a manual step after reviewing the reorder alerts.
See tasks.md for full task reference.
Architecture Highlights
SanityIntegratedModel Base
Supplier, PurchaseOrder, and PurchaseOrderLineItem all inherit SanityIntegratedModel, which provides:
sanity_idCharField: links to the Sanity document_idcreated_at/updated_attimestamps
Analytics Service
ProcurementAnalyticsService (analytics_service.py) provides cached aggregation methods:
get_procurement_summary(): PO counts, total value, status breakdown, open PO totals. Exposed at/purchase-orders/analytics/summary/get_supplier_performance(): per-supplier order counts, total spend, received/active counts. Exposed at/suppliers/analytics/performance/get_supplier_lead_time_performance(): per-supplier on-time receipt rate and lead time variance againstSupplier.lead_time_days. Not currently bound to a view or URL
All three methods support optional start_date / end_date filtering and use the replica database when USE_REPLICA_DATABASE=True.
Related Domains
- Products:
InventoryLevelFK onPurchaseOrderLineItem, updated when a PO is received - Integrations: Sanity webhook adapter syncs supplier/PO records
- Transactions: Transaction payments may drive procurement replenishment decisions