diff --git a/frontend/src/components/finance/PayrollStatementDetailModal.tsx b/frontend/src/components/finance/PayrollStatementDetailModal.tsx index d1c602af..f7e84f16 100644 --- a/frontend/src/components/finance/PayrollStatementDetailModal.tsx +++ b/frontend/src/components/finance/PayrollStatementDetailModal.tsx @@ -31,6 +31,7 @@ import { formatPayrollDateTime, formatPayrollMoney, getPayrollLineDirection, + getPayrollLineLabel, getPayrollPeriodLabel, getPayrollPeriodRange, groupPayrollLines, @@ -407,7 +408,7 @@ export function PayrollStatementDetailModal({

- {line.label} + {getPayrollLineLabel(line)}

{line.is_retro ? ( diff --git a/frontend/src/lib/payroll.test.js b/frontend/src/lib/payroll.test.js index 8d777685..1caf48b2 100644 --- a/frontend/src/lib/payroll.test.js +++ b/frontend/src/lib/payroll.test.js @@ -6,6 +6,7 @@ import { formatPayrollDate, formatPayrollMoney, getPayrollLineDirection, + getPayrollLineLabel, getPayrollPeriodLabel, groupPayrollLines, normalizePayrollStatements, @@ -76,3 +77,29 @@ test("payroll lines are grouped and debit-like lines have a negative direction", assert.equal(getPayrollLineDirection("deduction"), "negative"); assert.equal(getPayrollLineDirection("payment"), "negative"); }); + +test("calculator-generated statement labels are displayed in Russian without changing snapshots", () => { + for (const [code, label, expected] of [ + ["BASE", "Base accrual", "Оклад"], + ["POINT_ADJUSTMENT", "Point-based recalculation", "Перерасчёт по баллам"], + ["POINT_EXCESS", "Points above target", "Доплата за баллы сверх нормы"], + ]) { + const line = Object.freeze({ code, label, amount: "80000.00" }); + assert.equal(getPayrollLineLabel(line), expected); + assert.equal(line.label, label); + assert.equal(line.amount, "80000.00"); + } + assert.equal(getPayrollLineLabel({ code: "BASE", label: " BASE ACCRUAL " }), "Оклад"); +}); + +test("Russian and custom payroll labels are preserved", () => { + for (const line of [ + { code: "BASE", label: "Базовое начисление" }, + { code: "POINT_ADJUSTMENT", label: "Перерасчёт по баллам" }, + { code: "BONUS", label: "Премия" }, + { code: "CUSTOM_BONUS", label: "Performance bonus" }, + { code: "CUSTOM_BASE", label: "Base accrual" }, + ]) { + assert.equal(getPayrollLineLabel(line), line.label); + } +}); diff --git a/frontend/src/lib/payroll.ts b/frontend/src/lib/payroll.ts index 07a8c789..d4b123c5 100644 --- a/frontend/src/lib/payroll.ts +++ b/frontend/src/lib/payroll.ts @@ -171,6 +171,33 @@ export function groupPayrollLines( .map((key) => ({ key, label: labels[key], lines: groups[key] })); } +// Localize labels produced by the language-neutral calculator at display time. +// Historical statement snapshots and user-defined labels remain unchanged. +const SYSTEM_LINE_LABELS: Record = { + BASE: { original: "Base accrual", russian: "Оклад" }, + POINT_ADJUSTMENT: { + original: "Point-based recalculation", + russian: "Перерасчёт по баллам", + }, + POINT_EXCESS: { + original: "Points above target", + russian: "Доплата за баллы сверх нормы", + }, +}; + +export function getPayrollLineLabel( + line: Pick, +): string { + const systemLabel = SYSTEM_LINE_LABELS[line.code]; + if ( + systemLabel && + line.label.trim().toLowerCase() === systemLabel.original.toLowerCase() + ) { + return systemLabel.russian; + } + return line.label; +} + export function getPayrollLineDirection( kind: PayrollStatementLine["kind"], ): "positive" | "negative" {