Auto-resolve FX rate on account creation (base=1, else fetch from API)
Deploy to LXC / deploy (push) Successful in 1m56s
Validate / validate (push) Successful in 37s

Account creation no longer requires the user to enter an FX rate.
On create:
  - If account currency == company base → fxRateToBase = 1
  - Otherwise → fetchRate(accountCurrency, baseCurrency) from the
    fawazahmed0 FX API (same helper the daily scheduler uses)
  - Fallback to 1 if API call fails
The manual override field is still shown on the edit form for admin
corrections, and the daily scheduler keeps it fresh.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-20 15:57:08 +07:00
parent 7367aa9572
commit 8376116765
2 changed files with 43 additions and 15 deletions
@@ -2,6 +2,7 @@ import { error, fail } from '@sveltejs/kit';
import type { Actions, PageServerLoad } from './$types';
import { db } from '$lib/server/db/index.js';
import {
companies,
companyAccounts,
companyAccountTransactions,
externalAccounts
@@ -13,8 +14,26 @@ import {
postTransfer,
type CompanyAccountTxnType
} from '$lib/server/accounts/ledger.js';
import { fetchRate } from '$lib/server/fx/index.js';
import { and, asc, eq, isNull, sql } from 'drizzle-orm';
async function resolveFxRate(companyId: string, accountCurrency: string): Promise<string> {
const [company] = await db
.select({ currency: companies.currency })
.from(companies)
.where(eq(companies.id, companyId))
.limit(1);
const base = company?.currency ?? 'THB';
if (accountCurrency.toUpperCase() === base.toUpperCase()) return '1';
try {
const rate = await fetchRate(accountCurrency, base);
if (rate !== null && rate > 0) return rate.toFixed(8);
} catch {
// fall through
}
return '1';
}
const MANUAL_TXN_TYPES = ['deposit', 'adjustment'] as const;
type ManualTxnType = (typeof MANUAL_TXN_TYPES)[number];
@@ -281,6 +300,9 @@ export const actions: Actions = {
const openingBalanceDate =
parseDate(fd.get('openingBalanceDate')) ?? new Date();
// Auto-determine FX rate: 1 for base currency, API rate otherwise
const fxRateToBase = await resolveFxRate(params.companyId, f.currency);
const inserted = await db.transaction(async (tx) => {
const [row] = await tx
.insert(companyAccounts)
@@ -306,7 +328,7 @@ export const actions: Actions = {
creditLimit: f.creditLimit,
statementCloseDay: f.statementCloseDay,
paymentDueDay: f.paymentDueDay,
fxRateToBase: f.fxRateToBase,
fxRateToBase,
externalAccountId: f.externalAccountId
})
.returning({ id: companyAccounts.id });
@@ -177,6 +177,7 @@
class={inputCls}
/>
</div>
{#if prefix.startsWith('edit-')}
<div>
<label for="{prefix}-fxRate" class={labelCls}>FX Rate to Base</label>
<input
@@ -189,8 +190,13 @@
placeholder="1.0 for THB, 34.5 for USDTHB"
class={inputCls}
/>
<p class="mt-0.5 text-xs text-gray-400 dark:text-gray-500">1.0 if same as company currency</p>
<p class="mt-0.5 text-xs text-gray-400 dark:text-gray-500">Auto-refreshed daily from FX API. Override here to override.</p>
</div>
{:else}
<div class="text-xs text-gray-500 dark:text-gray-400 self-end pb-2">
FX rate: auto-set on create (1 if base currency, else fetched from FX API)
</div>
{/if}
{#if type === 'bank'}
<div>