From 24062ed729e83bd1f4be14897f325aed1da75b1f Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Thu, 12 Nov 2026 16:04:12 +0400 Subject: [PATCH 1/4] =?UTF-8?q?fix(form2):=20=D1=84=D0=BE=D0=BD=D0=B4=20?= =?UTF-8?q?=D1=8D=D0=BA=D0=BE=D0=BD=D0=BE=D0=BC=D0=B8=D0=B8=20(transfer=5F?= =?UTF-8?q?econ)=20=D1=81=D1=87=D0=B8=D1=82=D0=B0=D0=B5=D1=82=D1=81=D1=8F?= =?UTF-8?q?=20=D0=BD=D0=B0=20FORM=5F2=20=C2=AB=D0=A1=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D0=B0=20=D0=A0=D0=A4=C2=BB,=20=D0=BE=D1=82=D0=BA=D0=B0=D1=82?= =?UTF-8?q?=20=D0=BD=D0=B0=20FORM=5F4=20(AURORA-1938,=20#335)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Миграция 0063: v_form2_sheet_sections считает q*.transfer_econ по шаблону Excel ((cp-aq) минус подколонки переноса минус переносы через квартал), total = остаток после факта, групповые строки тем же; _apply_form_cell запрещает запись q*.transfer_econ для FORM_2; v_form4_sheet_sections возвращена к версии 0033. Co-Authored-By: Claude Opus 5.5 --- .../versions/0063_form2_transfer_econ_calc.py | 15 + .../sql/0063_form2_transfer_econ_calc.sql | 1969 +++++++++++++++++ .../integration/test_form2_transfer_econ.py | 175 ++ .../integration/test_form4_transfer_econ.py | 206 +- 4 files changed, 2195 insertions(+), 170 deletions(-) create mode 100644 api/alembic/versions/0063_form2_transfer_econ_calc.py create mode 100644 api/alembic/versions/sql/0063_form2_transfer_econ_calc.sql create mode 100644 api/tests/integration/test_form2_transfer_econ.py diff --git a/api/alembic/versions/0063_form2_transfer_econ_calc.py b/api/alembic/versions/0063_form2_transfer_econ_calc.py new file mode 100644 index 0000000..de24bfb --- /dev/null +++ b/api/alembic/versions/0063_form2_transfer_econ_calc.py @@ -0,0 +1,15 @@ +from sql_migration import run_sql_file_migration + + +revision = "0063" +down_revision = "0059" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + run_sql_file_migration("0063_form2_transfer_econ_calc.sql") + + +def downgrade() -> None: + pass diff --git a/api/alembic/versions/sql/0063_form2_transfer_econ_calc.sql b/api/alembic/versions/sql/0063_form2_transfer_econ_calc.sql new file mode 100644 index 0000000..f6c27aa --- /dev/null +++ b/api/alembic/versions/sql/0063_form2_transfer_econ_calc.sql @@ -0,0 +1,1969 @@ +-- AURORA-1938 / Gitea #335: «Смета РФ» в UI — это FORM_2 (web/src/constants/constants.jsx), +-- а FORM_4 — «Проектная деятельность». Миграция 0055 (PR #262) сделала расчёт фонда экономии +-- не на той форме. Здесь: +-- +-- 1) v3.v_form2_sheet_sections (тело — последняя версия, 0052): листы АХР/КВ/Операц, шаблон Excel. +-- cp_n — скорректированный план, aq_n — факт; tda/tdp/terf — подколонки переноса +-- «задержка актов / задержка закупок / экономия РФ» (вычитаем именно их сумму, а не сырое +-- transfer_to_qN — так в шаблоне и так уже считает «Всего»): +-- transfer_econ_Q1 = (cp1-aq1) - (tda1+tdp1+terf1) - q1.transfer_q3 - q1.transfer_q4 +-- transfer_econ_Q2 = (cp2-aq2) - (tda2+tdp2+terf2) - q2.transfer_q4 +-- transfer_econ_Q3 = (cp3-aq3) - (tda3+tdp3+terf3) +-- transfer_econ_Q4 = cp4-aq4 +-- «Всего» (q*.total) = подколонки + переносы через квартал + рассчитанный фонд = cp_n - aq_n. +-- Групповые строки считают то же формулой от своих агрегатов (сумма по строкам линейна). +-- Выгрузка Excel и /sheet читают это же через v_form2_view/v_form2_sheet_jsonb. +-- +-- 2) v3._apply_form_cell (тело — 0055): запрет записи q*.transfer_econ теперь для FORM_2. +-- +-- 3) v3.v_form4_sheet_sections: откат дельты 0055 — тело = версия 0033 (последняя до 0055), +-- transfer_econ снова raw (q*_te / tb*.te), «Всего» по прежней формуле. +-- +-- Ловушка _split_statements: только именованные dollar-теги ($function$), без DO $$. + +CREATE OR REPLACE FUNCTION v3.v_form2_sheet_sections(p_form_id integer, p_sheet character varying, p_sections text[] DEFAULT NULL::text[], p_focus_lines integer[] DEFAULT NULL::integer[]) + RETURNS TABLE(row_type character varying, depth integer, line_id integer, col_section_code character varying, col_item_id character varying, col_num_group_id character varying, col_name character varying, col_vsp_id integer, col_vsp_address character varying, col_plan_q1 numeric, col_plan_q2 numeric, col_plan_q3 numeric, col_plan_q4 numeric, col_plan_year numeric, col_plan_comment character varying, col_seq_dfip_q1 numeric, col_seq_dfip_q2 numeric, col_seq_dfip_q3 numeric, col_seq_dfip_q4 numeric, col_seq_dfip_year numeric, col_seq_dfip_just character varying, col_seq_ssp_q1 numeric, col_seq_ssp_q2 numeric, col_seq_ssp_q3 numeric, col_seq_ssp_q4 numeric, col_seq_ssp_year numeric, col_seq_ssp_just character varying, col_appr_q1 numeric, col_appr_q2 numeric, col_appr_q3 numeric, col_appr_q4 numeric, col_appr_year numeric, col_cd_counterparty character varying, col_cd_reference character varying, col_cd_date date, col_cd_subject character varying, col_cd_currency character varying, col_cd_ceiling numeric, col_cd_vat_rate character varying, col_cd_deadline character varying, col_cd_scheme character varying, col_cd_act character varying, col_cd_comment character varying, col_book_q1 numeric, col_book_q2 numeric, col_book_q3 numeric, col_book_q4 numeric, col_book_next_q1 numeric, col_book_next_q2 numeric, col_book_next_q3 numeric, col_book_next_q4 numeric, col_q1_adj_current numeric, col_q1_adj_ssp numeric, col_q1_adj_reserve numeric, col_q1_adj_comment character varying, col_q1_corrected_plan numeric, col_q1_pay_date date, col_q1_pay_amount numeric, col_q1_pay_comment character varying, col_q1_pay_act character varying, col_q1_booking numeric, col_q1_actual_m1 numeric, col_q1_actual_m2 numeric, col_q1_actual_m3 numeric, col_q1_actual_quarter numeric, col_q1_residual_after_booking numeric, col_q1_residual_after_actual numeric, col_q1_transfer_q2 numeric, col_q1_transfer_q2_delay_acts numeric, col_q1_transfer_q2_delay_procurement numeric, col_q1_transfer_q2_economy_rf numeric, col_q1_transfer_next_comment character varying, col_q1_transfer_q3 numeric, col_q1_transfer_q4 numeric, col_q1_transfer_far_comment character varying, col_q1_transfer_econ numeric, col_q1_total numeric, col_q2_target_change numeric, col_q2_base_correction numeric, col_q2_base_correction_comment character varying, col_q2_revision_inc numeric, col_q2_revision_seq numeric, col_q2_revision_comment character varying, col_q2_new_plan numeric, col_q2_adj_current numeric, col_q2_adj_ssp numeric, col_q2_adj_reserve numeric, col_q2_adj_comment character varying, col_q2_corrected_plan numeric, col_q2_pay_date date, col_q2_pay_amount numeric, col_q2_pay_comment character varying, col_q2_pay_act character varying, col_q2_booking numeric, col_q2_actual_m1 numeric, col_q2_actual_m2 numeric, col_q2_actual_m3 numeric, col_q2_actual_quarter numeric, col_q2_residual_after_booking numeric, col_q2_residual_after_actual numeric, col_q2_transfer_q3 numeric, col_q2_transfer_q3_delay_acts numeric, col_q2_transfer_q3_delay_procurement numeric, col_q2_transfer_q3_economy_rf numeric, col_q2_transfer_next_comment character varying, col_q2_transfer_q4 numeric, col_q2_transfer_far_comment character varying, col_q2_transfer_econ numeric, col_q2_total numeric, col_q3_target_change numeric, col_q3_base_correction numeric, col_q3_base_correction_comment character varying, col_q3_revision_inc numeric, col_q3_revision_seq numeric, col_q3_revision_comment character varying, col_q3_new_plan numeric, col_q3_adj_current numeric, col_q3_adj_ssp numeric, col_q3_adj_reserve numeric, col_q3_adj_comment character varying, col_q3_corrected_plan numeric, col_q3_pay_date date, col_q3_pay_amount numeric, col_q3_pay_comment character varying, col_q3_pay_act character varying, col_q3_booking numeric, col_q3_actual_m1 numeric, col_q3_actual_m2 numeric, col_q3_actual_m3 numeric, col_q3_actual_quarter numeric, col_q3_residual_after_booking numeric, col_q3_residual_after_actual numeric, col_q3_transfer_q4 numeric, col_q3_transfer_q4_delay_acts numeric, col_q3_transfer_q4_delay_procurement numeric, col_q3_transfer_q4_economy_rf numeric, col_q3_transfer_next_comment character varying, col_q3_transfer_econ numeric, col_q3_total numeric, col_q4_target_change numeric, col_q4_base_correction numeric, col_q4_base_correction_comment character varying, col_q4_revision_inc numeric, col_q4_revision_seq numeric, col_q4_revision_comment character varying, col_q4_new_plan numeric, col_q4_adj_current numeric, col_q4_adj_ssp numeric, col_q4_adj_reserve numeric, col_q4_adj_comment character varying, col_q4_corrected_plan numeric, col_q4_pay_date date, col_q4_pay_amount numeric, col_q4_pay_comment character varying, col_q4_pay_act character varying, col_q4_booking numeric, col_q4_actual_m1 numeric, col_q4_actual_m2 numeric, col_q4_actual_m3 numeric, col_q4_actual_spod numeric, col_q4_actual_quarter numeric, col_q4_residual_after_booking numeric, col_q4_residual_after_actual numeric, col_q4_transfer_econ numeric, col_q4_total numeric, col_fact_year numeric, col_pay_year numeric, col_economy_year numeric, col_internal_order character varying, col_cs_total numeric, col_cs_counterparty character varying, col_cs_deadline character varying, col_cs_comment character varying, col_cs_future_y1 numeric, col_cs_future_y2 numeric, col_cs_other_ssp numeric, col_al_contract_ref character varying, col_al_purpose character varying, col_al_order character varying, col_al_property character varying, col_ca_approved numeric, col_ca_protocol character varying, col_ca_note character varying, col_ckk_ceiling numeric, col_ckk_rf_sched character varying, col_ckk_deadline character varying, col_ckk_proc_plan character varying, col_ckk_proc_meth character varying, col_ckk_comment character varying, col_cd_addenda character varying, col_cd_exp_q1 numeric, col_cd_exp_q2 numeric, col_cd_exp_q3 numeric, col_cd_exp_q4 numeric, col_cd_rf_sched character varying, col_cd_exchange_rate numeric, col_cd_amount_foreign numeric, col_q1_adj_rf numeric, col_q1_pay_ho numeric, col_q1_pay_rf numeric, col_q2_adj_rf numeric, col_q2_pay_ho numeric, col_q2_pay_rf numeric, col_q3_adj_rf numeric, col_q3_pay_ho numeric, col_q3_pay_rf numeric, col_q4_adj_rf numeric, col_q4_pay_ho numeric, col_q4_pay_rf numeric, _sort_path integer[], _sort_rank bigint) + LANGUAGE plpgsql + STABLE + SET jit TO 'off' +AS $function$ +#variable_conflict use_column +DECLARE + s_plan BOOL; s_seq_d BOOL; s_seq_s BOOL; s_appr BOOL; + s_cd BOOL; s_book BOOL; + s_cs BOOL; s_al BOOL; s_ca BOOL; s_ckk BOOL; -- НОВЫЕ блоки + s_q1 BOOL; s_q2 BOOL; s_q3 BOOL; s_q4 BOOL; s_tot BOOL; + s_need_ap BOOL; -- plan/seq/reserve нужны для approved + cp1..cp4 + v_focus_nodes INT[]; -- AURORA-1850: узлы фокуса (путь статей строк фокуса ∩ tw); NULL = весь лист + v_focus_eids INT[]; -- AURORA-1850: объединение desc_ids узлов фокуса +BEGIN + s_plan := p_sections IS NULL OR 'plan' = ANY(p_sections); + s_seq_d := p_sections IS NULL OR 'seq_dfip' = ANY(p_sections); + s_seq_s := p_sections IS NULL OR 'seq_ssp' = ANY(p_sections); + s_appr := p_sections IS NULL OR 'approved' = ANY(p_sections); + s_cd := p_sections IS NULL OR 'contract' = ANY(p_sections); + s_book := p_sections IS NULL OR 'booking' = ANY(p_sections); + s_cs := p_sections IS NULL OR 'contract_summary' = ANY(p_sections); + s_al := p_sections IS NULL OR 'allocation' = ANY(p_sections); + s_ca := p_sections IS NULL OR 'collegial' = ANY(p_sections); + s_ckk := p_sections IS NULL OR 'ckk' = ANY(p_sections); + s_q1 := p_sections IS NULL OR 'q1' = ANY(p_sections); + s_q2 := p_sections IS NULL OR 'q2' = ANY(p_sections); + s_q3 := p_sections IS NULL OR 'q3' = ANY(p_sections); + s_q4 := p_sections IS NULL OR 'q4' = ANY(p_sections); + s_tot := p_sections IS NULL OR 'totals' = ANY(p_sections); + s_need_ap := s_plan OR s_appr OR s_q1 OR s_q2 OR s_q3 OR s_q4; + + -- Фокус (D1): NULL = весь лист, '{}' = пустой результат. COALESCE обязателен: + -- пустой фокус не должен превратиться в NULL и провалиться в полную сборку. + IF p_focus_lines IS NOT NULL THEN + WITH fp AS ( + SELECT v3._expense_item_path_set( + (SELECT array_agg(DISTINCT bl.expense_item_id) + FROM v3.budget_line bl + WHERE bl.id = ANY(p_focus_lines))) AS ids + ), + fn AS ( + SELECT t.id, t.desc_ids + FROM v3.mv_expense_item_tree t + JOIN v3.expense_item_form_type eft ON eft.expense_item_id = t.id + JOIN v3.budget_form bf ON bf.id = p_form_id AND bf.form_type_code = eft.form_type_code + WHERE t.sheet = p_sheet + AND t.id = ANY(COALESCE((SELECT fp.ids FROM fp), '{}'::int[])) + ) + SELECT COALESCE((SELECT array_agg(fn.id) FROM fn), '{}'::int[]), + COALESCE((SELECT array_agg(DISTINCT d) FROM fn, unnest(fn.desc_ids) AS d), '{}'::int[]) + INTO v_focus_nodes, v_focus_eids; + END IF; + + RETURN QUERY + WITH + tw AS ( + SELECT t.id, t.section_code, t.item_id, t.num_group_id, t.name, + t.depth, t.path, t.parent_id, t.parent_item_id, t.desc_ids, + p.parent_item_id AS grandparent_item_id + FROM v3.mv_expense_item_tree t + LEFT JOIN v3.mv_expense_item_tree p ON p.id = t.parent_id + JOIN v3.expense_item_form_type eft ON eft.expense_item_id = t.id + JOIN v3.budget_form bf ON bf.id = p_form_id AND bf.form_type_code = eft.form_type_code + WHERE t.sheet = p_sheet + ), + page AS ( + SELECT bl.id AS lid, bl.expense_item_id AS eid, bl.name AS bname, bl.vsp_id, + bl.internal_order AS io + FROM v3.budget_line bl + JOIN v3.expense_item ei ON ei.id = bl.expense_item_id + WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet + ), + tw_f AS ( + SELECT * FROM tw WHERE p_focus_lines IS NULL OR tw.id = ANY(v_focus_nodes) + ), + page_f AS ( + SELECT * FROM page WHERE p_focus_lines IS NULL OR page.lid = ANY(p_focus_lines) + ), + -- Скелет листа (D2): ключи всех узлов tw и всех строк page JOIN tw, без jsonb и сумм. + -- Ранг одинаков в обоих режимах; tie-break D6: узел раньше строки. + sk AS ( + SELECT n.k, + ROW_NUMBER() OVER (ORDER BY n._sort, n.lid NULLS FIRST) AS rnk + FROM ( + SELECT -t0.id::BIGINT AS k, NULL::INT AS lid, t0.path AS _sort FROM tw t0 + UNION ALL + SELECT pg0.lid::BIGINT, pg0.lid::INT, t1.path || ARRAY[pg0.lid::INT] + FROM page pg0 JOIN tw t1 ON t1.id = pg0.eid + ) n + ), + jp AS (SELECT p.* FROM v3.plan p JOIN page_f pg ON p.line_id = pg.lid WHERE s_need_ap), + jsd AS (SELECT s.* FROM v3.sequestration s JOIN page_f pg ON s.line_id = pg.lid WHERE s_need_ap AND s.actor='DFIP'), + jsg AS (SELECT s.* FROM v3.sequestration s JOIN page_f pg ON s.line_id = pg.lid WHERE s_need_ap AND s.actor='SSP_GO'), + jr AS (SELECT r.* FROM v3.reserve r JOIN page_f pg ON r.line_id = pg.lid WHERE s_need_ap), + jcd AS (SELECT c.* FROM v3.contract_detail c JOIN page_f pg ON c.line_id = pg.lid WHERE s_cd), + jck AS (SELECT c.* FROM v3.ckk c JOIN page_f pg ON c.line_id = pg.lid WHERE s_book OR s_ckk), + jcs AS (SELECT c.* FROM v3.contract_summary c JOIN page_f pg ON c.line_id = pg.lid WHERE s_cs), + jal AS (SELECT a.* FROM v3.allocation a JOIN page_f pg ON a.line_id = pg.lid WHERE s_al), + jca AS (SELECT c.* FROM v3.collegial_approval c JOIN page_f pg ON c.line_id = pg.lid WHERE s_ca), + jq1 AS (SELECT q.* FROM v3.budget_line_quarter q JOIN page_f pg ON q.line_id = pg.lid WHERE s_q1 AND q.quarter=1), + jq2 AS (SELECT q.* FROM v3.budget_line_quarter q JOIN page_f pg ON q.line_id = pg.lid WHERE s_q2 AND q.quarter=2), + jq3 AS (SELECT q.* FROM v3.budget_line_quarter q JOIN page_f pg ON q.line_id = pg.lid WHERE s_q3 AND q.quarter=3), + jq4 AS (SELECT q.* FROM v3.budget_line_quarter q JOIN page_f pg ON q.line_id = pg.lid WHERE (s_q4 OR s_tot) AND q.quarter=4), + + input_base AS ( + SELECT + pg.lid, pg.eid, pg.bname, pg.vsp_id, + v.address AS vsp_addr, + CASE + WHEN t.depth < 3 THEN t.item_id + WHEN t.depth = 3 THEN t.parent_item_id + WHEN t.depth = 4 THEN t.grandparent_item_id + END AS sc, + t.item_id AS ic, t.num_group_id AS ng, t.name AS ename, t.path AS tree_path, + COALESCE(p.plan_q1,0) AS pq1, COALESCE(p.plan_q2,0) AS pq2, + COALESCE(p.plan_q3,0) AS pq3, COALESCE(p.plan_q4,0) AS pq4, + p.comment AS pcmt, + COALESCE(sd.adj_q1,0) AS dq1, COALESCE(sd.adj_q2,0) AS dq2, + COALESCE(sd.adj_q3,0) AS dq3, COALESCE(sd.adj_q4,0) AS dq4, + sd.justification AS djust, + COALESCE(sg.adj_q1,0) AS gq1, COALESCE(sg.adj_q2,0) AS gq2, + COALESCE(sg.adj_q3,0) AS gq3, COALESCE(sg.adj_q4,0) AS gq4, + sg.justification AS gjust, + COALESCE(r.amount_q1,0) AS rq1, COALESCE(r.amount_q2,0) AS rq2, + COALESCE(r.amount_q3,0) AS rq3, COALESCE(r.amount_q4,0) AS rq4, + cd.counterparty AS cd_cp, cd.reference AS cd_ref, cd.contract_date AS cd_dt, + cd.subject AS cd_subj, cd.currency AS cd_cur, cd.ceiling_amount AS cd_ceil, + cd.vat_rate AS cd_vat, cd.deadline AS cd_dl, cd.payment_scheme AS cd_sch, + cd.act AS cd_act, cd.comment AS cd_cmt, + ck.expenses_q1 AS bk1, ck.expenses_q2 AS bk2, ck.expenses_q3 AS bk3, ck.expenses_q4 AS bk4, + ck.expenses_next_year_q1 AS bn1, ck.expenses_next_year_q2 AS bn2, + ck.expenses_next_year_q3 AS bn3, ck.expenses_next_year_q4 AS bn4, + pg.io AS io, + cs.total_amount AS cs_total, cs.counterparty AS cs_cp, cs.deadline AS cs_dl, + cs.comment AS cs_cmt, cs.future_payments_y1 AS cs_fy1, cs.future_payments_y2 AS cs_fy2, + cs.other_ssp_amount AS cs_oth, + al.contract_ref AS al_cref, al.allocation_purpose AS al_purp, + al.internal_order AS al_io, al.property_object AS al_prop, + ca.approved_amount AS ca_amt, ca.protocol_reference AS ca_proto, ca.note AS ca_note, + ck.ceiling_amount AS ckk_ceil, ck.rf_schedule AS ckk_rf, ck.delivery_deadline AS ckk_dl, + ck.procurement_plan AS ckk_pplan, ck.procurement_method AS ckk_pmeth, ck.comment AS ckk_cmt, + cd.addenda AS cd_add, cd.expenses_q1 AS cd_e1, cd.expenses_q2 AS cd_e2, + cd.expenses_q3 AS cd_e3, cd.expenses_q4 AS cd_e4, cd.rf_schedule AS cd_rf, + cd.exchange_rate AS cd_rate, cd.amount_foreign AS cd_fx, + q1.adj_rf AS q1_arf, q1.payment_amount_ho AS q1_pho, q1.payment_amount_rf AS q1_prf, + q2.adj_rf AS q2_arf, q2.payment_amount_ho AS q2_pho, q2.payment_amount_rf AS q2_prf, + q3.adj_rf AS q3_arf, q3.payment_amount_ho AS q3_pho, q3.payment_amount_rf AS q3_prf, + q4.adj_rf AS q4_arf, q4.payment_amount_ho AS q4_pho, q4.payment_amount_rf AS q4_prf, + q1.adj_current AS q1_ac, q1.adj_ssp AS q1_as, q1.adj_reserve AS q1_arv, q1.adj_comment AS q1_acmt, + q1.payment_date AS q1_pd, q1.payment_amount AS q1_pa, + q1.payment_comment AS q1_pcmt, + q1.payment_act AS q1_pact, + COALESCE( + q1.booking_amount, + CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q1 + WHEN ck.expenses_q1 > 0 THEN ck.expenses_q1 + ELSE cd.expenses_q1 + END + + ) AS q1_book, + q1.actual_m1 AS q1_m1, q1.actual_m2 AS q1_m2, q1.actual_m3 AS q1_m3, + q1.transfer_to_q2 AS q1_tq2, q1.transfer_to_q3 AS q1_tq3, q1.transfer_to_q4 AS q1_tq4, + q1.transfer_to_economy AS q1_te, + q1.transfer_delay_acts AS q1_tda, q1.transfer_delay_procurement AS q1_tdp, + q1.transfer_economy_rf AS q1_terf, + q1.transfer_next_comment AS q1_tnc, q1.transfer_far_comment AS q1_tfc, + q2.target_change AS q2_tc, q2.base_plan_correction AS q2_bc, q2.base_plan_correction_comment AS q2_bcc, + q2.plan_revision_increase AS q2_rinc, q2.plan_revision_sequester AS q2_rseq, + q2.plan_revision_comment AS q2_rcmt, + q2.adj_current AS q2_ac, q2.adj_ssp AS q2_as, q2.adj_reserve AS q2_arv, + q2.adj_comment AS q2_acmt, + q2.payment_date AS q2_pd, q2.payment_amount AS q2_pa, + q2.payment_comment AS q2_pcmt, q2.payment_act AS q2_pact, + COALESCE( + q2.booking_amount, + CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q2 + WHEN ck.expenses_q2 > 0 THEN ck.expenses_q2 + ELSE cd.expenses_q2 + END + + ) AS q2_book, + q2.actual_m1 AS q2_m1, q2.actual_m2 AS q2_m2, q2.actual_m3 AS q2_m3, + q2.transfer_to_q3 AS q2_tq3, q2.transfer_to_q4 AS q2_tq4, q2.transfer_to_economy AS q2_te, + q2.transfer_delay_acts AS q2_tda, q2.transfer_delay_procurement AS q2_tdp, + q2.transfer_economy_rf AS q2_terf, + q2.transfer_next_comment AS q2_tnc, q2.transfer_far_comment AS q2_tfc, + q3.target_change AS q3_tc, q3.base_plan_correction AS q3_bc, + q3.base_plan_correction_comment AS q3_bcc, + q3.plan_revision_increase AS q3_rinc, q3.plan_revision_sequester AS q3_rseq, + q3.plan_revision_comment AS q3_rcmt, + q3.adj_current AS q3_ac, q3.adj_ssp AS q3_as, q3.adj_reserve AS q3_arv, + q3.adj_comment AS q3_acmt, + q3.payment_date AS q3_pd, q3.payment_amount AS q3_pa, + q3.payment_comment AS q3_pcmt, q3.payment_act AS q3_pact, + COALESCE( + q3.booking_amount, + CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q3 + WHEN ck.expenses_q3 > 0 THEN ck.expenses_q3 + ELSE cd.expenses_q3 + END + + ) AS q3_book, + q3.actual_m1 AS q3_m1, q3.actual_m2 AS q3_m2, q3.actual_m3 AS q3_m3, + q3.transfer_to_q4 AS q3_tq4, q3.transfer_to_economy AS q3_te, + q3.transfer_delay_acts AS q3_tda, q3.transfer_delay_procurement AS q3_tdp, + q3.transfer_economy_rf AS q3_terf, q3.transfer_next_comment AS q3_tnc, + q4.target_change AS q4_tc, q4.base_plan_correction AS q4_bc, + q4.base_plan_correction_comment AS q4_bcc, + q4.plan_revision_increase AS q4_rinc, q4.plan_revision_sequester AS q4_rseq, + q4.plan_revision_comment AS q4_rcmt, + q4.adj_current AS q4_ac, q4.adj_ssp AS q4_as, q4.adj_reserve AS q4_arv, + q4.adj_comment AS q4_acmt, + q4.payment_date AS q4_pd, q4.payment_amount AS q4_pa, + q4.payment_comment AS q4_pcmt, q4.payment_act AS q4_pact, + COALESCE( + q4.booking_amount, + CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q4 + WHEN ck.expenses_q4 > 0 THEN ck.expenses_q4 + ELSE cd.expenses_q4 + END + + ) AS q4_book, + q4.actual_m1 AS q4_m1, q4.actual_m2 AS q4_m2, q4.actual_m3 AS q4_m3, q4.actual_spod AS q4_spod, + q4.transfer_to_economy AS q4_te, + COALESCE(p.plan_q1,0)+COALESCE(sd.adj_q1,0)+COALESCE(sg.adj_q1,0)+COALESCE(r.amount_q1,0) AS ap1, + COALESCE(p.plan_q2,0)+COALESCE(sd.adj_q2,0)+COALESCE(sg.adj_q2,0)+COALESCE(r.amount_q2,0) AS ap2, + COALESCE(p.plan_q3,0)+COALESCE(sd.adj_q3,0)+COALESCE(sg.adj_q3,0)+COALESCE(r.amount_q3,0) AS ap3, + COALESCE(p.plan_q4,0)+COALESCE(sd.adj_q4,0)+COALESCE(sg.adj_q4,0)+COALESCE(r.amount_q4,0) AS ap4 + FROM page_f pg + JOIN tw t ON t.id = pg.eid + LEFT JOIN v3.vsp v ON v.id = pg.vsp_id + LEFT JOIN jp p ON p.line_id = pg.lid + LEFT JOIN jsd sd ON sd.line_id = pg.lid + LEFT JOIN jsg sg ON sg.line_id = pg.lid + LEFT JOIN jr r ON r.line_id = pg.lid + LEFT JOIN jcd cd ON cd.line_id = pg.lid + LEFT JOIN jck ck ON ck.line_id = pg.lid + LEFT JOIN jcs cs ON cs.line_id = pg.lid + LEFT JOIN jal al ON al.line_id = pg.lid + LEFT JOIN jca ca ON ca.line_id = pg.lid + LEFT JOIN jq1 q1 ON q1.line_id = pg.lid + LEFT JOIN jq2 q2 ON q2.line_id = pg.lid + LEFT JOIN jq3 q3 ON q3.line_id = pg.lid + LEFT JOIN jq4 q4 ON q4.line_id = pg.lid + ), + enriched AS ( + SELECT b.*, + b.ap1 + COALESCE(b.q1_ac,0)+COALESCE(b.q1_as,0)+COALESCE(b.q1_arv,0) AS cp1, + COALESCE(b.q1_m1,0)+COALESCE(b.q1_m2,0)+COALESCE(b.q1_m3,0) AS aq1, + b.ap2 + COALESCE(b.q1_tq2,0) + COALESCE(b.q2_rinc,0)+COALESCE(b.q2_rseq,0) + COALESCE(b.q2_bc,0) AS np2, + COALESCE(b.q2_m1,0)+COALESCE(b.q2_m2,0)+COALESCE(b.q2_m3,0) AS aq2, + b.ap3 + COALESCE(b.q1_tq3,0)+COALESCE(b.q2_tq3,0) + COALESCE(b.q3_rinc,0)+COALESCE(b.q3_rseq,0) + COALESCE(b.q3_bc,0) AS np3, + COALESCE(b.q3_m1,0)+COALESCE(b.q3_m2,0)+COALESCE(b.q3_m3,0) AS aq3, + b.ap4 + COALESCE(b.q1_tq4,0)+COALESCE(b.q2_tq4,0)+COALESCE(b.q3_tq4,0) + COALESCE(b.q4_rinc,0)+COALESCE(b.q4_rseq,0) + COALESCE(b.q4_bc,0) AS np4, + COALESCE(b.q4_m1,0)+COALESCE(b.q4_m2,0)+COALESCE(b.q4_m3,0)+COALESCE(b.q4_spod,0) AS aq4 + FROM input_base b + ), + final_input AS ( + SELECT e.*, + e.np2 + COALESCE(e.q2_ac,0)+COALESCE(e.q2_as,0)+COALESCE(e.q2_arv,0) AS cp2, + e.np3 + COALESCE(e.q3_ac,0)+COALESCE(e.q3_as,0)+COALESCE(e.q3_arv,0) AS cp3, + e.np4 + COALESCE(e.q4_ac,0)+COALESCE(e.q4_as,0)+COALESCE(e.q4_arv,0) AS cp4 + FROM enriched e + ), + + agg AS ( + SELECT bl.expense_item_id AS eid, + SUM(p.plan_q1) AS sp1, SUM(p.plan_q2) AS sp2, SUM(p.plan_q3) AS sp3, SUM(p.plan_q4) AS sp4, + SUM(sd.adj_q1) AS sd1, SUM(sd.adj_q2) AS sd2, SUM(sd.adj_q3) AS sd3, SUM(sd.adj_q4) AS sd4, + SUM(sg.adj_q1) AS sg1, SUM(sg.adj_q2) AS sg2, SUM(sg.adj_q3) AS sg3, SUM(sg.adj_q4) AS sg4, + SUM(r.amount_q1) AS sr1, SUM(r.amount_q2) AS sr2, SUM(r.amount_q3) AS sr3, SUM(r.amount_q4) AS sr4, + SUM(ck.expenses_q1) AS bk1, SUM(ck.expenses_q2) AS bk2, SUM(ck.expenses_q3) AS bk3, SUM(ck.expenses_q4) AS bk4, + SUM(cd.expenses_q1) AS bkc1, SUM(cd.expenses_q2) AS bkc2, SUM(cd.expenses_q3) AS bkc3, SUM(cd.expenses_q4) AS bkc4, + SUM(ck.expenses_next_year_q1) AS bn1, SUM(ck.expenses_next_year_q2) AS bn2, + SUM(ck.expenses_next_year_q3) AS bn3, SUM(ck.expenses_next_year_q4) AS bn4 + FROM v3.budget_line bl + JOIN v3.expense_item ei ON ei.id = bl.expense_item_id + LEFT JOIN v3.plan p ON p.line_id = bl.id AND s_need_ap + LEFT JOIN v3.sequestration sd ON sd.line_id = bl.id AND sd.actor='DFIP' AND s_need_ap + LEFT JOIN v3.sequestration sg ON sg.line_id = bl.id AND sg.actor='SSP_GO' AND s_need_ap + LEFT JOIN v3.reserve r ON r.line_id = bl.id AND s_need_ap + LEFT JOIN v3.ckk ck ON ck.line_id = bl.id AND s_book + LEFT JOIN v3.contract_detail cd ON cd.line_id = bl.id AND s_cd + WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet + AND (s_need_ap OR s_book OR s_cd) + AND (p_focus_lines IS NULL OR bl.expense_item_id = ANY(v_focus_eids)) + GROUP BY bl.expense_item_id + ), + aq_q AS ( + SELECT bl.expense_item_id AS eid, qrtrs.qrtr as quarter, + SUM(q.adj_current) AS ac, SUM(q.adj_ssp) AS as_v, SUM(q.adj_reserve) AS arv, + SUM(q.payment_amount) AS pa, + SUM( + COALESCE( + q.booking_amount, + CASE + WHEN qrtrs.qrtr = 1 + THEN CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q1 + WHEN ck.expenses_q1 > 0 THEN ck.expenses_q1 + ELSE cd.expenses_q1 + END + WHEN qrtrs.qrtr = 2 THEN CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q2 + WHEN ck.expenses_q2 > 0 THEN ck.expenses_q2 + ELSE cd.expenses_q2 + END + WHEN qrtrs.qrtr = 3 THEN CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q3 + WHEN ck.expenses_q3 > 0 THEN ck.expenses_q3 + ELSE cd.expenses_q3 + END + WHEN qrtrs.qrtr = 4 THEN CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q4 + WHEN ck.expenses_q4 > 0 THEN ck.expenses_q4 + ELSE cd.expenses_q4 + END + END + + )) AS bk, + SUM(q.actual_m1) AS m1, SUM(q.actual_m2) AS m2, SUM(q.actual_m3) AS m3, + SUM(q.actual_spod) AS spod, + SUM(q.transfer_to_q2) AS tq2, SUM(q.transfer_to_q3) AS tq3, SUM(q.transfer_to_q4) AS tq4, + SUM(q.transfer_to_economy) AS te, + SUM(q.transfer_delay_acts) AS tda, + SUM(q.transfer_delay_procurement) AS tdp, + SUM(q.transfer_economy_rf) AS terf, + SUM(COALESCE(q.plan_revision_increase,0) + + COALESCE(q.plan_revision_sequester,0) + + COALESCE(q.target_change,0) + + COALESCE(q.base_plan_correction,0)) AS rev + FROM v3.budget_line bl + CROSS JOIN (VALUES (1), (2), (3), (4)) AS qrtrs(qrtr) + + JOIN v3.expense_item ei ON ei.id = bl.expense_item_id + LEFT JOIN v3.budget_line_quarter q ON q.line_id = bl.id AND q.quarter = qrtrs.qrtr + LEFT JOIN v3.ckk ck ON bl.id = ck.line_id + LEFT JOIN v3.contract_detail cd ON bl.id = cd.line_id + WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet + AND (p_focus_lines IS NULL OR bl.expense_item_id = ANY(v_focus_eids)) + GROUP BY bl.expense_item_id, qrtrs.qrtr + ), + tw_agg AS ( + SELECT tw.id, + SUM(a.sp1) AS sp1, SUM(a.sp2) AS sp2, SUM(a.sp3) AS sp3, SUM(a.sp4) AS sp4, + SUM(a.sd1) AS sd1, SUM(a.sd2) AS sd2, SUM(a.sd3) AS sd3, SUM(a.sd4) AS sd4, + SUM(a.sg1) AS sg1, SUM(a.sg2) AS sg2, SUM(a.sg3) AS sg3, SUM(a.sg4) AS sg4, + SUM(a.sr1) AS sr1, SUM(a.sr2) AS sr2, SUM(a.sr3) AS sr3, SUM(a.sr4) AS sr4, + SUM(a.bk1) AS bk1, SUM(a.bk2) AS bk2, SUM(a.bk3) AS bk3, SUM(a.bk4) AS bk4, + SUM(a.bkc1) AS bkc1, SUM(a.bkc2) AS bkc2, SUM(a.bkc3) AS bkc3, SUM(a.bkc4) AS bkc4, + SUM(a.bn1) AS bn1, SUM(a.bn2) AS bn2, SUM(a.bn3) AS bn3, SUM(a.bn4) AS bn4 + FROM tw_f tw LEFT JOIN agg a ON a.eid = ANY(tw.desc_ids) + GROUP BY tw.id + ), + tw_aq1 AS ( + SELECT tw.id, SUM(b.ac) AS ac, SUM(b.as_v) AS as_v, SUM(b.arv) AS arv, + SUM(b.pa) AS pa, SUM(b.bk) AS bk, + SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3, + SUM(b.tq2) AS tq2, SUM(b.tq3) AS tq3, SUM(b.tq4) AS tq4, SUM(b.te) AS te, + SUM(b.tda) AS tda, SUM(b.tdp) AS tdp, SUM(b.terf) AS terf + FROM tw_f tw LEFT JOIN aq_q b ON b.eid = ANY(tw.desc_ids) AND b.quarter=1 GROUP BY tw.id + ), + tw_aq2 AS ( + SELECT tw.id, SUM(b.ac) AS ac, SUM(b.as_v) AS as_v, SUM(b.arv) AS arv, + SUM(b.pa) AS pa, SUM(b.bk) AS bk, + SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3, + SUM(b.tq3) AS tq3, SUM(b.tq4) AS tq4, SUM(b.te) AS te, SUM(b.rev) AS rev, + SUM(b.tda) AS tda, SUM(b.tdp) AS tdp, SUM(b.terf) AS terf + FROM tw_f tw LEFT JOIN aq_q b ON b.eid = ANY(tw.desc_ids) AND b.quarter=2 GROUP BY tw.id + ), + tw_aq3 AS ( + SELECT tw.id, SUM(b.ac) AS ac, SUM(b.as_v) AS as_v, SUM(b.arv) AS arv, + SUM(b.pa) AS pa, SUM(b.bk) AS bk, + SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3, + SUM(b.tq4) AS tq4, SUM(b.te) AS te, SUM(b.rev) AS rev, + SUM(b.tda) AS tda, SUM(b.tdp) AS tdp, SUM(b.terf) AS terf + FROM tw_f tw LEFT JOIN aq_q b ON b.eid = ANY(tw.desc_ids) AND b.quarter=3 GROUP BY tw.id + ), + tw_aq4 AS ( + SELECT tw.id, SUM(b.ac) AS ac, SUM(b.as_v) AS as_v, SUM(b.arv) AS arv, + SUM(b.pa) AS pa, SUM(b.bk) AS bk, + SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3, SUM(b.spod) AS spod, + SUM(b.te) AS te, SUM(b.rev) AS rev + FROM tw_f tw LEFT JOIN aq_q b ON b.eid = ANY(tw.desc_ids) AND b.quarter=4 GROUP BY tw.id + ) + + SELECT sub.row_type, sub.depth, sub.line_id, sub.col_section_code, sub.col_item_id, sub.col_num_group_id, sub.col_name, sub.col_vsp_id, sub.col_vsp_address, sub.col_plan_q1, sub.col_plan_q2, sub.col_plan_q3, sub.col_plan_q4, sub.col_plan_year, sub.col_plan_comment, sub.col_seq_dfip_q1, sub.col_seq_dfip_q2, sub.col_seq_dfip_q3, sub.col_seq_dfip_q4, sub.col_seq_dfip_year, sub.col_seq_dfip_just, sub.col_seq_ssp_q1, sub.col_seq_ssp_q2, sub.col_seq_ssp_q3, sub.col_seq_ssp_q4, sub.col_seq_ssp_year, sub.col_seq_ssp_just, sub.col_appr_q1, sub.col_appr_q2, sub.col_appr_q3, sub.col_appr_q4, sub.col_appr_year, sub.col_cd_counterparty, sub.col_cd_reference, sub.col_cd_date, sub.col_cd_subject, sub.col_cd_currency, sub.col_cd_ceiling, sub.col_cd_vat_rate, sub.col_cd_deadline, sub.col_cd_scheme, sub.col_cd_act, sub.col_cd_comment, sub.col_book_q1, sub.col_book_q2, sub.col_book_q3, sub.col_book_q4, sub.col_book_next_q1, sub.col_book_next_q2, sub.col_book_next_q3, sub.col_book_next_q4, sub.col_q1_adj_current, sub.col_q1_adj_ssp, sub.col_q1_adj_reserve, sub.col_q1_adj_comment, sub.col_q1_corrected_plan, sub.col_q1_pay_date, sub.col_q1_pay_amount, sub.col_q1_pay_comment, sub.col_q1_pay_act, sub.col_q1_booking, sub.col_q1_actual_m1, sub.col_q1_actual_m2, sub.col_q1_actual_m3, sub.col_q1_actual_quarter, sub.col_q1_residual_after_booking, sub.col_q1_residual_after_actual, sub.col_q1_transfer_q2, sub.col_q1_transfer_q2_delay_acts, sub.col_q1_transfer_q2_delay_procurement, sub.col_q1_transfer_q2_economy_rf, sub.col_q1_transfer_next_comment, sub.col_q1_transfer_q3, sub.col_q1_transfer_q4, sub.col_q1_transfer_far_comment, sub.col_q1_transfer_econ, sub.col_q1_total, sub.col_q2_target_change, sub.col_q2_base_correction, sub.col_q2_base_correction_comment, sub.col_q2_revision_inc, sub.col_q2_revision_seq, sub.col_q2_revision_comment, sub.col_q2_new_plan, sub.col_q2_adj_current, sub.col_q2_adj_ssp, sub.col_q2_adj_reserve, sub.col_q2_adj_comment, sub.col_q2_corrected_plan, sub.col_q2_pay_date, sub.col_q2_pay_amount, sub.col_q2_pay_comment, sub.col_q2_pay_act, sub.col_q2_booking, sub.col_q2_actual_m1, sub.col_q2_actual_m2, sub.col_q2_actual_m3, sub.col_q2_actual_quarter, sub.col_q2_residual_after_booking, sub.col_q2_residual_after_actual, sub.col_q2_transfer_q3, sub.col_q2_transfer_q3_delay_acts, sub.col_q2_transfer_q3_delay_procurement, sub.col_q2_transfer_q3_economy_rf, sub.col_q2_transfer_next_comment, sub.col_q2_transfer_q4, sub.col_q2_transfer_far_comment, sub.col_q2_transfer_econ, sub.col_q2_total, sub.col_q3_target_change, sub.col_q3_base_correction, sub.col_q3_base_correction_comment, sub.col_q3_revision_inc, sub.col_q3_revision_seq, sub.col_q3_revision_comment, sub.col_q3_new_plan, sub.col_q3_adj_current, sub.col_q3_adj_ssp, sub.col_q3_adj_reserve, sub.col_q3_adj_comment, sub.col_q3_corrected_plan, sub.col_q3_pay_date, sub.col_q3_pay_amount, sub.col_q3_pay_comment, sub.col_q3_pay_act, sub.col_q3_booking, sub.col_q3_actual_m1, sub.col_q3_actual_m2, sub.col_q3_actual_m3, sub.col_q3_actual_quarter, sub.col_q3_residual_after_booking, sub.col_q3_residual_after_actual, sub.col_q3_transfer_q4, sub.col_q3_transfer_q4_delay_acts, sub.col_q3_transfer_q4_delay_procurement, sub.col_q3_transfer_q4_economy_rf, sub.col_q3_transfer_next_comment, sub.col_q3_transfer_econ, sub.col_q3_total, sub.col_q4_target_change, sub.col_q4_base_correction, sub.col_q4_base_correction_comment, sub.col_q4_revision_inc, sub.col_q4_revision_seq, sub.col_q4_revision_comment, sub.col_q4_new_plan, sub.col_q4_adj_current, sub.col_q4_adj_ssp, sub.col_q4_adj_reserve, sub.col_q4_adj_comment, sub.col_q4_corrected_plan, sub.col_q4_pay_date, sub.col_q4_pay_amount, sub.col_q4_pay_comment, sub.col_q4_pay_act, sub.col_q4_booking, sub.col_q4_actual_m1, sub.col_q4_actual_m2, sub.col_q4_actual_m3, sub.col_q4_actual_spod, sub.col_q4_actual_quarter, sub.col_q4_residual_after_booking, sub.col_q4_residual_after_actual, sub.col_q4_transfer_econ, sub.col_q4_total, sub.col_fact_year, sub.col_pay_year, sub.col_economy_year, sub.col_internal_order, sub.col_cs_total, sub.col_cs_counterparty, sub.col_cs_deadline, sub.col_cs_comment, sub.col_cs_future_y1, sub.col_cs_future_y2, sub.col_cs_other_ssp, sub.col_al_contract_ref, sub.col_al_purpose, sub.col_al_order, sub.col_al_property, sub.col_ca_approved, sub.col_ca_protocol, sub.col_ca_note, sub.col_ckk_ceiling, sub.col_ckk_rf_sched, sub.col_ckk_deadline, sub.col_ckk_proc_plan, sub.col_ckk_proc_meth, sub.col_ckk_comment, sub.col_cd_addenda, sub.col_cd_exp_q1, sub.col_cd_exp_q2, sub.col_cd_exp_q3, sub.col_cd_exp_q4, sub.col_cd_rf_sched, sub.col_cd_exchange_rate, sub.col_cd_amount_foreign, sub.col_q1_adj_rf, sub.col_q1_pay_ho, sub.col_q1_pay_rf, sub.col_q2_adj_rf, sub.col_q2_pay_ho, sub.col_q2_pay_rf, sub.col_q3_adj_rf, sub.col_q3_pay_ho, sub.col_q3_pay_rf, sub.col_q4_adj_rf, sub.col_q4_pay_ho, sub.col_q4_pay_rf, sub._sort_path, sk.rnk + FROM ( + SELECT + 'INPUT'::VARCHAR, array_length(f.tree_path, 1)::INT, f.lid::INT, + f.sc, f.ic, f.ng, COALESCE(f.bname, f.ename), f.vsp_id, f.vsp_addr, + CASE WHEN s_plan THEN f.pq1 END, CASE WHEN s_plan THEN f.pq2 END, + CASE WHEN s_plan THEN f.pq3 END, CASE WHEN s_plan THEN f.pq4 END, + CASE WHEN s_plan THEN f.pq1+f.pq2+f.pq3+f.pq4 END, CASE WHEN s_plan THEN f.pcmt END, + CASE WHEN s_seq_d THEN f.dq1 END, CASE WHEN s_seq_d THEN f.dq2 END, + CASE WHEN s_seq_d THEN f.dq3 END, CASE WHEN s_seq_d THEN f.dq4 END, + CASE WHEN s_seq_d THEN f.dq1+f.dq2+f.dq3+f.dq4 END, CASE WHEN s_seq_d THEN f.djust END, + CASE WHEN s_seq_s THEN f.gq1 END, CASE WHEN s_seq_s THEN f.gq2 END, + CASE WHEN s_seq_s THEN f.gq3 END, CASE WHEN s_seq_s THEN f.gq4 END, + CASE WHEN s_seq_s THEN f.gq1+f.gq2+f.gq3+f.gq4 END, CASE WHEN s_seq_s THEN f.gjust END, + CASE WHEN s_appr THEN f.ap1 END, CASE WHEN s_appr THEN f.ap2 END, + CASE WHEN s_appr THEN f.ap3 END, CASE WHEN s_appr THEN f.ap4 END, + CASE WHEN s_appr THEN f.ap1+f.ap2+f.ap3+f.ap4 END, + CASE WHEN s_cd THEN f.cd_cp END, CASE WHEN s_cd THEN f.cd_ref END, + CASE WHEN s_cd THEN f.cd_dt END, CASE WHEN s_cd THEN f.cd_subj END, + CASE WHEN s_cd THEN f.cd_cur END, CASE WHEN s_cd THEN f.cd_ceil END, + CASE WHEN s_cd THEN f.cd_vat END, CASE WHEN s_cd THEN f.cd_dl END, + CASE WHEN s_cd THEN f.cd_sch END, CASE WHEN s_cd THEN f.cd_act END, + CASE WHEN s_cd THEN f.cd_cmt END, + CASE WHEN s_book THEN f.bk1 END, CASE WHEN s_book THEN f.bk2 END, + CASE WHEN s_book THEN f.bk3 END, CASE WHEN s_book THEN f.bk4 END, + CASE WHEN s_book THEN f.bn1 END, CASE WHEN s_book THEN f.bn2 END, + CASE WHEN s_book THEN f.bn3 END, CASE WHEN s_book THEN f.bn4 END, + CASE WHEN s_q1 THEN f.q1_ac END, CASE WHEN s_q1 THEN f.q1_as END, CASE WHEN s_q1 THEN f.q1_arv END, + CASE WHEN s_q1 THEN f.q1_acmt END, CASE WHEN s_q1 THEN f.cp1 END, + CASE WHEN s_q1 THEN f.q1_pd END, CASE WHEN s_q1 THEN f.q1_pa END, + CASE WHEN s_q1 THEN f.q1_pcmt END, + CASE WHEN s_q1 THEN f.q1_pact END, CASE WHEN s_q1 THEN f.q1_book END, + CASE WHEN s_q1 THEN f.q1_m1 END, CASE WHEN s_q1 THEN f.q1_m2 END, CASE WHEN s_q1 THEN f.q1_m3 END, + CASE WHEN s_q1 THEN f.aq1 END, + CASE WHEN s_q1 THEN f.cp1 - COALESCE(f.q1_book,0) END, -- residual_after_booking + CASE WHEN s_q1 THEN f.cp1 - f.aq1 END, -- residual_after_actual + CASE WHEN s_q1 THEN f.q1_tq2 END, + CASE WHEN s_q1 THEN f.q1_tda END, CASE WHEN s_q1 THEN f.q1_tdp END, CASE WHEN s_q1 THEN f.q1_terf END, + CASE WHEN s_q1 THEN f.q1_tnc END, + CASE WHEN s_q1 THEN f.q1_tq3 END, CASE WHEN s_q1 THEN f.q1_tq4 END, + CASE WHEN s_q1 THEN f.q1_tfc END, + CASE WHEN s_q1 THEN ((f.cp1 - f.aq1) - (COALESCE(f.q1_tda,0)+COALESCE(f.q1_tdp,0)+COALESCE(f.q1_terf,0)) - COALESCE(f.q1_tq3,0) - COALESCE(f.q1_tq4,0)) END, -- transfer_econ (расчёт, AURORA-1938) + CASE WHEN s_q1 THEN + COALESCE(f.q1_tda,0)+COALESCE(f.q1_tdp,0)+COALESCE(f.q1_terf,0) + +COALESCE(f.q1_tq3,0)+COALESCE(f.q1_tq4,0)+((f.cp1 - f.aq1) - (COALESCE(f.q1_tda,0)+COALESCE(f.q1_tdp,0)+COALESCE(f.q1_terf,0)) - COALESCE(f.q1_tq3,0) - COALESCE(f.q1_tq4,0)) + END, + CASE WHEN s_q2 THEN f.q2_tc END, CASE WHEN s_q2 THEN f.q2_bc END, CASE WHEN s_q2 THEN f.q2_bcc END, + CASE WHEN s_q2 THEN f.q2_rinc END, CASE WHEN s_q2 THEN f.q2_rseq END, + CASE WHEN s_q2 THEN f.q2_rcmt END, + CASE WHEN s_q2 THEN f.np2 END, + CASE WHEN s_q2 THEN f.q2_ac END, CASE WHEN s_q2 THEN f.q2_as END, CASE WHEN s_q2 THEN f.q2_arv END, + CASE WHEN s_q2 THEN f.q2_acmt END, + CASE WHEN s_q2 THEN f.cp2 END, + CASE WHEN s_q2 THEN f.q2_pd END, CASE WHEN s_q2 THEN f.q2_pa END, + CASE WHEN s_q2 THEN f.q2_pcmt END, CASE WHEN s_q2 THEN f.q2_pact END, + CASE WHEN s_q2 THEN f.q2_book END, + CASE WHEN s_q2 THEN f.q2_m1 END, CASE WHEN s_q2 THEN f.q2_m2 END, CASE WHEN s_q2 THEN f.q2_m3 END, + CASE WHEN s_q2 THEN f.aq2 END, + CASE WHEN s_q2 THEN f.cp2 - COALESCE(f.q2_book,0) END, + CASE WHEN s_q2 THEN f.cp2 - f.aq2 END, + CASE WHEN s_q2 THEN f.q2_tq3 END, + CASE WHEN s_q2 THEN f.q2_tda END, CASE WHEN s_q2 THEN f.q2_tdp END, CASE WHEN s_q2 THEN f.q2_terf END, + CASE WHEN s_q2 THEN f.q2_tnc END, + CASE WHEN s_q2 THEN f.q2_tq4 END, + CASE WHEN s_q2 THEN f.q2_tfc END, + CASE WHEN s_q2 THEN ((f.cp2 - f.aq2) - (COALESCE(f.q2_tda,0)+COALESCE(f.q2_tdp,0)+COALESCE(f.q2_terf,0)) - COALESCE(f.q2_tq4,0)) END, -- transfer_econ (расчёт, AURORA-1938) + CASE WHEN s_q2 THEN + COALESCE(f.q2_tda,0)+COALESCE(f.q2_tdp,0)+COALESCE(f.q2_terf,0) + +COALESCE(f.q2_tq4,0)+((f.cp2 - f.aq2) - (COALESCE(f.q2_tda,0)+COALESCE(f.q2_tdp,0)+COALESCE(f.q2_terf,0)) - COALESCE(f.q2_tq4,0)) + END, + CASE WHEN s_q3 THEN f.q3_tc END, CASE WHEN s_q3 THEN f.q3_bc END, + CASE WHEN s_q3 THEN f.q3_bcc END, + CASE WHEN s_q3 THEN f.q3_rinc END, CASE WHEN s_q3 THEN f.q3_rseq END, + CASE WHEN s_q3 THEN f.q3_rcmt END, + CASE WHEN s_q3 THEN f.np3 END, + CASE WHEN s_q3 THEN f.q3_ac END, CASE WHEN s_q3 THEN f.q3_as END, CASE WHEN s_q3 THEN f.q3_arv END, + CASE WHEN s_q3 THEN f.q3_acmt END, + CASE WHEN s_q3 THEN f.cp3 END, + CASE WHEN s_q3 THEN f.q3_pd END, CASE WHEN s_q3 THEN f.q3_pa END, + CASE WHEN s_q3 THEN f.q3_pcmt END, CASE WHEN s_q3 THEN f.q3_pact END, + CASE WHEN s_q3 THEN f.q3_book END, + CASE WHEN s_q3 THEN f.q3_m1 END, CASE WHEN s_q3 THEN f.q3_m2 END, CASE WHEN s_q3 THEN f.q3_m3 END, + CASE WHEN s_q3 THEN f.aq3 END, + CASE WHEN s_q3 THEN f.cp3 - COALESCE(f.q3_book,0) END, + CASE WHEN s_q3 THEN f.cp3 - f.aq3 END, + CASE WHEN s_q3 THEN f.q3_tq4 END, + CASE WHEN s_q3 THEN f.q3_tda END, CASE WHEN s_q3 THEN f.q3_tdp END, CASE WHEN s_q3 THEN f.q3_terf END, + CASE WHEN s_q3 THEN f.q3_tnc END, + CASE WHEN s_q3 THEN ((f.cp3 - f.aq3) - (COALESCE(f.q3_tda,0)+COALESCE(f.q3_tdp,0)+COALESCE(f.q3_terf,0))) END, -- transfer_econ (расчёт, AURORA-1938) + CASE WHEN s_q3 THEN + COALESCE(f.q3_tda,0)+COALESCE(f.q3_tdp,0)+COALESCE(f.q3_terf,0) + +((f.cp3 - f.aq3) - (COALESCE(f.q3_tda,0)+COALESCE(f.q3_tdp,0)+COALESCE(f.q3_terf,0))) + END, + CASE WHEN s_q4 THEN f.q4_tc END, CASE WHEN s_q4 THEN f.q4_bc END, + CASE WHEN s_q4 THEN f.q4_bcc END, + CASE WHEN s_q4 THEN f.q4_rinc END, CASE WHEN s_q4 THEN f.q4_rseq END, + CASE WHEN s_q4 THEN f.q4_rcmt END, + CASE WHEN s_q4 THEN f.np4 END, + CASE WHEN s_q4 THEN f.q4_ac END, CASE WHEN s_q4 THEN f.q4_as END, CASE WHEN s_q4 THEN f.q4_arv END, + CASE WHEN s_q4 THEN f.q4_acmt END, + CASE WHEN s_q4 THEN f.cp4 END, + CASE WHEN s_q4 THEN f.q4_pd END, CASE WHEN s_q4 THEN f.q4_pa END, + CASE WHEN s_q4 THEN f.q4_pcmt END, CASE WHEN s_q4 THEN f.q4_pact END, + CASE WHEN s_q4 THEN f.q4_book END, + CASE WHEN s_q4 THEN f.q4_m1 END, CASE WHEN s_q4 THEN f.q4_m2 END, CASE WHEN s_q4 THEN f.q4_m3 END, + CASE WHEN s_q4 THEN f.q4_spod END, + CASE WHEN s_q4 THEN f.aq4 END, + CASE WHEN s_q4 THEN f.cp4 - COALESCE(f.q4_book,0) END, + CASE WHEN s_q4 THEN f.cp4 - f.aq4 END, + CASE WHEN s_q4 THEN (f.cp4 - f.aq4) END, + CASE WHEN s_q4 THEN (f.cp4 - f.aq4) END, + CASE WHEN s_tot THEN f.aq1+f.aq2+f.aq3+f.aq4 END, + CASE WHEN s_tot THEN COALESCE(f.q1_pa,0)+COALESCE(f.q2_pa,0)+COALESCE(f.q3_pa,0)+COALESCE(f.q4_pa,0) END, + CASE WHEN s_tot THEN f.cp4 - f.aq4 END, + + f.io, + CASE WHEN s_cs THEN f.cs_total END, CASE WHEN s_cs THEN f.cs_cp END, + CASE WHEN s_cs THEN f.cs_dl END, CASE WHEN s_cs THEN f.cs_cmt END, + CASE WHEN s_cs THEN f.cs_fy1 END, CASE WHEN s_cs THEN f.cs_fy2 END, + CASE WHEN s_cs THEN f.cs_oth END, + CASE WHEN s_al THEN f.al_cref END, CASE WHEN s_al THEN f.al_purp END, + CASE WHEN s_al THEN f.al_io END, CASE WHEN s_al THEN f.al_prop END, + CASE WHEN s_ca THEN f.ca_amt END, CASE WHEN s_ca THEN f.ca_proto END, + CASE WHEN s_ca THEN f.ca_note END, + CASE WHEN s_ckk THEN f.ckk_ceil END, CASE WHEN s_ckk THEN f.ckk_rf END, + CASE WHEN s_ckk THEN f.ckk_dl END, CASE WHEN s_ckk THEN f.ckk_pplan END, + CASE WHEN s_ckk THEN f.ckk_pmeth END, CASE WHEN s_ckk THEN f.ckk_cmt END, + CASE WHEN s_cd THEN f.cd_add END, CASE WHEN s_cd THEN f.cd_e1 END, + CASE WHEN s_cd THEN f.cd_e2 END, CASE WHEN s_cd THEN f.cd_e3 END, + CASE WHEN s_cd THEN f.cd_e4 END, CASE WHEN s_cd THEN f.cd_rf END, + CASE WHEN s_cd THEN f.cd_rate END, CASE WHEN s_cd THEN f.cd_fx END, + CASE WHEN s_q1 THEN f.q1_arf END, CASE WHEN s_q1 THEN f.q1_pho END, CASE WHEN s_q1 THEN f.q1_prf END, + CASE WHEN s_q2 THEN f.q2_arf END, CASE WHEN s_q2 THEN f.q2_pho END, CASE WHEN s_q2 THEN f.q2_prf END, + CASE WHEN s_q3 THEN f.q3_arf END, CASE WHEN s_q3 THEN f.q3_pho END, CASE WHEN s_q3 THEN f.q3_prf END, + CASE WHEN s_q4 THEN f.q4_arf END, CASE WHEN s_q4 THEN f.q4_pho END, CASE WHEN s_q4 THEN f.q4_prf END, + + f.tree_path || ARRAY[f.lid::INT] AS _sort, + f.lid::BIGINT AS _k + FROM final_input f + + UNION ALL + + SELECT + CASE t.depth WHEN 0 THEN 'ROOT' WHEN 1 THEN 'GROUP' WHEN 2 THEN 'ITEM' WHEN 3 THEN 'SUB_ITEM' WHEN 4 THEN 'SUB_SUB_ITEM' END::VARCHAR, + t.depth, NULL::INT, + CASE + WHEN t.depth < 3 THEN t.item_id + WHEN t.depth = 3 THEN t.parent_item_id + WHEN t.depth = 4 THEN t.grandparent_item_id + END, + CASE WHEN t.depth >= 3 THEN t.item_id ELSE NULL END, + t.num_group_id, t.name, NULL::INT, NULL::VARCHAR, + CASE WHEN s_plan THEN ta.sp1 END, CASE WHEN s_plan THEN ta.sp2 END, + CASE WHEN s_plan THEN ta.sp3 END, CASE WHEN s_plan THEN ta.sp4 END, + CASE WHEN s_plan THEN COALESCE(ta.sp1,0)+COALESCE(ta.sp2,0)+COALESCE(ta.sp3,0)+COALESCE(ta.sp4,0) END, + NULL::VARCHAR, + CASE WHEN s_seq_d THEN ta.sd1 END, CASE WHEN s_seq_d THEN ta.sd2 END, + CASE WHEN s_seq_d THEN ta.sd3 END, CASE WHEN s_seq_d THEN ta.sd4 END, + CASE WHEN s_seq_d THEN COALESCE(ta.sd1,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sd4,0) END, + NULL::VARCHAR, + CASE WHEN s_seq_s THEN ta.sg1 END, CASE WHEN s_seq_s THEN ta.sg2 END, + CASE WHEN s_seq_s THEN ta.sg3 END, CASE WHEN s_seq_s THEN ta.sg4 END, + CASE WHEN s_seq_s THEN COALESCE(ta.sg1,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sg4,0) END, + NULL::VARCHAR, + CASE WHEN s_appr THEN COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sg1,0)+COALESCE(ta.sr1,0) END, + CASE WHEN s_appr THEN COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sr2,0) END, + CASE WHEN s_appr THEN COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sr3,0) END, + CASE WHEN s_appr THEN COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0) END, + CASE WHEN s_appr THEN COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sg1,0)+COALESCE(ta.sr1,0) + +COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sr2,0) + +COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sr3,0) + +COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0) END, + NULL::VARCHAR, NULL::VARCHAR, NULL::DATE, NULL::VARCHAR, + NULL::VARCHAR, NULL::NUMERIC, NULL::VARCHAR, NULL::VARCHAR, + NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, + CASE WHEN s_book THEN ta.bk1 END, CASE WHEN s_book THEN ta.bk2 END, + CASE WHEN s_book THEN ta.bk3 END, CASE WHEN s_book THEN ta.bk4 END, + CASE WHEN s_book THEN ta.bn1 END, CASE WHEN s_book THEN ta.bn2 END, + CASE WHEN s_book THEN ta.bn3 END, CASE WHEN s_book THEN ta.bn4 END, + CASE WHEN s_q1 THEN tb1.ac END, CASE WHEN s_q1 THEN tb1.as_v END, CASE WHEN s_q1 THEN tb1.arv END, + NULL::VARCHAR, + CASE WHEN s_q1 THEN COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sg1,0)+COALESCE(ta.sr1,0)+COALESCE(tb1.ac,0)+COALESCE(tb1.as_v,0)+COALESCE(tb1.arv,0) END, + NULL::DATE, + CASE WHEN s_q1 THEN tb1.pa END, + NULL::VARCHAR, -- pay_comment + NULL::VARCHAR, -- pay_act + CASE WHEN s_q1 THEN tb1.bk END, -- booking (rollup) + CASE WHEN s_q1 THEN tb1.m1 END, CASE WHEN s_q1 THEN tb1.m2 END, CASE WHEN s_q1 THEN tb1.m3 END, + CASE WHEN s_q1 THEN COALESCE(tb1.m1,0)+COALESCE(tb1.m2,0)+COALESCE(tb1.m3,0) END, + CASE WHEN s_q1 THEN + (COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sg1,0)+COALESCE(ta.sr1,0)+COALESCE(tb1.ac,0)+COALESCE(tb1.as_v,0)+COALESCE(tb1.arv,0)) + - COALESCE(tb1.bk,0) + END, + CASE WHEN s_q1 THEN + (COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sg1,0)+COALESCE(ta.sr1,0)+COALESCE(tb1.ac,0)+COALESCE(tb1.as_v,0)+COALESCE(tb1.arv,0)) + - (COALESCE(tb1.m1,0)+COALESCE(tb1.m2,0)+COALESCE(tb1.m3,0)) + END, + CASE WHEN s_q1 THEN tb1.tq2 END, + CASE WHEN s_q1 THEN tb1.tda END, CASE WHEN s_q1 THEN tb1.tdp END, CASE WHEN s_q1 THEN tb1.terf END, + NULL::VARCHAR, -- transfer_next_comment (text, no rollup) + CASE WHEN s_q1 THEN tb1.tq3 END, CASE WHEN s_q1 THEN tb1.tq4 END, + NULL::VARCHAR, -- far comment + CASE WHEN s_q1 THEN (((COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sg1,0)+COALESCE(ta.sr1,0)+COALESCE(tb1.ac,0)+COALESCE(tb1.as_v,0)+COALESCE(tb1.arv,0)) - (COALESCE(tb1.m1,0)+COALESCE(tb1.m2,0)+COALESCE(tb1.m3,0))) - (COALESCE(tb1.tda,0)+COALESCE(tb1.tdp,0)+COALESCE(tb1.terf,0)) - COALESCE(tb1.tq3,0) - COALESCE(tb1.tq4,0)) END, -- transfer_econ (агрегат, AURORA-1938) + CASE WHEN s_q1 THEN + COALESCE(tb1.tda,0)+COALESCE(tb1.tdp,0)+COALESCE(tb1.terf,0) + +COALESCE(tb1.tq3,0)+COALESCE(tb1.tq4,0)+(((COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sg1,0)+COALESCE(ta.sr1,0)+COALESCE(tb1.ac,0)+COALESCE(tb1.as_v,0)+COALESCE(tb1.arv,0)) - (COALESCE(tb1.m1,0)+COALESCE(tb1.m2,0)+COALESCE(tb1.m3,0))) - (COALESCE(tb1.tda,0)+COALESCE(tb1.tdp,0)+COALESCE(tb1.terf,0)) - COALESCE(tb1.tq3,0) - COALESCE(tb1.tq4,0)) + END, + NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, -- target_change/base_correction + NULL::NUMERIC, NULL::NUMERIC, -- revision_inc/revision_seq + NULL::VARCHAR, -- revision_comment + CASE WHEN s_q2 THEN + COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sr2,0)+COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0) + END, + CASE WHEN s_q2 THEN tb2.ac END, CASE WHEN s_q2 THEN tb2.as_v END, CASE WHEN s_q2 THEN tb2.arv END, + NULL::VARCHAR, -- adj_comment + CASE WHEN s_q2 THEN + COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sr2,0)+COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0)+COALESCE(tb2.ac,0)+COALESCE(tb2.as_v,0)+COALESCE(tb2.arv,0) + END, + NULL::DATE, + CASE WHEN s_q2 THEN tb2.pa END, + NULL::VARCHAR, -- pay_comment + NULL::VARCHAR, -- pay_act + CASE WHEN s_q2 THEN tb2.bk END, -- booking (rollup) + CASE WHEN s_q2 THEN tb2.m1 END, CASE WHEN s_q2 THEN tb2.m2 END, CASE WHEN s_q2 THEN tb2.m3 END, + CASE WHEN s_q2 THEN COALESCE(tb2.m1,0)+COALESCE(tb2.m2,0)+COALESCE(tb2.m3,0) END, + CASE WHEN s_q2 THEN + (COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sr2,0)+COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0)+COALESCE(tb2.ac,0)+COALESCE(tb2.as_v,0)+COALESCE(tb2.arv,0)) + - COALESCE(tb2.bk,0) + END, + CASE WHEN s_q2 THEN + (COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sr2,0)+COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0)+COALESCE(tb2.ac,0)+COALESCE(tb2.as_v,0)+COALESCE(tb2.arv,0)) + - (COALESCE(tb2.m1,0)+COALESCE(tb2.m2,0)+COALESCE(tb2.m3,0)) + END, + CASE WHEN s_q2 THEN tb2.tq3 END, + CASE WHEN s_q2 THEN tb2.tda END, CASE WHEN s_q2 THEN tb2.tdp END, CASE WHEN s_q2 THEN tb2.terf END, + NULL::VARCHAR, -- transfer_next_comment (text, no rollup) + CASE WHEN s_q2 THEN tb2.tq4 END, + NULL::VARCHAR, -- far comment + CASE WHEN s_q2 THEN (((COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sr2,0)+COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0)+COALESCE(tb2.ac,0)+COALESCE(tb2.as_v,0)+COALESCE(tb2.arv,0)) - (COALESCE(tb2.m1,0)+COALESCE(tb2.m2,0)+COALESCE(tb2.m3,0))) - (COALESCE(tb2.tda,0)+COALESCE(tb2.tdp,0)+COALESCE(tb2.terf,0)) - COALESCE(tb2.tq4,0)) END, -- transfer_econ (агрегат, AURORA-1938) + CASE WHEN s_q2 THEN + COALESCE(tb2.tda,0)+COALESCE(tb2.tdp,0)+COALESCE(tb2.terf,0) + +COALESCE(tb2.tq4,0)+(((COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sr2,0)+COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0)+COALESCE(tb2.ac,0)+COALESCE(tb2.as_v,0)+COALESCE(tb2.arv,0)) - (COALESCE(tb2.m1,0)+COALESCE(tb2.m2,0)+COALESCE(tb2.m3,0))) - (COALESCE(tb2.tda,0)+COALESCE(tb2.tdp,0)+COALESCE(tb2.terf,0)) - COALESCE(tb2.tq4,0)) + END, + NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, -- target_change/base_correction/comment + NULL::NUMERIC, NULL::NUMERIC, -- revision_inc/revision_seq + NULL::VARCHAR, + CASE WHEN s_q3 THEN + COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sr3,0)+COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0) + END, + CASE WHEN s_q3 THEN tb3.ac END, CASE WHEN s_q3 THEN tb3.as_v END, CASE WHEN s_q3 THEN tb3.arv END, + NULL::VARCHAR, + CASE WHEN s_q3 THEN + COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sr3,0)+COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0)+COALESCE(tb3.ac,0)+COALESCE(tb3.as_v,0)+COALESCE(tb3.arv,0) + END, + NULL::DATE, + CASE WHEN s_q3 THEN tb3.pa END, + NULL::VARCHAR, -- pay_comment + NULL::VARCHAR, -- pay_act + CASE WHEN s_q3 THEN tb3.bk END, -- booking (rollup) + CASE WHEN s_q3 THEN tb3.m1 END, CASE WHEN s_q3 THEN tb3.m2 END, CASE WHEN s_q3 THEN tb3.m3 END, + CASE WHEN s_q3 THEN COALESCE(tb3.m1,0)+COALESCE(tb3.m2,0)+COALESCE(tb3.m3,0) END, + CASE WHEN s_q3 THEN + (COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sr3,0)+COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0)+COALESCE(tb3.ac,0)+COALESCE(tb3.as_v,0)+COALESCE(tb3.arv,0)) + - COALESCE(tb3.bk,0) + END, + CASE WHEN s_q3 THEN + (COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sr3,0)+COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0)+COALESCE(tb3.ac,0)+COALESCE(tb3.as_v,0)+COALESCE(tb3.arv,0)) + - (COALESCE(tb3.m1,0)+COALESCE(tb3.m2,0)+COALESCE(tb3.m3,0)) + END, + CASE WHEN s_q3 THEN tb3.tq4 END, + CASE WHEN s_q3 THEN tb3.tda END, CASE WHEN s_q3 THEN tb3.tdp END, CASE WHEN s_q3 THEN tb3.terf END, + NULL::VARCHAR, -- transfer_next_comment (text, no rollup) + CASE WHEN s_q3 THEN (((COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sr3,0)+COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0)+COALESCE(tb3.ac,0)+COALESCE(tb3.as_v,0)+COALESCE(tb3.arv,0)) - (COALESCE(tb3.m1,0)+COALESCE(tb3.m2,0)+COALESCE(tb3.m3,0))) - (COALESCE(tb3.tda,0)+COALESCE(tb3.tdp,0)+COALESCE(tb3.terf,0))) END, -- transfer_econ (агрегат, AURORA-1938) + CASE WHEN s_q3 THEN + COALESCE(tb3.tda,0)+COALESCE(tb3.tdp,0)+COALESCE(tb3.terf,0) + +(((COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sr3,0)+COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0)+COALESCE(tb3.ac,0)+COALESCE(tb3.as_v,0)+COALESCE(tb3.arv,0)) - (COALESCE(tb3.m1,0)+COALESCE(tb3.m2,0)+COALESCE(tb3.m3,0))) - (COALESCE(tb3.tda,0)+COALESCE(tb3.tdp,0)+COALESCE(tb3.terf,0))) + END, + NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, -- target_change/base_correction/comment + NULL::NUMERIC, NULL::NUMERIC, -- revision_inc/revision_seq + NULL::VARCHAR, + CASE WHEN s_q4 THEN + COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0)+COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0) + END, + CASE WHEN s_q4 THEN tb4.ac END, CASE WHEN s_q4 THEN tb4.as_v END, CASE WHEN s_q4 THEN tb4.arv END, + NULL::VARCHAR, + CASE WHEN s_q4 THEN + COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0)+COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0)+COALESCE(tb4.ac,0)+COALESCE(tb4.as_v,0)+COALESCE(tb4.arv,0) + END, + NULL::DATE, + CASE WHEN s_q4 THEN tb4.pa END, + NULL::VARCHAR, -- pay_comment + NULL::VARCHAR, -- pay_act + CASE WHEN s_q4 THEN tb4.bk END, -- booking (rollup) + CASE WHEN s_q4 THEN tb4.m1 END, CASE WHEN s_q4 THEN tb4.m2 END, CASE WHEN s_q4 THEN tb4.m3 END, + CASE WHEN s_q4 THEN tb4.spod END, + CASE WHEN s_q4 THEN COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0) END, + CASE WHEN s_q4 THEN + (COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0)+COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0)+COALESCE(tb4.ac,0)+COALESCE(tb4.as_v,0)+COALESCE(tb4.arv,0)) + - COALESCE(tb4.bk,0) + END, + CASE WHEN s_q4 THEN + (COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0)+COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0)+COALESCE(tb4.ac,0)+COALESCE(tb4.as_v,0)+COALESCE(tb4.arv,0)) + - (COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0)) + END, + CASE WHEN s_q4 THEN ((COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0)+COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0)+COALESCE(tb4.ac,0)+COALESCE(tb4.as_v,0)+COALESCE(tb4.arv,0)) - (COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0))) END, + CASE WHEN s_q4 THEN ((COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0)+COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0)+COALESCE(tb4.ac,0)+COALESCE(tb4.as_v,0)+COALESCE(tb4.arv,0)) - (COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0))) END, + CASE WHEN s_tot THEN COALESCE(tb1.m1,0)+COALESCE(tb1.m2,0)+COALESCE(tb1.m3,0) + +COALESCE(tb2.m1,0)+COALESCE(tb2.m2,0)+COALESCE(tb2.m3,0) + +COALESCE(tb3.m1,0)+COALESCE(tb3.m2,0)+COALESCE(tb3.m3,0) + +COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0) END, + CASE WHEN s_tot THEN COALESCE(tb1.pa,0)+COALESCE(tb2.pa,0)+COALESCE(tb3.pa,0)+COALESCE(tb4.pa,0) END, + CASE WHEN s_tot THEN + (COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0) + +COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0) + +COALESCE(tb4.ac,0)+COALESCE(tb4.as_v,0)+COALESCE(tb4.arv,0)) + - (COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0)) + END, + + NULL::VARCHAR, -- internal_order + NULL::NUMERIC, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, -- contract_summary + NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, + NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, -- allocation + NULL::NUMERIC, NULL::VARCHAR, NULL::VARCHAR, -- collegial + NULL::NUMERIC, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, -- ckk + NULL::VARCHAR, NULL::VARCHAR, + NULL::VARCHAR, CASE WHEN s_cd THEN ta.bkc1 END, CASE WHEN s_cd THEN ta.bkc2 END, + CASE WHEN s_cd THEN ta.bkc3 END, CASE WHEN s_cd THEN ta.bkc4 END, + NULL::VARCHAR, NULL::NUMERIC, NULL::NUMERIC, + NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, -- q1 adj_rf/pho/prf + NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, -- q2 + NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, -- q3 + NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, -- q4 + + t.path AS _sort, + -t.id::BIGINT + FROM tw_f t + LEFT JOIN tw_agg ta ON ta.id = t.id + LEFT JOIN tw_aq1 tb1 ON tb1.id = t.id + LEFT JOIN tw_aq2 tb2 ON tb2.id = t.id + LEFT JOIN tw_aq3 tb3 ON tb3.id = t.id + LEFT JOIN tw_aq4 tb4 ON tb4.id = t.id + ) sub(row_type, depth, line_id, col_section_code, col_item_id, col_num_group_id, col_name, col_vsp_id, col_vsp_address, col_plan_q1, col_plan_q2, col_plan_q3, col_plan_q4, col_plan_year, col_plan_comment, col_seq_dfip_q1, col_seq_dfip_q2, col_seq_dfip_q3, col_seq_dfip_q4, col_seq_dfip_year, col_seq_dfip_just, col_seq_ssp_q1, col_seq_ssp_q2, col_seq_ssp_q3, col_seq_ssp_q4, col_seq_ssp_year, col_seq_ssp_just, col_appr_q1, col_appr_q2, col_appr_q3, col_appr_q4, col_appr_year, col_cd_counterparty, col_cd_reference, col_cd_date, col_cd_subject, col_cd_currency, col_cd_ceiling, col_cd_vat_rate, col_cd_deadline, col_cd_scheme, col_cd_act, col_cd_comment, col_book_q1, col_book_q2, col_book_q3, col_book_q4, col_book_next_q1, col_book_next_q2, col_book_next_q3, col_book_next_q4, col_q1_adj_current, col_q1_adj_ssp, col_q1_adj_reserve, col_q1_adj_comment, col_q1_corrected_plan, col_q1_pay_date, col_q1_pay_amount, col_q1_pay_comment, col_q1_pay_act, col_q1_booking, col_q1_actual_m1, col_q1_actual_m2, col_q1_actual_m3, col_q1_actual_quarter, col_q1_residual_after_booking, col_q1_residual_after_actual, col_q1_transfer_q2, col_q1_transfer_q2_delay_acts, col_q1_transfer_q2_delay_procurement, col_q1_transfer_q2_economy_rf, col_q1_transfer_next_comment, col_q1_transfer_q3, col_q1_transfer_q4, col_q1_transfer_far_comment, col_q1_transfer_econ, col_q1_total, col_q2_target_change, col_q2_base_correction, col_q2_base_correction_comment, col_q2_revision_inc, col_q2_revision_seq, col_q2_revision_comment, col_q2_new_plan, col_q2_adj_current, col_q2_adj_ssp, col_q2_adj_reserve, col_q2_adj_comment, col_q2_corrected_plan, col_q2_pay_date, col_q2_pay_amount, col_q2_pay_comment, col_q2_pay_act, col_q2_booking, col_q2_actual_m1, col_q2_actual_m2, col_q2_actual_m3, col_q2_actual_quarter, col_q2_residual_after_booking, col_q2_residual_after_actual, col_q2_transfer_q3, col_q2_transfer_q3_delay_acts, col_q2_transfer_q3_delay_procurement, col_q2_transfer_q3_economy_rf, col_q2_transfer_next_comment, col_q2_transfer_q4, col_q2_transfer_far_comment, col_q2_transfer_econ, col_q2_total, col_q3_target_change, col_q3_base_correction, col_q3_base_correction_comment, col_q3_revision_inc, col_q3_revision_seq, col_q3_revision_comment, col_q3_new_plan, col_q3_adj_current, col_q3_adj_ssp, col_q3_adj_reserve, col_q3_adj_comment, col_q3_corrected_plan, col_q3_pay_date, col_q3_pay_amount, col_q3_pay_comment, col_q3_pay_act, col_q3_booking, col_q3_actual_m1, col_q3_actual_m2, col_q3_actual_m3, col_q3_actual_quarter, col_q3_residual_after_booking, col_q3_residual_after_actual, col_q3_transfer_q4, col_q3_transfer_q4_delay_acts, col_q3_transfer_q4_delay_procurement, col_q3_transfer_q4_economy_rf, col_q3_transfer_next_comment, col_q3_transfer_econ, col_q3_total, col_q4_target_change, col_q4_base_correction, col_q4_base_correction_comment, col_q4_revision_inc, col_q4_revision_seq, col_q4_revision_comment, col_q4_new_plan, col_q4_adj_current, col_q4_adj_ssp, col_q4_adj_reserve, col_q4_adj_comment, col_q4_corrected_plan, col_q4_pay_date, col_q4_pay_amount, col_q4_pay_comment, col_q4_pay_act, col_q4_booking, col_q4_actual_m1, col_q4_actual_m2, col_q4_actual_m3, col_q4_actual_spod, col_q4_actual_quarter, col_q4_residual_after_booking, col_q4_residual_after_actual, col_q4_transfer_econ, col_q4_total, col_fact_year, col_pay_year, col_economy_year, col_internal_order, col_cs_total, col_cs_counterparty, col_cs_deadline, col_cs_comment, col_cs_future_y1, col_cs_future_y2, col_cs_other_ssp, col_al_contract_ref, col_al_purpose, col_al_order, col_al_property, col_ca_approved, col_ca_protocol, col_ca_note, col_ckk_ceiling, col_ckk_rf_sched, col_ckk_deadline, col_ckk_proc_plan, col_ckk_proc_meth, col_ckk_comment, col_cd_addenda, col_cd_exp_q1, col_cd_exp_q2, col_cd_exp_q3, col_cd_exp_q4, col_cd_rf_sched, col_cd_exchange_rate, col_cd_amount_foreign, col_q1_adj_rf, col_q1_pay_ho, col_q1_pay_rf, col_q2_adj_rf, col_q2_pay_ho, col_q2_pay_rf, col_q3_adj_rf, col_q3_pay_ho, col_q3_pay_rf, col_q4_adj_rf, col_q4_pay_ho, col_q4_pay_rf, _sort_path, _k) + JOIN sk ON sk.k = sub._k + ORDER BY sk.rnk; +END; +$function$ +; + +CREATE OR REPLACE FUNCTION v3.v_form4_sheet_sections(p_form_id integer, p_sheet character varying, p_sections text[] DEFAULT NULL::text[]) + RETURNS TABLE(row_type character varying, depth integer, line_id integer, col_section_code character varying, col_item_id character varying, col_num_group_id character varying, col_name character varying, col_justification character varying, col_internal_order character varying, col_vsp_id integer, col_vsp_address character varying, col_plan_q1 numeric, col_plan_q2 numeric, col_plan_q3 numeric, col_plan_q4 numeric, col_plan_year numeric, col_plan_comment character varying, col_seq_dfip_q1 numeric, col_seq_dfip_q2 numeric, col_seq_dfip_q3 numeric, col_seq_dfip_q4 numeric, col_seq_dfip_year numeric, col_seq_dfip_just character varying, col_appr_q1 numeric, col_appr_q2 numeric, col_appr_q3 numeric, col_appr_q4 numeric, col_appr_year numeric, col_cs_amount numeric, col_cs_reference character varying, col_cs_counterparty character varying, col_cs_deadline character varying, col_cs_comment character varying, col_cs_future_y1 numeric, col_cs_future_y2 numeric, col_cs_other_ssp numeric, col_cs_centralized_flag character varying, col_al_contract_ref character varying, col_al_purpose character varying, col_rsv_q1 numeric, col_rsv_q2 numeric, col_rsv_q3 numeric, col_rsv_q4 numeric, col_rsv_year numeric, col_rsv_just character varying, col_col_amount numeric, col_col_protocol character varying, col_col_note character varying, col_ckk_ceiling numeric, col_ckk_q1 numeric, col_ckk_q2 numeric, col_ckk_q3 numeric, col_ckk_q4 numeric, col_ckk_rf_schedule character varying, col_ckk_delivery_deadline character varying, col_ckk_procurement_plan character varying, col_ckk_procurement_method character varying, col_ckk_comment character varying, col_cd_counterparty character varying, col_cd_reference character varying, col_cd_addenda character varying, col_cd_date date, col_cd_subject character varying, col_cd_currency character varying, col_cd_ceiling numeric, col_cd_q1 numeric, col_cd_q2 numeric, col_cd_q3 numeric, col_cd_q4 numeric, col_cd_rf_schedule character varying, col_cd_vat_rate character varying, col_cd_exchange_rate numeric, col_cd_amount_foreign numeric, col_cd_deadline character varying, col_cd_scheme character varying, col_cd_act character varying, col_cd_comment character varying, col_book_q1 numeric, col_book_q2 numeric, col_book_q3 numeric, col_book_q4 numeric, col_book_next_q1 numeric, col_book_next_q2 numeric, col_book_next_q3 numeric, col_book_next_q4 numeric, col_q1_adj_current numeric, col_q1_adj_ssp numeric, col_q1_adj_rf numeric, col_q1_adj_reserve numeric, col_q1_adj_comment character varying, col_q1_corrected_plan numeric, col_q1_pay_date date, col_q1_pay_amount numeric, col_q1_pay_ho numeric, col_q1_pay_rf numeric, col_q1_pay_comment character varying, col_q1_pay_act character varying, col_q1_booking numeric, col_q1_actual_m1 numeric, col_q1_actual_m2 numeric, col_q1_actual_m3 numeric, col_q1_actual_quarter numeric, col_q1_residual_after_booking numeric, col_q1_residual_after_actual numeric, col_q1_transfer_q2 numeric, col_q1_transfer_q2_delay_acts numeric, col_q1_transfer_q2_delay_procurement numeric, col_q1_transfer_q2_economy_rf numeric, col_q1_transfer_next_comment character varying, col_q1_transfer_q3 numeric, col_q1_transfer_q4 numeric, col_q1_transfer_far_comment character varying, col_q1_transfer_econ numeric, col_q1_total numeric, col_q2_target_change numeric, col_q2_base_correction numeric, col_q2_base_correction_comment character varying, col_q2_revision_inc numeric, col_q2_revision_seq numeric, col_q2_revision_comment character varying, col_q2_new_plan numeric, col_q2_adj_current numeric, col_q2_adj_ssp numeric, col_q2_adj_rf numeric, col_q2_adj_reserve numeric, col_q2_adj_comment character varying, col_q2_corrected_plan numeric, col_q2_pay_date date, col_q2_pay_amount numeric, col_q2_pay_ho numeric, col_q2_pay_rf numeric, col_q2_pay_comment character varying, col_q2_pay_act character varying, col_q2_booking numeric, col_q2_actual_m1 numeric, col_q2_actual_m2 numeric, col_q2_actual_m3 numeric, col_q2_actual_quarter numeric, col_q2_residual_after_booking numeric, col_q2_residual_after_actual numeric, col_q2_transfer_q3 numeric, col_q2_transfer_q3_delay_acts numeric, col_q2_transfer_q3_delay_procurement numeric, col_q2_transfer_q3_economy_rf numeric, col_q2_transfer_next_comment character varying, col_q2_transfer_q4 numeric, col_q2_transfer_far_comment character varying, col_q2_transfer_econ numeric, col_q2_total numeric, col_q3_target_change numeric, col_q3_base_correction numeric, col_q3_base_correction_comment character varying, col_q3_revision_inc numeric, col_q3_revision_seq numeric, col_q3_revision_comment character varying, col_q3_new_plan numeric, col_q3_adj_current numeric, col_q3_adj_ssp numeric, col_q3_adj_rf numeric, col_q3_adj_reserve numeric, col_q3_adj_comment character varying, col_q3_corrected_plan numeric, col_q3_pay_date date, col_q3_pay_amount numeric, col_q3_pay_ho numeric, col_q3_pay_rf numeric, col_q3_pay_comment character varying, col_q3_pay_act character varying, col_q3_booking numeric, col_q3_actual_m1 numeric, col_q3_actual_m2 numeric, col_q3_actual_m3 numeric, col_q3_actual_quarter numeric, col_q3_residual_after_booking numeric, col_q3_residual_after_actual numeric, col_q3_transfer_q4 numeric, col_q3_transfer_q4_delay_acts numeric, col_q3_transfer_q4_delay_procurement numeric, col_q3_transfer_q4_economy_rf numeric, col_q3_transfer_next_comment character varying, col_q3_transfer_econ numeric, col_q3_total numeric, col_q4_target_change numeric, col_q4_base_correction numeric, col_q4_base_correction_comment character varying, col_q4_revision_inc numeric, col_q4_revision_seq numeric, col_q4_revision_comment character varying, col_q4_new_plan numeric, col_q4_adj_current numeric, col_q4_adj_ssp numeric, col_q4_adj_rf numeric, col_q4_adj_reserve numeric, col_q4_adj_comment character varying, col_q4_corrected_plan numeric, col_q4_pay_date date, col_q4_pay_amount numeric, col_q4_pay_ho numeric, col_q4_pay_rf numeric, col_q4_pay_comment character varying, col_q4_pay_act character varying, col_q4_booking numeric, col_q4_actual_m1 numeric, col_q4_actual_m2 numeric, col_q4_actual_m3 numeric, col_q4_actual_spod numeric, col_q4_actual_quarter numeric, col_q4_residual_after_booking numeric, col_q4_residual_after_actual numeric, col_q4_transfer_econ numeric, col_q4_total numeric, col_fact_year numeric, _sort_path integer[]) + LANGUAGE plpgsql + STABLE +AS $function$ +#variable_conflict use_column +DECLARE + s_plan BOOL; s_seq_d BOOL; s_appr BOOL; + s_cd BOOL; s_book BOOL; + s_q1 BOOL; s_q2 BOOL; s_q3 BOOL; s_q4 BOOL; s_tot BOOL; + s_cs BOOL; s_al BOOL; s_rsv BOOL; s_col BOOL; s_ckk BOOL; + s_need_ap BOOL; -- plan/seq/reserve нужны для approved + cp1..cp4 +BEGIN + s_plan := p_sections IS NULL OR 'plan' = ANY(p_sections); + s_seq_d := p_sections IS NULL OR 'seq_dfip' = ANY(p_sections); + s_appr := p_sections IS NULL OR 'approved' = ANY(p_sections); + s_cd := p_sections IS NULL OR 'contract' = ANY(p_sections); + s_book := p_sections IS NULL OR 'booking' = ANY(p_sections); + s_q1 := p_sections IS NULL OR 'q1' = ANY(p_sections); + s_q2 := p_sections IS NULL OR 'q2' = ANY(p_sections); + s_q3 := p_sections IS NULL OR 'q3' = ANY(p_sections); + s_q4 := p_sections IS NULL OR 'q4' = ANY(p_sections); + s_tot := p_sections IS NULL OR 'totals' = ANY(p_sections); + s_cs := p_sections IS NULL OR 'contract_summary' = ANY(p_sections); + s_al := p_sections IS NULL OR 'allocation' = ANY(p_sections); + s_rsv := p_sections IS NULL OR 'reserve' = ANY(p_sections); + s_col := p_sections IS NULL OR 'collegial' = ANY(p_sections); + s_ckk := p_sections IS NULL OR 'ckk' = ANY(p_sections); + s_need_ap := s_plan OR s_appr OR s_q1 OR s_q2 OR s_q3 OR s_q4; + + RETURN QUERY + WITH + tw AS ( + SELECT t.id, t.section_code, t.item_id, t.num_group_id, t.name, + t.depth, t.path, t.parent_id, t.parent_item_id, t.desc_ids, + p.parent_item_id AS grandparent_item_id + FROM v3.mv_expense_item_tree t + LEFT JOIN v3.mv_expense_item_tree p ON p.id = t.parent_id + JOIN v3.expense_item_form_type eft ON eft.expense_item_id = t.id + JOIN v3.budget_form bf ON bf.id = p_form_id AND bf.form_type_code = eft.form_type_code + WHERE t.sheet = p_sheet + ), + page AS ( + SELECT bl.id AS lid, bl.expense_item_id AS eid, bl.name AS bname, bl.vsp_id + FROM v3.budget_line bl + JOIN v3.expense_item ei ON ei.id = bl.expense_item_id + WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet + ), + jp AS (SELECT p.* FROM v3.plan p JOIN page pg ON p.line_id = pg.lid WHERE s_need_ap), + jsd AS (SELECT s.* FROM v3.sequestration s JOIN page pg ON s.line_id = pg.lid WHERE s_need_ap AND s.actor='DFIP'), + jr AS (SELECT r.* FROM v3.reserve r JOIN page pg ON r.line_id = pg.lid WHERE s_need_ap), + jcd AS (SELECT c.* FROM v3.contract_detail c JOIN page pg ON c.line_id = pg.lid WHERE s_cd), + jck AS (SELECT c.* FROM v3.ckk c JOIN page pg ON c.line_id = pg.lid WHERE s_book OR s_ckk), + jcs AS (SELECT c.* FROM v3.contract_summary c JOIN page pg ON c.line_id = pg.lid WHERE s_cs), + jal AS (SELECT a.* FROM v3.allocation a JOIN page pg ON a.line_id = pg.lid WHERE s_al), + jcol AS (SELECT c.* FROM v3.collegial_approval c JOIN page pg ON c.line_id = pg.lid WHERE s_col), + jq1 AS (SELECT q.* FROM v3.budget_line_quarter q JOIN page pg ON q.line_id = pg.lid WHERE s_q1 AND q.quarter=1), + jq2 AS (SELECT q.* FROM v3.budget_line_quarter q JOIN page pg ON q.line_id = pg.lid WHERE s_q2 AND q.quarter=2), + jq3 AS (SELECT q.* FROM v3.budget_line_quarter q JOIN page pg ON q.line_id = pg.lid WHERE s_q3 AND q.quarter=3), + jq4 AS (SELECT q.* FROM v3.budget_line_quarter q JOIN page pg ON q.line_id = pg.lid WHERE (s_q4 OR s_tot) AND q.quarter=4), + + input_base AS ( + SELECT + pg.lid, pg.eid, pg.bname, pg.vsp_id, + v.address AS vsp_addr, + bl_just.justification AS just, bl_just.internal_order AS io, + CASE + WHEN t.depth < 3 THEN t.item_id + WHEN t.depth = 3 THEN t.parent_item_id + WHEN t.depth = 4 THEN t.grandparent_item_id + END AS sc, + t.item_id AS ic, t.num_group_id AS ng, t.name AS ename, t.path AS tree_path, + COALESCE(p.plan_q1,0) AS pq1, COALESCE(p.plan_q2,0) AS pq2, + COALESCE(p.plan_q3,0) AS pq3, COALESCE(p.plan_q4,0) AS pq4, + p.comment AS pcmt, + COALESCE(sd.adj_q1,0) AS dq1, COALESCE(sd.adj_q2,0) AS dq2, + COALESCE(sd.adj_q3,0) AS dq3, COALESCE(sd.adj_q4,0) AS dq4, + sd.justification AS djust, + COALESCE(r.amount_q1,0) AS rq1, COALESCE(r.amount_q2,0) AS rq2, + COALESCE(r.amount_q3,0) AS rq3, COALESCE(r.amount_q4,0) AS rq4, + r.justification AS rjust, + cd.counterparty AS cd_cp, cd.reference AS cd_ref, cd.addenda AS cd_add, + cd.contract_date AS cd_dt, cd.subject AS cd_subj, cd.currency AS cd_cur, + cd.ceiling_amount AS cd_ceil, + cd.expenses_q1 AS cd_q1, cd.expenses_q2 AS cd_q2, cd.expenses_q3 AS cd_q3, cd.expenses_q4 AS cd_q4, + cd.rf_schedule AS cd_rfsch, + cd.vat_rate AS cd_vat, cd.exchange_rate AS cd_xr, cd.amount_foreign AS cd_af, + cd.deadline AS cd_dl, cd.payment_scheme AS cd_sch, + cd.act AS cd_act, cd.comment AS cd_cmt, + ck.expenses_q1 AS bk1, ck.expenses_q2 AS bk2, ck.expenses_q3 AS bk3, ck.expenses_q4 AS bk4, + ck.expenses_next_year_q1 AS bn1, ck.expenses_next_year_q2 AS bn2, + ck.expenses_next_year_q3 AS bn3, ck.expenses_next_year_q4 AS bn4, + ck.ceiling_amount AS ck_ceil, ck.rf_schedule AS ck_rfsch, + ck.delivery_deadline AS ck_dd, ck.procurement_plan AS ck_pp, + ck.procurement_method AS ck_pm, ck.comment AS ck_cmt, + cs.total_amount AS cs_amt, cs.reference AS cs_ref, cs.counterparty AS cs_cp, + cs.deadline AS cs_dl, cs.comment AS cs_cmt, + cs.future_payments_y1 AS cs_y1, cs.future_payments_y2 AS cs_y2, + cs.other_ssp_amount AS cs_oss, cs.centralized_flag AS cs_cflag, + al.contract_ref AS al_ref, al.allocation_purpose AS al_purp, + col.approved_amount AS col_amt, col.protocol_reference AS col_pr, col.note AS col_note, + q1.adj_current AS q1_ac, q1.adj_ssp AS q1_as, q1.adj_rf AS q1_arf, q1.adj_reserve AS q1_arv, q1.adj_comment AS q1_acmt, + q1.payment_date AS q1_pd, q1.payment_amount AS q1_pa, + q1.payment_amount_ho AS q1_pho, q1.payment_amount_rf AS q1_prf, q1.payment_comment AS q1_pcmt, + q1.payment_act AS q1_pact, + COALESCE( + q1.booking_amount, + CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q1 + WHEN ck.expenses_q1 > 0 THEN ck.expenses_q1 + ELSE cd.expenses_q1 + END + + ) AS q1_book, + q1.actual_m1 AS q1_m1, q1.actual_m2 AS q1_m2, q1.actual_m3 AS q1_m3, + q1.transfer_to_q2 AS q1_tq2, q1.transfer_to_q3 AS q1_tq3, q1.transfer_to_q4 AS q1_tq4, + q1.transfer_to_economy AS q1_te, + q1.transfer_delay_acts AS q1_tda, q1.transfer_delay_procurement AS q1_tdp, + q1.transfer_economy_rf AS q1_terf, + q1.transfer_next_comment AS q1_tnc, q1.transfer_far_comment AS q1_tfc, + q2.target_change AS q2_tc, q2.base_plan_correction AS q2_bc, q2.base_plan_correction_comment AS q2_bcc, + q2.plan_revision_increase AS q2_rinc, q2.plan_revision_sequester AS q2_rseq, + q2.plan_revision_comment AS q2_rcmt, + q2.adj_current AS q2_ac, q2.adj_ssp AS q2_as, q2.adj_rf AS q2_arf, q2.adj_reserve AS q2_arv, + q2.adj_comment AS q2_acmt, + q2.payment_date AS q2_pd, q2.payment_amount AS q2_pa, + q2.payment_amount_ho AS q2_pho, q2.payment_amount_rf AS q2_prf, + q2.payment_comment AS q2_pcmt, q2.payment_act AS q2_pact, + COALESCE( + q2.booking_amount, + CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q2 + WHEN ck.expenses_q2 > 0 THEN ck.expenses_q2 + ELSE cd.expenses_q2 + END + + ) AS q2_book, + q2.actual_m1 AS q2_m1, q2.actual_m2 AS q2_m2, q2.actual_m3 AS q2_m3, + q2.transfer_to_q3 AS q2_tq3, q2.transfer_to_q4 AS q2_tq4, q2.transfer_to_economy AS q2_te, + q2.transfer_delay_acts AS q2_tda, q2.transfer_delay_procurement AS q2_tdp, + q2.transfer_economy_rf AS q2_terf, + q2.transfer_next_comment AS q2_tnc, q2.transfer_far_comment AS q2_tfc, + q3.target_change AS q3_tc, q3.base_plan_correction AS q3_bc, + q3.base_plan_correction_comment AS q3_bcc, + q3.plan_revision_increase AS q3_rinc, q3.plan_revision_sequester AS q3_rseq, + q3.plan_revision_comment AS q3_rcmt, + q3.adj_current AS q3_ac, q3.adj_ssp AS q3_as, q3.adj_rf AS q3_arf, q3.adj_reserve AS q3_arv, + q3.adj_comment AS q3_acmt, + q3.payment_date AS q3_pd, q3.payment_amount AS q3_pa, + q3.payment_amount_ho AS q3_pho, q3.payment_amount_rf AS q3_prf, + q3.payment_comment AS q3_pcmt, q3.payment_act AS q3_pact, + COALESCE( + q3.booking_amount, + CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q3 + WHEN ck.expenses_q3 > 0 THEN ck.expenses_q3 + ELSE cd.expenses_q3 + END + + ) AS q3_book, + q3.actual_m1 AS q3_m1, q3.actual_m2 AS q3_m2, q3.actual_m3 AS q3_m3, + q3.transfer_to_q4 AS q3_tq4, q3.transfer_to_economy AS q3_te, + q3.transfer_delay_acts AS q3_tda, q3.transfer_delay_procurement AS q3_tdp, + q3.transfer_economy_rf AS q3_terf, q3.transfer_next_comment AS q3_tnc, + q4.target_change AS q4_tc, q4.base_plan_correction AS q4_bc, + q4.base_plan_correction_comment AS q4_bcc, + q4.plan_revision_increase AS q4_rinc, q4.plan_revision_sequester AS q4_rseq, + q4.plan_revision_comment AS q4_rcmt, + q4.adj_current AS q4_ac, q4.adj_ssp AS q4_as, q4.adj_rf AS q4_arf, q4.adj_reserve AS q4_arv, + q4.adj_comment AS q4_acmt, + q4.payment_date AS q4_pd, q4.payment_amount AS q4_pa, + q4.payment_amount_ho AS q4_pho, q4.payment_amount_rf AS q4_prf, + q4.payment_comment AS q4_pcmt, q4.payment_act AS q4_pact, + COALESCE( + q4.booking_amount, + CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q4 + WHEN ck.expenses_q4 > 0 THEN ck.expenses_q4 + ELSE cd.expenses_q4 + END + + ) AS q4_book, + q4.actual_m1 AS q4_m1, q4.actual_m2 AS q4_m2, q4.actual_m3 AS q4_m3, q4.actual_spod AS q4_spod, + q4.transfer_to_economy AS q4_te, + COALESCE(p.plan_q1,0)+COALESCE(sd.adj_q1,0)+COALESCE(r.amount_q1,0) AS ap1, + COALESCE(p.plan_q2,0)+COALESCE(sd.adj_q2,0)+COALESCE(r.amount_q2,0) AS ap2, + COALESCE(p.plan_q3,0)+COALESCE(sd.adj_q3,0)+COALESCE(r.amount_q3,0) AS ap3, + COALESCE(p.plan_q4,0)+COALESCE(sd.adj_q4,0)+COALESCE(r.amount_q4,0) AS ap4 + FROM page pg + JOIN tw t ON t.id = pg.eid + LEFT JOIN v3.vsp v ON v.id = pg.vsp_id + LEFT JOIN v3.budget_line bl_just ON bl_just.id = pg.lid + LEFT JOIN jp p ON p.line_id = pg.lid + LEFT JOIN jsd sd ON sd.line_id = pg.lid + LEFT JOIN jr r ON r.line_id = pg.lid + LEFT JOIN jcd cd ON cd.line_id = pg.lid + LEFT JOIN jck ck ON ck.line_id = pg.lid + LEFT JOIN jcs cs ON cs.line_id = pg.lid + LEFT JOIN jal al ON al.line_id = pg.lid + LEFT JOIN jcol col ON col.line_id = pg.lid + LEFT JOIN jq1 q1 ON q1.line_id = pg.lid + LEFT JOIN jq2 q2 ON q2.line_id = pg.lid + LEFT JOIN jq3 q3 ON q3.line_id = pg.lid + LEFT JOIN jq4 q4 ON q4.line_id = pg.lid + ), + enriched AS ( + SELECT b.*, + b.ap1 + COALESCE(b.q1_ac,0)+COALESCE(b.q1_as,0)+COALESCE(b.q1_arf,0)+COALESCE(b.q1_arv,0) + - COALESCE(b.q1_tq2,0) AS cp1, + COALESCE(b.q1_m1,0)+COALESCE(b.q1_m2,0)+COALESCE(b.q1_m3,0) AS aq1, + b.ap2 + COALESCE(b.q1_tq2,0) + COALESCE(b.q2_rinc,0)+COALESCE(b.q2_rseq,0) + COALESCE(b.q2_bc,0)+COALESCE(b.q2_tc,0) AS np2, + COALESCE(b.q2_m1,0)+COALESCE(b.q2_m2,0)+COALESCE(b.q2_m3,0) AS aq2, + b.ap3 + COALESCE(b.q1_tq3,0)+COALESCE(b.q2_tq3,0) + COALESCE(b.q3_rinc,0)+COALESCE(b.q3_rseq,0) + COALESCE(b.q3_bc,0)+COALESCE(b.q3_tc,0) AS np3, + COALESCE(b.q3_m1,0)+COALESCE(b.q3_m2,0)+COALESCE(b.q3_m3,0) AS aq3, + b.ap4 + COALESCE(b.q1_tq4,0)+COALESCE(b.q2_tq4,0)+COALESCE(b.q3_tq4,0) + COALESCE(b.q4_rinc,0)+COALESCE(b.q4_rseq,0) + COALESCE(b.q4_bc,0)+COALESCE(b.q4_tc,0) AS np4, + COALESCE(b.q4_m1,0)+COALESCE(b.q4_m2,0)+COALESCE(b.q4_m3,0)+COALESCE(b.q4_spod,0) AS aq4 + FROM input_base b + ), + final_input AS ( + SELECT e.*, + e.np2 + COALESCE(e.q2_ac,0)+COALESCE(e.q2_as,0)+COALESCE(e.q2_arf,0)+COALESCE(e.q2_arv,0) + - COALESCE(e.q2_tq3,0) AS cp2, + e.np3 + COALESCE(e.q3_ac,0)+COALESCE(e.q3_as,0)+COALESCE(e.q3_arf,0)+COALESCE(e.q3_arv,0) + - COALESCE(e.q3_tq4,0) AS cp3, + e.np4 + COALESCE(e.q4_ac,0)+COALESCE(e.q4_as,0)+COALESCE(e.q4_arf,0)+COALESCE(e.q4_arv,0) AS cp4 + FROM enriched e + ), + + agg AS ( + SELECT bl.expense_item_id AS eid, + SUM(p.plan_q1) AS sp1, SUM(p.plan_q2) AS sp2, SUM(p.plan_q3) AS sp3, SUM(p.plan_q4) AS sp4, + SUM(sd.adj_q1) AS sd1, SUM(sd.adj_q2) AS sd2, SUM(sd.adj_q3) AS sd3, SUM(sd.adj_q4) AS sd4, + SUM(r.amount_q1) AS sr1, SUM(r.amount_q2) AS sr2, SUM(r.amount_q3) AS sr3, SUM(r.amount_q4) AS sr4, + SUM(ck.expenses_q1) AS bk1, SUM(ck.expenses_q2) AS bk2, SUM(ck.expenses_q3) AS bk3, SUM(ck.expenses_q4) AS bk4, + SUM(cd.expenses_q1) AS bkc1, SUM(cd.expenses_q2) AS bkc2, SUM(cd.expenses_q3) AS bkc3, SUM(cd.expenses_q4) AS bkc4, + SUM(ck.expenses_next_year_q1) AS bn1, SUM(ck.expenses_next_year_q2) AS bn2, + SUM(ck.expenses_next_year_q3) AS bn3, SUM(ck.expenses_next_year_q4) AS bn4 + FROM v3.budget_line bl + JOIN v3.expense_item ei ON ei.id = bl.expense_item_id + LEFT JOIN v3.plan p ON p.line_id = bl.id AND s_need_ap + LEFT JOIN v3.sequestration sd ON sd.line_id = bl.id AND sd.actor='DFIP' AND s_need_ap + LEFT JOIN v3.reserve r ON r.line_id = bl.id AND s_need_ap + LEFT JOIN v3.ckk ck ON ck.line_id = bl.id AND s_book + LEFT JOIN v3.contract_detail cd ON cd.line_id = bl.id AND s_cd + WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet + AND (s_need_ap OR s_book OR s_cd) + GROUP BY bl.expense_item_id + ), + aq_q AS ( + SELECT bl.expense_item_id AS eid, qrtrs.qrtr as quarter, + SUM(q.adj_current) AS ac, SUM(q.adj_ssp) AS as_v, SUM(q.adj_rf) AS arf, SUM(q.adj_reserve) AS arv, + SUM(q.payment_amount) AS pa, + SUM(q.payment_amount_ho) AS pho, SUM(q.payment_amount_rf) AS prf, + SUM( + COALESCE( + q.booking_amount, + CASE + WHEN qrtrs.qrtr = 1 + THEN CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q1 + WHEN ck.expenses_q1 > 0 THEN ck.expenses_q1 + ELSE cd.expenses_q1 + END + WHEN qrtrs.qrtr = 2 THEN CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q2 + WHEN ck.expenses_q2 > 0 THEN ck.expenses_q2 + ELSE cd.expenses_q2 + END + WHEN qrtrs.qrtr = 3 THEN CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q3 + WHEN ck.expenses_q3 > 0 THEN ck.expenses_q3 + ELSE cd.expenses_q3 + END + WHEN qrtrs.qrtr = 4 THEN CASE + WHEN p_sheet = 'AHR' THEN ck.expenses_q4 + WHEN ck.expenses_q4 > 0 THEN ck.expenses_q4 + ELSE cd.expenses_q4 + END + END + + )) AS book, + SUM(q.actual_m1) AS m1, SUM(q.actual_m2) AS m2, SUM(q.actual_m3) AS m3, + SUM(q.actual_spod) AS spod, + SUM(q.transfer_to_q2) AS tq2, SUM(q.transfer_to_q3) AS tq3, SUM(q.transfer_to_q4) AS tq4, + SUM(q.transfer_to_economy) AS te, + SUM(COALESCE(q.plan_revision_increase,0) + + COALESCE(q.plan_revision_sequester,0) + + COALESCE(q.target_change,0) + + COALESCE(q.base_plan_correction,0)) AS rev + FROM v3.budget_line bl + CROSS JOIN (VALUES (1), (2), (3), (4)) AS qrtrs(qrtr) + JOIN v3.expense_item ei ON ei.id = bl.expense_item_id + JOIN v3.budget_line_quarter q ON q.line_id = bl.id AND q.quarter = qrtrs.qrtr + LEFT JOIN v3.ckk ck ON bl.id = ck.line_id + LEFT JOIN v3.contract_detail cd ON bl.id = cd.line_id + WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet + GROUP BY bl.expense_item_id, qrtrs.qrtr + ), + tw_agg AS ( + SELECT tw.id, + SUM(a.sp1) AS sp1, SUM(a.sp2) AS sp2, SUM(a.sp3) AS sp3, SUM(a.sp4) AS sp4, + SUM(a.sd1) AS sd1, SUM(a.sd2) AS sd2, SUM(a.sd3) AS sd3, SUM(a.sd4) AS sd4, + SUM(a.sr1) AS sr1, SUM(a.sr2) AS sr2, SUM(a.sr3) AS sr3, SUM(a.sr4) AS sr4, + SUM(a.bk1) AS bk1, SUM(a.bk2) AS bk2, SUM(a.bk3) AS bk3, SUM(a.bk4) AS bk4, + SUM(a.bkc1) AS bkc1, SUM(a.bkc2) AS bkc2, SUM(a.bkc3) AS bkc3, SUM(a.bkc4) AS bkc4, + SUM(a.bn1) AS bn1, SUM(a.bn2) AS bn2, SUM(a.bn3) AS bn3, SUM(a.bn4) AS bn4 + FROM tw LEFT JOIN agg a ON a.eid = ANY(tw.desc_ids) + GROUP BY tw.id + ), + tw_aq1 AS ( + SELECT tw.id, SUM(b.ac) AS ac, SUM(b.as_v) AS as_v, SUM(b.arf) AS arf, SUM(b.arv) AS arv, + SUM(b.pa) AS pa, SUM(b.book) AS book, + SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3, + SUM(b.tq2) AS tq2, SUM(b.tq3) AS tq3, SUM(b.tq4) AS tq4, SUM(b.te) AS te + FROM tw LEFT JOIN aq_q b ON b.eid = ANY(tw.desc_ids) AND b.quarter=1 GROUP BY tw.id + ), + tw_aq2 AS ( + SELECT tw.id, SUM(b.ac) AS ac, SUM(b.as_v) AS as_v, SUM(b.arf) AS arf, SUM(b.arv) AS arv, + SUM(b.pa) AS pa, SUM(b.book) AS book, + SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3, + SUM(b.tq3) AS tq3, SUM(b.tq4) AS tq4, SUM(b.te) AS te, SUM(b.rev) AS rev + FROM tw LEFT JOIN aq_q b ON b.eid = ANY(tw.desc_ids) AND b.quarter=2 GROUP BY tw.id + ), + tw_aq3 AS ( + SELECT tw.id, SUM(b.ac) AS ac, SUM(b.as_v) AS as_v, SUM(b.arf) AS arf, SUM(b.arv) AS arv, + SUM(b.pa) AS pa, SUM(b.book) AS book, + SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3, + SUM(b.tq4) AS tq4, SUM(b.te) AS te, SUM(b.rev) AS rev + FROM tw LEFT JOIN aq_q b ON b.eid = ANY(tw.desc_ids) AND b.quarter=3 GROUP BY tw.id + ), + tw_aq4 AS ( + SELECT tw.id, SUM(b.ac) AS ac, SUM(b.as_v) AS as_v, SUM(b.arf) AS arf, SUM(b.arv) AS arv, + SUM(b.pa) AS pa, SUM(b.book) AS book, + SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3, SUM(b.spod) AS spod, + SUM(b.te) AS te, SUM(b.rev) AS rev + FROM tw LEFT JOIN aq_q b ON b.eid = ANY(tw.desc_ids) AND b.quarter=4 GROUP BY tw.id + ) + + SELECT * FROM ( + SELECT + 'INPUT'::VARCHAR, array_length(f.tree_path, 1)::INT, f.lid::INT, + f.sc, f.ic, f.ng, COALESCE(f.bname, f.ename), + f.just, f.io, f.vsp_id, f.vsp_addr, + CASE WHEN s_plan THEN f.pq1 END, CASE WHEN s_plan THEN f.pq2 END, + CASE WHEN s_plan THEN f.pq3 END, CASE WHEN s_plan THEN f.pq4 END, + CASE WHEN s_plan THEN f.pq1+f.pq2+f.pq3+f.pq4 END, CASE WHEN s_plan THEN f.pcmt END, + CASE WHEN s_seq_d THEN f.dq1 END, CASE WHEN s_seq_d THEN f.dq2 END, + CASE WHEN s_seq_d THEN f.dq3 END, CASE WHEN s_seq_d THEN f.dq4 END, + CASE WHEN s_seq_d THEN f.dq1+f.dq2+f.dq3+f.dq4 END, CASE WHEN s_seq_d THEN f.djust END, + CASE WHEN s_appr THEN f.ap1 END, CASE WHEN s_appr THEN f.ap2 END, + CASE WHEN s_appr THEN f.ap3 END, CASE WHEN s_appr THEN f.ap4 END, + CASE WHEN s_appr THEN f.ap1+f.ap2+f.ap3+f.ap4 END, + CASE WHEN s_cs THEN f.cs_amt END, CASE WHEN s_cs THEN f.cs_ref END, + CASE WHEN s_cs THEN f.cs_cp END, CASE WHEN s_cs THEN f.cs_dl END, + CASE WHEN s_cs THEN f.cs_cmt END, + CASE WHEN s_cs THEN f.cs_y1 END, CASE WHEN s_cs THEN f.cs_y2 END, + CASE WHEN s_cs THEN f.cs_oss END, CASE WHEN s_cs THEN f.cs_cflag END, + CASE WHEN s_al THEN f.al_ref END, CASE WHEN s_al THEN f.al_purp END, + CASE WHEN s_rsv THEN f.rq1 END, CASE WHEN s_rsv THEN f.rq2 END, + CASE WHEN s_rsv THEN f.rq3 END, CASE WHEN s_rsv THEN f.rq4 END, + CASE WHEN s_rsv THEN f.rq1+f.rq2+f.rq3+f.rq4 END, CASE WHEN s_rsv THEN f.rjust END, + CASE WHEN s_col THEN f.col_amt END, CASE WHEN s_col THEN f.col_pr END, + CASE WHEN s_col THEN f.col_note END, + CASE WHEN s_ckk THEN f.ck_ceil END, + CASE WHEN s_ckk THEN f.bk1 END, CASE WHEN s_ckk THEN f.bk2 END, + CASE WHEN s_ckk THEN f.bk3 END, CASE WHEN s_ckk THEN f.bk4 END, + CASE WHEN s_ckk THEN f.ck_rfsch END, CASE WHEN s_ckk THEN f.ck_dd END, + CASE WHEN s_ckk THEN f.ck_pp END, CASE WHEN s_ckk THEN f.ck_pm END, + CASE WHEN s_ckk THEN f.ck_cmt END, + CASE WHEN s_cd THEN f.cd_cp END, CASE WHEN s_cd THEN f.cd_ref END, + CASE WHEN s_cd THEN f.cd_add END, + CASE WHEN s_cd THEN f.cd_dt END, CASE WHEN s_cd THEN f.cd_subj END, + CASE WHEN s_cd THEN f.cd_cur END, CASE WHEN s_cd THEN f.cd_ceil END, + CASE WHEN s_cd THEN f.cd_q1 END, CASE WHEN s_cd THEN f.cd_q2 END, + CASE WHEN s_cd THEN f.cd_q3 END, CASE WHEN s_cd THEN f.cd_q4 END, + CASE WHEN s_cd THEN f.cd_rfsch END, + CASE WHEN s_cd THEN f.cd_vat END, + CASE WHEN s_cd THEN f.cd_xr END, CASE WHEN s_cd THEN f.cd_af END, + CASE WHEN s_cd THEN f.cd_dl END, + CASE WHEN s_cd THEN f.cd_sch END, CASE WHEN s_cd THEN f.cd_act END, + CASE WHEN s_cd THEN f.cd_cmt END, + CASE WHEN s_book THEN f.bk1 END, CASE WHEN s_book THEN f.bk2 END, + CASE WHEN s_book THEN f.bk3 END, CASE WHEN s_book THEN f.bk4 END, + CASE WHEN s_book THEN f.bn1 END, CASE WHEN s_book THEN f.bn2 END, + CASE WHEN s_book THEN f.bn3 END, CASE WHEN s_book THEN f.bn4 END, + CASE WHEN s_q1 THEN f.q1_ac END, CASE WHEN s_q1 THEN f.q1_as END, + CASE WHEN s_q1 THEN f.q1_arf END, + CASE WHEN s_q1 THEN f.q1_arv END, + CASE WHEN s_q1 THEN f.q1_acmt END, CASE WHEN s_q1 THEN f.cp1 END, + CASE WHEN s_q1 THEN f.q1_pd END, CASE WHEN s_q1 THEN f.q1_pa END, + CASE WHEN s_q1 THEN f.q1_pho END, CASE WHEN s_q1 THEN f.q1_prf END, CASE WHEN s_q1 THEN f.q1_pcmt END, + CASE WHEN s_q1 THEN f.q1_pact END, CASE WHEN s_q1 THEN f.q1_book END, + CASE WHEN s_q1 THEN f.q1_m1 END, CASE WHEN s_q1 THEN f.q1_m2 END, CASE WHEN s_q1 THEN f.q1_m3 END, + CASE WHEN s_q1 THEN f.aq1 END, + CASE WHEN s_q1 THEN f.cp1 - COALESCE(f.q1_book,0) END, -- residual_after_booking + CASE WHEN s_q1 THEN f.cp1 - f.aq1 END, -- residual_after_actual + CASE WHEN s_q1 THEN f.q1_tq2 END, + CASE WHEN s_q1 THEN f.q1_tda END, CASE WHEN s_q1 THEN f.q1_tdp END, CASE WHEN s_q1 THEN f.q1_terf END, + CASE WHEN s_q1 THEN f.q1_tnc END, + CASE WHEN s_q1 THEN f.q1_tq3 END, CASE WHEN s_q1 THEN f.q1_tq4 END, + CASE WHEN s_q1 THEN f.q1_tfc END, + CASE WHEN s_q1 THEN f.q1_te END, + CASE WHEN s_q1 THEN COALESCE(f.q1_tq2,0)+COALESCE(f.q1_tq3,0)+COALESCE(f.q1_tq4,0)+COALESCE(f.q1_te,0) END, + CASE WHEN s_q2 THEN f.q2_tc END, CASE WHEN s_q2 THEN f.q2_bc END, CASE WHEN s_q2 THEN f.q2_bcc END, + CASE WHEN s_q2 THEN f.q2_rinc END, CASE WHEN s_q2 THEN f.q2_rseq END, + CASE WHEN s_q2 THEN f.q2_rcmt END, + CASE WHEN s_q2 THEN f.np2 END, + CASE WHEN s_q2 THEN f.q2_ac END, CASE WHEN s_q2 THEN f.q2_as END, + CASE WHEN s_q2 THEN f.q2_arf END, + CASE WHEN s_q2 THEN f.q2_arv END, + CASE WHEN s_q2 THEN f.q2_acmt END, + CASE WHEN s_q2 THEN f.cp2 END, + CASE WHEN s_q2 THEN f.q2_pd END, CASE WHEN s_q2 THEN f.q2_pa END, + CASE WHEN s_q2 THEN f.q2_pho END, CASE WHEN s_q2 THEN f.q2_prf END, + CASE WHEN s_q2 THEN f.q2_pcmt END, CASE WHEN s_q2 THEN f.q2_pact END, + CASE WHEN s_q2 THEN f.q2_book END, + CASE WHEN s_q2 THEN f.q2_m1 END, CASE WHEN s_q2 THEN f.q2_m2 END, CASE WHEN s_q2 THEN f.q2_m3 END, + CASE WHEN s_q2 THEN f.aq2 END, + CASE WHEN s_q2 THEN f.cp2 - COALESCE(f.q2_book,0) END, + CASE WHEN s_q2 THEN f.cp2 - f.aq2 END, + CASE WHEN s_q2 THEN f.q2_tq3 END, + CASE WHEN s_q2 THEN f.q2_tda END, CASE WHEN s_q2 THEN f.q2_tdp END, CASE WHEN s_q2 THEN f.q2_terf END, + CASE WHEN s_q2 THEN f.q2_tnc END, + CASE WHEN s_q2 THEN f.q2_tq4 END, + CASE WHEN s_q2 THEN f.q2_tfc END, + CASE WHEN s_q2 THEN f.q2_te END, + CASE WHEN s_q2 THEN COALESCE(f.q2_tq3,0)+COALESCE(f.q2_tq4,0)+COALESCE(f.q2_te,0) END, + CASE WHEN s_q3 THEN f.q3_tc END, CASE WHEN s_q3 THEN f.q3_bc END, + CASE WHEN s_q3 THEN f.q3_bcc END, + CASE WHEN s_q3 THEN f.q3_rinc END, CASE WHEN s_q3 THEN f.q3_rseq END, + CASE WHEN s_q3 THEN f.q3_rcmt END, + CASE WHEN s_q3 THEN f.np3 END, + CASE WHEN s_q3 THEN f.q3_ac END, CASE WHEN s_q3 THEN f.q3_as END, + CASE WHEN s_q3 THEN f.q3_arf END, + CASE WHEN s_q3 THEN f.q3_arv END, + CASE WHEN s_q3 THEN f.q3_acmt END, + CASE WHEN s_q3 THEN f.cp3 END, + CASE WHEN s_q3 THEN f.q3_pd END, CASE WHEN s_q3 THEN f.q3_pa END, + CASE WHEN s_q3 THEN f.q3_pho END, CASE WHEN s_q3 THEN f.q3_prf END, + CASE WHEN s_q3 THEN f.q3_pcmt END, CASE WHEN s_q3 THEN f.q3_pact END, + CASE WHEN s_q3 THEN f.q3_book END, + CASE WHEN s_q3 THEN f.q3_m1 END, CASE WHEN s_q3 THEN f.q3_m2 END, CASE WHEN s_q3 THEN f.q3_m3 END, + CASE WHEN s_q3 THEN f.aq3 END, + CASE WHEN s_q3 THEN f.cp3 - COALESCE(f.q3_book,0) END, + CASE WHEN s_q3 THEN f.cp3 - f.aq3 END, + CASE WHEN s_q3 THEN f.q3_tq4 END, + CASE WHEN s_q3 THEN f.q3_tda END, CASE WHEN s_q3 THEN f.q3_tdp END, CASE WHEN s_q3 THEN f.q3_terf END, + CASE WHEN s_q3 THEN f.q3_tnc END, + CASE WHEN s_q3 THEN f.q3_te END, + CASE WHEN s_q3 THEN COALESCE(f.q3_tq4,0)+COALESCE(f.q3_te,0) END, + CASE WHEN s_q4 THEN f.q4_tc END, CASE WHEN s_q4 THEN f.q4_bc END, + CASE WHEN s_q4 THEN f.q4_bcc END, + CASE WHEN s_q4 THEN f.q4_rinc END, CASE WHEN s_q4 THEN f.q4_rseq END, + CASE WHEN s_q4 THEN f.q4_rcmt END, + CASE WHEN s_q4 THEN f.np4 END, + CASE WHEN s_q4 THEN f.q4_ac END, CASE WHEN s_q4 THEN f.q4_as END, + CASE WHEN s_q4 THEN f.q4_arf END, + CASE WHEN s_q4 THEN f.q4_arv END, + CASE WHEN s_q4 THEN f.q4_acmt END, + CASE WHEN s_q4 THEN f.cp4 END, + CASE WHEN s_q4 THEN f.q4_pd END, CASE WHEN s_q4 THEN f.q4_pa END, + CASE WHEN s_q4 THEN f.q4_pho END, CASE WHEN s_q4 THEN f.q4_prf END, + CASE WHEN s_q4 THEN f.q4_pcmt END, CASE WHEN s_q4 THEN f.q4_pact END, + CASE WHEN s_q4 THEN f.q4_book END, + CASE WHEN s_q4 THEN f.q4_m1 END, CASE WHEN s_q4 THEN f.q4_m2 END, CASE WHEN s_q4 THEN f.q4_m3 END, + CASE WHEN s_q4 THEN f.q4_spod END, + CASE WHEN s_q4 THEN f.aq4 END, + CASE WHEN s_q4 THEN f.cp4 - COALESCE(f.q4_book,0) END, + CASE WHEN s_q4 THEN f.cp4 - f.aq4 END, + CASE WHEN s_q4 THEN f.q4_te END, + CASE WHEN s_q4 THEN COALESCE(f.q4_te,0) END, + CASE WHEN s_tot THEN f.aq1+f.aq2+f.aq3+f.aq4 END, -- fact_year + + f.tree_path || ARRAY[f.lid::INT] AS _sort + FROM final_input f + + UNION ALL + + SELECT + CASE t.depth WHEN 0 THEN 'ROOT' WHEN 1 THEN 'GROUP' WHEN 2 THEN 'ITEM' WHEN 3 THEN 'SUB_ITEM' WHEN 4 THEN 'SUB_SUB_ITEM' END::VARCHAR, + t.depth, NULL::INT, + CASE + WHEN t.depth < 3 THEN t.item_id + WHEN t.depth = 3 THEN t.parent_item_id + WHEN t.depth = 4 THEN t.grandparent_item_id + END, + CASE WHEN t.depth >= 3 THEN t.item_id ELSE NULL END, + t.num_group_id, t.name, + NULL::VARCHAR, NULL::VARCHAR, -- justification, internal_order + NULL::INT, NULL::VARCHAR, + CASE WHEN s_plan THEN ta.sp1 END, CASE WHEN s_plan THEN ta.sp2 END, + CASE WHEN s_plan THEN ta.sp3 END, CASE WHEN s_plan THEN ta.sp4 END, + CASE WHEN s_plan THEN COALESCE(ta.sp1,0)+COALESCE(ta.sp2,0)+COALESCE(ta.sp3,0)+COALESCE(ta.sp4,0) END, + NULL::VARCHAR, + CASE WHEN s_seq_d THEN ta.sd1 END, CASE WHEN s_seq_d THEN ta.sd2 END, + CASE WHEN s_seq_d THEN ta.sd3 END, CASE WHEN s_seq_d THEN ta.sd4 END, + CASE WHEN s_seq_d THEN COALESCE(ta.sd1,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sd4,0) END, + NULL::VARCHAR, + CASE WHEN s_appr THEN COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sr1,0) END, + CASE WHEN s_appr THEN COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sr2,0) END, + CASE WHEN s_appr THEN COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sr3,0) END, + CASE WHEN s_appr THEN COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sr4,0) END, + CASE WHEN s_appr THEN COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sr1,0) + +COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sr2,0) + +COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sr3,0) + +COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sr4,0) END, + NULL::NUMERIC, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, + NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, + NULL::VARCHAR, NULL::VARCHAR, + CASE WHEN s_rsv THEN ta.sr1 END, CASE WHEN s_rsv THEN ta.sr2 END, + CASE WHEN s_rsv THEN ta.sr3 END, CASE WHEN s_rsv THEN ta.sr4 END, + CASE WHEN s_rsv THEN COALESCE(ta.sr1,0)+COALESCE(ta.sr2,0)+COALESCE(ta.sr3,0)+COALESCE(ta.sr4,0) END, + NULL::VARCHAR, + NULL::NUMERIC, NULL::VARCHAR, NULL::VARCHAR, + NULL::NUMERIC, + CASE WHEN s_ckk THEN ta.bk1 END, CASE WHEN s_ckk THEN ta.bk2 END, + CASE WHEN s_ckk THEN ta.bk3 END, CASE WHEN s_ckk THEN ta.bk4 END, + NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, + NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, + NULL::DATE, NULL::VARCHAR, + NULL::VARCHAR, NULL::NUMERIC, + NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, + NULL::VARCHAR, + NULL::VARCHAR, NULL::NUMERIC, NULL::NUMERIC, + NULL::VARCHAR, + NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, + CASE WHEN s_book THEN ta.bk1 END, CASE WHEN s_book THEN ta.bk2 END, + CASE WHEN s_book THEN ta.bk3 END, CASE WHEN s_book THEN ta.bk4 END, + CASE WHEN s_book THEN ta.bn1 END, CASE WHEN s_book THEN ta.bn2 END, + CASE WHEN s_book THEN ta.bn3 END, CASE WHEN s_book THEN ta.bn4 END, + CASE WHEN s_q1 THEN tb1.ac END, CASE WHEN s_q1 THEN tb1.as_v END, + CASE WHEN s_q1 THEN tb1.arf END, + CASE WHEN s_q1 THEN tb1.arv END, + NULL::VARCHAR, + CASE WHEN s_q1 THEN + COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sr1,0) + +COALESCE(tb1.ac,0)+COALESCE(tb1.as_v,0)+COALESCE(tb1.arf,0)+COALESCE(tb1.arv,0) + -COALESCE(tb1.tq2,0) + END, + NULL::DATE, + CASE WHEN s_q1 THEN tb1.pa END, + NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, + NULL::VARCHAR, -- pay_act + CASE WHEN s_q1 THEN tb1.book END, -- booking агрегат + CASE WHEN s_q1 THEN tb1.m1 END, CASE WHEN s_q1 THEN tb1.m2 END, CASE WHEN s_q1 THEN tb1.m3 END, + CASE WHEN s_q1 THEN COALESCE(tb1.m1,0)+COALESCE(tb1.m2,0)+COALESCE(tb1.m3,0) END, + CASE WHEN s_q1 THEN + (COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sr1,0) + +COALESCE(tb1.ac,0)+COALESCE(tb1.as_v,0)+COALESCE(tb1.arf,0)+COALESCE(tb1.arv,0) + -COALESCE(tb1.tq2,0)) + - COALESCE(tb1.book,0) + END, + CASE WHEN s_q1 THEN + (COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sr1,0) + +COALESCE(tb1.ac,0)+COALESCE(tb1.as_v,0)+COALESCE(tb1.arf,0)+COALESCE(tb1.arv,0) + -COALESCE(tb1.tq2,0)) + - (COALESCE(tb1.m1,0)+COALESCE(tb1.m2,0)+COALESCE(tb1.m3,0)) + END, + CASE WHEN s_q1 THEN tb1.tq2 END, + NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, -- tq2 reasons + next comment + CASE WHEN s_q1 THEN tb1.tq3 END, CASE WHEN s_q1 THEN tb1.tq4 END, + NULL::VARCHAR, -- far comment + CASE WHEN s_q1 THEN tb1.te END, + CASE WHEN s_q1 THEN + COALESCE(tb1.tq2,0)+COALESCE(tb1.tq3,0)+COALESCE(tb1.tq4,0)+COALESCE(tb1.te,0) + END, + NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, -- target_change/base_correction + NULL::NUMERIC, NULL::NUMERIC, -- revision_inc/revision_seq + NULL::VARCHAR, -- revision_comment + CASE WHEN s_q2 THEN + COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sr2,0)+COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0) + END, + CASE WHEN s_q2 THEN tb2.ac END, CASE WHEN s_q2 THEN tb2.as_v END, + CASE WHEN s_q2 THEN tb2.arf END, + CASE WHEN s_q2 THEN tb2.arv END, + NULL::VARCHAR, -- adj_comment + CASE WHEN s_q2 THEN + COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sr2,0) + +COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0) + +COALESCE(tb2.ac,0)+COALESCE(tb2.as_v,0)+COALESCE(tb2.arf,0)+COALESCE(tb2.arv,0) + -COALESCE(tb2.tq3,0) + END, + NULL::DATE, + CASE WHEN s_q2 THEN tb2.pa END, + NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, -- pay_ho/pay_rf/pay_comment + NULL::VARCHAR, -- pay_act + CASE WHEN s_q2 THEN tb2.book END, + CASE WHEN s_q2 THEN tb2.m1 END, CASE WHEN s_q2 THEN tb2.m2 END, CASE WHEN s_q2 THEN tb2.m3 END, + CASE WHEN s_q2 THEN COALESCE(tb2.m1,0)+COALESCE(tb2.m2,0)+COALESCE(tb2.m3,0) END, + CASE WHEN s_q2 THEN + (COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sr2,0) + +COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0) + +COALESCE(tb2.ac,0)+COALESCE(tb2.as_v,0)+COALESCE(tb2.arf,0)+COALESCE(tb2.arv,0) + -COALESCE(tb2.tq3,0)) + - COALESCE(tb2.book,0) + END, + CASE WHEN s_q2 THEN + (COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sr2,0) + +COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0) + +COALESCE(tb2.ac,0)+COALESCE(tb2.as_v,0)+COALESCE(tb2.arf,0)+COALESCE(tb2.arv,0) + -COALESCE(tb2.tq3,0)) + - (COALESCE(tb2.m1,0)+COALESCE(tb2.m2,0)+COALESCE(tb2.m3,0)) + END, + CASE WHEN s_q2 THEN tb2.tq3 END, + NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, -- tq3 reasons + next comment + CASE WHEN s_q2 THEN tb2.tq4 END, + NULL::VARCHAR, -- far comment + CASE WHEN s_q2 THEN tb2.te END, + CASE WHEN s_q2 THEN + COALESCE(tb2.tq3,0)+COALESCE(tb2.tq4,0)+COALESCE(tb2.te,0) + END, + NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, -- target_change/base_correction/comment + NULL::NUMERIC, NULL::NUMERIC, -- revision_inc/revision_seq + NULL::VARCHAR, + CASE WHEN s_q3 THEN + COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sr3,0)+COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0) + END, + CASE WHEN s_q3 THEN tb3.ac END, CASE WHEN s_q3 THEN tb3.as_v END, + CASE WHEN s_q3 THEN tb3.arf END, + CASE WHEN s_q3 THEN tb3.arv END, + NULL::VARCHAR, + CASE WHEN s_q3 THEN + COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sr3,0) + +COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0) + +COALESCE(tb3.ac,0)+COALESCE(tb3.as_v,0)+COALESCE(tb3.arf,0)+COALESCE(tb3.arv,0) + -COALESCE(tb3.tq4,0) + END, + NULL::DATE, + CASE WHEN s_q3 THEN tb3.pa END, + NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, + NULL::VARCHAR, + CASE WHEN s_q3 THEN tb3.book END, + CASE WHEN s_q3 THEN tb3.m1 END, CASE WHEN s_q3 THEN tb3.m2 END, CASE WHEN s_q3 THEN tb3.m3 END, + CASE WHEN s_q3 THEN COALESCE(tb3.m1,0)+COALESCE(tb3.m2,0)+COALESCE(tb3.m3,0) END, + CASE WHEN s_q3 THEN + (COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sr3,0) + +COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0) + +COALESCE(tb3.ac,0)+COALESCE(tb3.as_v,0)+COALESCE(tb3.arf,0)+COALESCE(tb3.arv,0) + -COALESCE(tb3.tq4,0)) + - COALESCE(tb3.book,0) + END, + CASE WHEN s_q3 THEN + (COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sr3,0) + +COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0) + +COALESCE(tb3.ac,0)+COALESCE(tb3.as_v,0)+COALESCE(tb3.arf,0)+COALESCE(tb3.arv,0) + -COALESCE(tb3.tq4,0)) + - (COALESCE(tb3.m1,0)+COALESCE(tb3.m2,0)+COALESCE(tb3.m3,0)) + END, + CASE WHEN s_q3 THEN tb3.tq4 END, + NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, + CASE WHEN s_q3 THEN tb3.te END, + CASE WHEN s_q3 THEN + COALESCE(tb3.tq4,0)+COALESCE(tb3.te,0) + END, + NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, -- target_change/base_correction/comment + NULL::NUMERIC, NULL::NUMERIC, -- revision_inc/revision_seq + NULL::VARCHAR, + CASE WHEN s_q4 THEN + COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sr4,0)+COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0) + END, + CASE WHEN s_q4 THEN tb4.ac END, CASE WHEN s_q4 THEN tb4.as_v END, + CASE WHEN s_q4 THEN tb4.arf END, + CASE WHEN s_q4 THEN tb4.arv END, + NULL::VARCHAR, + CASE WHEN s_q4 THEN + COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sr4,0) + +COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0) + +COALESCE(tb4.ac,0)+COALESCE(tb4.as_v,0)+COALESCE(tb4.arf,0)+COALESCE(tb4.arv,0) + END, + NULL::DATE, + CASE WHEN s_q4 THEN tb4.pa END, + NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, + NULL::VARCHAR, + CASE WHEN s_q4 THEN tb4.book END, + CASE WHEN s_q4 THEN tb4.m1 END, CASE WHEN s_q4 THEN tb4.m2 END, CASE WHEN s_q4 THEN tb4.m3 END, + CASE WHEN s_q4 THEN tb4.spod END, + CASE WHEN s_q4 THEN COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0) END, + CASE WHEN s_q4 THEN + (COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sr4,0) + +COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0) + +COALESCE(tb4.ac,0)+COALESCE(tb4.as_v,0)+COALESCE(tb4.arf,0)+COALESCE(tb4.arv,0)) + - COALESCE(tb4.book,0) + END, + CASE WHEN s_q4 THEN + (COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sr4,0) + +COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0) + +COALESCE(tb4.ac,0)+COALESCE(tb4.as_v,0)+COALESCE(tb4.arf,0)+COALESCE(tb4.arv,0)) + - (COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0)) + END, + CASE WHEN s_q4 THEN tb4.te END, + CASE WHEN s_q4 THEN COALESCE(tb4.te,0) END, + CASE WHEN s_tot THEN COALESCE(tb1.m1,0)+COALESCE(tb1.m2,0)+COALESCE(tb1.m3,0) + +COALESCE(tb2.m1,0)+COALESCE(tb2.m2,0)+COALESCE(tb2.m3,0) + +COALESCE(tb3.m1,0)+COALESCE(tb3.m2,0)+COALESCE(tb3.m3,0) + +COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0) END, -- fact_year + + t.path AS _sort + FROM tw t + LEFT JOIN tw_agg ta ON ta.id = t.id + LEFT JOIN tw_aq1 tb1 ON tb1.id = t.id + LEFT JOIN tw_aq2 tb2 ON tb2.id = t.id + LEFT JOIN tw_aq3 tb3 ON tb3.id = t.id + LEFT JOIN tw_aq4 tb4 ON tb4.id = t.id + ) sub + ORDER BY sub._sort; +END; +$function$ +; + +CREATE OR REPLACE FUNCTION v3._apply_form_cell(p_line_id integer, p_column text, p_value jsonb, p_sheet character varying DEFAULT NULL::character varying, p_form_id integer DEFAULT NULL::integer) + RETURNS void + LANGUAGE plpgsql +AS $function$ +DECLARE + v_parts TEXT[]; + v_scope TEXT; + v_field TEXT; + v_form_type TEXT; + v_q SMALLINT; + v_actor TEXT; + v_target_table TEXT; + v_target_col TEXT; + v_target_type TEXT; + v_key_kind TEXT; + v_str TEXT; + v_sql TEXT; + v_sat_table TEXT; + v_cnt INT; + +BEGIN + v_parts := string_to_array(p_column, '.'); + + IF p_sheet = 'OTCH9F' THEN + DECLARE v_far_month SMALLINT; + BEGIN + SELECT month INTO v_far_month FROM v3.fixed_asset_report WHERE id = p_line_id; + IF v_far_month IS NULL THEN + RAISE EXCEPTION 'fixed_asset_report.id=% не существует', p_line_id; + END IF; + + IF array_length(v_parts,1) = 1 THEN + IF v_parts[1] = 'equipment_name' THEN + RAISE EXCEPTION 'normative_field: equipment_name (нормативная номенклатура, правится отдельным API)'; + ELSIF v_parts[1] IN ('month','expense_item_id','item_id','section_code','id') THEN + RAISE EXCEPTION 'key_field: % (часть ключа строки, RO)', v_parts[1]; + ELSE + RAISE EXCEPTION 'unknown_column: %', p_column; + END IF; + END IF; + + IF array_length(v_parts,1) <> 2 THEN + RAISE EXCEPTION 'bad_column_format: %', p_column; + END IF; + + IF v_parts[1] = 'totals' THEN + IF v_parts[2] = 'go_balance_only' THEN + v_target_col := 'go_balance_only_amt'; v_target_type := 'NUMERIC'; + ELSIF v_parts[2] IN ('acquired_total','disposed_total','balance_qty','balance_amt') THEN + RAISE EXCEPTION 'computed_field: totals.%', v_parts[2]; + ELSE + RAISE EXCEPTION 'unknown_column: %', p_column; + END IF; + ELSIF v_parts[1] IN ('b_604','b_60415') THEN + IF v_parts[2] LIKE 'opening_%' AND v_far_month <> 1 THEN + RAISE EXCEPTION 'computed_field: % (opening для month>1 вычисляется из base + накопит. дельты)', p_column; + END IF; + IF v_parts[1] = 'b_604' THEN + v_target_col := CASE v_parts[2] + WHEN 'opening_qty' THEN 'opening_qty_604' + WHEN 'opening_amt' THEN 'opening_amt_604' + WHEN 'acquired_qty' THEN 'acquired_qty_604' + WHEN 'acquired_amt' THEN 'acquired_amt_604' + WHEN 'disposed_qty' THEN 'disposed_qty_604' + WHEN 'disposed_amt' THEN 'disposed_amt_604' + END; + IF v_target_col IS NULL THEN + RAISE EXCEPTION 'unknown_column: %', p_column; + END IF; + v_target_type := CASE WHEN v_parts[2] LIKE '%_qty' THEN 'INTEGER' ELSE 'NUMERIC' END; + ELSE -- b_60415 + v_target_col := CASE v_parts[2] + WHEN 'opening_qty' THEN 'opening_qty_60415' + WHEN 'opening_amt' THEN 'opening_amt_60415' + WHEN 'acquired_qty' THEN 'acquired_qty_60415' + WHEN 'acquired_amt' THEN 'acquired_amt_60415' + WHEN 'transferred_qty' THEN 'transferred_qty_60415' + WHEN 'transferred_amt' THEN 'transferred_amt_60415' + END; + IF v_target_col IS NULL THEN + RAISE EXCEPTION 'unknown_column: %', p_column; + END IF; + v_target_type := CASE WHEN v_parts[2] LIKE '%_qty' THEN 'INTEGER' ELSE 'NUMERIC' END; + END IF; + ELSE + RAISE EXCEPTION 'unknown_column: %', p_column; + END IF; + + EXECUTE format( + 'UPDATE v3.fixed_asset_report SET %I = ($2 #>> ''{}'')::%s WHERE id = $1', + v_target_col, v_target_type + ) USING p_line_id, p_value; + RETURN; + END; + END IF; + + IF p_sheet = 'AHR_LIMIT' THEN + IF p_form_id IS NULL THEN + RAISE EXCEPTION 'internal: p_form_id не передан для AHR_LIMIT'; + END IF; + + IF NOT EXISTS (SELECT 1 FROM v3.limit_template lt + WHERE lt.id = p_line_id AND lt.row_type = 'LEAF') THEN + RAISE EXCEPTION 'limit_template.id=% не существует или не LEAF (SECTION/GROUP не редактируется)', p_line_id; + END IF; + + IF array_length(v_parts,1) <> 1 THEN + RAISE EXCEPTION 'bad_column_format: %, expected single key', p_column; + END IF; + + CASE v_parts[1] + WHEN 'qty_q1','qty_q2','qty_q3','qty_q4' THEN + v_target_col := v_parts[1]; v_target_type := 'INTEGER'; + WHEN 'comment' THEN + v_target_col := 'comment'; v_target_type := 'TEXT'; + WHEN 'amount_q1','amount_q2','amount_q3','amount_q4' THEN + RAISE EXCEPTION 'computed_field: % (вычисляется из qty × limit × period_factor)', p_column; + WHEN 'name','unit','section_no','expense_item_code', + 'limit_with_vat','limit_without_vat' THEN + RAISE EXCEPTION 'normative_field: % (нормативный справочник, правится админом отдельно)', p_column; + ELSE + RAISE EXCEPTION 'unknown_column: %', p_column; + END CASE; + + IF (v_target_type = 'NUMERIC' OR v_target_type = 'INTEGER') AND p_value = '""'::jsonb THEN + p_value := '"0"'::jsonb; + END IF; + + EXECUTE format( + 'INSERT INTO v3.form_limit (budget_form_id, template_id, %1$I) ' + || 'VALUES ($1, $2, ($3 #>> ''{}'')::%2$s) ' + || 'ON CONFLICT (budget_form_id, template_id) DO UPDATE SET %1$I = EXCLUDED.%1$I', + v_target_col, v_target_type + ) USING p_form_id, p_line_id, p_value; + RETURN; + END IF; + + IF p_sheet IN ('AHR_RENT','AHR_UTILITY','AHR_SECURITY') THEN + v_sat_table := CASE p_sheet + WHEN 'AHR_RENT' THEN 'rent_detail' + WHEN 'AHR_UTILITY' THEN 'utility_detail' + WHEN 'AHR_SECURITY' THEN 'security_detail' + END; + + IF array_length(v_parts,1) = 1 THEN + CASE v_parts[1] + WHEN 'contract_number' THEN v_target_col := 'contract_number'; v_target_type := 'TEXT'; + WHEN 'contract_end_date' THEN v_target_col := 'contract_end_date'; v_target_type := 'DATE'; + WHEN 'comment' THEN v_target_col := 'comment'; v_target_type := 'TEXT'; + WHEN 'address','object_type','rented_area','object_area' THEN + RAISE EXCEPTION 'computed_field: % (атрибут v3.vsp, правится отдельно)', p_column; + ELSE RAISE EXCEPTION 'unknown_column: %', p_column; + END CASE; + ELSIF array_length(v_parts,1) = 2 AND v_parts[1] = 'plan' THEN + IF v_parts[2] = 'year' THEN RAISE EXCEPTION 'computed_field: %', p_column; END IF; + IF v_parts[2] NOT IN ('q1','q2','q3','q4') THEN RAISE EXCEPTION 'unknown_column: %', p_column; END IF; + v_target_col := 'plan_' || v_parts[2]; + v_target_type := 'NUMERIC'; + ELSIF array_length(v_parts,1) = 2 AND v_parts[1] LIKE 'fact_q%' THEN + IF v_parts[2] = 'total' THEN RAISE EXCEPTION 'computed_field: %', p_column; END IF; + IF (v_parts[1]='fact_q1' AND v_parts[2] NOT IN ('jan','feb','mar')) OR + (v_parts[1]='fact_q2' AND v_parts[2] NOT IN ('apr','may','jun')) OR + (v_parts[1]='fact_q3' AND v_parts[2] NOT IN ('jul','aug','sep')) OR + (v_parts[1]='fact_q4' AND v_parts[2] NOT IN ('oct','nov','dec','spod')) THEN + RAISE EXCEPTION 'unknown_column: % (месяц вне квартала)', p_column; + END IF; + v_target_col := 'actual_' || v_parts[2]; + v_target_type := 'NUMERIC'; + ELSE + RAISE EXCEPTION 'unknown_column: %', p_column; + END IF; + + + IF v_target_type = 'NUMERIC' AND p_value = '""'::jsonb THEN + p_value := '"0"'::jsonb; + END IF; + + EXECUTE format( + 'UPDATE v3.%I SET %I = ($2 #>> ''{}'')::%s WHERE id = $1', + v_sat_table, v_target_col, v_target_type + ) USING p_line_id, p_value; + + GET DIAGNOSTICS v_cnt = ROW_COUNT; + IF v_cnt = 0 THEN + RAISE EXCEPTION '%.id=% не существует', v_sat_table, p_line_id; + END IF; + RETURN; + END IF; + + IF array_length(v_parts, 1) < 2 THEN + RAISE EXCEPTION 'bad_column_format: %, expected scope.field', p_column; + END IF; + v_scope := v_parts[1]; + v_field := v_parts[2]; + + IF v_scope = 'header' THEN + IF v_field = 'name' THEN + SELECT form_type_code INTO v_form_type from v3.budget_form where id = p_form_id; + IF v_form_type = 'FORM_4' THEN + RAISE EXCEPTION 'Нельзя редактировать поле name'; + END IF; + UPDATE v3.budget_line SET name = (p_value #>> '{}'), updated_at = now() WHERE id = p_line_id; + ELSIF v_field = 'internal_order' THEN + UPDATE v3.budget_line SET internal_order = (p_value #>> '{}'), updated_at = now() WHERE id = p_line_id; + ELSIF v_field = 'justification' THEN + UPDATE v3.budget_line SET justification = (p_value #>> '{}')::TEXT, updated_at = now() WHERE id = p_line_id; + ELSIF v_field = 'vsp_id' THEN + UPDATE v3.budget_line SET vsp_id = (p_value #>> '{}')::INT, updated_at = now() WHERE id = p_line_id; + ELSIF v_field IN ('year','section','item_id','num_group','vsp_address') THEN + RAISE EXCEPTION 'computed_field: %', p_column; + ELSE + RAISE EXCEPTION 'unknown_column: %', p_column; + END IF; + RETURN; + END IF; + + IF v_scope IN ('approved','totals') THEN + RAISE EXCEPTION 'computed_field: %', p_column; + END IF; + + IF v_scope = 'booking' THEN + IF array_length(v_parts, 1) <> 3 THEN + RAISE EXCEPTION 'bad_column_format: %, expected booking.y{2026|2027}.qN', p_column; + END IF; + IF v_parts[2] NOT IN ('y2026','y2027') THEN + RAISE EXCEPTION 'bad_booking_year: % (only y2026/y2027)', v_parts[2]; + END IF; + IF v_parts[3] NOT IN ('q1','q2','q3','q4') THEN + RAISE EXCEPTION 'bad_booking_quarter: %', v_parts[3]; + END IF; + v_target_col := CASE v_parts[2] + WHEN 'y2026' THEN 'expenses_' || v_parts[3] + WHEN 'y2027' THEN 'expenses_next_year_' || v_parts[3] + END; + v_sql := format( + 'INSERT INTO v3.ckk (line_id, %1$I) VALUES ($1, ($2 #>> ''{}'')::NUMERIC) ' + || 'ON CONFLICT (line_id) DO UPDATE SET %1$I = EXCLUDED.%1$I', + v_target_col + ); + EXECUTE v_sql USING p_line_id, p_value; + UPDATE v3.budget_line SET updated_at = now() WHERE id = p_line_id; + RETURN; + END IF; + + + IF v_scope IN ('q1','q2','q3','q4') THEN + v_q := substring(v_scope FROM 2)::SMALLINT; + ELSE + v_q := NULL; + END IF; + + v_actor := CASE v_scope + WHEN 'sequestration' THEN 'DFIP' + WHEN 'seq_dfip' THEN 'DFIP' + WHEN 'seq_ssp' THEN 'SSP_GO' + ELSE NULL END; + + -- AURORA-1938 / #335: на FORM_2 («Смета РФ» в UI) q*.transfer_econ (перенос в фонд + -- экономии, столбцы 75/107/137 и Q4) — вычисляемое поле, как residual_after_actual, + -- и руками не редактируется. На остальных формах (в т.ч. FORM_4) поле обычное, raw. + IF v_q IS NOT NULL AND v_field = 'transfer_econ' THEN + SELECT form_type_code INTO v_form_type FROM v3.budget_form WHERE id = p_form_id; + IF v_form_type = 'FORM_2' THEN + RAISE EXCEPTION 'computed_field: %', p_column; + END IF; + END IF; + + SELECT m.target_table, m.target_col, m.target_type, m.key_kind + INTO v_target_table, v_target_col, v_target_type, v_key_kind + FROM (VALUES + ('plan','q1', 'plan','plan_q1','NUMERIC','line'), + ('plan','q2', 'plan','plan_q2','NUMERIC','line'), + ('plan','q3', 'plan','plan_q3','NUMERIC','line'), + ('plan','q4', 'plan','plan_q4','NUMERIC','line'), + ('plan','comment', 'plan','comment','TEXT', 'line'), + + ('reserve','q1', 'reserve','amount_q1','NUMERIC','line'), + ('reserve','q2', 'reserve','amount_q2','NUMERIC','line'), + ('reserve','q3', 'reserve','amount_q3','NUMERIC','line'), + ('reserve','q4', 'reserve','amount_q4','NUMERIC','line'), + ('reserve','justification','reserve','justification','TEXT','line'), + + ('allocation','order', 'allocation','internal_order','TEXT','line'), + ('allocation','property', 'allocation','property_object','TEXT','line'), + ('allocation','contract_ref', 'allocation','contract_ref','TEXT','line'), + ('allocation','allocation_purpose', 'allocation','allocation_purpose','TEXT','line'), + + ('contract_summary','total', 'contract_summary','total_amount', 'NUMERIC','line'), + ('contract_summary','counterparty', 'contract_summary','counterparty', 'TEXT', 'line'), + ('contract_summary','deadline', 'contract_summary','deadline', 'TEXT', 'line'), + ('contract_summary','comment', 'contract_summary','comment', 'TEXT', 'line'), + ('contract_summary','future_y1', 'contract_summary','future_payments_y1', 'NUMERIC','line'), + ('contract_summary','future_y2', 'contract_summary','future_payments_y2', 'NUMERIC','line'), + ('contract_summary','other_ssp', 'contract_summary','other_ssp_amount', 'NUMERIC','line'), + ('contract_summary','reference', 'contract_summary','reference', 'TEXT','line'), + ('contract_summary','centralized_flag', 'contract_summary','centralized_flag', 'TEXT','line'), + + ('collegial','approved', 'collegial_approval','approved_amount', 'NUMERIC','line'), + ('collegial','protocol', 'collegial_approval','protocol_reference','TEXT', 'line'), + ('collegial','note', 'collegial_approval','note', 'TEXT', 'line'), + + ('ckk','ceiling', 'ckk','ceiling_amount', 'NUMERIC','line'), + ('ckk','q1', 'ckk','expenses_q1', 'NUMERIC','line'), + ('ckk','q2', 'ckk','expenses_q2', 'NUMERIC','line'), + ('ckk','q3', 'ckk','expenses_q3', 'NUMERIC','line'), + ('ckk','q4', 'ckk','expenses_q4', 'NUMERIC','line'), + ('ckk','next_q1', 'ckk','expenses_next_year_q1', 'NUMERIC','line'), + ('ckk','next_q2', 'ckk','expenses_next_year_q2', 'NUMERIC','line'), + ('ckk','next_q3', 'ckk','expenses_next_year_q3', 'NUMERIC','line'), + ('ckk','next_q4', 'ckk','expenses_next_year_q4', 'NUMERIC','line'), + ('ckk','rf_schedule', 'ckk','rf_schedule', 'TEXT', 'line'), + ('ckk','deadline', 'ckk','delivery_deadline', 'TEXT', 'line'), + ('ckk','proc_plan', 'ckk','procurement_plan', 'TEXT', 'line'), + ('ckk','proc_method', 'ckk','procurement_method','TEXT', 'line'), + ('ckk','comment', 'ckk','comment', 'TEXT', 'line'), + + ('contract_detail','counterparty', 'contract_detail','counterparty', 'TEXT', 'line'), + ('contract_detail','reference', 'contract_detail','reference', 'TEXT', 'line'), + ('contract_detail','addenda', 'contract_detail','addenda', 'TEXT', 'line'), + ('contract_detail','subject', 'contract_detail','subject', 'TEXT', 'line'), + ('contract_detail','currency', 'contract_detail','currency', 'TEXT', 'line'), + ('contract_detail','ceiling', 'contract_detail','ceiling_amount','NUMERIC','line'), + ('contract_detail','q1', 'contract_detail','expenses_q1', 'NUMERIC','line'), + ('contract_detail','q2', 'contract_detail','expenses_q2', 'NUMERIC','line'), + ('contract_detail','q3', 'contract_detail','expenses_q3', 'NUMERIC','line'), + ('contract_detail','q4', 'contract_detail','expenses_q4', 'NUMERIC','line'), + ('contract_detail','rf_schedule', 'contract_detail','rf_schedule', 'TEXT', 'line'), + ('contract_detail','vat_rate', 'contract_detail','vat_rate', 'TEXT', 'line'), + ('contract_detail','exchange_rate', 'contract_detail','exchange_rate', 'NUMERIC','line'), + ('contract_detail','amount_foreign', 'contract_detail','amount_foreign','NUMERIC','line'), + ('contract_detail','deadline', 'contract_detail','deadline', 'TEXT', 'line'), + ('contract_detail','payment_scheme', 'contract_detail','payment_scheme','TEXT', 'line'), + ('contract_detail','act', 'contract_detail','act', 'TEXT', 'line'), + ('contract_detail','comment', 'contract_detail','comment', 'TEXT', 'line'), + ('contract_detail','contract_date', 'contract_detail','contract_date', 'DATE', 'line'), + + ('contract','counterparty', 'contract_detail','counterparty', 'TEXT', 'line'), + ('contract','addenda', 'contract_detail','addenda', 'TEXT', 'line'), + ('contract','amount_foreign', 'contract_detail','amount_foreign','NUMERIC','line'), + ('contract','q1', 'contract_detail','expenses_q1', 'NUMERIC','line'), + ('contract','q2', 'contract_detail','expenses_q2', 'NUMERIC','line'), + ('contract','q3', 'contract_detail','expenses_q3', 'NUMERIC','line'), + ('contract','q4', 'contract_detail','expenses_q4', 'NUMERIC','line'), + ('contract','rf_schedule', 'contract_detail','rf_schedule', 'TEXT', 'line'), + ('contract','exchange_rate', 'contract_detail','exchange_rate', 'NUMERIC','line'), + ('contract','reference', 'contract_detail','reference', 'TEXT', 'line'), + ('contract','date', 'contract_detail','contract_date', 'DATE', 'line'), + ('contract','subject', 'contract_detail','subject', 'TEXT', 'line'), + ('contract','currency', 'contract_detail','currency', 'TEXT', 'line'), + ('contract','ceiling', 'contract_detail','ceiling_amount','NUMERIC','line'), + ('contract','vat_rate', 'contract_detail','vat_rate', 'TEXT', 'line'), + ('contract','deadline', 'contract_detail','deadline', 'TEXT', 'line'), + ('contract','scheme', 'contract_detail','payment_scheme','TEXT', 'line'), + ('contract','act', 'contract_detail','act', 'TEXT', 'line'), + ('contract','comment', 'contract_detail','comment', 'TEXT', 'line'), + + ('sequestration','q1', 'sequestration','adj_q1', 'NUMERIC','line_actor'), + ('sequestration','q2', 'sequestration','adj_q2', 'NUMERIC','line_actor'), + ('sequestration','q3', 'sequestration','adj_q3', 'NUMERIC','line_actor'), + ('sequestration','q4', 'sequestration','adj_q4', 'NUMERIC','line_actor'), + ('sequestration','justification','sequestration','justification','TEXT', 'line_actor'), + + ('seq_dfip','q1', 'sequestration','adj_q1', 'NUMERIC','line_actor'), + ('seq_dfip','q2', 'sequestration','adj_q2', 'NUMERIC','line_actor'), + ('seq_dfip','q3', 'sequestration','adj_q3', 'NUMERIC','line_actor'), + ('seq_dfip','q4', 'sequestration','adj_q4', 'NUMERIC','line_actor'), + ('seq_dfip','justification','sequestration','justification','TEXT', 'line_actor'), + + ('seq_ssp','q1', 'sequestration','adj_q1', 'NUMERIC','line_actor'), + ('seq_ssp','q2', 'sequestration','adj_q2', 'NUMERIC','line_actor'), + ('seq_ssp','q3', 'sequestration','adj_q3', 'NUMERIC','line_actor'), + ('seq_ssp','q4', 'sequestration','adj_q4', 'NUMERIC','line_actor'), + ('seq_ssp','justification','sequestration','justification','TEXT', 'line_actor'), + + ('q','adj_current', 'budget_line_quarter','adj_current', 'NUMERIC','line_quarter'), + ('q','adj_ssp', 'budget_line_quarter','adj_ssp', 'NUMERIC','line_quarter'), + ('q','adj_rf', 'budget_line_quarter','adj_rf', 'NUMERIC','line_quarter'), + ('q','adj_reserve', 'budget_line_quarter','adj_reserve', 'NUMERIC','line_quarter'), + ('q','adj_comment', 'budget_line_quarter','adj_comment', 'TEXT', 'line_quarter'), + ('q','target_change', 'budget_line_quarter','target_change', 'NUMERIC','line_quarter'), + ('q','base_correction', 'budget_line_quarter','base_plan_correction', 'NUMERIC','line_quarter'), + ('q','base_correction_comment', 'budget_line_quarter','base_plan_correction_comment','TEXT', 'line_quarter'), + ('q','pay_date', 'budget_line_quarter','payment_date', 'DATE', 'line_quarter'), + ('q','pay_amount', 'budget_line_quarter','payment_amount', 'NUMERIC','line_quarter'), + ('q','pay_ho', 'budget_line_quarter','payment_amount_ho', 'NUMERIC','line_quarter'), + ('q','pay_rf', 'budget_line_quarter','payment_amount_rf', 'NUMERIC','line_quarter'), + ('q','pay_comment', 'budget_line_quarter','payment_comment', 'TEXT', 'line_quarter'), + ('q','pay_act', 'budget_line_quarter','payment_act', 'TEXT', 'line_quarter'), + ('q','actual_m1', 'budget_line_quarter','actual_m1', 'NUMERIC','line_quarter'), + ('q','actual_m2', 'budget_line_quarter','actual_m2', 'NUMERIC','line_quarter'), + ('q','actual_m3', 'budget_line_quarter','actual_m3', 'NUMERIC','line_quarter'), + ('q','actual_spod', 'budget_line_quarter','actual_spod', 'NUMERIC','line_quarter'), + ('q','transfer_q2', 'budget_line_quarter','transfer_to_q2', 'NUMERIC','line_quarter'), + ('q','transfer_q3', 'budget_line_quarter','transfer_to_q3', 'NUMERIC','line_quarter'), + ('q','transfer_q4', 'budget_line_quarter','transfer_to_q4', 'NUMERIC','line_quarter'), + ('q','transfer_econ', 'budget_line_quarter','transfer_to_economy', 'NUMERIC','line_quarter'), + ('q','transfer_q2_delay_acts', 'budget_line_quarter','transfer_delay_acts', 'NUMERIC','line_quarter'), + ('q','transfer_q2_delay_procurement', 'budget_line_quarter','transfer_delay_procurement', 'NUMERIC','line_quarter'), + ('q','transfer_q2_economy_rf', 'budget_line_quarter','transfer_economy_rf', 'NUMERIC','line_quarter'), + ('q','transfer_q3_delay_acts', 'budget_line_quarter','transfer_delay_acts', 'NUMERIC','line_quarter'), + ('q','transfer_q3_delay_procurement', 'budget_line_quarter','transfer_delay_procurement', 'NUMERIC','line_quarter'), + ('q','transfer_q3_economy_rf', 'budget_line_quarter','transfer_economy_rf', 'NUMERIC','line_quarter'), + ('q','transfer_q4_delay_acts', 'budget_line_quarter','transfer_delay_acts', 'NUMERIC','line_quarter'), + ('q','transfer_q4_delay_procurement', 'budget_line_quarter','transfer_delay_procurement', 'NUMERIC','line_quarter'), + ('q','transfer_q4_economy_rf', 'budget_line_quarter','transfer_economy_rf', 'NUMERIC','line_quarter'), + ('q','transfer_next_comment', 'budget_line_quarter','transfer_next_comment', 'TEXT', 'line_quarter'), + ('q','transfer_far_comment', 'budget_line_quarter','transfer_far_comment', 'TEXT', 'line_quarter'), + ('q','rev_eco', 'budget_line_quarter','plan_revision_eco_change','NUMERIC','line_quarter'), + ('q','rev_item', 'budget_line_quarter','plan_revision_item_adj', 'NUMERIC','line_quarter'), + ('q','rev_inc', 'budget_line_quarter','plan_revision_increase', 'NUMERIC','line_quarter'), + ('q','rev_seq', 'budget_line_quarter','plan_revision_sequester', 'NUMERIC','line_quarter'), + ('q','rev_comment', 'budget_line_quarter','plan_revision_comment', 'TEXT', 'line_quarter'), + ('q','revision_inc', 'budget_line_quarter','plan_revision_increase', 'NUMERIC','line_quarter'), + ('q','revision_seq', 'budget_line_quarter','plan_revision_sequester', 'NUMERIC','line_quarter'), + ('q','revision_comment', 'budget_line_quarter','plan_revision_comment', 'TEXT', 'line_quarter'), + ('q','booking_amount', 'budget_line_quarter','booking_amount', 'NUMERIC','line_quarter') + ) AS m(scope, field, target_table, target_col, target_type, key_kind) + WHERE m.scope = (CASE WHEN v_q IS NOT NULL THEN 'q' ELSE v_scope END) + AND m.field = v_field; + + IF v_target_table IS NULL THEN + IF v_field IN ('corrected_plan','new_plan','actual_quarter','booking', + 'rem_booking','rem_actual','residual_after_booking', + 'residual_after_actual','economy','total','year') THEN + RAISE EXCEPTION 'computed_field: %', p_column; + END IF; + RAISE EXCEPTION 'unknown_column: %', p_column; + END IF; + + IF v_target_type = 'NUMERIC' AND p_value = '""'::jsonb THEN + p_value := '"0"'::jsonb; + END IF; + IF v_key_kind = 'line' THEN + IF v_target_type = 'DATE' THEN + v_sql := format( + 'INSERT INTO v3.%1$I (line_id, %2$I) VALUES ($1, to_date($2->>0, ''DD.MM.YYYY'')) ' + || 'ON CONFLICT (line_id) DO UPDATE SET %2$I = EXCLUDED.%2$I', + v_target_table, v_target_col + ); + ELSE + v_sql := format( + 'INSERT INTO v3.%1$I (line_id, %2$I) VALUES ($1, ($2 #>> ''{}'')::%3$s) ' + || 'ON CONFLICT (line_id) DO UPDATE SET %2$I = EXCLUDED.%2$I', + v_target_table, v_target_col, v_target_type + ); + END IF; + EXECUTE v_sql USING p_line_id, p_value; + + ELSIF v_key_kind = 'line_quarter' THEN + IF v_q IS NULL THEN RAISE EXCEPTION 'internal: quarter not set for %', p_column; END IF; + + IF v_target_type = 'DATE' THEN + + v_sql := format( + 'INSERT INTO v3.%1$I (line_id, quarter, %2$I) VALUES ($1, $2, to_date($3->>0, ''DD.MM.YYYY'')) ' + || 'ON CONFLICT (line_id, quarter) DO UPDATE SET %2$I = EXCLUDED.%2$I', + v_target_table, v_target_col + ); + + ELSE + v_sql := format( + 'INSERT INTO v3.%1$I (line_id, quarter, %2$I) VALUES ($1, $2, ($3 #>> ''{}'')::%3$s) ' + || 'ON CONFLICT (line_id, quarter) DO UPDATE SET %2$I = EXCLUDED.%2$I', + v_target_table, v_target_col, v_target_type + ); + END IF; + + + EXECUTE v_sql USING p_line_id, v_q, p_value; + + ELSIF v_key_kind = 'line_actor' THEN + IF v_actor IS NULL THEN RAISE EXCEPTION 'internal: actor not set for %', p_column; END IF; + + IF v_target_type = 'DATE' THEN + v_sql := format( + 'INSERT INTO v3.%1$I (line_id, actor, %2$I) VALUES ($1, $2, to_date($3->>0, ''DD.MM.YYYY'')) ' + || 'ON CONFLICT (line_id, actor) DO UPDATE SET %2$I = EXCLUDED.%2$I', + v_target_table, v_target_col, v_target_type + ); + ELSE + v_sql := format( + 'INSERT INTO v3.%1$I (line_id, actor, %2$I) VALUES ($1, $2, ($3 #>> ''{}'')::%3$s) ' + || 'ON CONFLICT (line_id, actor) DO UPDATE SET %2$I = EXCLUDED.%2$I', + v_target_table, v_target_col, v_target_type + ); + END IF; + + EXECUTE v_sql USING p_line_id, v_actor, p_value; + + ELSE + RAISE EXCEPTION 'internal: unknown key_kind %', v_key_kind; + END IF; + + UPDATE v3.budget_line SET updated_at = now() WHERE id = p_line_id; +END; +$function$ +; diff --git a/api/tests/integration/test_form2_transfer_econ.py b/api/tests/integration/test_form2_transfer_econ.py new file mode 100644 index 0000000..74c0349 --- /dev/null +++ b/api/tests/integration/test_form2_transfer_econ.py @@ -0,0 +1,175 @@ +""" +AURORA-1938 / Gitea #335: «Смета РФ» в UI — это FORM_2 (а не FORM_4). Листы +АХР/КВ/Операц: перенос в фонд экономии (q1..q4.transfer_econ, столбцы 75/107/137) +считается, а не хранится, и входит в «Всего» (q*.total). + +Формулы (шаблон Excel; cp — скорректированный план, aq — факт; в скобках +подколонки переноса «задержка актов / задержка закупок / экономия РФ»): + + econ_Q1 = (cp1 - aq1) - (tda1 + tdp1 + terf1) - q1.transfer_q3 - q1.transfer_q4 + econ_Q2 = (cp2 - aq2) - (tda2 + tdp2 + terf2) - q2.transfer_q4 + econ_Q3 = (cp3 - aq3) - (tda3 + tdp3 + terf3) + econ_Q4 = (cp4 - aq4) + total_Qn = tda+tdp+terf + переносы через квартал + econ_Qn = cp_n - aq_n + +Вычитаются именно подколонки (tda/tdp/terf), а не сырое q*.transfer_q2/q3/q4. +""" + +import pytest + +API = "/api/v1/form" +# листы формы 2 -> год формы (уникальный на тест) +SHEET_YEAR = {"AHR": 91001, "CAP": 91002, "OPER": 91003} +# родители АХР-сателлитов (аренда/коммуналка/охрана) — у них особая логика +AHR_SATELLITE_PARENTS = (24, 314, 779) + + +@pytest.fixture +def headers(admin_tokens, auth_headers): + return auth_headers(admin_tokens) + + +def _new_form(client, headers, year): + r = client.post( + f"{API}/", + json={"year": year, "form_type_code": "FORM_2", "org_unit_ids": [1]}, + headers=headers, + ) + assert r.status_code == 200, r.text + return int(r.json()["result"][0]["id"]) + + +def _leaf_items(svod2_sql, sheet, n): + rows = svod2_sql( + """ + SELECT ei.id FROM v3.expense_item ei + JOIN v3.expense_item_form_type t ON t.expense_item_id = ei.id AND t.form_type_code = 'FORM_2' + WHERE ei.sheet = :sheet + AND NOT EXISTS (SELECT 1 FROM v3.expense_item c WHERE c.parent_id = ei.id) + AND ei.id <> ALL(:sat) + ORDER BY ei.id LIMIT :n + """, + {"sheet": sheet, "n": n, "sat": list(AHR_SATELLITE_PARENTS)}, + ) + assert len(rows) == n + return [r[0] for r in rows] + + +def _add_line(client, headers, form_id, sheet, item_id): + r = client.post( + f"{API}/{form_id}/sheet/{sheet}/line", + json={"expense_item_id": item_id}, + headers=headers, + ) + assert r.status_code == 200, r.text + return int(next(x for x in r.json()["result"] if x["row_type"] == "INPUT")["data"]["line_id"]) + + +def _set(client, headers, form_id, sheet, line_id, column, value): + r = client.patch( + f"{API}/{form_id}/sheet/{sheet}/cell", + json={"line_id": line_id, "column": column, "value": value}, + headers=headers, + ) + assert r.status_code == 200, r.text + + +def _rows(client, headers, form_id, sheet): + r = client.get(f"{API}/{form_id}/sheet/{sheet}", headers=headers) + assert r.status_code == 200, r.text + return r.json()["result"] + + +def _input(rows, line_id): + return next(x["data"] for x in rows if x["row_type"] == "INPUT" and x["data"].get("line_id") == line_id) + + +def _root(rows): + return next(x["data"] for x in rows if x["row_type"] == "ROOT") + + +@pytest.mark.parametrize("sheet", ["AHR", "CAP", "OPER"]) +def test_form2_transfer_econ_ticket_repro(client, headers, svod2_sql, sheet): + """Репро тикета: план 1000/2000/3000, остальное пусто.""" + form_id = _new_form(client, headers, SHEET_YEAR[sheet]) + line_id = _add_line(client, headers, form_id, sheet, _leaf_items(svod2_sql, sheet, 1)[0]) + for col, val in [("plan.q1", 1000), ("plan.q2", 2000), ("plan.q3", 3000)]: + _set(client, headers, form_id, sheet, line_id, col, val) + + d = _input(_rows(client, headers, form_id, sheet), line_id) + + assert [d[q].get("transfer_econ") for q in ("q1", "q2", "q3")] == [1000, 2000, 3000] + assert [d[q]["total"] for q in ("q1", "q2", "q3")] == [1000, 2000, 3000] + # Q4: план не задан -> остаток 0 + assert d["q4"].get("transfer_econ") == d["q4"]["residual_after_actual"] == 0 + assert d["q4"]["total"] == 0 + + +def test_form2_ahr_transfer_econ_subcolumns_and_far_transfers(client, headers, svod2_sql): + form_id = _new_form(client, headers, 91010) + line_id = _add_line(client, headers, form_id, "AHR", _leaf_items(svod2_sql, "AHR", 1)[0]) + changes = [ + ("plan.q1", 1000), ("plan.q2", 2000), ("plan.q3", 3000), + ("q1.transfer_q2_delay_acts", 100), ("q1.transfer_q3", 200), ("q1.transfer_q4", 300), + ("q2.transfer_q3_delay_acts", 400), ("q2.transfer_q4", 500), + ("q3.transfer_q4_delay_acts", 600), + ] + for col, val in changes: + _set(client, headers, form_id, "AHR", line_id, col, val) + + d = _input(_rows(client, headers, form_id, "AHR"), line_id) + + # Q1: cp1=1000, aq1=0 -> 1000 - 100 - 200 - 300 + assert d["q1"].get("transfer_econ") == 400 + # Q2: np2 = ap2 + q1.transfer_q2 (сырое поле, не задано => 0) = 2000; + # cp2 - aq2 = 2000 -> 2000 - tda2(400) - q2.transfer_q4(500) + assert d["q2"].get("transfer_econ") == 1100 + # Q3: np3 = ap3 + q1.transfer_q3(200) + q2.transfer_q3(сырое, 0) = 3200; + # 3200 - tda3(600) + assert d["q3"].get("transfer_econ") == 2600 + # Q4: np4 = ap4(0) + q1.tq4(300) + q2.tq4(500) + q3.tq4(0) = 800; econ = 800 + assert d["q4"].get("transfer_econ") == 800 + + # «Всего» = остаток после факта (cp - aq) этого квартала + assert d["q1"]["total"] == 1000 == d["q1"]["residual_after_actual"] + assert d["q2"]["total"] == 2000 == d["q2"]["residual_after_actual"] + assert d["q3"]["total"] == 3200 == d["q3"]["residual_after_actual"] + assert d["q4"]["total"] == 800 == d["q4"]["residual_after_actual"] + + # ROOT (агрегат) = сумма по строкам; строка в форме одна + root = _root(_rows(client, headers, form_id, "AHR")) + for q in ("q1", "q2", "q3", "q4"): + assert root[q].get("transfer_econ") == d[q].get("transfer_econ"), q + assert root[q]["total"] == d[q]["total"], q + + +@pytest.mark.parametrize("sheet", ["AHR", "CAP", "OPER"]) +def test_form2_transfer_econ_group_row_is_sum_of_lines(client, headers, svod2_sql, sheet): + form_id = _new_form(client, headers, SHEET_YEAR[sheet] + 100) + items = _leaf_items(svod2_sql, sheet, 2) + l1 = _add_line(client, headers, form_id, sheet, items[0]) + l2 = _add_line(client, headers, form_id, sheet, items[1]) + for col, val in [("plan.q1", 10), ("plan.q2", 20), ("plan.q3", 30)]: + _set(client, headers, form_id, sheet, l1, col, val) + for col, val in [("plan.q1", 1), ("plan.q2", 2), ("plan.q3", 3), ("q1.transfer_q4", 1)]: + _set(client, headers, form_id, sheet, l2, col, val) + + rows = _rows(client, headers, form_id, sheet) + d1, d2, root = _input(rows, l1), _input(rows, l2), _root(rows) + assert (d1["q1"].get("transfer_econ"), d2["q1"].get("transfer_econ")) == (10, 0) # 1 - tq4(1) + for q in ("q1", "q2", "q3", "q4"): + assert root[q].get("transfer_econ") == d1[q].get("transfer_econ") + d2[q].get("transfer_econ"), q + assert root[q]["total"] == d1[q]["total"] + d2[q]["total"], q + + +@pytest.mark.parametrize("column", ["q1.transfer_econ", "q2.transfer_econ", "q3.transfer_econ", "q4.transfer_econ"]) +def test_form2_transfer_econ_cell_is_read_only(client, headers, svod2_sql, column): + form_id = _new_form(client, headers, 91020) + line_id = _add_line(client, headers, form_id, "AHR", _leaf_items(svod2_sql, "AHR", 1)[0]) + r = client.patch( + f"{API}/{form_id}/sheet/AHR/cell", + json={"line_id": line_id, "column": column, "value": 123}, + headers=headers, + ) + assert r.status_code in (400, 422), r.text + assert "computed_field" in r.json()["message"] diff --git a/api/tests/integration/test_form4_transfer_econ.py b/api/tests/integration/test_form4_transfer_econ.py index 3fcd487..715a1eb 100644 --- a/api/tests/integration/test_form4_transfer_econ.py +++ b/api/tests/integration/test_form4_transfer_econ.py @@ -1,20 +1,11 @@ """ -AURORA-1938 / Gitea #255: форма РФ (FORM_4), листы АХР/КВ/Операц — -"перенос в фонд экономии" (столбцы 75/107/137 = q1/q2/q3.transfer_econ) -не считался, был пустым raw passthrough-полем. +AURORA-1938 / Gitea #335: FORM_4 («Проектная деятельность») — откат PR #262. -Формулы (поверх уже имеющегося в схеме residual_after_actual_QN, -который уже вычитает перенос в СЛЕДУЮЩИЙ квартал ровно один раз через cp1/cp2/cp3): - - transfer_econ_Q1 = residual_after_actual_Q1 - transfer_q3 - transfer_q4 - transfer_econ_Q2 = residual_after_actual_Q2 - transfer_q4 - transfer_econ_Q3 = residual_after_actual_Q3 - transfer_econ_Q4 = residual_after_actual_Q4 - -Буквальный перенос формулы Excel (CB9=BR9-BT9-BY9-BZ9, т.е. ещё и минус -transfer_q2) на residual_after_actual_Q1 был бы НЕВЕРНЫМ: residual_after_actual_Q1 -уже учитывает transfer_q2 (через cp1), и его повторное вычитание занижает -экономию ровно на величину transfer_q2 (аналогично для Q2/transfer_q3 и Q3/transfer_q4). +Фонд экономии (q1..q4.transfer_econ) считается на FORM_2 («Смета РФ»), см. +test_form2_transfer_econ.py. На FORM_4 поле осталось сырым +(budget_line_quarter.transfer_to_economy): его можно вводить руками, оно читается +как введено и входит в «Всего» по прежней формуле. Тест — регрессия на то, что +миграция 0063 откатила расчёт и запрет записи для FORM_4. """ import pytest @@ -67,178 +58,51 @@ def _get_input_row_data(client, admin_tokens, auth_headers, form_id, sheet, line headers=auth_headers(admin_tokens), ) assert response.status_code == 200, response.text - rows = response.json()["result"] - for row in rows: + for row in response.json()["result"]: if row["row_type"] == "INPUT" and row["data"].get("line_id") == line_id: return row["data"] pytest.fail(f"INPUT-строка для line_id={line_id} не найдена в ответе /sheet/{sheet}") -def _get_root_row_data(client, admin_tokens, auth_headers, form_id, sheet) -> dict: - """ROOT-строка (depth=0) — агрегат по разделу; с единственной budget_line - в разделе её q1..q4 должны совпадать с INPUT-строкой этой line — проверяет, - что ветка агрегации (tw_agg/tw_aq1..4) считает transfer_econ так же, как - ветка INPUT, а не просто не падает.""" - response = client.get( - f"/api/v1/form/{form_id}/sheet/{sheet}", - headers=auth_headers(admin_tokens), - ) - assert response.status_code == 200, response.text - rows = response.json()["result"] - for row in rows: - if row["row_type"] == "ROOT": - return row["data"] - pytest.fail(f"ROOT-строка не найдена в ответе /sheet/{sheet}") - - -def test_form4_ahr_transfer_econ_ticket_repro_zero_transfers( - client, admin_tokens, auth_headers -): - """Репро тикета: план 1000/2000/3000 по кв. I-III, переносы/факт пустые. - - Когда все transfer_qN == 0, корректная и наивная (Excel-буквальная) формулы - совпадают — этот тест один не доказывает правильность формулы, см. - test_form4_ahr_transfer_econ_with_forward_transfers_matches_non_naive_formula. - """ +def test_form4_transfer_econ_is_raw_editable_field(client, admin_tokens, auth_headers): + """План без ввода фонда: transfer_econ пуст (не считается); введённое руками + значение сохраняется как есть и входит в «Всего» (старая формула).""" form_id = _create_form4_form(client, admin_tokens, auth_headers, year=90101) line_id = _add_line(client, admin_tokens, auth_headers, form_id, "AHR", 1) - - for column, value in [("plan.q1", 1000), ("plan.q2", 2000), ("plan.q3", 3000)]: + for column, value in [("plan.q1", 1000), ("plan.q2", 2000), ("q1.transfer_q2", 100)]: _set_cell(client, admin_tokens, auth_headers, form_id, "AHR", line_id, column, value) data = _get_input_row_data(client, admin_tokens, auth_headers, form_id, "AHR", line_id) + assert data["q1"].get("transfer_econ") is None + assert data["q2"].get("transfer_econ") is None + assert data["q1"]["total"] == 100 # только перенос в Q2, фонд не считается - assert data["q1"]["residual_after_actual"] == 1000 - assert data["q2"]["residual_after_actual"] == 2000 - assert data["q3"]["residual_after_actual"] == 3000 - - assert data["q1"]["transfer_econ"] == 1000 - assert data["q2"]["transfer_econ"] == 2000 - assert data["q3"]["transfer_econ"] == 3000 - - assert data["q1"]["total"] == 1000 - assert data["q2"]["total"] == 2000 - assert data["q3"]["total"] == 3000 - - -def test_form4_ahr_transfer_econ_with_forward_transfers_matches_non_naive_formula( - client, admin_tokens, auth_headers -): - """Регрессия на двойное вычитание: с ненулевыми переносами вперёд - (q1.transfer_q2=100, q2.transfer_q3=50, q3.transfer_q4=20) корректная и - наивная формулы расходятся — тест проверяет, что реализация считает - НЕ наивно (не дважды вычитает уже учтённый в residual_after_actual перенос). - """ - form_id = _create_form4_form(client, admin_tokens, auth_headers, year=90102) - line_id = _add_line(client, admin_tokens, auth_headers, form_id, "AHR", 1) - - changes = [ - ("plan.q1", 1000), - ("plan.q2", 2000), - ("plan.q3", 3000), - ("plan.q4", 500), - ("q1.transfer_q2", 100), - ("q2.transfer_q3", 50), - ("q3.transfer_q4", 20), - ] - for column, value in changes: - _set_cell(client, admin_tokens, auth_headers, form_id, "AHR", line_id, column, value) + _set_cell(client, admin_tokens, auth_headers, form_id, "AHR", line_id, "q1.transfer_econ", 123) + _set_cell(client, admin_tokens, auth_headers, form_id, "AHR", line_id, "q2.transfer_econ", 45) data = _get_input_row_data(client, admin_tokens, auth_headers, form_id, "AHR", line_id) - - # residual_after_actual_QN уже минусует перенос в следующий квартал один раз - assert data["q1"]["residual_after_actual"] == 900 # 1000 - 100 - assert data["q2"]["residual_after_actual"] == 2050 # 2000 + 100 - 50 - assert data["q3"]["residual_after_actual"] == 3030 # 3000 + 50 - 20 - assert data["q4"]["residual_after_actual"] == 520 # 500 + 20 - - # Верная формула (НЕ наивный Excel-порт, который бы дал 800 / 2000 / 3010): - # te1 = residual_Q1 - tq3(0) - tq4(0) = 900 - # te2 = residual_Q2 - tq4(0) = 2050 - # te3 = residual_Q3 = 3030 - # te4 = residual_Q4 = 520 - assert data["q1"]["transfer_econ"] == 900 - assert data["q2"]["transfer_econ"] == 2050 - assert data["q3"]["transfer_econ"] == 3030 - assert data["q4"]["transfer_econ"] == 520 - - # Наивная (Excel-буквальная, дважды вычитающая) формула дала бы другие числа — - # явно фиксируем, что реализация НЕ равна наивной. - naive_te1 = data["q1"]["residual_after_actual"] - 100 - 0 - 0 # = 800 - naive_te2 = data["q2"]["residual_after_actual"] - 50 - 0 # = 2000 - naive_te3 = data["q3"]["residual_after_actual"] - 20 # = 3010 - assert data["q1"]["transfer_econ"] != naive_te1 - assert data["q2"]["transfer_econ"] != naive_te2 - assert data["q3"]["transfer_econ"] != naive_te3 - - # ВСЕГО блоков учитывает рассчитанный transfer_econ и равен полному - # (ещё не разнесённому по кварталам) остатку после факта. - assert data["q1"]["total"] == 100 + 0 + 0 + 900 # = 1000 - assert data["q2"]["total"] == 50 + 0 + 2050 # = 2100 - assert data["q3"]["total"] == 20 + 3030 # = 3050 - assert data["q4"]["total"] == 520 - - # Ветка агрегации (ROOT) считает transfer_econ так же, как ветка INPUT, а - # не просто не падает: с единственной budget_line в разделе ROOT.q1..q4 - # должны совпасть с INPUT-строкой этой line. - root_data = _get_root_row_data(client, admin_tokens, auth_headers, form_id, "AHR") - assert root_data["q1"]["transfer_econ"] == data["q1"]["transfer_econ"] == 900 - assert root_data["q2"]["transfer_econ"] == data["q2"]["transfer_econ"] == 2050 - assert root_data["q3"]["transfer_econ"] == data["q3"]["transfer_econ"] == 3030 - assert root_data["q4"]["transfer_econ"] == data["q4"]["transfer_econ"] == 520 + assert data["q1"]["transfer_econ"] == 123 + assert data["q2"]["transfer_econ"] == 45 + assert data["q1"]["total"] == 100 + 123 # q1.transfer_q2 + transfer_econ + assert data["q2"]["total"] == 45 -def test_form4_cap_and_oper_transfer_econ_use_same_formula_as_ahr( - client, admin_tokens, auth_headers -): - """КВ (CAP) и Операц (OPER) используют ту же v3.v_form4_sheet_sections, - что и АХР — проверяем, что расчёт даёт те же числа на этих листах.""" - for sheet in ("CAP", "OPER"): - expense_item_id = FORM4_EXPENSE_ITEM_BY_SHEET[sheet] - year = 90200 + FORM4_YEAR_OFFSET_BY_SHEET[sheet] - form_id = _create_form4_form(client, admin_tokens, auth_headers, year=year) - line_id = _add_line(client, admin_tokens, auth_headers, form_id, sheet, expense_item_id) +@pytest.mark.parametrize("sheet", ["CAP", "OPER"]) +def test_form4_cap_oper_transfer_econ_is_raw_editable_field(client, admin_tokens, auth_headers, sheet): + expense_item_id = FORM4_EXPENSE_ITEM_BY_SHEET[sheet] + year = 90200 + FORM4_YEAR_OFFSET_BY_SHEET[sheet] + form_id = _create_form4_form(client, admin_tokens, auth_headers, year=year) + line_id = _add_line(client, admin_tokens, auth_headers, form_id, sheet, expense_item_id) + _set_cell(client, admin_tokens, auth_headers, form_id, sheet, line_id, "plan.q1", 1000) + _set_cell(client, admin_tokens, auth_headers, form_id, sheet, line_id, "q1.transfer_econ", 77) - changes = [ - ("plan.q1", 1000), - ("plan.q2", 2000), - ("plan.q3", 3000), - ("q1.transfer_q2", 100), - ("q2.transfer_q3", 50), - ] - for column, value in changes: - _set_cell(client, admin_tokens, auth_headers, form_id, sheet, line_id, column, value) - - data = _get_input_row_data(client, admin_tokens, auth_headers, form_id, sheet, line_id) - - assert data["q1"]["transfer_econ"] == 900, sheet # 1000 - 100 - assert data["q2"]["transfer_econ"] == 2050, sheet # 2000 + 100 - 50 - assert data["q3"]["transfer_econ"] == 3050, sheet # 3000 + 50 + data = _get_input_row_data(client, admin_tokens, auth_headers, form_id, sheet, line_id) + assert data["q1"]["transfer_econ"] == 77 -def test_form4_ahr_transfer_econ_cell_is_read_only(client, admin_tokens, auth_headers): - """transfer_econ должен стать вычисляемым полем (как residual_after_actual) — - прямая запись в q1.transfer_econ для FORM_4 должна быть отклонена.""" - form_id = _create_form4_form(client, admin_tokens, auth_headers, year=90103) - line_id = _add_line(client, admin_tokens, auth_headers, form_id, "AHR", 1) - - response = client.patch( - f"/api/v1/form/{form_id}/sheet/AHR/cell", - json={"line_id": line_id, "column": "q1.transfer_econ", "value": 123}, - headers=auth_headers(admin_tokens), - ) - assert response.status_code in (400, 422), response.text - payload = response.json() - assert "message" in payload - assert "computed_field" in payload["message"] or "transfer_econ" in payload["message"] - - -async def test_apply_form_cell_keeps_aurora_1906_spod_and_aurora_1938_guard(): - """Регрессия на порядок миграций: 0055 (AURORA-1938) пересобирает - v3._apply_form_cell поверх 0050_form2_ahr_spod_column (AURORA-1906), а не - поверх старого тела 0023 — иначе CREATE OR REPLACE тихо откатил бы СПОД. - Проверяем итоговое определение функции в БД: в нём одновременно есть - разрешение fact_q4.spod (1906) и запрет ручной записи transfer_econ (1938).""" +async def test_apply_form_cell_guard_is_form2_only_and_keeps_spod(): + """0063 пересобирает v3._apply_form_cell поверх 0055: запрет transfer_econ — только + для FORM_2, СПОД (AURORA-1906, 0050) на месте.""" from sqlalchemy import text from sqlalchemy.ext.asyncio import create_async_engine from sqlalchemy.pool import NullPool @@ -261,4 +125,6 @@ async def test_apply_form_cell_keeps_aurora_1906_spod_and_aurora_1938_guard(): await engine.dispose() assert "'oct','nov','dec','spod'" in definition, "AURORA-1906 (СПОД) откачен" - assert "v_field = 'transfer_econ'" in definition, "AURORA-1938 (computed transfer_econ) потерян" + guard = definition[definition.index("v_field = 'transfer_econ'"):] + assert "v_form_type = 'FORM_2'" in guard[:400] + assert "v_form_type = 'FORM_4'" not in guard[:400] -- 2.47.2 From 9c8871139c41f796def41f50cbb459336f9dfc2e Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Thu, 12 Nov 2026 16:05:21 +0400 Subject: [PATCH 2/4] =?UTF-8?q?fix(form2):=20=D1=84=D0=BE=D0=BD=D0=B4=20?= =?UTF-8?q?=D1=8D=D0=BA=D0=BE=D0=BD=D0=BE=D0=BC=D0=B8=D0=B8=20=D0=BD=D0=B0?= =?UTF-8?q?=20FORM=5F2=20=D0=B2=D1=8B=D1=87=D0=B8=D1=81=D0=BB=D1=8F=D0=B5?= =?UTF-8?q?=D0=BC=D1=8B=D0=B9,=20=D0=BD=D0=B5=20=D1=80=D0=B5=D0=B4=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=B8=D1=80=D1=83=D0=B5=D1=82=D1=81=D1=8F=20=D0=B2?= =?UTF-8?q?=20UI=20(AURORA-1938,=20#335)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit isComputedColumn/isCellEditable принимают formType; transfer_econ вычисляемый только на FORM_2, на FORM_4 остаётся редактируемым. Co-Authored-By: Claude Opus 5.5 --- .../RealtimeTable/RealtimeTable.jsx | 6 ++-- .../components/RealtimeTable/tableColumns.jsx | 2 ++ .../RealtimeTable/utils/cellEditability.js | 15 ++++++---- .../utils/cellEditability.test.js | 29 +++++++++++++++++++ 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/web/src/components/RealtimeTable/RealtimeTable.jsx b/web/src/components/RealtimeTable/RealtimeTable.jsx index 93e456d..e6ec364 100644 --- a/web/src/components/RealtimeTable/RealtimeTable.jsx +++ b/web/src/components/RealtimeTable/RealtimeTable.jsx @@ -214,8 +214,9 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => { columnId, isAdmin: isAdminUser, stageColumns: stageColumnsSet, + formType, }), - [isAdminUser, stageColumnsSet], + [isAdminUser, stageColumnsSet, formType], ); // AURORA-1891: cellIsEditable(row, columnId) не знает про сам column.def — @@ -371,12 +372,13 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => { getCellErrors, denomination, sheetName, + formType, }); } catch (error) { console.error('Error creating columns:', error); return []; } - }, [columnsConfig, columnsCurStage, userRoleId, handleCellUpdateError, handleClickRowCell, handleUpdateCell, getCellErrors, denomination, sheetName]); + }, [columnsConfig, columnsCurStage, userRoleId, handleCellUpdateError, handleClickRowCell, handleUpdateCell, getCellErrors, denomination, sheetName, formType]); // Мемоизация ячеек MRT (memoMode: 'cells') сравнивает только объект cell. Состояние вне данных строки, // от которого зависят стили закрепления и подсветка фильтров, сбрасывает её новым массивом колонок. diff --git a/web/src/components/RealtimeTable/tableColumns.jsx b/web/src/components/RealtimeTable/tableColumns.jsx index 5cbece6..c16eb6b 100644 --- a/web/src/components/RealtimeTable/tableColumns.jsx +++ b/web/src/components/RealtimeTable/tableColumns.jsx @@ -126,6 +126,7 @@ export const getTableColumns = ({ getCellErrors, denomination = 'thousand', sheetName, + formType, }) => { const columnColors = { ...columnsConfig.colors }; const columns = structuredClone(columnsConfig.columns); @@ -159,6 +160,7 @@ export const getTableColumns = ({ columnId, isAdmin: userRoleId === ROLES_NAME_ID.admin, stageColumns, + formType, }) ) { return false; diff --git a/web/src/components/RealtimeTable/utils/cellEditability.js b/web/src/components/RealtimeTable/utils/cellEditability.js index 36efce4..07d12ba 100644 --- a/web/src/components/RealtimeTable/utils/cellEditability.js +++ b/web/src/components/RealtimeTable/utils/cellEditability.js @@ -62,7 +62,7 @@ const DATA_PREFIX = 'data.'; * OTCH9F). columnId — это React Table column.id в формате accessorKey, т.е. * всегда с префиксом "data." для полей строки (например "data.q1.booking"). */ -export const isComputedColumn = (columnId) => { +export const isComputedColumn = (columnId, formType) => { if (typeof columnId !== 'string' || !columnId.startsWith(DATA_PREFIX)) return false; const parts = columnId.slice(DATA_PREFIX.length).split('.'); @@ -81,6 +81,11 @@ export const isComputedColumn = (columnId) => { // qN. из списка вычисляемых полей квартала. if (QUARTER_SCOPE_PATTERN.test(scope) && QUARTER_COMPUTED_FIELDS.has(field)) return true; + // AURORA-1938: на FORM_2 («Смета РФ») перенос в фонд экономии считается на сервере + // (миграция 0063; _apply_form_cell отвергает запись для FORM_2). На FORM_4 + // («Проектная деятельность») transfer_econ — обычное редактируемое поле. + if (formType === 'FORM_2' && QUARTER_SCOPE_PATTERN.test(scope) && field === 'transfer_econ') return true; + // fact_qN.total — сателлиты AHR_RENT/AHR_UTILITY/AHR_SECURITY. if (FACT_QUARTER_SCOPE_PATTERN.test(scope) && field === 'total') return true; @@ -96,12 +101,12 @@ export const isComputedColumn = (columnId) => { }; /** - * isCellEditable({ rowType, columnId, isAdmin, stageColumns }) + * isCellEditable({ rowType, columnId, isAdmin, stageColumns, formType }) * - * rowType === 'INPUT' && !isComputedColumn(columnId) && (isAdmin || stageColumns.has(columnId)) + * rowType === 'INPUT' && !isComputedColumn(columnId, formType) && (isAdmin || stageColumns.has(columnId)) * * stageColumns — Set с ключами вида "data." (columnsCurStage из * useRealtimeData, отражающий окно текущего этапа пользователя). */ -export const isCellEditable = ({ rowType, columnId, isAdmin, stageColumns }) => - rowType === 'INPUT' && !isComputedColumn(columnId) && (isAdmin || stageColumns.has(columnId)); +export const isCellEditable = ({ rowType, columnId, isAdmin, stageColumns, formType }) => + rowType === 'INPUT' && !isComputedColumn(columnId, formType) && (isAdmin || stageColumns.has(columnId)); diff --git a/web/src/components/RealtimeTable/utils/cellEditability.test.js b/web/src/components/RealtimeTable/utils/cellEditability.test.js index 618a710..8d676d6 100644 --- a/web/src/components/RealtimeTable/utils/cellEditability.test.js +++ b/web/src/components/RealtimeTable/utils/cellEditability.test.js @@ -144,3 +144,32 @@ test('plain non-"data." column ids (row number, grouping headers) are never comp assert.equal(isComputedColumn(undefined), false); assert.equal(isComputedColumn(null), false); }); + +test('AURORA-1938: transfer_econ is computed on FORM_2 only (FORM_4 keeps it editable)', () => { + for (const q of ['q1', 'q2', 'q3', 'q4']) { + const columnId = `data.${q}.transfer_econ`; + assert.equal(isComputedColumn(columnId, 'FORM_2'), true, `${columnId} must be computed on FORM_2`); + assert.equal(isComputedColumn(columnId, 'FORM_4'), false, `${columnId} must stay editable on FORM_4`); + assert.equal(isComputedColumn(columnId), false); + } + assert.equal( + isCellEditable({ + rowType: 'INPUT', + columnId: 'data.q1.transfer_econ', + isAdmin: true, + stageColumns: new Set(['data.q1.transfer_econ']), + formType: 'FORM_2', + }), + false, + ); + assert.equal( + isCellEditable({ + rowType: 'INPUT', + columnId: 'data.q1.transfer_econ', + isAdmin: true, + stageColumns: new Set(), + formType: 'FORM_4', + }), + true, + ); +}); -- 2.47.2 From 1304be106a3b4f887ba6aa23016057eb160fe32a Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Thu, 12 Nov 2026 16:36:07 +0400 Subject: [PATCH 3/4] =?UTF-8?q?fix(svod2):=20=D1=84=D0=BE=D0=BD=D0=B4=20?= =?UTF-8?q?=D1=8D=D0=BA=D0=BE=D0=BD=D0=BE=D0=BC=D0=B8=D0=B8=20FORM=5F2=20?= =?UTF-8?q?=D0=B2=20=D1=81=D0=B2=D0=BE=D0=B4=D0=B5=202=20=C2=AB=D0=A0?= =?UTF-8?q?=D0=A4=C2=BB=20=D1=81=D1=87=D0=B8=D1=82=D0=B0=D0=B5=D1=82=D1=81?= =?UTF-8?q?=D1=8F=20=D0=BA=D0=B0=D0=BA=20=D0=BD=D0=B0=20=D0=BB=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D0=B5=20=D0=A1=D0=BC=D0=B5=D1=82=D1=8B=20=D0=A0=D0=A4=20?= =?UTF-8?q?(AURORA-1938,=20#335)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v_svod2_rf: q1..q4.transfer_to_economy вычисляется по формулам листа, сырое поле budget_line_quarter.transfer_to_economy игнорируется. Эталон RF обновлён: фонд экономии строк теперь расчётный (GO без изменений). Co-Authored-By: Claude Opus 5.5 --- .../sql/0063_form2_transfer_econ_calc.sql | 236 ++++++++++++++++ api/tests/fixtures/svod2_legacy_golden.json | 256 +++++++++++++----- .../integration/test_form2_transfer_econ.py | 59 ++++ 3 files changed, 482 insertions(+), 69 deletions(-) diff --git a/api/alembic/versions/sql/0063_form2_transfer_econ_calc.sql b/api/alembic/versions/sql/0063_form2_transfer_econ_calc.sql index f6c27aa..a230773 100644 --- a/api/alembic/versions/sql/0063_form2_transfer_econ_calc.sql +++ b/api/alembic/versions/sql/0063_form2_transfer_econ_calc.sql @@ -1967,3 +1967,239 @@ BEGIN END; $function$ ; + +-- 4) AURORA-1938 / #335: Свод 2 «РФ» (FORM_2). v3.v_svod2_rf (тело - версия 0057): q1..q4.transfer_to_economy +-- берётся не из budget_line_quarter.transfer_to_economy, а считается по формулам п. 1 из тех же +-- таблиц строки (план+секвестр+резерв, корректировки, пересмотры плана, входящие переносы, факт). +-- v_svod2_go / v_svod2_go_export / summary_repository: GO = FORM_1 и FORM_4, у FORM_1 своё поле, +-- у FORM_4 фонд сырой (откат 0055 в п. 3) - не менялись. + +CREATE OR REPLACE FUNCTION v3.v_svod2_rf( + p_year integer, + p_org_unit_ids integer[] DEFAULT NULL, + p_offset integer DEFAULT NULL, + p_limit integer DEFAULT NULL, + p_sort_by varchar DEFAULT 'org_name', + p_sort_direction varchar DEFAULT 'asc', + p_search text DEFAULT NULL, + p_smeta_type text DEFAULT NULL, + p_smeta_direction text DEFAULT NULL, + p_org_name text DEFAULT NULL, + p_compact boolean DEFAULT false +) +RETURNS TABLE( + source_form varchar, source_id integer, source_org_unit_id integer, + org_name varchar, smeta_type varchar, + source_line_id integer, sort_order bigint, data jsonb, total_count bigint +) +-- jit off: оценка стоимости большого плана включает JIT-компиляцию на сотни миллисекунд, а выигрыша нет. +LANGUAGE sql STABLE SET search_path TO 'v3', 'pg_catalog' SET jit TO off +AS $function$ +WITH empty_sections AS MATERIALIZED ( + SELECT + (to_jsonb(jsonb_populate_record(NULL::v3.plan, '{}'::jsonb)) - 'line_id') + || jsonb_build_object('plan_year', NULL) AS plan, + (to_jsonb(jsonb_populate_record(NULL::v3.sequestration, '{}'::jsonb)) + - 'id' - 'line_id' - 'actor') || jsonb_build_object('adj_year', NULL) AS sequestration, + (to_jsonb(jsonb_populate_record(NULL::v3.reserve, '{}'::jsonb)) - 'line_id') + || jsonb_build_object('amount_year', NULL) AS reserve, + jsonb_build_object( + 'approved_q1', NULL, 'approved_q2', NULL, + 'approved_q3', NULL, 'approved_q4', NULL, + 'approved_year', NULL + ) AS approved, + to_jsonb(jsonb_populate_record(NULL::v3.contract_summary, '{}'::jsonb)) - 'line_id' AS contract_summary, + to_jsonb(jsonb_populate_record(NULL::v3.allocation, '{}'::jsonb)) - 'line_id' AS allocation, + to_jsonb(jsonb_populate_record(NULL::v3.collegial_approval, '{}'::jsonb)) - 'line_id' AS collegial, + to_jsonb(jsonb_populate_record(NULL::v3.ckk, '{}'::jsonb)) - 'line_id' AS ckk, + to_jsonb(jsonb_populate_record(NULL::v3.contract_detail, '{}'::jsonb)) - 'line_id' AS contract_detail, + to_jsonb(jsonb_populate_record(NULL::v3.booking, '{}'::jsonb)) - 'id' - 'line_id' AS booking, + to_jsonb(jsonb_populate_record(NULL::v3.budget_line_quarter, '{}'::jsonb)) + - 'line_id' - 'quarter' AS budget_quarter +), numbered_lines AS ( + SELECT el.*, + count(*) OVER () AS total_count, + row_number() OVER ( + ORDER BY + CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'asc' + THEN el.org_name END ASC NULLS LAST, + CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'desc' + THEN el.org_name END DESC NULLS LAST, + CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'asc' + THEN el.item_id END ASC NULLS LAST, + CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'desc' + THEN el.item_id END DESC NULLS LAST, + CASE WHEN p_sort_by = 'section_code' AND lower(p_sort_direction) = 'asc' + THEN el.section_code END ASC NULLS LAST, + CASE WHEN p_sort_by = 'section_code' AND lower(p_sort_direction) = 'desc' + THEN el.section_code END DESC NULLS LAST, + el.form_type_code, el.form_id, el.line_id + ) AS sort_order + FROM v3.svod2_lines('RF', p_year, p_org_unit_ids, + NULLIF(v3.svod2_casefold(v3.svod2_js_trim(p_search)), '')) el + WHERE (NULLIF(v3.svod2_js_trim(v3.svod2_casefold(p_smeta_type)), '') IS NULL + OR (el.smeta_type IS NOT NULL + AND v3.svod2_js_trim(v3.svod2_casefold(el.smeta_type)) + = NULLIF(v3.svod2_js_trim(v3.svod2_casefold(p_smeta_type)), ''))) + AND (NULLIF(v3.svod2_js_trim(v3.svod2_casefold(p_smeta_direction)), '') IS NULL + OR (el.smeta_direction IS NOT NULL + AND v3.svod2_js_trim(v3.svod2_casefold(el.smeta_direction)) + = NULLIF(v3.svod2_js_trim(v3.svod2_casefold(p_smeta_direction)), ''))) + AND (NULLIF(v3.svod2_js_trim(v3.svod2_casefold(p_org_name)), '') IS NULL + OR (el.org_name IS NOT NULL + AND v3.svod2_js_trim(v3.svod2_casefold(el.org_name)) + = NULLIF(v3.svod2_js_trim(v3.svod2_casefold(p_org_name)), ''))) +), page AS MATERIALIZED ( + SELECT * FROM numbered_lines + ORDER BY sort_order + LIMIT p_limit OFFSET COALESCE(p_offset, 0) +), booking_data AS ( + SELECT b.line_id, + jsonb_agg(to_jsonb(b) - 'id' - 'line_id' ORDER BY b.quarter, b.source) AS booking + FROM page pg + JOIN v3.booking b ON b.line_id = pg.line_id + GROUP BY b.line_id +) +SELECT x.source_form, x.source_id, x.source_org_unit_id, x.org_name, x.smeta_type, + x.source_line_id, x.sort_order, + CASE WHEN p_compact THEN v3.svod2_compact(x.full_data) ELSE x.full_data END AS data, + x.total_count +FROM ( + SELECT + pg.form_type_code AS source_form, pg.form_id AS source_id, + pg.org_unit_id AS source_org_unit_id, pg.org_name, pg.smeta_type, + pg.line_id AS source_line_id, pg.sort_order, + jsonb_build_object( + 'line_id', bl.id, + 'header', jsonb_build_object( + 'section_code', ei.section_code, 'item_id', ei.item_id, + 'num_group', ei.num_group_id, 'name', COALESCE(bl.name, ei.name), + 'expense_item_id', ei.id, 'justification', bl.justification, + 'internal_order', bl.internal_order, 'vsp_id', bl.vsp_id, + 'project_id', bl.project_id, 'direction', bl.direction, + 'smeta_direction', CASE bl.direction + WHEN 'Support' THEN 'Поддержка' + WHEN 'Development' THEN 'Развитие' + ELSE NULL + END, + 'source_form', pg.form_type_code, 'source_id', pg.form_id, + 'source_org_unit_id', pg.org_unit_id, 'org_name', pg.org_name, + 'smeta_type', pg.smeta_type), + 'plan', COALESCE( + (to_jsonb(pl) - 'line_id') || jsonb_build_object( + 'plan_year', COALESCE(pl.plan_q1, 0) + COALESCE(pl.plan_q2, 0) + + COALESCE(pl.plan_q3, 0) + COALESCE(pl.plan_q4, 0)), + e.plan || jsonb_build_object('plan_year', NULL)), + 'sequestration', jsonb_build_object( + 'DFIP', COALESCE( + (to_jsonb(sd) - 'id' - 'line_id' - 'actor') || jsonb_build_object( + 'adj_year', COALESCE(sd.adj_q1, 0) + COALESCE(sd.adj_q2, 0) + + COALESCE(sd.adj_q3, 0) + COALESCE(sd.adj_q4, 0)), + e.sequestration || jsonb_build_object('adj_year', NULL)), + 'SSP_GO', COALESCE( + (to_jsonb(ss) - 'id' - 'line_id' - 'actor') || jsonb_build_object( + 'adj_year', COALESCE(ss.adj_q1, 0) + COALESCE(ss.adj_q2, 0) + + COALESCE(ss.adj_q3, 0) + COALESCE(ss.adj_q4, 0)), + e.sequestration || jsonb_build_object('adj_year', NULL))), + 'reserve', COALESCE( + (to_jsonb(rv) - 'line_id') || jsonb_build_object( + 'amount_year', COALESCE(rv.amount_q1, 0) + COALESCE(rv.amount_q2, 0) + + COALESCE(rv.amount_q3, 0) + COALESCE(rv.amount_q4, 0)), + e.reserve || jsonb_build_object('amount_year', NULL)), + 'approved', CASE WHEN pl.line_id IS NULL AND sd.id IS NULL + AND ss.id IS NULL AND rv.line_id IS NULL + THEN e.approved + ELSE jsonb_build_object( + 'approved_q1', COALESCE(pl.plan_q1, 0) + COALESCE(sd.adj_q1, 0) + + COALESCE(ss.adj_q1, 0) + COALESCE(rv.amount_q1, 0), + 'approved_q2', COALESCE(pl.plan_q2, 0) + COALESCE(sd.adj_q2, 0) + + COALESCE(ss.adj_q2, 0) + COALESCE(rv.amount_q2, 0), + 'approved_q3', COALESCE(pl.plan_q3, 0) + COALESCE(sd.adj_q3, 0) + + COALESCE(ss.adj_q3, 0) + COALESCE(rv.amount_q3, 0), + 'approved_q4', COALESCE(pl.plan_q4, 0) + COALESCE(sd.adj_q4, 0) + + COALESCE(ss.adj_q4, 0) + COALESCE(rv.amount_q4, 0), + 'approved_year', COALESCE(pl.plan_q1, 0) + COALESCE(pl.plan_q2, 0) + + COALESCE(pl.plan_q3, 0) + COALESCE(pl.plan_q4, 0) + + COALESCE(sd.adj_q1, 0) + COALESCE(sd.adj_q2, 0) + + COALESCE(sd.adj_q3, 0) + COALESCE(sd.adj_q4, 0) + + COALESCE(ss.adj_q1, 0) + COALESCE(ss.adj_q2, 0) + + COALESCE(ss.adj_q3, 0) + COALESCE(ss.adj_q4, 0) + + COALESCE(rv.amount_q1, 0) + COALESCE(rv.amount_q2, 0) + + COALESCE(rv.amount_q3, 0) + COALESCE(rv.amount_q4, 0)) END, + 'contract_summary', COALESCE(to_jsonb(cs) - 'line_id', e.contract_summary), + 'allocation', COALESCE(to_jsonb(al) - 'line_id', e.allocation), + 'collegial', COALESCE(to_jsonb(ca) - 'line_id', e.collegial), + 'ckk', COALESCE(to_jsonb(ck) - 'line_id', e.ckk), + 'contract_detail', COALESCE(to_jsonb(cd) - 'line_id', e.contract_detail) || jsonb_build_object( + 'expenses_q1', COALESCE(cd.expenses_q1, ck.expenses_q1), + 'expenses_q2', COALESCE(cd.expenses_q2, ck.expenses_q2), + 'expenses_q3', COALESCE(cd.expenses_q3, ck.expenses_q3), + 'expenses_q4', COALESCE(cd.expenses_q4, ck.expenses_q4)), + 'booking', COALESCE(bk.booking, jsonb_build_array(e.booking)), + 'q1', COALESCE(to_jsonb(q1) - 'line_id' - 'quarter', e.budget_quarter) || jsonb_build_object( + 'transfer_to_economy', ec.econ_q1, + 'booking_amount', COALESCE(q1.booking_amount, + CASE WHEN ei.sheet = 'AHR' THEN ck.expenses_q1 + WHEN ck.expenses_q1 > 0 THEN ck.expenses_q1 + ELSE cd.expenses_q1 END)), + 'q2', COALESCE(to_jsonb(q2) - 'line_id' - 'quarter', e.budget_quarter) || jsonb_build_object( + 'transfer_to_economy', ec.econ_q2, + 'booking_amount', COALESCE(q2.booking_amount, + CASE WHEN ei.sheet = 'AHR' THEN ck.expenses_q2 + WHEN ck.expenses_q2 > 0 THEN ck.expenses_q2 + ELSE cd.expenses_q2 END), + 'plan_revision_eco_change', COALESCE(q2.target_change, q2.plan_revision_eco_change), + 'plan_revision_item_adj', COALESCE(q2.base_plan_correction, q2.plan_revision_item_adj)), + 'q3', COALESCE(to_jsonb(q3) - 'line_id' - 'quarter', e.budget_quarter) || jsonb_build_object( + 'transfer_to_economy', ec.econ_q3, + 'booking_amount', COALESCE(q3.booking_amount, + CASE WHEN ei.sheet = 'AHR' THEN ck.expenses_q3 + WHEN ck.expenses_q3 > 0 THEN ck.expenses_q3 + ELSE cd.expenses_q3 END), + 'plan_revision_eco_change', COALESCE(q3.target_change, q3.plan_revision_eco_change), + 'plan_revision_item_adj', COALESCE(q3.base_plan_correction, q3.plan_revision_item_adj)), + 'q4', COALESCE(to_jsonb(q4) - 'line_id' - 'quarter', e.budget_quarter) || jsonb_build_object( + 'transfer_to_economy', ec.econ_q4, + 'booking_amount', COALESCE(q4.booking_amount, + CASE WHEN ei.sheet = 'AHR' THEN ck.expenses_q4 + WHEN ck.expenses_q4 > 0 THEN ck.expenses_q4 + ELSE cd.expenses_q4 END), + 'plan_revision_eco_change', COALESCE(q4.target_change, q4.plan_revision_eco_change), + 'plan_revision_item_adj', COALESCE(q4.base_plan_correction, q4.plan_revision_item_adj)) + ) AS full_data, + pg.total_count + FROM page pg + JOIN v3.budget_line bl ON bl.id = pg.line_id + JOIN v3.expense_item ei ON ei.id = bl.expense_item_id + LEFT JOIN v3.contract_detail cd ON cd.line_id = bl.id + CROSS JOIN empty_sections e + LEFT JOIN v3.plan pl ON pl.line_id = bl.id + LEFT JOIN v3.sequestration sd ON sd.line_id = bl.id AND sd.actor = 'DFIP' + LEFT JOIN v3.sequestration ss ON ss.line_id = bl.id AND ss.actor = 'SSP_GO' + LEFT JOIN v3.reserve rv ON rv.line_id = bl.id + LEFT JOIN v3.contract_summary cs ON cs.line_id = bl.id + LEFT JOIN v3.allocation al ON al.line_id = bl.id + LEFT JOIN v3.collegial_approval ca ON ca.line_id = bl.id + LEFT JOIN v3.ckk ck ON ck.line_id = bl.id + LEFT JOIN booking_data bk ON bk.line_id = bl.id + LEFT JOIN v3.budget_line_quarter q1 ON q1.line_id = bl.id AND q1.quarter = 1 + LEFT JOIN v3.budget_line_quarter q2 ON q2.line_id = bl.id AND q2.quarter = 2 + LEFT JOIN v3.budget_line_quarter q3 ON q3.line_id = bl.id AND q3.quarter = 3 + LEFT JOIN v3.budget_line_quarter q4 ON q4.line_id = bl.id AND q4.quarter = 4 + -- AURORA-1938 / #335: фонд экономии FORM_2 считается, а не читается из transfer_to_economy + -- (формулы v_form2_sheet_sections: cp - скорректированный план, aq - факт квартала). + CROSS JOIN LATERAL ( + SELECT + (COALESCE(pl.plan_q1,0)+COALESCE(sd.adj_q1,0)+COALESCE(ss.adj_q1,0)+COALESCE(rv.amount_q1,0)+COALESCE(q1.adj_current,0)+COALESCE(q1.adj_ssp,0)+COALESCE(q1.adj_reserve,0)-(COALESCE(q1.actual_m1,0)+COALESCE(q1.actual_m2,0)+COALESCE(q1.actual_m3,0)) + -(COALESCE(q1.transfer_delay_acts,0)+COALESCE(q1.transfer_delay_procurement,0)+COALESCE(q1.transfer_economy_rf,0))-COALESCE(q1.transfer_to_q3,0)-COALESCE(q1.transfer_to_q4,0)) AS econ_q1, + (COALESCE(pl.plan_q2,0)+COALESCE(sd.adj_q2,0)+COALESCE(ss.adj_q2,0)+COALESCE(rv.amount_q2,0)+COALESCE(q1.transfer_to_q2,0)+COALESCE(q2.plan_revision_increase,0)+COALESCE(q2.plan_revision_sequester,0)+COALESCE(q2.base_plan_correction,0)+COALESCE(q2.adj_current,0)+COALESCE(q2.adj_ssp,0)+COALESCE(q2.adj_reserve,0)-(COALESCE(q2.actual_m1,0)+COALESCE(q2.actual_m2,0)+COALESCE(q2.actual_m3,0)) + -(COALESCE(q2.transfer_delay_acts,0)+COALESCE(q2.transfer_delay_procurement,0)+COALESCE(q2.transfer_economy_rf,0))-COALESCE(q2.transfer_to_q4,0)) AS econ_q2, + (COALESCE(pl.plan_q3,0)+COALESCE(sd.adj_q3,0)+COALESCE(ss.adj_q3,0)+COALESCE(rv.amount_q3,0)+COALESCE(q1.transfer_to_q3,0)+COALESCE(q2.transfer_to_q3,0)+COALESCE(q3.plan_revision_increase,0)+COALESCE(q3.plan_revision_sequester,0)+COALESCE(q3.base_plan_correction,0)+COALESCE(q3.adj_current,0)+COALESCE(q3.adj_ssp,0)+COALESCE(q3.adj_reserve,0)-(COALESCE(q3.actual_m1,0)+COALESCE(q3.actual_m2,0)+COALESCE(q3.actual_m3,0)) + -(COALESCE(q3.transfer_delay_acts,0)+COALESCE(q3.transfer_delay_procurement,0)+COALESCE(q3.transfer_economy_rf,0))) AS econ_q3, + (COALESCE(pl.plan_q4,0)+COALESCE(sd.adj_q4,0)+COALESCE(ss.adj_q4,0)+COALESCE(rv.amount_q4,0)+COALESCE(q1.transfer_to_q4,0)+COALESCE(q2.transfer_to_q4,0)+COALESCE(q3.transfer_to_q4,0)+COALESCE(q4.plan_revision_increase,0)+COALESCE(q4.plan_revision_sequester,0)+COALESCE(q4.base_plan_correction,0)+COALESCE(q4.adj_current,0)+COALESCE(q4.adj_ssp,0)+COALESCE(q4.adj_reserve,0) + -(COALESCE(q4.actual_m1,0)+COALESCE(q4.actual_m2,0)+COALESCE(q4.actual_m3,0)+COALESCE(q4.actual_spod,0))) AS econ_q4 + ) ec +) x +ORDER BY x.sort_order; +$function$; + diff --git a/api/tests/fixtures/svod2_legacy_golden.json b/api/tests/fixtures/svod2_legacy_golden.json index 1217f98..8d1c22a 100644 --- a/api/tests/fixtures/svod2_legacy_golden.json +++ b/api/tests/fixtures/svod2_legacy_golden.json @@ -1771,7 +1771,7 @@ } }, "RF-admin_all": { - "sha256": "16f6324ffdbd750f099cd7879ed15e8c968424cf65c9ad9e54ee4b529050ee49", + "sha256": "80833aca8a902058e030f77db86e24bd7592816e58dc669d7834abe5bef23cef", "body": { "success": true, "message": null, @@ -1780,10 +1780,18 @@ "row_type": "INPUT", "depth": 3, "data": { - "q1": {}, - "q2": {}, - "q3": {}, - "q4": {}, + "q1": { + "transfer_to_economy": 300 + }, + "q2": { + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 + }, "ckk": {}, "plan": { "plan_q1": 300, @@ -1833,13 +1841,19 @@ "depth": 3, "data": { "q1": { - "booking_amount": 777.25 + "booking_amount": 777.25, + "transfer_to_economy": 0 }, "q2": { - "booking_amount": 5 + "booking_amount": 5, + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 }, - "q3": {}, - "q4": {}, "ckk": { "expenses_q1": 777.25, "expenses_q2": 5, @@ -1884,10 +1898,18 @@ "row_type": "INPUT", "depth": 3, "data": { - "q1": {}, - "q2": {}, - "q3": {}, - "q4": {}, + "q1": { + "transfer_to_economy": 42 + }, + "q2": { + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 + }, "ckk": {}, "plan": { "plan_q1": 42, @@ -1934,10 +1956,18 @@ "row_type": "INPUT", "depth": 3, "data": { - "q1": {}, - "q2": {}, - "q3": {}, - "q4": {}, + "q1": { + "transfer_to_economy": 9 + }, + "q2": { + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 + }, "ckk": {}, "plan": { "plan_q1": 9, @@ -1987,7 +2017,7 @@ } }, "RF-admin_one": { - "sha256": "a463b0b1858f4333b27713987cef3ddfdf9eb207df84122c41f654f58620b504", + "sha256": "a9bf8e54d4aef2f7f2fe0e5951d762bb3f2dadc00cd689f3d257aac0465cd5e6", "body": { "success": true, "message": null, @@ -1996,10 +2026,18 @@ "row_type": "INPUT", "depth": 3, "data": { - "q1": {}, - "q2": {}, - "q3": {}, - "q4": {}, + "q1": { + "transfer_to_economy": 300 + }, + "q2": { + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 + }, "ckk": {}, "plan": { "plan_q1": 300, @@ -2049,13 +2087,19 @@ "depth": 3, "data": { "q1": { - "booking_amount": 777.25 + "booking_amount": 777.25, + "transfer_to_economy": 0 }, "q2": { - "booking_amount": 5 + "booking_amount": 5, + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 }, - "q3": {}, - "q4": {}, "ckk": { "expenses_q1": 777.25, "expenses_q2": 5, @@ -2100,10 +2144,18 @@ "row_type": "INPUT", "depth": 3, "data": { - "q1": {}, - "q2": {}, - "q3": {}, - "q4": {}, + "q1": { + "transfer_to_economy": 42 + }, + "q2": { + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 + }, "ckk": {}, "plan": { "plan_q1": 42, @@ -2151,7 +2203,7 @@ } }, "RF-admin_page50": { - "sha256": "16f6324ffdbd750f099cd7879ed15e8c968424cf65c9ad9e54ee4b529050ee49", + "sha256": "80833aca8a902058e030f77db86e24bd7592816e58dc669d7834abe5bef23cef", "body": { "success": true, "message": null, @@ -2160,10 +2212,18 @@ "row_type": "INPUT", "depth": 3, "data": { - "q1": {}, - "q2": {}, - "q3": {}, - "q4": {}, + "q1": { + "transfer_to_economy": 300 + }, + "q2": { + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 + }, "ckk": {}, "plan": { "plan_q1": 300, @@ -2213,13 +2273,19 @@ "depth": 3, "data": { "q1": { - "booking_amount": 777.25 + "booking_amount": 777.25, + "transfer_to_economy": 0 }, "q2": { - "booking_amount": 5 + "booking_amount": 5, + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 }, - "q3": {}, - "q4": {}, "ckk": { "expenses_q1": 777.25, "expenses_q2": 5, @@ -2264,10 +2330,18 @@ "row_type": "INPUT", "depth": 3, "data": { - "q1": {}, - "q2": {}, - "q3": {}, - "q4": {}, + "q1": { + "transfer_to_economy": 42 + }, + "q2": { + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 + }, "ckk": {}, "plan": { "plan_q1": 42, @@ -2314,10 +2388,18 @@ "row_type": "INPUT", "depth": 3, "data": { - "q1": {}, - "q2": {}, - "q3": {}, - "q4": {}, + "q1": { + "transfer_to_economy": 9 + }, + "q2": { + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 + }, "ckk": {}, "plan": { "plan_q1": 9, @@ -2367,7 +2449,7 @@ } }, "RF-admin_two": { - "sha256": "f96f208a936ce1b609f7f940399152353aafc89953052ef9f312a3b7159a468f", + "sha256": "d3080fd50d726df99044450b08ecbc2a390b8627883d0cfca3b3f779227bc5b6", "body": { "success": true, "message": null, @@ -2376,10 +2458,18 @@ "row_type": "INPUT", "depth": 3, "data": { - "q1": {}, - "q2": {}, - "q3": {}, - "q4": {}, + "q1": { + "transfer_to_economy": 300 + }, + "q2": { + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 + }, "ckk": {}, "plan": { "plan_q1": 300, @@ -2429,13 +2519,19 @@ "depth": 3, "data": { "q1": { - "booking_amount": 777.25 + "booking_amount": 777.25, + "transfer_to_economy": 0 }, "q2": { - "booking_amount": 5 + "booking_amount": 5, + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 }, - "q3": {}, - "q4": {}, "ckk": { "expenses_q1": 777.25, "expenses_q2": 5, @@ -2481,7 +2577,7 @@ } }, "RF-isp_all": { - "sha256": "a463b0b1858f4333b27713987cef3ddfdf9eb207df84122c41f654f58620b504", + "sha256": "a9bf8e54d4aef2f7f2fe0e5951d762bb3f2dadc00cd689f3d257aac0465cd5e6", "body": { "success": true, "message": null, @@ -2490,10 +2586,18 @@ "row_type": "INPUT", "depth": 3, "data": { - "q1": {}, - "q2": {}, - "q3": {}, - "q4": {}, + "q1": { + "transfer_to_economy": 300 + }, + "q2": { + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 + }, "ckk": {}, "plan": { "plan_q1": 300, @@ -2543,13 +2647,19 @@ "depth": 3, "data": { "q1": { - "booking_amount": 777.25 + "booking_amount": 777.25, + "transfer_to_economy": 0 }, "q2": { - "booking_amount": 5 + "booking_amount": 5, + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 }, - "q3": {}, - "q4": {}, "ckk": { "expenses_q1": 777.25, "expenses_q2": 5, @@ -2594,10 +2704,18 @@ "row_type": "INPUT", "depth": 3, "data": { - "q1": {}, - "q2": {}, - "q3": {}, - "q4": {}, + "q1": { + "transfer_to_economy": 42 + }, + "q2": { + "transfer_to_economy": 0 + }, + "q3": { + "transfer_to_economy": 0 + }, + "q4": { + "transfer_to_economy": 0 + }, "ckk": {}, "plan": { "plan_q1": 42, diff --git a/api/tests/integration/test_form2_transfer_econ.py b/api/tests/integration/test_form2_transfer_econ.py index 74c0349..ff4b6b8 100644 --- a/api/tests/integration/test_form2_transfer_econ.py +++ b/api/tests/integration/test_form2_transfer_econ.py @@ -173,3 +173,62 @@ def test_form2_transfer_econ_cell_is_read_only(client, headers, svod2_sql, colum ) assert r.status_code in (400, 422), r.text assert "computed_field" in r.json()["message"] + + +# --- Свод 2 («РФ» = FORM_2) показывает тот же расчётный фонд экономии, что лист --- + +def _svod2_rf_quarters(svod2_sql, year, line_id): + rows = svod2_sql( + "SELECT data FROM v3.v_svod2_rf(:y, NULL, NULL, NULL, 'org_name', 'asc', " + "NULL, NULL, NULL, NULL, false) WHERE source_line_id = :l", + {"y": year, "l": line_id}, + ) + assert len(rows) == 1 + data = rows[0][0] + return [data[q].get("transfer_to_economy") for q in ("q1", "q2", "q3", "q4")] + + +def _sheet_econ(client, headers, form_id, sheet, line_id): + d = _input(_rows(client, headers, form_id, sheet), line_id) + return [d[q].get("transfer_econ") for q in ("q1", "q2", "q3", "q4")] + + +def test_svod2_rf_transfer_econ_ticket_repro(client, headers, svod2_sql): + form_id = _new_form(client, headers, 91030) + line_id = _add_line(client, headers, form_id, "AHR", _leaf_items(svod2_sql, "AHR", 1)[0]) + for col, val in [("plan.q1", 1000), ("plan.q2", 2000), ("plan.q3", 3000)]: + _set(client, headers, form_id, "AHR", line_id, col, val) + + assert _svod2_rf_quarters(svod2_sql, 91030, line_id) == [1000, 2000, 3000, 0] + assert _svod2_rf_quarters(svod2_sql, 91030, line_id) == _sheet_econ(client, headers, form_id, "AHR", line_id) + + +def test_svod2_rf_transfer_econ_subcolumns_match_sheet(client, headers, svod2_sql): + form_id = _new_form(client, headers, 91031) + line_id = _add_line(client, headers, form_id, "AHR", _leaf_items(svod2_sql, "AHR", 1)[0]) + for col, val in [ + ("plan.q1", 1000), ("plan.q2", 2000), ("plan.q3", 3000), + ("q1.transfer_q2_delay_acts", 100), ("q1.transfer_q3", 200), ("q1.transfer_q4", 300), + ("q2.transfer_q3_delay_acts", 400), ("q2.transfer_q4", 500), + ("q3.transfer_q4_delay_acts", 600), + ]: + _set(client, headers, form_id, "AHR", line_id, col, val) + + assert _svod2_rf_quarters(svod2_sql, 91031, line_id) == [400, 1100, 2600, 800] + assert _svod2_rf_quarters(svod2_sql, 91031, line_id) == _sheet_econ(client, headers, form_id, "AHR", line_id) + + +def test_svod2_rf_ignores_raw_transfer_to_economy(client, headers, svod2_sql): + form_id = _new_form(client, headers, 91032) + line_id = _add_line(client, headers, form_id, "AHR", _leaf_items(svod2_sql, "AHR", 1)[0]) + for col, val in [("plan.q1", 1000), ("plan.q2", 2000), ("plan.q3", 3000)]: + _set(client, headers, form_id, "AHR", line_id, col, val) + for quarter in (1, 2, 3, 4): + svod2_sql( + "INSERT INTO v3.budget_line_quarter (line_id, quarter, transfer_to_economy) " + "VALUES (:l, :q, 777) ON CONFLICT (line_id, quarter) " + "DO UPDATE SET transfer_to_economy = 777", + {"l": line_id, "q": quarter}, + ) + + assert _svod2_rf_quarters(svod2_sql, 91032, line_id) == [1000, 2000, 3000, 0] -- 2.47.2 From 7f18b0ae9df9d294cc600934569a8ef0bee60d18 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Thu, 12 Nov 2026 18:37:22 +0400 Subject: [PATCH 4/4] =?UTF-8?q?fix(migrations):=200063=20=D0=BF=D0=BE?= =?UTF-8?q?=D1=81=D0=BB=D0=B5=200062=20=E2=80=94=20=D0=B2=20test=20=D0=B2?= =?UTF-8?q?=D0=BB=D0=B8=D1=82=D1=8B=200060=E2=80=930062=20(AURORA-1938,=20?= =?UTF-8?q?#335)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Иначе после мержа у alembic две головы (0062 и 0063) и upgrade head падает. Функции 0063 (_apply_form_cell, v_form2/4_sheet_sections, v_svod2_rf) в 0060–0062 не переопределяются. Co-Authored-By: Claude Opus 5.5 --- api/alembic/versions/0063_form2_transfer_econ_calc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/alembic/versions/0063_form2_transfer_econ_calc.py b/api/alembic/versions/0063_form2_transfer_econ_calc.py index de24bfb..b7bc5c0 100644 --- a/api/alembic/versions/0063_form2_transfer_econ_calc.py +++ b/api/alembic/versions/0063_form2_transfer_econ_calc.py @@ -2,7 +2,7 @@ from sql_migration import run_sql_file_migration revision = "0063" -down_revision = "0059" +down_revision = "0062" branch_labels = None depends_on = None -- 2.47.2