Cash Management Domain - Models Documentation

Source: api/nextango/apps/cash_management/models.py Last Updated: 2026-07-09


Overview

The Cash Management models define the shift and drawer-count layer used for cash-handling audit and reconciliation. Both models are plain django.db.models.Model subclasses; neither syncs with Sanity.

ModelDB TablePurpose
CashierShiftcash_management_cashier_shiftA time window during which one employee operates one register at one store
DrawerCountcash_management_drawer_countA physical cash count event tied to a shift

CashierShift

File: models.py:7-81 DB table: cash_management_cashier_shift Inherits: django.db.models.Model

Anchors all cash events and drawer counts to a named employee-register-store-time tuple. terminal_id mirrors pos_gateway.TerminalSession.terminal_id as a plain string rather than a foreign key, so this app carries no dependency on pos_gateway.

Fields

FieldTypeConstraintsDescription
shift_idCharField(100)unique, indexed, not editableGenerated on first save: SHIFT-<YYYYMMDD>-<8-char hex>
storeForeignKey(stores.Store)PROTECT, related_name='cashier_shifts'Store the shift belongs to
employeeForeignKey(users.User)PROTECT, related_name='cashier_shifts'Employee operating the register during this shift
terminal_idCharField(100)indexedString identifier of the physical register, matches TerminalSession.terminal_id
statusCharField(20)choices, default=active, indexedShift lifecycle status, see below
started_atDateTimeFielddefault=nowWhen the shift began
ended_atDateTimeFieldnull, blankWhen the shift ended
opening_floatDecimalField(10,2)default=0.00Starting cash amount placed in the drawer
closing_submittedDecimalField(10,2)null, blankAmount the employee counted at close, populated by the blind-count workflow
closing_expectedDecimalField(10,2)null, blankSystem-computed expected close amount
discrepancyDecimalField(10,2)null, blankDifference between closing_submitted and closing_expected
reviewed_byForeignKey(users.User)SET_NULL, null, blank, related_name='reviewed_shifts'Manager who reviewed the shift close
reviewed_atDateTimeFieldnull, blankWhen the review occurred
notesTextFieldblankFree-text notes
created_atDateTimeFieldauto_now_addRecord creation timestamp
updated_atDateTimeFieldauto_nowRecord last-modified timestamp

Status Choices

ValueDisplay
activeActive
closingPending Close Review
closedClosed
abandonedAbandoned

Indexes

  • (terminal_id, status)
  • (store, started_at)
  • (employee, started_at)

Ordering

Default ordering: ['-started_at'] (most recent shift first)

shift_id generation

shift_id is assigned on first save if not already set, in the form SHIFT-<YYYYMMDD>-<8-character uppercase hex>. The date segment reflects the save time, and the hex segment is derived from a UUID4.

__str__

Returns "Shift <shift_id>" followed by the employee and the store name.


DrawerCount

File: models.py:84-133 DB table: cash_management_drawer_count Inherits: django.db.models.Model

Records a physical cash count event tied to a CashierShift.

Fields

FieldTypeConstraintsDescription
shiftForeignKey(CashierShift)PROTECT, related_name='drawer_counts'Shift this count belongs to
count_typeCharField(20)choicesWhich point in the shift this count represents, see below
submitted_byForeignKey(users.User)PROTECT, related_name='submitted_drawer_counts'Employee who submitted the count
submitted_amountDecimalField(10,2)requiredPhysical cash amount counted
cash_breakdownJSONFielddefault={}Denomination-level breakdown of the counted cash
expected_amountDecimalField(10,2)null, blankSystem-expected amount. For a closing count, this is left null until after submitted_amount is received, enforcing a blind count
discrepancyDecimalField(10,2)null, blankDifference between submitted_amount and expected_amount once both are known
witnessed_byForeignKey(users.User)SET_NULL, null, blank, related_name='witnessed_drawer_counts'Second employee who witnessed the count, if any
witnessed_atDateTimeFieldnull, blankWhen the witness signed off
submitted_atDateTimeFieldauto_now_addWhen the count was submitted

Count Type Choices

ValueDisplay
openOpening Count
mid_shiftMid-Shift Count
closeClosing Count

Ordering

Default ordering: ['-submitted_at'] (most recent count first)

__str__

Returns the count type display, the shift's shift_id, and the submitted dollar amount.


Model Relationships

CashierShift
  └─ drawer_counts (reverse FK) → DrawerCount (many)

CashierShift
  └─ cash_drawer_events (reverse FK) → transactions.CashDrawerEvent (many, nullable FK)

Cascade rules:

  • Deleting a CashierShift is blocked (PROTECT) if it has DrawerCount records
  • Deleting a Store or User referenced as CashierShift.store or CashierShift.employee is blocked (PROTECT) while shifts reference it
  • Deleting the User referenced as CashierShift.reviewed_by or DrawerCount.witnessed_by sets that field to null (SET_NULL)
  • Deleting a CashierShift referenced by transactions.CashDrawerEvent.shift sets that field to null (SET_NULL), the event record is not deleted

Was this page helpful?