From bc0699a99276a1b317acbdc5d271cd234b64f9cf Mon Sep 17 00:00:00 2001 From: grabowski Date: Fri, 17 Apr 2026 16:16:05 +0700 Subject: [PATCH] Derive total budget from account balances instead of manual field Total budget is now sum(account transaction amounts) across all non-deleted accounts. Removed the manual 'Add Budget' action and form. Budget page is now read-only for the total; allocations still work. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../companies/[companyId]/+layout.server.ts | 20 ++++++- .../[companyId]/budget/+page.server.ts | 37 ------------ .../companies/[companyId]/budget/+page.svelte | 58 ++----------------- 3 files changed, 22 insertions(+), 93 deletions(-) diff --git a/src/routes/(app)/companies/[companyId]/+layout.server.ts b/src/routes/(app)/companies/[companyId]/+layout.server.ts index 2a6fe86..a3da916 100644 --- a/src/routes/(app)/companies/[companyId]/+layout.server.ts +++ b/src/routes/(app)/companies/[companyId]/+layout.server.ts @@ -1,8 +1,8 @@ import { error } from '@sveltejs/kit'; import type { LayoutServerLoad } from './$types'; import { db } from '$lib/server/db/index.js'; -import { companies } from '$lib/server/db/schema.js'; -import { eq, and, isNull } from 'drizzle-orm'; +import { companies, companyAccounts, companyAccountTransactions } from '$lib/server/db/schema.js'; +import { eq, and, isNull, sql } from 'drizzle-orm'; import { requireAuth, getCompanyRoles } from '$lib/server/authorization.js'; import type { CompanyRole } from '$lib/types/index.js'; @@ -27,12 +27,26 @@ export const load: LayoutServerLoad = async ({ locals, params }) => { error(403, 'Not a member of this company'); } + // Total budget = sum of all non-deleted account balances + const [balanceRow] = await db + .select({ + total: sql`coalesce(sum(${companyAccountTransactions.amount}), '0')::text` + }) + .from(companyAccountTransactions) + .innerJoin(companyAccounts, eq(companyAccountTransactions.accountId, companyAccounts.id)) + .where( + and( + eq(companyAccountTransactions.companyId, company.id), + isNull(companyAccounts.deletedAt) + ) + ); + return { company: { id: company.id, name: company.name, description: company.description, - totalBudget: company.totalBudget, + totalBudget: balanceRow?.total ?? '0', currency: company.currency }, companyRoles: roles diff --git a/src/routes/(app)/companies/[companyId]/budget/+page.server.ts b/src/routes/(app)/companies/[companyId]/budget/+page.server.ts index c9a2592..7e60ea1 100644 --- a/src/routes/(app)/companies/[companyId]/budget/+page.server.ts +++ b/src/routes/(app)/companies/[companyId]/budget/+page.server.ts @@ -69,43 +69,6 @@ export const load: PageServerLoad = async ({ parent, params }) => { }; export const actions: Actions = { - addBudget: async ({ request, locals, params }) => { - const { user } = await requireCompanyRole(locals, params.companyId, 'admin'); - - const formData = await request.formData(); - const amount = parseFloat(formData.get('amount')?.toString() || '0'); - - if (isNaN(amount) || amount <= 0) { - return fail(400, { error: 'Amount must be a positive number' }); - } - - // Get current budget for the log - const [company] = await db - .select({ totalBudget: companies.totalBudget, currency: companies.currency }) - .from(companies) - .where(eq(companies.id, params.companyId)) - .limit(1); - - await db - .update(companies) - .set({ - totalBudget: sql`${companies.totalBudget}::numeric + ${amount.toFixed(2)}::numeric`, - updatedAt: new Date() - }) - .where(eq(companies.id, params.companyId)); - - const newTotal = parseFloat(company.totalBudget) + amount; - await logCompanyEvent( - params.companyId, - user.id, - 'budget_added', - `Budget increased by ${formatCurrency(amount, company.currency)} (new total: ${formatCurrency(newTotal, company.currency)})`, - { amount: amount.toFixed(2), previousTotal: company.totalBudget, newTotal: newTotal.toFixed(2) } - ); - - return { success: true }; - }, - allocate: async ({ request, locals, params }) => { const { user } = await requireCompanyRole(locals, params.companyId, 'manager'); diff --git a/src/routes/(app)/companies/[companyId]/budget/+page.svelte b/src/routes/(app)/companies/[companyId]/budget/+page.svelte index bc8042d..c96db06 100644 --- a/src/routes/(app)/companies/[companyId]/budget/+page.svelte +++ b/src/routes/(app)/companies/[companyId]/budget/+page.svelte @@ -14,7 +14,7 @@ const canAllocate = $derived(data.companyRoles.includes('admin') || data.companyRoles.includes('manager')); const isAdmin = $derived(data.companyRoles.includes('admin')); - let showAddBudget = $state(false); + // Budget total now comes from account balances — no manual add function getEventStyle(event: string) { const styles: Record = { @@ -44,65 +44,17 @@
-
+

Budget Allocation

- {#if isAdmin} - - {/if} +

+ Total budget reflects the sum of your account balances. Manage funds via the Accounts tab. +

{#if form?.error}
{form.error}
{/if} - - {#if showAddBudget && isAdmin} -
-

Replenish Company Budget

-
{ - return async ({ update }) => { - await update(); - showAddBudget = false; - }; - }} class="flex items-end gap-3"> -
- - -
- - -
-
- {/if} -