feat: Phase 2A — product cost tracking
- Products: add cost_simple (flat €) and cost_breakdown (JSON line items) - OrderItems: add unit_cost snapshot written at time of order creation - Snapshot logic: breakdown sum takes priority over cost_simple; NULL if neither set - Product schema: serialize/deserialize cost_breakdown as JSON; expose in API - Product performance report: add trackable_profit, uncosted_revenue, has_gap per product - Manager UI: cost section in product edit form (simple / detailed toggle with live margin %) - Products list: show margin % or cost-error badge per product - Product performance report: profit column + gap warning banner Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -75,6 +75,9 @@ class OrderItem(Base):
|
||||
payment_method = Column(String, nullable=True) # 'cash'|'card'|'other' — future use
|
||||
paid_in_shift_id = Column(Integer, ForeignKey("waiter_shifts.id"), nullable=True)
|
||||
|
||||
# Phase 2A — cost snapshot (copied from product at time of order, never updated)
|
||||
unit_cost = Column(Float, nullable=True)
|
||||
|
||||
order = relationship("Order", back_populates="items")
|
||||
product = relationship("Product", back_populates="order_items")
|
||||
added_by_user = relationship("User", foreign_keys=[added_by], back_populates="order_items")
|
||||
|
||||
@@ -46,6 +46,10 @@ class Product(Base):
|
||||
digital_discount = Column(Float, default=0.0, nullable=False)
|
||||
digital_image_url = Column(String, nullable=True)
|
||||
|
||||
# Phase 2A — cost tracking
|
||||
cost_simple = Column(Float, nullable=True)
|
||||
cost_breakdown = Column(Text, nullable=True) # JSON: [{"label": str, "amount": float}]
|
||||
|
||||
category = relationship("Category", back_populates="products")
|
||||
printer_zone = relationship("Printer", back_populates="products")
|
||||
quick_options = relationship("ProductQuickOption", back_populates="product", cascade="all, delete-orphan")
|
||||
|
||||
Reference in New Issue
Block a user