demo seed change 4
This commit is contained in:
@@ -9,7 +9,7 @@ from sqlalchemy import select, delete, func
|
||||
import structlog
|
||||
import uuid
|
||||
from datetime import datetime, timezone, timedelta, date
|
||||
from typing import Optional
|
||||
from typing import Optional, Dict, Any
|
||||
import os
|
||||
import json
|
||||
from pathlib import Path
|
||||
@@ -26,6 +26,7 @@ from shared.schemas.reasoning_types import (
|
||||
create_po_reasoning_supplier_contract
|
||||
)
|
||||
from app.core.config import settings
|
||||
from shared.clients.suppliers_client import SuppliersServiceClient
|
||||
|
||||
logger = structlog.get_logger()
|
||||
router = APIRouter(prefix="/internal/demo", tags=["internal"])
|
||||
@@ -42,6 +43,155 @@ def verify_internal_api_key(x_internal_api_key: Optional[str] = Header(None)):
|
||||
return True
|
||||
|
||||
|
||||
async def _emit_po_approval_alerts_for_demo(
|
||||
virtual_tenant_id: uuid.UUID,
|
||||
pending_pos: list[PurchaseOrder]
|
||||
) -> int:
|
||||
"""
|
||||
Emit alerts for pending approval POs during demo cloning.
|
||||
Creates clients internally to avoid dependency injection issues.
|
||||
Returns the number of alerts successfully emitted.
|
||||
"""
|
||||
if not pending_pos:
|
||||
return 0
|
||||
|
||||
alerts_emitted = 0
|
||||
|
||||
try:
|
||||
# Initialize clients locally for this operation
|
||||
from shared.clients.suppliers_client import SuppliersServiceClient
|
||||
from shared.messaging import RabbitMQClient
|
||||
|
||||
# Use the existing settings instead of creating a new config
|
||||
# This avoids issues with property-based configuration
|
||||
suppliers_client = SuppliersServiceClient(settings, "procurement-service")
|
||||
rabbitmq_client = RabbitMQClient(settings.RABBITMQ_URL, "procurement-service")
|
||||
|
||||
# Connect to RabbitMQ
|
||||
await rabbitmq_client.connect()
|
||||
|
||||
logger.info(
|
||||
"Emitting PO approval alerts for demo",
|
||||
pending_po_count=len(pending_pos),
|
||||
virtual_tenant_id=str(virtual_tenant_id)
|
||||
)
|
||||
|
||||
# Emit alerts for each pending PO
|
||||
for po in pending_pos:
|
||||
try:
|
||||
# Get supplier details
|
||||
supplier_details = await suppliers_client.get_supplier_by_id(
|
||||
tenant_id=str(virtual_tenant_id),
|
||||
supplier_id=str(po.supplier_id)
|
||||
)
|
||||
|
||||
# Skip if supplier not found
|
||||
if not supplier_details:
|
||||
logger.warning(
|
||||
"Supplier not found for PO, skipping alert",
|
||||
po_id=str(po.id),
|
||||
supplier_id=str(po.supplier_id)
|
||||
)
|
||||
continue
|
||||
|
||||
# Calculate urgency fields
|
||||
now = datetime.utcnow()
|
||||
hours_until_consequence = None
|
||||
deadline = None
|
||||
|
||||
if po.required_delivery_date:
|
||||
supplier_lead_time_days = supplier_details.get('standard_lead_time', 7)
|
||||
approval_deadline = po.required_delivery_date - timedelta(days=supplier_lead_time_days)
|
||||
deadline = approval_deadline
|
||||
hours_until_consequence = (approval_deadline - now).total_seconds() / 3600
|
||||
|
||||
# Prepare alert payload
|
||||
alert_data = {
|
||||
'id': str(uuid.uuid4()),
|
||||
'tenant_id': str(virtual_tenant_id),
|
||||
'service': 'procurement',
|
||||
'type': 'po_approval_needed',
|
||||
'alert_type': 'po_approval_needed',
|
||||
'type_class': 'action_needed',
|
||||
'severity': 'high' if po.priority == 'critical' else 'medium',
|
||||
'title': '',
|
||||
'message': '',
|
||||
'timestamp': datetime.utcnow().isoformat(),
|
||||
'metadata': {
|
||||
'po_id': str(po.id),
|
||||
'po_number': po.po_number,
|
||||
'supplier_id': str(po.supplier_id),
|
||||
'supplier_name': supplier_details.get('name', ''),
|
||||
'total_amount': float(po.total_amount),
|
||||
'currency': po.currency,
|
||||
'priority': po.priority,
|
||||
'required_delivery_date': po.required_delivery_date.isoformat() if po.required_delivery_date else None,
|
||||
'created_at': po.created_at.isoformat(),
|
||||
'financial_impact': float(po.total_amount),
|
||||
'urgency_score': 85,
|
||||
'deadline': deadline.isoformat() if deadline else None,
|
||||
'hours_until_consequence': round(hours_until_consequence, 1) if hours_until_consequence else None,
|
||||
'reasoning_data': po.reasoning_data or {}
|
||||
},
|
||||
'message_params': {
|
||||
'po_number': po.po_number,
|
||||
'supplier_name': supplier_details.get('name', ''),
|
||||
'total_amount': float(po.total_amount),
|
||||
'currency': po.currency,
|
||||
'priority': po.priority,
|
||||
'required_delivery_date': po.required_delivery_date.isoformat() if po.required_delivery_date else None,
|
||||
'items_count': 0,
|
||||
'created_at': po.created_at.isoformat()
|
||||
},
|
||||
'actions': ['approve_po', 'reject_po', 'modify_po'],
|
||||
'item_type': 'alert'
|
||||
}
|
||||
|
||||
# Publish to RabbitMQ
|
||||
await rabbitmq_client.publish_event(
|
||||
exchange_name='alerts.exchange',
|
||||
routing_key=f'alert.{alert_data["severity"]}.procurement',
|
||||
event_data=alert_data
|
||||
)
|
||||
|
||||
alerts_emitted += 1
|
||||
logger.debug(
|
||||
"PO approval alert emitted",
|
||||
po_id=str(po.id),
|
||||
po_number=po.po_number
|
||||
)
|
||||
|
||||
except Exception as po_error:
|
||||
logger.warning(
|
||||
"Failed to emit alert for PO",
|
||||
po_id=str(po.id),
|
||||
po_number=po.po_number,
|
||||
error=str(po_error)
|
||||
)
|
||||
# Continue with other POs
|
||||
|
||||
# Close RabbitMQ connection
|
||||
await rabbitmq_client.close()
|
||||
|
||||
logger.info(
|
||||
"PO approval alerts emission completed",
|
||||
alerts_emitted=alerts_emitted,
|
||||
total_pending=len(pending_pos)
|
||||
)
|
||||
|
||||
return alerts_emitted
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Failed to emit PO approval alerts",
|
||||
error=str(e),
|
||||
virtual_tenant_id=str(virtual_tenant_id),
|
||||
exc_info=True
|
||||
)
|
||||
# Don't fail the cloning process
|
||||
return alerts_emitted
|
||||
|
||||
|
||||
@router.post("/clone")
|
||||
async def clone_demo_data(
|
||||
base_tenant_id: str,
|
||||
@@ -420,6 +570,39 @@ async def clone_demo_data(
|
||||
# Commit all loaded data
|
||||
await db.commit()
|
||||
|
||||
# Emit alerts for pending approval POs (CRITICAL for demo dashboard)
|
||||
alerts_emitted = 0
|
||||
try:
|
||||
# Get all pending approval POs that were just created
|
||||
pending_approval_pos = await db.execute(
|
||||
select(PurchaseOrder).where(
|
||||
PurchaseOrder.tenant_id == virtual_uuid,
|
||||
PurchaseOrder.status == 'pending_approval'
|
||||
)
|
||||
)
|
||||
pending_pos = pending_approval_pos.scalars().all()
|
||||
|
||||
logger.info(
|
||||
"Found pending approval POs for alert emission",
|
||||
count=len(pending_pos),
|
||||
virtual_tenant_id=virtual_tenant_id
|
||||
)
|
||||
|
||||
# Emit alerts using refactored function
|
||||
if pending_pos:
|
||||
alerts_emitted = await _emit_po_approval_alerts_for_demo(
|
||||
virtual_tenant_id=virtual_uuid,
|
||||
pending_pos=pending_pos
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Failed to emit PO approval alerts during demo cloning",
|
||||
error=str(e),
|
||||
virtual_tenant_id=virtual_tenant_id
|
||||
)
|
||||
# Don't fail the entire cloning process if alert emission fails
|
||||
|
||||
# Calculate total records
|
||||
total_records = (stats["procurement_plans"] + stats["procurement_requirements"] +
|
||||
stats["purchase_orders"] + stats["purchase_order_items"] +
|
||||
@@ -439,7 +622,8 @@ async def clone_demo_data(
|
||||
"status": "completed",
|
||||
"records_cloned": total_records,
|
||||
"duration_ms": duration_ms,
|
||||
"details": stats
|
||||
"details": stats,
|
||||
"alerts_emitted": alerts_emitted
|
||||
}
|
||||
|
||||
except ValueError as e:
|
||||
|
||||
Reference in New Issue
Block a user