Cash Management - Shift and Drawer Reconciliation
Domain: Cash Management
Location: api/nextango/apps/cash_management/
Last Updated: 2026-07-09
Overview
The Cash Management app tracks physical cash handling at the register: who worked which terminal during which time window, and the physical cash counts taken during that window. It exists to support blind-count reconciliation and cash-handling audit requirements without depending on any specific regulatory vertical.
The app has no views, serializers, services, signals, or Celery tasks. It exposes two plain Django models and their admin registrations. No URL routes reference cash_management anywhere in the project (nextango/urls.py has no mount, root-router registration, or import for this app), so there is no HTTP API surface to document. Other domains read and write these models directly through the ORM.
Documentation Quick Links
| File | Description |
|---|---|
| models.md | CashierShift, DrawerCount |
Key Concepts
Shift as the anchor record
CashierShift records a time window during which one employee operates one register at one store. Every drawer count and every cash drawer event elsewhere in the platform ties back to a shift, either directly (DrawerCount.shift) or by nullable foreign key (transactions.CashDrawerEvent.shift). terminal_id is stored as a plain string rather than a foreign key to pos_gateway.TerminalSession, so this app does not import from or depend on pos_gateway.
Blind count workflow
DrawerCount records a physical cash count tied to a shift. For a closing count, expected_amount is left null until after submitted_amount is received: the counting employee submits what they physically counted before the system-expected total is computed or shown to them. discrepancy is the difference between the two once both are known.
Two-person verification
DrawerCount carries an optional witnessed_by employee and witnessed_at timestamp, supporting a second-person sign-off on a count. Both fields are nullable; the model does not require a witness.
Ownership and Sync
CashierShift and DrawerCount are plain django.db.models.Model subclasses. Neither inherits SanityIntegratedModel, neither carries a sanity_id field, and neither app has an outbound sync adapter. These are operational records born from register activity, not content authored in Sanity Studio, so Django owns them outright and no sync boundary applies.
Admin
cash_management/admin.py registers both models with the Django admin. CashierShiftAdmin lists shift_id, store, employee, terminal_id, status, started_at, and ended_at, filters by status and store, and searches shift_id, terminal_id, and employee__email. shift_id, created_at, and updated_at are read-only. DrawerCountAdmin lists shift, count_type, submitted_by, submitted_amount, expected_amount, discrepancy, and submitted_at, filters by count_type, and keeps submitted_at read-only.
Related Domains
- Transactions:
CashDrawerEvent.shiftis a nullable foreign key toCashierShift, andCashDrawerEvent.shiftIdstores the shift's string identifier for display - Payments: cash reconciliation reporting aggregates
TransactionPaymentrecords against each shift's store and time window - Compliance: the cannabis-vertical
CashReconciliationReportGeneratorreadsCashierShiftandDrawerCountto build a per-shift reconciliation report, one row per shift, joined againstTransactionPaymenttotals