applyLoaded now appends any DB machines whose name is no longer in the
current settings list, rather than silently dropping them. This prevents
their amounts being deleted from the database the next time the record
is saved (backend does DELETE + re-insert on every save).
Orphaned machines render with a red 'Removed from settings' badge, a
pink border, and read-only inputs so the data is visible but can't be
accidentally edited. They are still included in totalPdq and the save
payload, so nothing is lost.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Settings: add Card Machines section to configure number of PDQ terminals
and their names (1–6 machines, defaults to Front Desk + Restaurant / Bar).
Names are stored under the 'card_machines' settings key.
DailyCashUp: machine names now load from settings instead of hardcoded
const. Card Machines section changed from a 2-column grid to a vertical
list; each machine row shows the amount inputs on the left and its PDQ
Z-report photo uploader on the right. Separate PDQ Z-Reports card removed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mirrors the float count variance pattern — once Newbook data is loaded,
shows the reported cash amount and the counted vs reported variance
directly beneath the Cash Takings header.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Issue #1: Parse card machine amounts as floats in applyLoaded — pg returns
DECIMAL as strings which caused JS string concatenation in totalPdq and
reconciliation banked amounts. After draft save, reload cashUp to get the
persisted ID so PDQ Z-report uploads work on first save.
Issue #2: Guard empty gl_code in sales breakdown name-matching — an empty
column code caused gName.includes('') to always be true, making placeholder
columns (like WET 5%) match the first available GL group (dry dept).
Issue #3: Move Cash Summary above Safe Count in sidebar nav. Extend
cash-summary endpoint with per-day breakdown. Rewrite CashSummary.tsx
with a daily cash takings table above the denomination summary.
Issue #4: Add @media print CSS — hide sidebar nav, force colour printing,
style disabled inputs as plain text, and keep tables/cards intact on paper.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fetches receipts from the most recent petty cash count and populates the
current form — for when the tin hasn't been replenished and slips carry over.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Was filtering out any denomination with target=0 before checking if it
was counted, so notes (default target £0) never appeared even when surplus.
Now: skip only shortfalls where no target is set; surpluses always show.
Also use denom.target from the saved record (backend already populates this)
instead of current-settings lookup.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Qty entered for coins = number of bags, so total = BAG_VALUES[denom] × qty
not denom × qty. Fixes totalCounted, row totals, change order counted column,
and save payload. Also shows £X/bag hint under coin denomination labels.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Change tin count: remove bags/loose split — single qty per denomination
- After save: show saved record inline with Print button (clean new-window
printout with denomination table, receipts, totals/variance, signature lines)
- Change tin print: bank exchange order table (surplus=to bank, shortfall=in exchange)
- Petty cash history: Cash | Receipts | Total | Variance columns
- Fix uploads path: /app/src/../../uploads resolved to /uploads (container root,
outside Docker volume). Changed to /app/src/../uploads = /app/uploads so files
persist across rebuilds on the named volume.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Change tin targets:
- Add £5/£10/£20/£50 note rows (qty in individual notes, not bags)
- Fix label: (= £40.00 @ £20.00 each) instead of (= £40.00 each)
- Add per-denomination Notes text field (stored as change_tin_notes setting)
- Table layout replaces 2-col grid for all 10 denominations
Float count form:
- Notes denominations show a single Qty input; coins keep Bags + Loose
- Change order correctly shows "notes" vs "bags" needed per denomination
Sales breakdown columns:
- Add placeholder column button (gl_code='', labelled PLACEHOLDER)
- Add delete (×) button on every column row
- Fix key={i} to support multiple placeholders with same empty gl_code
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix history nav: type underscore → hyphen for route match
- Change tin: Bags + Loose inputs per denomination with UK bag values
- Change tin target: per-denomination bag count in Settings
- Change order sheet: inline table showing bags to order vs counted
- db: bag_quantity column on float_denominations
- api.ts: put text fields before file in FormData so busboy doesn't drain file stream early
- db.js: additive migration for uploaded_by column on cash_count_attachments
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Auto-save: every 60s while editing, silently saves draft if form has data.
Status "Auto-saved at HH:MM" appears next to the Save Draft button briefly.
- Float Count moved above Cash Takings and defaults to open so staff count
the float first before counting takings.
- Reconciliation table gains a fifth "Card Total" column: PDQ Visa/MC and
PDQ Amex rows share a rowspan=2 cell showing the combined PDQ variance.
If the per-category rows are red but the combined is green it means only
the Visa/Amex split was mis-allocated, not an actual cash discrepancy
(labelled "split only"). Gateway rows similarly grouped (always auto 0).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Persist transaction breakdown checked state in cash_ups.checked_transactions
(JSONB column); saved on each draft/final save and restored on page load.
Re-fetching Newbook no longer resets the checked items.
- Add till_float_target setting; Float Count card shows target and variance
so staff can see at a glance whether the till balances.
- Compress images client-side (max 1600px, 85% JPEG) before upload to avoid
504 gateway timeouts from NPM when uploading large mobile photos.
- File input accepts image/* only (was image/*+PDF which confuses mobile pickers).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds three new granular capabilities:
- history: gates /history page and GET /api/cashup/history
- cash_summary: gates /summary page and GET /api/reports/cash-summary
- safe_count: gates /safe page and safe_cash float routes
Updates legacy-token fallback to include all seven non-settings caps.
Route guards and nav items updated to use the split caps.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DB:
- Add attachment_type ('pdq_z_report' | 'receipt_error' | 'other') and
label columns to cash_count_attachments; migrate existing rows via
ALTER TABLE IF NOT EXISTS (no data loss)
Backend:
- Upload route now reads attachment_type and label from multipart form
fields alongside the file, stores them in the DB
Frontend:
- PhotoUploader component: thumbnail grid, upload button (images + PDF),
spinner during upload, ✕ delete button on each photo, click opens in
new tab
- DailyCashUp: PDQ Z-Reports card (one section per machine — Front Desk /
Restaurant Bar) appears after Card Machines when a cash up exists
- DailyCashUp: Receipt Evidence card at bottom for any discrepancy photos;
stays editable even on final cash ups so receipts can be added later
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Variance = banked total - reported total across all payment categories.
Green badge = balanced, red = short (▼), amber = over (▲).
Days with no cash up show no variance (nothing was counted).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Switch from onClick to onMouseDown + e.preventDefault() so the
browser never starts its own text-selection on click or drag
- Add document-level mousemove/mouseup listeners for drag-to-select:
hold mouse button and sweep across cells to highlight a range
- Shift+click still extends the rectangle from the anchor cell
- userSelect: none on tables prevents any residual text highlighting
- Stats bar (Count/Sum/Avg) now appears after mouseup so it reads the
final selection, not mid-drag state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend: sales_breakdown column matching was always failing because
Newbook returns numeric gl_group_id but column settings use string codes
like 'ACCOMMODATION'. Added normalised name-fuzzy fallback matching and
a daily_gross_sales field that sums ALL earned revenue for the date,
bypassing column config entirely.
- Frontend: dayGrossSales() now uses daily_gross_sales from backend first
- Add floating Excel-style status bar (bottom-right): when cells are
selected it shows Count, Sum, and Average of the selected values;
disappears when ✕ clicked or selection cleared
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Reconciliation now has two side-by-side tables (BANKED left, REPORTED right)
with dates on rows and payment categories on columns — matching original orientation
- BANKED table: green bg for manual cells (cash/PDQ), orange for auto (gateway/BACS),
red/amber bg + ▼/▲ indicator when variance vs reported
- REPORTED table: orange background, Audit column showing daily gross sales
- Gross Sales sub-table below reconciliation
- Sales Breakdown: dates now on rows, GL categories on columns (was transposed)
with Total Net / VAT / Gross Total / Audit columns
- Occupancy: dates on rows, metric columns, hover tooltips with category breakdown
- Copy/Paste Format section: spreadsheet-ready 2-row-per-date tables
- Excel-like cell selection: click to select, Shift+click for range,
Ctrl+C copies selected cells as TSV to clipboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Occupancy table now calculates from raw Newbook data:
- Rooms occupied (from per-category occupancy API)
- Occ % (rooms occupied / total rooms from sites_data)
- Guests (adults + children + infants from bookings_data)
- Net accommodation revenue (from earned_revenue, ACC GL group)
- Avg net per room, REVPAR
- GGR (average guest rate from tariffs_quoted)
- Avg lead time (arriving bookings only)
Sales breakdown now shows Net, VAT, Gross as separate total columns.
Debtors/Creditors adds Period Close row.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- History: slice session_date to YYYY-MM-DD before Date parse (postgres
returns date columns as ISO timestamps in JSON)
- History: fix Edit/View navigate link passing full ISO string as date param
- Weekly report: add daily status row, reconciliation variance (banked vs
Newbook reported), sales with gross+net, debtors/creditors lazy-loaded
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Newbook payments now fetched automatically when a date loads (in
parallel with the cash up fetch), not just on button click
- Refresh button still available for manual re-fetch
- Transaction breakdown section below reconciliation table: three
collapsible groups (Reception Manual, Reception Gateway, Restaurant/Bar)
each showing individual transactions with click-to-tick checkboxes
for tracking down discrepancies — local state, clears on refresh
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PostgreSQL NUMERIC columns return as strings in node-postgres. denomTotal
was doing 0 + "40.00" + "30.00" → "040.0030.00", and parseFloat in
fmtGBP read only the first portion, giving a wrong total (£40.00 instead
of £78.80 etc).
Fix: coerce total_amount and value_entered to numbers in applyLoaded;
make denomTotal's reducer defensively use Number() so strings can't
silently break the sum again.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Cash summary: derive total_quantity from total_amount/denomination_value
so value_entered rows are counted (previously SUM(quantity) returned
NULL for those rows)
- Safe Count: moved safe_cash out of Float Management tabs into its own
/safe/* route with a dedicated sidebar entry (Vault icon)
- FloatManagement now only shows Petty Cash and Change Tin tabs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- On mount/date change: auto-fetch existing cash up
- Draft found → auto-load into editing state
- Finalised → auto-load into locked state (read-only + banner)
- Not found → show 'Start Cash Up' prompt
- Removed manual 'Load Existing' / 'New' buttons
- Submitting final transitions immediately to locked state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tab now moves down the Qty column first (£50→£20→...→1p), then down
the Value Override column, for both takings and float grids.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AuthGate was calling /cashup/api/auth/verify and /cashup/api/auth/login,
which hit the cashup backend (404). Should call /api/auth/* which NPM
routes to the central auth service, matching all other apps.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add vite-env.d.ts for import.meta.env type support
- Remove unused imports: ChevronRight, useCallback (x2), today
- Silence unused attachments state read (keep setter for future use)
- Fix FloatManagement changeTinTargets reducer: drop unused k, fix v type
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>