diff --git a/api/src/services/export_service/__init__.py b/api/src/services/export_service/__init__.py new file mode 100644 index 0000000..cd25930 --- /dev/null +++ b/api/src/services/export_service/__init__.py @@ -0,0 +1 @@ +from .export_service import ExportService \ No newline at end of file diff --git a/api/src/services/export_service/export_mappings.py b/api/src/services/export_service/export_mappings.py new file mode 100644 index 0000000..cf61e5f --- /dev/null +++ b/api/src/services/export_service/export_mappings.py @@ -0,0 +1,2912 @@ + + +FORM1_FLAT_COLUMNS: list[tuple[str, str]] = [ + ("section", "Код раздела"), + ("item_id", "ID статьи"), + ("name", "Наименование"), + ("q1", "I квартал"), + ("q2", "II квартал"), + ("q3", "III квартал"), + ("q4", "IV квартал"), + ("totals", "ИТОГО"), + ("plan", "План"), + ("reserve", "Резерв"), + ("approved", "Согласовано"), + ("collegial", "Коллегиальный"), + ("allocation", "Аллокация"), + ("sequestration", "Секвестр"), + ("contract", "Договор"), + ("contract_sum", "Сумма договора"), + ("ckk", "ЦКК"), +] +FORM1_DEPTH_COLUMNS: dict = { + "data": { + "key": None, + "name": "Данные", + "children": { + "section": { + "name": "Код раздела", + }, + "item_id": { + "name": "ID статьи", + }, + "num_group": { + "name": "ID группы номенклатуры", + }, + "name": { + "name": "Наименование", + }, + "internal_order": { + "name": "Внутренний заказ", + }, + } + }, + + "upper_ssp_smeta": { + "name": "Планируемые ССП показатели сметы расходов", + "key": None, + "children": { + "plan": { + "name": "План 2024(тыс. руб.) с учетом НДС", + "children": { + "q1": { + "name": "1 кв. 24", + }, + "q2": { + "name": "2 кв. 24", + }, + "q3": { + "name": "3 кв. 24", + }, + "q4": { + "name": "4 кв. 24", + }, + "year": { + "name": "Год", + }, + "comment": { + "name": "Комментарий", + }, + } + }, + + "contract_summary": { + "name": "Договоры", + "children": { + "total": { + "name": "Сумма", + }, + "counterparty": { + "name": "Контрагент", + }, + "deadline": { + "name": "Срок", + }, + "comment": { + "name": "Комментарии", + }, + "future_y1": { + "name": "Платежи в 2025 году", + }, + "other_ssp": { + "name": "Сумма в других ССП", + }, + } + } + } + }, + "allocation": { + "name": "Аллокация расходов", + "children": { + "order": { + "name": "", + }, + "property": { + "name": "Назначение расхода фактической", + }, + } + }, + "sequestration": { + "name": "Секвестирование ДФиП", + "children": { + "correct": { + "name": "Корректировка сметы расходов", + "key": None, + "children": { + "q1": { + "name": "1 кв. 24", + }, + "q2": { + "name": "2 кв. 24", + }, + "q3": { + "name": "3 кв. 24", + }, + "q4": { + "name": "4 кв. 24", + }, + "year": { + "name": "Год", + }, + }, + }, + "justification": { + "name": "Обоснование", + }, + } + }, + "reserve": { + "name": "Отнесение в резерв", + "children": { + "correct": { + "name": "Резерв Финансового Комитета", + "key": None, + "children": { + "q1": { + "name": "1 кв. 24", + }, + "q2": { + "name": "2 кв. 24", + }, + "q3": { + "name": "3 кв. 24", + }, + "q4": { + "name": "4 кв. 24", + }, + "year": { + "name": "Год", + }, + }, + }, + "justification": { + "name": "Обоснование", + }, + } + }, + "approved": { + "name": "Итоговая смета расходов", + "children": { + "plan": { + "name": "Резерв Финансового Комитета", + "key": None, + "children": { + "q1": { + "name": "1 кв. 24", + }, + "q2": { + "name": "2 кв. 24", + }, + "q3": { + "name": "3 кв. 24", + }, + "q4": { + "name": "4 кв. 24", + }, + "year": { + "name": "Год", + }, + }, + } + } + }, + "empty_upper": { + "name": "", + "key": None, + "children": { + "collegial": { + "name": "Коллегиальные органы", + "children": { + "approved": { + "name": "Сумма (с учетом НДC)", + }, + "protocol": { + "name": "Реквизиты протокола (дата, номер)", + }, + "empty": { + "name": "", + }, + "note": { + "name": "Примечание", + }, + } + }, + "ckk": { + "name": "ЦКК", + "children": { + "ceiling": { + "name": "Предельная ст-ть, тыс. руб. (с учетом НДC)", + }, + "q1": { + "name": "Расходы 1 кв. 2024 г. тыс. руб. (с учетом НДC)", + }, + "q2": { + "name": "Расходы 2 кв. 2024 г. тыс. руб. (с учетом НДC)", + }, + "q3": { + "name": "Расходы 3 кв. 2024 г. тыс. руб. (с учетом НДC)", + }, + "q4": { + "name": "Расходы 4 кв. 2024 г. тыс. руб. (с учетом НДC)", + }, + "rf_schedule": { + "name": "по сметам РФ (1 кв, 2 кв, 3 кв, 4 кв)", + }, + "deadline": { + "name": "Срок поставки", + }, + "proc_plan": { + "name": "План закупок", + }, + "proc_method": { + "name": "Способ закупки", + }, + "comment": { + "name": "Комментарий", + } + } + }, + "contract_detail": { + "name": "Договор", + "children": { + "counterparty": { + "name": "Контрагент", + }, + "reference": { + "name": "Договор (реквизиты)", + }, + "addenda": { + "name": "Доп.соглашения, спецификации и пр. (реквизиты)", + }, + "subject": { + "name": "Предмет", + }, + "currency": { + "name": "Валюта договора", + }, + "ceiling": { + "name": "Предельная ст-ть, тыс. руб. (с учетом НДC)", + }, + "q1": { + "name": "Расходы 1 кв. 2024 г. тыс. руб. (с учетом НДC)", + }, + "q2": { + "name": "Расходы 2 кв. 2024 г. тыс. руб. (с учетом НДC)", + }, + "q3": { + "name": "Расходы 3 кв. 2024 г. тыс. руб. (с учетом НДC)", + }, + "q4": { + "name": "Расходы 4 кв. 2024 г. тыс. руб. (с учетом НДC)", + }, + "rf_schedule": { + "name": "по сметам РФ (1 кв, 2 кв, 3 кв, 4 кв)", + }, + "vat_rate": { + "name": "Ставка НДС (%)", + }, + "exchange_rate": { + "name": "Курс пересчета", + }, + "amount_foreign": { + "name": "Сумма (с учетом НДC), вал (с учетом НДC)", + }, + "deadline": { + "name": "Срок", + }, + "payment_scheme": { + "name": "Схема платежа", + }, + "act": { + "name": "АКТ", + }, + "comment": { + "name": "Комментарий", + } + } + }, + + } + }, + + + + "q1": { + "name": "1 квартал 2024", + "children": { + "cur_correct": { + "name": "Текущие корректировки( = 0)", + "key": None, + "children": { + "adj_current": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "ssp_correct": { + "name": "Корректировки с ССП / сметой развития", + "key": None, + "children": { + "adj_ssp": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rf_correct": { + "name": "Корректировки с РФ", + "key": None, + "children": { + "adj_rf": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "reserve_correct": { + "name": "Корректировки из резерва", + "key": None, + "children": { + "adj_reserve": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "shift": { + "name": "Перенос во 2 кв 24", + "key": None, + "children": { + "sum": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "comment_correct": { + "name": "Комментарий", + "key": None, + "children": { + "adj_comment": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "plan_correct": { + "name": "Скорр. план 1 кв. 24", + "key": None, + "children": { + "corrected_plan": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "empty": { + "name": "", + }, + "act_payment": { + "name": "Платеж и предоставление акта", + "key": None, + "children": { + "pay_date": { + "name": "Дата платежа" + }, + "pay_amount": { + "name": "Сумма, тыс. руб. (с учетом НДC)" + }, + "pay_ho": { + "name": "в т.ч. сумма (с учетом НДC) ГО" + }, + "pay_rf": { + "name": "в т.ч. сумма (с учетом НДC) РФ" + }, + "pay_comment": { + "name": "Комментарий" + }, + "pay_act": { + "name": "Предоставление акта (др. подтверждения)" + }, + } + }, + "empty": { + "name": "", + }, + "booking_correct": { + "name": "Бронь 1 кв 24", + "key": None, + "children": { + "booking": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "m1_correct": { + "name": "Факт РСБУ Январь 24", + "key": None, + "children": { + "actual_m1": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "m2_correct": { + "name": "Факт РСБУ Февраль 24", + "key": None, + "children": { + "actual_m2": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "m3_correct": { + "name": "Факт РСБУ Март 24", + "key": None, + "children": { + "actual_m3": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "quarter_correct": { + "name": "Факт РСБУ 1 кв 24", + "key": None, + "children": { + "actual_quarter": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rem_correct": { + "name": "Остаток после брони 1 кв. 24", + "key": None, + "children": { + "rem_booking": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rem_actual_correct": { + "name": "Остаток после факта 1 кв. 24", + "key": None, + "children": { + "rem_actual": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "close_q1": { + "name": "Закрытие 1-го квартала", + "key": None, + "children": { + "transfer_q2": { + "name": "Перенос во 2 кв.24" + }, + "transfer_q3": { + "name": "Перенос в 3 кв.24" + }, + "transfer_q4": { + "name": "Перенос в 4 кв.24" + }, + "economy": { + "name": "Перенос в фонд экономии" + }, + "total": { + "name": "ВСЕГО" + }, + } + }, + + } + }, + + "q2": { + "name": "2 квартал 2024", + "children": { + "rev_eco_correct": { + "name": "Изменение целевого назначения перенесенной экономии ( =0)", + "key": None, + "children": { + "rev_eco": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_item_correct": { + "name": "Корректировка статей базового плана ( =0)", + "key": None, + "children": { + "rev_item": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_inc_correct": { + "name": "Увеличение базового плана( >0)", + "key": None, + "children": { + "rev_inc": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_seq_correct": { + "name": "Cеквестр базового плана( <0)", + "key": None, + "children": { + "rev_seq": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_correct": { + "name": "Комментарий", + "key": None, + "children": { + "rev_comment": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "new_plan_correct": { + "name": "Новый план 2 кв 24", + "key": None, + "children": { + "new_plan": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_empty_correct": { + "name": "", + }, + + + "cur_correct": { + "name": "Текущие корректировки( = 0)", + "key": None, + "children": { + "adj_current": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "ssp_correct": { + "name": "Корректировки с ССП / сметой развития", + "key": None, + "children": { + "adj_ssp": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rf_correct": { + "name": "Корректировки с РФ", + "key": None, + "children": { + "adj_rf": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "reserve_correct": { + "name": "Корректировки из резерва", + "key": None, + "children": { + "adj_reserve": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "shift": { + "name": "Перенос в 3 кв 24", + "key": None, + "children": { + "sum": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "plan_correct": { + "name": "Скорр. план 2 кв. 24", + "key": None, + "children": { + "corrected_plan": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "empty": { + "name": "", + }, + "act_payment": { + "name": "Платеж и предоставление акта", + "key": None, + "children": { + "pay_date": { + "name": "Дата платежа" + }, + "pay_amount": { + "name": "Сумма, тыс. руб. (с учетом НДC)" + }, + "pay_ho": { + "name": "в т.ч. сумма (с учетом НДC) ГО" + }, + "pay_rf": { + "name": "в т.ч. сумма (с учетом НДC) РФ" + }, + "pay_comment": { + "name": "Комментарий" + }, + "pay_act": { + "name": "Предоставление акта (др. подтверждения)" + }, + } + }, + "booking_correct": { + "name": "Бронь 2 кв 24", + "key": None, + "children": { + "booking": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "m1_correct": { + "name": "Факт РСБУ Апрель 24", + "key": None, + "children": { + "actual_m1": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "m2_correct": { + "name": "Факт РСБУ Май 24", + "key": None, + "children": { + "actual_m2": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "m3_correct": { + "name": "Факт РСБУ Июнь 24", + "key": None, + "children": { + "actual_m3": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "quarter_correct": { + "name": "Факт РСБУ 2 кв 24", + "key": None, + "children": { + "actual_quarter": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rem_correct": { + "name": "Остаток после брони 2 кв. 24", + "key": None, + "children": { + "rem_booking": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rem_actual_correct": { + "name": "Остаток после факта 2 кв. 24", + "key": None, + "children": { + "rem_actual": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "close_q2": { + "name": "Закрытие 2-го квартала", + "key": None, + "children": { + "transfer_q3": { + "name": "Перенос в 3 кв.24" + }, + "transfer_q4": { + "name": "Перенос в 4 кв.24" + }, + "economy": { + "name": "Перенос в фонд экономии" + }, + "total": { + "name": "ВСЕГО" + }, + } + }, + } + }, + + "q3": { + "name": "3 квартал 2024", + "children": { + "rev_eco_correct": { + "name": "Изменение целевого назначения перенесенной экономии ( =0)", + "key": None, + "children": { + "rev_eco": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_item_correct": { + "name": "Корректировка статей базового плана ( =0)", + "key": None, + "children": { + "rev_item": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_inc_correct": { + "name": "Увеличение базового плана( >0)", + "key": None, + "children": { + "rev_inc": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_seq_correct": { + "name": "Cеквестр базового плана( <0)", + "key": None, + "children": { + "rev_seq": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_correct": { + "name": "Комментарий", + "key": None, + "children": { + "rev_comment": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "new_plan_correct": { + "name": "Новый план 3 кв 24", + "key": None, + "children": { + "new_plan": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_empty_correct": { + "name": "", + }, + + + "cur_correct": { + "name": "Текущие корректировки( = 0)", + "key": None, + "children": { + "adj_current": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "ssp_correct": { + "name": "Корректировки с ССП / сметой развития", + "key": None, + "children": { + "adj_ssp": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rf_correct": { + "name": "Корректировки с РФ", + "key": None, + "children": { + "adj_rf": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "reserve_correct": { + "name": "Корректировки из резерва", + "key": None, + "children": { + "adj_reserve": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "shift": { + "name": "Перенос в 4 кв 24", + "key": None, + "children": { + "sum": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "plan_correct": { + "name": "Скорр. план 3 кв. 24", + "key": None, + "children": { + "corrected_plan": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "empty": { + "name": "", + }, + "act_payment": { + "name": "Платеж и предоставление акта", + "key": None, + "children": { + "pay_date": { + "name": "Дата платежа" + }, + "pay_amount": { + "name": "Сумма, тыс. руб. (с учетом НДC)" + }, + "pay_ho": { + "name": "в т.ч. сумма (с учетом НДC) ГО" + }, + "pay_rf": { + "name": "в т.ч. сумма (с учетом НДC) РФ" + }, + "pay_comment": { + "name": "Комментарий" + }, + "pay_act": { + "name": "Предоставление акта (др. подтверждения)" + }, + } + }, + "booking_correct": { + "name": "Бронь 3 кв 24", + "key": None, + "children": { + "booking": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "m1_correct": { + "name": "Факт РСБУ июль 24", + "key": None, + "children": { + "actual_m1": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "m2_correct": { + "name": "Факт РСБУ август 24", + "key": None, + "children": { + "actual_m2": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "m3_correct": { + "name": "Факт РСБУ сентябрь 24", + "key": None, + "children": { + "actual_m3": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "quarter_correct": { + "name": "Факт РСБУ 3 кв 24", + "key": None, + "children": { + "actual_quarter": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rem_correct": { + "name": "Остаток после брони 3 кв. 24", + "key": None, + "children": { + "rem_booking": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rem_actual_correct": { + "name": "Остаток после факта 3 кв. 24", + "key": None, + "children": { + "rem_actual": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "close_q3": { + "name": "Закрытие 3-го квартала", + "key": None, + "children": { + "transfer_q4": { + "name": "Перенос в 4 кв.24" + }, + "economy": { + "name": "Перенос в фонд экономии" + }, + "total": { + "name": "ВСЕГО" + }, + } + }, + } + }, + + "q4": { + "name": "4 квартал 2024", + "children": { + "rev_eco_correct": { + "name": "Изменение целевого назначения перенесенной экономии ( =0)", + "key": None, + "children": { + "rev_eco": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_item_correct": { + "name": "Корректировка статей базового плана ( =0)", + "key": None, + "children": { + "rev_item": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_inc_correct": { + "name": "Увеличение базового плана( >0)", + "key": None, + "children": { + "rev_inc": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_seq_correct": { + "name": "Cеквестр базового плана( <0)", + "key": None, + "children": { + "rev_seq": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_correct": { + "name": "Комментарий", + "key": None, + "children": { + "rev_comment": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + }, + "adj_comment": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + }, + } + }, + "new_plan_correct": { + "name": "Новый план 4 кв 24", + "key": None, + "children": { + "new_plan": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rev_empty_correct": { + "name": "", + }, + + + "cur_correct": { + "name": "Текущие корректировки( = 0)", + "key": None, + "children": { + "adj_current": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "ssp_correct": { + "name": "Корректировки с ССП / сметой развития", + "key": None, + "children": { + "adj_ssp": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rf_correct": { + "name": "Корректировки с РФ", + "key": None, + "children": { + "adj_rf": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "reserve_correct": { + "name": "Корректировки из резерва", + "key": None, + "children": { + "adj_reserve": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "plan_correct": { + "name": "Скорр. план 4 кв. 24", + "key": None, + "children": { + "corrected_plan": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "empty": { + "name": "", + }, + "act_payment": { + "name": "Платеж и предоставление акта", + "key": None, + "children": { + "pay_date": { + "name": "Дата платежа" + }, + "pay_amount": { + "name": "Сумма, тыс. руб. (с учетом НДC)" + }, + "pay_ho": { + "name": "в т.ч. сумма (с учетом НДC) ГО" + }, + "pay_rf": { + "name": "в т.ч. сумма (с учетом НДC) РФ" + }, + "pay_comment": { + "name": "Комментарий" + }, + "pay_act": { + "name": "Предоставление акта (др. подтверждения)" + }, + } + }, + "booking_correct": { + "name": "Бронь 4 кв 24", + "key": None, + "children": { + "booking": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "m1_correct": { + "name": "Факт РСБУ октябрь 24", + "key": None, + "children": { + "actual_m1": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "m2_correct": { + "name": "Факт РСБУ ноябрь 24", + "key": None, + "children": { + "actual_m2": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "m3_correct": { + "name": "Факт РСБУ декабрь 24", + "key": None, + "children": { + "actual_m3": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "quarter_correct": { + "name": "Факт РСБУ 3 кв 24", + "key": None, + "children": { + "actual_quarter": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "quarter_actual_spod": { + "name": "Факт СПОД", + "key": None, + "children": { + "actual_spod": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rem_correct": { + "name": "Остаток после брони 4 кв. 24", + "key": None, + "children": { + "rem_booking": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "rem_actual_correct": { + "name": "Остаток после факта 4 кв. 24", + "key": None, + "children": { + "rem_actual": { + "name": "Сумма, тыс. руб. (с учетом НДC)", + } + } + }, + "close_q4": { + "name": "Закрытие 4-го квартала", + "key": None, + "children": { + "economy": { + "name": "Перенос в фонд экономии" + }, + "total": { + "name": "ВСЕГО" + }, + } + }, + } + }, + "totals": { + "name": "Факт 24", + "children": { + "fact_year": { + "name": "Факт 24", + }, + "empty": { + "name": "", + }, + "pay_year": { + "name": "Сумма оплаты", + }, + } + } +} + +PALETTE = { + "green": { + "ROOT": "C2D59A", + "GROUP": "D7E3BC", + "ITEM": "EAF0DD", + "SUB_ITEM": "FFFFFF", + "INPUT": "FFFFFF", + "COLOR": "000000", + }, + "orange": { + "ROOT": "F1C297", + "GROUP": "F6D6B9", + "ITEM": "FAEBDC", + "SUB_ITEM": "FFFFFF", + "INPUT": "FFFFFF", + "COLOR": "000000", + }, + "white": { + "ROOT": "FFFFFF", + "GROUP": "FFFFFF", + "ITEM": "ffffff", + "SUB_ITEM": "FFFFFF", + "INPUT": "FFFFFF", + "COLOR": "000000", + }, + "red": { + "ROOT": "933634", + "GROUP": "D89493", + "ITEM": "E5B7B6", + "SUB_ITEM": "FFFFFF", + "INPUT": "FFFFFF", + "COLOR": "ffffff", + }, + "blue": { + "ROOT": "538DD4", + "GROUP": "8DB3E2", + "ITEM": "C5D8F0", + "SUB_ITEM": "FFFFFF", + "INPUT": "FFFFFF", + "COLOR": "000000", + }, + "yellow": { + "ROOT": "FFFF66", + "GROUP": "FFFF99", + "ITEM": "FFFFCC", + "SUB_ITEM": "FFFFFF", + "INPUT": "FFFFFF", + "COLOR": "000000", + }, +} + +FORM1_COLORS: dict = { + "OPER": { + "data.section": { + "background": "f3f4f6", + "palette": "blue", + }, + "data.item_id": { + "background": "f3f4f6", + "palette": "blue", + }, + "data.num_group": { + "background": "f3f4f6", + "palette": "blue", + }, + "data.name": { + "background": "f3f4f6", + "palette": "blue", + }, + "data.internal_order": { + "background": "f3f4f6", + "palette": "blue", + }, + "upper_ssp_smeta": { + "background": "bfbfbf", + "palette": "blue", + }, + "upper_ssp_smeta.contract_summary.deadline": { + "palette": None, + }, + "allocation": { + "background": "bfbfbf", + "palette": "blue", + }, + "sequestration": { + "background": "bfbfbf", + "palette": "blue", + }, + "reserve": { + "background": "bfbfbf", + "palette": "blue", + }, + "approved": { + "background": "bfbfbf", + "palette": "blue", + }, + "empty_upper.collegial": { + "background": "fabf8f", + "palette": "orange", + }, + "empty_upper.ckk": { + "background": "fabf8f", + "palette": "orange", + }, + "empty_upper.ckk.q1": { + "background": "748c42", + }, + "empty_upper.ckk.q2": { + "background": "748c42", + }, + "empty_upper.ckk.q3": { + "background": "748c42", + }, + "empty_upper.ckk.q4": { + "background": "748c42", + }, + "empty_upper.contract_detail": { + "background": "fabf8f", + "palette": "orange", + }, + "empty_upper.contract_detail.q1": { + "background": "748c42", + }, + "empty_upper.contract_detail.q2": { + "background": "748c42", + }, + "empty_upper.contract_detail.q3": { + "background": "748c42", + }, + "empty_upper.contract_detail.q4": { + "background": "748c42", + }, + "q1.cur_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.ssp_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.rf_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.reserve_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.shift": { + "background": "933634", + "palette": "red", + }, + "q1.comment_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.act_payment": { + "background": "538DD4", + "palette": "blue", + }, + "q1.act_payment.pay_date": { + "background": "538DD4", + }, + "q1.act_payment.pay_amount": { + "background": "538DD4", + }, + "q1.act_payment.pay_ho": { + "background": "538DD4", + }, + "q1.act_payment.pay_rf": { + "background": "538DD4", + }, + "q1.act_payment.pay_comment": { + "background": "538DD4", + }, + "q1.act_payment.pay_act": { + "background": "538DD4", + }, + "q1.booking_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q1.m1_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.m2_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.m3_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.quarter_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.rem_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q1.rem_actual_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.close_q1": { + "background": "933634", + "palette": "red", + }, + "q1.close_q1.transfer_q2": { + "background": "933634", + }, + "q1.close_q1.transfer_q3": { + "background": "933634", + }, + "q1.close_q1.transfer_q4": { + "background": "933634", + }, + "q1.close_q1.economy": { + "background": "933634", + }, + "q1.close_q1.total": { + "background": "933634", + }, + + "q2.rev_eco_correct": { + "background": "933634", + "palette": "red", + }, + "q2.rev_item_correct": { + "background": "933634", + "palette": "red", + }, + "q2.rev_inc_correct": { + "background": "933634", + "palette": "red", + }, + "q2.rev_seq_correct": { + "background": "933634", + "palette": "red", + }, + "q2.rev_correct": { + "background": "933634", + "palette": "red", + }, + "q2.new_plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.cur_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.ssp_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.rf_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.reserve_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.shift": { + "background": "933634", + "palette": "red", + }, + "q2.comment_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.act_payment": { + "background": "538DD4", + "palette": "blue", + }, + "q2.act_payment.pay_date": { + "background": "538DD4", + }, + "q2.act_payment.pay_amount": { + "background": "538DD4", + }, + "q2.act_payment.pay_ho": { + "background": "538DD4", + }, + "q2.act_payment.pay_rf": { + "background": "538DD4", + }, + "q2.act_payment.pay_comment": { + "background": "538DD4", + }, + "q2.act_payment.pay_act": { + "background": "538DD4", + }, + "q2.booking_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q2.m1_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.m2_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.m3_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.quarter_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.rem_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q2.rem_actual_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.close_q2": { + "background": "933634", + "palette": "red", + }, + "q2.close_q2.transfer_q3": { + "background": "933634", + }, + "q2.close_q2.transfer_q4": { + "background": "933634", + }, + "q2.close_q2.economy": { + "background": "933634", + }, + "q2.close_q2.total": { + "background": "933634", + }, + + + "q3.rev_eco_correct": { + "background": "933634", + "palette": "red", + }, + "q3.rev_item_correct": { + "background": "933634", + "palette": "red", + }, + "q3.rev_inc_correct": { + "background": "933634", + "palette": "red", + }, + "q3.rev_seq_correct": { + "background": "933634", + "palette": "red", + }, + "q3.rev_correct": { + "background": "933634", + "palette": "red", + }, + "q3.new_plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.cur_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.ssp_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.rf_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.reserve_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.shift": { + "background": "933634", + "palette": "red", + }, + "q3.comment_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.act_payment": { + "background": "538DD4", + "palette": "blue", + }, + "q3.act_payment.pay_date": { + "background": "538DD4", + }, + "q3.act_payment.pay_amount": { + "background": "538DD4", + }, + "q3.act_payment.pay_ho": { + "background": "538DD4", + }, + "q3.act_payment.pay_rf": { + "background": "538DD4", + }, + "q3.act_payment.pay_comment": { + "background": "538DD4", + }, + "q3.act_payment.pay_act": { + "background": "538DD4", + }, + "q3.booking_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q3.m1_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.m2_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.m3_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.quarter_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.rem_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q3.rem_actual_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.close_q3": { + "background": "933634", + "palette": "red", + }, + "q3.close_q3.transfer_q4": { + "background": "933634", + }, + "q3.close_q3.economy": { + "background": "933634", + }, + "q3.close_q3.total": { + "background": "933634", + }, + + + + "q4.rev_eco_correct": { + "background": "933634", + "palette": "red", + }, + "q4.rev_item_correct": { + "background": "933634", + "palette": "red", + }, + "q4.rev_inc_correct": { + "background": "933634", + "palette": "red", + }, + "q4.rev_seq_correct": { + "background": "933634", + "palette": "red", + }, + "q4.rev_correct": { + "background": "933634", + "palette": "red", + }, + "q4.new_plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.cur_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.ssp_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.rf_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.reserve_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.shift": { + "background": "933634", + "palette": "red", + }, + "q4.comment_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.act_payment": { + "background": "538DD4", + "palette": "blue", + }, + "q4.act_payment.pay_date": { + "background": "538DD4", + }, + "q4.act_payment.pay_amount": { + "background": "538DD4", + }, + "q4.act_payment.pay_ho": { + "background": "538DD4", + }, + "q4.act_payment.pay_rf": { + "background": "538DD4", + }, + "q4.act_payment.pay_comment": { + "background": "538DD4", + }, + "q4.act_payment.pay_act": { + "background": "538DD4", + }, + "q4.booking_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q4.m1_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.m2_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.m3_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.quarter_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.rem_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q4.rem_actual_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.close_q4": { + "background": "933634", + "palette": "red", + }, + "q4.close_q4.economy": { + "background": "933634", + }, + "q4.close_q4.total": { + "background": "933634", + }, + + "totals.fact_year": { + "background": "C2D59A", + "palette": "green", + }, + "totals.pay_year": { + "background": "538DD4", + "palette": "blue", + } + }, + + "AHR": { + "data.section": { + "background": "f3f4f6", + "palette": "green", + }, + "data.item_id": { + "background": "f3f4f6", + "palette": "green", + }, + "data.num_group": { + "background": "f3f4f6", + "palette": "green", + }, + "data.name": { + "background": "f3f4f6", + "palette": "green", + }, + "data.internal_order": { + "background": "f3f4f6", + "palette": "green", + }, + "upper_ssp_smeta": { + "background": "bfbfbf", + "palette": "green", + }, + "upper_ssp_smeta.contract_summary.deadline": { + "palette": None, + }, + "allocation": { + "background": "bfbfbf", + "palette": "green", + }, + "sequestration": { + "background": "bfbfbf", + "palette": "green", + }, + "reserve": { + "background": "bfbfbf", + "palette": "green", + }, + "approved": { + "background": "bfbfbf", + "palette": "green", + }, + "empty_upper.collegial": { + "background": "fabf8f", + "palette": "orange", + }, + "empty_upper.ckk": { + "background": "fabf8f", + "palette": "orange", + }, + "empty_upper.ckk.q1": { + "background": "748c42", + }, + "empty_upper.ckk.q2": { + "background": "748c42", + }, + "empty_upper.ckk.q3": { + "background": "748c42", + }, + "empty_upper.ckk.q4": { + "background": "748c42", + }, + "empty_upper.contract_detail": { + "background": "fabf8f", + "palette": "orange", + }, + "empty_upper.contract_detail.q1": { + "background": "748c42", + }, + "empty_upper.contract_detail.q2": { + "background": "748c42", + }, + "empty_upper.contract_detail.q3": { + "background": "748c42", + }, + "empty_upper.contract_detail.q4": { + "background": "748c42", + }, + "q1.cur_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.ssp_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.rf_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.reserve_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.shift": { + "background": "933634", + "palette": "red", + }, + "q1.comment_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.act_payment": { + "background": "538DD4", + "palette": "blue", + }, + "q1.act_payment.pay_date": { + "background": "538DD4", + }, + "q1.act_payment.pay_amount": { + "background": "538DD4", + }, + "q1.act_payment.pay_ho": { + "background": "538DD4", + }, + "q1.act_payment.pay_rf": { + "background": "538DD4", + }, + "q1.act_payment.pay_comment": { + "background": "538DD4", + }, + "q1.act_payment.pay_act": { + "background": "538DD4", + }, + "q1.booking_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q1.m1_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.m2_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.m3_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.quarter_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.rem_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q1.rem_actual_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.close_q1": { + "background": "933634", + "palette": "red", + }, + "q1.close_q1.transfer_q2": { + "background": "933634", + }, + "q1.close_q1.transfer_q3": { + "background": "933634", + }, + "q1.close_q1.transfer_q4": { + "background": "933634", + }, + "q1.close_q1.economy": { + "background": "933634", + }, + "q1.close_q1.total": { + "background": "933634", + }, + + "q2.rev_eco_correct": { + "background": "933634", + "palette": "red", + }, + "q2.rev_item_correct": { + "background": "933634", + "palette": "red", + }, + "q2.rev_inc_correct": { + "background": "933634", + "palette": "red", + }, + "q2.rev_seq_correct": { + "background": "933634", + "palette": "red", + }, + "q2.rev_correct": { + "background": "933634", + "palette": "red", + }, + "q2.new_plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.cur_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.ssp_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.rf_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.reserve_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.shift": { + "background": "933634", + "palette": "red", + }, + "q2.comment_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.act_payment": { + "background": "538DD4", + "palette": "blue", + }, + "q2.act_payment.pay_date": { + "background": "538DD4", + }, + "q2.act_payment.pay_amount": { + "background": "538DD4", + }, + "q2.act_payment.pay_ho": { + "background": "538DD4", + }, + "q2.act_payment.pay_rf": { + "background": "538DD4", + }, + "q2.act_payment.pay_comment": { + "background": "538DD4", + }, + "q2.act_payment.pay_act": { + "background": "538DD4", + }, + "q2.booking_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q2.m1_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.m2_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.m3_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.quarter_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.rem_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q2.rem_actual_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.close_q2": { + "background": "933634", + "palette": "red", + }, + "q2.close_q2.transfer_q3": { + "background": "933634", + }, + "q2.close_q2.transfer_q4": { + "background": "933634", + }, + "q2.close_q2.economy": { + "background": "933634", + }, + "q2.close_q2.total": { + "background": "933634", + }, + + + "q3.rev_eco_correct": { + "background": "933634", + "palette": "red", + }, + "q3.rev_item_correct": { + "background": "933634", + "palette": "red", + }, + "q3.rev_inc_correct": { + "background": "933634", + "palette": "red", + }, + "q3.rev_seq_correct": { + "background": "933634", + "palette": "red", + }, + "q3.rev_correct": { + "background": "933634", + "palette": "red", + }, + "q3.new_plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.cur_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.ssp_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.rf_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.reserve_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.shift": { + "background": "933634", + "palette": "red", + }, + "q3.comment_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.act_payment": { + "background": "538DD4", + "palette": "blue", + }, + "q3.act_payment.pay_date": { + "background": "538DD4", + }, + "q3.act_payment.pay_amount": { + "background": "538DD4", + }, + "q3.act_payment.pay_ho": { + "background": "538DD4", + }, + "q3.act_payment.pay_rf": { + "background": "538DD4", + }, + "q3.act_payment.pay_comment": { + "background": "538DD4", + }, + "q3.act_payment.pay_act": { + "background": "538DD4", + }, + "q3.booking_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q3.m1_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.m2_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.m3_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.quarter_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.rem_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q3.rem_actual_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.close_q3": { + "background": "933634", + "palette": "red", + }, + "q3.close_q3.transfer_q4": { + "background": "933634", + }, + "q3.close_q3.economy": { + "background": "933634", + }, + "q3.close_q3.total": { + "background": "933634", + }, + + + + "q4.rev_eco_correct": { + "background": "933634", + "palette": "red", + }, + "q4.rev_item_correct": { + "background": "933634", + "palette": "red", + }, + "q4.rev_inc_correct": { + "background": "933634", + "palette": "red", + }, + "q4.rev_seq_correct": { + "background": "933634", + "palette": "red", + }, + "q4.rev_correct": { + "background": "933634", + "palette": "red", + }, + "q4.new_plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.cur_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.ssp_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.rf_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.reserve_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.shift": { + "background": "933634", + "palette": "red", + }, + "q4.comment_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.act_payment": { + "background": "538DD4", + "palette": "blue", + }, + "q4.act_payment.pay_date": { + "background": "538DD4", + }, + "q4.act_payment.pay_amount": { + "background": "538DD4", + }, + "q4.act_payment.pay_ho": { + "background": "538DD4", + }, + "q4.act_payment.pay_rf": { + "background": "538DD4", + }, + "q4.act_payment.pay_comment": { + "background": "538DD4", + }, + "q4.act_payment.pay_act": { + "background": "538DD4", + }, + "q4.booking_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q4.m1_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.m2_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.m3_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.quarter_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.rem_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q4.rem_actual_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.close_q4": { + "background": "933634", + "palette": "red", + }, + "q4.close_q4.economy": { + "background": "933634", + }, + "q4.close_q4.total": { + "background": "933634", + }, + + "totals.fact_year": { + "background": "C2D59A", + "palette": "green", + }, + "totals.pay_year": { + "background": "538DD4", + "palette": "blue", + } + }, + + "CAP": { + "data.section": { + "background": "f3f4f6", + "palette": "orange", + }, + "data.item_id": { + "background": "f3f4f6", + "palette": "orange", + }, + "data.num_group": { + "background": "f3f4f6", + "palette": "orange", + }, + "data.name": { + "background": "f3f4f6", + "palette": "orange", + }, + "data.internal_order": { + "background": "f3f4f6", + "palette": "orange", + }, + "upper_ssp_smeta": { + "background": "bfbfbf", + "palette": "orange", + }, + "upper_ssp_smeta.contract_summary.deadline": { + "palette": None, + }, + "allocation": { + "background": "bfbfbf", + "palette": "orange", + }, + "sequestration": { + "background": "bfbfbf", + "palette": "orange", + }, + "reserve": { + "background": "bfbfbf", + "palette": "orange", + }, + "approved": { + "background": "bfbfbf", + "palette": "orange", + }, + "empty_upper.collegial": { + "background": "fabf8f", + "palette": "orange", + }, + "empty_upper.ckk": { + "background": "fabf8f", + "palette": "orange", + }, + "empty_upper.ckk.q1": { + "background": "748c42", + }, + "empty_upper.ckk.q2": { + "background": "748c42", + }, + "empty_upper.ckk.q3": { + "background": "748c42", + }, + "empty_upper.ckk.q4": { + "background": "748c42", + }, + "empty_upper.contract_detail": { + "background": "fabf8f", + "palette": "orange", + }, + "empty_upper.contract_detail.q1": { + "background": "748c42", + }, + "empty_upper.contract_detail.q2": { + "background": "748c42", + }, + "empty_upper.contract_detail.q3": { + "background": "748c42", + }, + "empty_upper.contract_detail.q4": { + "background": "748c42", + }, + "q1.cur_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.ssp_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.rf_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.reserve_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.shift": { + "background": "933634", + "palette": "red", + }, + "q1.comment_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q1.act_payment": { + "background": "538DD4", + "palette": "blue", + }, + "q1.act_payment.pay_date": { + "background": "538DD4", + }, + "q1.act_payment.pay_amount": { + "background": "538DD4", + }, + "q1.act_payment.pay_ho": { + "background": "538DD4", + }, + "q1.act_payment.pay_rf": { + "background": "538DD4", + }, + "q1.act_payment.pay_comment": { + "background": "538DD4", + }, + "q1.act_payment.pay_act": { + "background": "538DD4", + }, + "q1.booking_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q1.m1_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.m2_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.m3_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.quarter_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.rem_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q1.rem_actual_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q1.close_q1": { + "background": "933634", + "palette": "red", + }, + "q1.close_q1.transfer_q2": { + "background": "933634", + }, + "q1.close_q1.transfer_q3": { + "background": "933634", + }, + "q1.close_q1.transfer_q4": { + "background": "933634", + }, + "q1.close_q1.economy": { + "background": "933634", + }, + "q1.close_q1.total": { + "background": "933634", + }, + + "q2.rev_eco_correct": { + "background": "933634", + "palette": "red", + }, + "q2.rev_item_correct": { + "background": "933634", + "palette": "red", + }, + "q2.rev_inc_correct": { + "background": "933634", + "palette": "red", + }, + "q2.rev_seq_correct": { + "background": "933634", + "palette": "red", + }, + "q2.rev_correct": { + "background": "933634", + "palette": "red", + }, + "q2.new_plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.cur_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.ssp_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.rf_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.reserve_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.shift": { + "background": "933634", + "palette": "red", + }, + "q2.comment_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q2.act_payment": { + "background": "538DD4", + "palette": "blue", + }, + "q2.act_payment.pay_date": { + "background": "538DD4", + }, + "q2.act_payment.pay_amount": { + "background": "538DD4", + }, + "q2.act_payment.pay_ho": { + "background": "538DD4", + }, + "q2.act_payment.pay_rf": { + "background": "538DD4", + }, + "q2.act_payment.pay_comment": { + "background": "538DD4", + }, + "q2.act_payment.pay_act": { + "background": "538DD4", + }, + "q2.booking_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q2.m1_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.m2_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.m3_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.quarter_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.rem_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q2.rem_actual_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q2.close_q2": { + "background": "933634", + "palette": "red", + }, + "q2.close_q2.transfer_q3": { + "background": "933634", + }, + "q2.close_q2.transfer_q4": { + "background": "933634", + }, + "q2.close_q2.economy": { + "background": "933634", + }, + "q2.close_q2.total": { + "background": "933634", + }, + + + "q3.rev_eco_correct": { + "background": "933634", + "palette": "red", + }, + "q3.rev_item_correct": { + "background": "933634", + "palette": "red", + }, + "q3.rev_inc_correct": { + "background": "933634", + "palette": "red", + }, + "q3.rev_seq_correct": { + "background": "933634", + "palette": "red", + }, + "q3.rev_correct": { + "background": "933634", + "palette": "red", + }, + "q3.new_plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.cur_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.ssp_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.rf_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.reserve_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.shift": { + "background": "933634", + "palette": "red", + }, + "q3.comment_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q3.act_payment": { + "background": "538DD4", + "palette": "blue", + }, + "q3.act_payment.pay_date": { + "background": "538DD4", + }, + "q3.act_payment.pay_amount": { + "background": "538DD4", + }, + "q3.act_payment.pay_ho": { + "background": "538DD4", + }, + "q3.act_payment.pay_rf": { + "background": "538DD4", + }, + "q3.act_payment.pay_comment": { + "background": "538DD4", + }, + "q3.act_payment.pay_act": { + "background": "538DD4", + }, + "q3.booking_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q3.m1_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.m2_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.m3_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.quarter_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.rem_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q3.rem_actual_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q3.close_q3": { + "background": "933634", + "palette": "red", + }, + "q3.close_q3.transfer_q4": { + "background": "933634", + }, + "q3.close_q3.economy": { + "background": "933634", + }, + "q3.close_q3.total": { + "background": "933634", + }, + + + + "q4.rev_eco_correct": { + "background": "933634", + "palette": "red", + }, + "q4.rev_item_correct": { + "background": "933634", + "palette": "red", + }, + "q4.rev_inc_correct": { + "background": "933634", + "palette": "red", + }, + "q4.rev_seq_correct": { + "background": "933634", + "palette": "red", + }, + "q4.rev_correct": { + "background": "933634", + "palette": "red", + }, + "q4.new_plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.cur_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.ssp_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.rf_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.reserve_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.shift": { + "background": "933634", + "palette": "red", + }, + "q4.comment_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.plan_correct": { + "background": "FFFF66", + "palette": "yellow", + }, + "q4.act_payment": { + "background": "538DD4", + "palette": "blue", + }, + "q4.act_payment.pay_date": { + "background": "538DD4", + }, + "q4.act_payment.pay_amount": { + "background": "538DD4", + }, + "q4.act_payment.pay_ho": { + "background": "538DD4", + }, + "q4.act_payment.pay_rf": { + "background": "538DD4", + }, + "q4.act_payment.pay_comment": { + "background": "538DD4", + }, + "q4.act_payment.pay_act": { + "background": "538DD4", + }, + "q4.booking_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q4.m1_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.m2_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.m3_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.quarter_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.rem_correct": { + "background": "fabf8f", + "palette": "orange", + }, + "q4.rem_actual_correct": { + "background": "C2D59A", + "palette": "green", + }, + "q4.close_q4": { + "background": "933634", + "palette": "red", + }, + "q4.close_q4.economy": { + "background": "933634", + }, + "q4.close_q4.total": { + "background": "933634", + }, + + "totals.fact_year": { + "background": "C2D59A", + "palette": "green", + }, + "totals.pay_year": { + "background": "538DD4", + "palette": "blue", + } + } + +} + + +SMETA_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", "План поддержка Год"), + ("plan_development_q1", "План развитие I квартал"), + ("plan_development_q2", "План развитие II квартал"), + ("plan_development_q3", "План развитие III квартал"), + ("plan_development_q4", "План развитие IV квартал"), + ("plan_development_year", "План развитие Год"), + ("plan_total_year", "План ИТОГО Год"), + ("approved_support_q1", "Утверждено поддержка I квартал"), + ("approved_support_q2", "Утверждено поддержка II квартал"), + ("approved_support_q3", "Утверждено поддержка III квартал"), + ("approved_support_q4", "Утверждено поддержка IV квартал"), + ("approved_support_year", "Утверждено поддержка Год"), + ("approved_development_q1", "Утверждено развитие I квартал"), + ("approved_development_q2", "Утверждено развитие II квартал"), + ("approved_development_q3", "Утверждено развитие III квартал"), + ("approved_development_q4", "Утверждено развитие IV квартал"), + ("approved_development_year", "Утверждено развитие Год"), + ("approved_total_year", "Утверждено ИТОГО Год"), + ("fact_support_q1", "Факт поддержка I квартал"), + ("fact_support_q2", "Факт поддержка II квартал"), + ("fact_support_q3", "Факт поддержка III квартал"), + ("fact_support_q4", "Факт поддержка IV квартал"), + ("fact_support_year", "Факт поддержка Год"), + ("fact_development_q1", "Факт развитие I квартал"), + ("fact_development_q2", "Факт развитие II квартал"), + ("fact_development_q3", "Факт развитие III квартал"), + ("fact_development_q4", "Факт развитие IV квартал"), + ("fact_development_year", "Факт развитие Год"), + ("fact_total_year", "Факт ИТОГО Год"), + ("corrected_support_q2", "Корректировка поддержка II квартал"), + ("corrected_support_q3", "Корректировка поддержка III квартал"), + ("corrected_support_q4", "Корректировка поддержка IV квартал"), + ("corrected_development_q2", "Корректировка развитие II квартал"), + ("corrected_development_q3", "Корректировка развитие III квартал"), + ("corrected_development_q4", "Корректировка развитие IV квартал"), +] +FORM3_REPORT_COLUMNS: list[tuple[str, str]] = [ + ("section", "Код раздела"), + ("item_id", "ID статьи"), + ("name", "Наименование"), + ("q1", "I квартал"), + ("q2", "II квартал"), + ("q3", "III квартал"), + ("q4", "IV квартал"), + ("totals", "ИТОГО"), +] diff --git a/api/src/services/export_normalizers.py b/api/src/services/export_service/export_normalizers.py similarity index 85% rename from api/src/services/export_normalizers.py rename to api/src/services/export_service/export_normalizers.py index c8fe28d..ea6412c 100644 --- a/api/src/services/export_normalizers.py +++ b/api/src/services/export_service/export_normalizers.py @@ -194,6 +194,41 @@ class Form1RowNormalizer: return None +class Form1DepthRowNormalizer: + def __init__(self, serializer: ExcelValueSerializer): + self.serializer = serializer + + def normalize(self, data: dict[str, Any]) -> dict[str, Any]: + normalized = dict() + self._merge_header_fields_into_row(normalized=normalized, original=data) + self._normilize_depth(normalized=normalized, original=data) + return normalized + + def _merge_header_fields_into_row(self, normalized: dict[str, Any], original: dict[str, Any]) -> None: + header = original.get("header") + if isinstance(header, dict): + if "section" in header and "section" not in normalized: + normalized["section"] = header.get("section") + if "item_id" in header and "item_id" not in normalized: + normalized["item_id"] = header.get("item_id") + if "name" in header and "name" not in normalized: + normalized["name"] = header.get("name") + if "num_group" in header and "num_group" not in normalized: + normalized["num_group"] = header.get("num_group") + if "internal_order" in header and "internal_order" not in normalized: + normalized["internal_order"] = header.get("internal_order") + + normalized.pop("header", None) + normalized.pop("line_id", None) + + def _normilize_depth(self, normalized: dict, original: dict, prefix: str = "") -> None: + for key, value in original.items(): + if isinstance(value, dict): + self._normilize_depth(normalized=normalized, original=value, prefix=f"{prefix}{key}.") + else: + normalized[f"{prefix}{key}"] = value + + class SmetaRowNormalizer: def normalize(self, data: dict[str, Any]) -> dict[str, Any]: return { diff --git a/api/src/services/export_service.py b/api/src/services/export_service/export_service.py similarity index 92% rename from api/src/services/export_service.py rename to api/src/services/export_service/export_service.py index 9c8629f..8593a00 100644 --- a/api/src/services/export_service.py +++ b/api/src/services/export_service/export_service.py @@ -15,16 +15,20 @@ from sqlalchemy.ext.asyncio import AsyncSession from src.db.models.app_user import AppUser from src.db.models.form_type import FormTypeEnum from src.services.budget_form_service import BudgetFormService -from src.services.export_normalizers import ( +from src.services.export_service.export_normalizers import ( ExcelValueSerializer, - Form1RowNormalizer, + Form1DepthRowNormalizer, Form3ReportRowNormalizer, SmetaRowNormalizer, ) -from src.services.export_writers import ( - FORM1_FLAT_COLUMNS, +from src.services.export_service.export_mappings import ( + FORM1_COLORS, + FORM1_DEPTH_COLUMNS, FORM3_REPORT_COLUMNS, SMETA_COLUMNS, +) +from src.services.export_service.export_writers import ( + DepthSheetWriter, TabularSheetWriter, ) from src.services.project_service import ProjectService @@ -51,12 +55,12 @@ class ExportService: self.sheet_service = SheetService(db) self.project_service = ProjectService(db) self.value_serializer = ExcelValueSerializer() - self.form1_row_normalizer = Form1RowNormalizer(self.value_serializer) + self.form1_row_normalizer = Form1DepthRowNormalizer(self.value_serializer) self.form3_report_row_normalizer = Form3ReportRowNormalizer() self.smeta_row_normalizer = SmetaRowNormalizer() self.sheet_writers: dict[str, TabularSheetWriter] = { - "__default__": TabularSheetWriter( - columns=FORM1_FLAT_COLUMNS, + "__default__": DepthSheetWriter( + columns_dict=FORM1_DEPTH_COLUMNS, row_normalizer=self.form1_row_normalizer.normalize, value_serializer=self.value_serializer.to_excel_value, ), @@ -65,6 +69,24 @@ class ExportService: row_normalizer=self.smeta_row_normalizer.normalize, value_serializer=self.value_serializer.to_excel_value, ), + "OPER": DepthSheetWriter( + columns_dict=FORM1_DEPTH_COLUMNS, + row_normalizer=self.form1_row_normalizer.normalize, + value_serializer=self.value_serializer.to_excel_value, + colors_config=FORM1_COLORS["OPER"], + ), + "AHR": DepthSheetWriter( + columns_dict=FORM1_DEPTH_COLUMNS, + row_normalizer=self.form1_row_normalizer.normalize, + value_serializer=self.value_serializer.to_excel_value, + colors_config=FORM1_COLORS["AHR"], + ), + "CAP": DepthSheetWriter( + columns_dict=FORM1_DEPTH_COLUMNS, + row_normalizer=self.form1_row_normalizer.normalize, + value_serializer=self.value_serializer.to_excel_value, + colors_config=FORM1_COLORS["CAP"], + ), } self.form3_report_writer = TabularSheetWriter( columns=FORM3_REPORT_COLUMNS, diff --git a/api/src/services/export_service/export_writers.py b/api/src/services/export_service/export_writers.py new file mode 100644 index 0000000..5ec69b5 --- /dev/null +++ b/api/src/services/export_service/export_writers.py @@ -0,0 +1,209 @@ +from __future__ import annotations + +from collections import defaultdict +from typing import Any, Callable + +from openpyxl.styles import Alignment, Border, Color, Font, PatternFill, Side +from openpyxl.worksheet.worksheet import Worksheet +from openpyxl.utils import get_column_letter + +from src.services.export_service.export_mappings import PALETTE + + + +RowNormalizer = Callable[[dict[str, Any]], dict[str, Any]] +ValueSerializer = Callable[[Any], Any] + + +class TabularSheetWriter: + def __init__( + self, + columns: list[tuple[str, str]], + row_normalizer: RowNormalizer, + value_serializer: ValueSerializer, + ): + self.columns = columns + self.row_normalizer = row_normalizer + self.value_serializer = value_serializer + + def write(self, worksheet: Worksheet, rows: list[tuple]) -> None: + worksheet.append([title for _, title in self.columns]) + for row in rows: + raw_data = row[3] if len(row) > 3 and isinstance(row[3], dict) else {} + normalized = self.row_normalizer(raw_data) + worksheet.append( + [self.value_serializer(normalized.get(key)) for key, _ in self.columns] + ) + + +class DepthSheetWriter: + alignment = Alignment(horizontal='center', vertical='center') + thin_border = Border( + left=Side(style='thin'), + right=Side(style='thin'), + top=Side(style='thin'), + bottom=Side(style='thin') + ) + + def __init__( + self, + columns_dict: dict, + row_normalizer: RowNormalizer, + value_serializer: ValueSerializer, + colors_config: dict | None = None, + ): + self.columns_dict = columns_dict + self._flat_columns_to_depth = {} + self.columns = self._columns_dict_to_flat(columns_dict) + self.row_normalizer = row_normalizer + self.value_serializer = value_serializer + self.colors_config = colors_config + self.palette = None + if colors_config: + self._set_palette() + + def _columns_dict_to_flat( + self, + columns_dict: dict | None = None, + prefix: str = "", + depth_prefix: str = "", + ) -> None: + if columns_dict is None: + columns_dict = self.columns_dict + result = [] + for key, sub_dict in columns_dict.items(): + if "key" in sub_dict: + new_key = sub_dict['key'] + else: + new_key = key + if "children" not in sub_dict: + if new_key: + add_key = f"{prefix}{new_key}" + result.append(add_key) + self._flat_columns_to_depth[add_key] = f"{depth_prefix}{key}" + else: + result += self._columns_dict_to_flat( + sub_dict["children"], + prefix=f"{prefix}{new_key}." if new_key else prefix, + depth_prefix=f"{depth_prefix}{key}.", + ) + return result + + def _set_palette(self) -> None: + self.palette = {} + for key in self.columns: + full_key = None + for sub_key in self._flat_columns_to_depth[key].split("."): + if full_key is None: + full_key = sub_key + else: + full_key = f"{full_key}.{sub_key}" + if full_key in self.colors_config and "palette" in self.colors_config[full_key]: + self.palette[key] = self.colors_config[full_key]["palette"] + + def _get_header_max_row(self, column_dict: dict) -> int: + max_depth = 0 + for sub_dict in column_dict.values(): + if "children" in sub_dict: + max_depth = max(max_depth, self._get_header_max_row(sub_dict["children"])) + return max_depth + 1 + + def _write_header( + self, + worksheet: Worksheet, + columns_dict: dict | None = None, + current_column: int = 1, + current_row: int = 1, + max_row: int | None = None, + full_key: str | None = None + ) -> int: + if columns_dict is None: + columns_dict = self.columns_dict + max_row = self._get_header_max_row(self.columns_dict) + for key, sub_dict in columns_dict.items(): + if full_key: + key = f"{full_key}.{key}" + + if "children" not in sub_dict: + cell = worksheet.cell(row=current_row, column=current_column, value=sub_dict["name"]) + cell.border = self.thin_border + if max_row != current_row: + worksheet.merge_cells( + start_row=current_row, + start_column=current_column, + end_row=max_row, + end_column=current_column, + ) + current_column += 1 + else: + new_column = self._write_header( + worksheet=worksheet, + columns_dict=sub_dict["children"], + current_column=current_column, + current_row=current_row+1, + max_row=max_row, + full_key=key, + ) + cell = worksheet.cell(row=current_row, column=current_column, value=sub_dict["name"]) + if new_column - current_column > 1: + worksheet.merge_cells( + start_row=current_row, + start_column=current_column, + end_row=current_row, + end_column=new_column - 1, + ) + cell.border = self.thin_border + current_column = new_column + cell.alignment = self.alignment + + if self.colors_config and (color := self.colors_config.get(key)): + if "background" in color: + cell.fill = PatternFill( + start_color=Color(rgb=color["background"]), + fill_type="solid", + ) + if "color" in color: + cell.font = Font(color=color["color"]) + return current_column + + def _set_widths(self, worksheet: Worksheet): + for col in worksheet.columns: + max_len = 0 + col_letter = get_column_letter(col[0].column) # Get alphabetical column name (e.g., 'A') + + for cell in col: + if cell.value is not None: + cell_len = len(str(cell.value)) + if cell_len > max_len: + max_len = cell_len + + adjusted_width = max(max_len + 3, 10) + worksheet.column_dimensions[col_letter].width = adjusted_width + + def _set_color_to_row(self, ws: Worksheet, item_type: str) -> None: + if not self.palette: + return + + row = ws.max_row + + for i, key in enumerate(self.columns): + if key not in self.palette: + continue + cell = ws.cell(column=i+1, row=row) + if (palette := self.palette.get(key)): + cell.fill = PatternFill( + start_color=Color(rgb=PALETTE[palette][item_type]), + fill_type="solid", + ) + + def write(self, worksheet: Worksheet, rows: list[tuple]) -> None: + self._write_header(worksheet) + for row in rows: + raw_data = row[3] if len(row) > 3 and isinstance(row[3], dict) else {} + normalized = self.row_normalizer(raw_data) + worksheet.append( + [self.value_serializer(normalized.get(key)) for key in self.columns] + ) + if len(row) > 3: + self._set_color_to_row(ws=worksheet, item_type=row[0]) + self._set_widths(worksheet) diff --git a/api/src/services/export_writers.py b/api/src/services/export_writers.py deleted file mode 100644 index fb70fdf..0000000 --- a/api/src/services/export_writers.py +++ /dev/null @@ -1,103 +0,0 @@ -from __future__ import annotations - -from typing import Any, Callable - -from openpyxl.worksheet.worksheet import Worksheet - -FORM1_FLAT_COLUMNS: list[tuple[str, str]] = [ - ("section", "Код раздела"), - ("item_id", "ID статьи"), - ("name", "Наименование"), - ("q1", "I квартал"), - ("q2", "II квартал"), - ("q3", "III квартал"), - ("q4", "IV квартал"), - ("totals", "ИТОГО"), - ("plan", "План"), - ("reserve", "Резерв"), - ("approved", "Согласовано"), - ("collegial", "Коллегиальный"), - ("allocation", "Аллокация"), - ("sequestration", "Секвестр"), - ("contract", "Договор"), - ("contract_sum", "Сумма договора"), - ("ckk", "ЦКК"), -] - -SMETA_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", "План поддержка Год"), - ("plan_development_q1", "План развитие I квартал"), - ("plan_development_q2", "План развитие II квартал"), - ("plan_development_q3", "План развитие III квартал"), - ("plan_development_q4", "План развитие IV квартал"), - ("plan_development_year", "План развитие Год"), - ("plan_total_year", "План ИТОГО Год"), - ("approved_support_q1", "Утверждено поддержка I квартал"), - ("approved_support_q2", "Утверждено поддержка II квартал"), - ("approved_support_q3", "Утверждено поддержка III квартал"), - ("approved_support_q4", "Утверждено поддержка IV квартал"), - ("approved_support_year", "Утверждено поддержка Год"), - ("approved_development_q1", "Утверждено развитие I квартал"), - ("approved_development_q2", "Утверждено развитие II квартал"), - ("approved_development_q3", "Утверждено развитие III квартал"), - ("approved_development_q4", "Утверждено развитие IV квартал"), - ("approved_development_year", "Утверждено развитие Год"), - ("approved_total_year", "Утверждено ИТОГО Год"), - ("fact_support_q1", "Факт поддержка I квартал"), - ("fact_support_q2", "Факт поддержка II квартал"), - ("fact_support_q3", "Факт поддержка III квартал"), - ("fact_support_q4", "Факт поддержка IV квартал"), - ("fact_support_year", "Факт поддержка Год"), - ("fact_development_q1", "Факт развитие I квартал"), - ("fact_development_q2", "Факт развитие II квартал"), - ("fact_development_q3", "Факт развитие III квартал"), - ("fact_development_q4", "Факт развитие IV квартал"), - ("fact_development_year", "Факт развитие Год"), - ("fact_total_year", "Факт ИТОГО Год"), - ("corrected_support_q2", "Корректировка поддержка II квартал"), - ("corrected_support_q3", "Корректировка поддержка III квартал"), - ("corrected_support_q4", "Корректировка поддержка IV квартал"), - ("corrected_development_q2", "Корректировка развитие II квартал"), - ("corrected_development_q3", "Корректировка развитие III квартал"), - ("corrected_development_q4", "Корректировка развитие IV квартал"), -] -FORM3_REPORT_COLUMNS: list[tuple[str, str]] = [ - ("section", "Код раздела"), - ("item_id", "ID статьи"), - ("name", "Наименование"), - ("q1", "I квартал"), - ("q2", "II квартал"), - ("q3", "III квартал"), - ("q4", "IV квартал"), - ("totals", "ИТОГО"), -] - -RowNormalizer = Callable[[dict[str, Any]], dict[str, Any]] -ValueSerializer = Callable[[Any], Any] - - -class TabularSheetWriter: - def __init__( - self, - columns: list[tuple[str, str]], - row_normalizer: RowNormalizer, - value_serializer: ValueSerializer, - ): - self.columns = columns - self.row_normalizer = row_normalizer - self.value_serializer = value_serializer - - def write(self, worksheet: Worksheet, rows: list[tuple]) -> None: - worksheet.append([title for _, title in self.columns]) - for row in rows: - raw_data = row[3] if len(row) > 3 and isinstance(row[3], dict) else {} - normalized = self.row_normalizer(raw_data) - worksheet.append( - [self.value_serializer(normalized.get(key)) for key, _ in self.columns] - ) diff --git a/api/tests/unit/test_export_normalizers.py b/api/tests/unit/test_export_normalizers.py index 933aaf6..0463844 100644 --- a/api/tests/unit/test_export_normalizers.py +++ b/api/tests/unit/test_export_normalizers.py @@ -1,4 +1,4 @@ -from src.services.export_normalizers import ( +from src.services.export_service.export_normalizers import ( ExcelValueSerializer, Form1RowNormalizer, Form3ReportRowNormalizer, diff --git a/api/tests/unit/test_export_writers.py b/api/tests/unit/test_export_writers.py index f16bd81..eb8e388 100644 --- a/api/tests/unit/test_export_writers.py +++ b/api/tests/unit/test_export_writers.py @@ -3,11 +3,11 @@ from unittest.mock import AsyncMock from openpyxl import Workbook from src.services.export_service import ExportService -from src.services.export_writers import ( +from src.services.export_service.export_mappings import ( FORM1_FLAT_COLUMNS, SMETA_COLUMNS, - TabularSheetWriter, ) +from src.services.export_service.export_writers import TabularSheetWriter def test_tabular_sheet_writer_writes_headers_and_rows():