From f331c6f01f80d0e924a16a2b744d5c53ba13a1a2 Mon Sep 17 00:00:00 2001 From: tsygankoviva Date: Thu, 1 Oct 2026 13:26:42 +0300 Subject: [PATCH] =?UTF-8?q?form3-source:=20=D0=B8=D1=81=D1=82=D0=BE=D1=87?= =?UTF-8?q?=D0=BD=D0=B8=D0=BA=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B2=20=D1=81=D0=B2=D0=BE=D0=B4=D0=B5=20=D1=84=D0=BE=D1=80?= =?UTF-8?q?=D0=BC=D1=8B=203?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../versions/0046_project_summary_source.py | 15 ++ .../sql/0046_project_summary_source.sql | 186 ++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 api/alembic/versions/0046_project_summary_source.py create mode 100644 api/alembic/versions/sql/0046_project_summary_source.sql diff --git a/api/alembic/versions/0046_project_summary_source.py b/api/alembic/versions/0046_project_summary_source.py new file mode 100644 index 0000000..e31baa5 --- /dev/null +++ b/api/alembic/versions/0046_project_summary_source.py @@ -0,0 +1,15 @@ +from sql_migration import run_sql_file_migration + + +revision = "0046" +down_revision = "0045" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + run_sql_file_migration("0046_project_summary_source.sql") + + +def downgrade() -> None: + pass diff --git a/api/alembic/versions/sql/0046_project_summary_source.sql b/api/alembic/versions/sql/0046_project_summary_source.sql new file mode 100644 index 0000000..c30c456 --- /dev/null +++ b/api/alembic/versions/sql/0046_project_summary_source.sql @@ -0,0 +1,186 @@ +-- DROP FUNCTION v3.v_project_summary_all_jsonb(integer, integer[], character varying); + +CREATE OR REPLACE FUNCTION v3.v_project_summary_all_jsonb( + p_year integer, + p_org_unit_ids integer[] DEFAULT NULL, + p_project_type character varying DEFAULT NULL +) +RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb) +LANGUAGE sql +STABLE +AS $function$ +WITH + filtered_projects AS ( + SELECT DISTINCT p.id + FROM v3.project p + JOIN v3.rf_project_year rpy + ON rpy.project_id = p.id + AND rpy.year = p_year + WHERE (p_org_unit_ids IS NULL OR p.org_unit_id = ANY(p_org_unit_ids)) + AND (p_project_type IS NULL OR p.project_type = p_project_type) + ), + form3_items AS ( + SELECT + t.id, + t.depth, + t.path, + t.parent_item_id, + t.desc_ids, + t.item_id, + t.num_group_id, + t.name + FROM v3.mv_expense_item_tree t + JOIN v3.expense_item_form_type eft + ON eft.expense_item_id = t.id + AND eft.form_type_code = 'FORM_3' + ), + matching_reports AS ( + SELECT r.id, r.report_type + FROM filtered_projects fp + JOIN v3.rf_project_report r + ON r.project_id = fp.id + JOIN v3.rf_project_year rpy + ON rpy.id = r.rf_project_year_id + AND rpy.year = p_year + ), + line_values AS ( + SELECT + l.expense_item_id, + mr.report_type, + q.quarter, + SUM(q.base_plan) AS base_plan, + SUM(q.corrected_plan) AS corrected_plan, + SUM(q.adj_by_items) AS adj_by_items, + SUM(q.adj_increase) AS adj_increase, + SUM(q.actual_m1) AS actual_m1, + SUM(q.actual_m2) AS actual_m2, + SUM(q.actual_m3) AS actual_m3, + SUM( + COALESCE(q.actual_m1, 0) + + COALESCE(q.actual_m2, 0) + + COALESCE(q.actual_m3, 0) + + CASE WHEN q.quarter = 4 THEN COALESCE(q.actual_spod, 0) ELSE 0 END + ) AS actual_amount + FROM matching_reports mr + JOIN v3.rf_project_report_line l + ON l.rf_project_report_id = mr.id + JOIN v3.rf_project_report_quarter q + ON q.rf_project_report_line_id = l.id + GROUP BY l.expense_item_id, mr.report_type, q.quarter + ), + aggregated AS ( + SELECT + fi.id AS expense_item_id, + CASE WHEN fi.depth < 3 THEN fi.item_id ELSE fi.parent_item_id END AS section_code, + CASE WHEN fi.depth = 3 THEN fi.item_id ELSE NULL END AS item_id, + fi.num_group_id, + fi.name, + fi.depth, + fi.path, + COALESCE(SUM(lv.base_plan) FILTER (WHERE lv.report_type = 'LIMIT' AND lv.quarter = 1), 0) AS base_limit_q1, + COALESCE(SUM(lv.base_plan) FILTER (WHERE lv.report_type = 'LIMIT' AND lv.quarter = 2), 0) AS base_limit_q2, + COALESCE(SUM(lv.base_plan) FILTER (WHERE lv.report_type = 'LIMIT' AND lv.quarter = 3), 0) AS base_limit_q3, + COALESCE(SUM(lv.base_plan) FILTER (WHERE lv.report_type = 'LIMIT' AND lv.quarter = 4), 0) AS base_limit_q4, + COALESCE(SUM(lv.base_plan) FILTER (WHERE lv.report_type = 'CURRENT_EXPENSES' AND lv.quarter = 1), 0) AS base_current_q1, + COALESCE(SUM(lv.base_plan) FILTER (WHERE lv.report_type = 'CURRENT_EXPENSES' AND lv.quarter = 2), 0) AS base_current_q2, + COALESCE(SUM(lv.base_plan) FILTER (WHERE lv.report_type = 'CURRENT_EXPENSES' AND lv.quarter = 3), 0) AS base_current_q3, + COALESCE(SUM(lv.base_plan) FILTER (WHERE lv.report_type = 'CURRENT_EXPENSES' AND lv.quarter = 4), 0) AS base_current_q4, + COALESCE(SUM(lv.corrected_plan) FILTER (WHERE lv.report_type = 'LIMIT' AND lv.quarter = 2), 0) AS correction_limit_q2, + COALESCE(SUM(lv.corrected_plan) FILTER (WHERE lv.report_type = 'LIMIT' AND lv.quarter = 3), 0) AS correction_limit_q3, + COALESCE(SUM(lv.corrected_plan) FILTER (WHERE lv.report_type = 'LIMIT' AND lv.quarter = 4), 0) AS correction_limit_q4, + COALESCE(SUM(lv.corrected_plan) FILTER (WHERE lv.report_type = 'CURRENT_EXPENSES' AND lv.quarter = 2), 0) AS correction_current_q2, + COALESCE(SUM(lv.corrected_plan) FILTER (WHERE lv.report_type = 'CURRENT_EXPENSES' AND lv.quarter = 3), 0) AS correction_current_q3, + COALESCE(SUM(lv.corrected_plan) FILTER (WHERE lv.report_type = 'CURRENT_EXPENSES' AND lv.quarter = 4), 0) AS correction_current_q4, + COALESCE(SUM(lv.actual_amount) FILTER (WHERE lv.report_type = 'LIMIT' AND lv.quarter = 1), 0) AS fact_limit_q1, + COALESCE(SUM(lv.actual_amount) FILTER (WHERE lv.report_type = 'LIMIT' AND lv.quarter = 2), 0) AS fact_limit_q2, + COALESCE(SUM(lv.actual_amount) FILTER (WHERE lv.report_type = 'LIMIT' AND lv.quarter = 3), 0) AS fact_limit_q3, + COALESCE(SUM(lv.actual_amount) FILTER (WHERE lv.report_type = 'LIMIT' AND lv.quarter = 4), 0) AS fact_limit_q4, + COALESCE(SUM(lv.actual_amount) FILTER (WHERE lv.report_type = 'CURRENT_EXPENSES' AND lv.quarter = 1), 0) AS fact_current_q1, + COALESCE(SUM(lv.actual_amount) FILTER (WHERE lv.report_type = 'CURRENT_EXPENSES' AND lv.quarter = 2), 0) AS fact_current_q2, + COALESCE(SUM(lv.actual_amount) FILTER (WHERE lv.report_type = 'CURRENT_EXPENSES' AND lv.quarter = 3), 0) AS fact_current_q3, + COALESCE(SUM(lv.actual_amount) FILTER (WHERE lv.report_type = 'CURRENT_EXPENSES' AND lv.quarter = 4), 0) AS fact_current_q4, + COALESCE(SUM(lv.adj_by_items) FILTER (WHERE lv.quarter = 1), 0) AS detail_adj_by_items_q1, + COALESCE(SUM(lv.adj_by_items) FILTER (WHERE lv.quarter = 2), 0) AS detail_adj_by_items_q2, + COALESCE(SUM(lv.adj_by_items) FILTER (WHERE lv.quarter = 3), 0) AS detail_adj_by_items_q3, + COALESCE(SUM(lv.adj_by_items) FILTER (WHERE lv.quarter = 4), 0) AS detail_adj_by_items_q4, + COALESCE(SUM(lv.adj_increase) FILTER (WHERE lv.quarter = 1), 0) AS detail_adj_increase_q1, + COALESCE(SUM(lv.adj_increase) FILTER (WHERE lv.quarter = 2), 0) AS detail_adj_increase_q2, + COALESCE(SUM(lv.adj_increase) FILTER (WHERE lv.quarter = 3), 0) AS detail_adj_increase_q3, + COALESCE(SUM(lv.adj_increase) FILTER (WHERE lv.quarter = 4), 0) AS detail_adj_increase_q4, + COALESCE(SUM(lv.actual_m1) FILTER (WHERE lv.quarter = 1), 0) AS detail_actual_m1_q1, + COALESCE(SUM(lv.actual_m1) FILTER (WHERE lv.quarter = 2), 0) AS detail_actual_m1_q2, + COALESCE(SUM(lv.actual_m1) FILTER (WHERE lv.quarter = 3), 0) AS detail_actual_m1_q3, + COALESCE(SUM(lv.actual_m1) FILTER (WHERE lv.quarter = 4), 0) AS detail_actual_m1_q4, + COALESCE(SUM(lv.actual_m2) FILTER (WHERE lv.quarter = 1), 0) AS detail_actual_m2_q1, + COALESCE(SUM(lv.actual_m2) FILTER (WHERE lv.quarter = 2), 0) AS detail_actual_m2_q2, + COALESCE(SUM(lv.actual_m2) FILTER (WHERE lv.quarter = 3), 0) AS detail_actual_m2_q3, + COALESCE(SUM(lv.actual_m2) FILTER (WHERE lv.quarter = 4), 0) AS detail_actual_m2_q4, + COALESCE(SUM(lv.actual_m3) FILTER (WHERE lv.quarter = 1), 0) AS detail_actual_m3_q1, + COALESCE(SUM(lv.actual_m3) FILTER (WHERE lv.quarter = 2), 0) AS detail_actual_m3_q2, + COALESCE(SUM(lv.actual_m3) FILTER (WHERE lv.quarter = 3), 0) AS detail_actual_m3_q3, + COALESCE(SUM(lv.actual_m3) FILTER (WHERE lv.quarter = 4), 0) AS detail_actual_m3_q4 + FROM form3_items fi + LEFT JOIN line_values lv + ON lv.expense_item_id = ANY(fi.desc_ids) + GROUP BY fi.id, fi.depth, fi.path, fi.parent_item_id, fi.item_id, fi.num_group_id, fi.name + ) +SELECT + CASE a.depth WHEN 0 THEN 'ROOT' WHEN 1 THEN 'GROUP' WHEN 2 THEN 'ITEM' ELSE 'SUB_ITEM' END::varchar AS row_type, + COALESCE(a.depth, 0)::integer AS depth, + ROW_NUMBER() OVER (ORDER BY a.path) AS sort_order, + jsonb_build_object( + 'header', jsonb_build_object( + 'expense_item_id', a.expense_item_id, + 'source', ei.sheet, + 'section_code', a.section_code, + 'item_id', a.item_id, + 'num_group_id', a.num_group_id, + 'name', a.name, + 'year', p_year + ), + 'base', jsonb_build_object( + 'limit', jsonb_build_object('q1', a.base_limit_q1, 'q2', a.base_limit_q2, 'q3', a.base_limit_q3, 'q4', a.base_limit_q4, 'year', a.base_limit_q1 + a.base_limit_q2 + a.base_limit_q3 + a.base_limit_q4), + 'current', jsonb_build_object('q1', a.base_current_q1, 'q2', a.base_current_q2, 'q3', a.base_current_q3, 'q4', a.base_current_q4, 'year', a.base_current_q1 + a.base_current_q2 + a.base_current_q3 + a.base_current_q4), + 'total', jsonb_build_object( + 'q1', a.base_limit_q1 + a.base_current_q1, + 'q2', a.base_limit_q2 + a.base_current_q2, + 'q3', a.base_limit_q3 + a.base_current_q3, + 'q4', a.base_limit_q4 + a.base_current_q4, + 'year', a.base_limit_q1 + a.base_limit_q2 + a.base_limit_q3 + a.base_limit_q4 + a.base_current_q1 + a.base_current_q2 + a.base_current_q3 + a.base_current_q4 + ) + ), + 'correction', jsonb_build_object( + 'limit', jsonb_build_object('q2', a.correction_limit_q2, 'q3', a.correction_limit_q3, 'q4', a.correction_limit_q4, 'year', a.correction_limit_q2 + a.correction_limit_q3 + a.correction_limit_q4), + 'current', jsonb_build_object('q2', a.correction_current_q2, 'q3', a.correction_current_q3, 'q4', a.correction_current_q4, 'year', a.correction_current_q2 + a.correction_current_q3 + a.correction_current_q4), + 'total', jsonb_build_object( + 'q2', a.correction_limit_q2 + a.correction_current_q2, + 'q3', a.correction_limit_q3 + a.correction_current_q3, + 'q4', a.correction_limit_q4 + a.correction_current_q4, + 'year', a.correction_limit_q2 + a.correction_limit_q3 + a.correction_limit_q4 + a.correction_current_q2 + a.correction_current_q3 + a.correction_current_q4 + ) + ), + 'fact', jsonb_build_object( + 'limit', jsonb_build_object('q1', a.fact_limit_q1, 'q2', a.fact_limit_q2, 'q3', a.fact_limit_q3, 'q4', a.fact_limit_q4, 'year', a.fact_limit_q1 + a.fact_limit_q2 + a.fact_limit_q3 + a.fact_limit_q4), + 'current', jsonb_build_object('q1', a.fact_current_q1, 'q2', a.fact_current_q2, 'q3', a.fact_current_q3, 'q4', a.fact_current_q4, 'year', a.fact_current_q1 + a.fact_current_q2 + a.fact_current_q3 + a.fact_current_q4), + 'total', jsonb_build_object( + 'q1', a.fact_limit_q1 + a.fact_current_q1, + 'q2', a.fact_limit_q2 + a.fact_current_q2, + 'q3', a.fact_limit_q3 + a.fact_current_q3, + 'q4', a.fact_limit_q4 + a.fact_current_q4, + 'year', a.fact_limit_q1 + a.fact_limit_q2 + a.fact_limit_q3 + a.fact_limit_q4 + a.fact_current_q1 + a.fact_current_q2 + a.fact_current_q3 + a.fact_current_q4 + ) + ), + 'detail', jsonb_build_object( + 'q1', jsonb_build_object('adj_by_items', a.detail_adj_by_items_q1, 'adj_increase', a.detail_adj_increase_q1, 'actual_m1', a.detail_actual_m1_q1, 'actual_m2', a.detail_actual_m2_q1, 'actual_m3', a.detail_actual_m3_q1, 'quarter', a.detail_actual_m1_q1 + a.detail_actual_m2_q1 + a.detail_actual_m3_q1, 'residual', a.detail_adj_by_items_q1 + a.detail_adj_increase_q1 - (a.detail_actual_m1_q1 + a.detail_actual_m2_q1 + a.detail_actual_m3_q1)), + 'q2', jsonb_build_object('adj_by_items', a.detail_adj_by_items_q2, 'adj_increase', a.detail_adj_increase_q2, 'actual_m1', a.detail_actual_m1_q2, 'actual_m2', a.detail_actual_m2_q2, 'actual_m3', a.detail_actual_m3_q2, 'quarter', a.detail_actual_m1_q2 + a.detail_actual_m2_q2 + a.detail_actual_m3_q2, 'residual', a.detail_adj_by_items_q2 + a.detail_adj_increase_q2 - (a.detail_actual_m1_q2 + a.detail_actual_m2_q2 + a.detail_actual_m3_q2)), + 'q3', jsonb_build_object('adj_by_items', a.detail_adj_by_items_q3, 'adj_increase', a.detail_adj_increase_q3, 'actual_m1', a.detail_actual_m1_q3, 'actual_m2', a.detail_actual_m2_q3, 'actual_m3', a.detail_actual_m3_q3, 'quarter', a.detail_actual_m1_q3 + a.detail_actual_m2_q3 + a.detail_actual_m3_q3, 'residual', a.detail_adj_by_items_q3 + a.detail_adj_increase_q3 - (a.detail_actual_m1_q3 + a.detail_actual_m2_q3 + a.detail_actual_m3_q3)), + 'q4', jsonb_build_object('adj_by_items', a.detail_adj_by_items_q4, 'adj_increase', a.detail_adj_increase_q4, 'actual_m1', a.detail_actual_m1_q4, 'actual_m2', a.detail_actual_m2_q4, 'actual_m3', a.detail_actual_m3_q4, 'quarter', a.detail_actual_m1_q4 + a.detail_actual_m2_q4 + a.detail_actual_m3_q4, 'residual', a.detail_adj_by_items_q4 + a.detail_adj_increase_q4 - (a.detail_actual_m1_q4 + a.detail_actual_m2_q4 + a.detail_actual_m3_q4)), + 'year', (a.detail_actual_m1_q1 + a.detail_actual_m2_q1 + a.detail_actual_m3_q1) + (a.detail_actual_m1_q2 + a.detail_actual_m2_q2 + a.detail_actual_m3_q2) + (a.detail_actual_m1_q3 + a.detail_actual_m2_q3 + a.detail_actual_m3_q3) + (a.detail_actual_m1_q4 + a.detail_actual_m2_q4 + a.detail_actual_m3_q4) + ) + ) AS data +FROM aggregated a +LEFT JOIN v3.expense_item ei ON ei.id = a.expense_item_id +ORDER BY a.path; +$function$ +; + -- 2.47.2