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` остаётся прежним.
|