Fix/invoice delivery and payment accounts (#1116)

* fix: reconcile annual reports with final closing entries

* test: cover annual report depreciation and VAT balances

* Merge remote-tracking branch 'origin/main' into fix/usr-fdbck-ch

* fix: show exact invoice delivery details

* fix: use currency account in invoice emails

* fix: address invoice delivery review feedback

* fix: harden invoice delivery and payment accounts

* test: assert RLS-denied zero-row updates

* fix: close remaining invoice compliance gaps

* fix: harden invoice archive authorization

* fix: close invoice delivery review findings

* fix: verify delivery finalization results

* fix: cap combined invoice email recipients

* fix: close final invoice compliance findings

* fix: prevent stale payment account saves

* test: prove invoice delivery isolation

* fix: close invoice privacy review findings

* test: normalize delivery retention dates
This commit is contained in:
Mattsson
2026-07-23 09:54:02 +02:00
committed by GitHub
parent 321e684523
commit 466e55a015
83 changed files with 6619 additions and 671 deletions
@@ -22,12 +22,14 @@ vi.mock('@/lib/bokslut/arsredovisning/model', () => ({
}))
vi.mock('@/lib/bokslut/arsredovisning/version-service', () => ({
createAnnualReportVersion: vi.fn(),
hasStatementIntegrityErrors: vi.fn(),
listAnnualReportVersions: vi.fn(),
}))
import { buildCanonicalAnnualReport } from '@/lib/bokslut/arsredovisning/model'
import {
createAnnualReportVersion,
hasStatementIntegrityErrors,
listAnnualReportVersions,
} from '@/lib/bokslut/arsredovisning/version-service'
import { GET, POST } from '../route'
@@ -57,6 +59,7 @@ function setup() {
vi.mocked(buildCanonicalAnnualReport).mockResolvedValue({
validation: { ok: true },
} as never)
vi.mocked(hasStatementIntegrityErrors).mockReturnValue(false)
return mock
}
@@ -134,6 +137,40 @@ describe('annual report versions route', () => {
)
})
it.each(['snapshot', 'finalize'] as const)(
'rejects an inconsistent report before creating a %s version',
async (action) => {
const { enqueue } = setup()
enqueue({ data: { id: 'period-1' } })
vi.mocked(hasStatementIntegrityErrors).mockReturnValue(true)
vi.mocked(buildCanonicalAnnualReport).mockResolvedValue({
validation: {
ok: false,
issues: [{ code: 'AR-RESULT-MISMATCH', severity: 'error' }],
},
} as never)
const { status, body } = await parseJsonResponse<{
error: { code: string; message: string; message_en: string }
}>(
await POST(
createMockRequest('/x', { method: 'POST', body: { action } }),
params,
),
)
expect(status).toBe(409)
expect(body.error).toEqual(
expect.objectContaining({
code: 'ARSREDOVISNING_INCOMPLETE',
message: expect.any(String),
message_en: expect.any(String),
}),
)
expect(createAnnualReportVersion).not.toHaveBeenCalled()
},
)
it('accepts a VD as the fastställelseintyg signer', async () => {
const { enqueue } = setup()
enqueue({ data: { id: 'period-1' } })
@@ -8,6 +8,7 @@ import { createServiceClient } from '@/lib/supabase/server'
import { buildCanonicalAnnualReport } from '@/lib/bokslut/arsredovisning/model'
import {
createAnnualReportVersion,
hasStatementIntegrityErrors,
listAnnualReportVersions,
} from '@/lib/bokslut/arsredovisning/version-service'
@@ -83,16 +84,17 @@ export const POST = withRouteContext(
}
: undefined,
})
if (hasStatementIntegrityErrors(model)) {
return errorResponseFromCode('ARSREDOVISNING_INCOMPLETE', log, {
requestId,
details: model.validation,
})
}
if (validation.data.action === 'finalize' && !model.validation.ok) {
return NextResponse.json(
{
error: {
code: 'ARSREDOVISNING_INCOMPLETE',
details: model.validation,
},
},
{ status: 409 },
)
return errorResponseFromCode('ARSREDOVISNING_INCOMPLETE', log, {
requestId,
details: model.validation,
})
}
const data = await createAnnualReportVersion(
validation.data.action === 'finalize' ? createServiceClient() : supabase,