diff --git a/frontend/src/pages/DailyCashUp.tsx b/frontend/src/pages/DailyCashUp.tsx index f3aa284..8375b57 100644 --- a/frontend/src/pages/DailyCashUp.tsx +++ b/frontend/src/pages/DailyCashUp.tsx @@ -53,7 +53,9 @@ export function DailyCashUp({ user }: Props) { const [fetching, setFetching] = useState(false) const [saving, setSaving] = useState(false) const [msg, setMsg] = useState<{ text: string; ok: boolean } | null>(null) - const [showFloat, setShowFloat] = useState(false) + const [showFloat, setShowFloat] = useState(true) + const [autoSaveMsg, setAutoSaveMsg] = useState('') + const autoSaveFnRef = useRef<() => void>(() => {}) const isFinal = pageState === 'locked' @@ -98,6 +100,33 @@ export function DailyCashUp({ user }: Props) { setPageState(data.cash_up.status === 'final' ? 'locked' : 'editing') } + // Auto-save ref — updated every render so the interval always captures latest state + autoSaveFnRef.current = () => { + if (pageState !== 'editing' || saving) return + const allDenoms = [...takings, ...float].filter(d => d.total_amount > 0) + if (allDenoms.length === 0 && machines.every(m => m.total_amount === 0) && !notes) return + api.post('/cashup/save', { + session_date: date, status: 'draft', notes, checked_transactions: [...checkedItems], + denominations: allDenoms.map(d => ({ + count_type: d.count_type, type: d.denomination_type, value: d.denomination_value, + quantity: d.quantity, value_entered: d.value_entered, total_amount: d.total_amount, + })), + card_machines: machines.map(m => ({ + name: m.machine_name, total: m.total_amount, amex: m.amex_amount, visa_mc: m.visa_mc_amount, + })), + }).then(() => { + setAutoSaveMsg('Auto-saved at ' + new Date().toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' })) + setTimeout(() => setAutoSaveMsg(''), 5000) + }).catch(() => {}) + } + + // Run auto-save every 60 s while editing + useEffect(() => { + if (pageState !== 'editing') return + const interval = setInterval(() => autoSaveFnRef.current(), 60_000) + return () => clearInterval(interval) + }, [pageState]) + // Fetch till float target once on mount useEffect(() => { api.get<{ till_float_target?: string }>('/settings') @@ -230,6 +259,9 @@ export function DailyCashUp({ user }: Props) { const totalPdq = machines.reduce((s, m) => s + m.total_amount, 0) const floatCounted = denomTotal(float) const floatVariance = tillFloatTarget > 0 ? floatCounted - tillFloatTarget : null + // Combined PDQ variance: accounts for Visa/Amex split being misallocated between machines + const pdqCombinedReported = newbookTotals ? newbookTotals.manual_visa_mc + newbookTotals.manual_amex : 0 + const pdqCombinedVariance = totalPdq - pdqCombinedReported return (
| {h} | - ))} +Category | +Banked | +Reported | +Variance | +Card Total |
|---|---|---|---|---|---|
| {row.category} | {fmtGBP(row.banked_amount)} | {fmtGBP(row.reported_amount)} | -- {Math.abs(variance) < 0.01 ? '—' : (variance > 0 ? '+' : '') + fmtGBP(Math.abs(variance))} - | +{fmtV(v)} | + {!isGroupSecond && ( +
+ {fmtV(isPDQFirst ? pdqCombinedVariance : v)}
+ {isPDQFirst && Math.abs(combinedV) < 0.01 && Math.abs(v) >= 0.01 && (
+ split only
+ )}
+ |
+ )}