docs: README с демо-сценарием, скрипт подготовки данных, CLAUDE.md и ARCHITECTURE.md (#3)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
This commit is contained in:
Andrey Epifancev 2026-10-08 01:36:54 +04:00
parent 2c7bdf19b0
commit ebae9edd74
4 changed files with 104 additions and 6 deletions

View File

@ -11,15 +11,16 @@ SPA поверх API arm-core (`/api/v1`). Своих данных и бизне
| `src/auth/` | профиль `/me`, dev-вход, права для меню | `api/` | | `src/auth/` | профиль `/me`, dev-вход, права для меню | `api/` |
| `src/services/api/` | вызовы ресурсов arm-core | `api/`, `types/` | | `src/services/api/` | вызовы ресурсов arm-core | `api/`, `types/` |
| `src/store/` | состояние экранов (Redux Toolkit) | `services/api/` | | `src/store/` | состояние экранов (Redux Toolkit) | `services/api/` |
| `src/utils/` | чистые функции: деньги, подписи, скачивание | `types/` |
| `src/types/` | DTO API (повторяют `arm-core/api/src/schemas/`) | — | | `src/types/` | DTO API (повторяют `arm-core/api/src/schemas/`) | — |
| `src/components/` | примитивы UI (таблица, теги, заголовок) | antd | | `src/components/` | примитивы UI (таблица, теги, заголовок) | antd |
| `src/widgets/` | блоки экранов | `components/`, `store/`, `services/api/` | | `src/widgets/` | блоки экранов (`DocumentReview` — проверка документа со сканом, `ConsolidatedOsv` — сводная ОСВ, `FilesDrawer`) | `components/`, `store/`, `services/api/`, `utils/` |
| `src/layouts/` | страницы-маршруты | `widgets/` | | `src/layouts/` | страницы-маршруты | `widgets/` |
## Границы ## Границы
- Сетевые запросы — только через `src/api/client.ts`; `fetch` напрямую не вызывается. - Сетевые запросы — только через `src/api/client.ts`; `fetch` напрямую не вызывается.
- `components/` не знают про store и API; `types/` ни от чего не зависят. - `components/` не знают про store и API; `types/` ни от чего не зависят.
- `services/api/mocks.ts` не импортируется из рабочих путей. - `utils/` не импортирует React, store и API.
## Открытые вопросы ## Открытые вопросы
- Судьба `arm-core/web` (дубль основы этого репозитория) — решает владелец. - Судьба `arm-core/web` (дубль основы этого репозитория) — решает владелец.

View File

@ -34,20 +34,23 @@ SPA кредитного аналитика АРМ КА: справочники
- `src/api/` — единый HTTP-клиент `apiFetch` / `api.get|post|put|patch|del`: префикс `${API_URL}/api/v1`, Bearer-токен, - `src/api/` — единый HTTP-клиент `apiFetch` / `api.get|post|put|patch|del`: префикс `${API_URL}/api/v1`, Bearer-токен,
`ApiError {status, code, message}` по трём формам `detail`, событие `armka:unauthorized` на 401. **Все запросы — только `ApiError {status, code, message}` по трём формам `detail`, событие `armka:unauthorized` на 401. **Все запросы — только
через него** (прямой `fetch` теряет токен и разбор ошибок). через него** (прямой `fetch` теряет токен и разбор ошибок).
- `src/services/api/` — функции по ресурсам arm-core (consolidations, files, directories/…). - `src/api/client.ts` также даёт `apiFetchBlob(path) → {blob, filename}` для бинарных ответов (скан PDF, экспорт xlsx) с той же обработкой токена/401/ошибок.
- `src/services/api/` — функции по ресурсам arm-core (consolidations, files, documents, osv, directories/…).
- `src/utils/` — чистые функции без React/store/API: деньги (`money.ts`, суммы — строки), подписи статусов/типов/периодов (`labels.ts`), скачивание и имя файла (`download.ts`).
- `src/auth/` — `AuthGate` (профиль `/me`), `DevLogin` (`POST /dev/token`, только не в production), `token.ts`. - `src/auth/` — `AuthGate` (профиль `/me`), `DevLogin` (`POST /dev/token`, только не в production), `token.ts`.
- `src/store/` — Redux Toolkit: `store.ts`, слайсы `companyGroupsSlice`, `consolidations`, `files` (+ `thunks.ts`). - `src/store/` — Redux Toolkit: `store.ts`, слайсы `companyGroupsSlice`, `consolidations`, `files` (+ `thunks.ts`).
- `src/layouts/` — страницы (маршруты в `src/App.tsx`), `src/widgets/` — блоки экранов, `src/components/` — примитивы - `src/layouts/` — страницы (маршруты в `src/App.tsx`, в т.ч. `/documents/:document_id` и `/groups/:group_id/versions/:version_id/osv`), `src/widgets/` — блоки экранов (`DocumentReview` — проверка ББ/ОФР/ОСВ со сканом, `ConsolidatedOsv` — сводная ОСВ, `FilesDrawer` — файлы ЮЛ), `src/components/` — примитивы
(`GenericTable`, `TableTag`, `PageTitle`), `src/types/` — DTO API. (`GenericTable`, `TableTag`, `PageTitle`), `src/types/` — DTO API.
## Команды ## Команды
- `make install` — `npm install`. - `make install` — `npm install`.
- `make test` — vitest (`npm test`).
- `make check` — install + biome + `tsc --noEmit` + vitest + `rsbuild build`. Зелёным считается только всё вместе; **перед PR**, вывод — - `make check` — install + biome + `tsc --noEmit` + vitest + `rsbuild build`. Зелёным считается только всё вместе; **перед PR**, вывод —
в описание PR. в описание PR.
- `make start` — dev-сервер (`rsbuild dev`); бэкенд — локальный стенд arm-core по `../docs/dev/qa-guide.md`. - `make start` — dev-сервер (`rsbuild dev`) с proxy `/api` → `ARM_CORE_URL` (по умолчанию `http://localhost:8000`); бэкенд — локальный стенд arm-core по `../docs/dev/qa-guide.md`.
## Тесты ## Тесты
Unit-тесты — vitest (`npm test`, `make test`); дополнительно — `make check` и ручной прогон пути по стенду arm-core с фикстурами из `../fixtures`. Unit-тесты — vitest на чистых функциях (`src/**/*.test.ts`, `npm test` / `make test`); компоненты не тестируются — ручной демо-сценарий по стенду arm-core с фикстурами из `../fixtures` (README, `scripts/demo-prepare.sh`).
## Для владельца ## Для владельца
Пользователь слаб во фронте: на TS/React-изменения агент делает код-ревью сам и объясняет решения простым языком. Пользователь слаб во фронте: на TS/React-изменения агент делает код-ревью сам и объясняет решения простым языком.

44
README.md Normal file
View File

@ -0,0 +1,44 @@
# arm-web — фронт АРМ КА (демо-прототип)
SPA кредитного аналитика поверх API `arm-core` (`/api/v1`). Контекст системы — `../CLAUDE.md`, архитектура —
`ARCHITECTURE.md`, соглашения репозитория — `CLAUDE.md`.
## Запуск
1. `make install`.
2. Стенд arm-core — по `../docs/dev/qa-guide.md` §4 (ядро на `localhost:8000`, `DEBUG=true`, `make seed-dev`); для
распознавания PDF — профиль `services` (§6.8: tabular-parser, scan-gateway, scan-emulator).
3. `make start` — dev-сервер rsbuild. Запросы `/api` проксируются на `ARM_CORE_URL` (по умолчанию
`http://localhost:8000`), `REACT_APP_API_URL` в dev не нужен.
4. Вход — через dev-форму (`POST /dev/token`), например `ka_go@armka.local`.
## Проверка
`make check` — install, biome, `tsc --noEmit`, vitest, `rsbuild build`. Зелёным считается всё вместе.
## Демо-сценарий
Пользователь с правами на ГК «Русский бисквит» (`ka_go@armka.local`).
1. Группы → «Русский бисквит» → «Новая консолидация»: ГСК, 2024, Q3 → члены ГСК отмечены → «Создать» → открыта версия №1.
2. Загрузить квартальные ОСВ РБ и Счастливочной за 2024 Q1–Q3 (`../fixtures/osv/{rb,schastl}/2024-Q{1,2,3}/*`) и подтвердить
ОСВ-документы. Вручную — панель «Организации» → ЮЛ → «Файлы», подтверждение на экране документа; быстро — скрипт:
`scripts/demo-prepare.sh <version_id> [email]` (каталог фикстур — `FIXTURES_DIR`, по умолчанию `../../fixtures`
относительно репозитория). В конце скрипт печатает сводку.
3. Загрузить `../fixtures/ocr/aep_6.pdf` одному из ЮЛ → ББ и ОФР «Требуется проверка» → открыть ББ: скан слева, форма
справа, исправить ячейку, принять нарушения с комментарием (если есть), подтвердить.
4. «К отчётности» → сводная ОСВ; фильтр «Счастливочная» + «бисквит» → строка `60.01` (сальдо нач. Кт 4 454 151.93,
обороты Дт 128 058 143.65, Кт 125 950 450.77, сальдо кон. Кт 2 346 459.05, `GSK`); «Скачать xlsx».
Ожидаемая сводка на чистой версии (`qa-guide` §7.2): `строк: 1385 документов: 22 несопоставленных: 1381 пробелов: 7`.
Если на стенде раньше вручную сопоставляли контрагентов (K14), алиасы сохраняются между версиями, и
«несопоставленных» будет меньше (на стенде прогона — 1349); остальные цифры не меняются.
## Ограничения прототипа
- Нет блокировки версии («Взять в работу»), сопоставления контрагентов, экрана чек-листа, комментариев, удаления файлов,
смены периметра и ручных переходов статусов.
- Нет правки строк ОСВ (в API нет), картинок страниц и координат ячеек в скане (в API нет).
- Нет генерации типов из OpenAPI и автотестов компонентов (vitest — только чистые функции).
- Нет сборки под контур и переноса в `arm-core/web` — отдельной задачей после демо.
- Изменений arm-core / scan-gateway нет: найденные пробелы — отдельные issue в тех репозиториях.

50
scripts/demo-prepare.sh Executable file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env bash
# Подготовка демо: загрузить квартальные ОСВ РБ и Счастливочной за 2024 Q1–Q3 в версию и подтвердить ОСВ-документы.
# Использование: scripts/demo-prepare.sh <version_id> [email] (стенд arm-core на localhost:8000, DEBUG=true)
# Каталог фикстур: FIXTURES_DIR (по умолчанию ../../fixtures относительно репозитория arm-web, т.е. workspace/fixtures).
set -euo pipefail
V="${1:?version_id}"
EMAIL="${2:-ka_go@armka.local}"
API="${API:-localhost:8000/api/v1}"
F="${FIXTURES_DIR:-$(cd "$(dirname "$0")/../.." && pwd)/fixtures}/osv"
H="Authorization: Bearer $(curl -sf -X POST "$API/dev/token" -H 'Content-Type: application/json' -d "{\"email\":\"$EMAIL\"}" | python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')"
members=$(curl -sf "$API/consolidations/versions/$V" -H "$H" | python3 -c '
import sys,json
for m in json.load(sys.stdin)["members"]: print(m["id"], m["inn"], m["name"])')
# ЮЛ ищем по ИНН (в справочнике имена могут быть в верхнем регистре)
le_by_inn() { echo "$members" | awk -v inn="$1" '$2 == inn { print $1; exit }'; }
LE_RB=$(le_by_inn 3528052718)
LE_SCH=$(le_by_inn 3528104476)
[ -n "$LE_RB" ] && [ -n "$LE_SCH" ] || { echo "В периметре версии нет РБ или Счастливочной:"; echo "$members"; exit 1; }
upload() { for f in "$F/$2"/2024-Q{1,2,3}/*; do
curl -s -X POST "$API/legal-entities/$1/files?consolidation_version_id=$V" -H "$H" -F "files=@$f" \
| python3 -c 'import sys,json;d=json.load(sys.stdin);print("принят" if d.get("accepted") else d.get("rejected") or d)'
done; }
upload "$LE_RB" rb
upload "$LE_SCH" schastl
echo "Жду распознавания…"
for _ in $(seq 1 60); do
pending=0
for LE in $LE_RB $LE_SCH; do
n=$(curl -sf "$API/legal-entities/$LE/files?limit=100" -H "$H" | python3 -c 'import sys,json;print(sum(f["status"] in ("UPLOADED","PROCESSING") for f in json.load(sys.stdin)["items"]))')
pending=$((pending + n))
done
[ "$pending" -eq 0 ] && break
sleep 5
done
for LE in $LE_RB $LE_SCH; do
curl -sf "$API/legal-entities/$LE/files?limit=100" -H "$H" | python3 -c '
import sys,json
for f in json.load(sys.stdin)["items"]:
for d in f["documents"]:
if d["document_type"] == "OSV" and d["status"] in ("RECOGNIZED","NEEDS_CHECK"): print(d["id"])' \
| while read -r DOC; do curl -s -o /dev/null -w "%{http_code} $DOC\n" -X POST "$API/documents/$DOC/confirm" -H "$H"; done
done
curl -sf "$API/consolidations/versions/$V/osv/summary" -H "$H" | python3 -c '
import sys,json
s=json.load(sys.stdin); print("строк:", s["rows_total"], "документов:", s["documents_total"], "несопоставленных:", s["unmapped_total"], "пробелов:", len(s["gaps"]))'