REFACTOR ALL APIs

This commit is contained in:
Urtzi Alfaro
2025-10-06 15:27:01 +02:00
parent dc8221bd2f
commit 38fb98bc27
166 changed files with 18454 additions and 13605 deletions

View File

@@ -1,7 +1,12 @@
# shared/clients/forecast_client.py
"""
Forecast Service Client
Forecast Service Client - Updated for refactored backend structure
Handles all API calls to the forecasting service
Backend structure:
- ATOMIC: /forecasting/forecasts (CRUD)
- BUSINESS: /forecasting/operations/* (single, multi-day, batch, etc.)
- ANALYTICS: /forecasting/analytics/* (predictions-performance)
"""
from typing import Dict, Any, Optional, List
@@ -12,17 +17,172 @@ from shared.config.base import BaseServiceSettings
class ForecastServiceClient(BaseServiceClient):
"""Client for communicating with the forecasting service"""
def __init__(self, config: BaseServiceSettings, calling_service_name: str = "unknown"):
super().__init__(calling_service_name, config)
def get_service_base_path(self) -> str:
return "/api/v1"
# ================================================================
# FORECASTS
# ATOMIC: Forecast CRUD Operations
# ================================================================
async def get_forecast(self, tenant_id: str, forecast_id: str) -> Optional[Dict[str, Any]]:
"""Get forecast details by ID"""
return await self.get(f"forecasting/forecasts/{forecast_id}", tenant_id=tenant_id)
async def list_forecasts(
self,
tenant_id: str,
inventory_product_id: Optional[str] = None,
start_date: Optional[date] = None,
end_date: Optional[date] = None,
limit: int = 50,
offset: int = 0
) -> Optional[List[Dict[str, Any]]]:
"""List forecasts for a tenant with optional filters"""
params = {"limit": limit, "offset": offset}
if inventory_product_id:
params["inventory_product_id"] = inventory_product_id
if start_date:
params["start_date"] = start_date.isoformat()
if end_date:
params["end_date"] = end_date.isoformat()
return await self.get("forecasting/forecasts", tenant_id=tenant_id, params=params)
async def delete_forecast(self, tenant_id: str, forecast_id: str) -> Optional[Dict[str, Any]]:
"""Delete a forecast"""
return await self.delete(f"forecasting/forecasts/{forecast_id}", tenant_id=tenant_id)
# ================================================================
# BUSINESS: Forecasting Operations
# ================================================================
async def generate_single_forecast(
self,
tenant_id: str,
inventory_product_id: str,
forecast_date: date,
include_recommendations: bool = False
) -> Optional[Dict[str, Any]]:
"""Generate a single product forecast"""
data = {
"inventory_product_id": inventory_product_id,
"forecast_date": forecast_date.isoformat(),
"include_recommendations": include_recommendations
}
return await self.post("forecasting/operations/single", data=data, tenant_id=tenant_id)
async def generate_multi_day_forecast(
self,
tenant_id: str,
inventory_product_id: str,
forecast_date: date,
forecast_days: int = 7,
include_recommendations: bool = False
) -> Optional[Dict[str, Any]]:
"""Generate multiple daily forecasts for the specified period"""
data = {
"inventory_product_id": inventory_product_id,
"forecast_date": forecast_date.isoformat(),
"forecast_days": forecast_days,
"include_recommendations": include_recommendations
}
return await self.post("forecasting/operations/multi-day", data=data, tenant_id=tenant_id)
async def generate_batch_forecast(
self,
tenant_id: str,
inventory_product_ids: List[str],
forecast_date: date,
forecast_days: int = 1
) -> Optional[Dict[str, Any]]:
"""Generate forecasts for multiple products in batch"""
data = {
"inventory_product_ids": inventory_product_ids,
"forecast_date": forecast_date.isoformat(),
"forecast_days": forecast_days
}
return await self.post("forecasting/operations/batch", data=data, tenant_id=tenant_id)
async def generate_realtime_prediction(
self,
tenant_id: str,
inventory_product_id: str,
model_id: str,
features: Dict[str, Any],
model_path: Optional[str] = None,
confidence_level: float = 0.8
) -> Optional[Dict[str, Any]]:
"""Generate real-time prediction"""
data = {
"inventory_product_id": inventory_product_id,
"model_id": model_id,
"features": features,
"confidence_level": confidence_level
}
if model_path:
data["model_path"] = model_path
return await self.post("forecasting/operations/realtime", data=data, tenant_id=tenant_id)
async def validate_predictions(
self,
tenant_id: str,
start_date: date,
end_date: date
) -> Optional[Dict[str, Any]]:
"""Validate predictions against actual sales data"""
params = {
"start_date": start_date.isoformat(),
"end_date": end_date.isoformat()
}
return await self.post("forecasting/operations/validate-predictions", params=params, tenant_id=tenant_id)
async def get_forecast_statistics(
self,
tenant_id: str,
start_date: Optional[date] = None,
end_date: Optional[date] = None
) -> Optional[Dict[str, Any]]:
"""Get forecast statistics"""
params = {}
if start_date:
params["start_date"] = start_date.isoformat()
if end_date:
params["end_date"] = end_date.isoformat()
return await self.get("forecasting/operations/statistics", tenant_id=tenant_id, params=params)
async def clear_prediction_cache(self, tenant_id: str) -> Optional[Dict[str, Any]]:
"""Clear prediction cache"""
return await self.delete("forecasting/operations/cache", tenant_id=tenant_id)
# ================================================================
# ANALYTICS: Forecasting Analytics
# ================================================================
async def get_predictions_performance(
self,
tenant_id: str,
start_date: Optional[date] = None,
end_date: Optional[date] = None
) -> Optional[Dict[str, Any]]:
"""Get predictions performance analytics"""
params = {}
if start_date:
params["start_date"] = start_date.isoformat()
if end_date:
params["end_date"] = end_date.isoformat()
return await self.get("forecasting/analytics/predictions-performance", tenant_id=tenant_id, params=params)
# ================================================================
# Legacy/Compatibility Methods (deprecated)
# ================================================================
async def create_forecast(
self,
tenant_id: str,
@@ -33,180 +193,16 @@ class ForecastServiceClient(BaseServiceClient):
include_confidence_intervals: bool = True,
**kwargs
) -> Optional[Dict[str, Any]]:
"""Create a new forecast"""
data = {
"model_id": model_id,
"start_date": start_date,
"end_date": end_date,
"include_confidence_intervals": include_confidence_intervals,
**kwargs
}
"""
DEPRECATED: Use generate_single_forecast or generate_batch_forecast instead
Legacy method for backward compatibility
"""
# Map to new batch forecast operation
if product_ids:
data["product_ids"] = product_ids
return await self.post("forecasts", data=data, tenant_id=tenant_id)
async def get_forecast(self, tenant_id: str, forecast_id: str) -> Optional[Dict[str, Any]]:
"""Get forecast details"""
return await self.get(f"forecasts/{forecast_id}", tenant_id=tenant_id)
async def list_forecasts(
self,
tenant_id: str,
status: Optional[str] = None,
model_id: Optional[str] = None,
limit: int = 50
) -> Optional[List[Dict[str, Any]]]:
"""List forecasts for a tenant"""
params = {"limit": limit}
if status:
params["status"] = status
if model_id:
params["model_id"] = model_id
result = await self.get("forecasts", tenant_id=tenant_id, params=params)
return result.get("forecasts", []) if result else None
async def delete_forecast(self, tenant_id: str, forecast_id: str) -> Optional[Dict[str, Any]]:
"""Delete a forecast"""
return await self.delete(f"forecasts/{forecast_id}", tenant_id=tenant_id)
# ================================================================
# PREDICTIONS
# ================================================================
async def get_predictions(
self,
tenant_id: str,
forecast_id: str,
start_date: Optional[str] = None,
end_date: Optional[str] = None,
product_id: Optional[str] = None
) -> Optional[List[Dict[str, Any]]]:
"""Get predictions from a forecast"""
params = {}
if start_date:
params["start_date"] = start_date
if end_date:
params["end_date"] = end_date
if product_id:
params["product_id"] = product_id
result = await self.get(f"forecasts/{forecast_id}/predictions", tenant_id=tenant_id, params=params)
return result.get("predictions", []) if result else None
async def create_realtime_prediction(
self,
tenant_id: str,
model_id: str,
target_date: str,
features: Dict[str, Any],
inventory_product_id: Optional[str] = None,
**kwargs
) -> Optional[Dict[str, Any]]:
"""Create a real-time prediction"""
data = {
"model_id": model_id,
"target_date": target_date,
"features": features,
**kwargs
}
# Add inventory_product_id if provided (required by forecasting service)
if inventory_product_id:
data["inventory_product_id"] = inventory_product_id
return await self.post("forecasts/single", data=data, tenant_id=tenant_id)
async def create_single_forecast(
self,
tenant_id: str,
inventory_product_id: str,
forecast_date: date,
location: str = "default",
forecast_days: int = 1,
confidence_level: float = 0.8,
**kwargs
) -> Optional[Dict[str, Any]]:
"""Create a single product forecast using new API format"""
from datetime import date as date_type
# Convert date to string if needed
if isinstance(forecast_date, date_type):
forecast_date_str = forecast_date.isoformat()
else:
forecast_date_str = str(forecast_date)
data = {
"inventory_product_id": inventory_product_id,
"forecast_date": forecast_date_str,
"forecast_days": forecast_days,
"location": location,
"confidence_level": confidence_level,
**kwargs
}
return await self.post("forecasts/single", data=data, tenant_id=tenant_id)
# ================================================================
# FORECAST VALIDATION & METRICS
# ================================================================
async def get_forecast_accuracy(
self,
tenant_id: str,
forecast_id: str,
start_date: Optional[str] = None,
end_date: Optional[str] = None
) -> Optional[Dict[str, Any]]:
"""Get forecast accuracy metrics"""
params = {}
if start_date:
params["start_date"] = start_date
if end_date:
params["end_date"] = end_date
return await self.get(f"forecasts/{forecast_id}/accuracy", tenant_id=tenant_id, params=params)
async def compare_forecasts(
self,
tenant_id: str,
forecast_ids: List[str],
metric: str = "mape"
) -> Optional[Dict[str, Any]]:
"""Compare multiple forecasts"""
data = {
"forecast_ids": forecast_ids,
"metric": metric
}
return await self.post("forecasts/compare", data=data, tenant_id=tenant_id)
# ================================================================
# FORECAST SCENARIOS
# ================================================================
async def create_scenario_forecast(
self,
tenant_id: str,
model_id: str,
scenario_name: str,
scenario_data: Dict[str, Any],
start_date: str,
end_date: str,
**kwargs
) -> Optional[Dict[str, Any]]:
"""Create a scenario-based forecast"""
data = {
"model_id": model_id,
"scenario_name": scenario_name,
"scenario_data": scenario_data,
"start_date": start_date,
"end_date": end_date,
**kwargs
}
return await self.post("scenarios", data=data, tenant_id=tenant_id)
async def list_scenarios(self, tenant_id: str) -> Optional[List[Dict[str, Any]]]:
"""List forecast scenarios for a tenant"""
result = await self.get("scenarios", tenant_id=tenant_id)
return result.get("scenarios", []) if result else None
return await self.generate_batch_forecast(
tenant_id=tenant_id,
inventory_product_ids=product_ids,
forecast_date=date.fromisoformat(start_date),
forecast_days=1
)
return None

View File

@@ -31,13 +31,13 @@ class InventoryServiceClient(BaseServiceClient):
async def get_ingredient_by_id(self, ingredient_id: UUID, tenant_id: str) -> Optional[Dict[str, Any]]:
"""Get ingredient details by ID"""
try:
result = await self.get(f"ingredients/{ingredient_id}", tenant_id=tenant_id)
result = await self.get(f"inventory/ingredients/{ingredient_id}", tenant_id=tenant_id)
if result:
logger.info("Retrieved ingredient from inventory service",
logger.info("Retrieved ingredient from inventory service",
ingredient_id=ingredient_id, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error fetching ingredient by ID",
logger.error("Error fetching ingredient by ID",
error=str(e), ingredient_id=ingredient_id, tenant_id=tenant_id)
return None
@@ -64,10 +64,10 @@ class InventoryServiceClient(BaseServiceClient):
if is_active is not None:
params["is_active"] = is_active
result = await self.get("ingredients", tenant_id=tenant_id, params=params)
result = await self.get("inventory/ingredients", tenant_id=tenant_id, params=params)
ingredients = result if isinstance(result, list) else []
logger.info("Searched ingredients in inventory service",
logger.info("Searched ingredients in inventory service",
search_term=search, count=len(ingredients), tenant_id=tenant_id)
return ingredients
@@ -83,7 +83,7 @@ class InventoryServiceClient(BaseServiceClient):
if is_active is not None:
params["is_active"] = is_active
ingredients = await self.get_paginated("ingredients", tenant_id=tenant_id, params=params)
ingredients = await self.get_paginated("inventory/ingredients", tenant_id=tenant_id, params=params)
logger.info("Retrieved all ingredients from inventory service",
count=len(ingredients), tenant_id=tenant_id)
@@ -101,7 +101,7 @@ class InventoryServiceClient(BaseServiceClient):
if is_active is not None:
params["is_active"] = is_active
result = await self.get("ingredients/count", tenant_id=tenant_id, params=params)
result = await self.get("inventory/ingredients/count", tenant_id=tenant_id, params=params)
count = result.get("ingredient_count", 0) if isinstance(result, dict) else 0
logger.info("Retrieved ingredient count from inventory service",
@@ -116,7 +116,7 @@ class InventoryServiceClient(BaseServiceClient):
async def create_ingredient(self, ingredient_data: Dict[str, Any], tenant_id: str) -> Optional[Dict[str, Any]]:
"""Create a new ingredient"""
try:
result = await self.post("ingredients", data=ingredient_data, tenant_id=tenant_id)
result = await self.post("inventory/ingredients", data=ingredient_data, tenant_id=tenant_id)
if result:
logger.info("Created ingredient in inventory service",
ingredient_name=ingredient_data.get('name'), tenant_id=tenant_id)
@@ -134,7 +134,7 @@ class InventoryServiceClient(BaseServiceClient):
) -> Optional[Dict[str, Any]]:
"""Update an existing ingredient"""
try:
result = await self.put(f"ingredients/{ingredient_id}", data=ingredient_data, tenant_id=tenant_id)
result = await self.put(f"inventory/ingredients/{ingredient_id}", data=ingredient_data, tenant_id=tenant_id)
if result:
logger.info("Updated ingredient in inventory service",
ingredient_id=ingredient_id, tenant_id=tenant_id)
@@ -147,7 +147,7 @@ class InventoryServiceClient(BaseServiceClient):
async def delete_ingredient(self, ingredient_id: UUID, tenant_id: str) -> bool:
"""Delete (deactivate) an ingredient"""
try:
result = await self.delete(f"ingredients/{ingredient_id}", tenant_id=tenant_id)
result = await self.delete(f"inventory/ingredients/{ingredient_id}", tenant_id=tenant_id)
success = result is not None
if success:
logger.info("Deleted ingredient in inventory service",
@@ -170,7 +170,7 @@ class InventoryServiceClient(BaseServiceClient):
if include_unavailable:
params["include_unavailable"] = include_unavailable
result = await self.get(f"ingredients/{ingredient_id}/stock", tenant_id=tenant_id, params=params)
result = await self.get(f"inventory/ingredients/{ingredient_id}/stock", tenant_id=tenant_id, params=params)
stock_entries = result if isinstance(result, list) else []
logger.info("Retrieved ingredient stock from inventory service",
@@ -193,7 +193,7 @@ class InventoryServiceClient(BaseServiceClient):
if ingredient_ids:
params["ingredient_ids"] = [str(id) for id in ingredient_ids]
result = await self.get("stock", tenant_id=tenant_id, params=params)
result = await self.get("inventory/stock", tenant_id=tenant_id, params=params)
stock_levels = result if isinstance(result, list) else []
logger.info("Retrieved stock levels from inventory service",
@@ -208,7 +208,7 @@ class InventoryServiceClient(BaseServiceClient):
async def get_low_stock_alerts(self, tenant_id: str) -> List[Dict[str, Any]]:
"""Get low stock alerts"""
try:
result = await self.get("alerts", tenant_id=tenant_id, params={"type": "low_stock"})
result = await self.get("inventory/alerts", tenant_id=tenant_id, params={"type": "low_stock"})
alerts = result if isinstance(result, list) else []
logger.info("Retrieved low stock alerts from inventory service",
@@ -227,7 +227,7 @@ class InventoryServiceClient(BaseServiceClient):
) -> Optional[Dict[str, Any]]:
"""Record stock consumption"""
try:
result = await self.post("stock/consume", data=consumption_data, tenant_id=tenant_id)
result = await self.post("inventory/operations/consume-stock", data=consumption_data, tenant_id=tenant_id)
if result:
logger.info("Recorded stock consumption",
tenant_id=tenant_id)
@@ -244,7 +244,7 @@ class InventoryServiceClient(BaseServiceClient):
) -> Optional[Dict[str, Any]]:
"""Record stock receipt"""
try:
result = await self.post("stock/receive", data=receipt_data, tenant_id=tenant_id)
result = await self.post("inventory/operations/receive-stock", data=receipt_data, tenant_id=tenant_id)
if result:
logger.info("Recorded stock receipt",
tenant_id=tenant_id)
@@ -271,7 +271,7 @@ class InventoryServiceClient(BaseServiceClient):
"sales_volume": sales_volume
}
result = await self.post("inventory/classify-product", data=classification_data, tenant_id=tenant_id)
result = await self.post("inventory/operations/classify-product", data=classification_data, tenant_id=tenant_id)
if result:
logger.info("Classified product",
product=product_name,
@@ -296,7 +296,7 @@ class InventoryServiceClient(BaseServiceClient):
"products": products
}
result = await self.post("inventory/classify-products-batch", data=classification_data, tenant_id=tenant_id)
result = await self.post("inventory/operations/classify-products-batch", data=classification_data, tenant_id=tenant_id)
if result:
suggestions = result.get('suggestions', [])
business_model = result.get('business_model_analysis', {}).get('model', 'unknown')
@@ -319,7 +319,7 @@ class InventoryServiceClient(BaseServiceClient):
async def get_inventory_dashboard(self, tenant_id: str) -> Optional[Dict[str, Any]]:
"""Get inventory dashboard data"""
try:
result = await self.get("dashboard", tenant_id=tenant_id)
result = await self.get("inventory/dashboard/overview", tenant_id=tenant_id)
if result:
logger.info("Retrieved inventory dashboard data", tenant_id=tenant_id)
return result
@@ -331,7 +331,7 @@ class InventoryServiceClient(BaseServiceClient):
async def get_inventory_summary(self, tenant_id: str) -> Optional[Dict[str, Any]]:
"""Get inventory summary statistics"""
try:
result = await self.get("dashboard/summary", tenant_id=tenant_id)
result = await self.get("inventory/dashboard/summary", tenant_id=tenant_id)
if result:
logger.info("Retrieved inventory summary", tenant_id=tenant_id)
return result
@@ -351,7 +351,7 @@ class InventoryServiceClient(BaseServiceClient):
) -> Optional[Dict[str, Any]]:
"""Create a product transformation (e.g., par-baked to fully baked)"""
try:
result = await self.post("transformations", data=transformation_data, tenant_id=tenant_id)
result = await self.post("inventory/transformations", data=transformation_data, tenant_id=tenant_id)
if result:
logger.info("Created product transformation",
transformation_reference=result.get('transformation_reference'),
@@ -388,7 +388,7 @@ class InventoryServiceClient(BaseServiceClient):
if notes:
params["notes"] = notes
result = await self.post("transformations/par-bake-to-fresh", params=params, tenant_id=tenant_id)
result = await self.post("inventory/transformations/par-bake-to-fresh", params=params, tenant_id=tenant_id)
if result:
logger.info("Created par-bake transformation",
transformation_id=result.get('transformation_id'),
@@ -426,7 +426,7 @@ class InventoryServiceClient(BaseServiceClient):
if days_back:
params["days_back"] = days_back
result = await self.get("transformations", tenant_id=tenant_id, params=params)
result = await self.get("inventory/transformations", tenant_id=tenant_id, params=params)
transformations = result if isinstance(result, list) else []
logger.info("Retrieved transformations from inventory service",
@@ -445,7 +445,7 @@ class InventoryServiceClient(BaseServiceClient):
) -> Optional[Dict[str, Any]]:
"""Get specific transformation by ID"""
try:
result = await self.get(f"transformations/{transformation_id}", tenant_id=tenant_id)
result = await self.get(f"inventory/transformations/{transformation_id}", tenant_id=tenant_id)
if result:
logger.info("Retrieved transformation by ID",
transformation_id=transformation_id, tenant_id=tenant_id)
@@ -463,7 +463,7 @@ class InventoryServiceClient(BaseServiceClient):
"""Get transformation summary for dashboard"""
try:
params = {"days_back": days_back}
result = await self.get("transformations/summary", tenant_id=tenant_id, params=params)
result = await self.get("inventory/dashboard/transformations-summary", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved transformation summary",
days_back=days_back, tenant_id=tenant_id)

View File

@@ -15,156 +15,156 @@ logger = structlog.get_logger()
class OrdersServiceClient(BaseServiceClient):
"""Client for communicating with the Orders Service"""
def __init__(self, config: BaseServiceSettings):
super().__init__("orders", config)
def get_service_base_path(self) -> str:
return "/api/v1"
# ================================================================
# PROCUREMENT PLANNING
# ================================================================
async def get_demand_requirements(self, tenant_id: str, date: str) -> Optional[Dict[str, Any]]:
"""Get demand requirements for production planning"""
try:
params = {"date": date}
result = await self.get("demand-requirements", tenant_id=tenant_id, params=params)
result = await self.get("orders/demand-requirements", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved demand requirements from orders service",
logger.info("Retrieved demand requirements from orders service",
date=date, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting demand requirements",
logger.error("Error getting demand requirements",
error=str(e), date=date, tenant_id=tenant_id)
return None
async def get_procurement_requirements(self, tenant_id: str, horizon: Optional[str] = None) -> Optional[Dict[str, Any]]:
"""Get procurement requirements for purchasing planning"""
try:
params = {}
if horizon:
params["horizon"] = horizon
result = await self.get("procurement-requirements", tenant_id=tenant_id, params=params)
result = await self.get("orders/procurement-requirements", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved procurement requirements from orders service",
logger.info("Retrieved procurement requirements from orders service",
horizon=horizon, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting procurement requirements",
logger.error("Error getting procurement requirements",
error=str(e), tenant_id=tenant_id)
return None
async def get_weekly_ingredient_needs(self, tenant_id: str) -> Optional[Dict[str, Any]]:
"""Get weekly ingredient ordering needs for dashboard"""
try:
result = await self.get("weekly-ingredient-needs", tenant_id=tenant_id)
result = await self.get("orders/dashboard/weekly-ingredient-needs", tenant_id=tenant_id)
if result:
logger.info("Retrieved weekly ingredient needs from orders service",
logger.info("Retrieved weekly ingredient needs from orders service",
tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting weekly ingredient needs",
logger.error("Error getting weekly ingredient needs",
error=str(e), tenant_id=tenant_id)
return None
# ================================================================
# CUSTOMER ORDERS
# ================================================================
async def get_customer_orders(self, tenant_id: str, params: Optional[Dict[str, Any]] = None) -> Optional[Dict[str, Any]]:
"""Get customer orders with optional filtering"""
try:
result = await self.get("customer-orders", tenant_id=tenant_id, params=params)
result = await self.get("orders/list", tenant_id=tenant_id, params=params)
if result:
orders_count = len(result.get('orders', [])) if isinstance(result, dict) else len(result) if isinstance(result, list) else 0
logger.info("Retrieved customer orders from orders service",
logger.info("Retrieved customer orders from orders service",
orders_count=orders_count, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting customer orders",
logger.error("Error getting customer orders",
error=str(e), tenant_id=tenant_id)
return None
async def create_customer_order(self, tenant_id: str, order_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Create a new customer order"""
try:
result = await self.post("customer-orders", data=order_data, tenant_id=tenant_id)
result = await self.post("orders/list", data=order_data, tenant_id=tenant_id)
if result:
logger.info("Created customer order",
logger.info("Created customer order",
order_id=result.get('id'), tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error creating customer order",
logger.error("Error creating customer order",
error=str(e), tenant_id=tenant_id)
return None
async def update_customer_order(self, tenant_id: str, order_id: str, order_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Update an existing customer order"""
try:
result = await self.put(f"customer-orders/{order_id}", data=order_data, tenant_id=tenant_id)
result = await self.put(f"orders/list/{order_id}", data=order_data, tenant_id=tenant_id)
if result:
logger.info("Updated customer order",
logger.info("Updated customer order",
order_id=order_id, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error updating customer order",
logger.error("Error updating customer order",
error=str(e), order_id=order_id, tenant_id=tenant_id)
return None
# ================================================================
# CENTRAL BAKERY ORDERS
# ================================================================
async def get_daily_finalized_orders(self, tenant_id: str, date: Optional[str] = None) -> Optional[Dict[str, Any]]:
"""Get daily finalized orders for central bakery"""
try:
params = {}
if date:
params["date"] = date
result = await self.get("daily-finalized-orders", tenant_id=tenant_id, params=params)
result = await self.get("orders/daily-finalized", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved daily finalized orders from orders service",
logger.info("Retrieved daily finalized orders from orders service",
date=date, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting daily finalized orders",
logger.error("Error getting daily finalized orders",
error=str(e), tenant_id=tenant_id)
return None
async def get_weekly_order_summaries(self, tenant_id: str) -> Optional[Dict[str, Any]]:
"""Get weekly order summaries for central bakery dashboard"""
try:
result = await self.get("weekly-order-summaries", tenant_id=tenant_id)
result = await self.get("orders/dashboard/weekly-summaries", tenant_id=tenant_id)
if result:
logger.info("Retrieved weekly order summaries from orders service",
logger.info("Retrieved weekly order summaries from orders service",
tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting weekly order summaries",
logger.error("Error getting weekly order summaries",
error=str(e), tenant_id=tenant_id)
return None
# ================================================================
# DASHBOARD AND ANALYTICS
# ================================================================
async def get_dashboard_summary(self, tenant_id: str) -> Optional[Dict[str, Any]]:
"""Get orders dashboard summary data"""
try:
result = await self.get("dashboard-summary", tenant_id=tenant_id)
result = await self.get("orders/dashboard/summary", tenant_id=tenant_id)
if result:
logger.info("Retrieved orders dashboard summary",
logger.info("Retrieved orders dashboard summary",
tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting orders dashboard summary",
logger.error("Error getting orders dashboard summary",
error=str(e), tenant_id=tenant_id)
return None
async def get_order_trends(self, tenant_id: str, start_date: str, end_date: str) -> Optional[Dict[str, Any]]:
"""Get order trends analysis"""
try:
@@ -172,50 +172,50 @@ class OrdersServiceClient(BaseServiceClient):
"start_date": start_date,
"end_date": end_date
}
result = await self.get("order-trends", tenant_id=tenant_id, params=params)
result = await self.get("orders/analytics/trends", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved order trends from orders service",
logger.info("Retrieved order trends from orders service",
start_date=start_date, end_date=end_date, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting order trends",
logger.error("Error getting order trends",
error=str(e), tenant_id=tenant_id)
return None
# ================================================================
# ALERTS AND NOTIFICATIONS
# ================================================================
async def get_central_bakery_alerts(self, tenant_id: str) -> Optional[List[Dict[str, Any]]]:
"""Get central bakery specific alerts"""
try:
result = await self.get("central-bakery-alerts", tenant_id=tenant_id)
result = await self.get("orders/alerts", tenant_id=tenant_id)
alerts = result.get('alerts', []) if result else []
logger.info("Retrieved central bakery alerts from orders service",
logger.info("Retrieved central bakery alerts from orders service",
alerts_count=len(alerts), tenant_id=tenant_id)
return alerts
except Exception as e:
logger.error("Error getting central bakery alerts",
logger.error("Error getting central bakery alerts",
error=str(e), tenant_id=tenant_id)
return []
async def acknowledge_alert(self, tenant_id: str, alert_id: str) -> Optional[Dict[str, Any]]:
"""Acknowledge an order-related alert"""
try:
result = await self.post(f"alerts/{alert_id}/acknowledge", data={}, tenant_id=tenant_id)
result = await self.post(f"orders/alerts/{alert_id}/acknowledge", data={}, tenant_id=tenant_id)
if result:
logger.info("Acknowledged order alert",
logger.info("Acknowledged order alert",
alert_id=alert_id, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error acknowledging order alert",
logger.error("Error acknowledging order alert",
error=str(e), alert_id=alert_id, tenant_id=tenant_id)
return None
# ================================================================
# UTILITY METHODS
# ================================================================
async def download_orders_pdf(self, tenant_id: str, order_ids: List[str], format_type: str = "supplier_communication") -> Optional[bytes]:
"""Download orders as PDF for supplier communication"""
try:
@@ -225,16 +225,16 @@ class OrdersServiceClient(BaseServiceClient):
"include_delivery_schedule": True
}
# Note: This would need special handling for binary data
result = await self.post("download/pdf", data=data, tenant_id=tenant_id)
result = await self.post("orders/operations/download-pdf", data=data, tenant_id=tenant_id)
if result:
logger.info("Generated orders PDF",
logger.info("Generated orders PDF",
orders_count=len(order_ids), tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error generating orders PDF",
logger.error("Error generating orders PDF",
error=str(e), tenant_id=tenant_id)
return None
async def health_check(self) -> bool:
"""Check if orders service is healthy"""
try:
@@ -248,4 +248,4 @@ class OrdersServiceClient(BaseServiceClient):
# Factory function for dependency injection
def create_orders_client(config: BaseServiceSettings) -> OrdersServiceClient:
"""Create orders service client instance"""
return OrdersServiceClient(config)
return OrdersServiceClient(config)

View File

@@ -15,51 +15,51 @@ logger = structlog.get_logger()
class ProductionServiceClient(BaseServiceClient):
"""Client for communicating with the Production Service"""
def __init__(self, config: BaseServiceSettings):
super().__init__("production", config)
def get_service_base_path(self) -> str:
return "/api/v1"
# ================================================================
# PRODUCTION PLANNING
# ================================================================
async def get_production_requirements(self, tenant_id: str, date: Optional[str] = None) -> Optional[Dict[str, Any]]:
"""Get production requirements for procurement planning"""
try:
params = {}
if date:
params["date"] = date
result = await self.get("requirements", tenant_id=tenant_id, params=params)
result = await self.get("production/requirements", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved production requirements from production service",
logger.info("Retrieved production requirements from production service",
date=date, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting production requirements",
logger.error("Error getting production requirements",
error=str(e), tenant_id=tenant_id)
return None
async def get_daily_requirements(self, tenant_id: str, date: Optional[str] = None) -> Optional[Dict[str, Any]]:
"""Get daily production requirements"""
try:
params = {}
if date:
params["date"] = date
result = await self.get("daily-requirements", tenant_id=tenant_id, params=params)
result = await self.get("production/daily-requirements", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved daily production requirements from production service",
logger.info("Retrieved daily production requirements from production service",
date=date, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting daily production requirements",
logger.error("Error getting daily production requirements",
error=str(e), tenant_id=tenant_id)
return None
async def get_production_schedule(self, tenant_id: str, start_date: Optional[str] = None, end_date: Optional[str] = None) -> Optional[Dict[str, Any]]:
"""Get production schedule for a date range"""
try:
@@ -68,134 +68,134 @@ class ProductionServiceClient(BaseServiceClient):
params["start_date"] = start_date
if end_date:
params["end_date"] = end_date
result = await self.get("schedule", tenant_id=tenant_id, params=params)
result = await self.get("production/schedules", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved production schedule from production service",
logger.info("Retrieved production schedule from production service",
start_date=start_date, end_date=end_date, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting production schedule",
logger.error("Error getting production schedule",
error=str(e), tenant_id=tenant_id)
return None
# ================================================================
# BATCH MANAGEMENT
# ================================================================
async def get_active_batches(self, tenant_id: str) -> Optional[List[Dict[str, Any]]]:
"""Get currently active production batches"""
try:
result = await self.get("batches/active", tenant_id=tenant_id)
result = await self.get("production/batches/active", tenant_id=tenant_id)
batches = result.get('batches', []) if result else []
logger.info("Retrieved active production batches from production service",
logger.info("Retrieved active production batches from production service",
batches_count=len(batches), tenant_id=tenant_id)
return batches
except Exception as e:
logger.error("Error getting active production batches",
logger.error("Error getting active production batches",
error=str(e), tenant_id=tenant_id)
return []
async def create_production_batch(self, tenant_id: str, batch_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Create a new production batch"""
try:
result = await self.post("batches", data=batch_data, tenant_id=tenant_id)
result = await self.post("production/batches", data=batch_data, tenant_id=tenant_id)
if result:
logger.info("Created production batch",
batch_id=result.get('id'),
logger.info("Created production batch",
batch_id=result.get('id'),
product_id=batch_data.get('product_id'),
tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error creating production batch",
logger.error("Error creating production batch",
error=str(e), tenant_id=tenant_id)
return None
async def update_batch_status(self, tenant_id: str, batch_id: str, status: str, actual_quantity: Optional[float] = None) -> Optional[Dict[str, Any]]:
"""Update production batch status"""
try:
data = {"status": status}
if actual_quantity is not None:
data["actual_quantity"] = actual_quantity
result = await self.put(f"batches/{batch_id}/status", data=data, tenant_id=tenant_id)
result = await self.put(f"production/batches/{batch_id}/status", data=data, tenant_id=tenant_id)
if result:
logger.info("Updated production batch status",
logger.info("Updated production batch status",
batch_id=batch_id, status=status, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error updating production batch status",
logger.error("Error updating production batch status",
error=str(e), batch_id=batch_id, tenant_id=tenant_id)
return None
async def get_batch_details(self, tenant_id: str, batch_id: str) -> Optional[Dict[str, Any]]:
"""Get detailed information about a production batch"""
try:
result = await self.get(f"batches/{batch_id}", tenant_id=tenant_id)
result = await self.get(f"production/batches/{batch_id}", tenant_id=tenant_id)
if result:
logger.info("Retrieved production batch details",
logger.info("Retrieved production batch details",
batch_id=batch_id, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting production batch details",
logger.error("Error getting production batch details",
error=str(e), batch_id=batch_id, tenant_id=tenant_id)
return None
# ================================================================
# CAPACITY MANAGEMENT
# ================================================================
async def get_capacity_status(self, tenant_id: str, date: Optional[str] = None) -> Optional[Dict[str, Any]]:
"""Get production capacity status for a specific date"""
try:
params = {}
if date:
params["date"] = date
result = await self.get("capacity/status", tenant_id=tenant_id, params=params)
result = await self.get("production/capacity/status", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved production capacity status",
logger.info("Retrieved production capacity status",
date=date, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting production capacity status",
logger.error("Error getting production capacity status",
error=str(e), tenant_id=tenant_id)
return None
async def check_capacity_availability(self, tenant_id: str, requirements: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
"""Check if production capacity is available for requirements"""
try:
result = await self.post("capacity/check-availability",
{"requirements": requirements},
result = await self.post("production/capacity/check-availability",
{"requirements": requirements},
tenant_id=tenant_id)
if result:
logger.info("Checked production capacity availability",
logger.info("Checked production capacity availability",
requirements_count=len(requirements), tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error checking production capacity availability",
logger.error("Error checking production capacity availability",
error=str(e), tenant_id=tenant_id)
return None
# ================================================================
# QUALITY CONTROL
# ================================================================
async def record_quality_check(self, tenant_id: str, batch_id: str, quality_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Record quality control results for a batch"""
try:
result = await self.post(f"batches/{batch_id}/quality-check",
data=quality_data,
result = await self.post(f"production/batches/{batch_id}/quality-check",
data=quality_data,
tenant_id=tenant_id)
if result:
logger.info("Recorded quality check for production batch",
logger.info("Recorded quality check for production batch",
batch_id=batch_id, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error recording quality check",
logger.error("Error recording quality check",
error=str(e), batch_id=batch_id, tenant_id=tenant_id)
return None
async def get_yield_metrics(self, tenant_id: str, start_date: str, end_date: str) -> Optional[Dict[str, Any]]:
"""Get production yield metrics for analysis"""
try:
@@ -203,81 +203,81 @@ class ProductionServiceClient(BaseServiceClient):
"start_date": start_date,
"end_date": end_date
}
result = await self.get("metrics/yield", tenant_id=tenant_id, params=params)
result = await self.get("production/analytics/yield-metrics", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved production yield metrics",
logger.info("Retrieved production yield metrics",
start_date=start_date, end_date=end_date, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting production yield metrics",
logger.error("Error getting production yield metrics",
error=str(e), tenant_id=tenant_id)
return None
# ================================================================
# DASHBOARD AND ANALYTICS
# ================================================================
async def get_dashboard_summary(self, tenant_id: str) -> Optional[Dict[str, Any]]:
"""Get production dashboard summary data"""
try:
result = await self.get("dashboard-summary", tenant_id=tenant_id)
result = await self.get("production/dashboard/summary", tenant_id=tenant_id)
if result:
logger.info("Retrieved production dashboard summary",
logger.info("Retrieved production dashboard summary",
tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting production dashboard summary",
logger.error("Error getting production dashboard summary",
error=str(e), tenant_id=tenant_id)
return None
async def get_efficiency_metrics(self, tenant_id: str, period: str = "last_30_days") -> Optional[Dict[str, Any]]:
"""Get production efficiency metrics"""
try:
params = {"period": period}
result = await self.get("metrics/efficiency", tenant_id=tenant_id, params=params)
result = await self.get("production/analytics/efficiency", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved production efficiency metrics",
logger.info("Retrieved production efficiency metrics",
period=period, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error getting production efficiency metrics",
logger.error("Error getting production efficiency metrics",
error=str(e), tenant_id=tenant_id)
return None
# ================================================================
# ALERTS AND NOTIFICATIONS
# ================================================================
async def get_production_alerts(self, tenant_id: str) -> Optional[List[Dict[str, Any]]]:
"""Get production-related alerts"""
try:
result = await self.get("alerts", tenant_id=tenant_id)
result = await self.get("production/alerts", tenant_id=tenant_id)
alerts = result.get('alerts', []) if result else []
logger.info("Retrieved production alerts",
logger.info("Retrieved production alerts",
alerts_count=len(alerts), tenant_id=tenant_id)
return alerts
except Exception as e:
logger.error("Error getting production alerts",
logger.error("Error getting production alerts",
error=str(e), tenant_id=tenant_id)
return []
async def acknowledge_alert(self, tenant_id: str, alert_id: str) -> Optional[Dict[str, Any]]:
"""Acknowledge a production-related alert"""
try:
result = await self.post(f"alerts/{alert_id}/acknowledge", data={}, tenant_id=tenant_id)
result = await self.post(f"production/alerts/{alert_id}/acknowledge", data={}, tenant_id=tenant_id)
if result:
logger.info("Acknowledged production alert",
logger.info("Acknowledged production alert",
alert_id=alert_id, tenant_id=tenant_id)
return result
except Exception as e:
logger.error("Error acknowledging production alert",
logger.error("Error acknowledging production alert",
error=str(e), alert_id=alert_id, tenant_id=tenant_id)
return None
# ================================================================
# UTILITY METHODS
# ================================================================
async def health_check(self) -> bool:
"""Check if production service is healthy"""
try:
@@ -291,4 +291,4 @@ class ProductionServiceClient(BaseServiceClient):
# Factory function for dependency injection
def create_production_client(config: BaseServiceSettings) -> ProductionServiceClient:
"""Create production service client instance"""
return ProductionServiceClient(config)
return ProductionServiceClient(config)

View File

@@ -29,7 +29,7 @@ class RecipesServiceClient(BaseServiceClient):
async def get_recipe_by_id(self, tenant_id: str, recipe_id: str) -> Optional[Dict[str, Any]]:
"""Get recipe details by ID"""
try:
result = await self.get(f"recipes/{recipe_id}", tenant_id=tenant_id)
result = await self.get(f"recipes/recipes/{recipe_id}", tenant_id=tenant_id)
if result:
logger.info("Retrieved recipe details from recipes service",
recipe_id=recipe_id, tenant_id=tenant_id)
@@ -43,7 +43,7 @@ class RecipesServiceClient(BaseServiceClient):
"""Get recipes for multiple products"""
try:
params = {"product_ids": ",".join(product_ids)}
result = await self.get("recipes/by-products", tenant_id=tenant_id, params=params)
result = await self.get("recipes/recipes/by-products", tenant_id=tenant_id, params=params)
recipes = result.get('recipes', []) if result else []
logger.info("Retrieved recipes by product IDs from recipes service",
product_ids_count=len(product_ids),
@@ -82,7 +82,7 @@ class RecipesServiceClient(BaseServiceClient):
if recipe_ids:
params["recipe_ids"] = ",".join(recipe_ids)
result = await self.get("requirements", tenant_id=tenant_id, params=params)
result = await self.get("recipes/requirements", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved recipe requirements from recipes service",
recipe_ids_count=len(recipe_ids) if recipe_ids else 0,
@@ -100,7 +100,7 @@ class RecipesServiceClient(BaseServiceClient):
if product_ids:
params["product_ids"] = ",".join(product_ids)
result = await self.get("ingredient-requirements", tenant_id=tenant_id, params=params)
result = await self.get("recipes/ingredient-requirements", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved ingredient requirements from recipes service",
product_ids_count=len(product_ids) if product_ids else 0,
@@ -118,7 +118,7 @@ class RecipesServiceClient(BaseServiceClient):
"recipe_id": recipe_id,
"quantity": quantity
}
result = await self.post("calculate-ingredients", data=data, tenant_id=tenant_id)
result = await self.post("recipes/operations/calculate-ingredients", data=data, tenant_id=tenant_id)
if result:
logger.info("Calculated ingredient quantities from recipes service",
recipe_id=recipe_id, quantity=quantity, tenant_id=tenant_id)
@@ -132,7 +132,7 @@ class RecipesServiceClient(BaseServiceClient):
"""Calculate total ingredient requirements for multiple production batches"""
try:
data = {"production_requests": production_requests}
result = await self.post("calculate-batch-ingredients", data=data, tenant_id=tenant_id)
result = await self.post("recipes/operations/calculate-batch-ingredients", data=data, tenant_id=tenant_id)
if result:
logger.info("Calculated batch ingredient requirements from recipes service",
batches_count=len(production_requests), tenant_id=tenant_id)
@@ -149,7 +149,7 @@ class RecipesServiceClient(BaseServiceClient):
async def get_production_instructions(self, tenant_id: str, recipe_id: str) -> Optional[Dict[str, Any]]:
"""Get detailed production instructions for a recipe"""
try:
result = await self.get(f"recipes/{recipe_id}/production-instructions", tenant_id=tenant_id)
result = await self.get(f"recipes/recipes/{recipe_id}/production-instructions", tenant_id=tenant_id)
if result:
logger.info("Retrieved production instructions from recipes service",
recipe_id=recipe_id, tenant_id=tenant_id)
@@ -162,7 +162,7 @@ class RecipesServiceClient(BaseServiceClient):
async def get_recipe_yield_info(self, tenant_id: str, recipe_id: str) -> Optional[Dict[str, Any]]:
"""Get yield information for a recipe"""
try:
result = await self.get(f"recipes/{recipe_id}/yield", tenant_id=tenant_id)
result = await self.get(f"recipes/recipes/{recipe_id}/yield", tenant_id=tenant_id)
if result:
logger.info("Retrieved recipe yield info from recipes service",
recipe_id=recipe_id, tenant_id=tenant_id)
@@ -179,7 +179,7 @@ class RecipesServiceClient(BaseServiceClient):
"recipe_id": recipe_id,
"quantity": quantity
}
result = await self.post("validate-feasibility", data=data, tenant_id=tenant_id)
result = await self.post("recipes/operations/validate-feasibility", data=data, tenant_id=tenant_id)
if result:
logger.info("Validated recipe feasibility from recipes service",
recipe_id=recipe_id, quantity=quantity, tenant_id=tenant_id)
@@ -196,7 +196,7 @@ class RecipesServiceClient(BaseServiceClient):
async def get_recipe_cost_analysis(self, tenant_id: str, recipe_id: str) -> Optional[Dict[str, Any]]:
"""Get cost analysis for a recipe"""
try:
result = await self.get(f"recipes/{recipe_id}/cost-analysis", tenant_id=tenant_id)
result = await self.get(f"recipes/recipes/{recipe_id}/cost-analysis", tenant_id=tenant_id)
if result:
logger.info("Retrieved recipe cost analysis from recipes service",
recipe_id=recipe_id, tenant_id=tenant_id)
@@ -210,7 +210,7 @@ class RecipesServiceClient(BaseServiceClient):
"""Optimize production batch to minimize waste and cost"""
try:
data = {"requirements": requirements}
result = await self.post("optimize-batch", data=data, tenant_id=tenant_id)
result = await self.post("recipes/operations/optimize-batch", data=data, tenant_id=tenant_id)
if result:
logger.info("Optimized production batch from recipes service",
requirements_count=len(requirements), tenant_id=tenant_id)
@@ -227,7 +227,7 @@ class RecipesServiceClient(BaseServiceClient):
async def get_dashboard_summary(self, tenant_id: str) -> Optional[Dict[str, Any]]:
"""Get recipes dashboard summary data"""
try:
result = await self.get("dashboard-summary", tenant_id=tenant_id)
result = await self.get("recipes/dashboard/summary", tenant_id=tenant_id)
if result:
logger.info("Retrieved recipes dashboard summary",
tenant_id=tenant_id)
@@ -241,7 +241,7 @@ class RecipesServiceClient(BaseServiceClient):
"""Get most popular recipes based on production frequency"""
try:
params = {"period": period}
result = await self.get("popular-recipes", tenant_id=tenant_id, params=params)
result = await self.get("recipes/analytics/popular-recipes", tenant_id=tenant_id, params=params)
recipes = result.get('recipes', []) if result else []
logger.info("Retrieved popular recipes from recipes service",
period=period, recipes_count=len(recipes), tenant_id=tenant_id)

View File

@@ -44,7 +44,7 @@ class SalesServiceClient(BaseServiceClient):
if product_id:
params["product_id"] = product_id
result = await self.get("sales", tenant_id=tenant_id, params=params)
result = await self.get("sales/sales", tenant_id=tenant_id, params=params)
return result.get("sales", []) if result else None
async def get_all_sales_data(
@@ -72,7 +72,7 @@ class SalesServiceClient(BaseServiceClient):
# Use the inherited paginated request method
try:
all_records = await self.get_paginated(
"sales",
"sales/sales",
tenant_id=tenant_id,
params=params,
page_size=page_size,
@@ -95,7 +95,7 @@ class SalesServiceClient(BaseServiceClient):
) -> Optional[Dict[str, Any]]:
"""Upload sales data"""
data = {"sales": sales_data}
return await self.post("sales", data=data, tenant_id=tenant_id)
return await self.post("sales/sales", data=data, tenant_id=tenant_id)
# ================================================================
# PRODUCTS
@@ -103,12 +103,12 @@ class SalesServiceClient(BaseServiceClient):
async def get_products(self, tenant_id: str) -> Optional[List[Dict[str, Any]]]:
"""Get all products for a tenant"""
result = await self.get("products", tenant_id=tenant_id)
result = await self.get("sales/products", tenant_id=tenant_id)
return result.get("products", []) if result else None
async def get_product(self, tenant_id: str, product_id: str) -> Optional[Dict[str, Any]]:
"""Get a specific product"""
return await self.get(f"products/{product_id}", tenant_id=tenant_id)
return await self.get(f"sales/products/{product_id}", tenant_id=tenant_id)
async def create_product(
self,
@@ -125,8 +125,8 @@ class SalesServiceClient(BaseServiceClient):
"price": price,
**kwargs
}
return await self.post("products", data=data, tenant_id=tenant_id)
return await self.post("sales/products", data=data, tenant_id=tenant_id)
async def update_product(
self,
tenant_id: str,
@@ -134,7 +134,7 @@ class SalesServiceClient(BaseServiceClient):
**updates
) -> Optional[Dict[str, Any]]:
"""Update a product"""
return await self.put(f"products/{product_id}", data=updates, tenant_id=tenant_id)
return await self.put(f"sales/products/{product_id}", data=updates, tenant_id=tenant_id)
# ================================================================
# DATA IMPORT
@@ -153,4 +153,4 @@ class SalesServiceClient(BaseServiceClient):
"format": file_format,
"filename": filename
}
return await self.post("import", data=data, tenant_id=tenant_id)
return await self.post("sales/operations/import", data=data, tenant_id=tenant_id)

View File

@@ -28,7 +28,7 @@ class SuppliersServiceClient(BaseServiceClient):
async def get_supplier_by_id(self, tenant_id: str, supplier_id: str) -> Optional[Dict[str, Any]]:
"""Get supplier details by ID"""
try:
result = await self.get(f"suppliers/{supplier_id}", tenant_id=tenant_id)
result = await self.get(f"suppliers/list/{supplier_id}", tenant_id=tenant_id)
if result:
logger.info("Retrieved supplier details from suppliers service",
supplier_id=supplier_id, tenant_id=tenant_id)
@@ -45,7 +45,7 @@ class SuppliersServiceClient(BaseServiceClient):
if is_active is not None:
params["is_active"] = is_active
result = await self.get_paginated("suppliers", tenant_id=tenant_id, params=params)
result = await self.get_paginated("suppliers/list", tenant_id=tenant_id, params=params)
logger.info("Retrieved all suppliers from suppliers service",
suppliers_count=len(result), tenant_id=tenant_id)
return result
@@ -63,7 +63,7 @@ class SuppliersServiceClient(BaseServiceClient):
if category:
params["category"] = category
result = await self.get("suppliers/search", tenant_id=tenant_id, params=params)
result = await self.get("suppliers/list/search", tenant_id=tenant_id, params=params)
suppliers = result.get('suppliers', []) if result else []
logger.info("Searched suppliers from suppliers service",
search_term=search, suppliers_count=len(suppliers), tenant_id=tenant_id)
@@ -81,7 +81,7 @@ class SuppliersServiceClient(BaseServiceClient):
"""Get supplier recommendations for procurement"""
try:
params = {"ingredient_id": ingredient_id}
result = await self.get("recommendations", tenant_id=tenant_id, params=params)
result = await self.get("suppliers/recommendations", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved supplier recommendations from suppliers service",
ingredient_id=ingredient_id, tenant_id=tenant_id)
@@ -98,7 +98,7 @@ class SuppliersServiceClient(BaseServiceClient):
"ingredient_id": ingredient_id,
"criteria": criteria or {}
}
result = await self.post("find-best-supplier", data=data, tenant_id=tenant_id)
result = await self.post("suppliers/operations/find-best-supplier", data=data, tenant_id=tenant_id)
if result:
logger.info("Retrieved best supplier from suppliers service",
ingredient_id=ingredient_id, tenant_id=tenant_id)
@@ -115,7 +115,7 @@ class SuppliersServiceClient(BaseServiceClient):
async def create_purchase_order(self, tenant_id: str, order_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Create a new purchase order"""
try:
result = await self.post("purchase-orders", data=order_data, tenant_id=tenant_id)
result = await self.post("suppliers/purchase-orders", data=order_data, tenant_id=tenant_id)
if result:
logger.info("Created purchase order",
order_id=result.get('id'),
@@ -136,7 +136,7 @@ class SuppliersServiceClient(BaseServiceClient):
if supplier_id:
params["supplier_id"] = supplier_id
result = await self.get("purchase-orders", tenant_id=tenant_id, params=params)
result = await self.get("suppliers/purchase-orders", tenant_id=tenant_id, params=params)
orders = result.get('orders', []) if result else []
logger.info("Retrieved purchase orders from suppliers service",
orders_count=len(orders), tenant_id=tenant_id)
@@ -150,7 +150,7 @@ class SuppliersServiceClient(BaseServiceClient):
"""Update purchase order status"""
try:
data = {"status": status}
result = await self.put(f"purchase-orders/{order_id}/status", data=data, tenant_id=tenant_id)
result = await self.put(f"suppliers/purchase-orders/{order_id}/status", data=data, tenant_id=tenant_id)
if result:
logger.info("Updated purchase order status",
order_id=order_id, status=status, tenant_id=tenant_id)
@@ -173,7 +173,7 @@ class SuppliersServiceClient(BaseServiceClient):
if date:
params["date"] = date
result = await self.get("deliveries", tenant_id=tenant_id, params=params)
result = await self.get("suppliers/deliveries", tenant_id=tenant_id, params=params)
deliveries = result.get('deliveries', []) if result else []
logger.info("Retrieved deliveries from suppliers service",
deliveries_count=len(deliveries), tenant_id=tenant_id)
@@ -190,7 +190,7 @@ class SuppliersServiceClient(BaseServiceClient):
if notes:
data["notes"] = notes
result = await self.put(f"deliveries/{delivery_id}/status", data=data, tenant_id=tenant_id)
result = await self.put(f"suppliers/deliveries/{delivery_id}/status", data=data, tenant_id=tenant_id)
if result:
logger.info("Updated delivery status",
delivery_id=delivery_id, status=status, tenant_id=tenant_id)
@@ -203,7 +203,7 @@ class SuppliersServiceClient(BaseServiceClient):
async def get_supplier_order_summaries(self, tenant_id: str) -> Optional[Dict[str, Any]]:
"""Get supplier order summaries for central bakery dashboard"""
try:
result = await self.get("supplier-order-summaries", tenant_id=tenant_id)
result = await self.get("suppliers/dashboard/order-summaries", tenant_id=tenant_id)
if result:
logger.info("Retrieved supplier order summaries from suppliers service",
tenant_id=tenant_id)
@@ -221,7 +221,7 @@ class SuppliersServiceClient(BaseServiceClient):
"""Get supplier performance metrics"""
try:
params = {"period": period}
result = await self.get(f"suppliers/{supplier_id}/performance", tenant_id=tenant_id, params=params)
result = await self.get(f"suppliers/analytics/performance/{supplier_id}", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved supplier performance from suppliers service",
supplier_id=supplier_id, period=period, tenant_id=tenant_id)
@@ -234,7 +234,7 @@ class SuppliersServiceClient(BaseServiceClient):
async def get_performance_alerts(self, tenant_id: str) -> Optional[List[Dict[str, Any]]]:
"""Get supplier performance alerts"""
try:
result = await self.get("performance-alerts", tenant_id=tenant_id)
result = await self.get("suppliers/alerts/performance", tenant_id=tenant_id)
alerts = result.get('alerts', []) if result else []
logger.info("Retrieved supplier performance alerts",
alerts_count=len(alerts), tenant_id=tenant_id)
@@ -264,7 +264,7 @@ class SuppliersServiceClient(BaseServiceClient):
async def get_dashboard_summary(self, tenant_id: str) -> Optional[Dict[str, Any]]:
"""Get suppliers dashboard summary data"""
try:
result = await self.get("dashboard-summary", tenant_id=tenant_id)
result = await self.get("suppliers/dashboard/summary", tenant_id=tenant_id)
if result:
logger.info("Retrieved suppliers dashboard summary",
tenant_id=tenant_id)
@@ -281,7 +281,7 @@ class SuppliersServiceClient(BaseServiceClient):
"start_date": start_date,
"end_date": end_date
}
result = await self.get("cost-analysis", tenant_id=tenant_id, params=params)
result = await self.get("suppliers/analytics/cost-analysis", tenant_id=tenant_id, params=params)
if result:
logger.info("Retrieved supplier cost analysis",
start_date=start_date, end_date=end_date, tenant_id=tenant_id)
@@ -294,7 +294,7 @@ class SuppliersServiceClient(BaseServiceClient):
async def get_supplier_reliability_metrics(self, tenant_id: str) -> Optional[Dict[str, Any]]:
"""Get supplier reliability and quality metrics"""
try:
result = await self.get("reliability-metrics", tenant_id=tenant_id)
result = await self.get("suppliers/analytics/reliability-metrics", tenant_id=tenant_id)
if result:
logger.info("Retrieved supplier reliability metrics",
tenant_id=tenant_id)
@@ -311,7 +311,7 @@ class SuppliersServiceClient(BaseServiceClient):
async def acknowledge_alert(self, tenant_id: str, alert_id: str) -> Optional[Dict[str, Any]]:
"""Acknowledge a supplier-related alert"""
try:
result = await self.post(f"alerts/{alert_id}/acknowledge", data={}, tenant_id=tenant_id)
result = await self.post(f"suppliers/alerts/{alert_id}/acknowledge", data={}, tenant_id=tenant_id)
if result:
logger.info("Acknowledged supplier alert",
alert_id=alert_id, tenant_id=tenant_id)

View File

@@ -37,12 +37,12 @@ class TrainingServiceClient(BaseServiceClient):
"min_data_points": min_data_points,
**kwargs
}
return await self.post("jobs", data=data, tenant_id=tenant_id)
return await self.post("training/jobs", data=data, tenant_id=tenant_id)
async def get_training_job(self, tenant_id: str, job_id: str) -> Optional[Dict[str, Any]]:
"""Get training job details"""
return await self.get(f"jobs/{job_id}", tenant_id=tenant_id)
return await self.get(f"training/jobs/{job_id}/status", tenant_id=tenant_id)
async def list_training_jobs(
self,
tenant_id: str,
@@ -53,13 +53,13 @@ class TrainingServiceClient(BaseServiceClient):
params = {"limit": limit}
if status:
params["status"] = status
result = await self.get("jobs", tenant_id=tenant_id, params=params)
result = await self.get("training/jobs", tenant_id=tenant_id, params=params)
return result.get("jobs", []) if result else None
async def cancel_training_job(self, tenant_id: str, job_id: str) -> Optional[Dict[str, Any]]:
"""Cancel a training job"""
return await self.delete(f"jobs/{job_id}", tenant_id=tenant_id)
return await self.delete(f"training/jobs/{job_id}", tenant_id=tenant_id)
# ================================================================
# MODELS
@@ -67,7 +67,7 @@ class TrainingServiceClient(BaseServiceClient):
async def get_model(self, tenant_id: str, model_id: str) -> Optional[Dict[str, Any]]:
"""Get model details"""
return await self.get(f"models/{model_id}", tenant_id=tenant_id)
return await self.get(f"training/models/{model_id}", tenant_id=tenant_id)
async def list_models(
self,
@@ -83,7 +83,7 @@ class TrainingServiceClient(BaseServiceClient):
if model_type:
params["model_type"] = model_type
result = await self.get("models", tenant_id=tenant_id, params=params)
result = await self.get("training/models", tenant_id=tenant_id, params=params)
return result.get("models", []) if result else None
async def get_active_model_for_product(
@@ -95,16 +95,16 @@ class TrainingServiceClient(BaseServiceClient):
Get the active model for a specific product by inventory product ID
This is the preferred method since models are stored per product.
"""
result = await self.get(f"models/{inventory_product_id}/active", tenant_id=tenant_id)
result = await self.get(f"training/models/{inventory_product_id}/active", tenant_id=tenant_id)
return result
async def deploy_model(self, tenant_id: str, model_id: str) -> Optional[Dict[str, Any]]:
"""Deploy a trained model"""
return await self.post(f"models/{model_id}/deploy", data={}, tenant_id=tenant_id)
return await self.post(f"training/models/{model_id}/deploy", data={}, tenant_id=tenant_id)
async def delete_model(self, tenant_id: str, model_id: str) -> Optional[Dict[str, Any]]:
"""Delete a model"""
return await self.delete(f"models/{model_id}", tenant_id=tenant_id)
return await self.delete(f"training/models/{model_id}", tenant_id=tenant_id)
# ================================================================
# MODEL METRICS & PERFORMANCE
@@ -112,7 +112,7 @@ class TrainingServiceClient(BaseServiceClient):
async def get_model_metrics(self, tenant_id: str, model_id: str) -> Optional[Dict[str, Any]]:
"""Get model performance metrics"""
return await self.get(f"models/{model_id}/metrics", tenant_id=tenant_id)
return await self.get(f"training/models/{model_id}/metrics", tenant_id=tenant_id)
async def get_model_predictions(
self,
@@ -128,5 +128,5 @@ class TrainingServiceClient(BaseServiceClient):
if end_date:
params["end_date"] = end_date
result = await self.get(f"models/{model_id}/predictions", tenant_id=tenant_id, params=params)
result = await self.get(f"training/models/{model_id}/predictions", tenant_id=tenant_id, params=params)
return result.get("predictions", []) if result else None