fix(bookkeeping): editable verifikationstext on andringsverifikation (#1035)

The correction header was always built server-side as
"Rattelse: <original description>". When the original entry was labelled
after the wrong account, the correction kept echoing that stale label even
after the user switched to the correct account (follow-up to the
line-description fix in #1029).

- CorrectJournalEntrySchema gains an optional trimmed description
- correctEntry() accepts options.description; blank or absent falls back
  to the canonical "Rattelse: <original>" auto text
- both correct routes (dashboard + v1, which share the schema) thread the
  description through
- CorrectionEntryDialog surfaces an editable verifikationstext field,
  pre-filled with the auto text; an untouched or cleared prefill is NOT
  sent, so the server-side fallback stays the source of truth (same
  only-overwrite-auto-filled principle as #1029)

Forward-only: already-posted corrections are immutable per BFL.

Fixes #1031

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-07-16 18:14:02 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent 90e4f668c9
commit 6bd85f94b6
10 changed files with 236 additions and 8 deletions
@@ -113,7 +113,50 @@ describe('POST /api/bookkeeping/journal-entries/[id]/correct', () => {
expect(status).toBe(200)
expect(body.data.reversal).toEqual(reversal)
expect(body.data.corrected).toEqual(corrected)
expect(mockCorrectEntry).toHaveBeenCalledWith(expect.anything(), 'company-1', 'user-1', 'entry-1', lines)
expect(mockCorrectEntry).toHaveBeenCalledWith(expect.anything(), 'company-1', 'user-1', 'entry-1', lines, {
description: undefined,
})
})
it('threads an optional description through to correctEntry (issue #1031)', async () => {
const reversal = makeJournalEntry({ id: 'reversal-1', reverses_id: 'entry-1', source_type: 'storno' })
const corrected = makeJournalEntry({ id: 'corrected-1', correction_of_id: 'entry-1', source_type: 'correction' })
mockCorrectEntry.mockResolvedValue({ reversal, corrected })
const lines = [
{ account_number: '2893', debit_amount: 1000, credit_amount: 0 },
{ account_number: '1930', debit_amount: 0, credit_amount: 1000 },
]
const request = createMockRequest('/api/bookkeeping/journal-entries/entry-1/correct', {
method: 'POST',
body: { lines, description: 'Rättelse: Skulder till närstående personer, kortfristig del' },
})
const response = await POST(request, createMockRouteParams({ id: 'entry-1' }))
const { status } = await parseJsonResponse(response)
expect(status).toBe(200)
expect(mockCorrectEntry).toHaveBeenCalledWith(expect.anything(), 'company-1', 'user-1', 'entry-1', lines, {
description: 'Rättelse: Skulder till närstående personer, kortfristig del',
})
})
it('returns 400 when description is blank', async () => {
const lines = [
{ account_number: '1930', debit_amount: 1000, credit_amount: 0 },
{ account_number: '3001', debit_amount: 0, credit_amount: 1000 },
]
const request = createMockRequest('/api/bookkeeping/journal-entries/entry-1/correct', {
method: 'POST',
body: { lines, description: ' ' },
})
const response = await POST(request, createMockRouteParams({ id: 'entry-1' }))
const { status, body } = await parseJsonResponse<{ error: string }>(response)
expect(status).toBe(400)
expect(body.error).toBe('Validation failed')
expect(mockCorrectEntry).not.toHaveBeenCalled()
})
it('maps an unbalanced-correction engine error to the canonical envelope (400)', async () => {
@@ -13,7 +13,9 @@ export const POST = withRouteContext<{ params: Promise<{ id: string }> }>(
const { id } = await params
const validation = await validateBody(request, CorrectJournalEntrySchema)
if (!validation.success) return validation.response
const result = await correctEntry(supabase, companyId, user.id, id, validation.data.lines)
const result = await correctEntry(supabase, companyId, user.id, id, validation.data.lines, {
description: validation.data.description,
})
return NextResponse.json({ data: result })
},
{ requireWrite: true },