Create new services: inventory, recipes, suppliers

This commit is contained in:
Urtzi Alfaro
2025-08-13 17:39:35 +02:00
parent fbe7470ad9
commit 16b8a9d50c
151 changed files with 35799 additions and 857 deletions

View File

@@ -0,0 +1,222 @@
# services/sales/app/services/inventory_client.py
"""
Inventory Service Client - Inter-service communication
Handles communication with the inventory service to fetch product data
"""
import httpx
import structlog
from typing import Dict, Any, List, Optional
from uuid import UUID
from app.core.config import settings
logger = structlog.get_logger()
class InventoryServiceClient:
"""Client for communicating with the inventory service"""
def __init__(self):
self.base_url = settings.INVENTORY_SERVICE_URL
self.timeout = 30.0
async def classify_products_batch(self, product_list: Dict[str, Any], tenant_id: UUID) -> Optional[Dict[str, Any]]:
"""Get product details from inventory service by ID"""
try:
async with httpx.AsyncClient(timeout=self.timeout) as client:
response = await client.post(
f"{self.base_url}/api/v1/tenants/{tenant_id}/inventory/classify-products-batch",
headers=self._get_headers(),
product_list=product_list
)
if response.status_code == 200:
product_data = response.json()
logger.info("Retrieved product from inventory service",
tenant_id=tenant_id)
return product_data
elif response.status_code == 404:
logger.warning("Product not found in inventory service",
tenant_id=tenant_id)
return None
else:
logger.error("Failed to fetch product from inventory service",
status_code=response.status_code,
tenant_id=tenant_id)
return None
except httpx.TimeoutException:
logger.error("Timeout fetching product from inventory service",
tenant_id=tenant_id)
return None
except Exception as e:
logger.error("Error communicating with inventory service",
error=str(e), tenant_id=tenant_id)
return None
async def get_product_by_id(self, product_id: UUID, tenant_id: UUID) -> Optional[Dict[str, Any]]:
"""Get product details from inventory service by ID"""
try:
async with httpx.AsyncClient(timeout=self.timeout) as client:
response = await client.get(
f"{self.base_url}/api/v1/tenants/{tenant_id}/ingredients/{product_id}",
headers=self._get_headers()
)
if response.status_code == 200:
product_data = response.json()
logger.info("Retrieved product from inventory service",
product_id=product_id, tenant_id=tenant_id)
return product_data
elif response.status_code == 404:
logger.warning("Product not found in inventory service",
product_id=product_id, tenant_id=tenant_id)
return None
else:
logger.error("Failed to fetch product from inventory service",
status_code=response.status_code,
product_id=product_id, tenant_id=tenant_id)
return None
except httpx.TimeoutException:
logger.error("Timeout fetching product from inventory service",
product_id=product_id, tenant_id=tenant_id)
return None
except Exception as e:
logger.error("Error communicating with inventory service",
error=str(e), product_id=product_id, tenant_id=tenant_id)
return None
async def get_product_by_sku(self, sku: str, tenant_id: UUID) -> Optional[Dict[str, Any]]:
"""Get product details from inventory service by SKU"""
try:
async with httpx.AsyncClient(timeout=self.timeout) as client:
response = await client.get(
f"{self.base_url}/api/v1/tenants/{tenant_id}/ingredients",
params={"sku": sku, "limit": 1},
headers=self._get_headers()
)
if response.status_code == 200:
data = response.json()
products = data.get("items", [])
if products:
product_data = products[0]
logger.info("Retrieved product by SKU from inventory service",
sku=sku, tenant_id=tenant_id)
return product_data
else:
logger.warning("Product not found by SKU in inventory service",
sku=sku, tenant_id=tenant_id)
return None
else:
logger.error("Failed to fetch product by SKU from inventory service",
status_code=response.status_code,
sku=sku, tenant_id=tenant_id)
return None
except httpx.TimeoutException:
logger.error("Timeout fetching product by SKU from inventory service",
sku=sku, tenant_id=tenant_id)
return None
except Exception as e:
logger.error("Error communicating with inventory service for SKU",
error=str(e), sku=sku, tenant_id=tenant_id)
return None
async def search_products(self, search_term: str, tenant_id: UUID,
product_type: Optional[str] = None) -> List[Dict[str, Any]]:
"""Search products in inventory service"""
try:
params = {
"search": search_term,
"limit": 50
}
if product_type:
params["product_type"] = product_type
async with httpx.AsyncClient(timeout=self.timeout) as client:
response = await client.get(
f"{self.base_url}/api/v1/tenants/{tenant_id}/ingredients",
params=params,
headers=self._get_headers()
)
if response.status_code == 200:
data = response.json()
products = data.get("items", [])
logger.info("Searched products in inventory service",
search_term=search_term, count=len(products), tenant_id=tenant_id)
return products
else:
logger.error("Failed to search products in inventory service",
status_code=response.status_code,
search_term=search_term, tenant_id=tenant_id)
return []
except httpx.TimeoutException:
logger.error("Timeout searching products in inventory service",
search_term=search_term, tenant_id=tenant_id)
return []
except Exception as e:
logger.error("Error searching products in inventory service",
error=str(e), search_term=search_term, tenant_id=tenant_id)
return []
async def get_products_by_category(self, category: str, tenant_id: UUID,
product_type: Optional[str] = None) -> List[Dict[str, Any]]:
"""Get products by category from inventory service"""
try:
params = {
"limit": 100
}
if product_type == "ingredient":
params["ingredient_category"] = category
elif product_type == "finished_product":
params["product_category"] = category
else:
# Search in both categories if type not specified
params["category"] = category
async with httpx.AsyncClient(timeout=self.timeout) as client:
response = await client.get(
f"{self.base_url}/api/v1/tenants/{tenant_id}/ingredients",
params=params,
headers=self._get_headers()
)
if response.status_code == 200:
data = response.json()
products = data.get("items", [])
logger.info("Retrieved products by category from inventory service",
category=category, count=len(products), tenant_id=tenant_id)
return products
else:
logger.error("Failed to fetch products by category from inventory service",
status_code=response.status_code,
category=category, tenant_id=tenant_id)
return []
except httpx.TimeoutException:
logger.error("Timeout fetching products by category from inventory service",
category=category, tenant_id=tenant_id)
return []
except Exception as e:
logger.error("Error fetching products by category from inventory service",
error=str(e), category=category, tenant_id=tenant_id)
return []
# Cache synchronization removed - no longer needed with pure inventory reference approach
def _get_headers(self) -> Dict[str, str]:
"""Get headers for inventory service requests"""
return {
"Content-Type": "application/json",
"X-Service-Name": "sales-service",
# Add authentication headers if needed
}
# Dependency injection
async def get_inventory_client() -> InventoryServiceClient:
"""Get inventory service client instance"""
return InventoryServiceClient()