sort-fix: восстановил сортировку, новый слой expense_item в листах АХР

This commit is contained in:
tsygankoviva 2026-09-22 07:42:38 +03:00
parent 760b3a7d62
commit 99ef647c5c
6 changed files with 1839 additions and 103 deletions

View File

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

View File

@ -1,8 +1,8 @@
-- Исправления справочника статей расходов по файлу
-- "План_исправления_таблиц_статей_расходов.docx".
-- Правки, связанные с ГСМ, намеренно не включены.
-- Общие переименования.
update v3.expense_item
set name = 'Охр.инкассат перевоз'
where item_id = 'R061063000'
@ -23,7 +23,7 @@ set name = 'Другие операционные доходы'
where item_id = 'R014040037'
and num_group_id is null;
-- Перенос двух статей НВНОД в раздел 1.15.6.
update v3.expense_item
set parent_id = 81,
depth = 3
@ -32,7 +32,7 @@ where (item_id, num_group_id) in (
('R061159019', '216220174')
);
-- Раздел 1.14: коды по шаблону, включая добавление 1.14.7 для проектов.
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;
@ -42,14 +42,14 @@ insert into v3.expense_item_form_type (expense_item_id, form_type_code)
values (74, 'FORM_1'), (74, 'FORM_2')
on conflict (expense_item_id, form_type_code) do nothing;
-- Исправление ошибочного ID статьи по ПО информационной безопасности.
update v3.expense_item
set item_id = 'R061012024',
direction = coalesce(direction, 'Support')
where item_id = 'R061012025'
and name = 'Внедрение и доработка внешними контрагентами: ПО информационной безопасности';
-- Статья "Расходы по выбытию: ПО информационной безопасности" для раздела 1.17.2.
update v3.expense_item
set direction = coalesce(direction, 'Support'),
parent_id = 84,
@ -65,8 +65,8 @@ where item_id = 'R061172024'
and name = 'Расходы по выбытию: ПО информационной безопасности'
on conflict (expense_item_id, form_type_code) do nothing;
-- Девять статей Операц уже есть в справочнике. Настраиваем только доступность
-- для Сметы ССП, Сметы РФ и Проектной деятельности.
with source(expense_item_id, form_type_code) as (
values
(736, 'FORM_4'), (745, 'FORM_2'), (745, 'FORM_4'),
@ -85,7 +85,7 @@ 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
@ -95,7 +95,7 @@ where item_id in (
)
on conflict (expense_item_id, form_type_code) do nothing;
-- Проектная деятельность: добавить недостающие статьи АХР в FORM_4, кроме ГСМ.
with form4_ahr(expense_item_id) as (
values
(777), (778), (779), (347), (780), (781), (782), (783),
@ -108,8 +108,8 @@ select expense_item_id, 'FORM_4'
from form4_ahr
on conflict (expense_item_id, form_type_code) do nothing;
-- Проектная деятельность и проектные листы: исключить амортизацию и налоги
-- из FORM_4, не удаляя записи из справочника и не затрагивая FORM_2.
delete from v3.expense_item_form_type
where form_type_code = 'FORM_4'
and (
@ -119,7 +119,7 @@ where form_type_code = 'FORM_4'
or expense_item_id between 767 and 774
);
-- Проектные таблицы: исключить дубль перевозки валютных ценностей из формы.
delete from v3.expense_item_form_type
where form_type_code = 'FORM_4'
and expense_item_id = 70;

View File

@ -1,6 +1,6 @@
-- Исправления по файлу "План_исправления_таблиц_статей_расходов (2).docx".
-- В миграции намеренно не удаляются спорные строки листа Смета ПД и спорные
-- FORM_4-привязки из разделов 12.2-12.3 ТЗ.
CREATE OR REPLACE FUNCTION v3.expense_item_sort_path(p_path integer[])
RETURNS integer[]
@ -39,7 +39,7 @@ AS $function$
$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
@ -47,7 +47,7 @@ CREATE OR REPLACE FUNCTION v3.v_form2_view(p_form_id integer, p_sheet character
AS $function$
BEGIN
-- ═══ Основные листы АХР / КВ_П / Операц ════════════════════════════════
IF p_sheet IN ('AHR','CAP','OPER') THEN
RETURN QUERY
SELECT
@ -74,17 +74,17 @@ BEGIN
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 (
@ -95,7 +95,7 @@ BEGIN
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) *
@ -130,15 +130,15 @@ BEGIN
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)
'id', lt.id,
'section_no', lt.section_no,
'expense_item_code',lt.expense_item_code,
'name', lt.name,
@ -159,7 +159,7 @@ BEGIN
RETURN;
END IF;
-- ═══ АХР_аренда ════════════════════════════════════════════════════════
IF p_sheet = 'AHR_RENT' THEN
RETURN QUERY
SELECT
@ -167,7 +167,7 @@ BEGIN
0,
ROW_NUMBER() OVER (ORDER BY rd.id) AS sort_order,
jsonb_build_object(
'id', rd.id, -- id rent_detail (для write API)
'id', rd.id,
'line_id', rd.line_id,
'vsp_id', rd.vsp_id,
'address', v.address,
@ -195,7 +195,7 @@ BEGIN
RETURN;
END IF;
-- ═══ АХР_коммунал ═══════════════════════════════════════════════════════
IF p_sheet = 'AHR_UTILITY' THEN
RETURN QUERY
SELECT
@ -203,7 +203,7 @@ BEGIN
0,
ROW_NUMBER() OVER (ORDER BY ud.id) AS sort_order,
jsonb_build_object(
'id', ud.id, -- id utility_detail (для write API)
'id', ud.id,
'line_id', ud.line_id,
'vsp_id', ud.vsp_id,
'address', v.address,
@ -231,7 +231,7 @@ BEGIN
RETURN;
END IF;
-- ═══ АХР_охрана ═════════════════════════════════════════════════════════
IF p_sheet = 'AHR_SECURITY' THEN
RETURN QUERY
SELECT
@ -239,7 +239,7 @@ BEGIN
0,
ROW_NUMBER() OVER (ORDER BY sd.id) AS sort_order,
jsonb_build_object(
'id', sd.id, -- id security_detail (для write API)
'id', sd.id,
'line_id', sd.line_id,
'vsp_id', sd.vsp_id,
'address', v.address,
@ -267,14 +267,14 @@ BEGIN
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 (
@ -285,7 +285,7 @@ BEGIN
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))
@ -294,7 +294,7 @@ BEGIN
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))
@ -324,7 +324,7 @@ BEGIN
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)
'id', eff.id,
'expense_item_id', ei.id,
'item_id', ei.item_id,
'section_code', ei.section_code,
@ -347,13 +347,13 @@ BEGIN
'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))
@ -366,7 +366,7 @@ BEGIN
RETURN;
END IF;
-- ═══ Смета_2026 ═════════════════════════════════════════════════════════
IF p_sheet = 'SMETA' THEN
RETURN QUERY
SELECT
@ -388,8 +388,8 @@ BEGIN
'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)
@ -407,7 +407,7 @@ END;
$function$
;
-- Общие исправления справочника.
update v3.expense_item
@ -452,21 +452,21 @@ where parent_id = 766
'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,
@ -474,7 +474,7 @@ set direction = coalesce(direction, 'Support'),
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),
@ -496,13 +496,13 @@ set section_code = excluded.section_code,
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'),
@ -513,7 +513,7 @@ with source(expense_item_id, form_type_code) as (
(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)
@ -521,7 +521,7 @@ 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
@ -531,8 +531,8 @@ where item_id in (
)
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 (
@ -543,14 +543,14 @@ where form_type_code = 'FORM_4'
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 (
@ -560,7 +560,7 @@ where form_type_code = 'FORM_3'
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
@ -571,7 +571,7 @@ where q.rf_project_report_line_id = l.id
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)
@ -593,7 +593,7 @@ where not exists (
);
-- 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)

View File

@ -1,9 +1,9 @@
-- Синхронизация сателлитных листов FORM_2 в основной лист АХР.
--
-- Источник данных остается в сателлитах:
-- AHR_LIMIT, AHR_RENT, AHR_UTILITY, AHR_SECURITY.
-- Строки budget_line в основном листе АХР создаются/обновляются автоматически
-- и защищены от прямого редактирования через пользовательскую write-функцию.
CREATE TABLE IF NOT EXISTS v3.form2_ahr_satellite_line_source (
source_sheet character varying NOT NULL,
@ -327,10 +327,10 @@ BEGIN
RETURN;
END IF;
-- AHR_LIMIT: каждая редактируемая LEAF-строка из справочника лимитов
-- становится отдельной budget_line в АХР. Строки одной статьи расходов
-- различаются по budget_line.name, например "Директор",
-- "Заместитель директора" и т.д.
FOR v_rec IN
WITH limit_amounts AS (
SELECT
@ -374,7 +374,7 @@ BEGIN
);
END LOOP;
-- AHR_RENT -> одна строка АХР на каждую строку rent_detail.
FOR v_line_id IN
SELECT m.target_line_id
FROM v3.form2_ahr_satellite_line_source m
@ -424,7 +424,7 @@ BEGIN
AND m.source_id = rd.id;
END LOOP;
-- AHR_UTILITY -> одна строка АХР на каждую строку utility_detail.
FOR v_line_id IN
SELECT m.target_line_id
FROM v3.form2_ahr_satellite_line_source m
@ -474,7 +474,7 @@ BEGIN
AND m.source_id = ud.id;
END LOOP;
-- AHR_SECURITY -> одна строка АХР на каждую строку security_detail.
FOR v_line_id IN
SELECT m.target_line_id
FROM v3.form2_ahr_satellite_line_source m
@ -1042,9 +1042,9 @@ BEGIN
WHEN 'AHR_SECURITY' THEN 'security_detail'
END;
v_sat_eid := CASE p_sheet
WHEN 'AHR_RENT' THEN 293 -- Аренда_ОН
WHEN 'AHR_UTILITY' THEN 799 -- Коммунальные услуги
WHEN 'AHR_SECURITY' THEN 31 -- Охрана
WHEN 'AHR_RENT' THEN 293
WHEN 'AHR_UTILITY' THEN 799
WHEN 'AHR_SECURITY' THEN 31
END;
IF p_vsp_id IS NULL THEN
@ -1289,9 +1289,9 @@ BEGIN
END;
$function$;
-- В существующих данных могут быть строки budget_line, вставленные с явными id,
-- при этом serial sequence может отставать от MAX(id). Выравниваем sequence
-- перед backfill, который создает сгенерированные строки АХР.
DO $do$
DECLARE
v_sequence_name text;
@ -1308,7 +1308,7 @@ BEGIN
END;
$do$;
-- Повторно синхронизируем существующие формы FORM_2 во время миграции.
DO $do$
DECLARE
v_form_id integer;

View File

@ -1,9 +1,9 @@
-- Новые пользовательские строки создаются с пустым наименованием.
-- Заполнение header.name остается через обычную проверку этапов/ролей:
-- v3.can_edit(..., 'header.name', ...).
-- Сателлитные листы AHR_RENT/AHR_UTILITY/AHR_SECURITY оставлены в прежней логике.
-- Используем пустую строку, а не NULL: представления формы показывают
-- справочное имя статьи через COALESCE(budget_line.name, expense_item.name).
CREATE OR REPLACE FUNCTION v3.add_budget_line(p_form_id integer, p_expense_item_id integer DEFAULT NULL::integer, p_sheet character varying DEFAULT NULL::character varying, p_item_id character varying DEFAULT NULL::character varying, p_section_code character varying DEFAULT NULL::character varying, p_direction character varying DEFAULT NULL::character varying, p_name character varying DEFAULT NULL::character varying, p_internal_order character varying DEFAULT NULL::character varying, p_vsp_id integer DEFAULT NULL::integer, p_project_id integer DEFAULT NULL::integer, p_justification character varying DEFAULT NULL::character varying, p_contract_number character varying DEFAULT NULL::character varying, p_contract_end_date date DEFAULT NULL::date)
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)

File diff suppressed because one or more lines are too long