New alert system and panel de control page
This commit is contained in:
@@ -17,6 +17,8 @@ from app.models.procurement_plan import ProcurementPlan, ProcurementRequirement
|
||||
from app.models.purchase_order import PurchaseOrder, PurchaseOrderItem
|
||||
from app.models.replenishment import ReplenishmentPlan, ReplenishmentPlanItem
|
||||
from shared.utils.demo_dates import adjust_date_for_demo, BASE_REFERENCE_DATE
|
||||
from shared.messaging.rabbitmq import RabbitMQClient
|
||||
from app.core.config import settings
|
||||
|
||||
logger = structlog.get_logger()
|
||||
router = APIRouter(prefix="/internal/demo", tags=["internal"])
|
||||
@@ -267,6 +269,20 @@ async def clone_demo_data(
|
||||
# Generate a system user UUID for audit fields (demo purposes)
|
||||
system_user_id = uuid.uuid4()
|
||||
|
||||
# For demo sessions: 30-40% of POs should have delivery scheduled for TODAY
|
||||
# This ensures the ExecutionProgressTracker shows realistic delivery data
|
||||
import random
|
||||
expected_delivery = None
|
||||
if order.status in ['approved', 'sent_to_supplier'] and random.random() < 0.35:
|
||||
# Set delivery for today at various times (8am-6pm)
|
||||
hours_offset = random.randint(8, 18)
|
||||
minutes_offset = random.choice([0, 15, 30, 45])
|
||||
expected_delivery = session_time.replace(hour=hours_offset, minute=minutes_offset, second=0, microsecond=0)
|
||||
else:
|
||||
# Use the adjusted estimated delivery date
|
||||
expected_delivery = adjusted_estimated_delivery
|
||||
|
||||
# Create new PurchaseOrder - add expected_delivery_date only if column exists (after migration)
|
||||
new_order = PurchaseOrder(
|
||||
id=new_order_id,
|
||||
tenant_id=virtual_uuid,
|
||||
@@ -307,6 +323,11 @@ async def clone_demo_data(
|
||||
created_by=system_user_id,
|
||||
updated_by=system_user_id
|
||||
)
|
||||
|
||||
# Add expected_delivery_date if the model supports it (after migration)
|
||||
if hasattr(PurchaseOrder, 'expected_delivery_date'):
|
||||
new_order.expected_delivery_date = expected_delivery
|
||||
|
||||
db.add(new_order)
|
||||
stats["purchase_orders"] += 1
|
||||
|
||||
@@ -415,12 +436,116 @@ async def clone_demo_data(
|
||||
await db.commit()
|
||||
|
||||
total_records = sum(stats.values())
|
||||
|
||||
# EMIT ALERTS FOR PENDING APPROVAL POs
|
||||
# After cloning, emit PO approval alerts for any pending_approval POs
|
||||
# This ensures the action queue is populated when the demo session starts
|
||||
pending_pos_for_alerts = []
|
||||
for order_id in order_id_map.values():
|
||||
result = await db.execute(
|
||||
select(PurchaseOrder).where(
|
||||
PurchaseOrder.id == order_id,
|
||||
PurchaseOrder.status == 'pending_approval'
|
||||
)
|
||||
)
|
||||
po = result.scalar_one_or_none()
|
||||
if po:
|
||||
pending_pos_for_alerts.append(po)
|
||||
|
||||
logger.info(
|
||||
"Emitting PO approval alerts for cloned pending POs",
|
||||
pending_po_count=len(pending_pos_for_alerts),
|
||||
virtual_tenant_id=virtual_tenant_id
|
||||
)
|
||||
|
||||
# Initialize RabbitMQ client for alert emission
|
||||
alerts_emitted = 0
|
||||
if pending_pos_for_alerts:
|
||||
rabbitmq_client = RabbitMQClient(settings.RABBITMQ_URL, "procurement")
|
||||
try:
|
||||
await rabbitmq_client.connect()
|
||||
|
||||
for po in pending_pos_for_alerts:
|
||||
try:
|
||||
# Get deadline for urgency calculation
|
||||
now_utc = datetime.now(timezone.utc)
|
||||
if po.required_delivery_date:
|
||||
deadline = po.required_delivery_date
|
||||
if deadline.tzinfo is None:
|
||||
deadline = deadline.replace(tzinfo=timezone.utc)
|
||||
else:
|
||||
days_until = 3 if po.priority == 'critical' else 7
|
||||
deadline = now_utc + timedelta(days=days_until)
|
||||
|
||||
hours_until = (deadline - now_utc).total_seconds() / 3600
|
||||
|
||||
# Prepare alert payload
|
||||
alert_data = {
|
||||
'id': str(uuid.uuid4()),
|
||||
'tenant_id': str(virtual_uuid),
|
||||
'service': 'procurement',
|
||||
'type': 'po_approval_needed',
|
||||
'alert_type': 'po_approval_needed',
|
||||
'type_class': 'action_needed',
|
||||
'severity': 'high' if po.priority == 'critical' else 'medium',
|
||||
'title': f'Purchase Order #{po.po_number} requires approval',
|
||||
'message': f'Purchase order totaling {po.currency} {po.total_amount:.2f} is pending approval.',
|
||||
'timestamp': now_utc.isoformat(),
|
||||
'metadata': {
|
||||
'po_id': str(po.id),
|
||||
'po_number': po.po_number,
|
||||
'supplier_id': str(po.supplier_id),
|
||||
'supplier_name': f'Supplier-{po.supplier_id}', # Simplified for demo
|
||||
'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),
|
||||
'deadline': deadline.isoformat(),
|
||||
'hours_until_consequence': int(hours_until),
|
||||
'reasoning_data': po.reasoning_data if po.reasoning_data else None, # Include orchestrator reasoning
|
||||
},
|
||||
'actions': ['approve_po', 'reject_po', 'modify_po'],
|
||||
'item_type': 'alert'
|
||||
}
|
||||
|
||||
# Publish to RabbitMQ
|
||||
success = await rabbitmq_client.publish_event(
|
||||
exchange_name='alerts.exchange',
|
||||
routing_key=f'alert.{alert_data["severity"]}.procurement',
|
||||
event_data=alert_data
|
||||
)
|
||||
|
||||
if success:
|
||||
alerts_emitted += 1
|
||||
logger.info(
|
||||
"PO approval alert emitted during cloning",
|
||||
po_id=str(po.id),
|
||||
po_number=po.po_number,
|
||||
tenant_id=str(virtual_uuid)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Failed to emit PO approval alert during cloning",
|
||||
po_id=str(po.id),
|
||||
error=str(e)
|
||||
)
|
||||
# Continue with other POs
|
||||
continue
|
||||
|
||||
finally:
|
||||
await rabbitmq_client.disconnect()
|
||||
|
||||
stats["alerts_emitted"] = alerts_emitted
|
||||
|
||||
duration_ms = int((datetime.now(timezone.utc) - start_time).total_seconds() * 1000)
|
||||
|
||||
logger.info(
|
||||
"Procurement data cloning completed",
|
||||
virtual_tenant_id=virtual_tenant_id,
|
||||
total_records=total_records,
|
||||
alerts_emitted=alerts_emitted,
|
||||
stats=stats,
|
||||
duration_ms=duration_ms
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user