feat: Complete backend i18n implementation with error codes and demo data

Demo Seed Scripts:
- Updated seed_demo_purchase_orders.py to use structured reasoning_data
  * Imports create_po_reasoning_low_stock and create_po_reasoning_supplier_contract
  * Generates reasoning_data with product names, stock levels, and consequences
  * Removed deprecated reasoning/consequence TEXT fields
- Updated seed_demo_batches.py to use structured reasoning_data
  * Imports create_batch_reasoning_forecast_demand and create_batch_reasoning_regular_schedule
  * Generates intelligent reasoning based on batch priority and AI assistance
  * Adds reasoning_data to all production batches

Backend Services - Error Code Implementation:
- Updated safety_stock_calculator.py with error codes
  * Replaced "Lead time or demand std dev is zero or negative" with ERROR:LEAD_TIME_INVALID
  * Replaced "Insufficient historical demand data" with ERROR:INSUFFICIENT_DATA
- Updated replenishment_planning_service.py with error codes
  * Replaced "Insufficient data for safety stock calculation" with ERROR:INSUFFICIENT_DATA
  * Frontend can now translate error codes using i18n

Demo data will now display with translatable reasoning in EN/ES/EU languages.
Backend services return error codes that frontend translates for user's language.
This commit is contained in:
Claude
2025-11-07 18:40:44 +00:00
parent 28136cf198
commit ed7db4d4f2
4 changed files with 70 additions and 29 deletions

View File

@@ -34,6 +34,13 @@ from app.models.purchase_order import (
PurchaseOrder, PurchaseOrderItem, PurchaseOrderStatus
)
# Import reasoning helper functions for i18n support
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
from shared.schemas.reasoning_types import (
create_po_reasoning_low_stock,
create_po_reasoning_supplier_contract
)
# Configure logging
logger = structlog.get_logger()
@@ -116,31 +123,39 @@ async def create_purchase_order(
# Generate reasoning for JTBD dashboard (if columns exist after migration)
days_until_delivery = (required_delivery - created_at).days
reasoning_text = None
reasoning_json = None
consequence_text = None
# Generate structured reasoning_data for i18n support
reasoning_data = None
try:
# Try to set reasoning fields (will work after migration)
# Get product names from items
product_names = [item.product_name for item in items if hasattr(item, 'product_name')]
if not product_names:
product_names = [f"Product {i+1}" for i in range(len(items))]
if status == PurchaseOrderStatus.pending_approval:
reasoning_text = f"Low stock detected for {supplier.name} items. Current inventory projected to run out in {days_until_delivery + 2} days."
consequence_text = f"Stock-out risk in {days_until_delivery + 2} days if not approved. Production may be impacted."
reasoning_json = {
"trigger": "low_stock",
"urgency_score": 75 if days_until_delivery < 5 else 50,
"days_remaining": days_until_delivery + 2,
"supplier_trust_score": supplier.trust_score
}
# Low stock detection reasoning
days_until_stockout = days_until_delivery + 2
reasoning_data = create_po_reasoning_low_stock(
supplier_name=supplier.name,
product_names=product_names,
current_stock=random.uniform(20, 50), # Demo: low stock
required_stock=random.uniform(100, 200), # Demo: needed stock
days_until_stockout=days_until_stockout,
threshold_percentage=20,
affected_products=product_names[:2] if len(product_names) > 1 else product_names,
estimated_lost_orders=random.randint(5, 15) if days_until_stockout <= 3 else None
)
elif auto_approved:
reasoning_text = f"Auto-approved based on supplier trust score ({supplier.trust_score:.0%}) and amount within threshold (€{subtotal:.2f})."
reasoning_json = {
"trigger": "auto_approval",
"trust_score": supplier.trust_score,
"amount": float(subtotal),
"threshold": 500.0
}
except Exception:
# Columns don't exist yet, that's ok
# Supplier contract/auto-approval reasoning
reasoning_data = create_po_reasoning_supplier_contract(
supplier_name=supplier.name,
product_names=product_names,
contract_terms=f"Auto-approval threshold: €500",
trust_score=float(supplier.trust_score) if hasattr(supplier, 'trust_score') else 0.85
)
except Exception as e:
logger.warning(f"Failed to generate reasoning_data: {e}")
pass
# Create PO
@@ -165,12 +180,10 @@ async def create_purchase_order(
updated_by=SYSTEM_USER_ID
)
# Set reasoning fields if they exist (after migration)
if reasoning_text:
# Set structured reasoning_data for i18n support
if reasoning_data:
try:
po.reasoning = reasoning_text
po.consequence = consequence_text
po.reasoning_data = reasoning_json
po.reasoning_data = reasoning_data
except Exception:
pass # Columns don't exist yet