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.
Unfilled-shift alerts show the current job-function name, such as Cashier. If that name is missing or temporarily unavailable, they show Job function instead of an internal role identifier. The staffing requirement, coverage counts and dated block link are unchanged.
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.
A stale or multi-day punch can clock out using its original clock-in and the actual current clock-out time; midnight does not reset or truncate it. Cash reconciliation is separate: every drawer opened for that punch must have its own subsequent shift-close count, even if it belongs to another terminal. A clock-out cash blocker identifies the drawer, terminal, store and shift. Ask a manager to record the real count and close that specific drawer for that shift, then make a new clock-out attempt. Do not reopen an already-counted drawer or change attendance timestamps to bypass another drawer's missing count.
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
Cash management is included in every business vertical and plan, including Liquor Store and Peak Terminal. Android drawer access follows the cash module; it does not require appointment or service-resource scheduling. Your role and the selected store's drawer configuration still determine which actions are available.
In the portal, open Reports > 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.
Cash management requires an active drawer assigned to the terminal. When a signed-in terminal operator with cash read and write access opens More (which prepares cash state), Cash Management, or the open-register flow, device drawer discovery automatically creates its default drawer if none exists. Repeated or concurrent discovery returns the same drawer; merchants do not need to register it in the portal first. Read-only operators and portal balance/history reads never create drawers. This is a cash-ledger assignment, separate from whether an external physical drawer is connected.
Unavailable controls explain the missing drawer, completed POS count, or recovery check. Reload command status refreshes recorded activity without creating a drawer or executing a cash request. Command history shows requests made by your account for the selected drawer, not all POS activity. Related Tools links to Employees → Team and Employees → Schedule appear only when your role and plan allow those destinations.
Manual registration remains available to authorized cash writers through POST /api/v1/orgs/{orgId}/stores/{storeId}/cash/drawers, with a JSON body containing terminalId. overrideThreshold is optional and defaults to 100. Registering the same terminal again returns its existing active drawer without resetting its threshold or history.
Android cashier flow
Open More → Cash management. Start the day by counting the cash already present, then record a Pay in or Pay out with a positive amount and a required reason. Expected cash is the ledger balance, including the starting count, cash payments, refunds, and recorded cash movements. A negative expected amount means more cash has gone out than came in; for example, a cash refund after a zero starting count can make the expected balance negative. Count only the cash actually present.
Cash refunds have a separate physical handoff step. Submitting a refund reserves its amount but does not mean cash has been returned. The original sale shows Refund needs completion → Resume refund, and reopening its refund form restores the pending task even when no refundable balance remains. Cash refunds to finish in Android Cash Management lists unfinished cash refunds for this terminal; Open sale / resume refund returns to the original request without creating another refund.
Request cash handoff permission before paying the customer. A fresh server grant permits that one handoff; then confirm Cash handed to customer. If permission was already admitted or its response was lost, the resumed task is record-only: do not pay again. Record an existing handoff only if cash was actually returned; if not or unsure, ask a manager to review. Loading the queue, reopening a sale, or refreshing status never grants permission or records a drawer movement. The confirmed request keeps its original identity so retries cannot debit the drawer twice.
Android cash controls display the blocking condition instead of silently ignoring a cash action: operator sign-in or switch, permission refresh, terminal/store assignment, store cash capability, cash permissions, or the active drawer shift. Go to Shifts lets you clock in or reopen cash actions for the current operator. Newly discovered or automatically registered drawers trigger a server shift-policy refresh before relying on a previously loaded no-drawer decision.
The cash screen keeps your operator, clock status, till status and available expected balance visible. Use Open till → Pay In / Out / Drop → Review → Confirm → recorded result → Count / Close. Review does not yet record cash; the confirmation shows the amount, reason and whether a manager must approve. Drop to Safe removes cash from the till. The recorded result identifies the movement, operator and any approving manager; Done acknowledges it without moving cash again.
Till status follows the terminal's current cash session, even when it belongs to an earlier employee punch. Handhelds without drawer hardware still track a virtual till; opening, reviewing and counting do not require a drawer kick. A $0 opening count is valid for a closed till. Before reviewing an opening, Android refreshes the live till state; if it is already open, use the existing Pay In, Pay Out, Cash Drop, or Count & Close controls instead of opening it again. Existing cash permissions and manager approvals still apply; if another cashier opened the till, ask them or a manager to close it. This does not transfer ownership of their cash session.
Cash steps explains the separate lifecycle actions: Sign out ends only the POS login session; Clock out ends actual work time; Cash close records the count and report; processor batch close settles card transactions separately. Neither signing out nor closing cash should be treated as a card batch close.
Every cash-in, pay-out, safe-drop, or correction requires a Reason of 1–255 characters. Submit the form to validate it; a missing or overlong reason is shown under that field without sending a cash command. Correct the field and submit explicitly. Known server reason-validation failures are also displayed specifically, rather than only as a generic API error.
At End of day, count the cash, review Expected cash, Counted cash, and the Difference, then close cash. Manager approval requirements still come from the server. Closing cash does not clock out an employee unless the flow explicitly says it will do both. Terminal drawer assignment is automatic; cashiers do not choose a drawer in the normal single-terminal flow.
Android prepares cash state when opening More. The count form appears while the latest balance and recovery status load; a cached expected amount is explicitly marked Last-known expected cash — refreshing… and belongs only to the current store, terminal, operator, and shift. Review count stays unavailable until fresh cash state is ready. Denominations load as you scroll, but counts already entered remain included in the total even when off screen.
For a drawer already discovered in that same scope, balance, drawer discovery, and recovery-status reads overlap. The balance is accepted only if discovery still selects the same active drawer; a reassigned or removed drawer discards that speculative result. More also prepares the count-screen composition without showing it or enabling any cash action. Entering the screen still refreshes recovery and balance before enabling Review; leaving it discards the count draft. Navigation does not repeat unrelated Activity setup.
The cash-refund queue refreshes again when entering the prepared screen, so tasks added or completed while More was open are not left at their prefetched state. A recovered recorded command can have completed after the parallel balance read. Android therefore revalidates the selected drawer's phase and balance after resolving any recorded opening, closing, or movement before enabling cash actions. Unavailable revalidation keeps those actions disabled.
After a successful cash close, Android shows the End of Day report with Print End of Day report using the configured receipt printer. Review it before Finish and clock out when closing cash as part of clock-out. Printing is optional and never repeats a cash operation. Reopen your completed sessions later from Shifts → Shift history / End of Day reports, including after clock-out; Older and Newer browse bounded command-history pages.
When a terminal has replacement drawers, history starts with its active drawer and offers Previous drawer / Next drawer to reopen earlier drawer sessions; each drawer has its own Older/Newer command pages. Cash sales normalize legacy cash received minus change versus applied cash using the sale's durable checkout-ticket evidence.
The report is frozen in the successful server command, in the store currency and store timezone: opening float, posted cash sales/refunds, card totals by tender, gift-card totals, sale/refund/void counts, net tracked tips, Pay In/Out, expected/count/over-short, operator IDs, drawer/terminal/shift identifiers and opening/closing timestamps. Other cash / recount adjustments reconciles posted cash activity with authoritative cash returns, compensation and audit recounts. Counts and tender totals describe activity posted at close; pending/failed payments are excluded. Later posting or manager reconciliation never silently rewrites this original close-time report. Sessions closed before report support explicitly show report unavailable for older session. Server store cash-read authorization and authenticated operator/terminal command ownership apply to history and report reads.
Uncertain cash requests update in the background using their original request identity. Cashiers do not retry a new movement to clear an updating state and can leave cash management to continue selling.
Cash movement forms start with no type selected. Explicitly choose the operation; Cash in options and Cash out options are muted grouping headings, not selections. The chosen operation remains visible beside the amount and confirmation, such as Petty Cash In $1.00. Submitting without a choice shows Choose a type and sends no command. After acknowledging a recorded movement, choose the type again for the next movement.
Review drawers and variance
On Android, the closing-count denomination list scrolls within the available screen height. Review expected cash at the top, then scroll to reach smaller denominations and the count confirmation.
- 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.