# shared/clients/orders_client.py """ Orders Service Client for Inter-Service Communication Provides access to orders and procurement planning from other services """ import structlog from typing import Dict, Any, Optional, List from uuid import UUID from shared.clients.base_service_client import BaseServiceClient from shared.config.base import BaseServiceSettings 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("orders/demand-requirements", tenant_id=tenant_id, params=params) if result: 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", 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("orders/procurement-requirements", tenant_id=tenant_id, params=params) if result: 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", 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("orders/dashboard/weekly-ingredient-needs", tenant_id=tenant_id) if result: 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", 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("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", orders_count=orders_count, tenant_id=tenant_id) return result except Exception as e: 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("orders/list", data=order_data, tenant_id=tenant_id) if result: 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", 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"orders/list/{order_id}", data=order_data, tenant_id=tenant_id) if result: 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", 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("orders/daily-finalized", tenant_id=tenant_id, params=params) if result: 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", 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("orders/dashboard/weekly-summaries", tenant_id=tenant_id) if result: 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", 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("orders/dashboard/summary", tenant_id=tenant_id) if result: logger.info("Retrieved orders dashboard summary", tenant_id=tenant_id) return result except Exception as e: 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: params = { "start_date": start_date, "end_date": end_date } result = await self.get("orders/analytics/trends", tenant_id=tenant_id, params=params) if result: 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", 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("orders/alerts", tenant_id=tenant_id) alerts = result.get('alerts', []) if result else [] 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", 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"orders/alerts/{alert_id}/acknowledge", data={}, tenant_id=tenant_id) if result: 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", 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: data = { "order_ids": order_ids, "format": format_type, "include_delivery_schedule": True } # Note: This would need special handling for binary data result = await self.post("orders/operations/download-pdf", data=data, tenant_id=tenant_id) if result: 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", error=str(e), tenant_id=tenant_id) return None async def health_check(self) -> bool: """Check if orders service is healthy""" try: result = await self.get("../health") # Health endpoint is not tenant-scoped return result is not None except Exception as e: logger.error("Orders service health check failed", error=str(e)) return False # Factory function for dependency injection def create_orders_client(config: BaseServiceSettings) -> OrdersServiceClient: """Create orders service client instance""" return OrdersServiceClient(config)