Как работает бюджет в Unitcore
Бюджет — первый продукт Unitcore, и правило у него то же, что у остальных: всё живёт внутри пространства и видно всем, кто в нём есть. В личном это ваши траты, в семейном — общие, где не нужно пересылать друг другу скриншоты, чтобы понять, куда ушёл месяц.
Ниже — от простого к сложному: из чего собрана запись о деньгах, что показывает дашборд, что умеет ассистент и как это устроено внутри.
Из чего собрана запись
Транзакция — это один поход в магазин, а не одна сумма. Внутри неё позиции, и категория стоит у каждой позиции отдельно.
Один чек — три строки и три разные категории. Если бы категория стояла на всей транзакции, «Еда» и «Дом» пришлось бы записывать двумя походами в один и тот же магазин.
Категории заводятся в пространстве и бывают двух видов: расход и доход. Больше у категории ничего нет — ни лимита, ни цвета, ни вложенности.
Суммы всегда положительные
В базе нет ни одного отрицательного числа: сумма — это всегда «сколько», а знак берётся из категории. Зарплата с категорией-доходом приходит плюсом, продукты с категорией-расходом — минусом, и считает это сама база, а не приложение.
Деньги без категории считаются расходом. Это не забытая ветка, а решение: чаще всего строка без категории — это трата, которую не успели разложить, и она должна портить итог месяца, а не тихо исчезать из него.
Отсюда же следует, что удаление категории не удаляет деньги: позиции остаются на месте и просто теряют ярлык. Пересобрать категории можно в любой момент, история от этого не пострадает.
Что показывает дашборд
Карточка на каждую валюту. Валюты никогда не складываются — курсов в проекте нет вообще, и общая цифра «потрачено» была бы выдуманной.
Каждая карточка отвечает на одни и те же четыре вопроса про свою валюту: сколько в этом месяце, как это по сравнению с прошлым, как выглядели последние полгода и на что именно ушло. Категорий в списке пять, остальное сворачивается в «Прочее».
Ниже — последние операции из всех ваших пространств сразу. По умолчанию дашборд показывает всё вместе; фильтр сверху сужает его до одного пространства.
Как записать трату руками
Форма открывается прямо с карточки: название, дата, валюта и строки — по одной на покупку. Категорию можно создать не выходя из формы, вместе с выбором «расход или доход». Открыв существующую транзакцию, её же можно исправить или удалить.
Удаление транзакции необратимо. Корзины, как у задач, здесь нет: транзакция уходит вместе со всеми своими позициями и не возвращается. Это единственное действие в бюджете, которое нельзя отменить.
То же самое — ассистенту
Два решения, которые стоит назвать вслух:
- Право на запись выдаётся каждому подключению отдельно. Незнакомый клиент, который добрался до вашего аккаунта, может только читать — писать он не сможет, пока вы сами не разрешите это в настройках подключений.
- Транзакция без позиций не остаётся. Если позиции не записались, ассистент удаляет созданную транзакцию сам: пустая запись в бюджете хуже, чем её отсутствие.
Как это устроено внутри
Отдельная схема в базе и никакого промежуточного слоя приложения: и веб, и ассистент ходят в одну и ту же базу под вашим собственным пользователем, а решает, что вам видно, сама база.
Читают готовое представление, пишут прямо в таблицы.
Те же запросы, тот же пользователь — просто вызванные словами.
Одна проверка на все таблицы, список колонок, которые вообще нельзя записать, и отдельный рубеж для подключённых ассистентов.
Три таблицы и триггеры, которые держат правила: позиции наследуют дату и валюту от своей транзакции, сумма всегда больше нуля.
Представление подставляет имя категории и знак суммы — чтобы клиент не считал это сам и не считал по-своему.
Подробности для тех, кому интересен SQL
- Позиция ссылается на транзакцию и категорию парой
(id, space_id). Поэтому категория из чужого пространства структурно невозможна, без единой проверки в коде. - Синхронизация только сверху вниз: дата и валюта живут на транзакции, а позиции их наследуют. Изменили дату у чека — уехали все его строки; обратно это не работает, и колонки на запись позиции просто не выданы.
amount numeric(14,4). Три знака после точки нужны фактически — у динара и риала минорная единица тысячная; четвёртый оставлен под остатки от деления счёта между участниками и под конвертацию, чтобы сумма строк сходилась с итогом чека.- Валюта — это домен с проверкой
^[A-Z]{3}$, а не список: продукт не решает за вас, в чём вы считаете. - «Кто записал» проставляется из вашей сессии и клиенту на запись не выдано.
- Дашборд читает полгода постранично: PostgREST отдаёт не больше 1000 строк за запрос независимо от лимита в коде.
Чего пока нет
Нет курсов валют и конвертации, регулярных платежей, счетов и остатков на них, лимитов по категориям, импорта банковских выписок и деления чека между участниками. Последнее спроектировано — под него в схеме уже оставлен запас точности.
Второй продукт поверх тех же пространств — задачи, и устроен он по тем же правилам: Как работают задачи в Unitcore. А если бюджет ещё не подключён к ассистенту, это пара минут: Подключите Unitcore к Claude.