feat(invoices): ROT/RUT payout file dialog and file guards (#1380)
* feat(invoices): ROT/RUT payout file dialog and file guards Rebuild the UI for the existing headless HUS V6 payout-file flow (demanded via #789): a dialog on the invoices page to pick eligible paid ROT/RUT invoices, generate the XML, download it and track request status. Adds file-level guards from the Skatteverket spec: future payment dates blocked, one file per payment year, max 100 cases per file, with per-invoice blocker messages. Submission stays manual (upload + sign in the SKV e-service); no direct submission API exists. Fixes #789 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(invoices): compute rot-rut gating date in Europe/Stockholm The candidate and begäran date defaults used the UTC calendar day, which near midnight Swedish time could wrongly block or admit an invoice via FUTURE_PAYMENT_DATE and shift the 31 January deadline warning. Use getSwedishLocalDate() like the bookkeeping engine. Raised by the Swedish compliance review on PR #1380. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
df34cae9bf
commit
00d4c8a49e
@@ -232,6 +232,36 @@ describe('POST /api/rot-rut/payout-file', () => {
|
||||
expect(body.error.code).toBe('ROT_RUT_INVOICES_BLOCKED')
|
||||
})
|
||||
|
||||
it('rejects a file that mixes payment years', async () => {
|
||||
const otherInvoiceId = '33333333-3333-4333-8333-333333333333'
|
||||
enqueue({
|
||||
data: [
|
||||
makePaidRotInvoice(),
|
||||
makePaidRotInvoice({
|
||||
id: otherInvoiceId,
|
||||
invoice_number: 'F-2025',
|
||||
paid_at: '2025-12-30T10:00:00Z',
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
const response = await payoutFilePOST(
|
||||
createMockRequest('/api/rot-rut/payout-file', {
|
||||
method: 'POST',
|
||||
body: { deduction_type: 'rot', invoice_ids: [INVOICE_ID, otherInvoiceId] },
|
||||
}),
|
||||
)
|
||||
const { status, body } = await parseJsonResponse<{
|
||||
error: { code: string; details?: { blockers: Array<{ code: string }> } }
|
||||
}>(response)
|
||||
|
||||
expect(status).toBe(400)
|
||||
expect(body.error.code).toBe('ROT_RUT_INVOICES_BLOCKED')
|
||||
expect(body.error.details?.blockers).toEqual([
|
||||
expect.objectContaining({ invoice_id: otherInvoiceId, code: 'MIXED_PAYMENT_YEARS' }),
|
||||
])
|
||||
})
|
||||
|
||||
it('returns 404 when an invoice id does not belong to the company', async () => {
|
||||
enqueue({ data: [] })
|
||||
const response = await payoutFilePOST(
|
||||
|
||||
Reference in New Issue
Block a user