Add quality template logic

This commit is contained in:
Urtzi Alfaro
2025-09-24 16:42:23 +02:00
parent 474d7176bf
commit 2de1e6ce40
11 changed files with 450 additions and 228 deletions

View File

@@ -16,7 +16,7 @@ import type {
} from '../types/qualityTemplates';
class QualityTemplateService {
private readonly baseURL = '/production/api/v1/quality-templates';
private readonly baseURL = '/tenants';
/**
* Create a new quality check template
@@ -25,10 +25,10 @@ class QualityTemplateService {
tenantId: string,
templateData: QualityCheckTemplateCreate
): Promise<QualityCheckTemplate> {
const response = await apiClient.post(this.baseURL, templateData, {
const data = await apiClient.post(`${this.baseURL}/${tenantId}/production/quality-templates`, templateData, {
headers: { 'X-Tenant-ID': tenantId }
});
return response.data;
return data;
}
/**
@@ -38,11 +38,11 @@ class QualityTemplateService {
tenantId: string,
params?: QualityTemplateQueryParams
): Promise<QualityCheckTemplateList> {
const response = await apiClient.get(this.baseURL, {
const data = await apiClient.get(`${this.baseURL}/${tenantId}/production/quality-templates`, {
params,
headers: { 'X-Tenant-ID': tenantId }
});
return response.data;
return data;
}
/**
@@ -52,10 +52,10 @@ class QualityTemplateService {
tenantId: string,
templateId: string
): Promise<QualityCheckTemplate> {
const response = await apiClient.get(`${this.baseURL}/${templateId}`, {
const data = await apiClient.get(`${this.baseURL}/${tenantId}/production/quality-templates/${templateId}`, {
headers: { 'X-Tenant-ID': tenantId }
});
return response.data;
return data;
}
/**
@@ -66,17 +66,17 @@ class QualityTemplateService {
templateId: string,
templateData: QualityCheckTemplateUpdate
): Promise<QualityCheckTemplate> {
const response = await apiClient.put(`${this.baseURL}/${templateId}`, templateData, {
const data = await apiClient.put(`${this.baseURL}/${tenantId}/production/quality-templates/${templateId}`, templateData, {
headers: { 'X-Tenant-ID': tenantId }
});
return response.data;
return data;
}
/**
* Delete a quality check template
*/
async deleteTemplate(tenantId: string, templateId: string): Promise<void> {
await apiClient.delete(`${this.baseURL}/${templateId}`, {
await apiClient.delete(`${this.baseURL}/${tenantId}/production/quality-templates/${templateId}`, {
headers: { 'X-Tenant-ID': tenantId }
});
}
@@ -89,11 +89,11 @@ class QualityTemplateService {
stage: ProcessStage,
isActive: boolean = true
): Promise<QualityCheckTemplateList> {
const response = await apiClient.get(`${this.baseURL}/stages/${stage}`, {
const data = await apiClient.get(`${this.baseURL}/${tenantId}/production/quality-templates/stages/${stage}`, {
params: { is_active: isActive },
headers: { 'X-Tenant-ID': tenantId }
});
return response.data;
return data;
}
/**
@@ -103,10 +103,10 @@ class QualityTemplateService {
tenantId: string,
templateId: string
): Promise<QualityCheckTemplate> {
const response = await apiClient.post(`${this.baseURL}/${templateId}/duplicate`, {}, {
const data = await apiClient.post(`${this.baseURL}/${tenantId}/production/quality-templates/${templateId}/duplicate`, {}, {
headers: { 'X-Tenant-ID': tenantId }
});
return response.data;
return data;
}
/**
@@ -116,10 +116,10 @@ class QualityTemplateService {
tenantId: string,
executionData: QualityCheckExecutionRequest
): Promise<QualityCheckExecutionResponse> {
const response = await apiClient.post('/production/api/v1/quality-checks/execute', executionData, {
const data = await apiClient.post(`${this.baseURL}/${tenantId}/production/quality-checks/execute`, executionData, {
headers: { 'X-Tenant-ID': tenantId }
});
return response.data;
return data;
}
/**
@@ -130,11 +130,11 @@ class QualityTemplateService {
batchId: string,
stage?: ProcessStage
): Promise<any[]> {
const response = await apiClient.get('/production/api/v1/quality-checks', {
const data = await apiClient.get(`${this.baseURL}/${tenantId}/production/quality-checks`, {
params: { batch_id: batchId, process_stage: stage },
headers: { 'X-Tenant-ID': tenantId }
});
return response.data;
return data;
}
/**
@@ -168,15 +168,15 @@ class QualityTemplateService {
templateData: Partial<QualityCheckTemplateCreate | QualityCheckTemplateUpdate>
): Promise<{ valid: boolean; errors: string[] }> {
try {
const response = await apiClient.post(`${this.baseURL}/validate`, templateData, {
const data = await apiClient.post(`${this.baseURL}/${tenantId}/production/quality-templates/validate`, templateData, {
headers: { 'X-Tenant-ID': tenantId }
});
return response.data;
return data;
} catch (error: any) {
if (error.response?.status === 400) {
return {
valid: false,
errors: [error.response.data.detail || 'Validation failed']
errors: [error.response?.data?.detail || 'Validation failed']
};
}
throw error;

View File

@@ -0,0 +1,19 @@
import React from 'react';
import { QualityTemplateManager } from '../../../../components/domain/production';
/**
* QualityTemplatesPage - Page wrapper for the QualityTemplateManager component
*
* This page provides access to quality template management functionality,
* allowing users to create, edit, duplicate, and manage quality control templates
* that are used during production processes.
*/
const QualityTemplatesPage: React.FC = () => {
return (
<div className="container mx-auto px-4 py-6">
<QualityTemplateManager />
</div>
);
};
export default QualityTemplatesPage;

View File

@@ -37,6 +37,7 @@ const OrganizationsPage = React.lazy(() => import('../pages/app/settings/organiz
// Database pages
const DatabasePage = React.lazy(() => import('../pages/app/database/DatabasePage'));
const ModelsConfigPage = React.lazy(() => import('../pages/app/database/models/ModelsConfigPage'));
const QualityTemplatesPage = React.lazy(() => import('../pages/app/database/quality-templates/QualityTemplatesPage'));
// Data pages
const WeatherPage = React.lazy(() => import('../pages/app/data/weather/WeatherPage'));
@@ -190,6 +191,16 @@ export const AppRouter: React.FC = () => {
</ProtectedRoute>
}
/>
<Route
path="/app/database/quality-templates"
element={
<ProtectedRoute>
<AppShell>
<QualityTemplatesPage />
</AppShell>
</ProtectedRoute>
}
/>
<Route
path="/app/database/maquinaria"
element={

View File

@@ -137,6 +137,7 @@ export const ROUTES = {
SETTINGS_BILLING: '/settings/billing',
SETTINGS_BAKERY_CONFIG: '/app/database/bakery-config',
SETTINGS_TEAM: '/app/database/team',
QUALITY_TEMPLATES: '/app/database/quality-templates',
// Reports
REPORTS: '/reports',
@@ -353,6 +354,17 @@ export const routesConfig: RouteConfig[] = [
showInNavigation: true,
showInBreadcrumbs: true,
},
{
path: '/app/database/quality-templates',
name: 'QualityTemplates',
component: 'QualityTemplatesPage',
title: 'Plantillas de Calidad',
icon: 'settings',
requiresAuth: true,
requiredRoles: ROLE_COMBINATIONS.MANAGEMENT_ACCESS,
showInNavigation: true,
showInBreadcrumbs: true,
},
],
},

View File

@@ -22,7 +22,6 @@ from app.schemas.production import (
ProductionStatusEnum
)
from app.core.config import settings
from .quality_templates import router as quality_templates_router
logger = structlog.get_logger()
@@ -1096,6 +1095,211 @@ async def get_yield_metrics(
return metrics
except Exception as e:
logger.error("Error getting yield metrics",
logger.error("Error getting yield metrics",
error=str(e), tenant_id=str(tenant_id))
raise HTTPException(status_code=500, detail="Failed to get yield metrics")
raise HTTPException(status_code=500, detail="Failed to get yield metrics")
# ================================================================
# QUALITY TEMPLATES ENDPOINTS
# ================================================================
from app.repositories.quality_template_repository import QualityTemplateRepository
from app.schemas.quality_templates import (
QualityCheckTemplateCreate,
QualityCheckTemplateUpdate,
QualityCheckTemplateResponse,
QualityCheckTemplateList
)
@router.get("/tenants/{tenant_id}/production/quality-templates", response_model=QualityCheckTemplateList)
async def get_quality_templates(
tenant_id: UUID = Path(...),
stage: Optional[str] = Query(None, description="Filter by process stage"),
check_type: Optional[str] = Query(None, description="Filter by check type"),
is_active: Optional[bool] = Query(True, description="Filter by active status"),
skip: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=1000),
current_user: dict = Depends(get_current_user_dep),
db=Depends(get_db)
):
"""Get quality check templates for tenant"""
try:
repo = QualityTemplateRepository(db)
# Convert stage string to ProcessStage enum if provided
stage_enum = None
if stage:
try:
stage_enum = ProcessStage(stage)
except ValueError:
raise HTTPException(status_code=400, detail=f"Invalid stage: {stage}")
templates, total = await repo.get_templates_by_tenant(
tenant_id=str(tenant_id),
stage=stage_enum,
check_type=check_type,
is_active=is_active,
skip=skip,
limit=limit
)
return QualityCheckTemplateList(
templates=[QualityCheckTemplateResponse.from_orm(t) for t in templates],
total=total,
skip=skip,
limit=limit
)
except HTTPException:
raise
except Exception as e:
logger.error("Error getting quality templates",
error=str(e), tenant_id=str(tenant_id))
raise HTTPException(status_code=500, detail="Failed to get quality templates")
@router.post("/tenants/{tenant_id}/production/quality-templates", response_model=QualityCheckTemplateResponse)
async def create_quality_template(
template_data: QualityCheckTemplateCreate,
tenant_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep),
db=Depends(get_db)
):
"""Create a new quality check template"""
try:
repo = QualityTemplateRepository(db)
# Add tenant_id to the template data
create_data = template_data.dict()
create_data['tenant_id'] = str(tenant_id)
template = await repo.create(create_data)
return QualityCheckTemplateResponse.from_orm(template)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error("Error creating quality template",
error=str(e), tenant_id=str(tenant_id))
raise HTTPException(status_code=500, detail="Failed to create quality template")
@router.get("/tenants/{tenant_id}/production/quality-templates/{template_id}", response_model=QualityCheckTemplateResponse)
async def get_quality_template(
tenant_id: UUID = Path(...),
template_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep),
db=Depends(get_db)
):
"""Get a specific quality check template"""
try:
repo = QualityTemplateRepository(db)
template = await repo.get_by_tenant_and_id(str(tenant_id), template_id)
if not template:
raise HTTPException(status_code=404, detail="Quality template not found")
return QualityCheckTemplateResponse.from_orm(template)
except HTTPException:
raise
except Exception as e:
logger.error("Error getting quality template",
error=str(e), tenant_id=str(tenant_id), template_id=str(template_id))
raise HTTPException(status_code=500, detail="Failed to get quality template")
@router.put("/tenants/{tenant_id}/production/quality-templates/{template_id}", response_model=QualityCheckTemplateResponse)
async def update_quality_template(
template_data: QualityCheckTemplateUpdate,
tenant_id: UUID = Path(...),
template_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep),
db=Depends(get_db)
):
"""Update a quality check template"""
try:
repo = QualityTemplateRepository(db)
# First check if template exists and belongs to tenant
existing = await repo.get_by_tenant_and_id(str(tenant_id), template_id)
if not existing:
raise HTTPException(status_code=404, detail="Quality template not found")
template = await repo.update(template_id, template_data.dict(exclude_unset=True))
return QualityCheckTemplateResponse.from_orm(template)
except HTTPException:
raise
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error("Error updating quality template",
error=str(e), tenant_id=str(tenant_id), template_id=str(template_id))
raise HTTPException(status_code=500, detail="Failed to update quality template")
@router.delete("/tenants/{tenant_id}/production/quality-templates/{template_id}")
async def delete_quality_template(
tenant_id: UUID = Path(...),
template_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep),
db=Depends(get_db)
):
"""Delete a quality check template"""
try:
repo = QualityTemplateRepository(db)
# First check if template exists and belongs to tenant
existing = await repo.get_by_tenant_and_id(str(tenant_id), template_id)
if not existing:
raise HTTPException(status_code=404, detail="Quality template not found")
await repo.delete(template_id)
return {"message": "Quality template deleted successfully"}
except HTTPException:
raise
except Exception as e:
logger.error("Error deleting quality template",
error=str(e), tenant_id=str(tenant_id), template_id=str(template_id))
raise HTTPException(status_code=500, detail="Failed to delete quality template")
@router.post("/tenants/{tenant_id}/production/quality-templates/{template_id}/duplicate", response_model=QualityCheckTemplateResponse)
async def duplicate_quality_template(
tenant_id: UUID = Path(...),
template_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep),
db=Depends(get_db)
):
"""Duplicate an existing quality check template"""
try:
repo = QualityTemplateRepository(db)
# Get original template
original = await repo.get_by_tenant_and_id(str(tenant_id), template_id)
if not original:
raise HTTPException(status_code=404, detail="Quality template not found")
# Create duplicate data
duplicate_data = {
"tenant_id": original.tenant_id,
"name": f"{original.name} (Copy)",
"template_code": None, # Will be auto-generated
"check_type": original.check_type,
"category": original.category,
"description": original.description,
"instructions": original.instructions,
"criteria": original.criteria,
"is_required": original.is_required,
"is_critical": original.is_critical,
"weight": original.weight,
"min_value": original.min_value,
"max_value": original.max_value,
"unit": original.unit,
"tolerance_percentage": original.tolerance_percentage,
"applicable_stages": original.applicable_stages,
"created_by": original.created_by
}
template = await repo.create(duplicate_data)
return QualityCheckTemplateResponse.from_orm(template)
except HTTPException:
raise
except Exception as e:
logger.error("Error duplicating quality template",
error=str(e), tenant_id=str(tenant_id), template_id=str(template_id))
raise HTTPException(status_code=500, detail="Failed to duplicate quality template")

View File

@@ -1,174 +0,0 @@
# services/production/app/api/quality_templates.py
"""
Quality Check Template API endpoints for production service
"""
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from typing import List, Optional
from uuid import UUID
from ..core.database import get_db
from ..models.production import QualityCheckTemplate, ProcessStage
from ..services.quality_template_service import QualityTemplateService
from ..schemas.quality_templates import (
QualityCheckTemplateCreate,
QualityCheckTemplateUpdate,
QualityCheckTemplateResponse,
QualityCheckTemplateList
)
from shared.auth.tenant_access import get_current_tenant_id
router = APIRouter(prefix="/quality-templates", tags=["quality-templates"])
@router.post("", response_model=QualityCheckTemplateResponse, status_code=status.HTTP_201_CREATED)
async def create_quality_template(
template_data: QualityCheckTemplateCreate,
tenant_id: str = Depends(get_current_tenant_id),
db: Session = Depends(get_db)
):
"""Create a new quality check template"""
try:
service = QualityTemplateService(db)
template = await service.create_template(tenant_id, template_data)
return QualityCheckTemplateResponse.from_orm(template)
except ValueError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
except Exception as e:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
@router.get("", response_model=QualityCheckTemplateList)
async def get_quality_templates(
tenant_id: str = Depends(get_current_tenant_id),
stage: Optional[ProcessStage] = Query(None, description="Filter by process stage"),
check_type: Optional[str] = Query(None, description="Filter by check type"),
is_active: Optional[bool] = Query(True, description="Filter by active status"),
skip: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=1000),
db: Session = Depends(get_db)
):
"""Get quality check templates for tenant"""
try:
service = QualityTemplateService(db)
templates, total = await service.get_templates(
tenant_id=tenant_id,
stage=stage,
check_type=check_type,
is_active=is_active,
skip=skip,
limit=limit
)
return QualityCheckTemplateList(
templates=[QualityCheckTemplateResponse.from_orm(t) for t in templates],
total=total,
skip=skip,
limit=limit
)
except Exception as e:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
@router.get("/{template_id}", response_model=QualityCheckTemplateResponse)
async def get_quality_template(
template_id: UUID,
tenant_id: str = Depends(get_current_tenant_id),
db: Session = Depends(get_db)
):
"""Get a specific quality check template"""
try:
service = QualityTemplateService(db)
template = await service.get_template(tenant_id, template_id)
if not template:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Template not found")
return QualityCheckTemplateResponse.from_orm(template)
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
@router.put("/{template_id}", response_model=QualityCheckTemplateResponse)
async def update_quality_template(
template_id: UUID,
template_data: QualityCheckTemplateUpdate,
tenant_id: str = Depends(get_current_tenant_id),
db: Session = Depends(get_db)
):
"""Update a quality check template"""
try:
service = QualityTemplateService(db)
template = await service.update_template(tenant_id, template_id, template_data)
if not template:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Template not found")
return QualityCheckTemplateResponse.from_orm(template)
except HTTPException:
raise
except ValueError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
except Exception as e:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
@router.delete("/{template_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_quality_template(
template_id: UUID,
tenant_id: str = Depends(get_current_tenant_id),
db: Session = Depends(get_db)
):
"""Delete a quality check template"""
try:
service = QualityTemplateService(db)
success = await service.delete_template(tenant_id, template_id)
if not success:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Template not found")
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
@router.get("/stages/{stage}", response_model=QualityCheckTemplateList)
async def get_templates_for_stage(
stage: ProcessStage,
tenant_id: str = Depends(get_current_tenant_id),
is_active: Optional[bool] = Query(True, description="Filter by active status"),
db: Session = Depends(get_db)
):
"""Get quality check templates applicable to a specific process stage"""
try:
service = QualityTemplateService(db)
templates = await service.get_templates_for_stage(tenant_id, stage, is_active)
return QualityCheckTemplateList(
templates=[QualityCheckTemplateResponse.from_orm(t) for t in templates],
total=len(templates),
skip=0,
limit=len(templates)
)
except Exception as e:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
@router.post("/{template_id}/duplicate", response_model=QualityCheckTemplateResponse, status_code=status.HTTP_201_CREATED)
async def duplicate_quality_template(
template_id: UUID,
tenant_id: str = Depends(get_current_tenant_id),
db: Session = Depends(get_db)
):
"""Duplicate an existing quality check template"""
try:
service = QualityTemplateService(db)
template = await service.duplicate_template(tenant_id, template_id)
if not template:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Template not found")
return QualityCheckTemplateResponse.from_orm(template)
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))

View File

@@ -14,7 +14,6 @@ import structlog
from app.core.config import settings
from app.core.database import init_database, get_db_health
from app.api.production import router as production_router
from app.api.quality_templates import router as quality_templates_router
from app.services.production_alert_service import ProductionAlertService
# Configure logging
@@ -74,7 +73,6 @@ app.add_middleware(
# Include routers
app.include_router(production_router, prefix="/api/v1")
app.include_router(quality_templates_router, prefix="/api/v1")
@app.get("/health")

View File

@@ -43,6 +43,17 @@ class EquipmentStatus(str, enum.Enum):
WARNING = "warning"
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 EquipmentType(str, enum.Enum):
"""Equipment type enumeration"""
OVEN = "oven"
@@ -329,17 +340,6 @@ 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"

View File

@@ -0,0 +1,152 @@
"""
Quality Template Repository for Production Service
"""
from typing import List, Optional, Tuple
from sqlalchemy import and_, or_, func, select
from sqlalchemy.ext.asyncio import AsyncSession
from uuid import UUID
import structlog
from .base import ProductionBaseRepository
from ..models.production import QualityCheckTemplate, ProcessStage
logger = structlog.get_logger()
class QualityTemplateRepository(ProductionBaseRepository):
"""Repository for quality check template operations"""
def __init__(self, session: AsyncSession):
super().__init__(QualityCheckTemplate, session)
async def get_templates_by_tenant(
self,
tenant_id: str,
stage: Optional[ProcessStage] = None,
check_type: Optional[str] = None,
is_active: Optional[bool] = True,
skip: int = 0,
limit: int = 100
) -> Tuple[List[QualityCheckTemplate], int]:
"""Get quality check templates with filtering and pagination"""
filters = [QualityCheckTemplate.tenant_id == tenant_id]
if is_active is not None:
filters.append(QualityCheckTemplate.is_active == is_active)
if check_type:
filters.append(QualityCheckTemplate.check_type == check_type)
if stage:
filters.append(
or_(
func.json_contains(
QualityCheckTemplate.applicable_stages,
f'"{stage.value}"'
),
QualityCheckTemplate.applicable_stages.is_(None)
)
)
# Get total count with SQLAlchemy conditions
count_query = select(func.count(QualityCheckTemplate.id)).where(and_(*filters))
count_result = await self.session.execute(count_query)
total = count_result.scalar()
# Get templates with ordering
query = select(QualityCheckTemplate).where(and_(*filters)).order_by(
QualityCheckTemplate.is_critical.desc(),
QualityCheckTemplate.is_required.desc(),
QualityCheckTemplate.name
).offset(skip).limit(limit)
result = await self.session.execute(query)
templates = result.scalars().all()
return templates, total
async def get_by_tenant_and_id(
self,
tenant_id: str,
template_id: UUID
) -> Optional[QualityCheckTemplate]:
"""Get a specific quality check template by tenant and ID"""
return await self.get_by_filters(
and_(
QualityCheckTemplate.tenant_id == tenant_id,
QualityCheckTemplate.id == template_id
)
)
async def get_templates_for_stage(
self,
tenant_id: str,
stage: ProcessStage,
is_active: Optional[bool] = True
) -> List[QualityCheckTemplate]:
"""Get all quality check templates applicable to a specific process stage"""
filters = [
QualityCheckTemplate.tenant_id == tenant_id,
or_(
func.json_contains(
QualityCheckTemplate.applicable_stages,
f'"{stage.value}"'
),
QualityCheckTemplate.applicable_stages.is_(None)
)
]
if is_active is not None:
filters.append(QualityCheckTemplate.is_active == is_active)
return await self.get_multi(
filters=and_(*filters),
order_by=[
QualityCheckTemplate.is_critical.desc(),
QualityCheckTemplate.is_required.desc(),
QualityCheckTemplate.weight.desc(),
QualityCheckTemplate.name
]
)
async def check_template_code_exists(
self,
tenant_id: str,
template_code: str,
exclude_id: Optional[UUID] = None
) -> bool:
"""Check if a template code already exists for the tenant"""
filters = [
QualityCheckTemplate.tenant_id == tenant_id,
QualityCheckTemplate.template_code == template_code
]
if exclude_id:
filters.append(QualityCheckTemplate.id != exclude_id)
existing = await self.get_by_filters(and_(*filters))
return existing is not None
async def get_templates_by_ids(
self,
tenant_id: str,
template_ids: List[UUID]
) -> List[QualityCheckTemplate]:
"""Get quality check templates by list of IDs"""
return await self.get_multi(
filters=and_(
QualityCheckTemplate.tenant_id == tenant_id,
QualityCheckTemplate.id.in_(template_ids)
),
order_by=[
QualityCheckTemplate.is_critical.desc(),
QualityCheckTemplate.is_required.desc(),
QualityCheckTemplate.weight.desc()
]
)

View File

@@ -104,7 +104,7 @@ class ProductionAlertService(BaseAlertService, AlertServiceMixin):
JOIN production_capacity pc ON pc.equipment_id = p.equipment_id
WHERE p.planned_date >= CURRENT_DATE
AND p.planned_date <= CURRENT_DATE + INTERVAL '3 days'
AND p.status IN ('planned', 'in_progress')
AND p.status IN ('PENDING', 'IN_PROGRESS')
AND p.tenant_id = $1
GROUP BY p.tenant_id, p.planned_date
)
@@ -226,10 +226,10 @@ class ProductionAlertService(BaseAlertService, AlertServiceMixin):
COALESCE(pb.priority::text, 'medium') as priority_level,
1 as affected_orders -- Default to 1 since we can't count orders
FROM production_batches pb
WHERE pb.status IN ('IN_PROGRESS', 'DELAYED')
WHERE pb.status IN ('IN_PROGRESS', 'ON_HOLD', 'QUALITY_CHECK')
AND (
(pb.planned_end_time < NOW() AND pb.status = 'IN_PROGRESS')
OR pb.status = 'DELAYED'
OR pb.status IN ('ON_HOLD', 'QUALITY_CHECK')
)
AND pb.planned_end_time > NOW() - INTERVAL '24 hours'
ORDER BY
@@ -831,7 +831,7 @@ class ProductionAlertService(BaseAlertService, AlertServiceMixin):
FROM production_batches pb
JOIN recipe_ingredients ri ON ri.recipe_id = pb.recipe_id
WHERE ri.ingredient_id = $1
AND pb.status IN ('planned', 'in_progress')
AND pb.status IN ('PENDING', 'IN_PROGRESS')
AND pb.planned_completion_time > NOW()
"""

View File

@@ -19,7 +19,7 @@ class QualityTemplateService:
def __init__(self, db: Session):
self.db = db
async def create_template(
def create_template(
self,
tenant_id: str,
template_data: QualityCheckTemplateCreate
@@ -50,7 +50,7 @@ class QualityTemplateService:
return template
async def get_templates(
def get_templates(
self,
tenant_id: str,
stage: Optional[ProcessStage] = None,
@@ -93,7 +93,7 @@ class QualityTemplateService:
return templates, total
async def get_template(
def get_template(
self,
tenant_id: str,
template_id: UUID
@@ -107,7 +107,7 @@ class QualityTemplateService:
)
).first()
async def update_template(
def update_template(
self,
tenant_id: str,
template_id: UUID,
@@ -115,7 +115,7 @@ class QualityTemplateService:
) -> Optional[QualityCheckTemplate]:
"""Update a quality check template"""
template = await self.get_template(tenant_id, template_id)
template = self.get_template(tenant_id, template_id)
if not template:
return None
@@ -143,14 +143,14 @@ class QualityTemplateService:
return template
async def delete_template(
def delete_template(
self,
tenant_id: str,
template_id: UUID
) -> bool:
"""Delete a quality check template"""
template = await self.get_template(tenant_id, template_id)
template = self.get_template(tenant_id, template_id)
if not template:
return False
@@ -165,7 +165,7 @@ class QualityTemplateService:
return True
async def get_templates_for_stage(
def get_templates_for_stage(
self,
tenant_id: str,
stage: ProcessStage,
@@ -198,14 +198,14 @@ class QualityTemplateService:
QualityCheckTemplate.name
).all()
async def duplicate_template(
def duplicate_template(
self,
tenant_id: str,
template_id: UUID
) -> Optional[QualityCheckTemplate]:
"""Duplicate an existing quality check template"""
original = await self.get_template(tenant_id, template_id)
original = self.get_template(tenant_id, template_id)
if not original:
return None
@@ -234,9 +234,9 @@ class QualityTemplateService:
}
create_data = QualityCheckTemplateCreate(**duplicate_data)
return await self.create_template(tenant_id, create_data)
return self.create_template(tenant_id, create_data)
async def get_templates_by_recipe_config(
def get_templates_by_recipe_config(
self,
tenant_id: str,
stage: ProcessStage,
@@ -268,7 +268,7 @@ class QualityTemplateService:
return templates
async def validate_template_configuration(
def validate_template_configuration(
self,
tenant_id: str,
template_data: dict