Cashup: preserve orphaned card machine data when settings name is removed

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>
This commit is contained in:
jtricerolph 2026-07-07 11:38:12 +00:00
parent dcb450bd99
commit 8b2dc4ad78

View file

@ -92,7 +92,14 @@ export function DailyCashUp({ user }: Props) {
setFloat(rebuild('float')) setFloat(rebuild('float'))
if (data.card_machines.length) { if (data.card_machines.length) {
setMachines(machineNamesRef.current.map(name => { const settingsNames = machineNamesRef.current
// Any DB machine whose name is no longer in settings — keep it visible and in
// the save payload so its data isn't silently deleted when the record is re-saved.
const orphanedNames = data.card_machines
.map(m => m.machine_name)
.filter(n => !settingsNames.includes(n))
const allNames = [...settingsNames, ...orphanedNames]
setMachines(allNames.map(name => {
const m = data.card_machines.find(c => c.machine_name === name) const m = data.card_machines.find(c => c.machine_name === name)
if (!m) return { machine_name: name, total_amount: 0, amex_amount: 0, visa_mc_amount: 0 } if (!m) return { machine_name: name, total_amount: 0, amex_amount: 0, visa_mc_amount: 0 }
return { return {
@ -398,21 +405,33 @@ export function DailyCashUp({ user }: Props) {
<span style={{ fontWeight: 700, fontSize: '1.1rem' }}>Total: {fmtGBP(totalPdq)}</span> <span style={{ fontWeight: 700, fontSize: '1.1rem' }}>Total: {fmtGBP(totalPdq)}</span>
</div> </div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}> <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
{machines.map((m, i) => ( {machines.map((m, i) => {
const isOrphaned = !machineNamesRef.current.includes(m.machine_name)
const inputDisabled = isFinal || isOrphaned
return (
<div key={m.machine_name} style={{ <div key={m.machine_name} style={{
display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem',
border: '1px solid var(--card-border)', borderRadius: '8px', padding: '1rem', border: `1px solid ${isOrphaned ? '#fca5a5' : 'var(--card-border)'}`,
borderRadius: '8px', padding: '1rem',
background: isOrphaned ? '#fff8f8' : undefined,
}}> }}>
{/* Left: amount inputs */} {/* Left: amount inputs */}
<div> <div>
<h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '0.75rem', color: 'var(--text-mid)' }}> <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.75rem' }}>
{m.machine_name} <h3 style={{ fontSize: '0.875rem', fontWeight: 600, color: 'var(--text-mid)' }}>
</h3> {m.machine_name}
</h3>
{isOrphaned && (
<span style={{ fontSize: '0.65rem', fontWeight: 700, color: '#b91c1c', background: '#fee2e2', padding: '0.1rem 0.4rem', borderRadius: '3px', whiteSpace: 'nowrap' }}>
Removed from settings
</span>
)}
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}> <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
<MoneyInput label="Total" value={m.total_amount} <MoneyInput label="Total" value={m.total_amount}
onChange={v => updateMachine(i, 'total_amount', v)} disabled={isFinal} /> onChange={v => updateMachine(i, 'total_amount', v)} disabled={inputDisabled} />
<MoneyInput label="Amex" value={m.amex_amount} <MoneyInput label="Amex" value={m.amex_amount}
onChange={v => updateMachine(i, 'amex_amount', v)} disabled={isFinal} /> onChange={v => updateMachine(i, 'amex_amount', v)} disabled={inputDisabled} />
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.875rem', paddingTop: '0.25rem' }}> <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.875rem', paddingTop: '0.25rem' }}>
<span style={{ color: 'var(--text-mid)' }}>Visa / MC</span> <span style={{ color: 'var(--text-mid)' }}>Visa / MC</span>
<span style={{ fontWeight: 600 }}>{fmtGBP(m.visa_mc_amount)}</span> <span style={{ fontWeight: 600 }}>{fmtGBP(m.visa_mc_amount)}</span>
@ -441,7 +460,8 @@ export function DailyCashUp({ user }: Props) {
)} )}
</div> </div>
</div> </div>
))} )
})}
</div> </div>
</Card> </Card>