fix: Align dashboard API calls with actual procurement and production service endpoints

CRITICAL FIX: Dashboard was calling non-existent API endpoints

The Problem:
------------
The orchestrator dashboard service was calling API endpoints that don't exist:
1. Procurement: Expected dict {items: [...]} but API returns array [...]
2. Production: Called /production/production-batches/today - doesn't exist
3. Production: Called /production/production-batches - doesn't exist

Root Cause:
-----------
Created client methods without verifying actual backend API structure.
Made assumptions about response formats that didn't match reality.

The Fix:
--------
**1. Procurement Client (shared/clients/procurement_client.py)**
- Fixed get_pending_purchase_orders return type: Dict → List
- Procurement API returns: List[PurchaseOrderResponse] directly
- Changed: "Dict with {items: [...], total: n}" → "List of purchase order dicts"

**2. Production Client (shared/clients/production_client.py)**
- Fixed get_todays_batches endpoint:
  OLD: "/production/production-batches/today" (doesn't exist)
  NEW: "/production/batches?start_date=today&end_date=today"

- Fixed get_production_batches_by_status endpoint:
  OLD: "/production/production-batches?status=X"
  NEW: "/production/batches?status=X"

- Updated return type docs: {"items": [...]} → {"batches": [...], "total_count": n}
- Response structure: ProductionBatchListResponse (batches, total_count, page, page_size)

**3. Orchestrator Dashboard API (services/orchestrator/app/api/dashboard.py)**
- Fixed all po_data access patterns:
  OLD: po_data.get("items", [])
  NEW: direct list access or po_data if isinstance(po_data, list)

- Fixed production batch access:
  OLD: prod_data.get("items", [])
  NEW: prod_data.get("batches", [])

- Updated 6 locations:
  * Line 206: health-status pending POs count
  * Line 216: health-status production delays count
  * Line 274-281: orchestration-summary PO summaries
  * Line 328-329: action-queue pending POs
  * Line 472-487: insights deliveries calculation
  * Line 499-519: insights savings calculation

Verified Against:
-----------------
Frontend successfully calls these exact APIs:
- /tenants/{id}/procurement/purchase-orders (ProcurementPage.tsx)
- /tenants/{id}/production/batches (production hooks)

Both return arrays/objects as documented in their respective API files:
- services/procurement/app/api/purchase_orders.py: returns List[PurchaseOrderResponse]
- services/production/app/api/production_batches.py: returns ProductionBatchListResponse

Now dashboard calls match actual backend APIs! 
This commit is contained in:
Claude
2025-11-08 06:56:30 +00:00
parent fa0802c9f2
commit 413f652bbc
3 changed files with 22 additions and 23 deletions

View File

@@ -202,8 +202,8 @@ async def get_bakery_health_status(
# Get pending PO count
try:
po_data = await procurement_client.get_pending_purchase_orders(tenant_id, limit=100) or {}
pending_approvals = len(po_data.get("items", []))
po_data = await procurement_client.get_pending_purchase_orders(tenant_id, limit=100) or []
pending_approvals = len(po_data) if isinstance(po_data, list) else 0
except Exception as e:
logger.warning(f"Failed to fetch POs: {e}")
pending_approvals = 0
@@ -213,7 +213,7 @@ async def get_bakery_health_status(
prod_data = await production_client.get_production_batches_by_status(
tenant_id, status="ON_HOLD", limit=100
) or {}
production_delays = len(prod_data.get("items", []))
production_delays = len(prod_data.get("batches", []))
except Exception as e:
logger.warning(f"Failed to fetch production batches: {e}")
production_delays = 0
@@ -271,15 +271,14 @@ async def get_orchestration_summary(
if summary["purchaseOrdersCreated"] > 0:
try:
po_data = await procurement_client.get_pending_purchase_orders(tenant_id, limit=10)
if po_data:
pos = po_data.get("items", [])
if po_data and isinstance(po_data, list):
summary["purchaseOrdersSummary"] = [
PurchaseOrderSummary(
supplierName=po.get("supplier_name", "Unknown"),
itemCategories=[item.get("ingredient_name", "Item") for item in po.get("items", [])[:3]],
totalAmount=float(po.get("total_amount", 0))
)
for po in pos[:5] # Show top 5
for po in po_data[:5] # Show top 5
]
except Exception as e:
logger.warning(f"Failed to fetch PO details: {e}")
@@ -326,8 +325,8 @@ async def get_action_queue(
pending_pos = []
try:
po_data = await procurement_client.get_pending_purchase_orders(tenant_id, limit=20)
if po_data:
pending_pos = po_data.get("items", [])
if po_data and isinstance(po_data, list):
pending_pos = po_data
except Exception as e:
logger.warning(f"Failed to fetch pending POs: {e}")
@@ -470,15 +469,14 @@ async def get_insights(
try:
# Get recent POs with pending deliveries
pos_result = await procurement_client.get_pending_purchase_orders(tenant_id, limit=100)
if pos_result and isinstance(pos_result, dict):
pos = pos_result.get("items", [])
if pos_result and isinstance(pos_result, list):
# Count deliveries expected today
from datetime import datetime, timezone
today_start = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
today_end = today_start.replace(hour=23, minute=59, second=59)
deliveries_today = 0
for po in pos:
for po in pos_result:
expected_date = po.get("expected_delivery_date")
if expected_date:
if isinstance(expected_date, str):
@@ -498,12 +496,10 @@ async def get_insights(
seven_days_ago = datetime.now(timezone.utc) - timedelta(days=7)
pos_result = await procurement_client.get_pending_purchase_orders(tenant_id, limit=200)
if pos_result and isinstance(pos_result, dict):
pos = pos_result.get("items", [])
if pos_result and isinstance(pos_result, list):
weekly_savings = 0
# Calculate savings from price optimization
for po in pos:
for po in pos_result:
# Check if PO was created in last 7 days
created_at = po.get("created_at")
if created_at: