# ================================================================ # services/procurement/app/core/config.py # ================================================================ """ Procurement Service Configuration """ import os from decimal import Decimal from pydantic import Field from shared.config.base import BaseServiceSettings class ProcurementSettings(BaseServiceSettings): """Procurement service specific settings""" # Service Identity APP_NAME: str = "Procurement Service" SERVICE_NAME: str = "procurement-service" VERSION: str = "1.0.0" DESCRIPTION: str = "Procurement planning, purchase order management, and supplier integration" # Database configuration (secure approach - build from components) @property def DATABASE_URL(self) -> str: """Build database URL from secure components""" # Try complete URL first (for backward compatibility) complete_url = os.getenv("PROCUREMENT_DATABASE_URL") if complete_url: return complete_url # Build from components (secure approach) user = os.getenv("PROCUREMENT_DB_USER", "procurement_user") password = os.getenv("PROCUREMENT_DB_PASSWORD", "procurement_pass123") host = os.getenv("PROCUREMENT_DB_HOST", "localhost") port = os.getenv("PROCUREMENT_DB_PORT", "5432") name = os.getenv("PROCUREMENT_DB_NAME", "procurement_db") return f"postgresql+asyncpg://{user}:{password}@{host}:{port}/{name}" # Procurement Planning PROCUREMENT_PLANNING_ENABLED: bool = os.getenv("PROCUREMENT_PLANNING_ENABLED", "true").lower() == "true" PROCUREMENT_LEAD_TIME_DAYS: int = int(os.getenv("PROCUREMENT_LEAD_TIME_DAYS", "3")) DEMAND_FORECAST_DAYS: int = int(os.getenv("DEMAND_FORECAST_DAYS", "14")) SAFETY_STOCK_PERCENTAGE: float = float(os.getenv("SAFETY_STOCK_PERCENTAGE", "20.0")) # Purchase Order Settings AUTO_APPROVE_POS: bool = os.getenv("AUTO_APPROVE_POS", "false").lower() == "true" AUTO_APPROVAL_MAX_AMOUNT: float = float(os.getenv("AUTO_APPROVAL_MAX_AMOUNT", "1000.0")) MAX_PO_ITEMS: int = int(os.getenv("MAX_PO_ITEMS", "100")) PO_EXPIRY_DAYS: int = int(os.getenv("PO_EXPIRY_DAYS", "30")) # Local Production Settings SUPPORT_LOCAL_PRODUCTION: bool = os.getenv("SUPPORT_LOCAL_PRODUCTION", "true").lower() == "true" MAX_BOM_EXPLOSION_DEPTH: int = int(os.getenv("MAX_BOM_EXPLOSION_DEPTH", "5")) RECIPE_CACHE_TTL_SECONDS: int = int(os.getenv("RECIPE_CACHE_TTL_SECONDS", "3600")) # Supplier Integration SUPPLIER_VALIDATION_ENABLED: bool = os.getenv("SUPPLIER_VALIDATION_ENABLED", "true").lower() == "true" MIN_SUPPLIER_RATING: float = float(os.getenv("MIN_SUPPLIER_RATING", "3.0")) MULTI_SUPPLIER_ENABLED: bool = os.getenv("MULTI_SUPPLIER_ENABLED", "true").lower() == "true" # Plan Management STALE_PLAN_DAYS: int = int(os.getenv("STALE_PLAN_DAYS", "7")) ARCHIVE_PLAN_DAYS: int = int(os.getenv("ARCHIVE_PLAN_DAYS", "90")) MAX_CONCURRENT_PLANS: int = int(os.getenv("MAX_CONCURRENT_PLANS", "10")) # Integration Settings INVENTORY_SERVICE_URL: str = os.getenv("INVENTORY_SERVICE_URL", "http://inventory-service:8000") SUPPLIERS_SERVICE_URL: str = os.getenv("SUPPLIERS_SERVICE_URL", "http://suppliers-service:8000") # ================================================================ # REPLENISHMENT PLANNING SETTINGS # ================================================================ # Projection Settings REPLENISHMENT_PROJECTION_HORIZON_DAYS: int = Field( default=7, description="Days to project ahead for inventory planning" ) REPLENISHMENT_SERVICE_LEVEL: float = Field( default=0.95, description="Target service level for safety stock (0-1)" ) REPLENISHMENT_BUFFER_DAYS: int = Field( default=1, description="Buffer days to add to lead time" ) # Safety Stock Settings SAFETY_STOCK_SERVICE_LEVEL: float = Field( default=0.95, description="Default service level for safety stock calculation" ) SAFETY_STOCK_METHOD: str = Field( default="statistical", description="Method for safety stock: 'statistical' or 'fixed_percentage'" ) # MOQ Aggregation Settings MOQ_CONSOLIDATION_WINDOW_DAYS: int = Field( default=7, description="Days within which to consolidate orders for MOQ" ) MOQ_ALLOW_EARLY_ORDERING: bool = Field( default=True, description="Allow ordering early to meet MOQ" ) # Supplier Selection Settings SUPPLIER_PRICE_WEIGHT: float = Field( default=0.40, description="Weight for price in supplier selection (0-1)" ) SUPPLIER_LEAD_TIME_WEIGHT: float = Field( default=0.20, description="Weight for lead time in supplier selection (0-1)" ) SUPPLIER_QUALITY_WEIGHT: float = Field( default=0.20, description="Weight for quality in supplier selection (0-1)" ) SUPPLIER_RELIABILITY_WEIGHT: float = Field( default=0.20, description="Weight for reliability in supplier selection (0-1)" ) SUPPLIER_DIVERSIFICATION_THRESHOLD: Decimal = Field( default=Decimal('1000'), description="Quantity threshold for supplier diversification" ) SUPPLIER_MAX_SINGLE_PERCENTAGE: float = Field( default=0.70, description="Maximum % of order to single supplier (0-1)" ) FORECASTING_SERVICE_URL: str = os.getenv("FORECASTING_SERVICE_URL", "http://forecasting-service:8000") RECIPES_SERVICE_URL: str = os.getenv("RECIPES_SERVICE_URL", "http://recipes-service:8000") NOTIFICATION_SERVICE_URL: str = os.getenv("NOTIFICATION_SERVICE_URL", "http://notification-service:8000") TENANT_SERVICE_URL: str = os.getenv("TENANT_SERVICE_URL", "http://tenant-service:8000") # Global settings instance settings = ProcurementSettings()