From ae23e9fbd10d8b8dff10f147c3e993b4aeec59e4 Mon Sep 17 00:00:00 2001 From: Linus Miller Date: Thu, 4 Dec 2025 21:30:59 +0100 Subject: [PATCH] add balances --- .bruno/BRF/api-balances.bru | 15 +++++ .../components/balances_page.module.scss | 9 +++ client/public/components/balances_page.tsx | 59 +++++++++++++++++++ client/public/routes.ts | 7 +++ server/routes/api.ts | 2 + server/routes/api/balances.ts | 36 +++++++++++ 6 files changed, 128 insertions(+) create mode 100644 .bruno/BRF/api-balances.bru create mode 100644 client/public/components/balances_page.module.scss create mode 100644 client/public/components/balances_page.tsx create mode 100644 server/routes/api/balances.ts diff --git a/.bruno/BRF/api-balances.bru b/.bruno/BRF/api-balances.bru new file mode 100644 index 0000000..66d373f --- /dev/null +++ b/.bruno/BRF/api-balances.bru @@ -0,0 +1,15 @@ +meta { + name: /api/balances + type: http + seq: 15 +} + +get { + url: {{base_url}}/api/balances + body: none + auth: inherit +} + +settings { + encodeUrl: true +} diff --git a/client/public/components/balances_page.module.scss b/client/public/components/balances_page.module.scss new file mode 100644 index 0000000..73f7bcc --- /dev/null +++ b/client/public/components/balances_page.module.scss @@ -0,0 +1,9 @@ +.table { + a { + text-decoration: none; + color: black; + &:hover { + font-weight: bold; + } + } +} diff --git a/client/public/components/balances_page.tsx b/client/public/components/balances_page.tsx new file mode 100644 index 0000000..dfe38c5 --- /dev/null +++ b/client/public/components/balances_page.tsx @@ -0,0 +1,59 @@ +import { h } from 'preact' +import { useEffect, useState } from 'preact/hooks' +import cn from 'classnames' +import rek from 'rek' +import Head from './head.ts' +import Balance from './balance.tsx' +import Balances from './balances.tsx' +import { formatNumber } from '../utils/format_number.ts' +import s from './balances_page.module.scss' + +const BalancesPage = () => { + const [balances, setBalances] = useState([]) + const [years, setYears] = useState([]) + + useEffect(() => { + rek(`/api/balances`).then(setBalances) + rek(`/api/financial-years`).then((years) => setYears(years.map((fy) => fy.year))) + }, []) + + return ( +
+ + : Balances + + +

Balances

+ {years.length && balances.length && ( + + + + + + {years.map((year) => ( + + ))} + + + + {balances.map((balance) => ( + + + + {years.map((year) => ( + + ))} + + ))} + +
AccountDescription{year}
{balance.accountNumber}{balance.description} + + {formatNumber(balance[year])} + +
+ )} +
+ ) +} + +export default BalancesPage diff --git a/client/public/routes.ts b/client/public/routes.ts index b5a2fce..1549ddd 100644 --- a/client/public/routes.ts +++ b/client/public/routes.ts @@ -1,4 +1,5 @@ import Accounts from './components/accounts_page.tsx' +import Balances from './components/balances_page.tsx' import Entries from './components/entries_page.tsx' import Entry from './components/entry_page.tsx' import Invoice from './components/invoice_page.tsx' @@ -61,6 +62,12 @@ export default [ component: InvoicesBySupplier, nav: false, }, + { + path: '/balances', + name: 'balances', + title: 'Balances', + component: Balances, + }, { path: '/results', name: 'results', diff --git a/server/routes/api.ts b/server/routes/api.ts index 810d040..e96c999 100644 --- a/server/routes/api.ts +++ b/server/routes/api.ts @@ -3,6 +3,7 @@ import { Type, type Static, type FastifyPluginCallbackTypebox } from '@fastify/t import knex from '../lib/knex.ts' import accounts from './api/accounts.ts' +import balances from './api/balances.ts' import entries from './api/entries.ts' import financialYears from './api/financial_years.ts' import invoices from './api/invoices.ts' @@ -22,6 +23,7 @@ export type FinancialYearType = Static const apiRoutes: FastifyPluginCallbackTypebox = (fastify, _, done) => { fastify.register(accounts, { prefix: '/accounts' }) + fastify.register(balances, { prefix: '/balances' }) fastify.register(entries, { prefix: '/entries' }) fastify.register(financialYears, { prefix: '/financial-years' }) fastify.register(invoices, { prefix: '/invoices' }) diff --git a/server/routes/api/balances.ts b/server/routes/api/balances.ts new file mode 100644 index 0000000..ee20c32 --- /dev/null +++ b/server/routes/api/balances.ts @@ -0,0 +1,36 @@ +import _ from 'lodash' +import { Type, type Static, type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox' +import knex from '../../lib/knex.ts' + +const balanceRoutes: FastifyPluginCallbackTypebox = (fastify, _, done) => { + fastify.route({ + url: '/', + method: 'GET', + async handler() { + const financialYears = await knex('financialYear').select('*').orderBy('year', 'asc') + + return knex('accountBalance AS ab') + .select( + 'ab.accountNumber', + 'a.description', + Object.fromEntries( + financialYears.map((fy) => [ + fy.year, + knex.raw(`SUM(CASE WHEN fy.year = ${fy.year} THEN ab."out" ELSE 0 END)`), + ]), + ), + ) + .innerJoin('financialYear AS fy', 'fy.id', 'ab.financialYearId') + .innerJoin('account AS a', function () { + this.on('ab.accountNumber', '=', 'a.number') + }) + .groupBy('ab.accountNumber', 'a.description') + .where('ab.accountNumber', '<', 3000) + .orderBy('ab.accountNumber') + }, + }) + + done() +} + +export default balanceRoutes