301 lines
10 KiB
Markdown
301 lines
10 KiB
Markdown
# 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:**
|
|
```json
|
|
{
|
|
"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:**
|
|
```json
|
|
{
|
|
"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:**
|
|
```python
|
|
# 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:
|
|
```python
|
|
# 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**
|
|
```sql
|
|
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:
|
|
|
|
```bash
|
|
# 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
|