delete-sattelite-rows #175

Merged
tsygankoviva merged 1 commits from delete-sattelite-rows into test 2026-09-24 13:49:36 +03:00
3 changed files with 187 additions and 0 deletions

View File

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

View File

@ -0,0 +1,128 @@
CREATE OR REPLACE FUNCTION v3.del_budget_line(p_id integer, p_direction character varying DEFAULT NULL::character varying, p_sheet character varying DEFAULT NULL::character varying)
RETURNS TABLE(row_type character varying, depth integer, sort_order bigint, data jsonb)
LANGUAGE plpgsql
AS $function$
DECLARE
v_form_id INT;
v_org_id INT;
v_sheet VARCHAR;
v_sat_table TEXT;
v_eid INT;
v_line_id INT;
v_ei_path INT[];
BEGIN
IF p_sheet IN ('AHR_RENT','AHR_UTILITY','AHR_SECURITY') THEN
v_sat_table := CASE p_sheet
WHEN 'AHR_RENT' THEN 'rent_detail'
WHEN 'AHR_UTILITY' THEN 'utility_detail'
WHEN 'AHR_SECURITY' THEN 'security_detail'
END;
EXECUTE format(
'SELECT bl.budget_form_id FROM v3.%I s
JOIN v3.budget_line bl ON bl.id = s.line_id
WHERE s.id = $1', v_sat_table
) USING p_id INTO v_form_id;
IF v_form_id IS NULL THEN
RAISE EXCEPTION '%.id=% не существует', v_sat_table, p_id;
END IF;
SELECT m.target_line_id
INTO v_line_id
FROM v3.form2_ahr_satellite_line_source m
WHERE m.source_sheet = p_sheet
AND m.source_id = p_id;
EXECUTE format('DELETE FROM v3.%I WHERE id = $1', v_sat_table) USING p_id;
IF v_line_id IS NOT NULL THEN
SELECT bl.expense_item_id
INTO v_eid
FROM v3.budget_line bl
WHERE bl.id = v_line_id;
DELETE FROM v3.plan WHERE line_id = v_line_id;
DELETE FROM v3.budget_line_quarter WHERE line_id = v_line_id;
DELETE FROM v3.budget_line WHERE id = v_line_id;
SELECT org_unit_id INTO v_org_id FROM v3.budget_form WHERE id = v_form_id;
PERFORM v3.log_event(
'ROW_DELETE', 'ROW',
jsonb_build_object(
'sheet', 'AHR',
'line_id', v_line_id,
'entity_id', v_line_id,
'expense_item_id', v_eid,
'direction', 'Support',
'source_sheet', p_sheet,
'source_id', p_id
),
v_form_id, NULL, v_org_id
);
END IF;
RETURN;
END IF;
SELECT bl.budget_form_id, ei.sheet, bl.expense_item_id
INTO v_form_id, v_sheet, v_eid
FROM v3.budget_line bl
JOIN v3.expense_item ei ON ei.id = bl.expense_item_id
WHERE bl.id = p_id;
IF v_form_id IS NULL THEN
RAISE EXCEPTION 'budget_line id=% не существует', p_id;
END IF;
IF v_sheet = 'AHR' AND EXISTS (
SELECT 1
FROM v3.form2_ahr_satellite_line_source m
WHERE m.target_line_id = p_id
) THEN
RAISE EXCEPTION 'Строку АХР, созданную из листа-сателлита, нельзя удалить напрямую'
USING ERRCODE = '23503',
HINT = 'Удалите строку на исходном листе АХР Аренда / АХР Коммунальные услуги / АХР Безопасность';
END IF;
v_ei_path := v3._expense_item_path_set(ARRAY[v_eid]);
DELETE FROM v3.plan WHERE line_id = p_id;
DELETE FROM v3.sequestration WHERE line_id = p_id;
DELETE FROM v3.reserve WHERE line_id = p_id;
DELETE FROM v3.allocation WHERE line_id = p_id;
DELETE FROM v3.contract_summary WHERE line_id = p_id;
DELETE FROM v3.collegial_approval WHERE line_id = p_id;
DELETE FROM v3.ckk WHERE line_id = p_id;
DELETE FROM v3.contract_detail WHERE line_id = p_id;
DELETE FROM v3.booking WHERE line_id = p_id;
DELETE FROM v3.budget_line_quarter WHERE line_id = p_id;
DELETE FROM v3.rent_detail WHERE line_id = p_id;
DELETE FROM v3.utility_detail WHERE line_id = p_id;
DELETE FROM v3.security_detail WHERE line_id = p_id;
DELETE FROM v3.budget_line WHERE id = p_id;
SELECT org_unit_id INTO v_org_id FROM v3.budget_form WHERE id = v_form_id;
PERFORM v3.log_event(
'ROW_DELETE', 'ROW',
jsonb_build_object(
'sheet', v_sheet,
'line_id', p_id,
'entity_id', p_id,
'expense_item_id', v_eid,
'direction', p_direction
),
v_form_id, NULL, v_org_id
);
RETURN QUERY
SELECT v.row_type, v.depth, v.sort_order, v.data
FROM v3.v_form_view(v_form_id, v_sheet, NULL, p_direction) v
WHERE v.row_type IN ('ROOT','GROUP','ITEM','SUB_ITEM')
AND (v.data->'header'->>'expense_item_id')::INT = ANY(v_ei_path)
ORDER BY v.sort_order;
END;
$function$;

View File

@ -9,6 +9,7 @@ import time
from typing import Any, Optional from typing import Any, Optional
# #
from sqlalchemy import text
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from fastapi import APIRouter, WebSocket, WebSocketDisconnect, status from fastapi import APIRouter, WebSocket, WebSocketDisconnect, status
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
@ -800,6 +801,49 @@ async def notify_ahr_about_satellite_change(
user_id: int, user_id: int,
): ):
try: try:
if source_event == "row_deleted":
async with get_db_session(user_id=user_id) as db:
deleted_line_id = (
await db.execute(
text(
"""
SELECT (event_data->>'line_id')::int
FROM v3.audit_log
WHERE form_id = :form_id
AND event = 'ROW_DELETE'
AND event_type = 'ROW'
AND event_data->>'sheet' = 'AHR'
AND event_data->>'source_sheet' = :source_sheet
AND (event_data->>'source_id')::int = :source_row_id
ORDER BY id DESC
LIMIT 1
"""
),
{
"form_id": form_id,
"source_sheet": source_sheet,
"source_row_id": source_row_id,
},
)
).scalar_one_or_none()
if deleted_line_id is None:
return
await manager.broadcast_to_form_sheet(
data={
"event": "row_deleted",
"data": {
"row_id": deleted_line_id,
"source_sheet": source_sheet,
},
"result": [],
},
form_id=form_id,
sheet="AHR",
)
return
async with get_db_session(user_id=user_id) as db: async with get_db_session(user_id=user_id) as db:
ahr_delta = await SheetService(db).get_form2_ahr_satellite_delta( ahr_delta = await SheetService(db).get_form2_ahr_satellite_delta(
form_id=form_id, form_id=form_id,