151 lines
6.1 KiB
Python
151 lines
6.1 KiB
Python
# services/recipes/app/services/inventory_client.py
|
|
"""
|
|
Client for communicating with Inventory Service
|
|
"""
|
|
|
|
import logging
|
|
from typing import List, Optional, Dict, Any
|
|
from uuid import UUID
|
|
|
|
from shared.clients.inventory_client import InventoryServiceClient as SharedInventoryClient
|
|
from ..core.config import settings
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class InventoryClient:
|
|
"""Client for inventory service communication via shared client"""
|
|
|
|
def __init__(self):
|
|
self._shared_client = SharedInventoryClient(settings)
|
|
|
|
async def get_ingredient_by_id(self, tenant_id: UUID, ingredient_id: UUID) -> Optional[Dict[str, Any]]:
|
|
"""Get ingredient details from inventory service"""
|
|
try:
|
|
result = await self._shared_client.get_ingredient_by_id(ingredient_id, str(tenant_id))
|
|
return result
|
|
except Exception as e:
|
|
logger.error(f"Error getting ingredient {ingredient_id}: {e}")
|
|
return None
|
|
|
|
async def get_ingredients_by_ids(self, tenant_id: UUID, ingredient_ids: List[UUID]) -> List[Dict[str, Any]]:
|
|
"""Get multiple ingredients by IDs"""
|
|
try:
|
|
# For now, get ingredients individually - could be optimized with batch endpoint
|
|
results = []
|
|
for ingredient_id in ingredient_ids:
|
|
ingredient = await self._shared_client.get_ingredient_by_id(ingredient_id, str(tenant_id))
|
|
if ingredient:
|
|
results.append(ingredient)
|
|
return results
|
|
except Exception as e:
|
|
logger.error(f"Error getting ingredients batch: {e}")
|
|
return []
|
|
|
|
async def get_ingredient_stock_level(self, tenant_id: UUID, ingredient_id: UUID) -> Optional[Dict[str, Any]]:
|
|
"""Get current stock level for ingredient"""
|
|
try:
|
|
stock_entries = await self._shared_client.get_ingredient_stock(ingredient_id, str(tenant_id))
|
|
if stock_entries:
|
|
# Calculate total available stock from all entries
|
|
total_stock = sum(entry.get('available_quantity', 0) for entry in stock_entries)
|
|
return {
|
|
'ingredient_id': str(ingredient_id),
|
|
'total_available': total_stock,
|
|
'stock_entries': stock_entries
|
|
}
|
|
return None
|
|
except Exception as e:
|
|
logger.error(f"Error getting stock level for {ingredient_id}: {e}")
|
|
return None
|
|
|
|
async def reserve_ingredients(
|
|
self,
|
|
tenant_id: UUID,
|
|
reservations: List[Dict[str, Any]]
|
|
) -> Dict[str, Any]:
|
|
"""Reserve ingredients for production"""
|
|
try:
|
|
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
|
response = await client.post(
|
|
f"{self.base_url}/api/v1/stock/reserve",
|
|
headers={"X-Tenant-ID": str(tenant_id)},
|
|
json={"reservations": reservations}
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
return {"success": True, "data": response.json()}
|
|
else:
|
|
logger.error(f"Failed to reserve ingredients: {response.status_code}")
|
|
return {"success": False, "error": response.text}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error reserving ingredients: {e}")
|
|
return {"success": False, "error": str(e)}
|
|
|
|
async def consume_ingredients(
|
|
self,
|
|
tenant_id: UUID,
|
|
consumptions: List[Dict[str, Any]],
|
|
production_batch_id: UUID
|
|
) -> Dict[str, Any]:
|
|
"""Record ingredient consumption for production"""
|
|
try:
|
|
consumption_data = {
|
|
"consumptions": consumptions,
|
|
"reference_number": str(production_batch_id),
|
|
"movement_type": "production_use"
|
|
}
|
|
|
|
result = await self._shared_client.consume_stock(consumption_data, str(tenant_id))
|
|
|
|
if result:
|
|
return {"success": True, "data": result}
|
|
else:
|
|
return {"success": False, "error": "Failed to consume ingredients"}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error consuming ingredients: {e}")
|
|
return {"success": False, "error": str(e)}
|
|
|
|
async def add_finished_product_to_inventory(
|
|
self,
|
|
tenant_id: UUID,
|
|
product_data: Dict[str, Any]
|
|
) -> Dict[str, Any]:
|
|
"""Add finished product to inventory after production"""
|
|
try:
|
|
result = await self._shared_client.receive_stock(product_data, str(tenant_id))
|
|
|
|
if result:
|
|
return {"success": True, "data": result}
|
|
else:
|
|
return {"success": False, "error": "Failed to add finished product"}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error adding finished product: {e}")
|
|
return {"success": False, "error": str(e)}
|
|
|
|
async def check_ingredient_availability(
|
|
self,
|
|
tenant_id: UUID,
|
|
required_ingredients: List[Dict[str, Any]]
|
|
) -> Dict[str, Any]:
|
|
"""Check if required ingredients are available for production"""
|
|
try:
|
|
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
|
response = await client.post(
|
|
f"{self.base_url}/api/v1/stock/check-availability",
|
|
headers={"X-Tenant-ID": str(tenant_id)},
|
|
json={"required_ingredients": required_ingredients}
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
return {"success": True, "data": response.json()}
|
|
else:
|
|
logger.error(f"Failed to check availability: {response.status_code}")
|
|
return {"success": False, "error": response.text}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error checking availability: {e}")
|
|
return {"success": False, "error": str(e)} |