Files
bakery-ia/services/inventory/app/services/inventory_notification_service.py
2025-12-05 20:07:01 +01:00

170 lines
5.5 KiB
Python

"""
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
)