Merge pull request 'new-expenses: правки по статьям расхода' (#165) from new-expenses into test
Reviewed-on: #165
This commit is contained in:
commit
357dc9efaa
15
api/alembic/versions/0030_expense_items_fixes.py
Normal file
15
api/alembic/versions/0030_expense_items_fixes.py
Normal 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
|
||||
691
api/alembic/versions/sql/0030_expense_items_fixes.sql
Normal file
691
api/alembic/versions/sql/0030_expense_items_fixes.sql
Normal 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;
|
||||
Loading…
x
Reference in New Issue
Block a user