Initial commit - production deployment
This commit is contained in:
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