Shift / POS Session
1. Overview
| Property | Value |
|---|---|
| ID | FEAT-SALE-SHIFT |
| Status | Stable |
| Owner | sale-team |
| Depends on | Device, SaleChannel, SaleOrder (linked via openedInSessionId/closedInSessionId) |
A PosSession represents a cashier's shift on a specific POS device. It opens with a cash float, attaches to all SaleOrder rows created during the shift, and closes with a reconciliation step (expected vs actual cash). When shift management is enabled, SaleOrderService blocks order creation if no open session exists for the device.
2. Entity Model
Field details — see Domain Model §3.7.
3. Lifecycle
| From | Event | To | Side effect |
|---|---|---|---|
OPEN | closeSession | CLOSED | compute cashDiscrepancy = actualCash − expectedCash; create PosSessionReport snapshot |
Invariant: at most one OPEN session per (merchantId, deviceId). Enforced by ShiftService.validateAndAttachSession and a partial unique index.
4. Operations
ShiftService (shift.service.ts — 943 lines).
| Method | Signature | Purpose |
|---|---|---|
getCurrentSession | { context, deviceId } | Get the OPEN session for a device, or null |
listSessions | { context, where, page, ... } | Filtered list (CRUD) |
openSession | { context, data: TOpenSessionRequest } | Validate no other open session; create OPEN with openingFloat |
closeSession | { context, id, data: TCloseSessionRequest } | Compute discrepancy; create PosSessionReport; OPEN → CLOSED. Tracks closeRecountCount for retries. |
isShiftManagementEnabled | { merchantId } | Per-merchant feature flag (read from Configuration) |
validateAndAttachSession | { deviceId, merchantId } | Used by SaleOrderService.createDraftOrder — blocks if shift required but no open session |
5. REST Endpoints
| Verb | Path | Auth | Permission | Handler |
|---|---|---|---|---|
POST | /pos/sessions | JWT/BASIC | Shift.openSession | openSession |
POST | /pos/sessions/:id/close | JWT/BASIC | Shift.closeSession | closeSession |
GET | /pos/sessions | JWT/BASIC | Shift.list | CRUD |
GET | /pos/sessions/current?deviceId=... | JWT/BASIC | Shift.getCurrent | getCurrentSession |
| 6× CRUD | /pos/sessions | JWT/BASIC | Shift.<crud> | CRUD |
Live OpenAPI:
/v1/api/sale/doc/openapi.json.
6. Events
Outbound: Sale does not currently emit shift-specific WebSocket or Kafka events. Sessions are observed via REST.
7. Reconciliation Flow
8. Related Pages
- Sale Order — orders attach to sessions
- Domain Model