Initial commit - production deployment
This commit is contained in:
174
services/pos/app/models/pos_transaction.py
Normal file
174
services/pos/app/models/pos_transaction.py
Normal file
@@ -0,0 +1,174 @@
|
||||
# services/pos/app/models/pos_transaction.py
|
||||
"""
|
||||
POS Transaction Models
|
||||
Stores transaction data from POS systems
|
||||
"""
|
||||
|
||||
from sqlalchemy import Column, String, DateTime, Boolean, Numeric, Integer, Text, JSON, Index, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
import uuid
|
||||
|
||||
from shared.database.base import Base
|
||||
|
||||
|
||||
class POSTransaction(Base):
|
||||
"""
|
||||
Main transaction record from POS systems
|
||||
"""
|
||||
__tablename__ = "pos_transactions"
|
||||
|
||||
# Primary identifiers
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
||||
pos_config_id = Column(UUID(as_uuid=True), ForeignKey("pos_configurations.id"), nullable=False, index=True)
|
||||
|
||||
# POS Provider Information
|
||||
pos_system = Column(String(50), nullable=False, index=True) # square, toast, lightspeed
|
||||
external_transaction_id = Column(String(255), nullable=False, index=True) # POS system's transaction ID
|
||||
external_order_id = Column(String(255), nullable=True, index=True) # POS system's order ID
|
||||
|
||||
# Transaction Details
|
||||
transaction_type = Column(String(50), nullable=False) # sale, refund, void, exchange
|
||||
status = Column(String(50), nullable=False) # completed, pending, failed, refunded, voided
|
||||
|
||||
# Financial Information
|
||||
subtotal = Column(Numeric(10, 2), nullable=False)
|
||||
tax_amount = Column(Numeric(10, 2), default=0, nullable=False)
|
||||
tip_amount = Column(Numeric(10, 2), default=0, nullable=False)
|
||||
discount_amount = Column(Numeric(10, 2), default=0, nullable=False)
|
||||
total_amount = Column(Numeric(10, 2), nullable=False)
|
||||
currency = Column(String(3), default="EUR", nullable=False)
|
||||
|
||||
# Payment Information
|
||||
payment_method = Column(String(50), nullable=True) # card, cash, digital_wallet, etc.
|
||||
payment_status = Column(String(50), nullable=True) # paid, pending, failed
|
||||
|
||||
# Transaction Timing
|
||||
transaction_date = Column(DateTime(timezone=True), nullable=False, index=True)
|
||||
pos_created_at = Column(DateTime(timezone=True), nullable=False) # Original POS timestamp
|
||||
pos_updated_at = Column(DateTime(timezone=True), nullable=True) # Last update in POS
|
||||
|
||||
# Location & Staff
|
||||
location_id = Column(String(100), nullable=True)
|
||||
location_name = Column(String(255), nullable=True)
|
||||
staff_id = Column(String(100), nullable=True)
|
||||
staff_name = Column(String(255), nullable=True)
|
||||
|
||||
# Customer Information
|
||||
customer_id = Column(String(100), nullable=True)
|
||||
customer_email = Column(String(255), nullable=True)
|
||||
customer_phone = Column(String(50), nullable=True)
|
||||
|
||||
# Order Context
|
||||
order_type = Column(String(50), nullable=True) # dine_in, takeout, delivery, pickup
|
||||
table_number = Column(String(20), nullable=True)
|
||||
receipt_number = Column(String(100), nullable=True)
|
||||
|
||||
# Sync Status
|
||||
is_synced_to_sales = Column(Boolean, default=False, nullable=False, index=True)
|
||||
sales_record_id = Column(UUID(as_uuid=True), nullable=True, index=True) # Reference to sales service
|
||||
sync_attempted_at = Column(DateTime(timezone=True), nullable=True)
|
||||
sync_completed_at = Column(DateTime(timezone=True), nullable=True)
|
||||
sync_error = Column(Text, nullable=True)
|
||||
sync_retry_count = Column(Integer, default=0, nullable=False)
|
||||
|
||||
# Raw Data
|
||||
raw_data = Column(JSON, nullable=True) # Complete raw response from POS
|
||||
|
||||
# Processing Status
|
||||
is_processed = Column(Boolean, default=False, nullable=False)
|
||||
processing_error = Column(Text, nullable=True)
|
||||
|
||||
# Duplicate Detection
|
||||
is_duplicate = Column(Boolean, default=False, nullable=False)
|
||||
duplicate_of = Column(UUID(as_uuid=True), nullable=True)
|
||||
|
||||
# Timestamps
|
||||
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
|
||||
items = relationship("POSTransactionItem", back_populates="transaction", cascade="all, delete-orphan")
|
||||
|
||||
# Indexes for performance
|
||||
__table_args__ = (
|
||||
Index('idx_pos_transaction_tenant_date', 'tenant_id', 'transaction_date'),
|
||||
Index('idx_pos_transaction_external_id', 'pos_system', 'external_transaction_id'),
|
||||
Index('idx_pos_transaction_sync_status', 'is_synced_to_sales'),
|
||||
Index('idx_pos_transaction_status', 'status'),
|
||||
Index('idx_pos_transaction_type', 'transaction_type'),
|
||||
Index('idx_pos_transaction_processed', 'is_processed'),
|
||||
Index('idx_pos_transaction_duplicate', 'is_duplicate'),
|
||||
Index('idx_pos_transaction_location', 'location_id'),
|
||||
Index('idx_pos_transaction_customer', 'customer_id'),
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<POSTransaction(id={self.id}, external_id='{self.external_transaction_id}', pos_system='{self.pos_system}', total={self.total_amount})>"
|
||||
|
||||
|
||||
class POSTransactionItem(Base):
|
||||
"""
|
||||
Individual items within a POS transaction
|
||||
"""
|
||||
__tablename__ = "pos_transaction_items"
|
||||
|
||||
# Primary identifiers
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
transaction_id = Column(UUID(as_uuid=True), ForeignKey("pos_transactions.id"), nullable=False, index=True)
|
||||
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
||||
|
||||
# POS Item Information
|
||||
external_item_id = Column(String(255), nullable=True) # POS system's item ID
|
||||
sku = Column(String(100), nullable=True, index=True)
|
||||
|
||||
# Product Details
|
||||
product_name = Column(String(255), nullable=False)
|
||||
product_category = Column(String(100), nullable=True, index=True)
|
||||
product_subcategory = Column(String(100), nullable=True)
|
||||
|
||||
# Quantity & Pricing
|
||||
quantity = Column(Numeric(10, 3), nullable=False)
|
||||
unit_price = Column(Numeric(10, 2), nullable=False)
|
||||
total_price = Column(Numeric(10, 2), nullable=False)
|
||||
|
||||
# Discounts & Modifiers
|
||||
discount_amount = Column(Numeric(10, 2), default=0, nullable=False)
|
||||
tax_amount = Column(Numeric(10, 2), default=0, nullable=False)
|
||||
|
||||
# Modifiers (e.g., extra shot, no foam for coffee)
|
||||
modifiers = Column(JSON, nullable=True)
|
||||
|
||||
# Inventory Mapping
|
||||
inventory_product_id = Column(UUID(as_uuid=True), nullable=True, index=True) # Mapped to inventory service
|
||||
is_mapped_to_inventory = Column(Boolean, default=False, nullable=False)
|
||||
|
||||
# Sync Status
|
||||
is_synced_to_sales = Column(Boolean, default=False, nullable=False)
|
||||
sync_error = Column(Text, nullable=True)
|
||||
|
||||
# Raw Data
|
||||
raw_data = Column(JSON, nullable=True)
|
||||
|
||||
# Timestamps
|
||||
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
|
||||
transaction = relationship("POSTransaction", back_populates="items")
|
||||
|
||||
# Indexes for performance
|
||||
__table_args__ = (
|
||||
Index('idx_pos_item_transaction', 'transaction_id'),
|
||||
Index('idx_pos_item_product', 'product_name'),
|
||||
Index('idx_pos_item_category', 'product_category'),
|
||||
Index('idx_pos_item_sku', 'sku'),
|
||||
Index('idx_pos_item_inventory', 'inventory_product_id'),
|
||||
Index('idx_pos_item_sync', 'is_synced_to_sales'),
|
||||
Index('idx_pos_item_mapped', 'is_mapped_to_inventory'),
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<POSTransactionItem(id={self.id}, product='{self.product_name}', quantity={self.quantity}, price={self.total_price})>"
|
||||
Reference in New Issue
Block a user