fix(salary): book lonevaxling pension provision from frozen run snapshot (#1382)
The book flow never populated pension_contribution/pension_slp, so a gross_deduction_pension line item reduced the salary entry but the 7410/2740 pension provision and 7533/2514 SLP lines were never posted. Derive both at the createSalaryRunEntries boundary from the run's frozen calculation_params.slpRate snapshot so the dashboard, MCP and v1 booking paths all emit the pension verifikat, and reuse the exact 1.058 factor via calculateLoneVaxlingPensionProvision shared with the planning calculator. Fixes #317 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
6782da3e9e
commit
c9625fa45c
@@ -421,6 +421,7 @@ describe('POST /salary-runs/:id/book', () => {
|
||||
total_net: 25500,
|
||||
total_avgifter: 10997,
|
||||
total_vacation_accrual: 0,
|
||||
calculation_params: { slpRate: 0.2426 },
|
||||
}
|
||||
|
||||
const employeeRow = {
|
||||
@@ -478,6 +479,12 @@ describe('POST /salary-runs/:id/book', () => {
|
||||
expect(body.data.entry_ids).toEqual(['je_salary', 'je_avg'])
|
||||
expect(body.meta.audit.voucher_number).toBe('L2026-0023')
|
||||
expect(body.meta.audit.voucher_url).toContain('je_salary')
|
||||
expect(mocks.createSalaryRunEntries).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
COMPANY_ID,
|
||||
USER_ID,
|
||||
expect.objectContaining({ calculation_params: { slpRate: 0.2426 } }),
|
||||
)
|
||||
})
|
||||
|
||||
it('returns PERIOD_LOCKED before invoking the engine when payment_date is locked', async () => {
|
||||
|
||||
@@ -190,8 +190,16 @@ export const POST = withApiV1<{ params: Promise<{ companyId: string; id: string
|
||||
'salary (gross + tax withholding + net payment)',
|
||||
'arbetsgivaravgifter',
|
||||
...(totalVacation > 0 || totalVacationAvgifter > 0 ? ['vacation accrual'] : []),
|
||||
// Pension cannot be detected without the engine: we'd need
|
||||
// to inspect line_items for löneväxling. Omitted from preview.
|
||||
...((employees as Array<{ line_items: Array<{ item_type: string; amount: number }> | null }>).some(
|
||||
(employee) =>
|
||||
(employee.line_items || []).some(
|
||||
(line) =>
|
||||
line.item_type === 'gross_deduction_pension' &&
|
||||
Math.round(Math.abs(line.amount) * 100) !== 0,
|
||||
),
|
||||
)
|
||||
? ['pension provision and SLP']
|
||||
: []),
|
||||
],
|
||||
note: 'A live call posts 2-4 verifikationer atomically via createSalaryRunEntries. Voucher numbers are assigned at commit time.',
|
||||
},
|
||||
@@ -234,6 +242,7 @@ export const POST = withApiV1<{ params: Promise<{ companyId: string; id: string
|
||||
total_net: (run as { total_net: number }).total_net,
|
||||
total_avgifter: (run as { total_avgifter: number }).total_avgifter,
|
||||
total_vacation_accrual: (run as { total_vacation_accrual: number }).total_vacation_accrual,
|
||||
calculation_params: (run as { calculation_params: Record<string, unknown> | null }).calculation_params,
|
||||
employees: (employees as EmpRow[]).map((sre) => ({
|
||||
employee_id: sre.employee_id,
|
||||
employment_type: sre.employee?.employment_type || 'employee',
|
||||
|
||||
Reference in New Issue
Block a user