Files
bakery-ia/services/production/app/models/production.py

609 lines
27 KiB
Python
Raw Normal View History

2025-08-21 20:28:14 +02:00
# ================================================================
# services/production/app/models/production.py
# ================================================================
"""
Production models for the production service
"""
from sqlalchemy import Column, String, Integer, Float, DateTime, Boolean, Text, JSON, Enum as SQLEnum
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import deferred
2025-08-21 20:28:14 +02:00
from sqlalchemy.sql import func
from datetime import datetime, timezone
from typing import Dict, Any, Optional
import uuid
import enum
from shared.database.base import Base
class ProductionStatus(str, enum.Enum):
"""Production batch status enumeration"""
2025-09-21 07:45:19 +02:00
PENDING = "PENDING"
IN_PROGRESS = "IN_PROGRESS"
COMPLETED = "COMPLETED"
CANCELLED = "CANCELLED"
ON_HOLD = "ON_HOLD"
QUALITY_CHECK = "QUALITY_CHECK"
FAILED = "FAILED"
2025-08-21 20:28:14 +02:00
class ProductionPriority(str, enum.Enum):
"""Production priority levels"""
2025-09-21 07:45:19 +02:00
LOW = "LOW"
MEDIUM = "MEDIUM"
HIGH = "HIGH"
URGENT = "URGENT"
2025-08-21 20:28:14 +02:00
2025-09-23 19:24:22 +02:00
class EquipmentStatus(str, enum.Enum):
"""Equipment status enumeration"""
OPERATIONAL = "operational"
MAINTENANCE = "maintenance"
DOWN = "down"
WARNING = "warning"
2025-09-24 16:42:23 +02:00
class ProcessStage(str, enum.Enum):
"""Production process stages where quality checks can occur"""
MIXING = "mixing"
PROOFING = "proofing"
SHAPING = "shaping"
BAKING = "baking"
COOLING = "cooling"
PACKAGING = "packaging"
FINISHING = "finishing"
2025-09-23 19:24:22 +02:00
class EquipmentType(str, enum.Enum):
"""Equipment type enumeration"""
OVEN = "oven"
MIXER = "mixer"
PROOFER = "proofer"
FREEZER = "freezer"
PACKAGING = "packaging"
OTHER = "other"
2025-08-21 20:28:14 +02:00
class ProductionBatch(Base):
"""Production batch model for tracking individual production runs"""
__tablename__ = "production_batches"
# Primary identification
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
batch_number = Column(String(50), nullable=False, unique=True, index=True)
# Product and recipe information
product_id = Column(UUID(as_uuid=True), nullable=False, index=True) # Reference to inventory/recipes
product_name = Column(String(255), nullable=False)
recipe_id = Column(UUID(as_uuid=True), nullable=True)
# Production planning
planned_start_time = Column(DateTime(timezone=True), nullable=False)
planned_end_time = Column(DateTime(timezone=True), nullable=False)
planned_quantity = Column(Float, nullable=False)
planned_duration_minutes = Column(Integer, nullable=False)
2025-09-23 19:24:22 +02:00
2025-08-21 20:28:14 +02:00
# Actual production tracking
actual_start_time = Column(DateTime(timezone=True), nullable=True)
actual_end_time = Column(DateTime(timezone=True), nullable=True)
actual_quantity = Column(Float, nullable=True)
actual_duration_minutes = Column(Integer, nullable=True)
2025-09-23 19:24:22 +02:00
2025-08-21 20:28:14 +02:00
# Status and priority
status = Column(SQLEnum(ProductionStatus), nullable=False, default=ProductionStatus.PENDING, index=True)
priority = Column(SQLEnum(ProductionPriority), nullable=False, default=ProductionPriority.MEDIUM)
2025-09-23 19:24:22 +02:00
# Process stage tracking
current_process_stage = Column(SQLEnum(ProcessStage), nullable=True, index=True)
process_stage_history = Column(JSON, nullable=True) # Track stage transitions with timestamps
pending_quality_checks = Column(JSON, nullable=True) # Required quality checks for current stage
completed_quality_checks = Column(JSON, nullable=True) # Completed quality checks by stage
2025-08-21 20:28:14 +02:00
# Cost tracking
estimated_cost = Column(Float, nullable=True)
actual_cost = Column(Float, nullable=True)
labor_cost = Column(Float, nullable=True)
material_cost = Column(Float, nullable=True)
overhead_cost = Column(Float, nullable=True)
# Quality metrics
yield_percentage = Column(Float, nullable=True) # actual/planned quantity
quality_score = Column(Float, nullable=True)
waste_quantity = Column(Float, nullable=True)
defect_quantity = Column(Float, nullable=True)
2025-10-24 13:05:04 +02:00
waste_defect_type = Column(String(100), nullable=True) # Type of defect causing waste (burnt, misshapen, underproofed, temperature_issues, expired)
2025-08-21 20:28:14 +02:00
# Equipment and resources
equipment_used = Column(JSON, nullable=True) # List of equipment IDs
staff_assigned = Column(JSON, nullable=True) # List of staff IDs
station_id = Column(String(50), nullable=True)
# Business context
order_id = Column(UUID(as_uuid=True), nullable=True) # Associated customer order
forecast_id = Column(UUID(as_uuid=True), nullable=True) # Associated demand forecast
is_rush_order = Column(Boolean, default=False)
is_special_recipe = Column(Boolean, default=False)
2025-10-24 13:05:04 +02:00
is_ai_assisted = Column(Boolean, default=False) # Whether batch used AI forecasting/optimization
2025-08-21 20:28:14 +02:00
# Notes and tracking
production_notes = Column(Text, nullable=True)
quality_notes = Column(Text, nullable=True)
delay_reason = Column(String(255), nullable=True)
cancellation_reason = Column(String(255), nullable=True)
feat: Complete JTBD-aligned bakery dashboard redesign Implements comprehensive dashboard redesign based on Jobs To Be Done methodology focused on answering: "What requires my attention right now?" ## Backend Implementation ### Dashboard Service (NEW) - Health status calculation (green/yellow/red traffic light) - Action queue prioritization (critical/important/normal) - Orchestration summary with narrative format - Production timeline transformation - Insights calculation and consequence prediction ### API Endpoints (NEW) - GET /dashboard/health-status - Overall bakery health indicator - GET /dashboard/orchestration-summary - What system did automatically - GET /dashboard/action-queue - Prioritized tasks requiring attention - GET /dashboard/production-timeline - Today's production schedule - GET /dashboard/insights - Key metrics (savings, inventory, waste, deliveries) ### Enhanced Models - PurchaseOrder: Added reasoning, consequence, reasoning_data fields - ProductionBatch: Added reasoning, reasoning_data fields - Enables transparency into automation decisions ## Frontend Implementation ### API Hooks (NEW) - useBakeryHealthStatus() - Real-time health monitoring - useOrchestrationSummary() - System transparency - useActionQueue() - Prioritized action management - useProductionTimeline() - Production tracking - useInsights() - Glanceable metrics ### Dashboard Components (NEW) - HealthStatusCard: Traffic light indicator with checklist - ActionQueueCard: Prioritized actions with reasoning/consequences - OrchestrationSummaryCard: Narrative of what system did - ProductionTimelineCard: Chronological production view - InsightsGrid: 2x2 grid of key metrics ### Main Dashboard Page (REPLACED) - Complete rewrite with mobile-first design - All sections integrated with error handling - Real-time refresh and quick action links - Old dashboard backed up as DashboardPage.legacy.tsx ## Key Features ### Automation-First - Shows what orchestrator did overnight - Builds trust through transparency - Explains reasoning for all automated decisions ### Action-Oriented - Prioritizes tasks over information display - Clear consequences for each action - Large touch-friendly buttons ### Progressive Disclosure - Shows 20% of info that matters 80% of time - Expandable details when needed - No overwhelming metrics ### Mobile-First - One-handed operation - Large touch targets (min 44px) - Responsive grid layouts ### Trust-Building - Narrative format ("I planned your day") - Reasoning inputs transparency - Clear status indicators ## User Segments Supported 1. Solo Bakery Owner (Primary) - Simple health indicator - Action checklist (max 3-5 items) - Mobile-optimized 2. Multi-Location Owner - Multi-tenant support (existing) - Comparison capabilities - Delegation ready 3. Enterprise/Central Bakery (Future) - Network topology support - Advanced analytics ready ## JTBD Analysis Delivered Main Job: "Help me quickly understand bakery status and know what needs my intervention" Emotional Jobs Addressed: - Feel in control despite automation - Reduce daily anxiety - Feel competent with technology - Trust system as safety net Social Jobs Addressed: - Demonstrate professional management - Avoid being bottleneck - Show sustainability ## Technical Stack Backend: Python, FastAPI, SQLAlchemy, PostgreSQL Frontend: React, TypeScript, TanStack Query, Tailwind CSS Architecture: Microservices with circuit breakers ## Breaking Changes - Complete dashboard page rewrite (old version backed up) - New API endpoints require orchestrator service deployment - Database migrations needed for reasoning fields ## Migration Required Run migrations to add new model fields: - purchase_orders: reasoning, consequence, reasoning_data - production_batches: reasoning, reasoning_data ## Documentation See DASHBOARD_REDESIGN_SUMMARY.md for complete implementation details, JTBD analysis, success metrics, and deployment guide. BREAKING CHANGE: Dashboard page completely redesigned with new data structures
2025-11-07 17:10:17 +00:00
2025-11-07 18:20:05 +00:00
# JTBD Dashboard: Structured reasoning data for i18n support
# Backend stores structured data, frontend translates using i18n
reasoning_data = Column(JSON, nullable=True) # Structured reasoning data for multilingual support
# reasoning_data structure (see shared/schemas/reasoning_types.py):
# {
# "type": "forecast_demand" | "customer_order" | "stock_replenishment" | etc.,
# "parameters": {
# "product_name": "Croissant",
# "predicted_demand": 500,
# "current_stock": 120,
# "production_needed": 380,
# "confidence_score": 87
# },
# "urgency": {
# "level": "normal",
# "ready_by_time": "08:00",
# "customer_commitment": false
# },
# "metadata": {
# "trigger_source": "orchestrator_auto",
# "forecast_id": "uuid-here",
# "ai_assisted": true
# }
feat: Complete JTBD-aligned bakery dashboard redesign Implements comprehensive dashboard redesign based on Jobs To Be Done methodology focused on answering: "What requires my attention right now?" ## Backend Implementation ### Dashboard Service (NEW) - Health status calculation (green/yellow/red traffic light) - Action queue prioritization (critical/important/normal) - Orchestration summary with narrative format - Production timeline transformation - Insights calculation and consequence prediction ### API Endpoints (NEW) - GET /dashboard/health-status - Overall bakery health indicator - GET /dashboard/orchestration-summary - What system did automatically - GET /dashboard/action-queue - Prioritized tasks requiring attention - GET /dashboard/production-timeline - Today's production schedule - GET /dashboard/insights - Key metrics (savings, inventory, waste, deliveries) ### Enhanced Models - PurchaseOrder: Added reasoning, consequence, reasoning_data fields - ProductionBatch: Added reasoning, reasoning_data fields - Enables transparency into automation decisions ## Frontend Implementation ### API Hooks (NEW) - useBakeryHealthStatus() - Real-time health monitoring - useOrchestrationSummary() - System transparency - useActionQueue() - Prioritized action management - useProductionTimeline() - Production tracking - useInsights() - Glanceable metrics ### Dashboard Components (NEW) - HealthStatusCard: Traffic light indicator with checklist - ActionQueueCard: Prioritized actions with reasoning/consequences - OrchestrationSummaryCard: Narrative of what system did - ProductionTimelineCard: Chronological production view - InsightsGrid: 2x2 grid of key metrics ### Main Dashboard Page (REPLACED) - Complete rewrite with mobile-first design - All sections integrated with error handling - Real-time refresh and quick action links - Old dashboard backed up as DashboardPage.legacy.tsx ## Key Features ### Automation-First - Shows what orchestrator did overnight - Builds trust through transparency - Explains reasoning for all automated decisions ### Action-Oriented - Prioritizes tasks over information display - Clear consequences for each action - Large touch-friendly buttons ### Progressive Disclosure - Shows 20% of info that matters 80% of time - Expandable details when needed - No overwhelming metrics ### Mobile-First - One-handed operation - Large touch targets (min 44px) - Responsive grid layouts ### Trust-Building - Narrative format ("I planned your day") - Reasoning inputs transparency - Clear status indicators ## User Segments Supported 1. Solo Bakery Owner (Primary) - Simple health indicator - Action checklist (max 3-5 items) - Mobile-optimized 2. Multi-Location Owner - Multi-tenant support (existing) - Comparison capabilities - Delegation ready 3. Enterprise/Central Bakery (Future) - Network topology support - Advanced analytics ready ## JTBD Analysis Delivered Main Job: "Help me quickly understand bakery status and know what needs my intervention" Emotional Jobs Addressed: - Feel in control despite automation - Reduce daily anxiety - Feel competent with technology - Trust system as safety net Social Jobs Addressed: - Demonstrate professional management - Avoid being bottleneck - Show sustainability ## Technical Stack Backend: Python, FastAPI, SQLAlchemy, PostgreSQL Frontend: React, TypeScript, TanStack Query, Tailwind CSS Architecture: Microservices with circuit breakers ## Breaking Changes - Complete dashboard page rewrite (old version backed up) - New API endpoints require orchestrator service deployment - Database migrations needed for reasoning fields ## Migration Required Run migrations to add new model fields: - purchase_orders: reasoning, consequence, reasoning_data - production_batches: reasoning, reasoning_data ## Documentation See DASHBOARD_REDESIGN_SUMMARY.md for complete implementation details, JTBD analysis, success metrics, and deployment guide. BREAKING CHANGE: Dashboard page completely redesigned with new data structures
2025-11-07 17:10:17 +00:00
# }
2025-08-21 20:28:14 +02:00
# Timestamps
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
completed_at = Column(DateTime(timezone=True), nullable=True)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary following shared pattern"""
return {
"id": str(self.id),
"tenant_id": str(self.tenant_id),
"batch_number": self.batch_number,
"product_id": str(self.product_id),
"product_name": self.product_name,
"recipe_id": str(self.recipe_id) if self.recipe_id else None,
"planned_start_time": self.planned_start_time.isoformat() if self.planned_start_time else None,
"planned_end_time": self.planned_end_time.isoformat() if self.planned_end_time else None,
"planned_quantity": self.planned_quantity,
"planned_duration_minutes": self.planned_duration_minutes,
"actual_start_time": self.actual_start_time.isoformat() if self.actual_start_time else None,
"actual_end_time": self.actual_end_time.isoformat() if self.actual_end_time else None,
"actual_quantity": self.actual_quantity,
"actual_duration_minutes": self.actual_duration_minutes,
"status": self.status.value if self.status else None,
"priority": self.priority.value if self.priority else None,
"estimated_cost": self.estimated_cost,
"actual_cost": self.actual_cost,
"labor_cost": self.labor_cost,
"material_cost": self.material_cost,
"overhead_cost": self.overhead_cost,
"yield_percentage": self.yield_percentage,
"quality_score": self.quality_score,
"waste_quantity": self.waste_quantity,
"defect_quantity": self.defect_quantity,
2025-10-24 13:05:04 +02:00
"waste_defect_type": self.waste_defect_type,
2025-08-21 20:28:14 +02:00
"equipment_used": self.equipment_used,
"staff_assigned": self.staff_assigned,
"station_id": self.station_id,
"order_id": str(self.order_id) if self.order_id else None,
"forecast_id": str(self.forecast_id) if self.forecast_id else None,
"is_rush_order": self.is_rush_order,
"is_special_recipe": self.is_special_recipe,
2025-10-24 13:05:04 +02:00
"is_ai_assisted": self.is_ai_assisted,
2025-08-21 20:28:14 +02:00
"production_notes": self.production_notes,
"quality_notes": self.quality_notes,
"delay_reason": self.delay_reason,
"cancellation_reason": self.cancellation_reason,
feat: Complete JTBD-aligned bakery dashboard redesign Implements comprehensive dashboard redesign based on Jobs To Be Done methodology focused on answering: "What requires my attention right now?" ## Backend Implementation ### Dashboard Service (NEW) - Health status calculation (green/yellow/red traffic light) - Action queue prioritization (critical/important/normal) - Orchestration summary with narrative format - Production timeline transformation - Insights calculation and consequence prediction ### API Endpoints (NEW) - GET /dashboard/health-status - Overall bakery health indicator - GET /dashboard/orchestration-summary - What system did automatically - GET /dashboard/action-queue - Prioritized tasks requiring attention - GET /dashboard/production-timeline - Today's production schedule - GET /dashboard/insights - Key metrics (savings, inventory, waste, deliveries) ### Enhanced Models - PurchaseOrder: Added reasoning, consequence, reasoning_data fields - ProductionBatch: Added reasoning, reasoning_data fields - Enables transparency into automation decisions ## Frontend Implementation ### API Hooks (NEW) - useBakeryHealthStatus() - Real-time health monitoring - useOrchestrationSummary() - System transparency - useActionQueue() - Prioritized action management - useProductionTimeline() - Production tracking - useInsights() - Glanceable metrics ### Dashboard Components (NEW) - HealthStatusCard: Traffic light indicator with checklist - ActionQueueCard: Prioritized actions with reasoning/consequences - OrchestrationSummaryCard: Narrative of what system did - ProductionTimelineCard: Chronological production view - InsightsGrid: 2x2 grid of key metrics ### Main Dashboard Page (REPLACED) - Complete rewrite with mobile-first design - All sections integrated with error handling - Real-time refresh and quick action links - Old dashboard backed up as DashboardPage.legacy.tsx ## Key Features ### Automation-First - Shows what orchestrator did overnight - Builds trust through transparency - Explains reasoning for all automated decisions ### Action-Oriented - Prioritizes tasks over information display - Clear consequences for each action - Large touch-friendly buttons ### Progressive Disclosure - Shows 20% of info that matters 80% of time - Expandable details when needed - No overwhelming metrics ### Mobile-First - One-handed operation - Large touch targets (min 44px) - Responsive grid layouts ### Trust-Building - Narrative format ("I planned your day") - Reasoning inputs transparency - Clear status indicators ## User Segments Supported 1. Solo Bakery Owner (Primary) - Simple health indicator - Action checklist (max 3-5 items) - Mobile-optimized 2. Multi-Location Owner - Multi-tenant support (existing) - Comparison capabilities - Delegation ready 3. Enterprise/Central Bakery (Future) - Network topology support - Advanced analytics ready ## JTBD Analysis Delivered Main Job: "Help me quickly understand bakery status and know what needs my intervention" Emotional Jobs Addressed: - Feel in control despite automation - Reduce daily anxiety - Feel competent with technology - Trust system as safety net Social Jobs Addressed: - Demonstrate professional management - Avoid being bottleneck - Show sustainability ## Technical Stack Backend: Python, FastAPI, SQLAlchemy, PostgreSQL Frontend: React, TypeScript, TanStack Query, Tailwind CSS Architecture: Microservices with circuit breakers ## Breaking Changes - Complete dashboard page rewrite (old version backed up) - New API endpoints require orchestrator service deployment - Database migrations needed for reasoning fields ## Migration Required Run migrations to add new model fields: - purchase_orders: reasoning, consequence, reasoning_data - production_batches: reasoning, reasoning_data ## Documentation See DASHBOARD_REDESIGN_SUMMARY.md for complete implementation details, JTBD analysis, success metrics, and deployment guide. BREAKING CHANGE: Dashboard page completely redesigned with new data structures
2025-11-07 17:10:17 +00:00
"reasoning_data": self.reasoning_data,
2025-08-21 20:28:14 +02:00
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
}
class ProductionSchedule(Base):
"""Production schedule model for planning and tracking daily production"""
__tablename__ = "production_schedules"
# Primary identification
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
# Schedule information
schedule_date = Column(DateTime(timezone=True), nullable=False, index=True)
shift_start = Column(DateTime(timezone=True), nullable=False)
shift_end = Column(DateTime(timezone=True), nullable=False)
# Capacity planning
total_capacity_hours = Column(Float, nullable=False)
planned_capacity_hours = Column(Float, nullable=False)
actual_capacity_hours = Column(Float, nullable=True)
overtime_hours = Column(Float, nullable=True, default=0.0)
# Staff and equipment
staff_count = Column(Integer, nullable=False)
equipment_capacity = Column(JSON, nullable=True) # Equipment availability
station_assignments = Column(JSON, nullable=True) # Station schedules
# Production metrics
total_batches_planned = Column(Integer, nullable=False, default=0)
total_batches_completed = Column(Integer, nullable=True, default=0)
total_quantity_planned = Column(Float, nullable=False, default=0.0)
total_quantity_produced = Column(Float, nullable=True, default=0.0)
# Status tracking
is_finalized = Column(Boolean, default=False)
is_active = Column(Boolean, default=True)
# Performance metrics
efficiency_percentage = Column(Float, nullable=True)
utilization_percentage = Column(Float, nullable=True)
on_time_completion_rate = Column(Float, nullable=True)
# Notes and adjustments
schedule_notes = Column(Text, nullable=True)
schedule_adjustments = Column(JSON, nullable=True) # Track changes made
# Timestamps
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
finalized_at = Column(DateTime(timezone=True), nullable=True)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary following shared pattern"""
return {
"id": str(self.id),
"tenant_id": str(self.tenant_id),
"schedule_date": self.schedule_date.isoformat() if self.schedule_date else None,
"shift_start": self.shift_start.isoformat() if self.shift_start else None,
"shift_end": self.shift_end.isoformat() if self.shift_end else None,
"total_capacity_hours": self.total_capacity_hours,
"planned_capacity_hours": self.planned_capacity_hours,
"actual_capacity_hours": self.actual_capacity_hours,
"overtime_hours": self.overtime_hours,
"staff_count": self.staff_count,
"equipment_capacity": self.equipment_capacity,
"station_assignments": self.station_assignments,
"total_batches_planned": self.total_batches_planned,
"total_batches_completed": self.total_batches_completed,
"total_quantity_planned": self.total_quantity_planned,
"total_quantity_produced": self.total_quantity_produced,
"is_finalized": self.is_finalized,
"is_active": self.is_active,
"efficiency_percentage": self.efficiency_percentage,
"utilization_percentage": self.utilization_percentage,
"on_time_completion_rate": self.on_time_completion_rate,
"schedule_notes": self.schedule_notes,
"schedule_adjustments": self.schedule_adjustments,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
"finalized_at": self.finalized_at.isoformat() if self.finalized_at else None,
}
class ProductionCapacity(Base):
"""Production capacity model for tracking equipment and resource availability"""
__tablename__ = "production_capacity"
# Primary identification
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
# Capacity definition
resource_type = Column(String(50), nullable=False) # equipment, staff, station
resource_id = Column(String(100), nullable=False)
resource_name = Column(String(255), nullable=False)
# Time period
date = Column(DateTime(timezone=True), nullable=False, index=True)
start_time = Column(DateTime(timezone=True), nullable=False)
end_time = Column(DateTime(timezone=True), nullable=False)
# Capacity metrics
total_capacity_units = Column(Float, nullable=False) # Total available capacity
allocated_capacity_units = Column(Float, nullable=False, default=0.0)
remaining_capacity_units = Column(Float, nullable=False)
# Status
is_available = Column(Boolean, default=True)
is_maintenance = Column(Boolean, default=False)
is_reserved = Column(Boolean, default=False)
# Equipment specific
equipment_type = Column(String(100), nullable=True)
max_batch_size = Column(Float, nullable=True)
min_batch_size = Column(Float, nullable=True)
setup_time_minutes = Column(Integer, nullable=True)
cleanup_time_minutes = Column(Integer, nullable=True)
# Performance tracking
efficiency_rating = Column(Float, nullable=True)
maintenance_status = Column(String(50), nullable=True)
last_maintenance_date = Column(DateTime(timezone=True), nullable=True)
# Notes
notes = Column(Text, nullable=True)
restrictions = Column(JSON, nullable=True) # Product type restrictions, etc.
# Timestamps
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary following shared pattern"""
return {
"id": str(self.id),
"tenant_id": str(self.tenant_id),
"resource_type": self.resource_type,
"resource_id": self.resource_id,
"resource_name": self.resource_name,
"date": self.date.isoformat() if self.date else None,
"start_time": self.start_time.isoformat() if self.start_time else None,
"end_time": self.end_time.isoformat() if self.end_time else None,
"total_capacity_units": self.total_capacity_units,
"allocated_capacity_units": self.allocated_capacity_units,
"remaining_capacity_units": self.remaining_capacity_units,
"is_available": self.is_available,
"is_maintenance": self.is_maintenance,
"is_reserved": self.is_reserved,
"equipment_type": self.equipment_type,
"max_batch_size": self.max_batch_size,
"min_batch_size": self.min_batch_size,
"setup_time_minutes": self.setup_time_minutes,
"cleanup_time_minutes": self.cleanup_time_minutes,
"efficiency_rating": self.efficiency_rating,
"maintenance_status": self.maintenance_status,
"last_maintenance_date": self.last_maintenance_date.isoformat() if self.last_maintenance_date else None,
"notes": self.notes,
"restrictions": self.restrictions,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}
2025-09-23 19:24:22 +02:00
class QualityCheckTemplate(Base):
"""Quality check templates for tenant-specific quality standards"""
__tablename__ = "quality_check_templates"
# Primary identification
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
# Template identification
name = Column(String(255), nullable=False)
template_code = Column(String(100), nullable=True, index=True)
check_type = Column(String(50), nullable=False) # visual, measurement, temperature, weight, boolean
category = Column(String(100), nullable=True) # appearance, structure, texture, etc.
# Template configuration
description = Column(Text, nullable=True)
instructions = Column(Text, nullable=True)
parameters = Column(JSON, nullable=True) # Dynamic check parameters
thresholds = Column(JSON, nullable=True) # Pass/fail criteria
scoring_criteria = Column(JSON, nullable=True) # Scoring methodology
# Configurability settings
is_active = Column(Boolean, default=True)
is_required = Column(Boolean, default=False)
is_critical = Column(Boolean, default=False) # Critical failures block production
weight = Column(Float, default=1.0) # Weight in overall quality score
# Measurement specifications
min_value = Column(Float, nullable=True)
max_value = Column(Float, nullable=True)
target_value = Column(Float, nullable=True)
unit = Column(String(20), nullable=True)
tolerance_percentage = Column(Float, nullable=True)
# Process stage applicability
applicable_stages = Column(JSON, nullable=True) # List of ProcessStage values
# Metadata
created_by = Column(UUID(as_uuid=True), nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary following shared pattern"""
return {
"id": str(self.id),
"tenant_id": str(self.tenant_id),
"name": self.name,
"template_code": self.template_code,
"check_type": self.check_type,
"category": self.category,
"description": self.description,
"instructions": self.instructions,
"parameters": self.parameters,
"thresholds": self.thresholds,
"scoring_criteria": self.scoring_criteria,
"is_active": self.is_active,
"is_required": self.is_required,
"is_critical": self.is_critical,
"weight": self.weight,
"min_value": self.min_value,
"max_value": self.max_value,
"target_value": self.target_value,
"unit": self.unit,
"tolerance_percentage": self.tolerance_percentage,
"applicable_stages": self.applicable_stages,
"created_by": str(self.created_by),
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}
2025-08-21 20:28:14 +02:00
class QualityCheck(Base):
2025-09-23 19:24:22 +02:00
"""Quality check model for tracking production quality metrics with stage support"""
2025-08-21 20:28:14 +02:00
__tablename__ = "quality_checks"
2025-09-23 19:24:22 +02:00
2025-08-21 20:28:14 +02:00
# Primary identification
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
batch_id = Column(UUID(as_uuid=True), nullable=False, index=True) # FK to ProductionBatch
2025-09-23 19:24:22 +02:00
template_id = Column(UUID(as_uuid=True), nullable=True, index=True) # FK to QualityCheckTemplate
2025-08-21 20:28:14 +02:00
# Check information
check_type = Column(String(50), nullable=False) # visual, weight, temperature, etc.
2025-09-23 19:24:22 +02:00
process_stage = Column(SQLEnum(ProcessStage), nullable=True, index=True) # Stage when check was performed
2025-08-21 20:28:14 +02:00
check_time = Column(DateTime(timezone=True), nullable=False)
checker_id = Column(String(100), nullable=True) # Staff member who performed check
2025-09-23 19:24:22 +02:00
2025-08-21 20:28:14 +02:00
# Quality metrics
quality_score = Column(Float, nullable=False) # 1-10 scale
pass_fail = Column(Boolean, nullable=False)
defect_count = Column(Integer, nullable=False, default=0)
defect_types = Column(JSON, nullable=True) # List of defect categories
2025-09-23 19:24:22 +02:00
2025-08-21 20:28:14 +02:00
# Measurements
measured_weight = Column(Float, nullable=True)
measured_temperature = Column(Float, nullable=True)
measured_moisture = Column(Float, nullable=True)
measured_dimensions = Column(JSON, nullable=True)
2025-09-23 19:24:22 +02:00
stage_specific_data = Column(JSON, nullable=True) # Stage-specific measurements
2025-08-21 20:28:14 +02:00
# Standards comparison
target_weight = Column(Float, nullable=True)
target_temperature = Column(Float, nullable=True)
target_moisture = Column(Float, nullable=True)
tolerance_percentage = Column(Float, nullable=True)
2025-09-23 19:24:22 +02:00
2025-08-21 20:28:14 +02:00
# Results
within_tolerance = Column(Boolean, nullable=True)
corrective_action_needed = Column(Boolean, default=False)
corrective_actions = Column(JSON, nullable=True)
2025-09-23 19:24:22 +02:00
# Template-based results
template_results = Column(JSON, nullable=True) # Results from template-based checks
criteria_scores = Column(JSON, nullable=True) # Individual criteria scores
2025-08-21 20:28:14 +02:00
# Notes and documentation
check_notes = Column(Text, nullable=True)
photos_urls = Column(JSON, nullable=True) # URLs to quality check photos
certificate_url = Column(String(500), nullable=True)
2025-09-23 19:24:22 +02:00
2025-08-21 20:28:14 +02:00
# Timestamps
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary following shared pattern"""
return {
"id": str(self.id),
"tenant_id": str(self.tenant_id),
"batch_id": str(self.batch_id),
"check_type": self.check_type,
"check_time": self.check_time.isoformat() if self.check_time else None,
"checker_id": self.checker_id,
"quality_score": self.quality_score,
"pass_fail": self.pass_fail,
"defect_count": self.defect_count,
"defect_types": self.defect_types,
"measured_weight": self.measured_weight,
"measured_temperature": self.measured_temperature,
"measured_moisture": self.measured_moisture,
"measured_dimensions": self.measured_dimensions,
"target_weight": self.target_weight,
"target_temperature": self.target_temperature,
"target_moisture": self.target_moisture,
"tolerance_percentage": self.tolerance_percentage,
"within_tolerance": self.within_tolerance,
"corrective_action_needed": self.corrective_action_needed,
"corrective_actions": self.corrective_actions,
"check_notes": self.check_notes,
"photos_urls": self.photos_urls,
"certificate_url": self.certificate_url,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}
2025-09-23 19:24:22 +02:00
class Equipment(Base):
"""Equipment model for tracking production equipment"""
__tablename__ = "equipment"
# Primary identification
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
# Equipment identification
name = Column(String(255), nullable=False)
type = Column(SQLEnum(EquipmentType), nullable=False)
model = Column(String(100), nullable=True)
serial_number = Column(String(100), nullable=True)
location = Column(String(255), nullable=True)
# Status tracking
status = Column(SQLEnum(EquipmentStatus), nullable=False, default=EquipmentStatus.OPERATIONAL)
# Dates
install_date = Column(DateTime(timezone=True), nullable=True)
last_maintenance_date = Column(DateTime(timezone=True), nullable=True)
next_maintenance_date = Column(DateTime(timezone=True), nullable=True)
maintenance_interval_days = Column(Integer, nullable=True) # Maintenance interval in days
# Performance metrics
efficiency_percentage = Column(Float, nullable=True) # Current efficiency
uptime_percentage = Column(Float, nullable=True) # Overall equipment effectiveness
energy_usage_kwh = Column(Float, nullable=True) # Current energy usage
# Specifications
power_kw = Column(Float, nullable=True) # Power in kilowatts
capacity = Column(Float, nullable=True) # Capacity (units depend on equipment type)
weight_kg = Column(Float, nullable=True) # Weight in kilograms
# Temperature monitoring
current_temperature = Column(Float, nullable=True) # Current temperature reading
target_temperature = Column(Float, nullable=True) # Target temperature
# Status
is_active = Column(Boolean, default=True)
# Notes
notes = Column(Text, nullable=True)
# Timestamps
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary following shared pattern"""
return {
"id": str(self.id),
"tenant_id": str(self.tenant_id),
"name": self.name,
"type": self.type.value if self.type else None,
"model": self.model,
"serial_number": self.serial_number,
"location": self.location,
"status": self.status.value if self.status else None,
"install_date": self.install_date.isoformat() if self.install_date else None,
"last_maintenance_date": self.last_maintenance_date.isoformat() if self.last_maintenance_date else None,
"next_maintenance_date": self.next_maintenance_date.isoformat() if self.next_maintenance_date else None,
"maintenance_interval_days": self.maintenance_interval_days,
"efficiency_percentage": self.efficiency_percentage,
"uptime_percentage": self.uptime_percentage,
"energy_usage_kwh": self.energy_usage_kwh,
"power_kw": self.power_kw,
"capacity": self.capacity,
"weight_kg": self.weight_kg,
"current_temperature": self.current_temperature,
"target_temperature": self.target_temperature,
"is_active": self.is_active,
"notes": self.notes,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}