arm-core/docs/superpowers/plans/2026-11-06-goodwill-review-front.md
Andrey Epifancev fe4c5db0e6 Гудвилл: ветка без правок вне задачи, влит main
Откатены к main правки справочников, шахматки, корректировок и types/api.ts
(обработка ошибок, Alert, удалённые комментарии) — не относятся к AURORA-1544 и
конфликтовали с #219/#221; шахматка переведена на общий фильтр единиц
components/ConsolidationFormatTableFilter; удалены тестовые MOCK_DATA; формат.
План доводки — docs/superpowers/plans/2026-11-06-goodwill-review-front.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 18:31:47 +04:00

14 KiB
Raw Blame History

Гудвилл: доводка фронта по ревью (arm-core #220) — план

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: довести PR arm-core #220 (ветка feat/goodWill, AURORA-1544) до мержа: убрать правки вне задачи, влить main, перевести вкладку «Гудвилл» на фактический API бэка (#222 после доводки по плану 2026-11-06-goodwill-review-back.md), закрыть расхождения с DOC-259025 и макетом 31-03.

Architecture: чистые функции вкладки — web/src/utils/goodwill.ts (+ vitest); типы — web/src/types/goodwill/goodwill.ts по схемам api/src/schemas/goodwill.py; запросы — web/src/services/api/goodwill.ts; экран — web/src/widgets/ConsolidationGoodwill/*. Агрегаты (итоги «Всего» по ГО и по листу) фронт не считает — берёт groups[].totals и totals из ответа.

Tech Stack: React 18 + TS strict, antd 6, vitest, biome + ESLint/Prettier.

Spec: контракт ../docs/dev/api-contract-goodwill.md (обновлён в docs #204), DOC-259025 «Гудвилл» §4–§6, макет ../docs/mockup/screens/31-03-goodwill.png, 31-03-01-goodwill-edit.png.

Рабочий каталог: /Users/andrey.epifancev/projects/awp-ca/.wt/review-220/arm-core (ветка feat/goodWill). Бэк для ручной проверки — ветка feat/goodwill-back (worktree .wt/review-222/arm-core), пока #222 не в main.

Global Constraints

  • Деньги — строки; арифметика только через toCents/fromCents (utils/money.ts), никаких Number над суммами.
  • null — «—», 0.00 — ноль по формату единиц; нерассчитанный гудвилл не попадает ни в один фильтр знака.
  • Доля: ввод с запятой или точкой, 0–100, не больше двух знаков; ошибку показать у поля и заблокировать «Сохранить», значение не обрезать и не подменять.
  • Все запросы — через api/client.ts; ошибки показываются только для ApiError, ошибки валидации форм antd тостом не показываются.
  • Код вне widgets/ConsolidationGoodwill, types/goodwill, services/api/goodwill.ts, utils/goodwill*, hooks/useFullScreen.tsx, components/ConsolidationFormatTableFilter и вкладок отчёта (ConsolidationReportPage.tsx, OsvTabs.tsx) совпадает с main (кроме строки импорта фильтра в ConsolidatedChessContent.tsx).
  • Коммиты — по-русски, Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>.

Review Focus

  • Снять галочку у строки, где доля ни разу не вводилась: пакет уходит с ownership_share: null, сервер отвечает 200.
  • Сервер вернул 422 по одной строке пакета: поле этой строки подсвечено, режим редактирования не закрыт, введённое не потеряно.
  • «Выделить все» в блоке, где есть «Нет вложений» и скрытые фильтром строки: отмечаются все действующие строки блока, «Нет вложений» — нет.
  • Версия без своей блокировки (read_only_reason = LOCK_REQUIRED): «Редактировать» и переключатель недоступны, видна причина.
  • Фильтр «Положительные» + Экспорт: в файл уходит тот же фильтр (sign=positive).

Task 1: Ветка — только гудвилл

Files:

  • Restore from origin/main: web/src/layouts/CatalogueGroups.tsx, web/src/types/api.ts, web/src/widgets/CatalogueCompanyGroup/CompanyGroupForm/CompanyGroupForm.tsx, web/src/widgets/CatalogueJur/**, web/src/widgets/RelatedGroup/**, web/src/widgets/CorrectionTable/CorrectionContent.tsx, web/src/widgets/ConsolidationChess/** (кроме удаления ConsolidationChessFormatTableFilter — см. Step 2)

  • Modify: web/src/widgets/ConsolidationChess/ConsolidationChessContent/ConsolidatedChessContent.tsx (только импорт фильтра), web/src/widgets/ConsolidationGoodwill/ConsolidationGoodwillContent/ConsolidationGoodwillContent.tsx (удалить MOCK_DATA)

  • Step 1: git merge origin/main (конфликты в CatalogueGroups.tsx, ConsolidatedChessContent.tsx, ConsolidationChessTable.tsx — брать сторону main), затем git checkout origin/main -- <файлы из списка>.

  • Step 2: общий фильтр единиц — оставить components/ConsolidationFormatTableFilter, удалить widgets/ConsolidationChess/ConsolidationChessFormatTableFilter/, в шахматке поменять только импорт и имя компонента.

  • Step 3: git diff --stat origin/main...HEAD — изменены только файлы из Global Constraints; useFullscreen использует только вкладка гудвилла.

  • Step 4: из web/: npm run format, затем make -C .. web-check, npm test, npm run lint — зелёные.

  • Step 5: коммит «Гудвилл: ветка без правок вне задачи, влит main».

Task 2: Типы, запросы и чистые функции

Files:

  • Modify: web/src/types/goodwill/goodwill.ts, web/src/services/api/goodwill.ts
  • Create: web/src/utils/goodwill.ts, web/src/utils/goodwill.test.ts

Interfaces:

  • Produces (типы, по api/src/schemas/goodwill.py ветки feat/goodwill-back): GoodwillRow — суммы string | null, has_investments: boolean, warnings: {code: string; message: string}[]; GoodwillTotals + charter_capital_amount; GoodwillGroup {head_legal_entity: Ref; totals: GoodwillTotals}; GoodwillTableResponse.groups: GoodwillGroup[]; GoodwillRowKeyIn {head_legal_entity_id; subsidiary_legal_entity_id; ownership_share: string | null; include_in_kfo: boolean; comment: string | null}; GoodwillSign = 'all' | 'positive' | 'negative' | 'zero'.

  • Produces (запросы): saveGoodwillRows(versionId, rows: GoodwillRowKeyIn[]): Promise<GoodwillTableResponse> (PUT …/goodwill, тело {rows}); saveGoodwillSettings(versionId, {goodwill_enabled}); exportGoodwillTable(versionId, sign: GoodwillSign) (?sign=). saveGoodwillRow удалить.

  • Produces (utils/goodwill.ts): rowKey(row: Pick<GoodwillRow,'head_legal_entity'|'subsidiary_legal_entity'>): string (${head}:${sub}); parseShare(input: string): {ok: true; value: string | null} | {ok: false; error: string} (пусто → null; запятая → точка); previewAmounts(row: GoodwillRow, share: string | null, include: boolean): {participation: string | null; goodwill: string | null}; matchesSign(goodwill: string | null, sign: GoodwillSign): boolean; changedRows(rows: GoodwillRow[], edits: Record<string, GoodwillEdit>): GoodwillRowKeyIn[], где GoodwillEdit = {include_in_kfo?: boolean; ownership_share?: string | null; comment?: string | null}.

  • Step 1: тесты goodwill.test.ts: parseShare: "80,12" → {ok, "80.12"}, "100" ok, "" → {ok, null}, "80,123", "100,01", "-1", "abc" → {ok:false}; previewAmounts (DOC-259025 §11.1): ФВ "180000.00", УК "100000.00", доля "80", отмечена → {participation:"80000.00", goodwill:"100000.00"}; ФВ "50000.00" → goodwill "-30000.00"; УК "371.17", доля "99.99" → participation "371.13" (HALF_UP: 371.13288…); не отмечена или доля null или УК null → {null, null}; matchesSign: null → false для всех, кроме all; "0.00" — только zero/all; changedRows: правка, совпадающая с исходной строкой, не попадает; снятая галочка у строки без доли даёт ownership_share: null.

  • Step 2: npm test -- goodwill — FAIL; реализовать (previewAmounts — в копейках через toCents/fromCents, доля в сотых долях процента BigInt, P = round(УК_коп × доля_сотых / 10000) HALF_UP); PASS.

  • Step 3: npx tsc --noEmit — ошибки только в компонентах вкладки (чинятся в Task 3); коммит «Гудвилл: типы и запросы по API бэка, чистые функции вкладки».

Task 3: Вкладка по API, DOC-259025 и макету

Files:

  • Modify: web/src/widgets/ConsolidationGoodwill/ConsolidationGoodwillContent/ConsolidationGoodwillContent.tsx, .../ConsolidationGoodwillTable/{ConsolidationGoodwillTable.tsx,config.tsx,ConsolidationGoodwillTable.css}, .../ConsolidationGoodwillSummary/ConsolidationGoodwillSummary.tsx

Interfaces:

  • Consumes: Task 2.

  • Step 1: отображение. Суммы — null → «—», иначе формат единиц (ноль — 0,00); строки «Нет вложений» (!has_investments) — серым, подпись «Нет вложений» в колонке сумм, чекбокс и поля недоступны; warnings — иконка WarningOutlined с Tooltip из message у названия ДО; «Всего» блока — из groups[].totals, «Итого по листу» — из totals (без sumAmounts/Number).

  • Step 2: режим «Редактировать». Правки — Record<rowKey, GoodwillEdit>, стартует пустым; P и G в режиме — previewAmounts; доля — parseShare с ошибкой под полем; групповой чекбокс отмечает/снимает все действующие строки блока, включая скрытые фильтром, indeterminate при частичном выборе; «Сохранить» недоступно, если у отмеченной действующей строки нет доли, доля с ошибкой или пустой комментарий; сохранение — один saveGoodwillRows(changedRows(...)), ответ заменяет таблицу и закрывает режим; ApiError с body.detail.subsidiary_legal_entity_id подсвечивает поле этой строки (COMMENT_REQUIRED → комментарий, INVALID_SHARE → доля), режим не закрывается; «Отменить изменения» — сброс правок.

  • Step 3: переключатель. Switch «Учитывать гудвилл в КФО» в панели инструментов, значение goodwill_enabled, доступен при editable; смена → saveGoodwillSettings, ответ заменяет таблицу; при выключенном — подпись «Гудвилл не учитывается в КФО». Если !editable — у «Редактировать» Tooltip с причиной (LOCK_REQUIRED — «Возьмите версию в работу», LOCKED_BY_OTHER — «Версия в работе у другого пользователя», VERSION_NOT_EDITABLE — «Версия недоступна для изменения»).

  • Step 4: фильтр и экспорт. Варианты «Все значения», «Положительные», «Отрицательные», «Нулевые» через matchesSign; экспорт передаёт текущий sign.

  • Step 5: npm run format, make -C .. web-check, npm test, npm run lint — зелёные; коммит «Гудвилл: вкладка по API и DOC-259025».

Task 4: UI-спека и проверка на стенде

Files:

  • Create: openspec/changes/goodwill-ui/{proposal.md,design.md,tasks.md,specs/goodwill-ui/spec.md}

  • Step 1: OpenSpec change goodwill-ui — требования Steps 1–4 Task 3 со сценариями WHEN/THEN, ссылки на контракт и DOC-259025; design.md — раздел ## Decisions made without discussion (R-1: правки вне задачи откатены; R-2: итоги с бэка; R-3: предпросмотр P/G только по строкам, итоги — после сохранения); openspec validate goodwill-ui --strict.

  • Step 2: стенд. Поднять бэк из .wt/review-222/arm-core (ветка feat/goodwill-back, порт 8000, БД стенда), фронт отсюда (REACT_APP_ROOT_PATH=/ npm run start), версия «РБ 2024 Q3» или любая с ОСВ 58.01; в Playwright пройти: открыть вкладку, сверить с 31-03-goodwill.png, «Редактировать» — сверить с 31-03-01-goodwill-edit.png, ввести долю 80,5 и комментарий, сохранить, снять галочку у строки без доли и сохранить, переключить «Учитывать гудвилл», фильтр «Нулевые», экспорт. Скриншоты — в scratchpad, найденное — чинить в Task 3.

  • Step 3: коммит «Гудвилл: UI-спека goodwill-ui».