feat(connect): Phase 1 — add digital product fields to local_backend
Adds seven digital-menu-specific columns to the products table so
managers can control the public Xenia Connect menu independently of POS
data:
- digital_visible (INTEGER, default 1) — hide product from menu
- digital_available (INTEGER, default 1) — show as out-of-stock
- digital_name (VARCHAR, nullable) — override display name
- digital_description (VARCHAR, nullable)
- digital_price (REAL, nullable) — override price
- digital_discount (REAL, default 0.0) — percentage discount
- digital_image_url (VARCHAR, nullable) — menu-specific image
Changes:
- main.py: 7 additive ALTER TABLE migrations (safe on existing DBs)
- models/product.py: matching SQLAlchemy columns on Product
- schemas/product.py: ProductBase carries defaults (visible/available
default True); ProductUpdate has all fields Optional; ProductOut
inherits everything via ProductBase
No router changes needed — PUT /api/products/{id} picks up the new
fields from ProductUpdate automatically.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -218,6 +218,14 @@ def _run_migrations():
|
|||||||
"INSERT OR IGNORE INTO pos_settings (key, value, updated_at) VALUES ('security.auto_lock_seconds', '300', CURRENT_TIMESTAMP)",
|
"INSERT OR IGNORE INTO pos_settings (key, value, updated_at) VALUES ('security.auto_lock_seconds', '300', CURRENT_TIMESTAMP)",
|
||||||
"INSERT OR IGNORE INTO pos_settings (key, value, updated_at) VALUES ('security.auto_logout', 'false', CURRENT_TIMESTAMP)",
|
"INSERT OR IGNORE INTO pos_settings (key, value, updated_at) VALUES ('security.auto_logout', 'false', CURRENT_TIMESTAMP)",
|
||||||
"INSERT OR IGNORE INTO pos_settings (key, value, updated_at) VALUES ('security.auto_logout_seconds', '1800', CURRENT_TIMESTAMP)",
|
"INSERT OR IGNORE INTO pos_settings (key, value, updated_at) VALUES ('security.auto_logout_seconds', '1800', CURRENT_TIMESTAMP)",
|
||||||
|
# Xenia Connect — digital menu fields on products
|
||||||
|
"ALTER TABLE products ADD COLUMN digital_visible INTEGER NOT NULL DEFAULT 1",
|
||||||
|
"ALTER TABLE products ADD COLUMN digital_available INTEGER NOT NULL DEFAULT 1",
|
||||||
|
"ALTER TABLE products ADD COLUMN digital_name VARCHAR",
|
||||||
|
"ALTER TABLE products ADD COLUMN digital_description VARCHAR",
|
||||||
|
"ALTER TABLE products ADD COLUMN digital_price REAL",
|
||||||
|
"ALTER TABLE products ADD COLUMN digital_discount REAL NOT NULL DEFAULT 0.0",
|
||||||
|
"ALTER TABLE products ADD COLUMN digital_image_url VARCHAR",
|
||||||
]
|
]
|
||||||
for sql in migrations:
|
for sql in migrations:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -36,6 +36,15 @@ class Product(Base):
|
|||||||
image_url = Column(String, nullable=True)
|
image_url = Column(String, nullable=True)
|
||||||
sort_order = Column(Integer, default=0, nullable=False)
|
sort_order = Column(Integer, default=0, nullable=False)
|
||||||
|
|
||||||
|
# Xenia Connect — digital menu overrides
|
||||||
|
digital_visible = Column(Integer, default=1, nullable=False)
|
||||||
|
digital_available = Column(Integer, default=1, nullable=False)
|
||||||
|
digital_name = Column(String, nullable=True)
|
||||||
|
digital_description = Column(String, nullable=True)
|
||||||
|
digital_price = Column(Float, nullable=True)
|
||||||
|
digital_discount = Column(Float, default=0.0, nullable=False)
|
||||||
|
digital_image_url = Column(String, nullable=True)
|
||||||
|
|
||||||
category = relationship("Category", back_populates="products")
|
category = relationship("Category", back_populates="products")
|
||||||
printer_zone = relationship("Printer", back_populates="products")
|
printer_zone = relationship("Printer", back_populates="products")
|
||||||
quick_options = relationship("ProductQuickOption", back_populates="product", cascade="all, delete-orphan")
|
quick_options = relationship("ProductQuickOption", back_populates="product", cascade="all, delete-orphan")
|
||||||
|
|||||||
@@ -263,6 +263,14 @@ class ProductBase(BaseModel):
|
|||||||
lifecycle_status: str = "active"
|
lifecycle_status: str = "active"
|
||||||
printer_zone_id: Optional[int] = None
|
printer_zone_id: Optional[int] = None
|
||||||
sort_order: int = 0
|
sort_order: int = 0
|
||||||
|
# Xenia Connect — digital menu overrides
|
||||||
|
digital_visible: bool = True
|
||||||
|
digital_available: bool = True
|
||||||
|
digital_name: Optional[str] = None
|
||||||
|
digital_description: Optional[str] = None
|
||||||
|
digital_price: Optional[float] = None
|
||||||
|
digital_discount: float = 0.0
|
||||||
|
digital_image_url: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class ProductCreate(ProductBase):
|
class ProductCreate(ProductBase):
|
||||||
@@ -284,6 +292,14 @@ class ProductUpdate(BaseModel):
|
|||||||
options: Optional[List[ProductOptionCreate]] = None
|
options: Optional[List[ProductOptionCreate]] = None
|
||||||
ingredients: Optional[List[ProductIngredientCreate]] = None
|
ingredients: Optional[List[ProductIngredientCreate]] = None
|
||||||
preference_sets: Optional[List[PreferenceSetCreate]] = None
|
preference_sets: Optional[List[PreferenceSetCreate]] = None
|
||||||
|
# Xenia Connect — digital menu overrides
|
||||||
|
digital_visible: Optional[bool] = None
|
||||||
|
digital_available: Optional[bool] = None
|
||||||
|
digital_name: Optional[str] = None
|
||||||
|
digital_description: Optional[str] = None
|
||||||
|
digital_price: Optional[float] = None
|
||||||
|
digital_discount: Optional[float] = None
|
||||||
|
digital_image_url: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class ProductReorderItem(BaseModel):
|
class ProductReorderItem(BaseModel):
|
||||||
|
|||||||
Reference in New Issue
Block a user