2465 lines
129 KiB
PL/PgSQL
2465 lines
129 KiB
PL/PgSQL
-- удаление проектов
|
||
CREATE OR REPLACE FUNCTION v3.can_delete_project(p_project_id integer)
|
||
RETURNS BOOL
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old RECORD;
|
||
v_actor_id INTEGER;
|
||
v_role VARCHAR;
|
||
-- v_phase v3.form3_phase;
|
||
BEGIN
|
||
v_actor_id := v3.current_user_id();
|
||
v_role := v3.user_role_code(v_actor_id);
|
||
|
||
SELECT p.name, p.level, p.org_unit_id, p.created_at
|
||
INTO v_old
|
||
FROM v3.project p WHERE p.id = p_project_id;
|
||
|
||
IF v_role != 'ADMIN' AND NOT EXISTS (
|
||
SELECT 1 FROM v3.user_org uo
|
||
WHERE uo.org_unit_id = v_old.org_unit_id AND uo.user_id = v_actor_id
|
||
) THEN
|
||
RETURN false;
|
||
END IF;
|
||
|
||
IF (
|
||
select count(*) from (
|
||
select rpr.id, max(fp.closes_at) from v3.form3_phase fp
|
||
inner join v3.rf_project_report rpr
|
||
on rpr.id = fp.rf_project_report_id
|
||
and rpr.project_id = p_project_id
|
||
and fp.opens_at <= current_timestamp
|
||
and current_timestamp <= fp.closes_at
|
||
and fp.opens_at <= v_old.created_at
|
||
and v_old.created_at <= fp.closes_at
|
||
group by rpr.id
|
||
)
|
||
) = (select COUNT(*) from v3.rf_project_report rpr where rpr.project_id = p_project_id) THEN
|
||
RETURN true;
|
||
END IF;
|
||
|
||
IF NOT EXISTS (
|
||
SELECT 1
|
||
FROM v3.rf_project_report_line rprl
|
||
INNER JOIN v3.rf_project_report rpr ON rpr.id = rprl.rf_project_report_id
|
||
WHERE rpr.project_id = p_project_id
|
||
) THEN
|
||
RETURN true;
|
||
END IF;
|
||
|
||
RETURN false;
|
||
|
||
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
CREATE OR REPLACE FUNCTION v3.del_project(p_project_id integer)
|
||
RETURNS integer
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old RECORD;
|
||
v_actor_id INTEGER;
|
||
v_role VARCHAR;
|
||
BEGIN
|
||
SELECT p.name, p.level, p.org_unit_id
|
||
INTO v_old
|
||
FROM v3.project p WHERE p.id = p_project_id;
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'project #% не существует', p_project_id;
|
||
END IF;
|
||
|
||
IF NOT v3.can_delete_project(p_project_id) THEN
|
||
RAISE EXCEPTION 'project #% невозможно удалить', p_project_id;
|
||
END IF;
|
||
|
||
-- Каскад вручную: rf_project_report_quarter / line / report (нет ON DELETE)
|
||
DELETE FROM v3.form3_phase f3p WHERE f3p.rf_project_report_id IN (
|
||
SELECT id FROM v3.rf_project_report rpr WHERE project_id = p_project_id
|
||
);
|
||
DELETE FROM v3.rf_project_report_quarter
|
||
WHERE rf_project_report_line_id IN (
|
||
SELECT l.id FROM v3.rf_project_report_line l
|
||
JOIN v3.rf_project_report r ON r.id = l.rf_project_report_id
|
||
WHERE r.project_id = p_project_id);
|
||
DELETE FROM v3.rf_project_report_line
|
||
WHERE rf_project_report_id IN (
|
||
SELECT id FROM v3.rf_project_report WHERE project_id = p_project_id);
|
||
DELETE FROM v3.rf_project_report WHERE project_id = p_project_id;
|
||
DELETE FROM v3.project WHERE id = p_project_id;
|
||
|
||
PERFORM v3.log_event(
|
||
'PROJECT_DELETE', 'PROJECT',
|
||
jsonb_build_object(
|
||
'project_id', p_project_id,
|
||
'name', v_old.name,
|
||
'level', v_old.level,
|
||
'org_unit_id', v_old.org_unit_id), null, null, v_old.org_unit_id);
|
||
|
||
RETURN p_project_id;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
|
||
|
||
-- Форма 4
|
||
|
||
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
|
||
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
|
||
),
|
||
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
|
||
),
|
||
-- Conditional joins (gated by section flags)
|
||
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 строки: enriched данные
|
||
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,
|
||
t.parent_item_id AS sc, t.item_id AS ic, t.num_group_id AS ng, t.name AS ename, t.path AS tree_path,
|
||
-- plan
|
||
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,
|
||
-- seq dfip / ssp_go
|
||
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,
|
||
-- reserve
|
||
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,
|
||
-- contract (расширено: q1-4, addenda, exchange_rate, amount_foreign)
|
||
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,
|
||
-- ckk (booking из expenses_q* + полный блок: ceiling, rf_schedule, …)
|
||
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,
|
||
-- contract_summary (Действующий договор)
|
||
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,
|
||
-- allocation
|
||
al.contract_ref AS al_ref, al.allocation_purpose AS al_purp,
|
||
-- collegial
|
||
col.approved_amount AS col_amt, col.protocol_reference AS col_pr, col.note AS col_note,
|
||
-- Q1
|
||
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, q1.booking_amount 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
|
||
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,
|
||
q2.booking_amount 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
|
||
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,
|
||
q3.booking_amount 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
|
||
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,
|
||
q4.booking_amount 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,
|
||
-- approved per quarter
|
||
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
|
||
),
|
||
-- Computed: corrected/new plans
|
||
-- Excel: CH10 (cp1) = AM10 + SUM(CB:CE) − CF10
|
||
-- DJ10 (np2) = SUM(AN10, CY10, DH10, CF10, DE10:DG10)
|
||
-- В Excel CY и CF — две отдельные колонки (CY = «Закрытие 1-го квартала / Перенос
|
||
-- во 2 кв», CF = плановый «Перенос во 2 кв» внутри корректировок 1 кв). В DB
|
||
-- хранится одно поле q1.transfer_to_q2 — оно работает «за двоих»: вычитается
|
||
-- из cp1 (как CF) и прибавляется к np2 (как CY). Допущение: CY = CF.
|
||
enriched AS (
|
||
SELECT b.*,
|
||
-- Q1 corrected_plan = approved + adj_* − transfer_to_q2
|
||
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,
|
||
-- Q2 new_plan = approved + transfer_q1 + revision_inc/seq + base_correction + target_change
|
||
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.*,
|
||
-- Q2 cp = new_plan + adj_* − transfer_to_q3
|
||
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,
|
||
-- Q3 cp = new_plan + adj_* − transfer_to_q4
|
||
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,
|
||
-- Q4 cp = new_plan + adj_* (нет дальнейшего переноса)
|
||
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
|
||
),
|
||
|
||
-- Aggregates per expense_item (для иерархии)
|
||
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(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
|
||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet
|
||
AND (s_need_ap OR s_book)
|
||
GROUP BY bl.expense_item_id
|
||
),
|
||
-- Per-quarter aggregates from blq
|
||
aq_q AS (
|
||
SELECT bl.expense_item_id AS eid, q.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(q.booking_amount) 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,
|
||
-- Δ к approved для перехода к new_plan: revision_inc/seq + target_change + base_correction
|
||
-- (Excel: DJ10 = SUM(AN, CY, DH, CF, DE:DG)). Должно совпадать с INPUT-формулой np.
|
||
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
|
||
JOIN v3.expense_item ei ON ei.id = bl.expense_item_id
|
||
JOIN v3.budget_line_quarter q ON q.line_id = bl.id
|
||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet
|
||
GROUP BY bl.expense_item_id, q.quarter
|
||
),
|
||
-- Tree-rollup
|
||
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.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
|
||
)
|
||
|
||
-- ═══ Часть A: INPUT строки ═════════════════════════════════════════════
|
||
SELECT * FROM (
|
||
SELECT
|
||
'INPUT'::VARCHAR, 3, f.lid::INT,
|
||
f.sc, f.ic, f.ng, COALESCE(f.bname, f.ename),
|
||
f.just, f.io, f.vsp_id, f.vsp_addr,
|
||
-- plan
|
||
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,
|
||
-- seq dfip
|
||
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,
|
||
-- approved
|
||
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,
|
||
-- contract_summary (Действующий договор)
|
||
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,
|
||
-- allocation
|
||
CASE WHEN s_al THEN f.al_ref END, CASE WHEN s_al THEN f.al_purp END,
|
||
-- reserve (отдельный блок)
|
||
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,
|
||
-- collegial
|
||
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,
|
||
-- ckk полный
|
||
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,
|
||
-- contract (расширено)
|
||
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,
|
||
-- booking 2026
|
||
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,
|
||
-- booking 2027
|
||
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,
|
||
-- Q1
|
||
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,
|
||
-- total = "Закрытие квартала" = сумма transfer-колонок
|
||
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,
|
||
-- Q2
|
||
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,
|
||
-- Q3
|
||
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,
|
||
-- Q4
|
||
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,
|
||
-- totals
|
||
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
|
||
|
||
-- ═══ Часть B: Иерархия ════════════════════════════════════════════════
|
||
SELECT
|
||
CASE t.depth WHEN 0 THEN 'ROOT' WHEN 1 THEN 'GROUP' WHEN 2 THEN 'ITEM' WHEN 3 THEN 'SUB_ITEM' END::VARCHAR,
|
||
t.depth, NULL::INT,
|
||
CASE WHEN t.depth < 3 THEN t.item_id ELSE t.parent_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,
|
||
-- plan
|
||
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,
|
||
-- seq dfip
|
||
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,
|
||
-- approved
|
||
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,
|
||
-- contract_summary (NULL на иерархии — текстовые)
|
||
NULL::NUMERIC, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR,
|
||
NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR,
|
||
-- allocation
|
||
NULL::VARCHAR, NULL::VARCHAR,
|
||
-- reserve (q1-4 + year агрегаты, justification NULL)
|
||
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,
|
||
-- collegial
|
||
NULL::NUMERIC, NULL::VARCHAR, NULL::VARCHAR,
|
||
-- ckk полный (q1-4 агрегаты есть в ta.bk*, остальное NULL)
|
||
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,
|
||
-- contract (расширено) — все NULL на иерархии
|
||
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,
|
||
-- booking 2026 / 2027
|
||
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,
|
||
-- Q1 hierarchy
|
||
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,
|
||
-- corrected_plan = approved + adj_* − transfer_to_q2
|
||
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,
|
||
-- residual_after_booking = cp − booking
|
||
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,
|
||
-- residual_after_actual = cp − actual
|
||
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,
|
||
-- total = "Закрытие квартала" = сумма transfer-колонок
|
||
CASE WHEN s_q1 THEN
|
||
COALESCE(tb1.tq2,0)+COALESCE(tb1.tq3,0)+COALESCE(tb1.tq4,0)+COALESCE(tb1.te,0)
|
||
END,
|
||
-- Q2 hierarchy
|
||
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
|
||
-- corrected_plan = new_plan + adj_* − transfer_to_q3
|
||
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,
|
||
-- residual_after_booking = cp − booking
|
||
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,
|
||
-- residual_after_actual = cp − actual
|
||
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,
|
||
-- total = сумма transfer-колонок q2
|
||
CASE WHEN s_q2 THEN
|
||
COALESCE(tb2.tq3,0)+COALESCE(tb2.tq4,0)+COALESCE(tb2.te,0)
|
||
END,
|
||
-- Q3 hierarchy
|
||
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,
|
||
-- corrected_plan = new_plan + adj_* − transfer_to_q4
|
||
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,
|
||
-- residual_after_booking = cp − booking
|
||
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,
|
||
-- residual_after_actual = cp − actual
|
||
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,
|
||
-- total = сумма transfer-колонок q3
|
||
CASE WHEN s_q3 THEN
|
||
COALESCE(tb3.tq4,0)+COALESCE(tb3.te,0)
|
||
END,
|
||
-- Q4 hierarchy
|
||
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,
|
||
-- corrected_plan = new_plan + adj_* (нет дальнейшего переноса)
|
||
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,
|
||
-- residual_after_booking = cp − booking
|
||
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,
|
||
-- residual_after_actual = cp − actual
|
||
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,
|
||
-- total = "Закрытие" q4 = transfer_to_economy
|
||
CASE WHEN s_q4 THEN COALESCE(tb4.te,0) END,
|
||
-- totals
|
||
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.v_form4_sheet_lines_jsonb(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, header jsonb, plan_data jsonb, seq_dfip_data jsonb, approved_data jsonb, contract_summary_data jsonb, allocation_data jsonb, reserve_data jsonb, collegial_data jsonb, ckk_data jsonb, contract_data jsonb, booking_data jsonb, q1_data jsonb, q2_data jsonb, q3_data jsonb, q4_data jsonb, totals_data jsonb, _sort_path integer[])
|
||
LANGUAGE sql
|
||
STABLE
|
||
AS $function$
|
||
WITH flags AS (
|
||
SELECT
|
||
(p_sections IS NULL OR 'plan' = ANY(p_sections)) AS s_plan,
|
||
(p_sections IS NULL OR 'seq_dfip' = ANY(p_sections)) AS s_seq_d,
|
||
(p_sections IS NULL OR 'approved' = ANY(p_sections)) AS s_appr,
|
||
(p_sections IS NULL OR 'contract_summary' = ANY(p_sections)) AS s_cs,
|
||
(p_sections IS NULL OR 'allocation' = ANY(p_sections)) AS s_al,
|
||
(p_sections IS NULL OR 'reserve' = ANY(p_sections)) AS s_rsv,
|
||
(p_sections IS NULL OR 'collegial' = ANY(p_sections)) AS s_col,
|
||
(p_sections IS NULL OR 'ckk' = ANY(p_sections)) AS s_ckk,
|
||
(p_sections IS NULL OR 'contract' = ANY(p_sections)) AS s_cd,
|
||
(p_sections IS NULL OR 'booking' = ANY(p_sections)) AS s_book,
|
||
(p_sections IS NULL OR 'q1' = ANY(p_sections)) AS s_q1,
|
||
(p_sections IS NULL OR 'q2' = ANY(p_sections)) AS s_q2,
|
||
(p_sections IS NULL OR 'q3' = ANY(p_sections)) AS s_q3,
|
||
(p_sections IS NULL OR 'q4' = ANY(p_sections)) AS s_q4,
|
||
(p_sections IS NULL OR 'totals' = ANY(p_sections)) AS s_tot
|
||
)
|
||
SELECT
|
||
s.row_type, s.depth, s.line_id,
|
||
|
||
-- header (всегда). expense_item_id из _sort_path (см. v_form1_jsonb).
|
||
jsonb_build_object(
|
||
'section_code', s.col_section_code,
|
||
'item_id', s.col_item_id,
|
||
'num_group', s.col_num_group_id,
|
||
'name', s.col_name,
|
||
'justification', s.col_justification,
|
||
'internal_order', s.col_internal_order,
|
||
'vsp_id', s.col_vsp_id,
|
||
'vsp_address', s.col_vsp_address,
|
||
'expense_item_id', CASE WHEN s.row_type = 'INPUT'
|
||
THEN s._sort_path[array_upper(s._sort_path,1)-1]
|
||
ELSE s._sort_path[array_upper(s._sort_path,1)] END
|
||
) AS header,
|
||
|
||
CASE WHEN f.s_plan THEN jsonb_build_object(
|
||
'q1', s.col_plan_q1, 'q2', s.col_plan_q2,
|
||
'q3', s.col_plan_q3, 'q4', s.col_plan_q4,
|
||
'year', s.col_plan_year, 'comment', s.col_plan_comment
|
||
) END AS plan_data,
|
||
|
||
CASE WHEN f.s_seq_d THEN jsonb_build_object(
|
||
'q1', s.col_seq_dfip_q1, 'q2', s.col_seq_dfip_q2,
|
||
'q3', s.col_seq_dfip_q3, 'q4', s.col_seq_dfip_q4,
|
||
'year', s.col_seq_dfip_year,
|
||
'justification', s.col_seq_dfip_just
|
||
) END AS seq_dfip_data,
|
||
|
||
CASE WHEN f.s_appr THEN jsonb_build_object(
|
||
'q1', s.col_appr_q1, 'q2', s.col_appr_q2,
|
||
'q3', s.col_appr_q3, 'q4', s.col_appr_q4,
|
||
'year', s.col_appr_year
|
||
) END AS approved_data,
|
||
|
||
CASE WHEN f.s_cs THEN jsonb_build_object(
|
||
'total', s.col_cs_amount,
|
||
'reference', s.col_cs_reference,
|
||
'counterparty', s.col_cs_counterparty,
|
||
'deadline', s.col_cs_deadline,
|
||
'comment', s.col_cs_comment,
|
||
'future_y1', s.col_cs_future_y1,
|
||
'future_y2', s.col_cs_future_y2,
|
||
'other_ssp', s.col_cs_other_ssp,
|
||
'centralized_flag', s.col_cs_centralized_flag
|
||
) END AS contract_summary_data,
|
||
|
||
CASE WHEN f.s_al THEN jsonb_build_object(
|
||
'contract_ref', s.col_al_contract_ref,
|
||
'allocation_purpose', s.col_al_purpose
|
||
) END AS allocation_data,
|
||
|
||
CASE WHEN f.s_rsv THEN jsonb_build_object(
|
||
'q1', s.col_rsv_q1, 'q2', s.col_rsv_q2,
|
||
'q3', s.col_rsv_q3, 'q4', s.col_rsv_q4,
|
||
'year', s.col_rsv_year,
|
||
'justification', s.col_rsv_just
|
||
|
||
) END AS reserve_data,
|
||
|
||
CASE WHEN f.s_col THEN jsonb_build_object(
|
||
'approved', s.col_col_amount,
|
||
'protocol', s.col_col_protocol,
|
||
'note', s.col_col_note
|
||
) END AS collegial_data,
|
||
|
||
CASE WHEN f.s_ckk THEN jsonb_build_object(
|
||
'ceiling', s.col_ckk_ceiling,
|
||
'q1', s.col_ckk_q1,
|
||
'q2', s.col_ckk_q2,
|
||
'q3', s.col_ckk_q3,
|
||
'q4', s.col_ckk_q4,
|
||
'rf_schedule', s.col_ckk_rf_schedule,
|
||
'deadline', s.col_ckk_delivery_deadline,
|
||
'proc_plan', s.col_ckk_procurement_plan,
|
||
'proc_method', s.col_ckk_procurement_method,
|
||
'comment', s.col_ckk_comment
|
||
) END AS ckk_data,
|
||
|
||
CASE WHEN f.s_cd THEN jsonb_build_object(
|
||
'counterparty', s.col_cd_counterparty,
|
||
'reference', s.col_cd_reference,
|
||
'addenda', s.col_cd_addenda,
|
||
'date', s.col_cd_date,
|
||
'subject', s.col_cd_subject,
|
||
'currency', s.col_cd_currency,
|
||
'ceiling', s.col_cd_ceiling,
|
||
'q1', s.col_cd_q1,
|
||
'q2', s.col_cd_q2,
|
||
'q3', s.col_cd_q3,
|
||
'q4', s.col_cd_q4,
|
||
'rf_schedule', s.col_cd_rf_schedule,
|
||
'vat_rate', s.col_cd_vat_rate,
|
||
'exchange_rate', s.col_cd_exchange_rate,
|
||
'amount_foreign', s.col_cd_amount_foreign,
|
||
'deadline', s.col_cd_deadline,
|
||
'scheme', s.col_cd_scheme,
|
||
'act', s.col_cd_act,
|
||
'comment', s.col_cd_comment
|
||
) END AS contract_data,
|
||
|
||
CASE WHEN f.s_book THEN jsonb_build_object(
|
||
'y2026', jsonb_build_object('q1', s.col_book_q1, 'q2', s.col_book_q2, 'q3', s.col_book_q3, 'q4', s.col_book_q4),
|
||
'y2027', jsonb_build_object('q1', s.col_book_next_q1, 'q2', s.col_book_next_q2, 'q3', s.col_book_next_q3, 'q4', s.col_book_next_q4)
|
||
) END AS booking_data,
|
||
|
||
CASE WHEN f.s_q1 THEN jsonb_build_object(
|
||
'adj_current', s.col_q1_adj_current,
|
||
'adj_ssp', s.col_q1_adj_ssp,
|
||
'adj_rf', s.col_q1_adj_rf,
|
||
'adj_reserve', s.col_q1_adj_reserve,
|
||
'adj_comment', s.col_q1_adj_comment,
|
||
'corrected_plan', s.col_q1_corrected_plan,
|
||
'pay_date', s.col_q1_pay_date,
|
||
'pay_amount', s.col_q1_pay_amount,
|
||
'pay_ho', s.col_q1_pay_ho,
|
||
'pay_rf', s.col_q1_pay_rf,
|
||
'pay_comment', s.col_q1_pay_comment,
|
||
'pay_act', s.col_q1_pay_act,
|
||
'booking', s.col_q1_booking,
|
||
'actual_m1', s.col_q1_actual_m1,
|
||
'actual_m2', s.col_q1_actual_m2,
|
||
'actual_m3', s.col_q1_actual_m3,
|
||
'actual_quarter', s.col_q1_actual_quarter,
|
||
'residual_after_booking', s.col_q1_residual_after_booking,
|
||
'residual_after_actual', s.col_q1_residual_after_actual,
|
||
'transfer_q2', s.col_q1_transfer_q2,
|
||
'transfer_q3', s.col_q1_transfer_q3,
|
||
'transfer_q4', s.col_q1_transfer_q4,
|
||
'transfer_econ', s.col_q1_transfer_econ,
|
||
'total', s.col_q1_total
|
||
) END AS q1_data,
|
||
|
||
CASE WHEN f.s_q2 THEN jsonb_build_object(
|
||
'target_change', s.col_q2_target_change,
|
||
'base_correction', s.col_q2_base_correction,
|
||
'base_correction_comment', s.col_q2_base_correction_comment,
|
||
'revision_inc', s.col_q2_revision_inc,
|
||
'revision_seq', s.col_q2_revision_seq,
|
||
'revision_comment', s.col_q2_revision_comment,
|
||
'new_plan', s.col_q2_new_plan,
|
||
'adj_current', s.col_q2_adj_current,
|
||
'adj_ssp', s.col_q2_adj_ssp,
|
||
'adj_rf', s.col_q2_adj_rf,
|
||
'adj_reserve', s.col_q2_adj_reserve,
|
||
'adj_comment', s.col_q2_adj_comment,
|
||
'corrected_plan', s.col_q2_corrected_plan,
|
||
'pay_date', s.col_q2_pay_date,
|
||
'pay_amount', s.col_q2_pay_amount,
|
||
'pay_ho', s.col_q2_pay_ho,
|
||
'pay_rf', s.col_q2_pay_rf,
|
||
'pay_comment', s.col_q2_pay_comment,
|
||
'pay_act', s.col_q2_pay_act,
|
||
'booking', s.col_q2_booking,
|
||
'actual_m1', s.col_q2_actual_m1,
|
||
'actual_m2', s.col_q2_actual_m2,
|
||
'actual_m3', s.col_q2_actual_m3,
|
||
'actual_quarter', s.col_q2_actual_quarter,
|
||
'residual_after_booking', s.col_q2_residual_after_booking,
|
||
'residual_after_actual', s.col_q2_residual_after_actual,
|
||
'transfer_q3', s.col_q2_transfer_q3,
|
||
'transfer_q4', s.col_q2_transfer_q4,
|
||
'transfer_econ', s.col_q2_transfer_econ,
|
||
'total', s.col_q2_total
|
||
) END AS q2_data,
|
||
|
||
CASE WHEN f.s_q3 THEN jsonb_build_object(
|
||
'target_change', s.col_q3_target_change,
|
||
'base_correction', s.col_q3_base_correction,
|
||
'base_correction_comment', s.col_q3_base_correction_comment,
|
||
'revision_inc', s.col_q3_revision_inc,
|
||
'revision_seq', s.col_q3_revision_seq,
|
||
'revision_comment', s.col_q3_revision_comment,
|
||
'new_plan', s.col_q3_new_plan,
|
||
'adj_current', s.col_q3_adj_current,
|
||
'adj_ssp', s.col_q3_adj_ssp,
|
||
'adj_rf', s.col_q3_adj_rf,
|
||
'adj_reserve', s.col_q3_adj_reserve,
|
||
'adj_comment', s.col_q3_adj_comment,
|
||
'corrected_plan', s.col_q3_corrected_plan,
|
||
'pay_date', s.col_q3_pay_date,
|
||
'pay_amount', s.col_q3_pay_amount,
|
||
'pay_ho', s.col_q3_pay_ho,
|
||
'pay_rf', s.col_q3_pay_rf,
|
||
'pay_comment', s.col_q3_pay_comment,
|
||
'pay_act', s.col_q3_pay_act,
|
||
'booking', s.col_q3_booking,
|
||
'actual_m1', s.col_q3_actual_m1,
|
||
'actual_m2', s.col_q3_actual_m2,
|
||
'actual_m3', s.col_q3_actual_m3,
|
||
'actual_quarter', s.col_q3_actual_quarter,
|
||
'residual_after_booking', s.col_q3_residual_after_booking,
|
||
'residual_after_actual', s.col_q3_residual_after_actual,
|
||
'transfer_q4', s.col_q3_transfer_q4,
|
||
'transfer_econ', s.col_q3_transfer_econ,
|
||
'total', s.col_q3_total
|
||
) END AS q3_data,
|
||
|
||
CASE WHEN f.s_q4 THEN jsonb_build_object(
|
||
'target_change', s.col_q4_target_change,
|
||
'base_correction', s.col_q4_base_correction,
|
||
'base_correction_comment', s.col_q4_base_correction_comment,
|
||
'revision_inc', s.col_q4_revision_inc,
|
||
'revision_seq', s.col_q4_revision_seq,
|
||
'revision_comment', s.col_q4_revision_comment,
|
||
'new_plan', s.col_q4_new_plan,
|
||
'adj_current', s.col_q4_adj_current,
|
||
'adj_ssp', s.col_q4_adj_ssp,
|
||
'adj_rf', s.col_q4_adj_rf,
|
||
'adj_reserve', s.col_q4_adj_reserve,
|
||
'adj_comment', s.col_q4_adj_comment,
|
||
'corrected_plan', s.col_q4_corrected_plan,
|
||
'pay_date', s.col_q4_pay_date,
|
||
'pay_amount', s.col_q4_pay_amount,
|
||
'pay_ho', s.col_q4_pay_ho,
|
||
'pay_rf', s.col_q4_pay_rf,
|
||
'pay_comment', s.col_q4_pay_comment,
|
||
'pay_act', s.col_q4_pay_act,
|
||
'booking', s.col_q4_booking,
|
||
'actual_m1', s.col_q4_actual_m1,
|
||
'actual_m2', s.col_q4_actual_m2,
|
||
'actual_m3', s.col_q4_actual_m3,
|
||
'actual_spod', s.col_q4_actual_spod,
|
||
'actual_quarter', s.col_q4_actual_quarter,
|
||
'residual_after_booking', s.col_q4_residual_after_booking,
|
||
'residual_after_actual', s.col_q4_residual_after_actual,
|
||
'transfer_econ', s.col_q4_transfer_econ,
|
||
'total', s.col_q4_total
|
||
) END AS q4_data,
|
||
|
||
CASE WHEN f.s_tot THEN jsonb_build_object(
|
||
'fact_year', s.col_fact_year
|
||
) END AS totals_data,
|
||
|
||
s._sort_path
|
||
FROM v3.v_form4_sheet_sections(p_form_id, p_sheet, p_sections) s
|
||
CROSS JOIN flags f
|
||
ORDER BY s._sort_path;
|
||
$function$
|
||
;
|
||
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form4_sheet_jsonb(p_form_id integer, p_sheet character varying, p_sections text[] DEFAULT NULL::text[])
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE sql
|
||
STABLE
|
||
AS $function$
|
||
-- ═══ 1. Базовые INPUT-строки с расширенной иерархией ═══════════════════════
|
||
WITH inp_raw AS (
|
||
SELECT j.*
|
||
FROM v3.v_form4_sheet_lines_jsonb(p_form_id, p_sheet, p_sections) j
|
||
WHERE j.row_type = 'INPUT'
|
||
),
|
||
inp AS (
|
||
SELECT
|
||
i.*,
|
||
bl.project_id AS line_project_id,
|
||
|
||
-- --- разрешение проекта ---
|
||
-- A. bl.project_id → project (level='project')
|
||
-- B. bl.project_id → program (level='program') — тогда проекта нет
|
||
CASE WHEN prj.id IS NOT NULL THEN prj.id END AS prj_id,
|
||
CASE WHEN prj.id IS NOT NULL THEN prj.name END AS prj_name,
|
||
|
||
-- --- разрешение программы ---
|
||
-- A. через проект: prog_via_prj
|
||
-- B. напрямую: prog_direct
|
||
COALESCE(prog_via_prj.id, prog_direct.id) AS prog_id,
|
||
COALESCE(prog_via_prj.name, prog_direct.name) AS prog_name,
|
||
|
||
-- --- section (depth=0) ---
|
||
sec.id AS sec_id,
|
||
sec.section_code AS sec_code,
|
||
sec.name AS sec_name
|
||
|
||
FROM inp_raw i
|
||
JOIN v3.budget_line bl ON bl.id = i.line_id
|
||
|
||
-- проект (level='project')
|
||
LEFT JOIN v3.form4_project prj
|
||
ON prj.id = bl.project_id AND prj.level = 'project'
|
||
|
||
-- программа через проект
|
||
LEFT JOIN v3.form4_project prog_via_prj
|
||
ON prog_via_prj.id = prj.parent_id AND prog_via_prj.level = 'program'
|
||
|
||
-- программа напрямую (bl.project_id → program)
|
||
LEFT JOIN v3.form4_project prog_direct
|
||
ON prog_direct.id = bl.project_id AND prog_direct.level = 'program'
|
||
|
||
-- раздел depth=0 (через expense_item)
|
||
JOIN v3.expense_item ei ON ei.id = bl.expense_item_id
|
||
LEFT JOIN LATERAL (
|
||
WITH RECURSIVE up AS (
|
||
SELECT id, parent_id, name, section_code, depth
|
||
FROM v3.expense_item WHERE id = ei.id
|
||
UNION ALL
|
||
SELECT e.id, e.parent_id, e.name, e.section_code, e.depth
|
||
FROM v3.expense_item e JOIN up u ON u.parent_id = e.id
|
||
)
|
||
SELECT id, name, section_code FROM up WHERE depth = 0 LIMIT 1
|
||
) sec ON TRUE
|
||
),
|
||
tp_agg AS (
|
||
SELECT 1 AS tp_id,
|
||
jsonb_build_object(
|
||
'q1', 0, 'q2', 0,
|
||
'q3', 0, 'q4', 0,
|
||
'year', 0, 'comment', null
|
||
) AS tp_plan,
|
||
|
||
|
||
|
||
jsonb_build_object(
|
||
'q1', 0, 'q2', 0,
|
||
'q3', 0, 'q4', 0,
|
||
'year', 0,
|
||
'justification', null
|
||
) AS tp_seq_dfip,
|
||
|
||
jsonb_build_object(
|
||
'q1', 0, 'q2', 0,
|
||
'q3', 0, 'q4', 0,
|
||
'year', 0
|
||
) AS tp_approved,
|
||
|
||
jsonb_build_object(
|
||
'total', null,
|
||
'reference', null,
|
||
'counterparty', null,
|
||
'deadline', null,
|
||
'comment', null,
|
||
'future_y1', null,
|
||
'future_y2', null,
|
||
'other_ssp', null,
|
||
'centralized_flag', null
|
||
) AS tp_contract_summary,
|
||
|
||
jsonb_build_object(
|
||
'contract_ref', null,
|
||
'allocation_purpose', null
|
||
) AS tp_allocation,
|
||
|
||
jsonb_build_object(
|
||
'q1', 0, 'q2', 0,
|
||
'q3', 0, 'q4', 0,
|
||
'year', 0,
|
||
'justification', null
|
||
) AS tp_reserve,
|
||
|
||
jsonb_build_object(
|
||
'approved', null,
|
||
'protocol', null,
|
||
'note', null
|
||
) AS tp_collegial,
|
||
|
||
jsonb_build_object(
|
||
'ceiling', null,
|
||
'q1', null,
|
||
'q2', null,
|
||
'q3', null,
|
||
'q4', null,
|
||
'rf_schedule', null,
|
||
'delivery_deadline', null,
|
||
'proc_plan', null,
|
||
'proc_method', null,
|
||
'comment', null
|
||
) AS tp_ckk,
|
||
|
||
jsonb_build_object(
|
||
'act', null,
|
||
'date', null,
|
||
'scheme', null,
|
||
'addenda', null,
|
||
'ceiling', null,
|
||
'comment', null,
|
||
'subject', null,
|
||
'currency', null,
|
||
'deadline', null,
|
||
'vat_rate', null,
|
||
'reference', null,
|
||
'q1', null,
|
||
'q2', null,
|
||
'q3', null,
|
||
'q4', null,
|
||
'rf_schedule', null,
|
||
'counterparty', null,
|
||
'exchange_rate', null,
|
||
'amount_foreign', null
|
||
) AS tp_contract,
|
||
|
||
jsonb_build_object(
|
||
'y2026', jsonb_build_object('q1', null, 'q2', null, 'q3', null, 'q4', null),
|
||
'y2027', jsonb_build_object('q1', null, 'q2', null, 'q3', null, 'q4', null)
|
||
) AS tp_booking,
|
||
|
||
jsonb_build_object(
|
||
'total', 0,
|
||
'adj_rf', null,
|
||
'pay_ho', null,
|
||
'pay_rf', null,
|
||
'adj_ssp', null,
|
||
'booking', null,
|
||
'pay_act', null,
|
||
'pay_date', null,
|
||
'actual_m1', null,
|
||
'actual_m2', null,
|
||
'actual_m3', null,
|
||
'pay_amount', null,
|
||
'adj_comment', null,
|
||
'adj_current', null,
|
||
'adj_reserve', null,
|
||
'pay_comment', null,
|
||
'transfer_q2', null,
|
||
'transfer_q3', null,
|
||
'transfer_q4', null,
|
||
'transfer_econ', null,
|
||
'actual_quarter', 0,
|
||
'corrected_plan', 0,
|
||
'residual_after_actual', 0,
|
||
'residual_after_booking', 0
|
||
) AS tp_q1,
|
||
|
||
jsonb_build_object(
|
||
'target_change', null,
|
||
'base_correction', null,
|
||
'base_correction_comment', null,
|
||
'revision_inc', null,
|
||
'revision_seq', null,
|
||
'revision_comment', null,
|
||
'new_plan', 0,
|
||
'adj_current', null,
|
||
'adj_ssp', null,
|
||
'adj_rf', null,
|
||
'adj_reserve', null,
|
||
'adj_comment', null,
|
||
'corrected_plan', 0,
|
||
'pay_date', null,
|
||
'pay_amount', null,
|
||
'pay_ho', null,
|
||
'pay_rf', null,
|
||
'pay_comment', null,
|
||
'pay_act', null,
|
||
'booking', null,
|
||
'actual_m1', null,
|
||
'actual_m2', null,
|
||
'actual_m3', null,
|
||
'actual_quarter', 0,
|
||
'residual_after_booking', 0,
|
||
'residual_after_actual', 0,
|
||
'transfer_q3', null,
|
||
'transfer_q4', null,
|
||
'transfer_econ', null,
|
||
'total', 0
|
||
) AS tp_q2,
|
||
|
||
jsonb_build_object(
|
||
'target_change', null,
|
||
'base_correction', null,
|
||
'base_correction_comment', null,
|
||
'revision_inc', null,
|
||
'revision_seq', null,
|
||
'revision_comment', null,
|
||
'new_plan', 0,
|
||
'adj_current', null,
|
||
'adj_ssp', null,
|
||
'adj_rf', null,
|
||
'adj_reserve', null,
|
||
'adj_comment', null,
|
||
'corrected_plan', 0,
|
||
'pay_date', null,
|
||
'pay_amount', null,
|
||
'pay_ho', null,
|
||
'pay_rf', null,
|
||
'pay_comment', null,
|
||
'pay_act', null,
|
||
'booking', null,
|
||
'actual_m1', null,
|
||
'actual_m2', null,
|
||
'actual_m3', null,
|
||
'actual_quarter', 0,
|
||
'residual_after_booking', 0,
|
||
'residual_after_actual', 0,
|
||
'transfer_q4', null,
|
||
'transfer_econ', null,
|
||
'total', 0
|
||
) AS tp_q3,
|
||
|
||
jsonb_build_object(
|
||
'target_change', null,
|
||
'base_correction', null,
|
||
'base_correction_comment', null,
|
||
'revision_inc', null,
|
||
'revision_seq', null,
|
||
'revision_comment', null,
|
||
'new_plan', 0,
|
||
'adj_current', null,
|
||
'adj_ssp', null,
|
||
'adj_rf', null,
|
||
'adj_reserve', null,
|
||
'adj_comment', null,
|
||
'corrected_plan', 0,
|
||
'pay_date', null,
|
||
'pay_amount', null,
|
||
'pay_ho', null,
|
||
'pay_rf', null,
|
||
'pay_comment', null,
|
||
'pay_act', null,
|
||
'booking', null,
|
||
'actual_m1', null,
|
||
'actual_m2', null,
|
||
'actual_m3', null,
|
||
'actual_spod', null,
|
||
'actual_quarter', 0,
|
||
'residual_after_booking', 0,
|
||
'residual_after_actual', 0,
|
||
'transfer_econ', null,
|
||
'total', 0
|
||
) AS tp_q4,
|
||
jsonb_build_object(
|
||
'fact_year', 0
|
||
) AS tp_totals
|
||
|
||
|
||
|
||
|
||
|
||
),
|
||
-- ═══ 2. Все программы (включая пустые) ═════════════════════════════════════
|
||
all_programs AS (
|
||
SELECT
|
||
fp.id AS prog_id,
|
||
fp.name AS prog_name,
|
||
fp.section_code,
|
||
sec.id AS sec_id,
|
||
sec.section_code AS sec_code,
|
||
sec.name AS sec_name
|
||
FROM v3.form4_project fp
|
||
LEFT JOIN LATERAL (
|
||
SELECT id, section_code, name
|
||
FROM v3.expense_item
|
||
WHERE section_code = fp.section_code AND depth = 0
|
||
LIMIT 1
|
||
) sec ON TRUE
|
||
WHERE fp.level = 'program'
|
||
AND (p_sections IS NULL OR fp.section_code = ANY(p_sections))
|
||
AND (fp.section_code = p_sheet) AND fp.form_id = p_form_id
|
||
),
|
||
|
||
-- ═══ 3. Все проекты под программами (включая пустые) ═══════════════════════
|
||
all_projects AS (
|
||
SELECT
|
||
prj.id AS prj_id,
|
||
prj.name AS prj_name,
|
||
prj.parent_id AS prog_id,
|
||
ap.sec_id, ap.sec_code, ap.sec_name
|
||
FROM v3.form4_project prj
|
||
JOIN all_programs ap ON ap.prog_id = prj.parent_id
|
||
WHERE prj.level = 'project' AND (ap.section_code = p_sheet)
|
||
),
|
||
|
||
-- ═══ 4. Агрегаты с финансовыми данными (только из inp) ════════════════════
|
||
|
||
-- PROJECT-агрегаты
|
||
prj_agg AS (
|
||
SELECT
|
||
sec_id, MAX(sec_code) AS sec_code, MAX(sec_name) AS sec_name,
|
||
prog_id, MAX(prog_name) AS prog_name,
|
||
prj_id, MAX(prj_name) AS prj_name,
|
||
v3.jsonb_sum(plan_data) AS plan_data,
|
||
v3.jsonb_sum(seq_dfip_data) AS seq_dfip_data,
|
||
v3.jsonb_sum(approved_data) AS approved_data,
|
||
v3.jsonb_sum(contract_summary_data) AS contract_summary_data,
|
||
v3.jsonb_sum(allocation_data) AS allocation_data,
|
||
v3.jsonb_sum(reserve_data) AS reserve_data,
|
||
v3.jsonb_sum(collegial_data) AS collegial_data,
|
||
v3.jsonb_sum(ckk_data) AS ckk_data,
|
||
v3.jsonb_sum(contract_data) AS contract_data,
|
||
v3.jsonb_sum(booking_data) AS booking_data,
|
||
v3.jsonb_sum(q1_data) AS q1_data,
|
||
v3.jsonb_sum(q2_data) AS q2_data,
|
||
v3.jsonb_sum(q3_data) AS q3_data,
|
||
v3.jsonb_sum(q4_data) AS q4_data,
|
||
v3.jsonb_sum(totals_data) AS totals_data
|
||
FROM inp WHERE prj_id IS NOT NULL
|
||
GROUP BY sec_id, prog_id, prj_id
|
||
),
|
||
|
||
-- PROGRAM-агрегаты
|
||
prog_agg AS (
|
||
SELECT
|
||
sec_id, MAX(sec_code) AS sec_code, MAX(sec_name) AS sec_name,
|
||
prog_id, MAX(prog_name) AS prog_name,
|
||
v3.jsonb_sum(plan_data) AS plan_data,
|
||
v3.jsonb_sum(seq_dfip_data) AS seq_dfip_data,
|
||
v3.jsonb_sum(approved_data) AS approved_data,
|
||
v3.jsonb_sum(contract_summary_data) AS contract_summary_data,
|
||
v3.jsonb_sum(allocation_data) AS allocation_data,
|
||
v3.jsonb_sum(reserve_data) AS reserve_data,
|
||
v3.jsonb_sum(collegial_data) AS collegial_data,
|
||
v3.jsonb_sum(ckk_data) AS ckk_data,
|
||
v3.jsonb_sum(contract_data) AS contract_data,
|
||
v3.jsonb_sum(booking_data) AS booking_data,
|
||
v3.jsonb_sum(q1_data) AS q1_data,
|
||
v3.jsonb_sum(q2_data) AS q2_data,
|
||
v3.jsonb_sum(q3_data) AS q3_data,
|
||
v3.jsonb_sum(q4_data) AS q4_data,
|
||
v3.jsonb_sum(totals_data) AS totals_data
|
||
FROM inp WHERE prog_id IS NOT NULL
|
||
GROUP BY sec_id, prog_id
|
||
),
|
||
|
||
-- ROOT-агрегаты
|
||
root_agg AS (
|
||
SELECT
|
||
sec_id, MAX(sec_code) AS sec_code, MAX(sec_name) AS sec_name,
|
||
v3.jsonb_sum(plan_data) AS plan_data,
|
||
v3.jsonb_sum(seq_dfip_data) AS seq_dfip_data,
|
||
v3.jsonb_sum(approved_data) AS approved_data,
|
||
v3.jsonb_sum(contract_summary_data) AS contract_summary_data,
|
||
v3.jsonb_sum(allocation_data) AS allocation_data,
|
||
v3.jsonb_sum(reserve_data) AS reserve_data,
|
||
v3.jsonb_sum(collegial_data) AS collegial_data,
|
||
v3.jsonb_sum(ckk_data) AS ckk_data,
|
||
v3.jsonb_sum(contract_data) AS contract_data,
|
||
v3.jsonb_sum(booking_data) AS booking_data,
|
||
v3.jsonb_sum(q1_data) AS q1_data,
|
||
v3.jsonb_sum(q2_data) AS q2_data,
|
||
v3.jsonb_sum(q3_data) AS q3_data,
|
||
v3.jsonb_sum(q4_data) AS q4_data,
|
||
v3.jsonb_sum(totals_data) AS totals_data
|
||
FROM inp
|
||
GROUP BY sec_id
|
||
),
|
||
|
||
-- Нулевой JSONB-шаблон (для пустых программ/проектов)
|
||
zero AS (
|
||
SELECT '{}'::jsonb AS z
|
||
),
|
||
|
||
-- ═══ 5. UNION всех уровней ═════════════════════════════════════════════════
|
||
unioned AS (
|
||
-- ROOT — только из агрегатов (не имеет смысла пустой ROOT без строк)
|
||
SELECT
|
||
'ROOT'::VARCHAR AS row_type, 0 AS depth, NULL::INT AS line_id,
|
||
jsonb_build_object('section_code', sec_code, 'name', sec_name, 'expense_item_id', sec_id) AS header,
|
||
plan_data, seq_dfip_data, approved_data,
|
||
contract_summary_data, allocation_data, reserve_data, collegial_data, ckk_data,
|
||
contract_data, booking_data,
|
||
q1_data, q2_data, q3_data, q4_data, totals_data,
|
||
ARRAY[sec_id, 0, 0, 0]::INT[] AS sort_path
|
||
FROM root_agg
|
||
|
||
UNION ALL
|
||
|
||
-- GROUP — из агрегатов (программы с бюджетными строками)
|
||
SELECT
|
||
'GROUP', 1, NULL,
|
||
jsonb_build_object('section_code', sec_code, 'name', prog_name, 'program_id', prog_id, 'expense_item_id', sec_id),
|
||
plan_data, seq_dfip_data, approved_data,
|
||
contract_summary_data, allocation_data, reserve_data, collegial_data, ckk_data,
|
||
contract_data, booking_data,
|
||
q1_data, q2_data, q3_data, q4_data, totals_data,
|
||
ARRAY[sec_id, prog_id, 0, 0]::INT[]
|
||
FROM prog_agg
|
||
|
||
UNION ALL
|
||
|
||
-- GROUP — пустые программы (нет ни одной budget_line)
|
||
SELECT
|
||
'GROUP', 1, NULL,
|
||
jsonb_build_object('section_code', sec_code, 'name', prog_name, 'program_id', prog_id, 'expense_item_id', sec_id),
|
||
tp_plan, tp_seq_dfip, tp_approved, -- plan, seq_dfip, approved
|
||
tp_contract_summary, tp_allocation, tp_reserve, -- contract_summary, allocation, reserve
|
||
tp_collegial, tp_ckk, -- collegial, ckk
|
||
tp_contract, tp_booking, -- contract, booking
|
||
tp_q1, tp_q2, tp_q3, tp_q4, tp_totals, -- q1-q4, totals
|
||
ARRAY[sec_id, prog_id, 0, 0]::INT[]
|
||
FROM all_programs ap
|
||
JOIN tp_agg ON tp_agg.tp_id = 1
|
||
WHERE NOT EXISTS (
|
||
SELECT 1 FROM v3.budget_line bl
|
||
WHERE bl.budget_form_id = p_form_id
|
||
AND (
|
||
bl.project_id = ap.prog_id -- прямая привязка к программе
|
||
OR bl.project_id IN (SELECT id FROM v3.form4_project pr -- через проект
|
||
WHERE pr.parent_id = ap.prog_id AND pr.level = 'project')
|
||
)
|
||
)
|
||
|
||
UNION ALL
|
||
|
||
-- ITEM — из агрегатов (проекты с бюджетными строками)
|
||
SELECT
|
||
'ITEM', 2, NULL,
|
||
jsonb_build_object('section_code', sec_code, 'name', prj_name,
|
||
'program_id', prog_id, 'project_id', prj_id, 'expense_item_id', sec_id),
|
||
plan_data, seq_dfip_data, approved_data,
|
||
contract_summary_data, allocation_data, reserve_data, collegial_data, ckk_data,
|
||
contract_data, booking_data,
|
||
q1_data, q2_data, q3_data, q4_data, totals_data,
|
||
ARRAY[sec_id, prog_id, prj_id, 0]::INT[]
|
||
FROM prj_agg
|
||
|
||
UNION ALL
|
||
|
||
-- ITEM — пустые проекты (нет budget_line)
|
||
SELECT
|
||
'ITEM', 2, NULL,
|
||
jsonb_build_object('section_code', sec_code, 'name', prj_name,
|
||
'program_id', prog_id, 'project_id', prj_id, 'expense_item_id', sec_id),
|
||
tp_plan, tp_seq_dfip, tp_approved, -- plan, seq_dfip, approved
|
||
tp_contract_summary, tp_allocation, tp_reserve, -- contract_summary, allocation, reserve
|
||
tp_collegial, tp_ckk, -- collegial, ckk
|
||
tp_contract, tp_booking, -- contract, booking
|
||
tp_q1, tp_q2, tp_q3, tp_q4, tp_totals, -- q1-q4, totals
|
||
ARRAY[sec_id, prog_id, prj_id, 0]::INT[]
|
||
FROM all_projects apr
|
||
JOIN tp_agg ON tp_agg.tp_id = 1
|
||
WHERE NOT EXISTS (
|
||
SELECT 1 FROM v3.budget_line bl
|
||
WHERE bl.budget_form_id = p_form_id
|
||
AND bl.project_id = apr.prj_id
|
||
)
|
||
|
||
UNION ALL
|
||
|
||
-- INPUT — все строки бюджета
|
||
SELECT
|
||
'INPUT', 3, line_id,
|
||
header || jsonb_build_object(
|
||
'project_id', prj_id, 'project_name', prj_name,
|
||
'program_id', prog_id, 'program_name', prog_name
|
||
),
|
||
plan_data, seq_dfip_data, approved_data,
|
||
contract_summary_data, allocation_data, reserve_data, collegial_data, ckk_data,
|
||
contract_data, booking_data,
|
||
q1_data, q2_data, q3_data, q4_data, totals_data,
|
||
ARRAY[sec_id, COALESCE(prog_id, 999999), COALESCE(CASE WHEN prj_id is null and prog_id is not null THEN 0 ELSE prj_id END, 999999), line_id]::INT[]
|
||
FROM inp
|
||
)
|
||
|
||
SELECT
|
||
u.row_type, u.depth,
|
||
ROW_NUMBER() OVER (ORDER BY u.sort_path) AS sort_order,
|
||
jsonb_build_object(
|
||
'sort_path', u.sort_path,
|
||
'line_id', u.line_id,
|
||
'header', u.header,
|
||
'plan', u.plan_data,
|
||
'seq_dfip', u.seq_dfip_data,
|
||
'approved', u.approved_data,
|
||
'contract_summary', u.contract_summary_data,
|
||
'allocation', u.allocation_data,
|
||
'reserve', u.reserve_data,
|
||
'collegial', u.collegial_data,
|
||
'ckk', u.ckk_data,
|
||
'contract', u.contract_data,
|
||
'booking', u.booking_data,
|
||
'q1', u.q1_data,
|
||
'q2', u.q2_data,
|
||
'q3', u.q3_data,
|
||
'q4', u.q4_data,
|
||
'totals', u.totals_data
|
||
) AS data
|
||
FROM unioned u
|
||
ORDER BY u.sort_path;
|
||
$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_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, '.');
|
||
|
||
-- ═══ OTCH9F (fixed_asset_report) ══════════════════════════════════════
|
||
-- p_line_id = fixed_asset_report.id.
|
||
-- Editable: b_604.{acquired,disposed}_*, b_60415.{acquired,transferred}_*,
|
||
-- totals.go_balance_only;
|
||
-- b_604.opening_*, b_60415.opening_* — ТОЛЬКО для month=1 (для
|
||
-- месяцев 2..12 это computed накопительно из месяца 1).
|
||
-- RO: equipment_name (нормативная номенклатура), month/expense_item_id/
|
||
-- item_id/section_code (ключ строки), totals.{acquired_total,
|
||
-- disposed_total, balance_qty, balance_amt} (computed).
|
||
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;
|
||
|
||
-- Одноуровневые ключи — только нормативные/ключевые → RO
|
||
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;
|
||
|
||
-- Двухуровневые: totals.x / b_604.x / b_60415.x
|
||
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
|
||
-- opening_* для месяцев 2..12 — computed
|
||
IF v_parts[2] LIKE 'opening_%' AND v_far_month <> 1 THEN
|
||
RAISE EXCEPTION 'computed_field: % (opening для month>1 вычисляется из base + накопит. дельты)', p_column;
|
||
END IF;
|
||
-- Маппинг b_604/60415 → колонки таблицы
|
||
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;
|
||
|
||
-- ═══ AHR_LIMIT (limit_template + form_limit) ═════════════════════════
|
||
-- p_line_id здесь = limit_template.id LEAF (глобальный каталог нормативов).
|
||
-- Editable: qty_q1..q4 (INTEGER), comment (TEXT) — пишутся в v3.form_limit
|
||
-- (UPSERT по (budget_form_id, template_id)).
|
||
-- amount_q1..q4 — computed по формуле в read (qty × limit × period_factor).
|
||
-- name/unit/limit_*/section_no/expense_item_code — нормативный справочник
|
||
-- (правится отдельным админ-API, не через write UI).
|
||
IF p_sheet = 'AHR_LIMIT' THEN
|
||
IF p_form_id IS NULL THEN
|
||
RAISE EXCEPTION 'internal: p_form_id не передан для AHR_LIMIT';
|
||
END IF;
|
||
|
||
-- Sanity: строка должна быть LEAF
|
||
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;
|
||
|
||
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;
|
||
|
||
-- ═══ Сателлиты (AHR_RENT / AHR_UTILITY / AHR_SECURITY) ═══════════════
|
||
-- Здесь p_line_id трактуется как id строки сателлита (rent/utility/
|
||
-- security_detail.id) — НЕ budget_line.id. UPDATE по этому id; INSERT/
|
||
-- DELETE — через add_budget_line / del_budget_line с тем же sheet.
|
||
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')) 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;
|
||
|
||
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;
|
||
|
||
-- ═══ Основная сетка (FORM_1/2/4 AHR/CAP/OPER) ════════════════════════
|
||
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];
|
||
|
||
-- ── header: editable budget_line columns ──────────────────────────────
|
||
IF v_scope = 'header' THEN
|
||
IF v_field = 'name' THEN
|
||
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 = '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;
|
||
|
||
-- ── computed scopes (RO) ──────────────────────────────────────────────
|
||
IF v_scope IN ('approved','totals') THEN
|
||
RAISE EXCEPTION 'computed_field: %', p_column;
|
||
END IF;
|
||
|
||
-- ── booking.y{2026|2027}.qN → v3.ckk (см. read FORM_2/4 jsonb) ────────
|
||
-- В read-обёртках booking синтезируется из ckk.expenses_qN (= y2026,
|
||
-- текущий год) и ckk.expenses_next_year_qN (= y2027, следующий).
|
||
-- Имена y2026/y2027 захардкожены в read; повторяем то же в write.
|
||
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;
|
||
|
||
-- ── Маппинг (scope, field) → (table, col, type, key_kind, actor) ─────
|
||
-- key_kind: 'line' — PK/UQ = (line_id)
|
||
-- 'line_quarter'— PK = (line_id, quarter), quarter из v_q (берётся из scope qN)
|
||
-- 'line_actor' — UQ = (line_id, actor), actor из v_actor
|
||
--
|
||
-- VALUES — это и есть white-list редактируемых полей. Поля, отсутствующие
|
||
-- здесь (computed: corrected_plan, new_plan, actual_quarter, rem_*, economy,
|
||
-- total, booking; FORM_2 q1.adj_rf — нет такого поля у FORM_2; и т.п.) →
|
||
-- падают с unknown_column ниже.
|
||
|
||
-- Quarter scope qN → ставим v_q и нормализуем scope в 'q' для маппинга
|
||
IF v_scope IN ('q1','q2','q3','q4') THEN
|
||
v_q := substring(v_scope FROM 2)::SMALLINT;
|
||
ELSE
|
||
v_q := NULL;
|
||
END IF;
|
||
|
||
-- Actor для sequestration scopes
|
||
v_actor := CASE v_scope
|
||
WHEN 'sequestration' THEN 'DFIP'
|
||
WHEN 'seq_dfip' THEN 'DFIP'
|
||
WHEN 'seq_ssp' THEN 'SSP_GO'
|
||
ELSE NULL END;
|
||
|
||
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 ──────────────────────────────────────────────────────────
|
||
('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 ───────────────────────────────────────────────────────
|
||
('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 (FORM_1/4) ─────────────────────────────────────────
|
||
('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 (FORM_1/4) ───────────────────────────────────
|
||
('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_approval (FORM_1/4) ─────────────────────────────────
|
||
('collegial','approved', 'collegial_approval','approved_amount', 'NUMERIC','line'),
|
||
('collegial','protocol', 'collegial_approval','protocol_reference','TEXT', 'line'),
|
||
('collegial','note', 'collegial_approval','note', 'TEXT', 'line'),
|
||
|
||
-- ─── ckk (FORM_1/4) ────────────────────────────────────────────────
|
||
('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 (FORM_1) и его alias contract (FORM_2/4) ─────
|
||
('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 (line_id, actor) — три алиаса scope ────────────
|
||
('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'),
|
||
|
||
-- ─── budget_line_quarter — все 4 квартала через нормализованный scope='q' ─
|
||
-- (v_scope qN, v_q заполнен; маппинг ниже использует scope='q')
|
||
('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'),
|
||
-- разбивка переноса в следующий квартал (transfer_q{N+1}_*) — единые поля,
|
||
-- индекс N+1 в имени ключа для UI смыслово важен, в БД одно и то же поле:
|
||
('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'),
|
||
-- plan_revision_* — две группы алиасов: FORM_1 (rev_*) и FORM_2/4 (revision_*).
|
||
('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
|
||
-- Распознаваемые computed-ключи отдельно — для понятной ошибки:
|
||
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;
|
||
|
||
-- Извлекаем скаляр из JSONB — '#>> {}' возвращает NULL для jsonb null
|
||
-- Cast прицельный (NUMERIC/TEXT/DATE/INT) делается в dynamic SQL ниже.
|
||
|
||
-- ── Dynamic UPSERT ────────────────────────────────────────────────────
|
||
IF v_key_kind = 'line' THEN
|
||
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
|
||
);
|
||
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;
|
||
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
|
||
);
|
||
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;
|
||
-- Для quarter-полей sequestration: scope.field=qN→adj_qN, justification без quarter
|
||
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
|
||
);
|
||
EXECUTE v_sql USING p_line_id, v_actor, p_value;
|
||
|
||
ELSE
|
||
RAISE EXCEPTION 'internal: unknown key_kind %', v_key_kind;
|
||
END IF;
|
||
|
||
-- Помечаем budget_line как изменённую (для updated_at iteration)
|
||
UPDATE v3.budget_line SET updated_at = now() WHERE id = p_line_id;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
|
||
-- Удаляем FK у audit_log на случай удаления исконной записи и сохранения в журнале
|
||
ALTER TABLE v3.audit_log DROP CONSTRAINT IF EXISTS audit_log_app_user_fk;
|
||
ALTER TABLE v3.audit_log DROP CONSTRAINT IF EXISTS audit_log_org_unit_fk;
|
||
ALTER TABLE v3.audit_log DROP CONSTRAINT IF EXISTS audit_log_budget_form_fk;
|
||
ALTER TABLE v3.project ADD COLUMN created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now();
|
||
|
||
|
||
-- ════════════════════════════════════════════════════════════════════════════
|
||
-- ПАТЧ: v3.v_form_view — добавление p_user_id + META-строки (editable-маска)
|
||
-- ════════════════════════════════════════════════════════════════════════════
|
||
-- Идемпотентен. Пересоздаёт функцию (DROP обеих сигнатур + CREATE).
|
||
-- Зависимости (должны уже быть на целевой БД): v3.editable_columns_for(INT,INT,VARCHAR),
|
||
-- v3.user_role_code(INT), v3.budget_form, а также v_form1/2/4-функции.
|
||
--
|
||
-- Применение:
|
||
-- psql -U dfip -d dfip -f patch_v_form_view_meta.sql
|
||
-- docker exec -i <container> psql -U dfip -d dfip < patch_v_form_view_meta.sql
|
||
-- ════════════════════════════════════════════════════════════════════════════
|
||
|
||
-- ─── precheck зависимостей: падаем ДО DROP, если чего-то нет ─────────────────
|
||
DO $precheck$
|
||
BEGIN
|
||
IF to_regprocedure('v3.editable_columns_for(int,int,varchar)') IS NULL THEN
|
||
RAISE EXCEPTION 'Зависимость отсутствует: v3.editable_columns_for(INT,INT,VARCHAR). Накатите sql/v3/permissions.sql';
|
||
END IF;
|
||
IF to_regprocedure('v3.user_role_code(int)') IS NULL THEN
|
||
RAISE EXCEPTION 'Зависимость отсутствует: v3.user_role_code(INT). Накатите sql/v3/permissions.sql';
|
||
END IF;
|
||
END
|
||
$precheck$;
|
||
|
||
DROP FUNCTION IF EXISTS v3.v_form_view(INT, VARCHAR, TEXT[], VARCHAR);
|
||
DROP FUNCTION IF EXISTS v3.v_form_view(INT, VARCHAR, TEXT[], VARCHAR, INT);
|
||
|
||
CREATE FUNCTION v3.v_form_view(
|
||
p_form_id INT,
|
||
p_sheet VARCHAR,
|
||
p_sections TEXT[] DEFAULT NULL,
|
||
p_direction VARCHAR DEFAULT NULL,
|
||
p_user_id INT DEFAULT NULL
|
||
)
|
||
RETURNS TABLE (
|
||
row_type VARCHAR,
|
||
depth INT,
|
||
sort_order BIGINT,
|
||
data JSONB
|
||
)
|
||
AS $function$
|
||
DECLARE
|
||
v_form_type VARCHAR;
|
||
v_year INT;
|
||
v_org_unit_id INT;
|
||
BEGIN
|
||
-- Определяем form_type автоматически
|
||
SELECT form_type_code, year, org_unit_id
|
||
INTO v_form_type, v_year, v_org_unit_id
|
||
FROM v3.budget_form
|
||
WHERE id = p_form_id;
|
||
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'budget_form id=% не существует в v3.budget_form', p_form_id;
|
||
END IF;
|
||
|
||
-- ═══ META-строка (маска редактируемых колонок) ════════════════════════════
|
||
-- Отдаётся первой (sort_order=0), только если передан p_user_id. НЕ гейт на
|
||
-- просмотр — сами строки листа возвращаются ниже независимо от прав.
|
||
IF p_user_id IS NOT NULL THEN
|
||
RETURN QUERY
|
||
SELECT
|
||
'META'::VARCHAR,
|
||
-1,
|
||
0::BIGINT,
|
||
jsonb_build_object(
|
||
'user_id', p_user_id,
|
||
'role', v3.user_role_code(p_user_id),
|
||
'sheet', p_sheet,
|
||
'editable', COALESCE(
|
||
(SELECT jsonb_agg(
|
||
jsonb_build_object('column', ec.column_key,
|
||
'closes_at', ec.closes_at)
|
||
ORDER BY ec.column_key)
|
||
FROM v3.editable_columns_for(p_form_id, p_user_id, p_sheet) ec),
|
||
'[]'::JSONB)
|
||
);
|
||
END IF;
|
||
|
||
-- ═══ FORM_1 ═══════════════════════════════════════════════════════════════
|
||
IF v_form_type = 'FORM_1' THEN
|
||
|
||
IF p_sheet = 'SMETA' THEN
|
||
-- v3.v_form1_smeta параметризуется year+org_unit_id
|
||
RETURN QUERY
|
||
SELECT
|
||
sm.row_type,
|
||
sm.depth,
|
||
ROW_NUMBER() OVER (ORDER BY sm.section_code) AS sort_order,
|
||
jsonb_build_object(
|
||
'section_code', sm.section_code,
|
||
'name', sm.name,
|
||
'plan', jsonb_build_object(
|
||
'support', jsonb_build_object('q1', sm.supp_plan_q1, 'q2', sm.supp_plan_q2,
|
||
'q3', sm.supp_plan_q3, 'q4', sm.supp_plan_q4,
|
||
'year', sm.supp_plan_year),
|
||
'development', jsonb_build_object('q1', sm.dev_plan_q1, 'q2', sm.dev_plan_q2,
|
||
'q3', sm.dev_plan_q3, 'q4', sm.dev_plan_q4,
|
||
'year', sm.dev_plan_year),
|
||
'total_year', sm.total_plan_year),
|
||
'approved', jsonb_build_object(
|
||
'support', jsonb_build_object('q1', sm.supp_appr_q1, 'q2', sm.supp_appr_q2,
|
||
'q3', sm.supp_appr_q3, 'q4', sm.supp_appr_q4,
|
||
'year', sm.supp_appr_year),
|
||
'development', jsonb_build_object('q1', sm.dev_appr_q1, 'q2', sm.dev_appr_q2,
|
||
'q3', sm.dev_appr_q3, 'q4', sm.dev_appr_q4,
|
||
'year', sm.dev_appr_year),
|
||
'total_year', sm.total_appr_year),
|
||
'fact', jsonb_build_object(
|
||
'support', jsonb_build_object('q1', sm.supp_act_q1, 'q2', sm.supp_act_q2,
|
||
'q3', sm.supp_act_q3, 'q4', sm.supp_act_q4,
|
||
'year', sm.supp_act_year),
|
||
'development', jsonb_build_object('q1', sm.dev_act_q1, 'q2', sm.dev_act_q2,
|
||
'q3', sm.dev_act_q3, 'q4', sm.dev_act_q4,
|
||
'year', sm.dev_act_year),
|
||
'total_year', sm.total_act_year),
|
||
'corrected', jsonb_build_object(
|
||
'support', jsonb_build_object('q2', sm.supp_corr_q2, 'q3', sm.supp_corr_q3, 'q4', sm.supp_corr_q4),
|
||
'development', jsonb_build_object('q2', sm.dev_corr_q2, 'q3', sm.dev_corr_q3, 'q4', sm.dev_corr_q4))
|
||
)
|
||
FROM v3.v_form1_smeta(v_year, v_org_unit_id) sm
|
||
ORDER BY sm.section_code;
|
||
RETURN;
|
||
END IF;
|
||
|
||
IF p_sheet IN ('AHR','CAP','OPER') THEN
|
||
RETURN QUERY
|
||
SELECT
|
||
j.row_type,
|
||
j.depth,
|
||
ROW_NUMBER() OVER (ORDER BY j._sort_path) AS sort_order,
|
||
jsonb_build_object(
|
||
'line_id', j.line_id,
|
||
'header', j.header,
|
||
'plan', j.plan_data,
|
||
'contract_summary', j.contract_summary,
|
||
'allocation', j.allocation_data,
|
||
'sequestration', j.sequestration_data,
|
||
'reserve', j.reserve_data,
|
||
'approved', j.approved_data,
|
||
'collegial', j.collegial_data,
|
||
'ckk', j.ckk_data,
|
||
'contract_detail', j.contract_detail,
|
||
'q1', j.q1_data,
|
||
'q2', j.q2_data,
|
||
'q3', j.q3_data,
|
||
'q4', j.q4_data,
|
||
'totals', j.totals_data
|
||
)
|
||
FROM v3.v_form1_sheet_jsonb(p_form_id, p_sheet, p_direction, p_sections) j
|
||
ORDER BY j._sort_path;
|
||
RETURN;
|
||
END IF;
|
||
|
||
RAISE EXCEPTION 'Unknown FORM_1 sheet: %', p_sheet
|
||
USING HINT = 'Use AHR / CAP / OPER / SMETA';
|
||
END IF;
|
||
|
||
-- ═══ FORM_2 ═══════════════════════════════════════════════════════════════
|
||
IF v_form_type = 'FORM_2' THEN
|
||
RETURN QUERY SELECT * FROM v3.v_form2_view(p_form_id, p_sheet, p_sections);
|
||
RETURN;
|
||
END IF;
|
||
|
||
-- ═══ FORM_4 ═══════════════════════════════════════════════════════════════
|
||
IF v_form_type = 'FORM_4' THEN
|
||
RETURN QUERY SELECT * FROM v3.v_form4_view(p_form_id, p_sheet, p_sections);
|
||
RETURN;
|
||
END IF;
|
||
|
||
RAISE EXCEPTION 'Unsupported form_type=% for form_id=%', v_form_type, p_form_id;
|
||
|
||
END;
|
||
$function$
|
||
LANGUAGE plpgsql STABLE;
|
||
|
||
|
||
-- ════════════════════════════════════════════════════════════════════════════
|
||
-- ПАТЧ: v3.v_form3_report_jsonb — добавление p_user_id + META-строки (editable)
|
||
-- ════════════════════════════════════════════════════════════════════════════
|
||
-- Идемпотентен. Пересоздаёт функцию (DROP 2-арг и 3-арг сигнатур + CREATE).
|
||
-- Зависимости (должны быть на целевой БД): v3.editable_columns_for3(INT,INT),
|
||
-- v3.user_role_code(INT), v3.rf_project_report, v3.v_form3_report_sections.
|
||
--
|
||
-- Применение:
|
||
-- psql -U dfip -d dfip -f patch_v_form3_report_jsonb_meta.sql
|
||
-- docker exec -i <container> psql -U dfip -d dfip < patch_v_form3_report_jsonb_meta.sql
|
||
-- ════════════════════════════════════════════════════════════════════════════
|
||
|
||
DO $precheck$
|
||
BEGIN
|
||
IF to_regprocedure('v3.editable_columns_for3(int,int)') IS NULL THEN
|
||
RAISE EXCEPTION 'Зависимость отсутствует: v3.editable_columns_for3(INT,INT). Накатите sql/v3/permissions_form3.sql';
|
||
END IF;
|
||
IF to_regprocedure('v3.user_role_code(int)') IS NULL THEN
|
||
RAISE EXCEPTION 'Зависимость отсутствует: v3.user_role_code(INT). Накатите sql/v3/permissions.sql';
|
||
END IF;
|
||
END
|
||
$precheck$;
|
||
|
||
-- ════════════════════════════════════════════════════════════════════════════
|
||
-- v3.v_form3_report_jsonb — JSONB-обёртка над v_form3_report_sections
|
||
-- ════════════════════════════════════════════════════════════════════════════
|
||
-- Один трек FORM_3 (LIMIT либо CURRENT_EXPENSES) как (row_type, depth,
|
||
-- sort_order, data jsonb) — общий формат UI с FORM_1/2/4.
|
||
--
|
||
-- p_user_id — если задан, ПЕРВОЙ строкой (row_type='META', depth=-1,
|
||
-- sort_order=0) идёт маска редактируемых колонок текущей фазы для этого юзера:
|
||
-- data = { user_id, role, report_id, report_type, editable:[{column,closes_at}] }.
|
||
-- Маска — v3.editable_columns_for3(report, user) (якорь rf_project_report,
|
||
-- лист/трек определяется отчётом внутри). Это НЕ гейт на просмотр — строки
|
||
-- отчёта отдаются всегда; при отсутствии прав editable=[] → фронт read-only.
|
||
-- NULL (по умолчанию) → META-строки нет (обратная совместимость).
|
||
-- ════════════════════════════════════════════════════════════════════════════
|
||
|
||
DROP FUNCTION IF EXISTS v3.v_form3_report_jsonb(INT, TEXT[]);
|
||
DROP FUNCTION IF EXISTS v3.v_form3_report_jsonb(INT, TEXT[], INT);
|
||
|
||
CREATE FUNCTION v3.v_form3_report_jsonb(
|
||
p_report_id INT,
|
||
p_sections TEXT[] DEFAULT NULL,
|
||
p_user_id INT DEFAULT NULL
|
||
)
|
||
RETURNS TABLE (
|
||
row_type VARCHAR,
|
||
depth INT,
|
||
sort_order BIGINT,
|
||
data JSONB
|
||
)
|
||
AS $function$
|
||
SELECT u.row_type, u.depth, u.sort_order, u.data
|
||
FROM (
|
||
-- ═══ META-строка (маска редактируемых колонок) ══════════════════════════
|
||
-- Только при переданном p_user_id. НЕ гейт на просмотр.
|
||
SELECT
|
||
'META'::VARCHAR AS row_type,
|
||
-1 AS depth,
|
||
0::BIGINT AS sort_order,
|
||
jsonb_build_object(
|
||
'user_id', p_user_id,
|
||
'role', v3.user_role_code(p_user_id),
|
||
'report_id', p_report_id,
|
||
'report_type', (SELECT report_type FROM v3.rf_project_report WHERE id = p_report_id),
|
||
'editable', COALESCE(
|
||
(SELECT jsonb_agg(
|
||
jsonb_build_object('column', ec.column_key,
|
||
'closes_at', ec.closes_at)
|
||
ORDER BY ec.column_key)
|
||
FROM v3.editable_columns_for3(p_report_id, p_user_id) ec),
|
||
'[]'::JSONB)
|
||
) AS data
|
||
WHERE p_user_id IS NOT NULL
|
||
|
||
UNION ALL
|
||
|
||
SELECT
|
||
s.row_type,
|
||
s.depth,
|
||
ROW_NUMBER() OVER (ORDER BY s._sort_path) AS sort_order,
|
||
jsonb_build_object(
|
||
'line_id', s.line_id,
|
||
'header', jsonb_build_object(
|
||
'section_code', s.col_section_code,
|
||
'item_id', s.col_item_id,
|
||
'num_group_id', s.col_num_group_id,
|
||
'name', s.col_name,
|
||
-- expense_item_id из _sort_path: для INPUT путь = {...ei_id, line_id},
|
||
-- для иерархии = {...ei_id} → последний/предпоследний элемент.
|
||
'expense_item_id', CASE WHEN s.row_type = 'INPUT'
|
||
THEN s._sort_path[array_upper(s._sort_path,1)-1]
|
||
ELSE s._sort_path[array_upper(s._sort_path,1)] END
|
||
),
|
||
'q1', jsonb_build_object(
|
||
'base_plan', s.col_q1_base_plan,
|
||
'adj_by_items', s.col_q1_adj_by_items,
|
||
'adj_increase', s.col_q1_adj_increase,
|
||
'total_corr', s.col_q1_total_corr,
|
||
'm1', s.col_q1_m1,
|
||
'm2', s.col_q1_m2,
|
||
'm3', s.col_q1_m3,
|
||
'quarter_actual', s.col_q1_quarter_actual,
|
||
'economy', s.col_q1_economy -- «Остаток» = корр+увелич−квартал
|
||
),
|
||
'q2', jsonb_build_object(
|
||
'base_plan', s.col_q2_base_plan,
|
||
'corrected_plan', s.col_q2_corrected_plan,
|
||
'carryover', s.col_q2_carryover,
|
||
'adj_by_items', s.col_q2_adj_by_items,
|
||
'adj_increase', s.col_q2_adj_increase,
|
||
'total_corr', s.col_q2_total_corr,
|
||
'm1', s.col_q2_m1,
|
||
'm2', s.col_q2_m2,
|
||
'm3', s.col_q2_m3,
|
||
'quarter_actual', s.col_q2_quarter_actual,
|
||
'economy', s.col_q2_economy
|
||
),
|
||
'q3', jsonb_build_object(
|
||
'base_plan', s.col_q3_base_plan,
|
||
'corrected_plan', s.col_q3_corrected_plan,
|
||
'carryover', s.col_q3_carryover,
|
||
'adj_by_items', s.col_q3_adj_by_items,
|
||
'adj_increase', s.col_q3_adj_increase,
|
||
'total_corr', s.col_q3_total_corr,
|
||
'm1', s.col_q3_m1,
|
||
'm2', s.col_q3_m2,
|
||
'm3', s.col_q3_m3,
|
||
'quarter_actual', s.col_q3_quarter_actual,
|
||
'economy', s.col_q3_economy
|
||
),
|
||
'q4', jsonb_build_object(
|
||
'base_plan', s.col_q4_base_plan,
|
||
'corrected_plan', s.col_q4_corrected_plan,
|
||
'carryover', s.col_q4_carryover,
|
||
'adj_by_items', s.col_q4_adj_by_items,
|
||
'adj_increase', s.col_q4_adj_increase,
|
||
'total_corr', s.col_q4_total_corr,
|
||
'm1', s.col_q4_m1,
|
||
'm2', s.col_q4_m2,
|
||
'm3', s.col_q4_m3,
|
||
'spod', s.col_q4_spod,
|
||
'quarter_actual', s.col_q4_quarter_actual,
|
||
'economy', s.col_q4_economy
|
||
),
|
||
'totals', jsonb_build_object(
|
||
'base_plan', s.col_year_base_plan, -- Год Базового плана (Σ q1–q4)
|
||
'total_corr', s.col_year_total_corr,
|
||
'total_actual', s.col_year_total_actual,
|
||
'economy', s.col_year_economy
|
||
)
|
||
) AS data
|
||
FROM v3.v_form3_report_sections(p_report_id, p_sections) s
|
||
) u
|
||
ORDER BY u.sort_order;
|
||
$function$
|
||
LANGUAGE sql STABLE;
|
||
|