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:
co-authored by
Claude Fable 5
parent
90e4f668c9
commit
6bd85f94b6
@@ -113,7 +113,50 @@ describe('POST /api/bookkeeping/journal-entries/[id]/correct', () => {
|
|||||||
expect(status).toBe(200)
|
expect(status).toBe(200)
|
||||||
expect(body.data.reversal).toEqual(reversal)
|
expect(body.data.reversal).toEqual(reversal)
|
||||||
expect(body.data.corrected).toEqual(corrected)
|
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 () => {
|
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 { id } = await params
|
||||||
const validation = await validateBody(request, CorrectJournalEntrySchema)
|
const validation = await validateBody(request, CorrectJournalEntrySchema)
|
||||||
if (!validation.success) return validation.response
|
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 })
|
return NextResponse.json({ data: result })
|
||||||
},
|
},
|
||||||
{ requireWrite: true },
|
{ requireWrite: true },
|
||||||
|
|||||||
@@ -6,9 +6,11 @@
|
|||||||
* posted with the new lines. All three remain in the verifikationsserie,
|
* posted with the new lines. All three remain in the verifikationsserie,
|
||||||
* linked via reverses_id, reversed_by_id, and correction_of_id.
|
* linked via reverses_id, reversed_by_id, and correction_of_id.
|
||||||
*
|
*
|
||||||
* Body: `{ lines: [...] }`: the new balanced lines. The corrected entry
|
* Body: `{ lines: [...], description? }`: the new balanced lines. The
|
||||||
* inherits entry_date, fiscal_period_id, description, and voucher_series
|
* corrected entry inherits entry_date, fiscal_period_id, and voucher_series
|
||||||
* from the original.
|
* from the original. Its description defaults to "Rättelse: <original>";
|
||||||
|
* pass `description` to override it (e.g. when the original label named the
|
||||||
|
* wrong account).
|
||||||
*
|
*
|
||||||
* Idempotent (mandatory Idempotency-Key). Dry-runnable.
|
* Idempotent (mandatory Idempotency-Key). Dry-runnable.
|
||||||
*/
|
*/
|
||||||
@@ -108,7 +110,7 @@ export const POST = withApiV1<{ params: Promise<{ companyId: string; id: string
|
|||||||
details: { issues: parsed.error.issues.map((i) => ({ field: i.path.join('.'), message: i.message })) },
|
details: { issues: parsed.error.issues.map((i) => ({ field: i.path.join('.'), message: i.message })) },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const { lines } = parsed.data
|
const { lines, description } = parsed.data
|
||||||
|
|
||||||
const balance = validateBalance(lines)
|
const balance = validateBalance(lines)
|
||||||
if (!balance.valid) {
|
if (!balance.valid) {
|
||||||
@@ -178,6 +180,7 @@ export const POST = withApiV1<{ params: Promise<{ companyId: string; id: string
|
|||||||
ctx.userId,
|
ctx.userId,
|
||||||
entryId,
|
entryId,
|
||||||
lines,
|
lines,
|
||||||
|
{ description },
|
||||||
)
|
)
|
||||||
return ok(
|
return ok(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,8 +11,13 @@ import {
|
|||||||
} from '@/components/ui/dialog'
|
} from '@/components/ui/dialog'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
|
import { Label } from '@/components/ui/label'
|
||||||
import AccountCombobox from '@/components/bookkeeping/AccountCombobox'
|
import AccountCombobox from '@/components/bookkeeping/AccountCombobox'
|
||||||
import CorrectionPreview from '@/components/bookkeeping/CorrectionPreview'
|
import CorrectionPreview from '@/components/bookkeeping/CorrectionPreview'
|
||||||
|
import {
|
||||||
|
autoCorrectionDescription,
|
||||||
|
correctionDescriptionForSubmit,
|
||||||
|
} from '@/components/bookkeeping/correction-entry-description'
|
||||||
import { nextLineDescriptionForAccountChange } from '@/components/bookkeeping/correction-line-description'
|
import { nextLineDescriptionForAccountChange } from '@/components/bookkeeping/correction-line-description'
|
||||||
import { useToast } from '@/components/ui/use-toast'
|
import { useToast } from '@/components/ui/use-toast'
|
||||||
import { getErrorMessage } from '@/lib/errors/get-error-message'
|
import { getErrorMessage } from '@/lib/errors/get-error-message'
|
||||||
@@ -40,6 +45,7 @@ export default function CorrectionEntryDialog({ entry, open, onOpenChange, onCor
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [accounts, setAccounts] = useState<BASAccount[]>([])
|
const [accounts, setAccounts] = useState<BASAccount[]>([])
|
||||||
const [lines, setLines] = useState<CorrectionLine[]>([])
|
const [lines, setLines] = useState<CorrectionLine[]>([])
|
||||||
|
const [description, setDescription] = useState('')
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
|
|
||||||
const originalLines = ((entry.lines || []) as JournalEntryLine[])
|
const originalLines = ((entry.lines || []) as JournalEntryLine[])
|
||||||
@@ -57,6 +63,9 @@ export default function CorrectionEntryDialog({ entry, open, onOpenChange, onCor
|
|||||||
line_description: l.line_description || '',
|
line_description: l.line_description || '',
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
// Pre-fill the verifikationstext with the same auto text the server
|
||||||
|
// would generate; only a user edit is sent along (see handleSubmit).
|
||||||
|
setDescription(autoCorrectionDescription(entry.description))
|
||||||
fetchAccounts()
|
fetchAccounts()
|
||||||
}
|
}
|
||||||
}, [open, entry.id]) // eslint-disable-line react-hooks/exhaustive-deps
|
}, [open, entry.id]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
@@ -124,7 +133,12 @@ export default function CorrectionEntryDialog({ entry, open, onOpenChange, onCor
|
|||||||
const res = await fetch(`/api/bookkeeping/journal-entries/${entry.id}/correct`, {
|
const res = await fetch(`/api/bookkeeping/journal-entries/${entry.id}/correct`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ lines: apiLines }),
|
body: JSON.stringify({
|
||||||
|
lines: apiLines,
|
||||||
|
// Only sent when the user changed the auto prefill: the server
|
||||||
|
// fallback ("Rättelse: <original>") stays the source of truth.
|
||||||
|
description: correctionDescriptionForSubmit(description, entry.description),
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = await res.json()
|
const result = await res.json()
|
||||||
@@ -194,6 +208,23 @@ export default function CorrectionEntryDialog({ entry, open, onOpenChange, onCor
|
|||||||
{/* Live diff: original | storno | correction | förändring */}
|
{/* Live diff: original | storno | correction | förändring */}
|
||||||
<CorrectionPreview originalLines={originalLines} correctedLines={lines} />
|
<CorrectionPreview originalLines={originalLines} correctedLines={lines} />
|
||||||
|
|
||||||
|
{/* Verifikationstext for the new (corrected) entry. Pre-filled with
|
||||||
|
the auto text; editable so a header named after the wrong account
|
||||||
|
is not echoed on the correction (issue #1031). */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label htmlFor="correction-description">Verifikationstext</Label>
|
||||||
|
<Input
|
||||||
|
id="correction-description"
|
||||||
|
value={description}
|
||||||
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
|
placeholder={autoCorrectionDescription(entry.description)}
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Texten på den nya verifikationen. Ändra den om originalets beskrivning inte längre
|
||||||
|
stämmer, till exempel när rättelsen byter konto.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Corrected lines (editable) */}
|
{/* Corrected lines (editable) */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
import {
|
||||||
|
autoCorrectionDescription,
|
||||||
|
correctionDescriptionForSubmit,
|
||||||
|
} from '@/components/bookkeeping/correction-entry-description'
|
||||||
|
|
||||||
|
const ORIGINAL = 'Lån från närstående personer, långfristig del'
|
||||||
|
|
||||||
|
describe('autoCorrectionDescription', () => {
|
||||||
|
it('matches the server-side fallback format', () => {
|
||||||
|
expect(autoCorrectionDescription(ORIGINAL)).toBe(`Rättelse: ${ORIGINAL}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('correctionDescriptionForSubmit', () => {
|
||||||
|
it('sends nothing when the field still equals the auto prefill', () => {
|
||||||
|
expect(correctionDescriptionForSubmit(`Rättelse: ${ORIGINAL}`, ORIGINAL)).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sends nothing when the prefill only gained surrounding whitespace', () => {
|
||||||
|
expect(correctionDescriptionForSubmit(` Rättelse: ${ORIGINAL} `, ORIGINAL)).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sends nothing when the field was cleared (server fallback applies)', () => {
|
||||||
|
expect(correctionDescriptionForSubmit('', ORIGINAL)).toBeUndefined()
|
||||||
|
expect(correctionDescriptionForSubmit(' ', ORIGINAL)).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sends a user-edited description (the reported bug: relabel after account change)', () => {
|
||||||
|
expect(
|
||||||
|
correctionDescriptionForSubmit('Rättelse: Skulder till närstående personer, kortfristig del', ORIGINAL),
|
||||||
|
).toBe('Rättelse: Skulder till närstående personer, kortfristig del')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('trims a user-edited description before sending', () => {
|
||||||
|
expect(correctionDescriptionForSubmit(' Omföring till rätt konto ', ORIGINAL)).toBe(
|
||||||
|
'Omföring till rätt konto',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Entry-level verifikationstext handling for the correction dialog.
|
||||||
|
*
|
||||||
|
* The correction header defaults server-side to "Rättelse: <original>"
|
||||||
|
* (lib/core/bookkeeping/storno-service.ts). If the original entry was
|
||||||
|
* labelled after the wrong account (e.g. "Lån från närstående personer,
|
||||||
|
* långfristig del"), that header keeps echoing the wrong label even after
|
||||||
|
* the account itself is corrected (issue #1031). The dialog therefore
|
||||||
|
* pre-fills an editable field with the same auto text and only sends a
|
||||||
|
* description when the user actually changed it: an untouched auto prefill
|
||||||
|
* is omitted from the request so the server-side fallback stays the single
|
||||||
|
* source of truth for the default format. Same only-overwrite-auto-filled
|
||||||
|
* principle as the line-description guard from #1029.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** The auto text the server would generate for the correction header. */
|
||||||
|
export function autoCorrectionDescription(originalDescription: string): string {
|
||||||
|
return `Rättelse: ${originalDescription}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decide what to send as the correction's description.
|
||||||
|
* Returns undefined when the field is blank or still equals the auto prefill:
|
||||||
|
* in both cases the server-side fallback should apply.
|
||||||
|
*/
|
||||||
|
export function correctionDescriptionForSubmit(
|
||||||
|
currentDescription: string,
|
||||||
|
originalDescription: string,
|
||||||
|
): string | undefined {
|
||||||
|
const trimmed = currentDescription.trim()
|
||||||
|
if (!trimmed) return undefined
|
||||||
|
if (trimmed === autoCorrectionDescription(originalDescription).trim()) return undefined
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
@@ -2212,6 +2212,31 @@ describe('CorrectJournalEntrySchema', () => {
|
|||||||
})
|
})
|
||||||
expect(result.success).toBe(false)
|
expect(result.success).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('accepts an optional description and trims it', () => {
|
||||||
|
const result = CorrectJournalEntrySchema.safeParse({
|
||||||
|
description: ' Rättelse: Skulder till närstående personer, kortfristig del ',
|
||||||
|
lines: [
|
||||||
|
validJournalEntryLine({ account_number: '6200', debit_amount: 500, credit_amount: 0 }),
|
||||||
|
validJournalEntryLine({ account_number: '1930', debit_amount: 0, credit_amount: 500 }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
if (result.success) {
|
||||||
|
expect(result.data.description).toBe('Rättelse: Skulder till närstående personer, kortfristig del')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rejects a blank description (omit it to use the server fallback)', () => {
|
||||||
|
const result = CorrectJournalEntrySchema.safeParse({
|
||||||
|
description: ' ',
|
||||||
|
lines: [
|
||||||
|
validJournalEntryLine({ account_number: '6200', debit_amount: 500, credit_amount: 0 }),
|
||||||
|
validJournalEntryLine({ account_number: '1930', debit_amount: 0, credit_amount: 500 }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
expect(result.success).toBe(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
@@ -910,6 +910,10 @@ export const CreateJournalEntrySchema = z.object({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export const CorrectJournalEntrySchema = z.object({
|
export const CorrectJournalEntrySchema = z.object({
|
||||||
|
// Optional verifikationstext for the corrected entry. When omitted the
|
||||||
|
// server falls back to "Rättelse: <original description>"; supplying it lets
|
||||||
|
// the user replace a header that echoed the wrong account's label (#1031).
|
||||||
|
description: z.string().trim().min(1, 'Description cannot be empty').optional(),
|
||||||
lines: z.array(CreateJournalEntryLineSchema).min(2, 'At least two lines are required for double-entry'),
|
lines: z.array(CreateJournalEntryLineSchema).min(2, 'At least two lines are required for double-entry'),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -287,6 +287,43 @@ describe('correctEntry', () => {
|
|||||||
expect(result.corrected).toBeDefined()
|
expect(result.corrected).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('uses a caller-supplied description for the corrected entry (issue #1031)', async () => {
|
||||||
|
setupResults()
|
||||||
|
const supabase = makeClient()
|
||||||
|
await correctEntry(supabase as never, 'company-1', 'user-1', 'orig-1', correctedLines, {
|
||||||
|
description: 'Rättelse: Skulder till närstående personer, kortfristig del',
|
||||||
|
})
|
||||||
|
|
||||||
|
const entryInserts = inserts.filter((i) => i.table === 'journal_entries')
|
||||||
|
expect(entryInserts).toHaveLength(2)
|
||||||
|
const corrected = entryInserts[1].payload as { source_type: string; description: string }
|
||||||
|
expect(corrected.source_type).toBe('correction')
|
||||||
|
expect(corrected.description).toBe('Rättelse: Skulder till närstående personer, kortfristig del')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('falls back to "Rättelse: <original>" when no description is supplied', async () => {
|
||||||
|
setupResults()
|
||||||
|
const supabase = makeClient()
|
||||||
|
await correctEntry(supabase as never, 'company-1', 'user-1', 'orig-1', correctedLines)
|
||||||
|
|
||||||
|
const entryInserts = inserts.filter((i) => i.table === 'journal_entries')
|
||||||
|
const corrected = entryInserts[1].payload as { source_type: string; description: string }
|
||||||
|
expect(corrected.source_type).toBe('correction')
|
||||||
|
expect(corrected.description).toBe('Rättelse: Test purchase')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('falls back to the auto text when the supplied description is blank', async () => {
|
||||||
|
setupResults()
|
||||||
|
const supabase = makeClient()
|
||||||
|
await correctEntry(supabase as never, 'company-1', 'user-1', 'orig-1', correctedLines, {
|
||||||
|
description: ' ',
|
||||||
|
})
|
||||||
|
|
||||||
|
const entryInserts = inserts.filter((i) => i.table === 'journal_entries')
|
||||||
|
const corrected = entryInserts[1].payload as { description: string }
|
||||||
|
expect(corrected.description).toBe('Rättelse: Test purchase')
|
||||||
|
})
|
||||||
|
|
||||||
it('accepts a source_type=correction entry as the original (chained correction, BFL 5 kap. 5 §)', async () => {
|
it('accepts a source_type=correction entry as the original (chained correction, BFL 5 kap. 5 §)', async () => {
|
||||||
// The user just corrected entry A → got correction C. They now want to
|
// The user just corrected entry A → got correction C. They now want to
|
||||||
// correct C. Service must not care about source_type of the original:
|
// correct C. Service must not care about source_type of the original:
|
||||||
|
|||||||
@@ -130,6 +130,13 @@ export async function correctEntry(
|
|||||||
* the small TOCTOU window a second independent read would open.
|
* the small TOCTOU window a second independent read would open.
|
||||||
*/
|
*/
|
||||||
preloadedOriginal?: OriginalWithLines
|
preloadedOriginal?: OriginalWithLines
|
||||||
|
/**
|
||||||
|
* Verifikationstext for the corrected entry. Defaults to
|
||||||
|
* "Rättelse: <original description>". Supplying it lets a user who
|
||||||
|
* corrects the account BECAUSE the original label was wrong avoid the
|
||||||
|
* stale label echoing in the correction header (issue #1031).
|
||||||
|
*/
|
||||||
|
description?: string
|
||||||
}
|
}
|
||||||
): Promise<{ reversal: JournalEntry; corrected: JournalEntry; documentRelinkError?: string }> {
|
): Promise<{ reversal: JournalEntry; corrected: JournalEntry; documentRelinkError?: string }> {
|
||||||
// Validate the corrected lines are balanced
|
// Validate the corrected lines are balanced
|
||||||
@@ -340,7 +347,9 @@ export async function correctEntry(
|
|||||||
voucher_number: correctedVoucherNumber,
|
voucher_number: correctedVoucherNumber,
|
||||||
voucher_series: original.voucher_series || 'A',
|
voucher_series: original.voucher_series || 'A',
|
||||||
entry_date: correctedDate,
|
entry_date: correctedDate,
|
||||||
description: `Rättelse: ${original.description}`,
|
// A caller-supplied verifikationstext wins; blank falls back to the
|
||||||
|
// canonical auto text so the header never ends up empty.
|
||||||
|
description: options?.description?.trim() || `Rättelse: ${original.description}`,
|
||||||
source_type: 'correction',
|
source_type: 'correction',
|
||||||
correction_of_id: originalEntryId,
|
correction_of_id: originalEntryId,
|
||||||
status: 'draft',
|
status: 'draft',
|
||||||
|
|||||||
Reference in New Issue
Block a user