feat: Phase 2H — void/waste log

Backend:
- New model: WasteLog (waste_log table) — product, quantity (float), reason, reason_notes,
  unit_cost_snapshot (copied at log time from product's effective cost), total_cost (stored),
  logged_by, business_day_id
- Cost snapshot: same logic as Phase 2A — breakdown sum first, fallback to cost_simple, null if neither
- New router /api/waste/:
  - GET / — list entries (?business_day_id= or ?from=&to=), ordered by logged_at desc
  - POST / — log waste; snapshots cost; attaches to open business day automatically
  - DELETE /{id} — manager only; only allowed within the same open business day
  - GET /summary — totals by reason + by product for a period
- product_performance report: waste_qty + waste_cost added per product from the same period;
  products with only waste (no sales) also included
- Migration: CREATE TABLE IF NOT EXISTS waste_log

Frontend:
- WastePage (/waste): fast log form (product picker, quantity, reason chips, notes);
  today's entries list below (scoped to active business day); cost shown per entry;
  delete button for today's entries; history section behind toggle with date-range picker
- ProductPerformance report: Απόβλητα column showing waste_qty + cost in amber
- Sidebar: Trash2 icon for Αποβλήτα (after Καρτέλες)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 03:19:33 +03:00
parent 2ff48730f7
commit 9cfbde0e3e
9 changed files with 607 additions and 1 deletions

View File

@@ -379,6 +379,7 @@ export default function ProductPerformance() {
<TH align="right">Τεμάχια</TH>
<TH align="right">Έσοδα</TH>
<TH align="right">Κέρδος</TH>
<TH align="right">Απόβλητα</TH>
<TH align="right">% Συνόλου</TH>
<TH align="right">Παραγγελίες</TH>
</THead>
@@ -403,6 +404,14 @@ export default function ProductPerformance() {
</span>
)}
</TD>
<TD mono align="right">
{p.waste_qty > 0 ? (
<span className="text-amber-600">
{p.waste_qty}
{p.waste_cost > 0 && <span className="text-[10px] text-amber-400 ml-1">({fmtEUR(p.waste_cost)})</span>}
</span>
) : <span className="text-slate-300"></span>}
</TD>
<TD mono align="right">{pct.toFixed(1)}%</TD>
<TD mono align="right">{fmtNum(p.order_count)}</TD>
</TR>