fix(transactions): derive pre-migration marker cutoff from imported voucher dates, not fiscal_year_end (#2047)

* fix(transactions): derive pre-migration marker cutoff from imported voucher dates, not fiscal_year_end

A SIE file exported mid-year still declares the full fiscal year in #RAR 0,
so sie_imports.fiscal_year_end is a future date for mid-year migrators and
the 'fran perioden fore din migrering' marker fired on every new bank
transaction until New Year. The cutoff now comes from the latest posted
source_type='import' entry_date (excluding the M-series omforingsverifikation,
which is deliberately dated at fiscal year end), so it tracks where the
imported bokforing actually ends and self-corrects on undo/replace.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UU5QbL3p8xNB3tSZDbyrr3

* fix(transactions): arm pre-migration cutoff on completed SIE import, exclude omforing by description

Skeptic findings on the frozen commit: (1) source_type='import' is accepted
from v1 API clients, so a never-migrated company with an API-labeled backfill
would get a false cutoff; the marker is now armed only when a completed
sie_imports row exists. (2) Imported vouchers keep the source file's voucher
series, so excluding series M dropped genuine M-series vouchers (6 prod
companies); the omforingsverifikation is now excluded by its hardcoded
description prefix instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UU5QbL3p8xNB3tSZDbyrr3

* docs(transactions): state the pre-migration cutoff residuals truthfully

The ledger duplicate guard only reaches the completed-year single-skip case
(7-day window vs a fiscal-year-end-dated aggregate omforing), so it is not
a general backstop for the skip-window gap; the gap is accepted on rarity.
Also documents the rattelse-rename fragility of the description-keyed
exclusion. Skeptic re-review condition, no code change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UU5QbL3p8xNB3tSZDbyrr3

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mattsson
2026-08-30 23:16:34 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent f43a6653f1
commit 84ba8323b4
4 changed files with 192 additions and 14 deletions
+10 -14
View File
@@ -76,6 +76,7 @@ import { cn, formatCurrency, formatDate } from '@/lib/utils'
import { roundOre } from '@/lib/money'
import type { TransactionCategory, CreateTransactionInput, Invoice, Customer, SupplierInvoice, Supplier, VatTreatment, EntityType, LinePatternEntry, BookingTemplateLibrary } from '@/types'
import type { SuggestedTemplate } from '@/lib/transactions/category-suggestions'
import { fetchMigrationCoverageEnd } from '@/lib/transactions/migration-coverage'
import { isImportedTransaction } from '@/lib/transactions/origin'
import { computeJeUnderlagStatus, type JeUnderlagStatus } from '@/lib/transactions/underlag-status'
import { isWithinBounds, resolvePeriodBounds } from '@/lib/transactions/period-filter'
@@ -560,10 +561,13 @@ export default function TransactionsPage() {
// "Kör matchning igen" in the review surface.
const [rerunningMatch, setRerunningMatch] = useState(false)
// End of the company's completed SIE-import coverage (latest
// fiscal_year_end). Drives the quiet "från perioden före din migrering"
// marker on inbox rows: period-based on purpose, it labels which period a
// row belongs to, it never suggests a sync skip date (that was #917).
// End of the company's SIE-migration data coverage (latest imported
// voucher date, see lib/transactions/migration-coverage.ts). Drives the
// quiet "från perioden före din migrering" marker on inbox rows: date-based
// on purpose, it labels rows the imported bokföring should already cover,
// it never suggests a sync skip date (that was #917). Not fiscal_year_end:
// for a mid-year migration that is a future date and the marker fired on
// every new transaction until New Year.
const [sieCoverageEnd, setSieCoverageEnd] = useState<string | null>(null)
useEffect(() => {
if (!companyId) {
@@ -572,17 +576,9 @@ export default function TransactionsPage() {
}
let cancelled = false
;(async () => {
const { data } = await supabase
.from('sie_imports')
.select('fiscal_year_end')
.eq('company_id', companyId)
.eq('status', 'completed')
.not('fiscal_year_end', 'is', null)
.order('fiscal_year_end', { ascending: false })
.limit(1)
.maybeSingle()
const coverageEnd = await fetchMigrationCoverageEnd(supabase, companyId)
if (!cancelled) {
setSieCoverageEnd((data as { fiscal_year_end?: string } | null)?.fiscal_year_end || null)
setSieCoverageEnd(coverageEnd)
}
})()
return () => {