# services/tenant/app/models/tenant_settings.py """ Tenant Settings Model Centralized configuration storage for all tenant-specific operational settings """ from sqlalchemy import Column, String, Boolean, DateTime, ForeignKey, JSON from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship from datetime import datetime, timezone import uuid from shared.database.base import Base class TenantSettings(Base): """ Centralized tenant settings model Stores all operational configurations for a tenant across all services """ __tablename__ = "tenant_settings" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) tenant_id = Column(UUID(as_uuid=True), ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, unique=True, index=True) # Procurement & Auto-Approval Settings (Orders Service) procurement_settings = Column(JSON, nullable=False, default=lambda: { "auto_approve_enabled": True, "auto_approve_threshold_eur": 500.0, "auto_approve_min_supplier_score": 0.80, "require_approval_new_suppliers": True, "require_approval_critical_items": True, "procurement_lead_time_days": 3, "demand_forecast_days": 14, "safety_stock_percentage": 20.0, "po_approval_reminder_hours": 24, "po_critical_escalation_hours": 12, "use_reorder_rules": True, "economic_rounding": True, "respect_storage_limits": True, "use_supplier_minimums": True, "optimize_price_tiers": True }) # Inventory Management Settings (Inventory Service) inventory_settings = Column(JSON, nullable=False, default=lambda: { "low_stock_threshold": 10, "reorder_point": 20, "reorder_quantity": 50, "expiring_soon_days": 7, "expiration_warning_days": 3, "quality_score_threshold": 8.0, "temperature_monitoring_enabled": True, "refrigeration_temp_min": 1.0, "refrigeration_temp_max": 4.0, "freezer_temp_min": -20.0, "freezer_temp_max": -15.0, "room_temp_min": 18.0, "room_temp_max": 25.0, "temp_deviation_alert_minutes": 15, "critical_temp_deviation_minutes": 5 }) # Production Settings (Production Service) production_settings = Column(JSON, nullable=False, default=lambda: { "planning_horizon_days": 7, "minimum_batch_size": 1.0, "maximum_batch_size": 100.0, "production_buffer_percentage": 10.0, "working_hours_per_day": 12, "max_overtime_hours": 4, "capacity_utilization_target": 0.85, "capacity_warning_threshold": 0.95, "quality_check_enabled": True, "minimum_yield_percentage": 85.0, "quality_score_threshold": 8.0, "schedule_optimization_enabled": True, "prep_time_buffer_minutes": 30, "cleanup_time_buffer_minutes": 15, "labor_cost_per_hour_eur": 15.0, "overhead_cost_percentage": 20.0 }) # Supplier Settings (Suppliers Service) supplier_settings = Column(JSON, nullable=False, default=lambda: { "default_payment_terms_days": 30, "default_delivery_days": 3, "excellent_delivery_rate": 95.0, "good_delivery_rate": 90.0, "excellent_quality_rate": 98.0, "good_quality_rate": 95.0, "critical_delivery_delay_hours": 24, "critical_quality_rejection_rate": 10.0, "high_cost_variance_percentage": 15.0, # Supplier Approval Workflow Settings "require_supplier_approval": True, "auto_approve_for_admin_owner": True, "approval_required_roles": ["member", "viewer"] }) # POS Integration Settings (POS Service) pos_settings = Column(JSON, nullable=False, default=lambda: { "sync_interval_minutes": 5, "auto_sync_products": True, "auto_sync_transactions": True }) # Order & Business Rules Settings (Orders Service) order_settings = Column(JSON, nullable=False, default=lambda: { "max_discount_percentage": 50.0, "default_delivery_window_hours": 48, "dynamic_pricing_enabled": False, "discount_enabled": True, "delivery_tracking_enabled": True }) # Replenishment Planning Settings (Orchestrator Service) replenishment_settings = Column(JSON, nullable=False, default=lambda: { "projection_horizon_days": 7, "service_level": 0.95, "buffer_days": 1, "enable_auto_replenishment": True, "min_order_quantity": 1.0, "max_order_quantity": 1000.0, "demand_forecast_days": 14 }) # Safety Stock Settings (Orchestrator Service) safety_stock_settings = Column(JSON, nullable=False, default=lambda: { "service_level": 0.95, "method": "statistical", "min_safety_stock": 0.0, "max_safety_stock": 100.0, "reorder_point_calculation": "safety_stock_plus_lead_time_demand" }) # MOQ Aggregation Settings (Orchestrator Service) moq_settings = Column(JSON, nullable=False, default=lambda: { "consolidation_window_days": 7, "allow_early_ordering": True, "enable_batch_optimization": True, "min_batch_size": 1.0, "max_batch_size": 1000.0 }) # Supplier Selection Settings (Orchestrator Service) supplier_selection_settings = Column(JSON, nullable=False, default=lambda: { "price_weight": 0.40, "lead_time_weight": 0.20, "quality_weight": 0.20, "reliability_weight": 0.20, "diversification_threshold": 1000, "max_single_percentage": 0.70, "enable_supplier_score_optimization": True }) # ML Insights Settings (AI Insights Service) ml_insights_settings = Column(JSON, nullable=False, default=lambda: { # Inventory ML (Safety Stock Optimization) "inventory_lookback_days": 90, "inventory_min_history_days": 30, # Production ML (Yield Prediction) "production_lookback_days": 90, "production_min_history_runs": 30, # Procurement ML (Supplier Analysis & Price Forecasting) "supplier_analysis_lookback_days": 180, "supplier_analysis_min_orders": 10, "price_forecast_lookback_days": 180, "price_forecast_horizon_days": 30, # Forecasting ML (Dynamic Rules) "rules_generation_lookback_days": 90, "rules_generation_min_samples": 10, # Global ML Settings "enable_ml_insights": True, "ml_insights_auto_trigger": False, "ml_confidence_threshold": 0.80 }) # Timestamps created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False) updated_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False) # Relationships tenant = relationship("Tenant", backref="settings") def __repr__(self): return f"" @staticmethod def get_default_settings() -> dict: """ Get default settings for all categories Returns a dictionary with default values for all setting categories """ return { "procurement_settings": { "auto_approve_enabled": True, "auto_approve_threshold_eur": 500.0, "auto_approve_min_supplier_score": 0.80, "require_approval_new_suppliers": True, "require_approval_critical_items": True, "procurement_lead_time_days": 3, "demand_forecast_days": 14, "safety_stock_percentage": 20.0, "po_approval_reminder_hours": 24, "po_critical_escalation_hours": 12, "use_reorder_rules": True, "economic_rounding": True, "respect_storage_limits": True, "use_supplier_minimums": True, "optimize_price_tiers": True }, "inventory_settings": { "low_stock_threshold": 10, "reorder_point": 20, "reorder_quantity": 50, "expiring_soon_days": 7, "expiration_warning_days": 3, "quality_score_threshold": 8.0, "temperature_monitoring_enabled": True, "refrigeration_temp_min": 1.0, "refrigeration_temp_max": 4.0, "freezer_temp_min": -20.0, "freezer_temp_max": -15.0, "room_temp_min": 18.0, "room_temp_max": 25.0, "temp_deviation_alert_minutes": 15, "critical_temp_deviation_minutes": 5 }, "production_settings": { "planning_horizon_days": 7, "minimum_batch_size": 1.0, "maximum_batch_size": 100.0, "production_buffer_percentage": 10.0, "working_hours_per_day": 12, "max_overtime_hours": 4, "capacity_utilization_target": 0.85, "capacity_warning_threshold": 0.95, "quality_check_enabled": True, "minimum_yield_percentage": 85.0, "quality_score_threshold": 8.0, "schedule_optimization_enabled": True, "prep_time_buffer_minutes": 30, "cleanup_time_buffer_minutes": 15, "labor_cost_per_hour_eur": 15.0, "overhead_cost_percentage": 20.0 }, "supplier_settings": { "default_payment_terms_days": 30, "default_delivery_days": 3, "excellent_delivery_rate": 95.0, "good_delivery_rate": 90.0, "excellent_quality_rate": 98.0, "good_quality_rate": 95.0, "critical_delivery_delay_hours": 24, "critical_quality_rejection_rate": 10.0, "high_cost_variance_percentage": 15.0, "require_supplier_approval": True, "auto_approve_for_admin_owner": True, "approval_required_roles": ["member", "viewer"] }, "pos_settings": { "sync_interval_minutes": 5, "auto_sync_products": True, "auto_sync_transactions": True }, "order_settings": { "max_discount_percentage": 50.0, "default_delivery_window_hours": 48, "dynamic_pricing_enabled": False, "discount_enabled": True, "delivery_tracking_enabled": True }, "replenishment_settings": { "projection_horizon_days": 7, "service_level": 0.95, "buffer_days": 1, "enable_auto_replenishment": True, "min_order_quantity": 1.0, "max_order_quantity": 1000.0, "demand_forecast_days": 14 }, "safety_stock_settings": { "service_level": 0.95, "method": "statistical", "min_safety_stock": 0.0, "max_safety_stock": 100.0, "reorder_point_calculation": "safety_stock_plus_lead_time_demand" }, "moq_settings": { "consolidation_window_days": 7, "allow_early_ordering": True, "enable_batch_optimization": True, "min_batch_size": 1.0, "max_batch_size": 1000.0 }, "supplier_selection_settings": { "price_weight": 0.40, "lead_time_weight": 0.20, "quality_weight": 0.20, "reliability_weight": 0.20, "diversification_threshold": 1000, "max_single_percentage": 0.70, "enable_supplier_score_optimization": True }, "ml_insights_settings": { "inventory_lookback_days": 90, "inventory_min_history_days": 30, "production_lookback_days": 90, "production_min_history_runs": 30, "supplier_analysis_lookback_days": 180, "supplier_analysis_min_orders": 10, "price_forecast_lookback_days": 180, "price_forecast_horizon_days": 30, "rules_generation_lookback_days": 90, "rules_generation_min_samples": 10, "enable_ml_insights": True, "ml_insights_auto_trigger": False, "ml_confidence_threshold": 0.80 } }