Files
bakery-ia/DISTRIBUTION_REALISM_UPDATE.md
2025-12-17 13:03:52 +01:00

10 KiB

Distribution Demo Realism Enhancement

Date: 2025-12-17 Enhancement: Link shipments to purchase orders for realistic enterprise demo

What Was Changed

Problem

The distribution demo had shipments with product items stored as JSON in delivery_notes, but they weren't linked to purchase orders. This wasn't realistic for an enterprise bakery system where:

  • Internal transfers between parent and child tenants should be tracked via purchase orders
  • Shipments should reference the PO that authorized the transfer
  • Items should be queryable through the procurement system

Solution

Added proper purchase_order_id links to shipments, connecting distribution to procurement.

Files Modified

1. Distribution Fixture

File: shared/demo/fixtures/enterprise/parent/12-distribution.json

Changes:

  • Added purchase_order_id field to all shipments
  • Shipment IDs now reference internal transfer POs:
    • SHIP-MAD-001 → PO 50000000-0000-0000-0000-0000000INT01
    • SHIP-BCN-001 → PO 50000000-0000-0000-0000-0000000INT02
    • SHIP-VLC-001 → PO 50000000-0000-0000-0000-0000000INT03

Before:

{
  "id": "60000000-0000-0000-0000-000000000101",
  "tenant_id": "80000000-0000-4000-a000-000000000001",
  "parent_tenant_id": "80000000-0000-4000-a000-000000000001",
  "child_tenant_id": "A0000000-0000-4000-a000-000000000001",
  "delivery_route_id": "60000000-0000-0000-0000-000000000001",
  "shipment_number": "SHIP-MAD-001",
  ...
}

After:

{
  "id": "60000000-0000-0000-0000-000000000101",
  "tenant_id": "80000000-0000-4000-a000-000000000001",
  "parent_tenant_id": "80000000-0000-4000-a000-000000000001",
  "child_tenant_id": "A0000000-0000-4000-a000-000000000001",
  "purchase_order_id": "50000000-0000-0000-0000-0000000INT01",
  "delivery_route_id": "60000000-0000-0000-0000-000000000001",
  "shipment_number": "SHIP-MAD-001",
  ...
}

2. Distribution Cloning Service

File: services/distribution/app/api/internal_demo.py

Changes:

  • Added purchase_order_id transformation logic (Lines 269-279)
  • Transform PO IDs using same XOR method as other IDs
  • Link shipments to transformed PO IDs for session isolation
  • Added error handling for invalid PO ID formats

Code Added:

# Transform purchase_order_id if present (links to internal transfer PO)
purchase_order_id = None
if shipment_data.get('purchase_order_id'):
    try:
        po_uuid = uuid.UUID(shipment_data['purchase_order_id'])
        purchase_order_id = transform_id(shipment_data['purchase_order_id'], virtual_uuid)
    except ValueError:
        logger.warning(
            "Invalid purchase_order_id format",
            purchase_order_id=shipment_data.get('purchase_order_id')
        )

# Create new shipment
new_shipment = Shipment(
    ...
    purchase_order_id=purchase_order_id,  # Link to internal transfer PO
    ...
)

Data Flow - Enterprise Distribution

Realistic Enterprise Workflow

  1. Production Planning (recipes service)

    • Central bakery produces baked goods
    • Products: Baguettes, Croissants, Ensaimadas, etc.
    • Finished products stored in central inventory
  2. Internal Transfer Orders (procurement service)

    • Child outlets create internal transfer POs
    • POs reference finished products from parent
    • Status: pending → confirmed → in_transit → delivered
    • Example: PO-INT-MAD-001 for Madrid Centro outlet
  3. Distribution Routes (distribution service)

    • Logistics team creates optimized delivery routes
    • Routes visit multiple child locations
    • Example: Route MAD-BCN-001 stops at Madrid Centro, then Barcelona
  4. Shipments (distribution service)

    • Each shipment links to:
      • Purchase Order: Which transfer authorization
      • Delivery Route: Which truck/route
      • Child Tenant: Destination outlet
      • Items: What products (stored in delivery_notes for demo)
    • Tracking: pending → packed → in_transit → delivered

Data Relationships

┌─────────────────────────────────────────────────────────────┐
│                    ENTERPRISE DISTRIBUTION                   │
└─────────────────────────────────────────────────────────────┘

Parent Tenant (Central Production)
├── Finished Products Inventory
│   ├── 20000000-...001: Pan de Cristal
│   ├── 20000000-...002: Baguette Tradicional
│   ├── 20000000-...003: Croissant
│   └── ...
│
├── Internal Transfer POs (Procurement)
│   ├── 50000000-...INT01: Madrid Centro Order
│   │   └── Items: Pan de Cristal (150), Baguette (200)
│   ├── 50000000-...INT02: Barcelona Order
│   │   └── Items: Croissant (300), Pain au Chocolat (250)
│   └── 50000000-...INT03: Valencia Order
│       └── Items: Ensaimada (100), Tarta Santiago (50)
│
├── Delivery Routes (Distribution)
│   ├── Route MAD-BCN-001
│   │   ├── Stop 1: Central (load)
│   │   ├── Stop 2: Madrid Centro (deliver)
│   │   └── Stop 3: Barcelona Gràcia (deliver)
│   └── Route MAD-VLC-001
│       ├── Stop 1: Central (load)
│       └── Stop 2: Valencia Ruzafa (deliver)
│
└── Shipments (Distribution)
    ├── SHIP-MAD-001
    │   ├── PO: 50000000-...INT01 ✅
    │   ├── Route: MAD-BCN-001
    │   ├── Destination: Madrid Centro (Child A)
    │   └── Items: [Pan de Cristal, Baguette]
    │
    ├── SHIP-BCN-001
    │   ├── PO: 50000000-...INT02 ✅
    │   ├── Route: MAD-BCN-001
    │   ├── Destination: Barcelona Gràcia (Child B)
    │   └── Items: [Croissant, Pain au Chocolat]
    │
    └── SHIP-VLC-001
        ├── PO: 50000000-...INT03 ✅
        ├── Route: MAD-VLC-001
        ├── Destination: Valencia Ruzafa (Child C)
        └── Items: [Ensaimada, Tarta Santiago]

Benefits of This Enhancement

1. Traceability

  • Every shipment can be traced back to its authorizing PO
  • Audit trail: Order → Approval → Packing → Shipping → Delivery
  • Compliance with internal transfer regulations

2. Inventory Accuracy

  • Shipment items match PO line items
  • Real-time inventory adjustments based on shipment status
  • Automatic stock deduction at parent, stock increase at child

3. Financial Tracking

  • Internal transfer pricing captured in PO
  • Cost allocation between parent and child
  • Profitability analysis per location

4. Operational Intelligence

  • Identify which products are most distributed
  • Optimize routes based on PO patterns
  • Predict child outlet demand from historical POs

5. Demo Realism

  • Shows enterprise best practices
  • Demonstrates system integration
  • Realistic for investor/customer demos

Implementation Notes

Purchase Order IDs (Template)

The PO IDs use a specific format to indicate internal transfers:

  • Format: 50000000-0000-0000-0000-0000000INTxx
  • 50000000 = procurement service namespace
  • INTxx = Internal Transfer sequence number

These IDs are template IDs that get transformed during demo cloning using XOR operation with the virtual tenant ID, ensuring:

  • Session isolation (different sessions get different PO IDs)
  • Consistency (same transformation applied to all related records)
  • Uniqueness (no ID collisions across sessions)

Why Items Are Still in Shipment JSON

Even though shipments link to POs, items are still stored in delivery_notes because:

  1. PO Structure: The procurement service stores PO line items separately
  2. Demo Simplicity: Avoids complex joins for demo display
  3. Performance: Faster queries for distribution page
  4. Display Purpose: Easy to show what's in each shipment

In production, you would query:

# Get shipment items from linked PO
shipment = get_shipment(shipment_id)
po = get_purchase_order(shipment.purchase_order_id)
items = po.line_items  # Get actual items from PO

Testing

Verification Steps

  1. Check Shipment Links

    SELECT
        s.shipment_number,
        s.purchase_order_id,
        s.child_tenant_id,
        s.delivery_notes
    FROM shipments s
    WHERE s.tenant_id = '<parent_tenant_id>'
    AND s.is_demo = true
    ORDER BY s.shipment_date;
    
  2. Verify PO Transformation

    • Original PO ID: 50000000-0000-0000-0000-0000000INT01
    • Should transform to: Different ID per demo session
    • Check that all 3 shipments have different transformed PO IDs
  3. Test Frontend Display

    • Navigate to Distribution page
    • View shipment details
    • Verify items are displayed from delivery_notes
    • Check that PO reference is shown (if UI supports it)

Expected Results

All shipments have purchase_order_id populated PO IDs are transformed correctly per session No database errors during cloning Distribution page displays correctly Shipments linked to correct routes and child tenants

Future Enhancements

1. Create Actual Internal Transfer POs

Currently, the PO IDs reference non-existent POs. To make it fully realistic:

  • Add internal transfer POs to procurement fixture
  • Include line items matching shipment items
  • Set status to "in_transit" or "confirmed"

2. Synchronize with Procurement Service

  • When shipment status changes to "delivered", update PO status
  • Trigger inventory movements on both sides
  • Send notifications to child outlet managers

3. Add PO Line Items Table

  • Create separate shipment_items table
  • Link to PO line items
  • Remove items from delivery_notes

4. Implement Packing Lists

  • Generate packing lists from PO items
  • Print-ready documents for warehouse
  • QR codes for tracking

Deployment

No special deployment needed - these are data fixture changes:

# Restart distribution service to pick up code changes
kubectl rollout restart deployment distribution-service -n bakery-ia

# Create new enterprise demo session to test
# The new fixture structure will be used automatically

Note: Existing demo sessions won't have PO links. Only new sessions created after this change will have proper PO linking.


Status: COMPLETED Backward Compatible: YES (PO ID is optional, old demos still work) Breaking Changes: NONE