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:
@@ -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"])
|
||||
|
||||
Reference in New Issue
Block a user