* fix(arcim-migration): require a completed SIE import for Fortnox too (#2000) POST /migrate refused entity import without a completed SIE import for every provider except Fortnox, on the assumption that the wizard always runs SIE-over-API first. The wizard lets the user uncheck "Bokföringsdata (SIE)" while keeping customers, suppliers and invoices checked, so a Fortnox company could end up with subledgers but no chart of accounts and no opening balances: the incomplete bokföring the guard exists to prevent. The guard now applies to all providers as "a completed SIE import must exist for the company", not "must be part of this run", so an entities-only re-run after an earlier full migration still works. For providers that serve SIE over the API (Fortnox, Briox, Björn Lundén, WINT) the 409 message points at the wizard checkbox instead of "ladda upp en SIE-fil". The options step shows one attn line and disables "Starta migrering" when SIE is unchecked, never imported, and any API import is selected, so the user sees the rule before the run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015nAd8XJ2RPCmG2eKoLBdna * fix(arcim-migration): address review findings (#2000) - Findings 1, 3, 4 (stale hasSieData on retry): the options step now derives "SIE already imported" from the preview OR this session's successful /import-sie results, so an entities-only retry after a phase-2 failure is no longer blocked with a false "not imported yet" hint and a forced replace-mode ledger re-import. - Finding 2 (company-info-only run gated): importCompanyInfo is dropped from the wizard gate and POST /migrate skips the SIE guard when every entity flag is false. Company info writes no accounts, balances or subledger rows, so the BFL rationale does not apply. Tests pin both the company-info-only pass and the block when any entity flag is set. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015nAd8XJ2RPCmG2eKoLBdna --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
Jakob Wennberg
parent
7993700589
commit
b4b11c33ca
@@ -1060,6 +1060,7 @@ function OptionsStep({
|
||||
options,
|
||||
sieAvailable,
|
||||
sieData,
|
||||
hasSieData,
|
||||
provider,
|
||||
onChange,
|
||||
onStart,
|
||||
@@ -1068,6 +1069,8 @@ function OptionsStep({
|
||||
options: MigrationOptions
|
||||
sieAvailable: boolean
|
||||
sieData: SIEData | null
|
||||
/** The company already has a completed SIE import (any origin). */
|
||||
hasSieData: boolean
|
||||
provider: ArcimProvider | null
|
||||
onChange: (options: MigrationOptions) => void
|
||||
onStart: () => void
|
||||
@@ -1096,6 +1099,18 @@ function OptionsStep({
|
||||
if (options.importSalesInvoices) selectedItems.push('Kundfakturor')
|
||||
if (options.importSupplierInvoices) selectedItems.push('Leverantörsfakturor')
|
||||
|
||||
// Entities without the SIE-derived ledger leave an incomplete bokföring:
|
||||
// POST /migrate refuses with PROVIDER_SIE_IMPORT_REQUIRED unless a completed
|
||||
// SIE import exists. Say so here, before the run, when the user has
|
||||
// unchecked SIE for a company that has never imported it (#2000).
|
||||
// Company info (name, org number, VAT number) writes no ledger data and is
|
||||
// not gated, matching the route.
|
||||
const hasApiImport = options.importCustomers ||
|
||||
options.importSuppliers ||
|
||||
options.importSalesInvoices ||
|
||||
options.importSupplierInvoices
|
||||
const sieRequiredButUnchecked = sieAvailable && !options.importSIEData && !hasSieData && hasApiImport
|
||||
|
||||
return (
|
||||
<div className="stagger-enter space-y-8">
|
||||
<StepHeading
|
||||
@@ -1104,8 +1119,10 @@ function OptionsStep({
|
||||
/>
|
||||
|
||||
{/* Years whose provider export failed: must be visible before the user
|
||||
proceeds, otherwise an IB/UB gap slips through. One ochre sentence. */}
|
||||
{sieAvailable && failedYears.length > 0 && (
|
||||
proceeds, otherwise an IB/UB gap slips through. One ochre sentence.
|
||||
Yields to the SIE-required line below: with SIE unchecked no year is
|
||||
imported, and the page carries at most one attn line. */}
|
||||
{sieAvailable && failedYears.length > 0 && !sieRequiredButUnchecked && (
|
||||
<AttnLine>
|
||||
{failedYears.length === 1
|
||||
? `Räkenskapsår ${failedYears[0].year} kunde inte hämtas från källsystemet: om du fortsätter importeras övriga år, men ingående och utgående balanser kan sakna kontinuitet. Försök igen senare eller ladda upp en SIE-fil för det saknade året manuellt.`
|
||||
@@ -1202,12 +1219,16 @@ function OptionsStep({
|
||||
/>
|
||||
</div>
|
||||
|
||||
{sieRequiredButUnchecked && (
|
||||
<AttnLine>{t('ext_arcim_option_sie_required_hint')}</AttnLine>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col-reverse gap-3 border-t border-border pt-6 sm:flex-row sm:justify-between">
|
||||
<Button variant="outline" className="min-h-11" onClick={onBack}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Tillbaka
|
||||
</Button>
|
||||
<Button className="min-h-11" onClick={() => setShowConfirm(true)} disabled={selectedItems.length === 0}>
|
||||
<Button className="min-h-11" onClick={() => setShowConfirm(true)} disabled={selectedItems.length === 0 || sieRequiredButUnchecked}>
|
||||
Starta migrering
|
||||
<ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
@@ -2975,6 +2996,7 @@ export default function ArcimMigrationWorkspace({
|
||||
options={migrationOptions}
|
||||
sieAvailable={preview?.sieAvailable ?? false}
|
||||
sieData={sieData}
|
||||
hasSieData={(preview?.hasSieData ?? false) || sieImportResults.some(r => r.success)}
|
||||
provider={preview?.consent.provider ?? null}
|
||||
onChange={setMigrationOptions}
|
||||
onStart={handleStartMigration}
|
||||
|
||||
Reference in New Issue
Block a user