Improve the production frontend
This commit is contained in:
@@ -46,15 +46,20 @@ async def get_dashboard_summary(
|
||||
):
|
||||
"""Get production dashboard summary using shared auth"""
|
||||
try:
|
||||
# Extract tenant from user context for security
|
||||
current_tenant = current_user.get("tenant_id")
|
||||
if str(tenant_id) != current_tenant:
|
||||
raise HTTPException(status_code=403, detail="Access denied to this tenant")
|
||||
|
||||
summary = await production_service.get_dashboard_summary(tenant_id)
|
||||
|
||||
logger.info("Retrieved production dashboard summary",
|
||||
|
||||
logger.info("Retrieved production dashboard summary",
|
||||
tenant_id=str(tenant_id), user_id=current_user.get("user_id"))
|
||||
|
||||
|
||||
return summary
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Error getting production dashboard summary",
|
||||
logger.error("Error getting production dashboard summary",
|
||||
error=str(e), tenant_id=str(tenant_id))
|
||||
raise HTTPException(status_code=500, detail="Failed to get dashboard summary")
|
||||
|
||||
@@ -68,6 +73,7 @@ async def get_daily_requirements(
|
||||
):
|
||||
"""Get daily production requirements"""
|
||||
try:
|
||||
current_tenant = current_user.get("tenant_id")
|
||||
if str(tenant_id) != current_tenant:
|
||||
raise HTTPException(status_code=403, detail="Access denied to this tenant")
|
||||
|
||||
@@ -94,6 +100,7 @@ async def get_production_requirements(
|
||||
):
|
||||
"""Get production requirements for procurement planning"""
|
||||
try:
|
||||
current_tenant = current_user.get("tenant_id")
|
||||
if str(tenant_id) != current_tenant:
|
||||
raise HTTPException(status_code=403, detail="Access denied to this tenant")
|
||||
|
||||
@@ -124,6 +131,7 @@ async def create_production_batch(
|
||||
):
|
||||
"""Create a new production batch"""
|
||||
try:
|
||||
current_tenant = current_user.get("tenant_id")
|
||||
if str(tenant_id) != current_tenant:
|
||||
raise HTTPException(status_code=403, detail="Access denied to this tenant")
|
||||
|
||||
@@ -151,6 +159,7 @@ async def get_active_batches(
|
||||
):
|
||||
"""Get currently active production batches"""
|
||||
try:
|
||||
current_tenant = current_user.get("tenant_id")
|
||||
if str(tenant_id) != current_tenant:
|
||||
raise HTTPException(status_code=403, detail="Access denied to this tenant")
|
||||
|
||||
@@ -185,6 +194,7 @@ async def get_batch_details(
|
||||
):
|
||||
"""Get detailed information about a production batch"""
|
||||
try:
|
||||
current_tenant = current_user.get("tenant_id")
|
||||
if str(tenant_id) != current_tenant:
|
||||
raise HTTPException(status_code=403, detail="Access denied to this tenant")
|
||||
|
||||
@@ -218,6 +228,7 @@ async def update_batch_status(
|
||||
):
|
||||
"""Update production batch status"""
|
||||
try:
|
||||
current_tenant = current_user.get("tenant_id")
|
||||
if str(tenant_id) != current_tenant:
|
||||
raise HTTPException(status_code=403, detail="Access denied to this tenant")
|
||||
|
||||
@@ -253,6 +264,7 @@ async def get_production_schedule(
|
||||
):
|
||||
"""Get production schedule for a date range"""
|
||||
try:
|
||||
current_tenant = current_user.get("tenant_id")
|
||||
if str(tenant_id) != current_tenant:
|
||||
raise HTTPException(status_code=403, detail="Access denied to this tenant")
|
||||
|
||||
@@ -316,6 +328,7 @@ async def get_capacity_status(
|
||||
):
|
||||
"""Get production capacity status for a specific date"""
|
||||
try:
|
||||
current_tenant = current_user.get("tenant_id")
|
||||
if str(tenant_id) != current_tenant:
|
||||
raise HTTPException(status_code=403, detail="Access denied to this tenant")
|
||||
|
||||
@@ -353,6 +366,7 @@ async def get_yield_metrics(
|
||||
):
|
||||
"""Get production yield metrics for analysis"""
|
||||
try:
|
||||
current_tenant = current_user.get("tenant_id")
|
||||
if str(tenant_id) != current_tenant:
|
||||
raise HTTPException(status_code=403, detail="Access denied to this tenant")
|
||||
|
||||
|
||||
@@ -18,21 +18,21 @@ from shared.database.base import Base
|
||||
|
||||
class ProductionStatus(str, enum.Enum):
|
||||
"""Production batch status enumeration"""
|
||||
PENDING = "pending"
|
||||
IN_PROGRESS = "in_progress"
|
||||
COMPLETED = "completed"
|
||||
CANCELLED = "cancelled"
|
||||
ON_HOLD = "on_hold"
|
||||
QUALITY_CHECK = "quality_check"
|
||||
FAILED = "failed"
|
||||
PENDING = "PENDING"
|
||||
IN_PROGRESS = "IN_PROGRESS"
|
||||
COMPLETED = "COMPLETED"
|
||||
CANCELLED = "CANCELLED"
|
||||
ON_HOLD = "ON_HOLD"
|
||||
QUALITY_CHECK = "QUALITY_CHECK"
|
||||
FAILED = "FAILED"
|
||||
|
||||
|
||||
class ProductionPriority(str, enum.Enum):
|
||||
"""Production priority levels"""
|
||||
LOW = "low"
|
||||
MEDIUM = "medium"
|
||||
HIGH = "high"
|
||||
URGENT = "urgent"
|
||||
LOW = "LOW"
|
||||
MEDIUM = "MEDIUM"
|
||||
HIGH = "HIGH"
|
||||
URGENT = "URGENT"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ class ProductionBaseRepository(BaseRepository):
|
||||
# Production data is more dynamic, shorter cache time (5 minutes)
|
||||
super().__init__(model, session, cache_ttl)
|
||||
|
||||
@transactional
|
||||
async def get_by_tenant_id(self, tenant_id: str, skip: int = 0, limit: int = 100) -> List:
|
||||
"""Get records by tenant ID"""
|
||||
if hasattr(self.model, 'tenant_id'):
|
||||
@@ -36,7 +35,6 @@ class ProductionBaseRepository(BaseRepository):
|
||||
)
|
||||
return await self.get_multi(skip=skip, limit=limit)
|
||||
|
||||
@transactional
|
||||
async def get_by_status(
|
||||
self,
|
||||
tenant_id: str,
|
||||
|
||||
@@ -26,7 +26,6 @@ class ProductionBatchRepository(ProductionBaseRepository, BatchCountProvider):
|
||||
# Production batches are dynamic, short cache time (5 minutes)
|
||||
super().__init__(ProductionBatch, session, cache_ttl)
|
||||
|
||||
@transactional
|
||||
async def create_batch(self, batch_data: Dict[str, Any]) -> ProductionBatch:
|
||||
"""Create a new production batch with validation"""
|
||||
try:
|
||||
@@ -84,7 +83,6 @@ class ProductionBatchRepository(ProductionBaseRepository, BatchCountProvider):
|
||||
logger.error("Error creating production batch", error=str(e))
|
||||
raise DatabaseError(f"Failed to create production batch: {str(e)}")
|
||||
|
||||
@transactional
|
||||
async def get_active_batches(self, tenant_id: str) -> List[ProductionBatch]:
|
||||
"""Get active production batches for a tenant"""
|
||||
try:
|
||||
@@ -113,7 +111,6 @@ class ProductionBatchRepository(ProductionBaseRepository, BatchCountProvider):
|
||||
logger.error("Error fetching active batches", error=str(e))
|
||||
raise DatabaseError(f"Failed to fetch active batches: {str(e)}")
|
||||
|
||||
@transactional
|
||||
async def get_batches_by_date_range(
|
||||
self,
|
||||
tenant_id: str,
|
||||
@@ -152,7 +149,6 @@ class ProductionBatchRepository(ProductionBaseRepository, BatchCountProvider):
|
||||
logger.error("Error fetching batches by date range", error=str(e))
|
||||
raise DatabaseError(f"Failed to fetch batches by date range: {str(e)}")
|
||||
|
||||
@transactional
|
||||
async def get_batches_by_product(
|
||||
self,
|
||||
tenant_id: str,
|
||||
@@ -182,7 +178,6 @@ class ProductionBatchRepository(ProductionBaseRepository, BatchCountProvider):
|
||||
logger.error("Error fetching batches by product", error=str(e))
|
||||
raise DatabaseError(f"Failed to fetch batches by product: {str(e)}")
|
||||
|
||||
@transactional
|
||||
async def update_batch_status(
|
||||
self,
|
||||
batch_id: UUID,
|
||||
@@ -240,7 +235,6 @@ class ProductionBatchRepository(ProductionBaseRepository, BatchCountProvider):
|
||||
logger.error("Error updating batch status", error=str(e))
|
||||
raise DatabaseError(f"Failed to update batch status: {str(e)}")
|
||||
|
||||
@transactional
|
||||
async def get_production_metrics(
|
||||
self,
|
||||
tenant_id: str,
|
||||
@@ -297,7 +291,6 @@ class ProductionBatchRepository(ProductionBaseRepository, BatchCountProvider):
|
||||
logger.error("Error calculating production metrics", error=str(e))
|
||||
raise DatabaseError(f"Failed to calculate production metrics: {str(e)}")
|
||||
|
||||
@transactional
|
||||
async def get_urgent_batches(self, tenant_id: str, hours_ahead: int = 4) -> List[ProductionBatch]:
|
||||
"""Get batches that need to start within the specified hours"""
|
||||
try:
|
||||
|
||||
@@ -14,21 +14,21 @@ 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"
|
||||
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"
|
||||
LOW = "LOW"
|
||||
MEDIUM = "MEDIUM"
|
||||
HIGH = "HIGH"
|
||||
URGENT = "URGENT"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -49,14 +49,14 @@ class ProductionAlertService(BaseAlertService, AlertServiceMixin):
|
||||
max_instances=1
|
||||
)
|
||||
|
||||
# Equipment monitoring - every 3 minutes (alerts)
|
||||
self.scheduler.add_job(
|
||||
self.check_equipment_status,
|
||||
CronTrigger(minute='*/3'),
|
||||
id='equipment_check',
|
||||
misfire_grace_time=30,
|
||||
max_instances=1
|
||||
)
|
||||
# Equipment monitoring - disabled (equipment tables not available in production database)
|
||||
# self.scheduler.add_job(
|
||||
# self.check_equipment_status,
|
||||
# CronTrigger(minute='*/3'),
|
||||
# id='equipment_check',
|
||||
# misfire_grace_time=30,
|
||||
# max_instances=1
|
||||
# )
|
||||
|
||||
# Efficiency recommendations - every 30 minutes (recommendations)
|
||||
self.scheduler.add_job(
|
||||
@@ -127,7 +127,7 @@ class ProductionAlertService(BaseAlertService, AlertServiceMixin):
|
||||
FROM production_batches pb
|
||||
WHERE pb.planned_start_time >= CURRENT_DATE
|
||||
AND pb.planned_start_time <= CURRENT_DATE + INTERVAL '3 days'
|
||||
AND pb.status IN ('planned', 'pending', 'in_progress')
|
||||
AND pb.status IN ('PLANNED', 'PENDING', 'IN_PROGRESS')
|
||||
GROUP BY pb.tenant_id, DATE(pb.planned_start_time)
|
||||
HAVING COUNT(*) > 10 -- Alert if more than 10 batches per day
|
||||
ORDER BY total_planned DESC
|
||||
@@ -226,15 +226,15 @@ 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', 'DELAYED')
|
||||
AND (
|
||||
(pb.planned_end_time < NOW() AND pb.status = 'in_progress')
|
||||
OR pb.status = 'delayed'
|
||||
(pb.planned_end_time < NOW() AND pb.status = 'IN_PROGRESS')
|
||||
OR pb.status = 'DELAYED'
|
||||
)
|
||||
AND pb.planned_end_time > NOW() - INTERVAL '24 hours'
|
||||
ORDER BY
|
||||
CASE COALESCE(pb.priority::text, 'medium')
|
||||
WHEN 'urgent' THEN 1 WHEN 'high' THEN 2 ELSE 3
|
||||
CASE COALESCE(pb.priority::text, 'MEDIUM')
|
||||
WHEN 'URGENT' THEN 1 WHEN 'HIGH' THEN 2 ELSE 3
|
||||
END,
|
||||
delay_minutes DESC
|
||||
"""
|
||||
@@ -481,7 +481,7 @@ class ProductionAlertService(BaseAlertService, AlertServiceMixin):
|
||||
AVG(pb.yield_percentage) as avg_yield,
|
||||
EXTRACT(hour FROM pb.actual_start_time) as start_hour
|
||||
FROM production_batches pb
|
||||
WHERE pb.status = 'completed'
|
||||
WHERE pb.status = 'COMPLETED'
|
||||
AND pb.actual_completion_time > CURRENT_DATE - INTERVAL '30 days'
|
||||
AND pb.tenant_id = $1
|
||||
GROUP BY pb.tenant_id, pb.product_name, EXTRACT(hour FROM pb.actual_start_time)
|
||||
|
||||
@@ -78,7 +78,6 @@ class ProductionService:
|
||||
error=str(e), tenant_id=str(tenant_id), date=target_date.isoformat())
|
||||
raise
|
||||
|
||||
@transactional
|
||||
async def create_production_batch(
|
||||
self,
|
||||
tenant_id: UUID,
|
||||
@@ -129,7 +128,6 @@ class ProductionService:
|
||||
error=str(e), tenant_id=str(tenant_id))
|
||||
raise
|
||||
|
||||
@transactional
|
||||
async def update_batch_status(
|
||||
self,
|
||||
tenant_id: UUID,
|
||||
@@ -167,7 +165,6 @@ class ProductionService:
|
||||
error=str(e), batch_id=str(batch_id), tenant_id=str(tenant_id))
|
||||
raise
|
||||
|
||||
@transactional
|
||||
async def get_dashboard_summary(self, tenant_id: UUID) -> ProductionDashboardSummary:
|
||||
"""Get production dashboard summary data"""
|
||||
try:
|
||||
@@ -215,7 +212,6 @@ class ProductionService:
|
||||
error=str(e), tenant_id=str(tenant_id))
|
||||
raise
|
||||
|
||||
@transactional
|
||||
async def get_production_requirements(
|
||||
self,
|
||||
tenant_id: UUID,
|
||||
|
||||
Reference in New Issue
Block a user