From a6531724182206f043cc4116d217acba83236cc6 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Tue, 27 Oct 2026 22:54:56 +0400 Subject: [PATCH 1/2] =?UTF-8?q?fix(web):=20=C2=AB=D0=9D=D0=B0=D0=B7=D0=B0?= =?UTF-8?q?=D0=B4=C2=BB=20=D0=B8=D0=B7=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=B0=20=D0=B2=D0=BE=D0=B7=D0=B2=D1=80=D0=B0?= =?UTF-8?q?=D1=89=D0=B0=D0=B5=D1=82=20=D0=BF=D0=B0=D0=BD=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=20=C2=AB=D0=9F=D0=B5=D1=80=D0=B2=D0=B8=D1=87=D0=BD=D1=8B=D0=B5?= =?UTF-8?q?=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D1=8B=C2=BB?= =?UTF-8?q?=20=D0=BE=D1=80=D0=B3=D0=B0=D0=BD=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20(#197,=20AURORA-1682)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Открытая панель «Первичные документы» живёт в адресе экрана консолидации (?entity= рядом с ?version=), открытие и закрытие заменяют запись истории. «Назад» из документа, открытого из панели, возвращает на консолидацию с той же версией и панелью той же организации; переключатель документа (AURORA-1692) и прямая ссылка — как прежде. - utils/consolidations.ts: panelEntity / withPanelEntity + тест; - ConsolidationsList: панель открыта по ?entity=, когда ЮЛ из адреса уже в сторе (без мелькания прежнего ЮЛ); - ConsoleLegalEntities: onDrawerOpen(id ЮЛ строки); - change back-to-primary-docs: MODIFIED document-review-ui «Переключатель документа», ADDED primary-files-ui «Открытая панель в адресе экрана консолидации». Co-Authored-By: Claude Opus 5.5 --- .../back-to-primary-docs/.openspec.yaml | 2 + .../changes/back-to-primary-docs/proposal.md | 38 +++++++++++++++++++ .../specs/document-review-ui/spec.md | 20 ++++++++++ .../specs/primary-files-ui/spec.md | 16 ++++++++ .../changes/back-to-primary-docs/tasks.md | 11 ++++++ web/src/layouts/ConsolidationsList.tsx | 29 ++++++++------ web/src/utils/consolidations.test.ts | 20 ++++++++++ web/src/utils/consolidations.ts | 17 +++++++++ .../ConsoleLegalEntities.tsx | 7 ++-- 9 files changed, 144 insertions(+), 16 deletions(-) create mode 100644 openspec/changes/back-to-primary-docs/.openspec.yaml create mode 100644 openspec/changes/back-to-primary-docs/proposal.md create mode 100644 openspec/changes/back-to-primary-docs/specs/document-review-ui/spec.md create mode 100644 openspec/changes/back-to-primary-docs/specs/primary-files-ui/spec.md create mode 100644 openspec/changes/back-to-primary-docs/tasks.md diff --git a/openspec/changes/back-to-primary-docs/.openspec.yaml b/openspec/changes/back-to-primary-docs/.openspec.yaml new file mode 100644 index 0000000..97e3642 --- /dev/null +++ b/openspec/changes/back-to-primary-docs/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-27 diff --git a/openspec/changes/back-to-primary-docs/proposal.md b/openspec/changes/back-to-primary-docs/proposal.md new file mode 100644 index 0000000..d885a58 --- /dev/null +++ b/openspec/changes/back-to-primary-docs/proposal.md @@ -0,0 +1,38 @@ +## Why + +AURORA-1682 (замечание тестирования 16.10.2026, п. 1; чек-лист Циликова 23.10.2026, строка 9): аналитик открывает документ +из панели «Первичные документы» организации, проверяет его и нажимает «Назад» — открывается карточка консолидации без +панели, и для следующего документа панель приходится открывать заново. Панель открывалась локальным состоянием страницы +консолидации (`drawerOpen`), а «Назад» (`navigate(-1)`) монтирует страницу заново. + +## What Changes + +Репозиторий — только arm-core (`web/`), эндпоинты без изменений. + +- Открытая панель «Первичные документы» хранится в адресе экрана консолидации параметром `?entity=` + рядом с `?version=`; открытие и закрытие панели заменяют текущую запись истории (как выбор версии). Расчёт параметров — + `panelEntity` / `withPanelEntity` (`web/src/utils/consolidations.ts`), закреплён тестом. +- «Назад» из документа, открытого из панели, возвращает на адрес с `?entity=` — панель той же организации открыта. + Переключатель документа (AURORA-1692) и прямая ссылка ведут себя как прежде. +- Дельты: `document-review-ui` — MODIFIED «Переключатель документа» (сценарий «Назад после перебора документов»); + `primary-files-ui` — ADDED «Открытая панель в адресе экрана консолидации». + +## Non-goals + +- Режим «Все документы организации» в адрес не попадает: после «Назад» панель открывается столом консолидации, как при + обычном открытии. +- Изменения бэкенда и DBML. + +## Capabilities + +### New Capabilities + +### Modified Capabilities + +- `document-review-ui`: MODIFIED «Переключатель документа». +- `primary-files-ui`: ADDED «Открытая панель в адресе экрана консолидации». + +## Impact + +`web/src/layouts/ConsolidationsList.tsx`, `web/src/widgets/ConsoleLegalEntities/ConsoleLegalEntities.tsx`, +`web/src/utils/consolidations.ts` (+ тест). Ссылки на экран консолидации с `?entity=` открывают панель этой организации. diff --git a/openspec/changes/back-to-primary-docs/specs/document-review-ui/spec.md b/openspec/changes/back-to-primary-docs/specs/document-review-ui/spec.md new file mode 100644 index 0000000..8db3690 --- /dev/null +++ b/openspec/changes/back-to-primary-docs/specs/document-review-ui/spec.md @@ -0,0 +1,20 @@ +## MODIFIED Requirements + +### Requirement: Переключатель документа +В шапке экрана документа SHALL быть выпадающий список «Документ» с документами файлов того же ЮЛ в той же консолидации, +что и файл открытого документа (файл вне консолидации — все файлы ЮЛ): у пункта имя файла, вторая строка (тип · формат · +дата) и статус; пункты, которые нельзя открыть, неактивны. Выбор пункта SHALL открывать `/documents/:document_id` +выбранного документа заменой текущей записи истории браузера, без новой записи: кнопка «Назад» экрана и браузера +возвращает на экран, с которого открыт первый документ, а не к предыдущему выбранному. Если первый документ открыт из +панели «Первичные документы» консолидации, «Назад» SHALL открывать экран консолидации с этой панелью той же организации. + +#### Scenario: Назад после перебора документов +- **WHEN** аналитик открыл документ из панели «Первичные документы» организации в карточке консолидации, выбрал в списке + «Документ» подряд ещё два документа и нажал «Назад» +- **THEN** открыт экран консолидации с той же выбранной версией и открытой панелью «Первичные документы» той же + организации + +#### Scenario: Документ открыт прямой ссылкой +- **WHEN** документ открыт прямой ссылкой в новой вкладке, аналитик выбрал в списке «Документ» другой документ и нажал + «Назад» экрана +- **THEN** открыт список групп компаний `/groups` diff --git a/openspec/changes/back-to-primary-docs/specs/primary-files-ui/spec.md b/openspec/changes/back-to-primary-docs/specs/primary-files-ui/spec.md new file mode 100644 index 0000000..5f94acf --- /dev/null +++ b/openspec/changes/back-to-primary-docs/specs/primary-files-ui/spec.md @@ -0,0 +1,16 @@ +## ADDED Requirements + +### Requirement: Открытая панель в адресе экрана консолидации +Открытая панель «Первичные документы» SHALL храниться в адресе экрана консолидации `/groups/:group_id` параметром +`?entity=` рядом с `?version=`. Клик по организации в панели «Организации» SHALL добавлять параметр, +закрытие панели — убирать его; оба перехода SHALL заменять текущую запись истории браузера, без новой записи. Адрес с +`?entity=` SHALL открывать панель этой организации. Режим «Все документы организации» в адрес не входит: панель, +открытая по адресу, показывает стол консолидации. + +#### Scenario: Возврат из документа +- **WHEN** аналитик открыл панель организации, перешёл по ссылке документа и вернулся «Назад» +- **THEN** открыт экран консолидации с той же версией и панелью «Первичные документы» той же организации + +#### Scenario: Закрытая панель не возвращается +- **WHEN** аналитик открыл панель организации, закрыл её и нажал «Назад» браузера +- **THEN** открыт экран, с которого пришли на экран консолидации, панель не открывается снова diff --git a/openspec/changes/back-to-primary-docs/tasks.md b/openspec/changes/back-to-primary-docs/tasks.md new file mode 100644 index 0000000..800a25e --- /dev/null +++ b/openspec/changes/back-to-primary-docs/tasks.md @@ -0,0 +1,11 @@ +## 1. «Назад» на панель «Первичные документы» (AURORA-1682, RSHB/arm-core#197) + +- [x] 1.1 `web/src/utils/consolidations.ts`: `panelEntity`, `withPanelEntity` — ЮЛ панели в `?entity=`; тест в + `consolidations.test.ts`. +- [x] 1.2 `web/src/layouts/ConsolidationsList.tsx`: панель открыта по `?entity=` (когда ЮЛ из адреса уже в сторе), + открытие и закрытие — `setSearchParams(…, { replace: true })`; `ConsoleLegalEntities` передаёт id ЮЛ строки. +- [x] 1.3 Дельты `document-review-ui` (MODIFIED «Переключатель документа») и `primary-files-ui` (ADDED «Открытая панель + в адресе экрана консолидации»). +- [x] 1.4 Проверка в браузере: панель организации → документ → переключить 2–3 документа → «Назад» — карточка + консолидации с панелью той же организации; закрыть панель → «Назад» браузера не открывает её снова; сверка с макетом + [39], [41]. diff --git a/web/src/layouts/ConsolidationsList.tsx b/web/src/layouts/ConsolidationsList.tsx index 52a952b..4a26eee 100644 --- a/web/src/layouts/ConsolidationsList.tsx +++ b/web/src/layouts/ConsolidationsList.tsx @@ -4,8 +4,9 @@ import { useParams, useSearchParams } from 'react-router-dom'; import { ApiError } from '../api/client'; import PageTitle from '../components/PageTitle'; import { fetchOneCompanyGroup } from '../services/api/directories/company_groups'; -import { setSelectedVersion } from '../store/slices/consolidations/consolidationsSlice'; -import { useAppDispatch } from '../store/store'; +import { setSelectedLegalEntity, setSelectedVersion } from '../store/slices/consolidations/consolidationsSlice'; +import { useAppDispatch, useAppSelector } from '../store/store'; +import { panelEntity, withPanelEntity } from '../utils/consolidations'; import { ConsoleLegalEntitiesTable } from '../widgets/ConsoleLegalEntities/ConsoleLegalEntities'; import { ConsoleTableWidget } from '../widgets/ConsoleTable/ConsoleTable'; import { DocumentsDrawer } from '../widgets/FilesDrawer/FilesDrawer'; @@ -17,12 +18,16 @@ function errorText(e: unknown): string { } export function ConsolidationPage() { - const [drawerOpen, setDrawerOpen] = useState(false); const [reloadKey, setReloadKey] = useState(0); const { group_id } = useParams(); - const [searchParams] = useSearchParams(); + const [searchParams, setSearchParams] = useSearchParams(); const versionParam = searchParams.get('version'); + // открытая панель «Первичные документы» — в адресе (?entity=): «Назад» из документа возвращает с той же панелью + const entityParam = panelEntity(searchParams); + // панель открывается, когда ЮЛ из адреса уже в сторе: иначе на первом рендере она показала бы прежнее ЮЛ + const panelOpen = useAppSelector((s) => entityParam !== null && s.consolidations.current_legal_entity_id === entityParam); const dispatch = useAppDispatch(); + const setPanelEntity = (entityId: string | null) => setSearchParams((params) => withPanelEntity(params, entityId), { replace: true }); const [comGroupName, setComGroupName] = useState(''); const [locationPath, setLocationPath] = useState('Отчётность>'); @@ -48,6 +53,11 @@ export function ConsolidationPage() { dispatch(setSelectedVersion(versionParam)); }, [dispatch, versionParam]); + // ЮЛ панели следует за ?entity=; закрытие панели выбор не сбрасывает (панель скрыта, ЮЛ без запроса не перечитывается) + useEffect(() => { + if (entityParam) dispatch(setSelectedLegalEntity(entityParam)); + }, [dispatch, entityParam]); + // уход со страницы сбрасывает выбор (при смене группы ?version= пропадает из URL — выбор сбросит эффект выше) useEffect(() => { return () => { @@ -97,18 +107,13 @@ export function ConsolidationPage() { flexDirection: 'column', minHeight: 0, }}> - { - setDrawerOpen(true); - }} - /> + { - setDrawerOpen(false); + setPanelEntity(null); setReloadKey((k) => k + 1); }} /> diff --git a/web/src/utils/consolidations.test.ts b/web/src/utils/consolidations.test.ts index 5a56c7d..f127262 100644 --- a/web/src/utils/consolidations.test.ts +++ b/web/src/utils/consolidations.test.ts @@ -12,9 +12,11 @@ import { isValidReason, manualTransitions, needsUnmappedWarning, + panelEntity, parseInWorkOnly, reportTabNavigation, versionLabel, + withPanelEntity, } from './consolidations'; const lock = (userId: string): LockOut => ({ @@ -143,3 +145,21 @@ describe('вкладки отчёта (AURORA-1851)', () => { }); }); }); + +describe('панель «Первичные документы» в адресе (AURORA-1682)', () => { + it('открытие добавляет ?entity= к выбранной версии, исходные параметры не меняются', () => { + const params = new URLSearchParams('version=v1&in_work_only=true'); + const next = withPanelEntity(params, 'le1'); + expect(next.toString()).toBe('version=v1&in_work_only=true&entity=le1'); + expect(params.toString()).toBe('version=v1&in_work_only=true'); + }); + + it('закрытие убирает ?entity=, версия остаётся', () => { + expect(withPanelEntity(new URLSearchParams('version=v1&entity=le1'), null).toString()).toBe('version=v1'); + }); + + it('организация панели читается из адреса', () => { + expect(panelEntity(new URLSearchParams('version=v1&entity=le1'))).toBe('le1'); + expect(panelEntity(new URLSearchParams('version=v1'))).toBeNull(); + }); +}); diff --git a/web/src/utils/consolidations.ts b/web/src/utils/consolidations.ts index 77b3854..a556be6 100644 --- a/web/src/utils/consolidations.ts +++ b/web/src/utils/consolidations.ts @@ -100,3 +100,20 @@ export const reportTabNavigation = (pathname: string, activeKey: string, newKey: to: pathname.replace(new RegExp(`/${activeKey}/?$`), `/${newKey}`), options: { replace: true }, }); + +/** Параметр адреса экрана консолидации с ЮЛ открытой панели «Первичные документы» (AURORA-1682). */ +const PANEL_ENTITY_PARAM = 'entity'; + +/** ЮЛ открытой панели «Первичные документы» из адреса; панель закрыта — `null`. */ +export const panelEntity = (params: URLSearchParams): string | null => params.get(PANEL_ENTITY_PARAM); + +/** + * Параметры адреса с открытой (`entityId`) или закрытой (`null`) панелью «Первичные документы»; исходные не меняются. + * Панель живёт в адресе, как выбранная версия: «Назад» из документа возвращает на консолидацию с той же панелью. + */ +export function withPanelEntity(params: URLSearchParams, entityId: string | null): URLSearchParams { + const next = new URLSearchParams(params); + if (entityId === null) next.delete(PANEL_ENTITY_PARAM); + else next.set(PANEL_ENTITY_PARAM, entityId); + return next; +} diff --git a/web/src/widgets/ConsoleLegalEntities/ConsoleLegalEntities.tsx b/web/src/widgets/ConsoleLegalEntities/ConsoleLegalEntities.tsx index 6fabf87..cde77fa 100644 --- a/web/src/widgets/ConsoleLegalEntities/ConsoleLegalEntities.tsx +++ b/web/src/widgets/ConsoleLegalEntities/ConsoleLegalEntities.tsx @@ -6,7 +6,7 @@ import { ColumnSettings, DataTable, useFilteredRows, useVisibleColumns } from '. import { useTableFilters } from '../../components/GenericTable/useTableFilters'; import { downloadChecklistCsv } from '../../services/api/consolidations'; import { getVersionCard } from '../../services/api/consolidations'; -import { setSelectedLegalEntity, setSelectedVersion } from '../../store/slices/consolidations/consolidationsSlice'; +import { setSelectedVersion } from '../../store/slices/consolidations/consolidationsSlice'; import { useAppDispatch, useAppSelector } from '../../store/store'; import type { IVersionCard, MemberRow } from '../../types/consolidations/version_members'; import { saveBlob } from '../../utils/download'; @@ -15,7 +15,7 @@ import { membersConfig } from './config'; import { normalizeData } from './normalize'; import { checklistFilename, responsibleName } from './panelInfo'; -export function ConsoleLegalEntitiesTable({ onDrawerOpen, reloadKey }: { onDrawerOpen: () => void; reloadKey: number }) { +export function ConsoleLegalEntitiesTable({ onDrawerOpen, reloadKey }: { onDrawerOpen: (entityId: string) => void; reloadKey: number }) { const dispatch = useAppDispatch(); const [loading, setLoading] = useState(false); const consolidation_id = useAppSelector((s) => s.consolidations.current_version_id); @@ -60,8 +60,7 @@ export function ConsoleLegalEntitiesTable({ onDrawerOpen, reloadKey }: { onDrawe const onRow = (row: MemberRow) => ({ onClick: () => { dispatch(setSelectedVersion(row.version_id)); - dispatch(setSelectedLegalEntity(row.id)); - onDrawerOpen(); + onDrawerOpen(row.id); }, }); if (consolidationData === null) { From c60a53acc86c1034287c1bf3cc068252423ad491 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Tue, 27 Oct 2026 23:05:55 +0400 Subject: [PATCH 2/2] =?UTF-8?q?docs(openspec):=20back-to-primary-docs=20?= =?UTF-8?q?=E2=80=94=20=D1=80=D0=B5=D1=88=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B1?= =?UTF-8?q?=D0=B5=D0=B7=20=D0=BE=D0=B1=D1=81=D1=83=D0=B6=D0=B4=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20(#197)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../changes/back-to-primary-docs/design.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 openspec/changes/back-to-primary-docs/design.md diff --git a/openspec/changes/back-to-primary-docs/design.md b/openspec/changes/back-to-primary-docs/design.md new file mode 100644 index 0000000..899cc10 --- /dev/null +++ b/openspec/changes/back-to-primary-docs/design.md @@ -0,0 +1,20 @@ +## Context + +AURORA-1682, RSHB/arm-core#197. Панель «Первичные документы» открывалась локальным состоянием `ConsolidationPage` +(`drawerOpen`), ЮЛ панели — в сторе (`current_legal_entity_id`). «Назад» из документа (`navigate(-1)`) монтирует страницу +консолидации заново, и панель закрыта. + +## Decisions made without discussion + +- **2026-10-27, R-1.** Открытая панель хранится в адресе (`?entity=`), а не в сторе или sessionStorage. + Обоснование: выбранная версия уже живёт в `?version=` (ConsoleTable), история браузера сама возвращает адрес с панелью, + а ссылку с панелью можно переслать. Цена ошибки: если параметр не нужен в адресе — заменить на состояние в сторе + (правка в `ConsolidationsList.tsx` и двух функциях `utils/consolidations.ts`). +- **2026-10-27, R-2.** Открытие и закрытие панели заменяют запись истории (`replace: true`), как выбор версии. Обоснование: + иначе «Назад» браузера после закрытия панели снова открывал бы её. Цена ошибки: низкая — одна опция `setSearchParams`. +- **2026-10-27, R-3.** Режим «Все документы организации» в адрес не входит: после «Назад» панель открыта столом + консолидации. Обоснование: в задаче речь о возврате к панели организации; переключатель и без того сбрасывается при каждом + открытии панели. Цена ошибки: низкая — добавить второй параметр адреса. +- **2026-10-27, R-4.** Панель открывается, только когда ЮЛ из адреса уже в сторе (`current_legal_entity_id === entity`). + Обоснование: стор обновляется эффектом после первого рендера — без условия панель на мгновение показала бы прежнее ЮЛ и + запросила бы его файлы. Цена ошибки: нет (поведение для пользователя то же).