feat: bump client-services (accumulated feature work + deploy fixes)
Snapshot of in-progress work across local_backend, manager_dashboard, and waiter_pwa (pricing, chat, fiscal, prep zones, recovery codes, CRM, inventory, permissions), plus the nginx/docker-compose deploy fixes for the Unraid + NPM reverse-proxy setup. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,11 @@ class Order(Base):
|
||||
closed_by = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
notes = Column(Text, nullable=True)
|
||||
business_day_id = Column(Integer, ForeignKey("business_days.id"), nullable=True)
|
||||
# KDS
|
||||
kds_status = Column(String, default="pending", nullable=False) # pending|preparing|done
|
||||
kds_status_changed_at = Column(DateTime(timezone=True), nullable=True)
|
||||
order_type = Column(String, default="here", nullable=False) # here|takeaway|delivery
|
||||
|
||||
# Xenia Connect — online order fields
|
||||
source = Column(String, default="pos", nullable=False) # "pos" | "online"
|
||||
online_order_ref = Column(String, nullable=True) # e.g. "ORD-0042"
|
||||
@@ -33,6 +38,12 @@ class Order(Base):
|
||||
# Phase 2F — customer CRM
|
||||
customer_id = Column(Integer, ForeignKey("customers.id"), nullable=True)
|
||||
|
||||
# Number of customers at the table for this order
|
||||
customer_count = Column(Integer, nullable=True)
|
||||
|
||||
# Fiscal printer status: NULL = fiscal was off, "pending" | "success" | "failed"
|
||||
fiscal_status = Column(String, nullable=True)
|
||||
|
||||
table = relationship("Table", back_populates="orders")
|
||||
opener = relationship("User", foreign_keys=[opened_by], back_populates="orders_opened")
|
||||
closer = relationship("User", foreign_keys=[closed_by], back_populates="orders_closed")
|
||||
@@ -40,6 +51,7 @@ class Order(Base):
|
||||
items = relationship("OrderItem", back_populates="order", cascade="all, delete-orphan")
|
||||
waiters = relationship("OrderWaiter", back_populates="order", cascade="all, delete-orphan")
|
||||
print_logs = relationship("PrintLog", back_populates="order", cascade="all, delete-orphan")
|
||||
print_jobs = relationship("PrintJob", back_populates="order", cascade="all, delete-orphan")
|
||||
audit_logs = relationship("OrderAuditLog", back_populates="order", cascade="all, delete-orphan")
|
||||
discounts = relationship("OrderDiscount", back_populates="order", cascade="all, delete-orphan")
|
||||
|
||||
@@ -63,12 +75,13 @@ class OrderItem(Base):
|
||||
order_id = Column(Integer, ForeignKey("orders.id"), nullable=False)
|
||||
product_id = Column(Integer, ForeignKey("products.id"), nullable=False)
|
||||
added_by = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||
quantity = Column(Integer, nullable=False)
|
||||
quantity = Column(Float, nullable=False)
|
||||
unit_price = Column(Float, nullable=False) # price snapshot at time of order
|
||||
selected_options = Column(Text, nullable=True) # JSON array of option ids
|
||||
removed_ingredients = Column(Text, nullable=True) # JSON array of ingredient ids
|
||||
notes = Column(Text, nullable=True)
|
||||
status = Column(String, default="active", nullable=False) # active|paid|cancelled
|
||||
status = Column(String, default="active", nullable=False) # active|paid|cancelled
|
||||
kds_status = Column(String, default="pending", nullable=False) # pending|preparing|done|served|declined
|
||||
added_at = Column(DateTime(timezone=True), default=_utcnow)
|
||||
printed = Column(Boolean, default=False, nullable=False)
|
||||
|
||||
@@ -81,16 +94,33 @@ class OrderItem(Base):
|
||||
# Phase 2A — cost snapshot (copied from product at time of order, never updated)
|
||||
unit_cost = Column(Float, nullable=True)
|
||||
|
||||
# On-the-fly price adjustment (positive = surcharge, negative = discount)
|
||||
price_adjustment = Column(Float, nullable=False, default=0.0)
|
||||
|
||||
# Phase 2B — cancellation tracking
|
||||
cancelled_by = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
cancelled_at = Column(DateTime(timezone=True), nullable=True)
|
||||
cancel_reason = Column(Text, nullable=True)
|
||||
|
||||
# KDS decline tracking
|
||||
decline_note = Column(Text, nullable=True) # reason set by kitchen when declining
|
||||
|
||||
# Courses — optional course assignment for sequenced firing
|
||||
course_id = Column(Integer, nullable=True)
|
||||
|
||||
# Deal binding — which deal created this item, and which original item it's linked to
|
||||
deal_id = Column(Integer, ForeignKey("deals.id"), nullable=True)
|
||||
linked_item_id = Column(Integer, ForeignKey("order_items.id"), 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")
|
||||
paid_by_user = relationship("User", foreign_keys=[paid_by], back_populates="items_paid")
|
||||
|
||||
@property
|
||||
def unit_type(self) -> str:
|
||||
return self.product.unit_type if self.product and self.product.unit_type else "piece"
|
||||
|
||||
|
||||
class PrintLog(Base):
|
||||
__tablename__ = "print_log"
|
||||
@@ -107,6 +137,32 @@ class PrintLog(Base):
|
||||
printer = relationship("Printer", back_populates="print_logs")
|
||||
|
||||
|
||||
class PrintJob(Base):
|
||||
"""
|
||||
One row per (order, printer, zone) print job. Tracks the full lifecycle:
|
||||
pending → success or cancelled. PrintLog rows record each individual attempt.
|
||||
"""
|
||||
__tablename__ = "print_jobs"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
order_id = Column(Integer, ForeignKey("orders.id"), nullable=False)
|
||||
printer_id = Column(Integer, ForeignKey("printers.id"), nullable=False)
|
||||
zone_id = Column(Integer, nullable=True) # prep zone id, NULL for legacy
|
||||
item_ids = Column(Text, nullable=False) # JSON array of order_item ids
|
||||
copies = Column(Integer, nullable=False, default=1)
|
||||
|
||||
status = Column(String, nullable=False, default="pending") # pending|success|cancelled
|
||||
retry_count = Column(Integer, nullable=False, default=0)
|
||||
first_attempted_at = Column(DateTime(timezone=True), default=_utcnow)
|
||||
last_attempted_at = Column(DateTime(timezone=True), nullable=True)
|
||||
succeeded_at = Column(DateTime(timezone=True), nullable=True)
|
||||
cancelled_at = Column(DateTime(timezone=True), nullable=True)
|
||||
cancel_reason = Column(String, nullable=True) # "staff_cancelled" | "order_closed"
|
||||
|
||||
order = relationship("Order", back_populates="print_jobs")
|
||||
printer = relationship("Printer", back_populates="print_jobs")
|
||||
|
||||
|
||||
class OrderAuditLog(Base):
|
||||
"""Immutable append-only audit trail for every action on an order."""
|
||||
__tablename__ = "order_audit_log"
|
||||
|
||||
Reference in New Issue
Block a user