Track the approved proposal, delta specification and design with all 29 verified implementation tasks. Keep the change active without archiving or syncing main specifications.
315 lines
54 KiB
Markdown
315 lines
54 KiB
Markdown
# Реализация ручного учёта банковских карт
|
||
|
||
**Статус:** согласованный объём реализован и проверен 12 сентября 2026 года.
|
||
Независимое ревью завершено; change остаётся активным, без архивирования
|
||
и синхронизации в основные specs.
|
||
Основание — согласованные [Design revision 1](design.md), включая полностью
|
||
inline UI/UX package revision 1, и [Delta Specs](specs/manual-bank-cards/spec.md).
|
||
Внешних визуальных материалов или дополнительных пакетов для начала нет.
|
||
|
||
Checkbox закрывается только после выполнения действия и его Verification.
|
||
Verification ниже задаёт критерии; наблюдённые результаты отмечаются отдельно.
|
||
Перед Node-командами из affected repo выполняются
|
||
`source ~/.nvm/nvm.sh && nvm use --silent`; для specs используется frontend
|
||
`.nvmrc`. Git-команды выполняются из соответствующего независимого репозитория.
|
||
|
||
DB/HTTP-проверки используют уникальную временную SQLite с вымышленными данными
|
||
и явно заданной `DATABASE_URL`, без доступа к пользовательской базе или T-Bank.
|
||
Общий `npm run test:e2e` с фиксированной `/tmp/wealth-ledger-e2e.db` для них
|
||
не используется. Рабочая финансовая база не сбрасывается и не заменяется
|
||
ради выполнения проверки.
|
||
|
||
Задачи одного раздела выполняются по указанным зависимостям. Задача 1.2
|
||
может идти независимо от 1.1; остальные параллельные работы допустимы только
|
||
при закрытых зависимостях и раздельном владении изменяемыми файлами.
|
||
|
||
## 1. Основа хранения и точных денег
|
||
|
||
- [x] 1.1 [backend] Подтвердить применимость выбранного Solution Assessment: существующие Prisma/SQLite и migration boundary под Node 22; зафиксировать исходную версию схемы/приложения для проверки перехода и отката.
|
||
- **Covers:** `N/A` — техническое предусловие нового долговременного хранения из Design, без самостоятельного пользовательского поведения или смены выбранной архитектуры.
|
||
- **Verification:** `npx prisma validate`; существующие migrations применяются через `npx prisma migrate deploy` с `DATABASE_URL` каждой уникальной временной базы. Для снятия известного blocker нужны три последовательных успешных fresh-DB применения согласно [правилу проверки](../../../../docs/development/verification.md#известное-ограничение-сквозной-проверки). При ошибке сохранить версию и точный failure, не закрывать задачу и не подменять migration proof через `db push`, reset или ручной обход migrations.
|
||
- **Результат 2026-09-12:** Node `22.21.0`, Prisma/client `7.9.1`, engine `e922089b7d7502aff4249d5da3420f6fa55fc6ad`; `prisma validate` — passed. Исходный backend `8ec172f48eefa2237dc0c16919fbb2e726b39a65`, frontend `427ca23a433a20be25b1f4373f32c9e1f6f8c400`; оба working tree были чистыми. Обычный fresh deploy воспроизвёл `Error: Schema engine error:` без деталей, в том числе вне sandbox. С `RUST_LOG=info` штатный `npx prisma migrate deploy` вне sandbox прошёл три раза подряд: `/tmp/wealthledger-bank-cards-migrations-logging.rFEHKw/fresh-{1,2,3}.db`, логи `migrate-{1,2,3}.log` рядом. В каждом применены обе исходные migrations. Это подтверждает путь с указанной переменной окружения; запуск без неё остаётся ограничением. Версии зависимостей, применённые migrations и пользовательская база не менялись.
|
||
|
||
- [x] 1.2 [backend] Выделить общего владельца decimal parsing/formatting и MoneyDto, сохранить protobuf-преобразование у T-Bank и добавить точное преобразование допустимого RUB-ввода в копейки.
|
||
- **Covers:** `manual-bank-cards` — `Точные денежные значения и проверка до сохранения`.
|
||
- **Verification:** существующие decimal и затронутые брокерские calculation tests проходят до и после переноса; targeted Jest проверяет signed-64-bit границы, отрицательный/нулевой начальный остаток, строго положительную сумму операции, запрет лишней точности и точный Money roundtrip. `npm run typecheck:app`; прежний Money wire contract сохраняется.
|
||
- **Результат 2026-09-12:** общий owner `src/common/decimal.ts`, `money.dto.ts`, `rub-money.ts`; protobuf-преобразование осталось в T-Bank. До переноса `npm test -- --runInBand src/tbank` — 24 suites / 191 tests passed; после `npm test -- --runInBand 'src/tbank|src/common/rub-money'` — 25 suites / 213 tests passed. Targeted ESLint, `npm run typecheck:app`, `git diff --check` — passed. `npm run test:openapi-contract` до добавления банковских endpoints подтвердил неизменность прежнего контракта, но проверка обнаружила безусловную замену внешнего `DATABASE_URL` в `test/e2e-env.cjs` на фиксированную временную базу. Этот запуск не принимается как доказательство требуемой изоляции; после исправления test setup сравнение повторяется на уникальной базе. Пользовательская база не использовалась. Новый banking API требует отдельного финального сравнения.
|
||
|
||
- [x] 1.3 [backend] После 1.1 добавить BankCard, ExpenseCategory и BankCardOperation, additive migration, связи, ограничения уникальности, индексы, версии, tombstones и fingerprints из Design.
|
||
- **Covers:** `manual-bank-cards` — `Сохранённый ручной учёт переживает повторный вход и перезапуск`; `manual-bank-cards` — `Точные денежные значения и проверка до сохранения`.
|
||
- **Verification:** `npx prisma validate`, `npx prisma generate`, `npm run typecheck:app`; migration на изолированной базе с representative User/Session/TbankConnection сохраняет их данные. Реальная DB-проверка подтверждает FK/unique constraints и точное хранение копеек больше `2^53` и обеих граничных величин; применённые migrations не переписаны.
|
||
|
||
Если для устранения подтверждённого migration blocker потребуется другой
|
||
подход или изменение согласованного стека, это возвращает решение в Design.
|
||
Успех unit/Swagger-проверки не разрешает закрыть 1.1 или 1.3 без DB-доказательства.
|
||
|
||
Результат 1.3: additive migration `20260912090000_add_manual_bank_cards`
|
||
(существующие migrations неизменны), `prisma validate/generate` и app typecheck
|
||
— passed. Реальный adapter/Prisma roundtrip подтвердил `9007199254740993n`
|
||
и обе signed64 границы; configured DB-тесты подтвердили FK/unique.
|
||
Переход с исходной схемы сохранил representative User/Session/TbankConnection
|
||
в проверке сохранности 3.4.
|
||
|
||
## 2. Backend: пользовательский учёт и денежные изменения
|
||
|
||
- [x] 2.1 [backend] После 1.2–1.3 создать BankCardsModule и сервис сценариев: добавление карт с защитой повторов, пользовательские категории, начальный набор и переименование с сохранением идентичности и default role.
|
||
- **Covers:** `manual-bank-cards` — `Ручное добавление независимых банковских карт`; `manual-bank-cards` — `Общие для пользователя категории расходов`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`.
|
||
- **Verification:** targeted service/DB cases проверяют ограничения сведений карты, собственника, atomic card+seed creation, concurrent first-card creation без дублей категорий, регистронезависимую уникальность, rename default category и same-intent create карты с тем же/другим содержанием. Чтение не создаёт данных и не обращается к T-Bank.
|
||
|
||
- [x] 2.2 [backend] После 1.2 реализовать чистые правила календарных дат и точные расчёты баланса и месячных расходов из первичных записей, сохранив O(N) подход Design.
|
||
- **Covers:** `manual-bank-cards` — `Даты начала учёта и ручных операций`; `manual-bank-cards` — `Баланс определяется начальным остатком и внесёнными операциями`; `manual-bank-cards` — `Месячный журнал и сводка расходов по категориям`.
|
||
- **Verification:** targeted Jest проверяет `1 − 0,10 − 0,20 = 0,70`, отрицательный баланс, итог выше предела отдельного ввода, московскую границу дня/месяца, реальные календарные даты, возврат прошлого месяца и исключение переводов/пополнений из расходов. Расчёт не зависит от страницы журнала; комментарий `ponytail:` фиксирует предел O(N) и условие оптимизации.
|
||
|
||
- [x] 2.3 [backend] После 2.1–2.2 реализовать создание расходов и пополнений с проверкой карты/категории/дат, транзакцией и разрешением сохранённого намерения до повторной проверки изменяемых условий.
|
||
- **Covers:** `manual-bank-cards` — `Ручное внесение расходов и пополнений`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`.
|
||
- **Verification:** service/DB cases проверяют default category, сохранение при отрицательном остатке, отказ чужим ссылкам и недопустимому вводу без частичной записи; same UUID не создаёт дубликат, другой payload даёт конфликт, две независимые успешные траты сохраняются обе.
|
||
|
||
- [x] 2.4 [backend] После 2.3 реализовать перевод одной записью с проверкой обеих карт и дат начала учёта, двумя проекциями истории и общим денежным эффектом.
|
||
- **Covers:** `manual-bank-cards` — `Перевод между своими картами как одно действие`; `manual-bank-cards` — `Даты начала учёта и ручных операций`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`.
|
||
- **Verification:** реальная DB-проверка подтверждает обе стороны перевода, допустимый отрицательный остаток источника и отсутствие частичного результата при ошибке; отклоняются одинаковые, чужие и архивные карты и дата до начала учёта любой стороны. Сумма не включается в расходы одной или всех карт.
|
||
|
||
- [x] 2.5 [backend] После 2.3 реализовать связанные частичные и повторные возвраты, наследование категории покупки, общий лимит и чтение доступных исходных расходов за любой период.
|
||
- **Covers:** `manual-bank-cards` — `Возврат связан с исходным расходом`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`.
|
||
- **Verification:** DB cases проверяют несколько возвратов, исходный расход прошлого месяца, чужую/удалённую покупку, дату до покупки и два конкурирующих возврата с превышением общего лимита. Повтор успешно сохранённого полного возврата после потерянного ответа возвращает существующий результат без ошибки нового лимита и без дубликата.
|
||
|
||
- [x] 2.6 [backend] После 2.4–2.5 реализовать version-protected исправление и логическое удаление всех видов операций с проверкой прежних и новых связей, сохранением creation fingerprint и запретом изменения вида/исходной покупки возврата.
|
||
- **Covers:** `manual-bank-cards` — `Исправление и удаление операций с согласованным пересчётом`; `manual-bank-cards` — `Возврат связан с исходным расходом`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`.
|
||
- **Verification:** DB cases покрывают перенос расхода/пополнения между картами, смену получателя и удаление целого перевода, смену месяца/категории, ограничения исходного расхода с возвратами, edit refund с исключением собственной прежней суммы, stale edit/delete и повтор delete. Late create retry после edit/delete не возвращает старые суммы и не воскрешает запись; гонка refund с уменьшением покупки сохраняет общий лимит.
|
||
|
||
- [x] 2.7 [backend] После 2.6 реализовать изменение сведений и начала учёта карты, архив и возврат из архива с проверкой всей затронутой истории и version conflict.
|
||
- **Covers:** `manual-bank-cards` — `Ручное добавление независимых банковских карт`; `manual-bank-cards` — `Исправление начала ручного учёта`; `manual-bank-cards` — `Архив карт сохраняет историю`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`.
|
||
- **Verification:** DB cases проверяют opening balance без новой операции/изменения месячных трат, недопустимое новое начало с входящим переводом, архив при любом остатке, запрет create/edit/delete по архиву, unarchive и stale card edit. Гонка архива или смены даты с финансовым изменением даёт последовательный допустимый результат; повтор create архивированной карты возвращает ту же архивную карту.
|
||
|
||
Результат раздела 2: `src/bank-cards/bank-card.calculations.spec.ts` — 23
|
||
pure tests passed; полный backend gate — 33 suites / 253 tests passed.
|
||
`test/bank-cards.e2e-spec.ts` — 18 configured HTTP/SQLite tests passed
|
||
(`/tmp/bank-cards-http.log`): атомарные повторы/переводы, независимые траты,
|
||
конкурирующие возвраты и уменьшение покупки, смена начала/архив одновременно
|
||
с операцией, версии, foreign ownership, исправления/удаления, категории и
|
||
точные денежные границы. Fixtures вымышленные, URL уникальный; setup сохраняет
|
||
внешний `DATABASE_URL`, банковский suite запрещает shared/user DB.
|
||
|
||
## 3. Backend: чтение, HTTP и доказательство сохранности
|
||
|
||
- [x] 3.1 [backend] После раздела 2 подключить расчёты к согласованным чтениям списка/деталей карт, месячного журнала, исходных расходов возврата и сводок одной/всех карт.
|
||
- **Covers:** `manual-bank-cards` — `Баланс определяется начальным остатком и внесёнными операциями`; `manual-bank-cards` — `Месячный журнал и сводка расходов по категориям`; `manual-bank-cards` — `Архив карт сохраняет историю`; `manual-bank-cards` — `Возврат связан с исходным расходом`.
|
||
- **Verification:** integration cases проверяют пагинацию и стабильный порядок равных дат, весь набор для баланса/сводки, отрицательный net, архив в общей сводке, перенос между месяцами и перегруппировку связанных возвратов после изменения категории покупки. Связанные данные одного ответа согласованы; отсутствуют server cache и materialized balance.
|
||
|
||
- [x] 3.2 [backend] После 3.1 завершить controller/runtime DTO под `/api/v1/bank-cards`, подключить существующие JWT/filter и сформировать полный runtime OpenAPI для frontend.
|
||
- **Covers:** `manual-bank-cards` — `Отдельный раздел ручного учёта банковских карт`; `manual-bank-cards` — `Точные денежные значения и проверка до сохранения`; `manual-bank-cards` — `Даты начала учёта и ручных операций`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`.
|
||
- **Verification:** configured HTTP cases проверяют фактические validation/status/error codes, отсутствие сессии, чужие связи, архив/version/refund conflicts, безопасность ответов и работу без T-Bank. Runtime OpenAPI отражает действующие DTO и Money; сырые Prisma records не выдаются вместо оговорённого контракта, финансовые DTO не попадают в ответы с ошибками.
|
||
|
||
- [x] 3.3 [backend] После 3.2 завершить runnable configured HTTP/DB проверки критических сценариев и конкурентных изменений в отдельной временной базе, дополнив уже созданные cases вместо их дублирования.
|
||
- **Covers:** `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`; `manual-bank-cards` — `Исправление и удаление операций с согласованным пересчётом`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`.
|
||
- **Verification:** с уникальной `DATABASE_URL` выполнить `NODE_ENV=development NODE_OPTIONS=--experimental-vm-modules npx jest --config ./test/jest-e2e.json --runInBand bank-cards`. Проверяемые исходы: один результат concurrent same-intent create карты/операции, сохранность обеих независимых трат, no partial transfer/refund, stale conflicts, архивные/чужие ссылки, loss-of-response replay и отсутствие ложного успеха. Mock Prisma не заменяет DB cases.
|
||
|
||
- [x] 3.4 [backend] После 3.3 доказать долговременность ручных данных, корректный backup/restore и совместимость отката приложения с расширенной схемой без удаления новой истории.
|
||
- **Covers:** `manual-bank-cards` — `Сохранённый ручной учёт переживает повторный вход и перезапуск`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`.
|
||
- **Verification:** на disposable базах перезапуск сохраняет карты, операции, связи, переименованные категории, архив, tombstones и fingerprints; повтор после перезапуска не создаёт дубликат. Проверяются согласованный SQLite backup/restore, сохранность User/Session/TbankConnection, запуск исходной версии приложения с additive schema и доступность новых записей после возврата новой версии. Старый backup не восстанавливается поверх новой истории как штатный rollback.
|
||
|
||
Результат раздела 3: те же 18 HTTP/DB tests подтверждают чтения, пагинацию,
|
||
month/category regrouping, validation/status и безопасные ошибки без T-Bank.
|
||
Дополнительный процессный сценарий `node /tmp/bank-cards-durability.cjs` — passed:
|
||
исходный backend собран из Git archive `8ec172f48eefa2237dc0c16919fbb2e726b39a65`
|
||
в `/tmp/wealthledger-bank-baseline.8hcDz3`; миграция сохранила старые таблицы,
|
||
перезапуск — категории/архив/возвраты/tombstones/fingerprints, старое приложение
|
||
запустилось с расширенной схемой, новое вновь прочитало историю, согласованный
|
||
SQLite backup восстановлен и прочитан приложением. Свидетельство:
|
||
`/var/folders/j_/h2ft23gd3qq35ps3v5bcftfm0000gn/T/wealthledger-bank-durability-hPvsEO/result.json`.
|
||
Runnable версия сохранена в `backend/test/bank-cards-durability.cjs`; ей
|
||
передаётся каталог отдельно собранной исходной версии. Проверки создают свои
|
||
временные базы; старый backup не записывается поверх новой истории.
|
||
|
||
## 4. Контракт и слой данных frontend
|
||
|
||
- [x] 4.1 [frontend] После 3.2 обновить committed OpenAPI snapshot из настроенного backend runtime document и сгенерировать frontend types, сохранив backend единственным editable HTTP source.
|
||
- **Covers:** `N/A` — производные артефакты двустороннего контракта из Design обеспечивают типизированного потребителя, но не вводят отдельного пользовательского поведения.
|
||
- **Verification:** `npm run openapi:generate`, `npm run openapi:check`, `npm run typecheck`; snapshot получен по [существующему workflow](../../../../docs/development/runtime-openapi-verification.md), generated types не исправлялись вручную, существующие инвестиционные Money-контракты сохранены.
|
||
|
||
- [x] 4.2 [frontend] После 4.1 добавить banking queries/mutations у domain owner: session-aware transport, стабильный create UUID, version inputs, безопасные retries и invalidation всех затронутых банковских GET-представлений.
|
||
- **Covers:** `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`; `manual-bank-cards` — `Отдельный раздел ручного учёта банковских карт`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`.
|
||
- **Verification:** Vitest/MSW через `npm run test:run -- src/entities/bank-card` проверяют одинаковый UUID при network/auth retry, отсутствие optimistic balance, invalidation обеих карт/всех месяцев/исходных расходов, rename категории, сохранение данных при ошибке, GET после version conflict, logout isolation и независимость от T-Bank connection mutation. Свой refresh loop и второй query cache отсутствуют.
|
||
|
||
Результат 4.2: `src/entities/bank-card/api/banking.test.tsx` — 10 MSW tests
|
||
passed; typecheck, focused Biome и dependency boundaries — passed. Повторы
|
||
сохраняют UUID, подтверждённые mutations инвалидируют только все banking GETs,
|
||
конфликты вызывают перечитывание, ошибки не меняют суммы оптимистически.
|
||
Дополнены реальные feature tests AuthProvider/TbankConnectionSection: 10 tests
|
||
passed доказывают очистку banking cache при logout/refresh failure и его
|
||
сохранение без invalidation при connect/replace/delete T-Bank.
|
||
|
||
Результат 4.1: snapshot экспортирован из configured runtime Nest/Swagger
|
||
(42 paths) на уникальной временной SQLite, включая банковские 409/410.
|
||
`npm run openapi:generate`, `npm run openapi:check`, `npm run typecheck` — passed;
|
||
generated types не редактировались вручную. После финального добавления
|
||
описаний 409/410 snapshot/types повторно сгенерированы, `openapi:check` passed.
|
||
|
||
## 5. Экраны и формы из UI/UX package revision 1
|
||
|
||
Все задачи раздела используют существующие Mantine-тему, i18n, container/VM/view,
|
||
RHF/Zod и shared UI из Design. Проверки формы и состояний выполняются через
|
||
видимый результат и сеть MSW; отдельный тест декларативной обёртки не нужен.
|
||
|
||
- [x] 5.1 [frontend] После 4.2 собрать `/cards`: навигацию, список активных карт и архива, месячную сводку всех карт и форму добавления карты с начальным остатком/датой.
|
||
- **Covers:** `manual-bank-cards` — `Отдельный раздел ручного учёта банковских карт`; `manual-bank-cards` — `Ручное добавление независимых банковских карт`; `manual-bank-cards` — `Месячный журнал и сводка расходов по категориям`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`.
|
||
- **Verification:** `npm run test:run -- src/pages/cards` и targeted route tests проверяют login/returnTo без T-Bank, обязательные/необязательные поля, точный отрицательный остаток, no request при invalid, блокировку pending, сохранение ввода при ошибке и один create UUID при повторе. Loaded/empty/error/skeleton и архивный переключатель соответствуют Design; сводка всех карт включает архив независимо от фильтра списка.
|
||
|
||
- [x] 5.2 [frontend] После 5.1 собрать `/cards/:id`: крошки и title, текущий баланс, общий выбор месяца для журнала/сводки, пагинацию и представления четырёх видов операций.
|
||
- **Covers:** `manual-bank-cards` — `Баланс определяется начальным остатком и внесёнными операциями`; `manual-bank-cards` — `Месячный журнал и сводка расходов по категориям`; `manual-bank-cards` — `Архив карт сохраняет историю`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`.
|
||
- **Verification:** `npm run test:run -- src/pages/card` проверяет month/URL и страницу, неизменный текущий баланс, расходы/возвраты/net со знаком, понятные стороны перевода, локальную ошибку сводки, cached snapshot при refetch failure и безопасное состояние отсутствующей карты. Frontend форматирует Money без собственной финансовой арифметики.
|
||
|
||
- [x] 5.3 [frontend] После 5.2 добавить формы создания расхода, пополнения и перевода с общими проверками денежного ввода/дат и сохранением одного намерения до определённого результата.
|
||
- **Covers:** `manual-bank-cards` — `Ручное внесение расходов и пополнений`; `manual-bank-cards` — `Перевод между своими картами как одно действие`; `manual-bank-cards` — `Точные денежные значения и проверка до сохранения`; `manual-bank-cards` — `Даты начала учёта и ручных операций`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`.
|
||
- **Verification:** RTL/MSW cases проверяют default category, положительную сумму/копейки без потери больших значений, даты обеих карт перевода, отрицательный итог без запрета реальной траты, visible labels, pending/ошибки с сохранённым вводом и повтор одного перевода без дубликата. Новое подтверждённое состояние приходит из банковских queries.
|
||
|
||
- [x] 5.4 [frontend] После 5.3 добавить создание и редактирование возврата с выбором исходной покупки за всю историю карты и корректным лимитом текущей записи.
|
||
- **Covers:** `manual-bank-cards` — `Возврат связан с исходным расходом`; `manual-bank-cards` — `Исправление и удаление операций с согласованным пересчётом`; `manual-bank-cards` — `Месячный журнал и сводка расходов по категориям`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`.
|
||
- **Verification:** RTL/MSW journey выбирает покупку прошлого месяца, записывает частичный возврат и видит его в месяце получения; полный возврат позволяет исправить описание, исключая собственную старую сумму из лимита. Проверяются превышение, stale source, immutable purchase link, архив, network retry полного возврата и сохранение формы без ложного успеха.
|
||
|
||
- [x] 5.5 [frontend] После 5.3–5.4 добавить остальные исправления/удаления операций, изменение сведений и начала учёта карты, архив и возврат из архива с понятными конфликтами.
|
||
- **Covers:** `manual-bank-cards` — `Исправление и удаление операций с согласованным пересчётом`; `manual-bank-cards` — `Исправление начала ручного учёта`; `manual-bank-cards` — `Архив карт сохраняет историю`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`.
|
||
- **Verification:** RTL/MSW проверяет edit с version и последующим GET при конфликте, сохранение черновика, перенос между картами/месяцами, подтверждение удаления перевода с обеими картами, запреты связанного расхода и архива, opening date error и обратимый архив. После успешного действия все затронутые видимые суммы соответствуют новым серверным данным.
|
||
|
||
- [x] 5.6 [frontend] После 5.3 добавить управление начальными и своими категориями и создание категории из формы расхода без потери введённой операции.
|
||
- **Covers:** `manual-bank-cards` — `Общие для пользователя категории расходов`; `manual-bank-cards` — `Ручное внесение расходов и пополнений`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`.
|
||
- **Verification:** RTL/MSW проверяет add/rename, уникальность без регистра и длину, переименование default category без утраты роли, обновление названий в истории/сводке и неизменный черновик расхода при добавлении категории. Удаление и вложенные подкатегории отсутствуют согласно scope.
|
||
|
||
Результат 5.1–5.6: реализованы страницы, четыре вида операций, исправления,
|
||
удаление, архив и категории. RTL/MSW покрывают точный ввод и UUID после ошибки,
|
||
month/page/returnTo, неизменный текущий баланс, полный возврат с собственным
|
||
лимитом, stale version с явным принятием новой версии, opening-date conflict,
|
||
архив, обе стороны удаления перевода и вложенное создание категории.
|
||
Финальный frontend gate включает 43 test files / 263 passed tests.
|
||
|
||
Браузер обнаружил потерю нового имени категории после reset неконтролируемого
|
||
поля: поле переведено на RHF Controller, исправление проверено RTL и настоящим
|
||
PATCH/GET. Независимое ревью обнаружило скрытые ошибки вспомогательных GET
|
||
карт/категорий: добавлены локальные сообщения и retry, две regression-проверки
|
||
и browser recovery passed. Существующие authentication/инвестиционные tests
|
||
прошли; returnTo сохраняет карточку и query выбранного месяца.
|
||
|
||
## 6. Проверка пользовательского результата и документация
|
||
|
||
- [x] 6.1 [frontend] После 3.3–3.4 и разделов 4–5 проверить UI package в браузере с изолированным backend и подготовить Storybook-примеры новых собственных представлений на вымышленных данных.
|
||
- **Covers:** `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`; `manual-bank-cards` — `Отдельный раздел ручного учёта банковских карт`; `manual-bank-cards` — `Сохранённый ручной учёт переживает повторный вход и перезапуск`.
|
||
- **Verification:** реальный UI journey без T-Bank: две карты → расход/пополнение → перевод → возврат прошлой покупки → исправление/удаление → архив/unarchive → reload/login. Проверить широкий экран и 320–375 px, пять пунктов навигации, длинные суммы/названия, keyboard/focus return, reduced motion и loading/empty/error/refetch recovery. Сохранить краткие результаты и локальные визуальные свидетельства; Storybook build не подменяет браузерную проверку.
|
||
|
||
- [x] 6.2 [docs] После подтверждения реализованного поведения обновить документацию: область банковских карт, глоссарий, обзор продукта и тематическую навигацию, хранимые данные, денежные правила и безопасное восстановление.
|
||
- **Covers:** `N/A` — as-built объяснение поставленного поведения и восстановления из Delivery Map; новая норма не вводится и не копируется параллельно Delta Specs.
|
||
- **Verification:** пройти путь README → банковские карты → понятия/расчёты/исправления и восстановление; сверить текст с поставленными flows и ограничениями, включая ручной источник баланса, отрицательные итоги, архив, повторы и сохранение новой истории при rollback. Ссылки ведут на существующие targets; исторические материалы не переписываются, обещания неподтверждённой миграции не добавляются.
|
||
|
||
Результат 6.1: реальный браузер работал с отдельным BrowserContext и backend
|
||
на уникальной временной SQLite, с вымышленным пользователем без T-Bank.
|
||
Через UI выполнены две карты (1 000 ₽ и 0 ₽), августовский расход 100 ₽,
|
||
сентябрьские пополнение 200 ₽, перевод 150 ₽ и возврат 60 ₽: баланс A 1 010 ₽,
|
||
B 150 ₽, сентябрьский net −60 ₽. Возврат исправлен до полного 100 ₽,
|
||
его описание изменено, затем сумма возвращена к 60 ₽. Исправление покупки
|
||
100 → 150 ₽ дало A 960 ₽; удаление перевода с подтверждением обеих карт —
|
||
A 1 110 ₽ и B 0 ₽. Архив сохранил историю и общий net; unarchive вернул действия.
|
||
|
||
Добавление категории из расхода сохранило сумму 12,34 ₽ и описание черновика;
|
||
переименование «Другое» сохранило default role. Проверены Escape и возврат
|
||
фокуса к действию, visible labels, initial loading/empty, ошибки GET и retry,
|
||
сохранённый net при неудачном refetch, reduced motion. Logout → protected URL →
|
||
login вернул ту же карту и август; reload сохранил баланс 1 110 ₽ и расход 150 ₽.
|
||
Экран 1440 px и 320/375 px проверен визуально: пять пунктов навигации,
|
||
длинное название и точная предельная сумма доступны без горизонтального overflow.
|
||
|
||
Локальные screenshots итогового UI:
|
||
`/tmp/wealthledger-bank-cards-final-desktop.png`,
|
||
`/tmp/wealthledger-bank-cards-final-mobile.png`,
|
||
`/tmp/wealthledger-bank-cards-final-mobile-long.png`,
|
||
`/tmp/wealthledger-bank-card-final-mobile.png`;
|
||
состояние cached error — `/tmp/wealthledger-bank-cards-cached-error.png`.
|
||
Добавлены две изолированные story groups и 9 примеров на вымышленных данных;
|
||
storybook dependency isolation и build passed.
|
||
|
||
Результат 6.2: обновлены README/overview, glossary, область cards, описание
|
||
хранения, локальная миграция/восстановление и UI-каталог. Маршруты README → cards →
|
||
Money/восстановление проверены; объяснения сверены с Delta Specs, реализацией
|
||
и наблюдёнными flows. Новых Mermaid-диаграмм и изменений исторических ADR нет.
|
||
|
||
## 7. Финальные gates и состояние SDD-артефактов
|
||
|
||
Backend `npm run verify` — passed (`/tmp/bank-cards-verify.log`): lint без
|
||
warnings, app/test TypeScript, 33 suites / 253 tests, Prisma generate и build.
|
||
Последующий итоговый HTTP-прогон и independent review описаны ниже;
|
||
успех gates не является заменой semantic review.
|
||
|
||
После независимого review исправлен P2 в DTO: явный `null` для Money,
|
||
архива и non-null полей отклоняется; nullable реквизиты очищаются по-прежнему.
|
||
Configured HTTP/SQLite — 19/19 passed, включая 400 без изменения данных/версий;
|
||
OpenAPI parity на уникальной базе — passed, wire shape не изменился.
|
||
Итоговый `npm run verify` повторно passed
|
||
(`/tmp/bank-cards-final-backend-verify.log`). Сохранённый runnable durability
|
||
сценарий повторно passed на итоговом backend:
|
||
`/var/folders/j_/h2ft23gd3qq35ps3v5bcftfm0000gn/T/wealthledger-bank-durability-dXSTmz/result.json`.
|
||
Reviewer перепроверил исправление и не нашёл других backend findings.
|
||
|
||
Gate применяется к итоговому состоянию соответствующего repo. Уже прошедшие
|
||
проверки не повторяются без новых изменений, сбоев или оставшегося риска.
|
||
Если финальная правка меняет проверенную границу, повторяется её нужный gate.
|
||
|
||
- [x] 7.1 [backend] После разделов 1–3 выполнить canonical backend gate и сохранить результаты DB/HTTP/durability проверок для того же состояния кода.
|
||
- **Covers:** `N/A` — обязательная проверка backend delivery из Design, дополняющая уже покрытые поведенческие и DB cases без новой возможности.
|
||
- **Verification:** `npm run verify`; отдельные результаты 3.3–3.4 остаются применимы к итоговому коду. Type/lint/unit/build не объявляются доказательством транзакций или миграции, если DB-проверка не запускалась успешно.
|
||
|
||
- [x] 7.2 [frontend] После 6.1 выполнить canonical frontend gate для итоговых API types, UI, i18n, stories и dependency boundaries.
|
||
- **Covers:** `N/A` — обязательный frontend delivery gate из Design, без добавления самостоятельного пользовательского поведения.
|
||
- **Verification:** `npm run verify` включает OpenAPI drift, typecheck, lint, coverage, Knip, dependency boundaries и обе сборки. Отдельный результат браузерной проверки 6.1 сохраняет применимость; процент покрытия и Storybook build не заменяют его.
|
||
|
||
- [x] 7.3 [workspace] После 4.1, 7.1 и 7.2 подтвердить двусторонний runtime OpenAPI contract по итоговому backend и frontend snapshot/types.
|
||
- **Covers:** `N/A` — межрепозиторная verification из Contract Gate Decision, без владения кодом приложений или отдельным поведением.
|
||
- **Verification:** из backend выполнить `npm run test:openapi-contract`; использовать актуальный frontend `openapi:check` из 7.2, не повторяя его при неизменном snapshot/types. Проверка не требует T-Bank и не изменяет snapshot автоматически.
|
||
|
||
- [x] 7.4 [docs] После 6.2 выполнить canonical docs gate, ручную смысловую сверку и проверку затронутых маршрутов чтения.
|
||
- **Covers:** `N/A` — проверка связности as-built документации из Delivery Map, без самостоятельного нормативного поведения.
|
||
- **Verification:** из docs выполнить `npm run verify`; проверить новые Markdown-файлы и затронутые внешние относительно docs ссылки. Если изменены Mermaid-диаграммы, отдельно проверить доступным локальным renderer их вид либо явно сохранить ограничение; новый стек для этого не устанавливать.
|
||
|
||
- [x] 7.5 [specs] После всех implementation-owned задач сверить поставленный результат со всеми Delta Requirements и обновить этот Tasks только по выполненным действиям и наблюдённым проверкам, затем выполнить canonical validation.
|
||
- **Covers:** `N/A` — поддержание существующих SDD-артефактов и traceability реализации из Delivery Map; новые требования, архитектура и отдельный журнал доказательств не создаются.
|
||
- **Verification:** каждое требование `manual-bank-cards` связано с выполненной задачей и подходящим доказательством; все оставшиеся ограничения названы. Выполнить `openspec validate add-manual-bank-cards --type change --strict --store wealth-ledger-specs`, `openspec validate --specs --strict --store wealth-ledger-specs` и `git diff --check`, отдельно просмотрев новые файлы и ссылки. Approval и завершение реализации не выводятся только из наличия файлов.
|
||
|
||
Независимая проверка результата и завершение lifecycle выполняются после
|
||
implementation-owned задач; они не маскируются checkbox, который исполнитель
|
||
не может закрыть сам. Этот план не включает преждевременную синхронизацию
|
||
нового capability в основные specs, архивирование или изменение истории.
|
||
Такие действия относятся к последующему завершению выбранного SDD lifecycle.
|
||
|
||
Итоговые результаты 7.2–7.4: frontend `npm run verify` — passed
|
||
(`/tmp/bank-cards-final-frontend-verify.log`): OpenAPI drift, TypeScript,
|
||
Biome, 43 files / 263 tests с coverage, Knip, обе dependency проверки,
|
||
app и Storybook builds. Backend `npm run test:openapi-contract` — passed
|
||
на уникальной базе (`/tmp/bank-cards-contract.log`); последний DTO fix
|
||
не меняет wire shape, frontend snapshot/types после этого не изменялись.
|
||
Docs `npm run verify` — passed, 54 Markdown-файла, 0 issues.
|
||
|
||
Независимое ревью backend/frontend/docs завершено: найденные null-validation,
|
||
category-form и auxiliary-query recovery дефекты исправлены и перепроверены;
|
||
открытых actionable findings нет. Обычный Prisma fresh migrate deploy без
|
||
`RUST_LOG=info` по-прежнему имеет указанное в 1.1 ограничение; причина
|
||
не установлена. Подтверждён только штатный путь с переменной окружения.
|
||
Предупреждение Storybook/Vite о размере chunks не препятствует сборке.
|
||
|
||
Результат 7.5: все 17 Delta Requirements сверены с реализацией и Covers/проверками
|
||
выше; 29 implementation-owned задач завершены. `openspec validate
|
||
add-manual-bank-cards --type change --strict --store wealth-ledger-specs` — valid;
|
||
`openspec validate --specs --strict --store wealth-ledger-specs` — 25 passed,
|
||
0 failed. Итоговый routing повторён по modified/untracked файлам четырёх repo;
|
||
новые файлы и ссылки просмотрены, `git diff --check` — passed в каждом repo.
|
||
Proposal, Delta Specs, Design, основные specs и архивы не изменялись.
|
||
|
||
Последующие проверки перед commit: по запросу пользователя все четыре нативных
|
||
поля дат/месяца заменены на Mantine DateInput/MonthPickerInput. Сохранение
|
||
календарных дат, month/URL и календарь на 320 px проверены в браузере
|
||
на новой временной базе; будущие даты недоступны. Полный frontend
|
||
`npm run verify` повторно passed: 43 files / 263 tests и обе сборки
|
||
(`/tmp/bank-mantine-verify.log`).
|
||
|
||
После отдельного разрешения пользователя миграция применена к рабочей SQLite
|
||
через `RUST_LOG=info npx prisma migrate deploy`. До применения создан
|
||
согласованный SQLite backup; после — проверены integrity/FK и неизменность
|
||
User/Session/TbankConnection. Это обновление рабочей среды, не тестовые записи
|
||
в пользовательской базе. Backup исключён из Git; ограничение обычного запуска
|
||
Prisma без `RUST_LOG=info` остаётся прежним.
|