Files
bakery-ia/services/suppliers/app/schemas/performance.py
2025-08-21 20:28:14 +02:00

385 lines
12 KiB
Python

# ================================================================
# services/suppliers/app/schemas/performance.py
# ================================================================
"""
Performance Tracking and Alert Schemas for Suppliers Service
"""
from datetime import datetime
from typing import List, Optional, Dict, Any
from uuid import UUID
from pydantic import BaseModel, Field, validator
from decimal import Decimal
from app.models.performance import (
AlertSeverity, AlertType, AlertStatus, PerformanceMetricType,
PerformancePeriod
)
# ===== Base Schemas =====
class PerformanceMetricBase(BaseModel):
"""Base schema for performance metrics"""
metric_type: PerformanceMetricType
period: PerformancePeriod
period_start: datetime
period_end: datetime
metric_value: float = Field(ge=0, le=100)
target_value: Optional[float] = None
total_orders: int = Field(ge=0, default=0)
total_deliveries: int = Field(ge=0, default=0)
on_time_deliveries: int = Field(ge=0, default=0)
late_deliveries: int = Field(ge=0, default=0)
quality_issues: int = Field(ge=0, default=0)
total_amount: Decimal = Field(ge=0, default=0)
notes: Optional[str] = None
class PerformanceMetricCreate(PerformanceMetricBase):
"""Schema for creating performance metrics"""
supplier_id: UUID
metrics_data: Optional[Dict[str, Any]] = None
external_factors: Optional[Dict[str, Any]] = None
class PerformanceMetricUpdate(BaseModel):
"""Schema for updating performance metrics"""
metric_value: Optional[float] = Field(None, ge=0, le=100)
target_value: Optional[float] = None
notes: Optional[str] = None
metrics_data: Optional[Dict[str, Any]] = None
external_factors: Optional[Dict[str, Any]] = None
class PerformanceMetric(PerformanceMetricBase):
"""Complete performance metric schema"""
id: UUID
tenant_id: UUID
supplier_id: UUID
previous_value: Optional[float] = None
trend_direction: Optional[str] = None
trend_percentage: Optional[float] = None
metrics_data: Optional[Dict[str, Any]] = None
external_factors: Optional[Dict[str, Any]] = None
calculated_at: datetime
class Config:
orm_mode = True
# ===== Alert Schemas =====
class AlertBase(BaseModel):
"""Base schema for alerts"""
alert_type: AlertType
severity: AlertSeverity
title: str = Field(max_length=255)
message: str
description: Optional[str] = None
trigger_value: Optional[float] = None
threshold_value: Optional[float] = None
metric_type: Optional[PerformanceMetricType] = None
recommended_actions: Optional[List[Dict[str, Any]]] = None
auto_resolve: bool = False
class AlertCreate(AlertBase):
"""Schema for creating alerts"""
supplier_id: UUID
purchase_order_id: Optional[UUID] = None
delivery_id: Optional[UUID] = None
performance_metric_id: Optional[UUID] = None
priority_score: int = Field(ge=1, le=100, default=50)
business_impact: Optional[str] = None
tags: Optional[List[str]] = None
class AlertUpdate(BaseModel):
"""Schema for updating alerts"""
status: Optional[AlertStatus] = None
actions_taken: Optional[List[Dict[str, Any]]] = None
resolution_notes: Optional[str] = None
escalated: Optional[bool] = None
class Alert(AlertBase):
"""Complete alert schema"""
id: UUID
tenant_id: UUID
supplier_id: UUID
status: AlertStatus
purchase_order_id: Optional[UUID] = None
delivery_id: Optional[UUID] = None
performance_metric_id: Optional[UUID] = None
triggered_at: datetime
acknowledged_at: Optional[datetime] = None
acknowledged_by: Optional[UUID] = None
resolved_at: Optional[datetime] = None
resolved_by: Optional[UUID] = None
actions_taken: Optional[List[Dict[str, Any]]] = None
resolution_notes: Optional[str] = None
escalated: bool = False
escalated_at: Optional[datetime] = None
notification_sent: bool = False
priority_score: int
business_impact: Optional[str] = None
tags: Optional[List[str]] = None
created_at: datetime
class Config:
orm_mode = True
# ===== Scorecard Schemas =====
class ScorecardBase(BaseModel):
"""Base schema for supplier scorecards"""
scorecard_name: str = Field(max_length=255)
period: PerformancePeriod
period_start: datetime
period_end: datetime
overall_score: float = Field(ge=0, le=100)
quality_score: float = Field(ge=0, le=100)
delivery_score: float = Field(ge=0, le=100)
cost_score: float = Field(ge=0, le=100)
service_score: float = Field(ge=0, le=100)
on_time_delivery_rate: float = Field(ge=0, le=100)
quality_rejection_rate: float = Field(ge=0, le=100)
order_accuracy_rate: float = Field(ge=0, le=100)
response_time_hours: float = Field(ge=0)
cost_variance_percentage: float
total_orders_processed: int = Field(ge=0, default=0)
total_amount_processed: Decimal = Field(ge=0, default=0)
average_order_value: Decimal = Field(ge=0, default=0)
cost_savings_achieved: Decimal = Field(default=0)
class ScorecardCreate(ScorecardBase):
"""Schema for creating scorecards"""
supplier_id: UUID
strengths: Optional[List[str]] = None
improvement_areas: Optional[List[str]] = None
recommended_actions: Optional[List[Dict[str, Any]]] = None
notes: Optional[str] = None
class ScorecardUpdate(BaseModel):
"""Schema for updating scorecards"""
overall_score: Optional[float] = Field(None, ge=0, le=100)
quality_score: Optional[float] = Field(None, ge=0, le=100)
delivery_score: Optional[float] = Field(None, ge=0, le=100)
cost_score: Optional[float] = Field(None, ge=0, le=100)
service_score: Optional[float] = Field(None, ge=0, le=100)
strengths: Optional[List[str]] = None
improvement_areas: Optional[List[str]] = None
recommended_actions: Optional[List[Dict[str, Any]]] = None
notes: Optional[str] = None
is_final: Optional[bool] = None
class Scorecard(ScorecardBase):
"""Complete scorecard schema"""
id: UUID
tenant_id: UUID
supplier_id: UUID
overall_rank: Optional[int] = None
category_rank: Optional[int] = None
total_suppliers_evaluated: Optional[int] = None
score_trend: Optional[str] = None
score_change_percentage: Optional[float] = None
strengths: Optional[List[str]] = None
improvement_areas: Optional[List[str]] = None
recommended_actions: Optional[List[Dict[str, Any]]] = None
is_final: bool = False
approved_by: Optional[UUID] = None
approved_at: Optional[datetime] = None
notes: Optional[str] = None
attachments: Optional[List[Dict[str, Any]]] = None
generated_at: datetime
generated_by: UUID
class Config:
orm_mode = True
# ===== Dashboard Schemas =====
class PerformanceDashboardSummary(BaseModel):
"""Performance dashboard summary schema"""
total_suppliers: int
active_suppliers: int
suppliers_above_threshold: int
suppliers_below_threshold: int
average_overall_score: float
average_delivery_rate: float
average_quality_rate: float
total_active_alerts: int
critical_alerts: int
high_priority_alerts: int
recent_scorecards_generated: int
cost_savings_this_month: Decimal
# Performance trends
performance_trend: str # improving, declining, stable
delivery_trend: str
quality_trend: str
# Business model insights
detected_business_model: str # individual_bakery, central_bakery, hybrid
model_confidence: float
business_model_metrics: Dict[str, Any]
class SupplierPerformanceInsights(BaseModel):
"""Supplier performance insights schema"""
supplier_id: UUID
supplier_name: str
current_overall_score: float
previous_score: Optional[float] = None
score_change_percentage: Optional[float] = None
performance_rank: Optional[int] = None
# Key performance indicators
delivery_performance: float
quality_performance: float
cost_performance: float
service_performance: float
# Recent metrics
orders_last_30_days: int
average_delivery_time: float
quality_issues_count: int
cost_variance: float
# Alert summary
active_alerts: int
resolved_alerts_last_30_days: int
alert_trend: str
# Performance categorization
performance_category: str # excellent, good, acceptable, needs_improvement, poor
risk_level: str # low, medium, high, critical
# Recommendations
top_strengths: List[str]
improvement_priorities: List[str]
recommended_actions: List[Dict[str, Any]]
class PerformanceAnalytics(BaseModel):
"""Advanced performance analytics schema"""
period_start: datetime
period_end: datetime
total_suppliers_analyzed: int
# Performance distribution
performance_distribution: Dict[str, int] # excellent, good, etc.
score_ranges: Dict[str, List[float]] # min, max, avg per range
# Trend analysis
overall_trend: Dict[str, float] # month-over-month changes
delivery_trends: Dict[str, float]
quality_trends: Dict[str, float]
cost_trends: Dict[str, float]
# Comparative analysis
top_performers: List[SupplierPerformanceInsights]
underperformers: List[SupplierPerformanceInsights]
most_improved: List[SupplierPerformanceInsights]
biggest_declines: List[SupplierPerformanceInsights]
# Risk analysis
high_risk_suppliers: List[Dict[str, Any]]
contract_renewals_due: List[Dict[str, Any]]
certification_expiries: List[Dict[str, Any]]
# Financial impact
total_procurement_value: Decimal
cost_savings_achieved: Decimal
cost_avoidance: Decimal
financial_risk_exposure: Decimal
class AlertSummary(BaseModel):
"""Alert summary schema"""
alert_type: AlertType
severity: AlertSeverity
count: int
avg_resolution_time_hours: Optional[float] = None
oldest_alert_age_hours: Optional[float] = None
trend_percentage: Optional[float] = None
class DashboardFilter(BaseModel):
"""Dashboard filter schema"""
supplier_ids: Optional[List[UUID]] = None
supplier_categories: Optional[List[str]] = None
performance_categories: Optional[List[str]] = None
date_from: Optional[datetime] = None
date_to: Optional[datetime] = None
include_inactive: bool = False
class AlertFilter(BaseModel):
"""Alert filter schema"""
alert_types: Optional[List[AlertType]] = None
severities: Optional[List[AlertSeverity]] = None
statuses: Optional[List[AlertStatus]] = None
supplier_ids: Optional[List[UUID]] = None
date_from: Optional[datetime] = None
date_to: Optional[datetime] = None
metric_types: Optional[List[PerformanceMetricType]] = None
# ===== Business Model Detection =====
class BusinessModelInsights(BaseModel):
"""Business model detection and insights schema"""
detected_model: str # individual_bakery, central_bakery, hybrid
confidence_score: float
model_characteristics: Dict[str, Any]
# Model-specific metrics
supplier_diversity_score: float
procurement_volume_patterns: Dict[str, Any]
delivery_frequency_patterns: Dict[str, Any]
order_size_patterns: Dict[str, Any]
# Recommendations
optimization_opportunities: List[Dict[str, Any]]
recommended_supplier_mix: Dict[str, Any]
cost_optimization_potential: Decimal
risk_mitigation_suggestions: List[str]
# Benchmarking
industry_comparison: Dict[str, float]
peer_comparison: Optional[Dict[str, float]] = None
# ===== Export and Reporting =====
class PerformanceReportRequest(BaseModel):
"""Performance report generation request"""
report_type: str # scorecard, analytics, alerts, comprehensive
format: str = Field(pattern="^(pdf|excel|csv|json)$")
period: PerformancePeriod
date_from: datetime
date_to: datetime
supplier_ids: Optional[List[UUID]] = None
include_charts: bool = True
include_recommendations: bool = True
include_benchmarks: bool = True
custom_metrics: Optional[List[str]] = None
class ExportDataResponse(BaseModel):
"""Export data response schema"""
export_id: UUID
format: str
file_url: Optional[str] = None
file_size_bytes: Optional[int] = None
generated_at: datetime
expires_at: datetime
status: str # generating, ready, expired, failed
error_message: Optional[str] = None