786 lines
40 KiB
Python
786 lines
40 KiB
Python
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
|
|
revision = "0009"
|
|
down_revision = "0008"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.execute(
|
|
"""
|
|
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);
|
|
BEGIN
|
|
SELECT report_type, org_unit_id INTO v_report_type, v_org_unit
|
|
FROM v3.rf_project_report AS rpr
|
|
INNER JOIN v3.project AS p ON p.id = rpr.project_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::text || v_utc_offset)::timestamptz,
|
|
(pt.closes_at::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$
|
|
;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
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, rpr.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
|
|
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$
|
|
;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
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, rpr.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
|
|
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$;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
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, rpr.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
|
|
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$
|
|
;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
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, p_system_code character varying DEFAULT NULL::character varying)
|
|
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,
|
|
system_code
|
|
) 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,
|
|
p_system_code
|
|
) RETURNING id INTO v_pid;
|
|
|
|
-- Первый год: 2 отчёта (LIMIT + CURRENT_EXPENSES) + snapshot фаз + сетка статей.
|
|
-- p_emit_audit=false — событие года покрыто PROJECT_CREATE ниже. Дальнейшие
|
|
-- годы добавляются отдельно через v3.add_project_year.
|
|
SELECT y.limit_report_id, y.current_expenses_report_id
|
|
INTO v_lim, v_cur
|
|
FROM v3.add_project_year(v_pid, p_year, false) y;
|
|
|
|
-- Аудит: 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,
|
|
'system_code', p_system_code,
|
|
'staff_count', p_staff_count,
|
|
'total_area', p_total_area,
|
|
'limit_report_id', v_lim,
|
|
'current_expenses_report_id', v_cur
|
|
))
|
|
);
|
|
|
|
RETURN QUERY SELECT v_pid, v_lim, v_cur;
|
|
END;
|
|
$function$
|
|
;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE OR REPLACE FUNCTION v3.upd_phases_timezones(p_org_unit_id integer, p_old_tz character varying, p_new_tz character varying)
|
|
RETURNS integer
|
|
LANGUAGE plpgsql
|
|
AS $function$
|
|
DECLARE
|
|
v_result integer;
|
|
v_result3 integer;
|
|
BEGIN
|
|
IF p_old_tz = p_new_tz THEN
|
|
RETURN 0;
|
|
END IF;
|
|
WITH updated AS (
|
|
UPDATE v3.form_phase
|
|
SET opens_at = ((opens_at AT TIME zone (p_old_tz || '::00')::interval)::text || p_new_tz)::timestamptz,
|
|
closes_at = ((closes_at AT TIME zone (p_old_tz || '::00')::interval)::text || p_new_tz)::timestamptz
|
|
WHERE budget_form_id IN (SELECT id FROM v3.budget_form WHERE org_unit_id = p_org_unit_id)
|
|
AND (
|
|
opens_at >= CURRENT_TIMESTAMP
|
|
OR closes_at >= CURRENT_TIMESTAMP
|
|
)
|
|
RETURNING *
|
|
)
|
|
SELECT COUNT(*) INTO v_result FROM updated;
|
|
|
|
WITH updated AS (
|
|
UPDATE v3.form3_phase
|
|
SET opens_at = ((opens_at AT TIME zone (p_old_tz || '::00')::interval)::text || p_new_tz)::timestamptz,
|
|
closes_at = ((closes_at AT TIME zone (p_old_tz || '::00')::interval)::text || p_new_tz)::timestamptz
|
|
WHERE rf_project_report_id IN (
|
|
SELECT id FROM V3.rf_project_report rpr WHERE project_id IN (
|
|
SELECT id FROM v3.project WHERE org_unit_id = p_org_unit_id
|
|
)
|
|
)
|
|
AND (
|
|
opens_at >= CURRENT_TIMESTAMP
|
|
OR closes_at >= CURRENT_TIMESTAMP
|
|
)
|
|
RETURNING *
|
|
)
|
|
SELECT COUNT(*) INTO v_result3 FROM updated;
|
|
RETURN v_result + v_result3;
|
|
|
|
END;
|
|
$function$
|
|
;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
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 without time zone, p_closes_at timestamp without 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;
|
|
v_utc_offset VARCHAR(3);
|
|
BEGIN
|
|
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;
|
|
|
|
SELECT utc_offset INTO v_utc_offset
|
|
FROM v3.org_unit
|
|
WHERE id = v_org_unit_id;
|
|
|
|
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,
|
|
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(
|
|
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',
|
|
'entity_id', p_budget_form_id,
|
|
'core_entity_type', 'form_phase',
|
|
'core_entity_id', v_phase_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$
|
|
;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
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',
|
|
'entity_id', p_budget_form_id,
|
|
'core_entity_type', 'form_phase',
|
|
'core_entity_id', v_phase_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$
|
|
;
|
|
"""
|
|
)
|
|
op.execute(
|
|
"""
|
|
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 without time zone DEFAULT NULL::timestamp without time zone, p_closes_at timestamp without time zone DEFAULT NULL::timestamp without 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;
|
|
v_utc_offset VARCHAR(3);
|
|
v_opens_at timestamptz;
|
|
v_closes_at timestamptz;
|
|
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;
|
|
|
|
SELECT bf.org_unit_id, ou.title, bf.form_type_code, bf.year, ou.utc_offset
|
|
INTO v_org_unit_id, v_org_unit_title, v_form_type_code, v_year, v_utc_offset
|
|
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
|
|
));
|
|
|
|
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.form_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 budget_form_id = p_budget_form_id
|
|
AND sheet = p_sheet
|
|
AND phase_code = p_phase_code
|
|
RETURNING * INTO v_new;
|
|
|
|
|
|
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(
|
|
'entity_type', 'form',
|
|
'entity_id', p_budget_form_id,
|
|
'core_entity_type', 'form_phase',
|
|
'core_entity_id', v_phase_id,
|
|
|
|
'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',
|
|
'entity_id', p_budget_form_id,
|
|
'core_entity_type', 'form_phase',
|
|
'core_entity_id', v_phase_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$
|
|
;
|
|
"""
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
pass
|