Procurement Domain - Serializers Documentation
Source: api/nextango/apps/procurement/serializers.py
Last Updated: 2026-07-09
Overview
The procurement serializers handle two distinct input paths:
- API requests: snake_case fields, direct FK IDs
- Sanity webhooks: camelCase fields,
_refSanity references instead of DB IDs
Both paths are handled by overriding to_internal_value() in each serializer. The serializers detect camelCase keys and transform them to snake_case, and resolve Sanity _ref objects to Django PKs.
SupplierSerializer
Source: serializers.py:16-71
Model: Supplier
Fields
All model fields are included. Read-only fields: id, sanity_id, created_at, updated_at.
| Field | Writable | Notes |
|---|---|---|
id | No | Auto UUID |
sanity_id | No | Set by webhook handler |
name | Yes | |
supplier_code | Yes | |
tax_identification | Yes | |
is_active | Yes | |
notes | Yes | |
address | Yes | JSON object |
contacts | Yes | JSON array |
payment_terms | Yes | |
account_number | Yes | |
minimum_order_amount | Yes | |
lead_time_days | Yes | |
return_policy | Yes | |
supplied_product_types | Yes | JSON array |
supplied_products | Yes | JSON array |
documents | Yes | JSON array |
created_at | No | |
updated_at | No |
camelCase → snake_case Mappings
to_internal_value() handles these transformations automatically for Sanity webhook payloads:
| Sanity (camelCase) | Django (snake_case) |
|---|---|
supplierCode | supplier_code |
taxIdentification | tax_identification |
isActive | is_active |
paymentTerms | payment_terms |
accountNumber | account_number |
minimumOrderAmount | minimum_order_amount |
leadTimeDays | lead_time_days |
returnPolicy | return_policy |
suppliedProductTypes | supplied_product_types |
suppliedProducts | supplied_products |
Fields name, address, contacts, documents, notes pass through unchanged (same name in both systems).
PurchaseOrderLineItemSerializer
Source: serializers.py:74-89
Model: PurchaseOrderLineItem
Read-only serializer used for nested display within PurchaseOrderSerializer. Not used directly for write operations.
Fields
| Field | Notes |
|---|---|
id | Read-only |
sanity_key | Sanity _key identifier |
inventory_level | FK ID |
inventory_level_sanity_ref | Raw Sanity ref |
variant_sku | From inventory_level.variant_sku |
variant_name | From inventory_level.variant_name |
product_name | From inventory_level.product.name |
store_name | From inventory_level.store.name |
quantity | |
unit_cost |
variant_sku, variant_name, product_name, store_name are read_only=True fields sourced through the inventory_level relation (inventory_level.variant_sku, inventory_level.product.name, and so on), each defaulting to an empty string when unresolved.
PurchaseOrderSerializer
Source: serializers.py:92-164
Model: PurchaseOrder
Fields
| Field | Writable | Notes |
|---|---|---|
id | No | Auto UUID |
sanity_id | No | Set by webhook handler |
po_number | Yes | Unique |
supplier | Yes | FK ID (write) |
supplier_name | No | From supplier.name |
supplier_code | No | From supplier.supplier_code |
expected_date | Yes | |
status | Yes | |
items | Yes | Raw Sanity JSON array |
total_cost | Yes | |
received_date | Yes | |
compliance_verification | Yes | JSON object |
notes | Yes | |
line_items | No | Nested PurchaseOrderLineItemSerializer (read-only) |
created_at | No | |
updated_at | No |
camelCase → snake_case Mappings
| Sanity (camelCase) | Django (snake_case) |
|---|---|
poNumber | po_number |
expectedDate | expected_date |
totalCost | total_cost |
receivedDate | received_date |
complianceVerification | compliance_verification |
Supplier Reference Resolution
to_internal_value() resolves the Sanity supplier reference to a Django FK:
# Webhook payload:
{ "supplier": { "_ref": "some-sanity-supplier-id" } }
# or
{ "supplier": "some-sanity-supplier-id" }
# Resolved to:
{ "supplier": <Supplier.pk> }
If the supplier is not found by sanity_id, a warning is logged and the field is skipped: the dependency fetcher may resolve it asynchronously.