Initial commit - production deployment
This commit is contained in:
395
services/procurement/app/schemas/purchase_order_schemas.py
Normal file
395
services/procurement/app/schemas/purchase_order_schemas.py
Normal file
@@ -0,0 +1,395 @@
|
||||
# ================================================================
|
||||
# services/procurement/app/schemas/purchase_order_schemas.py
|
||||
# ================================================================
|
||||
"""
|
||||
Purchase Order Schemas - Request/response models for purchase orders
|
||||
Migrated from Suppliers Service with procurement-specific additions
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from datetime import datetime, date
|
||||
from decimal import Decimal
|
||||
from typing import Optional, List, Dict, Any
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
|
||||
|
||||
# ================================================================
|
||||
# BASE SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class PurchaseOrderBase(BaseModel):
|
||||
"""Base schema for purchase order entities"""
|
||||
model_config = ConfigDict(from_attributes=True, str_strip_whitespace=True)
|
||||
|
||||
|
||||
# ================================================================
|
||||
# PURCHASE ORDER ITEM SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class PurchaseOrderItemCreate(PurchaseOrderBase):
|
||||
"""Schema for creating purchase order items"""
|
||||
inventory_product_id: uuid.UUID # Changed from ingredient_id to match model
|
||||
ordered_quantity: Decimal = Field(..., gt=0)
|
||||
unit_price: Decimal = Field(..., gt=0)
|
||||
unit_of_measure: str = Field(..., max_length=50)
|
||||
quality_requirements: Optional[str] = None
|
||||
item_notes: Optional[str] = None
|
||||
|
||||
|
||||
class PurchaseOrderItemUpdate(PurchaseOrderBase):
|
||||
"""Schema for updating purchase order items"""
|
||||
ordered_quantity: Optional[Decimal] = Field(None, gt=0)
|
||||
unit_price: Optional[Decimal] = Field(None, gt=0)
|
||||
quality_requirements: Optional[str] = None
|
||||
item_notes: Optional[str] = None
|
||||
|
||||
|
||||
class PurchaseOrderItemResponse(PurchaseOrderBase):
|
||||
"""Schema for purchase order item responses"""
|
||||
id: uuid.UUID
|
||||
tenant_id: uuid.UUID
|
||||
purchase_order_id: uuid.UUID
|
||||
inventory_product_id: uuid.UUID # Changed from ingredient_id to match model
|
||||
product_name: Optional[str] = None
|
||||
ordered_quantity: Decimal
|
||||
received_quantity: Decimal
|
||||
unit_price: Decimal
|
||||
unit_of_measure: str
|
||||
line_total: Decimal
|
||||
quality_requirements: Optional[str] = None
|
||||
item_notes: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
# ================================================================
|
||||
# PURCHASE ORDER SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class PurchaseOrderCreate(PurchaseOrderBase):
|
||||
"""Schema for creating purchase orders"""
|
||||
supplier_id: uuid.UUID
|
||||
required_delivery_date: datetime # Use datetime with timezone
|
||||
priority: str = Field(default="normal", pattern="^(low|normal|high|critical)$")
|
||||
|
||||
# Financial information
|
||||
tax_amount: Decimal = Field(default=Decimal("0"), ge=0)
|
||||
shipping_cost: Decimal = Field(default=Decimal("0"), ge=0)
|
||||
discount_amount: Decimal = Field(default=Decimal("0"), ge=0)
|
||||
subtotal: Decimal = Field(..., ge=0)
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
|
||||
# NEW: Procurement-specific fields
|
||||
procurement_plan_id: Optional[uuid.UUID] = None
|
||||
|
||||
# Items
|
||||
items: List[PurchaseOrderItemCreate] = Field(..., min_length=1)
|
||||
|
||||
|
||||
class PurchaseOrderUpdate(PurchaseOrderBase):
|
||||
"""Schema for updating purchase orders"""
|
||||
required_delivery_date: Optional[datetime] = None # Use datetime with timezone
|
||||
priority: Optional[str] = Field(None, pattern="^(low|normal|high|critical)$")
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
class PurchaseOrderApproval(PurchaseOrderBase):
|
||||
"""Schema for purchase order approval/rejection"""
|
||||
action: str = Field(..., pattern="^(approve|reject)$")
|
||||
notes: Optional[str] = None
|
||||
approved_by: Optional[uuid.UUID] = None
|
||||
|
||||
|
||||
class SupplierSummary(PurchaseOrderBase):
|
||||
"""Schema for supplier summary - matches the structure returned by suppliers service"""
|
||||
id: str
|
||||
name: str
|
||||
supplier_code: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
contact_person: Optional[str] = None
|
||||
address_line1: Optional[str] = None
|
||||
city: Optional[str] = None
|
||||
country: Optional[str] = None
|
||||
supplier_type: Optional[str] = None
|
||||
status: Optional[str] = None
|
||||
mobile: Optional[str] = None
|
||||
website: Optional[str] = None
|
||||
payment_terms: Optional[str] = None
|
||||
standard_lead_time: Optional[int] = None
|
||||
quality_rating: Optional[float] = None
|
||||
delivery_rating: Optional[float] = None
|
||||
total_orders: Optional[int] = None
|
||||
total_amount: Optional[float] = None
|
||||
|
||||
|
||||
class PurchaseOrderResponse(PurchaseOrderBase):
|
||||
"""Schema for purchase order responses"""
|
||||
id: uuid.UUID
|
||||
tenant_id: uuid.UUID
|
||||
supplier_id: uuid.UUID
|
||||
supplier_name: Optional[str] = None
|
||||
po_number: str
|
||||
status: str
|
||||
priority: str
|
||||
|
||||
order_date: datetime
|
||||
required_delivery_date: Optional[datetime] = None # Use datetime with timezone
|
||||
estimated_delivery_date: Optional[datetime] = None # Use datetime with timezone
|
||||
actual_delivery_date: Optional[datetime] = None # Use datetime with timezone
|
||||
|
||||
# Financial information
|
||||
subtotal: Decimal
|
||||
tax_amount: Decimal
|
||||
shipping_cost: Decimal
|
||||
discount_amount: Decimal
|
||||
total_amount: Decimal
|
||||
currency: str
|
||||
|
||||
# Approval workflow
|
||||
approved_by: Optional[uuid.UUID] = None
|
||||
approved_at: Optional[datetime] = None
|
||||
rejection_reason: Optional[str] = None
|
||||
|
||||
# NEW: Procurement-specific fields
|
||||
procurement_plan_id: Optional[uuid.UUID] = None
|
||||
auto_approved: bool = False
|
||||
auto_approval_rule_id: Optional[uuid.UUID] = None
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
|
||||
# AI/ML reasoning for procurement decisions (JTBD dashboard support)
|
||||
reasoning_data: Optional[Dict[str, Any]] = None
|
||||
|
||||
# Audit fields
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
created_by: Optional[uuid.UUID] = None
|
||||
updated_by: Optional[uuid.UUID] = None
|
||||
|
||||
# Related data
|
||||
items: List[PurchaseOrderItemResponse] = []
|
||||
|
||||
|
||||
class PurchaseOrderWithSupplierResponse(PurchaseOrderResponse):
|
||||
"""Schema for purchase order responses with supplier information"""
|
||||
supplier: Optional[SupplierSummary] = None
|
||||
|
||||
|
||||
class PurchaseOrderSummary(PurchaseOrderBase):
|
||||
"""Schema for purchase order summary (list view)"""
|
||||
id: uuid.UUID
|
||||
po_number: str
|
||||
supplier_id: uuid.UUID
|
||||
supplier_name: Optional[str] = None
|
||||
status: str
|
||||
priority: str
|
||||
order_date: datetime
|
||||
required_delivery_date: datetime # Use datetime with timezone
|
||||
total_amount: Decimal
|
||||
currency: str
|
||||
auto_approved: bool = False
|
||||
created_at: datetime
|
||||
|
||||
|
||||
# ================================================================
|
||||
# DELIVERY SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class DeliveryItemCreate(PurchaseOrderBase):
|
||||
"""Schema for creating delivery items"""
|
||||
purchase_order_item_id: uuid.UUID
|
||||
inventory_product_id: uuid.UUID # Changed from ingredient_id to match model
|
||||
ordered_quantity: Decimal = Field(..., gt=0)
|
||||
delivered_quantity: Decimal = Field(..., ge=0)
|
||||
accepted_quantity: Decimal = Field(..., ge=0)
|
||||
rejected_quantity: Decimal = Field(default=Decimal("0"), ge=0)
|
||||
|
||||
# Quality information
|
||||
batch_lot_number: Optional[str] = Field(None, max_length=100)
|
||||
expiry_date: Optional[datetime] = None # Use datetime with timezone
|
||||
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(PurchaseOrderBase):
|
||||
"""Schema for delivery item responses"""
|
||||
id: uuid.UUID
|
||||
tenant_id: uuid.UUID
|
||||
delivery_id: uuid.UUID
|
||||
purchase_order_item_id: uuid.UUID
|
||||
inventory_product_id: uuid.UUID # Changed from ingredient_id to match model
|
||||
ingredient_name: Optional[str] = None
|
||||
ordered_quantity: Decimal
|
||||
delivered_quantity: Decimal
|
||||
accepted_quantity: Decimal
|
||||
rejected_quantity: Decimal
|
||||
batch_lot_number: Optional[str] = None
|
||||
expiry_date: Optional[datetime] = None # Use datetime with timezone
|
||||
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 DeliveryCreate(PurchaseOrderBase):
|
||||
"""Schema for creating deliveries"""
|
||||
purchase_order_id: uuid.UUID
|
||||
supplier_id: uuid.UUID
|
||||
supplier_delivery_note: Optional[str] = Field(None, max_length=100)
|
||||
scheduled_date: Optional[datetime] = None # Use datetime with timezone
|
||||
estimated_arrival: Optional[datetime] = None
|
||||
|
||||
# Delivery details
|
||||
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_length=1)
|
||||
|
||||
|
||||
class DeliveryUpdate(PurchaseOrderBase):
|
||||
"""Schema for updating deliveries"""
|
||||
supplier_delivery_note: Optional[str] = Field(None, max_length=100)
|
||||
scheduled_date: Optional[datetime] = None # Use datetime with timezone
|
||||
estimated_arrival: Optional[datetime] = None
|
||||
actual_arrival: Optional[datetime] = None
|
||||
|
||||
# Delivery details
|
||||
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 DeliveryResponse(PurchaseOrderBase):
|
||||
"""Schema for delivery responses"""
|
||||
id: uuid.UUID
|
||||
tenant_id: uuid.UUID
|
||||
purchase_order_id: uuid.UUID
|
||||
supplier_id: uuid.UUID
|
||||
supplier_name: Optional[str] = None
|
||||
delivery_number: str
|
||||
supplier_delivery_note: Optional[str] = None
|
||||
status: str
|
||||
|
||||
# Timing
|
||||
scheduled_date: Optional[datetime] = None # Use datetime with timezone
|
||||
estimated_arrival: Optional[datetime] = None
|
||||
actual_arrival: Optional[datetime] = None
|
||||
completed_at: Optional[datetime] = None
|
||||
|
||||
# Delivery details
|
||||
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.UUID] = None
|
||||
received_at: Optional[datetime] = None
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
|
||||
# Audit fields
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
created_by: uuid.UUID
|
||||
|
||||
# Related data
|
||||
items: List[DeliveryItemResponse] = []
|
||||
|
||||
|
||||
# ================================================================
|
||||
# INVOICE SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class SupplierInvoiceCreate(PurchaseOrderBase):
|
||||
"""Schema for creating supplier invoices"""
|
||||
purchase_order_id: uuid.UUID
|
||||
supplier_id: uuid.UUID
|
||||
invoice_number: str = Field(..., max_length=100)
|
||||
invoice_date: datetime # Use datetime with timezone
|
||||
due_date: datetime # Use datetime with timezone
|
||||
|
||||
# Financial information
|
||||
subtotal: Decimal = Field(..., ge=0)
|
||||
tax_amount: Decimal = Field(default=Decimal("0"), ge=0)
|
||||
shipping_cost: Decimal = Field(default=Decimal("0"), ge=0)
|
||||
discount_amount: Decimal = Field(default=Decimal("0"), ge=0)
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
payment_reference: Optional[str] = Field(None, max_length=100)
|
||||
|
||||
|
||||
class SupplierInvoiceUpdate(PurchaseOrderBase):
|
||||
"""Schema for updating supplier invoices"""
|
||||
due_date: Optional[datetime] = None # Use datetime with timezone
|
||||
payment_reference: Optional[str] = Field(None, max_length=100)
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class SupplierInvoiceResponse(PurchaseOrderBase):
|
||||
"""Schema for supplier invoice responses"""
|
||||
id: uuid.UUID
|
||||
tenant_id: uuid.UUID
|
||||
purchase_order_id: uuid.UUID
|
||||
supplier_id: uuid.UUID
|
||||
supplier_name: Optional[str] = None
|
||||
invoice_number: str
|
||||
status: str
|
||||
invoice_date: datetime # Use datetime with timezone
|
||||
due_date: datetime # Use datetime with timezone
|
||||
|
||||
# Financial information
|
||||
subtotal: Decimal
|
||||
tax_amount: Decimal
|
||||
shipping_cost: Decimal
|
||||
discount_amount: Decimal
|
||||
total_amount: Decimal
|
||||
currency: str
|
||||
|
||||
# Payment tracking
|
||||
paid_amount: Decimal
|
||||
remaining_amount: Decimal
|
||||
payment_date: Optional[datetime] = None # Use datetime with timezone
|
||||
payment_reference: Optional[str] = None
|
||||
|
||||
# Additional information
|
||||
notes: Optional[str] = None
|
||||
|
||||
# Audit fields
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
created_by: uuid.UUID
|
||||
updated_by: uuid.UUID
|
||||
Reference in New Issue
Block a user