Compare commits
No commits in common. "64302ea1270fb7b25e21de964e2893d5c261ae6d" and "8c978b4e339bb31ecbcd216f47bcd32d38e58b23" have entirely different histories.
64302ea127
...
8c978b4e33
@ -1,84 +0,0 @@
|
|||||||
import os
|
|
||||||
import re
|
|
||||||
import time
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
|
|
||||||
revision = "0011"
|
|
||||||
down_revision = "0010"
|
|
||||||
branch_labels = None
|
|
||||||
depends_on = None
|
|
||||||
|
|
||||||
|
|
||||||
_DOLLAR_TAG_RE = re.compile(r"\$\w+\$")
|
|
||||||
|
|
||||||
|
|
||||||
def _find_dollar_tag(line: str) -> str | None:
|
|
||||||
m = _DOLLAR_TAG_RE.search(line.strip())
|
|
||||||
return m.group(0) if m else None
|
|
||||||
|
|
||||||
|
|
||||||
def _split_statements(sql: str) -> list[str]:
|
|
||||||
statements: list[str] = []
|
|
||||||
current: list[str] = []
|
|
||||||
in_dollar = False
|
|
||||||
dollar_tag: str | None = None
|
|
||||||
|
|
||||||
for line in sql.split("\n"):
|
|
||||||
stripped = line.strip()
|
|
||||||
if stripped.startswith("--"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not in_dollar:
|
|
||||||
tag = _find_dollar_tag(stripped)
|
|
||||||
if tag and tag.endswith("$") and tag.startswith("$"):
|
|
||||||
dollar_tag = tag
|
|
||||||
in_dollar = True
|
|
||||||
current.append(line)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if in_dollar and dollar_tag and stripped.startswith(dollar_tag):
|
|
||||||
after = stripped[len(dollar_tag):].strip()
|
|
||||||
if after == ";" or after == "":
|
|
||||||
in_dollar = False
|
|
||||||
dollar_tag = None
|
|
||||||
if after == ";":
|
|
||||||
current.append(line)
|
|
||||||
statements.append("\n".join(current))
|
|
||||||
current = []
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not in_dollar and stripped.rstrip().endswith(";"):
|
|
||||||
current.append(line)
|
|
||||||
statements.append("\n".join(current))
|
|
||||||
current = []
|
|
||||||
continue
|
|
||||||
|
|
||||||
current.append(line)
|
|
||||||
|
|
||||||
remaining = "\n".join(current).strip()
|
|
||||||
if remaining:
|
|
||||||
statements.append(remaining)
|
|
||||||
|
|
||||||
return statements
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
|
||||||
ddl_path = os.path.join(os.path.dirname(__file__), "sql", "0011_form3_log.sql")
|
|
||||||
with open(ddl_path) as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
statements = _split_statements(content)
|
|
||||||
for stmt in statements:
|
|
||||||
stripped = stmt.strip().rstrip(";").strip()
|
|
||||||
if not stripped:
|
|
||||||
continue
|
|
||||||
if all(l.strip().startswith("--") or not l.strip() for l in stripped.split("\n")):
|
|
||||||
continue
|
|
||||||
op.execute(stripped)
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
|
||||||
pass
|
|
||||||
@ -822,7 +822,7 @@ BEGIN
|
|||||||
|
|
||||||
-- 5а. Аудит: ROW_CREATE
|
-- 5а. Аудит: ROW_CREATE
|
||||||
PERFORM v3.log_event(
|
PERFORM v3.log_event(
|
||||||
'ROW_CREATE', 'ROW',
|
'ROW_CREATE', 'ROW_CREATE',
|
||||||
jsonb_build_object(
|
jsonb_build_object(
|
||||||
'sheet', v_sheet,
|
'sheet', v_sheet,
|
||||||
'line_id', v_new_id,
|
'line_id', v_new_id,
|
||||||
|
|||||||
@ -1,172 +0,0 @@
|
|||||||
CREATE OR REPLACE FUNCTION v3.add_project_year(p_project_id integer, p_year integer, p_emit_audit boolean DEFAULT true)
|
|
||||||
RETURNS TABLE(limit_report_id integer, current_expenses_report_id integer)
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $function$
|
|
||||||
DECLARE
|
|
||||||
v_lim INT;
|
|
||||||
v_cur INT;
|
|
||||||
BEGIN
|
|
||||||
IF p_year IS NULL THEN
|
|
||||||
RAISE EXCEPTION 'p_year обязателен';
|
|
||||||
END IF;
|
|
||||||
IF NOT EXISTS (SELECT 1 FROM v3.project WHERE id = p_project_id) THEN
|
|
||||||
RAISE EXCEPTION 'project id=% не существует', p_project_id;
|
|
||||||
END IF;
|
|
||||||
IF EXISTS (SELECT 1 FROM v3.rf_project_report
|
|
||||||
WHERE project_id = p_project_id AND year = p_year) THEN
|
|
||||||
RAISE EXCEPTION 'год % у проекта id=% уже заведён', p_year, p_project_id;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
INSERT INTO v3.rf_project_report (project_id, year, report_type)
|
|
||||||
VALUES (p_project_id, p_year, 'LIMIT') RETURNING id INTO v_lim;
|
|
||||||
INSERT INTO v3.rf_project_report (project_id, year, report_type)
|
|
||||||
VALUES (p_project_id, p_year, 'CURRENT_EXPENSES') RETURNING id INTO v_cur;
|
|
||||||
|
|
||||||
-- Snapshot RBAC-фаз (иначе default deny → отчёт read-only)
|
|
||||||
PERFORM v3.copy_template_to_report(v_lim);
|
|
||||||
PERFORM v3.copy_template_to_report(v_cur);
|
|
||||||
|
|
||||||
-- Фиксированная сетка статей на оба трека
|
|
||||||
PERFORM v3.seed_form3_report_lines(v_lim);
|
|
||||||
PERFORM v3.seed_form3_report_lines(v_cur);
|
|
||||||
|
|
||||||
IF p_emit_audit THEN
|
|
||||||
PERFORM v3.log_event(
|
|
||||||
'PROJECT_YEAR_ADD', 'PROJECT',
|
|
||||||
jsonb_build_object(
|
|
||||||
'project_id', p_project_id,
|
|
||||||
'year', p_year,
|
|
||||||
'limit_report_id', v_lim,
|
|
||||||
'current_expenses_report_id', v_cur
|
|
||||||
)
|
|
||||||
);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
RETURN QUERY SELECT v_lim, v_cur;
|
|
||||||
END;
|
|
||||||
$function$
|
|
||||||
;
|
|
||||||
|
|
||||||
-- FKs
|
|
||||||
ALTER TABLE v3.app_user ADD CONSTRAINT app_user_role_fk FOREIGN KEY (role_id) REFERENCES v3."role"(id);
|
|
||||||
ALTER TABLE v3.audit_log ADD CONSTRAINT audit_log_app_user_fk FOREIGN KEY (user_id) REFERENCES v3.app_user(id);
|
|
||||||
ALTER TABLE v3.audit_log ADD CONSTRAINT audit_log_org_unit_fk FOREIGN KEY (org_unit_id) REFERENCES v3.org_unit(id);
|
|
||||||
ALTER TABLE v3.audit_log ADD CONSTRAINT audit_log_budget_form_fk FOREIGN KEY (form_id) REFERENCES v3.budget_form(id);
|
|
||||||
ALTER TABLE v3.booking ADD CONSTRAINT booking_budget_line_fk FOREIGN KEY (line_id) REFERENCES v3.budget_line(id);
|
|
||||||
ALTER TABLE v3.budget_form ADD CONSTRAINT budget_form_app_user_fk FOREIGN KEY (created_by) REFERENCES v3.app_user(id);
|
|
||||||
ALTER TABLE v3.budget_form ADD CONSTRAINT budget_form_app_user_fk_1 FOREIGN KEY (updated_by) REFERENCES v3.app_user(id);
|
|
||||||
ALTER TABLE v3.budget_form ADD CONSTRAINT budget_form_org_unit_fk FOREIGN KEY (org_unit_id) REFERENCES v3.org_unit(id);
|
|
||||||
ALTER TABLE v3.budget_line ADD CONSTRAINT budget_line_budget_form_fk FOREIGN KEY (budget_form_id) REFERENCES v3.budget_form(id);
|
|
||||||
ALTER TABLE v3.budget_line ADD CONSTRAINT budget_line_expense_item_fk FOREIGN KEY (expense_item_id) REFERENCES v3.expense_item(id);
|
|
||||||
ALTER TABLE v3.budget_line ADD CONSTRAINT budget_line_vsp_fk FOREIGN KEY (vsp_id) REFERENCES v3.vsp(id);
|
|
||||||
ALTER TABLE v3.budget_line ADD CONSTRAINT budget_line_app_user_fk FOREIGN KEY (created_by) REFERENCES v3.app_user(id);
|
|
||||||
ALTER TABLE v3.budget_line ADD CONSTRAINT budget_line_app_user_fk_1 FOREIGN KEY (updated_by) REFERENCES v3.app_user(id);
|
|
||||||
ALTER TABLE v3.budget_line_quarter ADD CONSTRAINT budget_line_quarter_budget_line_fk FOREIGN KEY (line_id) REFERENCES v3.budget_line(id);
|
|
||||||
ALTER TABLE v3.ckk ADD CONSTRAINT ckk_budget_line_fk FOREIGN KEY (line_id) REFERENCES v3.budget_line(id);
|
|
||||||
ALTER TABLE v3.collegial_approval ADD CONSTRAINT collegial_approval_budget_line_fk FOREIGN KEY (line_id) REFERENCES v3.budget_line(id);
|
|
||||||
ALTER TABLE v3.contract_detail ADD CONSTRAINT contract_detail_budget_line_fk FOREIGN KEY (line_id) REFERENCES v3.budget_line(id);
|
|
||||||
ALTER TABLE v3.contract_summary ADD CONSTRAINT contract_summary_budget_line_fk FOREIGN KEY (line_id) REFERENCES v3.budget_line(id);
|
|
||||||
ALTER TABLE v3.expense_item ADD CONSTRAINT expense_item_expense_item_fk FOREIGN KEY (parent_id) REFERENCES v3.expense_item(id);
|
|
||||||
ALTER TABLE v3.expense_item_form_type ADD CONSTRAINT expense_item_form_type_expense_item_fk FOREIGN KEY (expense_item_id) REFERENCES v3.expense_item(id);
|
|
||||||
ALTER TABLE v3.fixed_asset_report ADD CONSTRAINT fixed_asset_report_budget_form_fk FOREIGN KEY (budget_form_id) REFERENCES v3.budget_form(id);
|
|
||||||
ALTER TABLE v3.fixed_asset_report ADD CONSTRAINT fixed_asset_report_expense_item_fk FOREIGN KEY (expense_item_id) REFERENCES v3.expense_item(id);
|
|
||||||
ALTER TABLE v3.form3_phase ADD CONSTRAINT form3_phase_rf_project_report_fk FOREIGN KEY (rf_project_report_id) REFERENCES v3.rf_project_report(id);
|
|
||||||
ALTER TABLE v3.form_limit ADD CONSTRAINT form_limit_budget_form_fk FOREIGN KEY (budget_form_id) REFERENCES v3.budget_form(id);
|
|
||||||
ALTER TABLE v3.form_phase ADD CONSTRAINT form_phase_budget_form_fk FOREIGN KEY (budget_form_id) REFERENCES v3.budget_form(id);
|
|
||||||
ALTER TABLE v3.phase_template ADD CONSTRAINT phase_template_form_type_fk FOREIGN KEY (form_type) REFERENCES v3.form_type(code);
|
|
||||||
ALTER TABLE v3."plan" ADD CONSTRAINT plan_budget_line_fk FOREIGN KEY (line_id) REFERENCES v3.budget_line(id);
|
|
||||||
ALTER TABLE v3.project ADD CONSTRAINT project_org_unit_fk FOREIGN KEY (org_unit_id) REFERENCES v3.org_unit(id);
|
|
||||||
ALTER TABLE v3.rent_detail ADD CONSTRAINT rent_detail_budget_line_fk FOREIGN KEY (line_id) REFERENCES v3.budget_line(id);
|
|
||||||
ALTER TABLE v3.rent_detail ADD CONSTRAINT rent_detail_vsp_fk FOREIGN KEY (vsp_id) REFERENCES v3.vsp(id);
|
|
||||||
ALTER TABLE v3.reserve ADD CONSTRAINT reserve_budget_line_fk FOREIGN KEY (line_id) REFERENCES v3.budget_line(id);
|
|
||||||
ALTER TABLE v3.rf_project_report ADD CONSTRAINT rf_project_report_project_fk FOREIGN KEY (project_id) REFERENCES v3.project(id);
|
|
||||||
ALTER TABLE v3.rf_project_report ADD CONSTRAINT rf_project_report_app_user_fk FOREIGN KEY (created_by) REFERENCES v3.app_user(id);
|
|
||||||
ALTER TABLE v3.rf_project_report ADD CONSTRAINT rf_project_report_app_user_fk_1 FOREIGN KEY (updated_by) REFERENCES v3.app_user(id);
|
|
||||||
ALTER TABLE v3.rf_project_report_line ADD CONSTRAINT rf_project_report_line_rf_project_report_fk FOREIGN KEY (rf_project_report_id) REFERENCES v3.rf_project_report(id);
|
|
||||||
ALTER TABLE v3.rf_project_report_line ADD CONSTRAINT rf_project_report_line_expense_item_fk FOREIGN KEY (expense_item_id) REFERENCES v3.expense_item(id);
|
|
||||||
ALTER TABLE v3.rf_project_report_quarter ADD CONSTRAINT rf_project_report_quarter_rf_project_report_line_fk FOREIGN KEY (rf_project_report_line_id) REFERENCES v3.rf_project_report_line(id);
|
|
||||||
ALTER TABLE v3.security_detail ADD CONSTRAINT security_detail_budget_line_fk FOREIGN KEY (line_id) REFERENCES v3.budget_line(id);
|
|
||||||
ALTER TABLE v3.security_detail ADD CONSTRAINT security_detail_vsp_fk FOREIGN KEY (vsp_id) REFERENCES v3.vsp(id);
|
|
||||||
ALTER TABLE v3.sequestration ADD CONSTRAINT sequestration_budget_line_fk FOREIGN KEY (line_id) REFERENCES v3.budget_line(id);
|
|
||||||
ALTER TABLE v3.user_org ADD CONSTRAINT user_org_app_user_fk FOREIGN KEY (user_id) REFERENCES v3.app_user(id);
|
|
||||||
ALTER TABLE v3.user_org ADD CONSTRAINT user_org_org_unit_fk FOREIGN KEY (org_unit_id) REFERENCES v3.org_unit(id);
|
|
||||||
ALTER TABLE v3.utility_detail ADD CONSTRAINT utility_detail_budget_line_fk FOREIGN KEY (line_id) REFERENCES v3.budget_line(id);
|
|
||||||
ALTER TABLE v3.utility_detail ADD CONSTRAINT utility_detail_vsp_fk FOREIGN KEY (vsp_id) REFERENCES v3.vsp(id);
|
|
||||||
ALTER TABLE v3.vsp ADD CONSTRAINT vsp_org_unit_fk FOREIGN KEY (branch_id) REFERENCES v3.org_unit(id);
|
|
||||||
ALTER TABLE v3.vsp ADD CONSTRAINT vsp_app_user_fk FOREIGN KEY (created_by) REFERENCES v3.app_user(id);
|
|
||||||
ALTER TABLE v3.vsp ADD CONSTRAINT vsp_app_user_fk_1 FOREIGN KEY (updated_by) REFERENCES v3.app_user(id);
|
|
||||||
|
|
||||||
|
|
||||||
-- DROP FUNCTION v3.add_project(varchar, int4, int4, varchar, int4, varchar, varchar, varchar, varchar, int4, numeric, varchar);
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION v3.add_project(p_name character varying, p_year integer, p_org_unit_id integer, p_level character varying DEFAULT 'project'::character varying, p_parent_id integer DEFAULT NULL::integer, p_project_type character varying DEFAULT NULL::character varying, p_vsp_format character varying DEFAULT NULL::character varying, p_placement_type character varying DEFAULT NULL::character varying, p_object_address character varying DEFAULT NULL::character varying, p_staff_count integer DEFAULT NULL::integer, p_total_area numeric DEFAULT NULL::numeric, p_system_code character varying DEFAULT NULL::character varying)
|
|
||||||
RETURNS TABLE(project_id integer, limit_report_id integer, current_expenses_report_id integer)
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $function$
|
|
||||||
DECLARE
|
|
||||||
v_pid INT;
|
|
||||||
v_lim INT;
|
|
||||||
v_cur INT;
|
|
||||||
BEGIN
|
|
||||||
IF p_name IS NULL OR length(trim(p_name)) = 0 THEN
|
|
||||||
RAISE EXCEPTION 'p_name обязателен';
|
|
||||||
END IF;
|
|
||||||
IF p_year IS NULL THEN
|
|
||||||
RAISE EXCEPTION 'p_year обязателен';
|
|
||||||
END IF;
|
|
||||||
IF p_org_unit_id IS NULL THEN
|
|
||||||
RAISE EXCEPTION 'p_org_unit_id обязателен';
|
|
||||||
END IF;
|
|
||||||
IF NOT EXISTS (SELECT 1 FROM v3.org_unit ou WHERE id = p_org_unit_id AND ou.is_active ) THEN
|
|
||||||
RAISE EXCEPTION 'org_unit id=% не существует', p_org_unit_id;
|
|
||||||
END IF;
|
|
||||||
IF p_parent_id IS NOT NULL AND
|
|
||||||
NOT EXISTS (SELECT 1 FROM v3.project WHERE id = p_parent_id AND level = 'program') THEN
|
|
||||||
RAISE EXCEPTION 'parent project id=% не существует или не имеет level=program', p_parent_id;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
-- INSERT project (CHECK-ы name/project_type/vsp_format/placement_type
|
|
||||||
-- из migrate_form3_v1.sql применятся автоматически).
|
|
||||||
INSERT INTO v3.project (
|
|
||||||
name, level, parent_id, org_unit_id,
|
|
||||||
project_type, vsp_format, placement_type, object_address, staff_count, total_area,
|
|
||||||
system_code
|
|
||||||
) VALUES (
|
|
||||||
p_name, p_level, p_parent_id, p_org_unit_id,
|
|
||||||
p_project_type, p_vsp_format, p_placement_type, p_object_address, p_staff_count, p_total_area,
|
|
||||||
p_system_code
|
|
||||||
) RETURNING id INTO v_pid;
|
|
||||||
|
|
||||||
-- Первый год: 2 отчёта (LIMIT + CURRENT_EXPENSES) + snapshot фаз + сетка статей.
|
|
||||||
-- p_emit_audit=false — событие года покрыто PROJECT_CREATE ниже. Дальнейшие
|
|
||||||
-- годы добавляются отдельно через v3.add_project_year.
|
|
||||||
SELECT y.limit_report_id, y.current_expenses_report_id
|
|
||||||
INTO v_lim, v_cur
|
|
||||||
FROM v3.add_project_year(v_pid, p_year, false) y;
|
|
||||||
|
|
||||||
-- Аудит: PROJECT_CREATE — все заполненные поля + id отчётов
|
|
||||||
PERFORM v3.log_event(
|
|
||||||
'PROJECT_CREATE', 'PROJECT',
|
|
||||||
jsonb_strip_nulls(jsonb_build_object(
|
|
||||||
'project_id', v_pid,
|
|
||||||
'name', p_name,
|
|
||||||
'year', p_year,
|
|
||||||
'level', p_level,
|
|
||||||
'parent_id', p_parent_id,
|
|
||||||
'org_unit_id', p_org_unit_id,
|
|
||||||
'project_type', p_project_type,
|
|
||||||
'vsp_format', p_vsp_format,
|
|
||||||
'placement_type', p_placement_type,
|
|
||||||
'object_address', p_object_address,
|
|
||||||
'system_code', p_system_code,
|
|
||||||
'staff_count', p_staff_count,
|
|
||||||
'total_area', p_total_area,
|
|
||||||
'limit_report_id', v_lim,
|
|
||||||
'current_expenses_report_id', v_cur
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
RETURN QUERY SELECT v_pid, v_lim, v_cur;
|
|
||||||
END;
|
|
||||||
$function$
|
|
||||||
;
|
|
||||||
@ -46,7 +46,6 @@ async def get_vsp(
|
|||||||
staff_count_min=staff_count_min,
|
staff_count_min=staff_count_min,
|
||||||
staff_count_max=staff_count_max,
|
staff_count_max=staff_count_max,
|
||||||
load_org_unit=True,
|
load_org_unit=True,
|
||||||
is_active=True,
|
|
||||||
with_count=True,
|
with_count=True,
|
||||||
)
|
)
|
||||||
return BaseListResponse(
|
return BaseListResponse(
|
||||||
|
|||||||
@ -33,7 +33,6 @@ class Vsp(Base):
|
|||||||
total_area: Mapped[float | None] = mapped_column(Numeric)
|
total_area: Mapped[float | None] = mapped_column(Numeric)
|
||||||
closed_at: Mapped[date | None] = mapped_column(Date)
|
closed_at: Mapped[date | None] = mapped_column(Date)
|
||||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||||
#ToDo: а нужно ли нам и is_active и is_deleted?
|
|
||||||
is_deleted: Mapped[bool] = mapped_column(Boolean, default=False)
|
is_deleted: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||||
system_code: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
system_code: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
||||||
created_at: Mapped[datetime] = mapped_column(
|
created_at: Mapped[datetime] = mapped_column(
|
||||||
|
|||||||
@ -14,14 +14,8 @@ class VSPRepository:
|
|||||||
def __init__(self, db: AsyncSession):
|
def __init__(self, db: AsyncSession):
|
||||||
self.db = db
|
self.db = db
|
||||||
|
|
||||||
async def get(
|
async def get(self, vsp_id: int, is_active: bool | None = None, load_org_unit: bool = False) -> Optional[Vsp]:
|
||||||
self,
|
queries = [Vsp.id == vsp_id, Vsp.is_deleted.is_(False)]
|
||||||
vsp_id: int,
|
|
||||||
is_active: bool | None = None,
|
|
||||||
load_org_unit: bool = False,
|
|
||||||
with_deleted: bool = False,
|
|
||||||
) -> Optional[Vsp]:
|
|
||||||
queries = [Vsp.id == vsp_id, Vsp.is_deleted.is_(with_deleted)]
|
|
||||||
if is_active is not None:
|
if is_active is not None:
|
||||||
queries.append(Vsp.is_active == is_active)
|
queries.append(Vsp.is_active == is_active)
|
||||||
if load_org_unit:
|
if load_org_unit:
|
||||||
|
|||||||
@ -49,7 +49,6 @@ SHEET_NAMES = {
|
|||||||
'AHR_SECURITY': 'АХР Безопасность',
|
'AHR_SECURITY': 'АХР Безопасность',
|
||||||
'AHR_UTILITY': 'АХР Коммунальные услуги',
|
'AHR_UTILITY': 'АХР Коммунальные услуги',
|
||||||
'SMETA': 'Смета',
|
'SMETA': 'Смета',
|
||||||
'STRUCTURE': 'Структура',
|
|
||||||
}
|
}
|
||||||
SKIP_SHEETS = ('OTCH9F',)
|
SKIP_SHEETS = ('OTCH9F',)
|
||||||
|
|
||||||
@ -328,8 +327,8 @@ class ExportService:
|
|||||||
("Форма ID", form.id),
|
("Форма ID", form.id),
|
||||||
("Тип формы", form.form_type_code),
|
("Тип формы", form.form_type_code),
|
||||||
("Год", form.year),
|
("Год", form.year),
|
||||||
("ID ССП.РФ", form.org_unit_id),
|
("Org Unit ID", form.org_unit_id),
|
||||||
("ССП/РФ", form.org_unit.title if form.org_unit else None),
|
("Org Unit", form.org_unit.title if form.org_unit else None),
|
||||||
("Экспортировано", datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")),
|
("Экспортировано", datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")),
|
||||||
]
|
]
|
||||||
for row_idx, (key, value) in enumerate(metadata_rows, start=1):
|
for row_idx, (key, value) in enumerate(metadata_rows, start=1):
|
||||||
|
|||||||
@ -36,11 +36,7 @@ class VSPService:
|
|||||||
self.user_repo = UserRepository(db)
|
self.user_repo = UserRepository(db)
|
||||||
|
|
||||||
async def get(self, vsp_id: int, user: AppUser, load_org_unit: bool = False) -> Optional[Vsp]:
|
async def get(self, vsp_id: int, user: AppUser, load_org_unit: bool = False) -> Optional[Vsp]:
|
||||||
vsp = await self.vsp_repo.get(
|
vsp = await self.vsp_repo.get(vsp_id=vsp_id, load_org_unit=load_org_unit)
|
||||||
vsp_id=vsp_id,
|
|
||||||
load_org_unit=load_org_unit,
|
|
||||||
with_deleted=user.role_id == UserRoleEnum.ADMIN,
|
|
||||||
)
|
|
||||||
if not vsp:
|
if not vsp:
|
||||||
return None
|
return None
|
||||||
if user.role_id == UserRoleEnum.ADMIN:
|
if user.role_id == UserRoleEnum.ADMIN:
|
||||||
@ -177,7 +173,7 @@ class VSPService:
|
|||||||
placement_type=placement_type,
|
placement_type=placement_type,
|
||||||
staff_count_min=staff_count_min,
|
staff_count_min=staff_count_min,
|
||||||
staff_count_max=staff_count_max,
|
staff_count_max=staff_count_max,
|
||||||
is_active=True,
|
is_active=None,
|
||||||
load_org_unit=True,
|
load_org_unit=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -58,8 +58,8 @@ def test_vsp_dropdown_with_filter(client, admin_tokens, auth_headers, params, co
|
|||||||
assert len(payload["result"]) == count
|
assert len(payload["result"]) == count
|
||||||
|
|
||||||
|
|
||||||
def test_vsp_get_by_id(client, isp_tokens, auth_headers):
|
def test_vsp_get_by_id(client, admin_tokens, auth_headers):
|
||||||
response = client.get("/api/v1/dict/info/1", headers=auth_headers(isp_tokens))
|
response = client.get("/api/v1/dict/info/1", headers=auth_headers(admin_tokens))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
payload = response.json()
|
payload = response.json()
|
||||||
assert "result" in payload
|
assert "result" in payload
|
||||||
|
|||||||
@ -28,9 +28,9 @@ INSERT INTO v3.form_phase (budget_form_id,sheet,phase_code,"role",column_keys,op
|
|||||||
(4,'AHR','test','DFIP','{{plan.q1}}','2029-05-05 03:00:00+05','2029-06-06 03:00:00+05');
|
(4,'AHR','test','DFIP','{{plan.q1}}','2029-05-05 03:00:00+05','2029-06-06 03:00:00+05');
|
||||||
|
|
||||||
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,rent_contract_num,rent_end_date) VALUES
|
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,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',1,'','','',NULL),
|
(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',1,'123-1234',1,'субаренда','Работает ','12-45','2027-10-02'),
|
(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','2027-10-02'),
|
||||||
(3,2,'3434','победы 12','формат','2026-06-01','субаренда', NULL,34,NULL,true,'2026-07-01 13:32:35.327097',false,'2026-06-29 15:53:50.236062',1,'3434',NULL,'2','примечание','567','2027-07-01');
|
(3,2,'3434','победы 12','формат','2026-06-01','субаренда', NULL,34,NULL,true,'2026-07-01 13:32:35.327097',false,'2026-06-29 15:53:50.236062',91,'3434',NULL,'2','примечание','567','2027-07-01');
|
||||||
SELECT setval('v3.vsp_id_seq', 3);
|
SELECT setval('v3.vsp_id_seq', 3);
|
||||||
|
|
||||||
INSERT INTO v3.project ("id","name","level",parent_id,project_type,vsp_format,placement_type,object_address,staff_count,total_area,org_unit_id) VALUES
|
INSERT INTO v3.project ("id","name","level",parent_id,project_type,vsp_format,placement_type,object_address,staff_count,total_area,org_unit_id) VALUES
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { getDataRowId, getParentId } from "./rowUtils";
|
import { getParentId } from "./rowUtils";
|
||||||
|
|
||||||
export function setNewValueToData(data, colId, rowId, value) {
|
export function setNewValueToData(data, colId, rowId, value) {
|
||||||
let targetObject = data;
|
let targetObject = data;
|
||||||
@ -209,9 +209,8 @@ export function deleteRow(data, lineId) {
|
|||||||
function findAndDelete(rows) {
|
function findAndDelete(rows) {
|
||||||
for (let i = 0; i < rows.length; i++) {
|
for (let i = 0; i < rows.length; i++) {
|
||||||
const row = rows[i];
|
const row = rows[i];
|
||||||
const rowId = getDataRowId(row.data);
|
|
||||||
|
|
||||||
if (rowId == lineId) {
|
if (row.data.line_id == lineId) {
|
||||||
rowDeleteSortOrder = row.sort_order;
|
rowDeleteSortOrder = row.sort_order;
|
||||||
rows.splice(i, 1);
|
rows.splice(i, 1);
|
||||||
i--;
|
i--;
|
||||||
@ -222,7 +221,7 @@ export function deleteRow(data, lineId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (row.subRows && Array.isArray(row.subRows) && row.subRows.length > 0) {
|
if (row.subRows && Array.isArray(row.subRows) && row.subRows.length > 0) {
|
||||||
findAndDelete(row.subRows);
|
const found = findAndDelete(row.subRows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
export function getDataRowId(data) {
|
|
||||||
return data?.id || data?.line_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRowId(row) {
|
export function getRowId(row) {
|
||||||
return getDataRowId(row.original.data);
|
return row.original.data.id || row.original.data.line_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isVspRow(row) {
|
export function isVspRow(row) {
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { CalendarInput } from './Calendar/CalendarInput';
|
|||||||
import { Cancel } from '../common/icons/icons';
|
import { Cancel } from '../common/icons/icons';
|
||||||
import { getStageStatus, getStatusColors } from './StagesTable/utils';
|
import { getStageStatus, getStatusColors } from './StagesTable/utils';
|
||||||
import { StageStatusChip } from './StageStatusChip';
|
import { StageStatusChip } from './StageStatusChip';
|
||||||
import { formatDateForApi } from '../../utils/formatDate';
|
import { formatDateForApi } from './utils';
|
||||||
import CollapsibleTree from '../common/CollapsibleTree';
|
import CollapsibleTree from '../common/CollapsibleTree';
|
||||||
import { collectLeafColumns } from '../RealtimeTable/utils/columnUtils';
|
import { collectLeafColumns } from '../RealtimeTable/utils/columnUtils';
|
||||||
import { STAGE_ROLE_RUSSIAN_NAME } from '../../constants/constants';
|
import { STAGE_ROLE_RUSSIAN_NAME } from '../../constants/constants';
|
||||||
|
|||||||
@ -1,3 +1,11 @@
|
|||||||
|
export const formatDateForApi = (value) => {
|
||||||
|
if (!value) return '';
|
||||||
|
const d = new Date(value);
|
||||||
|
const year = d.getFullYear();
|
||||||
|
const month = String(d.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(d.getDate()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
};
|
||||||
export const isPastDate = (value) => {
|
export const isPastDate = (value) => {
|
||||||
if (!value) return false;
|
if (!value) return false;
|
||||||
const date = new Date(value);
|
const date = new Date(value);
|
||||||
@ -6,3 +14,4 @@ export const isPastDate = (value) => {
|
|||||||
today.setHours(0, 0, 0, 0);
|
today.setHours(0, 0, 0, 0);
|
||||||
return date < today;
|
return date < today;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,6 @@ import { UsersApi } from '../../../api/users';
|
|||||||
import AuditLogsFilters from './components/AuditLogsFilters';
|
import AuditLogsFilters from './components/AuditLogsFilters';
|
||||||
import AuditLogsTable from './components/AuditLogsTable';
|
import AuditLogsTable from './components/AuditLogsTable';
|
||||||
import { DEFAULT_ACTIONS } from './constants/auditLogs.jsx';
|
import { DEFAULT_ACTIONS } from './constants/auditLogs.jsx';
|
||||||
import {formatDateForApi} from '../../../utils/formatDate.js'
|
|
||||||
|
|
||||||
const AuditLogsPage = () => {
|
const AuditLogsPage = () => {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
@ -72,13 +71,12 @@ const AuditLogsPage = () => {
|
|||||||
user_id: selectedUser?.id,
|
user_id: selectedUser?.id,
|
||||||
org_unit_id: selectedSsp?.id,
|
org_unit_id: selectedSsp?.id,
|
||||||
event: selectedEventType?.id,
|
event: selectedEventType?.id,
|
||||||
// date_from: dateFrom ? new Date(dateFrom.getTime() - (dateFrom.getTimezoneOffset() * 60 * 1000)).toISOString().slice(0, 10): undefined,
|
date_from: dateFrom ? dateFrom.toISOString().slice(0, 10) : undefined,
|
||||||
// date_to: dateTo ? new Date(dateTo.getTime() - (dateTo.getTimezoneOffset() * 60 * 1000)).toISOString().slice(0, 10) : undefined,
|
date_to: dateTo ? dateTo.toISOString().slice(0, 10) : undefined,
|
||||||
date_from: dateFrom ? formatDateForApi(dateFrom) : undefined,
|
|
||||||
date_to: dateTo ? formatDateForApi(dateTo) : undefined,
|
|
||||||
task_id: taskId ? taskId : undefined,
|
task_id: taskId ? taskId : undefined,
|
||||||
form_id: formId ? formId : undefined,
|
form_id: formId ? formId : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const data = await AuditLogsApi.getAll(params);
|
const data = await AuditLogsApi.getAll(params);
|
||||||
setLogs(data.result ?? []);
|
setLogs(data.result ?? []);
|
||||||
setTotalCount(data.count ?? 0);
|
setTotalCount(data.count ?? 0);
|
||||||
|
|||||||
@ -129,11 +129,7 @@ const AuditLogsTable = ({
|
|||||||
header: 'Контекст',
|
header: 'Контекст',
|
||||||
size: 160,
|
size: 160,
|
||||||
Cell: ({ row }) => (
|
Cell: ({ row }) => (
|
||||||
<Typography>
|
<Typography>{`${ENTITY_RUSSIAN_NAMES[row.original.entity.toUpperCase()][1]} #${row.original.entity_id}`}</Typography>
|
||||||
{ENTITY_RUSSIAN_NAMES[row.original.entity?.toUpperCase()]?.[1] != null
|
|
||||||
? `${ENTITY_RUSSIAN_NAMES[row.original.entity?.toUpperCase()]?.[1]} #${row.original.entity_id}`
|
|
||||||
: '-'}
|
|
||||||
</Typography>
|
|
||||||
),
|
),
|
||||||
enableColumnActions: false,
|
enableColumnActions: false,
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
|
|||||||
@ -13,8 +13,6 @@ export const DEFAULT_ACTIONS = {
|
|||||||
VSP_CREATE: 'Создание ВСП',
|
VSP_CREATE: 'Создание ВСП',
|
||||||
VSP_UPDATE: 'Обновление ВСП',
|
VSP_UPDATE: 'Обновление ВСП',
|
||||||
DELETE_VSP: 'Удаление ВСП',
|
DELETE_VSP: 'Удаление ВСП',
|
||||||
PROJECT_CREATE: 'Создание проекта',
|
|
||||||
PROJECT_DELETE: 'Удаление проекта',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ENTITY_RUSSIAN_NAMES = {
|
export const ENTITY_RUSSIAN_NAMES = {
|
||||||
|
|||||||
@ -76,7 +76,7 @@ export default function ProjectsPage() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
if (user.role_id == ROLES_NAME_ID.admin) {
|
if (user.role_id == ROLES_NAME_ID.admin) {
|
||||||
SspApi.getAll({is_active: true}).then(data => {
|
SspApi.getAll().then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
setProjectData(prev => ({
|
setProjectData(prev => ({
|
||||||
...prev, 'ССП/РФ': {
|
...prev, 'ССП/РФ': {
|
||||||
|
|||||||
@ -5,22 +5,3 @@ export const formatDate = (dateString) => {
|
|||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
return `${day}.${month}.${year}`;
|
return `${day}.${month}.${year}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const formatDateForApi = (value) => {
|
|
||||||
if (!value) return '';
|
|
||||||
const d = new Date(value);
|
|
||||||
const year = String(d.getFullYear()).padStart(4, '0');
|
|
||||||
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
||||||
const day = String(d.getDate()).padStart(2, '0');
|
|
||||||
return `${year}-${month}-${day}`;
|
|
||||||
};
|
|
||||||
export const isPastDate = (value) => {
|
|
||||||
if (!value) return false;
|
|
||||||
const date = new Date(value);
|
|
||||||
date.setHours(0, 0, 0, 0);
|
|
||||||
const today = new Date();
|
|
||||||
today.setHours(0, 0, 0, 0);
|
|
||||||
return date < today;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user