feat(salary): repay utlägg with the salary as a tax-free payslip line (#2361)
* feat(salary): repay utlägg with the salary as a tax-free payslip line (#2331) - expense_reimbursement line type: kostnadsersättning outside gross, tax, avgifter and the AGI. The engine adds tax-free reimbursements (utlägg, skattefritt traktamente, skattefri milersättning) to the net payout only. - booking debits the claim's liability account (2820) on top of gross, never a 7xxx cost; a run that only repays utlägg posts 2820 D / 1930 K instead of being treated as a nollkörning - salary_line_items.source_expense_claim_id (tenant-scoped FK, cascade, one payslip line per claim); settle_expense_claims_via_salary_run marks the claims paid with an expense_payout_batches row pointing at the salary verifikat, no second verifikat, idempotent on retry; wired into bookLoadedRun and the v1 book route with a pre-check before posting - create_expense_payout_batch refuses claims scheduled on a payslip (ON_PAYSLIP); deleteExpenseClaim refuses once the run has left draft - "Lägg till utlägg" on the employee row of a draft run; the payslip page labels and removes the lines - pg-real: tests/pg/utlagg-via-lon.pg.test.ts + ON_PAYSLIP case Claude-Session: https://claude.ai/code/session_01LvMaHcTnwAfxzgYD1fGYX1 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(salary): PR #2361 review: claim delete cannot cascade into a booked payslip; AGI excludes the utlägg line - salary_line_items_source_expense_claim_fkey is ON DELETE RESTRICT (edited in the unmerged 20260906210300): the database refuses to delete a claim a payslip line still references, whichever path issues the DELETE - deleteExpenseClaim removes the draft line first (before the storno) and keeps refusing with ON_PAYSLIP once the run has left draft - pg-real: delete refused with 23503 on a booked and on a draft run; the app order (line, then claim) succeeds - unit: AGI builder keeps FK011/FK001/FK487 and emits no benefit field for an expense_reimbursement line (FK011 derives from sre.gross_salary; only benefit_* types are read from line items) Claude-Session: https://claude.ai/code/session_01LvMaHcTnwAfxzgYD1fGYX1 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- 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
Claude Fable 5.1
Jakob Wennberg
parent
0069a3f59a
commit
4fce2d7b94
@@ -3395,6 +3395,22 @@ const SALARY: Record<string, StructuredErrorEntry> = {
|
||||
message_sv: 'Lönekörningen är kopplad till en verifikation och kan inte raderas (BFL 5 kap räkenskapsinformation).',
|
||||
message_en: 'Salary run is linked to a journal entry and cannot be deleted (BFL 5 kap räkenskapsinformation).',
|
||||
},
|
||||
// Utlägg repaid with the salary (#2331).
|
||||
SALARY_RUN_NO_OPEN_EXPENSE_CLAIMS: {
|
||||
httpStatus: 404,
|
||||
message_sv: 'Den anställda har inga öppna utlägg att lägga till.',
|
||||
message_en: 'The employee has no open expense claims to add.',
|
||||
},
|
||||
EXPENSE_CLAIM_ALREADY_ON_PAYSLIP: {
|
||||
httpStatus: 409,
|
||||
message_sv: 'Utlägget ligger redan på ett lönebesked.',
|
||||
message_en: 'The expense claim is already on a payslip.',
|
||||
},
|
||||
SALARY_RUN_EXPENSE_CLAIM_NOT_OPEN: {
|
||||
httpStatus: 409,
|
||||
message_sv: 'Ett utlägg på lönebeskedet är inte längre öppet (utbetalt eller borttaget). Ta bort raden och beräkna om innan bokföring.',
|
||||
message_en: 'An expense claim on the payslip is no longer open (paid or removed). Remove the line and recalculate before booking.',
|
||||
},
|
||||
// Phase 5 PR-3: additional import error codes.
|
||||
SIE_IMPORT_DUPLICATE: {
|
||||
httpStatus: 409,
|
||||
|
||||
Reference in New Issue
Block a user