# models/database_models.py - Updated with Marketplace Support from sqlalchemy import Column, Integer, String, Float, DateTime, Boolean, Text, ForeignKey, UniqueConstraint, Index from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship from datetime import datetime Base = declarative_base() class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) email = Column(String, unique=True, index=True, nullable=False) username = Column(String, unique=True, index=True, nullable=False) hashed_password = Column(String, nullable=False) role = Column(String, nullable=False, default="user") # user, admin, shop_owner is_active = Column(Boolean, default=True, nullable=False) last_login = Column(DateTime, nullable=True) created_at = Column(DateTime, default=datetime.utcnow, nullable=False) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) # Relationships marketplace_import_jobs = relationship("MarketplaceImportJob", back_populates="user") owned_shops = relationship("Shop", back_populates="owner") def __repr__(self): return f"" class Shop(Base): __tablename__ = "shops" id = Column(Integer, primary_key=True, index=True) shop_code = Column(String, unique=True, index=True, nullable=False) # e.g., "TECHSTORE", "FASHIONHUB" shop_name = Column(String, nullable=False) # Display name description = Column(Text) owner_id = Column(Integer, ForeignKey("users.id"), nullable=False) # Contact information contact_email = Column(String) contact_phone = Column(String) website = Column(String) # Business information business_address = Column(Text) tax_number = Column(String) # Status is_active = Column(Boolean, default=True) is_verified = Column(Boolean, default=False) # Timestamps created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # Relationships owner = relationship("User", back_populates="owned_shops") shop_products = relationship("ShopProduct", back_populates="shop") marketplace_import_jobs = relationship("MarketplaceImportJob", back_populates="shop") class Product(Base): __tablename__ = "products" id = Column(Integer, primary_key=True, index=True) product_id = Column(String, unique=True, index=True, nullable=False) title = Column(String, nullable=False) description = Column(String) link = Column(String) image_link = Column(String) availability = Column(String, index=True) # Index for filtering price = Column(String) brand = Column(String, index=True) # Index for filtering gtin = Column(String, index=True) # Index for stock lookups mpn = Column(String) condition = Column(String) adult = Column(String) multipack = Column(Integer) is_bundle = Column(String) age_group = Column(String) color = Column(String) gender = Column(String) material = Column(String) pattern = Column(String) size = Column(String) size_type = Column(String) size_system = Column(String) item_group_id = Column(String) google_product_category = Column(String, index=True) # Index for filtering product_type = Column(String) custom_label_0 = Column(String) custom_label_1 = Column(String) custom_label_2 = Column(String) custom_label_3 = Column(String) custom_label_4 = Column(String) additional_image_link = Column(String) sale_price = Column(String) unit_pricing_measure = Column(String) unit_pricing_base_measure = Column(String) identifier_exists = Column(String) shipping = Column(String) currency = Column(String) # New marketplace fields marketplace = Column(String, index=True, nullable=True, default="Letzshop") # Index for marketplace filtering shop_name = Column(String, index=True, nullable=True) # Index for shop filtering created_at = Column(DateTime, default=datetime.utcnow, nullable=False) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) # Relationship to stock (one-to-many via GTIN) stock_entries = relationship("Stock", foreign_keys="Stock.gtin", primaryjoin="Product.gtin == Stock.gtin", viewonly=True) shop_products = relationship("ShopProduct", back_populates="product") # Additional indexes for marketplace queries __table_args__ = ( Index('idx_marketplace_shop', 'marketplace', 'shop_name'), # Composite index for marketplace+shop queries Index('idx_marketplace_brand', 'marketplace', 'brand'), # Composite index for marketplace+brand queries ) def __repr__(self): return (f"") class ShopProduct(Base): __tablename__ = "shop_products" id = Column(Integer, primary_key=True, index=True) shop_id = Column(Integer, ForeignKey("shops.id"), nullable=False) product_id = Column(Integer, ForeignKey("products.id"), nullable=False) # Shop-specific overrides (can override the main product data) shop_product_id = Column(String) # Shop's internal product ID shop_price = Column(Float) # Override main product price shop_sale_price = Column(Float) shop_currency = Column(String) shop_availability = Column(String) # Override availability shop_condition = Column(String) # Shop-specific metadata is_featured = Column(Boolean, default=False) is_active = Column(Boolean, default=True) display_order = Column(Integer, default=0) # Inventory management min_quantity = Column(Integer, default=1) max_quantity = Column(Integer) # Timestamps created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # Relationships shop = relationship("Shop", back_populates="shop_products") product = relationship("Product", back_populates="shop_products") # Constraints __table_args__ = ( UniqueConstraint('shop_id', 'product_id', name='uq_shop_product'), Index('idx_shop_product_active', 'shop_id', 'is_active'), Index('idx_shop_product_featured', 'shop_id', 'is_featured'), ) class Stock(Base): __tablename__ = "stock" id = Column(Integer, primary_key=True, index=True) gtin = Column(String, index=True, nullable=False) # Foreign key relationship would be ideal location = Column(String, nullable=False, index=True) quantity = Column(Integer, nullable=False, default=0) reserved_quantity = Column(Integer, default=0) # For orders being processed shop_id = Column(Integer, ForeignKey("shops.id")) # Optional: shop-specific stock created_at = Column(DateTime, default=datetime.utcnow, nullable=False) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) # Relationships shop = relationship("Shop") # Composite unique constraint to prevent duplicate GTIN-location combinations __table_args__ = ( UniqueConstraint('gtin', 'location', name='uq_stock_gtin_location'), Index('idx_stock_gtin_location', 'gtin', 'location'), # Composite index for efficient queries ) def __repr__(self): return f"" class MarketplaceImportJob(Base): __tablename__ = "marketplace_import_jobs" id = Column(Integer, primary_key=True, index=True) status = Column(String, nullable=False, default="pending") # pending, processing, completed, failed, completed_with_errors source_url = Column(String, nullable=False) marketplace = Column(String, nullable=False, index=True, default="Letzshop") # Index for marketplace filtering shop_name = Column(String, nullable=False, index=True) # Index for shop filtering shop_id = Column(Integer, ForeignKey("shops.id"), nullable=False) # Add proper foreign key user_id = Column(Integer, ForeignKey('users.id'), nullable=False) # Foreign key to users table # Results imported_count = Column(Integer, default=0) updated_count = Column(Integer, default=0) error_count = Column(Integer, default=0) total_processed = Column(Integer, default=0) # Error handling error_message = Column(String) # Timestamps created_at = Column(DateTime, default=datetime.utcnow, nullable=False) started_at = Column(DateTime) completed_at = Column(DateTime) # Relationship to user user = relationship("User", foreign_keys=[user_id]) shop = relationship("Shop", back_populates="marketplace_import_jobs") # Additional indexes for marketplace import job queries __table_args__ = ( Index('idx_marketplace_import_user_marketplace', 'user_id', 'marketplace'), # User's marketplace imports Index('idx_marketplace_import_shop_status', 'status'), # Shop import status Index('idx_marketplace_import_shop_id', 'shop_id'), ) def __repr__(self): return (f"")