Files
specs/openspec/specs/banking/manual-bank-cards/spec.md
T
codex-bot 3821d9ed58
Specs security / security (push) Successful in 7s
Specs verify / verify (push) Successful in 14s
refactor: group canonical specs by domain and archive account-top-positions (#74)
2026-11-02 21:50:49 +03:00

62 KiB

manual-bank-cards Specification

Purpose

Позволяет пользователю вручную вести дебетовые банковские карты в рублях: сохранять операции, видеть независимые балансы, исправлять записи и оценивать месячные расходы по категориям с учётом возвратов.

Requirements

Requirement: Отдельный раздел ручного учёта банковских карт

Система SHALL предоставлять авторизованному пользователю раздел банковских карт на /cards и просмотр отдельной своей карты на /cards/:id. Раздел MUST работать без подключения T-Bank и не зависеть от доступности инвестиционных данных. Банковские карты MUST NOT становиться брокерскими счетами, а банковские суммы и операции MUST NOT включаться в инвестиционный портфель, пассивный доход или прогноз выплат.

Первая версия SHALL поддерживать только ручной учёт дебетовых карт в RUB. Автоматическая загрузка из банков, кредитные карты, другие валюты, общий денежный баланс нескольких карт, прогнозы и бюджеты не входят в возможность.

Scenario: Работа без инвестиционного подключения

  • WHEN пользователь с действующей локальной сессией, но без подключения T-Bank, открывает раздел карт
  • THEN он может просматривать и вести свои банковские карты без перехода к подключению брокера

Scenario: Независимость от инвестиционных данных

  • WHEN пользователь сохраняет банковское пополнение или расход, а инвестиционные данные временно недоступны
  • THEN банковская операция сохраняется по правилам ручного учёта и не меняет инвестиционные суммы или пассивный доход

Requirement: Ручное добавление независимых банковских карт

Система SHALL позволять пользователю добавить карту с названием, начальным остатком и датой начала учёта. Название MUST содержать от 1 до 100 символов после удаления внешних пробелов. Название банка необязательно и MUST содержать не более 100 символов после удаления внешних пробелов. Последние четыре цифры карты необязательны; указанное значение MUST состоять ровно из четырёх цифр. Полный номер карты, срок действия, PIN и код безопасности не запрашиваются и не сохраняются как реквизиты этой возможности.

Каждая карта MUST иметь собственную идентичность, баланс и историю независимо от совпадения банка, названия или последних цифр. Пользователь SHALL иметь возможность изменить указанные сведения о своей карте с теми же ограничениями. Финансовые изменения начального остатка и даты начала подчиняются отдельным требованиям исправления и архива.

Scenario: Добавление карты с начальным остатком

  • WHEN пользователь сохраняет карту с допустимым названием, датой начала учёта и начальным остатком 10 000 ₽
  • THEN карта появляется в его списке с балансом 10 000 ₽ и без вручную внесённых операций

Scenario: Необязательные сведения не заполнены

  • WHEN пользователь добавляет карту без названия банка и последних четырёх цифр
  • THEN отсутствие этих сведений не препятствует сохранению карты при корректности обязательных значений

Scenario: Несколько карт одного банка

  • WHEN пользователь добавляет две карты одного банка с одинаковым названием и начальными остатками 1 000 ₽ и 2 000 ₽
  • THEN система сохраняет две самостоятельные карты с раздельными балансами и историей

Scenario: Некорректные сведения карты

  • WHEN название после удаления внешних пробелов пустое или длиннее 100 символов, название банка длиннее 100 символов либо указанные последние цифры не состоят ровно из четырёх цифр
  • THEN система отклоняет сохранение, объясняет ошибку соответствующего поля и не создаёт или не изменяет карту

Requirement: Точные денежные значения и проверка до сохранения

Система MUST принимать денежные значения только в RUB, с точностью не более двух знаков после десятичного разделителя, без округления введённой суммы. Сумма расхода, пополнения, перевода или возврата MUST быть строго положительной и не превышать 92 233 720 368 547 758,07 ₽. Начальный остаток MUST находиться в диапазоне от −92 233 720 368 547 758,08 ₽ до 92 233 720 368 547 758,07 ₽ включительно; ноль и отрицательный начальный остаток допустимы.

Значение, не представляющее корректную денежную сумму, неподдерживаемая валюта, лишние десятичные знаки или выход за диапазон MUST приводить к понятной ошибке без частичного сохранения. Применение ограничения к каждому денежному вводу MUST NOT приводить к округлению, переполнению или ограничению точного результата сложения уже сохранённых сумм.

Scenario: Нулевой и отрицательный начальный остаток

  • WHEN пользователь сохраняет отдельные карты с начальными остатками 0 ₽ и −100 ₽
  • THEN система сохраняет обе карты и показывает соответствующие известные балансы

Scenario: Неположительная сумма операции

  • WHEN пользователь пытается внести операцию с суммой 0 ₽ или −1 ₽
  • THEN система отклоняет операцию, сохраняя прежние историю, балансы и сводки

Scenario: Точность не исправляется округлением

  • WHEN пользователь пытается сохранить начальный остаток или операцию со значением 1,001 ₽
  • THEN система сообщает о допустимых двух десятичных знаках и не заменяет введённое значение на 1 ₽

Scenario: Границы денежных значений

  • WHEN пользователь указывает верхнюю допустимую сумму операции или нижний допустимый начальный остаток
  • THEN сами эти граничные значения принимаются, а выход за соответствующую границу на одну копейку отклоняется без изменения данных

Scenario: Некорректная сумма или валюта

  • WHEN поступает попытка сохранить нечисловую сумму либо сумму в валюте, отличной от RUB
  • THEN система объясняет ошибку и не сохраняет карту, операцию или изменение существующих данных

Requirement: Даты начала учёта и ручных операций

Дата начала учёта и дата операции MUST быть существующими календарными датами без времени. Сегодня определяется по Europe/Moscow. Дата начала учёта MUST NOT быть позже сегодняшней даты. Начальный остаток означает деньги до всех вручную внесённых операций начиная с указанной даты включительно.

Дата операции MUST находиться между датой начала учёта затронутой карты и сегодняшним днём включительно. Для перевода ограничение MUST выполняться для обеих карт. Ввод прошедших операций внутри этого интервала SHALL быть доступен. Необязательное описание операции MUST содержать не более 500 символов. Сохранённая календарная дата MUST NOT сдвигаться при смене часового пояса устройства.

Scenario: Операция в первый день учёта

  • WHEN при начальном остатке 1 000 ₽ на 1 сентября пользователь вносит расход 100 ₽ с датой 1 сентября
  • THEN расход учитывается после начального остатка и баланс становится 900 ₽

Scenario: Прошедшая операция

  • WHEN пользователь вносит операцию задним числом не раньше начала учёта и не позже сегодняшнего дня
  • THEN операция сохраняется с указанной датой и изменяет текущий баланс и сводку соответствующего месяца

Scenario: Недопустимая дата или описание

  • WHEN дата не существует, находится до начала учёта или в будущем либо описание длиннее 500 символов
  • THEN система объясняет ошибку и не сохраняет изменение

Scenario: Перевод до начала учёта получателя

  • WHEN дата перевода допустима для карты-источника, но предшествует началу учёта карты-получателя
  • THEN перевод отклоняется без изменения обеих карт

Scenario: Московская календарная граница

  • WHEN на устройстве ещё предыдущий день, а в Europe/Moscow уже наступил следующий
  • THEN допустимая сегодняшняя дата и текущий месяц определяются по Europe/Moscow, а сохранённые даты операций остаются прежними

Requirement: Баланс определяется начальным остатком и внесёнными операциями

Баланс карты MUST равняться начальному остатку плюс денежное влияние всех её сохранённых и не удалённых операций: пополнение и возврат увеличивают баланс, расход уменьшает, перевод уменьшает баланс источника и увеличивает баланс получателя. Начальный остаток MUST учитываться ровно один раз и MUST NOT считаться расходом, возвратом или пополнением.

Баланс MUST рассчитываться точно и может быть отрицательным. Недостаточный учётный остаток MUST NOT препятствовать сохранению корректного расхода или перевода. Баланс MUST NOT зависеть от выбранного месяца, категории, показанной страницы истории или нахождения карты в архиве.

Система SHALL представлять баланс как результат ручного учёта и MUST NOT выдавать его за подтверждённый банком остаток или обещать полноту невнесённых операций. Отдельной произвольной операции корректировки баланса SHALL NOT быть.

Scenario: Последовательные расходы с копейками

  • WHEN при начальном остатке 1 ₽ сохраняются расходы 0,10 ₽ и 0,20 ₽
  • THEN баланс составляет ровно 0,70 ₽

Scenario: Полное расходование и отрицательный остаток

  • WHEN при балансе 100 ₽ пользователь сохраняет расход 100 ₽, затем расход 50 ₽
  • THEN первая операция даёт известный баланс 0 ₽, вторая сохраняется и даёт баланс −50 ₽

Scenario: Начальный остаток и страница истории

  • WHEN при начальном остатке 1 000 ₽ и расходах 100 ₽ и 200 ₽ пользователь повторно открывает карту и видит только расход 200 ₽
  • THEN текущий баланс остаётся 700 ₽: учитываются обе операции, а начальный остаток не добавляется повторно

Scenario: Итог превышает границу отдельного ввода

  • WHEN на карту с начальным остатком 92 233 720 368 547 758,07 ₽ сохраняется пополнение 0,01 ₽
  • THEN баланс составляет точно 92 233 720 368 547 758,08 ₽ без переполнения, округления или ограничения верхней границей ввода

Requirement: Ручное внесение расходов и пополнений

Система SHALL позволять вручную внести расход или пополнение по своей активной карте с суммой, датой и необязательным описанием. Расход MUST иметь категорию; если пользователь не выбирает другую, применяется его категория по умолчанию. После успешного сохранения операция MUST появиться в истории соответствующей карты и ровно один раз изменить её баланс.

Расход SHALL уменьшать баланс на свою сумму, пополнение SHALL увеличивать. Категория MUST NOT изменять денежное влияние расхода. Пополнение MUST NOT включаться в расходы, возвраты или пассивный инвестиционный доход.

Scenario: Расход с категорией

  • WHEN при балансе 10 000 ₽ пользователь сохраняет расход 500 ₽ с категорией «Кафе»
  • THEN история показывает расход 500 ₽ с этой категорией, а баланс составляет 9 500 ₽

Scenario: Категория по умолчанию

  • WHEN пользователь сохраняет расход, не выбрав другую категорию
  • THEN расход сохраняется с принадлежащей пользователю категорией по умолчанию и отображается под её текущим названием

Scenario: Пополнение карты

  • WHEN при балансе −500 ₽ пользователь сохраняет пополнение 2 000 ₽
  • THEN баланс становится 1 500 ₽, история содержит пополнение, а расходы и возвраты не меняются

Scenario: Независимость другой карты

  • WHEN при балансах A 1 000 ₽ и B 2 000 ₽ пользователь сохраняет расход 125,50 ₽ по A
  • THEN баланс A становится 874,50 ₽, а баланс и история B остаются прежними

Requirement: Перевод между своими картами как одно действие

Система SHALL позволять одним действием внести перевод между двумя разными активными картами одного пользователя. Сумма перевода MUST одинаково уменьшать баланс источника и увеличивать баланс получателя. История каждой стороны MUST показывать направление и другую карту этого перевода.

Перевод MUST сохраняться, исправляться и удаляться целиком для обеих сторон. Нельзя сохранять только одну сторону, переводить на ту же или чужую карту. Перевод MUST NOT увеличивать расходы или возвраты в сводках одной или всех карт.

Scenario: Перевод между картами

  • WHEN при балансах A 5 000 ₽ и B 1 000 ₽ пользователь сохраняет перевод 2 000 ₽ из A в B
  • THEN баланс A становится 3 000 ₽, баланс B — 3 000 ₽, обе истории показывают стороны одного перевода, а расходы не меняются

Scenario: Недостаточный остаток источника

  • WHEN при балансах A 100 ₽ и B 0 ₽ пользователь сохраняет перевод 150 ₽ из A в B
  • THEN перевод сохраняется целиком, баланс A становится −50 ₽, а баланс B — 150 ₽

Scenario: Недопустимый получатель

  • WHEN получателем указана та же карта или карта другого пользователя
  • THEN система отклоняет перевод без изменения историй, балансов и сводок

Scenario: Сбой сохранения перевода

  • WHEN перевод не удаётся сохранить
  • THEN система не подтверждает успех и не оставляет сохранённой только одну его сторону

Requirement: Возврат связан с исходным расходом

Возврат SHALL ссылаться на существующий не удалённый расход той же карты. При внесении возврата пользователь SHALL иметь возможность выбрать исходный расход карты за любой период учёта, независимо от месяца открытого журнала. Частичные и несколько возвратов допустимы; сумма всех не удалённых возвратов MUST NOT превышать текущую сумму исходного расхода. Дата возврата MUST быть не раньше даты расхода и соответствовать общим ограничениям даты карты. При редактировании возврата проверка общей возвращённой суммы MUST заменять его прежнюю сумму новой, не учитывая обе версии одновременно.

Возврат MUST увеличивать баланс карты и наследовать категорию исходного расхода. Отдельный выбор категории возврата SHALL NOT предоставляться. Изменение категории исходного расхода MUST менять категорию связанных возвратов и результаты затронутых месячных сводок, сохраняя их суммы и даты.

Scenario: Частичные возвраты

  • WHEN по расходу 1 000 ₽ пользователь последовательно сохраняет возвраты 300 ₽ и 700 ₽
  • THEN оба возврата связаны с расходом, увеличивают баланс суммарно на 1 000 ₽ и используют его категорию

Scenario: Выбор покупки прошлого месяца

  • WHEN пользователь открывает сентябрьский журнал и вносит возврат по августовской покупке
  • THEN форма позволяет найти и выбрать исходный августовский расход этой карты без ограничения сентябрьским журналом

Scenario: Исправление полностью возвращённой покупки

  • WHEN по расходу 500 ₽ сохранён единственный возврат 500 ₽ и пользователь изменяет только описание возврата
  • THEN исправление сохраняется: общая возвращённая сумма остаётся 500 ₽, а прежняя и новая версии не складываются как два возврата

Scenario: Превышение возвращаемой суммы

  • WHEN при расходе 1 000 ₽ и сохранённом возврате 800 ₽ пользователь пытается сохранить ещё 300 ₽
  • THEN система отклоняет новый возврат без изменения существующих записей и сумм

Scenario: Недопустимая связь или дата возврата

  • WHEN возврат ссылается на удалённую операцию, операцию другого вида или другой карты либо датирован раньше расхода
  • THEN система отклоняет возврат без изменения истории, баланса и сводок

Scenario: Изменение категории покупки

  • WHEN пользователь меняет категорию расхода с «Покупки» на «Здоровье», а возврат по нему произошёл в другом месяце
  • THEN расход и возврат учитываются в категории «Здоровье» в своих месяцах с прежними денежными суммами

Requirement: Общие для пользователя категории расходов

Система SHALL предоставлять каждому пользователю собственный общий для всех его карт список категорий: «Продукты», «Кафе», «Такси», «Транспорт», «Жильё», «Здоровье», «Покупки», «Развлечения», «Другое». «Другое» является начальной категорией по умолчанию. Категории MUST быть доступны до первого расхода. Начальный набор MUST создаваться при успешном добавлении первой карты пользователя и не дублироваться при добавлении следующих карт.

Пользователь SHALL иметь возможность добавлять и переименовывать любые свои категории, включая начальные. Название MUST содержать от 1 до 80 символов после удаления внешних пробелов и быть уникальным среди категорий пользователя без учёта регистра. Переименование MUST сохранять идентичность категории: все старые расходы, возвраты и сводки показывают её текущее название. Переименование категории по умолчанию MUST сохранять её роль. Удаление категорий и вложенные подкатегории SHALL NOT предоставляться в V1.

Scenario: Начальный набор и повторный вход

  • WHEN пользователь сохраняет первую карту, затем добавляет следующую карту и повторно входит в приложение
  • THEN после первой карты ему доступен начальный набор категорий до первого расхода, а дальнейшие действия не создают дубликатов

Scenario: Категория доступна на всех своих картах

  • WHEN пользователь создаёт категорию «Питомец»
  • THEN она доступна для расходов по всем его картам и не появляется у другого пользователя

Scenario: Переименование используемой начальной категории

  • WHEN пользователь переименовывает категорию «Другое» в «Остальное»
  • THEN прежние записи отображаются как «Остальное», суммы не меняются, а новые расходы без другого выбора используют ту же категорию «Остальное»

Scenario: Дублирующееся или недопустимое название

  • WHEN пользователь пытается добавить « кафе » при существующей «Кафе», сохранить пустое после удаления пробелов название или название длиннее 80 символов
  • THEN система объясняет ошибку и не изменяет список категорий

Requirement: Исправление и удаление операций с согласованным пересчётом

Пользователь SHALL иметь возможность изменить сумму, дату, описание и применимые реквизиты сохранённой операции либо удалить её. Вид операции и связь возврата с исходным расходом MUST быть неизменны после создания. Все затронутые карты MUST быть активны; каждое исправление MUST соблюдать те же денежные, календарные и пользовательские ограничения, что и создание.

Обычный расход или пополнение SHALL быть переносимы на другую свою активную карту. Расход с не удалёнными возвратами MUST NOT переноситься на другую карту, удаляться, уменьшаться ниже суммы возвратов или сдвигаться на дату позже любого связанного возврата. Для такого действия сначала исправляются или удаляются мешающие возвраты. Возврат остаётся на карте исходного расхода.

Исправление или удаление MUST пересчитать балансы, историю и все затронутые месячные сводки; изменение даты переносит влияние между соответствующими месяцами. Исправление сторон перевода MUST учитывать его прежние и новые карты. Удаление перевода MUST удалять обе стороны одним действием. Повторное удаление уже удалённой собственной операции SHALL быть безрезультатным успешным действием без повторного денежного влияния.

Scenario: Изменение суммы и месяца расхода

  • WHEN пользователь меняет расход 100 ₽ за август на 150 ₽ за сентябрь
  • THEN баланс дополнительно уменьшается на 50 ₽, август перестаёт учитывать 100 ₽, а сентябрь учитывает 150 ₽

Scenario: Перенос пополнения на другую карту

  • WHEN пользователь переносит пополнение 500 ₽ с A на свою активную B с допустимой датой
  • THEN баланс A уменьшается на 500 ₽, баланс B увеличивается на 500 ₽, а запись относится к B

Scenario: Изменение получателя перевода

  • WHEN перевод 200 ₽ из A в B исправляется на перевод 300 ₽ из A в C и все три карты активны
  • THEN прежнее влияние на B отменяется, A уменьшается дополнительно на 100 ₽, C увеличивается на 300 ₽, а обе стороны исправленного перевода сохраняются согласованно

Scenario: Исправление расхода ограничено возвратами

  • WHEN для расхода 1 000 ₽ существует возврат 600 ₽ и пользователь пытается удалить расход, перенести его на другую карту, уменьшить до 500 ₽ или сдвинуть позже даты возврата
  • THEN система отклоняет действие и объясняет необходимость сначала исправить связанные возвраты

Scenario: Удаление возврата и затем расхода

  • WHEN пользователь удаляет все возвраты расхода, а затем сам расход
  • THEN влияние каждой удалённой записи отменяется в балансе и в её месячной сводке, а ограничение удаления исходного расхода больше не действует

Scenario: Удаление перевода из любой стороны

  • WHEN пользователь удаляет перевод из истории получателя и затем повторяет удаление
  • THEN обе стороны удаляются при первом действии, денежное влияние отменяется один раз, а повторное удаление ничего не меняет

Scenario: Смена вида или исходного расхода

  • WHEN пользователь пытается превратить расход в пополнение либо связать существующий возврат с другим расходом
  • THEN изменение отклоняется без подмены вида операции или её исходной связи

Requirement: Исправление начала ручного учёта

Пользователь SHALL иметь возможность изменить начальный остаток и дату начала учёта активной карты. Изменение MUST проверять всю затронутую сохранённую историю, включая стороны переводов и связанные возвраты, и отклоняться без частичного результата, если после него какая-либо операция нарушает действующие ограничения. Удалённые операции не ограничивают новую дату.

Изменение начального остатка SHALL пересчитывать текущий баланс, сохраняя суммы, даты и категории операций. Оно MUST NOT создавать отдельную операцию или изменять месячные расходы и возвраты. Пропущенное движение SHALL исправляться внесением реальной операции с допустимой датой.

Scenario: Исправление начального остатка

  • WHEN при начальном остатке 1 000 ₽ и расходах 300 ₽ пользователь меняет начальный остаток на 1 200 ₽
  • THEN баланс становится 900 ₽, история операций не пополняется корректировкой, а месячные расходы остаются 300 ₽

Scenario: Новое начало противоречит истории

  • WHEN пользователь переносит дату начала учёта с 1 на 10 сентября, а по карте сохранён расход или перевод от 5 сентября
  • THEN система отклоняет изменение и сохраняет прежние дату, баланс и всю историю

Requirement: Архив карт сохраняет историю

Пользователь SHALL иметь возможность архивировать свою карту при любом балансе, включая положительный, нулевой и отрицательный, и вернуть её из архива. Архивация MUST NOT менять деньги или операции. Обычный список SHALL показывать активные карты; переключатель архива SHALL открывать архивные карты и их историю.

Пока карта архивна, система MUST запрещать создание, редактирование и удаление затрагивающих её операций, а также изменение начального остатка и даты начала учёта. Для исправления или новой операции карту сначала возвращают из архива. Перевод или возврат MUST требовать активного состояния всех затронутых карт. Изменение описательных сведений карты само по себе не является финансовой операцией.

Архивная карта MUST продолжать участвовать в исторических сводках «Все карты». Безвозвратное удаление карты SHALL NOT предоставляться в первой версии.

Scenario: Архивирование с ненулевым остатком

  • WHEN пользователь архивирует карту с балансом −500 ₽ и сохранённой историей
  • THEN карта скрывается из обычного списка, остаётся доступной через переключатель архива с теми же балансом и историей и сохраняет вклад в сводки всех карт

Scenario: Финансовое действие по архивной карте

  • WHEN пользователь пытается внести, исправить или удалить операцию либо изменить начало учёта архивной карты
  • THEN система отклоняет действие без изменения данных и объясняет необходимость вернуть карту из архива

Scenario: Возврат или перевод затрагивает архив

  • WHEN возврат относится к расходу архивной карты либо хотя бы одна прежняя или новая сторона изменяемого перевода архивна
  • THEN финансовое действие отклоняется до возвращения всех затронутых карт из архива

Scenario: Возвращение карты

  • WHEN пользователь возвращает карту из архива
  • THEN она снова видна в обычном списке с сохранёнными данными и допускает финансовые действия по общим правилам

Requirement: Месячный журнал и сводка расходов по категориям

Система SHALL показывать историю и месячную сводку выбранной карты, а также месячную сводку всех банковских карт пользователя. По умолчанию выбирается текущий месяц Europe/Moscow; пользователь SHALL иметь возможность явно выбрать другой месяц. История карты и её сводка MUST использовать один выбранный месяц. Операции SHALL отображаться по убыванию календарной даты со стабильным порядком внутри даты при повторном чтении неизменившихся данных и доступом к следующим страницам истории.

Сводка MUST показывать расходы, возвраты и итог после возвратов, а также соответствующие суммы по категориям. Расход входит в месяц собственной даты, возврат — в месяц даты возврата, даже если покупка была раньше. Итог MUST равняться расходам минус возвраты и может быть отрицательным. Начальные остатки, пополнения и переводы MUST NOT включаться в эти суммы.

Сводка MUST учитывать все подходящие не удалённые записи, а не только текущую страницу истории. «Все карты» MUST включать активные и архивные карты владельца. Выбор месяца и охвата MUST NOT изменять текущие балансы. При отсутствии расходов и возвратов SHALL показываться известные нули.

Scenario: Месяц по умолчанию и переключение

  • WHEN пользователь открывает карту в сентябре по Europe/Moscow и затем выбирает август
  • THEN сначала история и сводка относятся к сентябрю, после выбора обе относятся к августу, а текущий баланс остаётся прежним

Scenario: Сводка расходов и переводов

  • WHEN в месяце сохранены расходы «Кафе» 500 ₽ и «Такси» 300 ₽, пополнение 2 000 ₽ и перевод 1 000 ₽ без возвратов
  • THEN сводка показывает расходы 800 ₽, возвраты 0 ₽, итог 800 ₽ и категории 500 ₽ и 300 ₽ без включения пополнения или перевода

Scenario: Возврат покупки прошлого месяца

  • WHEN расход «Покупки» 1 000 ₽ совершён в августе, возврат 600 ₽ — в сентябре и других операций этих видов нет
  • THEN август показывает расходы 1 000 ₽, возвраты 0 ₽ и итог 1 000 ₽, а сентябрь — расходы 0 ₽, возвраты 600 ₽ и итог −600 ₽ в той же категории

Scenario: Все карты включают архивные

  • WHEN расходы активной A за месяц составили 800 ₽, архивной B — 200 ₽, а между ними ранее был перевод 1 000 ₽
  • THEN общая сводка показывает расходы 1 000 ₽ и не добавляет перевод, независимо от переключателя архива в списке карт

Scenario: Несколько страниц и равные даты

  • WHEN операции выбранного месяца занимают несколько страниц и часть операций имеет одинаковую дату
  • THEN пользователь может просмотреть все страницы в стабильном порядке, а сводка и баланс учитывают все соответствующие записи независимо от открытой страницы

Requirement: Изоляция пользовательских финансовых данных

Карты, категории, операции и связи между ними MUST принадлежать локальному пользователю WealthLedger. Система MUST предоставлять чтение, создание, изменение, удаление, архив и восстановление только владельцу с действующей сессией. Идентификатор сам по себе MUST NOT подтверждать доступ.

Проверка владельца MUST распространяться на каждую упомянутую карту, категорию и операцию, включая обе стороны перевода, исходный расход возврата и новые связи при редактировании. Отказ MUST NOT раскрывать чужие финансовые сведения или менять данные любой стороны. Сводки MUST включать только данные текущего пользователя.

Scenario: Нет действующей сессии

  • WHEN пользователь без действующей сессии пытается прочитать карты, сводку или изменить финансовые данные
  • THEN система отказывает в доступе без раскрытия и изменения сохранённых данных

Scenario: Чужая карта или категория

  • WHEN пользователь обращается к чужой карте или пытается назначить своей операции чужую категорию
  • THEN система отклоняет действие, не раскрывает чужие сведения и не меняет баланс, историю или категории

Scenario: Чужая связь перевода или возврата

  • WHEN пользователь подставляет чужую карту в перевод или чужой расход в возврат при создании либо редактировании
  • THEN система отклоняет всё действие без сохранения частичного результата и без раскрытия данных другого пользователя

Scenario: Чужие исправления и архив

  • WHEN пользователь пытается изменить или удалить чужую операцию, переименовать чужую категорию либо архивировать или восстановить чужую карту
  • THEN система отказывает и сохраняет все данные владельца без изменений

Requirement: Согласованные изменения и защита повторной отправки

Успешное финансовое изменение MUST согласованно сохранять операции, связи, денежное влияние и пересчитанные результаты для всех затронутых карт. Система MUST NOT подтверждать успех несохранённого действия или оставлять частичный результат. Независимые одновременные операции MUST сохранять влияние каждой успешной записи, не затирая друг друга.

Повторная отправка одного намерения создать карту или операцию с тем же содержанием MUST приводить не более чем к одному соответствующему результату и однократному учёту начального остатка или денежного влияния операции. Иное содержание при повторном использовании того же намерения MUST приводить к конфликту без изменения данных. После исправления или удаления результата поздняя повторная отправка исходного создания MUST NOT восстанавливать старые значения, удалённую запись или её денежное влияние. Ответ на повтор MUST показывать актуальное состояние ранее созданной карты или операции либо сообщать, что операция удалена, не выдавая старые данные за текущий результат. Уже сохранённое намерение MUST NOT проверяться как новая финансовая запись и повторно расходовать доступную сумму возврата. Архивирование ранее созданной карты MUST NOT превращать повтор её создания в создание другой карты или возвращение из архива.

Редактирование или удаление существующей операции и изменение карты, основанные на её устаревшем состоянии, MUST завершаться понятным конфликтом без перезаписи более свежих изменений. Повторное удаление уже удалённой операции не считается перезаписью и остаётся безрезультатным действием. Проверки суммы возвратов, состояния архива, дат и других ограничений MUST сохраняться при одновременных действиях.

Scenario: Повтор после потерянного ответа

  • WHEN создание расхода 100 ₽ успешно сохранено, ответ потерян и пользователь повторяет то же намерение с тем же содержанием
  • THEN история содержит один расход, баланс изменён один раз, а повтор не создаёт дубликат

Scenario: Повтор создания карты после архивации

  • WHEN карта с начальным остатком 1 000 ₽ создана, ответ потерян, карта затем архивирована и приходит повтор её исходного создания
  • THEN система показывает актуальную архивную карту, не создаёт вторую карту, не дублирует начальный остаток и не возвращает карту из архива

Scenario: Повтор полного возврата

  • WHEN возврат 500 ₽ полностью покрывает расход 500 ₽, успешно сохранён, но ответ потерян и пользователь повторяет то же намерение создания возврата
  • THEN система возвращает сохранённый возврат без новой записи, повторного изменения баланса или ошибки о превышении суммы покупки

Scenario: Изменённое содержание того же намерения

  • WHEN после сохранения расхода 100 ₽ повтор того же намерения содержит сумму 200 ₽
  • THEN система сообщает о конфликте и сохраняет исходный результат без дополнительной операции

Scenario: Позднее создание после исправления или удаления

  • WHEN сохранённый расход 100 ₽ исправлен на 150 ₽ или удалён, после чего приходит повтор его исходного создания
  • THEN система показывает актуальный расход 150 ₽ либо сообщает об удалении; прежняя сумма или удалённая запись не восстанавливаются и дополнительного денежного влияния нет

Scenario: Два независимых расхода

  • WHEN при балансе 1 000 ₽ расходы 100 ₽ и 200 ₽ успешно сохраняются одновременно как разные намерения
  • THEN история содержит оба расхода, а баланс составляет 700 ₽

Scenario: Два возврата превышают покупку вместе

  • WHEN для расхода 1 000 ₽ одновременно сохраняются независимые возвраты 600 ₽ и 600 ₽
  • THEN система не допускает общей сохранённой суммы 1 200 ₽: конфликтующее действие отклоняется без частичного результата

Scenario: Исправление по устаревшим данным

  • WHEN операция или карта изменилась после того, как пользователь начал её редактирование, а он отправляет изменение прежнего состояния
  • THEN система сообщает о конфликте, сохраняет более свежие данные и позволяет получить актуальное состояние для нового исправления

Scenario: Удаление по устаревшим данным

  • WHEN пользователь пытается удалить существующую операцию, которую другой успешный запрос уже изменил
  • THEN система отклоняет устаревшее удаление без потери более свежего исправления

Scenario: Архивирование одновременно с финансовым изменением

  • WHEN архивирование карты пересекается с созданием или исправлением затрагивающей её операции
  • THEN результаты соответствуют последовательному выполнению допустимых действий и не допускают финансовую запись, выполненную после вступившего в силу архива

Requirement: Сохранённый ручной учёт переживает повторный вход и перезапуск

Успешно сохранённые карты, начальные остатки, даты начала, операции, категории, архивное состояние и связи возвратов и переводов MUST оставаться доступными владельцу после обновления страницы, выхода, повторного входа и обычного перезапуска приложения. Эти действия MUST NOT создавать дубликаты, восстанавливать удалённые операции или сбрасывать балансы. Защита повторной отправки сохранённых намерений MUST сохранять действие после этих переходов.

Scenario: Повторный вход

  • WHEN пользователь выходит и снова входит после сохранения карты с начальным остатком 1 000 ₽ и расходом 100 ₽
  • THEN он видит ту же карту, дату начала, расход с его текущей категорией и баланс 900 ₽

Scenario: Перезапуск с переводом, возвратом и архивом

  • WHEN приложение перезапускается после сохранения переводов, связанных возвратов, переименования категорий и архивирования карты
  • THEN владелец получает прежние связи, названия, архивное состояние и рассчитанные суммы без дубликатов или потери истории

Scenario: Повтор создания после перезапуска

  • WHEN после перезапуска повторяется намерение создания ранее сохранённой, а затем удалённой операции
  • THEN удалённая операция и её денежное влияние не восстанавливаются

Requirement: Доступные формы и различимые состояния без ложного успеха

Раздел SHALL различать отсутствие карт, отсутствие операций за месяц, известный нулевой баланс, загрузку и ошибку получения данных. Ошибка MUST NOT подменяться пустым списком или нулевой суммой. Пользователь SHALL иметь возможность повторить неуспешную загрузку.

Формы карт, операций, исправлений и категорий MUST иметь доступные названия полей и действий, управление с клавиатуры и понятные ошибки, связанные с соответствующим полем или действием. Обязательность, направление денег и ошибки MUST NOT передаваться только цветом.

Во время сохранения, ошибки и конфликта введённый черновик MUST оставаться доступным до явного закрытия или успешного завершения. Пока результат сохранения не подтверждён, система MUST NOT показывать введённую сумму как подтверждённый новый баланс или выдавать действие за успешное. При неопределённом результате после потери ответа повтор SHALL использовать защиту того же намерения создания. После подтверждённого изменения отображаемые история, балансы и сводки MUST согласовываться с сохранёнными данными всех затронутых карт.

Scenario: Пустой список

  • WHEN раздел успешно загружен и у пользователя ещё нет карт
  • THEN система показывает штатное пустое состояние с доступным действием добавления карты

Scenario: Нет операций при ненулевом балансе

  • WHEN в выбранном месяце нет операций, но текущий баланс карты составляет 10 000 ₽
  • THEN история сообщает об отсутствии операций за месяц, сводка содержит нули, а текущий баланс остаётся 10 000 ₽

Scenario: Ошибка загрузки

  • WHEN загрузка карт, истории или сводки завершается ошибкой
  • THEN система показывает ошибку и действие повтора, не выдавая её за отсутствие данных или нулевой результат

Scenario: Доступный ввод и проверка

  • WHEN пользователь управляет формой с клавиатуры или средством чтения экрана и вводит некорректную сумму
  • THEN названия полей и действий доступны, ошибка суммы определима без цвета, а введённые значения остаются для исправления

Scenario: Ожидание и неопределённый результат

  • WHEN пользователь отправляет перевод и ответ ещё не получен либо потерян
  • THEN черновик сохраняется, неподтверждённые балансы не выдаются за сохранённые, а повтор того же создания не порождает второй перевод

Scenario: Ошибка или конфликт исправления

  • WHEN сохранение исправления отклоняется из-за ошибки или устаревшего состояния
  • THEN пользователь видит причину, сохраняет черновик и может получить актуальные данные для следующего действия без перезаписи чужого успешного изменения