fix(import): let provider re-sync re-import an earlier fiscal year after data deletion (#1763)
* fix(import): let provider re-sync re-import an earlier fiscal year after data deletion After partially deleting imported data, a provider re-sync could not bring back the previous fiscal year: the sie_imports 'completed' watermark survives data deletion, the replace path aborted the whole year when the prior import row could not be resolved, and prior-import detection picked an arbitrary row when several overlapped the same year. - findOverlappingPeriodImports returns ALL overlapping completed rows, newest first; checkDuplicatePeriodImport now picks deterministically. - executeSIEImport replace mode resolves every overlapping row. A row that is gone or no longer 'completed' (replaceSIEImport codes not_found / not_completed) is a stale watermark: skip it with a warning and import the year fresh instead of stranding the user. Locked/closed periods and RPC failures still abort the year. - The arcim-migration wizard names the fiscal year in every per-file import failure and shows the newest prior import in the options step. Fixes #1667 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Emil <emilmattsson14@gmail.com> * fix(import): fail closed when the replace pre-check query errors replaceSIEImport's pre-check discarded the .single() error, so a transient query failure (statement timeout, network error, 5xx via PostgREST) was indistinguishable from a genuinely absent row and got classified not_found. The replace loop in executeSIEImport then treated it as a stale watermark and imported the fiscal year fresh while the prior completed import's verifikationer were still in the ledger, with duplicate checks skipped in replace mode: silent duplicate verifikationer for a whole year (BFL 4:1 risk). Only PGRST116 (zero rows from .single()) now classifies as not_found; any other pre-check error returns rpc_error, which aborts the year in the replace loop. Tests cover both classifications plus the executeSIEImport-level abort. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Emil <emilmattsson14@gmail.com> * fix(import): fail closed on overlap lookup, verify zero surviving entries before a stale-watermark skip Review findings on #1763 (CodeRabbit + Swedish compliance review): - findOverlappingPeriodImports now uses fetchAllRows: query errors throw instead of returning [] (which let replace mode import fresh over rows it never resolved), pagination passes the PostgREST row cap, id tiebreak keeps the order total. - A stale-watermark skip (not_found/not_completed) is only trusted after a positive check that zero posted import entries survive in the fiscal year: replace_sie_import deletes by fiscal period, so entries can outlive their sie_imports row. Survivors or a failed check abort the year. - Contract comment tying the stale-race regex to the RPC's RAISE wording. - Suite-level beforeEach clears mocks and the event bus (repo convention). Signed-off-by: Emil <emilmattsson14@gmail.com> --------- Signed-off-by: Emil <emilmattsson14@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
3f6f1ab06e
commit
febb4cc0c2
@@ -31,7 +31,7 @@ import { parseSIEFile, validateSIEFile } from '@/lib/import/sie-parser'
|
||||
import { mergeParsedSIEFiles } from '@/lib/import/sie-merge'
|
||||
import { scanSieForCp1252Artifacts, formatSieArtifactWarning } from '@/lib/import/sie-artifact-scan'
|
||||
import { suggestMappings, getMappingStats, isSystemAccount } from '@/lib/import/account-mapper'
|
||||
import { loadMappings, generateImportPreview, executeSIEImport } from '@/lib/import/sie-import'
|
||||
import { loadMappings, generateImportPreview, executeSIEImport, findOverlappingPeriodImports } from '@/lib/import/sie-import'
|
||||
import { BAS_REFERENCE } from '@/lib/bookkeeping/bas-reference'
|
||||
import type { ProviderName } from '@/lib/providers/types'
|
||||
import { errorResponseFromCode } from '@/lib/errors/get-structured-error'
|
||||
@@ -921,16 +921,17 @@ export const arcimMigrationExtension: Extension = {
|
||||
} | null = null
|
||||
|
||||
if (fyStart && fyEnd) {
|
||||
const { data } = await supabase
|
||||
.from('sie_imports')
|
||||
.select('imported_at, fiscal_year_start, fiscal_year_end')
|
||||
.eq('company_id', companyId)
|
||||
.eq('status', 'completed')
|
||||
.lte('fiscal_year_start', fyEnd)
|
||||
.gte('fiscal_year_end', fyStart)
|
||||
.limit(1)
|
||||
.maybeSingle()
|
||||
priorImport = data
|
||||
// Newest first: several completed rows can overlap the same
|
||||
// year (manual upload + provider sync, or residue from
|
||||
// partially deleted data). The unordered .limit(1) this
|
||||
// replaces picked an arbitrary row, so the wizard could show
|
||||
// a stale "ersätter tidigare import från <date>" (issue #1667).
|
||||
// Import-time replace resolves ALL rows regardless of which
|
||||
// one is displayed here.
|
||||
const overlapping = await findOverlappingPeriodImports(
|
||||
supabase, companyId, fyStart, fyEnd
|
||||
)
|
||||
priorImport = overlapping[0] ?? null
|
||||
}
|
||||
|
||||
fileStatuses.push({
|
||||
|
||||
Reference in New Issue
Block a user