Add improved production UI 3

This commit is contained in:
Urtzi Alfaro
2025-09-23 19:24:22 +02:00
parent 7f871fc933
commit 7892c5a739
47 changed files with 6211 additions and 267 deletions

View File

@@ -35,6 +35,22 @@ class ProductionPriority(str, enum.Enum):
URGENT = "URGENT"
class EquipmentStatus(str, enum.Enum):
"""Equipment status enumeration"""
OPERATIONAL = "operational"
MAINTENANCE = "maintenance"
DOWN = "down"
WARNING = "warning"
class EquipmentType(str, enum.Enum):
"""Equipment type enumeration"""
OVEN = "oven"
MIXER = "mixer"
PROOFER = "proofer"
FREEZER = "freezer"
PACKAGING = "packaging"
OTHER = "other"
class ProductionBatch(Base):
@@ -56,16 +72,22 @@ class ProductionBatch(Base):
planned_end_time = Column(DateTime(timezone=True), nullable=False)
planned_quantity = Column(Float, nullable=False)
planned_duration_minutes = Column(Integer, nullable=False)
# 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)
# Status and priority
status = Column(SQLEnum(ProductionStatus), nullable=False, default=ProductionStatus.PENDING, index=True)
priority = Column(SQLEnum(ProductionPriority), nullable=False, default=ProductionPriority.MEDIUM)
# 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
# Cost tracking
estimated_cost = Column(Float, nullable=True)
@@ -307,48 +329,138 @@ class ProductionCapacity(Base):
}
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"
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,
}
class QualityCheck(Base):
"""Quality check model for tracking production quality metrics"""
"""Quality check model for tracking production quality metrics with stage support"""
__tablename__ = "quality_checks"
# 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
template_id = Column(UUID(as_uuid=True), nullable=True, index=True) # FK to QualityCheckTemplate
# Check information
check_type = Column(String(50), nullable=False) # visual, weight, temperature, etc.
process_stage = Column(SQLEnum(ProcessStage), nullable=True, index=True) # Stage when check was performed
check_time = Column(DateTime(timezone=True), nullable=False)
checker_id = Column(String(100), nullable=True) # Staff member who performed check
# 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
# 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)
stage_specific_data = Column(JSON, nullable=True) # Stage-specific measurements
# 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)
# Results
within_tolerance = Column(Boolean, nullable=True)
corrective_action_needed = Column(Boolean, default=False)
corrective_actions = Column(JSON, nullable=True)
# Template-based results
template_results = Column(JSON, nullable=True) # Results from template-based checks
criteria_scores = Column(JSON, nullable=True) # Individual criteria scores
# 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)
# Timestamps
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
@@ -385,3 +497,81 @@ class QualityCheck(Base):
}
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,
}