283 lines
10 KiB
Python
283 lines
10 KiB
Python
# ================================================================
|
|
# services/inventory/app/schemas/food_safety.py
|
|
# ================================================================
|
|
"""
|
|
Food safety schemas for Inventory Service
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from decimal import Decimal
|
|
from typing import List, Optional, Dict, Any
|
|
from uuid import UUID
|
|
from pydantic import BaseModel, Field, validator
|
|
|
|
|
|
# ===== Food Safety Compliance Schemas =====
|
|
|
|
class FoodSafetyComplianceBase(BaseModel):
|
|
ingredient_id: UUID
|
|
standard: str
|
|
compliance_status: str = Field(default="pending_review")
|
|
certification_number: Optional[str] = None
|
|
certifying_body: Optional[str] = None
|
|
certification_date: Optional[datetime] = None
|
|
expiration_date: Optional[datetime] = None
|
|
requirements: Optional[Dict[str, Any]] = None
|
|
compliance_notes: Optional[str] = None
|
|
documentation_url: Optional[str] = None
|
|
last_audit_date: Optional[datetime] = None
|
|
next_audit_date: Optional[datetime] = None
|
|
auditor_name: Optional[str] = None
|
|
audit_score: Optional[float] = Field(None, ge=0, le=100)
|
|
risk_level: str = Field(default="medium")
|
|
risk_factors: Optional[List[str]] = None
|
|
mitigation_measures: Optional[List[str]] = None
|
|
requires_monitoring: bool = Field(default=True)
|
|
monitoring_frequency_days: Optional[int] = Field(None, gt=0)
|
|
|
|
|
|
class FoodSafetyComplianceCreate(FoodSafetyComplianceBase):
|
|
tenant_id: UUID
|
|
|
|
|
|
class FoodSafetyComplianceUpdate(BaseModel):
|
|
compliance_status: Optional[str] = None
|
|
certification_number: Optional[str] = None
|
|
certifying_body: Optional[str] = None
|
|
certification_date: Optional[datetime] = None
|
|
expiration_date: Optional[datetime] = None
|
|
requirements: Optional[Dict[str, Any]] = None
|
|
compliance_notes: Optional[str] = None
|
|
documentation_url: Optional[str] = None
|
|
last_audit_date: Optional[datetime] = None
|
|
next_audit_date: Optional[datetime] = None
|
|
auditor_name: Optional[str] = None
|
|
audit_score: Optional[float] = Field(None, ge=0, le=100)
|
|
risk_level: Optional[str] = None
|
|
risk_factors: Optional[List[str]] = None
|
|
mitigation_measures: Optional[List[str]] = None
|
|
requires_monitoring: Optional[bool] = None
|
|
monitoring_frequency_days: Optional[int] = Field(None, gt=0)
|
|
|
|
|
|
class FoodSafetyComplianceResponse(FoodSafetyComplianceBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
is_active: bool
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
created_by: Optional[UUID] = None
|
|
updated_by: Optional[UUID] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# ===== Temperature Monitoring Schemas =====
|
|
|
|
class TemperatureLogBase(BaseModel):
|
|
storage_location: str = Field(..., min_length=1, max_length=100)
|
|
warehouse_zone: Optional[str] = Field(None, max_length=50)
|
|
equipment_id: Optional[str] = Field(None, max_length=100)
|
|
temperature_celsius: float
|
|
humidity_percentage: Optional[float] = Field(None, ge=0, le=100)
|
|
target_temperature_min: Optional[float] = None
|
|
target_temperature_max: Optional[float] = None
|
|
measurement_method: str = Field(default="manual")
|
|
device_id: Optional[str] = Field(None, max_length=100)
|
|
calibration_date: Optional[datetime] = None
|
|
|
|
|
|
class TemperatureLogCreate(TemperatureLogBase):
|
|
tenant_id: UUID
|
|
|
|
|
|
class TemperatureLogResponse(TemperatureLogBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
is_within_range: bool
|
|
alert_triggered: bool
|
|
deviation_minutes: Optional[int] = None
|
|
recorded_at: datetime
|
|
created_at: datetime
|
|
recorded_by: Optional[UUID] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# ===== Food Safety Alert Schemas =====
|
|
|
|
class FoodSafetyAlertBase(BaseModel):
|
|
alert_type: str
|
|
severity: str = Field(default="medium")
|
|
risk_level: str = Field(default="medium")
|
|
source_entity_type: str
|
|
source_entity_id: UUID
|
|
ingredient_id: Optional[UUID] = None
|
|
stock_id: Optional[UUID] = None
|
|
title: str = Field(..., min_length=1, max_length=200)
|
|
description: str = Field(..., min_length=1)
|
|
detailed_message: Optional[str] = None
|
|
regulatory_requirement: Optional[str] = Field(None, max_length=100)
|
|
compliance_standard: Optional[str] = None
|
|
regulatory_action_required: bool = Field(default=False)
|
|
trigger_condition: Optional[str] = Field(None, max_length=200)
|
|
threshold_value: Optional[Decimal] = None
|
|
actual_value: Optional[Decimal] = None
|
|
alert_data: Optional[Dict[str, Any]] = None
|
|
environmental_factors: Optional[Dict[str, Any]] = None
|
|
affected_products: Optional[List[UUID]] = None
|
|
public_health_risk: bool = Field(default=False)
|
|
business_impact: Optional[str] = None
|
|
estimated_loss: Optional[Decimal] = Field(None, ge=0)
|
|
|
|
|
|
class FoodSafetyAlertCreate(FoodSafetyAlertBase):
|
|
tenant_id: UUID
|
|
alert_code: str = Field(..., min_length=1, max_length=50)
|
|
|
|
|
|
class FoodSafetyAlertUpdate(BaseModel):
|
|
status: Optional[str] = None
|
|
alert_state: Optional[str] = None
|
|
immediate_actions_taken: Optional[List[str]] = None
|
|
investigation_notes: Optional[str] = None
|
|
resolution_action: Optional[str] = Field(None, max_length=200)
|
|
resolution_notes: Optional[str] = None
|
|
corrective_actions: Optional[List[str]] = None
|
|
preventive_measures: Optional[List[str]] = None
|
|
assigned_to: Optional[UUID] = None
|
|
assigned_role: Optional[str] = Field(None, max_length=50)
|
|
escalated_to: Optional[UUID] = None
|
|
escalation_deadline: Optional[datetime] = None
|
|
documentation: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
class FoodSafetyAlertResponse(FoodSafetyAlertBase):
|
|
id: UUID
|
|
tenant_id: UUID
|
|
alert_code: str
|
|
status: str
|
|
alert_state: str
|
|
immediate_actions_taken: Optional[List[str]] = None
|
|
investigation_notes: Optional[str] = None
|
|
resolution_action: Optional[str] = None
|
|
resolution_notes: Optional[str] = None
|
|
corrective_actions: Optional[List[str]] = None
|
|
preventive_measures: Optional[List[str]] = None
|
|
first_occurred_at: datetime
|
|
last_occurred_at: datetime
|
|
acknowledged_at: Optional[datetime] = None
|
|
resolved_at: Optional[datetime] = None
|
|
escalation_deadline: Optional[datetime] = None
|
|
occurrence_count: int
|
|
is_recurring: bool
|
|
recurrence_pattern: Optional[str] = None
|
|
assigned_to: Optional[UUID] = None
|
|
assigned_role: Optional[str] = None
|
|
escalated_to: Optional[UUID] = None
|
|
escalation_level: int
|
|
notification_sent: bool
|
|
notification_methods: Optional[List[str]] = None
|
|
notification_recipients: Optional[List[str]] = None
|
|
regulatory_notification_required: bool
|
|
regulatory_notification_sent: bool
|
|
documentation: Optional[Dict[str, Any]] = None
|
|
audit_trail: Optional[List[Dict[str, Any]]] = None
|
|
external_reference: Optional[str] = None
|
|
detection_time: Optional[datetime] = None
|
|
response_time_minutes: Optional[int] = None
|
|
resolution_time_minutes: Optional[int] = None
|
|
alert_accuracy: Optional[bool] = None
|
|
false_positive: bool
|
|
feedback_notes: Optional[str] = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
created_by: Optional[UUID] = None
|
|
updated_by: Optional[UUID] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# ===== Bulk Operations Schemas =====
|
|
|
|
class BulkTemperatureLogCreate(BaseModel):
|
|
"""Schema for bulk temperature logging"""
|
|
tenant_id: UUID
|
|
readings: List[TemperatureLogBase] = Field(..., min_items=1, max_items=100)
|
|
|
|
|
|
class BulkComplianceUpdate(BaseModel):
|
|
"""Schema for bulk compliance updates"""
|
|
tenant_id: UUID
|
|
updates: List[Dict[str, Any]] = Field(..., min_items=1, max_items=50)
|
|
|
|
|
|
# ===== Filter and Query Schemas =====
|
|
|
|
class FoodSafetyFilter(BaseModel):
|
|
"""Filtering options for food safety data"""
|
|
compliance_standards: Optional[List[str]] = None
|
|
compliance_statuses: Optional[List[str]] = None
|
|
risk_levels: Optional[List[str]] = None
|
|
alert_types: Optional[List[str]] = None
|
|
severities: Optional[List[str]] = None
|
|
date_from: Optional[datetime] = None
|
|
date_to: Optional[datetime] = None
|
|
assigned_to: Optional[UUID] = None
|
|
include_resolved: bool = False
|
|
regulatory_action_required: Optional[bool] = None
|
|
|
|
|
|
class TemperatureMonitoringFilter(BaseModel):
|
|
"""Filtering options for temperature monitoring"""
|
|
storage_locations: Optional[List[str]] = None
|
|
equipment_ids: Optional[List[str]] = None
|
|
date_from: Optional[datetime] = None
|
|
date_to: Optional[datetime] = None
|
|
violations_only: bool = False
|
|
alerts_only: bool = False
|
|
|
|
|
|
# ===== Analytics Schemas =====
|
|
|
|
class FoodSafetyMetrics(BaseModel):
|
|
"""Food safety performance metrics"""
|
|
compliance_rate: Decimal = Field(..., ge=0, le=100)
|
|
temperature_compliance_rate: Decimal = Field(..., ge=0, le=100)
|
|
alert_response_time_avg: Optional[Decimal] = None
|
|
alert_resolution_time_avg: Optional[Decimal] = None
|
|
recurring_issues_count: int
|
|
regulatory_violations: int
|
|
certification_coverage: Decimal = Field(..., ge=0, le=100)
|
|
audit_score_avg: Optional[Decimal] = Field(None, ge=0, le=100)
|
|
risk_score: Decimal = Field(..., ge=0, le=10)
|
|
|
|
|
|
class TemperatureAnalytics(BaseModel):
|
|
"""Temperature monitoring analytics"""
|
|
total_readings: int
|
|
violations_count: int
|
|
violation_rate: Decimal = Field(..., ge=0, le=100)
|
|
average_temperature: Decimal
|
|
temperature_range: Dict[str, Decimal]
|
|
longest_violation_hours: Optional[int] = None
|
|
equipment_performance: List[Dict[str, Any]]
|
|
location_performance: List[Dict[str, Any]]
|
|
|
|
|
|
# ===== Notification Schemas =====
|
|
|
|
class AlertNotificationPreferences(BaseModel):
|
|
"""User preferences for alert notifications"""
|
|
email_enabled: bool = True
|
|
sms_enabled: bool = False
|
|
whatsapp_enabled: bool = False
|
|
dashboard_enabled: bool = True
|
|
severity_threshold: str = Field(default="medium") # Only notify for this severity and above
|
|
alert_types: Optional[List[str]] = None # Specific alert types to receive
|
|
quiet_hours_start: Optional[str] = Field(None, pattern=r"^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$")
|
|
quiet_hours_end: Optional[str] = Field(None, pattern=r"^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$")
|
|
weekend_notifications: bool = True |