Shifts and Cash Management
The Employees section is scoped to the store selected in the portal. Its pages are Overview, Team, Schedule, and Timesheets. Change the selected store before reviewing schedules or today's arrivals; records from one store are not combined with another store's records.
The pages and actions you see depend on your role. Read access opens the relevant page, while write or administrator access enables the actions described below.
Schedule
Open Employees > Schedule. The default view is an employee-by-day grid for a Monday–Sunday week, shown in the store's timezone. Readers see the schedule; scheduling managers can change it.
- Click an employee's day and choose Add Shift or Add Time Off. Alternatively, Add Shift enters assignment mode: choose a cell, or press Escape to exit.
- In Add Shift, choose Custom Hours or a Shift Template, then set start/end time, planned break and notes. Template choices show their names and working hours, including when they finish the following day. Templates are reusable working-hours presets, not staffing requirements. Save, edit and delete presets inside the dialog; changing one shift does not change its template.
- For recurring work, select weekdays and an end rule: Never, On Date (inclusive), or After N Weeks. An end time earlier than its start time finishes the following day.
- Open a shift to Edit, Copy, or Delete it. Copy requires an explicit destination employee and date and does not repeat unless requested. Recurring edits/deletions offer This shift or This and following. Following edits split the future schedule into a new series; refresh after changes.
- Booked or punched shifts retain their concrete records. Conflicts explain when a saved booking/punch no longer matches the future schedule. Attached shifts cannot be silently removed or moved in a way that invalidates their records.
- Time off supports Full Day, Custom Hours, and Multiple Days, with optional reason and notes. Custom-hours blocks and details show dates and hours in the current store timezone, including after a store-timezone change, and mark overnight intervals as finishing the following day. Full Day and Multiple Days details preserve their saved calendar dates. Approved time off blocks booking availability. Scheduling over it requires an authorized manager to explicitly provide an override reason; an override does not restore booking availability.
Approved time off remains visible and editable after an employee leaves the current store roster, but that historical row cannot receive a new assignment. If an authorized name and email are unavailable, the row, time-off details and editor identify the employee by their stable user ID. Missing directory details do not grant a job function or assignment eligibility. Overnight shift warnings cover the shift's full window, including approved leave beyond the selected week's boundary.
Closing a scheduling dialog returns keyboard focus to its originating control. After a saved change refreshes the grid, focus returns to the employee's day; if that day is no longer actionable, focus moves to Refresh schedule.
Use Requests within Schedule for availability, time-off and change requests. Employees retain their own submission/edit/withdrawal controls; managers approve or reject the current request version. Availability uses your current store-assigned job functions, even without coverage blocks or upcoming shifts: one assignment is selected automatically, while multiple assignments require your choice. If none are assigned, ask a manager to assign one; time-off requests remain available. A schedule-change request keeps the selected shift's job function, not the availability selector's choice. Approved leave becomes the same time-off block shown on the grid.
If job-function discovery is temporarily unavailable, existing requests and exact request links remain visible. The error banner offers Refresh; availability submission stays disabled until current assignments can be loaded.
Changing the selected store or organization clears unsaved request selections and notes. Start a new request in the new store; availability painted for the previous store is not carried over.
Use Staffing & Coverage within Schedule to maintain required job functions, hours and headcounts. Each dated block shows Required, Scheduled, and Needed; scheduled counts include distinct employees covering the full block after approved time off is excluded. Date-specific fill links return to the relevant week and coverage block. Coverage requirements remain distinct from Shift Templates.
Overview shows active employees, current punches, pending requests and weekly scheduled hours after planned breaks. Its Needs Attention items link to the specific request, late shift or staffing gap. Team shows store-scoped contacts and profile controls. Job-function colors are configured in Staff & Access and reused across the schedule, Overview, Team and actual-based Timesheets; colors never replace status text.
Refresh updates Overview to the current store-local week after a Monday rollover. Readers without coverage-management access open the affected week from an unfilled-shift alert instead of the coverage editor. Timesheets clears the previous snapshot when its store or date range changes; if loading fails, previous employees' worked hours are not shown under the new selection. Its individual punch records show actual clock-in/out timestamps in the store's timezone, including legacy punch values when canonical values are missing, alongside the existing worked-hours summaries and CSV export.
On Android POS, recurring occurrences are shown in store-local time with planned breaks and a recurring indicator. The assigned-list date range follows the store's calendar day, even when the device is in another timezone. Clocking in or booking against a virtual occurrence attaches its concrete record; the app refreshes afterward. Stale/deleted selections require a refresh, not a fabricated assignment. Actual punches and cash-drawer linkage remain concrete records.
Public booking and rescheduling keep the exact selected recurring occurrence, even before it has a concrete assignment. Booking attaches that occurrence; browsing available slots does not create assignments.
Timeclock and retry safety
The server selects the eligible scheduled occurrence for a clock-in when no occurrence is explicitly requested. Android does not decide attendance eligibility from the device clock or a local four-hour window. Active-punch, schedule-window and break-duration validation remain server decisions.
Clock-in and clock-out requests require a fresh UUID attemptId for each new operator attempt. Clock-out also identifies the original actualId and exact breakMinutes. Clients save this immutable request before delivery and retain it across interruption or restart. If delivery is uncertain, retry the same request and key—not a new clock action. A changed request or operation using an existing key is rejected. The punch and its receipt commit together; replay returns the original receipt even if that punch has since closed or another punch is active. Read-only admission validation/conflict rejections also commit terminal server receipts for the original key and request before returning the error. They can never become successful punches on later replay; failures during mutation or projection instead roll back and are not recorded as known rejections.
GET /api/v1/stores/{storeId}/shifts/me/punch-attempts/{attemptId} reads the receipt for the authenticated employee and selected store. It is an exact lookup, not a paginated attendance search. After a replay, clients refresh current status before showing current punch or drawer actions; historical receipts are not current availability.
Android recommits each immutable saved command successfully before every delivery, including restored requests; an in-memory SharedPreferences value left by a failed disk commit cannot authorize dispatch. A definitive validation/conflict rejection attempts local cleanup and refreshes current punch status, without reporting clock success, resetting breaks, or opening a drawer. Failed local cleanup does not replace the original API rejection or disable that refresh: a request restored from disk safely replays its terminal server rejection even after another punch closes. Unknown network/transient outcomes retain the same request/key. Rejection messages remain visible if status refresh also fails.
Current status returns server-calculated durationMinutes and overtimeMinutes. Android displays those values without subtracting locally pending breaks or applying a local eight-hour payroll threshold. The server projection uses store-local days, the configured daily regular allowance, prior punches for the same employee, and paid/unpaid break policy.
The retail portal currently exposes punch transport wrappers but has no live clock-in/clock-out UI or callers; its Employees pages read punches rather than initiating them. Those wrappers require caller-owned attempt identities, preserve the original clock-out reference, and expose exact receipt lookup without selecting a schedule locally. The simulator's real shift scenario likewise retains the same clock-in/clock-out identities and original actual punch reference after a lost response, including day rollover; only a completed shift starts a new daily identity.
Service Reporting
Open Shifts > Service Reporting after selecting a store. This page is read-only and summarizes the period from 30 days before today through 30 days after today.
Review the summary cards for:
- Active Services
- Open Bookings
- Completed
- Booked Value
- Staff
- Booking Sites
The Service Performance table breaks each service down by Channel, Duration, Completed, Upcoming, No-show, and Booked Value. The booking-site card also reports shift conflicts. If this page is not available, your role must have all three required read capabilities: appointments, service catalog, and service resources.
Check-ins
Open Shifts > Check-ins to operate today's front-desk arrival board for the selected store. Appointment read access is enough to view the board. Check-in write or check-in administrator access enables status changes.
- Confirm that the heading shows Today's arrivals for the correct store.
- Use Search by customer name or phone to find a booking. Email text is also matched when available.
- In Awaiting check-in, select Check in when a confirmed guest arrives, or No-show when appropriate.
- In Checked in, select Complete when service is finished, or No-show if the booking must be recorded that way.
- Select Refresh to reload the board.
Only confirmed appointments for the store's current calendar day appear under Awaiting check-in. Only appointments already marked checked in appear under Checked in. Booking edits remain in Appointments, and staff-coverage conflicts are reviewed in Employees → Schedule → Staffing & Coverage.
Cash Management
Open Shifts > Cash Management to review cash activity for the selected store. Cash read access is required to load drawer balances and history, even when a role has a separate cash write or administrator capability.
Review drawers and variance
- Review each active drawer card for Expected balance, Override threshold, Last snapshot, and Last variance.
- Open a row in Shift-close variance history to see the close time, coverage, cashier, expected amount, variance, notes, and recorded movement log.
- Use Cashier variance trend to compare rolling close variance by cashier. An Outlier badge identifies a pattern that needs review.
- In Closeouts, deposits, and exceptions, filter by All statuses, Open, Investigating, or Resolved. Use Next and Previous to browse closeouts in creation order. Newer closeouts do not shift the next page; changing the store or status filter, or recording cash activity, restarts browsing from the newest closeouts. Exception statuses are reconciled live, not frozen for the browsing session.
- Select View history for the attributed exception and reconciliation timeline. Cash readers can also select Export CSV.
The cash-closeout history API accepts size (1–100, default 50) and an optional opaque cursor, and returns data.items plus data.nextCursor. Continue with the returned cursor until nextCursor is absent or null, including when the final page is full. Reuse a cursor only with the same store, drawer, shift, status, and date filters; malformed or mismatched cursors are rejected. Page-number and offset parameters are not supported. CSV export still includes the complete matching history.
Record routine cash activity
Cash read plus cash write access enables Record cash movement. Choose the drawer, then select one of the available operations:
- Opening float
- Safe drop
- Petty cash out
- Petty cash in
- Vendor payment
Enter the amount and Reason or reference, then select Record movement. The movement is attributed to your signed-in account and the drawer's current open shift. Manager deposit is available only when the same role also has cash administrator access.
Cash opening, closing counts, recounts, and movements are durable server commands. The server resolves the drawer's active shift and the cash-in/cash-out sign, calculates approval requirements, and binds approval to the exact command, value, and currency. Enter movement amounts as positive amounts; the server derives direction from the selected movement type. Corrections require an explicit direction, and contradictory directions are rejected. Manager deposits always require cash administrator permission, even below the drawer threshold. Portal operations requiring cash administrator permission are rejected before saving a command when the signed-in account lacks that permission.
Preflight saves a Pending command before manager approval. Refreshing or reopening the page discovers that command instead of automatically replaying a locally saved request. Recorded means the command and its ledger records committed together; Rejected means the accepted command definitively failed validation without committing cash effects. A timeout or unavailable status read is Unknown, not a rejection. Check the existing command by its operation key before starting another explicit attempt. Approval waiting and command status are separate from a closeout's Open, Investigating, or Resolved exception status.
Pending closing commands also freeze the exact server-selected opening snapshot in the optional response field openingSnapshotId; clients do not choose that boundary. Closing and reopening the same punch invalidates the earlier pending close even when the balance is unchanged. Execution rejects it before consuming approval or writing cash effects, retaining its original command identity. Older pending closes without opening proof are likewise rejected; start a new explicit closing attempt against the current opening.
For API integrations, use POST /cash/drawers/{drawerId}/commands/preflight, then explicitly execute the saved immutable intent with POST /cash/drawers/{drawerId}/commands/{operationKey}/execute. Recover the next owned pending/unknown command or unacknowledged movement receipt using GET /cash/commands/recovery; it preserves the original drawer and operation key even after reassignment. A successful nullable response means there is no recovery item; a failed read never means no prior attempt. Use the original drawer's commands/by-operation-key/{operationKey} read for a known attempt. Command history is separate and bounded by limit (1–100) and offset, not a lifetime scan. These merchant routes retain the normal organization/store prefix. Devices use verified terminal routes under /v1/stores/{storeId}/cash; terminal and current operator identities come from physical authentication and signed terminal claims, not client body IDs. Approval uses the exact action, protected drawer, approval idempotency key, amount, and currency returned by preflight. Cash effects are database-local and atomic; no background replay worker is needed.
An operator may explicitly cancel a pending attempt with POST /cash/drawers/{drawerId}/commands/{operationKey}/cancel. Cancellation records Rejected with code CASH_COMMAND_CANCELLED without consuming approval or changing money. If execution already won the race, cancellation returns the actual recorded receipt instead. Keep the original identity until a definitive response is received.
Recorded movement receipts remain discoverable with acknowledgementRequired=true across restart until the operator has viewed the receipt and explicitly selects Done or New action. That action calls POST /cash/drawers/{drawerId}/commands/{operationKey}/acknowledge. Acknowledgement is durable and idempotent: it changes only receipt metadata, never the original monetary result. It must not happen automatically when a wire response arrives or before the receipt is rendered. Opening and closing phases continue to use server phase/snapshot fields rather than movement acknowledgements.
Portal command intents may omit terminalId; the server resolves the selected drawer's current assignment and saves it as the immutable command target. An explicitly supplied terminal must match that assignment. Terminal-device requests remain bound to their authenticated terminal claims. Retrying the same raw portal intent returns the original command target, even if the drawer was subsequently reassigned.
Once a command is discovered, resume it through its original drawer and operation key after a fresh status read; do not send its server-filled intent back to preflight. Preflight replay validates the original submitted body, not the canonical response. Unknown or approval-required status cannot be bypassed by resuming.
Variance reads expose an explicit shiftOpen phase flag for the current matching drawer boundary; counted indicates closing-count evidence, not whether the drawer is open. The required nullable currentClosingSnapshotId identifies the current matching closing boundary, or is null when no such boundary is available. A previously recorded close command may be restored only when its snapshot ID matches this server field and shiftOpen is false. Reopening a drawer under the same punch requires a new closing count before clock-out or manager closeout. The latest matching close must be strictly later than the latest opening; an older close or equal-timestamp ambiguity cannot satisfy that requirement or appear as a closeout candidate.
Complete manager review
Cash read plus cash administrator access enables Manager closeout and Manage exception:
- In Manager closeout, choose a server-provided completed drawer count, then optionally enter Deposit amount and Deposit reference. The server supplies eligible closeout candidates rather than asking the browser to infer them from history. A candidate's terminal identifies the historical assignment at the time of its count, not the drawer's current assignment; missing historical assignment remains unknown.
- Each manager closeout belongs to its original closing count and drawer cycle, not every cycle of the same active shift. After reopening and recording a fresh closing count, that new cycle can have its own closeout and deposit. Reconciliation of an earlier closeout retains its original opening/closing evidence and counted amount; it never substitutes a later reopened cycle.
- Closeout requests must send the selected candidate's immutable
snapshotIdas requiredclosingSnapshotId. The server validates that reference under the drawer lock before closeout or deposit effects. A form opened before the same punch is reopened cannot silently target a later count, even when both counts have the same amount. Keep the original reference and operation key for retries; explicitly select the refreshed candidate to start a new attempt. - Older receipts whose original cycle evidence cannot be established remain readable in history and export with their stored amounts and status.
reconciliationReviewReason: ORIGINAL_CYCLE_EVIDENCE_UNAVAILABLEmeans reconciliation is unknown and needs review, not a zero balance or a reconstructed later count. Investigation remains available where the stored exception status permits it, but resolution is blocked until the original evidence is established. Reopening does not authorize another deposit against the older count. - In Manage exception, use the available server-provided transitions, assign an owner, add investigation notes, and provide a disposition when resolving it. Select Save transition. Every transition remains attributed in Cash closeout history, and stale versions or invalid transitions are rejected by the server.
Actions that change the audit record require narrower combinations of access:
- Audit recount and Record cash adjustment require cash read, write, and administrator access.
- Reassign drawer requires cash read and administrator access plus terminal read access.
- Manager closeout and exception management require cash read and administrator access; cash write access is not required for these two actions.
Use these controls only for real merchant drawer activity. Store staff ownership remains in Employees → Team, and shift coverage remains in Employees → Schedule → Staffing & Coverage.