7798 lines
393 KiB
PL/PgSQL
7798 lines
393 KiB
PL/PgSQL
-- DROP FUNCTION v3._apply_form3_cell(int4, text, jsonb);
|
||
|
||
CREATE OR REPLACE FUNCTION v3._apply_form3_cell(p_line_id integer, p_column text, p_value jsonb)
|
||
RETURNS void
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_parts TEXT[];
|
||
v_scope TEXT;
|
||
v_field TEXT;
|
||
v_q SMALLINT;
|
||
v_target_col TEXT;
|
||
v_target_type TEXT;
|
||
v_sql TEXT;
|
||
BEGIN
|
||
v_parts := string_to_array(p_column, '.');
|
||
IF array_length(v_parts, 1) < 2 THEN
|
||
RAISE EXCEPTION 'bad_column_format: %, expected scope.field', p_column;
|
||
END IF;
|
||
v_scope := v_parts[1];
|
||
v_field := v_parts[2];
|
||
|
||
IF v_scope = 'totals' OR v_field IN ('total_corr','quarter_actual','economy','carryover') THEN
|
||
RAISE EXCEPTION 'computed_field: %', p_column;
|
||
END IF;
|
||
|
||
IF v_scope NOT IN ('q1','q2','q3','q4') THEN
|
||
RAISE EXCEPTION 'unknown_scope: % (FORM_3 supports only q1..q4)', v_scope;
|
||
END IF;
|
||
|
||
v_q := substring(v_scope FROM 2)::SMALLINT;
|
||
|
||
-- Маппинг JSONB-ключ → колонка rf_project_report_quarter
|
||
CASE v_field
|
||
WHEN 'adj_by_items' THEN v_target_col := 'adj_by_items'; v_target_type := 'NUMERIC';
|
||
WHEN 'adj_increase' THEN v_target_col := 'adj_increase'; v_target_type := 'NUMERIC';
|
||
WHEN 'm1' THEN v_target_col := 'actual_m1'; v_target_type := 'NUMERIC';
|
||
WHEN 'm2' THEN v_target_col := 'actual_m2'; v_target_type := 'NUMERIC';
|
||
WHEN 'm3' THEN v_target_col := 'actual_m3'; v_target_type := 'NUMERIC';
|
||
WHEN 'spod' THEN
|
||
IF v_q <> 4 THEN RAISE EXCEPTION 'spod is q4-only: %', p_column; END IF;
|
||
v_target_col := 'actual_spod'; v_target_type := 'NUMERIC';
|
||
ELSE
|
||
RAISE EXCEPTION 'unknown_column: %', p_column;
|
||
END CASE;
|
||
|
||
v_sql := format(
|
||
'INSERT INTO v3.rf_project_report_quarter (rf_project_report_line_id, quarter, %1$I) '
|
||
|| 'VALUES ($1, $2, ($3 #>> ''{}'')::%2$s) '
|
||
|| 'ON CONFLICT (rf_project_report_line_id, quarter) DO UPDATE SET %1$I = EXCLUDED.%1$I',
|
||
v_target_col, v_target_type
|
||
);
|
||
EXECUTE v_sql USING p_line_id, v_q, p_value;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3._apply_form_cell(int4, text, jsonb, varchar, int4);
|
||
|
||
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'),
|
||
|
||
-- ─── 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'),
|
||
|
||
-- ─── 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','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$
|
||
;
|
||
|
||
-- DROP FUNCTION v3._diff_val(anyelement);
|
||
|
||
CREATE OR REPLACE FUNCTION v3._diff_val(p_val anyelement)
|
||
RETURNS jsonb
|
||
LANGUAGE sql
|
||
IMMUTABLE
|
||
AS $function$
|
||
SELECT CASE WHEN p_val IS NULL THEN 'null'::jsonb
|
||
ELSE to_jsonb(p_val::TEXT) END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3._expense_item_path_set(_int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3._expense_item_path_set(p_eids integer[])
|
||
RETURNS integer[]
|
||
LANGUAGE sql
|
||
STABLE
|
||
AS $function$
|
||
WITH RECURSIVE up AS (
|
||
SELECT id, parent_id FROM v3.expense_item WHERE id = ANY(p_eids)
|
||
UNION
|
||
SELECT p.id, p.parent_id
|
||
FROM v3.expense_item p
|
||
JOIN up u ON p.id = u.parent_id
|
||
)
|
||
SELECT array_agg(DISTINCT id) FROM up;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3._jsonb_diff(jsonb, jsonb);
|
||
|
||
CREATE OR REPLACE FUNCTION v3._jsonb_diff(a jsonb, b jsonb)
|
||
RETURNS jsonb
|
||
LANGUAGE plpgsql
|
||
IMMUTABLE
|
||
AS $function$
|
||
DECLARE
|
||
result JSONB := '{}'::jsonb;
|
||
k TEXT;
|
||
va JSONB;
|
||
vb JSONB;
|
||
sub JSONB;
|
||
BEGIN
|
||
IF a IS NULL THEN
|
||
RETURN COALESCE(b, '{}'::jsonb);
|
||
END IF;
|
||
IF b IS NULL THEN
|
||
RETURN '{}'::jsonb;
|
||
END IF;
|
||
|
||
-- Ключи `b`: если отличаются от `a` — добавляем
|
||
FOR k IN SELECT jsonb_object_keys(b) LOOP
|
||
va := a -> k;
|
||
vb := b -> k;
|
||
IF jsonb_typeof(va) = 'object' AND jsonb_typeof(vb) = 'object' THEN
|
||
sub := v3._jsonb_diff(va, vb);
|
||
IF sub <> '{}'::jsonb THEN
|
||
result := result || jsonb_build_object(k, sub);
|
||
END IF;
|
||
ELSIF va IS DISTINCT FROM vb THEN
|
||
result := result || jsonb_build_object(k, vb);
|
||
END IF;
|
||
END LOOP;
|
||
|
||
-- Ключи которые БЫЛИ в `a` но УДАЛЕНЫ в `b` — представляем как null
|
||
FOR k IN SELECT jsonb_object_keys(a) LOOP
|
||
IF NOT (b ? k) THEN
|
||
result := result || jsonb_build_object(k, NULL);
|
||
END IF;
|
||
END LOOP;
|
||
|
||
RETURN result;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3._limit_template_path_set(_int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3._limit_template_path_set(p_ids integer[])
|
||
RETURNS integer[]
|
||
LANGUAGE sql
|
||
STABLE
|
||
AS $function$
|
||
WITH RECURSIVE up AS (
|
||
SELECT id, parent_id FROM v3.limit_template WHERE id = ANY(p_ids)
|
||
UNION
|
||
SELECT p.id, p.parent_id
|
||
FROM v3.limit_template p
|
||
JOIN up u ON p.id = u.parent_id
|
||
)
|
||
SELECT array_agg(DISTINCT id) FROM up;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.add_budget_form(varchar, int4, int4, int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.add_budget_form(p_form_type character varying, p_year integer, p_org_unit_id integer DEFAULT NULL::integer, p_user_id integer DEFAULT NULL::integer)
|
||
RETURNS integer
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_form_id INTEGER;
|
||
v_ssp_list JSONB := '[]'::jsonb;
|
||
BEGIN
|
||
IF p_user_id IS NOT NULL
|
||
AND NOT EXISTS (SELECT 1 FROM v3.app_user WHERE id = p_user_id) THEN
|
||
RAISE EXCEPTION 'app_user id=% не существует', p_user_id;
|
||
END IF;
|
||
|
||
IF p_org_unit_id IS NOT NULL
|
||
AND NOT EXISTS (SELECT 1 FROM v3.org_unit WHERE id = p_org_unit_id) THEN
|
||
RAISE EXCEPTION 'org_unit id=% не существует', p_org_unit_id;
|
||
END IF;
|
||
|
||
INSERT INTO v3.budget_form
|
||
(form_type_code, year, org_unit_id, created_by)
|
||
VALUES
|
||
(p_form_type, p_year, p_org_unit_id, p_user_id)
|
||
RETURNING id INTO v_form_id;
|
||
|
||
PERFORM v3.copy_template_to_form(v_form_id);
|
||
|
||
IF p_org_unit_id IS NOT NULL THEN
|
||
SELECT jsonb_build_array(
|
||
jsonb_build_object(
|
||
'id', ou.id,
|
||
'title', ou.title
|
||
)
|
||
)
|
||
INTO v_ssp_list
|
||
FROM v3.org_unit ou
|
||
WHERE ou.id = p_org_unit_id;
|
||
END IF;
|
||
|
||
PERFORM v3.log_event(
|
||
'CREATE_TASK', 'TASK',
|
||
jsonb_build_object(
|
||
'form_type', p_form_type,
|
||
'year', p_year,
|
||
'org_unit_id', p_org_unit_id,
|
||
'ssp_list', COALESCE(v_ssp_list, '[]'::jsonb),
|
||
'entity_id', v_form_id
|
||
),
|
||
v_form_id, null, p_org_unit_id
|
||
);
|
||
|
||
RETURN v_form_id;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.add_budget_line(int4, int4, varchar, varchar, varchar, varchar, varchar, varchar, int4, int4, varchar, varchar, date);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.add_budget_line(p_form_id integer, p_expense_item_id integer DEFAULT NULL::integer, p_sheet character varying DEFAULT NULL::character varying, p_item_id character varying DEFAULT NULL::character varying, p_section_code character varying DEFAULT NULL::character varying, p_direction character varying DEFAULT NULL::character varying, p_name character varying DEFAULT NULL::character varying, p_internal_order character varying DEFAULT NULL::character varying, p_vsp_id integer DEFAULT NULL::integer, p_project_id integer DEFAULT NULL::integer, p_justification character varying DEFAULT NULL::character varying, p_contract_number character varying DEFAULT NULL::character varying, p_contract_end_date date DEFAULT NULL::date)
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_form_type VARCHAR;
|
||
v_org_id INT;
|
||
v_eid INT;
|
||
v_sheet VARCHAR;
|
||
v_auto_dir VARCHAR;
|
||
v_final_dir VARCHAR;
|
||
v_new_id INT;
|
||
v_cnt INT;
|
||
v_sat_table TEXT;
|
||
v_sat_eid INT;
|
||
v_parent_line INT;
|
||
BEGIN
|
||
-- 1. form_id → form_type
|
||
SELECT form_type_code, org_unit_id INTO v_form_type, v_org_id FROM v3.budget_form WHERE id = p_form_id;
|
||
IF v_form_type IS NULL THEN
|
||
RAISE EXCEPTION 'budget_form id=% не существует', p_form_id;
|
||
END IF;
|
||
IF v_form_type = 'FORM_3' THEN
|
||
RAISE EXCEPTION 'FORM_3 не использует budget_line — см. add_form3_line (TBD)';
|
||
END IF;
|
||
|
||
-- ═══ Сателлитные листы (AHR_RENT/UTILITY/SECURITY) ═══════════════════
|
||
-- Фиксированный expense_item_id на лист — UI не передаёт R-код.
|
||
-- p_vsp_id обязателен. Опц.: p_contract_number, p_contract_end_date.
|
||
-- Внутри: находим/создаём родительскую budget_line с фиксированным ei,
|
||
-- затем INSERT в *_detail. Возврат — одна новая LEAF.
|
||
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;
|
||
v_sat_eid := CASE p_sheet
|
||
WHEN 'AHR_RENT' THEN 293 -- R061031001 "Аренда_ОН"
|
||
WHEN 'AHR_UTILITY' THEN 24 -- 1.05.1. "Коммунальные услуги"
|
||
WHEN 'AHR_SECURITY' THEN 779 -- R061062001 "Пультовая охр.ОН"
|
||
END;
|
||
|
||
IF p_vsp_id IS NULL THEN
|
||
RAISE EXCEPTION 'p_vsp_id обязателен для сателлитного листа %', p_sheet;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM v3.vsp WHERE id = p_vsp_id) THEN
|
||
RAISE EXCEPTION 'vsp id=% не существует', p_vsp_id;
|
||
END IF;
|
||
|
||
-- Находим/создаём родительскую budget_line под фиксированным ei
|
||
SELECT id INTO v_parent_line
|
||
FROM v3.budget_line
|
||
WHERE budget_form_id = p_form_id AND expense_item_id = v_sat_eid
|
||
LIMIT 1;
|
||
IF v_parent_line IS NULL THEN
|
||
INSERT INTO v3.budget_line (budget_form_id, expense_item_id, direction)
|
||
VALUES (
|
||
p_form_id, v_sat_eid,
|
||
CASE v_form_type WHEN 'FORM_2' THEN 'Support' WHEN 'FORM_4' THEN 'Development' ELSE NULL END
|
||
) RETURNING id INTO v_parent_line;
|
||
END IF;
|
||
|
||
-- INSERT в сателлит
|
||
EXECUTE format(
|
||
'INSERT INTO v3.%I (line_id, vsp_id, contract_number, contract_end_date) '
|
||
|| 'VALUES ($1, $2, $3, $4) RETURNING id',
|
||
v_sat_table
|
||
) USING v_parent_line, p_vsp_id, p_contract_number, p_contract_end_date
|
||
INTO v_new_id;
|
||
|
||
-- Возврат — одна новая INPUT-строка (отфильтрована по новому id сателлита)
|
||
RETURN QUERY
|
||
SELECT v.row_type, v.depth, v.sort_order, v.data
|
||
FROM v3.v_form_view(p_form_id, p_sheet, NULL, NULL) v
|
||
WHERE v.row_type = 'INPUT' AND (v.data->>'id')::INT = v_new_id;
|
||
RETURN;
|
||
END IF;
|
||
|
||
-- 2. Резолв expense_item_id
|
||
IF p_expense_item_id IS NOT NULL THEN
|
||
SELECT ei.id, ei.sheet INTO v_eid, v_sheet
|
||
FROM v3.expense_item ei WHERE ei.id = p_expense_item_id;
|
||
IF v_eid IS NULL THEN
|
||
RAISE EXCEPTION 'expense_item id=% не существует', p_expense_item_id;
|
||
END IF;
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM v3.expense_item_form_type
|
||
WHERE expense_item_id = v_eid AND form_type_code = v_form_type
|
||
) THEN
|
||
RAISE EXCEPTION 'expense_item id=% не привязан к form_type %', v_eid, v_form_type;
|
||
END IF;
|
||
ELSE
|
||
IF p_sheet IS NULL OR p_item_id IS NULL THEN
|
||
RAISE EXCEPTION 'нужен либо p_expense_item_id, либо (p_sheet + p_item_id)';
|
||
END IF;
|
||
|
||
SELECT count(*) INTO v_cnt
|
||
FROM v3.expense_item ei
|
||
JOIN v3.expense_item_form_type eift ON eift.expense_item_id = ei.id
|
||
WHERE ei.item_id = p_item_id
|
||
AND ei.sheet = p_sheet
|
||
AND eift.form_type_code = v_form_type
|
||
AND (p_section_code IS NULL OR ei.section_code = p_section_code)
|
||
AND (p_direction IS NULL OR ei.direction IS NULL OR ei.direction = p_direction);
|
||
|
||
IF v_cnt = 0 THEN
|
||
RAISE EXCEPTION 'expense_item не найден: sheet=%, item_id=%, section=%, direction=%, form=%',
|
||
p_sheet, p_item_id, p_section_code, p_direction, v_form_type;
|
||
END IF;
|
||
IF v_cnt > 1 THEN
|
||
RAISE EXCEPTION 'expense_item не уникален (% совпадений): sheet=%, item_id=%, section=%, direction=%. Уточните p_section_code или передайте p_expense_item_id напрямую',
|
||
v_cnt, p_sheet, p_item_id, p_section_code, p_direction;
|
||
END IF;
|
||
|
||
SELECT ei.id, ei.sheet INTO v_eid, v_sheet
|
||
FROM v3.expense_item ei
|
||
JOIN v3.expense_item_form_type eift ON eift.expense_item_id = ei.id
|
||
WHERE ei.item_id = p_item_id
|
||
AND ei.sheet = p_sheet
|
||
AND eift.form_type_code = v_form_type
|
||
AND (p_section_code IS NULL OR ei.section_code = p_section_code)
|
||
AND (p_direction IS NULL OR ei.direction IS NULL OR ei.direction = p_direction);
|
||
END IF;
|
||
|
||
-- 3. Авто-direction (для FORM_1/AHR|CAP — обязателен у юзера)
|
||
v_auto_dir := CASE
|
||
WHEN v_form_type = 'FORM_2' THEN 'Support'
|
||
WHEN v_form_type = 'FORM_4' THEN 'Development'
|
||
WHEN v_form_type = 'FORM_1' AND v_sheet = 'OPER' THEN NULL
|
||
ELSE NULL
|
||
END;
|
||
|
||
IF v_form_type = 'FORM_1' AND v_sheet IN ('AHR','CAP') AND p_direction IS NULL THEN
|
||
RAISE EXCEPTION 'p_direction обязателен для FORM_1 / sheet=% (Support|Development)', v_sheet;
|
||
END IF;
|
||
|
||
IF v_form_type IN ('FORM_2','FORM_4') AND p_direction IS NOT NULL AND p_direction <> v_auto_dir THEN
|
||
RAISE EXCEPTION 'p_direction=% несовместим с form_type=% (фиксирован=%)',
|
||
p_direction, v_form_type, v_auto_dir;
|
||
END IF;
|
||
|
||
IF v_form_type = 'FORM_1' AND v_sheet = 'OPER' AND p_direction IS NOT NULL THEN
|
||
RAISE EXCEPTION 'p_direction должен быть NULL для FORM_1 / sheet=OPER (получено: %)', p_direction;
|
||
END IF;
|
||
|
||
v_final_dir := COALESCE(p_direction, v_auto_dir);
|
||
|
||
-- 4. Sanity на vsp/project
|
||
IF p_vsp_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM v3.vsp WHERE id = p_vsp_id) THEN
|
||
RAISE EXCEPTION 'vsp id=% не существует', p_vsp_id;
|
||
END IF;
|
||
IF p_project_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM v3.project WHERE id = p_project_id) THEN
|
||
RAISE EXCEPTION 'project id=% не существует', p_project_id;
|
||
END IF;
|
||
|
||
-- 5. INSERT
|
||
INSERT INTO v3.budget_line (
|
||
budget_form_id, expense_item_id, name, internal_order,
|
||
vsp_id, project_id, direction, justification
|
||
) VALUES (
|
||
p_form_id, v_eid, p_name, p_internal_order,
|
||
p_vsp_id, p_project_id, v_final_dir, p_justification
|
||
) RETURNING id INTO v_new_id;
|
||
|
||
-- 5а. Аудит: ROW_CREATE
|
||
PERFORM v3.log_event(
|
||
'ROW_CREATE', 'ROW',
|
||
jsonb_build_object(
|
||
'sheet', v_sheet,
|
||
'line_id', v_new_id,
|
||
'expense_item_id', v_eid,
|
||
'direction', v_final_dir,
|
||
'name', p_name,
|
||
'vsp_id', p_vsp_id,
|
||
'project_id', p_project_id,
|
||
'entity_id', v_new_id,
|
||
'form_id', p_form_id
|
||
),
|
||
p_form_id, null, v_org_id
|
||
);
|
||
|
||
-- 6. Возврат: только иерархия по пути нового ei + новая INPUT
|
||
DECLARE v_ei_path INT[];
|
||
BEGIN
|
||
v_ei_path := v3._expense_item_path_set(ARRAY[v_eid]);
|
||
RETURN QUERY
|
||
SELECT v.row_type, v.depth, v.sort_order, v.data
|
||
FROM v3.v_form_view(p_form_id, v_sheet, NULL, v_final_dir) v
|
||
WHERE (v.row_type IN ('ROOT','GROUP','ITEM','SUB_ITEM')
|
||
AND (v.data->'header'->>'expense_item_id')::INT = ANY(v_ei_path))
|
||
OR (v.row_type = 'INPUT' AND (v.data->>'line_id')::INT = v_new_id)
|
||
ORDER BY v.sort_order;
|
||
END;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.add_form3_line(int4, int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.add_form3_line(p_report_id integer, p_expense_item_id integer)
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_new_id INT;
|
||
v_project_id INT;
|
||
v_org_id INT;
|
||
BEGIN
|
||
SELECT project_id INTO v_project_id FROM v3.rf_project_report WHERE id = p_report_id;
|
||
IF v_project_id IS NULL THEN
|
||
RAISE EXCEPTION 'rf_project_report id=% не существует', p_report_id;
|
||
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM v3.expense_item WHERE id = p_expense_item_id) THEN
|
||
RAISE EXCEPTION 'expense_item id=% не существует', p_expense_item_id;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM v3.expense_item_form_type
|
||
WHERE expense_item_id = p_expense_item_id AND form_type_code = 'FORM_3') THEN
|
||
RAISE EXCEPTION 'expense_item id=% не привязан к form_type FORM_3', p_expense_item_id;
|
||
END IF;
|
||
|
||
INSERT INTO v3.rf_project_report_line (rf_project_report_id, expense_item_id)
|
||
VALUES (p_report_id, p_expense_item_id)
|
||
RETURNING id INTO v_new_id;
|
||
|
||
SELECT org_unit_id INTO v_org_id FROM v3.project WHERE id = v_project_id;
|
||
|
||
|
||
|
||
-- Аудит: ROW_CREATE
|
||
PERFORM v3.log_event(
|
||
'ROW_CREATE', 'ROW',
|
||
jsonb_build_object(
|
||
'sheet', 'FORM_3',
|
||
'line_id', v_new_id,
|
||
'report_id', p_report_id,
|
||
'expense_item_id', p_expense_item_id,
|
||
'entity_id', v_new_id
|
||
), null, null, v_org_id
|
||
);
|
||
|
||
-- Возврат: только иерархия по пути нового ei + новая INPUT
|
||
DECLARE v_ei_path INT[];
|
||
BEGIN
|
||
v_ei_path := v3._expense_item_path_set(ARRAY[p_expense_item_id]);
|
||
RETURN QUERY
|
||
SELECT v.row_type, v.depth, v.sort_order, v.data
|
||
FROM v3.v_form3_report_jsonb(p_report_id, NULL) v
|
||
WHERE (v.row_type IN ('ROOT','GROUP','ITEM','SUB_ITEM')
|
||
AND (v.data->'header'->>'expense_item_id')::INT = ANY(v_ei_path))
|
||
OR (v.row_type = 'INPUT' AND (v.data->>'line_id')::INT = v_new_id)
|
||
ORDER BY v.sort_order;
|
||
END;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.add_form_phase(int4, varchar, varchar, varchar, _text, timestamptz, timestamptz);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.add_form_phase(p_budget_form_id integer, p_sheet character varying, p_phase_code character varying, p_role character varying, p_column_keys text[], p_opens_at timestamp with time zone, p_closes_at timestamp with time zone)
|
||
RETURNS v3.form_phase
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_phase v3.form_phase;
|
||
v_org_unit_id INT;
|
||
v_org_unit_title VARCHAR;
|
||
v_form_type_code VARCHAR;
|
||
v_year INT;
|
||
v_task_name TEXT;
|
||
v_phase_id TEXT;
|
||
BEGIN
|
||
INSERT INTO v3.form_phase(
|
||
budget_form_id, sheet, phase_code, role, column_keys, opens_at, closes_at
|
||
)
|
||
VALUES (
|
||
p_budget_form_id, p_sheet, p_phase_code, p_role, p_column_keys, p_opens_at, p_closes_at
|
||
)
|
||
RETURNING * INTO v_phase;
|
||
|
||
SELECT bf.org_unit_id, ou.title, bf.form_type_code, bf.year
|
||
INTO v_org_unit_id, v_org_unit_title, v_form_type_code, v_year
|
||
FROM v3.budget_form bf
|
||
LEFT JOIN v3.org_unit ou ON ou.id = bf.org_unit_id
|
||
WHERE bf.id = p_budget_form_id;
|
||
v_task_name := trim(concat(
|
||
COALESCE(v_form_type_code, ''),
|
||
CASE WHEN v_year IS NOT NULL THEN ' ' || v_year::TEXT ELSE '' END,
|
||
CASE WHEN v_org_unit_title IS NOT NULL THEN ' [' || v_org_unit_title || ']' ELSE '' END
|
||
));
|
||
v_phase_id := format('%s:%s:%s', p_budget_form_id, p_sheet, p_phase_code);
|
||
|
||
PERFORM v3.log_event(
|
||
'ACCESS_WINDOW_CHANGE', 'ACCESS',
|
||
jsonb_build_object(
|
||
'entity_type', 'form_phase',
|
||
'entity_id', p_budget_form_id,
|
||
'action', 'create',
|
||
'phase_id', v_phase_id,
|
||
'phase_name', p_phase_code,
|
||
'task_id', p_budget_form_id,
|
||
'form_id', p_budget_form_id,
|
||
'budget_form_id', p_budget_form_id,
|
||
'org_unit_id', v_org_unit_id,
|
||
'org_unit_name', v_org_unit_title,
|
||
'sheet', p_sheet,
|
||
'phase_code', p_phase_code,
|
||
'role', v_phase.role,
|
||
'column_keys', v_phase.column_keys,
|
||
'opens_at', v_phase.opens_at,
|
||
'closes_at', v_phase.closes_at
|
||
),
|
||
p_budget_form_id, null, v_org_unit_id
|
||
);
|
||
|
||
RETURN v_phase;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.add_project(varchar, int4, int4, varchar, int4, varchar, varchar, varchar, varchar, int4, numeric);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.add_project(p_name character varying, p_year integer, p_org_unit_id integer, p_level character varying DEFAULT 'project'::character varying, p_parent_id integer DEFAULT NULL::integer, p_project_type character varying DEFAULT NULL::character varying, p_vsp_format character varying DEFAULT NULL::character varying, p_placement_type character varying DEFAULT NULL::character varying, p_object_address character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric)
|
||
RETURNS TABLE(project_id integer, limit_report_id integer, current_expenses_report_id integer)
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_pid INT;
|
||
v_lim INT;
|
||
v_cur INT;
|
||
BEGIN
|
||
IF p_name IS NULL OR length(trim(p_name)) = 0 THEN
|
||
RAISE EXCEPTION 'p_name обязателен';
|
||
END IF;
|
||
IF p_year IS NULL THEN
|
||
RAISE EXCEPTION 'p_year обязателен';
|
||
END IF;
|
||
IF p_org_unit_id IS NULL THEN
|
||
RAISE EXCEPTION 'p_org_unit_id обязателен';
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM v3.org_unit WHERE id = p_org_unit_id) THEN
|
||
RAISE EXCEPTION 'org_unit id=% не существует', p_org_unit_id;
|
||
END IF;
|
||
IF p_parent_id IS NOT NULL AND
|
||
NOT EXISTS (SELECT 1 FROM v3.project WHERE id = p_parent_id AND level = 'program') THEN
|
||
RAISE EXCEPTION 'parent project id=% не существует или не имеет level=program', p_parent_id;
|
||
END IF;
|
||
|
||
-- INSERT project (CHECK-ы name/project_type/vsp_format/placement_type
|
||
-- из migrate_form3_v1.sql применятся автоматически).
|
||
INSERT INTO v3.project (
|
||
name, level, parent_id, org_unit_id,
|
||
project_type, vsp_format, placement_type, object_address, staff_count, total_area
|
||
) VALUES (
|
||
p_name, p_level, p_parent_id, p_org_unit_id,
|
||
p_project_type, p_vsp_format, p_placement_type, p_object_address, p_staff_count, p_total_area
|
||
) RETURNING id INTO v_pid;
|
||
|
||
-- Создаём 2 отчёта (LIMIT + CURRENT_EXPENSES) для проекта × год
|
||
INSERT INTO v3.rf_project_report (project_id, year, report_type)
|
||
VALUES (v_pid, p_year, 'LIMIT') RETURNING id INTO v_lim;
|
||
INSERT INTO v3.rf_project_report (project_id, year, report_type)
|
||
VALUES (v_pid, p_year, 'CURRENT_EXPENSES') RETURNING id INTO v_cur;
|
||
|
||
-- Аудит: PROJECT_CREATE — все заполненные поля + id отчётов
|
||
PERFORM v3.log_event(
|
||
'PROJECT_CREATE', 'PROJECT',
|
||
jsonb_strip_nulls(jsonb_build_object(
|
||
'project_id', v_pid,
|
||
'name', p_name,
|
||
'year', p_year,
|
||
'level', p_level,
|
||
'parent_id', p_parent_id,
|
||
'org_unit_id', p_org_unit_id,
|
||
'project_type', p_project_type,
|
||
'vsp_format', p_vsp_format,
|
||
'placement_type', p_placement_type,
|
||
'object_address', p_object_address,
|
||
'staff_count', p_staff_count,
|
||
'total_area', p_total_area,
|
||
'limit_report_id', v_lim,
|
||
'current_expenses_report_id', v_cur,
|
||
'entity_id', v_pid
|
||
)), null, null, p_org_unit_id
|
||
);
|
||
|
||
RETURN QUERY SELECT v_pid, v_lim, v_cur;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.add_user(varchar, varchar, varchar, varchar, int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.add_user(p_email character varying, p_username character varying, p_hashed_password character varying, p_full_name character varying, p_role_id integer)
|
||
RETURNS v3.app_user
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_user v3.app_user;
|
||
BEGIN
|
||
INSERT INTO v3.app_user(email, username, hashed_password, full_name, role_id, is_active)
|
||
VALUES (p_email, p_username, p_hashed_password, p_full_name, p_role_id, TRUE)
|
||
RETURNING * INTO v_user;
|
||
|
||
PERFORM v3.log_event(
|
||
'USER_CREATE', 'USER',
|
||
jsonb_strip_nulls(jsonb_build_object(
|
||
'user_id', v_user.id,
|
||
'email', v_user.email,
|
||
'username', v_user.username,
|
||
'full_name', v_user.full_name,
|
||
'role_id', v_user.role_id,
|
||
'entity_id', v_user.id
|
||
))
|
||
);
|
||
|
||
RETURN v_user;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.add_vsp(int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, varchar, date);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.add_vsp(p_branch_id integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT true, p_is_deleted boolean DEFAULT false, p_system_code character varying DEFAULT NULL::character varying, p_created_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_location_form character varying DEFAULT NULL::character varying, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date)
|
||
RETURNS integer
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_id INT;
|
||
v_branch_name VARCHAR;
|
||
v_actor_id INT;
|
||
v_actor_email VARCHAR;
|
||
v_actor_full_name VARCHAR;
|
||
BEGIN
|
||
SELECT ou.title INTO v_branch_name
|
||
FROM v3.org_unit ou
|
||
WHERE ou.id = p_branch_id;
|
||
|
||
v_actor_id := COALESCE(p_created_by, v3.current_user_id());
|
||
|
||
IF v_actor_id IS NOT NULL THEN
|
||
SELECT u.email, u.full_name
|
||
INTO v_actor_email, v_actor_full_name
|
||
FROM v3.app_user u
|
||
WHERE u.id = v_actor_id;
|
||
END IF;
|
||
|
||
INSERT INTO v3.vsp
|
||
(branch_id, reg_number, address, format, opened_at,
|
||
placement_type, staff_count, total_area, closed_at,
|
||
is_active, is_deleted, system_code, created_by,
|
||
vsp_type, notes, location_form, rent_contract_num, rent_end_date)
|
||
VALUES
|
||
(p_branch_id, p_reg_number, p_address, p_format, p_opened_at,
|
||
p_placement_type, p_staff_count, p_total_area, p_closed_at,
|
||
p_is_active, p_is_deleted, p_system_code, p_created_by,
|
||
p_vsp_type, p_notes, p_location_form, p_rent_contract_num, p_rent_end_date)
|
||
RETURNING id INTO v_id;
|
||
|
||
PERFORM v3.log_event(
|
||
'VSP_CREATE', 'VSP',
|
||
jsonb_build_object(
|
||
'vsp_id', v_id,
|
||
'entity_id', v_id,
|
||
'branch_id', p_branch_id,
|
||
'branch_name', v_branch_name,
|
||
'reg_number', p_reg_number,
|
||
'address', p_address,
|
||
'format', p_format,
|
||
'opened_at', v3._diff_val(p_opened_at),
|
||
'placement_type', p_placement_type,
|
||
'staff_count', p_staff_count,
|
||
'total_area', v3._diff_val(p_total_area),
|
||
'closed_at', v3._diff_val(p_closed_at),
|
||
'is_active', p_is_active,
|
||
'is_deleted', p_is_deleted,
|
||
'system_code', p_system_code,
|
||
'created_by', p_created_by,
|
||
'vsp_type', p_vsp_type,
|
||
'notes', p_notes,
|
||
'location_form', p_location_form,
|
||
'rent_contract_num', p_rent_contract_num,
|
||
'rent_end_date', v3._diff_val(p_rent_end_date),
|
||
'user_email', v_actor_email,
|
||
'user_full_name', v_actor_full_name
|
||
), null, null, p_branch_id
|
||
);
|
||
|
||
RETURN v_id;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.can_edit(int4, varchar, text, int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.can_edit(p_form_id integer, p_sheet character varying, p_column_key text, p_user_id integer)
|
||
RETURNS jsonb
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
AS $function$
|
||
DECLARE
|
||
v_role VARCHAR;
|
||
v_active_close TIMESTAMPTZ;
|
||
v_role_has_col BOOLEAN;
|
||
v_next_open TIMESTAMPTZ;
|
||
v_last_close TIMESTAMPTZ;
|
||
BEGIN
|
||
IF NOT EXISTS (SELECT 1 FROM v3.budget_form WHERE id = p_form_id) THEN
|
||
RAISE EXCEPTION 'budget_form id=% не существует', p_form_id;
|
||
END IF;
|
||
|
||
v_role := v3.user_role_code(p_user_id);
|
||
IF v_role IS NULL THEN
|
||
RETURN jsonb_build_object(
|
||
'ok', false,
|
||
'code', 'role_not_allowed',
|
||
'detail', 'unknown or inactive user'
|
||
);
|
||
END IF;
|
||
|
||
-- ADMIN — bypass всех проверок.
|
||
IF v_role = 'ADMIN' THEN
|
||
RETURN jsonb_build_object('ok', true, 'admin', true);
|
||
END IF;
|
||
|
||
-- EXECUTOR_RF: фильтр по user_org формы.
|
||
IF v_role = 'EXECUTOR_RF' AND NOT v3.user_in_form_org(p_user_id, p_form_id) THEN
|
||
RETURN jsonb_build_object(
|
||
'ok', false,
|
||
'code', 'org_not_assigned',
|
||
'detail', p_column_key
|
||
);
|
||
END IF;
|
||
|
||
-- 1. Активная сейчас фаза, в которой эта колонка перечислена для этой роли?
|
||
SELECT MAX(fp.closes_at)
|
||
INTO v_active_close
|
||
FROM v3.form_phase fp
|
||
WHERE fp.budget_form_id = p_form_id
|
||
AND fp.sheet = p_sheet
|
||
AND fp.role = v_role
|
||
AND p_column_key = ANY(fp.column_keys)
|
||
AND now() BETWEEN fp.opens_at AND fp.closes_at;
|
||
|
||
IF v_active_close IS NOT NULL THEN
|
||
RETURN jsonb_build_object(
|
||
'ok', true,
|
||
'closes_at', v_active_close
|
||
);
|
||
END IF;
|
||
|
||
-- 2. Колонка В ПРИНЦИПЕ есть хоть в одной фазе этой роли (на этой форме/листе)?
|
||
-- Если нет — это вопрос whitelist, отдаём role_not_allowed.
|
||
SELECT EXISTS (
|
||
SELECT 1
|
||
FROM v3.form_phase fp
|
||
WHERE fp.budget_form_id = p_form_id
|
||
AND fp.sheet = p_sheet
|
||
AND fp.role = v_role
|
||
AND p_column_key = ANY(fp.column_keys)
|
||
) INTO v_role_has_col;
|
||
|
||
IF NOT v_role_has_col THEN
|
||
RETURN jsonb_build_object(
|
||
'ok', false,
|
||
'code', 'role_not_allowed',
|
||
'detail', p_column_key
|
||
);
|
||
END IF;
|
||
|
||
-- 3. Колонка доступна, но окно не активно. Подсказать ближайшее.
|
||
SELECT MIN(fp.opens_at)
|
||
INTO v_next_open
|
||
FROM v3.form_phase fp
|
||
WHERE fp.budget_form_id = p_form_id
|
||
AND fp.sheet = p_sheet
|
||
AND fp.role = v_role
|
||
AND p_column_key = ANY(fp.column_keys)
|
||
AND fp.opens_at > now();
|
||
|
||
IF v_next_open IS NOT NULL THEN
|
||
RETURN jsonb_build_object(
|
||
'ok', false,
|
||
'code', 'window_closed',
|
||
'detail', p_column_key,
|
||
'opens_at', v_next_open
|
||
);
|
||
END IF;
|
||
|
||
SELECT MAX(fp.closes_at)
|
||
INTO v_last_close
|
||
FROM v3.form_phase fp
|
||
WHERE fp.budget_form_id = p_form_id
|
||
AND fp.sheet = p_sheet
|
||
AND fp.role = v_role
|
||
AND p_column_key = ANY(fp.column_keys)
|
||
AND fp.closes_at <= now();
|
||
|
||
RETURN jsonb_build_object(
|
||
'ok', false,
|
||
'code', 'window_closed',
|
||
'detail', p_column_key,
|
||
'closes_at', v_last_close
|
||
);
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.copy_template_to_form(int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.copy_template_to_form(p_form_id integer)
|
||
RETURNS integer
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_form_type VARCHAR;
|
||
v_inserted INTEGER;
|
||
BEGIN
|
||
SELECT form_type_code INTO v_form_type
|
||
FROM v3.budget_form
|
||
WHERE id = p_form_id;
|
||
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'budget_form id=% не существует', p_form_id;
|
||
END IF;
|
||
|
||
INSERT INTO v3.form_phase
|
||
(budget_form_id, sheet, phase_code, role, column_keys, opens_at, closes_at)
|
||
SELECT p_form_id, pt.sheet, pt.phase_code, pt.role,
|
||
pt.column_keys, pt.opens_at, pt.closes_at
|
||
FROM v3.phase_template pt
|
||
WHERE pt.form_type = v_form_type
|
||
ON CONFLICT (budget_form_id, sheet, phase_code) DO NOTHING;
|
||
|
||
GET DIAGNOSTICS v_inserted = ROW_COUNT;
|
||
RETURN v_inserted;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.current_user_id();
|
||
|
||
CREATE OR REPLACE FUNCTION v3.current_user_id()
|
||
RETURNS integer
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
AS $function$
|
||
DECLARE
|
||
v_raw TEXT;
|
||
v_id INTEGER;
|
||
BEGIN
|
||
v_raw := current_setting('app.user_id', true);
|
||
IF v_raw IS NULL OR v_raw = '' THEN
|
||
RETURN NULL;
|
||
END IF;
|
||
BEGIN
|
||
v_id := v_raw::INTEGER;
|
||
EXCEPTION WHEN OTHERS THEN
|
||
RETURN NULL;
|
||
END;
|
||
RETURN v_id;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.deactivate_vsp(int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.deactivate_vsp(p_vsp_id integer)
|
||
RETURNS date
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_closed DATE;
|
||
v_old_closed DATE;
|
||
v_branch_id INT;
|
||
v_branch_name VARCHAR;
|
||
v_actor_id INT;
|
||
v_actor_email VARCHAR;
|
||
v_actor_full_name VARCHAR;
|
||
BEGIN
|
||
SELECT v.branch_id, v.closed_at, ou.title
|
||
INTO v_branch_id, v_old_closed, v_branch_name
|
||
FROM v3.vsp v
|
||
LEFT JOIN v3.org_unit ou ON ou.id = v.branch_id
|
||
WHERE v.id = p_vsp_id;
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'vsp #% не существует', p_vsp_id;
|
||
END IF;
|
||
IF v_old_closed IS NOT NULL THEN
|
||
RAISE EXCEPTION 'vsp #% уже закрыт', p_vsp_id;
|
||
END IF;
|
||
|
||
UPDATE v3.vsp SET closed_at = CURRENT_DATE
|
||
WHERE id = p_vsp_id
|
||
RETURNING closed_at INTO v_closed;
|
||
|
||
v_actor_id := v3.current_user_id();
|
||
IF v_actor_id IS NOT NULL THEN
|
||
SELECT u.email, u.full_name
|
||
INTO v_actor_email, v_actor_full_name
|
||
FROM v3.app_user u
|
||
WHERE u.id = v_actor_id;
|
||
END IF;
|
||
|
||
PERFORM v3.log_event(
|
||
'VSP_UPDATE', 'VSP',
|
||
jsonb_build_object(
|
||
'vsp_id', p_vsp_id,
|
||
'branch_id', v_branch_id,
|
||
'branch_name', v_branch_name,
|
||
'changes', jsonb_build_object(
|
||
'closed_at', jsonb_build_object(
|
||
'before', v3._diff_val(v_old_closed),
|
||
'after', v3._diff_val(v_closed)
|
||
)
|
||
),
|
||
'closed_at', v_closed::TEXT,
|
||
'user_id', v_actor_id,
|
||
'user_email', v_actor_email,
|
||
'user_full_name', v_actor_full_name
|
||
), null, null, v_branch_id);
|
||
|
||
RETURN v_closed;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.del_budget_form(int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.del_budget_form(p_form_id integer)
|
||
RETURNS integer
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old RECORD;
|
||
BEGIN
|
||
SELECT bf.form_type_code, bf.year, bf.org_unit_id
|
||
INTO v_old
|
||
FROM v3.budget_form bf WHERE bf.id = p_form_id;
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'budget_form #% не существует', p_form_id;
|
||
END IF;
|
||
|
||
DELETE FROM v3.budget_form WHERE id = p_form_id;
|
||
|
||
PERFORM v3.log_event(
|
||
'DELETE_TASK', 'TASK',
|
||
jsonb_build_object(
|
||
'form_id', p_form_id,
|
||
'form_type', v_old.form_type_code,
|
||
'year', v_old.year,
|
||
'org_unit_id', v_old.org_unit_id),
|
||
p_form_id, null, v_old.org_unit_id);
|
||
|
||
RETURN p_form_id;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.del_budget_line(int4, varchar, varchar);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.del_budget_line(p_id integer, p_direction character varying DEFAULT NULL::character varying, p_sheet character varying DEFAULT NULL::character varying)
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_form_id INT;
|
||
v_org_id INT;
|
||
v_sheet VARCHAR;
|
||
v_sat_table TEXT;
|
||
v_eid INT;
|
||
v_ei_path INT[];
|
||
BEGIN
|
||
-- ═══ Сателлитные листы — DELETE из *_detail по id ═════════════════════
|
||
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;
|
||
|
||
-- Извлекаем form_id для возврата (через JOIN на budget_line)
|
||
EXECUTE format(
|
||
'SELECT bl.budget_form_id FROM v3.%I s
|
||
JOIN v3.budget_line bl ON bl.id = s.line_id
|
||
WHERE s.id = $1', v_sat_table
|
||
) USING p_id INTO v_form_id;
|
||
|
||
IF v_form_id IS NULL THEN
|
||
RAISE EXCEPTION '%.id=% не существует', v_sat_table, p_id;
|
||
END IF;
|
||
|
||
EXECUTE format('DELETE FROM v3.%I WHERE id = $1', v_sat_table) USING p_id;
|
||
|
||
-- Возврат пустой — UI сам уберёт строку по id
|
||
RETURN;
|
||
END IF;
|
||
|
||
-- ═══ Основная сетка ═══════════════════════════════════════════════════
|
||
-- Sanity + извлечение form_id/sheet/expense_item_id (нужен для path
|
||
-- ДО DELETE — после удаления budget_line его уже не получить).
|
||
SELECT bl.budget_form_id, ei.sheet, bl.expense_item_id
|
||
INTO v_form_id, v_sheet, v_eid
|
||
FROM v3.budget_line bl
|
||
JOIN v3.expense_item ei ON ei.id = bl.expense_item_id
|
||
WHERE bl.id = p_id;
|
||
|
||
IF v_form_id IS NULL THEN
|
||
RAISE EXCEPTION 'budget_line id=% не существует', p_id;
|
||
END IF;
|
||
|
||
v_ei_path := v3._expense_item_path_set(ARRAY[v_eid]);
|
||
|
||
-- Каскадное удаление дочерних записей
|
||
DELETE FROM v3.plan WHERE line_id = p_id;
|
||
DELETE FROM v3.sequestration WHERE line_id = p_id;
|
||
DELETE FROM v3.reserve WHERE line_id = p_id;
|
||
DELETE FROM v3.allocation WHERE line_id = p_id;
|
||
DELETE FROM v3.contract_summary WHERE line_id = p_id;
|
||
DELETE FROM v3.collegial_approval WHERE line_id = p_id;
|
||
DELETE FROM v3.ckk WHERE line_id = p_id;
|
||
DELETE FROM v3.contract_detail WHERE line_id = p_id;
|
||
DELETE FROM v3.booking WHERE line_id = p_id;
|
||
DELETE FROM v3.budget_line_quarter WHERE line_id = p_id;
|
||
DELETE FROM v3.rent_detail WHERE line_id = p_id;
|
||
DELETE FROM v3.utility_detail WHERE line_id = p_id;
|
||
DELETE FROM v3.security_detail WHERE line_id = p_id;
|
||
-- AHR_LIMIT с v3 не имеет привязки к budget_line — каталог limit_template
|
||
-- глобальный, per-form qty в form_limit удаляется через ON DELETE CASCADE
|
||
-- от budget_form.
|
||
|
||
-- Удаляем сам budget_line
|
||
DELETE FROM v3.budget_line WHERE id = p_id;
|
||
|
||
SELECT org_unit_id INTO v_org_id FROM v3.budget_form WHERE id = v_form_id;
|
||
|
||
-- Аудит: ROW_DELETE
|
||
PERFORM v3.log_event(
|
||
'ROW_DELETE', 'ROW',
|
||
jsonb_build_object(
|
||
'sheet', v_sheet,
|
||
'line_id', p_id,
|
||
'entity_id', p_id,
|
||
'expense_item_id', v_eid,
|
||
'direction', p_direction
|
||
),
|
||
v_form_id, NULL, v_org_id
|
||
);
|
||
|
||
-- Возврат: иерархия только по пути удалённой строки (INPUT уже нет)
|
||
RETURN QUERY
|
||
SELECT v.row_type, v.depth, v.sort_order, v.data
|
||
FROM v3.v_form_view(v_form_id, v_sheet, NULL, p_direction) v
|
||
WHERE v.row_type IN ('ROOT','GROUP','ITEM','SUB_ITEM')
|
||
AND (v.data->'header'->>'expense_item_id')::INT = ANY(v_ei_path)
|
||
ORDER BY v.sort_order;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.del_form3_line(int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.del_form3_line(p_line_id integer)
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_report_id INT;
|
||
v_eid INT;
|
||
v_form_id INT;
|
||
v_ei_path INT[];
|
||
BEGIN
|
||
-- Извлекаем report_id + ei ДО DELETE (нужен для path)
|
||
SELECT rfprl.rf_project_report_id, rfprl.expense_item_id
|
||
INTO v_report_id, v_eid
|
||
FROM v3.rf_project_report_line rfprl
|
||
WHERE rfprl.id = p_line_id;
|
||
IF v_report_id IS NULL THEN
|
||
RAISE EXCEPTION 'rf_project_report_line id=% не существует', p_line_id;
|
||
END IF;
|
||
|
||
v_ei_path := v3._expense_item_path_set(ARRAY[v_eid]);
|
||
|
||
SELECT bf.id
|
||
INTO v_form_id
|
||
FROM v3.rf_project_report r
|
||
JOIN v3.project p
|
||
ON p.id = r.project_id
|
||
LEFT JOIN v3.budget_form bf
|
||
ON bf.form_type_code = 'FORM_3'
|
||
AND bf.year = r.year
|
||
AND bf.org_unit_id = p.org_unit_id
|
||
WHERE r.id = v_report_id
|
||
LIMIT 1;
|
||
|
||
-- Каскад (FK без ON DELETE CASCADE)
|
||
DELETE FROM v3.rf_project_report_quarter WHERE rf_project_report_line_id = p_line_id;
|
||
DELETE FROM v3.rf_project_report_line WHERE id = p_line_id;
|
||
|
||
-- Аудит: ROW_DELETE
|
||
PERFORM v3.log_event(
|
||
'ROW_DELETE', 'ROW',
|
||
jsonb_build_object(
|
||
'sheet', 'FORM_3',
|
||
'line_id', p_line_id,
|
||
'form_id', v_form_id,
|
||
'report_id', v_report_id,
|
||
'expense_item_id', v_eid
|
||
),
|
||
v_form_id
|
||
);
|
||
|
||
-- Возврат: только иерархия по пути удалённой (INPUT уже нет)
|
||
RETURN QUERY
|
||
SELECT v.row_type, v.depth, v.sort_order, v.data
|
||
FROM v3.v_form3_report_jsonb(v_report_id, NULL) v
|
||
WHERE v.row_type IN ('ROOT','GROUP','ITEM','SUB_ITEM')
|
||
AND (v.data->'header'->>'expense_item_id')::INT = ANY(v_ei_path)
|
||
ORDER BY v.sort_order;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.del_form_phase(int4, varchar, varchar);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.del_form_phase(p_budget_form_id integer, p_sheet character varying, p_phase_code character varying)
|
||
RETURNS boolean
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old v3.form_phase;
|
||
v_org_unit_id INT;
|
||
v_org_unit_title VARCHAR;
|
||
v_form_type_code VARCHAR;
|
||
v_year INT;
|
||
v_task_name TEXT;
|
||
v_phase_id TEXT;
|
||
BEGIN
|
||
DELETE FROM v3.form_phase
|
||
WHERE budget_form_id = p_budget_form_id
|
||
AND sheet = p_sheet
|
||
AND phase_code = p_phase_code
|
||
RETURNING * INTO v_old;
|
||
|
||
IF NOT FOUND THEN
|
||
RETURN FALSE;
|
||
END IF;
|
||
|
||
SELECT bf.org_unit_id, ou.title, bf.form_type_code, bf.year
|
||
INTO v_org_unit_id, v_org_unit_title, v_form_type_code, v_year
|
||
FROM v3.budget_form bf
|
||
LEFT JOIN v3.org_unit ou ON ou.id = bf.org_unit_id
|
||
WHERE bf.id = p_budget_form_id;
|
||
v_task_name := trim(concat(
|
||
COALESCE(v_form_type_code, ''),
|
||
CASE WHEN v_year IS NOT NULL THEN ' ' || v_year::TEXT ELSE '' END,
|
||
CASE WHEN v_org_unit_title IS NOT NULL THEN ' [' || v_org_unit_title || ']' ELSE '' END
|
||
));
|
||
v_phase_id := format('%s:%s:%s', p_budget_form_id, p_sheet, p_phase_code);
|
||
|
||
PERFORM v3.log_event(
|
||
'ACCESS_WINDOW_CHANGE', 'ACCESS',
|
||
jsonb_build_object(
|
||
'entity_type', 'form_phase',
|
||
'entity_id', p_budget_form_id,
|
||
'action', 'delete',
|
||
'phase_id', v_phase_id,
|
||
'phase_name', p_phase_code,
|
||
'task_id', p_budget_form_id,
|
||
'task_name', NULLIF(v_task_name, ''),
|
||
'form_id', p_budget_form_id,
|
||
'budget_form_id', p_budget_form_id,
|
||
'org_unit_id', v_org_unit_id,
|
||
'org_unit_name', v_org_unit_title,
|
||
'sheet', p_sheet,
|
||
'phase_code', p_phase_code,
|
||
'role', v_old.role,
|
||
'column_keys', v_old.column_keys,
|
||
'opens_at', v_old.opens_at,
|
||
'closes_at', v_old.closes_at
|
||
),
|
||
p_budget_form_id, null, v_org_unit_id
|
||
);
|
||
RETURN TRUE;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.del_project(int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.del_project(p_project_id integer)
|
||
RETURNS integer
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old RECORD;
|
||
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;
|
||
|
||
-- Каскад вручную: rf_project_report_quarter / line / report (нет ON DELETE)
|
||
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$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.del_vsp(int4, int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.del_vsp(p_vsp_id integer, p_deleted_by integer)
|
||
RETURNS integer
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old RECORD;
|
||
v_usr RECORD;
|
||
v_closed DATE;
|
||
BEGIN
|
||
SELECT vsp.id, vsp.branch_id
|
||
INTO v_old
|
||
FROM v3.vsp vsp WHERE vsp.id = p_vsp_id;
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'vsp #% не существует', p_vsp_id;
|
||
END IF;
|
||
SELECT usr.id
|
||
INTO v_usr
|
||
FROM v3.app_user usr WHERE usr.id = p_deleted_by;
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'user #% не существует', p_deleted_by;
|
||
END IF;
|
||
|
||
|
||
UPDATE v3.vsp SET is_deleted = true, updated_by = p_deleted_by WHERE id = p_vsp_id;
|
||
UPDATE v3.vsp SET closed_at = CURRENT_DATE
|
||
WHERE id = p_vsp_id AND closed_at IS NULL
|
||
RETURNING closed_at INTO v_closed;
|
||
|
||
IF v_closed IS NULL THEN
|
||
IF EXISTS (SELECT 1 FROM v3.vsp WHERE id = p_vsp_id) THEN
|
||
RAISE EXCEPTION 'vsp #% уже закрыт', p_vsp_id;
|
||
ELSE
|
||
RAISE EXCEPTION 'vsp #% не существует', p_vsp_id;
|
||
END IF;
|
||
END IF;
|
||
|
||
PERFORM v3.log_event(
|
||
'DELETE_VSP', 'VSP',
|
||
jsonb_build_object(
|
||
'vsp_id', p_vsp_id,
|
||
'entity_id', p_vsp_id,
|
||
'branch_id', v_old.branch_id,
|
||
'updated_by', p_deleted_by
|
||
),
|
||
--vsp_id,
|
||
null, null, v_old.branch_id);
|
||
|
||
RETURN p_vsp_id;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.editable_columns_for(int4, int4, varchar);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.editable_columns_for(p_form_id integer, p_user_id integer, p_sheet character varying)
|
||
RETURNS TABLE(column_key text, closes_at timestamp with time zone)
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
AS $function$
|
||
DECLARE
|
||
v_role VARCHAR;
|
||
BEGIN
|
||
IF NOT EXISTS (SELECT 1 FROM v3.budget_form WHERE id = p_form_id) THEN
|
||
RAISE EXCEPTION 'budget_form id=% не существует', p_form_id;
|
||
END IF;
|
||
|
||
v_role := v3.user_role_code(p_user_id);
|
||
IF v_role IS NULL THEN
|
||
RETURN; -- неизвестный/неактивный юзер → пустая маска
|
||
END IF;
|
||
|
||
-- ADMIN: вернуть UNION всех column_keys всех phase'ов на форме/листе.
|
||
IF v_role = 'ADMIN' THEN
|
||
RETURN QUERY
|
||
SELECT ck AS column_key,
|
||
MAX(fp.closes_at) AS closes_at
|
||
FROM v3.form_phase fp,
|
||
LATERAL unnest(fp.column_keys) AS ck
|
||
WHERE fp.budget_form_id = p_form_id
|
||
AND fp.sheet = p_sheet
|
||
GROUP BY ck;
|
||
RETURN;
|
||
END IF;
|
||
|
||
-- EXECUTOR_RF без назначения → пустая маска (default deny).
|
||
IF v_role = 'EXECUTOR_RF' AND NOT v3.user_in_form_org(p_user_id, p_form_id) THEN
|
||
RETURN;
|
||
END IF;
|
||
|
||
RETURN QUERY
|
||
SELECT ck AS column_key,
|
||
MAX(fp.closes_at) AS closes_at
|
||
FROM v3.form_phase fp,
|
||
LATERAL unnest(fp.column_keys) AS ck
|
||
WHERE fp.budget_form_id = p_form_id
|
||
AND fp.sheet = p_sheet
|
||
AND fp.role = v_role
|
||
AND now() BETWEEN fp.opens_at AND fp.closes_at
|
||
GROUP BY ck;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.grant_user_org_access(int4, int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.grant_user_org_access(p_user_id integer, p_org_unit_id integer)
|
||
RETURNS boolean
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_inserted INT;
|
||
v_user_email VARCHAR;
|
||
v_user_full_name VARCHAR;
|
||
v_org_name VARCHAR;
|
||
BEGIN
|
||
SELECT email, full_name
|
||
INTO v_user_email, v_user_full_name
|
||
FROM v3.app_user
|
||
WHERE id = p_user_id;
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'app_user #% не существует', p_user_id;
|
||
END IF;
|
||
|
||
SELECT title INTO v_org_name FROM v3.org_unit WHERE id = p_org_unit_id;
|
||
IF v_org_name IS NULL THEN
|
||
RAISE EXCEPTION 'org_unit #% не существует', p_org_unit_id;
|
||
END IF;
|
||
|
||
INSERT INTO v3.user_org(user_id, org_unit_id)
|
||
VALUES (p_user_id, p_org_unit_id)
|
||
ON CONFLICT (user_id, org_unit_id) DO NOTHING;
|
||
|
||
GET DIAGNOSTICS v_inserted = ROW_COUNT;
|
||
|
||
IF v_inserted > 0 THEN
|
||
PERFORM v3.log_event(
|
||
'USER_ACCESS_GRANTED', 'USER',
|
||
jsonb_build_object(
|
||
'user_id', p_user_id,
|
||
'entity_id', p_user_id,
|
||
'email', v_user_email,
|
||
'full_name', v_user_full_name,
|
||
'org_unit_id', p_org_unit_id,
|
||
'org_unit_name', v_org_name
|
||
), null, null, p_org_unit_id
|
||
);
|
||
RETURN TRUE;
|
||
END IF;
|
||
|
||
RETURN FALSE;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.jsonb_numeric_sum(jsonb, jsonb);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.jsonb_numeric_sum(a jsonb, b jsonb)
|
||
RETURNS jsonb
|
||
LANGUAGE sql
|
||
IMMUTABLE
|
||
AS $function$
|
||
SELECT CASE
|
||
WHEN a IS NULL THEN b
|
||
WHEN b IS NULL THEN a
|
||
WHEN jsonb_typeof(a) = 'number' AND jsonb_typeof(b) = 'number' THEN
|
||
to_jsonb((a)::text::numeric + (b)::text::numeric)
|
||
WHEN jsonb_typeof(a) = 'object' AND jsonb_typeof(b) = 'object' THEN (
|
||
SELECT jsonb_object_agg(k,
|
||
v3.jsonb_numeric_sum(a -> k, b -> k)
|
||
)
|
||
FROM (
|
||
SELECT k FROM jsonb_object_keys(a) k
|
||
UNION
|
||
SELECT k FROM jsonb_object_keys(b) k
|
||
) keys
|
||
)
|
||
-- одна сторона число/объект, другая нет → берём числовую/объектную
|
||
WHEN jsonb_typeof(a) IN ('number','object') THEN a
|
||
WHEN jsonb_typeof(b) IN ('number','object') THEN b
|
||
ELSE NULL
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP AGGREGATE v3.jsonb_sum(jsonb);
|
||
|
||
CREATE OR REPLACE AGGREGATE v3.jsonb_sum(pg_catalog.jsonb) (
|
||
SFUNC = v3.jsonb_numeric_sum,
|
||
STYPE = jsonb
|
||
);
|
||
|
||
-- DROP FUNCTION v3.log_event(text, text, jsonb, int4, int4, int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.log_event(p_event text, p_event_type text, p_data jsonb, p_form_id integer DEFAULT NULL::integer, p_task_id integer DEFAULT NULL::integer, p_org_unit_id integer DEFAULT NULL::integer)
|
||
RETURNS bigint
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_user_id INTEGER;
|
||
v_audit_id BIGINT;
|
||
BEGIN
|
||
v_user_id := v3.current_user_id();
|
||
|
||
-- Невалидный user_id → пишем NULL (не падаем).
|
||
IF v_user_id IS NOT NULL
|
||
AND NOT EXISTS (SELECT 1 FROM v3.app_user WHERE id = v_user_id) THEN
|
||
v_user_id := NULL;
|
||
END IF;
|
||
|
||
INSERT INTO v3.audit_log
|
||
(user_id, org_unit_id, form_id, task_id, event, event_type, event_data)
|
||
VALUES
|
||
(v_user_id, p_org_unit_id, p_form_id, p_task_id,
|
||
p_event, p_event_type, COALESCE(p_data, '{}'::jsonb))
|
||
RETURNING id INTO v_audit_id;
|
||
|
||
RETURN v_audit_id;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.revoke_many_user_org_access(int4, _int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.revoke_many_user_org_access(p_user_id integer, p_org_unit_ids integer[])
|
||
RETURNS integer
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_org_id INT;
|
||
v_count INT := 0;
|
||
BEGIN
|
||
IF p_org_unit_ids IS NULL OR cardinality(p_org_unit_ids) = 0 THEN
|
||
RETURN 0;
|
||
END IF;
|
||
|
||
FOREACH v_org_id IN ARRAY p_org_unit_ids
|
||
LOOP
|
||
IF v3.revoke_user_org_access(p_user_id, v_org_id) THEN
|
||
v_count := v_count + 1;
|
||
END IF;
|
||
END LOOP;
|
||
|
||
RETURN v_count;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.revoke_user_org_access(int4, int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.revoke_user_org_access(p_user_id integer, p_org_unit_id integer)
|
||
RETURNS boolean
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_deleted INT;
|
||
v_user_email VARCHAR;
|
||
v_user_full_name VARCHAR;
|
||
v_org_name VARCHAR;
|
||
BEGIN
|
||
SELECT email, full_name
|
||
INTO v_user_email, v_user_full_name
|
||
FROM v3.app_user
|
||
WHERE id = p_user_id;
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'app_user #% не существует', p_user_id;
|
||
END IF;
|
||
|
||
SELECT title INTO v_org_name FROM v3.org_unit WHERE id = p_org_unit_id;
|
||
IF v_org_name IS NULL THEN
|
||
RAISE EXCEPTION 'org_unit #% не существует', p_org_unit_id;
|
||
END IF;
|
||
|
||
DELETE FROM v3.user_org
|
||
WHERE user_id = p_user_id
|
||
AND org_unit_id = p_org_unit_id;
|
||
|
||
GET DIAGNOSTICS v_deleted = ROW_COUNT;
|
||
|
||
IF v_deleted > 0 THEN
|
||
PERFORM v3.log_event(
|
||
'USER_ACCESS_REVOKED', 'USER',
|
||
jsonb_build_object(
|
||
'user_id', p_user_id,
|
||
'entity_id', p_user_id,
|
||
'email', v_user_email,
|
||
'full_name', v_user_full_name,
|
||
'org_unit_id', p_org_unit_id,
|
||
'org_unit_name', v_org_name
|
||
), null, null, p_org_unit_id
|
||
);
|
||
RETURN TRUE;
|
||
END IF;
|
||
|
||
RETURN FALSE;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.upd_budget_form(int4, int4, int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_budget_form(p_form_id integer, p_year integer DEFAULT NULL::integer, p_org_unit_id integer DEFAULT NULL::integer)
|
||
RETURNS TABLE(id integer, form_type_code character varying, year integer, org_unit_id integer)
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old RECORD;
|
||
v_changes JSONB := '{}'::jsonb;
|
||
BEGIN
|
||
SELECT bf.year, bf.org_unit_id
|
||
INTO v_old
|
||
FROM v3.budget_form bf WHERE bf.id = p_form_id;
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'budget_form #% не существует', p_form_id;
|
||
END IF;
|
||
|
||
IF p_year IS NOT NULL AND p_year IS DISTINCT FROM v_old.year THEN
|
||
UPDATE v3.budget_form bf SET year = p_year WHERE bf.id = p_form_id;
|
||
v_changes := v_changes || jsonb_build_object(
|
||
'year', jsonb_build_object(
|
||
'before', v3._diff_val(v_old.year),
|
||
'after', v3._diff_val(p_year)));
|
||
END IF;
|
||
|
||
IF p_org_unit_id IS NOT NULL AND p_org_unit_id IS DISTINCT FROM v_old.org_unit_id THEN
|
||
UPDATE v3.budget_form bf SET org_unit_id = p_org_unit_id WHERE bf.id = p_form_id;
|
||
v_changes := v_changes || jsonb_build_object(
|
||
'org_unit_id', jsonb_build_object(
|
||
'before', v3._diff_val(v_old.org_unit_id),
|
||
'after', v3._diff_val(p_org_unit_id)));
|
||
END IF;
|
||
|
||
IF v_changes <> '{}'::jsonb THEN
|
||
UPDATE v3.budget_form bf SET updated_at = now() WHERE bf.id = p_form_id;
|
||
PERFORM v3.log_event(
|
||
'UPDATE_TASK', 'UPDATE_TASK',
|
||
jsonb_build_object('form_id', p_form_id, 'changes', v_changes),
|
||
p_form_id, null, COALESCE(p_org_unit_id, v_old.org_unit_id));
|
||
END IF;
|
||
|
||
RETURN QUERY
|
||
SELECT bf.id, bf.form_type_code, bf.year, bf.org_unit_id
|
||
FROM v3.budget_form bf WHERE bf.id = p_form_id;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.upd_form3_cells(int4, jsonb);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_form3_cells(p_report_id integer, p_changes jsonb)
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_change JSONB;
|
||
v_line_id INT;
|
||
v_column TEXT;
|
||
v_value JSONB;
|
||
v_changed_lines INT[] := ARRAY[]::INT[];
|
||
BEGIN
|
||
IF p_changes IS NULL OR jsonb_typeof(p_changes) <> 'array' THEN
|
||
RAISE EXCEPTION 'p_changes must be a JSONB array';
|
||
END IF;
|
||
|
||
IF NOT EXISTS (SELECT 1 FROM v3.rf_project_report WHERE id = p_report_id) THEN
|
||
RAISE EXCEPTION 'rf_project_report id=% не существует', p_report_id;
|
||
END IF;
|
||
|
||
FOR v_change IN SELECT * FROM jsonb_array_elements(p_changes)
|
||
LOOP
|
||
v_line_id := (v_change->>'line_id')::INT;
|
||
v_column := v_change->>'column';
|
||
v_value := v_change->'value';
|
||
|
||
IF v_line_id IS NULL OR v_column IS NULL THEN
|
||
RAISE EXCEPTION 'change must have line_id and column: %', v_change;
|
||
END IF;
|
||
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM v3.rf_project_report_line
|
||
WHERE id = v_line_id AND rf_project_report_id = p_report_id
|
||
) THEN
|
||
RAISE EXCEPTION 'rf_project_report_line id=% не принадлежит report id=%',
|
||
v_line_id, p_report_id;
|
||
END IF;
|
||
|
||
PERFORM v3._apply_form3_cell(v_line_id, v_column, v_value);
|
||
|
||
IF NOT (v_line_id = ANY(v_changed_lines)) THEN
|
||
v_changed_lines := v_changed_lines || v_line_id;
|
||
END IF;
|
||
END LOOP;
|
||
|
||
-- Возврат: иерархия по пути изменённых rfprl + изменённые INPUT по line_id
|
||
DECLARE
|
||
v_eid_set INT[];
|
||
v_ei_path INT[];
|
||
BEGIN
|
||
SELECT array_agg(DISTINCT rfprl.expense_item_id)
|
||
INTO v_eid_set
|
||
FROM v3.rf_project_report_line rfprl
|
||
WHERE rfprl.id = ANY(v_changed_lines);
|
||
v_ei_path := v3._expense_item_path_set(v_eid_set);
|
||
|
||
RETURN QUERY
|
||
SELECT v.row_type, v.depth, v.sort_order, v.data
|
||
FROM v3.v_form3_report_jsonb(p_report_id, NULL) v
|
||
WHERE (v.row_type IN ('ROOT','GROUP','ITEM','SUB_ITEM')
|
||
AND (v.data->'header'->>'expense_item_id')::INT = ANY(v_ei_path))
|
||
OR (v.row_type = 'INPUT' AND (v.data->>'line_id')::INT = ANY(v_changed_lines))
|
||
ORDER BY v.sort_order;
|
||
END;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.upd_form3_cell(int4, int4, text, jsonb);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_form3_cell(p_report_id integer, p_line_id integer, p_column text, p_value jsonb)
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE sql
|
||
AS $function$
|
||
SELECT * FROM v3.upd_form3_cells(
|
||
p_report_id,
|
||
jsonb_build_array(jsonb_build_object(
|
||
'line_id', p_line_id,
|
||
'column', p_column,
|
||
'value', p_value
|
||
))
|
||
);
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.upd_form_cells(int4, varchar, jsonb, varchar, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_form_cells(p_form_id integer, p_sheet character varying, p_changes jsonb, p_direction character varying DEFAULT NULL::character varying, p_sections text[] DEFAULT NULL::text[])
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_change JSONB;
|
||
v_line_id INT;
|
||
v_column TEXT;
|
||
v_value JSONB;
|
||
v_changed_lines INT[] := ARRAY[]::INT[];
|
||
v_before JSONB := '{}'::jsonb; -- map sort_order(text) → data BEFORE
|
||
v_lc_path INT[];
|
||
v_eid_set INT[];
|
||
v_ei_path INT[];
|
||
BEGIN
|
||
IF p_changes IS NULL OR jsonb_typeof(p_changes) <> 'array' THEN
|
||
RAISE EXCEPTION 'p_changes must be a JSONB array';
|
||
END IF;
|
||
|
||
-- Проверка: form_id существует
|
||
IF NOT EXISTS (SELECT 1 FROM v3.budget_form WHERE id = p_form_id) THEN
|
||
RAISE EXCEPTION 'budget_form id=% не существует', p_form_id;
|
||
END IF;
|
||
|
||
-- Применяем все изменения в одной транзакции (вся функция = одна tx)
|
||
FOR v_change IN SELECT * FROM jsonb_array_elements(p_changes)
|
||
LOOP
|
||
v_line_id := (v_change->>'line_id')::INT;
|
||
v_column := v_change->>'column';
|
||
v_value := v_change->'value'; -- именно ->, чтобы NULL остался jsonb null
|
||
|
||
IF v_line_id IS NULL OR v_column IS NULL THEN
|
||
RAISE EXCEPTION 'change must have line_id and column: %', v_change;
|
||
END IF;
|
||
|
||
-- Sanity: для основной сетки — line_id принадлежит budget_form;
|
||
-- для сателлитов — id принадлежит соответствующей таблице сателлита
|
||
-- и через её line_id — данной budget_form;
|
||
-- для AHR_LIMIT — id принадлежит limit_template (глобальный каталог,
|
||
-- проверим что строка LEAF; per-form значения летят в form_limit).
|
||
IF p_sheet = 'AHR_LIMIT' THEN
|
||
IF NOT EXISTS (SELECT 1 FROM v3.limit_template lt
|
||
WHERE lt.id = v_line_id AND lt.row_type = 'LEAF') THEN
|
||
RAISE EXCEPTION 'limit_template.id=% не существует или не LEAF', v_line_id;
|
||
END IF;
|
||
ELSIF p_sheet = 'OTCH9F' THEN
|
||
IF NOT EXISTS (SELECT 1 FROM v3.fixed_asset_report
|
||
WHERE id = v_line_id AND budget_form_id = p_form_id) THEN
|
||
RAISE EXCEPTION 'fixed_asset_report.id=% не принадлежит budget_form id=%', v_line_id, p_form_id;
|
||
END IF;
|
||
ELSIF p_sheet = 'AHR_RENT' THEN
|
||
IF NOT EXISTS (SELECT 1 FROM v3.rent_detail rd
|
||
JOIN v3.budget_line bl ON bl.id = rd.line_id
|
||
WHERE rd.id = v_line_id AND bl.budget_form_id = p_form_id) THEN
|
||
RAISE EXCEPTION 'rent_detail id=% не принадлежит budget_form id=%', v_line_id, p_form_id;
|
||
END IF;
|
||
ELSIF p_sheet = 'AHR_UTILITY' THEN
|
||
IF NOT EXISTS (SELECT 1 FROM v3.utility_detail ud
|
||
JOIN v3.budget_line bl ON bl.id = ud.line_id
|
||
WHERE ud.id = v_line_id AND bl.budget_form_id = p_form_id) THEN
|
||
RAISE EXCEPTION 'utility_detail id=% не принадлежит budget_form id=%', v_line_id, p_form_id;
|
||
END IF;
|
||
ELSIF p_sheet = 'AHR_SECURITY' THEN
|
||
IF NOT EXISTS (SELECT 1 FROM v3.security_detail sd
|
||
JOIN v3.budget_line bl ON bl.id = sd.line_id
|
||
WHERE sd.id = v_line_id AND bl.budget_form_id = p_form_id) THEN
|
||
RAISE EXCEPTION 'security_detail id=% не принадлежит budget_form id=%', v_line_id, p_form_id;
|
||
END IF;
|
||
ELSE
|
||
IF NOT EXISTS (SELECT 1 FROM v3.budget_line
|
||
WHERE id = v_line_id AND budget_form_id = p_form_id) THEN
|
||
RAISE EXCEPTION 'budget_line id=% не принадлежит budget_form id=%', v_line_id, p_form_id;
|
||
END IF;
|
||
END IF;
|
||
|
||
-- Сначала ТОЛЬКО регистрируем v_changed_lines, не применяем (для snapshot
|
||
-- before нужно знать все затронутые до изменения).
|
||
IF NOT (v_line_id = ANY(v_changed_lines)) THEN
|
||
v_changed_lines := v_changed_lines || v_line_id;
|
||
END IF;
|
||
END LOOP;
|
||
|
||
-- ── Вычисляем path-set для каждого типа листа (ОДИН раз, для before и after)
|
||
IF p_sheet = 'AHR_LIMIT' THEN
|
||
v_lc_path := v3._limit_template_path_set(v_changed_lines);
|
||
ELSIF p_sheet NOT IN ('AHR_RENT','AHR_UTILITY','AHR_SECURITY','OTCH9F') THEN
|
||
SELECT array_agg(DISTINCT bl.expense_item_id)
|
||
INTO v_eid_set
|
||
FROM v3.budget_line bl
|
||
WHERE bl.id = ANY(v_changed_lines);
|
||
v_ei_path := v3._expense_item_path_set(v_eid_set);
|
||
END IF;
|
||
|
||
-- ── Snapshot BEFORE (тот же фильтр что и для возврата) ────────────────
|
||
IF p_sheet IN ('AHR_RENT','AHR_UTILITY','AHR_SECURITY') THEN
|
||
SELECT COALESCE(jsonb_object_agg(v.sort_order::text, v.data), '{}'::jsonb)
|
||
INTO v_before
|
||
FROM v3.v_form_view(p_form_id, p_sheet, p_sections, p_direction) v
|
||
WHERE v.row_type = 'INPUT' AND (v.data->>'id')::INT = ANY(v_changed_lines);
|
||
ELSIF p_sheet = 'AHR_LIMIT' THEN
|
||
SELECT COALESCE(jsonb_object_agg(v.sort_order::text, v.data), '{}'::jsonb)
|
||
INTO v_before
|
||
FROM v3.v_form_view(p_form_id, p_sheet, p_sections, p_direction) v
|
||
WHERE (v.data->>'id')::INT = ANY(v_lc_path);
|
||
ELSIF p_sheet = 'OTCH9F' THEN
|
||
SELECT COALESCE(jsonb_object_agg(v.sort_order::text, v.data), '{}'::jsonb)
|
||
INTO v_before
|
||
FROM v3.v_form_view(p_form_id, p_sheet, p_sections, p_direction) v
|
||
WHERE EXISTS (
|
||
SELECT 1 FROM v3.fixed_asset_report far
|
||
WHERE far.id = ANY(v_changed_lines)
|
||
AND far.expense_item_id = (v.data->>'expense_item_id')::INT
|
||
AND far.equipment_name IS NOT DISTINCT FROM (v.data->>'equipment_name'));
|
||
ELSE
|
||
SELECT COALESCE(jsonb_object_agg(v.sort_order::text, v.data), '{}'::jsonb)
|
||
INTO v_before
|
||
FROM v3.v_form_view(p_form_id, p_sheet, p_sections, p_direction) v
|
||
WHERE (v.row_type IN ('ROOT','GROUP','ITEM','SUB_ITEM')
|
||
AND (v.data->'header'->>'expense_item_id')::INT = ANY(v_ei_path))
|
||
OR (v.row_type = 'INPUT'
|
||
AND (v.data->>'line_id')::INT = ANY(v_changed_lines));
|
||
END IF;
|
||
|
||
-- ── Применяем изменения ─────────────────────────────────────────────
|
||
FOR v_change IN SELECT * FROM jsonb_array_elements(p_changes)
|
||
LOOP
|
||
PERFORM v3._apply_form_cell(
|
||
(v_change->>'line_id')::INT,
|
||
v_change->>'column',
|
||
v_change->'value',
|
||
p_sheet,
|
||
p_form_id);
|
||
END LOOP;
|
||
|
||
-- ── Возврат: AFTER vs BEFORE diff. Только строки с непустым diff.
|
||
-- Identifier-ключи (line_id / header.expense_item_id / id) гарантированно
|
||
-- добавлены в diff, чтобы фронт мог match с своим state.
|
||
IF p_sheet IN ('AHR_RENT','AHR_UTILITY','AHR_SECURITY') THEN
|
||
RETURN QUERY
|
||
WITH after_rows AS (
|
||
SELECT v.row_type, v.depth, v.sort_order, v.data,
|
||
v3._jsonb_diff(v_before->(v.sort_order::text), v.data) AS diff
|
||
FROM v3.v_form_view(p_form_id, p_sheet, p_sections, p_direction) v
|
||
WHERE v.row_type = 'INPUT' AND (v.data->>'id')::INT = ANY(v_changed_lines)
|
||
)
|
||
SELECT a.row_type, a.depth, a.sort_order,
|
||
a.diff || jsonb_build_object('id', (a.data->>'id')::INT)
|
||
FROM after_rows a
|
||
WHERE a.diff <> '{}'::jsonb
|
||
ORDER BY a.sort_order;
|
||
|
||
ELSIF p_sheet = 'AHR_LIMIT' THEN
|
||
RETURN QUERY
|
||
WITH after_rows AS (
|
||
SELECT v.row_type, v.depth, v.sort_order, v.data,
|
||
v3._jsonb_diff(v_before->(v.sort_order::text), v.data) AS diff
|
||
FROM v3.v_form_view(p_form_id, p_sheet, p_sections, p_direction) v
|
||
WHERE (v.data->>'id')::INT = ANY(v_lc_path)
|
||
)
|
||
SELECT a.row_type, a.depth, a.sort_order,
|
||
a.diff || jsonb_build_object('id', (a.data->>'id')::INT)
|
||
FROM after_rows a
|
||
WHERE a.diff <> '{}'::jsonb
|
||
ORDER BY a.sort_order;
|
||
|
||
ELSIF p_sheet = 'OTCH9F' THEN
|
||
RETURN QUERY
|
||
WITH after_rows AS (
|
||
SELECT v.row_type, v.depth, v.sort_order, v.data,
|
||
v3._jsonb_diff(v_before->(v.sort_order::text), v.data) AS diff
|
||
FROM v3.v_form_view(p_form_id, p_sheet, p_sections, p_direction) v
|
||
WHERE EXISTS (
|
||
SELECT 1 FROM v3.fixed_asset_report far
|
||
WHERE far.id = ANY(v_changed_lines)
|
||
AND far.expense_item_id = (v.data->>'expense_item_id')::INT
|
||
AND far.equipment_name IS NOT DISTINCT FROM (v.data->>'equipment_name'))
|
||
)
|
||
SELECT a.row_type, a.depth, a.sort_order,
|
||
a.diff || jsonb_build_object('id', (a.data->>'id')::INT)
|
||
FROM after_rows a
|
||
WHERE a.diff <> '{}'::jsonb
|
||
ORDER BY a.sort_order;
|
||
|
||
ELSE
|
||
-- Основная сетка FORM_1/2/4
|
||
RETURN QUERY
|
||
WITH after_rows AS (
|
||
SELECT v.row_type, v.depth, v.sort_order, v.data,
|
||
v3._jsonb_diff(v_before->(v.sort_order::text), v.data) AS diff
|
||
FROM v3.v_form_view(p_form_id, p_sheet, p_sections, p_direction) v
|
||
WHERE (v.row_type IN ('ROOT','GROUP','ITEM','SUB_ITEM')
|
||
AND (v.data->'header'->>'expense_item_id')::INT = ANY(v_ei_path))
|
||
OR (v.row_type = 'INPUT'
|
||
AND (v.data->>'line_id')::INT = ANY(v_changed_lines))
|
||
)
|
||
SELECT a.row_type, a.depth, a.sort_order,
|
||
CASE
|
||
WHEN a.row_type = 'INPUT' THEN
|
||
a.diff || jsonb_build_object('line_id', (a.data->>'line_id')::INT)
|
||
ELSE
|
||
-- иерархия: добавить header.expense_item_id если diff его не содержит
|
||
a.diff || jsonb_build_object('header',
|
||
COALESCE(a.diff->'header', '{}'::jsonb)
|
||
|| jsonb_build_object('expense_item_id',
|
||
(a.data->'header'->>'expense_item_id')::INT))
|
||
END
|
||
FROM after_rows a
|
||
WHERE a.diff <> '{}'::jsonb
|
||
ORDER BY a.sort_order;
|
||
END IF;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
|
||
-- DROP FUNCTION v3.upd_form_cells(int4, varchar, jsonb, int4, varchar, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_form_cells(p_form_id integer, p_sheet character varying, p_changes jsonb, p_user_id integer, p_direction character varying DEFAULT NULL::character varying, p_sections text[] DEFAULT NULL::text[])
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_change JSONB;
|
||
v_column TEXT;
|
||
v_decision JSONB;
|
||
v_msg TEXT;
|
||
BEGIN
|
||
IF p_user_id IS NULL THEN
|
||
RAISE EXCEPTION 'role_not_allowed: <missing user_id>';
|
||
END IF;
|
||
|
||
IF NOT EXISTS (SELECT 1 FROM v3.app_user WHERE id = p_user_id AND is_active) THEN
|
||
RAISE EXCEPTION 'role_not_allowed: unknown or inactive user %', p_user_id;
|
||
END IF;
|
||
|
||
IF p_changes IS NULL OR jsonb_typeof(p_changes) <> 'array' THEN
|
||
RAISE EXCEPTION 'p_changes must be a JSONB array';
|
||
END IF;
|
||
|
||
-- Проверяем КАЖДУЮ ячейку до начала записи. Падаем на первой запрещённой —
|
||
-- транзакция целиком откатится, частичной записи не будет.
|
||
FOR v_change IN SELECT * FROM jsonb_array_elements(p_changes)
|
||
LOOP
|
||
v_column := v_change->>'column';
|
||
IF v_column IS NULL THEN
|
||
RAISE EXCEPTION 'change must have column: %', v_change;
|
||
END IF;
|
||
|
||
v_decision := v3.can_edit(p_form_id, p_sheet, v_column, p_user_id);
|
||
|
||
IF NOT (v_decision->>'ok')::boolean THEN
|
||
IF v_decision->>'code' = 'role_not_allowed' THEN
|
||
RAISE EXCEPTION 'role_not_allowed: %', v_column;
|
||
|
||
ELSIF v_decision->>'code' = 'org_not_assigned' THEN
|
||
RAISE EXCEPTION 'org_not_assigned: %', v_column;
|
||
|
||
ELSIF v_decision->>'code' = 'window_closed' THEN
|
||
IF v_decision ? 'opens_at' THEN
|
||
v_msg := format('window_closed: %s (opens %s)',
|
||
v_column, v_decision->>'opens_at');
|
||
ELSIF v_decision->>'closes_at' IS NOT NULL THEN
|
||
v_msg := format('window_closed: %s (closed %s)',
|
||
v_column, v_decision->>'closes_at');
|
||
ELSE
|
||
v_msg := format('window_closed: %s (no phase)', v_column);
|
||
END IF;
|
||
RAISE EXCEPTION '%', v_msg;
|
||
|
||
ELSE
|
||
RAISE EXCEPTION '%: %', v_decision->>'code', v_column;
|
||
END IF;
|
||
END IF;
|
||
END LOOP;
|
||
|
||
-- Все ячейки разрешены — делегируем в v3.
|
||
-- ROW_UPDATE намеренно НЕ логируется (см. logging_events.md): cell-edit'ы
|
||
-- слишком частые. В audit_log пишем только структурные события
|
||
-- (ROW_CREATE / ROW_DELETE) + бизнес-операции (CREATE_TASK и т.д.).
|
||
RETURN QUERY
|
||
SELECT * FROM v3.upd_form_cells(
|
||
p_form_id, p_sheet, p_changes, p_direction, p_sections
|
||
);
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.upd_form_cell(int4, varchar, int4, text, jsonb, varchar, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_form_cell(p_form_id integer, p_sheet character varying, p_budget_line_id integer, p_column text, p_value jsonb, p_direction character varying DEFAULT NULL::character varying, p_sections text[] DEFAULT NULL::text[])
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE sql
|
||
AS $function$
|
||
SELECT * FROM v3.upd_form_cells(
|
||
p_form_id,
|
||
p_sheet,
|
||
jsonb_build_array(jsonb_build_object(
|
||
'line_id', p_budget_line_id,
|
||
'column', p_column,
|
||
'value', p_value
|
||
)),
|
||
p_direction,
|
||
p_sections
|
||
);
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.upd_form_cell(int4, varchar, int4, text, jsonb, int4, varchar, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_form_cell(p_form_id integer, p_sheet character varying, p_budget_line_id integer, p_column text, p_value jsonb, p_user_id integer, p_direction character varying DEFAULT NULL::character varying, p_sections text[] DEFAULT NULL::text[])
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE sql
|
||
AS $function$
|
||
SELECT * FROM v3.upd_form_cells(
|
||
p_form_id,
|
||
p_sheet,
|
||
jsonb_build_array(jsonb_build_object(
|
||
'line_id', p_budget_line_id,
|
||
'column', p_column,
|
||
'value', p_value
|
||
)),
|
||
p_user_id,
|
||
p_direction,
|
||
p_sections
|
||
);
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.upd_form_phase(int4, varchar, varchar, varchar, _text, timestamptz, timestamptz);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_form_phase(p_budget_form_id integer, p_sheet character varying, p_phase_code character varying, p_role character varying DEFAULT NULL::character varying, p_column_keys text[] DEFAULT NULL::text[], p_opens_at timestamp with time zone DEFAULT NULL::timestamp with time zone, p_closes_at timestamp with time zone DEFAULT NULL::timestamp with time zone)
|
||
RETURNS v3.form_phase
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old v3.form_phase;
|
||
v_new v3.form_phase;
|
||
v_is_extend BOOLEAN := FALSE;
|
||
v_has_non_extend_change BOOLEAN := FALSE;
|
||
v_has_window_change BOOLEAN := FALSE;
|
||
v_changes JSONB := '{}'::jsonb;
|
||
v_org_unit_id INT;
|
||
v_org_unit_title VARCHAR;
|
||
v_form_type_code VARCHAR;
|
||
v_year INT;
|
||
v_task_name TEXT;
|
||
v_phase_id TEXT;
|
||
BEGIN
|
||
SELECT * INTO v_old
|
||
FROM v3.form_phase
|
||
WHERE budget_form_id = p_budget_form_id
|
||
AND sheet = p_sheet
|
||
AND phase_code = p_phase_code;
|
||
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'form_phase не найден (form_id=%, sheet=%, phase=%)',
|
||
p_budget_form_id, p_sheet, p_phase_code;
|
||
END IF;
|
||
|
||
UPDATE v3.form_phase
|
||
SET role = COALESCE(p_role, role),
|
||
column_keys = COALESCE(p_column_keys, column_keys),
|
||
opens_at = COALESCE(p_opens_at, opens_at),
|
||
closes_at = COALESCE(p_closes_at, closes_at)
|
||
WHERE budget_form_id = p_budget_form_id
|
||
AND sheet = p_sheet
|
||
AND phase_code = p_phase_code
|
||
RETURNING * INTO v_new;
|
||
|
||
SELECT bf.org_unit_id, ou.title, bf.form_type_code, bf.year
|
||
INTO v_org_unit_id, v_org_unit_title, v_form_type_code, v_year
|
||
FROM v3.budget_form bf
|
||
LEFT JOIN v3.org_unit ou ON ou.id = bf.org_unit_id
|
||
WHERE bf.id = p_budget_form_id;
|
||
v_task_name := trim(concat(
|
||
COALESCE(v_form_type_code, ''),
|
||
CASE WHEN v_year IS NOT NULL THEN ' ' || v_year::TEXT ELSE '' END,
|
||
CASE WHEN v_org_unit_title IS NOT NULL THEN ' [' || v_org_unit_title || ']' ELSE '' END
|
||
));
|
||
v_phase_id := format('%s:%s:%s', p_budget_form_id, p_sheet, p_phase_code);
|
||
|
||
v_is_extend := v_new.closes_at > v_old.closes_at;
|
||
v_has_non_extend_change := v_old.role IS DISTINCT FROM v_new.role
|
||
OR v_old.column_keys IS DISTINCT FROM v_new.column_keys
|
||
OR v_old.opens_at IS DISTINCT FROM v_new.opens_at;
|
||
v_has_window_change := v_has_non_extend_change
|
||
OR (v_old.closes_at IS DISTINCT FROM v_new.closes_at AND NOT v_is_extend);
|
||
|
||
IF v_old.role IS DISTINCT FROM v_new.role THEN
|
||
v_changes := v_changes || jsonb_build_object(
|
||
'role', jsonb_build_object('before', v_old.role, 'after', v_new.role)
|
||
);
|
||
END IF;
|
||
IF v_old.column_keys IS DISTINCT FROM v_new.column_keys THEN
|
||
v_changes := v_changes || jsonb_build_object(
|
||
'column_keys', jsonb_build_object('before', v_old.column_keys, 'after', v_new.column_keys)
|
||
);
|
||
END IF;
|
||
IF v_old.opens_at IS DISTINCT FROM v_new.opens_at THEN
|
||
v_changes := v_changes || jsonb_build_object(
|
||
'opens_at', jsonb_build_object(
|
||
'before', v_old.opens_at,
|
||
'after', v_new.opens_at
|
||
)
|
||
);
|
||
END IF;
|
||
IF v_old.closes_at IS DISTINCT FROM v_new.closes_at THEN
|
||
v_changes := v_changes || jsonb_build_object(
|
||
'closes_at', jsonb_build_object(
|
||
'before', v_old.closes_at,
|
||
'after', v_new.closes_at
|
||
)
|
||
);
|
||
END IF;
|
||
|
||
IF v_is_extend THEN
|
||
PERFORM v3.log_event(
|
||
'ACCESS_EXTEND', 'ACCESS',
|
||
jsonb_build_object(
|
||
'phase_id', v_phase_id,
|
||
'phase_name', p_phase_code,
|
||
'task_id', p_budget_form_id,
|
||
'task_name', NULLIF(v_task_name, ''),
|
||
'form_id', p_budget_form_id,
|
||
'budget_form_id', p_budget_form_id,
|
||
'org_unit_id', v_org_unit_id,
|
||
'org_unit_name', v_org_unit_title,
|
||
'sheet', p_sheet,
|
||
'phase_code', p_phase_code,
|
||
'closes_at_before', v_old.closes_at,
|
||
'closes_at_after', v_new.closes_at
|
||
),
|
||
p_budget_form_id
|
||
);
|
||
END IF;
|
||
|
||
IF v_has_window_change THEN
|
||
PERFORM v3.log_event(
|
||
'ACCESS_WINDOW_CHANGE', 'ACCESS',
|
||
jsonb_build_object(
|
||
'entity_type', 'form_phase',
|
||
'entity_id', p_budget_form_id,
|
||
'action', 'update',
|
||
'phase_id', v_phase_id,
|
||
'phase_name', p_phase_code,
|
||
'task_id', p_budget_form_id,
|
||
'task_name', NULLIF(v_task_name, ''),
|
||
'form_id', p_budget_form_id,
|
||
'budget_form_id', p_budget_form_id,
|
||
'org_unit_id', v_org_unit_id,
|
||
'org_unit_name', v_org_unit_title,
|
||
'sheet', p_sheet,
|
||
'phase_code', p_phase_code,
|
||
'changes', v_changes
|
||
),
|
||
p_budget_form_id, null, v_org_unit_id
|
||
);
|
||
END IF;
|
||
|
||
RETURN v_new;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.upd_project(int4, text, jsonb);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_project(p_project_id integer, p_column text, p_value jsonb)
|
||
RETURNS jsonb
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_target_col TEXT;
|
||
v_target_type TEXT;
|
||
v_sql TEXT;
|
||
v_out JSONB;
|
||
BEGIN
|
||
IF NOT EXISTS (SELECT 1 FROM v3.project WHERE id = p_project_id) THEN
|
||
RAISE EXCEPTION 'project id=% не существует', p_project_id;
|
||
END IF;
|
||
|
||
-- Editable атрибуты (CHECK-ы из migrate_form3_v1.sql применятся при UPDATE).
|
||
-- Не editable: level (структура), parent_id (иерархия).
|
||
CASE p_column
|
||
WHEN 'name' THEN v_target_col := 'name'; v_target_type := 'TEXT';
|
||
WHEN 'project_type' THEN v_target_col := 'project_type'; v_target_type := 'TEXT';
|
||
WHEN 'vsp_format' THEN v_target_col := 'vsp_format'; v_target_type := 'TEXT';
|
||
WHEN 'placement_type' THEN v_target_col := 'placement_type'; v_target_type := 'TEXT';
|
||
WHEN 'object_address' THEN v_target_col := 'object_address'; v_target_type := 'TEXT';
|
||
WHEN 'staff_count' THEN v_target_col := 'staff_count'; v_target_type := 'INTEGER';
|
||
WHEN 'total_area' THEN v_target_col := 'total_area'; v_target_type := 'NUMERIC';
|
||
WHEN 'org_unit_id' THEN v_target_col := 'org_unit_id'; v_target_type := 'INTEGER';
|
||
WHEN 'level','parent_id' THEN
|
||
RAISE EXCEPTION 'structural_field: % (правится отдельно через ADD project / структурный API)', p_column;
|
||
WHEN 'id' THEN
|
||
RAISE EXCEPTION 'key_field: id (RO)';
|
||
ELSE
|
||
RAISE EXCEPTION 'unknown_column: %', p_column;
|
||
END CASE;
|
||
|
||
-- FK санити (если применимо)
|
||
IF v_target_col = 'org_unit_id' AND p_value <> 'null'::jsonb THEN
|
||
IF NOT EXISTS (SELECT 1 FROM v3.org_unit WHERE id = (p_value #>> '{}')::INT) THEN
|
||
RAISE EXCEPTION 'org_unit id=% не существует', (p_value #>> '{}');
|
||
END IF;
|
||
END IF;
|
||
|
||
v_sql := format(
|
||
'UPDATE v3.project SET %1$I = ($2 #>> ''{}'')::%2$s WHERE id = $1',
|
||
v_target_col, v_target_type
|
||
);
|
||
EXECUTE v_sql USING p_project_id, p_value;
|
||
|
||
-- Возврат — весь header проекта
|
||
SELECT jsonb_build_object(
|
||
'id', p.id,
|
||
'name', p.name,
|
||
'level', p.level,
|
||
'parent_id', p.parent_id,
|
||
'org_unit_id', p.org_unit_id,
|
||
'project_type', p.project_type,
|
||
'vsp_format', p.vsp_format,
|
||
'placement_type', p.placement_type,
|
||
'object_address', p.object_address,
|
||
'staff_count', p.staff_count,
|
||
'total_area', p.total_area
|
||
)
|
||
INTO v_out
|
||
FROM v3.project p WHERE p.id = p_project_id;
|
||
RETURN v_out;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.upd_user(int4, varchar, varchar, varchar, varchar, int4, bool);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_user(p_user_id integer, p_email character varying DEFAULT NULL::character varying, p_username character varying DEFAULT NULL::character varying, p_hashed_password character varying DEFAULT NULL::character varying, p_full_name character varying DEFAULT NULL::character varying, p_role_id integer DEFAULT NULL::integer, p_is_active boolean DEFAULT NULL::boolean)
|
||
RETURNS v3.app_user
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old_role_id INT;
|
||
v_new v3.app_user;
|
||
BEGIN
|
||
SELECT role_id INTO v_old_role_id
|
||
FROM v3.app_user
|
||
WHERE id = p_user_id;
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'app_user #% не существует', p_user_id;
|
||
END IF;
|
||
|
||
UPDATE v3.app_user
|
||
SET email = COALESCE(p_email, email),
|
||
username = COALESCE(p_username, username),
|
||
hashed_password = COALESCE(p_hashed_password, hashed_password),
|
||
full_name = COALESCE(p_full_name, full_name),
|
||
role_id = COALESCE(p_role_id, role_id),
|
||
is_active = COALESCE(p_is_active, is_active),
|
||
updated_at = now()
|
||
WHERE id = p_user_id
|
||
RETURNING * INTO v_new;
|
||
|
||
IF v_old_role_id IS DISTINCT FROM v_new.role_id THEN
|
||
PERFORM v3.log_event(
|
||
'USER_ROLE_CHANGE', 'USER',
|
||
jsonb_build_object(
|
||
'user_id', p_user_id,
|
||
'entity_id', p_user_id,
|
||
'email', v_new.email,
|
||
'full_name', v_new.full_name,
|
||
'role_id_before', v_old_role_id,
|
||
'role_id_after', v_new.role_id
|
||
)
|
||
);
|
||
END IF;
|
||
|
||
RETURN v_new;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.upd_vsp(int4, int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, varchar, date);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_vsp(p_vsp_id integer, p_branch_id integer DEFAULT NULL::integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT NULL::boolean, p_is_deleted boolean DEFAULT NULL::boolean, p_system_code character varying DEFAULT NULL::character varying, p_updated_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_location_form character varying DEFAULT NULL::character varying, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date)
|
||
RETURNS void
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old RECORD;
|
||
v_changes JSONB := '{}'::jsonb;
|
||
|
||
v_branch_name_before VARCHAR;
|
||
v_branch_name_after VARCHAR;
|
||
|
||
v_actor_id INT;
|
||
v_actor_email VARCHAR;
|
||
v_actor_full_name VARCHAR;
|
||
BEGIN
|
||
SELECT v.branch_id, v.reg_number, v.address, v.format, v.opened_at,
|
||
v.placement_type, v.staff_count, v.total_area, v.closed_at,
|
||
v.is_active, v.is_deleted, v.system_code,
|
||
v.vsp_type, v.notes, v.location_form, v.rent_contract_num, v.rent_end_date
|
||
INTO v_old
|
||
FROM v3.vsp v
|
||
WHERE v.id = p_vsp_id;
|
||
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'vsp #% не существует', p_vsp_id;
|
||
END IF;
|
||
|
||
SELECT ou.title
|
||
INTO v_branch_name_before
|
||
FROM v3.org_unit ou
|
||
WHERE ou.id = v_old.branch_id;
|
||
|
||
v_branch_name_after := v_branch_name_before;
|
||
|
||
v_actor_id := COALESCE(p_updated_by, v3.current_user_id());
|
||
IF v_actor_id IS NOT NULL THEN
|
||
SELECT u.email, u.full_name
|
||
INTO v_actor_email, v_actor_full_name
|
||
FROM v3.app_user u
|
||
WHERE u.id = v_actor_id;
|
||
END IF;
|
||
|
||
IF p_branch_id IS NOT NULL AND p_branch_id IS DISTINCT FROM v_old.branch_id THEN
|
||
SELECT ou.title INTO v_branch_name_after
|
||
FROM v3.org_unit ou
|
||
WHERE ou.id = p_branch_id;
|
||
|
||
UPDATE v3.vsp SET branch_id = p_branch_id WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object(
|
||
'branch_id', jsonb_build_object(
|
||
'before', v3._diff_val(v_old.branch_id),
|
||
'after', v3._diff_val(p_branch_id)
|
||
),
|
||
'branch_name', jsonb_build_object(
|
||
'before', v3._diff_val(v_branch_name_before),
|
||
'after', v3._diff_val(v_branch_name_after)
|
||
)
|
||
);
|
||
END IF;
|
||
|
||
IF p_reg_number IS NOT NULL AND p_reg_number IS DISTINCT FROM v_old.reg_number THEN
|
||
UPDATE v3.vsp SET reg_number = p_reg_number WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('reg_number',
|
||
jsonb_build_object('before', v3._diff_val(v_old.reg_number),
|
||
'after', v3._diff_val(p_reg_number)));
|
||
END IF;
|
||
|
||
IF p_address IS NOT NULL AND p_address IS DISTINCT FROM v_old.address THEN
|
||
UPDATE v3.vsp SET address = p_address WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('address',
|
||
jsonb_build_object('before', v3._diff_val(v_old.address),
|
||
'after', v3._diff_val(p_address)));
|
||
END IF;
|
||
|
||
IF p_format IS NOT NULL AND p_format IS DISTINCT FROM v_old.format THEN
|
||
UPDATE v3.vsp SET format = p_format WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('format',
|
||
jsonb_build_object('before', v3._diff_val(v_old.format),
|
||
'after', v3._diff_val(p_format)));
|
||
END IF;
|
||
|
||
IF p_opened_at IS NOT NULL AND p_opened_at IS DISTINCT FROM v_old.opened_at THEN
|
||
UPDATE v3.vsp SET opened_at = p_opened_at WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('opened_at',
|
||
jsonb_build_object('before', v3._diff_val(v_old.opened_at),
|
||
'after', v3._diff_val(p_opened_at)));
|
||
END IF;
|
||
|
||
IF p_placement_type IS NOT NULL AND p_placement_type IS DISTINCT FROM v_old.placement_type THEN
|
||
UPDATE v3.vsp SET placement_type = p_placement_type WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('placement_type',
|
||
jsonb_build_object('before', v3._diff_val(v_old.placement_type),
|
||
'after', v3._diff_val(p_placement_type)));
|
||
END IF;
|
||
|
||
IF p_staff_count IS NOT NULL AND p_staff_count IS DISTINCT FROM v_old.staff_count THEN
|
||
UPDATE v3.vsp SET staff_count = p_staff_count WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('staff_count',
|
||
jsonb_build_object('before', v3._diff_val(v_old.staff_count),
|
||
'after', v3._diff_val(p_staff_count)));
|
||
END IF;
|
||
|
||
IF p_total_area IS NOT NULL AND p_total_area IS DISTINCT FROM v_old.total_area THEN
|
||
UPDATE v3.vsp SET total_area = p_total_area WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('total_area',
|
||
jsonb_build_object('before', v3._diff_val(v_old.total_area),
|
||
'after', v3._diff_val(p_total_area)));
|
||
END IF;
|
||
|
||
IF p_closed_at IS NOT NULL AND p_closed_at IS DISTINCT FROM v_old.closed_at THEN
|
||
UPDATE v3.vsp SET closed_at = p_closed_at WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('closed_at',
|
||
jsonb_build_object('before', v3._diff_val(v_old.closed_at),
|
||
'after', v3._diff_val(p_closed_at)));
|
||
END IF;
|
||
|
||
IF p_is_active IS NOT NULL AND p_is_active IS DISTINCT FROM v_old.is_active THEN
|
||
UPDATE v3.vsp SET is_active = p_is_active WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('is_active',
|
||
jsonb_build_object('before', v3._diff_val(v_old.is_active),
|
||
'after', v3._diff_val(p_is_active)));
|
||
END IF;
|
||
|
||
IF p_is_deleted IS NOT NULL AND p_is_deleted IS DISTINCT FROM v_old.is_deleted THEN
|
||
UPDATE v3.vsp SET is_deleted = p_is_deleted WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('is_deleted',
|
||
jsonb_build_object('before', v3._diff_val(v_old.is_deleted),
|
||
'after', v3._diff_val(p_is_deleted)));
|
||
END IF;
|
||
|
||
IF p_system_code IS NOT NULL AND p_system_code IS DISTINCT FROM v_old.system_code THEN
|
||
UPDATE v3.vsp SET system_code = p_system_code WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('system_code',
|
||
jsonb_build_object('before', v3._diff_val(v_old.system_code),
|
||
'after', v3._diff_val(p_system_code)));
|
||
END IF;
|
||
|
||
IF p_vsp_type IS NOT NULL AND p_vsp_type IS DISTINCT FROM v_old.vsp_type THEN
|
||
UPDATE v3.vsp SET vsp_type = p_vsp_type WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('vsp_type',
|
||
jsonb_build_object('before', v3._diff_val(v_old.vsp_type),
|
||
'after', v3._diff_val(p_vsp_type)));
|
||
END IF;
|
||
|
||
IF p_notes IS NOT NULL AND p_notes IS DISTINCT FROM v_old.notes THEN
|
||
UPDATE v3.vsp SET notes = p_notes WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('notes',
|
||
jsonb_build_object('before', v3._diff_val(v_old.notes),
|
||
'after', v3._diff_val(p_notes)));
|
||
END IF;
|
||
|
||
IF p_location_form IS NOT NULL AND p_location_form IS DISTINCT FROM v_old.location_form THEN
|
||
UPDATE v3.vsp SET location_form = p_location_form WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('location_form',
|
||
jsonb_build_object('before', v3._diff_val(v_old.location_form),
|
||
'after', v3._diff_val(p_location_form)));
|
||
END IF;
|
||
|
||
IF p_rent_contract_num IS NOT NULL AND p_rent_contract_num IS DISTINCT FROM v_old.rent_contract_num THEN
|
||
UPDATE v3.vsp SET rent_contract_num = p_rent_contract_num WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('rent_contract_num',
|
||
jsonb_build_object('before', v3._diff_val(v_old.rent_contract_num),
|
||
'after', v3._diff_val(p_rent_contract_num)));
|
||
END IF;
|
||
|
||
IF p_rent_end_date IS NOT NULL AND p_rent_end_date IS DISTINCT FROM v_old.rent_end_date THEN
|
||
UPDATE v3.vsp SET rent_end_date = p_rent_end_date WHERE id = p_vsp_id;
|
||
v_changes := v_changes || jsonb_build_object('rent_end_date',
|
||
jsonb_build_object('before', v3._diff_val(v_old.rent_end_date),
|
||
'after', v3._diff_val(p_rent_end_date)));
|
||
END IF;
|
||
|
||
IF v_changes <> '{}'::jsonb THEN
|
||
UPDATE v3.vsp
|
||
SET updated_at = now(),
|
||
updated_by = p_updated_by
|
||
WHERE id = p_vsp_id;
|
||
|
||
PERFORM v3.log_event(
|
||
'VSP_UPDATE', 'VSP',
|
||
jsonb_build_object(
|
||
'vsp_id', p_vsp_id,
|
||
'entity_id', p_vsp_id,
|
||
'branch_id', COALESCE(p_branch_id, v_old.branch_id),
|
||
'branch_name', COALESCE(v_branch_name_after, v_branch_name_before),
|
||
'changes', v_changes,
|
||
'user_email', v_actor_email,
|
||
'user_full_name', v_actor_full_name
|
||
), null, null, COALESCE(p_branch_id, v_old.branch_id)
|
||
);
|
||
END IF;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.user_in_form_org(int4, int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.user_in_form_org(p_user_id integer, p_form_id integer)
|
||
RETURNS boolean
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
AS $function$
|
||
DECLARE
|
||
v_org INT;
|
||
BEGIN
|
||
SELECT org_unit_id INTO v_org
|
||
FROM v3.budget_form
|
||
WHERE id = p_form_id;
|
||
|
||
IF v_org IS NULL THEN
|
||
RETURN TRUE; -- форма не привязана к org_unit — пропускаем фильтр
|
||
END IF;
|
||
|
||
RETURN EXISTS (
|
||
SELECT 1
|
||
FROM v3.user_org
|
||
WHERE user_id = p_user_id
|
||
AND org_unit_id = v_org
|
||
);
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.user_role_code(int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.user_role_code(p_user_id integer)
|
||
RETURNS character varying
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
AS $function$
|
||
DECLARE
|
||
v_code VARCHAR;
|
||
BEGIN
|
||
SELECT r.code
|
||
INTO v_code
|
||
FROM v3.app_user u
|
||
JOIN v3.role r ON r.id = u.role_id
|
||
WHERE u.id = p_user_id
|
||
AND u.is_active;
|
||
RETURN v_code; -- NULL если юзер не найден или неактивен
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form1_sheet_sections(int4, varchar, varchar, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form1_sheet_sections(p_form_id integer, p_sheet character varying, p_direction character varying DEFAULT NULL::character varying, p_sections text[] DEFAULT NULL::text[])
|
||
RETURNS TABLE(row_type character varying, depth integer, line_id integer, col_b_year integer, col_c_section character varying, col_d_item_id character varying, col_e_num_group character varying, col_f_name character varying, col_g_internal_order character varying, col_h_plan_q1 numeric, col_i_plan_q2 numeric, col_j_plan_q3 numeric, col_k_plan_q4 numeric, col_l_plan_year numeric, col_m_plan_comment character varying, col_n_cs_total numeric, col_o_cs_counterparty character varying, col_p_cs_deadline character varying, col_q_cs_comment character varying, col_r_cs_future_y1 numeric, col_s_cs_future_y2 numeric, col_t_cs_other_ssp numeric, col_v_al_order character varying, col_w_al_property character varying, col_y_seq_q1 numeric, col_z_seq_q2 numeric, col_aa_seq_q3 numeric, col_ab_seq_q4 numeric, col_ac_seq_year numeric, col_ad_seq_justification character varying, col_af_res_q1 numeric, col_ag_res_q2 numeric, col_ah_res_q3 numeric, col_ai_res_q4 numeric, col_aj_res_year numeric, col_ak_res_justification character varying, col_am_approved_q1 numeric, col_an_approved_q2 numeric, col_ao_approved_q3 numeric, col_ap_approved_q4 numeric, col_aq_approved_year numeric, col_as_ca_approved numeric, col_at_ca_protocol character varying, col_au_ca_note character varying, col_aw_ckk_ceiling numeric, col_ax_ckk_exp_q1 numeric, col_ay_ckk_exp_q2 numeric, col_az_ckk_exp_q3 numeric, col_ba_ckk_exp_q4 numeric, col_bb_ckk_rf_sched character varying, col_bc_ckk_deadline character varying, col_bd_ckk_proc_plan character varying, col_be_ckk_proc_meth character varying, col_bf_ckk_comment character varying, col_bh_cd_counterparty character varying, col_bi_cd_reference character varying, col_bj_cd_addenda character varying, col_bk_cd_subject character varying, col_bl_cd_currency character varying, col_bm_cd_ceiling numeric, col_bn_cd_exp_q1 numeric, col_bo_cd_exp_q2 numeric, col_bp_cd_exp_q3 numeric, col_bq_cd_exp_q4 numeric, col_br_cd_rf_sched character varying, col_bs_cd_vat character varying, col_bt_cd_rate numeric, col_bu_cd_amount_fx numeric, col_bv_cd_deadline character varying, col_bw_cd_scheme character varying, col_bx_cd_act character varying, col_by_cd_comment character varying, col_ca_q1_adj_current numeric, col_cb_q1_adj_ssp numeric, col_cc_q1_adj_rf numeric, col_cd_q1_adj_reserve numeric, col_ce_q1_adj_comment character varying, col_cf_q1_corrected_plan numeric, col_ch_q1_pay_date date, col_ci_q1_pay_amount numeric, col_cj_q1_pay_ho numeric, col_ck_q1_pay_rf numeric, col_cl_q1_pay_comment character varying, col_cm_q1_pay_act character varying, col_co_q1_booking numeric, col_cp_q1_actual_m1 numeric, col_cq_q1_actual_m2 numeric, col_cr_q1_actual_m3 numeric, col_cs_q1_actual_quarter numeric, col_ct_q1_rem_booking numeric, col_cu_q1_rem_actual numeric, col_cw_q1_trans_q2 numeric, col_cx_q1_trans_q3 numeric, col_cy_q1_trans_q4 numeric, col_cz_q1_economy numeric, col_da_q1_total numeric, col_dc_q2_rev_eco numeric, col_dd_q2_rev_item numeric, col_de_q2_rev_inc numeric, col_df_q2_rev_seq numeric, col_dg_q2_rev_comment character varying, col_dh_q2_new_plan numeric, col_dj_q2_adj_current numeric, col_dk_q2_adj_ssp numeric, col_dl_q2_adj_rf numeric, col_dm_q2_adj_reserve numeric, col_dn_q2_adj_comment character varying, col_do_q2_corrected_plan numeric, col_dq_q2_pay_date date, col_dr_q2_pay_amount numeric, col_ds_q2_pay_ho numeric, col_dt_q2_pay_rf numeric, col_du_q2_pay_comment character varying, col_dv_q2_pay_act character varying, col_dx_q2_booking numeric, col_dy_q2_actual_m1 numeric, col_dz_q2_actual_m2 numeric, col_ea_q2_actual_m3 numeric, col_eb_q2_actual_quarter numeric, col_ec_q2_rem_booking numeric, col_ed_q2_rem_actual numeric, col_ef_q2_trans_q3 numeric, col_eg_q2_trans_q4 numeric, col_eh_q2_economy numeric, col_ei_q2_total numeric, col_ek_q3_rev_eco numeric, col_el_q3_rev_item numeric, col_em_q3_rev_inc numeric, col_en_q3_rev_seq numeric, col_eo_q3_rev_comment character varying, col_ep_q3_new_plan numeric, col_er_q3_adj_current numeric, col_es_q3_adj_ssp numeric, col_et_q3_adj_rf numeric, col_eu_q3_adj_reserve numeric, col_ev_q3_adj_comment character varying, col_ew_q3_corrected_plan numeric, col_ey_q3_pay_date date, col_ez_q3_pay_amount numeric, col_fa_q3_pay_ho numeric, col_fb_q3_pay_rf numeric, col_fc_q3_pay_comment character varying, col_fd_q3_pay_act character varying, col_ff_q3_booking numeric, col_fg_q3_actual_m1 numeric, col_fh_q3_actual_m2 numeric, col_fi_q3_actual_m3 numeric, col_fj_q3_actual_quarter numeric, col_fk_q3_rem_booking numeric, col_fl_q3_rem_actual numeric, col_fn_q3_trans_q4 numeric, col_fo_q3_economy numeric, col_fp_q3_total numeric, col_fr_q4_rev_eco numeric, col_fs_q4_rev_item numeric, col_ft_q4_rev_inc numeric, col_fu_q4_rev_seq numeric, col_fv_q4_rev_comment character varying, col_fw_q4_new_plan numeric, col_fy_q4_adj_current numeric, col_fz_q4_adj_ssp numeric, col_ga_q4_adj_rf numeric, col_gb_q4_adj_reserve numeric, col_gc_q4_adj_comment character varying, col_gd_q4_corrected_plan numeric, col_gf_q4_pay_date date, col_gg_q4_pay_amount numeric, col_gh_q4_pay_ho numeric, col_gi_q4_pay_rf numeric, col_gj_q4_pay_comment character varying, col_gk_q4_pay_act character varying, col_gm_q4_booking numeric, col_gn_q4_actual_m1 numeric, col_go_q4_actual_m2 numeric, col_gp_q4_actual_m3 numeric, col_gq_q4_actual_quarter numeric, col_gr_q4_actual_spod numeric, col_gs_q4_rem_booking numeric, col_gt_q4_rem_actual numeric, col_gv_q4_economy numeric, col_gw_q4_total numeric, col_gz_fact_year numeric, col_hb_pay_year numeric, _sort_path integer[])
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
SET search_path TO 'v3', 'pg_catalog'
|
||
AS $function$
|
||
#variable_conflict use_column
|
||
DECLARE
|
||
v_year INT;
|
||
s_plan BOOL; s_cs BOOL; s_al BOOL; s_seq BOOL; s_res BOOL;
|
||
s_appr BOOL; s_ca BOOL; s_ckk BOOL; s_cd BOOL;
|
||
s_q1 BOOL; s_q2 BOOL; s_q3 BOOL; s_q4 BOOL; s_tot BOOL;
|
||
s_any_q BOOL;
|
||
s_need_ap BOOL; -- plan/seq/res нужны: approved И/ИЛИ cp1..cp4
|
||
s_need_bk BOOL; -- ckk/cd нужны: booking bk1..bk4 (в Q-секциях)
|
||
BEGIN
|
||
SELECT bf.year INTO v_year FROM budget_form bf WHERE bf.id = p_form_id;
|
||
|
||
s_plan := p_sections IS NULL OR 'plan' = 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_seq := p_sections IS NULL OR 'sequestration' = ANY(p_sections);
|
||
s_res := p_sections IS NULL OR 'reserve' = ANY(p_sections);
|
||
s_appr := p_sections IS NULL OR 'approved' = ANY(p_sections);
|
||
s_ca := p_sections IS NULL OR 'collegial' = ANY(p_sections);
|
||
s_ckk := p_sections IS NULL OR 'ckk' = ANY(p_sections);
|
||
s_cd := p_sections IS NULL OR 'contract_detail' = 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_any_q := s_q1 OR s_q2 OR s_q3 OR s_q4 OR s_tot;
|
||
s_need_ap := s_plan OR s_appr OR s_q1 OR s_q2 OR s_q3 OR s_q4;
|
||
s_need_bk := s_q1 OR s_q2 OR s_q3 OR s_q4;
|
||
|
||
RETURN QUERY
|
||
WITH
|
||
-- ═══ 1. Дерево expense_item из materialized view ══════════════════════
|
||
-- JOIN с expense_item_form_type фильтрует узлы по form_type текущей формы
|
||
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 mv_expense_item_tree t
|
||
JOIN expense_item_form_type eft ON eft.expense_item_id = t.id
|
||
JOIN budget_form bf ON bf.id = p_form_id AND bf.form_type_code = eft.form_type_code
|
||
WHERE t.sheet = p_sheet
|
||
),
|
||
|
||
-- ═══ 2. Все INPUT строки формы ══════════════════════════════════════════
|
||
-- p_direction: фильтр по budget_line.direction. NULL = без фильтра.
|
||
-- bl.direction IS NULL обходит фильтр (для листов без direction, напр. Операц).
|
||
page AS (
|
||
SELECT bl.id AS lid, bl.expense_item_id AS eid, bl.internal_order AS io,
|
||
bl.name AS bname
|
||
FROM budget_line bl
|
||
JOIN expense_item ei ON ei.id = bl.expense_item_id
|
||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet
|
||
AND (p_direction IS NULL OR bl.direction IS NULL OR bl.direction = p_direction)
|
||
),
|
||
|
||
-- ═══ 3. Satellite JOINs (условные) ════════════════════════════════════
|
||
jp AS (SELECT p.* FROM plan p JOIN page pg ON p.line_id = pg.lid WHERE s_plan),
|
||
jcs AS (SELECT cs.* FROM contract_summary cs JOIN page pg ON cs.line_id = pg.lid WHERE s_cs),
|
||
jal AS (SELECT al.* FROM allocation al JOIN page pg ON al.line_id = pg.lid WHERE s_al),
|
||
jsd AS (SELECT sd.* FROM sequestration sd JOIN page pg ON sd.line_id = pg.lid WHERE s_seq AND sd.actor='DFIP'),
|
||
jr AS (SELECT r.* FROM reserve r JOIN page pg ON r.line_id = pg.lid WHERE s_res),
|
||
jca AS (SELECT ca.* FROM collegial_approval ca JOIN page pg ON ca.line_id = pg.lid WHERE s_ca),
|
||
jck AS (SELECT ck.* FROM ckk ck JOIN page pg ON ck.line_id = pg.lid WHERE s_ckk),
|
||
jcd AS (SELECT cd.* FROM contract_detail cd JOIN page pg ON cd.line_id = pg.lid WHERE s_cd),
|
||
-- Direct quarter joins (no pivot — WHERE quarter=N)
|
||
jq1 AS (SELECT blq.* FROM budget_line_quarter blq JOIN page pg ON blq.line_id = pg.lid WHERE s_q1 AND blq.quarter = 1),
|
||
jq2 AS (SELECT blq.* FROM budget_line_quarter blq JOIN page pg ON blq.line_id = pg.lid WHERE s_q2 AND blq.quarter = 2),
|
||
jq3 AS (SELECT blq.* FROM budget_line_quarter blq JOIN page pg ON blq.line_id = pg.lid WHERE s_q3 AND blq.quarter = 3),
|
||
jq4 AS (SELECT blq.* FROM budget_line_quarter blq JOIN page pg ON blq.line_id = pg.lid WHERE (s_q4 OR s_tot) AND blq.quarter = 4),
|
||
-- For totals: need all 4 quarters to sum actual across year — but only when s_tot and not all q loaded
|
||
jtot AS (
|
||
SELECT blq.line_id AS tlid,
|
||
SUM(CASE WHEN blq.quarter=1 THEN COALESCE(blq.actual_m1,0)+COALESCE(blq.actual_m2,0)+COALESCE(blq.actual_m3,0) END) AS tot_aq1,
|
||
SUM(CASE WHEN blq.quarter=2 THEN COALESCE(blq.actual_m1,0)+COALESCE(blq.actual_m2,0)+COALESCE(blq.actual_m3,0) END) AS tot_aq2,
|
||
SUM(CASE WHEN blq.quarter=3 THEN COALESCE(blq.actual_m1,0)+COALESCE(blq.actual_m2,0)+COALESCE(blq.actual_m3,0) END) AS tot_aq3,
|
||
SUM(CASE WHEN blq.quarter=4 THEN COALESCE(blq.actual_m1,0)+COALESCE(blq.actual_m2,0)+COALESCE(blq.actual_m3,0)+COALESCE(blq.actual_spod,0) END) AS tot_aq4,
|
||
SUM(CASE WHEN blq.quarter=1 THEN blq.payment_amount END) AS tot_pa1,
|
||
SUM(CASE WHEN blq.quarter=2 THEN blq.payment_amount END) AS tot_pa2,
|
||
SUM(CASE WHEN blq.quarter=3 THEN blq.payment_amount END) AS tot_pa3,
|
||
SUM(CASE WHEN blq.quarter=4 THEN blq.payment_amount END) AS tot_pa4
|
||
FROM budget_line_quarter blq JOIN page pg ON blq.line_id = pg.lid
|
||
WHERE s_tot
|
||
GROUP BY blq.line_id
|
||
),
|
||
|
||
-- ═══ 4. INPUT строки: enriched с computed ═════════════════════════════
|
||
-- Промежуточные вычисления для computed-полей
|
||
input_base AS (
|
||
SELECT
|
||
pg.lid, pg.eid, pg.io, pg.bname,
|
||
-- section_code = parent's item_id (hierarchy code like 1.01.1.)
|
||
t.parent_item_id AS sc,
|
||
-- item_id = R-code for depth=3, else hierarchy code
|
||
t.item_id AS ic,
|
||
t.num_group_id AS ng, t.name AS ename,
|
||
-- raw satellite values (for computed)
|
||
COALESCE(p.plan_q1,0) AS pq1, COALESCE(p.plan_q2,0) AS pq2,
|
||
COALESCE(p.plan_q3,0) AS pq3, COALESCE(p.plan_q4,0) AS pq4,
|
||
p.comment AS pcmt,
|
||
COALESCE(sd.adj_q1,0) AS sq1, COALESCE(sd.adj_q2,0) AS sq2,
|
||
COALESCE(sd.adj_q3,0) AS sq3, COALESCE(sd.adj_q4,0) AS sq4,
|
||
sd.justification AS sjust,
|
||
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,
|
||
cs.*, al.internal_order AS al_io, al.property_object AS al_po,
|
||
ca.approved_amount AS ca_amt, ca.protocol_reference AS ca_prot, ca.note AS ca_note,
|
||
ck.ceiling_amount AS ckk_ceil,
|
||
COALESCE(ck.expenses_q1,0) AS ckk1, COALESCE(ck.expenses_q2,0) AS ckk2,
|
||
COALESCE(ck.expenses_q3,0) AS ckk3, COALESCE(ck.expenses_q4,0) AS ckk4,
|
||
ck.rf_schedule AS ckk_rf, ck.delivery_deadline AS ckk_dl,
|
||
ck.procurement_plan AS ckk_pp, ck.procurement_method AS ckk_pm, ck.comment AS ckk_cmt,
|
||
cd.counterparty AS cd_cp, cd.reference AS cd_ref, cd.addenda AS cd_add,
|
||
cd.subject AS cd_subj, cd.currency AS cd_cur, cd.ceiling_amount AS cd_ceil,
|
||
COALESCE(cd.expenses_q1,0) AS cdq1, COALESCE(cd.expenses_q2,0) AS cdq2,
|
||
COALESCE(cd.expenses_q3,0) AS cdq3, COALESCE(cd.expenses_q4,0) AS cdq4,
|
||
cd.rf_schedule AS cd_rf, cd.vat_rate AS cd_vat, cd.exchange_rate AS cd_rate,
|
||
cd.amount_foreign AS cd_fx, cd.deadline AS cd_dl, cd.payment_scheme AS cd_sch,
|
||
cd.act AS cd_act, cd.comment AS cd_cmt2,
|
||
-- Q1 direct
|
||
q1.adj_current AS q1_ac, q1.adj_ssp AS q1_as, q1.adj_rf AS q1_ar, 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.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,
|
||
-- Q2 direct
|
||
q2.plan_revision_eco_change AS q2_reco, q2.plan_revision_item_adj AS q2_ritm,
|
||
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_ar, 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.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,
|
||
-- Q3 direct
|
||
q3.plan_revision_eco_change AS q3_reco, q3.plan_revision_item_adj AS q3_ritm,
|
||
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_ar, 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.actual_m1 AS q3_m1, q3.actual_m2 AS q3_m2, q3.actual_m3 AS q3_m3,
|
||
q3.transfer_to_q4 AS q3_tq4,
|
||
-- Q4 direct
|
||
q4.plan_revision_eco_change AS q4_reco, q4.plan_revision_item_adj AS q4_ritm,
|
||
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_ar, 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.actual_m1 AS q4_m1, q4.actual_m2 AS q4_m2, q4.actual_m3 AS q4_m3,
|
||
q4.actual_spod AS q4_spod,
|
||
-- totals
|
||
tot.tot_aq1, tot.tot_aq2, tot.tot_aq3, tot.tot_aq4,
|
||
tot.tot_pa1, tot.tot_pa2, tot.tot_pa3, tot.tot_pa4,
|
||
-- booking per quarter
|
||
CASE WHEN COALESCE(cd.expenses_q1,0)>0 THEN cd.expenses_q1 ELSE ck.expenses_q1 END AS bk1,
|
||
CASE WHEN COALESCE(cd.expenses_q2,0)>0 THEN cd.expenses_q2 ELSE ck.expenses_q2 END AS bk2,
|
||
CASE WHEN COALESCE(cd.expenses_q3,0)>0 THEN cd.expenses_q3 ELSE ck.expenses_q3 END AS bk3,
|
||
CASE WHEN COALESCE(cd.expenses_q4,0)>0 THEN cd.expenses_q4 ELSE ck.expenses_q4 END AS bk4,
|
||
-- 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,
|
||
-- tree path for ordering
|
||
t.path AS tree_path
|
||
FROM page pg
|
||
JOIN tw t ON t.id = pg.eid
|
||
LEFT JOIN jp p ON p.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 jsd sd ON sd.line_id = pg.lid
|
||
LEFT JOIN jr r ON r.line_id = pg.lid
|
||
LEFT JOIN jca ca ON ca.line_id = pg.lid
|
||
LEFT JOIN jck ck ON ck.line_id = pg.lid
|
||
LEFT JOIN jcd cd ON cd.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
|
||
LEFT JOIN jtot tot ON tot.tlid = pg.lid
|
||
),
|
||
-- Q1-Q4 computed fields
|
||
enriched AS (
|
||
SELECT b.*,
|
||
-- Q1 corrected_plan
|
||
b.ap1 + COALESCE(b.q1_ac,0)+COALESCE(b.q1_as,0)+COALESCE(b.q1_ar,0)+COALESCE(b.q1_arv,0) AS cp1,
|
||
-- Q1 actual_quarter
|
||
COALESCE(b.q1_m1,0)+COALESCE(b.q1_m2,0)+COALESCE(b.q1_m3,0) AS aq1,
|
||
-- Q2 new_plan
|
||
b.ap2 + COALESCE(b.q1_tq2,0) + COALESCE(b.q2_reco,0)+COALESCE(b.q2_ritm,0)+COALESCE(b.q2_rinc,0)+COALESCE(b.q2_rseq,0) AS np2,
|
||
-- Q2 actual_quarter
|
||
COALESCE(b.q2_m1,0)+COALESCE(b.q2_m2,0)+COALESCE(b.q2_m3,0) AS aq2,
|
||
-- Q3 new_plan
|
||
b.ap3 + COALESCE(b.q1_tq3,0)+COALESCE(b.q2_tq3,0) + COALESCE(b.q3_reco,0)+COALESCE(b.q3_ritm,0)+COALESCE(b.q3_rinc,0)+COALESCE(b.q3_rseq,0) AS np3,
|
||
-- Q3 actual_quarter
|
||
COALESCE(b.q3_m1,0)+COALESCE(b.q3_m2,0)+COALESCE(b.q3_m3,0) AS aq3,
|
||
-- Q4 new_plan
|
||
b.ap4 + COALESCE(b.q1_tq4,0)+COALESCE(b.q2_tq4,0)+COALESCE(b.q3_tq4,0) + COALESCE(b.q4_reco,0)+COALESCE(b.q4_ritm,0)+COALESCE(b.q4_rinc,0)+COALESCE(b.q4_rseq,0) AS np4,
|
||
-- Q4 actual_quarter (без СПОД)
|
||
COALESCE(b.q4_m1,0)+COALESCE(b.q4_m2,0)+COALESCE(b.q4_m3,0) AS aq4_no_spod,
|
||
-- Q4 actual with СПОД
|
||
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 computed: corrected_plan Qn = new_plan + adj, rem/economy/total
|
||
final_input AS (
|
||
SELECT e.*,
|
||
-- Q2 corrected_plan
|
||
e.np2 + COALESCE(e.q2_ac,0)+COALESCE(e.q2_as,0)+COALESCE(e.q2_ar,0)+COALESCE(e.q2_arv,0) AS cp2,
|
||
-- Q3 corrected_plan
|
||
e.np3 + COALESCE(e.q3_ac,0)+COALESCE(e.q3_as,0)+COALESCE(e.q3_ar,0)+COALESCE(e.q3_arv,0) AS cp3,
|
||
-- Q4 corrected_plan
|
||
e.np4 + COALESCE(e.q4_ac,0)+COALESCE(e.q4_as,0)+COALESCE(e.q4_ar,0)+COALESCE(e.q4_arv,0) AS cp4
|
||
FROM enriched e
|
||
),
|
||
|
||
-- ═══ 5. Агрегаты для иерархии ═════════════════════════════════════════
|
||
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 ss1, SUM(sd.adj_q2) AS ss2, SUM(sd.adj_q3) AS ss3, SUM(sd.adj_q4) AS ss4,
|
||
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(ca.approved_amount) AS sca,
|
||
SUM(ck.ceiling_amount) AS sckc,
|
||
SUM(ck.expenses_q1) AS sck1, SUM(ck.expenses_q2) AS sck2, SUM(ck.expenses_q3) AS sck3, SUM(ck.expenses_q4) AS sck4,
|
||
SUM(cd.ceiling_amount) AS scdc,
|
||
SUM(cd.expenses_q1) AS scd1, SUM(cd.expenses_q2) AS scd2, SUM(cd.expenses_q3) AS scd3, SUM(cd.expenses_q4) AS scd4,
|
||
SUM(cs.total_amount) AS scst, SUM(cs.future_payments_y1) AS sfy1, SUM(cs.future_payments_y2) AS sfy2,
|
||
SUM(cs.other_ssp_amount) AS sossp
|
||
FROM budget_line bl
|
||
JOIN expense_item ei ON ei.id = bl.expense_item_id
|
||
LEFT JOIN plan p ON p.line_id = bl.id AND s_need_ap
|
||
LEFT JOIN contract_summary cs ON cs.line_id = bl.id AND s_cs
|
||
LEFT JOIN sequestration sd ON sd.line_id = bl.id AND sd.actor = 'DFIP' AND s_need_ap
|
||
LEFT JOIN reserve r ON r.line_id = bl.id AND s_need_ap
|
||
LEFT JOIN collegial_approval ca ON ca.line_id = bl.id AND s_ca
|
||
LEFT JOIN ckk ck ON ck.line_id = bl.id AND (s_ckk OR s_need_bk)
|
||
LEFT JOIN contract_detail cd ON cd.line_id = bl.id AND (s_cd OR s_need_bk)
|
||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||
AND (s_need_ap OR s_cs OR s_ca OR s_ckk OR s_cd OR s_need_bk)
|
||
GROUP BY bl.expense_item_id
|
||
),
|
||
-- Per-quarter hierarchy aggregates (no pivot — direct WHERE quarter=N)
|
||
aq1 AS (
|
||
SELECT bl.expense_item_id AS eid,
|
||
SUM(blq.adj_current) AS ac, SUM(blq.adj_ssp) AS as2, SUM(blq.adj_rf) AS ar, SUM(blq.adj_reserve) AS arv,
|
||
SUM(blq.payment_amount) AS pa, SUM(blq.payment_amount_ho) AS pho, SUM(blq.payment_amount_rf) AS prf,
|
||
SUM(blq.actual_m1) AS m1, SUM(blq.actual_m2) AS m2, SUM(blq.actual_m3) AS m3,
|
||
SUM(blq.transfer_to_q2) AS tq2, SUM(blq.transfer_to_q3) AS tq3, SUM(blq.transfer_to_q4) AS tq4
|
||
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
||
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id AND blq.quarter = 1
|
||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND s_q1 AND (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||
GROUP BY bl.expense_item_id
|
||
),
|
||
aq2 AS (
|
||
SELECT bl.expense_item_id AS eid,
|
||
SUM(blq.plan_revision_eco_change) AS reco, SUM(blq.plan_revision_item_adj) AS ritm,
|
||
SUM(blq.plan_revision_increase) AS rinc, SUM(blq.plan_revision_sequester) AS rseq,
|
||
SUM(blq.adj_current) AS ac, SUM(blq.adj_ssp) AS as2, SUM(blq.adj_rf) AS ar, SUM(blq.adj_reserve) AS arv,
|
||
SUM(blq.payment_amount) AS pa, SUM(blq.payment_amount_ho) AS pho, SUM(blq.payment_amount_rf) AS prf,
|
||
SUM(blq.actual_m1) AS m1, SUM(blq.actual_m2) AS m2, SUM(blq.actual_m3) AS m3,
|
||
SUM(blq.transfer_to_q3) AS tq3, SUM(blq.transfer_to_q4) AS tq4
|
||
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
||
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id AND blq.quarter = 2
|
||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND s_q2 AND (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||
GROUP BY bl.expense_item_id
|
||
),
|
||
aq3 AS (
|
||
SELECT bl.expense_item_id AS eid,
|
||
SUM(blq.plan_revision_eco_change) AS reco, SUM(blq.plan_revision_item_adj) AS ritm,
|
||
SUM(blq.plan_revision_increase) AS rinc, SUM(blq.plan_revision_sequester) AS rseq,
|
||
SUM(blq.adj_current) AS ac, SUM(blq.adj_ssp) AS as2, SUM(blq.adj_rf) AS ar, SUM(blq.adj_reserve) AS arv,
|
||
SUM(blq.payment_amount) AS pa, SUM(blq.payment_amount_ho) AS pho, SUM(blq.payment_amount_rf) AS prf,
|
||
SUM(blq.actual_m1) AS m1, SUM(blq.actual_m2) AS m2, SUM(blq.actual_m3) AS m3,
|
||
SUM(blq.transfer_to_q4) AS tq4
|
||
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
||
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id AND blq.quarter = 3
|
||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND s_q3 AND (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||
GROUP BY bl.expense_item_id
|
||
),
|
||
aq4 AS (
|
||
SELECT bl.expense_item_id AS eid,
|
||
SUM(blq.plan_revision_eco_change) AS reco, SUM(blq.plan_revision_item_adj) AS ritm,
|
||
SUM(blq.plan_revision_increase) AS rinc, SUM(blq.plan_revision_sequester) AS rseq,
|
||
SUM(blq.adj_current) AS ac, SUM(blq.adj_ssp) AS as2, SUM(blq.adj_rf) AS ar, SUM(blq.adj_reserve) AS arv,
|
||
SUM(blq.payment_amount) AS pa, SUM(blq.payment_amount_ho) AS pho, SUM(blq.payment_amount_rf) AS prf,
|
||
SUM(blq.actual_m1) AS m1, SUM(blq.actual_m2) AS m2, SUM(blq.actual_m3) AS m3,
|
||
SUM(blq.actual_spod) AS spod
|
||
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
||
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id AND blq.quarter = 4
|
||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND (s_q4 OR s_tot) AND (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||
GROUP BY bl.expense_item_id
|
||
),
|
||
atot AS (
|
||
SELECT bl.expense_item_id AS eid,
|
||
SUM(CASE WHEN blq.quarter=1 THEN COALESCE(blq.actual_m1,0)+COALESCE(blq.actual_m2,0)+COALESCE(blq.actual_m3,0) END) AS fq1,
|
||
SUM(CASE WHEN blq.quarter=2 THEN COALESCE(blq.actual_m1,0)+COALESCE(blq.actual_m2,0)+COALESCE(blq.actual_m3,0) END) AS fq2,
|
||
SUM(CASE WHEN blq.quarter=3 THEN COALESCE(blq.actual_m1,0)+COALESCE(blq.actual_m2,0)+COALESCE(blq.actual_m3,0) END) AS fq3,
|
||
SUM(CASE WHEN blq.quarter=4 THEN COALESCE(blq.actual_m1,0)+COALESCE(blq.actual_m2,0)+COALESCE(blq.actual_m3,0)+COALESCE(blq.actual_spod,0) END) AS fq4,
|
||
SUM(blq.payment_amount) AS pay
|
||
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
||
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id
|
||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND s_tot AND (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||
GROUP BY bl.expense_item_id
|
||
),
|
||
-- ═══ Pre-computed per-node aggregates (one pass over tw × agg) ═════════
|
||
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.ss1) AS ss1, SUM(a.ss2) AS ss2, SUM(a.ss3) AS ss3, SUM(a.ss4) AS ss4,
|
||
SUM(a.sr1) AS sr1, SUM(a.sr2) AS sr2, SUM(a.sr3) AS sr3, SUM(a.sr4) AS sr4,
|
||
SUM(a.sca) AS sca,
|
||
SUM(a.sckc) AS sckc, SUM(a.sck1) AS sck1, SUM(a.sck2) AS sck2, SUM(a.sck3) AS sck3, SUM(a.sck4) AS sck4,
|
||
SUM(a.scdc) AS scdc, SUM(a.scd1) AS scd1, SUM(a.scd2) AS scd2, SUM(a.scd3) AS scd3, SUM(a.scd4) AS scd4,
|
||
SUM(a.scst) AS scst, SUM(a.sfy1) AS sfy1, SUM(a.sfy2) AS sfy2, SUM(a.sossp) AS sossp,
|
||
SUM(CASE WHEN COALESCE(a.scd1,0)>0 THEN a.scd1 ELSE a.sck1 END) AS bk1,
|
||
SUM(CASE WHEN COALESCE(a.scd2,0)>0 THEN a.scd2 ELSE a.sck2 END) AS bk2,
|
||
SUM(CASE WHEN COALESCE(a.scd3,0)>0 THEN a.scd3 ELSE a.sck3 END) AS bk3,
|
||
SUM(CASE WHEN COALESCE(a.scd4,0)>0 THEN a.scd4 ELSE a.sck4 END) AS bk4
|
||
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.as2) AS as2, SUM(b.ar) AS ar, SUM(b.arv) AS arv,
|
||
SUM(b.pa) AS pa, SUM(b.pho) AS pho, SUM(b.prf) AS prf,
|
||
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
|
||
FROM tw LEFT JOIN aq1 b ON b.eid = ANY(tw.desc_ids) GROUP BY tw.id
|
||
),
|
||
tw_aq2 AS (
|
||
SELECT tw.id, SUM(b.reco) AS reco, SUM(b.ritm) AS ritm, SUM(b.rinc) AS rinc, SUM(b.rseq) AS rseq,
|
||
SUM(b.ac) AS ac, SUM(b.as2) AS as2, SUM(b.ar) AS ar, SUM(b.arv) AS arv,
|
||
SUM(b.pa) AS pa, SUM(b.pho) AS pho, SUM(b.prf) AS prf,
|
||
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
|
||
FROM tw LEFT JOIN aq2 b ON b.eid = ANY(tw.desc_ids) GROUP BY tw.id
|
||
),
|
||
tw_aq3 AS (
|
||
SELECT tw.id, SUM(b.reco) AS reco, SUM(b.ritm) AS ritm, SUM(b.rinc) AS rinc, SUM(b.rseq) AS rseq,
|
||
SUM(b.ac) AS ac, SUM(b.as2) AS as2, SUM(b.ar) AS ar, SUM(b.arv) AS arv,
|
||
SUM(b.pa) AS pa, SUM(b.pho) AS pho, SUM(b.prf) AS prf,
|
||
SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3,
|
||
SUM(b.tq4) AS tq4
|
||
FROM tw LEFT JOIN aq3 b ON b.eid = ANY(tw.desc_ids) GROUP BY tw.id
|
||
),
|
||
tw_aq4 AS (
|
||
SELECT tw.id, SUM(b.reco) AS reco, SUM(b.ritm) AS ritm, SUM(b.rinc) AS rinc, SUM(b.rseq) AS rseq,
|
||
SUM(b.ac) AS ac, SUM(b.as2) AS as2, SUM(b.ar) AS ar, SUM(b.arv) AS arv,
|
||
SUM(b.pa) AS pa, SUM(b.pho) AS pho, SUM(b.prf) AS prf,
|
||
SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3,
|
||
SUM(b.spod) AS spod
|
||
FROM tw LEFT JOIN aq4 b ON b.eid = ANY(tw.desc_ids) GROUP BY tw.id
|
||
),
|
||
tw_atot AS (
|
||
SELECT tw.id, SUM(b.fq1) AS fq1, SUM(b.fq2) AS fq2, SUM(b.fq3) AS fq3, SUM(b.fq4) AS fq4,
|
||
SUM(b.pay) AS pay
|
||
FROM tw LEFT JOIN atot b ON b.eid = ANY(tw.desc_ids) GROUP BY tw.id
|
||
)
|
||
|
||
-- ═══ ЧАСТЬ A: INPUT строки ════════════════════════════════════════════
|
||
SELECT * FROM (
|
||
SELECT
|
||
'INPUT'::VARCHAR, 3, f.lid,
|
||
v_year, f.sc, f.ic, f.ng, COALESCE(f.bname, f.ename), f.io,
|
||
CASE WHEN s_plan THEN f.pq1 END, CASE WHEN s_plan THEN f.pq2 END,
|
||
CASE WHEN s_plan THEN f.pq3 END, CASE WHEN s_plan THEN f.pq4 END,
|
||
CASE WHEN s_plan THEN f.pq1+f.pq2+f.pq3+f.pq4 END, CASE WHEN s_plan THEN f.pcmt END,
|
||
CASE WHEN s_cs THEN f.total_amount END, CASE WHEN s_cs THEN f.counterparty END,
|
||
CASE WHEN s_cs THEN f.deadline END, CASE WHEN s_cs THEN f.comment END,
|
||
CASE WHEN s_cs THEN f.future_payments_y1 END, CASE WHEN s_cs THEN f.future_payments_y2 END,
|
||
CASE WHEN s_cs THEN f.other_ssp_amount END,
|
||
CASE WHEN s_al THEN f.al_io END, CASE WHEN s_al THEN f.al_po END,
|
||
CASE WHEN s_seq THEN f.sq1 END, CASE WHEN s_seq THEN f.sq2 END,
|
||
CASE WHEN s_seq THEN f.sq3 END, CASE WHEN s_seq THEN f.sq4 END,
|
||
CASE WHEN s_seq THEN f.sq1+f.sq2+f.sq3+f.sq4 END, CASE WHEN s_seq THEN f.sjust END,
|
||
CASE WHEN s_res THEN f.rq1 END, CASE WHEN s_res THEN f.rq2 END,
|
||
CASE WHEN s_res THEN f.rq3 END, CASE WHEN s_res THEN f.rq4 END,
|
||
CASE WHEN s_res THEN f.rq1+f.rq2+f.rq3+f.rq4 END, CASE WHEN s_res THEN f.rjust END,
|
||
CASE WHEN s_appr THEN f.ap1 END, CASE WHEN s_appr THEN f.ap2 END,
|
||
CASE WHEN s_appr THEN f.ap3 END, CASE WHEN s_appr THEN f.ap4 END,
|
||
CASE WHEN s_appr THEN f.ap1+f.ap2+f.ap3+f.ap4 END,
|
||
CASE WHEN s_ca THEN f.ca_amt END, CASE WHEN s_ca THEN f.ca_prot END, CASE WHEN s_ca THEN f.ca_note END,
|
||
CASE WHEN s_ckk THEN f.ckk_ceil END, CASE WHEN s_ckk THEN f.ckk1 END, CASE WHEN s_ckk THEN f.ckk2 END,
|
||
CASE WHEN s_ckk THEN f.ckk3 END, CASE WHEN s_ckk THEN f.ckk4 END,
|
||
CASE WHEN s_ckk THEN f.ckk_rf END, CASE WHEN s_ckk THEN f.ckk_dl END,
|
||
CASE WHEN s_ckk THEN f.ckk_pp END, CASE WHEN s_ckk THEN f.ckk_pm END, CASE WHEN s_ckk THEN f.ckk_cmt END,
|
||
CASE WHEN s_cd THEN f.cd_cp END, CASE WHEN s_cd THEN f.cd_ref END, CASE WHEN s_cd THEN f.cd_add END,
|
||
CASE WHEN s_cd THEN f.cd_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.cdq1 END, CASE WHEN s_cd THEN f.cdq2 END,
|
||
CASE WHEN s_cd THEN f.cdq3 END, CASE WHEN s_cd THEN f.cdq4 END,
|
||
CASE WHEN s_cd THEN f.cd_rf END, CASE WHEN s_cd THEN f.cd_vat END,
|
||
CASE WHEN s_cd THEN f.cd_rate END, CASE WHEN s_cd THEN f.cd_fx 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_cmt2 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_ar 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 COALESCE(f.bk1,0) 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.bk1,0) END,
|
||
CASE WHEN s_q1 THEN f.cp1 - f.aq1 END,
|
||
CASE WHEN s_q1 THEN f.q1_tq2 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.cp1 - f.aq1 - COALESCE(f.q1_tq2,0) - COALESCE(f.q1_tq3,0) - COALESCE(f.q1_tq4,0) END,
|
||
CASE WHEN s_q1 THEN f.cp1 - f.aq1 END,
|
||
-- Q2
|
||
CASE WHEN s_q2 THEN f.q2_reco END, CASE WHEN s_q2 THEN f.q2_ritm 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_ar 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 COALESCE(f.bk2,0) 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.bk2,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_tq4 END,
|
||
CASE WHEN s_q2 THEN f.cp2 - f.aq2 - COALESCE(f.q2_tq3,0) - COALESCE(f.q2_tq4,0) END,
|
||
CASE WHEN s_q2 THEN f.cp2 - f.aq2 END,
|
||
-- Q3
|
||
CASE WHEN s_q3 THEN f.q3_reco END, CASE WHEN s_q3 THEN f.q3_ritm 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_ar 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 COALESCE(f.bk3,0) 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.bk3,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.cp3 - f.aq3 - COALESCE(f.q3_tq4,0) END,
|
||
CASE WHEN s_q3 THEN f.cp3 - f.aq3 END,
|
||
-- Q4
|
||
CASE WHEN s_q4 THEN f.q4_reco END, CASE WHEN s_q4 THEN f.q4_ritm 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_ar 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 COALESCE(f.bk4,0) 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.aq4_no_spod END,
|
||
CASE WHEN s_q4 THEN f.q4_spod END,
|
||
CASE WHEN s_q4 THEN f.cp4 - COALESCE(f.bk4,0) END,
|
||
CASE WHEN s_q4 THEN f.cp4 - f.aq4 END,
|
||
CASE WHEN s_q4 THEN f.cp4 - f.aq4 END,
|
||
CASE WHEN s_q4 THEN f.cp4 - f.aq4 END,
|
||
-- totals
|
||
CASE WHEN s_tot THEN COALESCE(f.tot_aq1,0)+COALESCE(f.tot_aq2,0)+COALESCE(f.tot_aq3,0)+COALESCE(f.tot_aq4,0) END,
|
||
CASE WHEN s_tot THEN COALESCE(f.tot_pa1,0)+COALESCE(f.tot_pa2,0)+COALESCE(f.tot_pa3,0)+COALESCE(f.tot_pa4,0) END,
|
||
f.tree_path || ARRAY[f.lid] 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,
|
||
v_year,
|
||
-- col_C = hierarchy code for depth 0-2, parent's code for depth 3 (SUB_ITEM)
|
||
CASE WHEN t.depth < 3 THEN t.item_id ELSE t.parent_item_id END,
|
||
-- col_D = NULL for hierarchy, R-code for SUB_ITEM
|
||
CASE WHEN t.depth = 3 THEN t.item_id ELSE NULL::VARCHAR END,
|
||
t.num_group_id, t.name, 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,
|
||
-- contract_summary
|
||
CASE WHEN s_cs THEN ta.scst END,
|
||
NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR,
|
||
CASE WHEN s_cs THEN ta.sfy1 END,
|
||
CASE WHEN s_cs THEN ta.sfy2 END,
|
||
CASE WHEN s_cs THEN ta.sossp END,
|
||
NULL::VARCHAR, NULL::VARCHAR,
|
||
-- sequestration
|
||
CASE WHEN s_seq THEN ta.ss1 END,
|
||
CASE WHEN s_seq THEN ta.ss2 END,
|
||
CASE WHEN s_seq THEN ta.ss3 END,
|
||
CASE WHEN s_seq THEN ta.ss4 END,
|
||
CASE WHEN s_seq THEN (COALESCE(ta.ss1,0)+COALESCE(ta.ss2,0)+COALESCE(ta.ss3,0)+COALESCE(ta.ss4,0)) END,
|
||
NULL::VARCHAR,
|
||
-- reserve
|
||
CASE WHEN s_res THEN ta.sr1 END,
|
||
CASE WHEN s_res THEN ta.sr2 END,
|
||
CASE WHEN s_res THEN ta.sr3 END,
|
||
CASE WHEN s_res THEN ta.sr4 END,
|
||
CASE WHEN s_res THEN (COALESCE(ta.sr1,0)+COALESCE(ta.sr2,0)+COALESCE(ta.sr3,0)+COALESCE(ta.sr4,0)) END,
|
||
NULL::VARCHAR,
|
||
-- approved
|
||
CASE WHEN s_appr THEN (COALESCE(ta.sp1,0)+COALESCE(ta.ss1,0)+COALESCE(ta.sr1,0)) END,
|
||
CASE WHEN s_appr THEN (COALESCE(ta.sp2,0)+COALESCE(ta.ss2,0)+COALESCE(ta.sr2,0)) END,
|
||
CASE WHEN s_appr THEN (COALESCE(ta.sp3,0)+COALESCE(ta.ss3,0)+COALESCE(ta.sr3,0)) END,
|
||
CASE WHEN s_appr THEN (COALESCE(ta.sp4,0)+COALESCE(ta.ss4,0)+COALESCE(ta.sr4,0)) END,
|
||
CASE WHEN s_appr THEN (COALESCE(ta.sp1,0)+COALESCE(ta.sp2,0)+COALESCE(ta.sp3,0)+COALESCE(ta.sp4,0)
|
||
+COALESCE(ta.ss1,0)+COALESCE(ta.ss2,0)+COALESCE(ta.ss3,0)+COALESCE(ta.ss4,0)
|
||
+COALESCE(ta.sr1,0)+COALESCE(ta.sr2,0)+COALESCE(ta.sr3,0)+COALESCE(ta.sr4,0)) END,
|
||
-- collegial
|
||
CASE WHEN s_ca THEN ta.sca END,
|
||
NULL::VARCHAR, NULL::VARCHAR,
|
||
-- ckk
|
||
CASE WHEN s_ckk THEN ta.sckc END,
|
||
CASE WHEN s_ckk THEN ta.sck1 END,
|
||
CASE WHEN s_ckk THEN ta.sck2 END,
|
||
CASE WHEN s_ckk THEN ta.sck3 END,
|
||
CASE WHEN s_ckk THEN ta.sck4 END,
|
||
NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR,
|
||
-- contract_detail
|
||
NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR,
|
||
CASE WHEN s_cd THEN ta.scdc END,
|
||
CASE WHEN s_cd THEN ta.scd1 END,
|
||
CASE WHEN s_cd THEN ta.scd2 END,
|
||
CASE WHEN s_cd THEN ta.scd3 END,
|
||
CASE WHEN s_cd THEN ta.scd4 END,
|
||
NULL::VARCHAR, NULL::VARCHAR, NULL::NUMERIC, NULL::NUMERIC, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR, NULL::VARCHAR,
|
||
-- Q1 hierarchy (from aq1)
|
||
CASE WHEN s_q1 THEN tb1.ac END,
|
||
CASE WHEN s_q1 THEN tb1.as2 END,
|
||
CASE WHEN s_q1 THEN tb1.ar END,
|
||
CASE WHEN s_q1 THEN tb1.arv END,
|
||
NULL::VARCHAR,
|
||
-- corrected_plan Q1 = approved_q1 + SUM(adj_*)
|
||
CASE WHEN s_q1 THEN
|
||
(COALESCE(ta.sp1,0)+COALESCE(ta.ss1,0)+COALESCE(ta.sr1,0))
|
||
+ COALESCE((COALESCE(tb1.ac,0)+COALESCE(tb1.as2,0)+COALESCE(tb1.ar,0)+COALESCE(tb1.arv,0)), 0)
|
||
END,
|
||
NULL::DATE,
|
||
CASE WHEN s_q1 THEN tb1.pa END,
|
||
CASE WHEN s_q1 THEN tb1.pho END,
|
||
CASE WHEN s_q1 THEN tb1.prf END,
|
||
NULL::VARCHAR, NULL::VARCHAR,
|
||
-- booking Q1 = SUM(CASE cd>0 THEN cd ELSE ckk)
|
||
CASE WHEN s_q1 THEN ta.bk1 END,
|
||
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,
|
||
-- rem_booking Q1 = corrected_plan - booking (ckk or cd)
|
||
CASE WHEN s_q1 THEN
|
||
(COALESCE(ta.sp1,0)+COALESCE(ta.ss1,0)+COALESCE(ta.sr1,0))
|
||
+ COALESCE((COALESCE(tb1.ac,0)+COALESCE(tb1.as2,0)+COALESCE(tb1.ar,0)+COALESCE(tb1.arv,0)),0)
|
||
- COALESCE(ta.bk1,0)
|
||
END,
|
||
-- rem_actual Q1 = corrected_plan - actual_quarter
|
||
CASE WHEN s_q1 THEN
|
||
(COALESCE(ta.sp1,0)+COALESCE(ta.ss1,0)+COALESCE(ta.sr1,0))
|
||
+ COALESCE((COALESCE(tb1.ac,0)+COALESCE(tb1.as2,0)+COALESCE(tb1.ar,0)+COALESCE(tb1.arv,0)),0)
|
||
- COALESCE((COALESCE(tb1.m1,0)+COALESCE(tb1.m2,0)+COALESCE(tb1.m3,0)),0)
|
||
END,
|
||
CASE WHEN s_q1 THEN tb1.tq2 END,
|
||
CASE WHEN s_q1 THEN tb1.tq3 END,
|
||
CASE WHEN s_q1 THEN tb1.tq4 END,
|
||
-- economy Q1 = rem_actual - transfers
|
||
CASE WHEN s_q1 THEN
|
||
(COALESCE(ta.sp1,0)+COALESCE(ta.ss1,0)+COALESCE(ta.sr1,0))
|
||
+ COALESCE((COALESCE(tb1.ac,0)+COALESCE(tb1.as2,0)+COALESCE(tb1.ar,0)+COALESCE(tb1.arv,0)),0)
|
||
- COALESCE((COALESCE(tb1.m1,0)+COALESCE(tb1.m2,0)+COALESCE(tb1.m3,0)),0)
|
||
- COALESCE((COALESCE(tb1.tq2,0)+COALESCE(tb1.tq3,0)+COALESCE(tb1.tq4,0)),0)
|
||
END,
|
||
-- total Q1 = rem_actual
|
||
CASE WHEN s_q1 THEN
|
||
(COALESCE(ta.sp1,0)+COALESCE(ta.ss1,0)+COALESCE(ta.sr1,0))
|
||
+ COALESCE((COALESCE(tb1.ac,0)+COALESCE(tb1.as2,0)+COALESCE(tb1.ar,0)+COALESCE(tb1.arv,0)),0)
|
||
- COALESCE((COALESCE(tb1.m1,0)+COALESCE(tb1.m2,0)+COALESCE(tb1.m3,0)),0)
|
||
END,
|
||
-- Q2 hierarchy (from aq2)
|
||
CASE WHEN s_q2 THEN tb2.reco END,
|
||
CASE WHEN s_q2 THEN tb2.ritm END,
|
||
CASE WHEN s_q2 THEN tb2.rinc END,
|
||
CASE WHEN s_q2 THEN tb2.rseq END,
|
||
NULL::VARCHAR,
|
||
-- new_plan Q2 = approved_q2 + transfer_q1→q2 + revisions
|
||
CASE WHEN s_q2 THEN
|
||
(COALESCE(ta.sp2,0)+COALESCE(ta.ss2,0)+COALESCE(ta.sr2,0))
|
||
+ COALESCE(tb1.tq2, 0)
|
||
+ COALESCE((COALESCE(tb2.reco,0)+COALESCE(tb2.ritm,0)+COALESCE(tb2.rinc,0)+COALESCE(tb2.rseq,0)), 0)
|
||
END,
|
||
CASE WHEN s_q2 THEN tb2.ac END,
|
||
CASE WHEN s_q2 THEN tb2.as2 END,
|
||
CASE WHEN s_q2 THEN tb2.ar END,
|
||
CASE WHEN s_q2 THEN tb2.arv END,
|
||
NULL::VARCHAR,
|
||
-- corrected_plan Q2 = new_plan_q2 + adj_*
|
||
CASE WHEN s_q2 THEN
|
||
(COALESCE(ta.sp2,0)+COALESCE(ta.ss2,0)+COALESCE(ta.sr2,0))
|
||
+ COALESCE(tb1.tq2, 0)
|
||
+ COALESCE((COALESCE(tb2.reco,0)+COALESCE(tb2.ritm,0)+COALESCE(tb2.rinc,0)+COALESCE(tb2.rseq,0)+COALESCE(tb2.ac,0)+COALESCE(tb2.as2,0)+COALESCE(tb2.ar,0)+COALESCE(tb2.arv,0)), 0)
|
||
END,
|
||
NULL::DATE,
|
||
CASE WHEN s_q2 THEN tb2.pa END,
|
||
CASE WHEN s_q2 THEN tb2.pho END,
|
||
CASE WHEN s_q2 THEN tb2.prf END,
|
||
NULL::VARCHAR, NULL::VARCHAR,
|
||
CASE WHEN s_q2 THEN ta.bk2 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,
|
||
-- rem_booking Q2
|
||
CASE WHEN s_q2 THEN
|
||
(COALESCE(ta.sp2,0)+COALESCE(ta.ss2,0)+COALESCE(ta.sr2,0))
|
||
+ COALESCE(tb1.tq2,0)
|
||
+ COALESCE((COALESCE(tb2.reco,0)+COALESCE(tb2.ritm,0)+COALESCE(tb2.rinc,0)+COALESCE(tb2.rseq,0)+COALESCE(tb2.ac,0)+COALESCE(tb2.as2,0)+COALESCE(tb2.ar,0)+COALESCE(tb2.arv,0)),0)
|
||
- COALESCE(ta.bk2,0)
|
||
END,
|
||
-- rem_actual Q2
|
||
CASE WHEN s_q2 THEN
|
||
(COALESCE(ta.sp2,0)+COALESCE(ta.ss2,0)+COALESCE(ta.sr2,0))
|
||
+ COALESCE(tb1.tq2,0)
|
||
+ COALESCE((COALESCE(tb2.reco,0)+COALESCE(tb2.ritm,0)+COALESCE(tb2.rinc,0)+COALESCE(tb2.rseq,0)+COALESCE(tb2.ac,0)+COALESCE(tb2.as2,0)+COALESCE(tb2.ar,0)+COALESCE(tb2.arv,0)),0)
|
||
- COALESCE((COALESCE(tb2.m1,0)+COALESCE(tb2.m2,0)+COALESCE(tb2.m3,0)),0)
|
||
END,
|
||
CASE WHEN s_q2 THEN tb2.tq3 END,
|
||
CASE WHEN s_q2 THEN tb2.tq4 END,
|
||
-- economy Q2
|
||
CASE WHEN s_q2 THEN
|
||
(COALESCE(ta.sp2,0)+COALESCE(ta.ss2,0)+COALESCE(ta.sr2,0))
|
||
+ COALESCE(tb1.tq2,0)
|
||
+ COALESCE((COALESCE(tb2.reco,0)+COALESCE(tb2.ritm,0)+COALESCE(tb2.rinc,0)+COALESCE(tb2.rseq,0)+COALESCE(tb2.ac,0)+COALESCE(tb2.as2,0)+COALESCE(tb2.ar,0)+COALESCE(tb2.arv,0)),0)
|
||
- COALESCE((COALESCE(tb2.m1,0)+COALESCE(tb2.m2,0)+COALESCE(tb2.m3,0)),0)
|
||
- COALESCE((COALESCE(tb2.tq3,0)+COALESCE(tb2.tq4,0)),0)
|
||
END,
|
||
-- total Q2 = rem_actual
|
||
CASE WHEN s_q2 THEN
|
||
(COALESCE(ta.sp2,0)+COALESCE(ta.ss2,0)+COALESCE(ta.sr2,0))
|
||
+ COALESCE(tb1.tq2,0)
|
||
+ COALESCE((COALESCE(tb2.reco,0)+COALESCE(tb2.ritm,0)+COALESCE(tb2.rinc,0)+COALESCE(tb2.rseq,0)+COALESCE(tb2.ac,0)+COALESCE(tb2.as2,0)+COALESCE(tb2.ar,0)+COALESCE(tb2.arv,0)),0)
|
||
- COALESCE((COALESCE(tb2.m1,0)+COALESCE(tb2.m2,0)+COALESCE(tb2.m3,0)),0)
|
||
END,
|
||
-- Q3 hierarchy (from aq3)
|
||
CASE WHEN s_q3 THEN tb3.reco END,
|
||
CASE WHEN s_q3 THEN tb3.ritm END,
|
||
CASE WHEN s_q3 THEN tb3.rinc END,
|
||
CASE WHEN s_q3 THEN tb3.rseq END,
|
||
NULL::VARCHAR,
|
||
-- new_plan Q3 = approved_q3 + transfer_q1→q3 + transfer_q2→q3 + revisions
|
||
CASE WHEN s_q3 THEN
|
||
(COALESCE(ta.sp3,0)+COALESCE(ta.ss3,0)+COALESCE(ta.sr3,0))
|
||
+ COALESCE(tb1.tq3, 0)
|
||
+ COALESCE(tb2.tq3, 0)
|
||
+ COALESCE((COALESCE(tb3.reco,0)+COALESCE(tb3.ritm,0)+COALESCE(tb3.rinc,0)+COALESCE(tb3.rseq,0)), 0)
|
||
END,
|
||
CASE WHEN s_q3 THEN tb3.ac END,
|
||
CASE WHEN s_q3 THEN tb3.as2 END,
|
||
CASE WHEN s_q3 THEN tb3.ar END,
|
||
CASE WHEN s_q3 THEN tb3.arv END,
|
||
NULL::VARCHAR,
|
||
-- corrected_plan Q3 = new_plan + adj_*
|
||
CASE WHEN s_q3 THEN
|
||
(COALESCE(ta.sp3,0)+COALESCE(ta.ss3,0)+COALESCE(ta.sr3,0))
|
||
+ COALESCE(tb1.tq3, 0)
|
||
+ COALESCE(tb2.tq3, 0)
|
||
+ COALESCE((COALESCE(tb3.reco,0)+COALESCE(tb3.ritm,0)+COALESCE(tb3.rinc,0)+COALESCE(tb3.rseq,0)+COALESCE(tb3.ac,0)+COALESCE(tb3.as2,0)+COALESCE(tb3.ar,0)+COALESCE(tb3.arv,0)), 0)
|
||
END,
|
||
NULL::DATE,
|
||
CASE WHEN s_q3 THEN tb3.pa END,
|
||
CASE WHEN s_q3 THEN tb3.pho END,
|
||
CASE WHEN s_q3 THEN tb3.prf END,
|
||
NULL::VARCHAR, NULL::VARCHAR,
|
||
CASE WHEN s_q3 THEN ta.bk3 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,
|
||
-- rem_booking Q3
|
||
CASE WHEN s_q3 THEN
|
||
(COALESCE(ta.sp3,0)+COALESCE(ta.ss3,0)+COALESCE(ta.sr3,0))
|
||
+ COALESCE(tb1.tq3,0)
|
||
+ COALESCE(tb2.tq3,0)
|
||
+ COALESCE((COALESCE(tb3.reco,0)+COALESCE(tb3.ritm,0)+COALESCE(tb3.rinc,0)+COALESCE(tb3.rseq,0)+COALESCE(tb3.ac,0)+COALESCE(tb3.as2,0)+COALESCE(tb3.ar,0)+COALESCE(tb3.arv,0)),0)
|
||
- COALESCE(ta.bk3,0)
|
||
END,
|
||
-- rem_actual Q3
|
||
CASE WHEN s_q3 THEN
|
||
(COALESCE(ta.sp3,0)+COALESCE(ta.ss3,0)+COALESCE(ta.sr3,0))
|
||
+ COALESCE(tb1.tq3,0)
|
||
+ COALESCE(tb2.tq3,0)
|
||
+ COALESCE((COALESCE(tb3.reco,0)+COALESCE(tb3.ritm,0)+COALESCE(tb3.rinc,0)+COALESCE(tb3.rseq,0)+COALESCE(tb3.ac,0)+COALESCE(tb3.as2,0)+COALESCE(tb3.ar,0)+COALESCE(tb3.arv,0)),0)
|
||
- COALESCE((COALESCE(tb3.m1,0)+COALESCE(tb3.m2,0)+COALESCE(tb3.m3,0)),0)
|
||
END,
|
||
CASE WHEN s_q3 THEN tb3.tq4 END,
|
||
-- economy Q3
|
||
CASE WHEN s_q3 THEN
|
||
(COALESCE(ta.sp3,0)+COALESCE(ta.ss3,0)+COALESCE(ta.sr3,0))
|
||
+ COALESCE(tb1.tq3,0)
|
||
+ COALESCE(tb2.tq3,0)
|
||
+ COALESCE((COALESCE(tb3.reco,0)+COALESCE(tb3.ritm,0)+COALESCE(tb3.rinc,0)+COALESCE(tb3.rseq,0)+COALESCE(tb3.ac,0)+COALESCE(tb3.as2,0)+COALESCE(tb3.ar,0)+COALESCE(tb3.arv,0)),0)
|
||
- COALESCE((COALESCE(tb3.m1,0)+COALESCE(tb3.m2,0)+COALESCE(tb3.m3,0)),0)
|
||
- COALESCE(COALESCE(tb3.tq4,0),0)
|
||
END,
|
||
-- total Q3 = rem_actual
|
||
CASE WHEN s_q3 THEN
|
||
(COALESCE(ta.sp3,0)+COALESCE(ta.ss3,0)+COALESCE(ta.sr3,0))
|
||
+ COALESCE(tb1.tq3,0)
|
||
+ COALESCE(tb2.tq3,0)
|
||
+ COALESCE((COALESCE(tb3.reco,0)+COALESCE(tb3.ritm,0)+COALESCE(tb3.rinc,0)+COALESCE(tb3.rseq,0)+COALESCE(tb3.ac,0)+COALESCE(tb3.as2,0)+COALESCE(tb3.ar,0)+COALESCE(tb3.arv,0)),0)
|
||
- COALESCE((COALESCE(tb3.m1,0)+COALESCE(tb3.m2,0)+COALESCE(tb3.m3,0)),0)
|
||
END,
|
||
-- Q4 hierarchy (from aq4)
|
||
CASE WHEN s_q4 THEN tb4.reco END,
|
||
CASE WHEN s_q4 THEN tb4.ritm END,
|
||
CASE WHEN s_q4 THEN tb4.rinc END,
|
||
CASE WHEN s_q4 THEN tb4.rseq END,
|
||
NULL::VARCHAR,
|
||
-- new_plan Q4 = approved_q4 + transfers_q1→q4 + q2→q4 + q3→q4 + revisions
|
||
CASE WHEN s_q4 THEN
|
||
(COALESCE(ta.sp4,0)+COALESCE(ta.ss4,0)+COALESCE(ta.sr4,0))
|
||
+ COALESCE(tb1.tq4, 0)
|
||
+ COALESCE(tb2.tq4, 0)
|
||
+ COALESCE(tb3.tq4, 0)
|
||
+ COALESCE((COALESCE(tb4.reco,0)+COALESCE(tb4.ritm,0)+COALESCE(tb4.rinc,0)+COALESCE(tb4.rseq,0)), 0)
|
||
END,
|
||
CASE WHEN s_q4 THEN tb4.ac END,
|
||
CASE WHEN s_q4 THEN tb4.as2 END,
|
||
CASE WHEN s_q4 THEN tb4.ar END,
|
||
CASE WHEN s_q4 THEN tb4.arv END,
|
||
NULL::VARCHAR,
|
||
-- corrected_plan Q4 = new_plan + adj_*
|
||
CASE WHEN s_q4 THEN
|
||
(COALESCE(ta.sp4,0)+COALESCE(ta.ss4,0)+COALESCE(ta.sr4,0))
|
||
+ COALESCE(tb1.tq4, 0)
|
||
+ COALESCE(tb2.tq4, 0)
|
||
+ COALESCE(tb3.tq4, 0)
|
||
+ COALESCE((COALESCE(tb4.reco,0)+COALESCE(tb4.ritm,0)+COALESCE(tb4.rinc,0)+COALESCE(tb4.rseq,0)+COALESCE(tb4.ac,0)+COALESCE(tb4.as2,0)+COALESCE(tb4.ar,0)+COALESCE(tb4.arv,0)), 0)
|
||
END,
|
||
NULL::DATE,
|
||
CASE WHEN s_q4 THEN tb4.pa END,
|
||
CASE WHEN s_q4 THEN tb4.pho END,
|
||
CASE WHEN s_q4 THEN tb4.prf END,
|
||
NULL::VARCHAR, NULL::VARCHAR,
|
||
CASE WHEN s_q4 THEN ta.bk4 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 (COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)) END,
|
||
CASE WHEN s_q4 THEN tb4.spod END,
|
||
-- rem_booking Q4
|
||
CASE WHEN s_q4 THEN
|
||
(COALESCE(ta.sp4,0)+COALESCE(ta.ss4,0)+COALESCE(ta.sr4,0))
|
||
+ COALESCE(tb1.tq4,0)
|
||
+ COALESCE(tb2.tq4,0)
|
||
+ COALESCE(tb3.tq4,0)
|
||
+ COALESCE((COALESCE(tb4.reco,0)+COALESCE(tb4.ritm,0)+COALESCE(tb4.rinc,0)+COALESCE(tb4.rseq,0)+COALESCE(tb4.ac,0)+COALESCE(tb4.as2,0)+COALESCE(tb4.ar,0)+COALESCE(tb4.arv,0)),0)
|
||
- COALESCE(ta.bk4,0)
|
||
END,
|
||
-- rem_actual Q4 (with spod)
|
||
CASE WHEN s_q4 THEN
|
||
(COALESCE(ta.sp4,0)+COALESCE(ta.ss4,0)+COALESCE(ta.sr4,0))
|
||
+ COALESCE(tb1.tq4,0)
|
||
+ COALESCE(tb2.tq4,0)
|
||
+ COALESCE(tb3.tq4,0)
|
||
+ COALESCE((COALESCE(tb4.reco,0)+COALESCE(tb4.ritm,0)+COALESCE(tb4.rinc,0)+COALESCE(tb4.rseq,0)+COALESCE(tb4.ac,0)+COALESCE(tb4.as2,0)+COALESCE(tb4.ar,0)+COALESCE(tb4.arv,0)),0)
|
||
- COALESCE((COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0)),0)
|
||
END,
|
||
-- economy Q4 = rem_actual (no transfers from Q4)
|
||
CASE WHEN s_q4 THEN
|
||
(COALESCE(ta.sp4,0)+COALESCE(ta.ss4,0)+COALESCE(ta.sr4,0))
|
||
+ COALESCE(tb1.tq4,0)
|
||
+ COALESCE(tb2.tq4,0)
|
||
+ COALESCE(tb3.tq4,0)
|
||
+ COALESCE((COALESCE(tb4.reco,0)+COALESCE(tb4.ritm,0)+COALESCE(tb4.rinc,0)+COALESCE(tb4.rseq,0)+COALESCE(tb4.ac,0)+COALESCE(tb4.as2,0)+COALESCE(tb4.ar,0)+COALESCE(tb4.arv,0)),0)
|
||
- COALESCE((COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0)),0)
|
||
END,
|
||
-- total Q4 = rem_actual
|
||
CASE WHEN s_q4 THEN
|
||
(COALESCE(ta.sp4,0)+COALESCE(ta.ss4,0)+COALESCE(ta.sr4,0))
|
||
+ COALESCE(tb1.tq4,0)
|
||
+ COALESCE(tb2.tq4,0)
|
||
+ COALESCE(tb3.tq4,0)
|
||
+ COALESCE((COALESCE(tb4.reco,0)+COALESCE(tb4.ritm,0)+COALESCE(tb4.rinc,0)+COALESCE(tb4.rseq,0)+COALESCE(tb4.ac,0)+COALESCE(tb4.as2,0)+COALESCE(tb4.ar,0)+COALESCE(tb4.arv,0)),0)
|
||
- COALESCE((COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0)),0)
|
||
END,
|
||
-- totals hierarchy (from atot)
|
||
CASE WHEN s_tot THEN (COALESCE(tt.fq1,0)+COALESCE(tt.fq2,0)+COALESCE(tt.fq3,0)+COALESCE(tt.fq4,0)) END,
|
||
CASE WHEN s_tot THEN tt.pay END,
|
||
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
|
||
LEFT JOIN tw_atot tt ON tt.id = t.id
|
||
) sub
|
||
ORDER BY sub._sort;
|
||
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form1_sheet_jsonb(int4, varchar, varchar, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form1_sheet_jsonb(p_form_id integer, p_sheet character varying, p_direction character varying DEFAULT NULL::character varying, p_sections text[] DEFAULT NULL::text[])
|
||
RETURNS TABLE(row_type character varying, depth integer, line_id integer, header jsonb, plan_data jsonb, contract_summary jsonb, allocation_data jsonb, sequestration_data jsonb, reserve_data jsonb, approved_data jsonb, collegial_data jsonb, ckk_data jsonb, contract_detail jsonb, q1_data jsonb, q2_data jsonb, q3_data jsonb, q4_data jsonb, totals_data jsonb, _sort_path integer[])
|
||
LANGUAGE sql
|
||
STABLE
|
||
SET search_path TO 'v3', 'pg_catalog'
|
||
AS $function$
|
||
WITH flags AS (
|
||
SELECT
|
||
(p_sections IS NULL OR 'plan' = ANY(p_sections)) AS s_plan,
|
||
(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 'sequestration' = ANY(p_sections)) AS s_seq,
|
||
(p_sections IS NULL OR 'reserve' = ANY(p_sections)) AS s_res,
|
||
(p_sections IS NULL OR 'approved' = ANY(p_sections)) AS s_appr,
|
||
(p_sections IS NULL OR 'collegial' = ANY(p_sections)) AS s_ca,
|
||
(p_sections IS NULL OR 'ckk' = ANY(p_sections)) AS s_ckk,
|
||
(p_sections IS NULL OR 'contract_detail' = ANY(p_sections)) AS s_cd,
|
||
(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:
|
||
-- для INPUT путь = {...ei_id, line_id} → предпоследний элемент
|
||
-- для иерархии путь = {...ei_id} → последний элемент
|
||
jsonb_build_object(
|
||
'year', s.col_B_year,
|
||
'section', s.col_C_section,
|
||
'item_id', s.col_D_item_id,
|
||
'num_group', s.col_E_num_group,
|
||
'name', s.col_F_name,
|
||
'internal_order', s.col_G_internal_order,
|
||
'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_H_plan_q1,
|
||
'q2', s.col_I_plan_q2,
|
||
'q3', s.col_J_plan_q3,
|
||
'q4', s.col_K_plan_q4,
|
||
'year', s.col_L_plan_year,
|
||
'comment', s.col_M_plan_comment
|
||
) END AS plan_data,
|
||
|
||
CASE WHEN f.s_cs THEN jsonb_build_object(
|
||
'total', s.col_N_cs_total,
|
||
'counterparty', s.col_O_cs_counterparty,
|
||
'deadline', s.col_P_cs_deadline,
|
||
'comment', s.col_Q_cs_comment,
|
||
'future_y1', s.col_R_cs_future_y1,
|
||
'future_y2', s.col_S_cs_future_y2,
|
||
'other_ssp', s.col_T_cs_other_ssp
|
||
) END AS contract_summary,
|
||
|
||
CASE WHEN f.s_al THEN jsonb_build_object(
|
||
'order', s.col_V_al_order,
|
||
'property', s.col_W_al_property
|
||
) END AS allocation_data,
|
||
|
||
CASE WHEN f.s_seq THEN jsonb_build_object(
|
||
'q1', s.col_Y_seq_q1,
|
||
'q2', s.col_Z_seq_q2,
|
||
'q3', s.col_AA_seq_q3,
|
||
'q4', s.col_AB_seq_q4,
|
||
'year', s.col_AC_seq_year,
|
||
'justification', s.col_AD_seq_justification
|
||
) END AS sequestration_data,
|
||
|
||
CASE WHEN f.s_res THEN jsonb_build_object(
|
||
'q1', s.col_AF_res_q1,
|
||
'q2', s.col_AG_res_q2,
|
||
'q3', s.col_AH_res_q3,
|
||
'q4', s.col_AI_res_q4,
|
||
'year', s.col_AJ_res_year,
|
||
'justification', s.col_AK_res_justification
|
||
) END AS reserve_data,
|
||
|
||
CASE WHEN f.s_appr THEN jsonb_build_object(
|
||
'q1', s.col_AM_approved_q1,
|
||
'q2', s.col_AN_approved_q2,
|
||
'q3', s.col_AO_approved_q3,
|
||
'q4', s.col_AP_approved_q4,
|
||
'year', s.col_AQ_approved_year
|
||
) END AS approved_data,
|
||
|
||
CASE WHEN f.s_ca THEN jsonb_build_object(
|
||
'approved', s.col_AS_ca_approved,
|
||
'protocol', s.col_AT_ca_protocol,
|
||
'note', s.col_AU_ca_note
|
||
) END AS collegial_data,
|
||
|
||
CASE WHEN f.s_ckk THEN jsonb_build_object(
|
||
'ceiling', s.col_AW_ckk_ceiling,
|
||
'q1', s.col_AX_ckk_exp_q1,
|
||
'q2', s.col_AY_ckk_exp_q2,
|
||
'q3', s.col_AZ_ckk_exp_q3,
|
||
'q4', s.col_BA_ckk_exp_q4,
|
||
'rf_schedule', s.col_BB_ckk_rf_sched,
|
||
'deadline', s.col_BC_ckk_deadline,
|
||
'proc_plan', s.col_BD_ckk_proc_plan,
|
||
'proc_method', s.col_BE_ckk_proc_meth,
|
||
'comment', s.col_BF_ckk_comment
|
||
) END AS ckk_data,
|
||
|
||
CASE WHEN f.s_cd THEN jsonb_build_object(
|
||
'counterparty', s.col_BH_cd_counterparty,
|
||
'reference', s.col_BI_cd_reference,
|
||
'addenda', s.col_BJ_cd_addenda,
|
||
'subject', s.col_BK_cd_subject,
|
||
'currency', s.col_BL_cd_currency,
|
||
'ceiling', s.col_BM_cd_ceiling,
|
||
'q1', s.col_BN_cd_exp_q1,
|
||
'q2', s.col_BO_cd_exp_q2,
|
||
'q3', s.col_BP_cd_exp_q3,
|
||
'q4', s.col_BQ_cd_exp_q4,
|
||
'rf_schedule', s.col_BR_cd_rf_sched,
|
||
'vat_rate', s.col_BS_cd_vat,
|
||
'exchange_rate', s.col_BT_cd_rate,
|
||
'amount_foreign', s.col_BU_cd_amount_fx,
|
||
'deadline', s.col_BV_cd_deadline,
|
||
'payment_scheme', s.col_BW_cd_scheme,
|
||
'act', s.col_BX_cd_act,
|
||
'comment', s.col_BY_cd_comment
|
||
) END AS contract_detail,
|
||
|
||
CASE WHEN f.s_q1 THEN jsonb_build_object(
|
||
'adj_current', s.col_CA_q1_adj_current,
|
||
'adj_ssp', s.col_CB_q1_adj_ssp,
|
||
'adj_rf', s.col_CC_q1_adj_rf,
|
||
'adj_reserve', s.col_CD_q1_adj_reserve,
|
||
'adj_comment', s.col_CE_q1_adj_comment,
|
||
'corrected_plan', s.col_CF_q1_corrected_plan,
|
||
'pay_date', s.col_CH_q1_pay_date,
|
||
'pay_amount', s.col_CI_q1_pay_amount,
|
||
'pay_ho', s.col_CJ_q1_pay_ho,
|
||
'pay_rf', s.col_CK_q1_pay_rf,
|
||
'pay_comment', s.col_CL_q1_pay_comment,
|
||
'pay_act', s.col_CM_q1_pay_act,
|
||
'booking', s.col_CO_q1_booking,
|
||
'actual_m1', s.col_CP_q1_actual_m1,
|
||
'actual_m2', s.col_CQ_q1_actual_m2,
|
||
'actual_m3', s.col_CR_q1_actual_m3,
|
||
'actual_quarter', s.col_CS_q1_actual_quarter,
|
||
'rem_booking', s.col_CT_q1_rem_booking,
|
||
'rem_actual', s.col_CU_q1_rem_actual,
|
||
'transfer_q2', s.col_CW_q1_trans_q2,
|
||
'transfer_q3', s.col_CX_q1_trans_q3,
|
||
'transfer_q4', s.col_CY_q1_trans_q4,
|
||
'economy', s.col_CZ_q1_economy,
|
||
'total', s.col_DA_q1_total
|
||
) END AS q1_data,
|
||
|
||
CASE WHEN f.s_q2 THEN jsonb_build_object(
|
||
'rev_eco', s.col_DC_q2_rev_eco,
|
||
'rev_item', s.col_DD_q2_rev_item,
|
||
'rev_inc', s.col_DE_q2_rev_inc,
|
||
'rev_seq', s.col_DF_q2_rev_seq,
|
||
'rev_comment', s.col_DG_q2_rev_comment,
|
||
'new_plan', s.col_DH_q2_new_plan,
|
||
'adj_current', s.col_DJ_q2_adj_current,
|
||
'adj_ssp', s.col_DK_q2_adj_ssp,
|
||
'adj_rf', s.col_DL_q2_adj_rf,
|
||
'adj_reserve', s.col_DM_q2_adj_reserve,
|
||
'adj_comment', s.col_DN_q2_adj_comment,
|
||
'corrected_plan', s.col_DO_q2_corrected_plan,
|
||
'pay_date', s.col_DQ_q2_pay_date,
|
||
'pay_amount', s.col_DR_q2_pay_amount,
|
||
'pay_ho', s.col_DS_q2_pay_ho,
|
||
'pay_rf', s.col_DT_q2_pay_rf,
|
||
'pay_comment', s.col_DU_q2_pay_comment,
|
||
'pay_act', s.col_DV_q2_pay_act,
|
||
'booking', s.col_DX_q2_booking,
|
||
'actual_m1', s.col_DY_q2_actual_m1,
|
||
'actual_m2', s.col_DZ_q2_actual_m2,
|
||
'actual_m3', s.col_EA_q2_actual_m3,
|
||
'actual_quarter', s.col_EB_q2_actual_quarter,
|
||
'rem_booking', s.col_EC_q2_rem_booking,
|
||
'rem_actual', s.col_ED_q2_rem_actual,
|
||
'transfer_q3', s.col_EF_q2_trans_q3,
|
||
'transfer_q4', s.col_EG_q2_trans_q4,
|
||
'economy', s.col_EH_q2_economy,
|
||
'total', s.col_EI_q2_total
|
||
) END AS q2_data,
|
||
|
||
CASE WHEN f.s_q3 THEN jsonb_build_object(
|
||
'rev_eco', s.col_EK_q3_rev_eco,
|
||
'rev_item', s.col_EL_q3_rev_item,
|
||
'rev_inc', s.col_EM_q3_rev_inc,
|
||
'rev_seq', s.col_EN_q3_rev_seq,
|
||
'rev_comment', s.col_EO_q3_rev_comment,
|
||
'new_plan', s.col_EP_q3_new_plan,
|
||
'adj_current', s.col_ER_q3_adj_current,
|
||
'adj_ssp', s.col_ES_q3_adj_ssp,
|
||
'adj_rf', s.col_ET_q3_adj_rf,
|
||
'adj_reserve', s.col_EU_q3_adj_reserve,
|
||
'adj_comment', s.col_EV_q3_adj_comment,
|
||
'corrected_plan', s.col_EW_q3_corrected_plan,
|
||
'pay_date', s.col_EY_q3_pay_date,
|
||
'pay_amount', s.col_EZ_q3_pay_amount,
|
||
'pay_ho', s.col_FA_q3_pay_ho,
|
||
'pay_rf', s.col_FB_q3_pay_rf,
|
||
'pay_comment', s.col_FC_q3_pay_comment,
|
||
'pay_act', s.col_FD_q3_pay_act,
|
||
'booking', s.col_FF_q3_booking,
|
||
'actual_m1', s.col_FG_q3_actual_m1,
|
||
'actual_m2', s.col_FH_q3_actual_m2,
|
||
'actual_m3', s.col_FI_q3_actual_m3,
|
||
'actual_quarter', s.col_FJ_q3_actual_quarter,
|
||
'rem_booking', s.col_FK_q3_rem_booking,
|
||
'rem_actual', s.col_FL_q3_rem_actual,
|
||
'transfer_q4', s.col_FN_q3_trans_q4,
|
||
'economy', s.col_FO_q3_economy,
|
||
'total', s.col_FP_q3_total
|
||
) END AS q3_data,
|
||
|
||
CASE WHEN f.s_q4 THEN jsonb_build_object(
|
||
'rev_eco', s.col_FR_q4_rev_eco,
|
||
'rev_item', s.col_FS_q4_rev_item,
|
||
'rev_inc', s.col_FT_q4_rev_inc,
|
||
'rev_seq', s.col_FU_q4_rev_seq,
|
||
'rev_comment', s.col_FV_q4_rev_comment,
|
||
'new_plan', s.col_FW_q4_new_plan,
|
||
'adj_current', s.col_FY_q4_adj_current,
|
||
'adj_ssp', s.col_FZ_q4_adj_ssp,
|
||
'adj_rf', s.col_GA_q4_adj_rf,
|
||
'adj_reserve', s.col_GB_q4_adj_reserve,
|
||
'adj_comment', s.col_GC_q4_adj_comment,
|
||
'corrected_plan', s.col_GD_q4_corrected_plan,
|
||
'pay_date', s.col_GF_q4_pay_date,
|
||
'pay_amount', s.col_GG_q4_pay_amount,
|
||
'pay_ho', s.col_GH_q4_pay_ho,
|
||
'pay_rf', s.col_GI_q4_pay_rf,
|
||
'pay_comment', s.col_GJ_q4_pay_comment,
|
||
'pay_act', s.col_GK_q4_pay_act,
|
||
'booking', s.col_GM_q4_booking,
|
||
'actual_m1', s.col_GN_q4_actual_m1,
|
||
'actual_m2', s.col_GO_q4_actual_m2,
|
||
'actual_m3', s.col_GP_q4_actual_m3,
|
||
'actual_quarter', s.col_GQ_q4_actual_quarter,
|
||
'actual_spod', s.col_GR_q4_actual_spod,
|
||
'rem_booking', s.col_GS_q4_rem_booking,
|
||
'rem_actual', s.col_GT_q4_rem_actual,
|
||
'economy', s.col_GV_q4_economy,
|
||
'total', s.col_GW_q4_total
|
||
) END AS q4_data,
|
||
|
||
CASE WHEN f.s_tot THEN jsonb_build_object(
|
||
'fact_year', s.col_GZ_fact_year,
|
||
'pay_year', s.col_HB_pay_year
|
||
) END AS totals_data,
|
||
|
||
s._sort_path
|
||
FROM v_form1_sheet_sections(p_form_id, p_sheet, p_direction, p_sections) s
|
||
CROSS JOIN flags f
|
||
ORDER BY s._sort_path;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form1_smeta(int4, int4);
|
||
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form1_smeta(p_year INT, p_org_unit_id INT)
|
||
RETURNS TABLE (
|
||
row_type VARCHAR,
|
||
depth INT,
|
||
section_code VARCHAR,
|
||
name VARCHAR,
|
||
supp_plan_q1 NUMERIC, supp_plan_q2 NUMERIC, supp_plan_q3 NUMERIC, supp_plan_q4 NUMERIC, supp_plan_year NUMERIC,
|
||
dev_plan_q1 NUMERIC, dev_plan_q2 NUMERIC, dev_plan_q3 NUMERIC, dev_plan_q4 NUMERIC, dev_plan_year NUMERIC,
|
||
total_plan_year NUMERIC,
|
||
supp_appr_q1 NUMERIC, supp_appr_q2 NUMERIC, supp_appr_q3 NUMERIC, supp_appr_q4 NUMERIC, supp_appr_year NUMERIC,
|
||
dev_appr_q1 NUMERIC, dev_appr_q2 NUMERIC, dev_appr_q3 NUMERIC, dev_appr_q4 NUMERIC, dev_appr_year NUMERIC,
|
||
total_appr_year NUMERIC,
|
||
supp_act_q1 NUMERIC, supp_act_q2 NUMERIC, supp_act_q3 NUMERIC, supp_act_q4 NUMERIC, supp_act_year NUMERIC,
|
||
dev_act_q1 NUMERIC, dev_act_q2 NUMERIC, dev_act_q3 NUMERIC, dev_act_q4 NUMERIC, dev_act_year NUMERIC,
|
||
total_act_year NUMERIC,
|
||
supp_corr_q2 NUMERIC, supp_corr_q3 NUMERIC, supp_corr_q4 NUMERIC,
|
||
dev_corr_q2 NUMERIC, dev_corr_q3 NUMERIC, dev_corr_q4 NUMERIC
|
||
)
|
||
AS $function$
|
||
DECLARE
|
||
v_ahr INT; v_cap INT; v_oper INT;
|
||
v_sections TEXT[] := ARRAY['plan','approved','q1','q2','q3','q4'];
|
||
BEGIN
|
||
-- Одна budget_form на лист содержит ОБА направления (budget_line.direction).
|
||
-- Support/Development различаются параметром p_direction, а не отдельной формой.
|
||
WITH ranked AS (
|
||
SELECT bf.id AS fid, ei.sheet AS sh
|
||
FROM budget_form bf
|
||
JOIN budget_line bl ON bl.budget_form_id = bf.id
|
||
JOIN expense_item ei ON ei.id = bl.expense_item_id
|
||
WHERE bf.year = p_year AND bf.org_unit_id = p_org_unit_id
|
||
GROUP BY bf.id, ei.sheet
|
||
)
|
||
SELECT
|
||
MAX(CASE WHEN sh='AHR' THEN fid END),
|
||
MAX(CASE WHEN sh='CAP' THEN fid END),
|
||
MAX(CASE WHEN sh='OPER' THEN fid END)
|
||
INTO v_ahr, v_cap, v_oper
|
||
FROM ranked;
|
||
|
||
RETURN QUERY
|
||
WITH
|
||
ahr_s AS (
|
||
SELECT col_C_section AS sc, col_F_name AS nm, depth, row_type,
|
||
col_H_plan_q1 AS pq1, col_I_plan_q2 AS pq2, col_J_plan_q3 AS pq3, col_K_plan_q4 AS pq4, col_L_plan_year AS pyr,
|
||
col_AM_approved_q1 AS aq1, col_AN_approved_q2 AS aq2, col_AO_approved_q3 AS aq3, col_AP_approved_q4 AS aq4, col_AQ_approved_year AS ayr,
|
||
col_CS_q1_actual_quarter AS fq1, col_EB_q2_actual_quarter AS fq2, col_FJ_q3_actual_quarter AS fq3,
|
||
COALESCE(col_GQ_q4_actual_quarter,0) + COALESCE(col_GR_q4_actual_spod,0) AS fq4,
|
||
col_DH_q2_new_plan AS np2, col_EP_q3_new_plan AS np3, col_FW_q4_new_plan AS np4
|
||
FROM v_form1_sheet_sections(v_ahr, 'AHR', 'Support', v_sections)
|
||
WHERE row_type IN ('ROOT','GROUP','ITEM')
|
||
),
|
||
ahr_d AS (
|
||
SELECT col_C_section AS sc, col_F_name AS nm, depth, row_type,
|
||
col_H_plan_q1 AS pq1, col_I_plan_q2 AS pq2, col_J_plan_q3 AS pq3, col_K_plan_q4 AS pq4, col_L_plan_year AS pyr,
|
||
col_AM_approved_q1 AS aq1, col_AN_approved_q2 AS aq2, col_AO_approved_q3 AS aq3, col_AP_approved_q4 AS aq4, col_AQ_approved_year AS ayr,
|
||
col_CS_q1_actual_quarter AS fq1, col_EB_q2_actual_quarter AS fq2, col_FJ_q3_actual_quarter AS fq3,
|
||
COALESCE(col_GQ_q4_actual_quarter,0) + COALESCE(col_GR_q4_actual_spod,0) AS fq4,
|
||
col_DH_q2_new_plan AS np2, col_EP_q3_new_plan AS np3, col_FW_q4_new_plan AS np4
|
||
FROM v_form1_sheet_sections(v_ahr, 'AHR', 'Development', v_sections)
|
||
WHERE row_type IN ('ROOT','GROUP','ITEM')
|
||
),
|
||
cap_s AS (
|
||
SELECT col_C_section AS sc, col_F_name AS nm, depth, row_type,
|
||
col_H_plan_q1 AS pq1, col_I_plan_q2 AS pq2, col_J_plan_q3 AS pq3, col_K_plan_q4 AS pq4, col_L_plan_year AS pyr,
|
||
col_AM_approved_q1 AS aq1, col_AN_approved_q2 AS aq2, col_AO_approved_q3 AS aq3, col_AP_approved_q4 AS aq4, col_AQ_approved_year AS ayr,
|
||
col_CS_q1_actual_quarter AS fq1, col_EB_q2_actual_quarter AS fq2, col_FJ_q3_actual_quarter AS fq3,
|
||
COALESCE(col_GQ_q4_actual_quarter,0) + COALESCE(col_GR_q4_actual_spod,0) AS fq4,
|
||
col_DH_q2_new_plan AS np2, col_EP_q3_new_plan AS np3, col_FW_q4_new_plan AS np4
|
||
FROM v_form1_sheet_sections(v_cap, 'CAP', 'Support', v_sections)
|
||
WHERE row_type IN ('ROOT','GROUP','ITEM')
|
||
),
|
||
cap_d AS (
|
||
SELECT col_C_section AS sc, col_F_name AS nm, depth, row_type,
|
||
col_H_plan_q1 AS pq1, col_I_plan_q2 AS pq2, col_J_plan_q3 AS pq3, col_K_plan_q4 AS pq4, col_L_plan_year AS pyr,
|
||
col_AM_approved_q1 AS aq1, col_AN_approved_q2 AS aq2, col_AO_approved_q3 AS aq3, col_AP_approved_q4 AS aq4, col_AQ_approved_year AS ayr,
|
||
col_CS_q1_actual_quarter AS fq1, col_EB_q2_actual_quarter AS fq2, col_FJ_q3_actual_quarter AS fq3,
|
||
COALESCE(col_GQ_q4_actual_quarter,0) + COALESCE(col_GR_q4_actual_spod,0) AS fq4,
|
||
col_DH_q2_new_plan AS np2, col_EP_q3_new_plan AS np3, col_FW_q4_new_plan AS np4
|
||
FROM v_form1_sheet_sections(v_cap, 'CAP', 'Development', v_sections)
|
||
WHERE row_type IN ('ROOT','GROUP','ITEM')
|
||
),
|
||
oper_s AS (
|
||
SELECT col_C_section AS sc, col_F_name AS nm, depth, row_type,
|
||
col_H_plan_q1 AS pq1, col_I_plan_q2 AS pq2, col_J_plan_q3 AS pq3, col_K_plan_q4 AS pq4, col_L_plan_year AS pyr,
|
||
col_AM_approved_q1 AS aq1, col_AN_approved_q2 AS aq2, col_AO_approved_q3 AS aq3, col_AP_approved_q4 AS aq4, col_AQ_approved_year AS ayr,
|
||
col_CS_q1_actual_quarter AS fq1, col_EB_q2_actual_quarter AS fq2, col_FJ_q3_actual_quarter AS fq3,
|
||
COALESCE(col_GQ_q4_actual_quarter,0) + COALESCE(col_GR_q4_actual_spod,0) AS fq4,
|
||
col_DH_q2_new_plan AS np2, col_EP_q3_new_plan AS np3, col_FW_q4_new_plan AS np4
|
||
FROM v_form1_sheet_sections(v_oper, 'OPER', 'Support', v_sections)
|
||
WHERE row_type IN ('ROOT','GROUP','ITEM')
|
||
),
|
||
merged AS (
|
||
SELECT COALESCE(s.row_type, d.row_type) AS row_type,
|
||
COALESCE(s.depth, d.depth) AS depth,
|
||
COALESCE(s.sc, d.sc) AS sc,
|
||
COALESCE(s.nm, d.nm) AS nm,
|
||
s.pq1, s.pq2, s.pq3, s.pq4, s.pyr,
|
||
d.pq1 AS d_pq1, d.pq2 AS d_pq2, d.pq3 AS d_pq3, d.pq4 AS d_pq4, d.pyr AS d_pyr,
|
||
s.aq1, s.aq2, s.aq3, s.aq4, s.ayr,
|
||
d.aq1 AS d_aq1, d.aq2 AS d_aq2, d.aq3 AS d_aq3, d.aq4 AS d_aq4, d.ayr AS d_ayr,
|
||
s.fq1, s.fq2, s.fq3, s.fq4,
|
||
d.fq1 AS d_fq1, d.fq2 AS d_fq2, d.fq3 AS d_fq3, d.fq4 AS d_fq4,
|
||
s.np2, s.np3, s.np4,
|
||
d.np2 AS d_np2, d.np3 AS d_np3, d.np4 AS d_np4
|
||
FROM ahr_s s FULL OUTER JOIN ahr_d d ON s.sc = d.sc
|
||
UNION ALL
|
||
SELECT COALESCE(s.row_type, d.row_type), COALESCE(s.depth, d.depth),
|
||
COALESCE(s.sc, d.sc), COALESCE(s.nm, d.nm),
|
||
s.pq1, s.pq2, s.pq3, s.pq4, s.pyr,
|
||
d.pq1, d.pq2, d.pq3, d.pq4, d.pyr,
|
||
s.aq1, s.aq2, s.aq3, s.aq4, s.ayr,
|
||
d.aq1, d.aq2, d.aq3, d.aq4, d.ayr,
|
||
s.fq1, s.fq2, s.fq3, s.fq4,
|
||
d.fq1, d.fq2, d.fq3, d.fq4,
|
||
s.np2, s.np3, s.np4,
|
||
d.np2, d.np3, d.np4
|
||
FROM cap_s s FULL OUTER JOIN cap_d d ON s.sc = d.sc
|
||
UNION ALL
|
||
SELECT s.row_type, s.depth, s.sc, s.nm,
|
||
s.pq1, s.pq2, s.pq3, s.pq4, s.pyr,
|
||
NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC,
|
||
s.aq1, s.aq2, s.aq3, s.aq4, s.ayr,
|
||
NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC,
|
||
s.fq1, s.fq2, s.fq3, s.fq4,
|
||
NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC,
|
||
s.np2, s.np3, s.np4,
|
||
NULL::NUMERIC, NULL::NUMERIC, NULL::NUMERIC
|
||
FROM oper_s s
|
||
)
|
||
SELECT
|
||
m.row_type, m.depth, m.sc, m.nm,
|
||
m.pq1, m.pq2, m.pq3, m.pq4, m.pyr,
|
||
m.d_pq1, m.d_pq2, m.d_pq3, m.d_pq4, m.d_pyr,
|
||
COALESCE(m.pyr,0) + COALESCE(m.d_pyr,0) AS total_plan_year,
|
||
m.aq1, m.aq2, m.aq3, m.aq4, m.ayr,
|
||
m.d_aq1, m.d_aq2, m.d_aq3, m.d_aq4, m.d_ayr,
|
||
COALESCE(m.ayr,0) + COALESCE(m.d_ayr,0) AS total_appr_year,
|
||
m.fq1, m.fq2, m.fq3, m.fq4, COALESCE(m.fq1,0)+COALESCE(m.fq2,0)+COALESCE(m.fq3,0)+COALESCE(m.fq4,0),
|
||
m.d_fq1, m.d_fq2, m.d_fq3, m.d_fq4, COALESCE(m.d_fq1,0)+COALESCE(m.d_fq2,0)+COALESCE(m.d_fq3,0)+COALESCE(m.d_fq4,0),
|
||
COALESCE(m.fq1,0)+COALESCE(m.fq2,0)+COALESCE(m.fq3,0)+COALESCE(m.fq4,0)
|
||
+ COALESCE(m.d_fq1,0)+COALESCE(m.d_fq2,0)+COALESCE(m.d_fq3,0)+COALESCE(m.d_fq4,0),
|
||
m.np2, m.np3, m.np4,
|
||
m.d_np2, m.d_np3, m.d_np4
|
||
FROM merged m
|
||
ORDER BY m.sc;
|
||
END;
|
||
$function$
|
||
LANGUAGE plpgsql STABLE SET search_path = v3, pg_catalog
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form2_sheet_sections(int4, varchar, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form2_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_vsp_id integer, col_vsp_address character varying, col_plan_q1 numeric, col_plan_q2 numeric, col_plan_q3 numeric, col_plan_q4 numeric, col_plan_year numeric, col_plan_comment character varying, col_seq_dfip_q1 numeric, col_seq_dfip_q2 numeric, col_seq_dfip_q3 numeric, col_seq_dfip_q4 numeric, col_seq_dfip_year numeric, col_seq_dfip_just character varying, col_seq_ssp_q1 numeric, col_seq_ssp_q2 numeric, col_seq_ssp_q3 numeric, col_seq_ssp_q4 numeric, col_seq_ssp_year numeric, col_seq_ssp_just character varying, col_appr_q1 numeric, col_appr_q2 numeric, col_appr_q3 numeric, col_appr_q4 numeric, col_appr_year numeric, col_cd_counterparty character varying, col_cd_reference character varying, col_cd_date date, col_cd_subject character varying, col_cd_currency character varying, col_cd_ceiling numeric, col_cd_vat_rate character varying, col_cd_deadline character varying, col_cd_scheme character varying, col_cd_act character varying, col_cd_comment character varying, col_book_q1 numeric, col_book_q2 numeric, col_book_q3 numeric, col_book_q4 numeric, col_book_next_q1 numeric, col_book_next_q2 numeric, col_book_next_q3 numeric, col_book_next_q4 numeric, col_q1_adj_current numeric, col_q1_adj_ssp numeric, col_q1_adj_reserve numeric, col_q1_adj_comment character varying, col_q1_corrected_plan numeric, col_q1_pay_date date, col_q1_pay_amount numeric, col_q1_pay_comment character varying, col_q1_pay_act character varying, col_q1_booking numeric, col_q1_actual_m1 numeric, col_q1_actual_m2 numeric, col_q1_actual_m3 numeric, col_q1_actual_quarter numeric, col_q1_residual_after_booking numeric, col_q1_residual_after_actual numeric, col_q1_transfer_q2 numeric, col_q1_transfer_q2_delay_acts numeric, col_q1_transfer_q2_delay_procurement numeric, col_q1_transfer_q2_economy_rf numeric, col_q1_transfer_next_comment character varying, col_q1_transfer_q3 numeric, col_q1_transfer_q4 numeric, col_q1_transfer_far_comment character varying, col_q1_transfer_econ numeric, col_q1_total numeric, col_q2_target_change numeric, col_q2_base_correction numeric, col_q2_base_correction_comment character varying, col_q2_revision_inc numeric, col_q2_revision_seq numeric, col_q2_revision_comment character varying, col_q2_new_plan numeric, col_q2_adj_current numeric, col_q2_adj_ssp numeric, col_q2_adj_reserve numeric, col_q2_adj_comment character varying, col_q2_corrected_plan numeric, col_q2_pay_date date, col_q2_pay_amount numeric, col_q2_pay_comment character varying, col_q2_pay_act character varying, col_q2_booking numeric, col_q2_actual_m1 numeric, col_q2_actual_m2 numeric, col_q2_actual_m3 numeric, col_q2_actual_quarter numeric, col_q2_residual_after_booking numeric, col_q2_residual_after_actual numeric, col_q2_transfer_q3 numeric, col_q2_transfer_q3_delay_acts numeric, col_q2_transfer_q3_delay_procurement numeric, col_q2_transfer_q3_economy_rf numeric, col_q2_transfer_next_comment character varying, col_q2_transfer_q4 numeric, col_q2_transfer_far_comment character varying, col_q2_transfer_econ numeric, col_q2_total numeric, col_q3_target_change numeric, col_q3_base_correction numeric, col_q3_base_correction_comment character varying, col_q3_revision_inc numeric, col_q3_revision_seq numeric, col_q3_revision_comment character varying, col_q3_new_plan numeric, col_q3_adj_current numeric, col_q3_adj_ssp numeric, col_q3_adj_reserve numeric, col_q3_adj_comment character varying, col_q3_corrected_plan numeric, col_q3_pay_date date, col_q3_pay_amount numeric, col_q3_pay_comment character varying, col_q3_pay_act character varying, col_q3_booking numeric, col_q3_actual_m1 numeric, col_q3_actual_m2 numeric, col_q3_actual_m3 numeric, col_q3_actual_quarter numeric, col_q3_residual_after_booking numeric, col_q3_residual_after_actual numeric, col_q3_transfer_q4 numeric, col_q3_transfer_q4_delay_acts numeric, col_q3_transfer_q4_delay_procurement numeric, col_q3_transfer_q4_economy_rf numeric, col_q3_transfer_next_comment character varying, col_q3_transfer_econ numeric, col_q3_total numeric, col_q4_target_change numeric, col_q4_base_correction numeric, col_q4_base_correction_comment character varying, col_q4_revision_inc numeric, col_q4_revision_seq numeric, col_q4_revision_comment character varying, col_q4_new_plan numeric, col_q4_adj_current numeric, col_q4_adj_ssp numeric, col_q4_adj_reserve numeric, col_q4_adj_comment character varying, col_q4_corrected_plan numeric, col_q4_pay_date date, col_q4_pay_amount numeric, col_q4_pay_comment character varying, col_q4_pay_act character varying, col_q4_booking numeric, col_q4_actual_m1 numeric, col_q4_actual_m2 numeric, col_q4_actual_m3 numeric, col_q4_actual_spod numeric, col_q4_actual_quarter numeric, col_q4_residual_after_booking numeric, col_q4_residual_after_actual numeric, col_q4_transfer_econ numeric, col_q4_total numeric, col_fact_year numeric, col_pay_year numeric, col_economy_year numeric, _sort_path integer[])
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
AS $function$
|
||
#variable_conflict use_column
|
||
DECLARE
|
||
s_plan BOOL; s_seq_d BOOL; s_seq_s BOOL; s_appr BOOL;
|
||
s_cd BOOL; s_book BOOL;
|
||
s_q1 BOOL; s_q2 BOOL; s_q3 BOOL; s_q4 BOOL; s_tot 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_seq_s := p_sections IS NULL OR 'seq_ssp' = ANY(p_sections);
|
||
s_appr := p_sections IS NULL OR 'approved' = ANY(p_sections);
|
||
s_cd := p_sections IS NULL OR 'contract' = ANY(p_sections);
|
||
s_book := p_sections IS NULL OR 'booking' = ANY(p_sections);
|
||
s_q1 := p_sections IS NULL OR 'q1' = ANY(p_sections);
|
||
s_q2 := p_sections IS NULL OR 'q2' = ANY(p_sections);
|
||
s_q3 := p_sections IS NULL OR 'q3' = ANY(p_sections);
|
||
s_q4 := p_sections IS NULL OR 'q4' = ANY(p_sections);
|
||
s_tot := p_sections IS NULL OR 'totals' = ANY(p_sections);
|
||
s_need_ap := s_plan OR s_appr OR s_q1 OR s_q2 OR s_q3 OR s_q4;
|
||
|
||
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'),
|
||
jsg AS (SELECT s.* FROM v3.sequestration s JOIN page pg ON s.line_id = pg.lid WHERE s_need_ap AND s.actor='SSP_GO'),
|
||
jr AS (SELECT r.* FROM v3.reserve r JOIN page 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),
|
||
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,
|
||
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,
|
||
COALESCE(sg.adj_q1,0) AS gq1, COALESCE(sg.adj_q2,0) AS gq2,
|
||
COALESCE(sg.adj_q3,0) AS gq3, COALESCE(sg.adj_q4,0) AS gq4,
|
||
sg.justification AS gjust,
|
||
-- 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,
|
||
-- contract
|
||
cd.counterparty AS cd_cp, cd.reference AS cd_ref, cd.contract_date AS cd_dt,
|
||
cd.subject AS cd_subj, cd.currency AS cd_cur, cd.ceiling_amount AS cd_ceil,
|
||
cd.vat_rate AS cd_vat, cd.deadline AS cd_dl, cd.payment_scheme AS cd_sch,
|
||
cd.act AS cd_act, cd.comment AS cd_cmt,
|
||
-- ckk (booking)
|
||
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,
|
||
-- Q1
|
||
q1.adj_current AS q1_ac, q1.adj_ssp AS q1_as, q1.adj_reserve AS q1_arv, q1.adj_comment AS q1_acmt,
|
||
q1.payment_date AS q1_pd, q1.payment_amount AS q1_pa,
|
||
q1.payment_comment AS q1_pcmt,
|
||
q1.payment_act AS q1_pact, 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_reserve AS q2_arv,
|
||
q2.adj_comment AS q2_acmt,
|
||
q2.payment_date AS q2_pd, q2.payment_amount AS q2_pa,
|
||
q2.payment_comment AS q2_pcmt, q2.payment_act AS q2_pact,
|
||
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_reserve AS q3_arv,
|
||
q3.adj_comment AS q3_acmt,
|
||
q3.payment_date AS q3_pd, q3.payment_amount AS q3_pa,
|
||
q3.payment_comment AS q3_pcmt, q3.payment_act AS q3_pact,
|
||
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_reserve AS q4_arv,
|
||
q4.adj_comment AS q4_acmt,
|
||
q4.payment_date AS q4_pd, q4.payment_amount AS q4_pa,
|
||
q4.payment_comment AS q4_pcmt, q4.payment_act AS q4_pact,
|
||
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(sg.adj_q1,0)+COALESCE(r.amount_q1,0) AS ap1,
|
||
COALESCE(p.plan_q2,0)+COALESCE(sd.adj_q2,0)+COALESCE(sg.adj_q2,0)+COALESCE(r.amount_q2,0) AS ap2,
|
||
COALESCE(p.plan_q3,0)+COALESCE(sd.adj_q3,0)+COALESCE(sg.adj_q3,0)+COALESCE(r.amount_q3,0) AS ap3,
|
||
COALESCE(p.plan_q4,0)+COALESCE(sd.adj_q4,0)+COALESCE(sg.adj_q4,0)+COALESCE(r.amount_q4,0) AS ap4
|
||
FROM page pg
|
||
JOIN tw t ON t.id = pg.eid
|
||
LEFT JOIN v3.vsp v ON v.id = pg.vsp_id
|
||
LEFT JOIN jp p ON p.line_id = pg.lid
|
||
LEFT JOIN jsd sd ON sd.line_id = pg.lid
|
||
LEFT JOIN jsg sg ON sg.line_id = pg.lid
|
||
LEFT JOIN jr r ON r.line_id = pg.lid
|
||
LEFT JOIN jcd cd ON cd.line_id = pg.lid
|
||
LEFT JOIN jck ck ON ck.line_id = pg.lid
|
||
LEFT JOIN 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
|
||
enriched AS (
|
||
SELECT b.*,
|
||
-- Q1 corrected_plan = approved + adj_*
|
||
b.ap1 + COALESCE(b.q1_ac,0)+COALESCE(b.q1_as,0)+COALESCE(b.q1_arv,0) AS cp1,
|
||
COALESCE(b.q1_m1,0)+COALESCE(b.q1_m2,0)+COALESCE(b.q1_m3,0) AS aq1,
|
||
-- Q2 new_plan = approved + transfer_q1 + revisions(inc/seq) + base_correction
|
||
b.ap2 + COALESCE(b.q1_tq2,0) + COALESCE(b.q2_rinc,0)+COALESCE(b.q2_rseq,0) + COALESCE(b.q2_bc,0) AS np2,
|
||
COALESCE(b.q2_m1,0)+COALESCE(b.q2_m2,0)+COALESCE(b.q2_m3,0) AS aq2,
|
||
b.ap3 + COALESCE(b.q1_tq3,0)+COALESCE(b.q2_tq3,0) + COALESCE(b.q3_rinc,0)+COALESCE(b.q3_rseq,0) + COALESCE(b.q3_bc,0) AS np3,
|
||
COALESCE(b.q3_m1,0)+COALESCE(b.q3_m2,0)+COALESCE(b.q3_m3,0) AS aq3,
|
||
b.ap4 + COALESCE(b.q1_tq4,0)+COALESCE(b.q2_tq4,0)+COALESCE(b.q3_tq4,0) + COALESCE(b.q4_rinc,0)+COALESCE(b.q4_rseq,0) + COALESCE(b.q4_bc,0) AS np4,
|
||
COALESCE(b.q4_m1,0)+COALESCE(b.q4_m2,0)+COALESCE(b.q4_m3,0)+COALESCE(b.q4_spod,0) AS aq4
|
||
FROM input_base b
|
||
),
|
||
final_input AS (
|
||
SELECT e.*,
|
||
e.np2 + COALESCE(e.q2_ac,0)+COALESCE(e.q2_as,0)+COALESCE(e.q2_arv,0) AS cp2,
|
||
e.np3 + COALESCE(e.q3_ac,0)+COALESCE(e.q3_as,0)+COALESCE(e.q3_arv,0) AS cp3,
|
||
e.np4 + COALESCE(e.q4_ac,0)+COALESCE(e.q4_as,0)+COALESCE(e.q4_arv,0) AS cp4
|
||
FROM enriched e
|
||
),
|
||
|
||
-- 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(sg.adj_q1) AS sg1, SUM(sg.adj_q2) AS sg2, SUM(sg.adj_q3) AS sg3, SUM(sg.adj_q4) AS sg4,
|
||
SUM(r.amount_q1) AS sr1, SUM(r.amount_q2) AS sr2, SUM(r.amount_q3) AS sr3, SUM(r.amount_q4) AS sr4,
|
||
SUM(ck.expenses_q1) AS bk1, SUM(ck.expenses_q2) AS bk2, SUM(ck.expenses_q3) AS bk3, SUM(ck.expenses_q4) AS bk4,
|
||
SUM(ck.expenses_next_year_q1) AS bn1, SUM(ck.expenses_next_year_q2) AS bn2,
|
||
SUM(ck.expenses_next_year_q3) AS bn3, SUM(ck.expenses_next_year_q4) AS bn4
|
||
FROM v3.budget_line bl
|
||
JOIN v3.expense_item ei ON ei.id = bl.expense_item_id
|
||
LEFT JOIN v3.plan p ON p.line_id = bl.id AND s_need_ap
|
||
LEFT JOIN v3.sequestration sd ON sd.line_id = bl.id AND sd.actor='DFIP' AND s_need_ap
|
||
LEFT JOIN v3.sequestration sg ON sg.line_id = bl.id AND sg.actor='SSP_GO' AND s_need_ap
|
||
LEFT JOIN v3.reserve r ON r.line_id = bl.id AND s_need_ap
|
||
LEFT JOIN v3.ckk ck ON ck.line_id = bl.id AND s_book
|
||
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_reserve) AS arv,
|
||
SUM(q.payment_amount) AS pa,
|
||
SUM(q.booking_amount) AS bk,
|
||
SUM(q.actual_m1) AS m1, SUM(q.actual_m2) AS m2, SUM(q.actual_m3) AS m3,
|
||
SUM(q.actual_spod) AS spod,
|
||
SUM(q.transfer_to_q2) AS tq2, SUM(q.transfer_to_q3) AS tq3, SUM(q.transfer_to_q4) AS tq4,
|
||
SUM(q.transfer_to_economy) AS te,
|
||
SUM(q.transfer_delay_acts) AS tda,
|
||
SUM(q.transfer_delay_procurement) AS tdp,
|
||
SUM(q.transfer_economy_rf) AS terf,
|
||
-- Δ к 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.sg1) AS sg1, SUM(a.sg2) AS sg2, SUM(a.sg3) AS sg3, SUM(a.sg4) AS sg4,
|
||
SUM(a.sr1) AS sr1, SUM(a.sr2) AS sr2, SUM(a.sr3) AS sr3, SUM(a.sr4) AS sr4,
|
||
SUM(a.bk1) AS bk1, SUM(a.bk2) AS bk2, SUM(a.bk3) AS bk3, SUM(a.bk4) AS bk4,
|
||
SUM(a.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.arv) AS arv,
|
||
SUM(b.pa) AS pa, SUM(b.bk) AS bk,
|
||
SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3,
|
||
SUM(b.tq2) AS tq2, SUM(b.tq3) AS tq3, SUM(b.tq4) AS tq4, SUM(b.te) AS te,
|
||
SUM(b.tda) AS tda, SUM(b.tdp) AS tdp, SUM(b.terf) AS terf
|
||
FROM tw LEFT JOIN aq_q b ON b.eid = ANY(tw.desc_ids) AND b.quarter=1 GROUP BY tw.id
|
||
),
|
||
tw_aq2 AS (
|
||
SELECT tw.id, SUM(b.ac) AS ac, SUM(b.as_v) AS as_v, SUM(b.arv) AS arv,
|
||
SUM(b.pa) AS pa, SUM(b.bk) AS bk,
|
||
SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3,
|
||
SUM(b.tq3) AS tq3, SUM(b.tq4) AS tq4, SUM(b.te) AS te, SUM(b.rev) AS rev,
|
||
SUM(b.tda) AS tda, SUM(b.tdp) AS tdp, SUM(b.terf) AS terf
|
||
FROM tw LEFT JOIN aq_q b ON b.eid = ANY(tw.desc_ids) AND b.quarter=2 GROUP BY tw.id
|
||
),
|
||
tw_aq3 AS (
|
||
SELECT tw.id, SUM(b.ac) AS ac, SUM(b.as_v) AS as_v, SUM(b.arv) AS arv,
|
||
SUM(b.pa) AS pa, SUM(b.bk) AS bk,
|
||
SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3,
|
||
SUM(b.tq4) AS tq4, SUM(b.te) AS te, SUM(b.rev) AS rev,
|
||
SUM(b.tda) AS tda, SUM(b.tdp) AS tdp, SUM(b.terf) AS terf
|
||
FROM tw LEFT JOIN aq_q b ON b.eid = ANY(tw.desc_ids) AND b.quarter=3 GROUP BY tw.id
|
||
),
|
||
tw_aq4 AS (
|
||
SELECT tw.id, SUM(b.ac) AS ac, SUM(b.as_v) AS as_v, SUM(b.arv) AS arv,
|
||
SUM(b.pa) AS pa, SUM(b.bk) AS bk,
|
||
SUM(b.m1) AS m1, SUM(b.m2) AS m2, SUM(b.m3) AS m3, SUM(b.spod) AS spod,
|
||
SUM(b.te) AS te, SUM(b.rev) AS rev
|
||
FROM tw 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.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,
|
||
-- seq ssp_go
|
||
CASE WHEN s_seq_s THEN f.gq1 END, CASE WHEN s_seq_s THEN f.gq2 END,
|
||
CASE WHEN s_seq_s THEN f.gq3 END, CASE WHEN s_seq_s THEN f.gq4 END,
|
||
CASE WHEN s_seq_s THEN f.gq1+f.gq2+f.gq3+f.gq4 END, CASE WHEN s_seq_s THEN f.gjust END,
|
||
-- 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
|
||
CASE WHEN s_cd THEN f.cd_cp END, CASE WHEN s_cd THEN f.cd_ref END,
|
||
CASE WHEN s_cd THEN f.cd_dt END, CASE WHEN s_cd THEN f.cd_subj END,
|
||
CASE WHEN s_cd THEN f.cd_cur END, CASE WHEN s_cd THEN f.cd_ceil END,
|
||
CASE WHEN s_cd THEN f.cd_vat END, CASE WHEN s_cd THEN f.cd_dl END,
|
||
CASE WHEN s_cd THEN f.cd_sch END, CASE WHEN s_cd THEN f.cd_act END,
|
||
CASE WHEN s_cd THEN f.cd_cmt END,
|
||
-- 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_arv END,
|
||
CASE WHEN s_q1 THEN f.q1_acmt END, CASE WHEN s_q1 THEN f.cp1 END,
|
||
CASE WHEN s_q1 THEN f.q1_pd END, CASE WHEN s_q1 THEN f.q1_pa END,
|
||
CASE WHEN s_q1 THEN f.q1_pcmt END,
|
||
CASE WHEN s_q1 THEN f.q1_pact END, CASE WHEN s_q1 THEN f.q1_book END,
|
||
CASE WHEN s_q1 THEN f.q1_m1 END, CASE WHEN s_q1 THEN f.q1_m2 END, CASE WHEN s_q1 THEN f.q1_m3 END,
|
||
CASE WHEN s_q1 THEN f.aq1 END,
|
||
CASE WHEN s_q1 THEN f.cp1 - COALESCE(f.q1_book,0) END, -- residual_after_booking
|
||
CASE WHEN s_q1 THEN f.cp1 - f.aq1 END, -- residual_after_actual
|
||
CASE WHEN s_q1 THEN f.q1_tq2 END,
|
||
CASE WHEN s_q1 THEN f.q1_tda END, CASE WHEN s_q1 THEN f.q1_tdp END, CASE WHEN s_q1 THEN f.q1_terf END,
|
||
CASE WHEN s_q1 THEN f.q1_tnc END,
|
||
CASE WHEN s_q1 THEN f.q1_tq3 END, CASE WHEN s_q1 THEN f.q1_tq4 END,
|
||
CASE WHEN s_q1 THEN f.q1_tfc END,
|
||
CASE WHEN s_q1 THEN f.q1_te END,
|
||
-- total = SUM transfer_q2_reasons + transfer_q3 + transfer_q4 + transfer_econ (Excel CC5)
|
||
CASE WHEN s_q1 THEN
|
||
COALESCE(f.q1_tda,0)+COALESCE(f.q1_tdp,0)+COALESCE(f.q1_terf,0)
|
||
+COALESCE(f.q1_tq3,0)+COALESCE(f.q1_tq4,0)+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_arv END,
|
||
CASE WHEN s_q2 THEN f.q2_acmt END,
|
||
CASE WHEN s_q2 THEN f.cp2 END,
|
||
CASE WHEN s_q2 THEN f.q2_pd END, CASE WHEN s_q2 THEN f.q2_pa END,
|
||
CASE WHEN s_q2 THEN f.q2_pcmt END, CASE WHEN s_q2 THEN f.q2_pact END,
|
||
CASE WHEN s_q2 THEN f.q2_book END,
|
||
CASE WHEN s_q2 THEN f.q2_m1 END, CASE WHEN s_q2 THEN f.q2_m2 END, CASE WHEN s_q2 THEN f.q2_m3 END,
|
||
CASE WHEN s_q2 THEN f.aq2 END,
|
||
CASE WHEN s_q2 THEN f.cp2 - COALESCE(f.q2_book,0) END,
|
||
CASE WHEN s_q2 THEN f.cp2 - f.aq2 END,
|
||
CASE WHEN s_q2 THEN f.q2_tq3 END,
|
||
CASE WHEN s_q2 THEN f.q2_tda END, CASE WHEN s_q2 THEN f.q2_tdp END, CASE WHEN s_q2 THEN f.q2_terf END,
|
||
CASE WHEN s_q2 THEN f.q2_tnc END,
|
||
CASE WHEN s_q2 THEN f.q2_tq4 END,
|
||
CASE WHEN s_q2 THEN f.q2_tfc END,
|
||
CASE WHEN s_q2 THEN f.q2_te END,
|
||
-- total = SUM transfer_q3_reasons + transfer_q4 + transfer_econ (Excel DN5)
|
||
CASE WHEN s_q2 THEN
|
||
COALESCE(f.q2_tda,0)+COALESCE(f.q2_tdp,0)+COALESCE(f.q2_terf,0)
|
||
+COALESCE(f.q2_tq4,0)+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_arv END,
|
||
CASE WHEN s_q3 THEN f.q3_acmt END,
|
||
CASE WHEN s_q3 THEN f.cp3 END,
|
||
CASE WHEN s_q3 THEN f.q3_pd END, CASE WHEN s_q3 THEN f.q3_pa END,
|
||
CASE WHEN s_q3 THEN f.q3_pcmt END, CASE WHEN s_q3 THEN f.q3_pact END,
|
||
CASE WHEN s_q3 THEN f.q3_book END,
|
||
CASE WHEN s_q3 THEN f.q3_m1 END, CASE WHEN s_q3 THEN f.q3_m2 END, CASE WHEN s_q3 THEN f.q3_m3 END,
|
||
CASE WHEN s_q3 THEN f.aq3 END,
|
||
CASE WHEN s_q3 THEN f.cp3 - COALESCE(f.q3_book,0) END,
|
||
CASE WHEN s_q3 THEN f.cp3 - f.aq3 END,
|
||
CASE WHEN s_q3 THEN f.q3_tq4 END,
|
||
CASE WHEN s_q3 THEN f.q3_tda END, CASE WHEN s_q3 THEN f.q3_tdp END, CASE WHEN s_q3 THEN f.q3_terf END,
|
||
CASE WHEN s_q3 THEN f.q3_tnc END,
|
||
CASE WHEN s_q3 THEN f.q3_te END,
|
||
-- total = SUM transfer_q4_reasons + transfer_econ (Excel EW5)
|
||
CASE WHEN s_q3 THEN
|
||
COALESCE(f.q3_tda,0)+COALESCE(f.q3_tdp,0)+COALESCE(f.q3_terf,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_arv END,
|
||
CASE WHEN s_q4 THEN f.q4_acmt END,
|
||
CASE WHEN s_q4 THEN f.cp4 END,
|
||
CASE WHEN s_q4 THEN f.q4_pd END, CASE WHEN s_q4 THEN f.q4_pa END,
|
||
CASE WHEN s_q4 THEN f.q4_pcmt END, CASE WHEN s_q4 THEN f.q4_pact END,
|
||
CASE WHEN s_q4 THEN f.q4_book END,
|
||
CASE WHEN s_q4 THEN f.q4_m1 END, CASE WHEN s_q4 THEN f.q4_m2 END, CASE WHEN s_q4 THEN f.q4_m3 END,
|
||
CASE WHEN s_q4 THEN f.q4_spod END,
|
||
CASE WHEN s_q4 THEN f.aq4 END,
|
||
CASE WHEN s_q4 THEN f.cp4 - COALESCE(f.q4_book,0) END,
|
||
CASE WHEN s_q4 THEN f.cp4 - f.aq4 END,
|
||
CASE WHEN s_q4 THEN f.q4_te END,
|
||
-- q4.total = q4.transfer_econ (Q4 has no other outgoing transfers)
|
||
CASE WHEN s_q4 THEN f.q4_te END,
|
||
-- totals
|
||
CASE WHEN s_tot THEN f.aq1+f.aq2+f.aq3+f.aq4 END,
|
||
CASE WHEN s_tot THEN COALESCE(f.q1_pa,0)+COALESCE(f.q2_pa,0)+COALESCE(f.q3_pa,0)+COALESCE(f.q4_pa,0) END,
|
||
-- economy_year = q4.corrected_plan − q4.actual_quarter (Excel GA5 = FY5 = FK5 − FW5)
|
||
CASE WHEN s_tot THEN f.cp4 - f.aq4 END,
|
||
|
||
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::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,
|
||
-- seq ssp_go
|
||
CASE WHEN s_seq_s THEN ta.sg1 END, CASE WHEN s_seq_s THEN ta.sg2 END,
|
||
CASE WHEN s_seq_s THEN ta.sg3 END, CASE WHEN s_seq_s THEN ta.sg4 END,
|
||
CASE WHEN s_seq_s THEN COALESCE(ta.sg1,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sg4,0) END,
|
||
NULL::VARCHAR,
|
||
-- approved
|
||
CASE WHEN s_appr THEN COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sg1,0)+COALESCE(ta.sr1,0) END,
|
||
CASE WHEN s_appr THEN COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sr2,0) END,
|
||
CASE WHEN s_appr THEN COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sr3,0) END,
|
||
CASE WHEN s_appr THEN COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0) END,
|
||
CASE WHEN s_appr THEN COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sg1,0)+COALESCE(ta.sr1,0)
|
||
+COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sr2,0)
|
||
+COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sr3,0)
|
||
+COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0) END,
|
||
-- contract — нет агрегации (текстовые)
|
||
NULL::VARCHAR, NULL::VARCHAR, NULL::DATE, NULL::VARCHAR,
|
||
NULL::VARCHAR, NULL::NUMERIC, NULL::VARCHAR, 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: новые поля (pay_act, booking, residuals, transfer reasons,
|
||
-- transfer comments) на агрегатах NULL'им — их корректный rollup можно
|
||
-- добавить позже расширением aq_q.
|
||
CASE WHEN s_q1 THEN tb1.ac END, CASE WHEN s_q1 THEN tb1.as_v END, CASE WHEN s_q1 THEN tb1.arv END,
|
||
NULL::VARCHAR,
|
||
CASE WHEN s_q1 THEN COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sg1,0)+COALESCE(ta.sr1,0)+COALESCE(tb1.ac,0)+COALESCE(tb1.as_v,0)+COALESCE(tb1.arv,0) END,
|
||
NULL::DATE,
|
||
CASE WHEN s_q1 THEN tb1.pa END,
|
||
NULL::VARCHAR, -- pay_comment
|
||
NULL::VARCHAR, -- pay_act
|
||
CASE WHEN s_q1 THEN tb1.bk END, -- booking (rollup)
|
||
CASE WHEN s_q1 THEN tb1.m1 END, CASE WHEN s_q1 THEN tb1.m2 END, CASE WHEN s_q1 THEN tb1.m3 END,
|
||
CASE WHEN s_q1 THEN COALESCE(tb1.m1,0)+COALESCE(tb1.m2,0)+COALESCE(tb1.m3,0) END,
|
||
-- residual_after_booking = q1.cp − Σ booking
|
||
CASE WHEN s_q1 THEN
|
||
(COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sg1,0)+COALESCE(ta.sr1,0)+COALESCE(tb1.ac,0)+COALESCE(tb1.as_v,0)+COALESCE(tb1.arv,0))
|
||
- COALESCE(tb1.bk,0)
|
||
END,
|
||
-- residual_after_actual = q1.cp − Σ actual
|
||
CASE WHEN s_q1 THEN
|
||
(COALESCE(ta.sp1,0)+COALESCE(ta.sd1,0)+COALESCE(ta.sg1,0)+COALESCE(ta.sr1,0)+COALESCE(tb1.ac,0)+COALESCE(tb1.as_v,0)+COALESCE(tb1.arv,0))
|
||
- (COALESCE(tb1.m1,0)+COALESCE(tb1.m2,0)+COALESCE(tb1.m3,0))
|
||
END,
|
||
CASE WHEN s_q1 THEN tb1.tq2 END,
|
||
CASE WHEN s_q1 THEN tb1.tda END, CASE WHEN s_q1 THEN tb1.tdp END, CASE WHEN s_q1 THEN tb1.terf END,
|
||
NULL::VARCHAR, -- transfer_next_comment (text, no rollup)
|
||
CASE WHEN s_q1 THEN tb1.tq3 END, CASE WHEN s_q1 THEN tb1.tq4 END,
|
||
NULL::VARCHAR, -- far comment
|
||
CASE WHEN s_q1 THEN tb1.te END,
|
||
-- total = SUM transfer_q2_reasons + transfer_q3 + transfer_q4 + transfer_econ (Excel CC)
|
||
CASE WHEN s_q1 THEN
|
||
COALESCE(tb1.tda,0)+COALESCE(tb1.tdp,0)+COALESCE(tb1.terf,0)
|
||
+COALESCE(tb1.tq3,0)+COALESCE(tb1.tq4,0)+COALESCE(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.sg2,0)+COALESCE(ta.sr2,0)+COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0)
|
||
END,
|
||
CASE WHEN s_q2 THEN tb2.ac END, CASE WHEN s_q2 THEN tb2.as_v END, CASE WHEN s_q2 THEN tb2.arv END,
|
||
NULL::VARCHAR, -- adj_comment
|
||
CASE WHEN s_q2 THEN
|
||
COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sr2,0)+COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0)+COALESCE(tb2.ac,0)+COALESCE(tb2.as_v,0)+COALESCE(tb2.arv,0)
|
||
END,
|
||
NULL::DATE,
|
||
CASE WHEN s_q2 THEN tb2.pa END,
|
||
NULL::VARCHAR, -- pay_comment
|
||
NULL::VARCHAR, -- pay_act
|
||
CASE WHEN s_q2 THEN tb2.bk END, -- booking (rollup)
|
||
CASE WHEN s_q2 THEN tb2.m1 END, CASE WHEN s_q2 THEN tb2.m2 END, CASE WHEN s_q2 THEN tb2.m3 END,
|
||
CASE WHEN s_q2 THEN COALESCE(tb2.m1,0)+COALESCE(tb2.m2,0)+COALESCE(tb2.m3,0) END,
|
||
-- residual_after_booking = q2.cp − Σ booking
|
||
CASE WHEN s_q2 THEN
|
||
(COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sr2,0)+COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0)+COALESCE(tb2.ac,0)+COALESCE(tb2.as_v,0)+COALESCE(tb2.arv,0))
|
||
- COALESCE(tb2.bk,0)
|
||
END,
|
||
-- residual_after_actual = q2.cp − Σ actual
|
||
CASE WHEN s_q2 THEN
|
||
(COALESCE(ta.sp2,0)+COALESCE(ta.sd2,0)+COALESCE(ta.sg2,0)+COALESCE(ta.sr2,0)+COALESCE(tb1.tq2,0)+COALESCE(tb2.rev,0)+COALESCE(tb2.ac,0)+COALESCE(tb2.as_v,0)+COALESCE(tb2.arv,0))
|
||
- (COALESCE(tb2.m1,0)+COALESCE(tb2.m2,0)+COALESCE(tb2.m3,0))
|
||
END,
|
||
CASE WHEN s_q2 THEN tb2.tq3 END,
|
||
CASE WHEN s_q2 THEN tb2.tda END, CASE WHEN s_q2 THEN tb2.tdp END, CASE WHEN s_q2 THEN tb2.terf END,
|
||
NULL::VARCHAR, -- transfer_next_comment (text, no rollup)
|
||
CASE WHEN s_q2 THEN tb2.tq4 END,
|
||
NULL::VARCHAR, -- far comment
|
||
CASE WHEN s_q2 THEN tb2.te END,
|
||
-- total = SUM transfer_q3_reasons + transfer_q4 + transfer_econ (Excel DN)
|
||
CASE WHEN s_q2 THEN
|
||
COALESCE(tb2.tda,0)+COALESCE(tb2.tdp,0)+COALESCE(tb2.terf,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.sg3,0)+COALESCE(ta.sr3,0)+COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0)
|
||
END,
|
||
CASE WHEN s_q3 THEN tb3.ac END, CASE WHEN s_q3 THEN tb3.as_v END, CASE WHEN s_q3 THEN tb3.arv END,
|
||
NULL::VARCHAR,
|
||
CASE WHEN s_q3 THEN
|
||
COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sr3,0)+COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0)+COALESCE(tb3.ac,0)+COALESCE(tb3.as_v,0)+COALESCE(tb3.arv,0)
|
||
END,
|
||
NULL::DATE,
|
||
CASE WHEN s_q3 THEN tb3.pa END,
|
||
NULL::VARCHAR, -- pay_comment
|
||
NULL::VARCHAR, -- pay_act
|
||
CASE WHEN s_q3 THEN tb3.bk END, -- booking (rollup)
|
||
CASE WHEN s_q3 THEN tb3.m1 END, CASE WHEN s_q3 THEN tb3.m2 END, CASE WHEN s_q3 THEN tb3.m3 END,
|
||
CASE WHEN s_q3 THEN COALESCE(tb3.m1,0)+COALESCE(tb3.m2,0)+COALESCE(tb3.m3,0) END,
|
||
-- residual_after_booking = q3.cp − Σ booking
|
||
CASE WHEN s_q3 THEN
|
||
(COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sr3,0)+COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0)+COALESCE(tb3.ac,0)+COALESCE(tb3.as_v,0)+COALESCE(tb3.arv,0))
|
||
- COALESCE(tb3.bk,0)
|
||
END,
|
||
-- residual_after_actual = q3.cp − Σ actual
|
||
CASE WHEN s_q3 THEN
|
||
(COALESCE(ta.sp3,0)+COALESCE(ta.sd3,0)+COALESCE(ta.sg3,0)+COALESCE(ta.sr3,0)+COALESCE(tb1.tq3,0)+COALESCE(tb2.tq3,0)+COALESCE(tb3.rev,0)+COALESCE(tb3.ac,0)+COALESCE(tb3.as_v,0)+COALESCE(tb3.arv,0))
|
||
- (COALESCE(tb3.m1,0)+COALESCE(tb3.m2,0)+COALESCE(tb3.m3,0))
|
||
END,
|
||
CASE WHEN s_q3 THEN tb3.tq4 END,
|
||
CASE WHEN s_q3 THEN tb3.tda END, CASE WHEN s_q3 THEN tb3.tdp END, CASE WHEN s_q3 THEN tb3.terf END,
|
||
NULL::VARCHAR, -- transfer_next_comment (text, no rollup)
|
||
CASE WHEN s_q3 THEN tb3.te END,
|
||
-- total = SUM transfer_q4_reasons + transfer_econ (Excel EW)
|
||
CASE WHEN s_q3 THEN
|
||
COALESCE(tb3.tda,0)+COALESCE(tb3.tdp,0)+COALESCE(tb3.terf,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.sg4,0)+COALESCE(ta.sr4,0)+COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0)
|
||
END,
|
||
CASE WHEN s_q4 THEN tb4.ac END, CASE WHEN s_q4 THEN tb4.as_v END, CASE WHEN s_q4 THEN tb4.arv END,
|
||
NULL::VARCHAR,
|
||
CASE WHEN s_q4 THEN
|
||
COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0)+COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0)+COALESCE(tb4.ac,0)+COALESCE(tb4.as_v,0)+COALESCE(tb4.arv,0)
|
||
END,
|
||
NULL::DATE,
|
||
CASE WHEN s_q4 THEN tb4.pa END,
|
||
NULL::VARCHAR, -- pay_comment
|
||
NULL::VARCHAR, -- pay_act
|
||
CASE WHEN s_q4 THEN tb4.bk END, -- booking (rollup)
|
||
CASE WHEN s_q4 THEN tb4.m1 END, CASE WHEN s_q4 THEN tb4.m2 END, CASE WHEN s_q4 THEN tb4.m3 END,
|
||
CASE WHEN s_q4 THEN tb4.spod END,
|
||
CASE WHEN s_q4 THEN COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0) END,
|
||
-- residual_after_booking = q4.cp − Σ booking
|
||
CASE WHEN s_q4 THEN
|
||
(COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0)+COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0)+COALESCE(tb4.ac,0)+COALESCE(tb4.as_v,0)+COALESCE(tb4.arv,0))
|
||
- COALESCE(tb4.bk,0)
|
||
END,
|
||
-- residual_after_actual = q4.cp − Σ actual (incl. spod)
|
||
CASE WHEN s_q4 THEN
|
||
(COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0)+COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0)+COALESCE(tb4.ac,0)+COALESCE(tb4.as_v,0)+COALESCE(tb4.arv,0))
|
||
- (COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0))
|
||
END,
|
||
CASE WHEN s_q4 THEN tb4.te END,
|
||
-- q4.total = q4.transfer_econ (Q4 has no other outgoing transfers)
|
||
CASE WHEN s_q4 THEN tb4.te 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,
|
||
CASE WHEN s_tot THEN COALESCE(tb1.pa,0)+COALESCE(tb2.pa,0)+COALESCE(tb3.pa,0)+COALESCE(tb4.pa,0) END,
|
||
-- economy_year = q4.corrected_plan − q4.actual_quarter (Excel GA = FY = FK − FW)
|
||
CASE WHEN s_tot THEN
|
||
(COALESCE(ta.sp4,0)+COALESCE(ta.sd4,0)+COALESCE(ta.sg4,0)+COALESCE(ta.sr4,0)
|
||
+COALESCE(tb1.tq4,0)+COALESCE(tb2.tq4,0)+COALESCE(tb3.tq4,0)+COALESCE(tb4.rev,0)
|
||
+COALESCE(tb4.ac,0)+COALESCE(tb4.as_v,0)+COALESCE(tb4.arv,0))
|
||
- (COALESCE(tb4.m1,0)+COALESCE(tb4.m2,0)+COALESCE(tb4.m3,0)+COALESCE(tb4.spod,0))
|
||
END,
|
||
|
||
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$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form2_sheet_jsonb(int4, varchar, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form2_sheet_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, seq_ssp_data jsonb, approved_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 'seq_ssp' = ANY(p_sections)) AS s_seq_s,
|
||
(p_sections IS NULL OR 'approved' = ANY(p_sections)) AS s_appr,
|
||
(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,
|
||
'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_seq_s THEN jsonb_build_object(
|
||
'q1', s.col_seq_ssp_q1, 'q2', s.col_seq_ssp_q2,
|
||
'q3', s.col_seq_ssp_q3, 'q4', s.col_seq_ssp_q4,
|
||
'year', s.col_seq_ssp_year,
|
||
'justification', s.col_seq_ssp_just
|
||
) END AS seq_ssp_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_cd THEN jsonb_build_object(
|
||
'counterparty', s.col_cd_counterparty,
|
||
'reference', s.col_cd_reference,
|
||
'date', s.col_cd_date,
|
||
'subject', s.col_cd_subject,
|
||
'currency', s.col_cd_currency,
|
||
'ceiling', s.col_cd_ceiling,
|
||
'vat_rate', s.col_cd_vat_rate,
|
||
'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_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_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_q2_delay_acts', s.col_q1_transfer_q2_delay_acts,
|
||
'transfer_q2_delay_procurement', s.col_q1_transfer_q2_delay_procurement,
|
||
'transfer_q2_economy_rf', s.col_q1_transfer_q2_economy_rf,
|
||
'transfer_next_comment', s.col_q1_transfer_next_comment,
|
||
'transfer_q3', s.col_q1_transfer_q3,
|
||
'transfer_q4', s.col_q1_transfer_q4,
|
||
'transfer_far_comment', s.col_q1_transfer_far_comment,
|
||
'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_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_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_q3_delay_acts', s.col_q2_transfer_q3_delay_acts,
|
||
'transfer_q3_delay_procurement', s.col_q2_transfer_q3_delay_procurement,
|
||
'transfer_q3_economy_rf', s.col_q2_transfer_q3_economy_rf,
|
||
'transfer_next_comment', s.col_q2_transfer_next_comment,
|
||
'transfer_q4', s.col_q2_transfer_q4,
|
||
'transfer_far_comment', s.col_q2_transfer_far_comment,
|
||
'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_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_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_q4_delay_acts', s.col_q3_transfer_q4_delay_acts,
|
||
'transfer_q4_delay_procurement', s.col_q3_transfer_q4_delay_procurement,
|
||
'transfer_q4_economy_rf', s.col_q3_transfer_q4_economy_rf,
|
||
'transfer_next_comment', s.col_q3_transfer_next_comment,
|
||
'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_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_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,
|
||
'pay_year', s.col_pay_year,
|
||
'economy_year', s.col_economy_year
|
||
) END AS totals_data,
|
||
|
||
s._sort_path
|
||
FROM v3.v_form2_sheet_sections(p_form_id, p_sheet, p_sections) s
|
||
CROSS JOIN flags f
|
||
ORDER BY s._sort_path;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form2_smeta(int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form2_smeta(p_form_id integer)
|
||
RETURNS TABLE(row_type character varying, depth integer, section_code character varying, name character varying, plan_supp_q1 numeric, plan_supp_q2 numeric, plan_supp_q3 numeric, plan_supp_q4 numeric, plan_supp_year numeric, plan_dev_q1 numeric, plan_dev_q2 numeric, plan_dev_q3 numeric, plan_dev_q4 numeric, plan_dev_year numeric, appr_supp_q1 numeric, appr_supp_q2 numeric, appr_supp_q3 numeric, appr_supp_q4 numeric, appr_supp_year numeric, appr_dev_q1 numeric, appr_dev_q2 numeric, appr_dev_q3 numeric, appr_dev_q4 numeric, appr_dev_year numeric, fact_supp_q1 numeric, fact_supp_q2 numeric, fact_supp_q3 numeric, fact_supp_q4 numeric, fact_supp_year numeric, fact_dev_q1 numeric, fact_dev_q2 numeric, fact_dev_q3 numeric, fact_dev_q4 numeric, fact_dev_year numeric, corr_supp_q2 numeric, corr_supp_q3 numeric, corr_supp_q4 numeric, corr_dev_q2 numeric, corr_dev_q3 numeric, corr_dev_q4 numeric)
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
AS $function$
|
||
#variable_conflict use_column
|
||
BEGIN
|
||
RETURN QUERY
|
||
WITH
|
||
-- Per-line per-direction агрегаты квартальных данных
|
||
q_per_line AS (
|
||
SELECT line_id,
|
||
SUM(CASE WHEN quarter=1 THEN COALESCE(actual_m1,0)+COALESCE(actual_m2,0)+COALESCE(actual_m3,0) END) AS fq1,
|
||
SUM(CASE WHEN quarter=2 THEN COALESCE(actual_m1,0)+COALESCE(actual_m2,0)+COALESCE(actual_m3,0) END) AS fq2,
|
||
SUM(CASE WHEN quarter=3 THEN COALESCE(actual_m1,0)+COALESCE(actual_m2,0)+COALESCE(actual_m3,0) END) AS fq3,
|
||
SUM(CASE WHEN quarter=4 THEN COALESCE(actual_m1,0)+COALESCE(actual_m2,0)+COALESCE(actual_m3,0)+COALESCE(actual_spod,0) END) AS fq4,
|
||
SUM(CASE WHEN quarter=1 THEN transfer_to_q2 END) AS t1q2,
|
||
SUM(CASE WHEN quarter=1 THEN transfer_to_q3 END) AS t1q3,
|
||
SUM(CASE WHEN quarter=1 THEN transfer_to_q4 END) AS t1q4,
|
||
SUM(CASE WHEN quarter=2 THEN transfer_to_q3 END) AS t2q3,
|
||
SUM(CASE WHEN quarter=2 THEN transfer_to_q4 END) AS t2q4,
|
||
SUM(CASE WHEN quarter=3 THEN transfer_to_q4 END) AS t3q4,
|
||
SUM(CASE WHEN quarter=1 THEN COALESCE(adj_current,0)+COALESCE(adj_ssp,0)+COALESCE(adj_reserve,0) END) AS adj1,
|
||
SUM(CASE WHEN quarter=2 THEN COALESCE(adj_current,0)+COALESCE(adj_ssp,0)+COALESCE(adj_reserve,0) END) AS adj2,
|
||
SUM(CASE WHEN quarter=3 THEN COALESCE(adj_current,0)+COALESCE(adj_ssp,0)+COALESCE(adj_reserve,0) END) AS adj3,
|
||
SUM(CASE WHEN quarter=4 THEN COALESCE(adj_current,0)+COALESCE(adj_ssp,0)+COALESCE(adj_reserve,0) END) AS adj4,
|
||
SUM(CASE WHEN quarter=2 THEN COALESCE(plan_revision_eco_change,0)+COALESCE(plan_revision_item_adj,0)+COALESCE(plan_revision_increase,0)+COALESCE(plan_revision_sequester,0) END) AS rev2,
|
||
SUM(CASE WHEN quarter=3 THEN COALESCE(plan_revision_eco_change,0)+COALESCE(plan_revision_item_adj,0)+COALESCE(plan_revision_increase,0)+COALESCE(plan_revision_sequester,0) END) AS rev3,
|
||
SUM(CASE WHEN quarter=4 THEN COALESCE(plan_revision_eco_change,0)+COALESCE(plan_revision_item_adj,0)+COALESCE(plan_revision_increase,0)+COALESCE(plan_revision_sequester,0) END) AS rev4
|
||
FROM v3.budget_line_quarter GROUP BY line_id
|
||
),
|
||
seq_dfip AS (
|
||
SELECT line_id, SUM(adj_q1) AS s1, SUM(adj_q2) AS s2, SUM(adj_q3) AS s3, SUM(adj_q4) AS s4
|
||
FROM v3.sequestration WHERE actor='DFIP' GROUP BY line_id
|
||
),
|
||
seq_ssp AS (
|
||
SELECT line_id, SUM(adj_q1) AS g1, SUM(adj_q2) AS g2, SUM(adj_q3) AS g3, SUM(adj_q4) AS g4
|
||
FROM v3.sequestration WHERE actor='SSP_GO' GROUP BY line_id
|
||
),
|
||
-- Per-line aggregate: учёт direction из expense_item
|
||
line_agg AS (
|
||
SELECT
|
||
bl.expense_item_id AS eid, ei.direction AS dir,
|
||
SUM(p.plan_q1) AS pq1, SUM(p.plan_q2) AS pq2, SUM(p.plan_q3) AS pq3, SUM(p.plan_q4) AS pq4,
|
||
SUM(sd.s1) AS sd1, SUM(sd.s2) AS sd2, SUM(sd.s3) AS sd3, SUM(sd.s4) AS sd4,
|
||
SUM(sg.g1) AS sg1, SUM(sg.g2) AS sg2, SUM(sg.g3) AS sg3, SUM(sg.g4) AS sg4,
|
||
SUM(r.amount_q1) AS rq1, SUM(r.amount_q2) AS rq2, SUM(r.amount_q3) AS rq3, SUM(r.amount_q4) AS rq4,
|
||
SUM(qp.fq1) AS fq1, SUM(qp.fq2) AS fq2, SUM(qp.fq3) AS fq3, SUM(qp.fq4) AS fq4,
|
||
SUM(qp.t1q2) AS t1q2, SUM(qp.t1q3) AS t1q3, SUM(qp.t1q4) AS t1q4,
|
||
SUM(qp.t2q3) AS t2q3, SUM(qp.t2q4) AS t2q4, SUM(qp.t3q4) AS t3q4,
|
||
SUM(qp.adj1) AS adj1, SUM(qp.adj2) AS adj2, SUM(qp.adj3) AS adj3, SUM(qp.adj4) AS adj4,
|
||
SUM(qp.rev2) AS rev2, SUM(qp.rev3) AS rev3, SUM(qp.rev4) AS rev4
|
||
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
|
||
LEFT JOIN seq_dfip sd ON sd.line_id = bl.id
|
||
LEFT JOIN seq_ssp sg ON sg.line_id = bl.id
|
||
LEFT JOIN v3.reserve r ON r.line_id = bl.id
|
||
LEFT JOIN q_per_line qp ON qp.line_id = bl.id
|
||
WHERE bl.budget_form_id = p_form_id
|
||
GROUP BY bl.expense_item_id, ei.direction
|
||
),
|
||
-- Tree rollup с расщеплением по direction. Используем FILTER (WHERE)
|
||
tw_rollup AS (
|
||
SELECT
|
||
t.id, t.depth, t.item_id AS section_code, t.name,
|
||
-- Поддержка
|
||
SUM(a.pq1) FILTER (WHERE a.dir='Support') AS s_pq1,
|
||
SUM(a.pq2) FILTER (WHERE a.dir='Support') AS s_pq2,
|
||
SUM(a.pq3) FILTER (WHERE a.dir='Support') AS s_pq3,
|
||
SUM(a.pq4) FILTER (WHERE a.dir='Support') AS s_pq4,
|
||
SUM(a.sd1) FILTER (WHERE a.dir='Support') AS s_sd1,
|
||
SUM(a.sd2) FILTER (WHERE a.dir='Support') AS s_sd2,
|
||
SUM(a.sd3) FILTER (WHERE a.dir='Support') AS s_sd3,
|
||
SUM(a.sd4) FILTER (WHERE a.dir='Support') AS s_sd4,
|
||
SUM(a.sg1) FILTER (WHERE a.dir='Support') AS s_sg1,
|
||
SUM(a.sg2) FILTER (WHERE a.dir='Support') AS s_sg2,
|
||
SUM(a.sg3) FILTER (WHERE a.dir='Support') AS s_sg3,
|
||
SUM(a.sg4) FILTER (WHERE a.dir='Support') AS s_sg4,
|
||
SUM(a.rq1) FILTER (WHERE a.dir='Support') AS s_rq1,
|
||
SUM(a.rq2) FILTER (WHERE a.dir='Support') AS s_rq2,
|
||
SUM(a.rq3) FILTER (WHERE a.dir='Support') AS s_rq3,
|
||
SUM(a.rq4) FILTER (WHERE a.dir='Support') AS s_rq4,
|
||
SUM(a.fq1) FILTER (WHERE a.dir='Support') AS s_fq1,
|
||
SUM(a.fq2) FILTER (WHERE a.dir='Support') AS s_fq2,
|
||
SUM(a.fq3) FILTER (WHERE a.dir='Support') AS s_fq3,
|
||
SUM(a.fq4) FILTER (WHERE a.dir='Support') AS s_fq4,
|
||
SUM(a.t1q2) FILTER (WHERE a.dir='Support') AS s_t1q2,
|
||
SUM(a.t1q3) FILTER (WHERE a.dir='Support') AS s_t1q3,
|
||
SUM(a.t1q4) FILTER (WHERE a.dir='Support') AS s_t1q4,
|
||
SUM(a.t2q3) FILTER (WHERE a.dir='Support') AS s_t2q3,
|
||
SUM(a.t2q4) FILTER (WHERE a.dir='Support') AS s_t2q4,
|
||
SUM(a.t3q4) FILTER (WHERE a.dir='Support') AS s_t3q4,
|
||
SUM(a.adj1) FILTER (WHERE a.dir='Support') AS s_adj1,
|
||
SUM(a.adj2) FILTER (WHERE a.dir='Support') AS s_adj2,
|
||
SUM(a.adj3) FILTER (WHERE a.dir='Support') AS s_adj3,
|
||
SUM(a.adj4) FILTER (WHERE a.dir='Support') AS s_adj4,
|
||
SUM(a.rev2) FILTER (WHERE a.dir='Support') AS s_rev2,
|
||
SUM(a.rev3) FILTER (WHERE a.dir='Support') AS s_rev3,
|
||
SUM(a.rev4) FILTER (WHERE a.dir='Support') AS s_rev4,
|
||
-- Развитие
|
||
SUM(a.pq1) FILTER (WHERE a.dir='Development') AS d_pq1,
|
||
SUM(a.pq2) FILTER (WHERE a.dir='Development') AS d_pq2,
|
||
SUM(a.pq3) FILTER (WHERE a.dir='Development') AS d_pq3,
|
||
SUM(a.pq4) FILTER (WHERE a.dir='Development') AS d_pq4,
|
||
SUM(a.sd1) FILTER (WHERE a.dir='Development') AS d_sd1,
|
||
SUM(a.sd2) FILTER (WHERE a.dir='Development') AS d_sd2,
|
||
SUM(a.sd3) FILTER (WHERE a.dir='Development') AS d_sd3,
|
||
SUM(a.sd4) FILTER (WHERE a.dir='Development') AS d_sd4,
|
||
SUM(a.sg1) FILTER (WHERE a.dir='Development') AS d_sg1,
|
||
SUM(a.sg2) FILTER (WHERE a.dir='Development') AS d_sg2,
|
||
SUM(a.sg3) FILTER (WHERE a.dir='Development') AS d_sg3,
|
||
SUM(a.sg4) FILTER (WHERE a.dir='Development') AS d_sg4,
|
||
SUM(a.rq1) FILTER (WHERE a.dir='Development') AS d_rq1,
|
||
SUM(a.rq2) FILTER (WHERE a.dir='Development') AS d_rq2,
|
||
SUM(a.rq3) FILTER (WHERE a.dir='Development') AS d_rq3,
|
||
SUM(a.rq4) FILTER (WHERE a.dir='Development') AS d_rq4,
|
||
SUM(a.fq1) FILTER (WHERE a.dir='Development') AS d_fq1,
|
||
SUM(a.fq2) FILTER (WHERE a.dir='Development') AS d_fq2,
|
||
SUM(a.fq3) FILTER (WHERE a.dir='Development') AS d_fq3,
|
||
SUM(a.fq4) FILTER (WHERE a.dir='Development') AS d_fq4,
|
||
SUM(a.t1q2) FILTER (WHERE a.dir='Development') AS d_t1q2,
|
||
SUM(a.t1q3) FILTER (WHERE a.dir='Development') AS d_t1q3,
|
||
SUM(a.t1q4) FILTER (WHERE a.dir='Development') AS d_t1q4,
|
||
SUM(a.t2q3) FILTER (WHERE a.dir='Development') AS d_t2q3,
|
||
SUM(a.t2q4) FILTER (WHERE a.dir='Development') AS d_t2q4,
|
||
SUM(a.t3q4) FILTER (WHERE a.dir='Development') AS d_t3q4,
|
||
SUM(a.adj1) FILTER (WHERE a.dir='Development') AS d_adj1,
|
||
SUM(a.adj2) FILTER (WHERE a.dir='Development') AS d_adj2,
|
||
SUM(a.adj3) FILTER (WHERE a.dir='Development') AS d_adj3,
|
||
SUM(a.adj4) FILTER (WHERE a.dir='Development') AS d_adj4,
|
||
SUM(a.rev2) FILTER (WHERE a.dir='Development') AS d_rev2,
|
||
SUM(a.rev3) FILTER (WHERE a.dir='Development') AS d_rev3,
|
||
SUM(a.rev4) FILTER (WHERE a.dir='Development') AS d_rev4
|
||
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
|
||
LEFT JOIN line_agg a ON a.eid = ANY(t.desc_ids)
|
||
WHERE t.depth <= 2
|
||
GROUP BY t.id, t.depth, t.item_id, t.name
|
||
)
|
||
SELECT
|
||
CASE r.depth WHEN 0 THEN 'ROOT' WHEN 1 THEN 'GROUP' WHEN 2 THEN 'ITEM' END::VARCHAR,
|
||
r.depth,
|
||
r.section_code::VARCHAR,
|
||
r.name::VARCHAR,
|
||
-- Plan supp / dev
|
||
r.s_pq1, r.s_pq2, r.s_pq3, r.s_pq4,
|
||
COALESCE(r.s_pq1,0)+COALESCE(r.s_pq2,0)+COALESCE(r.s_pq3,0)+COALESCE(r.s_pq4,0),
|
||
r.d_pq1, r.d_pq2, r.d_pq3, r.d_pq4,
|
||
COALESCE(r.d_pq1,0)+COALESCE(r.d_pq2,0)+COALESCE(r.d_pq3,0)+COALESCE(r.d_pq4,0),
|
||
-- Approved supp = plan + seq_dfip + seq_ssp + reserve
|
||
COALESCE(r.s_pq1,0)+COALESCE(r.s_sd1,0)+COALESCE(r.s_sg1,0)+COALESCE(r.s_rq1,0),
|
||
COALESCE(r.s_pq2,0)+COALESCE(r.s_sd2,0)+COALESCE(r.s_sg2,0)+COALESCE(r.s_rq2,0),
|
||
COALESCE(r.s_pq3,0)+COALESCE(r.s_sd3,0)+COALESCE(r.s_sg3,0)+COALESCE(r.s_rq3,0),
|
||
COALESCE(r.s_pq4,0)+COALESCE(r.s_sd4,0)+COALESCE(r.s_sg4,0)+COALESCE(r.s_rq4,0),
|
||
COALESCE(r.s_pq1,0)+COALESCE(r.s_sd1,0)+COALESCE(r.s_sg1,0)+COALESCE(r.s_rq1,0)
|
||
+COALESCE(r.s_pq2,0)+COALESCE(r.s_sd2,0)+COALESCE(r.s_sg2,0)+COALESCE(r.s_rq2,0)
|
||
+COALESCE(r.s_pq3,0)+COALESCE(r.s_sd3,0)+COALESCE(r.s_sg3,0)+COALESCE(r.s_rq3,0)
|
||
+COALESCE(r.s_pq4,0)+COALESCE(r.s_sd4,0)+COALESCE(r.s_sg4,0)+COALESCE(r.s_rq4,0),
|
||
-- Approved dev
|
||
COALESCE(r.d_pq1,0)+COALESCE(r.d_sd1,0)+COALESCE(r.d_sg1,0)+COALESCE(r.d_rq1,0),
|
||
COALESCE(r.d_pq2,0)+COALESCE(r.d_sd2,0)+COALESCE(r.d_sg2,0)+COALESCE(r.d_rq2,0),
|
||
COALESCE(r.d_pq3,0)+COALESCE(r.d_sd3,0)+COALESCE(r.d_sg3,0)+COALESCE(r.d_rq3,0),
|
||
COALESCE(r.d_pq4,0)+COALESCE(r.d_sd4,0)+COALESCE(r.d_sg4,0)+COALESCE(r.d_rq4,0),
|
||
COALESCE(r.d_pq1,0)+COALESCE(r.d_sd1,0)+COALESCE(r.d_sg1,0)+COALESCE(r.d_rq1,0)
|
||
+COALESCE(r.d_pq2,0)+COALESCE(r.d_sd2,0)+COALESCE(r.d_sg2,0)+COALESCE(r.d_rq2,0)
|
||
+COALESCE(r.d_pq3,0)+COALESCE(r.d_sd3,0)+COALESCE(r.d_sg3,0)+COALESCE(r.d_rq3,0)
|
||
+COALESCE(r.d_pq4,0)+COALESCE(r.d_sd4,0)+COALESCE(r.d_sg4,0)+COALESCE(r.d_rq4,0),
|
||
-- Fact supp / dev
|
||
r.s_fq1, r.s_fq2, r.s_fq3, r.s_fq4,
|
||
COALESCE(r.s_fq1,0)+COALESCE(r.s_fq2,0)+COALESCE(r.s_fq3,0)+COALESCE(r.s_fq4,0),
|
||
r.d_fq1, r.d_fq2, r.d_fq3, r.d_fq4,
|
||
COALESCE(r.d_fq1,0)+COALESCE(r.d_fq2,0)+COALESCE(r.d_fq3,0)+COALESCE(r.d_fq4,0),
|
||
-- Corrected supp Q2..Q4 = approved_qN + transfers_into_qN + revisions + adj
|
||
COALESCE(r.s_pq2,0)+COALESCE(r.s_sd2,0)+COALESCE(r.s_sg2,0)+COALESCE(r.s_rq2,0)
|
||
+COALESCE(r.s_t1q2,0)+COALESCE(r.s_rev2,0)+COALESCE(r.s_adj2,0),
|
||
COALESCE(r.s_pq3,0)+COALESCE(r.s_sd3,0)+COALESCE(r.s_sg3,0)+COALESCE(r.s_rq3,0)
|
||
+COALESCE(r.s_t1q3,0)+COALESCE(r.s_t2q3,0)+COALESCE(r.s_rev3,0)+COALESCE(r.s_adj3,0),
|
||
COALESCE(r.s_pq4,0)+COALESCE(r.s_sd4,0)+COALESCE(r.s_sg4,0)+COALESCE(r.s_rq4,0)
|
||
+COALESCE(r.s_t1q4,0)+COALESCE(r.s_t2q4,0)+COALESCE(r.s_t3q4,0)+COALESCE(r.s_rev4,0)+COALESCE(r.s_adj4,0),
|
||
-- Corrected dev Q2..Q4
|
||
COALESCE(r.d_pq2,0)+COALESCE(r.d_sd2,0)+COALESCE(r.d_sg2,0)+COALESCE(r.d_rq2,0)
|
||
+COALESCE(r.d_t1q2,0)+COALESCE(r.d_rev2,0)+COALESCE(r.d_adj2,0),
|
||
COALESCE(r.d_pq3,0)+COALESCE(r.d_sd3,0)+COALESCE(r.d_sg3,0)+COALESCE(r.d_rq3,0)
|
||
+COALESCE(r.d_t1q3,0)+COALESCE(r.d_t2q3,0)+COALESCE(r.d_rev3,0)+COALESCE(r.d_adj3,0),
|
||
COALESCE(r.d_pq4,0)+COALESCE(r.d_sd4,0)+COALESCE(r.d_sg4,0)+COALESCE(r.d_rq4,0)
|
||
+COALESCE(r.d_t1q4,0)+COALESCE(r.d_t2q4,0)+COALESCE(r.d_t3q4,0)+COALESCE(r.d_rev4,0)+COALESCE(r.d_adj4,0)
|
||
FROM tw_rollup r
|
||
ORDER BY r.section_code;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form2_view(int4, varchar, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form2_view(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 plpgsql
|
||
STABLE
|
||
AS $function$
|
||
BEGIN
|
||
|
||
-- ═══ Основные листы АХР / КВ_П / Операц ════════════════════════════════
|
||
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,
|
||
'seq_dfip', j.seq_dfip_data,
|
||
'seq_ssp', j.seq_ssp_data,
|
||
'approved', j.approved_data,
|
||
'contract', j.contract_data,
|
||
'booking', j.booking_data,
|
||
'q1', j.q1_data,
|
||
'q2', j.q2_data,
|
||
'q3', j.q3_data,
|
||
'q4', j.q4_data,
|
||
'totals', j.totals_data
|
||
) AS data
|
||
FROM v3.v_form2_sheet_jsonb(p_form_id, p_sheet, p_sections) j
|
||
ORDER BY j._sort_path;
|
||
RETURN;
|
||
END IF;
|
||
|
||
-- ═══ АХР_лимит ═════════════════════════════════════════════════════════
|
||
-- SECTION/GROUP-строки агрегируют qty/amount по всем LEAF-потомкам
|
||
-- (Excel: J6=SUM(J7:J13) и т.д.). Дерево строится через parent_id.
|
||
--
|
||
-- Структура (limit_template) — глобальный каталог нормативов, единый на банк.
|
||
-- Значения qty/comment — per-form (form_limit ON budget_form_id = p_form_id).
|
||
-- amount_qN — computed по формуле:
|
||
-- amount_qN = qty_qN * limit_with_vat * period_factor(unit), где
|
||
-- period_factor = 1/4 если unit ~ '/год'
|
||
-- 3 если unit ~ '/месяц' (3 месяца в квартале)
|
||
-- 1 иначе (по умолчанию /квартал)
|
||
IF p_sheet = 'AHR_LIMIT' THEN
|
||
RETURN QUERY
|
||
WITH RECURSIVE tree AS (
|
||
SELECT id AS root_id, id AS desc_id FROM v3.limit_template
|
||
UNION
|
||
SELECT t.root_id, child.id
|
||
FROM tree t
|
||
JOIN v3.limit_template child ON child.parent_id = t.desc_id
|
||
),
|
||
leaf_amt AS (
|
||
-- per-LEAF computed amount по формуле; qty берётся из form_limit для p_form_id
|
||
SELECT lt.id,
|
||
fl.qty_q1, fl.qty_q2, fl.qty_q3, fl.qty_q4,
|
||
COALESCE(fl.qty_q1,0) * COALESCE(lt.limit_with_vat,0) *
|
||
CASE WHEN lt.unit LIKE '%/год' THEN 0.25
|
||
WHEN lt.unit LIKE '%/месяц' THEN 3
|
||
ELSE 1 END AS amount_q1,
|
||
COALESCE(fl.qty_q2,0) * COALESCE(lt.limit_with_vat,0) *
|
||
CASE WHEN lt.unit LIKE '%/год' THEN 0.25
|
||
WHEN lt.unit LIKE '%/месяц' THEN 3
|
||
ELSE 1 END AS amount_q2,
|
||
COALESCE(fl.qty_q3,0) * COALESCE(lt.limit_with_vat,0) *
|
||
CASE WHEN lt.unit LIKE '%/год' THEN 0.25
|
||
WHEN lt.unit LIKE '%/месяц' THEN 3
|
||
ELSE 1 END AS amount_q3,
|
||
COALESCE(fl.qty_q4,0) * COALESCE(lt.limit_with_vat,0) *
|
||
CASE WHEN lt.unit LIKE '%/год' THEN 0.25
|
||
WHEN lt.unit LIKE '%/месяц' THEN 3
|
||
ELSE 1 END AS amount_q4
|
||
FROM v3.limit_template lt
|
||
LEFT JOIN v3.form_limit fl
|
||
ON fl.template_id = lt.id AND fl.budget_form_id = p_form_id
|
||
WHERE lt.row_type = 'LEAF'
|
||
),
|
||
agg AS (
|
||
SELECT t.root_id,
|
||
SUM(la.qty_q1) AS qty_q1, SUM(la.amount_q1) AS amount_q1,
|
||
SUM(la.qty_q2) AS qty_q2, SUM(la.amount_q2) AS amount_q2,
|
||
SUM(la.qty_q3) AS qty_q3, SUM(la.amount_q3) AS amount_q3,
|
||
SUM(la.qty_q4) AS qty_q4, SUM(la.amount_q4) AS amount_q4
|
||
FROM tree t
|
||
JOIN leaf_amt la ON la.id = t.desc_id
|
||
GROUP BY t.root_id
|
||
)
|
||
SELECT
|
||
-- Маппинг физического хранения limit_template → DTO:
|
||
-- SECTION → ROOT, GROUP → GROUP, LEAF → INPUT (редактируемая ячейка).
|
||
CASE lt.row_type WHEN 'SECTION' THEN 'ROOT'
|
||
WHEN 'GROUP' THEN 'GROUP'
|
||
WHEN 'LEAF' THEN 'INPUT' END::VARCHAR,
|
||
CASE lt.row_type WHEN 'SECTION' THEN 0 WHEN 'GROUP' THEN 1 ELSE 2 END,
|
||
lt.sort_order::BIGINT,
|
||
jsonb_build_object(
|
||
'id', lt.id, -- LEAF id для адресации в write API (= template_id)
|
||
'section_no', lt.section_no,
|
||
'expense_item_code',lt.expense_item_code,
|
||
'name', lt.name,
|
||
'unit', lt.unit,
|
||
'limit_with_vat', lt.limit_with_vat,
|
||
'limit_without_vat',lt.limit_without_vat,
|
||
'qty_q1', a.qty_q1, 'amount_q1', a.amount_q1,
|
||
'qty_q2', a.qty_q2, 'amount_q2', a.amount_q2,
|
||
'qty_q3', a.qty_q3, 'amount_q3', a.amount_q3,
|
||
'qty_q4', a.qty_q4, 'amount_q4', a.amount_q4,
|
||
'comment', fl.comment
|
||
)
|
||
FROM v3.limit_template lt
|
||
LEFT JOIN agg a ON a.root_id = lt.id
|
||
LEFT JOIN v3.form_limit fl
|
||
ON fl.template_id = lt.id AND fl.budget_form_id = p_form_id
|
||
ORDER BY lt.sort_order;
|
||
RETURN;
|
||
END IF;
|
||
|
||
-- ═══ АХР_аренда ════════════════════════════════════════════════════════
|
||
IF p_sheet = 'AHR_RENT' THEN
|
||
RETURN QUERY
|
||
SELECT
|
||
'INPUT'::VARCHAR,
|
||
0,
|
||
ROW_NUMBER() OVER (ORDER BY rd.id) AS sort_order,
|
||
jsonb_build_object(
|
||
'id', rd.id, -- id rent_detail (для write API)
|
||
'line_id', rd.line_id,
|
||
'vsp_id', rd.vsp_id,
|
||
'address', v.address,
|
||
'object_type', v.format,
|
||
'rented_area', v.total_area,
|
||
'contract_number', rd.contract_number,
|
||
'contract_end_date',rd.contract_end_date,
|
||
'plan', jsonb_build_object('q1', rd.plan_q1, 'q2', rd.plan_q2, 'q3', rd.plan_q3, 'q4', rd.plan_q4,
|
||
'year', COALESCE(rd.plan_q1,0)+COALESCE(rd.plan_q2,0)+COALESCE(rd.plan_q3,0)+COALESCE(rd.plan_q4,0)),
|
||
'fact_q1', jsonb_build_object('jan', rd.actual_jan, 'feb', rd.actual_feb, 'mar', rd.actual_mar,
|
||
'total', COALESCE(rd.actual_jan,0)+COALESCE(rd.actual_feb,0)+COALESCE(rd.actual_mar,0)),
|
||
'fact_q2', jsonb_build_object('apr', rd.actual_apr, 'may', rd.actual_may, 'jun', rd.actual_jun,
|
||
'total', COALESCE(rd.actual_apr,0)+COALESCE(rd.actual_may,0)+COALESCE(rd.actual_jun,0)),
|
||
'fact_q3', jsonb_build_object('jul', rd.actual_jul, 'aug', rd.actual_aug, 'sep', rd.actual_sep,
|
||
'total', COALESCE(rd.actual_jul,0)+COALESCE(rd.actual_aug,0)+COALESCE(rd.actual_sep,0)),
|
||
'fact_q4', jsonb_build_object('oct', rd.actual_oct, 'nov', rd.actual_nov, 'dec', rd.actual_dec,
|
||
'total', COALESCE(rd.actual_oct,0)+COALESCE(rd.actual_nov,0)+COALESCE(rd.actual_dec,0)),
|
||
'comment', rd.comment
|
||
)
|
||
FROM v3.rent_detail rd
|
||
JOIN v3.budget_line bl ON bl.id = rd.line_id
|
||
JOIN v3.vsp v ON v.id = rd.vsp_id
|
||
WHERE bl.budget_form_id = p_form_id
|
||
ORDER BY rd.id;
|
||
RETURN;
|
||
END IF;
|
||
|
||
-- ═══ АХР_коммунал ═══════════════════════════════════════════════════════
|
||
IF p_sheet = 'AHR_UTILITY' THEN
|
||
RETURN QUERY
|
||
SELECT
|
||
'INPUT'::VARCHAR,
|
||
0,
|
||
ROW_NUMBER() OVER (ORDER BY ud.id) AS sort_order,
|
||
jsonb_build_object(
|
||
'id', ud.id, -- id utility_detail (для write API)
|
||
'line_id', ud.line_id,
|
||
'vsp_id', ud.vsp_id,
|
||
'address', v.address,
|
||
'object_type', v.format,
|
||
'object_area', v.total_area,
|
||
'contract_number', ud.contract_number,
|
||
'contract_end_date',ud.contract_end_date,
|
||
'plan', jsonb_build_object('q1', ud.plan_q1, 'q2', ud.plan_q2, 'q3', ud.plan_q3, 'q4', ud.plan_q4,
|
||
'year', COALESCE(ud.plan_q1,0)+COALESCE(ud.plan_q2,0)+COALESCE(ud.plan_q3,0)+COALESCE(ud.plan_q4,0)),
|
||
'fact_q1', jsonb_build_object('jan', ud.actual_jan, 'feb', ud.actual_feb, 'mar', ud.actual_mar,
|
||
'total', COALESCE(ud.actual_jan,0)+COALESCE(ud.actual_feb,0)+COALESCE(ud.actual_mar,0)),
|
||
'fact_q2', jsonb_build_object('apr', ud.actual_apr, 'may', ud.actual_may, 'jun', ud.actual_jun,
|
||
'total', COALESCE(ud.actual_apr,0)+COALESCE(ud.actual_may,0)+COALESCE(ud.actual_jun,0)),
|
||
'fact_q3', jsonb_build_object('jul', ud.actual_jul, 'aug', ud.actual_aug, 'sep', ud.actual_sep,
|
||
'total', COALESCE(ud.actual_jul,0)+COALESCE(ud.actual_aug,0)+COALESCE(ud.actual_sep,0)),
|
||
'fact_q4', jsonb_build_object('oct', ud.actual_oct, 'nov', ud.actual_nov, 'dec', ud.actual_dec,
|
||
'total', COALESCE(ud.actual_oct,0)+COALESCE(ud.actual_nov,0)+COALESCE(ud.actual_dec,0)),
|
||
'comment', ud.comment
|
||
)
|
||
FROM v3.utility_detail ud
|
||
JOIN v3.budget_line bl ON bl.id = ud.line_id
|
||
JOIN v3.vsp v ON v.id = ud.vsp_id
|
||
WHERE bl.budget_form_id = p_form_id
|
||
ORDER BY ud.id;
|
||
RETURN;
|
||
END IF;
|
||
|
||
-- ═══ АХР_охрана ═════════════════════════════════════════════════════════
|
||
IF p_sheet = 'AHR_SECURITY' THEN
|
||
RETURN QUERY
|
||
SELECT
|
||
'INPUT'::VARCHAR,
|
||
0,
|
||
ROW_NUMBER() OVER (ORDER BY sd.id) AS sort_order,
|
||
jsonb_build_object(
|
||
'id', sd.id, -- id security_detail (для write API)
|
||
'line_id', sd.line_id,
|
||
'vsp_id', sd.vsp_id,
|
||
'address', v.address,
|
||
'object_type', v.format,
|
||
'object_area', v.total_area,
|
||
'contract_number', sd.contract_number,
|
||
'contract_end_date',sd.contract_end_date,
|
||
'plan', jsonb_build_object('q1', sd.plan_q1, 'q2', sd.plan_q2, 'q3', sd.plan_q3, 'q4', sd.plan_q4,
|
||
'year', COALESCE(sd.plan_q1,0)+COALESCE(sd.plan_q2,0)+COALESCE(sd.plan_q3,0)+COALESCE(sd.plan_q4,0)),
|
||
'fact_q1', jsonb_build_object('jan', sd.actual_jan, 'feb', sd.actual_feb, 'mar', sd.actual_mar,
|
||
'total', COALESCE(sd.actual_jan,0)+COALESCE(sd.actual_feb,0)+COALESCE(sd.actual_mar,0)),
|
||
'fact_q2', jsonb_build_object('apr', sd.actual_apr, 'may', sd.actual_may, 'jun', sd.actual_jun,
|
||
'total', COALESCE(sd.actual_apr,0)+COALESCE(sd.actual_may,0)+COALESCE(sd.actual_jun,0)),
|
||
'fact_q3', jsonb_build_object('jul', sd.actual_jul, 'aug', sd.actual_aug, 'sep', sd.actual_sep,
|
||
'total', COALESCE(sd.actual_jul,0)+COALESCE(sd.actual_aug,0)+COALESCE(sd.actual_sep,0)),
|
||
'fact_q4', jsonb_build_object('oct', sd.actual_oct, 'nov', sd.actual_nov, 'dec', sd.actual_dec,
|
||
'total', COALESCE(sd.actual_oct,0)+COALESCE(sd.actual_nov,0)+COALESCE(sd.actual_dec,0)),
|
||
'comment', sd.comment
|
||
)
|
||
FROM v3.security_detail sd
|
||
JOIN v3.budget_line bl ON bl.id = sd.line_id
|
||
JOIN v3.vsp v ON v.id = sd.vsp_id
|
||
WHERE bl.budget_form_id = p_form_id
|
||
ORDER BY sd.id;
|
||
RETURN;
|
||
END IF;
|
||
|
||
-- ═══ Отч.9ф (Fixed Asset Report) ════════════════════════════════════════
|
||
-- Одна строка = (expense_item × equipment_name × month).
|
||
--
|
||
-- opening_qty/amt_604/60415 хранятся в БД ТОЛЬКО для month=1 (база года).
|
||
-- Для month=2..12 вычисляются на лету: накопительная сумма дельт по
|
||
-- предыдущим месяцам от base (Excel: opening_M2 = F1+H1−J1).
|
||
--
|
||
-- totals.* — также computed (acquired_total, disposed_total, balance_qty/amt).
|
||
IF p_sheet = 'OTCH9F' THEN
|
||
RETURN QUERY
|
||
WITH base AS (
|
||
SELECT far.id, far.budget_form_id, far.expense_item_id,
|
||
far.equipment_name, far.month,
|
||
far.acquired_qty_604, far.acquired_amt_604,
|
||
far.disposed_qty_604, far.disposed_amt_604,
|
||
far.acquired_qty_60415, far.acquired_amt_60415,
|
||
far.transferred_qty_60415, far.transferred_amt_60415,
|
||
far.go_balance_only_amt,
|
||
-- база: opening_qty/amt из month=1 этой группы
|
||
FIRST_VALUE(COALESCE(far.opening_qty_604,0))
|
||
OVER w AS base_qty_604,
|
||
FIRST_VALUE(COALESCE(far.opening_amt_604,0))
|
||
OVER w AS base_amt_604,
|
||
FIRST_VALUE(COALESCE(far.opening_qty_60415,0))
|
||
OVER w AS base_qty_60415,
|
||
FIRST_VALUE(COALESCE(far.opening_amt_60415,0))
|
||
OVER w AS base_amt_60415,
|
||
-- кумулятивная дельта по предыдущим месяцам в группе
|
||
COALESCE(SUM(COALESCE(far.acquired_qty_604,0) - COALESCE(far.disposed_qty_604,0))
|
||
OVER w_prev, 0) AS cum_dq_604,
|
||
COALESCE(SUM(COALESCE(far.acquired_amt_604,0) - COALESCE(far.disposed_amt_604,0))
|
||
OVER w_prev, 0) AS cum_da_604,
|
||
COALESCE(SUM(COALESCE(far.acquired_qty_60415,0) - COALESCE(far.transferred_qty_60415,0))
|
||
OVER w_prev, 0) AS cum_dq_60415,
|
||
COALESCE(SUM(COALESCE(far.acquired_amt_60415,0) - COALESCE(far.transferred_amt_60415,0))
|
||
OVER w_prev, 0) AS cum_da_60415
|
||
FROM v3.fixed_asset_report far
|
||
WHERE far.budget_form_id = p_form_id
|
||
WINDOW w AS (PARTITION BY far.budget_form_id, far.expense_item_id, far.equipment_name
|
||
ORDER BY far.month),
|
||
w_prev AS (PARTITION BY far.budget_form_id, far.expense_item_id, far.equipment_name
|
||
ORDER BY far.month
|
||
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)
|
||
),
|
||
eff AS (
|
||
SELECT b.*,
|
||
(b.base_qty_604 + b.cum_dq_604) AS opening_qty_604_eff,
|
||
(b.base_amt_604 + b.cum_da_604) AS opening_amt_604_eff,
|
||
(b.base_qty_60415 + b.cum_dq_60415) AS opening_qty_60415_eff,
|
||
(b.base_amt_60415 + b.cum_da_60415) AS opening_amt_60415_eff
|
||
FROM base b
|
||
)
|
||
SELECT
|
||
'INPUT'::VARCHAR,
|
||
0,
|
||
ROW_NUMBER() OVER (ORDER BY eff.expense_item_id, eff.month) AS sort_order,
|
||
jsonb_build_object(
|
||
'id', eff.id, -- fixed_asset_report.id (для write API)
|
||
'expense_item_id', ei.id,
|
||
'item_id', ei.item_id,
|
||
'section_code', ei.section_code,
|
||
'equipment_name', eff.equipment_name,
|
||
'month', eff.month,
|
||
'b_604', jsonb_build_object(
|
||
'opening_qty', eff.opening_qty_604_eff,
|
||
'opening_amt', eff.opening_amt_604_eff,
|
||
'acquired_qty', eff.acquired_qty_604,
|
||
'acquired_amt', eff.acquired_amt_604,
|
||
'disposed_qty', eff.disposed_qty_604,
|
||
'disposed_amt', eff.disposed_amt_604
|
||
),
|
||
'b_60415', jsonb_build_object(
|
||
'opening_qty', eff.opening_qty_60415_eff,
|
||
'opening_amt', eff.opening_amt_60415_eff,
|
||
'acquired_qty', eff.acquired_qty_60415,
|
||
'acquired_amt', eff.acquired_amt_60415,
|
||
'transferred_qty',eff.transferred_qty_60415,
|
||
'transferred_amt',eff.transferred_amt_60415
|
||
),
|
||
'totals', jsonb_build_object(
|
||
-- Excel R = SUM(I, O, S) − Q
|
||
'acquired_total', COALESCE(eff.acquired_amt_604,0)+COALESCE(eff.acquired_amt_60415,0)
|
||
+COALESCE(eff.go_balance_only_amt,0)-COALESCE(eff.transferred_amt_60415,0),
|
||
'go_balance_only',eff.go_balance_only_amt,
|
||
-- Excel T = K
|
||
'disposed_total', COALESCE(eff.disposed_amt_604,0),
|
||
-- Excel U = (opening+acquired−disposed)_604 + (opening+acquired−transferred)_60415
|
||
'balance_qty', (eff.opening_qty_604_eff + COALESCE(eff.acquired_qty_604,0) - COALESCE(eff.disposed_qty_604,0))
|
||
+ (eff.opening_qty_60415_eff + COALESCE(eff.acquired_qty_60415,0) - COALESCE(eff.transferred_qty_60415,0)),
|
||
'balance_amt', (eff.opening_amt_604_eff + COALESCE(eff.acquired_amt_604,0) - COALESCE(eff.disposed_amt_604,0))
|
||
+ (eff.opening_amt_60415_eff + COALESCE(eff.acquired_amt_60415,0) - COALESCE(eff.transferred_amt_60415,0))
|
||
)
|
||
)
|
||
FROM eff
|
||
JOIN v3.expense_item ei ON ei.id = eff.expense_item_id
|
||
ORDER BY eff.expense_item_id, eff.month;
|
||
RETURN;
|
||
END IF;
|
||
|
||
-- ═══ Смета_2026 ═════════════════════════════════════════════════════════
|
||
IF p_sheet = 'SMETA' THEN
|
||
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.plan_supp_q1, 'q2', sm.plan_supp_q2, 'q3', sm.plan_supp_q3, 'q4', sm.plan_supp_q4, 'year', sm.plan_supp_year),
|
||
'development', jsonb_build_object('q1', sm.plan_dev_q1, 'q2', sm.plan_dev_q2, 'q3', sm.plan_dev_q3, 'q4', sm.plan_dev_q4, 'year', sm.plan_dev_year)
|
||
),
|
||
'approved', jsonb_build_object(
|
||
'support', jsonb_build_object('q1', sm.appr_supp_q1, 'q2', sm.appr_supp_q2, 'q3', sm.appr_supp_q3, 'q4', sm.appr_supp_q4, 'year', sm.appr_supp_year),
|
||
'development', jsonb_build_object('q1', sm.appr_dev_q1, 'q2', sm.appr_dev_q2, 'q3', sm.appr_dev_q3, 'q4', sm.appr_dev_q4, 'year', sm.appr_dev_year)
|
||
),
|
||
'fact', jsonb_build_object(
|
||
'support', jsonb_build_object('q1', sm.fact_supp_q1, 'q2', sm.fact_supp_q2, 'q3', sm.fact_supp_q3, 'q4', sm.fact_supp_q4, 'year', sm.fact_supp_year),
|
||
'development', jsonb_build_object('q1', sm.fact_dev_q1, 'q2', sm.fact_dev_q2, 'q3', sm.fact_dev_q3, 'q4', sm.fact_dev_q4, 'year', sm.fact_dev_year)
|
||
),
|
||
-- corrected: только Q2/Q3/Q4 (см. шаблон Смета_2026, блок V-X). Q1 нет
|
||
-- (Q1 corrected ≡ approved_q1), year нет (q1+q2+q3+q4 даёт двойной счёт переносов).
|
||
'corrected', jsonb_build_object(
|
||
'support', jsonb_build_object('q2', sm.corr_supp_q2, 'q3', sm.corr_supp_q3, 'q4', sm.corr_supp_q4),
|
||
'development', jsonb_build_object('q2', sm.corr_dev_q2, 'q3', sm.corr_dev_q3, 'q4', sm.corr_dev_q4)
|
||
)
|
||
)
|
||
FROM v3.v_form2_smeta(p_form_id) sm
|
||
ORDER BY sm.section_code;
|
||
RETURN;
|
||
END IF;
|
||
|
||
RAISE EXCEPTION 'Unknown sheet: %', p_sheet
|
||
USING HINT = 'Use AHR / CAP / OPER / AHR_LIMIT / AHR_RENT / AHR_UTILITY / AHR_SECURITY / OTCH9F / SMETA';
|
||
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form3_report_sections(int4, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form3_report_sections(p_report_id integer, 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_q1_adj_by_items numeric, col_q1_adj_increase numeric, col_q1_total_corr numeric, col_q1_m1 numeric, col_q1_m2 numeric, col_q1_m3 numeric, col_q1_quarter_actual numeric, col_q1_economy numeric, col_q2_carryover numeric, col_q2_adj_by_items numeric, col_q2_adj_increase numeric, col_q2_total_corr numeric, col_q2_m1 numeric, col_q2_m2 numeric, col_q2_m3 numeric, col_q2_quarter_actual numeric, col_q2_economy numeric, col_q3_carryover numeric, col_q3_adj_by_items numeric, col_q3_adj_increase numeric, col_q3_total_corr numeric, col_q3_m1 numeric, col_q3_m2 numeric, col_q3_m3 numeric, col_q3_quarter_actual numeric, col_q3_economy numeric, col_q4_carryover numeric, col_q4_adj_by_items numeric, col_q4_adj_increase numeric, col_q4_total_corr numeric, col_q4_m1 numeric, col_q4_m2 numeric, col_q4_m3 numeric, col_q4_spod numeric, col_q4_quarter_actual numeric, col_q4_economy numeric, col_year_total_corr numeric, col_year_total_actual numeric, col_year_economy numeric, _sort_path integer[])
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
AS $function$
|
||
DECLARE
|
||
s_q1 BOOLEAN := p_sections IS NULL OR 'q1' = ANY(p_sections);
|
||
s_q2 BOOLEAN := p_sections IS NULL OR 'q2' = ANY(p_sections);
|
||
s_q3 BOOLEAN := p_sections IS NULL OR 'q3' = ANY(p_sections);
|
||
s_q4 BOOLEAN := p_sections IS NULL OR 'q4' = ANY(p_sections);
|
||
s_year BOOLEAN := p_sections IS NULL OR 'year' = ANY(p_sections);
|
||
BEGIN
|
||
IF NOT EXISTS (SELECT 1 FROM v3.rf_project_report WHERE id = p_report_id) THEN
|
||
RAISE EXCEPTION 'rf_project_report id=% не существует', p_report_id;
|
||
END IF;
|
||
|
||
RETURN QUERY
|
||
WITH
|
||
-- Дерево expense_item, ограниченное FORM_3 (через junction)
|
||
tw AS (
|
||
SELECT t.* FROM v3.mv_expense_item_tree t
|
||
JOIN v3.expense_item_form_type eft
|
||
ON eft.expense_item_id = t.id AND eft.form_type_code = 'FORM_3'
|
||
),
|
||
-- INPUT строки: одна на v3.rf_project_report_line
|
||
page AS (
|
||
SELECT l.id AS lid, l.expense_item_id AS eid,
|
||
t.parent_item_id AS sc, -- section-код родителя в дереве (1.01.1.)
|
||
ei.item_id AS ic,
|
||
ei.num_group_id AS ng, ei.name AS ename,
|
||
t.path AS tree_path
|
||
FROM v3.rf_project_report_line l
|
||
JOIN v3.expense_item ei ON ei.id = l.expense_item_id
|
||
JOIN tw t ON t.id = l.expense_item_id
|
||
WHERE l.rf_project_report_id = p_report_id
|
||
),
|
||
-- Поквартальные данные
|
||
jq AS (
|
||
SELECT q.rf_project_report_line_id AS lid, q.quarter,
|
||
q.adj_by_items, q.adj_increase,
|
||
q.actual_m1, q.actual_m2, q.actual_m3, q.actual_spod
|
||
FROM v3.rf_project_report_quarter q
|
||
JOIN v3.rf_project_report_line l ON l.id = q.rf_project_report_line_id
|
||
WHERE l.rf_project_report_id = p_report_id
|
||
),
|
||
-- INPUT base: соединяем page + 4 квартала
|
||
input_base AS (
|
||
SELECT
|
||
pg.lid, pg.eid, pg.sc, pg.ic, pg.ng, pg.ename, pg.tree_path,
|
||
q1.adj_by_items AS q1_abi, q1.adj_increase AS q1_ai,
|
||
q1.actual_m1 AS q1_m1, q1.actual_m2 AS q1_m2, q1.actual_m3 AS q1_m3,
|
||
q2.adj_by_items AS q2_abi, q2.adj_increase AS q2_ai,
|
||
q2.actual_m1 AS q2_m1, q2.actual_m2 AS q2_m2, q2.actual_m3 AS q2_m3,
|
||
q3.adj_by_items AS q3_abi, q3.adj_increase AS q3_ai,
|
||
q3.actual_m1 AS q3_m1, q3.actual_m2 AS q3_m2, q3.actual_m3 AS q3_m3,
|
||
q4.adj_by_items AS q4_abi, q4.adj_increase AS q4_ai,
|
||
q4.actual_m1 AS q4_m1, q4.actual_m2 AS q4_m2, q4.actual_m3 AS q4_m3,
|
||
q4.actual_spod AS q4_spod
|
||
FROM page pg
|
||
LEFT JOIN jq q1 ON q1.lid = pg.lid AND q1.quarter = 1
|
||
LEFT JOIN jq q2 ON q2.lid = pg.lid AND q2.quarter = 2
|
||
LEFT JOIN jq q3 ON q3.lid = pg.lid AND q3.quarter = 3
|
||
LEFT JOIN jq q4 ON q4.lid = pg.lid AND q4.quarter = 4
|
||
),
|
||
-- Computed totals per row
|
||
enriched AS (
|
||
SELECT b.*,
|
||
COALESCE(b.q1_abi,0) + COALESCE(b.q1_ai,0) AS q1_tc,
|
||
COALESCE(b.q1_m1,0) + COALESCE(b.q1_m2,0) + COALESCE(b.q1_m3,0) AS q1_qa,
|
||
COALESCE(b.q2_abi,0) + COALESCE(b.q2_ai,0) AS q2_tc,
|
||
COALESCE(b.q2_m1,0) + COALESCE(b.q2_m2,0) + COALESCE(b.q2_m3,0) AS q2_qa,
|
||
COALESCE(b.q3_abi,0) + COALESCE(b.q3_ai,0) AS q3_tc,
|
||
COALESCE(b.q3_m1,0) + COALESCE(b.q3_m2,0) + COALESCE(b.q3_m3,0) AS q3_qa,
|
||
COALESCE(b.q4_abi,0) + COALESCE(b.q4_ai,0) AS q4_tc,
|
||
COALESCE(b.q4_m1,0) + COALESCE(b.q4_m2,0) + COALESCE(b.q4_m3,0) + COALESCE(b.q4_spod,0) AS q4_qa
|
||
FROM input_base b
|
||
),
|
||
-- Per-line aggregates for tree rollup
|
||
agg_q AS (
|
||
SELECT l.expense_item_id AS eid, q.quarter,
|
||
SUM(q.adj_by_items) AS abi, SUM(q.adj_increase) AS ai,
|
||
SUM(q.actual_m1) AS m1, SUM(q.actual_m2) AS m2, SUM(q.actual_m3) AS m3,
|
||
SUM(q.actual_spod) AS spod
|
||
FROM v3.rf_project_report_line l
|
||
JOIN v3.rf_project_report_quarter q ON q.rf_project_report_line_id = l.id
|
||
WHERE l.rf_project_report_id = p_report_id
|
||
GROUP BY l.expense_item_id, q.quarter
|
||
),
|
||
tw_aq1 AS (
|
||
SELECT tw.id, SUM(a.abi) AS abi, SUM(a.ai) AS ai,
|
||
SUM(a.m1) AS m1, SUM(a.m2) AS m2, SUM(a.m3) AS m3
|
||
FROM tw LEFT JOIN agg_q a ON a.eid = ANY(tw.desc_ids) AND a.quarter = 1
|
||
GROUP BY tw.id
|
||
),
|
||
tw_aq2 AS (
|
||
SELECT tw.id, SUM(a.abi) AS abi, SUM(a.ai) AS ai,
|
||
SUM(a.m1) AS m1, SUM(a.m2) AS m2, SUM(a.m3) AS m3
|
||
FROM tw LEFT JOIN agg_q a ON a.eid = ANY(tw.desc_ids) AND a.quarter = 2
|
||
GROUP BY tw.id
|
||
),
|
||
tw_aq3 AS (
|
||
SELECT tw.id, SUM(a.abi) AS abi, SUM(a.ai) AS ai,
|
||
SUM(a.m1) AS m1, SUM(a.m2) AS m2, SUM(a.m3) AS m3
|
||
FROM tw LEFT JOIN agg_q a ON a.eid = ANY(tw.desc_ids) AND a.quarter = 3
|
||
GROUP BY tw.id
|
||
),
|
||
tw_aq4 AS (
|
||
SELECT tw.id, SUM(a.abi) AS abi, SUM(a.ai) AS ai,
|
||
SUM(a.m1) AS m1, SUM(a.m2) AS m2, SUM(a.m3) AS m3,
|
||
SUM(a.spod) AS spod
|
||
FROM tw LEFT JOIN agg_q a ON a.eid = ANY(tw.desc_ids) AND a.quarter = 4
|
||
GROUP BY tw.id
|
||
)
|
||
|
||
-- ═══ Часть A: INPUT строки ════════════════════════════════════════════════
|
||
SELECT * FROM (
|
||
SELECT
|
||
'INPUT'::VARCHAR AS row_type, 3 AS depth, e.lid::INT AS line_id,
|
||
e.sc, e.ic, e.ng, e.ename,
|
||
-- Q1
|
||
CASE WHEN s_q1 THEN e.q1_abi END,
|
||
CASE WHEN s_q1 THEN e.q1_ai END,
|
||
CASE WHEN s_q1 THEN e.q1_tc END,
|
||
CASE WHEN s_q1 THEN e.q1_m1 END,
|
||
CASE WHEN s_q1 THEN e.q1_m2 END,
|
||
CASE WHEN s_q1 THEN e.q1_m3 END,
|
||
CASE WHEN s_q1 THEN e.q1_qa END,
|
||
CASE WHEN s_q1 THEN ROUND(e.q1_tc - e.q1_qa, 1) END,
|
||
-- Q2 (carryover пока NULL — нет столбца в _quarter; задел на будущее)
|
||
CASE WHEN s_q2 THEN NULL::NUMERIC END,
|
||
CASE WHEN s_q2 THEN e.q2_abi END,
|
||
CASE WHEN s_q2 THEN e.q2_ai END,
|
||
CASE WHEN s_q2 THEN e.q2_tc END,
|
||
CASE WHEN s_q2 THEN e.q2_m1 END,
|
||
CASE WHEN s_q2 THEN e.q2_m2 END,
|
||
CASE WHEN s_q2 THEN e.q2_m3 END,
|
||
CASE WHEN s_q2 THEN e.q2_qa END,
|
||
CASE WHEN s_q2 THEN ROUND(e.q2_tc - e.q2_qa, 1) END,
|
||
-- Q3
|
||
CASE WHEN s_q3 THEN NULL::NUMERIC END,
|
||
CASE WHEN s_q3 THEN e.q3_abi END,
|
||
CASE WHEN s_q3 THEN e.q3_ai END,
|
||
CASE WHEN s_q3 THEN e.q3_tc END,
|
||
CASE WHEN s_q3 THEN e.q3_m1 END,
|
||
CASE WHEN s_q3 THEN e.q3_m2 END,
|
||
CASE WHEN s_q3 THEN e.q3_m3 END,
|
||
CASE WHEN s_q3 THEN e.q3_qa END,
|
||
CASE WHEN s_q3 THEN ROUND(e.q3_tc - e.q3_qa, 1) END,
|
||
-- Q4
|
||
CASE WHEN s_q4 THEN NULL::NUMERIC END,
|
||
CASE WHEN s_q4 THEN e.q4_abi END,
|
||
CASE WHEN s_q4 THEN e.q4_ai END,
|
||
CASE WHEN s_q4 THEN e.q4_tc END,
|
||
CASE WHEN s_q4 THEN e.q4_m1 END,
|
||
CASE WHEN s_q4 THEN e.q4_m2 END,
|
||
CASE WHEN s_q4 THEN e.q4_m3 END,
|
||
CASE WHEN s_q4 THEN e.q4_spod END,
|
||
CASE WHEN s_q4 THEN e.q4_qa END,
|
||
CASE WHEN s_q4 THEN ROUND(e.q4_tc - e.q4_qa, 1) END,
|
||
-- year
|
||
CASE WHEN s_year THEN e.q1_tc + e.q2_tc + e.q3_tc + e.q4_tc END,
|
||
CASE WHEN s_year THEN e.q1_qa + e.q2_qa + e.q3_qa + e.q4_qa END,
|
||
CASE WHEN s_year THEN ROUND((e.q1_tc + e.q2_tc + e.q3_tc + e.q4_tc) -
|
||
(e.q1_qa + e.q2_qa + e.q3_qa + e.q4_qa), 1) END,
|
||
e.tree_path || ARRAY[e.lid::INT] AS _sort_path
|
||
FROM enriched e
|
||
|
||
UNION ALL
|
||
|
||
-- ═══ Часть B: Иерархия (ROOT / GROUP / ITEM / SUB_ITEM) ═════════════════
|
||
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,
|
||
-- Q1
|
||
CASE WHEN s_q1 THEN a1.abi END,
|
||
CASE WHEN s_q1 THEN a1.ai END,
|
||
CASE WHEN s_q1 THEN COALESCE(a1.abi,0)+COALESCE(a1.ai,0) END,
|
||
CASE WHEN s_q1 THEN a1.m1 END,
|
||
CASE WHEN s_q1 THEN a1.m2 END,
|
||
CASE WHEN s_q1 THEN a1.m3 END,
|
||
CASE WHEN s_q1 THEN COALESCE(a1.m1,0)+COALESCE(a1.m2,0)+COALESCE(a1.m3,0) END,
|
||
CASE WHEN s_q1 THEN ROUND((COALESCE(a1.abi,0)+COALESCE(a1.ai,0)) -
|
||
(COALESCE(a1.m1,0)+COALESCE(a1.m2,0)+COALESCE(a1.m3,0)), 1) END,
|
||
-- Q2
|
||
CASE WHEN s_q2 THEN NULL::NUMERIC END,
|
||
CASE WHEN s_q2 THEN a2.abi END,
|
||
CASE WHEN s_q2 THEN a2.ai END,
|
||
CASE WHEN s_q2 THEN COALESCE(a2.abi,0)+COALESCE(a2.ai,0) END,
|
||
CASE WHEN s_q2 THEN a2.m1 END,
|
||
CASE WHEN s_q2 THEN a2.m2 END,
|
||
CASE WHEN s_q2 THEN a2.m3 END,
|
||
CASE WHEN s_q2 THEN COALESCE(a2.m1,0)+COALESCE(a2.m2,0)+COALESCE(a2.m3,0) END,
|
||
CASE WHEN s_q2 THEN ROUND((COALESCE(a2.abi,0)+COALESCE(a2.ai,0)) -
|
||
(COALESCE(a2.m1,0)+COALESCE(a2.m2,0)+COALESCE(a2.m3,0)), 1) END,
|
||
-- Q3
|
||
CASE WHEN s_q3 THEN NULL::NUMERIC END,
|
||
CASE WHEN s_q3 THEN a3.abi END,
|
||
CASE WHEN s_q3 THEN a3.ai END,
|
||
CASE WHEN s_q3 THEN COALESCE(a3.abi,0)+COALESCE(a3.ai,0) END,
|
||
CASE WHEN s_q3 THEN a3.m1 END,
|
||
CASE WHEN s_q3 THEN a3.m2 END,
|
||
CASE WHEN s_q3 THEN a3.m3 END,
|
||
CASE WHEN s_q3 THEN COALESCE(a3.m1,0)+COALESCE(a3.m2,0)+COALESCE(a3.m3,0) END,
|
||
CASE WHEN s_q3 THEN ROUND((COALESCE(a3.abi,0)+COALESCE(a3.ai,0)) -
|
||
(COALESCE(a3.m1,0)+COALESCE(a3.m2,0)+COALESCE(a3.m3,0)), 1) END,
|
||
-- Q4
|
||
CASE WHEN s_q4 THEN NULL::NUMERIC END,
|
||
CASE WHEN s_q4 THEN a4.abi END,
|
||
CASE WHEN s_q4 THEN a4.ai END,
|
||
CASE WHEN s_q4 THEN COALESCE(a4.abi,0)+COALESCE(a4.ai,0) END,
|
||
CASE WHEN s_q4 THEN a4.m1 END,
|
||
CASE WHEN s_q4 THEN a4.m2 END,
|
||
CASE WHEN s_q4 THEN a4.m3 END,
|
||
CASE WHEN s_q4 THEN a4.spod END,
|
||
CASE WHEN s_q4 THEN COALESCE(a4.m1,0)+COALESCE(a4.m2,0)+COALESCE(a4.m3,0)+COALESCE(a4.spod,0) END,
|
||
CASE WHEN s_q4 THEN ROUND((COALESCE(a4.abi,0)+COALESCE(a4.ai,0)) -
|
||
(COALESCE(a4.m1,0)+COALESCE(a4.m2,0)+COALESCE(a4.m3,0)+COALESCE(a4.spod,0)), 1) END,
|
||
-- year
|
||
CASE WHEN s_year THEN COALESCE(a1.abi,0)+COALESCE(a1.ai,0)+
|
||
COALESCE(a2.abi,0)+COALESCE(a2.ai,0)+
|
||
COALESCE(a3.abi,0)+COALESCE(a3.ai,0)+
|
||
COALESCE(a4.abi,0)+COALESCE(a4.ai,0) END,
|
||
CASE WHEN s_year THEN COALESCE(a1.m1,0)+COALESCE(a1.m2,0)+COALESCE(a1.m3,0)+
|
||
COALESCE(a2.m1,0)+COALESCE(a2.m2,0)+COALESCE(a2.m3,0)+
|
||
COALESCE(a3.m1,0)+COALESCE(a3.m2,0)+COALESCE(a3.m3,0)+
|
||
COALESCE(a4.m1,0)+COALESCE(a4.m2,0)+COALESCE(a4.m3,0)+COALESCE(a4.spod,0) END,
|
||
CASE WHEN s_year THEN
|
||
ROUND(
|
||
(COALESCE(a1.abi,0)+COALESCE(a1.ai,0)+COALESCE(a2.abi,0)+COALESCE(a2.ai,0)+
|
||
COALESCE(a3.abi,0)+COALESCE(a3.ai,0)+COALESCE(a4.abi,0)+COALESCE(a4.ai,0))
|
||
-
|
||
(COALESCE(a1.m1,0)+COALESCE(a1.m2,0)+COALESCE(a1.m3,0)+
|
||
COALESCE(a2.m1,0)+COALESCE(a2.m2,0)+COALESCE(a2.m3,0)+
|
||
COALESCE(a3.m1,0)+COALESCE(a3.m2,0)+COALESCE(a3.m3,0)+
|
||
COALESCE(a4.m1,0)+COALESCE(a4.m2,0)+COALESCE(a4.m3,0)+COALESCE(a4.spod,0)),
|
||
1)
|
||
END,
|
||
t.path
|
||
FROM tw t
|
||
LEFT JOIN tw_aq1 a1 ON a1.id = t.id
|
||
LEFT JOIN tw_aq2 a2 ON a2.id = t.id
|
||
LEFT JOIN tw_aq3 a3 ON a3.id = t.id
|
||
LEFT JOIN tw_aq4 a4 ON a4.id = t.id
|
||
) sub
|
||
ORDER BY sub._sort_path;
|
||
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form3_report_jsonb(int4, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form3_report_jsonb(p_report_id integer, p_sections text[] DEFAULT NULL::text[])
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE sql
|
||
STABLE
|
||
AS $function$
|
||
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(
|
||
'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(
|
||
'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(
|
||
'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(
|
||
'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(
|
||
'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
|
||
ORDER BY s._sort_path;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form3_rf_rollup_sections(int4, int4, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form3_rf_rollup_sections(p_branch_id integer, p_year integer, p_sections text[] DEFAULT NULL::text[])
|
||
RETURNS TABLE(row_type character varying, depth integer, col_section_code character varying, col_item_id character varying, col_num_group_id character varying, col_name character varying, col_q1_adj_by_items numeric, col_q1_adj_increase numeric, col_q1_total_corr numeric, col_q1_m1 numeric, col_q1_m2 numeric, col_q1_m3 numeric, col_q1_quarter_actual numeric, col_q1_economy numeric, col_q2_adj_by_items numeric, col_q2_adj_increase numeric, col_q2_total_corr numeric, col_q2_m1 numeric, col_q2_m2 numeric, col_q2_m3 numeric, col_q2_quarter_actual numeric, col_q2_economy numeric, col_q3_adj_by_items numeric, col_q3_adj_increase numeric, col_q3_total_corr numeric, col_q3_m1 numeric, col_q3_m2 numeric, col_q3_m3 numeric, col_q3_quarter_actual numeric, col_q3_economy numeric, col_q4_adj_by_items numeric, col_q4_adj_increase numeric, col_q4_total_corr numeric, col_q4_m1 numeric, col_q4_m2 numeric, col_q4_m3 numeric, col_q4_spod numeric, col_q4_quarter_actual numeric, col_q4_economy numeric, col_year_total_corr numeric, col_year_total_actual numeric, col_year_economy numeric, _sort_path integer[])
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
AS $function$
|
||
DECLARE
|
||
s_q1 BOOLEAN := p_sections IS NULL OR 'q1' = ANY(p_sections);
|
||
s_q2 BOOLEAN := p_sections IS NULL OR 'q2' = ANY(p_sections);
|
||
s_q3 BOOLEAN := p_sections IS NULL OR 'q3' = ANY(p_sections);
|
||
s_q4 BOOLEAN := p_sections IS NULL OR 'q4' = ANY(p_sections);
|
||
s_year BOOLEAN := p_sections IS NULL OR 'year' = ANY(p_sections);
|
||
BEGIN
|
||
RETURN QUERY
|
||
WITH
|
||
tw AS (
|
||
SELECT t.* FROM v3.mv_expense_item_tree t
|
||
JOIN v3.expense_item_form_type eft
|
||
ON eft.expense_item_id = t.id AND eft.form_type_code = 'FORM_3'
|
||
),
|
||
-- Агрегаты per (expense_item_id, quarter) — sum по обоим трекам всех проектов РФ × год
|
||
agg_q AS (
|
||
SELECT l.expense_item_id AS eid, q.quarter,
|
||
SUM(q.adj_by_items) AS abi, SUM(q.adj_increase) AS ai,
|
||
SUM(q.actual_m1) AS m1, SUM(q.actual_m2) AS m2, SUM(q.actual_m3) AS m3,
|
||
SUM(q.actual_spod) AS spod
|
||
FROM v3.rf_project_report r
|
||
JOIN v3.project p ON p.id = r.project_id
|
||
JOIN v3.rf_project_report_line l ON l.rf_project_report_id = r.id
|
||
JOIN v3.rf_project_report_quarter q ON q.rf_project_report_line_id = l.id
|
||
WHERE p.org_unit_id = p_branch_id AND r.year = p_year
|
||
GROUP BY l.expense_item_id, q.quarter
|
||
),
|
||
tw_aq1 AS (SELECT tw.id, SUM(a.abi) AS abi, SUM(a.ai) AS ai,
|
||
SUM(a.m1) AS m1, SUM(a.m2) AS m2, SUM(a.m3) AS m3
|
||
FROM tw LEFT JOIN agg_q a ON a.eid = ANY(tw.desc_ids) AND a.quarter=1
|
||
GROUP BY tw.id),
|
||
tw_aq2 AS (SELECT tw.id, SUM(a.abi) AS abi, SUM(a.ai) AS ai,
|
||
SUM(a.m1) AS m1, SUM(a.m2) AS m2, SUM(a.m3) AS m3
|
||
FROM tw LEFT JOIN agg_q a ON a.eid = ANY(tw.desc_ids) AND a.quarter=2
|
||
GROUP BY tw.id),
|
||
tw_aq3 AS (SELECT tw.id, SUM(a.abi) AS abi, SUM(a.ai) AS ai,
|
||
SUM(a.m1) AS m1, SUM(a.m2) AS m2, SUM(a.m3) AS m3
|
||
FROM tw LEFT JOIN agg_q a ON a.eid = ANY(tw.desc_ids) AND a.quarter=3
|
||
GROUP BY tw.id),
|
||
tw_aq4 AS (SELECT tw.id, SUM(a.abi) AS abi, SUM(a.ai) AS ai,
|
||
SUM(a.m1) AS m1, SUM(a.m2) AS m2, SUM(a.m3) AS m3,
|
||
SUM(a.spod) AS spod
|
||
FROM tw LEFT JOIN agg_q a ON a.eid = ANY(tw.desc_ids) AND a.quarter=4
|
||
GROUP BY tw.id)
|
||
|
||
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,
|
||
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,
|
||
-- Q1
|
||
CASE WHEN s_q1 THEN a1.abi END,
|
||
CASE WHEN s_q1 THEN a1.ai END,
|
||
CASE WHEN s_q1 THEN COALESCE(a1.abi,0)+COALESCE(a1.ai,0) END,
|
||
CASE WHEN s_q1 THEN a1.m1 END,
|
||
CASE WHEN s_q1 THEN a1.m2 END,
|
||
CASE WHEN s_q1 THEN a1.m3 END,
|
||
CASE WHEN s_q1 THEN COALESCE(a1.m1,0)+COALESCE(a1.m2,0)+COALESCE(a1.m3,0) END,
|
||
CASE WHEN s_q1 THEN ROUND((COALESCE(a1.abi,0)+COALESCE(a1.ai,0))
|
||
-(COALESCE(a1.m1,0)+COALESCE(a1.m2,0)+COALESCE(a1.m3,0)), 1) END,
|
||
-- Q2
|
||
CASE WHEN s_q2 THEN a2.abi END,
|
||
CASE WHEN s_q2 THEN a2.ai END,
|
||
CASE WHEN s_q2 THEN COALESCE(a2.abi,0)+COALESCE(a2.ai,0) END,
|
||
CASE WHEN s_q2 THEN a2.m1 END,
|
||
CASE WHEN s_q2 THEN a2.m2 END,
|
||
CASE WHEN s_q2 THEN a2.m3 END,
|
||
CASE WHEN s_q2 THEN COALESCE(a2.m1,0)+COALESCE(a2.m2,0)+COALESCE(a2.m3,0) END,
|
||
CASE WHEN s_q2 THEN ROUND((COALESCE(a2.abi,0)+COALESCE(a2.ai,0))
|
||
-(COALESCE(a2.m1,0)+COALESCE(a2.m2,0)+COALESCE(a2.m3,0)), 1) END,
|
||
-- Q3
|
||
CASE WHEN s_q3 THEN a3.abi END,
|
||
CASE WHEN s_q3 THEN a3.ai END,
|
||
CASE WHEN s_q3 THEN COALESCE(a3.abi,0)+COALESCE(a3.ai,0) END,
|
||
CASE WHEN s_q3 THEN a3.m1 END,
|
||
CASE WHEN s_q3 THEN a3.m2 END,
|
||
CASE WHEN s_q3 THEN a3.m3 END,
|
||
CASE WHEN s_q3 THEN COALESCE(a3.m1,0)+COALESCE(a3.m2,0)+COALESCE(a3.m3,0) END,
|
||
CASE WHEN s_q3 THEN ROUND((COALESCE(a3.abi,0)+COALESCE(a3.ai,0))
|
||
-(COALESCE(a3.m1,0)+COALESCE(a3.m2,0)+COALESCE(a3.m3,0)), 1) END,
|
||
-- Q4
|
||
CASE WHEN s_q4 THEN a4.abi END,
|
||
CASE WHEN s_q4 THEN a4.ai END,
|
||
CASE WHEN s_q4 THEN COALESCE(a4.abi,0)+COALESCE(a4.ai,0) END,
|
||
CASE WHEN s_q4 THEN a4.m1 END,
|
||
CASE WHEN s_q4 THEN a4.m2 END,
|
||
CASE WHEN s_q4 THEN a4.m3 END,
|
||
CASE WHEN s_q4 THEN a4.spod END,
|
||
CASE WHEN s_q4 THEN COALESCE(a4.m1,0)+COALESCE(a4.m2,0)+COALESCE(a4.m3,0)+COALESCE(a4.spod,0) END,
|
||
CASE WHEN s_q4 THEN ROUND((COALESCE(a4.abi,0)+COALESCE(a4.ai,0))
|
||
-(COALESCE(a4.m1,0)+COALESCE(a4.m2,0)+COALESCE(a4.m3,0)+COALESCE(a4.spod,0)), 1) END,
|
||
-- year
|
||
CASE WHEN s_year THEN COALESCE(a1.abi,0)+COALESCE(a1.ai,0)+COALESCE(a2.abi,0)+COALESCE(a2.ai,0)+
|
||
COALESCE(a3.abi,0)+COALESCE(a3.ai,0)+COALESCE(a4.abi,0)+COALESCE(a4.ai,0) END,
|
||
CASE WHEN s_year THEN COALESCE(a1.m1,0)+COALESCE(a1.m2,0)+COALESCE(a1.m3,0)+
|
||
COALESCE(a2.m1,0)+COALESCE(a2.m2,0)+COALESCE(a2.m3,0)+
|
||
COALESCE(a3.m1,0)+COALESCE(a3.m2,0)+COALESCE(a3.m3,0)+
|
||
COALESCE(a4.m1,0)+COALESCE(a4.m2,0)+COALESCE(a4.m3,0)+COALESCE(a4.spod,0) END,
|
||
CASE WHEN s_year THEN
|
||
ROUND(
|
||
(COALESCE(a1.abi,0)+COALESCE(a1.ai,0)+COALESCE(a2.abi,0)+COALESCE(a2.ai,0)+
|
||
COALESCE(a3.abi,0)+COALESCE(a3.ai,0)+COALESCE(a4.abi,0)+COALESCE(a4.ai,0))
|
||
-(COALESCE(a1.m1,0)+COALESCE(a1.m2,0)+COALESCE(a1.m3,0)+
|
||
COALESCE(a2.m1,0)+COALESCE(a2.m2,0)+COALESCE(a2.m3,0)+
|
||
COALESCE(a3.m1,0)+COALESCE(a3.m2,0)+COALESCE(a3.m3,0)+
|
||
COALESCE(a4.m1,0)+COALESCE(a4.m2,0)+COALESCE(a4.m3,0)+COALESCE(a4.spod,0)),
|
||
1)
|
||
END,
|
||
t.path
|
||
FROM tw t
|
||
LEFT JOIN tw_aq1 a1 ON a1.id = t.id
|
||
LEFT JOIN tw_aq2 a2 ON a2.id = t.id
|
||
LEFT JOIN tw_aq3 a3 ON a3.id = t.id
|
||
LEFT JOIN tw_aq4 a4 ON a4.id = t.id
|
||
ORDER BY t.path;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form3_rf_rollup_jsonb(int4, int4, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form3_rf_rollup_jsonb(p_branch_id integer, p_year integer, p_sections text[] DEFAULT NULL::text[])
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE sql
|
||
STABLE
|
||
AS $function$
|
||
SELECT
|
||
s.row_type,
|
||
s.depth,
|
||
ROW_NUMBER() OVER (ORDER BY s._sort_path) AS sort_order,
|
||
jsonb_build_object(
|
||
'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
|
||
),
|
||
'q1', jsonb_build_object(
|
||
'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(
|
||
'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(
|
||
'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(
|
||
'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(
|
||
'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_rf_rollup_sections(p_branch_id, p_year, p_sections) s
|
||
ORDER BY s._sort_path;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form3_smeta(int4, int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form3_smeta(p_branch_id integer, p_year integer)
|
||
RETURNS TABLE(row_type character varying, section_code character varying, name character varying, total_corr numeric, total_actual numeric, economy numeric)
|
||
LANGUAGE sql
|
||
STABLE
|
||
AS $function$
|
||
-- Top-level sections (ROOT) + общая итоговая
|
||
SELECT row_type, col_section_code, col_name,
|
||
col_year_total_corr, col_year_total_actual, col_year_economy
|
||
FROM v3.v_form3_rf_rollup_sections(p_branch_id, p_year, ARRAY['year'])
|
||
WHERE depth = 0
|
||
|
||
UNION ALL
|
||
|
||
-- Итог: «Всего расходов по смете (п.1+п.3)»
|
||
SELECT 'TOTAL'::VARCHAR, NULL::VARCHAR,
|
||
'Всего расходов по смете (п.1+п.3)'::VARCHAR,
|
||
SUM(col_year_total_corr),
|
||
SUM(col_year_total_actual),
|
||
ROUND(SUM(col_year_total_corr) - SUM(col_year_total_actual), 1)
|
||
FROM v3.v_form3_rf_rollup_sections(p_branch_id, p_year, ARRAY['year'])
|
||
WHERE depth = 0;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form4_sheet_sections(int4, varchar, _text);
|
||
|
||
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,
|
||
-- 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, NULL::VARCHAR,
|
||
-- 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$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form4_sheet_lines_jsonb(int4, varchar, _text);
|
||
|
||
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(
|
||
'amount', 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_payments_y1', s.col_cs_future_y1,
|
||
'future_payments_y2', s.col_cs_future_y2,
|
||
'other_ssp_amount', 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(
|
||
'amount', s.col_col_amount,
|
||
'protocol_reference', 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,
|
||
'expenses_q1', s.col_ckk_q1,
|
||
'expenses_q2', s.col_ckk_q2,
|
||
'expenses_q3', s.col_ckk_q3,
|
||
'expenses_q4', s.col_ckk_q4,
|
||
'rf_schedule', s.col_ckk_rf_schedule,
|
||
'delivery_deadline', s.col_ckk_delivery_deadline,
|
||
'procurement_plan', s.col_ckk_procurement_plan,
|
||
'procurement_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,
|
||
'expenses_q1', s.col_cd_q1,
|
||
'expenses_q2', s.col_cd_q2,
|
||
'expenses_q3', s.col_cd_q3,
|
||
'expenses_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$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form4_sheet_jsonb(int4, varchar, _text);
|
||
|
||
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-чанки + project / section расширение
|
||
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,
|
||
pp.id AS prj_id, pp.name AS prj_name,
|
||
pg.id AS prog_id, pg.name AS prog_name,
|
||
ei.id AS ei_id, ei.section_code,
|
||
-- ROOT: ищем depth=0 предка через recursive CTE inline
|
||
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
|
||
JOIN v3.expense_item ei ON ei.id = bl.expense_item_id
|
||
LEFT JOIN v3.project pp ON pp.id = bl.project_id AND pp.level = 'project'
|
||
LEFT JOIN v3.project pg ON pg.id = pp.parent_id AND pg.level = 'program'
|
||
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
|
||
),
|
||
-- 2. PROJECT агрегаты
|
||
prj 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
|
||
),
|
||
-- 3. PROGRAM агрегаты
|
||
prog 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
|
||
),
|
||
-- 4. ROOT (section) агрегаты по всем строкам секции (с проектом и без)
|
||
root 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
|
||
),
|
||
-- 5. Все 4 уровня в едином формате (sort_path для упорядочения)
|
||
unioned AS (
|
||
SELECT
|
||
'ROOT'::VARCHAR AS row_type, 0 AS depth, NULL::INT AS line_id,
|
||
jsonb_build_object('section_code', sec_code, 'name', sec_name) 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
|
||
UNION ALL
|
||
SELECT
|
||
'GROUP', 1, NULL,
|
||
jsonb_build_object('section_code', sec_code, 'name', prog_name,
|
||
'program_id', prog_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
|
||
UNION ALL
|
||
SELECT
|
||
'ITEM', 2, NULL,
|
||
jsonb_build_object('section_code', sec_code, 'name', prj_name,
|
||
'program_id', prog_id, 'project_id', prj_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
|
||
UNION ALL
|
||
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(prj_id, 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(
|
||
'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$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form4_smeta(int4);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form4_smeta(p_form_id integer)
|
||
RETURNS TABLE(row_type character varying, depth integer, section_code character varying, name character varying, plan_supp_q1 numeric, plan_supp_q2 numeric, plan_supp_q3 numeric, plan_supp_q4 numeric, plan_supp_year numeric, plan_dev_q1 numeric, plan_dev_q2 numeric, plan_dev_q3 numeric, plan_dev_q4 numeric, plan_dev_year numeric, appr_supp_q1 numeric, appr_supp_q2 numeric, appr_supp_q3 numeric, appr_supp_q4 numeric, appr_supp_year numeric, appr_dev_q1 numeric, appr_dev_q2 numeric, appr_dev_q3 numeric, appr_dev_q4 numeric, appr_dev_year numeric, fact_supp_q1 numeric, fact_supp_q2 numeric, fact_supp_q3 numeric, fact_supp_q4 numeric, fact_supp_year numeric, fact_dev_q1 numeric, fact_dev_q2 numeric, fact_dev_q3 numeric, fact_dev_q4 numeric, fact_dev_year numeric, corr_supp_q1 numeric, corr_supp_q2 numeric, corr_supp_q3 numeric, corr_supp_q4 numeric, corr_supp_year numeric, corr_dev_q1 numeric, corr_dev_q2 numeric, corr_dev_q3 numeric, corr_dev_q4 numeric, corr_dev_year numeric)
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
AS $function$
|
||
#variable_conflict use_column
|
||
BEGIN
|
||
RETURN QUERY
|
||
WITH
|
||
-- Per-line per-direction агрегаты квартальных данных
|
||
q_per_line AS (
|
||
SELECT line_id,
|
||
SUM(CASE WHEN quarter=1 THEN COALESCE(actual_m1,0)+COALESCE(actual_m2,0)+COALESCE(actual_m3,0) END) AS fq1,
|
||
SUM(CASE WHEN quarter=2 THEN COALESCE(actual_m1,0)+COALESCE(actual_m2,0)+COALESCE(actual_m3,0) END) AS fq2,
|
||
SUM(CASE WHEN quarter=3 THEN COALESCE(actual_m1,0)+COALESCE(actual_m2,0)+COALESCE(actual_m3,0) END) AS fq3,
|
||
SUM(CASE WHEN quarter=4 THEN COALESCE(actual_m1,0)+COALESCE(actual_m2,0)+COALESCE(actual_m3,0)+COALESCE(actual_spod,0) END) AS fq4,
|
||
SUM(CASE WHEN quarter=1 THEN transfer_to_q2 END) AS t1q2,
|
||
SUM(CASE WHEN quarter=1 THEN transfer_to_q3 END) AS t1q3,
|
||
SUM(CASE WHEN quarter=1 THEN transfer_to_q4 END) AS t1q4,
|
||
SUM(CASE WHEN quarter=2 THEN transfer_to_q3 END) AS t2q3,
|
||
SUM(CASE WHEN quarter=2 THEN transfer_to_q4 END) AS t2q4,
|
||
SUM(CASE WHEN quarter=3 THEN transfer_to_q4 END) AS t3q4,
|
||
SUM(CASE WHEN quarter=1 THEN COALESCE(adj_current,0)+COALESCE(adj_ssp,0)+COALESCE(adj_reserve,0) END) AS adj1,
|
||
SUM(CASE WHEN quarter=2 THEN COALESCE(adj_current,0)+COALESCE(adj_ssp,0)+COALESCE(adj_reserve,0) END) AS adj2,
|
||
SUM(CASE WHEN quarter=3 THEN COALESCE(adj_current,0)+COALESCE(adj_ssp,0)+COALESCE(adj_reserve,0) END) AS adj3,
|
||
SUM(CASE WHEN quarter=4 THEN COALESCE(adj_current,0)+COALESCE(adj_ssp,0)+COALESCE(adj_reserve,0) END) AS adj4,
|
||
-- Δ approved → new_plan: revision_inc/seq + target_change + base_correction
|
||
-- (согласовано с v_form4_sheet_sections / Excel DJ10 = SUM(AN, CY, DH, CF, DE:DG))
|
||
SUM(CASE WHEN quarter=2 THEN COALESCE(plan_revision_increase,0)+COALESCE(plan_revision_sequester,0)+COALESCE(target_change,0)+COALESCE(base_plan_correction,0) END) AS rev2,
|
||
SUM(CASE WHEN quarter=3 THEN COALESCE(plan_revision_increase,0)+COALESCE(plan_revision_sequester,0)+COALESCE(target_change,0)+COALESCE(base_plan_correction,0) END) AS rev3,
|
||
SUM(CASE WHEN quarter=4 THEN COALESCE(plan_revision_increase,0)+COALESCE(plan_revision_sequester,0)+COALESCE(target_change,0)+COALESCE(base_plan_correction,0) END) AS rev4
|
||
FROM v3.budget_line_quarter GROUP BY line_id
|
||
),
|
||
seq_dfip AS (
|
||
SELECT line_id, SUM(adj_q1) AS s1, SUM(adj_q2) AS s2, SUM(adj_q3) AS s3, SUM(adj_q4) AS s4
|
||
FROM v3.sequestration WHERE actor='DFIP' GROUP BY line_id
|
||
),
|
||
seq_ssp AS (
|
||
SELECT line_id, SUM(adj_q1) AS g1, SUM(adj_q2) AS g2, SUM(adj_q3) AS g3, SUM(adj_q4) AS g4
|
||
FROM v3.sequestration WHERE actor='SSP_GO' GROUP BY line_id
|
||
),
|
||
-- Per-line aggregate.
|
||
-- FORM_4 = "Расходы на развитие" (АХР_Р!F5='ПРОЕКТНАЯ ДЕЯТЕЛЬНОСТЬ' в Excel),
|
||
-- поэтому все строки идут в колонку Development независимо от
|
||
-- expense_item.direction (этот справочник общий с FORM_2 и для FORM_2
|
||
-- проставлен 'Support'). Колонка Support по форме 4 всегда = 0.
|
||
line_agg AS (
|
||
SELECT
|
||
bl.expense_item_id AS eid, 'Development'::VARCHAR AS dir,
|
||
SUM(p.plan_q1) AS pq1, SUM(p.plan_q2) AS pq2, SUM(p.plan_q3) AS pq3, SUM(p.plan_q4) AS pq4,
|
||
SUM(sd.s1) AS sd1, SUM(sd.s2) AS sd2, SUM(sd.s3) AS sd3, SUM(sd.s4) AS sd4,
|
||
SUM(sg.g1) AS sg1, SUM(sg.g2) AS sg2, SUM(sg.g3) AS sg3, SUM(sg.g4) AS sg4,
|
||
SUM(r.amount_q1) AS rq1, SUM(r.amount_q2) AS rq2, SUM(r.amount_q3) AS rq3, SUM(r.amount_q4) AS rq4,
|
||
SUM(qp.fq1) AS fq1, SUM(qp.fq2) AS fq2, SUM(qp.fq3) AS fq3, SUM(qp.fq4) AS fq4,
|
||
SUM(qp.t1q2) AS t1q2, SUM(qp.t1q3) AS t1q3, SUM(qp.t1q4) AS t1q4,
|
||
SUM(qp.t2q3) AS t2q3, SUM(qp.t2q4) AS t2q4, SUM(qp.t3q4) AS t3q4,
|
||
SUM(qp.adj1) AS adj1, SUM(qp.adj2) AS adj2, SUM(qp.adj3) AS adj3, SUM(qp.adj4) AS adj4,
|
||
SUM(qp.rev2) AS rev2, SUM(qp.rev3) AS rev3, SUM(qp.rev4) AS rev4
|
||
FROM v3.budget_line bl
|
||
LEFT JOIN v3.plan p ON p.line_id = bl.id
|
||
LEFT JOIN seq_dfip sd ON sd.line_id = bl.id
|
||
LEFT JOIN seq_ssp sg ON sg.line_id = bl.id
|
||
LEFT JOIN v3.reserve r ON r.line_id = bl.id
|
||
LEFT JOIN q_per_line qp ON qp.line_id = bl.id
|
||
WHERE bl.budget_form_id = p_form_id
|
||
GROUP BY bl.expense_item_id
|
||
),
|
||
-- Tree rollup с расщеплением по direction. Используем FILTER (WHERE)
|
||
tw_rollup AS (
|
||
SELECT
|
||
t.id, t.depth, t.item_id AS section_code, t.name,
|
||
-- Поддержка
|
||
SUM(a.pq1) FILTER (WHERE a.dir='Support') AS s_pq1,
|
||
SUM(a.pq2) FILTER (WHERE a.dir='Support') AS s_pq2,
|
||
SUM(a.pq3) FILTER (WHERE a.dir='Support') AS s_pq3,
|
||
SUM(a.pq4) FILTER (WHERE a.dir='Support') AS s_pq4,
|
||
SUM(a.sd1) FILTER (WHERE a.dir='Support') AS s_sd1,
|
||
SUM(a.sd2) FILTER (WHERE a.dir='Support') AS s_sd2,
|
||
SUM(a.sd3) FILTER (WHERE a.dir='Support') AS s_sd3,
|
||
SUM(a.sd4) FILTER (WHERE a.dir='Support') AS s_sd4,
|
||
SUM(a.sg1) FILTER (WHERE a.dir='Support') AS s_sg1,
|
||
SUM(a.sg2) FILTER (WHERE a.dir='Support') AS s_sg2,
|
||
SUM(a.sg3) FILTER (WHERE a.dir='Support') AS s_sg3,
|
||
SUM(a.sg4) FILTER (WHERE a.dir='Support') AS s_sg4,
|
||
SUM(a.rq1) FILTER (WHERE a.dir='Support') AS s_rq1,
|
||
SUM(a.rq2) FILTER (WHERE a.dir='Support') AS s_rq2,
|
||
SUM(a.rq3) FILTER (WHERE a.dir='Support') AS s_rq3,
|
||
SUM(a.rq4) FILTER (WHERE a.dir='Support') AS s_rq4,
|
||
SUM(a.fq1) FILTER (WHERE a.dir='Support') AS s_fq1,
|
||
SUM(a.fq2) FILTER (WHERE a.dir='Support') AS s_fq2,
|
||
SUM(a.fq3) FILTER (WHERE a.dir='Support') AS s_fq3,
|
||
SUM(a.fq4) FILTER (WHERE a.dir='Support') AS s_fq4,
|
||
SUM(a.t1q2) FILTER (WHERE a.dir='Support') AS s_t1q2,
|
||
SUM(a.t1q3) FILTER (WHERE a.dir='Support') AS s_t1q3,
|
||
SUM(a.t1q4) FILTER (WHERE a.dir='Support') AS s_t1q4,
|
||
SUM(a.t2q3) FILTER (WHERE a.dir='Support') AS s_t2q3,
|
||
SUM(a.t2q4) FILTER (WHERE a.dir='Support') AS s_t2q4,
|
||
SUM(a.t3q4) FILTER (WHERE a.dir='Support') AS s_t3q4,
|
||
SUM(a.adj1) FILTER (WHERE a.dir='Support') AS s_adj1,
|
||
SUM(a.adj2) FILTER (WHERE a.dir='Support') AS s_adj2,
|
||
SUM(a.adj3) FILTER (WHERE a.dir='Support') AS s_adj3,
|
||
SUM(a.adj4) FILTER (WHERE a.dir='Support') AS s_adj4,
|
||
SUM(a.rev2) FILTER (WHERE a.dir='Support') AS s_rev2,
|
||
SUM(a.rev3) FILTER (WHERE a.dir='Support') AS s_rev3,
|
||
SUM(a.rev4) FILTER (WHERE a.dir='Support') AS s_rev4,
|
||
-- Развитие
|
||
SUM(a.pq1) FILTER (WHERE a.dir='Development') AS d_pq1,
|
||
SUM(a.pq2) FILTER (WHERE a.dir='Development') AS d_pq2,
|
||
SUM(a.pq3) FILTER (WHERE a.dir='Development') AS d_pq3,
|
||
SUM(a.pq4) FILTER (WHERE a.dir='Development') AS d_pq4,
|
||
SUM(a.sd1) FILTER (WHERE a.dir='Development') AS d_sd1,
|
||
SUM(a.sd2) FILTER (WHERE a.dir='Development') AS d_sd2,
|
||
SUM(a.sd3) FILTER (WHERE a.dir='Development') AS d_sd3,
|
||
SUM(a.sd4) FILTER (WHERE a.dir='Development') AS d_sd4,
|
||
SUM(a.sg1) FILTER (WHERE a.dir='Development') AS d_sg1,
|
||
SUM(a.sg2) FILTER (WHERE a.dir='Development') AS d_sg2,
|
||
SUM(a.sg3) FILTER (WHERE a.dir='Development') AS d_sg3,
|
||
SUM(a.sg4) FILTER (WHERE a.dir='Development') AS d_sg4,
|
||
SUM(a.rq1) FILTER (WHERE a.dir='Development') AS d_rq1,
|
||
SUM(a.rq2) FILTER (WHERE a.dir='Development') AS d_rq2,
|
||
SUM(a.rq3) FILTER (WHERE a.dir='Development') AS d_rq3,
|
||
SUM(a.rq4) FILTER (WHERE a.dir='Development') AS d_rq4,
|
||
SUM(a.fq1) FILTER (WHERE a.dir='Development') AS d_fq1,
|
||
SUM(a.fq2) FILTER (WHERE a.dir='Development') AS d_fq2,
|
||
SUM(a.fq3) FILTER (WHERE a.dir='Development') AS d_fq3,
|
||
SUM(a.fq4) FILTER (WHERE a.dir='Development') AS d_fq4,
|
||
SUM(a.t1q2) FILTER (WHERE a.dir='Development') AS d_t1q2,
|
||
SUM(a.t1q3) FILTER (WHERE a.dir='Development') AS d_t1q3,
|
||
SUM(a.t1q4) FILTER (WHERE a.dir='Development') AS d_t1q4,
|
||
SUM(a.t2q3) FILTER (WHERE a.dir='Development') AS d_t2q3,
|
||
SUM(a.t2q4) FILTER (WHERE a.dir='Development') AS d_t2q4,
|
||
SUM(a.t3q4) FILTER (WHERE a.dir='Development') AS d_t3q4,
|
||
SUM(a.adj1) FILTER (WHERE a.dir='Development') AS d_adj1,
|
||
SUM(a.adj2) FILTER (WHERE a.dir='Development') AS d_adj2,
|
||
SUM(a.adj3) FILTER (WHERE a.dir='Development') AS d_adj3,
|
||
SUM(a.adj4) FILTER (WHERE a.dir='Development') AS d_adj4,
|
||
SUM(a.rev2) FILTER (WHERE a.dir='Development') AS d_rev2,
|
||
SUM(a.rev3) FILTER (WHERE a.dir='Development') AS d_rev3,
|
||
SUM(a.rev4) FILTER (WHERE a.dir='Development') AS d_rev4
|
||
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
|
||
LEFT JOIN line_agg a ON a.eid = ANY(t.desc_ids)
|
||
WHERE t.depth <= 2
|
||
GROUP BY t.id, t.depth, t.item_id, t.name
|
||
)
|
||
SELECT
|
||
CASE r.depth WHEN 0 THEN 'ROOT' WHEN 1 THEN 'GROUP' WHEN 2 THEN 'ITEM' END::VARCHAR,
|
||
r.depth,
|
||
r.section_code::VARCHAR,
|
||
r.name::VARCHAR,
|
||
-- Plan supp / dev
|
||
r.s_pq1, r.s_pq2, r.s_pq3, r.s_pq4,
|
||
COALESCE(r.s_pq1,0)+COALESCE(r.s_pq2,0)+COALESCE(r.s_pq3,0)+COALESCE(r.s_pq4,0),
|
||
r.d_pq1, r.d_pq2, r.d_pq3, r.d_pq4,
|
||
COALESCE(r.d_pq1,0)+COALESCE(r.d_pq2,0)+COALESCE(r.d_pq3,0)+COALESCE(r.d_pq4,0),
|
||
-- Approved supp = plan + seq_dfip + seq_ssp + reserve
|
||
COALESCE(r.s_pq1,0)+COALESCE(r.s_sd1,0)+COALESCE(r.s_sg1,0)+COALESCE(r.s_rq1,0),
|
||
COALESCE(r.s_pq2,0)+COALESCE(r.s_sd2,0)+COALESCE(r.s_sg2,0)+COALESCE(r.s_rq2,0),
|
||
COALESCE(r.s_pq3,0)+COALESCE(r.s_sd3,0)+COALESCE(r.s_sg3,0)+COALESCE(r.s_rq3,0),
|
||
COALESCE(r.s_pq4,0)+COALESCE(r.s_sd4,0)+COALESCE(r.s_sg4,0)+COALESCE(r.s_rq4,0),
|
||
COALESCE(r.s_pq1,0)+COALESCE(r.s_sd1,0)+COALESCE(r.s_sg1,0)+COALESCE(r.s_rq1,0)
|
||
+COALESCE(r.s_pq2,0)+COALESCE(r.s_sd2,0)+COALESCE(r.s_sg2,0)+COALESCE(r.s_rq2,0)
|
||
+COALESCE(r.s_pq3,0)+COALESCE(r.s_sd3,0)+COALESCE(r.s_sg3,0)+COALESCE(r.s_rq3,0)
|
||
+COALESCE(r.s_pq4,0)+COALESCE(r.s_sd4,0)+COALESCE(r.s_sg4,0)+COALESCE(r.s_rq4,0),
|
||
-- Approved dev
|
||
COALESCE(r.d_pq1,0)+COALESCE(r.d_sd1,0)+COALESCE(r.d_sg1,0)+COALESCE(r.d_rq1,0),
|
||
COALESCE(r.d_pq2,0)+COALESCE(r.d_sd2,0)+COALESCE(r.d_sg2,0)+COALESCE(r.d_rq2,0),
|
||
COALESCE(r.d_pq3,0)+COALESCE(r.d_sd3,0)+COALESCE(r.d_sg3,0)+COALESCE(r.d_rq3,0),
|
||
COALESCE(r.d_pq4,0)+COALESCE(r.d_sd4,0)+COALESCE(r.d_sg4,0)+COALESCE(r.d_rq4,0),
|
||
COALESCE(r.d_pq1,0)+COALESCE(r.d_sd1,0)+COALESCE(r.d_sg1,0)+COALESCE(r.d_rq1,0)
|
||
+COALESCE(r.d_pq2,0)+COALESCE(r.d_sd2,0)+COALESCE(r.d_sg2,0)+COALESCE(r.d_rq2,0)
|
||
+COALESCE(r.d_pq3,0)+COALESCE(r.d_sd3,0)+COALESCE(r.d_sg3,0)+COALESCE(r.d_rq3,0)
|
||
+COALESCE(r.d_pq4,0)+COALESCE(r.d_sd4,0)+COALESCE(r.d_sg4,0)+COALESCE(r.d_rq4,0),
|
||
-- Fact supp / dev
|
||
r.s_fq1, r.s_fq2, r.s_fq3, r.s_fq4,
|
||
COALESCE(r.s_fq1,0)+COALESCE(r.s_fq2,0)+COALESCE(r.s_fq3,0)+COALESCE(r.s_fq4,0),
|
||
r.d_fq1, r.d_fq2, r.d_fq3, r.d_fq4,
|
||
COALESCE(r.d_fq1,0)+COALESCE(r.d_fq2,0)+COALESCE(r.d_fq3,0)+COALESCE(r.d_fq4,0),
|
||
-- Corrected supp = approved + transfers + revisions + adj
|
||
COALESCE(r.s_pq1,0)+COALESCE(r.s_sd1,0)+COALESCE(r.s_sg1,0)+COALESCE(r.s_rq1,0)
|
||
+COALESCE(r.s_adj1,0),
|
||
COALESCE(r.s_pq2,0)+COALESCE(r.s_sd2,0)+COALESCE(r.s_sg2,0)+COALESCE(r.s_rq2,0)
|
||
+COALESCE(r.s_t1q2,0)+COALESCE(r.s_rev2,0)+COALESCE(r.s_adj2,0),
|
||
COALESCE(r.s_pq3,0)+COALESCE(r.s_sd3,0)+COALESCE(r.s_sg3,0)+COALESCE(r.s_rq3,0)
|
||
+COALESCE(r.s_t1q3,0)+COALESCE(r.s_t2q3,0)+COALESCE(r.s_rev3,0)+COALESCE(r.s_adj3,0),
|
||
COALESCE(r.s_pq4,0)+COALESCE(r.s_sd4,0)+COALESCE(r.s_sg4,0)+COALESCE(r.s_rq4,0)
|
||
+COALESCE(r.s_t1q4,0)+COALESCE(r.s_t2q4,0)+COALESCE(r.s_t3q4,0)+COALESCE(r.s_rev4,0)+COALESCE(r.s_adj4,0),
|
||
COALESCE(r.s_pq1,0)+COALESCE(r.s_pq2,0)+COALESCE(r.s_pq3,0)+COALESCE(r.s_pq4,0)
|
||
+COALESCE(r.s_sd1,0)+COALESCE(r.s_sd2,0)+COALESCE(r.s_sd3,0)+COALESCE(r.s_sd4,0)
|
||
+COALESCE(r.s_sg1,0)+COALESCE(r.s_sg2,0)+COALESCE(r.s_sg3,0)+COALESCE(r.s_sg4,0)
|
||
+COALESCE(r.s_rq1,0)+COALESCE(r.s_rq2,0)+COALESCE(r.s_rq3,0)+COALESCE(r.s_rq4,0)
|
||
+COALESCE(r.s_adj1,0)+COALESCE(r.s_adj2,0)+COALESCE(r.s_adj3,0)+COALESCE(r.s_adj4,0)
|
||
+COALESCE(r.s_rev2,0)+COALESCE(r.s_rev3,0)+COALESCE(r.s_rev4,0),
|
||
-- Corrected dev
|
||
COALESCE(r.d_pq1,0)+COALESCE(r.d_sd1,0)+COALESCE(r.d_sg1,0)+COALESCE(r.d_rq1,0)
|
||
+COALESCE(r.d_adj1,0),
|
||
COALESCE(r.d_pq2,0)+COALESCE(r.d_sd2,0)+COALESCE(r.d_sg2,0)+COALESCE(r.d_rq2,0)
|
||
+COALESCE(r.d_t1q2,0)+COALESCE(r.d_rev2,0)+COALESCE(r.d_adj2,0),
|
||
COALESCE(r.d_pq3,0)+COALESCE(r.d_sd3,0)+COALESCE(r.d_sg3,0)+COALESCE(r.d_rq3,0)
|
||
+COALESCE(r.d_t1q3,0)+COALESCE(r.d_t2q3,0)+COALESCE(r.d_rev3,0)+COALESCE(r.d_adj3,0),
|
||
COALESCE(r.d_pq4,0)+COALESCE(r.d_sd4,0)+COALESCE(r.d_sg4,0)+COALESCE(r.d_rq4,0)
|
||
+COALESCE(r.d_t1q4,0)+COALESCE(r.d_t2q4,0)+COALESCE(r.d_t3q4,0)+COALESCE(r.d_rev4,0)+COALESCE(r.d_adj4,0),
|
||
COALESCE(r.d_pq1,0)+COALESCE(r.d_pq2,0)+COALESCE(r.d_pq3,0)+COALESCE(r.d_pq4,0)
|
||
+COALESCE(r.d_sd1,0)+COALESCE(r.d_sd2,0)+COALESCE(r.d_sd3,0)+COALESCE(r.d_sd4,0)
|
||
+COALESCE(r.d_sg1,0)+COALESCE(r.d_sg2,0)+COALESCE(r.d_sg3,0)+COALESCE(r.d_sg4,0)
|
||
+COALESCE(r.d_rq1,0)+COALESCE(r.d_rq2,0)+COALESCE(r.d_rq3,0)+COALESCE(r.d_rq4,0)
|
||
+COALESCE(r.d_adj1,0)+COALESCE(r.d_adj2,0)+COALESCE(r.d_adj3,0)+COALESCE(r.d_adj4,0)
|
||
+COALESCE(r.d_rev2,0)+COALESCE(r.d_rev3,0)+COALESCE(r.d_rev4,0)
|
||
FROM tw_rollup r
|
||
ORDER BY r.section_code;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form4_view(int4, varchar, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form4_view(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 plpgsql
|
||
STABLE
|
||
AS $function$
|
||
BEGIN
|
||
|
||
-- ═══ AHR / CAP / OPER — с проектной иерархией ════════════════════════════
|
||
IF p_sheet IN ('AHR','CAP','OPER') THEN
|
||
RETURN QUERY SELECT * FROM v3.v_form4_sheet_jsonb(p_form_id, p_sheet, p_sections);
|
||
RETURN;
|
||
END IF;
|
||
|
||
-- ═══ Структура_Р ═══════════════════════════════════════════════════════
|
||
-- AHR + CAP + OPER в одной выдаче, отсортировано по (section_code,
|
||
-- num_group, _sort_path). БЕЗ проектной иерархии.
|
||
IF p_sheet = 'STRUCTURE' THEN
|
||
RETURN QUERY
|
||
WITH all_sheets AS (
|
||
SELECT * FROM v3.v_form4_sheet_lines_jsonb(p_form_id, 'AHR', p_sections)
|
||
UNION ALL
|
||
SELECT * FROM v3.v_form4_sheet_lines_jsonb(p_form_id, 'CAP', p_sections)
|
||
UNION ALL
|
||
SELECT * FROM v3.v_form4_sheet_lines_jsonb(p_form_id, 'OPER', p_sections)
|
||
)
|
||
SELECT
|
||
j.row_type,
|
||
j.depth,
|
||
ROW_NUMBER() OVER (
|
||
ORDER BY (j.header->>'section_code'),
|
||
(j.header->>'num_group') NULLS FIRST,
|
||
j._sort_path
|
||
) AS sort_order,
|
||
jsonb_build_object(
|
||
'line_id', j.line_id,
|
||
'header', j.header,
|
||
'plan', j.plan_data,
|
||
'seq_dfip', j.seq_dfip_data,
|
||
'approved', j.approved_data,
|
||
'contract_summary', j.contract_summary_data,
|
||
'allocation', j.allocation_data,
|
||
'reserve', j.reserve_data,
|
||
'collegial', j.collegial_data,
|
||
'ckk', j.ckk_data,
|
||
'contract', j.contract_data,
|
||
'booking', j.booking_data,
|
||
'q1', j.q1_data,
|
||
'q2', j.q2_data,
|
||
'q3', j.q3_data,
|
||
'q4', j.q4_data,
|
||
'totals', j.totals_data
|
||
)
|
||
FROM all_sheets j
|
||
ORDER BY (j.header->>'section_code'),
|
||
(j.header->>'num_group') NULLS FIRST,
|
||
j._sort_path;
|
||
RETURN;
|
||
END IF;
|
||
|
||
-- ═══ Смета_2026 (с разделением Поддержка/Развитие) ═════════════════════
|
||
IF p_sheet = 'SMETA' THEN
|
||
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.plan_supp_q1, 'q2', sm.plan_supp_q2, 'q3', sm.plan_supp_q3, 'q4', sm.plan_supp_q4, 'year', sm.plan_supp_year),
|
||
'development', jsonb_build_object('q1', sm.plan_dev_q1, 'q2', sm.plan_dev_q2, 'q3', sm.plan_dev_q3, 'q4', sm.plan_dev_q4, 'year', sm.plan_dev_year)
|
||
),
|
||
'approved', jsonb_build_object(
|
||
'support', jsonb_build_object('q1', sm.appr_supp_q1, 'q2', sm.appr_supp_q2, 'q3', sm.appr_supp_q3, 'q4', sm.appr_supp_q4, 'year', sm.appr_supp_year),
|
||
'development', jsonb_build_object('q1', sm.appr_dev_q1, 'q2', sm.appr_dev_q2, 'q3', sm.appr_dev_q3, 'q4', sm.appr_dev_q4, 'year', sm.appr_dev_year)
|
||
),
|
||
'fact', jsonb_build_object(
|
||
'support', jsonb_build_object('q1', sm.fact_supp_q1, 'q2', sm.fact_supp_q2, 'q3', sm.fact_supp_q3, 'q4', sm.fact_supp_q4, 'year', sm.fact_supp_year),
|
||
'development', jsonb_build_object('q1', sm.fact_dev_q1, 'q2', sm.fact_dev_q2, 'q3', sm.fact_dev_q3, 'q4', sm.fact_dev_q4, 'year', sm.fact_dev_year)
|
||
),
|
||
'corrected', jsonb_build_object(
|
||
'support', jsonb_build_object('q1', sm.corr_supp_q1, 'q2', sm.corr_supp_q2, 'q3', sm.corr_supp_q3, 'q4', sm.corr_supp_q4, 'year', sm.corr_supp_year),
|
||
'development', jsonb_build_object('q1', sm.corr_dev_q1, 'q2', sm.corr_dev_q2, 'q3', sm.corr_dev_q3, 'q4', sm.corr_dev_q4, 'year', sm.corr_dev_year)
|
||
)
|
||
)
|
||
FROM v3.v_form4_smeta(p_form_id) sm
|
||
ORDER BY sm.section_code;
|
||
RETURN;
|
||
END IF;
|
||
|
||
RAISE EXCEPTION 'Unknown sheet for FORM_4: %', p_sheet
|
||
USING HINT = 'Use AHR / CAP / OPER / SMETA / STRUCTURE';
|
||
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
-- DROP FUNCTION v3.v_form_view(int4, varchar, _text, varchar);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form_view(p_form_id integer, p_sheet character varying, p_sections text[] DEFAULT NULL::text[], p_direction character varying DEFAULT NULL::character varying)
|
||
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
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;
|
||
|
||
-- ═══ 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$
|
||
; |