fix(export): выгрузка форм 1 и 4 — порядок столбцов и заголовки как на экране
Выгрузка Excel листов АХР/КВ/Операц/Структура форм 1 и 4 расходилась с экраном
по нескольким независимым причинам:
- FORM_4: _patch_form4_regular_columns() вызывался дважды — второй вызов
стирал только что добавленные actual_rsbu_spod_q1..q3, поэтому в выгрузке
были только «Факт 4 кв + СПОД», без «Факт 1/2/3 кв». Убрал второй вызов
и больше не удаляю эти ключи в самом патче.
- _patch_form4_actual_rsbu_spod_columns: SQL отдаёт actual_rsbu_spod только
для 4 кв.; для 1-3 кв. ключ теперь q{n}.actual_quarter (как на экране),
не q{n}.actual_rsbu_spod (всегда пустой).
- Базовая раскладка FORM1_DEPTH_COLUMNS (отражается и в FORM_4, т.к. она из неё
строится): убрал collegial.empty и группы "shift" (ключ sum, SQL не отдаёт);
в q2/q3 добавил comment_correct (adj_comment) сразу после reserve_correct —
раньше его не было вовсе; в q4 то же поле вынес из rev_correct в отдельный
comment_correct на то же место (было в начале квартала вместе с комментарием
корректировки плана). group "approved.plan" переименована из скопированного
"Резерв Финансового Комитета" в "План {year}"; q4 "quarter_correct" — из
ошибочного "Факт РСБУ 3 кв" в "Факт РСБУ 4 кв {year_short}".
- render_column_years: добавлен плейсхолдер year_plus_2 (year+2); contract_summary
получил future_y2 "Платежи в {year_plus_2} году" (раньше его не было, и FORM_4
patch всё равно дублировал его с неверным {next_year}). На экране «Операц»
формы 1 такой колонки нет — для неё новая FORM1_OPER_DEPTH_COLUMNS без future_y2.
- FORM4_OPER: proc_plan/proc_method после переименования уезжали в конец блока
ckk (pop+переприсвоение добавляет ключ в конец словаря) — вернул порядок
экрана через _reorder_children.
- web/.../FORM_1/{AHR,CAP,OPER}.js: в q4 узел "Комментарий" (adj_comment, GG)
шёл после "Скорректированный план" (corrected_plan, GH) — переставил перед,
как в 1-3 кв.
Issue: #253 (AURORA-1905, эпик AURORA-1876, Высокий приоритет)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
2a287480b6
commit
b04c87f47c
@ -90,6 +90,9 @@ FORM1_DEPTH_COLUMNS: dict = {
|
||||
"future_y1": {
|
||||
"name": "Платежи в {next_year} году",
|
||||
},
|
||||
"future_y2": {
|
||||
"name": "Платежи в {year_plus_2} году",
|
||||
},
|
||||
"other_ssp": {
|
||||
"name": "Сумма в других ССП",
|
||||
},
|
||||
@ -170,7 +173,7 @@ FORM1_DEPTH_COLUMNS: dict = {
|
||||
"name": "Итоговая смета расходов",
|
||||
"children": {
|
||||
"plan": {
|
||||
"name": "Резерв Финансового Комитета",
|
||||
"name": "План {year}",
|
||||
"key": None,
|
||||
"children": {
|
||||
"q1": {
|
||||
@ -205,9 +208,6 @@ FORM1_DEPTH_COLUMNS: dict = {
|
||||
"protocol": {
|
||||
"name": "Реквизиты протокола (дата, номер)",
|
||||
},
|
||||
"empty": {
|
||||
"name": "",
|
||||
},
|
||||
"note": {
|
||||
"name": "Примечание",
|
||||
},
|
||||
@ -352,15 +352,6 @@ FORM1_DEPTH_COLUMNS: dict = {
|
||||
}
|
||||
}
|
||||
},
|
||||
"shift": {
|
||||
"name": "Перенос во 2 кв {year_short}",
|
||||
"key": None,
|
||||
"children": {
|
||||
"sum": {
|
||||
"name": "Сумма, тыс. руб. (с учетом НДC)",
|
||||
}
|
||||
}
|
||||
},
|
||||
"comment_correct": {
|
||||
"name": "Комментарий",
|
||||
"key": None,
|
||||
@ -595,11 +586,11 @@ FORM1_DEPTH_COLUMNS: dict = {
|
||||
}
|
||||
}
|
||||
},
|
||||
"shift": {
|
||||
"name": "Перенос в 3 кв {year_short}",
|
||||
"comment_correct": {
|
||||
"name": "Комментарий",
|
||||
"key": None,
|
||||
"children": {
|
||||
"sum": {
|
||||
"adj_comment": {
|
||||
"name": "Сумма, тыс. руб. (с учетом НДC)",
|
||||
}
|
||||
}
|
||||
@ -822,11 +813,11 @@ FORM1_DEPTH_COLUMNS: dict = {
|
||||
}
|
||||
}
|
||||
},
|
||||
"shift": {
|
||||
"name": "Перенос в 4 кв {year_short}",
|
||||
"comment_correct": {
|
||||
"name": "Комментарий",
|
||||
"key": None,
|
||||
"children": {
|
||||
"sum": {
|
||||
"adj_comment": {
|
||||
"name": "Сумма, тыс. руб. (с учетом НДC)",
|
||||
}
|
||||
}
|
||||
@ -994,9 +985,6 @@ FORM1_DEPTH_COLUMNS: dict = {
|
||||
"rev_comment": {
|
||||
"name": "Сумма, тыс. руб. (с учетом НДC)",
|
||||
},
|
||||
"adj_comment": {
|
||||
"name": "Сумма, тыс. руб. (с учетом НДC)",
|
||||
},
|
||||
}
|
||||
},
|
||||
"new_plan_correct": {
|
||||
@ -1049,6 +1037,15 @@ FORM1_DEPTH_COLUMNS: dict = {
|
||||
}
|
||||
}
|
||||
},
|
||||
"comment_correct": {
|
||||
"name": "Комментарий",
|
||||
"key": None,
|
||||
"children": {
|
||||
"adj_comment": {
|
||||
"name": "Сумма, тыс. руб. (с учетом НДC)",
|
||||
}
|
||||
}
|
||||
},
|
||||
"plan_correct": {
|
||||
"name": "Скорр. план 4 кв. {year_short}",
|
||||
"key": None,
|
||||
@ -1122,7 +1119,7 @@ FORM1_DEPTH_COLUMNS: dict = {
|
||||
}
|
||||
},
|
||||
"quarter_correct": {
|
||||
"name": "Факт РСБУ 3 кв {year_short}",
|
||||
"name": "Факт РСБУ 4 кв {year_short}",
|
||||
"key": None,
|
||||
"children": {
|
||||
"actual_quarter": {
|
||||
@ -3243,6 +3240,12 @@ def _rename_color_paths(colors: dict, renames: dict[str, str]) -> dict:
|
||||
return renamed
|
||||
|
||||
|
||||
# На экране «Операц» формы 1 нет колонки «Платежи в {year+2} году» (она есть
|
||||
# только на АХР/КВ) — отдельная раскладка без contract_summary.future_y2 (AURORA-1905).
|
||||
FORM1_OPER_DEPTH_COLUMNS: dict = copy.deepcopy(FORM1_DEPTH_COLUMNS)
|
||||
FORM1_OPER_DEPTH_COLUMNS["upper_ssp_smeta"]["children"]["contract_summary"]["children"].pop("future_y2", None)
|
||||
|
||||
|
||||
FORM4_DEPTH_COLUMNS: dict = _rename_depth_columns(
|
||||
FORM1_DEPTH_COLUMNS,
|
||||
FORM4_COLUMN_RENAMES,
|
||||
@ -3283,26 +3286,8 @@ def _patch_form4_regular_columns() -> None:
|
||||
FORM4_DEPTH_COLUMNS["header"]["children"].pop(key, None)
|
||||
FORM4_STRUCTURE_DEPTH_COLUMNS["header"]["children"].pop("justification", None)
|
||||
|
||||
for quarter in ("q2", "q3"):
|
||||
FORM4_DEPTH_COLUMNS[quarter]["children"].setdefault(
|
||||
"comment_correct",
|
||||
{
|
||||
"key": None,
|
||||
"name": "Комментарий",
|
||||
"children": {"adj_comment": {"name": "Сумма"}},
|
||||
},
|
||||
)
|
||||
FORM4_STRUCTURE_DEPTH_COLUMNS[quarter]["children"].setdefault(
|
||||
"comment_correct",
|
||||
{
|
||||
"key": None,
|
||||
"name": "Комментарий",
|
||||
"children": {"adj_comment": {"name": "Сумма"}},
|
||||
},
|
||||
)
|
||||
|
||||
for quarter in (1, 2, 3):
|
||||
FORM4_DEPTH_COLUMNS.pop(f"actual_rsbu_spod_q{quarter}", None)
|
||||
# comment_correct (q2/q3) теперь приходит из FORM1_DEPTH_COLUMNS уже на нужном
|
||||
# месте — отдельный setdefault здесь больше не нужен (AURORA-1905).
|
||||
FORM4_DEPTH_COLUMNS["totals"]["children"].pop("pay_year", None)
|
||||
|
||||
|
||||
@ -3326,7 +3311,7 @@ def _patch_form4_contract_summary_columns() -> None:
|
||||
summary_parent = FORM4_DEPTH_COLUMNS["upper_ssp_smeta"]["children"]["contract_summary"]
|
||||
summary = summary_parent["children"]
|
||||
summary["reference"] = {"name": "Реквизиты договора"}
|
||||
summary["future_y2"] = {"name": "Платежи в {next_year} году"}
|
||||
summary["future_y2"] = {"name": "Платежи в {year_plus_2} году"}
|
||||
summary["centralized_flag"] = {"name": "Централизованный договор"}
|
||||
summary_parent["children"] = _reorder_children(
|
||||
summary,
|
||||
@ -3338,7 +3323,7 @@ def _patch_form4_contract_summary_columns() -> None:
|
||||
|
||||
structure_summary_parent = FORM4_STRUCTURE_DEPTH_COLUMNS["upper_ssp_smeta"]["children"]["contract_summary"]
|
||||
structure_summary = structure_summary_parent["children"]
|
||||
structure_summary["future_payments_y2"] = {"name": "Платежи в {next_year} году"}
|
||||
structure_summary["future_payments_y2"] = {"name": "Платежи в {year_plus_2} году"}
|
||||
structure_summary_parent["children"] = _reorder_children(
|
||||
structure_summary,
|
||||
(
|
||||
@ -3349,14 +3334,17 @@ def _patch_form4_contract_summary_columns() -> None:
|
||||
|
||||
|
||||
def _patch_form4_actual_rsbu_spod_columns() -> None:
|
||||
# SQL отдаёт actual_rsbu_spod только для 4 кв.; 1-3 кв. показывают то же
|
||||
# значение, что и q{n}.actual_quarter внутри самого квартала (AURORA-1905).
|
||||
totals = FORM4_DEPTH_COLUMNS.pop("totals")
|
||||
for quarter in range(1, 5):
|
||||
leaf_key = f"q{quarter}.actual_rsbu_spod" if quarter == 4 else f"q{quarter}.actual_quarter"
|
||||
FORM4_DEPTH_COLUMNS[f"actual_rsbu_spod_q{quarter}"] = {
|
||||
"key": None,
|
||||
"name": f"Факт {quarter} кв" if quarter < 4 else "Факт 4 кв + СПОД",
|
||||
"children": {
|
||||
"actual_rsbu_spod": {
|
||||
"key": f"q{quarter}.actual_rsbu_spod",
|
||||
"key": leaf_key,
|
||||
"name": "",
|
||||
},
|
||||
},
|
||||
@ -3368,7 +3356,6 @@ _patch_form4_regular_columns()
|
||||
_patch_form4_header_columns()
|
||||
_patch_form4_contract_summary_columns()
|
||||
_patch_form4_actual_rsbu_spod_columns()
|
||||
_patch_form4_regular_columns()
|
||||
|
||||
FORM4_OPER_DEPTH_COLUMNS: dict = copy.deepcopy(FORM4_DEPTH_COLUMNS)
|
||||
FORM4_OPER_DEPTH_COLUMNS["empty_upper"]["children"]["ckk"]["children"]["procurement_plan"] = (
|
||||
@ -3377,6 +3364,12 @@ FORM4_OPER_DEPTH_COLUMNS["empty_upper"]["children"]["ckk"]["children"]["procurem
|
||||
FORM4_OPER_DEPTH_COLUMNS["empty_upper"]["children"]["ckk"]["children"]["procurement_method"] = (
|
||||
FORM4_OPER_DEPTH_COLUMNS["empty_upper"]["children"]["ckk"]["children"].pop("proc_method")
|
||||
)
|
||||
# Переименование выше (pop+присвоение) уезжает в конец словаря — возвращаем
|
||||
# порядок экрана: ceiling, q1-q4, rf_schedule, deadline, затем закупки, comment (AURORA-1905).
|
||||
FORM4_OPER_DEPTH_COLUMNS["empty_upper"]["children"]["ckk"]["children"] = _reorder_children(
|
||||
FORM4_OPER_DEPTH_COLUMNS["empty_upper"]["children"]["ckk"]["children"],
|
||||
("ceiling", "q1", "q2", "q3", "q4", "rf_schedule", "deadline", "procurement_plan", "procurement_method", "comment"),
|
||||
)
|
||||
FORM4_OPER_COLORS: dict = _rename_color_paths(
|
||||
FORM4_COLORS["OPER"],
|
||||
{
|
||||
@ -4004,6 +3997,7 @@ def render_column_years(columns: dict, year: int) -> dict:
|
||||
"year_short": f"{year % 100:02d}",
|
||||
"next_year": str(year + 1),
|
||||
"next_year_short": f"{(year + 1) % 100:02d}",
|
||||
"year_plus_2": str(year + 2),
|
||||
}
|
||||
|
||||
_replace_column_year_placeholders(rendered, values)
|
||||
|
||||
@ -33,6 +33,7 @@ from src.services.export_service.export_normalizers import (
|
||||
from src.services.export_service.export_mappings import (
|
||||
FORM1_COLORS,
|
||||
FORM1_DEPTH_COLUMNS,
|
||||
FORM1_OPER_DEPTH_COLUMNS,
|
||||
FORM2_DEPTH_AHR_SUB_COLUMNS,
|
||||
FORM3_COLORS,
|
||||
FORM4_COLORS,
|
||||
@ -927,7 +928,8 @@ class ExportService:
|
||||
}
|
||||
else:
|
||||
writer_configs = {
|
||||
"OPER": (FORM1_DEPTH_COLUMNS, FORM1_COLORS["OPER"]),
|
||||
# AURORA-1905: на экране «Операц» формы 1 нет «Платежи в {year+2} году».
|
||||
"OPER": (FORM1_OPER_DEPTH_COLUMNS, FORM1_COLORS["OPER"]),
|
||||
"AHR": (FORM1_DEPTH_COLUMNS, FORM1_COLORS["AHR"]),
|
||||
"CAP": (FORM1_DEPTH_COLUMNS, FORM1_COLORS["CAP"]),
|
||||
"AHR_RENT": (FORM2_DEPTH_AHR_SUB_COLUMNS, None),
|
||||
|
||||
107
api/tests/unit/test_export_form1_form4_mapping.py
Normal file
107
api/tests/unit/test_export_form1_form4_mapping.py
Normal file
@ -0,0 +1,107 @@
|
||||
import importlib
|
||||
|
||||
from xlsxwriter import Workbook as XlsxWorkbook
|
||||
|
||||
from src.services.export_service import export_mappings
|
||||
from src.services.export_service.export_mappings import (
|
||||
FORM1_DEPTH_COLUMNS,
|
||||
FORM1_OPER_DEPTH_COLUMNS,
|
||||
FORM4_DEPTH_COLUMNS,
|
||||
FORM4_OPER_DEPTH_COLUMNS,
|
||||
FORM4_STRUCTURE_DEPTH_COLUMNS,
|
||||
render_column_years,
|
||||
)
|
||||
from src.services.export_service.export_writers import XlsxDepthSheetWriter
|
||||
|
||||
|
||||
def _flat(columns_dict):
|
||||
writer = XlsxDepthSheetWriter(
|
||||
workbook=XlsxWorkbook(),
|
||||
columns_dict=columns_dict,
|
||||
row_normalizer=lambda row: row,
|
||||
value_serializer=lambda value: value,
|
||||
)
|
||||
return writer.columns
|
||||
|
||||
|
||||
def _tree_node(tree, path):
|
||||
segments = path.split(".")
|
||||
node = tree[segments[0]]
|
||||
for segment in segments[1:]:
|
||||
node = node["children"][segment]
|
||||
return node
|
||||
|
||||
|
||||
def test_no_stray_collegial_empty_column():
|
||||
for columns in (FORM1_DEPTH_COLUMNS, FORM4_DEPTH_COLUMNS, FORM4_STRUCTURE_DEPTH_COLUMNS):
|
||||
assert "collegial.empty" not in _flat(columns)
|
||||
|
||||
|
||||
def test_no_shift_sum_columns_in_base_form1():
|
||||
flat = _flat(FORM1_DEPTH_COLUMNS)
|
||||
assert not [key for key in flat if key.endswith(".sum")]
|
||||
|
||||
|
||||
def test_form4_has_fact_q1_q2_q3_and_fact_4_plus_spod_before_totals():
|
||||
flat = _flat(FORM4_DEPTH_COLUMNS)
|
||||
# 1-3 кв. показывают то же значение, что q{n}.actual_quarter внутри квартала
|
||||
# (сознательный дубль ключа — влияет только на цвет, см. AURORA-1905); 4 кв.
|
||||
# использует свой собственный q4.actual_rsbu_spod (SQL отдаёт только его).
|
||||
assert flat.count("q1.actual_quarter") == 2
|
||||
assert flat.count("q2.actual_quarter") == 2
|
||||
assert flat.count("q3.actual_quarter") == 2
|
||||
assert flat.count("q4.actual_rsbu_spod") == 1
|
||||
totals_index = flat.index("totals.fact_year")
|
||||
assert flat.index("q4.actual_rsbu_spod") < totals_index
|
||||
|
||||
|
||||
def test_adj_comment_immediately_follows_adj_reserve():
|
||||
for quarter in ("q2", "q3", "q4"):
|
||||
for columns in (FORM1_DEPTH_COLUMNS, FORM4_DEPTH_COLUMNS, FORM4_STRUCTURE_DEPTH_COLUMNS):
|
||||
flat = _flat(columns)
|
||||
reserve_index = flat.index(f"{quarter}.adj_reserve")
|
||||
comment_index = flat.index(f"{quarter}.adj_comment")
|
||||
assert comment_index == reserve_index + 1, (columns is FORM1_DEPTH_COLUMNS, quarter)
|
||||
|
||||
|
||||
def test_form4_oper_ckk_procurement_before_comment():
|
||||
flat = _flat(FORM4_OPER_DEPTH_COLUMNS)
|
||||
assert flat.index("ckk.procurement_plan") < flat.index("ckk.comment")
|
||||
assert flat.index("ckk.procurement_method") < flat.index("ckk.comment")
|
||||
assert flat.index("ckk.deadline") < flat.index("ckk.procurement_plan")
|
||||
|
||||
|
||||
def test_form1_oper_has_no_future_y2_but_ahr_cap_do():
|
||||
assert "contract_summary.future_y2" in _flat(FORM1_DEPTH_COLUMNS)
|
||||
assert "contract_summary.future_y2" not in _flat(FORM1_OPER_DEPTH_COLUMNS)
|
||||
|
||||
|
||||
def test_render_column_years_form4():
|
||||
rendered = render_column_years(FORM4_DEPTH_COLUMNS, 2026)
|
||||
assert _tree_node(rendered, "upper_ssp_smeta.contract_summary.future_y2")["name"] == "Платежи в 2028 году"
|
||||
assert _tree_node(rendered, "approved.plan")["name"] == "План 2026"
|
||||
assert _tree_node(rendered, "q4.quarter_correct")["name"] == "Факт РСБУ 4 кв 26"
|
||||
|
||||
|
||||
def test_render_column_years_form4_structure():
|
||||
rendered = render_column_years(FORM4_STRUCTURE_DEPTH_COLUMNS, 2026)
|
||||
assert (
|
||||
_tree_node(rendered, "upper_ssp_smeta.contract_summary.future_payments_y2")["name"]
|
||||
== "Платежи в 2028 году"
|
||||
)
|
||||
|
||||
|
||||
def test_render_column_years_form1():
|
||||
rendered = render_column_years(FORM1_DEPTH_COLUMNS, 2026)
|
||||
assert _tree_node(rendered, "approved.plan")["name"] == "План 2026"
|
||||
assert _tree_node(rendered, "q4.quarter_correct")["name"] == "Факт РСБУ 4 кв 26"
|
||||
|
||||
|
||||
def test_module_reload_is_idempotent():
|
||||
# Патчи FORM_4 применяются один раз при импорте модуля; повторный импорт
|
||||
# не должен снова звать патчи и удваивать/ломать раскладку.
|
||||
before = _flat(export_mappings.FORM4_DEPTH_COLUMNS)
|
||||
importlib.reload(export_mappings)
|
||||
after = _flat(export_mappings.FORM4_DEPTH_COLUMNS)
|
||||
assert before == after
|
||||
importlib.reload(export_mappings)
|
||||
@ -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',
|
||||
},
|
||||
],
|
||||
|
||||
@ -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',
|
||||
},
|
||||
|
||||
@ -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',
|
||||
},
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import test from 'node:test';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
// AURORA-1905: в 1-3 кв. "Комментарий" (adj_comment) уже шёл сразу после
|
||||
// "Корректировки из резерва" (adj_reserve), перед "Скорректированный план"
|
||||
// (corrected_plan); в 4 кв. порядок был нарушен (corrected_plan раньше adj_comment).
|
||||
const SHEETS = ['AHR', 'CAP', 'OPER'];
|
||||
|
||||
function extractDataAccessorKeys(source) {
|
||||
// Пропускаем секцию "colors" (плоская карта, свой, не относящийся к
|
||||
// расположению столбцов порядок) — берём только дерево "columns".
|
||||
const columnsStart = source.indexOf('\n\t\tcolumns: [');
|
||||
const columnsSource = columnsStart === -1 ? source : source.slice(columnsStart);
|
||||
const matches = columnsSource.matchAll(/accessorKey:\s*'(data\.[^']+)'/g);
|
||||
return [...matches].map((m) => m[1]);
|
||||
}
|
||||
|
||||
for (const sheet of SHEETS) {
|
||||
test(`FORM_1/${sheet}.js: q4 adj_reserve -> adj_comment -> corrected_plan`, () => {
|
||||
const path = fileURLToPath(new URL(`../constants/FORM_1/${sheet}.js`, import.meta.url));
|
||||
const keys = extractDataAccessorKeys(readFileSync(path, 'utf8'));
|
||||
|
||||
const reserveIndex = keys.indexOf('data.q4.adj_reserve');
|
||||
const commentIndex = keys.indexOf('data.q4.adj_comment');
|
||||
const planIndex = keys.indexOf('data.q4.corrected_plan');
|
||||
|
||||
assert.notEqual(reserveIndex, -1, 'data.q4.adj_reserve не найден');
|
||||
assert.notEqual(commentIndex, -1, 'data.q4.adj_comment не найден');
|
||||
assert.notEqual(planIndex, -1, 'data.q4.corrected_plan не найден');
|
||||
assert.ok(reserveIndex < commentIndex, 'adj_comment должен идти после adj_reserve');
|
||||
assert.ok(commentIndex < planIndex, 'adj_comment должен идти перед corrected_plan');
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user