diff --git a/api/src/services/export_service/export_mappings.py b/api/src/services/export_service/export_mappings.py index 52210c7..3e9af41 100644 --- a/api/src/services/export_service/export_mappings.py +++ b/api/src/services/export_service/export_mappings.py @@ -88,7 +88,7 @@ FORM1_DEPTH_COLUMNS: dict = { "name": "Договоры", "children": { "total": { - "name": "Сумма", + "name": "Сумма, тыс. руб.", }, "counterparty": { "name": "Контрагент", @@ -100,13 +100,13 @@ FORM1_DEPTH_COLUMNS: dict = { "name": "Комментарии", }, "future_y1": { - "name": "Платежи в {next_year} году", + "name": "Платежи в {next_year} году, тыс. руб.", }, "future_y2": { - "name": "Платежи в {year_plus_2} году", + "name": "Платежи в {year_plus_2} году, тыс. руб.", }, "other_ssp": { - "name": "Сумма в других ССП", + "name": "Сумма в других ССП, тыс. руб.", }, } } @@ -127,7 +127,7 @@ FORM1_DEPTH_COLUMNS: dict = { "name": "Секвестирование ДФиП", "children": { "correct": { - "name": "Корректировка сметы расходов", + "name": "Корректировка сметы расходов, тыс. руб.", "key": None, "children": { "q1": { @@ -156,7 +156,7 @@ FORM1_DEPTH_COLUMNS: dict = { "name": "Отнесение в резерв", "children": { "correct": { - "name": "Резерв Финансового Комитета", + "name": "Резерв Финансового Комитета, тыс. руб.", "key": None, "children": { "q1": { @@ -182,7 +182,7 @@ FORM1_DEPTH_COLUMNS: dict = { } }, "approved": { - "name": "Итоговая смета расходов", + "name": "Итоговая смета расходов, тыс. руб.", "children": { "plan": { "name": "План {year}", @@ -215,7 +215,7 @@ FORM1_DEPTH_COLUMNS: dict = { "name": "Коллегиальные органы", "children": { "approved": { - "name": "Сумма ({vat})", + "name": "Сумма, тыс. руб. ({vat})", }, "protocol": { "name": "Реквизиты протокола (дата, номер)", @@ -369,7 +369,7 @@ FORM1_DEPTH_COLUMNS: dict = { "key": None, "children": { "adj_comment": { - "name": "Сумма, тыс. руб. ({vat})", + "name": "", } } }, @@ -396,10 +396,10 @@ FORM1_DEPTH_COLUMNS: dict = { "name": "Сумма, тыс. руб. ({vat})" }, "pay_ho": { - "name": "в т.ч. сумма ({vat}) ГО" + "name": "в т.ч. сумма, тыс. руб. ({vat}) ГО" }, "pay_rf": { - "name": "в т.ч. сумма ({vat}) РФ" + "name": "в т.ч. сумма, тыс. руб. ({vat}) РФ" }, "pay_comment": { "name": "Комментарий" @@ -476,7 +476,7 @@ FORM1_DEPTH_COLUMNS: dict = { } }, "close_q1": { - "name": "Закрытие 1-го квартала", + "name": "Закрытие 1-го квартала, тыс. руб.", "key": None, "children": { "transfer_q2": { @@ -544,7 +544,7 @@ FORM1_DEPTH_COLUMNS: dict = { "key": None, "children": { "rev_comment": { - "name": "Сумма, тыс. руб. ({vat})", + "name": "", } } }, @@ -603,7 +603,7 @@ FORM1_DEPTH_COLUMNS: dict = { "key": None, "children": { "adj_comment": { - "name": "Сумма, тыс. руб. ({vat})", + "name": "", } } }, @@ -630,10 +630,10 @@ FORM1_DEPTH_COLUMNS: dict = { "name": "Сумма, тыс. руб. ({vat})" }, "pay_ho": { - "name": "в т.ч. сумма ({vat}) ГО" + "name": "в т.ч. сумма, тыс. руб. ({vat}) ГО" }, "pay_rf": { - "name": "в т.ч. сумма ({vat}) РФ" + "name": "в т.ч. сумма, тыс. руб. ({vat}) РФ" }, "pay_comment": { "name": "Комментарий" @@ -707,7 +707,7 @@ FORM1_DEPTH_COLUMNS: dict = { } }, "close_q2": { - "name": "Закрытие 2-го квартала", + "name": "Закрытие 2-го квартала, тыс. руб.", "key": None, "children": { "transfer_q3": { @@ -771,7 +771,7 @@ FORM1_DEPTH_COLUMNS: dict = { "key": None, "children": { "rev_comment": { - "name": "Сумма, тыс. руб. ({vat})", + "name": "", } } }, @@ -830,7 +830,7 @@ FORM1_DEPTH_COLUMNS: dict = { "key": None, "children": { "adj_comment": { - "name": "Сумма, тыс. руб. ({vat})", + "name": "", } } }, @@ -857,10 +857,10 @@ FORM1_DEPTH_COLUMNS: dict = { "name": "Сумма, тыс. руб. ({vat})" }, "pay_ho": { - "name": "в т.ч. сумма ({vat}) ГО" + "name": "в т.ч. сумма, тыс. руб. ({vat}) ГО" }, "pay_rf": { - "name": "в т.ч. сумма ({vat}) РФ" + "name": "в т.ч. сумма, тыс. руб. ({vat}) РФ" }, "pay_comment": { "name": "Комментарий" @@ -934,7 +934,7 @@ FORM1_DEPTH_COLUMNS: dict = { } }, "close_q3": { - "name": "Закрытие 3-го квартала", + "name": "Закрытие 3-го квартала, тыс. руб.", "key": None, "children": { "transfer_q4": { @@ -995,7 +995,7 @@ FORM1_DEPTH_COLUMNS: dict = { "key": None, "children": { "rev_comment": { - "name": "Сумма, тыс. руб. ({vat})", + "name": "", }, } }, @@ -1054,7 +1054,7 @@ FORM1_DEPTH_COLUMNS: dict = { "key": None, "children": { "adj_comment": { - "name": "Сумма, тыс. руб. ({vat})", + "name": "", } } }, @@ -1081,10 +1081,10 @@ FORM1_DEPTH_COLUMNS: dict = { "name": "Сумма, тыс. руб. ({vat})" }, "pay_ho": { - "name": "в т.ч. сумма ({vat}) ГО" + "name": "в т.ч. сумма, тыс. руб. ({vat}) ГО" }, "pay_rf": { - "name": "в т.ч. сумма ({vat}) РФ" + "name": "в т.ч. сумма, тыс. руб. ({vat}) РФ" }, "pay_comment": { "name": "Комментарий" @@ -1167,7 +1167,7 @@ FORM1_DEPTH_COLUMNS: dict = { } }, "close_q4": { - "name": "Закрытие 4-го квартала", + "name": "Закрытие 4-го квартала, тыс. руб.", "key": None, "children": { "economy": { @@ -1184,7 +1184,7 @@ FORM1_DEPTH_COLUMNS: dict = { "name": "Факт {year_short}", "children": { "fact_year": { - "name": "Факт {year_short}", + "name": "Факт {year_short}, тыс. руб.", }, # "empty": { # "name": "", @@ -1239,7 +1239,7 @@ FORM2_DEPTH_AHR_SUB_COLUMNS: dict = { }, "plan": { # "key": None, - "name": "Планируемая сумма расходов на {next_year} год", + "name": "Планируемая сумма расходов на {next_year} год, тыс. руб.", "children": { "q1": { "name": "I квартал" @@ -3323,7 +3323,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": "Платежи в {year_plus_2} году"} + summary["future_y2"] = {"name": "Платежи в {year_plus_2} году, тыс. руб."} summary["centralized_flag"] = {"name": "Централизованный договор"} summary_parent["children"] = _reorder_children( summary, @@ -3335,7 +3335,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": "Платежи в {year_plus_2} году"} + structure_summary["future_payments_y2"] = {"name": "Платежи в {year_plus_2} году, тыс. руб."} structure_summary_parent["children"] = _reorder_children( structure_summary, ( @@ -3353,7 +3353,7 @@ def _patch_form4_actual_rsbu_spod_columns() -> None: 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 кв + СПОД", + "name": f"Факт {quarter} кв, тыс. руб." if quarter < 4 else "Факт 4 кв + СПОД, тыс. руб.", "children": { "actual_rsbu_spod": { "key": leaf_key, @@ -3442,46 +3442,46 @@ SMETA_FORM1_COLUMNS: list[tuple[str, str]] = [ SMETA_FORM2_COLUMNS: list[tuple[str, str]] = [ ("section_code", "Код раздела"), ("name", "Наименование"), - ("plan_support_q1", "План поддержка I квартал"), - ("plan_support_q2", "План поддержка II квартал"), - ("plan_support_q3", "План поддержка III квартал"), - ("plan_support_q4", "План поддержка IV квартал"), - ("plan_support_year", "План поддержка Год"), - ("approved_support_q1", "Утверждено поддержка I квартал"), - ("approved_support_q2", "Утверждено поддержка II квартал"), - ("approved_support_q3", "Утверждено поддержка III квартал"), - ("approved_support_q4", "Утверждено поддержка IV квартал"), - ("approved_support_year", "Утверждено поддержка Год"), - ("fact_support_q1", "Факт поддержка I квартал"), - ("fact_support_q2", "Факт поддержка II квартал"), - ("fact_support_q3", "Факт поддержка III квартал"), - ("fact_support_q4", "Факт поддержка IV квартал"), - ("fact_support_year", "Факт поддержка Год"), - ("corrected_support_q2", "Корректировка поддержка II квартал"), - ("corrected_support_q3", "Корректировка поддержка III квартал"), - ("corrected_support_q4", "Корректировка поддержка IV квартал"), + ("plan_support_q1", "План поддержка I квартал, тыс. руб."), + ("plan_support_q2", "План поддержка II квартал, тыс. руб."), + ("plan_support_q3", "План поддержка III квартал, тыс. руб."), + ("plan_support_q4", "План поддержка IV квартал, тыс. руб."), + ("plan_support_year", "План поддержка Год, тыс. руб."), + ("approved_support_q1", "Утверждено поддержка I квартал, тыс. руб."), + ("approved_support_q2", "Утверждено поддержка II квартал, тыс. руб."), + ("approved_support_q3", "Утверждено поддержка III квартал, тыс. руб."), + ("approved_support_q4", "Утверждено поддержка IV квартал, тыс. руб."), + ("approved_support_year", "Утверждено поддержка Год, тыс. руб."), + ("fact_support_q1", "Факт поддержка I квартал, тыс. руб."), + ("fact_support_q2", "Факт поддержка II квартал, тыс. руб."), + ("fact_support_q3", "Факт поддержка III квартал, тыс. руб."), + ("fact_support_q4", "Факт поддержка IV квартал, тыс. руб."), + ("fact_support_year", "Факт поддержка Год, тыс. руб."), + ("corrected_support_q2", "Корректировка поддержка II квартал, тыс. руб."), + ("corrected_support_q3", "Корректировка поддержка III квартал, тыс. руб."), + ("corrected_support_q4", "Корректировка поддержка IV квартал, тыс. руб."), ] SMETA_FORM4_COLUMNS: list[tuple[str, str]] = [ ("section_code", "Код раздела"), ("name", "Наименование"), - ("plan_development_q1", "План развитие I квартал"), - ("plan_development_q2", "План развитие II квартал"), - ("plan_development_q3", "План развитие III квартал"), - ("plan_development_q4", "План развитие IV квартал"), - ("plan_development_year", "План развитие Год"), - ("approved_development_q1", "Утверждено развитие I квартал"), - ("approved_development_q2", "Утверждено развитие II квартал"), - ("approved_development_q3", "Утверждено развитие III квартал"), - ("approved_development_q4", "Утверждено развитие IV квартал"), - ("approved_development_year", "Утверждено развитие Год"), - ("fact_development_q1", "Факт развитие I квартал"), - ("fact_development_q2", "Факт развитие II квартал"), - ("fact_development_q3", "Факт развитие III квартал"), - ("fact_development_q4", "Факт развитие IV квартал"), - ("fact_development_year", "Факт развитие Год"), - ("corrected_development_q2", "Корректировка развитие II квартал"), - ("corrected_development_q3", "Корректировка развитие III квартал"), - ("corrected_development_q4", "Корректировка развитие IV квартал"), + ("plan_development_q1", "План развитие I квартал, тыс. руб."), + ("plan_development_q2", "План развитие II квартал, тыс. руб."), + ("plan_development_q3", "План развитие III квартал, тыс. руб."), + ("plan_development_q4", "План развитие IV квартал, тыс. руб."), + ("plan_development_year", "План развитие Год, тыс. руб."), + ("approved_development_q1", "Утверждено развитие I квартал, тыс. руб."), + ("approved_development_q2", "Утверждено развитие II квартал, тыс. руб."), + ("approved_development_q3", "Утверждено развитие III квартал, тыс. руб."), + ("approved_development_q4", "Утверждено развитие IV квартал, тыс. руб."), + ("approved_development_year", "Утверждено развитие Год, тыс. руб."), + ("fact_development_q1", "Факт развитие I квартал, тыс. руб."), + ("fact_development_q2", "Факт развитие II квартал, тыс. руб."), + ("fact_development_q3", "Факт развитие III квартал, тыс. руб."), + ("fact_development_q4", "Факт развитие IV квартал, тыс. руб."), + ("fact_development_year", "Факт развитие Год, тыс. руб."), + ("corrected_development_q2", "Корректировка развитие II квартал, тыс. руб."), + ("corrected_development_q3", "Корректировка развитие III квартал, тыс. руб."), + ("corrected_development_q4", "Корректировка развитие IV квартал, тыс. руб."), ] SMETA_COLUMNS_BY_FORM: dict[str, list[tuple[str, str]]] = { "FORM_1": SMETA_FORM1_COLUMNS, @@ -3503,7 +3503,7 @@ FORM3_LIMIT_DEPTH_COLUMNS: dict = { }, }, "q1": { - "name": "Отчет об исполнении лимита затрат, утверждённого Правлением Банка в I квартале", + "name": "Отчет об исполнении лимита затрат, утверждённого Правлением Банка в I квартале, тыс. руб.", "children": { "korrektirovka_limita": { "key": None, @@ -3536,7 +3536,7 @@ FORM3_LIMIT_DEPTH_COLUMNS: dict = { }, }, "q2": { - "name": "Отчет об исполнении лимита затрат, утверждённого Правлением Банка в II квартале", + "name": "Отчет об исполнении лимита затрат, утверждённого Правлением Банка в II квартале, тыс. руб.", "children": { "limit_ostatok": { "key": None, @@ -3574,7 +3574,7 @@ FORM3_LIMIT_DEPTH_COLUMNS: dict = { }, }, "q3": { - "name": "Отчет об исполнении лимита затрат, утверждённого Правлением Банка в III квартале", + "name": "Отчет об исполнении лимита затрат, утверждённого Правлением Банка в III квартале, тыс. руб.", "children": { "limit_ostatok": { "key": None, @@ -3612,7 +3612,7 @@ FORM3_LIMIT_DEPTH_COLUMNS: dict = { }, }, "q4": { - "name": "Отчет об исполнении лимита затрат, утверждённого Правлением Банка в IV квартале", + "name": "Отчет об исполнении лимита затрат, утверждённого Правлением Банка в IV квартале, тыс. руб.", "children": { "limit_ostatok": { "key": None, @@ -3663,7 +3663,7 @@ FORM3_CURRENT_EXPENSES_DEPTH_COLUMNS: dict = { }, }, "q1": { - "name": "Отчет об исполнении текущих расходов в I квартале", + "name": "Отчет об исполнении текущих расходов в I квартале, тыс. руб.", "children": { "korrektirovka_limita": { "key": None, @@ -3696,7 +3696,7 @@ FORM3_CURRENT_EXPENSES_DEPTH_COLUMNS: dict = { }, }, "q2": { - "name": "Отчет об исполнении текущих расходов в II квартале", + "name": "Отчет об исполнении текущих расходов в II квартале, тыс. руб.", "children": { "limit_ostatok": { "key": None, @@ -3734,7 +3734,7 @@ FORM3_CURRENT_EXPENSES_DEPTH_COLUMNS: dict = { }, }, "q3": { - "name": "Отчет об исполнении текущих расходов в III квартале", + "name": "Отчет об исполнении текущих расходов в III квартале, тыс. руб.", "children": { "limit_ostatok": { "key": None, @@ -3772,7 +3772,7 @@ FORM3_CURRENT_EXPENSES_DEPTH_COLUMNS: dict = { }, }, "q4": { - "name": "Отчет об исполнении текущих расходов в IV квартале", + "name": "Отчет об исполнении текущих расходов в IV квартале, тыс. руб.", "children": { "limit_ostatok": { "key": None, @@ -4117,23 +4117,23 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "q1": { - "name": "1 квартал", + "name": "1 квартал, тыс. руб.", "key": "plan.q1", }, "q2": { - "name": "2 квартал", + "name": "2 квартал, тыс. руб.", "key": "plan.q2", }, "q3": { - "name": "3 квартал", + "name": "3 квартал, тыс. руб.", "key": "plan.q3", }, "q4": { - "name": "4 квартал", + "name": "4 квартал, тыс. руб.", "key": "plan.q4", }, "year": { - "name": "Год", + "name": "Год, тыс. руб.", "key": "plan.year", }, "comment": { @@ -4147,23 +4147,23 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "q1": { - "name": "1 квартал", + "name": "1 квартал, тыс. руб.", "key": "seq_dfip.q1", }, "q2": { - "name": "2 квартал", + "name": "2 квартал, тыс. руб.", "key": "seq_dfip.q2", }, "q3": { - "name": "3 квартал", + "name": "3 квартал, тыс. руб.", "key": "seq_dfip.q3", }, "q4": { - "name": "4 квартал", + "name": "4 квартал, тыс. руб.", "key": "seq_dfip.q4", }, "year": { - "name": "Год", + "name": "Год, тыс. руб.", "key": "seq_dfip.year", }, "justification": { @@ -4177,23 +4177,23 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "q1": { - "name": "1 квартал", + "name": "1 квартал, тыс. руб.", "key": "seq_ssp.q1", }, "q2": { - "name": "2 квартал", + "name": "2 квартал, тыс. руб.", "key": "seq_ssp.q2", }, "q3": { - "name": "3 квартал", + "name": "3 квартал, тыс. руб.", "key": "seq_ssp.q3", }, "q4": { - "name": "4 квартал", + "name": "4 квартал, тыс. руб.", "key": "seq_ssp.q4", }, "year": { - "name": "Год", + "name": "Год, тыс. руб.", "key": "seq_ssp.year", }, "justification": { @@ -4203,7 +4203,7 @@ FORM2_DEPTH_COLUMNS: dict = { }, }, "utverzhdennaya_smeta_raskhodov": { - "name": "Утвержденная смета расходов", + "name": "Утвержденная смета расходов, тыс. руб.", "key": None, "children": { "q1": { @@ -4253,7 +4253,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "contract.currency", }, "ceiling": { - "name": "Предельная стоимость", + "name": "Предельная стоимость, тыс. руб.", "key": "contract.ceiling", }, "vat_rate": { @@ -4277,19 +4277,19 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "contract.comment", }, "q1": { - "name": "Бронь средств в 1 квартале", + "name": "Бронь средств в 1 квартале, тыс. руб.", "key": "booking.y2026.q1", }, "q2": { - "name": "Бронь средств во 2 квартале", + "name": "Бронь средств во 2 квартале, тыс. руб.", "key": "booking.y2026.q2", }, "q3": { - "name": "Бронь средств в 3 квартале", + "name": "Бронь средств в 3 квартале, тыс. руб.", "key": "booking.y2026.q3", }, "q4": { - "name": "Бронь средств в 4 квартале", + "name": "Бронь средств в 4 квартале, тыс. руб.", "key": "booking.y2026.q4", }, }, @@ -4297,7 +4297,7 @@ FORM2_DEPTH_COLUMNS: dict = { }, }, "god_2": { - "name": "{next_year} год", + "name": "{next_year} год, тыс. руб.", "key": None, "children": { "q1": { @@ -4327,15 +4327,15 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "adj_current": { - "name": "Текущие корректировки (= 0)", + "name": "Текущие корректировки, тыс. руб. (= 0)", "key": "q1.adj_current", }, "adj_ssp": { - "name": "Корректировка из сметы ГО", + "name": "Корректировка из сметы ГО, тыс. руб.", "key": "q1.adj_ssp", }, "adj_reserve": { - "name": "Корректировка из резерва", + "name": "Корректировка из резерва, тыс. руб.", "key": "q1.adj_reserve", }, "adj_comment": { @@ -4343,7 +4343,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q1.adj_comment", }, "corrected_plan": { - "name": "Базовый план с учётом корректировок", + "name": "Базовый план с учётом корректировок, тыс. руб.", "key": "q1.corrected_plan", }, }, @@ -4357,7 +4357,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q1.pay_date", }, "pay_amount": { - "name": "Сумма платежа", + "name": "Сумма платежа, тыс. руб.", "key": "q1.pay_amount", }, "pay_comment": { @@ -4371,7 +4371,7 @@ FORM2_DEPTH_COLUMNS: dict = { }, }, "fakticheskie_raskhody": { - "name": "Фактические расходы", + "name": "Фактические расходы, тыс. руб.", "key": None, "children": { "booking": { @@ -4409,19 +4409,19 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "transfer_q2": { - "name": "Перенос во 2 квартал", + "name": "Перенос во 2 квартал, тыс. руб.", "key": "q1.transfer_q2", }, "transfer_q2_delay_acts": { - "name": "Задержка списания расходов / предоставления актов", + "name": "Задержка списания расходов / предоставления актов, тыс. руб.", "key": "q1.transfer_q2_delay_acts", }, "transfer_q2_delay_procurement": { - "name": "Задержка закупочных процедур", + "name": "Задержка закупочных процедур, тыс. руб.", "key": "q1.transfer_q2_delay_procurement", }, "transfer_q2_economy_rf": { - "name": "Экономия (будет использована РФ)", + "name": "Экономия (будет использована РФ), тыс. руб.", "key": "q1.transfer_q2_economy_rf", }, "transfer_next_comment": { @@ -4429,11 +4429,11 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q1.transfer_next_comment", }, "transfer_q3": { - "name": "Перенос в 3 квартал", + "name": "Перенос в 3 квартал, тыс. руб.", "key": "q1.transfer_q3", }, "transfer_q4": { - "name": "Перенос в 4 квартал", + "name": "Перенос в 4 квартал, тыс. руб.", "key": "q1.transfer_q4", }, "transfer_far_comment": { @@ -4441,11 +4441,11 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q1.transfer_far_comment", }, "transfer_econ": { - "name": "Перенос в фонд экономии", + "name": "Перенос в фонд экономии, тыс. руб.", "key": "q1.transfer_econ", }, "total": { - "name": "Всего", + "name": "Всего, тыс. руб.", "key": "q1.total", }, }, @@ -4461,11 +4461,11 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "target_change": { - "name": "Изменение целевого назначения перенесённой экономии (= 0)", + "name": "Изменение целевого назначения перенесённой экономии, тыс. руб. (= 0)", "key": "q2.target_change", }, "base_correction": { - "name": "Корректировка статей базового плана (= 0)", + "name": "Корректировка статей базового плана, тыс. руб. (= 0)", "key": "q2.base_correction", }, "base_correction_comment": { @@ -4473,11 +4473,11 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q2.base_correction_comment", }, "revision_inc": { - "name": "ДФиП: увеличение плана (> 0)", + "name": "ДФиП: увеличение плана, тыс. руб. (> 0)", "key": "q2.revision_inc", }, "revision_seq": { - "name": "ДФиП: секвестр плана (< 0)", + "name": "ДФиП: секвестр плана, тыс. руб. (< 0)", "key": "q2.revision_seq", }, "revision_comment": { @@ -4485,7 +4485,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q2.revision_comment", }, "new_plan": { - "name": "Скорректированный план на подпись", + "name": "Скорректированный план на подпись, тыс. руб.", "key": "q2.new_plan", }, }, @@ -4495,15 +4495,15 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "adj_current": { - "name": "Текущие корректировки (= 0)", + "name": "Текущие корректировки, тыс. руб. (= 0)", "key": "q2.adj_current", }, "adj_ssp": { - "name": "Корректировка из сметы ГО", + "name": "Корректировка из сметы ГО, тыс. руб.", "key": "q2.adj_ssp", }, "adj_reserve": { - "name": "Корректировка из резерва", + "name": "Корректировка из резерва, тыс. руб.", "key": "q2.adj_reserve", }, "adj_comment": { @@ -4511,7 +4511,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q2.adj_comment", }, "corrected_plan": { - "name": "Скорректированный план с учётом корректировок", + "name": "Скорректированный план с учётом корректировок, тыс. руб.", "key": "q2.corrected_plan", }, }, @@ -4525,7 +4525,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q2.pay_date", }, "pay_amount": { - "name": "Сумма платежа", + "name": "Сумма платежа, тыс. руб.", "key": "q2.pay_amount", }, "pay_comment": { @@ -4539,7 +4539,7 @@ FORM2_DEPTH_COLUMNS: dict = { }, }, "fakticheskie_raskhody": { - "name": "Фактические расходы", + "name": "Фактические расходы, тыс. руб.", "key": None, "children": { "booking": { @@ -4577,19 +4577,19 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "transfer_q3": { - "name": "Перенос в 3 квартал", + "name": "Перенос в 3 квартал, тыс. руб.", "key": "q2.transfer_q3", }, "transfer_q3_delay_acts": { - "name": "Задержка списания расходов / предоставления актов", + "name": "Задержка списания расходов / предоставления актов, тыс. руб.", "key": "q2.transfer_q3_delay_acts", }, "transfer_q3_delay_procurement": { - "name": "Задержка закупочных процедур", + "name": "Задержка закупочных процедур, тыс. руб.", "key": "q2.transfer_q3_delay_procurement", }, "transfer_q3_economy_rf": { - "name": "Экономия (будет использована РФ)", + "name": "Экономия (будет использована РФ), тыс. руб.", "key": "q2.transfer_q3_economy_rf", }, "transfer_next_comment": { @@ -4597,7 +4597,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q2.transfer_next_comment", }, "transfer_q4": { - "name": "Перенос в 4 квартал", + "name": "Перенос в 4 квартал, тыс. руб.", "key": "q2.transfer_q4", }, "transfer_far_comment": { @@ -4605,11 +4605,11 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q2.transfer_far_comment", }, "transfer_econ": { - "name": "Перенос в фонд экономии", + "name": "Перенос в фонд экономии, тыс. руб.", "key": "q2.transfer_econ", }, "total": { - "name": "Всего", + "name": "Всего, тыс. руб.", "key": "q2.total", }, }, @@ -4625,11 +4625,11 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "target_change": { - "name": "Изменение целевого назначения перенесённой экономии (= 0)", + "name": "Изменение целевого назначения перенесённой экономии, тыс. руб. (= 0)", "key": "q3.target_change", }, "base_correction": { - "name": "Корректировка статей базового плана (= 0)", + "name": "Корректировка статей базового плана, тыс. руб. (= 0)", "key": "q3.base_correction", }, "base_correction_comment": { @@ -4637,11 +4637,11 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q3.base_correction_comment", }, "revision_inc": { - "name": "ДФиП: увеличение плана (> 0)", + "name": "ДФиП: увеличение плана, тыс. руб. (> 0)", "key": "q3.revision_inc", }, "revision_seq": { - "name": "ДФиП: секвестр плана (< 0)", + "name": "ДФиП: секвестр плана, тыс. руб. (< 0)", "key": "q3.revision_seq", }, "revision_comment": { @@ -4649,7 +4649,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q3.revision_comment", }, "new_plan": { - "name": "Скорректированный план на подпись", + "name": "Скорректированный план на подпись, тыс. руб.", "key": "q3.new_plan", }, }, @@ -4659,15 +4659,15 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "adj_current": { - "name": "Текущие корректировки (= 0)", + "name": "Текущие корректировки, тыс. руб. (= 0)", "key": "q3.adj_current", }, "adj_ssp": { - "name": "Корректировка из сметы ГО", + "name": "Корректировка из сметы ГО, тыс. руб.", "key": "q3.adj_ssp", }, "adj_reserve": { - "name": "Корректировка из резерва", + "name": "Корректировка из резерва, тыс. руб.", "key": "q3.adj_reserve", }, "adj_comment": { @@ -4675,7 +4675,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q3.adj_comment", }, "corrected_plan": { - "name": "Скорректированный план с учётом корректировок", + "name": "Скорректированный план с учётом корректировок, тыс. руб.", "key": "q3.corrected_plan", }, }, @@ -4689,7 +4689,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q3.pay_date", }, "pay_amount": { - "name": "Сумма платежа", + "name": "Сумма платежа, тыс. руб.", "key": "q3.pay_amount", }, "pay_comment": { @@ -4703,7 +4703,7 @@ FORM2_DEPTH_COLUMNS: dict = { }, }, "fakticheskie_raskhody": { - "name": "Фактические расходы", + "name": "Фактические расходы, тыс. руб.", "key": None, "children": { "booking": { @@ -4741,19 +4741,19 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "transfer_q4": { - "name": "Перенос в 4 квартал", + "name": "Перенос в 4 квартал, тыс. руб.", "key": "q3.transfer_q4", }, "transfer_q4_delay_acts": { - "name": "Задержка списания расходов / предоставления актов", + "name": "Задержка списания расходов / предоставления актов, тыс. руб.", "key": "q3.transfer_q4_delay_acts", }, "transfer_q4_delay_procurement": { - "name": "Задержка закупочных процедур", + "name": "Задержка закупочных процедур, тыс. руб.", "key": "q3.transfer_q4_delay_procurement", }, "transfer_q4_economy_rf": { - "name": "Экономия (будет использована РФ)", + "name": "Экономия (будет использована РФ), тыс. руб.", "key": "q3.transfer_q4_economy_rf", }, "transfer_next_comment": { @@ -4761,11 +4761,11 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q3.transfer_next_comment", }, "transfer_econ": { - "name": "Перенос в фонд экономии", + "name": "Перенос в фонд экономии, тыс. руб.", "key": "q3.transfer_econ", }, "total": { - "name": "Всего", + "name": "Всего, тыс. руб.", "key": "q3.total", }, }, @@ -4781,11 +4781,11 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "target_change": { - "name": "Изменение целевого назначения перенесённой экономии (= 0)", + "name": "Изменение целевого назначения перенесённой экономии, тыс. руб. (= 0)", "key": "q4.target_change", }, "base_correction": { - "name": "Корректировка статей базового плана (= 0)", + "name": "Корректировка статей базового плана, тыс. руб. (= 0)", "key": "q4.base_correction", }, "base_correction_comment": { @@ -4793,11 +4793,11 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q4.base_correction_comment", }, "revision_inc": { - "name": "ДФиП: увеличение плана (> 0)", + "name": "ДФиП: увеличение плана, тыс. руб. (> 0)", "key": "q4.revision_inc", }, "revision_seq": { - "name": "ДФиП: секвестр плана (< 0)", + "name": "ДФиП: секвестр плана, тыс. руб. (< 0)", "key": "q4.revision_seq", }, "revision_comment": { @@ -4805,7 +4805,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q4.revision_comment", }, "new_plan": { - "name": "Скорректированный план на подпись", + "name": "Скорректированный план на подпись, тыс. руб.", "key": "q4.new_plan", }, }, @@ -4815,15 +4815,15 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "adj_current": { - "name": "Текущие корректировки (= 0)", + "name": "Текущие корректировки, тыс. руб. (= 0)", "key": "q4.adj_current", }, "adj_ssp": { - "name": "Корректировка из сметы ГО", + "name": "Корректировка из сметы ГО, тыс. руб.", "key": "q4.adj_ssp", }, "adj_reserve": { - "name": "Корректировка из резерва", + "name": "Корректировка из резерва, тыс. руб.", "key": "q4.adj_reserve", }, "adj_comment": { @@ -4831,7 +4831,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q4.adj_comment", }, "corrected_plan": { - "name": "Скорректированный план с учётом корректировок", + "name": "Скорректированный план с учётом корректировок, тыс. руб.", "key": "q4.corrected_plan", }, }, @@ -4845,7 +4845,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": "q4.pay_date", }, "pay_amount": { - "name": "Сумма платежа", + "name": "Сумма платежа, тыс. руб.", "key": "q4.pay_amount", }, "pay_comment": { @@ -4859,7 +4859,7 @@ FORM2_DEPTH_COLUMNS: dict = { }, }, "fakticheskie_raskhody": { - "name": "Фактические расходы", + "name": "Фактические расходы, тыс. руб.", "key": None, "children": { "booking": { @@ -4897,7 +4897,7 @@ FORM2_DEPTH_COLUMNS: dict = { }, }, "ekonomiya": { - "name": "Экономия", + "name": "Экономия, тыс. руб.", "key": None, "children": { "transfer_econ": { @@ -4913,7 +4913,7 @@ FORM2_DEPTH_COLUMNS: dict = { "key": None, "children": { "fact_year": { - "name": "Сумма фактических расходов за год", + "name": "Сумма фактических расходов за год, тыс. руб.", "key": "totals.fact_year", }, "pay_year": { diff --git a/api/src/services/export_service/export_service.py b/api/src/services/export_service/export_service.py index 12af8c3..460b660 100644 --- a/api/src/services/export_service/export_service.py +++ b/api/src/services/export_service/export_service.py @@ -88,18 +88,40 @@ SHEET_NAMES = { } SKIP_SHEETS = ('OTCH9F',) # AURORA-1900: текст НДС в экспорте должен совпадать с экраном (вместо текстовой -# опечатки «с учетом НДC» — Latin C). Ф.1 AHR и все листы Ф.4 показывают суммы без НДС, -# Ф.1 OPER/CAP — с учётом НДС (см. render_column_years(..., extra=...) ниже). +# опечатки «с учетом НДC» — Latin C). Подколонки ({vat}): Ф.1 AHR и все листы Ф.4 — +# «без НДС», Ф.1 OPER/CAP — «с учётом НДС». Шапка «План {year} (тыс. руб.) {vat_plan}» +# на экране подписана иначе, чем подколонки под ней (возврат 12.11): Ф.1 AHR и Ф.4 +# AHR/OPER/STRUCTURE — «с учётом складского НДС», Ф.4 CAP — «с учётом НДС». +# См. render_column_years(..., extra=...) ниже. FORM1_SHEET_VAT_WORDING = { "OPER": "с учётом НДС", "CAP": "с учётом НДС", "AHR": "без НДС", } FORM4_SHEET_VAT_WORDING = "без НДС" +FORM1_SHEET_PLAN_VAT_WORDING = { + "OPER": "с учётом НДС", + "CAP": "с учётом НДС", + "AHR": "с учётом складского НДС", +} +FORM4_SHEET_PLAN_VAT_WORDING = { + "OPER": "с учётом складского НДС", + "AHR": "с учётом складского НДС", + "CAP": "с учётом НДС", + "STRUCTURE": "с учётом складского НДС", +} -def _vat_placeholders(vat_phrase: str) -> dict[str, str]: - return {"vat": vat_phrase, "vat_plan": vat_phrase} +def _vat_placeholders(vat_phrase: str, plan_vat_phrase: str) -> dict[str, str]: + return {"vat": vat_phrase, "vat_plan": plan_vat_phrase} + + +def _form1_vat(sheet: str) -> dict[str, str]: + return _vat_placeholders(FORM1_SHEET_VAT_WORDING[sheet], FORM1_SHEET_PLAN_VAT_WORDING[sheet]) + + +def _form4_vat(sheet: str) -> dict[str, str]: + return _vat_placeholders(FORM4_SHEET_VAT_WORDING, FORM4_SHEET_PLAN_VAT_WORDING[sheet]) BULK_EXPORT_CONCURRENCY = 2 EXPORT_TMPDIR = os.getenv("EXPORT_TMPDIR") or tempfile.gettempdir() @@ -954,7 +976,7 @@ class ExportService: default_writer = XlsxDepthSheetWriter( workbook=workbook, columns_dict=render_column_years( - FORM1_DEPTH_COLUMNS, year, _vat_placeholders(FORM1_SHEET_VAT_WORDING["OPER"]) + FORM1_DEPTH_COLUMNS, year, _form1_vat("OPER") ), row_normalizer=self.form1_row_normalizer.normalize, value_serializer=self.value_serializer.to_excel_value, @@ -973,25 +995,25 @@ class ExportService: "OPER": ( FORM4_OPER_DEPTH_COLUMNS, FORM4_OPER_COLORS, - _vat_placeholders(FORM4_SHEET_VAT_WORDING), + _form4_vat("OPER"), self.form4_row_normalizer, ), "AHR": ( FORM4_DEPTH_COLUMNS, FORM4_COLORS["AHR"], - _vat_placeholders(FORM4_SHEET_VAT_WORDING), + _form4_vat("AHR"), self.form4_row_normalizer, ), "CAP": ( FORM4_DEPTH_COLUMNS, FORM4_COLORS["CAP"], - _vat_placeholders(FORM4_SHEET_VAT_WORDING), + _form4_vat("CAP"), self.form4_row_normalizer, ), "STRUCTURE": ( FORM4_STRUCTURE_DEPTH_COLUMNS, FORM4_STRUCTURE_COLORS, - _vat_placeholders(FORM4_SHEET_VAT_WORDING), + _form4_vat("STRUCTURE"), self.form1_row_normalizer, ), } @@ -1010,7 +1032,7 @@ class ExportService: "STRUCTURE": ( FORM4_STRUCTURE_DEPTH_COLUMNS, FORM4_STRUCTURE_COLORS, - _vat_placeholders(FORM4_SHEET_VAT_WORDING), + _form4_vat("STRUCTURE"), self.form1_row_normalizer, ), } @@ -1020,19 +1042,19 @@ class ExportService: "OPER": ( FORM1_OPER_DEPTH_COLUMNS, FORM1_COLORS["OPER"], - _vat_placeholders(FORM1_SHEET_VAT_WORDING["OPER"]), + _form1_vat("OPER"), self.form1_row_normalizer, ), "AHR": ( FORM1_DEPTH_COLUMNS, FORM1_COLORS["AHR"], - _vat_placeholders(FORM1_SHEET_VAT_WORDING["AHR"]), + _form1_vat("AHR"), self.form1_row_normalizer, ), "CAP": ( FORM1_DEPTH_COLUMNS, FORM1_COLORS["CAP"], - _vat_placeholders(FORM1_SHEET_VAT_WORDING["CAP"]), + _form1_vat("CAP"), self.form1_row_normalizer, ), "AHR_RENT": (FORM2_DEPTH_AHR_SUB_COLUMNS, None, None, self.form1_row_normalizer), @@ -1041,7 +1063,7 @@ class ExportService: "STRUCTURE": ( FORM4_STRUCTURE_DEPTH_COLUMNS, FORM4_STRUCTURE_COLORS, - _vat_placeholders(FORM4_SHEET_VAT_WORDING), + _form4_vat("STRUCTURE"), self.form1_row_normalizer, ), } diff --git a/api/tests/unit/test_export_form1_form4_mapping.py b/api/tests/unit/test_export_form1_form4_mapping.py index ad707ac..046d734 100644 --- a/api/tests/unit/test_export_form1_form4_mapping.py +++ b/api/tests/unit/test_export_form1_form4_mapping.py @@ -78,7 +78,7 @@ def test_form1_oper_has_no_future_y2_but_ahr_cap_do(): 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, "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" @@ -87,7 +87,7 @@ 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 году" + == "Платежи в 2028 году, тыс. руб." ) diff --git a/api/tests/unit/test_export_form2_mapping.py b/api/tests/unit/test_export_form2_mapping.py index 2f93bdf..8d43c34 100644 --- a/api/tests/unit/test_export_form2_mapping.py +++ b/api/tests/unit/test_export_form2_mapping.py @@ -122,7 +122,7 @@ def test_render_column_years_resolves_form2_top_group_names(): rendered = render_column_years(FORM2_DEPTH_COLUMNS, 2026) names = {node["name"] for node in rendered.values()} assert "2026 год" in names - assert "2027 год" in names + assert "2027 год, тыс. руб." in names assert "1 квартал 2026 года" in names # Original templates are untouched (render_column_years deep-copies). assert any("{year}" in node["name"] for node in FORM2_DEPTH_COLUMNS.values() if "{year}" in node.get("name", "")) diff --git a/api/tests/unit/test_export_unit_labels.py b/api/tests/unit/test_export_unit_labels.py new file mode 100644 index 0000000..4894076 --- /dev/null +++ b/api/tests/unit/test_export_unit_labels.py @@ -0,0 +1,123 @@ +"""AURORA-1900 (возврат 12.11): подписи единиц и НДС в выгрузке Excel так же, как на экране. + +Проверяются раскладки, которые реально использует выгрузка формы +(ExportService._create_xlsxwriter_sheet_writers), после подстановки года и НДС: +- у каждой денежной колонки «тыс. руб.» есть в её заголовке или в заголовке группы; +- у текстовых колонок (комментарии, коды, реквизиты) денежной подписи нет; +- подпись НДС в шапке плана совпадает с экраном того же листа. + +Деление на денежные и текстовые колонки повторяет web/src/components/RealtimeTable/ +utils/moneyUtils.js (getMoneyUnit/isTextColumn) по последнему сегменту ключа. +""" + +from unittest.mock import MagicMock + +import pytest + +from src.services.export_service.export_service import ExportService + +TEXT_LAST_SEGMENTS = { + "section", "section_code", "section_no", "kod_razdela", "item_id", "num_group", "num_group_id", + "internal_order", "contract_number", "vsp_id", "pay_year", "order", "expense_item_code", "id_stati", + "comment", "justification", "note", "name", "counterparty", "subject", "reference", "contract_ref", + "deadline", "delivery_deadline", "date", "pay_date", "contract_end_date", "currency", "act", "pay_act", + "scheme", "payment_scheme", "rf_schedule", "addenda", "proc_plan", "proc_method", "procurement_plan", + "procurement_method", "property", "allocation_purpose", "protocol", "protocol_reference", + "centralized_flag", "object_type", "unit", "address", "equipment_name", +} +NUMERIC_NON_MONEY_LAST_SEGMENTS = { + "qty_q1", "qty_q2", "qty_q3", "qty_q4", "rented_area", "vat_rate", "exchange_rate", "amount_foreign", +} + +# Текст шапки «План {year} (…) <НДС>» на экране (web/src/components/RealtimeTable/constants). +SCREEN_PLAN_VAT = { + ("FORM_1", "OPER"): "с учётом НДС", + ("FORM_1", "CAP"): "с учётом НДС", + ("FORM_1", "AHR"): "с учётом складского НДС", + ("FORM_1", "STRUCTURE"): "с учётом складского НДС", + ("FORM_2", "STRUCTURE"): "с учётом складского НДС", + ("FORM_4", "OPER"): "с учётом складского НДС", + ("FORM_4", "AHR"): "с учётом складского НДС", + ("FORM_4", "CAP"): "с учётом НДС", + ("FORM_4", "STRUCTURE"): "с учётом складского НДС", +} + +FORMS = ("FORM_1", "FORM_2", "FORM_4") + + +def _kind(flat_key: str) -> str: + last = flat_key.rsplit(".", 1)[-1] + if last in TEXT_LAST_SEGMENTS or last.endswith("_comment"): + return "text" + if last in NUMERIC_NON_MONEY_LAST_SEGMENTS or last.endswith("_qty"): + return "number" + return "money" + + +def _has_unit(label) -> bool: + return isinstance(label, str) and "руб" in label + + +def _leaves(columns: dict, prefix: str = "", trail: tuple = ()) -> list[tuple[str, tuple]]: + # Ключи собираются так же, как XlsxDepthSheetWriter._columns_dict_to_flat. + result = [] + for key, node in columns.items(): + new_key = node.get("key", key) + path = (*trail, node.get("name", "")) + if "children" in node: + result += _leaves(node["children"], f"{prefix}{new_key}." if new_key else prefix, path) + elif new_key: + result.append((f"{prefix}{new_key}", path)) + return result + + +def _sheet_writers(): + service = ExportService(MagicMock()) + for form in FORMS: + writers = service._create_xlsxwriter_sheet_writers(MagicMock(), form, 2026) + for sheet, writer in writers.items(): + if sheet != "__default__": + yield form, sheet, writer + + +SHEET_WRITERS = list(_sheet_writers()) +IDS = [f"{form}/{sheet}" for form, sheet, _ in SHEET_WRITERS] + + +def _labelled_leaves(writer) -> list[tuple[str, tuple]]: + if hasattr(writer, "columns_dict"): + return _leaves(writer.columns_dict) + return [(key, (label,)) for key, label in writer.columns] + + +@pytest.mark.parametrize(("form", "sheet", "writer"), SHEET_WRITERS, ids=IDS) +def test_every_money_column_has_unit(form, sheet, writer): + missing = [ + f"{key}: {' → '.join(path)}" + for key, path in _labelled_leaves(writer) + if _kind(key) == "money" and not any(map(_has_unit, path)) + ] + assert missing == [] + + +@pytest.mark.parametrize(("form", "sheet", "writer"), SHEET_WRITERS, ids=IDS) +def test_text_columns_have_no_money_label(form, sheet, writer): + labelled = [ + f"{key}: {' → '.join(path)}" + for key, path in _labelled_leaves(writer) + if _kind(key) == "text" and _has_unit(path[-1]) + ] + assert labelled == [] + + +@pytest.mark.parametrize(("form", "sheet"), sorted(SCREEN_PLAN_VAT), ids=[f"{f}/{s}" for f, s in sorted(SCREEN_PLAN_VAT)]) +def test_plan_header_vat_matches_screen(form, sheet): + writer = next(w for f, s, w in SHEET_WRITERS if (f, s) == (form, sheet)) + plan_headers = { + label + for _key, path in _leaves(writer.columns_dict) + for label in path + # «Итоговая смета расходов → План 2026, …» — другая группа, без НДС в тексте. + if isinstance(label, str) and label.startswith("План 2026") and "НДС" in label + } + assert plan_headers == {f"План 2026 (тыс. руб.) {SCREEN_PLAN_VAT[(form, sheet)]}"} diff --git a/web/src/components/RealtimeTable/constants/FORM_1/AHR.js b/web/src/components/RealtimeTable/constants/FORM_1/AHR.js index 81dd9b8..8f7d24f 100644 --- a/web/src/components/RealtimeTable/constants/FORM_1/AHR.js +++ b/web/src/components/RealtimeTable/constants/FORM_1/AHR.js @@ -1402,7 +1402,7 @@ export const config = { accessorKey: 'field_kollegialnye_organy', columns: [ { - header: 'Сумма (без НДС)', + header: 'Сумма, {unit} (без НДС)', accessorKey: 'data.collegial.approved', columnLetter: 'AS', size: 150, diff --git a/web/src/components/RealtimeTable/constants/FORM_1/AHR_P_NO_LOCAL.js b/web/src/components/RealtimeTable/constants/FORM_1/AHR_P_NO_LOCAL.js index 5acb520..e37c5bc 100644 --- a/web/src/components/RealtimeTable/constants/FORM_1/AHR_P_NO_LOCAL.js +++ b/web/src/components/RealtimeTable/constants/FORM_1/AHR_P_NO_LOCAL.js @@ -1390,7 +1390,7 @@ export const config = { accessorKey: 'field_kollegialnye_organy', columns: [ { - header: 'Сумма (без НДС)', + header: 'Сумма, {unit} (без НДС)', accessorKey: 'data.collegial.approved', columnLetter: 'AS', size: 150, diff --git a/web/src/components/RealtimeTable/constants/FORM_1/CAP.js b/web/src/components/RealtimeTable/constants/FORM_1/CAP.js index f5ac63c..2e78a4c 100644 --- a/web/src/components/RealtimeTable/constants/FORM_1/CAP.js +++ b/web/src/components/RealtimeTable/constants/FORM_1/CAP.js @@ -1410,7 +1410,7 @@ export const config = { accessorKey: 'field_kollegialnye_organy', columns: [ { - header: 'Сумма (с учётом НДС)', + header: 'Сумма, {unit} (с учётом НДС)', accessorKey: 'data.collegial.approved', columnLetter: 'AS', size: 200, diff --git a/web/src/components/RealtimeTable/constants/FORM_1/CAP_NO_LOCAL.js b/web/src/components/RealtimeTable/constants/FORM_1/CAP_NO_LOCAL.js index 72eb83d..fe16bbb 100644 --- a/web/src/components/RealtimeTable/constants/FORM_1/CAP_NO_LOCAL.js +++ b/web/src/components/RealtimeTable/constants/FORM_1/CAP_NO_LOCAL.js @@ -1410,7 +1410,7 @@ export const config = { accessorKey: 'field_kollegialnye_organy', columns: [ { - header: 'Сумма (с учётом НДС)', + header: 'Сумма, {unit} (с учётом НДС)', accessorKey: 'data.collegial.approved', columnLetter: 'AS', size: 200, diff --git a/web/src/components/RealtimeTable/constants/FORM_1/OPER.js b/web/src/components/RealtimeTable/constants/FORM_1/OPER.js index ade5f4e..9fd2c8d 100644 --- a/web/src/components/RealtimeTable/constants/FORM_1/OPER.js +++ b/web/src/components/RealtimeTable/constants/FORM_1/OPER.js @@ -1393,7 +1393,7 @@ export const config = { accessorKey: 'field_kollegialnye_organy', columns: [ { - header: 'Сумма (с учётом НДС)', + header: 'Сумма, {unit} (с учётом НДС)', accessorKey: 'data.collegial.approved', columnLetter: 'AS', size: 200, diff --git a/web/src/components/RealtimeTable/constants/FORM_4/AHR.js b/web/src/components/RealtimeTable/constants/FORM_4/AHR.js index c5b63c7..be2e2ae 100644 --- a/web/src/components/RealtimeTable/constants/FORM_4/AHR.js +++ b/web/src/components/RealtimeTable/constants/FORM_4/AHR.js @@ -1490,7 +1490,7 @@ export const config = { accessorKey: "field_kollegialnye_organy", columns: [ { - header: "Сумма (без НДС)", + header: "Сумма, {unit} (без НДС)", accessorKey: "data.collegial.approved", columnLetter: "AS", size: 150, diff --git a/web/src/components/RealtimeTable/constants/FORM_4/CAP.js b/web/src/components/RealtimeTable/constants/FORM_4/CAP.js index bdb1e1a..5708772 100644 --- a/web/src/components/RealtimeTable/constants/FORM_4/CAP.js +++ b/web/src/components/RealtimeTable/constants/FORM_4/CAP.js @@ -1490,7 +1490,7 @@ export const config = { accessorKey: "field_kollegialnye_organy", columns: [ { - header: "Сумма (без НДС)", + header: "Сумма, {unit} (без НДС)", accessorKey: "data.collegial.approved", columnLetter: "AS", size: 150, diff --git a/web/src/components/RealtimeTable/constants/FORM_4/OPER.js b/web/src/components/RealtimeTable/constants/FORM_4/OPER.js index 0bf0257..d88fd3e 100644 --- a/web/src/components/RealtimeTable/constants/FORM_4/OPER.js +++ b/web/src/components/RealtimeTable/constants/FORM_4/OPER.js @@ -1491,7 +1491,7 @@ export const config = { accessorKey: "field_kollegialnye_organy", columns: [ { - header: "Сумма (без НДС)", + header: "Сумма, {unit} (без НДС)", accessorKey: "data.collegial.approved", columnLetter: "AS", size: 150, diff --git a/web/src/components/RealtimeTable/utils/moneyUtils.js b/web/src/components/RealtimeTable/utils/moneyUtils.js index 864d142..c9aaac1 100644 --- a/web/src/components/RealtimeTable/utils/moneyUtils.js +++ b/web/src/components/RealtimeTable/utils/moneyUtils.js @@ -105,7 +105,6 @@ const TEXT_LAST_SEGMENTS = new Set([ 'allocation_purpose', 'protocol', 'protocol_reference', - 'approved', 'centralized_flag', 'object_type', 'unit', diff --git a/web/src/components/RealtimeTable/utils/moneyUtils.test.js b/web/src/components/RealtimeTable/utils/moneyUtils.test.js index 7e27c95..d7a68df 100644 --- a/web/src/components/RealtimeTable/utils/moneyUtils.test.js +++ b/web/src/components/RealtimeTable/utils/moneyUtils.test.js @@ -20,6 +20,10 @@ test('getDisplayFactor converts between units with thousand as the base', () => test('getMoneyUnit defaults ordinary money columns to thousand', () => { assert.equal(getMoneyUnit({ accessorKey: 'data.plan.q1' }, 'AHR'), 'thousand'); + // AURORA-1900 (возврат 12.11): «Коллегиальные органы → Сумма (без НДС)» — + // v3 collegial_approval.approved_amount NUMERIC, денежная колонка, а не флаг. + assert.equal(getMoneyUnit({ accessorKey: 'data.collegial.approved' }, 'AHR'), 'thousand'); + assert.equal(isTextColumn({ accessorKey: 'data.collegial.approved' }), false); }); test('getMoneyUnit returns null for quantities, rates, codes and ids regardless of sheet', () => { @@ -95,7 +99,6 @@ test('getMoneyUnit returns null for text columns: comments, justifications and o 'data.ckk.proc_method', 'data.allocation.property', 'data.collegial.protocol', - 'data.collegial.approved', 'data.contract_summary.centralized_flag', 'data.contract_end_date', 'data.object_type',