Files
bakery-ia/services/inventory/app/services/messaging.py
2025-08-13 17:39:35 +02:00

244 lines
7.7 KiB
Python

# services/inventory/app/services/messaging.py
"""
Messaging service for inventory events
"""
from typing import Dict, Any, Optional
from uuid import UUID
import structlog
from shared.messaging.rabbitmq import MessagePublisher
from shared.messaging.events import (
EVENT_TYPES,
InventoryEvent,
StockAlertEvent,
StockMovementEvent
)
logger = structlog.get_logger()
class InventoryMessagingService:
"""Service for publishing inventory-related events"""
def __init__(self):
self.publisher = MessagePublisher()
async def publish_ingredient_created(
self,
tenant_id: UUID,
ingredient_id: UUID,
ingredient_data: Dict[str, Any]
):
"""Publish ingredient creation event"""
try:
event = InventoryEvent(
event_type=EVENT_TYPES.INVENTORY.INGREDIENT_CREATED,
tenant_id=str(tenant_id),
ingredient_id=str(ingredient_id),
data=ingredient_data
)
await self.publisher.publish_event(
routing_key="inventory.ingredient.created",
event=event
)
logger.info(
"Published ingredient created event",
tenant_id=tenant_id,
ingredient_id=ingredient_id
)
except Exception as e:
logger.error(
"Failed to publish ingredient created event",
error=str(e),
tenant_id=tenant_id,
ingredient_id=ingredient_id
)
async def publish_stock_added(
self,
tenant_id: UUID,
ingredient_id: UUID,
stock_id: UUID,
quantity: float,
batch_number: Optional[str] = None
):
"""Publish stock addition event"""
try:
movement_event = StockMovementEvent(
event_type=EVENT_TYPES.INVENTORY.STOCK_ADDED,
tenant_id=str(tenant_id),
ingredient_id=str(ingredient_id),
stock_id=str(stock_id),
quantity=quantity,
movement_type="purchase",
data={
"batch_number": batch_number,
"movement_type": "purchase"
}
)
await self.publisher.publish_event(
routing_key="inventory.stock.added",
event=movement_event
)
logger.info(
"Published stock added event",
tenant_id=tenant_id,
ingredient_id=ingredient_id,
quantity=quantity
)
except Exception as e:
logger.error(
"Failed to publish stock added event",
error=str(e),
tenant_id=tenant_id,
ingredient_id=ingredient_id
)
async def publish_stock_consumed(
self,
tenant_id: UUID,
ingredient_id: UUID,
consumed_items: list,
total_quantity: float,
reference_number: Optional[str] = None
):
"""Publish stock consumption event"""
try:
for item in consumed_items:
movement_event = StockMovementEvent(
event_type=EVENT_TYPES.INVENTORY.STOCK_CONSUMED,
tenant_id=str(tenant_id),
ingredient_id=str(ingredient_id),
stock_id=item['stock_id'],
quantity=item['quantity_consumed'],
movement_type="production_use",
data={
"batch_number": item.get('batch_number'),
"reference_number": reference_number,
"movement_type": "production_use"
}
)
await self.publisher.publish_event(
routing_key="inventory.stock.consumed",
event=movement_event
)
logger.info(
"Published stock consumed events",
tenant_id=tenant_id,
ingredient_id=ingredient_id,
total_quantity=total_quantity,
items_count=len(consumed_items)
)
except Exception as e:
logger.error(
"Failed to publish stock consumed event",
error=str(e),
tenant_id=tenant_id,
ingredient_id=ingredient_id
)
async def publish_low_stock_alert(
self,
tenant_id: UUID,
ingredient_id: UUID,
ingredient_name: str,
current_stock: float,
threshold: float,
needs_reorder: bool = False
):
"""Publish low stock alert event"""
try:
alert_event = StockAlertEvent(
event_type=EVENT_TYPES.INVENTORY.LOW_STOCK_ALERT,
tenant_id=str(tenant_id),
ingredient_id=str(ingredient_id),
alert_type="low_stock" if not needs_reorder else "reorder_needed",
severity="medium" if not needs_reorder else "high",
data={
"ingredient_name": ingredient_name,
"current_stock": current_stock,
"threshold": threshold,
"needs_reorder": needs_reorder
}
)
await self.publisher.publish_event(
routing_key="inventory.alerts.low_stock",
event=alert_event
)
logger.info(
"Published low stock alert",
tenant_id=tenant_id,
ingredient_id=ingredient_id,
current_stock=current_stock
)
except Exception as e:
logger.error(
"Failed to publish low stock alert",
error=str(e),
tenant_id=tenant_id,
ingredient_id=ingredient_id
)
async def publish_expiration_alert(
self,
tenant_id: UUID,
ingredient_id: UUID,
stock_id: UUID,
ingredient_name: str,
batch_number: Optional[str],
expiration_date: str,
days_to_expiry: int,
quantity: float
):
"""Publish expiration alert event"""
try:
severity = "critical" if days_to_expiry <= 1 else "high"
alert_event = StockAlertEvent(
event_type=EVENT_TYPES.INVENTORY.EXPIRATION_ALERT,
tenant_id=str(tenant_id),
ingredient_id=str(ingredient_id),
alert_type="expiring_soon",
severity=severity,
data={
"stock_id": str(stock_id),
"ingredient_name": ingredient_name,
"batch_number": batch_number,
"expiration_date": expiration_date,
"days_to_expiry": days_to_expiry,
"quantity": quantity
}
)
await self.publisher.publish_event(
routing_key="inventory.alerts.expiration",
event=alert_event
)
logger.info(
"Published expiration alert",
tenant_id=tenant_id,
ingredient_id=ingredient_id,
days_to_expiry=days_to_expiry
)
except Exception as e:
logger.error(
"Failed to publish expiration alert",
error=str(e),
tenant_id=tenant_id,
ingredient_id=ingredient_id
)