diff --git a/api/alembic/versions/0011_form3_log.py b/api/alembic/versions/0011_form3_log.py new file mode 100644 index 0000000..175abc9 --- /dev/null +++ b/api/alembic/versions/0011_form3_log.py @@ -0,0 +1,84 @@ +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 diff --git a/api/alembic/versions/sql/0011_form3_log.sql b/api/alembic/versions/sql/0011_form3_log.sql new file mode 100644 index 0000000..6a5a4ab --- /dev/null +++ b/api/alembic/versions/sql/0011_form3_log.sql @@ -0,0 +1,172 @@ +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$ +; \ No newline at end of file diff --git a/api/src/api/v1/vsp.py b/api/src/api/v1/vsp.py index 4ab76da..0a2e0b3 100644 --- a/api/src/api/v1/vsp.py +++ b/api/src/api/v1/vsp.py @@ -46,6 +46,7 @@ async def get_vsp( staff_count_min=staff_count_min, staff_count_max=staff_count_max, load_org_unit=True, + is_active=True, with_count=True, ) return BaseListResponse( diff --git a/api/src/db/models/vsp.py b/api/src/db/models/vsp.py index a907fa5..84aa9a9 100644 --- a/api/src/db/models/vsp.py +++ b/api/src/db/models/vsp.py @@ -33,6 +33,7 @@ class Vsp(Base): total_area: Mapped[float | None] = mapped_column(Numeric) closed_at: Mapped[date | None] = mapped_column(Date) is_active: Mapped[bool] = mapped_column(Boolean, default=True) + #ToDo: а нужно ли нам и is_active и is_deleted? is_deleted: Mapped[bool] = mapped_column(Boolean, default=False) system_code: Mapped[str | None] = mapped_column(String(100), nullable=True) created_at: Mapped[datetime] = mapped_column( diff --git a/api/src/repository/vsp_repository.py b/api/src/repository/vsp_repository.py index a6b1b94..4858f04 100644 --- a/api/src/repository/vsp_repository.py +++ b/api/src/repository/vsp_repository.py @@ -14,8 +14,14 @@ class VSPRepository: def __init__(self, db: AsyncSession): self.db = db - async def get(self, vsp_id: int, is_active: bool | None = None, load_org_unit: bool = False) -> Optional[Vsp]: - queries = [Vsp.id == vsp_id, Vsp.is_deleted.is_(False)] + async def get( + self, + 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: queries.append(Vsp.is_active == is_active) if load_org_unit: diff --git a/api/src/services/export_service/export_service.py b/api/src/services/export_service/export_service.py index bbf33cf..c198455 100644 --- a/api/src/services/export_service/export_service.py +++ b/api/src/services/export_service/export_service.py @@ -49,6 +49,7 @@ SHEET_NAMES = { 'AHR_SECURITY': 'АХР Безопасность', 'AHR_UTILITY': 'АХР Коммунальные услуги', 'SMETA': 'Смета', + 'STRUCTURE': 'Структура', } SKIP_SHEETS = ('OTCH9F',) @@ -327,8 +328,8 @@ class ExportService: ("Форма ID", form.id), ("Тип формы", form.form_type_code), ("Год", form.year), - ("Org Unit ID", form.org_unit_id), - ("Org Unit", form.org_unit.title if form.org_unit else None), + ("ID ССП.РФ", form.org_unit_id), + ("ССП/РФ", form.org_unit.title if form.org_unit else None), ("Экспортировано", datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")), ] for row_idx, (key, value) in enumerate(metadata_rows, start=1): diff --git a/api/src/services/vsp_service.py b/api/src/services/vsp_service.py index fe577e0..df6d30c 100644 --- a/api/src/services/vsp_service.py +++ b/api/src/services/vsp_service.py @@ -36,7 +36,11 @@ class VSPService: self.user_repo = UserRepository(db) async def get(self, vsp_id: int, user: AppUser, load_org_unit: bool = False) -> Optional[Vsp]: - vsp = await self.vsp_repo.get(vsp_id=vsp_id, load_org_unit=load_org_unit) + vsp = await self.vsp_repo.get( + vsp_id=vsp_id, + load_org_unit=load_org_unit, + with_deleted=user.role_id == UserRoleEnum.ADMIN, + ) if not vsp: return None if user.role_id == UserRoleEnum.ADMIN: @@ -173,7 +177,7 @@ class VSPService: placement_type=placement_type, staff_count_min=staff_count_min, staff_count_max=staff_count_max, - is_active=None, + is_active=True, load_org_unit=True, ) diff --git a/api/tests/integration/test_vsp.py b/api/tests/integration/test_vsp.py index 3d926a6..cdbd118 100644 --- a/api/tests/integration/test_vsp.py +++ b/api/tests/integration/test_vsp.py @@ -58,8 +58,8 @@ def test_vsp_dropdown_with_filter(client, admin_tokens, auth_headers, params, co assert len(payload["result"]) == count -def test_vsp_get_by_id(client, admin_tokens, auth_headers): - response = client.get("/api/v1/dict/info/1", headers=auth_headers(admin_tokens)) +def test_vsp_get_by_id(client, isp_tokens, auth_headers): + response = client.get("/api/v1/dict/info/1", headers=auth_headers(isp_tokens)) assert response.status_code == 200 payload = response.json() assert "result" in payload diff --git a/api/tests/sqls/fixture.sql b/api/tests/sqls/fixture.sql index da9efeb..154004d 100644 --- a/api/tests/sqls/fixture.sql +++ b/api/tests/sqls/fixture.sql @@ -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'); 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',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','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',91,'3434',NULL,'2','примечание','567','2027-07-01'); + (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), + (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'), + (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'); 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 diff --git a/web/src/components/Stages/AddStagesModal.jsx b/web/src/components/Stages/AddStagesModal.jsx index cdcec58..7689571 100644 --- a/web/src/components/Stages/AddStagesModal.jsx +++ b/web/src/components/Stages/AddStagesModal.jsx @@ -22,7 +22,7 @@ import { CalendarInput } from './Calendar/CalendarInput'; import { Cancel } from '../common/icons/icons'; import { getStageStatus, getStatusColors } from './StagesTable/utils'; import { StageStatusChip } from './StageStatusChip'; -import { formatDateForApi } from './utils'; +import { formatDateForApi } from '../../utils/formatDate'; import CollapsibleTree from '../common/CollapsibleTree'; import { collectLeafColumns } from '../RealtimeTable/utils/columnUtils'; import { STAGE_ROLE_RUSSIAN_NAME } from '../../constants/constants'; diff --git a/web/src/components/Stages/utils.js b/web/src/components/Stages/utils.js index 4f0a434..3c496db 100644 --- a/web/src/components/Stages/utils.js +++ b/web/src/components/Stages/utils.js @@ -1,11 +1,3 @@ -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) => { if (!value) return false; const date = new Date(value); @@ -13,5 +5,4 @@ export const isPastDate = (value) => { const today = new Date(); today.setHours(0, 0, 0, 0); return date < today; -}; - +}; \ No newline at end of file diff --git a/web/src/pages/AdminPanel/AuditLogsPage/AuditLogsPage.jsx b/web/src/pages/AdminPanel/AuditLogsPage/AuditLogsPage.jsx index cc4a9ec..782002d 100644 --- a/web/src/pages/AdminPanel/AuditLogsPage/AuditLogsPage.jsx +++ b/web/src/pages/AdminPanel/AuditLogsPage/AuditLogsPage.jsx @@ -12,6 +12,7 @@ import { UsersApi } from '../../../api/users'; import AuditLogsFilters from './components/AuditLogsFilters'; import AuditLogsTable from './components/AuditLogsTable'; import { DEFAULT_ACTIONS } from './constants/auditLogs.jsx'; +import {formatDateForApi} from '../../../utils/formatDate.js' const AuditLogsPage = () => { const { user } = useAuth(); @@ -71,12 +72,13 @@ const AuditLogsPage = () => { user_id: selectedUser?.id, org_unit_id: selectedSsp?.id, event: selectedEventType?.id, - date_from: dateFrom ? dateFrom.toISOString().slice(0, 10) : undefined, - date_to: dateTo ? dateTo.toISOString().slice(0, 10) : undefined, + // date_from: dateFrom ? new Date(dateFrom.getTime() - (dateFrom.getTimezoneOffset() * 60 * 1000)).toISOString().slice(0, 10): undefined, + // date_to: dateTo ? new Date(dateTo.getTime() - (dateTo.getTimezoneOffset() * 60 * 1000)).toISOString().slice(0, 10) : undefined, + date_from: dateFrom ? formatDateForApi(dateFrom) : undefined, + date_to: dateTo ? formatDateForApi(dateTo) : undefined, task_id: taskId ? taskId : undefined, form_id: formId ? formId : undefined, }; - const data = await AuditLogsApi.getAll(params); setLogs(data.result ?? []); setTotalCount(data.count ?? 0); diff --git a/web/src/pages/ProjectsPage/ProjectsPage.jsx b/web/src/pages/ProjectsPage/ProjectsPage.jsx index 4b126b2..f9e6df7 100644 --- a/web/src/pages/ProjectsPage/ProjectsPage.jsx +++ b/web/src/pages/ProjectsPage/ProjectsPage.jsx @@ -76,7 +76,7 @@ export default function ProjectsPage() { useEffect(() => { if (!user) return; if (user.role_id == ROLES_NAME_ID.admin) { - SspApi.getAll().then(data => { + SspApi.getAll({is_active: true}).then(data => { if (data.success) { setProjectData(prev => ({ ...prev, 'ССП/РФ': { diff --git a/web/src/utils/formatDate.js b/web/src/utils/formatDate.js index 5e78b22..4edbce6 100644 --- a/web/src/utils/formatDate.js +++ b/web/src/utils/formatDate.js @@ -5,3 +5,22 @@ export const formatDate = (dateString) => { const year = date.getFullYear(); 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; +}; +