Initial commit - production deployment

This commit is contained in:
2026-01-21 17:17:16 +01:00
commit c23d00dd92
2289 changed files with 638440 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
# ================================================================
# services/production/app/schemas/__init__.py
# ================================================================
"""
Pydantic schemas for request/response models
"""

View File

@@ -0,0 +1,488 @@
# services/production/app/schemas/equipment.py
"""
Equipment schemas for Production Service
"""
from pydantic import BaseModel, Field, ConfigDict
from typing import Optional, List
from datetime import datetime
from uuid import UUID
from app.models.production import EquipmentType, EquipmentStatus, IoTProtocol, IoTConnectionStatus
class IoTConnectionConfig(BaseModel):
"""Schema for IoT connection configuration"""
protocol: str = Field(..., description="IoT protocol (rest_api, opc_ua, mqtt, modbus, custom)")
endpoint: str = Field(..., description="Connection endpoint (URL or IP address)")
port: Optional[int] = Field(None, description="Connection port")
username: Optional[str] = Field(None, description="Username for authentication")
password: Optional[str] = Field(None, description="Password for authentication")
api_key: Optional[str] = Field(None, description="API key for authentication")
token: Optional[str] = Field(None, description="Authentication token")
additional_config: Optional[dict] = Field(None, description="Additional protocol-specific configuration")
model_config = ConfigDict(
json_schema_extra={
"example": {
"protocol": "rest_api",
"endpoint": "https://connectedcooking.com/api/v1",
"port": 443,
"api_key": "your-api-key-here",
"additional_config": {"poll_interval": 30}
}
}
)
class EquipmentCreate(BaseModel):
"""Schema for creating new equipment"""
name: str = Field(..., min_length=1, max_length=255, description="Equipment name")
type: EquipmentType = Field(..., description="Equipment type")
model: Optional[str] = Field(None, max_length=100, description="Equipment model")
serial_number: Optional[str] = Field(None, max_length=100, description="Serial number")
location: Optional[str] = Field(None, max_length=255, description="Physical location")
manufacturer: Optional[str] = Field(None, max_length=100, description="Manufacturer")
firmware_version: Optional[str] = Field(None, max_length=50, description="Firmware version")
status: EquipmentStatus = Field(default=EquipmentStatus.OPERATIONAL, description="Equipment status")
# Installation and maintenance
install_date: Optional[datetime] = Field(None, description="Installation date")
last_maintenance_date: Optional[datetime] = Field(None, description="Last maintenance date")
next_maintenance_date: Optional[datetime] = Field(None, description="Next scheduled maintenance date")
maintenance_interval_days: Optional[int] = Field(None, ge=1, description="Maintenance interval in days")
# Performance metrics
efficiency_percentage: Optional[float] = Field(None, ge=0, le=100, description="Current efficiency percentage")
uptime_percentage: Optional[float] = Field(None, ge=0, le=100, description="Overall uptime percentage")
energy_usage_kwh: Optional[float] = Field(None, ge=0, description="Current energy usage in kWh")
# Specifications
power_kw: Optional[float] = Field(None, ge=0, description="Power consumption in kilowatts")
capacity: Optional[float] = Field(None, ge=0, description="Equipment capacity")
weight_kg: Optional[float] = Field(None, ge=0, description="Weight in kilograms")
# Temperature monitoring
current_temperature: Optional[float] = Field(None, description="Current temperature")
target_temperature: Optional[float] = Field(None, description="Target temperature")
# IoT Connectivity
iot_enabled: bool = Field(default=False, description="Enable IoT connectivity")
iot_protocol: Optional[str] = Field(None, description="IoT protocol")
iot_endpoint: Optional[str] = Field(None, description="IoT endpoint URL or IP")
iot_port: Optional[int] = Field(None, description="IoT connection port")
iot_config: Optional[dict] = Field(None, description="IoT configuration")
# Real-time monitoring
supports_realtime: bool = Field(default=False, description="Supports real-time monitoring")
poll_interval_seconds: Optional[int] = Field(None, ge=1, description="Polling interval in seconds")
# Sensor capabilities
temperature_zones: Optional[int] = Field(None, ge=1, description="Number of temperature zones")
supports_humidity: bool = Field(default=False, description="Supports humidity monitoring")
supports_energy_monitoring: bool = Field(default=False, description="Supports energy monitoring")
supports_remote_control: bool = Field(default=False, description="Supports remote control")
# Notes
notes: Optional[str] = Field(None, description="Additional notes")
# Support contact information
support_contact: Optional[dict] = Field(
None,
description="Support contact information for equipment maintenance",
json_schema_extra={
"example": {
"email": "support@ovenfactory.com",
"phone": "+1-800-555-1234",
"company": "OvenTech Support",
"contract_number": "SUP-2023-001",
"response_time_sla": 24
}
}
)
model_config = ConfigDict(
json_schema_extra={
"example": {
"name": "Horno Principal #1",
"type": "oven",
"model": "Miwe Condo CO 4.1212",
"serial_number": "MCO-2021-001",
"location": "Área de Horneado - Zona A",
"status": "operational",
"install_date": "2021-03-15T00:00:00Z",
"maintenance_interval_days": 90,
"efficiency_percentage": 92.0,
"uptime_percentage": 98.5,
"power_kw": 45.0,
"capacity": 24.0
}
}
)
class EquipmentUpdate(BaseModel):
"""Schema for updating equipment"""
name: Optional[str] = Field(None, min_length=1, max_length=255)
type: Optional[EquipmentType] = None
model: Optional[str] = Field(None, max_length=100)
serial_number: Optional[str] = Field(None, max_length=100)
location: Optional[str] = Field(None, max_length=255)
manufacturer: Optional[str] = Field(None, max_length=100)
firmware_version: Optional[str] = Field(None, max_length=50)
status: Optional[EquipmentStatus] = None
# Installation and maintenance
install_date: Optional[datetime] = None
last_maintenance_date: Optional[datetime] = None
next_maintenance_date: Optional[datetime] = None
maintenance_interval_days: Optional[int] = Field(None, ge=1)
# Performance metrics
efficiency_percentage: Optional[float] = Field(None, ge=0, le=100)
uptime_percentage: Optional[float] = Field(None, ge=0, le=100)
energy_usage_kwh: Optional[float] = Field(None, ge=0)
# Specifications
power_kw: Optional[float] = Field(None, ge=0)
capacity: Optional[float] = Field(None, ge=0)
weight_kg: Optional[float] = Field(None, ge=0)
# Temperature monitoring
current_temperature: Optional[float] = None
target_temperature: Optional[float] = None
# IoT Connectivity
iot_enabled: Optional[bool] = None
iot_protocol: Optional[str] = None
iot_endpoint: Optional[str] = None
iot_port: Optional[int] = None
iot_config: Optional[dict] = None
# Real-time monitoring
supports_realtime: Optional[bool] = None
poll_interval_seconds: Optional[int] = Field(None, ge=1)
# Sensor capabilities
temperature_zones: Optional[int] = Field(None, ge=1)
supports_humidity: Optional[bool] = None
supports_energy_monitoring: Optional[bool] = None
supports_remote_control: Optional[bool] = None
# Notes
notes: Optional[str] = None
# Support contact information
support_contact: Optional[dict] = None
# Status flag
is_active: Optional[bool] = None
model_config = ConfigDict(
json_schema_extra={
"example": {
"status": "maintenance",
"last_maintenance_date": "2024-01-15T00:00:00Z",
"next_maintenance_date": "2024-04-15T00:00:00Z",
"efficiency_percentage": 88.0
}
}
)
class EquipmentResponse(BaseModel):
"""Schema for equipment response"""
id: UUID
tenant_id: UUID
name: str
type: EquipmentType
model: Optional[str] = None
serial_number: Optional[str] = None
location: Optional[str] = None
manufacturer: Optional[str] = None
firmware_version: Optional[str] = None
status: EquipmentStatus
# Installation and maintenance
install_date: Optional[datetime] = None
last_maintenance_date: Optional[datetime] = None
next_maintenance_date: Optional[datetime] = None
maintenance_interval_days: Optional[int] = None
# Performance metrics
efficiency_percentage: Optional[float] = None
uptime_percentage: Optional[float] = None
energy_usage_kwh: Optional[float] = None
# Specifications
power_kw: Optional[float] = None
capacity: Optional[float] = None
weight_kg: Optional[float] = None
# Temperature monitoring
current_temperature: Optional[float] = None
target_temperature: Optional[float] = None
# IoT Connectivity
iot_enabled: bool = False
iot_protocol: Optional[str] = None
iot_endpoint: Optional[str] = None
iot_port: Optional[int] = None
iot_connection_status: Optional[str] = None
iot_last_connected: Optional[datetime] = None
iot_config: Optional[dict] = None
# Real-time monitoring
supports_realtime: bool = False
poll_interval_seconds: Optional[int] = None
# Sensor capabilities
temperature_zones: Optional[int] = None
supports_humidity: bool = False
supports_energy_monitoring: bool = False
supports_remote_control: bool = False
# Status
is_active: bool
notes: Optional[str] = None
# Support contact information
support_contact: Optional[dict] = None
# Timestamps
created_at: datetime
updated_at: datetime
model_config = ConfigDict(from_attributes=True)
class EquipmentListResponse(BaseModel):
"""Schema for paginated equipment list response"""
equipment: List[EquipmentResponse]
total_count: int
page: int
page_size: int
model_config = ConfigDict(
json_schema_extra={
"example": {
"equipment": [],
"total_count": 10,
"page": 1,
"page_size": 50
}
}
)
class EquipmentDeletionSummary(BaseModel):
"""Schema for equipment deletion summary"""
can_delete: bool = Field(..., description="Whether the equipment can be deleted")
warnings: List[str] = Field(default_factory=list, description="List of warnings about deletion")
production_batches_count: int = Field(default=0, description="Number of production batches using this equipment")
maintenance_records_count: int = Field(default=0, description="Number of maintenance records")
temperature_logs_count: int = Field(default=0, description="Number of temperature logs")
equipment_name: Optional[str] = Field(None, description="Equipment name")
equipment_type: Optional[str] = Field(None, description="Equipment type")
equipment_location: Optional[str] = Field(None, description="Equipment location")
model_config = ConfigDict(
json_schema_extra={
"example": {
"can_delete": True,
"warnings": ["3 production batch(es) are using this equipment"],
"production_batches_count": 3,
"maintenance_records_count": 5,
"temperature_logs_count": 120,
"equipment_name": "Horno Principal #1",
"equipment_type": "oven",
"equipment_location": "Área de Horneado"
}
}
)
# ================================================================
# IoT-SPECIFIC SCHEMAS
# ================================================================
class EquipmentSensorReadingResponse(BaseModel):
"""Schema for equipment sensor reading response"""
id: UUID
tenant_id: UUID
equipment_id: UUID
batch_id: Optional[UUID] = None
reading_time: datetime
# Temperature readings
temperature: Optional[float] = None
temperature_zones: Optional[dict] = None
target_temperature: Optional[float] = None
# Humidity
humidity: Optional[float] = None
target_humidity: Optional[float] = None
# Energy monitoring
energy_consumption_kwh: Optional[float] = None
power_current_kw: Optional[float] = None
# Equipment status
operational_status: Optional[str] = None
cycle_stage: Optional[str] = None
cycle_progress_percentage: Optional[float] = None
time_remaining_minutes: Optional[int] = None
# Process parameters
motor_speed_rpm: Optional[float] = None
door_status: Optional[str] = None
steam_level: Optional[float] = None
# Quality indicators
product_weight_kg: Optional[float] = None
moisture_content: Optional[float] = None
# Additional sensor data
additional_sensors: Optional[dict] = None
# Data quality
data_quality_score: Optional[float] = None
is_anomaly: bool = False
created_at: datetime
model_config = ConfigDict(from_attributes=True)
class EquipmentConnectionTestResponse(BaseModel):
"""Schema for IoT connection test response"""
success: bool = Field(..., description="Whether connection test succeeded")
status: str = Field(..., description="Connection status")
message: str = Field(..., description="Detailed message")
response_time_ms: Optional[int] = Field(None, description="Response time in milliseconds")
protocol_tested: str = Field(..., description="Protocol that was tested")
endpoint_tested: str = Field(..., description="Endpoint that was tested")
error_details: Optional[str] = Field(None, description="Error details if connection failed")
supported_features: Optional[List[str]] = Field(None, description="List of supported IoT features")
model_config = ConfigDict(
json_schema_extra={
"example": {
"success": True,
"status": "connected",
"message": "Successfully connected to equipment",
"response_time_ms": 145,
"protocol_tested": "rest_api",
"endpoint_tested": "https://connectedcooking.com/api/v1",
"supported_features": ["temperature", "humidity", "energy_monitoring"]
}
}
)
class RealTimeDataResponse(BaseModel):
"""Schema for real-time equipment data response"""
equipment_id: UUID
equipment_name: str
timestamp: datetime
connection_status: str
# Current readings
temperature: Optional[float] = None
temperature_zones: Optional[dict] = None
humidity: Optional[float] = None
energy_consumption_kwh: Optional[float] = None
power_current_kw: Optional[float] = None
# Status
operational_status: Optional[str] = None
cycle_stage: Optional[str] = None
cycle_progress_percentage: Optional[float] = None
time_remaining_minutes: Optional[int] = None
# Active batch
active_batch_id: Optional[UUID] = None
active_batch_name: Optional[str] = None
model_config = ConfigDict(
json_schema_extra={
"example": {
"equipment_id": "123e4567-e89b-12d3-a456-426614174000",
"equipment_name": "Horno Principal #1",
"timestamp": "2025-01-12T10:30:00Z",
"connection_status": "connected",
"temperature": 185.5,
"temperature_zones": {"zone1": 180, "zone2": 190, "zone3": 185},
"humidity": 65.0,
"operational_status": "running",
"cycle_stage": "baking",
"cycle_progress_percentage": 45.0,
"time_remaining_minutes": 12
}
}
)
class EquipmentIoTAlertResponse(BaseModel):
"""Schema for IoT alert response"""
id: UUID
tenant_id: UUID
equipment_id: UUID
batch_id: Optional[UUID] = None
# Alert information
alert_type: str
severity: str
alert_time: datetime
# Alert details
title: str
message: str
# Threshold information
threshold_value: Optional[float] = None
actual_value: Optional[float] = None
deviation_percentage: Optional[float] = None
# Status
is_active: bool
is_acknowledged: bool
acknowledged_by: Optional[UUID] = None
acknowledged_at: Optional[datetime] = None
is_resolved: bool
resolved_by: Optional[UUID] = None
resolved_at: Optional[datetime] = None
resolution_notes: Optional[str] = None
# Automated response
auto_resolved: bool
corrective_action_taken: Optional[str] = None
created_at: datetime
updated_at: datetime
model_config = ConfigDict(from_attributes=True)
class EquipmentSensorHistoryResponse(BaseModel):
"""Schema for sensor reading history response"""
equipment_id: UUID
equipment_name: str
start_time: datetime
end_time: datetime
total_readings: int
readings: List[EquipmentSensorReadingResponse]
model_config = ConfigDict(
json_schema_extra={
"example": {
"equipment_id": "123e4567-e89b-12d3-a456-426614174000",
"equipment_name": "Horno Principal #1",
"start_time": "2025-01-12T08:00:00Z",
"end_time": "2025-01-12T12:00:00Z",
"total_readings": 48,
"readings": []
}
}
)

View File

@@ -0,0 +1,352 @@
# ================================================================
# services/production/app/schemas/production.py
# ================================================================
"""
Pydantic schemas for production service
"""
from pydantic import BaseModel, Field, validator
from typing import Optional, List, Dict, Any, Union
from datetime import datetime, date
from uuid import UUID
from enum import Enum
class ProductionStatusEnum(str, Enum):
"""Production batch status enumeration for API"""
PENDING = "PENDING"
IN_PROGRESS = "IN_PROGRESS"
COMPLETED = "COMPLETED"
CANCELLED = "CANCELLED"
ON_HOLD = "ON_HOLD"
QUALITY_CHECK = "QUALITY_CHECK"
FAILED = "FAILED"
class ProductionPriorityEnum(str, Enum):
"""Production priority levels for API"""
LOW = "LOW"
MEDIUM = "MEDIUM"
HIGH = "HIGH"
URGENT = "URGENT"
# ================================================================
# PRODUCTION BATCH SCHEMAS
# ================================================================
class ProductionBatchBase(BaseModel):
"""Base schema for production batch"""
product_id: UUID
product_name: str = Field(..., min_length=1, max_length=255)
recipe_id: Optional[UUID] = None
planned_start_time: datetime
planned_end_time: datetime
planned_quantity: float = Field(..., gt=0)
planned_duration_minutes: int = Field(..., gt=0)
priority: ProductionPriorityEnum = ProductionPriorityEnum.MEDIUM
is_rush_order: bool = False
is_special_recipe: bool = False
production_notes: Optional[str] = None
@validator('planned_end_time')
def validate_end_time_after_start(cls, v, values):
if 'planned_start_time' in values and v <= values['planned_start_time']:
raise ValueError('planned_end_time must be after planned_start_time')
return v
class ProductionBatchCreate(ProductionBatchBase):
"""Schema for creating a production batch"""
batch_number: Optional[str] = Field(None, max_length=50)
order_id: Optional[UUID] = None
forecast_id: Optional[UUID] = None
equipment_used: Optional[List[str]] = None
staff_assigned: Optional[List[str]] = None
station_id: Optional[str] = Field(None, max_length=50)
class ProductionBatchUpdate(BaseModel):
"""Schema for updating a production batch"""
product_name: Optional[str] = Field(None, min_length=1, max_length=255)
planned_start_time: Optional[datetime] = None
planned_end_time: Optional[datetime] = None
planned_quantity: Optional[float] = Field(None, gt=0)
planned_duration_minutes: Optional[int] = Field(None, gt=0)
actual_quantity: Optional[float] = Field(None, ge=0)
priority: Optional[ProductionPriorityEnum] = None
equipment_used: Optional[List[str]] = None
staff_assigned: Optional[List[str]] = None
station_id: Optional[str] = Field(None, max_length=50)
production_notes: Optional[str] = None
class ProductionBatchStatusUpdate(BaseModel):
"""Schema for updating production batch status"""
status: ProductionStatusEnum
actual_quantity: Optional[float] = Field(None, ge=0)
notes: Optional[str] = None
class ProductionBatchResponse(BaseModel):
"""Schema for production batch response"""
id: UUID
tenant_id: UUID
batch_number: str
product_id: UUID
product_name: str
recipe_id: Optional[UUID]
planned_start_time: datetime
planned_end_time: datetime
planned_quantity: float
planned_duration_minutes: int
actual_start_time: Optional[datetime]
actual_end_time: Optional[datetime]
actual_quantity: Optional[float]
actual_duration_minutes: Optional[int]
status: ProductionStatusEnum
priority: ProductionPriorityEnum
# Process stage tracking (added to replace frontend mock data)
current_process_stage: Optional[str] = None
process_stage_history: Optional[List[Dict[str, Any]]] = None
pending_quality_checks: Optional[List[Dict[str, Any]]] = None
completed_quality_checks: Optional[List[Dict[str, Any]]] = None
estimated_cost: Optional[float]
actual_cost: Optional[float]
yield_percentage: Optional[float]
quality_score: Optional[float]
equipment_used: Optional[List[str]]
staff_assigned: Optional[List[str]]
station_id: Optional[str]
order_id: Optional[UUID]
forecast_id: Optional[UUID]
is_rush_order: bool
is_special_recipe: bool
production_notes: Optional[str]
quality_notes: Optional[str]
delay_reason: Optional[str]
cancellation_reason: Optional[str]
reasoning_data: Optional[Dict[str, Any]] = None
created_at: datetime
updated_at: datetime
completed_at: Optional[datetime]
class Config:
from_attributes = True
# ================================================================
# PRODUCTION SCHEDULE SCHEMAS
# ================================================================
class ProductionScheduleBase(BaseModel):
"""Base schema for production schedule"""
schedule_date: date
shift_start: datetime
shift_end: datetime
total_capacity_hours: float = Field(..., gt=0)
planned_capacity_hours: float = Field(..., gt=0)
staff_count: int = Field(..., gt=0)
equipment_capacity: Optional[Dict[str, Any]] = None
station_assignments: Optional[Dict[str, Any]] = None
schedule_notes: Optional[str] = None
@validator('shift_end')
def validate_shift_end_after_start(cls, v, values):
if 'shift_start' in values and v <= values['shift_start']:
raise ValueError('shift_end must be after shift_start')
return v
@validator('planned_capacity_hours')
def validate_planned_capacity(cls, v, values):
if 'total_capacity_hours' in values and v > values['total_capacity_hours']:
raise ValueError('planned_capacity_hours cannot exceed total_capacity_hours')
return v
class ProductionScheduleCreate(ProductionScheduleBase):
"""Schema for creating a production schedule"""
pass
class ProductionScheduleUpdate(BaseModel):
"""Schema for updating a production schedule"""
shift_start: Optional[datetime] = None
shift_end: Optional[datetime] = None
total_capacity_hours: Optional[float] = Field(None, gt=0)
planned_capacity_hours: Optional[float] = Field(None, gt=0)
staff_count: Optional[int] = Field(None, gt=0)
overtime_hours: Optional[float] = Field(None, ge=0)
equipment_capacity: Optional[Dict[str, Any]] = None
station_assignments: Optional[Dict[str, Any]] = None
schedule_notes: Optional[str] = None
class ProductionScheduleResponse(BaseModel):
"""Schema for production schedule response"""
id: UUID
tenant_id: UUID
schedule_date: date
shift_start: datetime
shift_end: datetime
total_capacity_hours: float
planned_capacity_hours: float
actual_capacity_hours: Optional[float]
overtime_hours: Optional[float]
staff_count: int
equipment_capacity: Optional[Dict[str, Any]]
station_assignments: Optional[Dict[str, Any]]
total_batches_planned: int
total_batches_completed: Optional[int]
total_quantity_planned: float
total_quantity_produced: Optional[float]
is_finalized: bool
is_active: bool
efficiency_percentage: Optional[float]
utilization_percentage: Optional[float]
on_time_completion_rate: Optional[float]
schedule_notes: Optional[str]
schedule_adjustments: Optional[Dict[str, Any]]
created_at: datetime
updated_at: datetime
finalized_at: Optional[datetime]
class Config:
from_attributes = True
# ================================================================
# QUALITY CHECK SCHEMAS
# ================================================================
class QualityCheckBase(BaseModel):
"""Base schema for quality check"""
batch_id: UUID
check_type: str = Field(..., min_length=1, max_length=50)
check_time: datetime
quality_score: float = Field(..., ge=1, le=10)
pass_fail: bool
defect_count: int = Field(0, ge=0)
defect_types: Optional[List[str]] = None
check_notes: Optional[str] = None
class QualityCheckCreate(QualityCheckBase):
"""Schema for creating a quality check"""
checker_id: Optional[str] = Field(None, max_length=100)
measured_weight: Optional[float] = Field(None, gt=0)
measured_temperature: Optional[float] = None
measured_moisture: Optional[float] = Field(None, ge=0, le=100)
measured_dimensions: Optional[Dict[str, float]] = None
target_weight: Optional[float] = Field(None, gt=0)
target_temperature: Optional[float] = None
target_moisture: Optional[float] = Field(None, ge=0, le=100)
tolerance_percentage: Optional[float] = Field(None, ge=0, le=100)
corrective_actions: Optional[List[str]] = None
class QualityCheckResponse(BaseModel):
"""Schema for quality check response"""
id: UUID
tenant_id: UUID
batch_id: UUID
check_type: str
check_time: datetime
checker_id: Optional[str]
quality_score: float
pass_fail: bool
defect_count: int
defect_types: Optional[List[str]]
measured_weight: Optional[float]
measured_temperature: Optional[float]
measured_moisture: Optional[float]
measured_dimensions: Optional[Dict[str, float]]
target_weight: Optional[float]
target_temperature: Optional[float]
target_moisture: Optional[float]
tolerance_percentage: Optional[float]
within_tolerance: Optional[bool]
corrective_action_needed: bool
corrective_actions: Optional[List[str]]
check_notes: Optional[str]
photos_urls: Optional[List[str]]
certificate_url: Optional[str]
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
# ================================================================
# DASHBOARD AND ANALYTICS SCHEMAS
# ================================================================
class ProductionDashboardSummary(BaseModel):
"""Schema for production dashboard summary"""
active_batches: int
todays_production_plan: List[Dict[str, Any]]
capacity_utilization: float
on_time_completion_rate: float
average_quality_score: float
total_output_today: float
efficiency_percentage: float
class DailyProductionRequirements(BaseModel):
"""Schema for daily production requirements"""
date: date
production_plan: List[Dict[str, Any]]
total_capacity_needed: float
available_capacity: float
capacity_gap: float
urgent_items: int
recommended_schedule: Optional[Dict[str, Any]]
class ProductionMetrics(BaseModel):
"""Schema for production metrics"""
period_start: date
period_end: date
total_batches: int
completed_batches: int
completion_rate: float
average_yield_percentage: float
on_time_completion_rate: float
total_production_cost: float
average_quality_score: float
efficiency_trends: List[Dict[str, Any]]
# ================================================================
# REQUEST/RESPONSE WRAPPERS
# ================================================================
class ProductionBatchListResponse(BaseModel):
"""Schema for production batch list response"""
batches: List[ProductionBatchResponse]
total_count: int
page: int
page_size: int
class ProductionScheduleListResponse(BaseModel):
"""Schema for production schedule list response"""
schedules: List[ProductionScheduleResponse]
total_count: int
page: int
page_size: int
class QualityCheckListResponse(BaseModel):
"""Schema for quality check list response"""
quality_checks: List[QualityCheckResponse]
total_count: int
page: int
page_size: int

View File

@@ -0,0 +1,180 @@
# services/production/app/schemas/quality_templates.py
"""
Quality Check Template Pydantic schemas for validation and serialization
"""
from pydantic import BaseModel, Field, validator
from typing import Optional, List, Dict, Any, Union
from uuid import UUID
from datetime import datetime
from enum import Enum
from ..models.production import ProcessStage
class QualityCheckType(str, Enum):
"""Quality check types"""
VISUAL = "visual"
MEASUREMENT = "measurement"
TEMPERATURE = "temperature"
WEIGHT = "weight"
BOOLEAN = "boolean"
TIMING = "timing"
CHECKLIST = "checklist"
class QualityCheckTemplateBase(BaseModel):
"""Base schema for quality check templates"""
name: str = Field(..., min_length=1, max_length=255, description="Template name")
template_code: Optional[str] = Field(None, max_length=100, description="Template code for reference")
check_type: QualityCheckType = Field(..., description="Type of quality check")
category: Optional[str] = Field(None, max_length=100, description="Check category (e.g., appearance, structure)")
description: Optional[str] = Field(None, description="Template description")
instructions: Optional[str] = Field(None, description="Check instructions for staff")
# Configuration
parameters: Optional[Dict[str, Any]] = Field(None, description="Dynamic check parameters")
thresholds: Optional[Dict[str, Any]] = Field(None, description="Pass/fail criteria")
scoring_criteria: Optional[Dict[str, Any]] = Field(None, description="Scoring methodology")
# Settings
is_active: bool = Field(True, description="Whether template is active")
is_required: bool = Field(False, description="Whether check is required")
is_critical: bool = Field(False, description="Whether failure blocks production")
weight: float = Field(1.0, ge=0.0, le=10.0, description="Weight in overall quality score")
# Measurement specifications
min_value: Optional[float] = Field(None, description="Minimum acceptable value")
max_value: Optional[float] = Field(None, description="Maximum acceptable value")
target_value: Optional[float] = Field(None, description="Target value")
unit: Optional[str] = Field(None, max_length=20, description="Unit of measurement")
tolerance_percentage: Optional[float] = Field(None, ge=0.0, le=100.0, description="Tolerance percentage")
# Process stage applicability
applicable_stages: Optional[List[ProcessStage]] = Field(None, description="Applicable process stages")
@validator('applicable_stages')
def validate_stages(cls, v):
if v is not None:
# Ensure all values are valid ProcessStage enums
for stage in v:
if stage not in ProcessStage:
raise ValueError(f"Invalid process stage: {stage}")
return v
@validator('min_value', 'max_value', 'target_value')
def validate_measurement_values(cls, v, values):
if v is not None and values.get('check_type') not in [QualityCheckType.MEASUREMENT, QualityCheckType.TEMPERATURE, QualityCheckType.WEIGHT]:
return None # Clear values for non-measurement types
return v
class QualityCheckTemplateCreate(QualityCheckTemplateBase):
"""Schema for creating quality check templates"""
created_by: UUID = Field(..., description="User ID who created the template")
class QualityCheckTemplateUpdate(BaseModel):
"""Schema for updating quality check templates"""
name: Optional[str] = Field(None, min_length=1, max_length=255)
template_code: Optional[str] = Field(None, max_length=100)
check_type: Optional[QualityCheckType] = None
category: Optional[str] = Field(None, max_length=100)
description: Optional[str] = None
instructions: Optional[str] = None
parameters: Optional[Dict[str, Any]] = None
thresholds: Optional[Dict[str, Any]] = None
scoring_criteria: Optional[Dict[str, Any]] = None
is_active: Optional[bool] = None
is_required: Optional[bool] = None
is_critical: Optional[bool] = None
weight: Optional[float] = Field(None, ge=0.0, le=10.0)
min_value: Optional[float] = None
max_value: Optional[float] = None
target_value: Optional[float] = None
unit: Optional[str] = Field(None, max_length=20)
tolerance_percentage: Optional[float] = Field(None, ge=0.0, le=100.0)
applicable_stages: Optional[List[ProcessStage]] = None
class QualityCheckTemplateResponse(QualityCheckTemplateBase):
"""Schema for quality check template responses"""
id: UUID
tenant_id: UUID
created_by: UUID
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class QualityCheckTemplateList(BaseModel):
"""Schema for paginated quality check template lists"""
templates: List[QualityCheckTemplateResponse]
total: int
skip: int
limit: int
class QualityCheckCriterion(BaseModel):
"""Individual quality check criterion within a template"""
id: str = Field(..., description="Unique criterion identifier")
name: str = Field(..., description="Criterion name")
description: str = Field(..., description="Criterion description")
check_type: QualityCheckType = Field(..., description="Type of check")
required: bool = Field(True, description="Whether criterion is required")
weight: float = Field(1.0, ge=0.0, le=10.0, description="Weight in template score")
acceptable_criteria: str = Field(..., description="Description of acceptable criteria")
min_value: Optional[float] = None
max_value: Optional[float] = None
unit: Optional[str] = None
is_critical: bool = Field(False, description="Whether failure is critical")
class QualityCheckResult(BaseModel):
"""Result of a quality check criterion"""
criterion_id: str = Field(..., description="Criterion identifier")
value: Union[float, str, bool] = Field(..., description="Check result value")
score: float = Field(..., ge=0.0, le=10.0, description="Score for this criterion")
notes: Optional[str] = Field(None, description="Additional notes")
photos: Optional[List[str]] = Field(None, description="Photo URLs")
pass_check: bool = Field(..., description="Whether criterion passed")
timestamp: datetime = Field(..., description="When check was performed")
class QualityCheckExecutionRequest(BaseModel):
"""Schema for executing a quality check using a template"""
template_id: UUID = Field(..., description="Quality check template ID")
batch_id: UUID = Field(..., description="Production batch ID")
process_stage: ProcessStage = Field(..., description="Current process stage")
checker_id: Optional[str] = Field(None, description="Staff member performing check")
results: List[QualityCheckResult] = Field(..., description="Check results")
final_notes: Optional[str] = Field(None, description="Final notes")
photos: Optional[List[str]] = Field(None, description="Additional photo URLs")
class QualityCheckExecutionResponse(BaseModel):
"""Schema for quality check execution results"""
check_id: UUID = Field(..., description="Created quality check ID")
overall_score: float = Field(..., ge=0.0, le=10.0, description="Overall quality score")
overall_pass: bool = Field(..., description="Whether check passed overall")
critical_failures: List[str] = Field(..., description="List of critical failures")
corrective_actions: List[str] = Field(..., description="Recommended corrective actions")
timestamp: datetime = Field(..., description="When check was completed")
class ProcessStageQualityConfig(BaseModel):
"""Configuration for quality checks at a specific process stage"""
stage: ProcessStage = Field(..., description="Process stage")
template_ids: List[UUID] = Field(..., description="Required template IDs")
custom_parameters: Optional[Dict[str, Any]] = Field(None, description="Stage-specific parameters")
is_required: bool = Field(True, description="Whether stage requires quality checks")
blocking: bool = Field(True, description="Whether stage blocks on failed checks")
class RecipeQualityConfiguration(BaseModel):
"""Quality check configuration for a recipe"""
stages: Dict[str, ProcessStageQualityConfig] = Field(..., description="Stage configurations")
global_parameters: Optional[Dict[str, Any]] = Field(None, description="Global quality parameters")
default_templates: Optional[List[UUID]] = Field(None, description="Default template IDs")