Localize system payroll statement labels in Russian
All checks were successful
Deploy production / deploy (push) Successful in 10m52s

This commit is contained in:
admin i 2026-10-15 15:16:36 +03:00
parent 4c44882ca5
commit 9d278f4743
3 changed files with 56 additions and 1 deletions

View File

@ -31,6 +31,7 @@ import {
formatPayrollDateTime,
formatPayrollMoney,
getPayrollLineDirection,
getPayrollLineLabel,
getPayrollPeriodLabel,
getPayrollPeriodRange,
groupPayrollLines,
@ -407,7 +408,7 @@ export function PayrollStatementDetailModal({
<div className="min-w-0">
<div className="flex flex-wrap items-center gap-1.5">
<p className="text-sm font-medium text-[var(--foreground)]">
{line.label}
{getPayrollLineLabel(line)}
</p>
{line.is_retro ? (
<span className="app-badge app-badge-accent px-2 py-0.5 text-[0.65rem]">

View File

@ -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);
}
});

View File

@ -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<string, { original: string; russian: string }> = {
BASE: { original: "Base accrual", russian: "Оклад" },
POINT_ADJUSTMENT: {
original: "Point-based recalculation",
russian: "Перерасчёт по баллам",
},
POINT_EXCESS: {
original: "Points above target",
russian: "Доплата за баллы сверх нормы",
},
};
export function getPayrollLineLabel(
line: Pick<PayrollStatementLine, "code" | "label">,
): 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" {