* 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>
41 lines
2.1 KiB
TypeScript
41 lines
2.1 KiB
TypeScript
/**
|
|
* User-facing Swedish messages for createPayoutBatch refusal codes, shared by
|
|
* POST /api/expense-claims/payouts and POST /api/transactions/[id]/match-expense-payout
|
|
* so the same refusal reads the same on both surfaces.
|
|
*/
|
|
export const PAYOUT_ERROR_MESSAGES: Record<string, { message: string; status: number }> = {
|
|
NO_CLAIMS: { message: 'Välj minst ett utlägg att betala ut.', status: 400 },
|
|
CLAIMS_NOT_FOUND: { message: 'Något av utläggen hittades inte.', status: 404 },
|
|
ALREADY_PAID: { message: 'Något av utläggen är redan utbetalt.', status: 409 },
|
|
MIXED_CLAIMANTS: {
|
|
message: 'En utbetalning kan bara avse en person. Dela upp per person.',
|
|
status: 400,
|
|
},
|
|
MIXED_LIABILITY: {
|
|
message: 'Utläggen har olika skuldkonton och kan inte betalas ut tillsammans.',
|
|
status: 400,
|
|
},
|
|
FISCAL_PERIOD_NOT_FOUND: {
|
|
message: 'Inget räkenskapsår täcker utbetalningsdatumet.',
|
|
status: 400,
|
|
},
|
|
BATCH_INSERT_FAILED: { message: 'Utbetalningen kunde inte sparas.', status: 500 },
|
|
PERIOD_LOCKED: { message: 'Perioden är låst. Lås upp den innan du bokför utbetalningen.', status: 409 },
|
|
ACCOUNT_NOT_IN_CHART: { message: 'Kontot finns inte i kontoplanen.', status: 400 },
|
|
INVALID_CASH_ACCOUNT: { message: 'Ange ett likvidkonto i 19xx-serien.', status: 400 },
|
|
FORBIDDEN: { message: 'Du saknar behörighet att bokföra utbetalningar i det här företaget.', status: 403 },
|
|
// Bank-line mode (p_transaction_id): the transfer that repays the claims.
|
|
TX_NOT_FOUND: { message: 'Transaktionen hittades inte.', status: 404 },
|
|
TX_ALREADY_BOOKED: { message: 'Transaktionen är redan bokförd.', status: 409 },
|
|
TX_CURRENCY: { message: 'Utlägg betalas ut i SEK och transaktionen har en annan valuta.', status: 400 },
|
|
TX_AMOUNT_MISMATCH: {
|
|
message: 'Beloppet stämmer inte med de valda utläggen. Välj de utlägg som överföringen täcker.',
|
|
status: 400,
|
|
},
|
|
// Scheduled on a payslip (#2331): that salary run repays it.
|
|
ON_PAYSLIP: {
|
|
message: 'Något av utläggen ligger på ett lönebesked och betalas ut via lön. Ta bort raden från lönebeskedet först.',
|
|
status: 409,
|
|
},
|
|
}
|