Initial commit - production deployment
This commit is contained in:
1
services/suppliers/app/schemas/__init__.py
Normal file
1
services/suppliers/app/schemas/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# services/suppliers/app/schemas/__init__.py
|
||||
385
services/suppliers/app/schemas/performance.py
Normal file
385
services/suppliers/app/schemas/performance.py
Normal file
@@ -0,0 +1,385 @@
|
||||
# ================================================================
|
||||
# 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
|
||||
732
services/suppliers/app/schemas/suppliers.py
Normal file
732
services/suppliers/app/schemas/suppliers.py
Normal file
@@ -0,0 +1,732 @@
|
||||
# services/suppliers/app/schemas/suppliers.py
|
||||
"""
|
||||
Pydantic schemas for supplier-related API requests and responses
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, Field, EmailStr
|
||||
from typing import List, Optional, Dict, Any, Union
|
||||
from uuid import UUID
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from app.models.suppliers import (
|
||||
SupplierType, SupplierStatus, PaymentTerms,
|
||||
QualityRating
|
||||
)
|
||||
|
||||
# NOTE: PO, Delivery, and Invoice schemas remain for backward compatibility
|
||||
# The primary implementation has moved to Procurement Service (services/procurement/)
|
||||
# These schemas support legacy endpoints in suppliers service (app/api/purchase_orders.py)
|
||||
#
|
||||
# Migration Status:
|
||||
# - ✅ Procurement Service fully operational with enhanced features
|
||||
# - ⚠️ Supplier service endpoints still active for backward compatibility
|
||||
# - 📋 Deprecation Timeline: Q2 2026 (after 6-month dual-operation period)
|
||||
#
|
||||
# Action Required:
|
||||
# 1. All new integrations should use Procurement Service endpoints
|
||||
# 2. Update client applications to use ProcurementServiceClient
|
||||
# 3. Monitor usage of supplier service PO endpoints via logs
|
||||
# 4. Plan migration of remaining clients by Q1 2026
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# SUPPLIER SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
class SupplierCreate(BaseModel):
|
||||
"""Schema for creating suppliers"""
|
||||
name: str = Field(..., min_length=1, max_length=255)
|
||||
supplier_code: Optional[str] = Field(None, max_length=50)
|
||||
tax_id: Optional[str] = Field(None, max_length=50)
|
||||
registration_number: Optional[str] = Field(None, max_length=100)
|
||||
supplier_type: SupplierType
|
||||
contact_person: Optional[str] = Field(None, max_length=200)
|
||||
email: Optional[EmailStr] = None
|
||||
phone: Optional[str] = Field(None, max_length=30)
|
||||
mobile: Optional[str] = Field(None, max_length=30)
|
||||
website: Optional[str] = Field(None, max_length=255)
|
||||
|
||||
# Address
|
||||
address_line1: Optional[str] = Field(None, max_length=255)
|
||||
address_line2: Optional[str] = Field(None, max_length=255)
|
||||
city: Optional[str] = Field(None, max_length=100)
|
||||
state_province: Optional[str] = Field(None, max_length=100)
|
||||
postal_code: Optional[str] = Field(None, max_length=20)
|
||||
country: Optional[str] = Field(None, max_length=100)
|
||||
|
||||
# Business terms
|
||||
payment_terms: PaymentTerms = PaymentTerms.net_30
|
||||
credit_limit: Optional[Decimal] = Field(None, ge=0)
|
||||
currency: str = Field(default="EUR", max_length=3)
|
||||
standard_lead_time: int = Field(default=3, ge=0, le=365)
|
||||
minimum_order_amount: Optional[Decimal] = Field(None, ge=0)
|
||||
delivery_area: Optional[str] = Field(None, max_length=255)
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
certifications: Optional[Union[Dict[str, Any], List[str]]] = None
|
||||
business_hours: Optional[Dict[str, Any]] = None
|
||||
specializations: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
class SupplierUpdate(BaseModel):
|
||||
"""Schema for updating suppliers"""
|
||||
name: Optional[str] = Field(None, min_length=1, max_length=255)
|
||||
supplier_code: Optional[str] = Field(None, max_length=50)
|
||||
tax_id: Optional[str] = Field(None, max_length=50)
|
||||
registration_number: Optional[str] = Field(None, max_length=100)
|
||||
supplier_type: Optional[SupplierType] = None
|
||||
status: Optional[SupplierStatus] = None
|
||||
contact_person: Optional[str] = Field(None, max_length=200)
|
||||
email: Optional[EmailStr] = None
|
||||
phone: Optional[str] = Field(None, max_length=30)
|
||||
mobile: Optional[str] = Field(None, max_length=30)
|
||||
website: Optional[str] = Field(None, max_length=255)
|
||||
|
||||
# Address
|
||||
address_line1: Optional[str] = Field(None, max_length=255)
|
||||
address_line2: Optional[str] = Field(None, max_length=255)
|
||||
city: Optional[str] = Field(None, max_length=100)
|
||||
state_province: Optional[str] = Field(None, max_length=100)
|
||||
postal_code: Optional[str] = Field(None, max_length=20)
|
||||
country: Optional[str] = Field(None, max_length=100)
|
||||
|
||||
# Business terms
|
||||
payment_terms: Optional[PaymentTerms] = None
|
||||
credit_limit: Optional[Decimal] = Field(None, ge=0)
|
||||
currency: Optional[str] = Field(None, max_length=3)
|
||||
standard_lead_time: Optional[int] = Field(None, ge=0, le=365)
|
||||
minimum_order_amount: Optional[Decimal] = Field(None, ge=0)
|
||||
delivery_area: Optional[str] = Field(None, max_length=255)
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
certifications: Optional[Union[Dict[str, Any], List[str]]] = None
|
||||
business_hours: Optional[Dict[str, Any]] = None
|
||||
specializations: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
class SupplierApproval(BaseModel):
|
||||
"""Schema for supplier approval/rejection"""
|
||||
action: str = Field(..., pattern="^(approve|reject)$")
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class SupplierResponse(BaseModel):
|
||||
"""Schema for supplier responses"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
name: str
|
||||
supplier_code: Optional[str] = None
|
||||
tax_id: Optional[str] = None
|
||||
registration_number: Optional[str] = None
|
||||
supplier_type: SupplierType
|
||||
status: SupplierStatus
|
||||
contact_person: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
mobile: Optional[str] = None
|
||||
website: Optional[str] = None
|
||||
|
||||
# Address
|
||||
address_line1: Optional[str] = None
|
||||
address_line2: Optional[str] = None
|
||||
city: Optional[str] = None
|
||||
state_province: Optional[str] = None
|
||||
postal_code: Optional[str] = None
|
||||
country: Optional[str] = None
|
||||
|
||||
# Business terms
|
||||
payment_terms: PaymentTerms
|
||||
credit_limit: Optional[Decimal] = None
|
||||
currency: str
|
||||
standard_lead_time: int
|
||||
minimum_order_amount: Optional[Decimal] = None
|
||||
delivery_area: Optional[str] = None
|
||||
|
||||
# Performance metrics
|
||||
quality_rating: Optional[float] = None
|
||||
delivery_rating: Optional[float] = None
|
||||
total_orders: int
|
||||
total_amount: Decimal
|
||||
|
||||
# Approval info
|
||||
approved_by: Optional[UUID] = None
|
||||
approved_at: Optional[datetime] = None
|
||||
rejection_reason: Optional[str] = None
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
certifications: Optional[Union[Dict[str, Any], List[str]]] = None
|
||||
business_hours: Optional[Dict[str, Any]] = None
|
||||
specializations: Optional[Dict[str, Any]] = None
|
||||
|
||||
# Audit fields
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
created_by: UUID
|
||||
updated_by: UUID
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class SupplierSummary(BaseModel):
|
||||
"""Schema for supplier summary (list view)"""
|
||||
id: UUID
|
||||
name: str
|
||||
supplier_code: Optional[str] = None
|
||||
supplier_type: SupplierType
|
||||
status: SupplierStatus
|
||||
contact_person: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
city: Optional[str] = None
|
||||
country: Optional[str] = None
|
||||
|
||||
# Business terms - Added for list view
|
||||
payment_terms: PaymentTerms
|
||||
standard_lead_time: int
|
||||
minimum_order_amount: Optional[Decimal] = None
|
||||
|
||||
# Performance metrics
|
||||
quality_rating: Optional[float] = None
|
||||
delivery_rating: Optional[float] = None
|
||||
total_orders: int
|
||||
total_amount: Decimal
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class SupplierDeletionSummary(BaseModel):
|
||||
"""Schema for supplier deletion summary"""
|
||||
supplier_name: str
|
||||
deleted_price_lists: int = 0
|
||||
deleted_quality_reviews: int = 0
|
||||
deleted_performance_metrics: int = 0
|
||||
deleted_alerts: int = 0
|
||||
deleted_scorecards: int = 0
|
||||
deletion_timestamp: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# PURCHASE ORDER SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
class PurchaseOrderItemCreate(BaseModel):
|
||||
"""Schema for creating purchase order items"""
|
||||
inventory_product_id: UUID
|
||||
product_code: Optional[str] = Field(None, max_length=100)
|
||||
ordered_quantity: int = Field(..., gt=0)
|
||||
unit_of_measure: str = Field(..., max_length=20)
|
||||
unit_price: Decimal = Field(..., gt=0)
|
||||
quality_requirements: Optional[str] = None
|
||||
item_notes: Optional[str] = None
|
||||
|
||||
|
||||
class PurchaseOrderItemUpdate(BaseModel):
|
||||
"""Schema for updating purchase order items"""
|
||||
ordered_quantity: Optional[int] = Field(None, gt=0)
|
||||
unit_price: Optional[Decimal] = Field(None, gt=0)
|
||||
quality_requirements: Optional[str] = None
|
||||
item_notes: Optional[str] = None
|
||||
|
||||
|
||||
class PurchaseOrderItemResponse(BaseModel):
|
||||
"""Schema for purchase order item responses"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
purchase_order_id: UUID
|
||||
price_list_item_id: Optional[UUID] = None
|
||||
inventory_product_id: UUID
|
||||
product_code: Optional[str] = None
|
||||
ordered_quantity: int
|
||||
unit_of_measure: str
|
||||
unit_price: Decimal
|
||||
line_total: Decimal
|
||||
received_quantity: int
|
||||
remaining_quantity: int
|
||||
quality_requirements: Optional[str] = None
|
||||
item_notes: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class PurchaseOrderCreate(BaseModel):
|
||||
"""Schema for creating purchase orders"""
|
||||
supplier_id: UUID
|
||||
reference_number: Optional[str] = Field(None, max_length=100)
|
||||
priority: str = Field(default="normal", max_length=20)
|
||||
required_delivery_date: Optional[datetime] = None
|
||||
|
||||
# Delivery information
|
||||
delivery_address: Optional[str] = None
|
||||
delivery_instructions: Optional[str] = None
|
||||
delivery_contact: Optional[str] = Field(None, max_length=200)
|
||||
delivery_phone: Optional[str] = Field(None, max_length=30)
|
||||
|
||||
# Financial information
|
||||
tax_amount: Decimal = Field(default=0, ge=0)
|
||||
shipping_cost: Decimal = Field(default=0, ge=0)
|
||||
discount_amount: Decimal = Field(default=0, ge=0)
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
internal_notes: Optional[str] = None
|
||||
terms_and_conditions: Optional[str] = None
|
||||
|
||||
# Items
|
||||
items: List[PurchaseOrderItemCreate] = Field(..., min_items=1)
|
||||
|
||||
|
||||
class PurchaseOrderUpdate(BaseModel):
|
||||
"""Schema for updating purchase orders"""
|
||||
reference_number: Optional[str] = Field(None, max_length=100)
|
||||
priority: Optional[str] = Field(None, max_length=20)
|
||||
required_delivery_date: Optional[datetime] = None
|
||||
estimated_delivery_date: Optional[datetime] = None
|
||||
|
||||
# Delivery information
|
||||
delivery_address: Optional[str] = None
|
||||
delivery_instructions: Optional[str] = None
|
||||
delivery_contact: Optional[str] = Field(None, max_length=200)
|
||||
delivery_phone: Optional[str] = Field(None, max_length=30)
|
||||
|
||||
# Financial information
|
||||
tax_amount: Optional[Decimal] = Field(None, ge=0)
|
||||
shipping_cost: Optional[Decimal] = Field(None, ge=0)
|
||||
discount_amount: Optional[Decimal] = Field(None, ge=0)
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
internal_notes: Optional[str] = None
|
||||
terms_and_conditions: Optional[str] = None
|
||||
|
||||
# Supplier communication
|
||||
supplier_reference: Optional[str] = Field(None, max_length=100)
|
||||
|
||||
|
||||
class PurchaseOrderStatusUpdate(BaseModel):
|
||||
"""Schema for updating purchase order status"""
|
||||
status: str # PurchaseOrderStatus - moved to Procurement Service
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class PurchaseOrderApproval(BaseModel):
|
||||
"""Schema for purchase order approval/rejection"""
|
||||
action: str = Field(..., pattern="^(approve|reject)$")
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class PurchaseOrderResponse(BaseModel):
|
||||
"""Schema for purchase order responses"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
supplier_id: UUID
|
||||
po_number: str
|
||||
reference_number: Optional[str] = None
|
||||
status: str # PurchaseOrderStatus
|
||||
priority: str
|
||||
order_date: datetime
|
||||
required_delivery_date: Optional[datetime] = None
|
||||
estimated_delivery_date: Optional[datetime] = None
|
||||
|
||||
# Financial information
|
||||
subtotal: Decimal
|
||||
tax_amount: Decimal
|
||||
shipping_cost: Decimal
|
||||
discount_amount: Decimal
|
||||
total_amount: Decimal
|
||||
currency: str
|
||||
|
||||
# Delivery information
|
||||
delivery_address: Optional[str] = None
|
||||
delivery_instructions: Optional[str] = None
|
||||
delivery_contact: Optional[str] = None
|
||||
delivery_phone: Optional[str] = None
|
||||
|
||||
# Approval workflow
|
||||
requires_approval: bool
|
||||
approved_by: Optional[UUID] = None
|
||||
approved_at: Optional[datetime] = None
|
||||
rejection_reason: Optional[str] = None
|
||||
|
||||
# Communication tracking
|
||||
sent_to_supplier_at: Optional[datetime] = None
|
||||
supplier_confirmation_date: Optional[datetime] = None
|
||||
supplier_reference: Optional[str] = None
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
internal_notes: Optional[str] = None
|
||||
terms_and_conditions: Optional[str] = None
|
||||
|
||||
# Audit fields
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
created_by: UUID
|
||||
updated_by: UUID
|
||||
|
||||
# Related data (populated separately)
|
||||
supplier: Optional[SupplierSummary] = None
|
||||
items: Optional[List[PurchaseOrderItemResponse]] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class PurchaseOrderSummary(BaseModel):
|
||||
"""Schema for purchase order summary (list view)"""
|
||||
id: UUID
|
||||
po_number: str
|
||||
supplier_id: UUID
|
||||
supplier_name: Optional[str] = None
|
||||
status: str # PurchaseOrderStatus
|
||||
priority: str
|
||||
order_date: datetime
|
||||
required_delivery_date: Optional[datetime] = None
|
||||
total_amount: Decimal
|
||||
currency: str
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# DELIVERY SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
class DeliveryItemCreate(BaseModel):
|
||||
"""Schema for creating delivery items"""
|
||||
purchase_order_item_id: UUID
|
||||
inventory_product_id: UUID
|
||||
ordered_quantity: int = Field(..., gt=0)
|
||||
delivered_quantity: int = Field(..., ge=0)
|
||||
accepted_quantity: int = Field(..., ge=0)
|
||||
rejected_quantity: int = Field(default=0, ge=0)
|
||||
|
||||
# Quality information
|
||||
batch_lot_number: Optional[str] = Field(None, max_length=100)
|
||||
expiry_date: Optional[datetime] = None
|
||||
quality_grade: Optional[str] = Field(None, max_length=20)
|
||||
|
||||
# Issues and notes
|
||||
quality_issues: Optional[str] = None
|
||||
rejection_reason: Optional[str] = None
|
||||
item_notes: Optional[str] = None
|
||||
|
||||
|
||||
class DeliveryItemResponse(BaseModel):
|
||||
"""Schema for delivery item responses"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
delivery_id: UUID
|
||||
purchase_order_item_id: UUID
|
||||
inventory_product_id: UUID
|
||||
ordered_quantity: int
|
||||
delivered_quantity: int
|
||||
accepted_quantity: int
|
||||
rejected_quantity: int
|
||||
batch_lot_number: Optional[str] = None
|
||||
expiry_date: Optional[datetime] = None
|
||||
quality_grade: Optional[str] = None
|
||||
quality_issues: Optional[str] = None
|
||||
rejection_reason: Optional[str] = None
|
||||
item_notes: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DeliveryCreate(BaseModel):
|
||||
"""Schema for creating deliveries"""
|
||||
purchase_order_id: UUID
|
||||
supplier_id: UUID
|
||||
supplier_delivery_note: Optional[str] = Field(None, max_length=100)
|
||||
scheduled_date: Optional[datetime] = None
|
||||
estimated_arrival: Optional[datetime] = None
|
||||
|
||||
# Delivery details
|
||||
delivery_address: Optional[str] = None
|
||||
delivery_contact: Optional[str] = Field(None, max_length=200)
|
||||
delivery_phone: Optional[str] = Field(None, max_length=30)
|
||||
carrier_name: Optional[str] = Field(None, max_length=200)
|
||||
tracking_number: Optional[str] = Field(None, max_length=100)
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
|
||||
# Items
|
||||
items: List[DeliveryItemCreate] = Field(..., min_items=1)
|
||||
|
||||
|
||||
class DeliveryUpdate(BaseModel):
|
||||
"""Schema for updating deliveries"""
|
||||
supplier_delivery_note: Optional[str] = Field(None, max_length=100)
|
||||
scheduled_date: Optional[datetime] = None
|
||||
estimated_arrival: Optional[datetime] = None
|
||||
actual_arrival: Optional[datetime] = None
|
||||
|
||||
# Delivery details
|
||||
delivery_address: Optional[str] = None
|
||||
delivery_contact: Optional[str] = Field(None, max_length=200)
|
||||
delivery_phone: Optional[str] = Field(None, max_length=30)
|
||||
carrier_name: Optional[str] = Field(None, max_length=200)
|
||||
tracking_number: Optional[str] = Field(None, max_length=100)
|
||||
|
||||
# Quality inspection
|
||||
inspection_passed: Optional[bool] = None
|
||||
inspection_notes: Optional[str] = None
|
||||
quality_issues: Optional[Dict[str, Any]] = None
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class DeliveryStatusUpdate(BaseModel):
|
||||
"""Schema for updating delivery status"""
|
||||
status: str # DeliveryStatus
|
||||
notes: Optional[str] = None
|
||||
update_timestamps: bool = Field(default=True)
|
||||
|
||||
|
||||
class DeliveryReceiptConfirmation(BaseModel):
|
||||
"""Schema for confirming delivery receipt"""
|
||||
inspection_passed: bool = True
|
||||
inspection_notes: Optional[str] = None
|
||||
quality_issues: Optional[Dict[str, Any]] = None
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class DeliveryResponse(BaseModel):
|
||||
"""Schema for delivery responses"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
purchase_order_id: UUID
|
||||
supplier_id: UUID
|
||||
delivery_number: str
|
||||
supplier_delivery_note: Optional[str] = None
|
||||
status: str # DeliveryStatus
|
||||
|
||||
# Timing
|
||||
scheduled_date: Optional[datetime] = None
|
||||
estimated_arrival: Optional[datetime] = None
|
||||
actual_arrival: Optional[datetime] = None
|
||||
completed_at: Optional[datetime] = None
|
||||
|
||||
# Delivery details
|
||||
delivery_address: Optional[str] = None
|
||||
delivery_contact: Optional[str] = None
|
||||
delivery_phone: Optional[str] = None
|
||||
carrier_name: Optional[str] = None
|
||||
tracking_number: Optional[str] = None
|
||||
|
||||
# Quality inspection
|
||||
inspection_passed: Optional[bool] = None
|
||||
inspection_notes: Optional[str] = None
|
||||
quality_issues: Optional[Dict[str, Any]] = None
|
||||
|
||||
# Receipt information
|
||||
received_by: Optional[UUID] = None
|
||||
received_at: Optional[datetime] = None
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
photos: Optional[Dict[str, Any]] = None
|
||||
|
||||
# Audit fields
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
created_by: UUID
|
||||
|
||||
# Related data
|
||||
supplier: Optional[SupplierSummary] = None
|
||||
purchase_order: Optional[PurchaseOrderSummary] = None
|
||||
items: Optional[List[DeliveryItemResponse]] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class DeliverySummary(BaseModel):
|
||||
"""Schema for delivery summary (list view)"""
|
||||
id: UUID
|
||||
delivery_number: str
|
||||
supplier_id: UUID
|
||||
supplier_name: Optional[str] = None
|
||||
purchase_order_id: UUID
|
||||
po_number: Optional[str] = None
|
||||
status: str # DeliveryStatus
|
||||
scheduled_date: Optional[datetime] = None
|
||||
actual_arrival: Optional[datetime] = None
|
||||
inspection_passed: Optional[bool] = None
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# SEARCH AND FILTER SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
class SupplierSearchParams(BaseModel):
|
||||
"""Search parameters for suppliers"""
|
||||
search_term: Optional[str] = Field(None, max_length=100)
|
||||
supplier_type: Optional[SupplierType] = None
|
||||
status: Optional[SupplierStatus] = None
|
||||
limit: int = Field(default=50, ge=1, le=1000)
|
||||
offset: int = Field(default=0, ge=0)
|
||||
|
||||
|
||||
class PurchaseOrderSearchParams(BaseModel):
|
||||
"""Search parameters for purchase orders"""
|
||||
supplier_id: Optional[UUID] = None
|
||||
status: Optional[str] = None # PurchaseOrderStatus
|
||||
priority: Optional[str] = None
|
||||
date_from: Optional[datetime] = None
|
||||
date_to: Optional[datetime] = None
|
||||
search_term: Optional[str] = Field(None, max_length=100)
|
||||
limit: int = Field(default=50, ge=1, le=1000)
|
||||
offset: int = Field(default=0, ge=0)
|
||||
|
||||
|
||||
class DeliverySearchParams(BaseModel):
|
||||
"""Search parameters for deliveries"""
|
||||
supplier_id: Optional[UUID] = None
|
||||
status: Optional[str] = None # DeliveryStatus
|
||||
date_from: Optional[datetime] = None
|
||||
date_to: Optional[datetime] = None
|
||||
search_term: Optional[str] = Field(None, max_length=100)
|
||||
limit: int = Field(default=50, ge=1, le=1000)
|
||||
offset: int = Field(default=0, ge=0)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# SUPPLIER PRICE LIST SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
class SupplierPriceListCreate(BaseModel):
|
||||
"""Schema for creating supplier price list items"""
|
||||
inventory_product_id: UUID
|
||||
product_code: Optional[str] = Field(None, max_length=100)
|
||||
unit_price: Decimal = Field(..., gt=0)
|
||||
unit_of_measure: str = Field(..., max_length=20)
|
||||
minimum_order_quantity: Optional[int] = Field(None, ge=1)
|
||||
price_per_unit: Decimal = Field(..., gt=0)
|
||||
tier_pricing: Optional[Dict[str, Any]] = None # [{quantity: 100, price: 2.50}, ...]
|
||||
effective_date: Optional[datetime] = Field(default_factory=lambda: datetime.now())
|
||||
expiry_date: Optional[datetime] = None
|
||||
is_active: bool = True
|
||||
brand: Optional[str] = Field(None, max_length=100)
|
||||
packaging_size: Optional[str] = Field(None, max_length=50)
|
||||
origin_country: Optional[str] = Field(None, max_length=100)
|
||||
shelf_life_days: Optional[int] = None
|
||||
storage_requirements: Optional[str] = None
|
||||
quality_specs: Optional[Dict[str, Any]] = None
|
||||
allergens: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
class SupplierPriceListUpdate(BaseModel):
|
||||
"""Schema for updating supplier price list items"""
|
||||
unit_price: Optional[Decimal] = Field(None, gt=0)
|
||||
unit_of_measure: Optional[str] = Field(None, max_length=20)
|
||||
minimum_order_quantity: Optional[int] = Field(None, ge=1)
|
||||
tier_pricing: Optional[Dict[str, Any]] = None
|
||||
effective_date: Optional[datetime] = None
|
||||
expiry_date: Optional[datetime] = None
|
||||
is_active: Optional[bool] = None
|
||||
brand: Optional[str] = Field(None, max_length=100)
|
||||
packaging_size: Optional[str] = Field(None, max_length=50)
|
||||
origin_country: Optional[str] = Field(None, max_length=100)
|
||||
shelf_life_days: Optional[int] = None
|
||||
storage_requirements: Optional[str] = None
|
||||
quality_specs: Optional[Dict[str, Any]] = None
|
||||
allergens: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
class SupplierPriceListResponse(BaseModel):
|
||||
"""Schema for supplier price list responses"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
supplier_id: UUID
|
||||
inventory_product_id: UUID
|
||||
product_code: Optional[str] = None
|
||||
unit_price: Decimal
|
||||
unit_of_measure: str
|
||||
minimum_order_quantity: Optional[int] = None
|
||||
price_per_unit: Decimal
|
||||
tier_pricing: Optional[Dict[str, Any]] = None
|
||||
effective_date: datetime
|
||||
expiry_date: Optional[datetime] = None
|
||||
is_active: bool
|
||||
brand: Optional[str] = None
|
||||
packaging_size: Optional[str] = None
|
||||
origin_country: Optional[str] = None
|
||||
shelf_life_days: Optional[int] = None
|
||||
storage_requirements: Optional[str] = None
|
||||
quality_specs: Optional[Dict[str, Any]] = None
|
||||
allergens: Optional[Dict[str, Any]] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
created_by: UUID
|
||||
updated_by: UUID
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# STATISTICS AND REPORTING SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
class SupplierStatistics(BaseModel):
|
||||
"""Schema for supplier statistics"""
|
||||
total_suppliers: int
|
||||
active_suppliers: int
|
||||
pending_suppliers: int
|
||||
avg_quality_rating: float
|
||||
avg_delivery_rating: float
|
||||
total_spend: float
|
||||
|
||||
|
||||
class PurchaseOrderStatistics(BaseModel):
|
||||
"""Schema for purchase order statistics"""
|
||||
total_orders: int
|
||||
status_counts: Dict[str, int]
|
||||
this_month_orders: int
|
||||
this_month_spend: float
|
||||
avg_order_value: float
|
||||
overdue_count: int
|
||||
pending_approval: int
|
||||
|
||||
|
||||
class DeliveryPerformanceStats(BaseModel):
|
||||
"""Schema for delivery performance statistics"""
|
||||
total_deliveries: int
|
||||
on_time_deliveries: int
|
||||
late_deliveries: int
|
||||
failed_deliveries: int
|
||||
on_time_percentage: float
|
||||
avg_delay_hours: float
|
||||
quality_pass_rate: float
|
||||
|
||||
|
||||
class DeliverySummaryStats(BaseModel):
|
||||
"""Schema for delivery summary statistics"""
|
||||
todays_deliveries: int
|
||||
this_week_deliveries: int
|
||||
overdue_deliveries: int
|
||||
in_transit_deliveries: int
|
||||
Reference in New Issue
Block a user