Initial commit - production deployment
This commit is contained in:
68
services/orders/app/models/__init__.py
Normal file
68
services/orders/app/models/__init__.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""
|
||||
Orders Service Models Package
|
||||
|
||||
Import all models to ensure they are registered with SQLAlchemy Base.
|
||||
"""
|
||||
|
||||
# Import AuditLog model for this service
|
||||
from shared.security import create_audit_log_model
|
||||
from shared.database.base import Base
|
||||
|
||||
# Create audit log model for this service
|
||||
AuditLog = create_audit_log_model(Base)
|
||||
|
||||
# Import all models to register them with the Base metadata
|
||||
from .customer import Customer, CustomerContact
|
||||
from .order import CustomerOrder, OrderItem, OrderStatusHistory
|
||||
|
||||
# Import enums
|
||||
from .enums import (
|
||||
CustomerType,
|
||||
DeliveryMethod,
|
||||
PaymentTerms,
|
||||
PaymentMethod,
|
||||
PaymentStatus,
|
||||
CustomerSegment,
|
||||
SalesChannel,
|
||||
BusinessModel,
|
||||
OrderType,
|
||||
OrderSource,
|
||||
OrderStatus,
|
||||
DeliveryStatus,
|
||||
ProcurementPlanType,
|
||||
ProcurementStrategy,
|
||||
PlanStatus,
|
||||
PriorityLevel,
|
||||
RequirementStatus,
|
||||
RiskLevel,
|
||||
)
|
||||
|
||||
# List all models for easier access
|
||||
__all__ = [
|
||||
# Models
|
||||
"Customer",
|
||||
"CustomerContact",
|
||||
"CustomerOrder",
|
||||
"OrderItem",
|
||||
"OrderStatusHistory",
|
||||
# Enums
|
||||
"CustomerType",
|
||||
"DeliveryMethod",
|
||||
"PaymentTerms",
|
||||
"PaymentMethod",
|
||||
"PaymentStatus",
|
||||
"CustomerSegment",
|
||||
"SalesChannel",
|
||||
"BusinessModel",
|
||||
"OrderType",
|
||||
"OrderSource",
|
||||
"OrderStatus",
|
||||
"DeliveryStatus",
|
||||
"ProcurementPlanType",
|
||||
"ProcurementStrategy",
|
||||
"PlanStatus",
|
||||
"PriorityLevel",
|
||||
"RequirementStatus",
|
||||
"RiskLevel",
|
||||
"AuditLog",
|
||||
]
|
||||
123
services/orders/app/models/customer.py
Normal file
123
services/orders/app/models/customer.py
Normal file
@@ -0,0 +1,123 @@
|
||||
# ================================================================
|
||||
# services/orders/app/models/customer.py
|
||||
# ================================================================
|
||||
"""
|
||||
Customer-related database models for Orders Service
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from typing import Optional, List
|
||||
from sqlalchemy import Column, String, Boolean, DateTime, Numeric, Text, ForeignKey, Integer
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from shared.database.base import Base
|
||||
|
||||
|
||||
class Customer(Base):
|
||||
"""Customer model for managing customer information"""
|
||||
__tablename__ = "customers"
|
||||
|
||||
# Primary identification
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
||||
customer_code = Column(String(50), nullable=False, index=True) # Human-readable code
|
||||
|
||||
# Basic information
|
||||
name = Column(String(200), nullable=False)
|
||||
business_name = Column(String(200), nullable=True)
|
||||
customer_type = Column(String(50), nullable=False, default="individual") # individual, business, central_bakery
|
||||
|
||||
# Contact information
|
||||
email = Column(String(255), nullable=True)
|
||||
phone = Column(String(50), nullable=True)
|
||||
|
||||
# Address information
|
||||
address_line1 = Column(String(255), nullable=True)
|
||||
address_line2 = Column(String(255), nullable=True)
|
||||
city = Column(String(100), nullable=True)
|
||||
state = Column(String(100), nullable=True)
|
||||
postal_code = Column(String(20), nullable=True)
|
||||
country = Column(String(100), nullable=False, default="US")
|
||||
|
||||
# Business information
|
||||
tax_id = Column(String(50), nullable=True)
|
||||
business_license = Column(String(100), nullable=True)
|
||||
|
||||
# Customer status and preferences
|
||||
is_active = Column(Boolean, nullable=False, default=True)
|
||||
preferred_delivery_method = Column(String(50), nullable=False, default="delivery") # delivery, pickup
|
||||
payment_terms = Column(String(50), nullable=False, default="immediate") # immediate, net_30, net_60
|
||||
credit_limit = Column(Numeric(10, 2), nullable=True)
|
||||
discount_percentage = Column(Numeric(5, 2), nullable=False, default=Decimal("0.00"))
|
||||
|
||||
# Customer categorization
|
||||
customer_segment = Column(String(50), nullable=False, default="regular") # vip, regular, wholesale
|
||||
priority_level = Column(String(20), nullable=False, default="normal") # high, normal, low
|
||||
|
||||
# Preferences and special requirements
|
||||
special_instructions = Column(Text, nullable=True)
|
||||
delivery_preferences = Column(JSONB, nullable=True) # Time windows, special requirements
|
||||
product_preferences = Column(JSONB, nullable=True) # Favorite products, allergies
|
||||
|
||||
# Customer metrics
|
||||
total_orders = Column(Integer, nullable=False, default=0)
|
||||
total_spent = Column(Numeric(12, 2), nullable=False, default=Decimal("0.00"))
|
||||
average_order_value = Column(Numeric(10, 2), nullable=False, default=Decimal("0.00"))
|
||||
last_order_date = Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# Audit fields
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
|
||||
created_by = Column(UUID(as_uuid=True), nullable=True)
|
||||
updated_by = Column(UUID(as_uuid=True), nullable=True)
|
||||
|
||||
# Relationships
|
||||
contacts = relationship("CustomerContact", back_populates="customer", cascade="all, delete-orphan")
|
||||
orders = relationship("CustomerOrder", back_populates="customer")
|
||||
|
||||
|
||||
class CustomerContact(Base):
|
||||
"""Additional contact persons for business customers"""
|
||||
__tablename__ = "customer_contacts"
|
||||
|
||||
# Primary identification
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
customer_id = Column(UUID(as_uuid=True), ForeignKey("customers.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
# Contact information
|
||||
name = Column(String(200), nullable=False)
|
||||
title = Column(String(100), nullable=True)
|
||||
department = Column(String(100), nullable=True)
|
||||
|
||||
# Contact details
|
||||
email = Column(String(255), nullable=True)
|
||||
phone = Column(String(50), nullable=True)
|
||||
mobile = Column(String(50), nullable=True)
|
||||
|
||||
# Contact preferences
|
||||
is_primary = Column(Boolean, nullable=False, default=False)
|
||||
contact_for_orders = Column(Boolean, nullable=False, default=True)
|
||||
contact_for_delivery = Column(Boolean, nullable=False, default=False)
|
||||
contact_for_billing = Column(Boolean, nullable=False, default=False)
|
||||
contact_for_support = Column(Boolean, nullable=False, default=False)
|
||||
|
||||
# Preferred contact methods
|
||||
preferred_contact_method = Column(String(50), nullable=False, default="email") # email, phone, sms
|
||||
contact_time_preferences = Column(JSONB, nullable=True) # Time windows for contact
|
||||
|
||||
# Notes and special instructions
|
||||
notes = Column(Text, nullable=True)
|
||||
|
||||
# Status
|
||||
is_active = Column(Boolean, nullable=False, default=True)
|
||||
|
||||
# Audit fields
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
|
||||
|
||||
# Relationships
|
||||
customer = relationship("Customer", back_populates="contacts")
|
||||
162
services/orders/app/models/enums.py
Normal file
162
services/orders/app/models/enums.py
Normal file
@@ -0,0 +1,162 @@
|
||||
# services/orders/app/models/enums.py
|
||||
"""
|
||||
Enum definitions for Orders Service
|
||||
Following the pattern used in the Inventory Service for better type safety and maintainability
|
||||
"""
|
||||
|
||||
import enum
|
||||
|
||||
|
||||
class CustomerType(enum.Enum):
|
||||
"""Customer type classifications"""
|
||||
INDIVIDUAL = "individual"
|
||||
BUSINESS = "business"
|
||||
CENTRAL_BAKERY = "central_bakery"
|
||||
RETAIL = "RETAIL"
|
||||
WHOLESALE = "WHOLESALE"
|
||||
RESTAURANT = "RESTAURANT"
|
||||
HOTEL = "HOTEL"
|
||||
ENTERPRISE = "ENTERPRISE"
|
||||
|
||||
|
||||
class DeliveryMethod(enum.Enum):
|
||||
"""Order delivery methods"""
|
||||
DELIVERY = "delivery"
|
||||
PICKUP = "pickup"
|
||||
STANDARD = "standard" # Standard delivery method
|
||||
|
||||
|
||||
class PaymentTerms(enum.Enum):
|
||||
"""Payment terms for customers and orders"""
|
||||
IMMEDIATE = "immediate"
|
||||
NET_15 = "net_15"
|
||||
NET_30 = "net_30"
|
||||
NET_60 = "net_60"
|
||||
|
||||
|
||||
class PaymentMethod(enum.Enum):
|
||||
"""Payment methods for orders"""
|
||||
CASH = "cash"
|
||||
CARD = "card"
|
||||
CREDIT_CARD = "credit_card" # Credit card payment
|
||||
CHECK = "check" # Bank check/cheque payment
|
||||
BANK_TRANSFER = "bank_transfer"
|
||||
ACCOUNT = "account"
|
||||
|
||||
|
||||
class PaymentStatus(enum.Enum):
|
||||
"""Payment status for orders"""
|
||||
PENDING = "pending"
|
||||
PARTIAL = "partial"
|
||||
PAID = "paid"
|
||||
FAILED = "failed"
|
||||
REFUNDED = "refunded"
|
||||
|
||||
|
||||
class CustomerSegment(enum.Enum):
|
||||
"""Customer segmentation categories"""
|
||||
VIP = "vip"
|
||||
REGULAR = "regular"
|
||||
WHOLESALE = "wholesale"
|
||||
|
||||
|
||||
class PriorityLevel(enum.Enum):
|
||||
"""Priority levels for orders and customers"""
|
||||
URGENT = "urgent"
|
||||
HIGH = "high"
|
||||
NORMAL = "normal"
|
||||
LOW = "low"
|
||||
|
||||
|
||||
class OrderType(enum.Enum):
|
||||
"""Order type classifications"""
|
||||
STANDARD = "standard"
|
||||
RUSH = "rush"
|
||||
RECURRING = "recurring"
|
||||
SPECIAL = "special"
|
||||
|
||||
|
||||
class OrderStatus(enum.Enum):
|
||||
"""Order status workflow"""
|
||||
PENDING = "pending"
|
||||
CONFIRMED = "confirmed"
|
||||
IN_PRODUCTION = "in_production"
|
||||
READY = "ready"
|
||||
OUT_FOR_DELIVERY = "out_for_delivery"
|
||||
DELIVERED = "delivered"
|
||||
CANCELLED = "cancelled"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
class OrderSource(enum.Enum):
|
||||
"""Source of order creation"""
|
||||
MANUAL = "manual"
|
||||
ONLINE = "online"
|
||||
PHONE = "phone"
|
||||
APP = "app"
|
||||
API = "api"
|
||||
|
||||
|
||||
class SalesChannel(enum.Enum):
|
||||
"""Sales channel classification"""
|
||||
DIRECT = "direct"
|
||||
WHOLESALE = "wholesale"
|
||||
RETAIL = "retail"
|
||||
|
||||
|
||||
class BusinessModel(enum.Enum):
|
||||
"""Business model types"""
|
||||
INDIVIDUAL_BAKERY = "individual_bakery"
|
||||
CENTRAL_BAKERY = "central_bakery"
|
||||
|
||||
|
||||
# Procurement-related enums
|
||||
class ProcurementPlanType(enum.Enum):
|
||||
"""Procurement plan types"""
|
||||
REGULAR = "regular"
|
||||
EMERGENCY = "emergency"
|
||||
SEASONAL = "seasonal"
|
||||
|
||||
|
||||
class ProcurementStrategy(enum.Enum):
|
||||
"""Procurement strategies"""
|
||||
JUST_IN_TIME = "just_in_time"
|
||||
BULK = "bulk"
|
||||
MIXED = "mixed"
|
||||
|
||||
|
||||
class RiskLevel(enum.Enum):
|
||||
"""Risk level classifications"""
|
||||
LOW = "low"
|
||||
MEDIUM = "medium"
|
||||
HIGH = "high"
|
||||
CRITICAL = "critical"
|
||||
|
||||
|
||||
class RequirementStatus(enum.Enum):
|
||||
"""Procurement requirement status"""
|
||||
PENDING = "pending"
|
||||
APPROVED = "approved"
|
||||
ORDERED = "ordered"
|
||||
PARTIALLY_RECEIVED = "partially_received"
|
||||
RECEIVED = "received"
|
||||
CANCELLED = "cancelled"
|
||||
|
||||
|
||||
class PlanStatus(enum.Enum):
|
||||
"""Procurement plan status"""
|
||||
DRAFT = "draft"
|
||||
PENDING_APPROVAL = "pending_approval"
|
||||
APPROVED = "approved"
|
||||
IN_EXECUTION = "in_execution"
|
||||
COMPLETED = "completed"
|
||||
CANCELLED = "cancelled"
|
||||
|
||||
|
||||
class DeliveryStatus(enum.Enum):
|
||||
"""Delivery status for procurement"""
|
||||
PENDING = "pending"
|
||||
IN_TRANSIT = "in_transit"
|
||||
DELIVERED = "delivered"
|
||||
DELAYED = "delayed"
|
||||
CANCELLED = "cancelled"
|
||||
218
services/orders/app/models/order.py
Normal file
218
services/orders/app/models/order.py
Normal file
@@ -0,0 +1,218 @@
|
||||
# ================================================================
|
||||
# services/orders/app/models/order.py
|
||||
# ================================================================
|
||||
"""
|
||||
Order-related database models for Orders Service
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from typing import Optional, List
|
||||
from sqlalchemy import Column, String, Boolean, DateTime, Numeric, Text, ForeignKey, Integer
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from shared.database.base import Base
|
||||
|
||||
|
||||
class CustomerOrder(Base):
|
||||
"""Customer order model for tracking orders throughout their lifecycle"""
|
||||
__tablename__ = "customer_orders"
|
||||
|
||||
# Primary identification
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
||||
order_number = Column(String(50), nullable=False, unique=True, index=True)
|
||||
|
||||
# Customer information
|
||||
customer_id = Column(UUID(as_uuid=True), ForeignKey("customers.id"), nullable=False, index=True)
|
||||
|
||||
# Order status and lifecycle
|
||||
status = Column(String(50), nullable=False, default="pending", index=True)
|
||||
# Status values: pending, confirmed, in_production, ready, out_for_delivery, delivered, cancelled, failed
|
||||
|
||||
order_type = Column(String(50), nullable=False, default="standard") # standard, rush, recurring, special
|
||||
priority = Column(String(20), nullable=False, default="normal") # high, normal, low
|
||||
|
||||
# Order timing
|
||||
order_date = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
requested_delivery_date = Column(DateTime(timezone=True), nullable=False)
|
||||
confirmed_delivery_date = Column(DateTime(timezone=True), nullable=True)
|
||||
actual_delivery_date = Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# Delivery information
|
||||
delivery_method = Column(String(50), nullable=False, default="delivery") # delivery, pickup
|
||||
delivery_address = Column(JSONB, nullable=True) # Complete delivery address
|
||||
delivery_instructions = Column(Text, nullable=True)
|
||||
delivery_window_start = Column(DateTime(timezone=True), nullable=True)
|
||||
delivery_window_end = Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# Financial information
|
||||
subtotal = Column(Numeric(10, 2), nullable=False, default=Decimal("0.00"))
|
||||
discount_amount = Column(Numeric(10, 2), nullable=False, default=Decimal("0.00"))
|
||||
discount_percentage = Column(Numeric(5, 2), nullable=False, default=Decimal("0.00"))
|
||||
tax_amount = Column(Numeric(10, 2), nullable=False, default=Decimal("0.00"))
|
||||
delivery_fee = Column(Numeric(10, 2), nullable=False, default=Decimal("0.00"))
|
||||
total_amount = Column(Numeric(10, 2), nullable=False, default=Decimal("0.00"))
|
||||
|
||||
# Payment information
|
||||
payment_status = Column(String(50), nullable=False, default="pending") # pending, partial, paid, failed, refunded
|
||||
payment_method = Column(String(50), nullable=True) # cash, card, bank_transfer, account
|
||||
payment_terms = Column(String(50), nullable=False, default="immediate")
|
||||
payment_due_date = Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# Special requirements and customizations
|
||||
special_instructions = Column(Text, nullable=True)
|
||||
custom_requirements = Column(JSONB, nullable=True) # Special dietary requirements, decorations
|
||||
allergen_warnings = Column(JSONB, nullable=True) # Allergen information
|
||||
|
||||
# Business model detection
|
||||
business_model = Column(String(50), nullable=True) # individual_bakery, central_bakery (auto-detected)
|
||||
estimated_business_model = Column(String(50), nullable=True) # Based on order patterns
|
||||
|
||||
# Order source and channel
|
||||
order_source = Column(String(50), nullable=False, default="manual") # manual, online, phone, app, api
|
||||
sales_channel = Column(String(50), nullable=False, default="direct") # direct, wholesale, retail
|
||||
order_origin = Column(String(100), nullable=True) # Website, app, store location
|
||||
|
||||
# Fulfillment tracking
|
||||
production_batch_id = Column(UUID(as_uuid=True), nullable=True) # Link to production batch
|
||||
fulfillment_location = Column(String(100), nullable=True) # Which location fulfills this order
|
||||
estimated_preparation_time = Column(Integer, nullable=True) # Minutes
|
||||
actual_preparation_time = Column(Integer, nullable=True) # Minutes
|
||||
|
||||
# Customer communication
|
||||
customer_notified_confirmed = Column(Boolean, nullable=False, default=False)
|
||||
customer_notified_ready = Column(Boolean, nullable=False, default=False)
|
||||
customer_notified_delivered = Column(Boolean, nullable=False, default=False)
|
||||
communication_preferences = Column(JSONB, nullable=True)
|
||||
|
||||
# Quality and feedback
|
||||
quality_score = Column(Numeric(3, 1), nullable=True) # 1.0 to 10.0
|
||||
customer_rating = Column(Integer, nullable=True) # 1-5 stars
|
||||
customer_feedback = Column(Text, nullable=True)
|
||||
|
||||
# Cancellation and refunds
|
||||
cancellation_reason = Column(String(200), nullable=True)
|
||||
cancelled_at = Column(DateTime(timezone=True), nullable=True)
|
||||
cancelled_by = Column(UUID(as_uuid=True), nullable=True)
|
||||
refund_amount = Column(Numeric(10, 2), nullable=False, default=Decimal("0.00"))
|
||||
refund_processed_at = Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# Audit fields
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
|
||||
created_by = Column(UUID(as_uuid=True), nullable=True)
|
||||
updated_by = Column(UUID(as_uuid=True), nullable=True)
|
||||
|
||||
# Additional metadata
|
||||
order_metadata = Column(JSONB, nullable=True) # Flexible field for additional data
|
||||
|
||||
# Relationships
|
||||
customer = relationship("Customer", back_populates="orders")
|
||||
items = relationship("OrderItem", back_populates="order", cascade="all, delete-orphan")
|
||||
status_history = relationship("OrderStatusHistory", back_populates="order", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class OrderItem(Base):
|
||||
"""Individual items within a customer order"""
|
||||
__tablename__ = "order_items"
|
||||
|
||||
# Primary identification
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
order_id = Column(UUID(as_uuid=True), ForeignKey("customer_orders.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
# Product information
|
||||
product_id = Column(UUID(as_uuid=True), nullable=False, index=True) # Reference to products service
|
||||
product_name = Column(String(200), nullable=False)
|
||||
product_sku = Column(String(100), nullable=True)
|
||||
product_category = Column(String(100), nullable=True)
|
||||
|
||||
# Quantity and units
|
||||
quantity = Column(Numeric(10, 3), nullable=False)
|
||||
unit_of_measure = Column(String(50), nullable=False, default="each")
|
||||
weight = Column(Numeric(10, 3), nullable=True) # For weight-based products
|
||||
|
||||
# Pricing information
|
||||
unit_price = Column(Numeric(10, 2), nullable=False)
|
||||
line_discount = Column(Numeric(10, 2), nullable=False, default=Decimal("0.00"))
|
||||
line_total = Column(Numeric(10, 2), nullable=False)
|
||||
|
||||
# Product specifications and customizations
|
||||
product_specifications = Column(JSONB, nullable=True) # Size, flavor, decorations
|
||||
customization_details = Column(Text, nullable=True)
|
||||
special_instructions = Column(Text, nullable=True)
|
||||
|
||||
# Production requirements
|
||||
recipe_id = Column(UUID(as_uuid=True), nullable=True) # Reference to recipes service
|
||||
production_requirements = Column(JSONB, nullable=True) # Ingredients, equipment needed
|
||||
estimated_production_time = Column(Integer, nullable=True) # Minutes
|
||||
|
||||
# Fulfillment tracking
|
||||
status = Column(String(50), nullable=False, default="pending") # pending, in_production, ready, delivered
|
||||
production_started_at = Column(DateTime(timezone=True), nullable=True)
|
||||
production_completed_at = Column(DateTime(timezone=True), nullable=True)
|
||||
quality_checked = Column(Boolean, nullable=False, default=False)
|
||||
quality_score = Column(Numeric(3, 1), nullable=True)
|
||||
|
||||
# Cost tracking
|
||||
ingredient_cost = Column(Numeric(10, 2), nullable=True)
|
||||
labor_cost = Column(Numeric(10, 2), nullable=True)
|
||||
overhead_cost = Column(Numeric(10, 2), nullable=True)
|
||||
total_cost = Column(Numeric(10, 2), nullable=True)
|
||||
margin = Column(Numeric(10, 2), nullable=True)
|
||||
|
||||
# Inventory impact
|
||||
reserved_inventory = Column(Boolean, nullable=False, default=False)
|
||||
inventory_allocated_at = Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# Audit fields
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
|
||||
|
||||
# Additional metadata
|
||||
customer_metadata = Column(JSONB, nullable=True)
|
||||
|
||||
# Relationships
|
||||
order = relationship("CustomerOrder", back_populates="items")
|
||||
|
||||
|
||||
class OrderStatusHistory(Base):
|
||||
"""Track status changes and important events in order lifecycle"""
|
||||
__tablename__ = "order_status_history"
|
||||
|
||||
# Primary identification
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
order_id = Column(UUID(as_uuid=True), ForeignKey("customer_orders.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
# Status change information
|
||||
from_status = Column(String(50), nullable=True)
|
||||
to_status = Column(String(50), nullable=False)
|
||||
change_reason = Column(String(200), nullable=True)
|
||||
|
||||
# Event details
|
||||
event_type = Column(String(50), nullable=False, default="status_change")
|
||||
# Event types: status_change, payment_received, production_started, delivery_scheduled, etc.
|
||||
|
||||
event_description = Column(Text, nullable=True)
|
||||
event_data = Column(JSONB, nullable=True) # Additional event-specific data
|
||||
|
||||
# Who made the change
|
||||
changed_by = Column(UUID(as_uuid=True), nullable=True)
|
||||
change_source = Column(String(50), nullable=False, default="manual") # manual, automatic, system, api
|
||||
|
||||
# Timing
|
||||
changed_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
|
||||
# Customer communication
|
||||
customer_notified = Column(Boolean, nullable=False, default=False)
|
||||
notification_method = Column(String(50), nullable=True) # email, sms, phone, app
|
||||
notification_sent_at = Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# Additional notes
|
||||
notes = Column(Text, nullable=True)
|
||||
|
||||
# Relationships
|
||||
order = relationship("CustomerOrder", back_populates="status_history")
|
||||
Reference in New Issue
Block a user