From 8ef2ef7465fc88afd6d06c667dc834cf6b9226c3 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 20 Apr 2026 16:33:51 +0700 Subject: [PATCH] Convert per-project spent to base currency via account FX rate Both the overview and budget page queries now multiply each approved expense amount by its account's fxRateToBase before summing. A -$434 USD expense on a USD account (rate 34.5) now contributes -14,973 THB to the total, not -434. Expenses with no account fall back to rate 1. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../(app)/companies/[companyId]/+page.server.ts | 13 ++++++++++--- .../companies/[companyId]/budget/+page.server.ts | 6 ++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/routes/(app)/companies/[companyId]/+page.server.ts b/src/routes/(app)/companies/[companyId]/+page.server.ts index 8a7e905..7ef587d 100644 --- a/src/routes/(app)/companies/[companyId]/+page.server.ts +++ b/src/routes/(app)/companies/[companyId]/+page.server.ts @@ -1,23 +1,30 @@ import type { PageServerLoad } from './$types'; import { db } from '$lib/server/db/index.js'; -import { projects, expenses, sales, saleLineItems } from '$lib/server/db/schema.js'; +import { + projects, + expenses, + sales, + saleLineItems, + companyAccounts +} from '$lib/server/db/schema.js'; import { eq, and, sql } from 'drizzle-orm'; export const load: PageServerLoad = async ({ parent }) => { const { company } = await parent(); - // Get projects with spent amounts + // Get projects with spent amounts (converted to base currency via each expense's account fx rate) const projectList = await db .select({ id: projects.id, name: projects.name, allocatedBudget: projects.allocatedBudget, isActive: projects.isActive, - spent: sql`coalesce(sum(case when ${expenses.status} = 'approved' then ${expenses.amount} else 0 end), 0)`, + spent: sql`coalesce(sum(case when ${expenses.status} = 'approved' then ${expenses.amount} * coalesce(${companyAccounts.fxRateToBase}, 1) else 0 end), 0)::text`, pendingCount: sql`count(case when ${expenses.status} = 'pending' then 1 end)::int` }) .from(projects) .leftJoin(expenses, eq(expenses.projectId, projects.id)) + .leftJoin(companyAccounts, eq(expenses.accountId, companyAccounts.id)) .where(eq(projects.companyId, company.id)) .groupBy(projects.id) .orderBy(projects.name); diff --git a/src/routes/(app)/companies/[companyId]/budget/+page.server.ts b/src/routes/(app)/companies/[companyId]/budget/+page.server.ts index b30b080..ec8b594 100644 --- a/src/routes/(app)/companies/[companyId]/budget/+page.server.ts +++ b/src/routes/(app)/companies/[companyId]/budget/+page.server.ts @@ -9,7 +9,8 @@ import { expenses, companyLog, sales, - saleLineItems + saleLineItems, + companyAccounts } from '$lib/server/db/schema.js'; import { and, eq, sql } from 'drizzle-orm'; import { requireCompanyRole } from '$lib/server/authorization.js'; @@ -24,10 +25,11 @@ export const load: PageServerLoad = async ({ parent, params }) => { id: projects.id, name: projects.name, allocatedBudget: projects.allocatedBudget, - spent: sql`coalesce(sum(case when ${expenses.status} = 'approved' then ${expenses.amount} else 0 end), 0)` + spent: sql`coalesce(sum(case when ${expenses.status} = 'approved' then ${expenses.amount} * coalesce(${companyAccounts.fxRateToBase}, 1) else 0 end), 0)::text` }) .from(projects) .leftJoin(expenses, eq(expenses.projectId, projects.id)) + .leftJoin(companyAccounts, eq(expenses.accountId, companyAccounts.id)) .where(eq(projects.companyId, params.companyId)) .groupBy(projects.id) .orderBy(projects.name);