diff --git a/backend/src/index.js b/backend/src/index.js
index aa2df17..2f139f6 100644
--- a/backend/src/index.js
+++ b/backend/src/index.js
@@ -16,6 +16,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
const UPLOADS_DIR = join(__dirname, '..', 'uploads')
const app = Fastify({ logger: true, trustProxy: true })
+const startedAt = Date.now()
await app.register(cookie)
await app.register(cors, {
@@ -29,7 +30,7 @@ await app.register(staticFiles, {
decorateReply: false,
})
-app.get('/health', async () => ({ status: 'healthy' }))
+app.get('/health', async () => ({ status: 'healthy', version: process.env.BUILD_VERSION || String(startedAt) }))
await app.register(cashupRoutes)
await app.register(newbookRoutes)
diff --git a/backend/src/lib/newbook.js b/backend/src/lib/newbook.js
index 89dd9cd..e667ebb 100644
--- a/backend/src/lib/newbook.js
+++ b/backend/src/lib/newbook.js
@@ -175,14 +175,19 @@ export async function fetchGlAccountList() {
return response?.data ?? []
}
+export async function fetchGlAccountGroupsList() {
+ const response = await callApi('gl_account_groups_list', {})
+ return response?.data ?? []
+}
+
export async function fetchGlAccountsGrouped() {
- const data = await fetchGlAccountList()
+ const data = await fetchGlAccountGroupsList()
const groups = {}
for (const item of data) {
if (item.gl_group_id && item.gl_group_name && !groups[item.gl_group_id]) {
let displayName = item.gl_group_name
if (displayName.includes(' - ')) displayName = displayName.split(' - ').slice(1).join(' - ').trim()
- groups[item.gl_group_id] = displayName
+ groups[String(item.gl_group_id)] = displayName
}
}
return groups
diff --git a/backend/src/routes/reports.js b/backend/src/routes/reports.js
index 4511be0..30b9fe7 100644
--- a/backend/src/routes/reports.js
+++ b/backend/src/routes/reports.js
@@ -131,27 +131,11 @@ export async function reportRoutes(app) {
{ category: 'bacs', banked_amount: pt.bacs, reported_amount: pt.bacs },
]
- // Build a lookup from gl_group_id → name using the accounts list
- // gl_group_id from Newbook is often numeric; normalise to string for comparison
- const glGroupById = {}
- for (const a of (glAccountList || [])) {
- const gid = String(a.gl_group_id ?? '')
- if (gid && !glGroupById[gid]) glGroupById[gid] = (a.gl_group_name ?? '').toLowerCase().replace(/[^a-z0-9]/g, '')
- }
-
- // Sales breakdown — match earned revenue to configured columns
- // Try: (1) exact code match, (2) normalised name contains code / vice-versa
+ // Sales breakdown — match earned revenue to configured columns by exact gl_group_id
const salesBreakdown = enabledColumns.map(col => {
- const code = col.gl_code.toLowerCase().replace(/[^a-z0-9]/g, '')
- const item = earnedRevenue.find(r => {
- if (r.period !== date) return false
- const gid = String(r.gl_group_id ?? '')
- if (gid === col.gl_code || gid.toUpperCase() === col.gl_code.toUpperCase()) return true
- const gName = glGroupById[gid] ?? ''
- // Only fuzzy-match when the column has a non-empty gl_code — prevents
- // placeholder columns (empty code) from matching any GL group via gName.includes('')
- return code.length > 0 && (gName.includes(code) || code.includes(gName))
- })
+ const item = col.gl_code
+ ? earnedRevenue.find(r => r.period === date && String(r.gl_group_id ?? '') === String(col.gl_code))
+ : undefined
const net = parseFloat(item?.earned_revenue_ex || 0)
const vat = parseFloat(item?.earned_revenue_tax || 0)
const gross = parseFloat(item?.earned_revenue || 0)
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index df59e08..5667afe 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,5 +1,7 @@
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import { AuthGate } from './components/AuthGate'
+import { UpdateBanner } from './components/UpdateBanner'
+import { useVersionCheck } from './hooks/useVersionCheck'
import { Layout } from './components/Layout'
import { DailyCashUp } from './pages/DailyCashUp'
import { History } from './pages/History'
@@ -42,11 +44,15 @@ function AppRoutes({ user }: { user: User }) {
}
export default function App() {
+ const updateAvailable = useVersionCheck('/cashup/health')
return (
-