Auto-resolve FX rate on account creation (base=1, else fetch from API)
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:
@@ -2,6 +2,7 @@ import { error, fail } from '@sveltejs/kit';
|
|||||||
import type { Actions, PageServerLoad } from './$types';
|
import type { Actions, PageServerLoad } from './$types';
|
||||||
import { db } from '$lib/server/db/index.js';
|
import { db } from '$lib/server/db/index.js';
|
||||||
import {
|
import {
|
||||||
|
companies,
|
||||||
companyAccounts,
|
companyAccounts,
|
||||||
companyAccountTransactions,
|
companyAccountTransactions,
|
||||||
externalAccounts
|
externalAccounts
|
||||||
@@ -13,8 +14,26 @@ import {
|
|||||||
postTransfer,
|
postTransfer,
|
||||||
type CompanyAccountTxnType
|
type CompanyAccountTxnType
|
||||||
} from '$lib/server/accounts/ledger.js';
|
} from '$lib/server/accounts/ledger.js';
|
||||||
|
import { fetchRate } from '$lib/server/fx/index.js';
|
||||||
import { and, asc, eq, isNull, sql } from 'drizzle-orm';
|
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;
|
const MANUAL_TXN_TYPES = ['deposit', 'adjustment'] as const;
|
||||||
type ManualTxnType = (typeof MANUAL_TXN_TYPES)[number];
|
type ManualTxnType = (typeof MANUAL_TXN_TYPES)[number];
|
||||||
|
|
||||||
@@ -281,6 +300,9 @@ export const actions: Actions = {
|
|||||||
const openingBalanceDate =
|
const openingBalanceDate =
|
||||||
parseDate(fd.get('openingBalanceDate')) ?? new Date();
|
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 inserted = await db.transaction(async (tx) => {
|
||||||
const [row] = await tx
|
const [row] = await tx
|
||||||
.insert(companyAccounts)
|
.insert(companyAccounts)
|
||||||
@@ -306,7 +328,7 @@ export const actions: Actions = {
|
|||||||
creditLimit: f.creditLimit,
|
creditLimit: f.creditLimit,
|
||||||
statementCloseDay: f.statementCloseDay,
|
statementCloseDay: f.statementCloseDay,
|
||||||
paymentDueDay: f.paymentDueDay,
|
paymentDueDay: f.paymentDueDay,
|
||||||
fxRateToBase: f.fxRateToBase,
|
fxRateToBase,
|
||||||
externalAccountId: f.externalAccountId
|
externalAccountId: f.externalAccountId
|
||||||
})
|
})
|
||||||
.returning({ id: companyAccounts.id });
|
.returning({ id: companyAccounts.id });
|
||||||
|
|||||||
@@ -177,6 +177,7 @@
|
|||||||
class={inputCls}
|
class={inputCls}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{#if prefix.startsWith('edit-')}
|
||||||
<div>
|
<div>
|
||||||
<label for="{prefix}-fxRate" class={labelCls}>FX Rate to Base</label>
|
<label for="{prefix}-fxRate" class={labelCls}>FX Rate to Base</label>
|
||||||
<input
|
<input
|
||||||
@@ -189,8 +190,13 @@
|
|||||||
placeholder="1.0 for THB, 34.5 for USD→THB"
|
placeholder="1.0 for THB, 34.5 for USD→THB"
|
||||||
class={inputCls}
|
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>
|
</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'}
|
{#if type === 'bank'}
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user