fix: printer history report double-counts order totals on reprints
Backend (reports.py):
- Cache order DB objects to avoid N+1 queries per log entry
- Track seen_order_totals keyed by order_id so reprinting the same
order doesn't inflate the total; returns total_amount pre-computed
Frontend (PrinterHistory.jsx):
- Remove client-side accumulation that caused the same issue; use
total_amount directly from the API response
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1063,9 +1063,18 @@ def printer_history(
|
|||||||
printers_db = {p.id: p for p in db.query(Printer).all()}
|
printers_db = {p.id: p for p in db.query(Printer).all()}
|
||||||
tables_db = {t.id: (t.label or f"T{t.number}") for t in db.query(Table).all()}
|
tables_db = {t.id: (t.label or f"T{t.number}") for t in db.query(Table).all()}
|
||||||
|
|
||||||
|
# Cache orders to avoid repeated queries
|
||||||
|
order_cache = {}
|
||||||
|
def _get_order(oid):
|
||||||
|
if oid not in order_cache:
|
||||||
|
order_cache[oid] = db.query(Order).filter(Order.id == oid).first()
|
||||||
|
return order_cache[oid]
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
|
seen_order_totals = {} # order_id → total, so we don't double-count reprints
|
||||||
|
|
||||||
for log in logs:
|
for log in logs:
|
||||||
order = db.query(Order).filter(Order.id == log.order_id).first()
|
order = _get_order(log.order_id)
|
||||||
printer = printers_db.get(log.printer_id)
|
printer = printers_db.get(log.printer_id)
|
||||||
try:
|
try:
|
||||||
item_ids = json.loads(log.item_ids)
|
item_ids = json.loads(log.item_ids)
|
||||||
@@ -1077,6 +1086,18 @@ def printer_history(
|
|||||||
if oi:
|
if oi:
|
||||||
pname = oi.product.name if oi.product else f"#{oi.product_id}"
|
pname = oi.product.name if oi.product else f"#{oi.product_id}"
|
||||||
items.append({"name": pname, "quantity": oi.quantity})
|
items.append({"name": pname, "quantity": oi.quantity})
|
||||||
|
|
||||||
|
# Compute full order total (all active+paid items, not just printed slice)
|
||||||
|
order_total = None
|
||||||
|
if order:
|
||||||
|
order_total = sum(
|
||||||
|
i.unit_price * i.quantity
|
||||||
|
for i in order.items
|
||||||
|
if i.status in ("active", "paid")
|
||||||
|
)
|
||||||
|
if log.success and log.order_id not in seen_order_totals:
|
||||||
|
seen_order_totals[log.order_id] = order_total
|
||||||
|
|
||||||
result.append({
|
result.append({
|
||||||
"id": log.id,
|
"id": log.id,
|
||||||
"printed_at": _dt(log.printed_at),
|
"printed_at": _dt(log.printed_at),
|
||||||
@@ -1086,11 +1107,14 @@ def printer_history(
|
|||||||
"items": items,
|
"items": items,
|
||||||
"success": log.success,
|
"success": log.success,
|
||||||
"error_message": log.error_message,
|
"error_message": log.error_message,
|
||||||
|
"order_total": round(order_total, 2) if order_total is not None else None,
|
||||||
})
|
})
|
||||||
|
|
||||||
total = len(result)
|
total = len(result)
|
||||||
failed = sum(1 for r in result if not r["success"])
|
failed = sum(1 for r in result if not r["success"])
|
||||||
return {"logs": result, "total": total, "failed": failed}
|
# Sum unique order totals (avoid inflating on reprints of the same order)
|
||||||
|
total_amount = round(sum(seen_order_totals.values()), 2) if seen_order_totals else None
|
||||||
|
return {"logs": result, "total": total, "failed": failed, "total_amount": total_amount}
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -289,16 +289,16 @@ export default function PrinterHistory() {
|
|||||||
const logs = data?.logs || []
|
const logs = data?.logs || []
|
||||||
const total = data?.total || 0
|
const total = data?.total || 0
|
||||||
const failed = data?.failed || 0
|
const failed = data?.failed || 0
|
||||||
|
// total_amount comes pre-computed from the backend (unique orders, no double-counting reprints)
|
||||||
|
const totalAmount = data?.total_amount ?? null
|
||||||
|
|
||||||
// Derived stats for StatCards + Print modal
|
// Derived stats for StatCards + Print modal
|
||||||
const itemCounts = {}
|
const itemCounts = {}
|
||||||
const waiterCounts = {}
|
const waiterCounts = {}
|
||||||
let totalItemQty = 0
|
let totalItemQty = 0
|
||||||
let totalAmount = null
|
|
||||||
|
|
||||||
logs.forEach(l => {
|
logs.forEach(l => {
|
||||||
if (l.waiter) waiterCounts[l.waiter] = (waiterCounts[l.waiter] || 0) + 1
|
if (l.waiter) waiterCounts[l.waiter] = (waiterCounts[l.waiter] || 0) + 1
|
||||||
if (l.order_total != null) totalAmount = (totalAmount || 0) + l.order_total
|
|
||||||
;(l.items || []).forEach(i => {
|
;(l.items || []).forEach(i => {
|
||||||
itemCounts[i.name] = (itemCounts[i.name] || 0) + i.quantity
|
itemCounts[i.name] = (itemCounts[i.name] || 0) + i.quantity
|
||||||
totalItemQty += i.quantity
|
totalItemQty += i.quantity
|
||||||
|
|||||||
Reference in New Issue
Block a user