diff --git a/models/database/vendor.py b/models/database/vendor.py index 0a3c1037..8e8a89e9 100644 --- a/models/database/vendor.py +++ b/models/database/vendor.py @@ -1,78 +1,91 @@ # models/database/vendor.py """ -Vendor model with theme support. +Vendor model representing entities that sell products or services. -A vendor has ONE active theme stored in the vendor_themes table. -Theme presets available: default, modern, classic, minimal, vibrant +This module defines the Vendor model along with its relationships to +other models such as User (owner), Product, Customer, Order, and MarketplaceImportJob. """ -from sqlalchemy import (Boolean, Column, ForeignKey, Integer, String, Text, JSON) +from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Text, JSON from sqlalchemy.orm import relationship -from app.core.config import settings +# Import Base from the central database module instead of creating a new one from app.core.database import Base from models.database.base import TimestampMixin +from app.core.config import settings class Vendor(Base, TimestampMixin): - __tablename__ = "vendors" + """Represents a vendor in the system.""" - id = Column(Integer, primary_key=True, index=True) - vendor_code = Column( - String, unique=True, index=True, nullable=False - ) - subdomain = Column(String(100), unique=True, nullable=False, index=True) - name = Column(String, nullable=False) - description = Column(Text) - owner_user_id = Column(Integer, ForeignKey("users.id"), nullable=False) + __tablename__ = "vendors" # Name of the table in the database + + id = Column(Integer, primary_key=True, index=True) # Primary key and indexed column for vendor ID + vendor_code = Column(String, unique=True, index=True, + nullable=False) # Unique, indexed, non-nullable vendor code column + subdomain = Column(String(100), unique=True, nullable=False, + index=True) # Unique, non-nullable subdomain column with indexing + name = Column(String, nullable=False) # Non-nullable name column for the vendor + description = Column(Text) # Optional text description column for the vendor + owner_user_id = Column(Integer, ForeignKey("users.id"), + nullable=False) # Foreign key to user ID of the vendor's owner # Contact information - contact_email = Column(String) - contact_phone = Column(String) - website = Column(String) + contact_email = Column(String) # Optional email column for contact information + contact_phone = Column(String) # Optional phone column for contact information + website = Column(String) # Optional website column for contact information # Letzshop URLs - multi-language support - letzshop_csv_url_fr = Column(String) - letzshop_csv_url_en = Column(String) - letzshop_csv_url_de = Column(String) + letzshop_csv_url_fr = Column(String) # URL for French CSV in Letzshop + letzshop_csv_url_en = Column(String) # URL for English CSV in Letzshop + letzshop_csv_url_de = Column(String) # URL for German CSV in Letzshop # Business information - business_address = Column(Text) - tax_number = Column(String) + business_address = Column(Text) # Optional text address column for business information + tax_number = Column(String) # Optional tax number column for business information # Status - is_active = Column(Boolean, default=True) - is_verified = Column(Boolean, default=False) + is_active = Column(Boolean, default=True) # Boolean to indicate if the vendor is active + is_verified = Column(Boolean, default=False) # Boolean to indicate if the vendor is verified + # ======================================================================== # Relationships # ======================================================================== - owner = relationship("User", back_populates="owned_vendors") - vendor_users = relationship("VendorUser", back_populates="vendor") - products = relationship("Product", back_populates="vendor") - customers = relationship("Customer", back_populates="vendor") - orders = relationship("Order", back_populates="vendor") - marketplace_import_jobs = relationship("MarketplaceImportJob", back_populates="vendor") + owner = relationship("User", back_populates="owned_vendors") # Relationship with User model for the vendor's owner + vendor_users = relationship("VendorUser", + back_populates="vendor") # Relationship with VendorUser model for users in this vendor + products = relationship("Product", + back_populates="vendor") # Relationship with Product model for products of this vendor + customers = relationship("Customer", + back_populates="vendor") # Relationship with Customer model for customers of this vendor + orders = relationship("Order", + back_populates="vendor") # Relationship with Order model for orders placed by this vendor + marketplace_import_jobs = relationship("MarketplaceImportJob", + back_populates="vendor") # Relationship with MarketplaceImportJob model for import jobs related to this vendor domains = relationship( "VendorDomain", back_populates="vendor", cascade="all, delete-orphan", order_by="VendorDomain.is_primary.desc()" - ) + ) # Relationship with VendorDomain model for custom domains of the vendor # Single theme relationship (ONE vendor = ONE theme) + # A vendor has ONE active theme stored in the vendor_themes table. + # Theme presets available: default, modern, classic, minimal, vibrant vendor_theme = relationship( "VendorTheme", back_populates="vendor", uselist=False, cascade="all, delete-orphan" - ) + ) # Relationship with VendorTheme model for the active theme of the vendor def __repr__(self): + """String representation of the Vendor object.""" return f"" # ======================================================================== - # Theme Helper Methods + # Theme Helper Methods to get active theme and other related information # ======================================================================== def get_effective_theme(self) -> dict: @@ -92,7 +105,7 @@ class Vendor(Base, TimestampMixin): return self._get_default_theme() def _get_default_theme(self) -> dict: - """Default theme configuration""" + """Return the default theme configuration.""" return { "theme_name": "default", "colors": { @@ -133,14 +146,14 @@ class Vendor(Base, TimestampMixin): } def get_primary_color(self) -> str: - """Get primary color from active theme""" + """Get primary color from active theme.""" theme = self.get_effective_theme() - return theme.get("colors", {}).get("primary", "#6366f1") + return theme.get("colors", {}).get("primary", "#6366f1") # Default to default theme if not found def get_logo_url(self) -> str: - """Get logo URL from active theme""" + """Get logo URL from active theme.""" theme = self.get_effective_theme() - return theme.get("branding", {}).get("logo") + return theme.get("branding", {}).get("logo") # Return None or the logo URL if found # ======================================================================== # Domain Helper Methods @@ -148,51 +161,93 @@ class Vendor(Base, TimestampMixin): @property def primary_domain(self): - """Get the primary custom domain for this vendor""" + """Get the primary custom domain for this vendor.""" for domain in self.domains: if domain.is_primary and domain.is_active: - return domain.domain + return domain.domain # Return the domain if it's primary and active return None @property def all_domains(self): - """Get all active domains (subdomain + custom domains)""" - domains = [f"{self.subdomain}.{settings.platform_domain}"] + """Get all active domains (subdomain + custom domains).""" + domains = [f"{self.subdomain}.{settings.platform_domain}"] # Start with the main subdomain for domain in self.domains: if domain.is_active: - domains.append(domain.domain) + domains.append(domain.domain) # Add other active custom domains return domains class VendorUser(Base, TimestampMixin): - __tablename__ = "vendor_users" + """Represents a user's role within a specific vendor.""" + + __tablename__ = "vendor_users" # Name of the table in the database id = Column(Integer, primary_key=True, index=True) + """Unique identifier for each VendorUser entry.""" + vendor_id = Column(Integer, ForeignKey("vendors.id"), nullable=False) + """Foreign key linking to the associated Vendor.""" + user_id = Column(Integer, ForeignKey("users.id"), nullable=False) + """Foreign key linking to the associated User.""" + role_id = Column(Integer, ForeignKey("roles.id"), nullable=False) + """Foreign key linking to the associated Role.""" + invited_by = Column(Integer, ForeignKey("users.id")) + """Foreign key linking to the user who invited this VendorUser.""" + is_active = Column(Boolean, default=True, nullable=False) + """Indicates whether the VendorUser role is active.""" vendor = relationship("Vendor", back_populates="vendor_users") - user = relationship("User", foreign_keys=[user_id], back_populates="vendor_memberships") - inviter = relationship("User", foreign_keys=[invited_by]) - role = relationship("Role", back_populates="vendor_users") + """Relationship to the Vendor model, representing the associated vendor.""" - def __repr__(self): + user = relationship("User", foreign_keys=[user_id], back_populates="vendor_memberships") + """Relationship to the User model, representing the user who holds this role within the vendor.""" + + inviter = relationship("User", foreign_keys=[invited_by]) + """Optional relationship to the User model, representing the user who invited this VendorUser.""" + + role = relationship("Role", back_populates="vendor_users") + """Relationship to the Role model, representing the role held by the vendor user.""" + + def __repr__(self) -> str: + """Return a string representation of the VendorUser instance. + + Returns: + str: A string that includes the vendor_id and user_id of the VendorUser instance. + """ return f"" class Role(Base, TimestampMixin): - __tablename__ = "roles" + """Represents a role within a vendor's system.""" + + __tablename__ = "roles" # Name of the table in the database id = Column(Integer, primary_key=True, index=True) + """Unique identifier for each Role entry.""" + vendor_id = Column(Integer, ForeignKey("vendors.id"), nullable=False) + """Foreign key linking to the associated Vendor.""" + name = Column(String(100), nullable=False) + """Name of the role, with a maximum length of 100 characters.""" + permissions = Column(JSON, default=list) + """Permissions assigned to this role, stored as a JSON array.""" vendor = relationship("Vendor") - vendor_users = relationship("VendorUser", back_populates="role") + """Relationship to the Vendor model, representing the associated vendor.""" - def __repr__(self): + vendor_users = relationship("VendorUser", back_populates="role") + """Back-relationship to the VendorUser model, representing users with this role.""" + + def __repr__(self) -> str: + """Return a string representation of the Role instance. + + Returns: + str: A string that includes the id and name of the Role instance. + """ return f""