From dbfd229ba8688e1277790bc13b436228b75a6805 Mon Sep 17 00:00:00 2001 From: grabowski Date: Fri, 17 Apr 2026 13:58:43 +0700 Subject: [PATCH] Link service accounts to recurring bills with dropdown and display chip Co-Authored-By: Claude Opus 4.6 (1M context) --- .../[companyId]/bills/+page.server.ts | 35 +++++++++++++++++-- .../companies/[companyId]/bills/+page.svelte | 21 +++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/routes/(app)/companies/[companyId]/bills/+page.server.ts b/src/routes/(app)/companies/[companyId]/bills/+page.server.ts index 5b9341d..d3d0a75 100644 --- a/src/routes/(app)/companies/[companyId]/bills/+page.server.ts +++ b/src/routes/(app)/companies/[companyId]/bills/+page.server.ts @@ -4,6 +4,7 @@ import { db } from '$lib/server/db/index.js'; import { recurringBills, companyAccounts, + companyServiceAccounts, projects, categories, parties @@ -60,6 +61,7 @@ type BillFormFields = { projectId: string; categoryId: string | null; partyId: string | null; + serviceAccountId: string | null; description: string | null; currency: string; startDate: string; @@ -106,6 +108,7 @@ function extractFields(fd: FormData): BillFormFields | string { projectId, categoryId: trimOrNull(fd.get('categoryId')), partyId: trimOrNull(fd.get('partyId')), + serviceAccountId: trimOrNull(fd.get('serviceAccountId')), description: trimOrNull(fd.get('description')), currency, startDate, @@ -118,7 +121,8 @@ export const load: PageServerLoad = async ({ locals, params, parent }) => { await requireCompanyRoleAny(locals, params.companyId, ['admin', 'manager', 'accountant']); await parent(); - const [billRows, accountRows, projectRows, categoryRows, partyRows] = await Promise.all([ + const [billRows, accountRows, projectRows, categoryRows, partyRows, serviceAccountRows] = + await Promise.all([ db .select({ id: recurringBills.id, @@ -144,6 +148,9 @@ export const load: PageServerLoad = async ({ locals, params, parent }) => { categoryName: categories.name, partyId: recurringBills.partyId, partyName: parties.name, + serviceAccountId: recurringBills.serviceAccountId, + serviceAccountProvider: companyServiceAccounts.providerName, + serviceAccountNumber: companyServiceAccounts.accountNumber, createdAt: recurringBills.createdAt, updatedAt: recurringBills.updatedAt }) @@ -152,6 +159,7 @@ export const load: PageServerLoad = async ({ locals, params, parent }) => { .leftJoin(projects, eq(recurringBills.projectId, projects.id)) .leftJoin(categories, eq(recurringBills.categoryId, categories.id)) .leftJoin(parties, eq(recurringBills.partyId, parties.id)) + .leftJoin(companyServiceAccounts, eq(recurringBills.serviceAccountId, companyServiceAccounts.id)) .where( and(eq(recurringBills.companyId, params.companyId), isNull(recurringBills.deletedAt)) ) @@ -190,7 +198,25 @@ export const load: PageServerLoad = async ({ locals, params, parent }) => { .select({ id: parties.id, name: parties.name }) .from(parties) .where(and(eq(parties.companyId, params.companyId), isNull(parties.deletedAt))) - .orderBy(asc(parties.name)) + .orderBy(asc(parties.name)), + + db + .select({ + id: companyServiceAccounts.id, + type: companyServiceAccounts.type, + providerName: companyServiceAccounts.providerName, + accountNumber: companyServiceAccounts.accountNumber, + customLabel: companyServiceAccounts.customLabel + }) + .from(companyServiceAccounts) + .where( + and( + eq(companyServiceAccounts.companyId, params.companyId), + isNull(companyServiceAccounts.deletedAt), + eq(companyServiceAccounts.isActive, true) + ) + ) + .orderBy(asc(companyServiceAccounts.type), asc(companyServiceAccounts.providerName)) ]); return { @@ -198,7 +224,8 @@ export const load: PageServerLoad = async ({ locals, params, parent }) => { accounts: accountRows, projects: projectRows, categories: categoryRows, - parties: partyRows + parties: partyRows, + serviceAccounts: serviceAccountRows }; }; @@ -259,6 +286,7 @@ export const actions: Actions = { accountId: parsed.accountId, categoryId: parsed.categoryId, partyId: parsed.partyId, + serviceAccountId: parsed.serviceAccountId, name: parsed.name, description: parsed.description, cycle: parsed.cycle, @@ -342,6 +370,7 @@ export const actions: Actions = { accountId: parsed.accountId, categoryId: parsed.categoryId, partyId: parsed.partyId, + serviceAccountId: parsed.serviceAccountId, name: parsed.name, description: parsed.description, cycle: parsed.cycle, diff --git a/src/routes/(app)/companies/[companyId]/bills/+page.svelte b/src/routes/(app)/companies/[companyId]/bills/+page.svelte index 75be966..188c94e 100644 --- a/src/routes/(app)/companies/[companyId]/bills/+page.svelte +++ b/src/routes/(app)/companies/[companyId]/bills/+page.svelte @@ -71,6 +71,7 @@ projectId?: string; categoryId?: string; partyId?: string; + serviceAccountId?: string; description?: string; currency?: string; startDate?: string; @@ -211,6 +212,20 @@ {/each} +
+ + +
{/if} + {#if bill.serviceAccountProvider} +
+ {bill.serviceAccountProvider} #{bill.serviceAccountNumber} +
+ {/if} {#if bill.status === 'paused' && bill.pausedAt}
Paused since {formatDate(bill.pausedAt)} @@ -526,6 +546,7 @@ projectId: bill.projectId, categoryId: bill.categoryId ?? '', partyId: bill.partyId ?? '', + serviceAccountId: bill.serviceAccountId ?? '', description: bill.description ?? '', currency: bill.currency, startDate: bill.startDate,