# 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")