fix(table): закрыть пропуски исходного перечня AURORA-1892 по сверке 02.11.2026

validation.js: добавлены колонки, пропущенные в первой реализации (разделы 5/6.1
сверки) — approved.*, collegial.approved, ckk.*, contract_detail.* (включая
amount_foreign), corrected_plan, pay_ho/pay_rf, booking, rem_actual/
residual_after_actual, qN.total, new_plan, data.qN.economy. Отдельно добавлен
transfer_econ формы 2 — явное исключение из общего префикса transfer_*, так как
перечень Салмина прямо требует контроль минуса для этих колонок (раздел 6.3,
противоречие с Word). Тест validationUtils.test.js обновлён под это исключение
и дополнен проверкой полноты перечня и отсутствия дублей с remaining-booking.

FORM_1/OPER.js, AHR.js, CAP.js: перестановка колонок Q4 — «Комментарий» теперь
перед «Скорректированный план 4 кв.» во всех пяти таблицах (раздел 8 сверки).

Не включено (требует отдельного решения заказчика, раздел 12.2 сверки):
контроль месячного факта/СПОД, контроль факта расшифровочных листов сверх
плановых колонок, область действия правила для формы 4.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-11-03 14:38:27 +04:00
parent d518961365
commit 4675b461d8
5 changed files with 174 additions and 30 deletions

View File

@ -3568,14 +3568,14 @@ export const config = {
},
},
{
header: 'Скорректированный план 4 кв. {year:short}',
accessorKey: '4_kvartal_2026_skorr_plan_4_kv_26',
header: 'Комментарий',
accessorKey: '4_kvartal_2024_kommentariy',
columns: [
{
header: 'Сумма, тыс. руб. (без НДС)',
accessorKey: 'data.q4.corrected_plan',
columnLetter: 'GH',
size: 250,
header: 'Сумма, тыс. руб. (с учётом НДС)',
accessorKey: 'data.q4.adj_comment',
columnLetter: 'GG',
size: 300,
filterFn: 'contains',
},
],
@ -3587,14 +3587,14 @@ export const config = {
},
},
{
header: 'Комментарий',
accessorKey: '4_kvartal_2024_kommentariy',
header: 'Скорректированный план 4 кв. {year:short}',
accessorKey: '4_kvartal_2026_skorr_plan_4_kv_26',
columns: [
{
header: 'Сумма, тыс. руб. (с учётом НДС)',
accessorKey: 'data.q4.adj_comment',
columnLetter: 'GG',
size: 300,
header: 'Сумма, тыс. руб. (без НДС)',
accessorKey: 'data.q4.corrected_plan',
columnLetter: 'GH',
size: 250,
filterFn: 'contains',
},
],

View File

@ -3582,13 +3582,13 @@ export const config = {
},
},
{
header: 'Скорректированный план 4 кв. {year:short}',
accessorKey: '4_kvartal_2024_skorr_plan_4_kv_24',
header: 'Комментарий',
accessorKey: '4_kvartal_2024_kommentariy',
columns: [
{
header: 'Сумма, тыс. руб. (с учётом НДС)',
accessorKey: 'data.q4.corrected_plan',
columnLetter: 'GH',
accessorKey: 'data.q4.adj_comment',
columnLetter: 'GG',
size: 300,
filterFn: 'contains',
},
@ -3601,13 +3601,13 @@ export const config = {
},
},
{
header: 'Комментарий',
accessorKey: '4_kvartal_2024_kommentariy',
header: 'Скорректированный план 4 кв. {year:short}',
accessorKey: '4_kvartal_2024_skorr_plan_4_kv_24',
columns: [
{
header: 'Сумма, тыс. руб. (с учётом НДС)',
accessorKey: 'data.q4.adj_comment',
columnLetter: 'GG',
accessorKey: 'data.q4.corrected_plan',
columnLetter: 'GH',
size: 300,
filterFn: 'contains',
},

View File

@ -3566,13 +3566,13 @@ export const config = {
},
},
{
header: 'Скорректированный план 4 кв. {year:short}',
accessorKey: '4_kvartal_2024_skorr_plan_4_kv_24',
header: 'Комментарий',
accessorKey: '4_kvartal_2024_kommentariy',
columns: [
{
header: 'Сумма, тыс. руб. (с учётом НДС)',
accessorKey: 'data.q4.corrected_plan',
columnLetter: 'GH',
accessorKey: 'data.q4.adj_comment',
columnLetter: 'GG',
size: 300,
filterFn: 'contains',
},
@ -3585,13 +3585,13 @@ export const config = {
},
},
{
header: 'Комментарий',
accessorKey: '4_kvartal_2024_kommentariy',
header: 'Скорректированный план 4 кв. {year:short}',
accessorKey: '4_kvartal_2024_skorr_plan_4_kv_24',
columns: [
{
header: 'Сумма, тыс. руб. (с учётом НДС)',
accessorKey: 'data.q4.adj_comment',
columnLetter: 'GG',
accessorKey: 'data.q4.corrected_plan',
columnLetter: 'GH',
size: 300,
filterFn: 'contains',
},

View File

@ -88,7 +88,10 @@ const bookingLessBalanceRuleScope = {
// Перечень — из вложения Салмина М.П. к AURORA-1892 (AURORA-1892_Перечень_колонок.md),
// уточнённого кодовыми именами колонок (см. рулинги в PR). Корректировки и переносы
// (adj_*, base_correction, revision_*, seq_*, target_change, transfer_*) сюда не входят —
// там минус — нормальная операция.
// там минус — нормальная операция. Исключение — transfer_econ (перенос в фонд экономии,
// формы 2): перечень Салмина прямо требует контроль для этих колонок (см. сверку
// AURORA-1892 от 02.11.2026, раздел 6.3); общий префикс transfer_* на этот ключ не
// распространяется.
export const nonNegativeColumnKeys = [
// Основные листы форм 1, 2, 4 (Операц/АХР/КВ): план по кварталам и за год.
"data.plan.q1",
@ -165,6 +168,95 @@ export const nonNegativeColumnKeys = [
"data.amount_q4",
"data.limit_with_vat",
"data.limit_without_vat",
// Доработка по сверке AURORA-1892 от 02.11.2026 (разделы 5, 6.1): колонки из
// исходного перечня Салмина, пропущенные при первой реализации.
// Платежи следующих лет и сумма других ССП.
"data.contract_summary.future_y1",
"data.contract_summary.future_y2",
"data.contract_summary.other_ssp",
// Утверждённый план по кварталам и за год.
"data.approved.q1",
"data.approved.q2",
"data.approved.q3",
"data.approved.q4",
"data.approved.year",
// Сумма коллегиального органа.
"data.collegial.approved",
// Расходы ЦКК по кварталам и по сметам РФ.
"data.ckk.q1",
"data.ckk.q2",
"data.ckk.q3",
"data.ckk.q4",
"data.ckk.rf_schedule",
// Расходы договора по кварталам, по сметам РФ и сумма в валюте.
"data.contract_detail.q1",
"data.contract_detail.q2",
"data.contract_detail.q3",
"data.contract_detail.q4",
"data.contract_detail.rf_schedule",
"data.contract_detail.amount_foreign",
// Скорректированный план по кварталам.
"data.q1.corrected_plan",
"data.q2.corrected_plan",
"data.q3.corrected_plan",
"data.q4.corrected_plan",
// Части платежа ГО и РФ.
"data.q1.pay_ho",
"data.q1.pay_rf",
"data.q2.pay_ho",
"data.q2.pay_rf",
"data.q3.pay_ho",
"data.q3.pay_rf",
"data.q4.pay_ho",
"data.q4.pay_rf",
// Бронь квартала (отдельно от ввода брони на 2026/2027 годы выше).
"data.q1.booking",
"data.q2.booking",
"data.q3.booking",
"data.q4.booking",
// Остаток после факта (формы 1 и 2 используют разные кодовые имена).
"data.q1.rem_actual",
"data.q2.rem_actual",
"data.q3.rem_actual",
"data.q4.rem_actual",
"data.q1.residual_after_actual",
"data.q2.residual_after_actual",
"data.q3.residual_after_actual",
"data.q4.residual_after_actual",
// Перенос в фонд экономии формы 1 (data.economy — другой ключ, чем transfer_econ
// формы 2 ниже, и не входит в исключённый префикс transfer_*).
"data.q1.economy",
"data.q2.economy",
"data.q3.economy",
"data.q4.economy",
// Перенос в фонд экономии формы 2 — исключение из transfer_*, см. комментарий
// к nonNegativeColumnKeys выше.
"data.q1.transfer_econ",
"data.q2.transfer_econ",
"data.q3.transfer_econ",
"data.q4.transfer_econ",
// Всего при закрытии квартала.
"data.q1.total",
"data.q2.total",
"data.q3.total",
"data.q4.total",
// Новый план на подпись (кварталов II–IV; для I квартала поле отсутствует).
"data.q2.new_plan",
"data.q3.new_plan",
"data.q4.new_plan",
];
const isNegativeNumber = (value) =>

View File

@ -51,11 +51,15 @@ test('a scoped rule is disabled outside its form/sheet scope', () => {
assert.equal(isValidationRuleEnabled(rule, { formType: 'FORM_2', sheetName: 'AHR' }), false);
});
test('the shared nonNegativeColumnKeys list excludes correction and transfer columns', () => {
test('the shared nonNegativeColumnKeys list excludes correction and transfer columns, except the explicit transfer_econ carve-out', () => {
const excludedPrefixes = ['adj_', 'base_correction', 'revision_', 'seq_', 'target_change', 'transfer_'];
// AURORA-1892, сверка 02.11.2026 (раздел 6.3): перечень Салмина прямо требует контроль
// минуса для transfer_econ, в отличие от остальных transfer_* — это намеренное исключение.
const allowedException = 'transfer_econ';
for (const key of nonNegativeColumnKeys) {
const segment = key.split('.').pop();
if (segment === allowedException) continue;
for (const prefix of excludedPrefixes) {
assert.equal(
segment.startsWith(prefix),
@ -65,3 +69,51 @@ test('the shared nonNegativeColumnKeys list excludes correction and transfer col
}
}
});
test('nonNegativeColumnKeys has no duplicate entries', () => {
assert.equal(nonNegativeColumnKeys.length, new Set(nonNegativeColumnKeys).size);
});
test('nonNegativeColumnKeys covers the gaps confirmed by the AURORA-1892 audit (02.11.2026)', () => {
const mustInclude = [
// Разделы 5/6.1 сверки: обязательные показатели, пропущенные в первой реализации.
'data.contract_summary.future_y1',
'data.contract_summary.other_ssp',
'data.approved.q1',
'data.approved.year',
'data.collegial.approved',
'data.ckk.q1',
'data.ckk.rf_schedule',
'data.contract_detail.q1',
'data.contract_detail.amount_foreign',
'data.q4.corrected_plan',
'data.q1.pay_ho',
'data.q1.pay_rf',
'data.q1.booking',
'data.q1.rem_actual',
'data.q1.residual_after_actual',
'data.q4.total',
'data.q2.new_plan',
// Раздел 6.3: исключение из transfer_* по прямому требованию перечня Салмина.
'data.q1.transfer_econ',
'data.q4.transfer_econ',
// Раздел 5: экономия формы 1 — другой ключ, чем transfer_econ, не затронут исключением.
'data.q1.economy',
];
for (const key of mustInclude) {
assert.ok(nonNegativeColumnKeys.includes(key), `${key} must be in nonNegativeColumnKeys`);
}
// Д-статус из раздела 5/6.1: эти остатки уже покрыты правилом remaining-booking
// и не должны дублироваться здесь (иначе пользователь увидит две причины ошибки).
const mustExclude = [
'data.q1.rem_booking',
'data.q4.rem_booking',
'data.q1.residual_after_booking',
'data.q4.residual_after_booking',
];
for (const key of mustExclude) {
assert.ok(!nonNegativeColumnKeys.includes(key), `${key} is already covered by remaining-booking and must not duplicate here`);
}
});