Repository navigation
feat(cash): opt-in one cash shift per staff member #1339
Copy link
Copy link
Open
Labels
area:backendBackend (Express/SQLite)Backend (Express/SQLite)area:frontendFrontend (React/Next.js)Frontend (React/Next.js)enhancementNew feature or requestNew feature or requeststate:readyScoped and ready for implementationScoped and ready for implementationtype:featureNew capability or enhancement requestNew capability or enhancement request
Description
Parent: #830 (S3b). Follows #279, which deliberately left out concurrent tills. Verified against main @ 8e39053.
Problem
Waiters who also take payment (waiter-as-cashier) need their own cash shift so each person counts and answers for their own cash (+1-829, 21 Sep).
Current behavior (verified)
- Only one open shift per install:
CREATE UNIQUE INDEX cash_sessions_one_open ON cash_sessions(status) WHERE status='open'(main/db.ts~L7013).main/routes/cash-sessions.ts~L259 returns 409session_already_open, asserted intests/cash-sessions.test.ts. cash_sessionsalready hasopened_by. Cashiers may only close their own shift (cash-sessions.ts~L420).getOpenSession(db)(main/services/shift-session-gate.ts~L32) is global and is used by payments (bills.ts~L3772), expenses (services/expenses.ts~L1197), cash closures, printers and the require-open-shift gate.- Payment lines and drawer movements carry
cash_session_id, and closures aggregate by it (cash-closures.ts~L615-740). - Day close is already blocked while a shift is open (fix(cash): non-blocking shift carry-over, day-close open-shift guard, and custom Cash drawer math #1318).
Decisions (made by maintainer, 23 Oct)
- Opt-in store setting "One shift per staff member", off by default. With it off, behavior is exactly as today.
- When on, each user opens and closes their own shift: at most one open shift per user. Day close waits until all shifts are closed and adds them all up.
- Money follows the person taking the payment. Cash payments, cash refunds, drawer movements and cash expenses go to the actor's open shift. If the actor has none, today's rules apply (blocked only when
require_open_shiftis on, otherwiseNO_CASH_SESSION_ID). - Shared physical drawer is allowed. The drawer kicks for whoever takes cash. The docs must warn that two shifts sharing one drawer make the counts unreliable.
Small decisions (taken during grooming)
- Owner and manager can see and close anyone's shift. A cashier only their own (existing rule).
- Turning the setting off is refused while more than one shift is open.
- The X report can show one shift or all open shifts. The Z for a shift stays per shift, and day close covers all.
Scope
- Migration: replace
cash_sessions_one_openwith a per-user partial unique index(opened_by) WHERE status='open', and enforce "only one open overall" in code when the setting is off. Never drop data. getOpenSession(db, userId)with call sites updated: payments, refunds, expenses, drawer movements, printers, gate.- UI:
useCashSession.ts, ShiftOpen and ShiftClose modals, the cashier status bar, and a setting toggle in Settings. - Docs:
docs/architecture/business-time.md(cash closures) and the roles reference. Add the shared-drawer warning to the user docs (docs.flopos.com).
Acceptance criteria
- Setting off: existing tests unchanged, and a second open shift is still 409.
- Setting on: users A and B each open a shift, and each one's cash payments and expenses land in their own expected cash.
- Day close is refused until both are closed, and the day Z equals A plus B.
- Upgrade from a DB with one open shift keeps it open and valid.
- Turning the setting off with two open shifts is refused.
Verification
- Extend
tests/cash-sessions.test.tsand the cash-closure tests, and runtests/upgrade-path.test.ts. npm run lint && npm test
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:backendBackend (Express/SQLite)Backend (Express/SQLite)area:frontendFrontend (React/Next.js)Frontend (React/Next.js)enhancementNew feature or requestNew feature or requeststate:readyScoped and ready for implementationScoped and ready for implementationtype:featureNew capability or enhancement requestNew capability or enhancement request
Activity