Skip to content

feat(cash): opt-in one cash shift per staff member #1339

Open

Description

@khaira777

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 409 session_already_open, asserted in tests/cash-sessions.test.ts.
  • cash_sessions already has opened_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_shift is on, otherwise NO_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_open with 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.ts and the cash-closure tests, and run tests/upgrade-path.test.ts.
  • npm run lint && npm test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:backendBackend (Express/SQLite)area:frontendFrontend (React/Next.js)enhancementNew feature or requeststate:readyScoped and ready for implementationtype:featureNew capability or enhancement request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions