Раньше предупреждение об отсутствии AURUM_BASIC_AUTH_USER/PASSWORD
было видно только в логах docker-контейнера при старте и терялось,
если пользователь забыл настроить .env. Теперь LoginGate различает
'авторизован' и 'auth вообще не настроен' и во втором случае
показывает заметный баннер прямо в интерфейсе.
На Firefox (Linux) программный .click() по input[type=file] со стилем
display:none может не открывать нативный диалог выбора файла. Теперь
инпут скрыт визуально (opacity + нулевой размер), но остаётся в layout.
Все minor/patch-апдейты одного пакетного менеджера теперь идут одним
PR, а major-обновления — отдельным, чтобы не терялись среди рутинных
и не сыпало десятком PR одновременно.
Создание с дефолтным/кастомным risk_level, правка через PATCH
/assets/{id}, создание/правка/сброс network через PATCH
/crypto/holdings/{id}, partial-update и 404 на неизвестный id.
Ссылается на чек-лист из CONTRIBUTING.md вместо дублирования, плюс
пункт про реальную проверку (не просто "тесты прошли" на словах) и
про отсутствие личных данных/ключей в дифе.
Vitest для чистой логики: lib/format.ts (заодно поймал баг в
trimTrailingZeros на "-0.000") и расчёт разбивки по риску/монете/сети
на вкладке Crypto — вынес их из компонентов в отдельные тестируемые
функции. Тесты подключены в CI. Версия приложения обновлена до 1.1.6.
Свободное текстовое поле на холдинге — какой сети принадлежит конкретная
монета (Ethereum, Tron и т.д.), с редактированием прямо в таблице и
разбивкой % по сетям на отдельной вкладке. Помогает увидеть, что часть
стейблкоинов лежит на рискованных сетях и стоит перевести на безопасные.
Риск (низкий/средний/высокий) задаётся при добавлении монеты и
редактируется прямо в таблице холдингов. Добавлена вкладка "Уровни
риска" в карточке портфеля с разбивкой % по риск-тирам, только для
крипты. Заодно поправлено округление процентов в риск-карточках.
Монеты в таблице активов шли в алфавитном порядке (сортировка по
имени в SQL-запросе) — с импортированными портфелями это выглядело
хаотично. Теперь список отсортирован по cost basis (сколько денег
реально вложено в монету) от большего к меньшему; полностью
проданная позиция уходит в конец списка, а не в начало.
График во вкладке «Криптовалюта» теперь по умолчанию показывает всю
историю, а не 30 дней — короткое окно у портфеля, растущего годами,
часто попадало на локальную просадку и выглядело демотивирующе.
Плитки «Лучший рост» и «Худшее падение» раньше всегда считались за
24 часа. Теперь они следуют за диапазоном, выбранным у графика
(7д/30д/90д/всё время): для 7д/30д используются закэшированные данные
CoinGecko, для «всё время» — метрика за 1 год (дальше бесплатный
тариф CoinGecko историю не отдаёт), а для 90д — отдельный запрос по
каждой монете, который выполняется только когда пользователь реально
выбрал этот период.
Криптовалюту теперь можно разделять на отдельные портфели (например,
долгосрочные инвестиции, накопления на недвижимость, спекулятивные
монеты) вместо одного общего списка — по тому же принципу, что уже
есть у счетов (Account): у портфеля есть имя, цветная метка и
архивация, каждая монета обязательно принадлежит одному портфелю,
один и тот же токен можно держать в нескольких портфелях сразу.
Тип счёта investment не входил в CASH_ACCOUNT_TYPES, из-за чего деньги
на инвестсчёте не попадали ни в блок "Наличные", ни в класс "Инвестиции"
раздела Капитал — просто выпадали из расчёта net worth (issue #14).
Раньше пул-реквесты никак не проверялись автоматически — сломанный PR
можно было смержить, ничего бы не показало проблему. Теперь на каждый PR
и пуш в main гоняются 115 backend-тестов (реальный Postgres в сервис-
контейнере, та же схема, что и в tests/README.md) и сборка фронтенда
(tsc + vite build).
Discussions в репозитории не включены (has_discussions: false), поэтому
../../discussions отдавал 404 в README и CONTRIBUTING. Issues уже
включены и используются для багов — направляем туда же и предложения фич/
неочевидные случаи лицензии.
/api/health now returns a version field, default categories can be deleted
once unused, and split transactions (dividing one purchase across a parent
category's subcategories) were fully undocumented — added a dedicated
section in DOCS.md and a mention in README's feature list. Also fixed the
"update to newer code" command, which pulled a Docker image tag that
doesn't exist for locally-built services instead of the actual git repo.
Версии бэкенда и фронтенда были не синхронизированы и нигде не отображались
в интерфейсе — непонятно было, обновилось ли приложение после пересборки.
Теперь единая версия 1.1.0 приходит с бэкенда через /api/health и мелким
текстом выводится внизу страницы настроек.
Cloning on Windows with core.autocrlf=true converted
frontend/docker-entrypoint.d/20-basic-auth.sh to CRLF. Inside the container
the kernel read the shebang as /bin/sh\r, found no such interpreter, and
nginx crashed with exit 127 in a restart loop, so `docker compose up` never
produced a working web container.
- .gitattributes: LF for everything Linux executes or parses
- frontend/Dockerfile: sed strips CR at build time, covering checkouts made
before .gitattributes existed
FastAPI регистрировал встроенные роуты документации на дефолтных путях
(/docs, /openapi.json), в то время как все остальные роуты подключены с
префиксом /api и только его проксирует nginx. Теперь docs_url/redoc_url/
openapi_url явно указаны под /api, поэтому Swagger UI доступен там же,
где его и ищут (как заявлено в README).
Раньше стандартные категории нельзя было удалить вообще. Теперь можно —
если у категории нет ни одной обычной транзакции, ни строки сплита с
ней. Пользовательские категории удаляются как и раньше, без проверок.
- delete_category() проверяет Transaction.category_id и
TransactionSplit.category_id для стандартных категорий перед
удалением.
- Кнопка удаления в списке категорий больше не блокируется заранее для
стандартных — бэкенд решает, бейдж «стандартная» остался чисто
информационным.
- При отказе (400) показывается понятное сообщение вместо тихого
игнорирования клика.
Транзакция на подкатегории показывала только её имя — непонятно было,
что это подкатегория и к какой родительской категории она относится.
Фильтры по категории в Транзакциях и Отчётах тоже перечисляли
подкатегории наравне с родительскими, без иерархии.
- categoryLabels.ts: categoryPath() и buildHierarchicalCategories() —
общая логика, вынесенная из TransactionFormModal.
- Список транзакций и «Последние транзакции» на Dashboard показывают
счёт, затем родителя, затем подкатегорию.
- Фильтр категорий в Транзакциях сгруппирован по типу (как в Отчётах);
оба списка показывают подкатегорию с отступом под родителем.
Резерв под стрелку справа от суммы отъедал место у самой суммы даже
когда у категории нет подкатегорий — суммы во всей колонке смотрелись
сдвинутыми от правого края. Кнопка перенесена в начало строки, где
резерв под неё не мешает колонке сумм. Иконка заменена на SquareDivide
(та же, что уже используется для сплит-транзакций в списке).
Стрелка-шеврон появлялась только у строк с дочерними категориями, из-за
чего сумма в такой строке сдвигалась влево относительно остальных.
Теперь под стрелку всегда резервируется одинаковое место (пустое, если
разбивки нет), и суммы во всех строках выравниваются по одному краю.
Инлайн-раскрытие внутри строки при большом числе подкатегорий/сплитов
раздувало высоту строки и ломало вёрстку (донат на Dashboard и список
справа расходились по высоте). Теперь клик по шеврону открывает общее
модальное окно CategoryBreakdownModal со списком категорий-источников —
строка сама больше не меняет размер.
Раньше сплит позволял смешивать вообще любые категории. Реальная задача —
разложить одну покупку по подкатегориям ОДНОЙ базовой категории (100 € на
продукты: сколько на сладости, сколько на уход). А раз Dashboard и рейтинг
категорий сворачивают подкатегории в родителя, эта разбивка нигде не была
видна.
- _build_splits() требует, чтобы все категории сплита были одной родительской
категорией или её прямыми подкатегориями.
- category_rollup.py отдаёт для каждого родительского тотала список
category-источников, которые в него скинулись (children) — пусто, если
источник один.
- Dashboard и рейтинг категорий: строка с непустым children раскрывается по
клику, показывая вклад каждой подкатегории.
- Форма транзакции: выбор категории при сплите — сначала базовая (верхний
уровень), затем строки сплита выбирают саму базовую или её подкатегорию.