feat: implement fiscal period date fields component and validation logic (#301)

* feat: implement fiscal period date fields component and validation logic

* feat: update fiscal period validation and naming logic
This commit is contained in:
Mattsson
2026-04-21 17:08:38 +02:00
committed by GitHub
parent 08991218ee
commit 885dd8a2e4
8 changed files with 504 additions and 257 deletions
+79
View File
@@ -6,6 +6,7 @@ import {
ensureFiscalPeriod,
importVouchers,
computeVoucherNumberRanges,
linkOpeningBalanceEntryToPeriod,
} from '../sie-import'
import { createQueuedMockSupabase } from '@/tests/helpers'
import type { ParsedSIEFile, AccountMapping } from '../types'
@@ -421,6 +422,84 @@ describe('ensureFiscalPeriod validation', () => {
})
})
describe('linkOpeningBalanceEntryToPeriod', () => {
// Regression: SIE import created the opening-balance entry but never wrote
// its ID back to fiscal_periods. Without the link, getOpeningBalances falls
// through to summing all prior journal lines, which inflates balance-sheet
// accounts across multi-year imports (each year's IB double-counted against
// the prior year's UB).
type Supabase = Parameters<typeof linkOpeningBalanceEntryToPeriod>[0]
it('writes opening_balance_entry_id and opening_balances_set to the fiscal period', async () => {
const updates: Array<{ payload: Record<string, unknown>; filters: Record<string, unknown> }> = []
const supabase = {
from: (table: string) => {
if (table !== 'fiscal_periods') {
throw new Error(`Unexpected table: ${table}`)
}
let pendingPayload: Record<string, unknown> = {}
const filters: Record<string, unknown> = {}
const chain = {
update: (payload: Record<string, unknown>) => {
pendingPayload = payload
return chain
},
eq: (col: string, val: unknown) => {
filters[col] = val
return chain
},
then: (resolve: (v: unknown) => void) => {
updates.push({ payload: pendingPayload, filters: { ...filters } })
resolve({ data: null, error: null })
},
}
return chain
},
}
await linkOpeningBalanceEntryToPeriod(
supabase as unknown as Supabase,
'company-1',
'period-1',
'ob-entry-1',
)
expect(updates).toHaveLength(1)
expect(updates[0].payload).toEqual({
opening_balance_entry_id: 'ob-entry-1',
opening_balances_set: true,
})
expect(updates[0].filters).toEqual({
id: 'period-1',
company_id: 'company-1',
})
})
it('throws a descriptive error when the update fails', async () => {
const supabase = {
from: () => {
const chain = {
update: () => chain,
eq: () => chain,
then: (resolve: (v: unknown) => void) =>
resolve({ data: null, error: { message: 'permission denied' } }),
}
return chain
},
}
await expect(
linkOpeningBalanceEntryToPeriod(
supabase as unknown as Supabase,
'company-1',
'period-1',
'ob-entry-1',
),
).rejects.toThrow(/Failed to link opening balance entry.*permission denied/)
})
})
describe('isBalanceSheetAccount', () => {
it('returns true for class 1 (assets)', () => {
expect(isBalanceSheetAccount('1510')).toBe(true)