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

@@ -24,6 +24,7 @@ import models.notes # noqa: F401 — registers SiteNote, SiteTodo
import models.expenses # noqa: F401 — registers Contact, Expense, ExpensePayment
import models.customers # noqa: F401 — registers Customer
import models.tabs # noqa: F401 — registers Tab, TabEntry, TabPayment
import models.waste # noqa: F401 — registers WasteLog
from routers import auth, tables, products, orders, waiters, reports, system, setup as setup_router
from routers import business_day as business_day_router
@@ -39,6 +40,7 @@ from routers import notes as notes_router
from routers.expenses import contacts_router, expenses_router
from routers import customers as customers_router
from routers import tabs as tabs_router
from routers import waste as waste_router
def _run_migrations():
@@ -277,6 +279,19 @@ def _run_migrations():
# Phase 2B — staff payroll
"ALTER TABLE users ADD COLUMN hourly_rate REAL",
"ALTER TABLE waiter_shifts ADD COLUMN hourly_rate_snapshot REAL",
# Phase 2H — void/waste log
"""CREATE TABLE IF NOT EXISTS waste_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product_id INTEGER NOT NULL REFERENCES products(id),
quantity REAL NOT NULL,
unit_cost_snapshot REAL,
total_cost REAL,
reason VARCHAR NOT NULL,
reason_notes TEXT,
logged_by_id INTEGER NOT NULL REFERENCES users(id),
business_day_id INTEGER REFERENCES business_days(id),
logged_at DATETIME DEFAULT CURRENT_TIMESTAMP
)""",
# Phase 2G — pay-later tab system
"""CREATE TABLE IF NOT EXISTS tabs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -444,3 +459,4 @@ app.include_router(contacts_router, prefix="/api/contacts", tag
app.include_router(expenses_router, prefix="/api/expenses", tags=["expenses"])
app.include_router(customers_router.router, prefix="/api/customers", tags=["customers"])
app.include_router(tabs_router.router, prefix="/api/tabs", tags=["tabs"])
app.include_router(waste_router.router, prefix="/api/waste", tags=["waste"])