feat(compliance): InvoiceRowsCompleted behandlingshistorik event for migrated invoice rows (#2312) (#2357)
Every migrated sales invoice whose rows complete_invoice_rows writes, from the migration wizard or the hourly row-completion pass, now leaves one InvoiceRowsCompleted row in processing_history on a new Invoice aggregate: the writer, the provider, the consent, the row count, and the header VAT split before and after when the pass rewrote it (BFL 5 kap 11 §, BFNAR 2013:2 p. 9.16). One run shares one correlation id. lib/invoices/complete-invoice-rows.ts is the one TypeScript call site for the RPC and the one emitter: it appends only on wrote = true, records nothing for already_filled or failed, and keeps the append best-effort (logged, eventId null) like every other processing_history writer. The wizard runs on the user's session client, so MigrationOptions takes a lazy createHistoryClient for the service role. Invoice numbers stay out of the payload (the personnummer guard would drop ten-digit ones). Migration 20260906210100 widens the aggregate_type CHECK with Invoice and registers the event type; pg test covers the catalog row, the aggregate, and that the CHECK still refuses unknown aggregates. Claude-Session: https://claude.ai/code/session_01LvMaHcTnwAfxzgYD1fGYX1 Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Jakob Wennberg
Claude Fable 5.1
parent
ebbe50c0f3
commit
6906bc4aa2
+2
-1
@@ -50,7 +50,8 @@ const mockCount = vi.mocked(countRowlessInvoices)
|
||||
|
||||
const EMPTY = {
|
||||
candidates: 0, providerInvoices: 0, matched: 0, unmatched: 0, completed: 0, headersUpdated: 0,
|
||||
totalMismatch: 0, noLinesAtProvider: 0, rowsMismatch: 0, notHydrated: 0, vatUnresolved: 0, failed: 0, remaining: 0,
|
||||
totalMismatch: 0, noLinesAtProvider: 0, rowsMismatch: 0, notHydrated: 0, vatUnresolved: 0, failed: 0,
|
||||
historyAppended: 0, remaining: 0,
|
||||
hydration: { needed: 0, hydrated: 0, failed: 0, skippedForBudget: 0 }, dryRun: false,
|
||||
}
|
||||
|
||||
|
||||
@@ -159,6 +159,7 @@ export const GET = withCronContext('cron.arcim_migration_complete_invoice_lines'
|
||||
totalMismatch: 0,
|
||||
rowsMismatch: 0,
|
||||
failed: 0,
|
||||
historyAppended: 0,
|
||||
}
|
||||
|
||||
const completing = await ctx.forEach('register', registers, async ({ consent, candidates }, itemCtx) => {
|
||||
@@ -188,6 +189,7 @@ export const GET = withCronContext('cron.arcim_migration_complete_invoice_lines'
|
||||
totals.totalMismatch += result.totalMismatch
|
||||
totals.rowsMismatch += result.rowsMismatch
|
||||
totals.failed += result.failed
|
||||
totals.historyAppended += result.historyAppended
|
||||
itemCtx.log.info('migrated invoice rows completed for company', {
|
||||
companyId: consent.company_id,
|
||||
provider: consent.provider,
|
||||
@@ -198,6 +200,8 @@ export const GET = withCronContext('cron.arcim_migration_complete_invoice_lines'
|
||||
notHydrated: result.notHydrated,
|
||||
totalMismatch: result.totalMismatch,
|
||||
rowsMismatch: result.rowsMismatch,
|
||||
failed: result.failed,
|
||||
historyAppended: result.historyAppended,
|
||||
hydration: result.hydration,
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user