303 lines
13 KiB
Python
303 lines
13 KiB
Python
|
|
from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, Text, ForeignKey, Enum as SQLEnum, JSON
|
||
|
|
from sqlalchemy.orm import relationship, declarative_base
|
||
|
|
from sqlalchemy.sql import func
|
||
|
|
from datetime import datetime, timezone
|
||
|
|
import enum
|
||
|
|
import uuid
|
||
|
|
|
||
|
|
Base = declarative_base()
|
||
|
|
|
||
|
|
def generate_uuid():
|
||
|
|
return str(uuid.uuid4())
|
||
|
|
|
||
|
|
class OrderStatus(enum.Enum):
|
||
|
|
PENDING = "pending"
|
||
|
|
PROCESSING = "processing"
|
||
|
|
LAYAWAY = "layaway"
|
||
|
|
SHIPPED = "shipped"
|
||
|
|
DELIVERED = "delivered"
|
||
|
|
CANCELLED = "cancelled"
|
||
|
|
REFUNDED = "refunded"
|
||
|
|
ON_HOLD = "on_hold"
|
||
|
|
|
||
|
|
class UserRole(enum.Enum):
|
||
|
|
USER = "user"
|
||
|
|
ADMIN = "admin"
|
||
|
|
EMPLOYEE = "employee"
|
||
|
|
ACCOUNTANT = "accountant"
|
||
|
|
SALES_MANAGER = "sales_manager"
|
||
|
|
|
||
|
|
class User(Base):
|
||
|
|
__tablename__ = "users"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
email = Column(String(255), unique=True, nullable=False, index=True)
|
||
|
|
name = Column(String(255), nullable=False)
|
||
|
|
password = Column(String(255), nullable=False)
|
||
|
|
role = Column(SQLEnum(UserRole), default=UserRole.USER)
|
||
|
|
is_active = Column(Boolean, default=True, nullable=False)
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||
|
|
|
||
|
|
cart_items = relationship("CartItem", back_populates="user", cascade="all, delete-orphan")
|
||
|
|
orders = relationship("Order", back_populates="user")
|
||
|
|
reviews = relationship("Review", back_populates="user")
|
||
|
|
bookings = relationship("Booking", back_populates="user")
|
||
|
|
|
||
|
|
class Category(Base):
|
||
|
|
__tablename__ = "categories"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
name = Column(String(100), unique=True, nullable=False)
|
||
|
|
slug = Column(String(100), unique=True, nullable=False)
|
||
|
|
description = Column(Text)
|
||
|
|
type = Column(String(50), default="product") # product or service
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
|
||
|
|
products = relationship("Product", back_populates="category_rel")
|
||
|
|
services = relationship("Service", back_populates="category_rel")
|
||
|
|
|
||
|
|
class Product(Base):
|
||
|
|
__tablename__ = "products"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
name = Column(String(255), nullable=False)
|
||
|
|
description = Column(Text) # Now supports HTML from rich text editor
|
||
|
|
price = Column(Float, nullable=False)
|
||
|
|
category = Column(String(100), nullable=False)
|
||
|
|
category_id = Column(String(36), ForeignKey("categories.id"), nullable=True)
|
||
|
|
image_url = Column(String(500)) # Deprecated - kept for backwards compatibility
|
||
|
|
stock = Column(Integer, default=10)
|
||
|
|
low_stock_threshold = Column(Integer, default=5)
|
||
|
|
brand = Column(String(100))
|
||
|
|
specs = Column(JSON, default={})
|
||
|
|
is_active = Column(Boolean, default=True)
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||
|
|
|
||
|
|
category_rel = relationship("Category", back_populates="products")
|
||
|
|
cart_items = relationship("CartItem", back_populates="product")
|
||
|
|
order_items = relationship("OrderItem", back_populates="product")
|
||
|
|
reviews = relationship("Review", back_populates="product", cascade="all, delete-orphan")
|
||
|
|
inventory_logs = relationship("InventoryLog", back_populates="product", cascade="all, delete-orphan")
|
||
|
|
images = relationship("ProductImage", back_populates="product", cascade="all, delete-orphan", order_by="ProductImage.display_order")
|
||
|
|
|
||
|
|
class ProductImage(Base):
|
||
|
|
__tablename__ = "product_images"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
product_id = Column(String(36), ForeignKey("products.id"), nullable=False)
|
||
|
|
image_url = Column(String(500), nullable=False)
|
||
|
|
display_order = Column(Integer, default=0)
|
||
|
|
is_primary = Column(Boolean, default=False)
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
|
||
|
|
product = relationship("Product", back_populates="images")
|
||
|
|
|
||
|
|
class ServiceImage(Base):
|
||
|
|
__tablename__ = "service_images"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
service_id = Column(String(36), ForeignKey("services.id"), nullable=False)
|
||
|
|
image_url = Column(String(500), nullable=False)
|
||
|
|
display_order = Column(Integer, default=0)
|
||
|
|
is_primary = Column(Boolean, default=False)
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
|
||
|
|
service = relationship("Service", back_populates="images")
|
||
|
|
|
||
|
|
class Service(Base):
|
||
|
|
__tablename__ = "services"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
name = Column(String(255), nullable=False)
|
||
|
|
description = Column(Text) # Now supports HTML from rich text editor
|
||
|
|
price = Column(Float, nullable=False)
|
||
|
|
duration = Column(String(50))
|
||
|
|
image_url = Column(String(500)) # Deprecated - kept for backwards compatibility
|
||
|
|
category = Column(String(100), nullable=False)
|
||
|
|
category_id = Column(String(36), ForeignKey("categories.id"), nullable=True)
|
||
|
|
is_active = Column(Boolean, default=True)
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||
|
|
|
||
|
|
category_rel = relationship("Category", back_populates="services")
|
||
|
|
bookings = relationship("Booking", back_populates="service")
|
||
|
|
reviews = relationship("Review", back_populates="service", cascade="all, delete-orphan")
|
||
|
|
images = relationship("ServiceImage", back_populates="service", cascade="all, delete-orphan", order_by="ServiceImage.display_order")
|
||
|
|
|
||
|
|
class CartItem(Base):
|
||
|
|
__tablename__ = "cart_items"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
|
||
|
|
product_id = Column(String(36), ForeignKey("products.id"), nullable=False)
|
||
|
|
quantity = Column(Integer, default=1)
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
|
||
|
|
user = relationship("User", back_populates="cart_items")
|
||
|
|
product = relationship("Product", back_populates="cart_items")
|
||
|
|
|
||
|
|
class Order(Base):
|
||
|
|
__tablename__ = "orders"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
|
||
|
|
status = Column(SQLEnum(OrderStatus), default=OrderStatus.PENDING)
|
||
|
|
subtotal = Column(Float, default=0)
|
||
|
|
tax = Column(Float, default=0)
|
||
|
|
shipping = Column(Float, default=0)
|
||
|
|
total = Column(Float, default=0)
|
||
|
|
shipping_address = Column(JSON, default={})
|
||
|
|
notes = Column(Text)
|
||
|
|
tracking_number = Column(String(100))
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||
|
|
|
||
|
|
user = relationship("User", back_populates="orders")
|
||
|
|
items = relationship("OrderItem", back_populates="order", cascade="all, delete-orphan")
|
||
|
|
status_history = relationship("OrderStatusHistory", back_populates="order", cascade="all, delete-orphan")
|
||
|
|
|
||
|
|
class OrderItem(Base):
|
||
|
|
__tablename__ = "order_items"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
order_id = Column(String(36), ForeignKey("orders.id"), nullable=False)
|
||
|
|
product_id = Column(String(36), ForeignKey("products.id"), nullable=False)
|
||
|
|
quantity = Column(Integer, default=1)
|
||
|
|
price = Column(Float, nullable=False)
|
||
|
|
product_name = Column(String(255))
|
||
|
|
product_image = Column(String(500))
|
||
|
|
|
||
|
|
order = relationship("Order", back_populates="items")
|
||
|
|
product = relationship("Product", back_populates="order_items")
|
||
|
|
|
||
|
|
class OrderStatusHistory(Base):
|
||
|
|
__tablename__ = "order_status_history"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
order_id = Column(String(36), ForeignKey("orders.id"), nullable=False)
|
||
|
|
status = Column(SQLEnum(OrderStatus), nullable=False)
|
||
|
|
notes = Column(Text)
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
created_by = Column(String(36))
|
||
|
|
|
||
|
|
order = relationship("Order", back_populates="status_history")
|
||
|
|
|
||
|
|
class Review(Base):
|
||
|
|
__tablename__ = "reviews"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
|
||
|
|
product_id = Column(String(36), ForeignKey("products.id"), nullable=True)
|
||
|
|
service_id = Column(String(36), ForeignKey("services.id"), nullable=True)
|
||
|
|
rating = Column(Integer, nullable=False) # 1-5
|
||
|
|
title = Column(String(255))
|
||
|
|
comment = Column(Text)
|
||
|
|
is_verified_purchase = Column(Boolean, default=False)
|
||
|
|
is_approved = Column(Boolean, default=True)
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
|
||
|
|
user = relationship("User", back_populates="reviews")
|
||
|
|
product = relationship("Product", back_populates="reviews")
|
||
|
|
service = relationship("Service", back_populates="reviews")
|
||
|
|
|
||
|
|
class Booking(Base):
|
||
|
|
__tablename__ = "bookings"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
service_id = Column(String(36), ForeignKey("services.id"), nullable=False)
|
||
|
|
user_id = Column(String(36), ForeignKey("users.id"), nullable=True)
|
||
|
|
name = Column(String(255), nullable=False)
|
||
|
|
email = Column(String(255), nullable=False)
|
||
|
|
phone = Column(String(50))
|
||
|
|
preferred_date = Column(String(50))
|
||
|
|
notes = Column(Text)
|
||
|
|
status = Column(String(50), default="pending")
|
||
|
|
service_name = Column(String(255))
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
|
||
|
|
service = relationship("Service", back_populates="bookings")
|
||
|
|
user = relationship("User", back_populates="bookings")
|
||
|
|
|
||
|
|
class Contact(Base):
|
||
|
|
__tablename__ = "contacts"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
name = Column(String(255), nullable=False)
|
||
|
|
email = Column(String(255), nullable=False)
|
||
|
|
subject = Column(String(255))
|
||
|
|
message = Column(Text, nullable=False)
|
||
|
|
status = Column(String(50), default="pending")
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
|
||
|
|
class InventoryLog(Base):
|
||
|
|
__tablename__ = "inventory_logs"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
product_id = Column(String(36), ForeignKey("products.id"), nullable=False)
|
||
|
|
action = Column(String(50), nullable=False) # add, remove, adjust, sale
|
||
|
|
quantity_change = Column(Integer, nullable=False)
|
||
|
|
previous_stock = Column(Integer)
|
||
|
|
new_stock = Column(Integer)
|
||
|
|
notes = Column(Text)
|
||
|
|
created_by = Column(String(36))
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
|
||
|
|
product = relationship("Product", back_populates="inventory_logs")
|
||
|
|
|
||
|
|
class SalesReport(Base):
|
||
|
|
__tablename__ = "sales_reports"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
report_type = Column(String(50), nullable=False) # daily, weekly, monthly
|
||
|
|
report_date = Column(DateTime(timezone=True), nullable=False)
|
||
|
|
start_date = Column(DateTime(timezone=True))
|
||
|
|
end_date = Column(DateTime(timezone=True))
|
||
|
|
total_orders = Column(Integer, default=0)
|
||
|
|
total_revenue = Column(Float, default=0)
|
||
|
|
total_products_sold = Column(Integer, default=0)
|
||
|
|
total_services_booked = Column(Integer, default=0)
|
||
|
|
report_data = Column(JSON, default={})
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
class AboutContent(Base):
|
||
|
|
__tablename__ = "about_content"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
section = Column(String(50), nullable=False, unique=True) # 'hero', 'story', 'stats'
|
||
|
|
title = Column(String(255))
|
||
|
|
subtitle = Column(Text)
|
||
|
|
content = Column(Text) # HTML content from rich text editor
|
||
|
|
image_url = Column(String(500))
|
||
|
|
data = Column(JSON, default={}) # For flexible content like stats
|
||
|
|
is_active = Column(Boolean, default=True)
|
||
|
|
display_order = Column(Integer, default=0)
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||
|
|
|
||
|
|
class TeamMember(Base):
|
||
|
|
__tablename__ = "team_members"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
name = Column(String(255), nullable=False)
|
||
|
|
role = Column(String(255), nullable=False)
|
||
|
|
bio = Column(Text) # HTML content from rich text editor
|
||
|
|
image_url = Column(String(500))
|
||
|
|
email = Column(String(255))
|
||
|
|
linkedin = Column(String(500))
|
||
|
|
display_order = Column(Integer, default=0)
|
||
|
|
is_active = Column(Boolean, default=True)
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||
|
|
|
||
|
|
class CompanyValue(Base):
|
||
|
|
__tablename__ = "company_values"
|
||
|
|
|
||
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
||
|
|
title = Column(String(255), nullable=False)
|
||
|
|
description = Column(Text)
|
||
|
|
icon = Column(String(50)) # Icon name (e.g., 'Target', 'Users', 'Award', 'Heart')
|
||
|
|
display_order = Column(Integer, default=0)
|
||
|
|
is_active = Column(Boolean, default=True)
|
||
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||
|
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|