new-expenses: правки по статьям расхода #165

Merged
tsygankoviva merged 1 commits from new-expenses into test 2026-09-17 13:10:20 +03:00
2 changed files with 706 additions and 0 deletions

View File

@ -0,0 +1,15 @@
from sql_migration import run_sql_file_migration
revision = "0030"
down_revision = "0029"
branch_labels = None
depends_on = None
def upgrade() -> None:
run_sql_file_migration("0030_expense_items_fixes.sql")
def downgrade() -> None:
pass

View File

@ -0,0 +1,691 @@
-- Исправления по файлу "План_исправления_таблиц_статей_расходов (2).docx".
-- В миграции намеренно не удаляются спорные строки листа Смета ПД и спорные
-- FORM_4-привязки из разделов 12.2-12.3 ТЗ.
CREATE OR REPLACE FUNCTION v3.expense_item_sort_path(p_path integer[])
RETURNS integer[]
LANGUAGE sql
STABLE
AS $function$
WITH sibling_order AS (
SELECT
ei.id,
row_number() OVER (
PARTITION BY ei.parent_id
ORDER BY
CASE
WHEN ei.item_id ~ '^\d+(\.\d+)*\.?$'
THEN regexp_split_to_array(trim(trailing '.' from ei.item_id), '\.')::integer[]
END NULLS LAST,
CASE
WHEN ei.item_id !~ '^\d+(\.\d+)*\.?$'
THEN ei.item_id
END NULLS LAST,
ei.num_group_id NULLS FIRST,
ei.id
)::integer AS sort_key
FROM v3.expense_item ei
),
path_item AS (
SELECT item_id, ordinality
FROM unnest(p_path) WITH ORDINALITY AS u(item_id, ordinality)
)
SELECT coalesce(
array_agg(coalesce(so.sort_key, pi.item_id) ORDER BY pi.ordinality),
ARRAY[]::integer[]
)
FROM path_item pi
LEFT JOIN sibling_order so ON so.id = pi.item_id;
$function$
;
-- FORM_2 uses natural expense-item ordering for tree rows.
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 v3.expense_item_sort_path(j._sort_path), 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 v3.expense_item_sort_path(j._sort_path), 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$
;
-- Общие исправления справочника.
update v3.expense_item
set name = 'Взносы в орг/ассоц'
where item_id = 'R061141000'
and num_group_id = '214920158';
update v3.expense_item
set name = 'Бланк ден/чек книжМЦ'
where item_id = 'R064040032'
and num_group_id = '402640197';
update v3.expense_item
set name = 'Другие операционные доходы'
where item_id = 'R014040037'
and num_group_id is null;
with renamed(id, name) as (
values
(32, 'Физическая охрана объектов недвижимости'),
(33, 'Пультовая охрана объектов недвижимости'),
(34, 'Охрана инкассаторских перевозок'),
(35, 'Охрана имущества при его транспортировке'),
(36, 'Пультовая охрана устройств самообслуживания'),
(37, 'Техническое обслуживание и эксплуатация: оборудование систем охраны объектов недвижимости'),
(38, 'Ремонт: оборудование систем охраны объектов недвижимости'),
(57, 'Оценка объектов недвижимости'),
(58, 'Оценка прочих активов'),
(59, 'Расходы на исследования и разработки')
)
update v3.expense_item ei
set name = renamed.name
from renamed
where ei.id = renamed.id or ei.parent_id = renamed.id;
update v3.expense_item
set depth = 3
where parent_id = 766
and item_id in (
'R065010000', 'R065020001', 'R065030012', 'R065040000',
'R065050005', 'R065070000', 'R065090000', 'R065100000'
);
-- Раздел 1.14 по шаблону ССП. Лишний раздел 1.14.4 отключается ниже
-- от проектных форм/отчётов, но не удаляется из справочника.
update v3.expense_item set item_id = '1.14.4' where id = 71;
update v3.expense_item set item_id = '1.14.5' where id = 72;
update v3.expense_item set item_id = '1.14.6' where id = 73;
update v3.expense_item set item_id = '1.14.7' where id = 74;
-- Исправление ошибочного ID статьи внешних контрагентов.
update v3.expense_item
set item_id = 'R061012024',
direction = coalesce(direction, 'Support')
where item_id = 'R061012025'
and name = 'Внедрение и доработка внешними контрагентами: ПО информационной безопасности';
-- Статья выбытия ПО информационной безопасности.
update v3.expense_item
set direction = coalesce(direction, 'Support'),
parent_id = 84,
depth = 3
where item_id = 'R061172024'
and name = 'Расходы по выбытию: ПО информационной безопасности';
-- Девять статей Операц. Если записи уже есть, приводим названия к ТЗ.
insert into v3.expense_item (id, section_code, item_id, num_group_id, name, sheet, direction, parent_id, depth)
values
(800, 'OPER', 'R064040033', '402740198', 'Бланки сбер книжекМЦ', 'OPER', 'Support', 715, 3),
(801, 'OPER', 'R064040044', '222420186', 'Изгот банк карт', 'OPER', 'Support', 717, 3),
(802, 'OPER', 'R064040036', '222620188', 'Госпошл_рег ипотеки', 'OPER', 'Support', 719, 3),
(803, 'OPER', 'R064040038', '223320190', 'Услуги п/размещ УС', 'OPER', 'Support', 720, 3),
(804, 'OPER', 'R064040039', null, 'Нотариальные и пр. аналогичные расходы по бизнесу', 'OPER', 'Support', 721, 3),
(805, 'OPER', 'R064040040', null, 'Штрафы за нарушения условий хозяйственных договоров', 'OPER', 'Support', 722, 3),
(806, 'OPER', 'R064040041', null, 'Расходы по списанию недостач материальных ценностей', 'OPER', 'Support', 723, 3),
(807, 'OPER', 'R064040042', null, 'Расходы по списанию дебиторской задолженности', 'OPER', 'Support', 724, 3),
(808, 'OPER', 'R064040043', null, 'Расходы от передачи активов в доверительное управление', 'OPER', 'Support', 725, 3)
on conflict (id) do update
set section_code = excluded.section_code,
item_id = excluded.item_id,
num_group_id = excluded.num_group_id,
name = excluded.name,
sheet = excluded.sheet,
direction = excluded.direction,
parent_id = excluded.parent_id,
depth = excluded.depth;
-- Доступность форм по ТЗ.
with source(expense_item_id, form_type_code) as (
values
-- FORM_1/FORM_2: раздел 1.14.7 и статья выбытия ПО
(74, 'FORM_1'), (74, 'FORM_2'),
(495, 'FORM_1'), (495, 'FORM_2'),
-- FORM_1/FORM_2/FORM_4: девять Операц и две существующие Операц
(736, 'FORM_4'), (745, 'FORM_2'), (745, 'FORM_4'),
(800, 'FORM_1'), (800, 'FORM_2'), (800, 'FORM_4'),
(801, 'FORM_1'), (801, 'FORM_2'), (801, 'FORM_4'),
(802, 'FORM_1'), (802, 'FORM_2'), (802, 'FORM_4'),
(803, 'FORM_1'), (803, 'FORM_2'), (803, 'FORM_4'),
(804, 'FORM_1'), (804, 'FORM_2'), (804, 'FORM_4'),
(805, 'FORM_1'), (805, 'FORM_2'), (805, 'FORM_4'),
(806, 'FORM_1'), (806, 'FORM_2'), (806, 'FORM_4'),
(807, 'FORM_1'), (807, 'FORM_2'), (807, 'FORM_4'),
(808, 'FORM_1'), (808, 'FORM_2'), (808, 'FORM_4'),
-- FORM_4: восстановить ГСМ в Структуре ПД без создания дубля
(776, 'FORM_4')
)
insert into v3.expense_item_form_type (expense_item_id, form_type_code)
select expense_item_id, form_type_code
from source
on conflict (expense_item_id, form_type_code) do nothing;
-- Налоговые статьи раздела 5.00.0 для Сметы РФ.
insert into v3.expense_item_form_type (expense_item_id, form_type_code)
select id, 'FORM_2'
from v3.expense_item
where item_id in (
'R065010000', 'R065020001', 'R065030012', 'R065040000',
'R065050005', 'R065070000', 'R065090000', 'R065100000'
)
on conflict (expense_item_id, form_type_code) do nothing;
-- Проектная деятельность: убрать амортизацию и налоги из FORM_4,
-- не удаляя записи из справочника.
delete from v3.expense_item_form_type
where form_type_code = 'FORM_4'
and (
expense_item_id = 70
or expense_item_id in (746, 747, 760, 766)
or expense_item_id between 748 and 759
or expense_item_id between 761 and 765
or expense_item_id between 767 and 774
);
-- Пункт 8. Исправление отчётов Проекты: Текущие расходы и Лимиты.
-- 8.2 добавить заголовок 1.14.7 "Другие административно-хозяйственные расходы";
insert into v3.expense_item_form_type (expense_item_id, form_type_code)
values (74, 'FORM_3'), (495, 'FORM_3')
on conflict (expense_item_id, form_type_code) do nothing;
-- 8.1 убрать лишний раздел 1.14.4 "Перевозка валютных ценностей_АХР";
delete from v3.expense_item_form_type
where form_type_code = 'FORM_3'
and (
expense_item_id = 70
or expense_item_id in (746, 747, 760)
or expense_item_id between 748 and 759
or expense_item_id between 761 and 765
);
-- 8.1 убрать лишний раздел 1.14.4 "Перевозка валютных ценностей_АХР";
delete from v3.rf_project_report_quarter q
using v3.rf_project_report_line l
where q.rf_project_report_line_id = l.id
and (
l.expense_item_id = 70
or l.expense_item_id in (746, 747, 760)
or l.expense_item_id between 748 and 759
or l.expense_item_id between 761 and 765
);
-- 8.1 убрать лишний раздел 1.14.4 "Перевозка валютных ценностей_АХР";
delete from v3.rf_project_report_line
where expense_item_id = 70
or expense_item_id in (746, 747, 760)
or expense_item_id between 748 and 759
or expense_item_id between 761 and 765;
with target_items(expense_item_id) as (
values (74), (495)
)
insert into v3.rf_project_report_line (rf_project_report_id, expense_item_id)
select r.id, ti.expense_item_id
from v3.rf_project_report r
cross join target_items ti
where not exists (
select 1
from v3.rf_project_report_line l
where l.rf_project_report_id = r.id
and l.expense_item_id = ti.expense_item_id
);
-- 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
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;
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) sq1 WHERE sq1.row_type != 'INPUT'
UNION ALL
SELECT * FROM v3.v_form4_sheet_lines_jsonb(p_form_id, 'CAP', p_sections) sq2 WHERE sq2.row_type != 'INPUT'
UNION ALL
SELECT * FROM v3.v_form4_sheet_lines_jsonb(p_form_id, 'OPER', p_sections) sq3 WHERE sq3.row_type != 'INPUT'
)
SELECT
j.row_type,
j.depth,
ROW_NUMBER() OVER (
ORDER BY v3.expense_item_sort_path(j._sort_path),
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 v3.expense_item_sort_path(j._sort_path),
j._sort_path;
RETURN;
END IF;
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$
;
select setval('v3.expense_item_id_seq', greatest(coalesce((select max(id) from v3.expense_item), 1), 808));
select setval('v3.rf_project_report_line_id_seq', greatest(coalesce((select max(id) from v3.rf_project_report_line), 1), 1));
refresh materialized view v3.mv_expense_item_tree;