Save transaction checks, till float variance, fix image upload

- 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>
This commit is contained in:
jtricerolph 2026-07-02 14:46:52 +00:00
parent 4754231f6f
commit fb66dcd6bf
6 changed files with 63 additions and 10 deletions

View file

@ -126,6 +126,7 @@ export async function initDb() {
);
ALTER TABLE cash_count_attachments ADD COLUMN IF NOT EXISTS attachment_type TEXT NOT NULL DEFAULT 'other';
ALTER TABLE cash_count_attachments ADD COLUMN IF NOT EXISTS label TEXT;
ALTER TABLE cash_ups ADD COLUMN IF NOT EXISTS checked_transactions JSONB NOT NULL DEFAULT '[]'::jsonb;
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
@ -138,6 +139,7 @@ export async function initDb() {
INSERT INTO settings (key, value) VALUES
('default_report_days', '7'),
('petty_cash_float', '200.00'),
('till_float_target', '0.00'),
('sales_breakdown_columns', '[]'),
('change_tin_breakdown', '{"50.00":0,"20.00":0,"10.00":0,"5.00":0,"2.00":20,"1.00":20,"0.50":10,"0.20":10,"0.10":5,"0.05":5}')
ON CONFLICT (key) DO NOTHING

View file

@ -33,7 +33,7 @@ export async function cashupRoutes(app) {
// POST /api/cashup/save
app.post('/api/cashup/save', async (req, reply) => {
const { session_date, status, notes, denominations = [], card_machines = [] } = req.body
const { session_date, status, notes, denominations = [], card_machines = [], checked_transactions = [] } = req.body
if (!session_date) return reply.status(400).send({ error: 'session_date required' })
if (!['draft', 'final'].includes(status)) return reply.status(400).send({ error: 'invalid status' })
@ -63,21 +63,22 @@ export async function cashupRoutes(app) {
status, totalFloat, totalCash, notes || null,
status === 'final' ? new Date() : null,
status === 'final' ? req.user.email : null,
new Date(), cashUpId,
new Date(), JSON.stringify(checked_transactions), cashUpId,
]
await pool.query(
`UPDATE cash_ups SET status=$1, total_float_counted=$2, total_cash_counted=$3, notes=$4,
submitted_at=$5, submitted_by=$6, updated_at=$7 WHERE id=$8`,
submitted_at=$5, submitted_by=$6, updated_at=$7, checked_transactions=$8 WHERE id=$9`,
updateData
)
} else {
const ins = await pool.query(
`INSERT INTO cash_ups (session_date, created_by, status, total_float_counted, total_cash_counted, notes, submitted_at, submitted_by)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8) RETURNING id`,
`INSERT INTO cash_ups (session_date, created_by, status, total_float_counted, total_cash_counted, notes, submitted_at, submitted_by, checked_transactions)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9) RETURNING id`,
[
session_date, req.user.email, status, totalFloat, totalCash, notes || null,
status === 'final' ? new Date() : null,
status === 'final' ? req.user.email : null,
JSON.stringify(checked_transactions),
]
)
cashUpId = ins.rows[0].id

View file

@ -3,7 +3,7 @@ import { requireAuth, hasCap } from '../auth.js'
import { testConnection, fetchGlAccountsGrouped } from '../lib/newbook.js'
const ALL_KEYS = [
'default_report_days', 'petty_cash_float',
'default_report_days', 'petty_cash_float', 'till_float_target',
'sales_breakdown_columns', 'change_tin_breakdown',
]