From 9c725e9678242022b3b028d36a22d9a8a6fe6b6f Mon Sep 17 00:00:00 2001 From: tsygankoviva Date: Mon, 29 Jun 2026 11:37:23 +0300 Subject: [PATCH 01/13] =?UTF-8?q?fix-ssp-title:=20=D0=BD=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D1=8F=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D1=82=D1=8C=20?= =?UTF-8?q?=D0=BE=D1=80=D0=B3=20=D1=8E=D0=BD=D0=B8=D1=82=20=D1=81=20=D0=BF?= =?UTF-8?q?=D1=83=D1=81=D1=82=D1=8B=D0=BC=20=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/api/v1/org_unit.py | 4 ++-- api/src/domain/schemas.py | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/api/src/api/v1/org_unit.py b/api/src/api/v1/org_unit.py index 725bcd9..661a5bc 100644 --- a/api/src/api/v1/org_unit.py +++ b/api/src/api/v1/org_unit.py @@ -6,7 +6,7 @@ from src.core.errors import AccessDeniedException from src.api.v1.deps import get_current_active_user, require_admin from src.db.models.app_user import AppUser from src.db.session import get_db -from src.domain.schemas import BaseListResponse, BaseSingleResponse, OrgUnitBaseSchema, OrgUnitListSchema, OrgUnitSchema, OrgUnitUpdateSchema, ResponseBase +from src.domain.schemas import BaseListResponse, BaseSingleResponse, OrgUnitCreateSchema, OrgUnitListSchema, OrgUnitSchema, OrgUnitUpdateSchema, ResponseBase from src.services.org_unit_service import OrgUnitService @@ -66,7 +66,7 @@ async def org_unit_id( @router.post("/") async def create_ssp( - org_data: OrgUnitBaseSchema, + org_data: OrgUnitCreateSchema, db: AsyncSession = Depends(get_db), current_user: AppUser = Depends(require_admin), ) -> BaseSingleResponse[OrgUnitSchema]: diff --git a/api/src/domain/schemas.py b/api/src/domain/schemas.py index 261cba8..060597c 100644 --- a/api/src/domain/schemas.py +++ b/api/src/domain/schemas.py @@ -233,6 +233,10 @@ class OrgUnitBaseSchema(BaseModel): model_config = ConfigDict(from_attributes=True) +class OrgUnitCreateSchema(OrgUnitBaseSchema): + title: str = Field(..., min_length=1) + + class OrgUnitUpdateSchema(OrgUnitBaseSchema): title: Optional[str] = None is_ssp: Optional[bool] = None From 7da934399e934b7764753e5715b9daf8a21a9e35 Mon Sep 17 00:00:00 2001 From: Raykov-MS Date: Mon, 29 Jun 2026 17:17:28 +0300 Subject: [PATCH 02/13] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B0?= =?UTF-8?q?=20=D0=B2=20=D1=81=D1=85=D0=B5=D0=BC=D0=B0=D1=85=20=D0=B8=20?= =?UTF-8?q?=D0=BB=D0=BE=D0=B3=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/alembic/versions/sql/0002_functions.sql | 25 +++++------------ api/src/api/v1/vsp.py | 12 ++++----- api/src/domain/schemas.py | 10 ++++--- api/src/repository/vsp_repository.py | 30 ++++++++++----------- api/src/services/vsp_service.py | 22 +++++++-------- api/tests/integration/test_vsp.py | 4 +-- 6 files changed, 46 insertions(+), 57 deletions(-) diff --git a/api/alembic/versions/sql/0002_functions.sql b/api/alembic/versions/sql/0002_functions.sql index 28f718a..cdf70dc 100644 --- a/api/alembic/versions/sql/0002_functions.sql +++ b/api/alembic/versions/sql/0002_functions.sql @@ -1123,9 +1123,9 @@ END; $function$ ; --- DROP FUNCTION v3.add_vsp(int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, int4, varchar, date); +-- DROP FUNCTION v3.add_vsp(int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, varchar, date); -CREATE OR REPLACE FUNCTION v3.add_vsp(p_branch_id integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT true, p_is_deleted boolean DEFAULT false, p_system_code character varying DEFAULT NULL::character varying, p_created_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_location_form character varying DEFAULT NULL::character varying, p_numbers integer DEFAULT NULL::integer, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date) +CREATE OR REPLACE FUNCTION v3.add_vsp(p_branch_id integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT true, p_is_deleted boolean DEFAULT false, p_system_code character varying DEFAULT NULL::character varying, p_created_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_location_form character varying DEFAULT NULL::character varying, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date) RETURNS integer LANGUAGE plpgsql AS $function$ @@ -1153,14 +1153,12 @@ BEGIN (branch_id, reg_number, address, format, opened_at, placement_type, staff_count, total_area, closed_at, is_active, is_deleted, system_code, created_by, - vsp_type, notes, location_form, numbers, - rent_contract_num, rent_end_date) + vsp_type, notes, location_form, rent_contract_num, rent_end_date) VALUES (p_branch_id, p_reg_number, p_address, p_format, p_opened_at, p_placement_type, p_staff_count, p_total_area, p_closed_at, p_is_active, p_is_deleted, p_system_code, p_created_by, - p_vsp_type, p_notes, p_location_form, p_numbers, - p_rent_contract_num, p_rent_end_date) + p_vsp_type, p_notes, p_location_form, p_rent_contract_num, p_rent_end_date) RETURNING id INTO v_id; PERFORM v3.log_event( @@ -1185,7 +1183,6 @@ BEGIN 'vsp_type', p_vsp_type, 'notes', p_notes, 'location_form', p_location_form, - 'numbers', p_numbers, 'rent_contract_num', p_rent_contract_num, 'rent_end_date', v3._diff_val(p_rent_end_date), 'user_email', v_actor_email, @@ -2738,9 +2735,9 @@ END; $function$ ; --- DROP FUNCTION v3.upd_vsp(int4, int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, int4, varchar, date); +-- DROP FUNCTION v3.upd_vsp(int4, int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, varchar, date); -CREATE OR REPLACE FUNCTION v3.upd_vsp(p_vsp_id integer, p_branch_id integer DEFAULT NULL::integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT NULL::boolean, p_is_deleted boolean DEFAULT NULL::boolean, p_system_code character varying DEFAULT NULL::character varying, p_updated_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_location_form character varying DEFAULT NULL::character varying, p_numbers integer DEFAULT NULL::integer, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date) +CREATE OR REPLACE FUNCTION v3.upd_vsp(p_vsp_id integer, p_branch_id integer DEFAULT NULL::integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT NULL::boolean, p_is_deleted boolean DEFAULT NULL::boolean, p_system_code character varying DEFAULT NULL::character varying, p_updated_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_location_form character varying DEFAULT NULL::character varying, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date) RETURNS void LANGUAGE plpgsql AS $function$ @@ -2758,8 +2755,7 @@ BEGIN SELECT v.branch_id, v.reg_number, v.address, v.format, v.opened_at, v.placement_type, v.staff_count, v.total_area, v.closed_at, v.is_active, v.is_deleted, v.system_code, - v.vsp_type, v.notes, v.location_form, v.numbers, - v.rent_contract_num, v.rent_end_date + v.vsp_type, v.notes, v.location_form, v.rent_contract_num, v.rent_end_date INTO v_old FROM v3.vsp v WHERE v.id = p_vsp_id; @@ -2899,13 +2895,6 @@ BEGIN 'after', v3._diff_val(p_location_form))); END IF; - IF p_numbers IS NOT NULL AND p_numbers IS DISTINCT FROM v_old.numbers THEN - UPDATE v3.vsp SET numbers = p_numbers WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('numbers', - jsonb_build_object('before', v3._diff_val(v_old.numbers), - 'after', v3._diff_val(p_numbers))); - END IF; - IF p_rent_contract_num IS NOT NULL AND p_rent_contract_num IS DISTINCT FROM v_old.rent_contract_num THEN UPDATE v3.vsp SET rent_contract_num = p_rent_contract_num WHERE id = p_vsp_id; v_changes := v_changes || jsonb_build_object('rent_contract_num', diff --git a/api/src/api/v1/vsp.py b/api/src/api/v1/vsp.py index b4947f4..1cd34c6 100644 --- a/api/src/api/v1/vsp.py +++ b/api/src/api/v1/vsp.py @@ -28,8 +28,8 @@ async def get_vsp( open_date_start: date | None = None, open_date_end: date | None = None, location_form: str | None = None, - numbers_min: int | None = None, - numbers_max: int | None = None, + staff_count_min: int | None = None, + staff_count_max: int | None = None, db: AsyncSession = Depends(get_db), current_user: AppUser = Depends(require_executor), ): @@ -43,8 +43,8 @@ async def get_vsp( open_date_start=open_date_start, open_date_end=open_date_end, location_form=location_form, - numbers_min=numbers_min, - numbers_max=numbers_max, + staff_count_min=staff_count_min, + staff_count_max=staff_count_max, load_org_unit=True, with_count=True, ) @@ -90,8 +90,8 @@ async def export_info( open_date_start=body.open_date_start, open_date_end=body.open_date_end, location_form=body.location_form, - numbers_min=body.numbers_min, - numbers_max=body.numbers_max, + staff_count_min=body.staff_count_min, + staff_count_max=body.staff_count_max, ) return StreamingResponse( stream, diff --git a/api/src/domain/schemas.py b/api/src/domain/schemas.py index 261cba8..ce441d3 100644 --- a/api/src/domain/schemas.py +++ b/api/src/domain/schemas.py @@ -490,8 +490,9 @@ class VSPBase(BaseModel): serialization_alias='approved_format', ) notes: Optional[str] = Field(None, description="Примечания") + placement_type: Optional[str] = Field(None, description="Тип размещения") + staff_count: Optional[int] = Field(None, description="Штатная численность") location_form: Optional[str] = Field(None, description="Форма расположения") - numbers: Optional[int] = Field(None, description="Штатная численность") area: Optional[float] = Field( None, description="Арендная площадь", @@ -539,8 +540,9 @@ class VSPEditBase(BaseModel): serialization_alias='format', ) notes: Optional[str] = Field(None, description="Примечания") + placement_type: Optional[str] = Field(None, description="Тип размещения") + staff_count: Optional[int] = Field(None, description="Штатная численность") location_form: Optional[str] = Field(None, description="Форма расположения") - numbers: Optional[int] = Field(None, description="Штатная численность") total_area: Optional[float] = Field( None, description="Арендная площадь", @@ -612,8 +614,8 @@ class VSPExportRequest(BaseModel): open_date_start: Optional[date] = None open_date_end: Optional[date] = None location_form: Optional[str] = None - numbers_min: Optional[int] = None - numbers_max: Optional[int] = None + staff_count_min: Optional[int] = None + staff_count_max: Optional[int] = None class ExpenseItemBaseSchema(BaseModel): diff --git a/api/src/repository/vsp_repository.py b/api/src/repository/vsp_repository.py index 5740b9c..51ef4a0 100644 --- a/api/src/repository/vsp_repository.py +++ b/api/src/repository/vsp_repository.py @@ -37,8 +37,8 @@ class VSPRepository: open_date_start: date | None = None, open_date_end: date | None = None, location_form: str | None = None, - numbers_min: int | None = None, - numbers_max: int | None = None, + staff_count_min: int | None = None, + staff_count_max: int | None = None, is_active: bool | None = True, close_date_is_null: bool | None = None, load_org_unit: bool = False, @@ -56,8 +56,8 @@ class VSPRepository: ("location_form", location_form), ("open_date_start", open_date_start), ("open_date_end", open_date_end), - ("numbers_min", numbers_min), - ("numbers_max", numbers_max), + ("staff_count_min", staff_count_min), + ("staff_count_max", staff_count_max), ("is_active", is_active), ] for filter_name, filter_value in filters: @@ -73,10 +73,10 @@ class VSPRepository: queries.append(Vsp.opened_at >= value) case ("open_date_end", value) if value is not None: queries.append(Vsp.opened_at <= value) - case ("numbers_min", value) if value is not None: - queries.append(Vsp.numbers >= value) - case ("numbers_max", value) if value is not None: - queries.append(Vsp.numbers <= value) + case ("staff_count_min", value) if value is not None: + queries.append(Vsp.staff_count >= value) + case ("staff_count_max", value) if value is not None: + queries.append(Vsp.staff_count <= value) case ("is_active", value) if value is not None: queries.append(Vsp.is_active == value) case _: @@ -116,6 +116,7 @@ class VSPRepository: async def create(self, payload: VSPCreate, created_by: int, load_org_unit: bool = False) -> Vsp: data = payload.model_dump() + vsp_id = ( await self.db.execute( text(""" @@ -124,8 +125,7 @@ class VSPRepository: :p_opened_at, :p_placement_type, :p_staff_count, :p_total_area, :p_closed_at, :p_is_active, :p_is_deleted, :p_system_code, :p_created_by, :p_vsp_type, :p_notes, - :p_location_form, :p_numbers, :p_rent_contract_num, - :p_rent_end_date + :p_location_form, :p_rent_contract_num, :p_rent_end_date ) """), { @@ -145,7 +145,6 @@ class VSPRepository: "p_vsp_type": data.get("vsp_type"), "p_notes": data.get("notes"), "p_location_form": data.get("location_form"), - "p_numbers": data.get("numbers"), "p_rent_contract_num": data.get("rent_contract_num"), "p_rent_end_date": data.get("rent_end_date"), } @@ -176,9 +175,10 @@ class VSPRepository: "opened_at": "p_opened_at", "closed_at": "p_closed_at", "format": "p_format", + "placement_type": "p_placement_type", + "staff_count": "p_staff_count", "notes": "p_notes", "location_form": "p_location_form", - "numbers": "p_numbers", "total_area": "p_total_area", "rent_contract_num": "p_rent_contract_num", "rent_end_date": "p_rent_end_date", @@ -190,8 +190,7 @@ class VSPRepository: params = {"p_vsp_id": vsp.id, "p_updated_by": updated_by} for field, param in FIELD_TO_PARAM.items(): params[param] = data.get(field) - params.setdefault("p_placement_type", data.get("placement_type")) - params.setdefault("p_staff_count", data.get("staff_count")) + await self.db.execute( text(""" SELECT v3.upd_vsp( @@ -199,8 +198,7 @@ class VSPRepository: :p_format, :p_opened_at, :p_placement_type, :p_staff_count, :p_total_area, :p_closed_at, :p_is_active, :p_is_deleted, :p_system_code, :p_updated_by, :p_vsp_type, :p_notes, - :p_location_form, :p_numbers, :p_rent_contract_num, - :p_rent_end_date + :p_location_form, :p_rent_contract_num, :p_rent_end_date ) """), params, diff --git a/api/src/services/vsp_service.py b/api/src/services/vsp_service.py index 22f26d8..d84dd40 100644 --- a/api/src/services/vsp_service.py +++ b/api/src/services/vsp_service.py @@ -21,7 +21,7 @@ _info_close_sync_lock = asyncio.Lock() class VSPService: EXECUTOR_EDITABLE_FIELDS = { "location_form", - "numbers", + "staff_count", "total_area", "rent_contract_num", "rent_end_date", @@ -55,8 +55,8 @@ class VSPService: open_date_start: date | None = None, open_date_end: date | None = None, location_form: str | None = None, - numbers_min: int | None = None, - numbers_max: int | None = None, + staff_count_min: int | None = None, + staff_count_max: int | None = None, is_active: bool | None = None, load_org_unit: bool = False, with_count: bool = False, @@ -85,8 +85,8 @@ class VSPService: open_date_start=open_date_start, open_date_end=open_date_end, location_form=location_form, - numbers_min=numbers_min, - numbers_max=numbers_max, + staff_count_min=staff_count_min, + staff_count_max=staff_count_max, is_active=is_active, load_org_unit=load_org_unit, with_count=with_count, @@ -155,8 +155,8 @@ class VSPService: open_date_start: date | None = None, open_date_end: date | None = None, location_form: str | None = None, - numbers_min: int | None = None, - numbers_max: int | None = None, + staff_count_min: int | None = None, + staff_count_max: int | None = None, ) -> tuple[io.BytesIO, str]: data = await self.get_list( user=user, @@ -166,8 +166,8 @@ class VSPService: open_date_start=open_date_start, open_date_end=open_date_end, location_form=location_form, - numbers_min=numbers_min, - numbers_max=numbers_max, + staff_count_min=staff_count_min, + staff_count_max=staff_count_max, is_active=None, load_org_unit=True, ) @@ -189,7 +189,7 @@ class VSPService: "close_date", "notes", "location_form", - "numbers", + "staff_count", "area", "rent_contract_num", "rent_end_date", @@ -216,7 +216,7 @@ class VSPService: item.closed_at.isoformat() if item.closed_at else None, item.notes or None, item.location_form or None, - item.numbers if item.numbers is not None else None, + item.staff_count if item.staff_count is not None else None, float(item.total_area) if item.total_area is not None else None, item.rent_contract_num or None, item.rent_end_date.isoformat() if item.rent_end_date else None, diff --git a/api/tests/integration/test_vsp.py b/api/tests/integration/test_vsp.py index 4bdfebf..2dc5559 100644 --- a/api/tests/integration/test_vsp.py +++ b/api/tests/integration/test_vsp.py @@ -111,7 +111,7 @@ def test_vsp_create_with_all_fields(client, admin_tokens, auth_headers): "approved_format": "Стандарт", "notes": "Тестовое примечание", "location_form": "Встроенное", - "numbers": 10, + "staff_count": 10, "area": 150.5, "rent_contract_num": "Д-123/24", "rent_end_date": "2025-12-31", @@ -128,7 +128,7 @@ def test_vsp_create_with_all_fields(client, admin_tokens, auth_headers): assert payload["result"]["close_date"] == "2025-12-31" assert payload["result"]["approved_format"] == "Стандарт" assert payload["result"]["location_form"] == "Встроенное" - assert payload["result"]["numbers"] == 10 + assert payload["result"]["staff_count"] == 10 assert payload["result"]["area"] == 150.5 assert payload["result"]["rent_contract_num"] == "Д-123/24" assert payload["result"]["rent_end_date"] == "2025-12-31" From 0652ba4aee3ce8b74c0c22f40e6983aa542fe802 Mon Sep 17 00:00:00 2001 From: Raykov-MS Date: Mon, 29 Jun 2026 17:29:35 +0300 Subject: [PATCH 03/13] 'alembic' --- .../0004_vsp_remove_p_numbers_param.py | 335 ++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 api/alembic/versions/0004_vsp_remove_p_numbers_param.py diff --git a/api/alembic/versions/0004_vsp_remove_p_numbers_param.py b/api/alembic/versions/0004_vsp_remove_p_numbers_param.py new file mode 100644 index 0000000..9d26d9d --- /dev/null +++ b/api/alembic/versions/0004_vsp_remove_p_numbers_param.py @@ -0,0 +1,335 @@ +"""vsp remove p_numbers param + +Revision ID: 0004 +Revises: 0003 +Create Date: 2026-06-29 17:20:08.998832 + +""" +from typing import Sequence, Union + +from alembic import op + + +# revision identifiers, used by Alembic. +revision: str = '0004' +down_revision: Union[str, None] = '0003' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + op.execute( + """ + CREATE OR REPLACE FUNCTION v3.add_vsp( + p_branch_id integer, + p_reg_number character varying DEFAULT NULL::character varying, + p_address character varying DEFAULT NULL::character varying, + p_format character varying DEFAULT NULL::character varying, + p_opened_at date DEFAULT NULL::date, + p_placement_type character varying DEFAULT NULL::character varying, + p_staff_count integer DEFAULT NULL::integer, + p_total_area numeric DEFAULT NULL::numeric, + p_closed_at date DEFAULT NULL::date, + p_is_active boolean DEFAULT true, + p_is_deleted boolean DEFAULT false, + p_system_code character varying DEFAULT NULL::character varying, + p_created_by integer DEFAULT NULL::integer, + p_vsp_type character varying DEFAULT NULL::character varying, + p_notes text DEFAULT NULL::text, + p_location_form character varying DEFAULT NULL::character varying, + p_rent_contract_num character varying DEFAULT NULL::character varying, + p_rent_end_date date DEFAULT NULL::date + ) + RETURNS integer + LANGUAGE plpgsql + AS $function$ + DECLARE + v_id INT; + v_branch_name VARCHAR; + v_actor_id INT; + v_actor_email VARCHAR; + v_actor_full_name VARCHAR; + BEGIN + SELECT ou.title INTO v_branch_name + FROM v3.org_unit ou + WHERE ou.id = p_branch_id; + + v_actor_id := COALESCE(p_created_by, v3.current_user_id()); + + IF v_actor_id IS NOT NULL THEN + SELECT u.email, u.full_name + INTO v_actor_email, v_actor_full_name + FROM v3.app_user u + WHERE u.id = v_actor_id; + END IF; + + INSERT INTO v3.vsp + (branch_id, reg_number, address, format, opened_at, + placement_type, staff_count, total_area, closed_at, + is_active, is_deleted, system_code, created_by, + vsp_type, notes, location_form, rent_contract_num, rent_end_date) + VALUES + (p_branch_id, p_reg_number, p_address, p_format, p_opened_at, + p_placement_type, p_staff_count, p_total_area, p_closed_at, + p_is_active, p_is_deleted, p_system_code, p_created_by, + p_vsp_type, p_notes, p_location_form, p_rent_contract_num, p_rent_end_date) + RETURNING id INTO v_id; + + PERFORM v3.log_event( + 'VSP_CREATE', 'VSP', + jsonb_build_object( + 'vsp_id', v_id, + 'entity_id', v_id, + 'branch_id', p_branch_id, + 'branch_name', v_branch_name, + 'reg_number', p_reg_number, + 'address', p_address, + 'format', p_format, + 'opened_at', v3._diff_val(p_opened_at), + 'placement_type', p_placement_type, + 'staff_count', p_staff_count, + 'total_area', v3._diff_val(p_total_area), + 'closed_at', v3._diff_val(p_closed_at), + 'is_active', p_is_active, + 'is_deleted', p_is_deleted, + 'system_code', p_system_code, + 'created_by', p_created_by, + 'vsp_type', p_vsp_type, + 'notes', p_notes, + 'location_form', p_location_form, + 'rent_contract_num', p_rent_contract_num, + 'rent_end_date', v3._diff_val(p_rent_end_date), + 'user_email', v_actor_email, + 'user_full_name', v_actor_full_name + ), null, null, p_branch_id + ); + + RETURN v_id; + END; + $function$; + """ + ) + + op.execute( + """ + CREATE OR REPLACE FUNCTION v3.upd_vsp( + p_vsp_id integer, + p_branch_id integer DEFAULT NULL::integer, + p_reg_number character varying DEFAULT NULL::character varying, + p_address character varying DEFAULT NULL::character varying, + p_format character varying DEFAULT NULL::character varying, + p_opened_at date DEFAULT NULL::date, + p_placement_type character varying DEFAULT NULL::character varying, + p_staff_count integer DEFAULT NULL::integer, + p_total_area numeric DEFAULT NULL::numeric, + p_closed_at date DEFAULT NULL::date, + p_is_active boolean DEFAULT NULL::boolean, + p_is_deleted boolean DEFAULT NULL::boolean, + p_system_code character varying DEFAULT NULL::character varying, + p_updated_by integer DEFAULT NULL::integer, + p_vsp_type character varying DEFAULT NULL::character varying, + p_notes text DEFAULT NULL::text, + p_location_form character varying DEFAULT NULL::character varying, + p_rent_contract_num character varying DEFAULT NULL::character varying, + p_rent_end_date date DEFAULT NULL::date + ) + RETURNS void + LANGUAGE plpgsql + AS $function$ + DECLARE + v_old RECORD; + v_changes JSONB := '{}'::jsonb; + + v_branch_name_before VARCHAR; + v_branch_name_after VARCHAR; + + v_actor_id INT; + v_actor_email VARCHAR; + v_actor_full_name VARCHAR; + BEGIN + SELECT v.branch_id, v.reg_number, v.address, v.format, v.opened_at, + v.placement_type, v.staff_count, v.total_area, v.closed_at, + v.is_active, v.is_deleted, v.system_code, + v.vsp_type, v.notes, v.location_form, v.rent_contract_num, v.rent_end_date + INTO v_old + FROM v3.vsp v + WHERE v.id = p_vsp_id; + + IF NOT FOUND THEN + RAISE EXCEPTION 'vsp #% не существует', p_vsp_id; + END IF; + + SELECT ou.title + INTO v_branch_name_before + FROM v3.org_unit ou + WHERE ou.id = v_old.branch_id; + + v_branch_name_after := v_branch_name_before; + + v_actor_id := COALESCE(p_updated_by, v3.current_user_id()); + IF v_actor_id IS NOT NULL THEN + SELECT u.email, u.full_name + INTO v_actor_email, v_actor_full_name + FROM v3.app_user u + WHERE u.id = v_actor_id; + END IF; + + IF p_branch_id IS NOT NULL AND p_branch_id IS DISTINCT FROM v_old.branch_id THEN + SELECT ou.title INTO v_branch_name_after + FROM v3.org_unit ou + WHERE ou.id = p_branch_id; + + UPDATE v3.vsp SET branch_id = p_branch_id WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object( + 'branch_id', jsonb_build_object( + 'before', v3._diff_val(v_old.branch_id), + 'after', v3._diff_val(p_branch_id) + ), + 'branch_name', jsonb_build_object( + 'before', v3._diff_val(v_branch_name_before), + 'after', v3._diff_val(v_branch_name_after) + ) + ); + END IF; + + IF p_reg_number IS NOT NULL AND p_reg_number IS DISTINCT FROM v_old.reg_number THEN + UPDATE v3.vsp SET reg_number = p_reg_number WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('reg_number', + jsonb_build_object('before', v3._diff_val(v_old.reg_number), + 'after', v3._diff_val(p_reg_number))); + END IF; + + IF p_address IS NOT NULL AND p_address IS DISTINCT FROM v_old.address THEN + UPDATE v3.vsp SET address = p_address WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('address', + jsonb_build_object('before', v3._diff_val(v_old.address), + 'after', v3._diff_val(p_address))); + END IF; + + IF p_format IS NOT NULL AND p_format IS DISTINCT FROM v_old.format THEN + UPDATE v3.vsp SET format = p_format WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('format', + jsonb_build_object('before', v3._diff_val(v_old.format), + 'after', v3._diff_val(p_format))); + END IF; + + IF p_opened_at IS NOT NULL AND p_opened_at IS DISTINCT FROM v_old.opened_at THEN + UPDATE v3.vsp SET opened_at = p_opened_at WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('opened_at', + jsonb_build_object('before', v3._diff_val(v_old.opened_at), + 'after', v3._diff_val(p_opened_at))); + END IF; + + IF p_placement_type IS NOT NULL AND p_placement_type IS DISTINCT FROM v_old.placement_type THEN + UPDATE v3.vsp SET placement_type = p_placement_type WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('placement_type', + jsonb_build_object('before', v3._diff_val(v_old.placement_type), + 'after', v3._diff_val(p_placement_type))); + END IF; + + IF p_staff_count IS NOT NULL AND p_staff_count IS DISTINCT FROM v_old.staff_count THEN + UPDATE v3.vsp SET staff_count = p_staff_count WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('staff_count', + jsonb_build_object('before', v3._diff_val(v_old.staff_count), + 'after', v3._diff_val(p_staff_count))); + END IF; + + IF p_total_area IS NOT NULL AND p_total_area IS DISTINCT FROM v_old.total_area THEN + UPDATE v3.vsp SET total_area = p_total_area WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('total_area', + jsonb_build_object('before', v3._diff_val(v_old.total_area), + 'after', v3._diff_val(p_total_area))); + END IF; + + IF p_closed_at IS NOT NULL AND p_closed_at IS DISTINCT FROM v_old.closed_at THEN + UPDATE v3.vsp SET closed_at = p_closed_at WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('closed_at', + jsonb_build_object('before', v3._diff_val(v_old.closed_at), + 'after', v3._diff_val(p_closed_at))); + END IF; + + IF p_is_active IS NOT NULL AND p_is_active IS DISTINCT FROM v_old.is_active THEN + UPDATE v3.vsp SET is_active = p_is_active WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('is_active', + jsonb_build_object('before', v3._diff_val(v_old.is_active), + 'after', v3._diff_val(p_is_active))); + END IF; + + IF p_is_deleted IS NOT NULL AND p_is_deleted IS DISTINCT FROM v_old.is_deleted THEN + UPDATE v3.vsp SET is_deleted = p_is_deleted WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('is_deleted', + jsonb_build_object('before', v3._diff_val(v_old.is_deleted), + 'after', v3._diff_val(p_is_deleted))); + END IF; + + IF p_system_code IS NOT NULL AND p_system_code IS DISTINCT FROM v_old.system_code THEN + UPDATE v3.vsp SET system_code = p_system_code WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('system_code', + jsonb_build_object('before', v3._diff_val(v_old.system_code), + 'after', v3._diff_val(p_system_code))); + END IF; + + IF p_vsp_type IS NOT NULL AND p_vsp_type IS DISTINCT FROM v_old.vsp_type THEN + UPDATE v3.vsp SET vsp_type = p_vsp_type WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('vsp_type', + jsonb_build_object('before', v3._diff_val(v_old.vsp_type), + 'after', v3._diff_val(p_vsp_type))); + END IF; + + IF p_notes IS NOT NULL AND p_notes IS DISTINCT FROM v_old.notes THEN + UPDATE v3.vsp SET notes = p_notes WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('notes', + jsonb_build_object('before', v3._diff_val(v_old.notes), + 'after', v3._diff_val(p_notes))); + END IF; + + IF p_location_form IS NOT NULL AND p_location_form IS DISTINCT FROM v_old.location_form THEN + UPDATE v3.vsp SET location_form = p_location_form WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('location_form', + jsonb_build_object('before', v3._diff_val(v_old.location_form), + 'after', v3._diff_val(p_location_form))); + END IF; + + IF p_rent_contract_num IS NOT NULL AND p_rent_contract_num IS DISTINCT FROM v_old.rent_contract_num THEN + UPDATE v3.vsp SET rent_contract_num = p_rent_contract_num WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('rent_contract_num', + jsonb_build_object('before', v3._diff_val(v_old.rent_contract_num), + 'after', v3._diff_val(p_rent_contract_num))); + END IF; + + IF p_rent_end_date IS NOT NULL AND p_rent_end_date IS DISTINCT FROM v_old.rent_end_date THEN + UPDATE v3.vsp SET rent_end_date = p_rent_end_date WHERE id = p_vsp_id; + v_changes := v_changes || jsonb_build_object('rent_end_date', + jsonb_build_object('before', v3._diff_val(v_old.rent_end_date), + 'after', v3._diff_val(p_rent_end_date))); + END IF; + + IF v_changes <> '{}'::jsonb THEN + UPDATE v3.vsp + SET updated_at = now(), + updated_by = p_updated_by + WHERE id = p_vsp_id; + + PERFORM v3.log_event( + 'VSP_UPDATE', 'VSP', + jsonb_build_object( + 'vsp_id', p_vsp_id, + 'entity_id', p_vsp_id, + 'branch_id', COALESCE(p_branch_id, v_old.branch_id), + 'branch_name', COALESCE(v_branch_name_after, v_branch_name_before), + 'changes', v_changes, + 'user_email', v_actor_email, + 'user_full_name', v_actor_full_name + ), null, null, COALESCE(p_branch_id, v_old.branch_id) + ); + END IF; + END; + $function$; + """ + ) + + +def downgrade() -> None: + """Downgrade schema.""" + pass From 3b7c93f5a7ce6a948282247a05f423ece8fbbb0d Mon Sep 17 00:00:00 2001 From: Raykov-MS Date: Tue, 30 Jun 2026 09:51:17 +0300 Subject: [PATCH 04/13] del mig4 --- .../0004_vsp_remove_p_numbers_param.py | 335 ------------------ 1 file changed, 335 deletions(-) delete mode 100644 api/alembic/versions/0004_vsp_remove_p_numbers_param.py diff --git a/api/alembic/versions/0004_vsp_remove_p_numbers_param.py b/api/alembic/versions/0004_vsp_remove_p_numbers_param.py deleted file mode 100644 index 9d26d9d..0000000 --- a/api/alembic/versions/0004_vsp_remove_p_numbers_param.py +++ /dev/null @@ -1,335 +0,0 @@ -"""vsp remove p_numbers param - -Revision ID: 0004 -Revises: 0003 -Create Date: 2026-06-29 17:20:08.998832 - -""" -from typing import Sequence, Union - -from alembic import op - - -# revision identifiers, used by Alembic. -revision: str = '0004' -down_revision: Union[str, None] = '0003' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - op.execute( - """ - CREATE OR REPLACE FUNCTION v3.add_vsp( - p_branch_id integer, - p_reg_number character varying DEFAULT NULL::character varying, - p_address character varying DEFAULT NULL::character varying, - p_format character varying DEFAULT NULL::character varying, - p_opened_at date DEFAULT NULL::date, - p_placement_type character varying DEFAULT NULL::character varying, - p_staff_count integer DEFAULT NULL::integer, - p_total_area numeric DEFAULT NULL::numeric, - p_closed_at date DEFAULT NULL::date, - p_is_active boolean DEFAULT true, - p_is_deleted boolean DEFAULT false, - p_system_code character varying DEFAULT NULL::character varying, - p_created_by integer DEFAULT NULL::integer, - p_vsp_type character varying DEFAULT NULL::character varying, - p_notes text DEFAULT NULL::text, - p_location_form character varying DEFAULT NULL::character varying, - p_rent_contract_num character varying DEFAULT NULL::character varying, - p_rent_end_date date DEFAULT NULL::date - ) - RETURNS integer - LANGUAGE plpgsql - AS $function$ - DECLARE - v_id INT; - v_branch_name VARCHAR; - v_actor_id INT; - v_actor_email VARCHAR; - v_actor_full_name VARCHAR; - BEGIN - SELECT ou.title INTO v_branch_name - FROM v3.org_unit ou - WHERE ou.id = p_branch_id; - - v_actor_id := COALESCE(p_created_by, v3.current_user_id()); - - IF v_actor_id IS NOT NULL THEN - SELECT u.email, u.full_name - INTO v_actor_email, v_actor_full_name - FROM v3.app_user u - WHERE u.id = v_actor_id; - END IF; - - INSERT INTO v3.vsp - (branch_id, reg_number, address, format, opened_at, - placement_type, staff_count, total_area, closed_at, - is_active, is_deleted, system_code, created_by, - vsp_type, notes, location_form, rent_contract_num, rent_end_date) - VALUES - (p_branch_id, p_reg_number, p_address, p_format, p_opened_at, - p_placement_type, p_staff_count, p_total_area, p_closed_at, - p_is_active, p_is_deleted, p_system_code, p_created_by, - p_vsp_type, p_notes, p_location_form, p_rent_contract_num, p_rent_end_date) - RETURNING id INTO v_id; - - PERFORM v3.log_event( - 'VSP_CREATE', 'VSP', - jsonb_build_object( - 'vsp_id', v_id, - 'entity_id', v_id, - 'branch_id', p_branch_id, - 'branch_name', v_branch_name, - 'reg_number', p_reg_number, - 'address', p_address, - 'format', p_format, - 'opened_at', v3._diff_val(p_opened_at), - 'placement_type', p_placement_type, - 'staff_count', p_staff_count, - 'total_area', v3._diff_val(p_total_area), - 'closed_at', v3._diff_val(p_closed_at), - 'is_active', p_is_active, - 'is_deleted', p_is_deleted, - 'system_code', p_system_code, - 'created_by', p_created_by, - 'vsp_type', p_vsp_type, - 'notes', p_notes, - 'location_form', p_location_form, - 'rent_contract_num', p_rent_contract_num, - 'rent_end_date', v3._diff_val(p_rent_end_date), - 'user_email', v_actor_email, - 'user_full_name', v_actor_full_name - ), null, null, p_branch_id - ); - - RETURN v_id; - END; - $function$; - """ - ) - - op.execute( - """ - CREATE OR REPLACE FUNCTION v3.upd_vsp( - p_vsp_id integer, - p_branch_id integer DEFAULT NULL::integer, - p_reg_number character varying DEFAULT NULL::character varying, - p_address character varying DEFAULT NULL::character varying, - p_format character varying DEFAULT NULL::character varying, - p_opened_at date DEFAULT NULL::date, - p_placement_type character varying DEFAULT NULL::character varying, - p_staff_count integer DEFAULT NULL::integer, - p_total_area numeric DEFAULT NULL::numeric, - p_closed_at date DEFAULT NULL::date, - p_is_active boolean DEFAULT NULL::boolean, - p_is_deleted boolean DEFAULT NULL::boolean, - p_system_code character varying DEFAULT NULL::character varying, - p_updated_by integer DEFAULT NULL::integer, - p_vsp_type character varying DEFAULT NULL::character varying, - p_notes text DEFAULT NULL::text, - p_location_form character varying DEFAULT NULL::character varying, - p_rent_contract_num character varying DEFAULT NULL::character varying, - p_rent_end_date date DEFAULT NULL::date - ) - RETURNS void - LANGUAGE plpgsql - AS $function$ - DECLARE - v_old RECORD; - v_changes JSONB := '{}'::jsonb; - - v_branch_name_before VARCHAR; - v_branch_name_after VARCHAR; - - v_actor_id INT; - v_actor_email VARCHAR; - v_actor_full_name VARCHAR; - BEGIN - SELECT v.branch_id, v.reg_number, v.address, v.format, v.opened_at, - v.placement_type, v.staff_count, v.total_area, v.closed_at, - v.is_active, v.is_deleted, v.system_code, - v.vsp_type, v.notes, v.location_form, v.rent_contract_num, v.rent_end_date - INTO v_old - FROM v3.vsp v - WHERE v.id = p_vsp_id; - - IF NOT FOUND THEN - RAISE EXCEPTION 'vsp #% не существует', p_vsp_id; - END IF; - - SELECT ou.title - INTO v_branch_name_before - FROM v3.org_unit ou - WHERE ou.id = v_old.branch_id; - - v_branch_name_after := v_branch_name_before; - - v_actor_id := COALESCE(p_updated_by, v3.current_user_id()); - IF v_actor_id IS NOT NULL THEN - SELECT u.email, u.full_name - INTO v_actor_email, v_actor_full_name - FROM v3.app_user u - WHERE u.id = v_actor_id; - END IF; - - IF p_branch_id IS NOT NULL AND p_branch_id IS DISTINCT FROM v_old.branch_id THEN - SELECT ou.title INTO v_branch_name_after - FROM v3.org_unit ou - WHERE ou.id = p_branch_id; - - UPDATE v3.vsp SET branch_id = p_branch_id WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object( - 'branch_id', jsonb_build_object( - 'before', v3._diff_val(v_old.branch_id), - 'after', v3._diff_val(p_branch_id) - ), - 'branch_name', jsonb_build_object( - 'before', v3._diff_val(v_branch_name_before), - 'after', v3._diff_val(v_branch_name_after) - ) - ); - END IF; - - IF p_reg_number IS NOT NULL AND p_reg_number IS DISTINCT FROM v_old.reg_number THEN - UPDATE v3.vsp SET reg_number = p_reg_number WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('reg_number', - jsonb_build_object('before', v3._diff_val(v_old.reg_number), - 'after', v3._diff_val(p_reg_number))); - END IF; - - IF p_address IS NOT NULL AND p_address IS DISTINCT FROM v_old.address THEN - UPDATE v3.vsp SET address = p_address WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('address', - jsonb_build_object('before', v3._diff_val(v_old.address), - 'after', v3._diff_val(p_address))); - END IF; - - IF p_format IS NOT NULL AND p_format IS DISTINCT FROM v_old.format THEN - UPDATE v3.vsp SET format = p_format WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('format', - jsonb_build_object('before', v3._diff_val(v_old.format), - 'after', v3._diff_val(p_format))); - END IF; - - IF p_opened_at IS NOT NULL AND p_opened_at IS DISTINCT FROM v_old.opened_at THEN - UPDATE v3.vsp SET opened_at = p_opened_at WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('opened_at', - jsonb_build_object('before', v3._diff_val(v_old.opened_at), - 'after', v3._diff_val(p_opened_at))); - END IF; - - IF p_placement_type IS NOT NULL AND p_placement_type IS DISTINCT FROM v_old.placement_type THEN - UPDATE v3.vsp SET placement_type = p_placement_type WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('placement_type', - jsonb_build_object('before', v3._diff_val(v_old.placement_type), - 'after', v3._diff_val(p_placement_type))); - END IF; - - IF p_staff_count IS NOT NULL AND p_staff_count IS DISTINCT FROM v_old.staff_count THEN - UPDATE v3.vsp SET staff_count = p_staff_count WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('staff_count', - jsonb_build_object('before', v3._diff_val(v_old.staff_count), - 'after', v3._diff_val(p_staff_count))); - END IF; - - IF p_total_area IS NOT NULL AND p_total_area IS DISTINCT FROM v_old.total_area THEN - UPDATE v3.vsp SET total_area = p_total_area WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('total_area', - jsonb_build_object('before', v3._diff_val(v_old.total_area), - 'after', v3._diff_val(p_total_area))); - END IF; - - IF p_closed_at IS NOT NULL AND p_closed_at IS DISTINCT FROM v_old.closed_at THEN - UPDATE v3.vsp SET closed_at = p_closed_at WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('closed_at', - jsonb_build_object('before', v3._diff_val(v_old.closed_at), - 'after', v3._diff_val(p_closed_at))); - END IF; - - IF p_is_active IS NOT NULL AND p_is_active IS DISTINCT FROM v_old.is_active THEN - UPDATE v3.vsp SET is_active = p_is_active WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('is_active', - jsonb_build_object('before', v3._diff_val(v_old.is_active), - 'after', v3._diff_val(p_is_active))); - END IF; - - IF p_is_deleted IS NOT NULL AND p_is_deleted IS DISTINCT FROM v_old.is_deleted THEN - UPDATE v3.vsp SET is_deleted = p_is_deleted WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('is_deleted', - jsonb_build_object('before', v3._diff_val(v_old.is_deleted), - 'after', v3._diff_val(p_is_deleted))); - END IF; - - IF p_system_code IS NOT NULL AND p_system_code IS DISTINCT FROM v_old.system_code THEN - UPDATE v3.vsp SET system_code = p_system_code WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('system_code', - jsonb_build_object('before', v3._diff_val(v_old.system_code), - 'after', v3._diff_val(p_system_code))); - END IF; - - IF p_vsp_type IS NOT NULL AND p_vsp_type IS DISTINCT FROM v_old.vsp_type THEN - UPDATE v3.vsp SET vsp_type = p_vsp_type WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('vsp_type', - jsonb_build_object('before', v3._diff_val(v_old.vsp_type), - 'after', v3._diff_val(p_vsp_type))); - END IF; - - IF p_notes IS NOT NULL AND p_notes IS DISTINCT FROM v_old.notes THEN - UPDATE v3.vsp SET notes = p_notes WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('notes', - jsonb_build_object('before', v3._diff_val(v_old.notes), - 'after', v3._diff_val(p_notes))); - END IF; - - IF p_location_form IS NOT NULL AND p_location_form IS DISTINCT FROM v_old.location_form THEN - UPDATE v3.vsp SET location_form = p_location_form WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('location_form', - jsonb_build_object('before', v3._diff_val(v_old.location_form), - 'after', v3._diff_val(p_location_form))); - END IF; - - IF p_rent_contract_num IS NOT NULL AND p_rent_contract_num IS DISTINCT FROM v_old.rent_contract_num THEN - UPDATE v3.vsp SET rent_contract_num = p_rent_contract_num WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('rent_contract_num', - jsonb_build_object('before', v3._diff_val(v_old.rent_contract_num), - 'after', v3._diff_val(p_rent_contract_num))); - END IF; - - IF p_rent_end_date IS NOT NULL AND p_rent_end_date IS DISTINCT FROM v_old.rent_end_date THEN - UPDATE v3.vsp SET rent_end_date = p_rent_end_date WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('rent_end_date', - jsonb_build_object('before', v3._diff_val(v_old.rent_end_date), - 'after', v3._diff_val(p_rent_end_date))); - END IF; - - IF v_changes <> '{}'::jsonb THEN - UPDATE v3.vsp - SET updated_at = now(), - updated_by = p_updated_by - WHERE id = p_vsp_id; - - PERFORM v3.log_event( - 'VSP_UPDATE', 'VSP', - jsonb_build_object( - 'vsp_id', p_vsp_id, - 'entity_id', p_vsp_id, - 'branch_id', COALESCE(p_branch_id, v_old.branch_id), - 'branch_name', COALESCE(v_branch_name_after, v_branch_name_before), - 'changes', v_changes, - 'user_email', v_actor_email, - 'user_full_name', v_actor_full_name - ), null, null, COALESCE(p_branch_id, v_old.branch_id) - ); - END IF; - END; - $function$; - """ - ) - - -def downgrade() -> None: - """Downgrade schema.""" - pass From 20d0520b7d56fd4f969ee6605102d6b04632b76e Mon Sep 17 00:00:00 2001 From: Raykov-MS Date: Tue, 30 Jun 2026 10:54:09 +0300 Subject: [PATCH 05/13] =?UTF-8?q?=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8=20=D0=B2=20?= =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=D0=B0=D1=85=20=D0=B8=20=D1=84=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D1=86=D0=B8=D1=8E=20=D0=B2=20=D0=B1=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/alembic/versions/sql/0002_functions.sql | 20 +++++++++++++++---- .../integration/test_projects_api_smoke.py | 2 ++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/api/alembic/versions/sql/0002_functions.sql b/api/alembic/versions/sql/0002_functions.sql index cdf70dc..c19ea53 100644 --- a/api/alembic/versions/sql/0002_functions.sql +++ b/api/alembic/versions/sql/0002_functions.sql @@ -1571,7 +1571,7 @@ AS $function$ DECLARE v_report_id INT; v_eid INT; - v_org_id INT; + v_form_id INT; v_ei_path INT[]; BEGIN -- Извлекаем report_id + ei ДО DELETE (нужен для path) @@ -1585,21 +1585,33 @@ BEGIN v_ei_path := v3._expense_item_path_set(ARRAY[v_eid]); + SELECT bf.id + INTO v_form_id + FROM v3.rf_project_report r + JOIN v3.project p + ON p.id = r.project_id + LEFT JOIN v3.budget_form bf + ON bf.form_type_code = 'FORM_3' + AND bf.year = r.year + AND bf.org_unit_id = p.org_unit_id + WHERE r.id = v_report_id + LIMIT 1; + -- Каскад (FK без ON DELETE CASCADE) DELETE FROM v3.rf_project_report_quarter WHERE rf_project_report_line_id = p_line_id; DELETE FROM v3.rf_project_report_line WHERE id = p_line_id; - SELECT org_unit_id INTO v_org_id FROM v3.project WHERE id = v_report_id; -- Аудит: ROW_DELETE PERFORM v3.log_event( 'ROW_DELETE', 'ROW', jsonb_build_object( 'sheet', 'FORM_3', 'line_id', p_line_id, - 'entity_id', p_line_id, + 'form_id', v_form_id, 'report_id', v_report_id, 'expense_item_id', v_eid - ), null, null, v_org_id + ), + v_form_id ); -- Возврат: только иерархия по пути удалённой (INPUT уже нет) diff --git a/api/tests/integration/test_projects_api_smoke.py b/api/tests/integration/test_projects_api_smoke.py index dc21b78..a4bd92b 100644 --- a/api/tests/integration/test_projects_api_smoke.py +++ b/api/tests/integration/test_projects_api_smoke.py @@ -86,6 +86,8 @@ def test_projects_write_smoke(client, admin_tokens, auth_headers): f"/api/v1/projects/{project_id}/report/2026/LIMIT/line/{line_id}", headers=auth_headers(admin_tokens), ) + + print(del_line_response.status_code, del_line_response.json()) assert del_line_response.status_code == 200 From 48c7ab3eb1237fcaf1f20d565bf62ae1f3813133 Mon Sep 17 00:00:00 2001 From: Raykov-MS Date: Tue, 30 Jun 2026 10:54:56 +0300 Subject: [PATCH 06/13] del print --- api/tests/integration/test_projects_api_smoke.py | 1 - 1 file changed, 1 deletion(-) diff --git a/api/tests/integration/test_projects_api_smoke.py b/api/tests/integration/test_projects_api_smoke.py index a4bd92b..eaac624 100644 --- a/api/tests/integration/test_projects_api_smoke.py +++ b/api/tests/integration/test_projects_api_smoke.py @@ -87,7 +87,6 @@ def test_projects_write_smoke(client, admin_tokens, auth_headers): headers=auth_headers(admin_tokens), ) - print(del_line_response.status_code, del_line_response.json()) assert del_line_response.status_code == 200 From 0c10be7eb10ff8c8d1117540fdcd00774d748136 Mon Sep 17 00:00:00 2001 From: Raykov-MS Date: Tue, 30 Jun 2026 10:56:23 +0300 Subject: [PATCH 07/13] del space --- api/tests/integration/test_projects_api_smoke.py | 1 - 1 file changed, 1 deletion(-) diff --git a/api/tests/integration/test_projects_api_smoke.py b/api/tests/integration/test_projects_api_smoke.py index eaac624..dc21b78 100644 --- a/api/tests/integration/test_projects_api_smoke.py +++ b/api/tests/integration/test_projects_api_smoke.py @@ -86,7 +86,6 @@ def test_projects_write_smoke(client, admin_tokens, auth_headers): f"/api/v1/projects/{project_id}/report/2026/LIMIT/line/{line_id}", headers=auth_headers(admin_tokens), ) - assert del_line_response.status_code == 200 From c1059736fb3c3d225917322fb1b099146ed667a2 Mon Sep 17 00:00:00 2001 From: tsygankoviva Date: Tue, 30 Jun 2026 11:14:23 +0300 Subject: [PATCH 08/13] =?UTF-8?q?mig2-fix:=20=D0=BF=D0=BE=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D0=BB=20sql?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/alembic/versions/sql/0002_functions.sql | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/api/alembic/versions/sql/0002_functions.sql b/api/alembic/versions/sql/0002_functions.sql index cdf70dc..2277d26 100644 --- a/api/alembic/versions/sql/0002_functions.sql +++ b/api/alembic/versions/sql/0002_functions.sql @@ -4075,7 +4075,7 @@ $function$ -- DROP FUNCTION v3.v_form1_smeta(int4, int4); -CREATE FUNCTION v3.v_form1_smeta(p_year INT, p_org_unit_id INT) +CREATE OR REPLACE FUNCTION v3.v_form1_smeta(p_year INT, p_org_unit_id INT) RETURNS TABLE ( row_type VARCHAR, depth INT, @@ -4093,8 +4093,7 @@ RETURNS TABLE ( supp_corr_q2 NUMERIC, supp_corr_q3 NUMERIC, supp_corr_q4 NUMERIC, dev_corr_q2 NUMERIC, dev_corr_q3 NUMERIC, dev_corr_q4 NUMERIC ) -AS $$ -#variable_conflict use_column +AS $function$ DECLARE v_ahr INT; v_cap INT; v_oper INT; v_sections TEXT[] := ARRAY['plan','approved','q1','q2','q3','q4']; @@ -4223,7 +4222,9 @@ BEGIN FROM merged m ORDER BY m.sc; END; -$$ LANGUAGE plpgsql STABLE SET search_path = v3, pg_catalog; +$function$ +LANGUAGE plpgsql STABLE SET search_path = v3, pg_catalog +; -- DROP FUNCTION v3.v_form2_sheet_sections(int4, varchar, _text); From 8869423ea64118fb684658401fa9d6f6407bcdc7 Mon Sep 17 00:00:00 2001 From: Raykov-MS Date: Tue, 30 Jun 2026 11:19:37 +0300 Subject: [PATCH 09/13] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BB=20numbres.?= =?UTF-8?q?..=D0=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/db/models/vsp.py | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/db/models/vsp.py b/api/src/db/models/vsp.py index ed24d10..5866718 100644 --- a/api/src/db/models/vsp.py +++ b/api/src/db/models/vsp.py @@ -46,7 +46,6 @@ class Vsp(Base): vsp_type: Mapped[str | None] = mapped_column(String(100), nullable=True) notes: Mapped[str | None] = mapped_column(Text, nullable=True) location_form: Mapped[str | None] = mapped_column(String, nullable=True) - numbers: Mapped[int | None] = mapped_column(Integer, nullable=True) rent_contract_num: Mapped[str | None] = mapped_column(String, nullable=True) rent_end_date : Mapped[date | None]= mapped_column(Date, nullable=True) From 03ea64d6b520a822d08760ff82b875eb8e960b4d Mon Sep 17 00:00:00 2001 From: Raykov-MS Date: Tue, 30 Jun 2026 11:52:39 +0300 Subject: [PATCH 10/13] del numbers from schema --- api/alembic/versions/0001_initial_schema.py | 1 - 1 file changed, 1 deletion(-) diff --git a/api/alembic/versions/0001_initial_schema.py b/api/alembic/versions/0001_initial_schema.py index 7872039..7b0c29b 100644 --- a/api/alembic/versions/0001_initial_schema.py +++ b/api/alembic/versions/0001_initial_schema.py @@ -920,7 +920,6 @@ def upgrade() -> None: sa.Column('vsp_type', sa.String(100)), sa.Column('notes', sa.Text()), sa.Column('location_form', sa.String()), - sa.Column('numbers', sa.Integer()), sa.Column('rent_contract_num', sa.String()), sa.Column('rent_end_date', sa.Date()), schema='v3', From 4f8393bedc88356c8f3776ac3ad9e14902aa513f Mon Sep 17 00:00:00 2001 From: PotapovaA Date: Tue, 30 Jun 2026 12:37:07 +0300 Subject: [PATCH 11/13] rename vsp.number to vsp.staff_count --- web/src/components/RealtimeTable/RealtimeTable.jsx | 2 +- .../DictVspPage/components/Modals/ModalEditAddVsp.jsx | 10 +++++----- web/src/pages/DictVspPage/components/TableDictVsp.jsx | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/web/src/components/RealtimeTable/RealtimeTable.jsx b/web/src/components/RealtimeTable/RealtimeTable.jsx index 67284d0..6041ca3 100644 --- a/web/src/components/RealtimeTable/RealtimeTable.jsx +++ b/web/src/components/RealtimeTable/RealtimeTable.jsx @@ -26,7 +26,7 @@ import { useRealtime } from './contexts/RealtimeContext'; import { toast } from 'react-toastify'; import { CircularProgress } from '@mui/material'; import { additionVspRowTable } from './constants/addingRowConfig'; -import { SelectVspModal } from './Modals/selectVspModal'; +import { SelectVspModal } from './Modals/SelectVspModal'; import { getRowId } from './utils/rowUtils'; const RealtimeTable = ({ formType, formId, sheetName, direction }) => { diff --git a/web/src/pages/DictVspPage/components/Modals/ModalEditAddVsp.jsx b/web/src/pages/DictVspPage/components/Modals/ModalEditAddVsp.jsx index 60d32ed..c782c40 100644 --- a/web/src/pages/DictVspPage/components/Modals/ModalEditAddVsp.jsx +++ b/web/src/pages/DictVspPage/components/Modals/ModalEditAddVsp.jsx @@ -95,7 +95,7 @@ const ModalEditAddVsp = ({ : null, notes: initialData.notes || '', location_form: initialData.location_form || '', - numbers: initialData.numbers?.toString() || '', + numbers: initialData.staff_count?.toString() || '', area: initialData.area || null, rent_contract_num: initialData.rent_contract_num || '', rent_end_date: initialData.rent_end_date @@ -215,7 +215,7 @@ const ModalEditAddVsp = ({ const submitData = { ...formData, - numbers: formData.numbers ? parseInt(formData.numbers, 10) : null, + numbers: formData.staff_count ? parseInt(formData.staff_count, 10) : null, area: formData.area ? parseFloat(formData.area) : null, open_date: formData.open_date ? formData.open_date.toLocaleDateString('en-CA') @@ -495,11 +495,11 @@ const ModalEditAddVsp = ({ Штатная численность ВСП Date: Tue, 30 Jun 2026 15:04:15 +0300 Subject: [PATCH 12/13] =?UTF-8?q?smeta-fix:=20=D0=BF=D0=BE=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=B4=D0=B4=D0=BB=20=D1=84=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D0=B8=D0=B8=20=D1=81=D0=BC=D0=B5=D1=82=D1=8B=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B2=D0=BE=D0=B9=20=D1=84=D0=BE=D1=80=D0=BC?= =?UTF-8?q?=D1=8B,=20=D0=BF=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=D1=82=D1=83=D1=80=D1=8B=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=82=D0=B5=D1=81=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/alembic/versions/sql/0002_functions.sql | 27 +++++---------------- api/tests/sqls/fixture.sql | 6 ++--- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/api/alembic/versions/sql/0002_functions.sql b/api/alembic/versions/sql/0002_functions.sql index 6c66595..63f0af2 100644 --- a/api/alembic/versions/sql/0002_functions.sql +++ b/api/alembic/versions/sql/0002_functions.sql @@ -4086,26 +4086,13 @@ $function$ -- DROP FUNCTION v3.v_form1_smeta(int4, int4); - -CREATE OR REPLACE FUNCTION v3.v_form1_smeta(p_year INT, p_org_unit_id INT) -RETURNS TABLE ( - row_type VARCHAR, - depth INT, - section_code VARCHAR, - name VARCHAR, - supp_plan_q1 NUMERIC, supp_plan_q2 NUMERIC, supp_plan_q3 NUMERIC, supp_plan_q4 NUMERIC, supp_plan_year NUMERIC, - dev_plan_q1 NUMERIC, dev_plan_q2 NUMERIC, dev_plan_q3 NUMERIC, dev_plan_q4 NUMERIC, dev_plan_year NUMERIC, - total_plan_year NUMERIC, - supp_appr_q1 NUMERIC, supp_appr_q2 NUMERIC, supp_appr_q3 NUMERIC, supp_appr_q4 NUMERIC, supp_appr_year NUMERIC, - dev_appr_q1 NUMERIC, dev_appr_q2 NUMERIC, dev_appr_q3 NUMERIC, dev_appr_q4 NUMERIC, dev_appr_year NUMERIC, - total_appr_year NUMERIC, - supp_act_q1 NUMERIC, supp_act_q2 NUMERIC, supp_act_q3 NUMERIC, supp_act_q4 NUMERIC, supp_act_year NUMERIC, - dev_act_q1 NUMERIC, dev_act_q2 NUMERIC, dev_act_q3 NUMERIC, dev_act_q4 NUMERIC, dev_act_year NUMERIC, - total_act_year NUMERIC, - supp_corr_q2 NUMERIC, supp_corr_q3 NUMERIC, supp_corr_q4 NUMERIC, - dev_corr_q2 NUMERIC, dev_corr_q3 NUMERIC, dev_corr_q4 NUMERIC -) +CREATE OR REPLACE FUNCTION v3.v_form1_smeta(p_year integer, p_org_unit_id integer) + RETURNS TABLE(row_type character varying, depth integer, section_code character varying, name character varying, supp_plan_q1 numeric, supp_plan_q2 numeric, supp_plan_q3 numeric, supp_plan_q4 numeric, supp_plan_year numeric, dev_plan_q1 numeric, dev_plan_q2 numeric, dev_plan_q3 numeric, dev_plan_q4 numeric, dev_plan_year numeric, total_plan_year numeric, supp_appr_q1 numeric, supp_appr_q2 numeric, supp_appr_q3 numeric, supp_appr_q4 numeric, supp_appr_year numeric, dev_appr_q1 numeric, dev_appr_q2 numeric, dev_appr_q3 numeric, dev_appr_q4 numeric, dev_appr_year numeric, total_appr_year numeric, supp_act_q1 numeric, supp_act_q2 numeric, supp_act_q3 numeric, supp_act_q4 numeric, supp_act_year numeric, dev_act_q1 numeric, dev_act_q2 numeric, dev_act_q3 numeric, dev_act_q4 numeric, dev_act_year numeric, total_act_year numeric, supp_corr_q2 numeric, supp_corr_q3 numeric, supp_corr_q4 numeric, dev_corr_q2 numeric, dev_corr_q3 numeric, dev_corr_q4 numeric) + LANGUAGE plpgsql + STABLE + SET search_path TO 'v3', 'pg_catalog' AS $function$ +#variable_conflict use_column DECLARE v_ahr INT; v_cap INT; v_oper INT; v_sections TEXT[] := ARRAY['plan','approved','q1','q2','q3','q4']; @@ -4235,9 +4222,7 @@ BEGIN ORDER BY m.sc; END; $function$ -LANGUAGE plpgsql STABLE SET search_path = v3, pg_catalog ; - -- DROP FUNCTION v3.v_form2_sheet_sections(int4, varchar, _text); CREATE OR REPLACE FUNCTION v3.v_form2_sheet_sections(p_form_id integer, p_sheet character varying, p_sections text[] DEFAULT NULL::text[]) diff --git a/api/tests/sqls/fixture.sql b/api/tests/sqls/fixture.sql index 6aa3e65..c587bf6 100644 --- a/api/tests/sqls/fixture.sql +++ b/api/tests/sqls/fixture.sql @@ -26,7 +26,7 @@ SELECT setval('v3.budget_line_id_seq', 1); INSERT INTO v3.form_phase (budget_form_id,sheet,phase_code,"role",column_keys,opens_at,closes_at) VALUES (1,'AHR','test','DFIP','{{plan.q1}}','2026-05-05 03:00:00+03','2026-06-06 03:00:00+03'); -INSERT INTO v3.vsp (id,branch_id,reg_number,address,format,opened_at,placement_type,staff_count,total_area,closed_at,is_active,updated_at,is_deleted,created_at,created_by,system_code,updated_by,vsp_type,notes,location_form,numbers,rent_contract_num,rent_end_date) VALUES - (1,2,'Тестовый всп 1','Тестовая 12','укукк','2026-05-07','ывс',2026,230,'2026-06-16',false,'2026-06-18 15:46:38.611903',false,'2026-06-04 14:36:59.737727',1,'1233',91,'','','встроенное помещение',NULL,'',NULL), - (2,2,'Test12 -----','Тестовая 125','формат 15 ','2026-06-08',NULL,NULL,58,NULL,true,'2026-06-16 18:53:17.986651',false,'2026-06-09 09:04:33.29858',91,'123-1234',91,'субаренда','Работает ','аренда',155,'12-45','2026-10-02'); +INSERT INTO v3.vsp (id,branch_id,reg_number,address,format,opened_at,placement_type,staff_count,total_area,closed_at,is_active,updated_at,is_deleted,created_at,created_by,system_code,updated_by,vsp_type,notes,location_form,rent_contract_num,rent_end_date) VALUES + (1,2,'Тестовый всп 1','Тестовая 12','укукк','2026-05-07','ывс',2026,230,'2026-06-16',false,'2026-06-18 15:46:38.611903',false,'2026-06-04 14:36:59.737727',1,'1233',91,'','','встроенное помещение','',NULL), + (2,2,'Test12 -----','Тестовая 125','формат 15 ','2026-06-08',NULL,NULL,58,NULL,true,'2026-06-16 18:53:17.986651',false,'2026-06-09 09:04:33.29858',91,'123-1234',91,'субаренда','Работает ','аренда','12-45','2026-10-02'); SELECT setval('v3.vsp_id_seq', 2); From 5d16915badd13ccd67140da887f876d7da59429d Mon Sep 17 00:00:00 2001 From: Raykov-MS Date: Tue, 30 Jun 2026 17:24:08 +0300 Subject: [PATCH 13/13] =?UTF-8?q?=D0=9E=D0=BA=D0=B0=D0=B7=D1=8B=D0=B2?= =?UTF-8?q?=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20location=5Fform=20=D1=8D=D1=82?= =?UTF-8?q?=D0=BE=20=D0=B8=20=D0=B5=D1=81=D1=82=D1=8C=20placement=5Ftype,?= =?UTF-8?q?=20=D1=83=D0=B1=D1=80=D0=B0=D0=BB=20location=5Fform?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/alembic/versions/sql/0002_functions.sql | 22 +++++++-------------- api/requirements-dev.txt | 1 + api/src/api/v1/vsp.py | 6 +++--- api/src/db/models/vsp.py | 1 - api/src/domain/schemas.py | 4 +--- api/src/repository/vsp_repository.py | 14 ++++++------- api/src/services/vsp_service.py | 14 ++++++------- api/tests/integration/test_vsp.py | 10 +++++----- 8 files changed, 30 insertions(+), 42 deletions(-) diff --git a/api/alembic/versions/sql/0002_functions.sql b/api/alembic/versions/sql/0002_functions.sql index c19ea53..119d335 100644 --- a/api/alembic/versions/sql/0002_functions.sql +++ b/api/alembic/versions/sql/0002_functions.sql @@ -1123,9 +1123,9 @@ END; $function$ ; --- DROP FUNCTION v3.add_vsp(int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, varchar, date); +-- DROP FUNCTION v3.add_vsp(int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, date); -CREATE OR REPLACE FUNCTION v3.add_vsp(p_branch_id integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT true, p_is_deleted boolean DEFAULT false, p_system_code character varying DEFAULT NULL::character varying, p_created_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_location_form character varying DEFAULT NULL::character varying, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date) +CREATE OR REPLACE FUNCTION v3.add_vsp(p_branch_id integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT true, p_is_deleted boolean DEFAULT false, p_system_code character varying DEFAULT NULL::character varying, p_created_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date) RETURNS integer LANGUAGE plpgsql AS $function$ @@ -1153,12 +1153,12 @@ BEGIN (branch_id, reg_number, address, format, opened_at, placement_type, staff_count, total_area, closed_at, is_active, is_deleted, system_code, created_by, - vsp_type, notes, location_form, rent_contract_num, rent_end_date) + vsp_type, notes, rent_contract_num, rent_end_date) VALUES (p_branch_id, p_reg_number, p_address, p_format, p_opened_at, p_placement_type, p_staff_count, p_total_area, p_closed_at, p_is_active, p_is_deleted, p_system_code, p_created_by, - p_vsp_type, p_notes, p_location_form, p_rent_contract_num, p_rent_end_date) + p_vsp_type, p_notes, p_rent_contract_num, p_rent_end_date) RETURNING id INTO v_id; PERFORM v3.log_event( @@ -1182,7 +1182,6 @@ BEGIN 'created_by', p_created_by, 'vsp_type', p_vsp_type, 'notes', p_notes, - 'location_form', p_location_form, 'rent_contract_num', p_rent_contract_num, 'rent_end_date', v3._diff_val(p_rent_end_date), 'user_email', v_actor_email, @@ -2747,9 +2746,9 @@ END; $function$ ; --- DROP FUNCTION v3.upd_vsp(int4, int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, varchar, date); +-- DROP FUNCTION v3.upd_vsp(int4, int4, varchar, varchar, varchar, date, varchar, int4, numeric, date, bool, bool, varchar, int4, varchar, text, varchar, date); -CREATE OR REPLACE FUNCTION v3.upd_vsp(p_vsp_id integer, p_branch_id integer DEFAULT NULL::integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT NULL::boolean, p_is_deleted boolean DEFAULT NULL::boolean, p_system_code character varying DEFAULT NULL::character varying, p_updated_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_location_form character varying DEFAULT NULL::character varying, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date) +CREATE OR REPLACE FUNCTION v3.upd_vsp(p_vsp_id integer, p_branch_id integer DEFAULT NULL::integer, p_reg_number character varying DEFAULT NULL::character varying, p_address character varying DEFAULT NULL::character varying, p_format character varying DEFAULT NULL::character varying, p_opened_at date DEFAULT NULL::date, p_placement_type character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_closed_at date DEFAULT NULL::date, p_is_active boolean DEFAULT NULL::boolean, p_is_deleted boolean DEFAULT NULL::boolean, p_system_code character varying DEFAULT NULL::character varying, p_updated_by integer DEFAULT NULL::integer, p_vsp_type character varying DEFAULT NULL::character varying, p_notes text DEFAULT NULL::text, p_rent_contract_num character varying DEFAULT NULL::character varying, p_rent_end_date date DEFAULT NULL::date) RETURNS void LANGUAGE plpgsql AS $function$ @@ -2767,7 +2766,7 @@ BEGIN SELECT v.branch_id, v.reg_number, v.address, v.format, v.opened_at, v.placement_type, v.staff_count, v.total_area, v.closed_at, v.is_active, v.is_deleted, v.system_code, - v.vsp_type, v.notes, v.location_form, v.rent_contract_num, v.rent_end_date + v.vsp_type, v.notes, v.rent_contract_num, v.rent_end_date INTO v_old FROM v3.vsp v WHERE v.id = p_vsp_id; @@ -2900,13 +2899,6 @@ BEGIN 'after', v3._diff_val(p_notes))); END IF; - IF p_location_form IS NOT NULL AND p_location_form IS DISTINCT FROM v_old.location_form THEN - UPDATE v3.vsp SET location_form = p_location_form WHERE id = p_vsp_id; - v_changes := v_changes || jsonb_build_object('location_form', - jsonb_build_object('before', v3._diff_val(v_old.location_form), - 'after', v3._diff_val(p_location_form))); - END IF; - IF p_rent_contract_num IS NOT NULL AND p_rent_contract_num IS DISTINCT FROM v_old.rent_contract_num THEN UPDATE v3.vsp SET rent_contract_num = p_rent_contract_num WHERE id = p_vsp_id; v_changes := v_changes || jsonb_build_object('rent_contract_num', diff --git a/api/requirements-dev.txt b/api/requirements-dev.txt index bc6fc87..faa293a 100644 --- a/api/requirements-dev.txt +++ b/api/requirements-dev.txt @@ -14,3 +14,4 @@ aiosqlite==0.20.0 asyncpg==0.30.0 pytest==8.3.2 pytest-asyncio==0.24.0 +openpyxl==3.1.5 diff --git a/api/src/api/v1/vsp.py b/api/src/api/v1/vsp.py index 1cd34c6..4ab76da 100644 --- a/api/src/api/v1/vsp.py +++ b/api/src/api/v1/vsp.py @@ -27,7 +27,7 @@ async def get_vsp( ssp_id: int | None = None, open_date_start: date | None = None, open_date_end: date | None = None, - location_form: str | None = None, + placement_type: str | None = None, staff_count_min: int | None = None, staff_count_max: int | None = None, db: AsyncSession = Depends(get_db), @@ -42,7 +42,7 @@ async def get_vsp( ssp_id=ssp_id, open_date_start=open_date_start, open_date_end=open_date_end, - location_form=location_form, + placement_type=placement_type, staff_count_min=staff_count_min, staff_count_max=staff_count_max, load_org_unit=True, @@ -89,7 +89,7 @@ async def export_info( ssp_ids=body.ssp_ids, open_date_start=body.open_date_start, open_date_end=body.open_date_end, - location_form=body.location_form, + placement_type=body.placement_type, staff_count_min=body.staff_count_min, staff_count_max=body.staff_count_max, ) diff --git a/api/src/db/models/vsp.py b/api/src/db/models/vsp.py index 5866718..a907fa5 100644 --- a/api/src/db/models/vsp.py +++ b/api/src/db/models/vsp.py @@ -45,7 +45,6 @@ class Vsp(Base): ) vsp_type: Mapped[str | None] = mapped_column(String(100), nullable=True) notes: Mapped[str | None] = mapped_column(Text, nullable=True) - location_form: Mapped[str | None] = mapped_column(String, nullable=True) rent_contract_num: Mapped[str | None] = mapped_column(String, nullable=True) rent_end_date : Mapped[date | None]= mapped_column(Date, nullable=True) diff --git a/api/src/domain/schemas.py b/api/src/domain/schemas.py index ce441d3..6b2dea1 100644 --- a/api/src/domain/schemas.py +++ b/api/src/domain/schemas.py @@ -492,7 +492,6 @@ class VSPBase(BaseModel): notes: Optional[str] = Field(None, description="Примечания") placement_type: Optional[str] = Field(None, description="Тип размещения") staff_count: Optional[int] = Field(None, description="Штатная численность") - location_form: Optional[str] = Field(None, description="Форма расположения") area: Optional[float] = Field( None, description="Арендная площадь", @@ -542,7 +541,6 @@ class VSPEditBase(BaseModel): notes: Optional[str] = Field(None, description="Примечания") placement_type: Optional[str] = Field(None, description="Тип размещения") staff_count: Optional[int] = Field(None, description="Штатная численность") - location_form: Optional[str] = Field(None, description="Форма расположения") total_area: Optional[float] = Field( None, description="Арендная площадь", @@ -613,7 +611,7 @@ class VSPExportRequest(BaseModel): ssp_ids: Optional[List[int]] = None open_date_start: Optional[date] = None open_date_end: Optional[date] = None - location_form: Optional[str] = None + placement_type: Optional[str] = None staff_count_min: Optional[int] = None staff_count_max: Optional[int] = None diff --git a/api/src/repository/vsp_repository.py b/api/src/repository/vsp_repository.py index 51ef4a0..a6b1b94 100644 --- a/api/src/repository/vsp_repository.py +++ b/api/src/repository/vsp_repository.py @@ -36,7 +36,7 @@ class VSPRepository: ssp_ids: list[int] | None = None, open_date_start: date | None = None, open_date_end: date | None = None, - location_form: str | None = None, + placement_type: str | None = None, staff_count_min: int | None = None, staff_count_max: int | None = None, is_active: bool | None = True, @@ -53,7 +53,7 @@ class VSPRepository: filters = [ ("registration_number", registration_number), ("address", address), - ("location_form", location_form), + ("placement_type", placement_type), ("open_date_start", open_date_start), ("open_date_end", open_date_end), ("staff_count_min", staff_count_min), @@ -67,8 +67,8 @@ class VSPRepository: queries.append(Vsp.reg_number.ilike(f"%{value}%")) case ("address", str(value)) if value: queries.append(Vsp.address.ilike(f"%{value}%")) - case ("location_form", str(value)) if value: - queries.append(Vsp.location_form.ilike(f"%{value}%")) + case ("placement_type", str(value)) if value: + queries.append(Vsp.placement_type.ilike(f"%{value}%")) case ("open_date_start", value) if value is not None: queries.append(Vsp.opened_at >= value) case ("open_date_end", value) if value is not None: @@ -125,7 +125,7 @@ class VSPRepository: :p_opened_at, :p_placement_type, :p_staff_count, :p_total_area, :p_closed_at, :p_is_active, :p_is_deleted, :p_system_code, :p_created_by, :p_vsp_type, :p_notes, - :p_location_form, :p_rent_contract_num, :p_rent_end_date + :p_rent_contract_num, :p_rent_end_date ) """), { @@ -144,7 +144,6 @@ class VSPRepository: "p_created_by": created_by, "p_vsp_type": data.get("vsp_type"), "p_notes": data.get("notes"), - "p_location_form": data.get("location_form"), "p_rent_contract_num": data.get("rent_contract_num"), "p_rent_end_date": data.get("rent_end_date"), } @@ -178,7 +177,6 @@ class VSPRepository: "placement_type": "p_placement_type", "staff_count": "p_staff_count", "notes": "p_notes", - "location_form": "p_location_form", "total_area": "p_total_area", "rent_contract_num": "p_rent_contract_num", "rent_end_date": "p_rent_end_date", @@ -198,7 +196,7 @@ class VSPRepository: :p_format, :p_opened_at, :p_placement_type, :p_staff_count, :p_total_area, :p_closed_at, :p_is_active, :p_is_deleted, :p_system_code, :p_updated_by, :p_vsp_type, :p_notes, - :p_location_form, :p_rent_contract_num, :p_rent_end_date + :p_rent_contract_num, :p_rent_end_date ) """), params, diff --git a/api/src/services/vsp_service.py b/api/src/services/vsp_service.py index d84dd40..822759d 100644 --- a/api/src/services/vsp_service.py +++ b/api/src/services/vsp_service.py @@ -20,7 +20,7 @@ _info_close_sync_lock = asyncio.Lock() class VSPService: EXECUTOR_EDITABLE_FIELDS = { - "location_form", + "placement_type", "staff_count", "total_area", "rent_contract_num", @@ -54,7 +54,7 @@ class VSPService: ssp_ids: list[int] | None = None, open_date_start: date | None = None, open_date_end: date | None = None, - location_form: str | None = None, + placement_type: str | None = None, staff_count_min: int | None = None, staff_count_max: int | None = None, is_active: bool | None = None, @@ -84,7 +84,7 @@ class VSPService: ssp_ids=effective_ssp_ids, open_date_start=open_date_start, open_date_end=open_date_end, - location_form=location_form, + placement_type=placement_type, staff_count_min=staff_count_min, staff_count_max=staff_count_max, is_active=is_active, @@ -154,7 +154,7 @@ class VSPService: ssp_ids: list[int] | None = None, open_date_start: date | None = None, open_date_end: date | None = None, - location_form: str | None = None, + placement_type: str | None = None, staff_count_min: int | None = None, staff_count_max: int | None = None, ) -> tuple[io.BytesIO, str]: @@ -165,7 +165,7 @@ class VSPService: ssp_ids=ssp_ids, open_date_start=open_date_start, open_date_end=open_date_end, - location_form=location_form, + placement_type=placement_type, staff_count_min=staff_count_min, staff_count_max=staff_count_max, is_active=None, @@ -188,7 +188,7 @@ class VSPService: "open_date", "close_date", "notes", - "location_form", + "placement_type", "staff_count", "area", "rent_contract_num", @@ -215,7 +215,7 @@ class VSPService: item.opened_at.isoformat() if item.opened_at else None, item.closed_at.isoformat() if item.closed_at else None, item.notes or None, - item.location_form or None, + item.placement_type or None, item.staff_count if item.staff_count is not None else None, float(item.total_area) if item.total_area is not None else None, item.rent_contract_num or None, diff --git a/api/tests/integration/test_vsp.py b/api/tests/integration/test_vsp.py index 2dc5559..7fd80f6 100644 --- a/api/tests/integration/test_vsp.py +++ b/api/tests/integration/test_vsp.py @@ -40,10 +40,10 @@ def test_vsp_dropdown_smoke(client, admin_tokens, auth_headers): @pytest.mark.parametrize( "params, count", [ - ([("ssp_id", 2)], 1), + ([("ssp_id", 2)], 2), ([("form_id", 1)], 0), - ([("form_id", 2)], 1), - ([("ssp_id", 2), ("form_id", 1)], 1), + ([("form_id", 2)], 2), + ([("ssp_id", 2), ("form_id", 1)], 2), ] ) def test_vsp_dropdown_with_filter(client, admin_tokens, auth_headers, params, count): @@ -110,7 +110,7 @@ def test_vsp_create_with_all_fields(client, admin_tokens, auth_headers): "close_date": "2025-12-31", "approved_format": "Стандарт", "notes": "Тестовое примечание", - "location_form": "Встроенное", + "placement_type": "Аренда", "staff_count": 10, "area": 150.5, "rent_contract_num": "Д-123/24", @@ -127,7 +127,7 @@ def test_vsp_create_with_all_fields(client, admin_tokens, auth_headers): assert payload["result"]["open_date"] == "2024-01-15" assert payload["result"]["close_date"] == "2025-12-31" assert payload["result"]["approved_format"] == "Стандарт" - assert payload["result"]["location_form"] == "Встроенное" + assert payload["result"]["placement_type"] == "Аренда" assert payload["result"]["staff_count"] == 10 assert payload["result"]["area"] == 150.5 assert payload["result"]["rent_contract_num"] == "Д-123/24"