diff --git a/local_backend/routers/settings.py b/local_backend/routers/settings.py
index 6cdbc29..e15218f 100644
--- a/local_backend/routers/settings.py
+++ b/local_backend/routers/settings.py
@@ -16,6 +16,7 @@ VALID_SETTINGS = {
"business_day.force_close_allowed": "Allow force-closing business day with open tables",
"system.timezone": "IANA timezone name used by the backend container (e.g. Europe/Athens). Requires container restart to take effect.",
"ui.table_colours": "JSON blob of table card colour scheme (light + dark modes) for the Waiter PWA.",
+ "dev.spoof_printing": "When enabled, all print jobs are silently dropped. Devices behave as if printing succeeded.",
}
DEFAULTS = {
@@ -24,6 +25,7 @@ DEFAULTS = {
"business_day.force_close_allowed": "true",
"system.timezone": "Europe/Athens",
"ui.table_colours": "",
+ "dev.spoof_printing": "false",
}
diff --git a/local_backend/services/printer_service.py b/local_backend/services/printer_service.py
index 976d842..9dd1b09 100644
--- a/local_backend/services/printer_service.py
+++ b/local_backend/services/printer_service.py
@@ -20,6 +20,7 @@ from database import SessionLocal
from models.order import Order, OrderItem, PrintLog
from models.printer import Printer
from models.product import Product
+from models.settings import PosSettings
logger = logging.getLogger(__name__)
@@ -73,7 +74,19 @@ def check_printer(ip: str, port: int) -> bool:
return False
+def is_spoof_mode() -> bool:
+ """Stateless check — opens its own DB session. For use outside route_and_print."""
+ db = SessionLocal()
+ try:
+ return _is_spoof_mode(db)
+ finally:
+ db.close()
+
+
def send_test_print(ip: str, port: int, name: str) -> Tuple[bool, str]:
+ if is_spoof_mode():
+ logger.info("Spoof printing ON — dropping test print for %s", name)
+ return True, ""
try:
p = _get_printer(ip, port)
p._raw(b'\x1b\x61\x01')
@@ -164,6 +177,9 @@ def _print_kitchen_ticket(p: Network, order: Order, items: List[OrderItem], db:
def print_waiter_report(ip: str, port: int, report: dict, mode: str):
"""Print a waiter shift/period report. mode='simple'|'extensive'."""
+ if is_spoof_mode():
+ logger.info("Spoof printing ON — dropping waiter report print")
+ return
try:
p = _get_printer(ip, port)
@@ -222,6 +238,9 @@ def print_waiter_report(ip: str, port: int, report: dict, mode: str):
def print_printer_report(ip: str, port: int, report: dict, mode: str):
"""Print a per-printer totals report. mode='simple'|'extensive'."""
+ if is_spoof_mode():
+ logger.info("Spoof printing ON — dropping printer report print")
+ return
try:
p = _get_printer(ip, port)
@@ -282,6 +301,9 @@ def print_printer_report(ip: str, port: int, report: dict, mode: str):
def print_order_receipt(ip: str, port: int, receipt: dict):
"""Print a manager-triggered order receipt."""
+ if is_spoof_mode():
+ logger.info("Spoof printing ON — dropping order receipt print")
+ return
try:
p = _get_printer(ip, port)
@@ -329,6 +351,9 @@ def print_order_receipt(ip: str, port: int, receipt: dict):
def print_order_synopsis(ip: str, port: int, synopsis: dict):
"""Print a waiter-triggered order synopsis (not a kitchen ticket)."""
+ if is_spoof_mode():
+ logger.info("Spoof printing ON — dropping order synopsis print")
+ return
try:
p = _get_printer(ip, port)
@@ -408,7 +433,21 @@ def route_and_print_sync(order_id: int, item_ids: List[int], db: Session) -> Lis
return _do_route_and_print(order_id, item_ids, db)
+def _is_spoof_mode(db: Session) -> bool:
+ row = db.query(PosSettings).filter(PosSettings.key == "dev.spoof_printing").first()
+ return row is not None and row.value == "true"
+
+
def _do_route_and_print(order_id: int, item_ids: List[int], db: Session) -> List[dict]:
+ if _is_spoof_mode(db):
+ logger.info("Spoof printing ON — dropping print job for order %s", order_id)
+ for item_id in item_ids:
+ item = db.query(OrderItem).filter(OrderItem.id == item_id).first()
+ if item:
+ item.printed = True
+ db.commit()
+ return [{"printer_name": "spoof", "success": True, "error": None}]
+
results = []
order = db.query(Order).filter(Order.id == order_id).first()
diff --git a/manager_dashboard/src/pages/Settings/SettingsPage.jsx b/manager_dashboard/src/pages/Settings/SettingsPage.jsx
index 5a0d71a..9445ac7 100644
--- a/manager_dashboard/src/pages/Settings/SettingsPage.jsx
+++ b/manager_dashboard/src/pages/Settings/SettingsPage.jsx
@@ -1,10 +1,12 @@
import { useState } from 'react'
import AppInfoTab from './tabs/AppInfoTab'
import ColoursTab from './tabs/ColoursTab'
+import DevelopmentTab from './tabs/DevelopmentTab'
const TABS = [
- { key: 'app-info', label: 'App Info' },
- { key: 'colours', label: 'UI Personalization' },
+ { key: 'app-info', label: 'App Info' },
+ { key: 'colours', label: 'UI Personalization' },
+ { key: 'development', label: 'Development' },
]
export default function SettingsPage() {
@@ -44,8 +46,9 @@ export default function SettingsPage() {
{/* Tab content */}
- {activeTab === 'app-info' &&
Loading…
+ + return ( +