f33628f005
* feat(import): say in the SIE wizard that the chart and fiscal year come along The preview scored the file's accounts against the BAS reference and said "matchas mot din kontoplan", so a consultant with a 41-account seeded company read "150 mappade" as "the file's chart replaces mine". A fiscal-year overlap with a non-empty period was only refused after the mapping step. - Parse route adds preview.chart (accounts new to THIS company vs already present, with a sample) via planChartChanges, and preview.fiscalYear from precheckFiscalPeriod: the containment/overlap verdict extracted out of ensureFiscalPeriod, which now consumes it, so preview and import cannot drift. - Preview card renamed to Kontoplan with the counts and the fiscal-year verdict (match / create / conflict with the import's own refusal text). - Review step lists the chart among "Vad händer när du importerar?". - executeSIEImport reports accountsCreated from the account sync; the result grid gets a Konton skapade card. No import logic changed; no migration. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014MgxEaU52nJgDQA41svdtC * fix(import): preview refuses what the import refuses, and the chart card survives "Skapa saknade konton" Skeptic pass on #2307 refuted the first cut twice: - "Skapas vid import" was shown for a #RAR the import then refuses under BFL 3 kap. (19 months, non-month-end finish, mid-month start after an earlier year). The shape rules move into precheckFiscalPeriod as a fourth verdict 'invalid' with the same refusal text; ensureFiscalPeriod stays a consumer of one verdict, same query order. - The Kontoplan card counted unmapped sources under "Läggs till" and kept listing them after the create button, while the result said 0 created. planChartChanges (now client-safe in lib/import/chart-plan.ts) counts mapped targets only; the create button moves those accounts from "Ej mappade" to "Finns redan" in place. - The review line claimed existing accounts keep their name unless you opt in; the switch defaults to on. Reworded to match. - Sample names follow the file for identity mappings, as the sync does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014MgxEaU52nJgDQA41svdtC --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
112 lines
3.3 KiB
TypeScript
112 lines
3.3 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { planChartChanges } from '../chart-plan'
|
|
import type { AccountMapping } from '../types'
|
|
|
|
function mapping(
|
|
partial: Partial<AccountMapping> & { sourceAccount: string; targetAccount: string }
|
|
): AccountMapping {
|
|
return {
|
|
sourceName: '',
|
|
targetName: '',
|
|
confidence: 1,
|
|
matchType: 'exact',
|
|
isOverride: false,
|
|
...partial,
|
|
}
|
|
}
|
|
|
|
describe('planChartChanges', () => {
|
|
it('splits distinct target accounts into new and existing for this company', () => {
|
|
const plan = planChartChanges(
|
|
[
|
|
mapping({ sourceAccount: '1930', targetAccount: '1930', sourceName: 'Företagskonto' }),
|
|
mapping({ sourceAccount: '3010', targetAccount: '3010', sourceName: 'Konsultarvoden' }),
|
|
mapping({ sourceAccount: '6110', targetAccount: '6110', targetName: 'Kontorsmateriel' }),
|
|
],
|
|
new Set(['1930']),
|
|
)
|
|
|
|
expect(plan.toCreate).toBe(2)
|
|
expect(plan.existing).toBe(1)
|
|
expect(plan.sample).toEqual([
|
|
{ number: '3010', name: 'Konsultarvoden' },
|
|
{ number: '6110', name: 'Kontorsmateriel' },
|
|
])
|
|
})
|
|
|
|
it('names a new identity-mapped account after the file, not the BAS reference', () => {
|
|
// Matches syncMappedAccounts with "Använd kontonamn från filen" on (the
|
|
// default): the file's #KONTO name wins for sourceAccount === targetAccount.
|
|
const plan = planChartChanges(
|
|
[
|
|
mapping({
|
|
sourceAccount: '1930',
|
|
targetAccount: '1930',
|
|
sourceName: 'Företagskonto Swedbank',
|
|
targetName: 'Företagskonto/checkkonto',
|
|
}),
|
|
],
|
|
new Set(),
|
|
)
|
|
|
|
expect(plan.sample).toEqual([{ number: '1930', name: 'Företagskonto Swedbank' }])
|
|
})
|
|
|
|
it('names a remapped account after its target', () => {
|
|
const plan = planChartChanges(
|
|
[
|
|
mapping({
|
|
sourceAccount: '1910',
|
|
targetAccount: '1930',
|
|
sourceName: 'Kassa',
|
|
targetName: 'Företagskonto/checkkonto',
|
|
}),
|
|
],
|
|
new Set(),
|
|
)
|
|
|
|
expect(plan.sample).toEqual([{ number: '1930', name: 'Företagskonto/checkkonto' }])
|
|
})
|
|
|
|
it('counts two sources remapped onto one target once', () => {
|
|
const plan = planChartChanges(
|
|
[
|
|
mapping({ sourceAccount: '1910', targetAccount: '1930' }),
|
|
mapping({ sourceAccount: '1920', targetAccount: '1930' }),
|
|
],
|
|
new Set(),
|
|
)
|
|
|
|
expect(plan.toCreate).toBe(1)
|
|
expect(plan.existing).toBe(0)
|
|
})
|
|
|
|
it('leaves an unmapped source out of both counts', () => {
|
|
// The import refuses while anything is unmapped; the account is neither
|
|
// added nor present until the user creates or maps it.
|
|
const plan = planChartChanges(
|
|
[
|
|
mapping({ sourceAccount: '9030', targetAccount: '', sourceName: 'Obokat resultat' }),
|
|
mapping({ sourceAccount: '1930', targetAccount: '1930' }),
|
|
],
|
|
new Set(['1930']),
|
|
)
|
|
|
|
expect(plan.toCreate).toBe(0)
|
|
expect(plan.existing).toBe(1)
|
|
expect(plan.sample).toEqual([])
|
|
})
|
|
|
|
it('caps the sample but not the count', () => {
|
|
const mappings = Array.from({ length: 12 }, (_, i) =>
|
|
mapping({ sourceAccount: `40${10 + i}`, targetAccount: `40${10 + i}` }),
|
|
)
|
|
|
|
const plan = planChartChanges(mappings, new Set())
|
|
|
|
expect(plan.toCreate).toBe(12)
|
|
expect(plan.sample).toHaveLength(8)
|
|
expect(plan.sample[0].number).toBe('4010')
|
|
})
|
|
})
|