v5.10.2 26 February 2026 Improvement
Currency Boundary Consolidation & Custom Linter
Custom Linter
SpeyBooks now has a bespoke Python linter enforcing the principle that the frontend is a view layer — all monetary calculations belong on the backend. The linter runs across both frontend and backend codebases with distinct rule sets.
- Frontend rules — detects client-side monetary aggregation, VAT calculations, debit/credit arithmetic, rounding on currency values, and inline pounds-to-pence conversions.
- Backend rules — enforces Row Level Security (all queries via
request.dbClient, neverpool.query), structured error handling, and logging via Fastify’s request logger. - Universal rules — catches hardcoded secrets, console logging, and overly broad exception handlers.
- Inline suppression —
// lint-ignore FE003on the preceding or same line, with file-level// lint-ignore-filefor generated files and sanctioned utilities.
The linter integrates into CI with exit code 1 on any unsuppressed error.
Currency Boundary Consolidation
All inline pounds-to-pence conversions (Math.round(parseFloat(x) * 100)) and pence-to-pounds display formatting ((pence / 100).toFixed(2)) have been replaced with two centralised utility functions.
toPence()— the single sanctioned conversion from form input (pounds) to integer pence, used at every form submission boundary.penceToPounds()— converts integer pence back to a pounds string for form input pre-fill, without a currency symbol.formatMoney()— existing utility now used consistently across all toast messages and display contexts.
This eliminates 16 scattered conversion patterns across 9 files and ensures every monetary boundary goes through tested, lint-enforced code.
API Client Correction
invoicesApi.recordPayment()— now accepts pence directly, matching the API contract. The pounds-to-pence conversion moved to the form boundary where it belongs.
Bug Fixes
- Fixed
parseInt()used instead ofparseFloat()for refund amounts in the admin panel — partial-pound refunds were silently truncated.
Known Issues
- Invoice and quote edit modes still compute VAT client-side for live preview. Suppressed with documented rationale. The production fix requires wiring
useDocumentPreviewinto edit modes and creating a/quotes/previewendpoint. Scheduled for a future release.