Files
simple-pos-system/local_backend/models/product.py
bonamin 2c9276e654 Backend: product sub-choices, sort_order, preference shared_subset, hard-delete
- ProductOption and ProductPreferenceChoice gain sub_choices (JSON Text column)
  for nested inline choices shown when the parent is selected
- ProductPreferenceSet gains default_choice_id and shared_subset (set-level
  sub-choice group shown for all choices that don't disable it)
- Product gains sort_order column; list endpoint orders by sort_order
- New PUT /products/reorder endpoint for drag-and-drop ordering
- DELETE /products/{id} now accepts ?hard=true for permanent deletion (blocked
  if product appears in any past order)
- Schemas updated with model_validators to parse stored JSON back to typed objects
- Add python-multipart to requirements (needed for file upload form parsing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 09:27:16 +03:00

90 lines
3.7 KiB
Python

from sqlalchemy import Column, Integer, String, Boolean, Float, ForeignKey, Text
from sqlalchemy.orm import relationship
from database import Base
class Category(Base):
__tablename__ = "categories"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False)
color = Column(String, nullable=True)
sort_order = Column(Integer, default=0)
products = relationship("Product", back_populates="category")
class Product(Base):
__tablename__ = "products"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False)
category_id = Column(Integer, ForeignKey("categories.id"), nullable=True)
base_price = Column(Float, nullable=False)
is_available = Column(Boolean, default=True, nullable=False)
printer_zone_id = Column(Integer, ForeignKey("printers.id"), nullable=True)
image_url = Column(String, nullable=True)
sort_order = Column(Integer, default=0, nullable=False)
category = relationship("Category", back_populates="products")
printer_zone = relationship("Printer", back_populates="products")
options = relationship("ProductOption", back_populates="product", cascade="all, delete-orphan")
ingredients = relationship("ProductIngredient", back_populates="product", cascade="all, delete-orphan")
preference_sets = relationship("ProductPreferenceSet", back_populates="product", cascade="all, delete-orphan")
order_items = relationship("OrderItem", back_populates="product")
class ProductOption(Base):
__tablename__ = "product_options"
id = Column(Integer, primary_key=True, index=True)
product_id = Column(Integer, ForeignKey("products.id"), nullable=False)
name = Column(String, nullable=False)
extra_cost = Column(Float, default=0.0)
# JSON array [{name, extra_cost, is_default}] — sub-options shown when this option is checked
sub_choices = Column(Text, nullable=True)
product = relationship("Product", back_populates="options")
class ProductIngredient(Base):
__tablename__ = "product_ingredients"
id = Column(Integer, primary_key=True, index=True)
product_id = Column(Integer, ForeignKey("products.id"), nullable=False)
name = Column(String, nullable=False)
extra_cost = Column(Float, default=0.0)
product = relationship("Product", back_populates="ingredients")
class ProductPreferenceSet(Base):
__tablename__ = "product_preference_sets"
id = Column(Integer, primary_key=True, index=True)
product_id = Column(Integer, ForeignKey("products.id"), nullable=False)
name = Column(String, nullable=False)
default_choice_id = Column(Integer, nullable=True)
# JSON: {name, default_choice_index, choices:[{name,extra_cost,is_default}]}
# Shared sub-set shown for all choices that don't have disables_subset=True
shared_subset = Column(Text, nullable=True)
product = relationship("Product", back_populates="preference_sets")
choices = relationship("ProductPreferenceChoice", back_populates="set", cascade="all, delete-orphan")
class ProductPreferenceChoice(Base):
__tablename__ = "product_preference_choices"
id = Column(Integer, primary_key=True, index=True)
set_id = Column(Integer, ForeignKey("product_preference_sets.id"), nullable=False)
name = Column(String, nullable=False)
extra_cost = Column(Float, default=0.0)
# JSON array of sub-choice objects: [{name, extra_cost, is_default}]
# Per-choice inline sub-preference shown only when this choice is selected.
sub_choices = Column(Text, nullable=True)
# When True this choice hides the set-level shared_subset on the PWA.
disables_subset = Column(Boolean, default=False, nullable=False)
set = relationship("ProductPreferenceSet", back_populates="choices")