941 lines
46 KiB
PL/PgSQL
941 lines
46 KiB
PL/PgSQL
-- ════════════════════════════════════════════════════════════════════════════
|
||
-- МИГРАЦИЯ: Поля project + реструктуризация rf_project_report
|
||
-- (year -> rf_project_year, атрибуты сметы -> smeta)
|
||
-- ════════════════════════════════════════════════════════════════════════════
|
||
|
||
-- Поля project
|
||
ALTER TABLE v3.project ADD COLUMN status VARCHAR DEFAULT 'created';
|
||
ALTER TABLE v3.project ADD COLUMN technical_number VARCHAR;
|
||
ALTER TABLE v3.project ADD COLUMN krf_decision_date DATE;
|
||
ALTER TABLE v3.project ADD COLUMN fk_decision_date DATE;
|
||
ALTER TABLE v3.project ADD COLUMN board_decision_date DATE;
|
||
ALTER TABLE v3.project ADD COLUMN open_relocate_close_date DATE;
|
||
ALTER TABLE v3.project ADD COLUMN funding_by_ko_decision VARCHAR;
|
||
|
||
ALTER TABLE v3.project DROP CONSTRAINT IF EXISTS project_status_check;
|
||
ALTER TABLE v3.project ADD CONSTRAINT project_status_check
|
||
CHECK (status IS NULL OR status IN ('created', 'agreed', 'archived'));
|
||
|
||
ALTER TABLE v3.project DROP CONSTRAINT IF EXISTS project_funding_ko_check;
|
||
ALTER TABLE v3.project ADD CONSTRAINT project_funding_ko_check
|
||
CHECK (funding_by_ko_decision IS NULL OR funding_by_ko_decision IN ('prrs_budget', 'bank_reserve'));
|
||
|
||
-- rf_project_year: год проекта как отдельная сущность
|
||
CREATE TABLE v3.rf_project_year (
|
||
id SERIAL PRIMARY KEY,
|
||
project_id INTEGER NOT NULL REFERENCES v3.project(id),
|
||
year INTEGER NOT NULL,
|
||
CONSTRAINT uq_v3_rf_project_year UNIQUE (project_id, year)
|
||
);
|
||
CREATE INDEX ix_v3_rf_project_year_project ON v3.rf_project_year(project_id);
|
||
|
||
-- Перенос существующих year из rf_project_report в rf_project_year
|
||
INSERT INTO v3.rf_project_year (project_id, year)
|
||
SELECT DISTINCT project_id, year FROM v3.rf_project_report;
|
||
|
||
-- Связь rf_project_report с rf_project_year
|
||
ALTER TABLE v3.rf_project_report ADD COLUMN rf_project_year_id INTEGER;
|
||
|
||
UPDATE v3.rf_project_report rpr
|
||
SET rf_project_year_id = rpy.id
|
||
FROM v3.rf_project_year rpy
|
||
WHERE rpy.project_id = rpr.project_id AND rpy.year = rpr.year;
|
||
|
||
ALTER TABLE v3.rf_project_report ALTER COLUMN rf_project_year_id SET NOT NULL;
|
||
ALTER TABLE v3.rf_project_report
|
||
ADD CONSTRAINT fk_v3_rf_project_report_year
|
||
FOREIGN KEY (rf_project_year_id) REFERENCES v3.rf_project_year(id);
|
||
|
||
-- Убираем year из rf_project_report
|
||
ALTER TABLE v3.rf_project_report DROP CONSTRAINT IF EXISTS uq_v3_rf_project_report;
|
||
DROP INDEX IF EXISTS v3.ix_v3_rf_project_report_proj_year;
|
||
ALTER TABLE v3.rf_project_report DROP COLUMN year;
|
||
|
||
-- Восстанавливаем уникальность и индекс на новой связке
|
||
ALTER TABLE v3.rf_project_report
|
||
ADD CONSTRAINT uq_v3_rf_project_report UNIQUE (project_id, rf_project_year_id, report_type);
|
||
CREATE INDEX ix_v3_rf_project_report_proj_year ON v3.rf_project_report(project_id, rf_project_year_id);
|
||
|
||
-- smeta: атрибуты сметы на год проекта
|
||
CREATE TABLE v3.smeta (
|
||
id SERIAL PRIMARY KEY,
|
||
rf_project_year_id INTEGER NOT NULL REFERENCES v3.rf_project_year(id),
|
||
is_in_plan BOOLEAN,
|
||
is_in_plan_q2 BOOLEAN,
|
||
is_in_plan_q3 BOOLEAN,
|
||
is_in_plan_q4 BOOLEAN,
|
||
development_block VARCHAR,
|
||
reserve_to_prrs_ahr NUMERIC,
|
||
reserve_to_prrs_kv NUMERIC,
|
||
CONSTRAINT smeta_development_block_check
|
||
CHECK (development_block IS NULL OR development_block IN ('out_of_base_plan', 'carryover', 'additional_funding'))
|
||
);
|
||
|
||
|
||
-- Funcs
|
||
CREATE OR REPLACE FUNCTION v3.add_form3_phase(p_rf_project_report_id integer, p_phase_code character varying, p_role character varying, p_column_keys text[], p_opens_at timestamp without time zone, p_closes_at timestamp without time zone)
|
||
RETURNS v3.form3_phase
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_phase v3.form3_phase;
|
||
v_org_unit_id INT;
|
||
v_org_unit_title VARCHAR;
|
||
v_year INT;
|
||
v_task_name TEXT;
|
||
v_phase_id TEXT;
|
||
v_utc_offset VARCHAR(3);
|
||
v_report_type VARCHAR;
|
||
v_project_id INT;
|
||
BEGIN
|
||
SELECT p.org_unit_id, ou.title, rpry.year, ou.utc_offset, rpr.report_type, rpr.project_id
|
||
INTO v_org_unit_id, v_org_unit_title, v_year, v_utc_offset, v_report_type, v_project_id
|
||
FROM v3.rf_project_report rpr
|
||
JOIN v3.rf_project_year rpry ON rpr.rf_project_year_id = rpry.id
|
||
LEFT JOIN v3.project p ON p.id = rpr.project_id
|
||
LEFT JOIN v3.org_unit ou ON ou.id = p.org_unit_id
|
||
WHERE rpr.id = p_rf_project_report_id;
|
||
|
||
INSERT INTO v3.form3_phase
|
||
(rf_project_report_id, phase_code, role, column_keys, opens_at, closes_at)
|
||
VALUES (
|
||
p_rf_project_report_id,
|
||
p_phase_code,
|
||
p_role,
|
||
p_column_keys,
|
||
CASE WHEN p_opens_at IS NOT NULL THEN (p_opens_at::text || v_utc_offset)::timestamptz ELSE NULL END,
|
||
CASE WHEN p_closes_at IS NOT NULL THEN (p_closes_at::text || v_utc_offset)::timestamptz ELSE NULL END
|
||
|
||
)
|
||
RETURNING * INTO v_phase;
|
||
|
||
|
||
v_task_name := trim(concat(
|
||
'FORM_3',
|
||
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', p_rf_project_report_id, p_phase_code);
|
||
|
||
PERFORM v3.log_event(
|
||
'ACCESS_WINDOW_CHANGE', 'ACCESS',
|
||
jsonb_build_object(
|
||
'entity_type', 'project',
|
||
'entity_id', v_project_id,
|
||
'core_entity_type', 'form3_phase',
|
||
'core_entity_id', v_phase_id,
|
||
'action', 'create',
|
||
'phase_id', v_phase_id,
|
||
'task_name', NULLIF(v_task_name, ''),
|
||
'report_id', p_rf_project_report_id,
|
||
'org_unit_id', v_org_unit_id,
|
||
'org_unit_name', v_org_unit_title,
|
||
'year', v_year,
|
||
'project_id', v_project_id,
|
||
'report_type', v_report_type,
|
||
'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
|
||
),
|
||
null, null, v_org_unit_id
|
||
);
|
||
|
||
RETURN v_phase;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
|
||
-- DROP FUNCTION v3.add_project_year(int4, int4, bool);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.add_project_year(p_project_id integer, p_year integer, p_emit_audit boolean DEFAULT true)
|
||
RETURNS TABLE(limit_report_id integer, current_expenses_report_id integer)
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_ryid INT;
|
||
v_lim INT;
|
||
v_cur INT;
|
||
BEGIN
|
||
IF p_year IS NULL THEN
|
||
RAISE EXCEPTION 'p_year обязателен';
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM v3.project WHERE id = p_project_id) THEN
|
||
RAISE EXCEPTION 'project id=% не существует', p_project_id;
|
||
END IF;
|
||
IF EXISTS (SELECT 1 FROM v3.rf_project_year
|
||
WHERE project_id = p_project_id AND year = p_year) THEN
|
||
RAISE EXCEPTION 'год % у проекта id=% уже заведён', p_year, p_project_id;
|
||
END IF;
|
||
|
||
INSERT INTO v3.rf_project_year (project_id, year)
|
||
VALUES (p_project_id, p_year)
|
||
RETURNING id INTO v_ryid;
|
||
|
||
INSERT INTO v3.smeta (rf_project_year_id)
|
||
VALUES (v_ryid);
|
||
|
||
INSERT INTO v3.rf_project_report (project_id, rf_project_year_id, report_type)
|
||
VALUES (p_project_id, v_ryid, 'LIMIT') RETURNING id INTO v_lim;
|
||
INSERT INTO v3.rf_project_report (project_id, rf_project_year_id, report_type)
|
||
VALUES (p_project_id, v_ryid, 'CURRENT_EXPENSES') RETURNING id INTO v_cur;
|
||
|
||
PERFORM v3.copy_template_to_report(v_lim);
|
||
PERFORM v3.copy_template_to_report(v_cur);
|
||
|
||
PERFORM v3.seed_form3_report_lines(v_lim);
|
||
PERFORM v3.seed_form3_report_lines(v_cur);
|
||
|
||
IF p_emit_audit THEN
|
||
PERFORM v3.log_event(
|
||
'PROJECT_YEAR_ADD', 'PROJECT',
|
||
jsonb_build_object(
|
||
'project_id', p_project_id,
|
||
'year', p_year,
|
||
'limit_report_id', v_lim,
|
||
'current_expenses_report_id', v_cur
|
||
)
|
||
);
|
||
END IF;
|
||
|
||
RETURN QUERY SELECT v_lim, v_cur;
|
||
END;
|
||
$function$
|
||
;
|
||
-- DROP FUNCTION v3.del_form3_phase(int4, varchar);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.del_form3_phase(p_rf_project_report_id integer, p_phase_code character varying)
|
||
RETURNS boolean
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old v3.form3_phase;
|
||
v_org_unit_id INT;
|
||
v_org_unit_title VARCHAR;
|
||
v_year INT;
|
||
v_task_name TEXT;
|
||
v_phase_id TEXT;
|
||
v_report_type VARCHAR;
|
||
v_project_id INT;
|
||
BEGIN
|
||
DELETE FROM v3.form3_phase
|
||
WHERE rf_project_report_id = p_rf_project_report_id
|
||
AND phase_code = p_phase_code
|
||
RETURNING * INTO v_old;
|
||
|
||
IF NOT FOUND THEN
|
||
RETURN FALSE;
|
||
END IF;
|
||
|
||
SELECT p.org_unit_id, ou.title, rpry.year, rpr.report_type, rpr.project_id
|
||
INTO v_org_unit_id, v_org_unit_title, v_year, v_report_type, v_project_id
|
||
FROM v3.rf_project_report rpr
|
||
JOIN v3.rf_project_year rpry ON rpr.rf_project_year_id = rpry.id
|
||
LEFT JOIN v3.project p ON p.id = rpr.project_id
|
||
LEFT JOIN v3.org_unit ou ON ou.id = p.org_unit_id
|
||
WHERE rpr.id = p_rf_project_report_id;
|
||
v_task_name := trim(concat(
|
||
'FORM_3',
|
||
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', p_rf_project_report_id, p_phase_code);
|
||
|
||
PERFORM v3.log_event(
|
||
'ACCESS_WINDOW_CHANGE', 'ACCESS',
|
||
jsonb_build_object(
|
||
'entity_type', 'project',
|
||
'entity_id', v_project_id,
|
||
'core_entity_type', 'form3_phase',
|
||
'core_entity_id', v_phase_id,
|
||
'action', 'delete',
|
||
'phase_id', v_phase_id,
|
||
'phase_name', p_phase_code,
|
||
'task_name', NULLIF(v_task_name, ''),
|
||
'project_id', v_project_id,
|
||
'year', v_year,
|
||
'report_type', v_report_type,
|
||
'org_unit_id', v_org_unit_id,
|
||
'org_unit_name', v_org_unit_title,
|
||
'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
|
||
),
|
||
null, null, v_org_unit_id
|
||
);
|
||
RETURN TRUE;
|
||
END;
|
||
$function$
|
||
;
|
||
-- DROP FUNCTION v3.upd_form3_phase(int4, varchar, varchar, _text, timestamp, timestamp);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_form3_phase(p_rf_project_report_id integer, p_phase_code character varying, p_role character varying DEFAULT NULL::character varying, p_column_keys text[] DEFAULT NULL::text[], p_opens_at timestamp without time zone DEFAULT NULL::timestamp without time zone, p_closes_at timestamp without time zone DEFAULT NULL::timestamp without time zone)
|
||
RETURNS v3.form3_phase
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old v3.form3_phase;
|
||
v_new v3.form3_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;
|
||
v_utc_offset VARCHAR(3);
|
||
v_opens_at timestamptz;
|
||
v_closes_at timestamptz;
|
||
v_report_type VARCHAR;
|
||
v_project_id INT;
|
||
BEGIN
|
||
SELECT * INTO v_old
|
||
FROM v3.form3_phase
|
||
WHERE rf_project_report_id = p_rf_project_report_id
|
||
AND phase_code = p_phase_code;
|
||
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'form3_phase не найден (rf_project_report_id=%, phase=%)',
|
||
p_rf_project_report_id, p_phase_code;
|
||
END IF;
|
||
|
||
SELECT p.org_unit_id, ou.title, rpry.year, ou.utc_offset, rpr.report_type, rpr.project_id
|
||
INTO v_org_unit_id, v_org_unit_title, v_year, v_utc_offset, v_report_type, v_project_id
|
||
FROM v3.rf_project_report rpr
|
||
JOIN v3.rf_project_year rpry ON rpr.rf_project_year_id = rpry.id
|
||
LEFT JOIN v3.project p ON p.id = rpr.project_id
|
||
LEFT JOIN v3.org_unit ou ON ou.id = p.org_unit_id
|
||
WHERE rpr.id = p_rf_project_report_id;
|
||
v_task_name := trim(concat(
|
||
'FORM_3',
|
||
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
|
||
));
|
||
|
||
IF p_opens_at is not null THEN
|
||
v_opens_at = (p_opens_at::text || v_utc_offset)::timestamptz;
|
||
END IF;
|
||
IF p_closes_at is not null THEN
|
||
v_closes_at = (p_closes_at::text || v_utc_offset)::timestamptz;
|
||
END IF;
|
||
|
||
UPDATE v3.form3_phase
|
||
SET role = COALESCE(p_role, role),
|
||
column_keys = COALESCE(p_column_keys, column_keys),
|
||
opens_at = COALESCE(v_opens_at, opens_at),
|
||
closes_at = COALESCE(v_closes_at, closes_at)
|
||
WHERE rf_project_report_id = p_rf_project_report_id
|
||
AND phase_code = p_phase_code
|
||
RETURNING * INTO v_new;
|
||
|
||
|
||
v_phase_id := format('%s:%s', p_rf_project_report_id, 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(
|
||
'entity_type', 'project',
|
||
'entity_id', v_project_id,
|
||
'core_entity_type', 'form3_phase',
|
||
'core_entity_id', v_phase_id,
|
||
'action', 'update',
|
||
'phase_id', v_phase_id,
|
||
'phase_name', p_phase_code,
|
||
'task_name', NULLIF(v_task_name, ''),
|
||
'report_id', p_rf_project_report_id,
|
||
'report_type', v_report_type,
|
||
'project_id', v_project_id,
|
||
'org_unit_id', v_org_unit_id,
|
||
'org_unit_name', v_org_unit_title,
|
||
'year', v_year,
|
||
'phase_code', p_phase_code,
|
||
'closes_at_before', v_old.closes_at,
|
||
'closes_at_after', v_new.closes_at
|
||
),
|
||
null, null, v_org_unit_id
|
||
);
|
||
END IF;
|
||
|
||
IF v_has_window_change THEN
|
||
PERFORM v3.log_event(
|
||
'ACCESS_WINDOW_CHANGE', 'ACCESS',
|
||
jsonb_build_object(
|
||
'entity_type', 'project',
|
||
'entity_id', v_project_id,
|
||
'core_entity_type', 'form3_phase',
|
||
'core_entity_id', v_phase_id,
|
||
'action', 'update',
|
||
'phase_id', v_phase_id,
|
||
'phase_name', p_phase_code,
|
||
'task_id', p_rf_project_report_id,
|
||
'task_name', NULLIF(v_task_name, ''),
|
||
'project_id', v_project_id,
|
||
'report_type', v_report_type,
|
||
'year', v_year,
|
||
'org_unit_id', v_org_unit_id,
|
||
'org_unit_name', v_org_unit_title,
|
||
'phase_code', p_phase_code,
|
||
'changes', v_changes
|
||
),
|
||
null, null, v_org_unit_id
|
||
);
|
||
END IF;
|
||
|
||
RETURN v_new;
|
||
END;
|
||
$function$
|
||
;
|
||
-- DROP FUNCTION v3.v_form3_project_jsonb(int4, varchar, _text);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.v_form3_project_jsonb(p_project_id integer, p_report_type 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$
|
||
WITH reports AS (
|
||
SELECT r.id AS report_id, rpry.year
|
||
FROM v3.rf_project_report r
|
||
JOIN v3.rf_project_year rpry ON rpry.id = r.rf_project_year_id
|
||
WHERE r.project_id = p_project_id
|
||
AND r.report_type = p_report_type
|
||
),
|
||
-- Разворачиваем каждый год в строки его листа
|
||
per_year AS (
|
||
SELECT
|
||
v.row_type,
|
||
v.depth,
|
||
(v.data->'header'->>'expense_item_id')::INT AS eiid,
|
||
v.data->'header' AS header,
|
||
v.sort_order AS y_sort,
|
||
rp.year AS year,
|
||
v.data AS ydata
|
||
FROM reports rp
|
||
CROSS JOIN LATERAL v3.v_form3_report_jsonb(rp.report_id, p_sections) v
|
||
),
|
||
-- Сшиваем по идентичности строки (row_type + статья): сетка одинакова во всех годах
|
||
agg AS (
|
||
SELECT
|
||
py.row_type,
|
||
py.depth,
|
||
py.eiid,
|
||
MIN(py.y_sort) AS sort_order,
|
||
(array_agg(py.header ORDER BY py.year))[1] AS header,
|
||
jsonb_object_agg(py.year::text, py.ydata ORDER BY py.year) AS years,
|
||
SUM((py.ydata->'totals'->>'total_actual')::numeric) AS project_total
|
||
FROM per_year py
|
||
GROUP BY py.row_type, py.depth, py.eiid
|
||
)
|
||
SELECT
|
||
a.row_type,
|
||
a.depth,
|
||
ROW_NUMBER() OVER (ORDER BY a.sort_order) AS sort_order,
|
||
jsonb_build_object(
|
||
'header', a.header,
|
||
'years', a.years,
|
||
'project_total', a.project_total
|
||
) AS data
|
||
FROM agg a
|
||
ORDER BY a.sort_order;
|
||
$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.rf_project_year rpry ON rpry.id = r.rf_project_year_id
|
||
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 rpry.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$
|
||
;
|
||
|
||
|
||
CREATE OR REPLACE FUNCTION v3.copy_template_to_report(p_report_id integer)
|
||
RETURNS integer
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_report_type VARCHAR;
|
||
v_inserted INTEGER;
|
||
v_org_unit INTEGER;
|
||
v_utc_offset VARCHAR(3);
|
||
v_year INTEGER;
|
||
BEGIN
|
||
SELECT report_type, org_unit_id, "year" INTO v_report_type, v_org_unit, v_year
|
||
FROM v3.rf_project_report AS rpr
|
||
INNER JOIN v3.project AS p ON p.id = rpr.project_id
|
||
INNER JOIN v3.rf_project_year AS rpy ON rpy.id = rpr.rf_project_year_id
|
||
WHERE rpr.id = p_report_id;
|
||
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'rf_project_report id=% не существует', p_report_id;
|
||
END IF;
|
||
|
||
SELECT utc_offset INTO v_utc_offset
|
||
FROM v3.org_unit
|
||
WHERE id = v_org_unit;
|
||
|
||
INSERT INTO v3.form3_phase
|
||
(rf_project_report_id, phase_code, role, column_keys, opens_at, closes_at)
|
||
SELECT p_report_id, pt.phase_code, pt.role,
|
||
pt.column_keys,
|
||
((pt.opens_at + INTERVAL '1 year' * v_year)::text || v_utc_offset)::timestamptz,
|
||
((pt.closes_at + INTERVAL '1 year' * v_year)::text || v_utc_offset)::timestamptz
|
||
FROM v3.phase_template pt
|
||
WHERE pt.form_type = 'FORM_3'
|
||
AND pt.sheet = v_report_type
|
||
ON CONFLICT (rf_project_report_id, phase_code) DO NOTHING;
|
||
|
||
GET DIAGNOSTICS v_inserted = ROW_COUNT;
|
||
RETURN v_inserted;
|
||
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;
|
||
v_actor_id INTEGER;
|
||
v_role VARCHAR;
|
||
BEGIN
|
||
SELECT p.name, p.level, p.org_unit_id
|
||
INTO v_old
|
||
FROM v3.project p WHERE p.id = p_project_id;
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'project #% не существует', p_project_id;
|
||
END IF;
|
||
|
||
IF NOT v3.can_delete_project(p_project_id) THEN
|
||
RAISE EXCEPTION 'project #% невозможно удалить', p_project_id;
|
||
END IF;
|
||
|
||
DELETE FROM v3.form3_phase f3p WHERE f3p.rf_project_report_id IN (
|
||
SELECT id FROM v3.rf_project_report rpr WHERE project_id = p_project_id
|
||
);
|
||
DELETE FROM v3.rf_project_report_quarter
|
||
WHERE rf_project_report_line_id IN (
|
||
SELECT l.id FROM v3.rf_project_report_line l
|
||
JOIN v3.rf_project_report r ON r.id = l.rf_project_report_id
|
||
WHERE r.project_id = p_project_id);
|
||
DELETE FROM v3.rf_project_report_line
|
||
WHERE rf_project_report_id IN (
|
||
SELECT id FROM v3.rf_project_report WHERE project_id = p_project_id);
|
||
DELETE FROM v3.rf_project_report WHERE project_id = p_project_id;
|
||
DELETE FROM v3.smeta WHERE rf_project_year_id IN (
|
||
SELECT id FROM v3.rf_project_year WHERE project_id = p_project_id
|
||
);
|
||
DELETE FROM v3.rf_project_year 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.upd_project(int4, varchar, varchar, varchar, varchar, varchar, varchar, varchar, int4, numeric, int4, date, date, date, date, varchar);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_project(
|
||
p_project_id integer,
|
||
p_name character varying DEFAULT NULL,
|
||
p_status character varying DEFAULT NULL,
|
||
p_technical_number character varying DEFAULT NULL,
|
||
p_project_type character varying DEFAULT NULL,
|
||
p_vsp_format character varying DEFAULT NULL,
|
||
p_placement_type character varying DEFAULT NULL,
|
||
p_object_address character varying DEFAULT NULL,
|
||
p_staff_count integer DEFAULT NULL,
|
||
p_total_area numeric DEFAULT NULL,
|
||
p_org_unit_id integer DEFAULT NULL,
|
||
p_krf_decision_date date DEFAULT NULL,
|
||
p_fk_decision_date date DEFAULT NULL,
|
||
p_board_decision_date date DEFAULT NULL,
|
||
p_open_relocate_close_date date DEFAULT NULL,
|
||
p_funding_by_ko_decision character varying DEFAULT NULL
|
||
)
|
||
RETURNS void
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old v3.project%ROWTYPE;
|
||
v_changes JSONB := '{}'::jsonb;
|
||
BEGIN
|
||
SELECT * INTO v_old FROM v3.project WHERE id = p_project_id;
|
||
IF NOT FOUND THEN
|
||
RAISE EXCEPTION 'project id=% не существует', p_project_id;
|
||
END IF;
|
||
|
||
IF p_name IS NOT NULL AND p_name IS DISTINCT FROM v_old.name THEN
|
||
UPDATE v3.project SET name = p_name WHERE id = p_project_id;
|
||
v_changes := v_changes || jsonb_build_object('name',
|
||
jsonb_build_object('before', v3._diff_val(v_old.name), 'after', v3._diff_val(p_name)));
|
||
END IF;
|
||
|
||
IF p_status IS NOT NULL AND p_status IS DISTINCT FROM v_old.status THEN
|
||
UPDATE v3.project SET status = p_status WHERE id = p_project_id;
|
||
v_changes := v_changes || jsonb_build_object('status',
|
||
jsonb_build_object('before', v3._diff_val(v_old.status), 'after', v3._diff_val(p_status)));
|
||
END IF;
|
||
|
||
IF p_technical_number IS NOT NULL AND p_technical_number IS DISTINCT FROM v_old.technical_number THEN
|
||
UPDATE v3.project SET technical_number = p_technical_number WHERE id = p_project_id;
|
||
v_changes := v_changes || jsonb_build_object('technical_number',
|
||
jsonb_build_object('before', v3._diff_val(v_old.technical_number), 'after', v3._diff_val(p_technical_number)));
|
||
END IF;
|
||
|
||
IF p_project_type IS NOT NULL AND p_project_type IS DISTINCT FROM v_old.project_type THEN
|
||
UPDATE v3.project SET project_type = p_project_type WHERE id = p_project_id;
|
||
v_changes := v_changes || jsonb_build_object('project_type',
|
||
jsonb_build_object('before', v3._diff_val(v_old.project_type), 'after', v3._diff_val(p_project_type)));
|
||
END IF;
|
||
|
||
IF p_vsp_format IS NOT NULL AND p_vsp_format IS DISTINCT FROM v_old.vsp_format THEN
|
||
UPDATE v3.project SET vsp_format = p_vsp_format WHERE id = p_project_id;
|
||
v_changes := v_changes || jsonb_build_object('vsp_format',
|
||
jsonb_build_object('before', v3._diff_val(v_old.vsp_format), 'after', v3._diff_val(p_vsp_format)));
|
||
END IF;
|
||
|
||
IF p_placement_type IS NOT NULL AND p_placement_type IS DISTINCT FROM v_old.placement_type THEN
|
||
UPDATE v3.project SET placement_type = p_placement_type WHERE id = p_project_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_object_address IS NOT NULL AND p_object_address IS DISTINCT FROM v_old.object_address THEN
|
||
UPDATE v3.project SET object_address = p_object_address WHERE id = p_project_id;
|
||
v_changes := v_changes || jsonb_build_object('object_address',
|
||
jsonb_build_object('before', v3._diff_val(v_old.object_address), 'after', v3._diff_val(p_object_address)));
|
||
END IF;
|
||
|
||
IF p_staff_count IS NOT NULL AND p_staff_count IS DISTINCT FROM v_old.staff_count THEN
|
||
UPDATE v3.project SET staff_count = p_staff_count WHERE id = p_project_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.project SET total_area = p_total_area WHERE id = p_project_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_org_unit_id IS NOT NULL AND p_org_unit_id IS DISTINCT FROM v_old.org_unit_id THEN
|
||
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;
|
||
UPDATE v3.project SET org_unit_id = p_org_unit_id WHERE id = p_project_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 p_krf_decision_date IS NOT NULL AND p_krf_decision_date IS DISTINCT FROM v_old.krf_decision_date THEN
|
||
UPDATE v3.project SET krf_decision_date = p_krf_decision_date WHERE id = p_project_id;
|
||
v_changes := v_changes || jsonb_build_object('krf_decision_date',
|
||
jsonb_build_object('before', v3._diff_val(v_old.krf_decision_date), 'after', v3._diff_val(p_krf_decision_date)));
|
||
END IF;
|
||
|
||
IF p_fk_decision_date IS NOT NULL AND p_fk_decision_date IS DISTINCT FROM v_old.fk_decision_date THEN
|
||
UPDATE v3.project SET fk_decision_date = p_fk_decision_date WHERE id = p_project_id;
|
||
v_changes := v_changes || jsonb_build_object('fk_decision_date',
|
||
jsonb_build_object('before', v3._diff_val(v_old.fk_decision_date), 'after', v3._diff_val(p_fk_decision_date)));
|
||
END IF;
|
||
|
||
IF p_board_decision_date IS NOT NULL AND p_board_decision_date IS DISTINCT FROM v_old.board_decision_date THEN
|
||
UPDATE v3.project SET board_decision_date = p_board_decision_date WHERE id = p_project_id;
|
||
v_changes := v_changes || jsonb_build_object('board_decision_date',
|
||
jsonb_build_object('before', v3._diff_val(v_old.board_decision_date), 'after', v3._diff_val(p_board_decision_date)));
|
||
END IF;
|
||
|
||
IF p_open_relocate_close_date IS NOT NULL AND p_open_relocate_close_date IS DISTINCT FROM v_old.open_relocate_close_date THEN
|
||
UPDATE v3.project SET open_relocate_close_date = p_open_relocate_close_date WHERE id = p_project_id;
|
||
v_changes := v_changes || jsonb_build_object('open_relocate_close_date',
|
||
jsonb_build_object('before', v3._diff_val(v_old.open_relocate_close_date), 'after', v3._diff_val(p_open_relocate_close_date)));
|
||
END IF;
|
||
|
||
IF p_funding_by_ko_decision IS NOT NULL AND p_funding_by_ko_decision IS DISTINCT FROM v_old.funding_by_ko_decision THEN
|
||
UPDATE v3.project SET funding_by_ko_decision = p_funding_by_ko_decision WHERE id = p_project_id;
|
||
v_changes := v_changes || jsonb_build_object('funding_by_ko_decision',
|
||
jsonb_build_object('before', v3._diff_val(v_old.funding_by_ko_decision), 'after', v3._diff_val(p_funding_by_ko_decision)));
|
||
END IF;
|
||
|
||
IF v_changes <> '{}'::jsonb THEN
|
||
PERFORM v3.log_event(
|
||
'PROJECT_UPDATE', 'PROJECT',
|
||
jsonb_build_object(
|
||
'project_id', p_project_id,
|
||
'entity_id', p_project_id,
|
||
'changes', v_changes
|
||
), null, null, COALESCE(p_org_unit_id, v_old.org_unit_id)
|
||
);
|
||
END IF;
|
||
END;
|
||
$function$
|
||
;
|
||
|
||
|
||
-- DROP FUNCTION v3.upd_smeta(int4, bool, bool, bool, bool, varchar, numeric, numeric);
|
||
|
||
CREATE OR REPLACE FUNCTION v3.upd_smeta(
|
||
p_rf_project_year_id integer,
|
||
p_is_in_plan boolean DEFAULT NULL,
|
||
p_is_in_plan_q2 boolean DEFAULT NULL,
|
||
p_is_in_plan_q3 boolean DEFAULT NULL,
|
||
p_is_in_plan_q4 boolean DEFAULT NULL,
|
||
p_development_block character varying DEFAULT NULL,
|
||
p_reserve_to_prrs_ahr numeric DEFAULT NULL,
|
||
p_reserve_to_prrs_kv numeric DEFAULT NULL
|
||
)
|
||
RETURNS void
|
||
LANGUAGE plpgsql
|
||
AS $function$
|
||
DECLARE
|
||
v_old v3.smeta%ROWTYPE;
|
||
v_changes JSONB := '{}'::jsonb;
|
||
v_project_id INT;
|
||
v_org_unit_id INT;
|
||
BEGIN
|
||
IF NOT EXISTS (SELECT 1 FROM v3.rf_project_year WHERE id = p_rf_project_year_id) THEN
|
||
RAISE EXCEPTION 'rf_project_year id=% не существует', p_rf_project_year_id;
|
||
END IF;
|
||
|
||
INSERT INTO v3.smeta (rf_project_year_id)
|
||
SELECT p_rf_project_year_id
|
||
WHERE NOT EXISTS (SELECT 1 FROM v3.smeta WHERE rf_project_year_id = p_rf_project_year_id);
|
||
|
||
SELECT * INTO v_old FROM v3.smeta WHERE rf_project_year_id = p_rf_project_year_id;
|
||
|
||
IF p_is_in_plan IS NOT NULL AND p_is_in_plan IS DISTINCT FROM v_old.is_in_plan THEN
|
||
UPDATE v3.smeta SET is_in_plan = p_is_in_plan WHERE id = v_old.id;
|
||
v_changes := v_changes || jsonb_build_object('is_in_plan',
|
||
jsonb_build_object('before', v3._diff_val(v_old.is_in_plan), 'after', v3._diff_val(p_is_in_plan)));
|
||
END IF;
|
||
|
||
IF p_is_in_plan_q2 IS NOT NULL AND p_is_in_plan_q2 IS DISTINCT FROM v_old.is_in_plan_q2 THEN
|
||
UPDATE v3.smeta SET is_in_plan_q2 = p_is_in_plan_q2 WHERE id = v_old.id;
|
||
v_changes := v_changes || jsonb_build_object('is_in_plan_q2',
|
||
jsonb_build_object('before', v3._diff_val(v_old.is_in_plan_q2), 'after', v3._diff_val(p_is_in_plan_q2)));
|
||
END IF;
|
||
|
||
IF p_is_in_plan_q3 IS NOT NULL AND p_is_in_plan_q3 IS DISTINCT FROM v_old.is_in_plan_q3 THEN
|
||
UPDATE v3.smeta SET is_in_plan_q3 = p_is_in_plan_q3 WHERE id = v_old.id;
|
||
v_changes := v_changes || jsonb_build_object('is_in_plan_q3',
|
||
jsonb_build_object('before', v3._diff_val(v_old.is_in_plan_q3), 'after', v3._diff_val(p_is_in_plan_q3)));
|
||
END IF;
|
||
|
||
IF p_is_in_plan_q4 IS NOT NULL AND p_is_in_plan_q4 IS DISTINCT FROM v_old.is_in_plan_q4 THEN
|
||
UPDATE v3.smeta SET is_in_plan_q4 = p_is_in_plan_q4 WHERE id = v_old.id;
|
||
v_changes := v_changes || jsonb_build_object('is_in_plan_q4',
|
||
jsonb_build_object('before', v3._diff_val(v_old.is_in_plan_q4), 'after', v3._diff_val(p_is_in_plan_q4)));
|
||
END IF;
|
||
|
||
IF p_development_block IS NOT NULL AND p_development_block IS DISTINCT FROM v_old.development_block THEN
|
||
UPDATE v3.smeta SET development_block = p_development_block WHERE id = v_old.id;
|
||
v_changes := v_changes || jsonb_build_object('development_block',
|
||
jsonb_build_object('before', v3._diff_val(v_old.development_block), 'after', v3._diff_val(p_development_block)));
|
||
END IF;
|
||
|
||
IF p_reserve_to_prrs_ahr IS NOT NULL AND p_reserve_to_prrs_ahr IS DISTINCT FROM v_old.reserve_to_prrs_ahr THEN
|
||
UPDATE v3.smeta SET reserve_to_prrs_ahr = p_reserve_to_prrs_ahr WHERE id = v_old.id;
|
||
v_changes := v_changes || jsonb_build_object('reserve_to_prrs_ahr',
|
||
jsonb_build_object('before', v3._diff_val(v_old.reserve_to_prrs_ahr), 'after', v3._diff_val(p_reserve_to_prrs_ahr)));
|
||
END IF;
|
||
|
||
IF p_reserve_to_prrs_kv IS NOT NULL AND p_reserve_to_prrs_kv IS DISTINCT FROM v_old.reserve_to_prrs_kv THEN
|
||
UPDATE v3.smeta SET reserve_to_prrs_kv = p_reserve_to_prrs_kv WHERE id = v_old.id;
|
||
v_changes := v_changes || jsonb_build_object('reserve_to_prrs_kv',
|
||
jsonb_build_object('before', v3._diff_val(v_old.reserve_to_prrs_kv), 'after', v3._diff_val(p_reserve_to_prrs_kv)));
|
||
END IF;
|
||
|
||
SELECT rpy.project_id, p.org_unit_id
|
||
INTO v_project_id, v_org_unit_id
|
||
FROM v3.rf_project_year rpy
|
||
JOIN v3.project p ON p.id = rpy.project_id
|
||
WHERE rpy.id = p_rf_project_year_id;
|
||
|
||
IF v_changes <> '{}'::jsonb THEN
|
||
PERFORM v3.log_event(
|
||
'SMETA_UPDATE', 'PROJECT',
|
||
jsonb_build_object(
|
||
'rf_project_year_id', p_rf_project_year_id,
|
||
'project_id', v_project_id,
|
||
'changes', v_changes
|
||
), null, null, v_org_unit_id
|
||
);
|
||
END IF;
|
||
END;
|
||
$function$
|
||
;
|