88 lines
3.6 KiB
Python

import enum
from uuid import uuid4
from sqlalchemy import Boolean, Column, DateTime, Enum, Float, ForeignKey, Integer, String, Text
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.core.database import Base
class ProductStatus(enum.Enum):
DRAFT = "draft"
PUBLISHED = "published"
OUT_OF_STOCK = "out_of_stock"
DISCONTINUED = "discontinued"
class Product(Base):
__tablename__ = "products"
id = Column(String(36), primary_key=True, default=lambda: str(uuid4()))
name = Column(String(255), nullable=False, index=True)
description = Column(Text, nullable=True)
price = Column(Float, nullable=False)
sku = Column(String(100), unique=True, nullable=True)
barcode = Column(String(100), unique=True, nullable=True)
stock_quantity = Column(Integer, default=0)
weight = Column(Float, nullable=True) # In kg
dimensions = Column(String(100), nullable=True) # Format: LxWxH in cm
status = Column(Enum(ProductStatus), default=ProductStatus.DRAFT)
is_featured = Column(Boolean, default=False)
is_digital = Column(Boolean, default=False)
digital_download_link = Column(String(512), nullable=True)
slug = Column(String(255), nullable=False, unique=True)
tax_rate = Column(Float, default=0.0) # As a percentage
discount_price = Column(Float, nullable=True)
discount_start_date = Column(DateTime(timezone=True), nullable=True)
discount_end_date = Column(DateTime(timezone=True), nullable=True)
category_id = Column(String(36), ForeignKey("categories.id"), nullable=True)
seller_id = Column(String(36), ForeignKey("users.id"), nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Relationships
category = relationship("Category", back_populates="products")
seller = relationship("User", back_populates="products")
images = relationship("ProductImage", back_populates="product", cascade="all, delete-orphan")
reviews = relationship("Review", back_populates="product", cascade="all, delete-orphan")
order_items = relationship("OrderItem", back_populates="product")
cart_items = relationship("CartItem", back_populates="product")
# Tags relationship
tags = relationship("Tag", secondary="product_tags")
def __repr__(self):
return f"<Product {self.name}>"
@property
def average_rating(self):
if not self.reviews:
return None
return sum(review.rating for review in self.reviews) / len(self.reviews)
@property
def current_price(self):
"""Returns the current effective price (discount or regular)"""
now = func.now()
if (self.discount_price and self.discount_start_date and self.discount_end_date and
self.discount_start_date <= now and now <= self.discount_end_date):
return self.discount_price
return self.price
class ProductImage(Base):
__tablename__ = "product_images"
id = Column(String(36), primary_key=True, default=lambda: str(uuid4()))
product_id = Column(String(36), ForeignKey("products.id", ondelete="CASCADE"), nullable=False)
image_url = Column(String(512), nullable=False)
alt_text = Column(String(255), nullable=True)
is_primary = Column(Boolean, default=False)
display_order = Column(Integer, default=0)
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationship
product = relationship("Product", back_populates="images")
def __repr__(self):
return f"<ProductImage {self.id} for Product {self.product_id}>"