""" Inventory Notification Service - Simplified Emits minimal events using EventPublisher. All enrichment handled by alert_processor. These are NOTIFICATIONS (not alerts) - informational state changes that don't require user action. """ from datetime import datetime, timezone from typing import Optional, Dict, Any from uuid import UUID import structlog from shared.messaging import UnifiedEventPublisher logger = structlog.get_logger() class InventoryNotificationService: """ Service for emitting inventory notifications using EventPublisher. """ def __init__(self, event_publisher: UnifiedEventPublisher): self.publisher = event_publisher async def emit_stock_received_notification( self, tenant_id: UUID, stock_receipt_id: str, ingredient_id: str, ingredient_name: str, quantity_received: float, unit: str, supplier_name: Optional[str] = None, delivery_id: Optional[str] = None, ) -> None: """ Emit notification when stock is received. """ message = f"Received {quantity_received} {unit} of {ingredient_name}" if supplier_name: message += f" from {supplier_name}" metadata = { "stock_receipt_id": stock_receipt_id, "ingredient_id": ingredient_id, "ingredient_name": ingredient_name, "quantity_received": float(quantity_received), "unit": unit, "supplier_name": supplier_name, "delivery_id": delivery_id, "received_at": datetime.now(timezone.utc).isoformat(), } await self.publisher.publish_notification( event_type="inventory.stock_received", tenant_id=tenant_id, data=metadata ) logger.info( "stock_received_notification_emitted", tenant_id=str(tenant_id), ingredient_name=ingredient_name, quantity_received=quantity_received ) async def emit_stock_movement_notification( self, tenant_id: UUID, movement_id: str, ingredient_id: str, ingredient_name: str, quantity: float, unit: str, movement_type: str, # 'transfer', 'adjustment', 'waste', 'return' from_location: Optional[str] = None, to_location: Optional[str] = None, reason: Optional[str] = None, ) -> None: """ Emit notification for stock movements (transfers, adjustments, waste). """ # Build message based on movement type if movement_type == "transfer": message = f"Transferred {quantity} {unit} of {ingredient_name}" if from_location and to_location: message += f" from {from_location} to {to_location}" elif movement_type == "adjustment": message = f"Adjusted {ingredient_name} by {quantity} {unit}" if reason: message += f" - {reason}" elif movement_type == "waste": message = f"Waste recorded: {quantity} {unit} of {ingredient_name}" if reason: message += f" - {reason}" elif movement_type == "return": message = f"Returned {quantity} {unit} of {ingredient_name}" else: message = f"Stock movement: {quantity} {unit} of {ingredient_name}" metadata = { "movement_id": movement_id, "ingredient_id": ingredient_id, "ingredient_name": ingredient_name, "quantity": float(quantity), "unit": unit, "movement_type": movement_type, "from_location": from_location, "to_location": to_location, "reason": reason, "moved_at": datetime.now(timezone.utc).isoformat(), } await self.publisher.publish_notification( event_type="inventory.stock_movement", tenant_id=tenant_id, data=metadata ) logger.info( "stock_movement_notification_emitted", tenant_id=str(tenant_id), movement_type=movement_type, ingredient_name=ingredient_name ) async def emit_stock_updated_notification( self, tenant_id: UUID, ingredient_id: str, ingredient_name: str, old_quantity: float, new_quantity: float, unit: str, update_reason: str, ) -> None: """ Emit notification when stock is updated. """ quantity_change = new_quantity - old_quantity change_direction = "increased" if quantity_change > 0 else "decreased" message = f"Stock {change_direction} by {abs(quantity_change)} {unit} - {update_reason}" metadata = { "ingredient_id": ingredient_id, "ingredient_name": ingredient_name, "old_quantity": float(old_quantity), "new_quantity": float(new_quantity), "quantity_change": float(quantity_change), "unit": unit, "update_reason": update_reason, "updated_at": datetime.now(timezone.utc).isoformat(), } await self.publisher.publish_notification( event_type="inventory.stock_updated", tenant_id=tenant_id, data=metadata ) logger.info( "stock_updated_notification_emitted", tenant_id=str(tenant_id), ingredient_name=ingredient_name, quantity_change=quantity_change )