Add Polaris FastAPI backend: models/schemas, pricing+scoring+order router, importer, Celery tasks, API routers, seed script
This commit is contained in:
185
backend/app/models.py
Normal file
185
backend/app/models.py
Normal file
@@ -0,0 +1,185 @@
|
||||
"""SQLAlchemy ORM models — mirror db/schema.sql table + column names EXACTLY.
|
||||
|
||||
Do NOT rename tables/columns here: the schema.sql in /opt/polaris/db is canonical
|
||||
and the database is already initialized from it.
|
||||
"""
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import (
|
||||
BigInteger,
|
||||
Boolean,
|
||||
Column,
|
||||
DateTime,
|
||||
ForeignKey,
|
||||
Integer,
|
||||
Numeric,
|
||||
Text,
|
||||
UniqueConstraint,
|
||||
func,
|
||||
text,
|
||||
)
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class Supplier(Base):
|
||||
__tablename__ = "suppliers"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"))
|
||||
name = Column(Text, nullable=False)
|
||||
adapter_type = Column(Text, nullable=False)
|
||||
api_endpoint = Column(Text)
|
||||
account_id = Column(Text)
|
||||
credentials_enc = Column(Text)
|
||||
fulfillment_caps = Column(JSONB, default=list)
|
||||
shipping_regions = Column(JSONB, default=list)
|
||||
reliability_score = Column(Numeric(5, 2), default=Decimal("50.00"))
|
||||
status = Column(Text, nullable=False, default="active")
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
supplier_products = relationship("SupplierProduct", back_populates="supplier")
|
||||
performance = relationship(
|
||||
"SupplierPerformance", back_populates="supplier", uselist=False, lazy="joined"
|
||||
)
|
||||
|
||||
|
||||
class Product(Base):
|
||||
__tablename__ = "products"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"))
|
||||
sku = Column(Text, unique=True, nullable=False)
|
||||
title = Column(Text, nullable=False)
|
||||
description = Column(Text)
|
||||
category = Column(Text)
|
||||
brand = Column(Text)
|
||||
manufacturer = Column(Text)
|
||||
upc_ean_gtin = Column(Text)
|
||||
images = Column(JSONB, default=list)
|
||||
dimensions = Column(JSONB)
|
||||
weight_grams = Column(Numeric(10, 2))
|
||||
cost = Column(Numeric(12, 2), nullable=False, default=Decimal("0"))
|
||||
retail_price = Column(Numeric(12, 2))
|
||||
min_price = Column(Numeric(12, 2))
|
||||
max_price = Column(Numeric(12, 2))
|
||||
status = Column(Text, nullable=False, default="discovered")
|
||||
lifecycle = Column(JSONB, default=dict)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
supplier_products = relationship("SupplierProduct", back_populates="product")
|
||||
|
||||
|
||||
class SupplierProduct(Base):
|
||||
__tablename__ = "supplier_products"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("supplier_id", "supplier_sku", name="supplier_products_supplier_id_supplier_sku_key"),
|
||||
)
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"))
|
||||
supplier_id = Column(UUID(as_uuid=True), ForeignKey("suppliers.id", ondelete="CASCADE"), nullable=False)
|
||||
supplier_sku = Column(Text, nullable=False)
|
||||
product_id = Column(UUID(as_uuid=True), ForeignKey("products.id", ondelete="SET NULL"))
|
||||
supplier_cost = Column(Numeric(12, 2), nullable=False, default=Decimal("0"))
|
||||
supplier_inventory = Column(Integer, nullable=False, default=0)
|
||||
shipping_cost = Column(Numeric(12, 2), default=Decimal("0"))
|
||||
shipping_time = Column(Text)
|
||||
last_updated = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
supplier = relationship("Supplier", back_populates="supplier_products")
|
||||
product = relationship("Product", back_populates="supplier_products")
|
||||
|
||||
|
||||
class Customer(Base):
|
||||
__tablename__ = "customers"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"))
|
||||
email = Column(Text, unique=True, nullable=False)
|
||||
name = Column(Text)
|
||||
shipping_addr = Column(JSONB)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
class Order(Base):
|
||||
__tablename__ = "orders"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"))
|
||||
order_number = Column(Text, unique=True, nullable=False)
|
||||
customer_id = Column(UUID(as_uuid=True), ForeignKey("customers.id"))
|
||||
items = Column(JSONB, nullable=False)
|
||||
retail_total = Column(Numeric(12, 2), nullable=False, default=Decimal("0"))
|
||||
supplier_id = Column(UUID(as_uuid=True), ForeignKey("suppliers.id"))
|
||||
supplier_cost = Column(Numeric(12, 2), default=Decimal("0"))
|
||||
shipping_cost = Column(Numeric(12, 2), default=Decimal("0"))
|
||||
fees = Column(Numeric(12, 2), default=Decimal("0"))
|
||||
profit = Column(Numeric(12, 2), default=Decimal("0"))
|
||||
status = Column(Text, nullable=False, default="new")
|
||||
tracking = Column(Text)
|
||||
carrier = Column(Text)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
customer = relationship("Customer")
|
||||
|
||||
|
||||
class PriceHistory(Base):
|
||||
__tablename__ = "price_history"
|
||||
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
product_id = Column(UUID(as_uuid=True), ForeignKey("products.id", ondelete="CASCADE"), nullable=False)
|
||||
price = Column(Numeric(12, 2), nullable=False)
|
||||
reason = Column(Text)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
class InventoryHistory(Base):
|
||||
__tablename__ = "inventory_history"
|
||||
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
supplier_product_id = Column(
|
||||
UUID(as_uuid=True), ForeignKey("supplier_products.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
inventory = Column(Integer, nullable=False)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
class SupplierPerformance(Base):
|
||||
__tablename__ = "supplier_performance"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()"))
|
||||
supplier_id = Column(UUID(as_uuid=True), ForeignKey("suppliers.id", ondelete="CASCADE"), nullable=False)
|
||||
fulfillment_rate = Column(Numeric(5, 2), default=Decimal("100.00"))
|
||||
avg_shipping_days = Column(Numeric(6, 2))
|
||||
cancellation_rate = Column(Numeric(5, 2), default=Decimal("0.00"))
|
||||
stock_accuracy = Column(Numeric(5, 2), default=Decimal("100.00"))
|
||||
return_rate = Column(Numeric(5, 2), default=Decimal("0.00"))
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
supplier = relationship("Supplier", back_populates="performance")
|
||||
|
||||
|
||||
class PriceRule(Base):
|
||||
__tablename__ = "price_rules"
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
min_cost = Column(Numeric(12, 2), nullable=False, default=Decimal("0"))
|
||||
max_cost = Column(Numeric(12, 2))
|
||||
markup_pct = Column(Numeric(6, 2), nullable=False)
|
||||
min_margin_pct = Column(Numeric(6, 2), default=Decimal("0"))
|
||||
active = Column(Boolean, default=True)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
class AuditLog(Base):
|
||||
__tablename__ = "audit_logs"
|
||||
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
actor = Column(Text)
|
||||
action = Column(Text, nullable=False)
|
||||
entity = Column(Text)
|
||||
entity_id = Column(Text)
|
||||
detail = Column(JSONB)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
Reference in New Issue
Block a user