- TypeScript 96.6%
- Shell 2.2%
- CSS 0.9%
- JavaScript 0.2%
|
|
||
|---|---|---|
| .devcontainer | ||
| .forgejo/workflows | ||
| doc | ||
| public/data | ||
| scripts | ||
| src | ||
| .env.example | ||
| .gitignore | ||
| bun.lock | ||
| CLAUDE.md | ||
| components.json | ||
| eslint.config.js | ||
| index.html | ||
| new-project.sh | ||
| package.json | ||
| README.md | ||
| tsconfig.app.json | ||
| tsconfig.json | ||
| tsconfig.node.json | ||
| vite.config.ts | ||
BrokerVergi
Веб-приложение для налогового резидента Турции. Принимает выгрузки Interactive Brokers (IBKR) в формате XML (Flex Query), рассчитывает налоговые базы по турецкому законодательству (GVK) и формирует сводную таблицу и экспортные файлы для налогового консультанта.
Статический SPA — без бэкенда, без хранения данных на сервере. Все расчёты выполняются в браузере.
Разработка
bun run dev # Dev-сервер → http://localhost:5173
bun run build # TypeScript check + сборка в dist/
bun run lint # ESLint
bun run preview # Предпросмотр production-сборки
Предварительная загрузка внешних данных
Перед сборкой рекомендуется обновить кэш курсов и индекса Yİ-ÜFE, чтобы приложение работало без API-ключа и без обращений к TCMB EVDS в браузере:
# Курсы USD/TRY (ежедневные)
bun run fetch-rates # 2018-01-01 → сегодня − 7 дней
bun run fetch-rates --from 2018-01-01 --to 2026-05-01 # явный диапазон
bun run fetch-rates --force # перезапросить все годы принудительно
# Индекс Yİ-ÜFE (ежемесячный)
bun run fetch-yuufe # 2010-01-01 → сегодня − 7 дней
bun run fetch-yuufe --from 2010-01-01 --to 2026-05-01 # явный диапазон
bun run fetch-yuufe --force # перезапросить принудительно
fetch-ratesпропускает прошлые полные годы (≥ 200 записей); запрашивает только текущий годfetch-yuufeпропускает, если файл уже содержит данные до предыдущего полного месяца- Оба скрипта требуют
VITE_EVDS_API_KEYв.env - Результаты коммитятся в репозиторий и включаются в
dist/при сборке
Обновление порогов декларирования
Пороги MSİ (GVK Madde 86/1-c) хранятся в src/data/declarationThresholds.ts и обновляются вручную раз в год после публикации нового порога GİB:
bun run update-thresholds # показать текущие значения
bun run update-thresholds --year 2027 --amount 26000 # добавить год (помечается unofficial)
bun run update-thresholds --year 2027 --official # подтвердить после проверки PDF GİB
- Новые записи помечаются
unofficial: trueдо ручной проверки по официальному PDF GİB - После проверки запускается
--officialдля снятия флага - Изменённый
src/data/declarationThresholds.tsкоммитится в репозиторий
Добавление shadcn/ui компонентов
bunx --bun shadcn@latest add <component>
# Компоненты появляются в src/components/ui/
Переменные окружения
Скопировать .env.example в .env. Vite видит только переменные с префиксом VITE_.
| Переменная | Назначение |
|---|---|
VITE_EVDS_API_KEY |
Ключ TCMB EVDS3 для загрузки курсов USD/TRY (нужен для fetch-rates и как фолбэк в браузере) |
VITE_BASE_URL |
Base URL для GitHub Pages (прописывается в CI) |
Стек
| Инструмент | Версия | Назначение |
|---|---|---|
| Bun | 1.x | Runtime, пакетный менеджер |
| React | 19 | UI |
| TypeScript | ~5.9 | Типизация (erasableSyntaxOnly: true) |
| Vite | 7 | Dev-сервер и бандлер |
| React Router | 7 | Клиентская маршрутизация |
| Tailwind CSS | v4 | Стили |
| shadcn/ui | latest | UI-компоненты (Neutral theme) |
| Zustand | 5 | Глобальное состояние; язык (lang) сохраняется в localStorage |
| lucide-react | latest | Иконки |
| fast-xml-parser | 5 | Парсинг IBKR Flex Query XML |
| xlsx (SheetJS) | 0.18 | Генерация Excel-файлов в браузере |
Локализация
Интерфейс доступен на трёх языках: RU / EN / TR. Переключатель — в правом верхнем углу шапки. Язык сохраняется в localStorage (bv_lang) и восстанавливается при следующем визите.
Строки хранятся в src/i18n/: ru.ts — эталонный файл с ключами as const; en.ts и tr.ts — явные Record<TranslationKey, string>. Компоненты используют хук useT(), сервисы — функцию t(lang, key, params?).
Прогресс реализации
| Шаг | Статус | Описание |
|---|---|---|
| 1 — Инструкция | ✅ | Ссылка на /instructions/ibkr-export |
| 2 — Загрузка файлов | ✅ | File input (.xml, multiple) + тестовые данные (dev) |
| 3 — Валидация | ✅ | Структурные проверки, дубли по accountId/периоду, продажи без истории покупок, статистика |
| 4 — Загрузка данных | ✅ | Курсы USD/TRY (TCMB EVDS3) + Yİ-ÜFE (TÜİK, bundled fallback) |
| 5 — Расчёт | ✅ | FIFO, Yİ-ÜFE индексация, дивиденды, проценты, SYEP |
| 6 — Результаты | ✅ | Таблицы по разделам, итог для GİB, прогноз налога по GVK 103, проверка обязанности декларирования по GVK 86 |
| 7 — Экспорт | ✅ | Excel (.xlsx, 4 листа) + HTML-отчёт (.html, открыть в браузере → Ctrl+P → Save as PDF). Сводный лист/раздел включает: таблицу доходов, прогноз налога по GVK 103, блок проверки обязанности декларирования по GVK 86/1-c (статус DKİ и MSİ с суммами и ссылкой на PDF GİB), методологические примечания. Всегда формируются два файла: на языке интерфейса и на турецком (для бухгалтера). Исключение: если язык интерфейса — TR, файл один. |
Именование экспортных файлов
Каждый файл содержит трёхбуквенный языковой суффикс:
| Язык интерфейса | Файлы Excel | Файлы HTML |
|---|---|---|
| RU | BrokerVergi-2024-RUS.xlsx + BrokerVergi-2024-TUR.xlsx |
BrokerVergi-2024-RUS.html + BrokerVergi-2024-TUR.html |
| EN | BrokerVergi-2024-ENG.xlsx + BrokerVergi-2024-TUR.xlsx |
BrokerVergi-2024-ENG.html + BrokerVergi-2024-TUR.html |
| TR | BrokerVergi-2024-TUR.xlsx |
BrokerVergi-2024-TUR.html |
Deploy
Forgejo (self-hosted) → GitHub (mirror) → GitHub Pages
Push в main → GitHub Actions: bun install → bun run build → deploy dist/.
Base URL берётся из переменной VITE_BASE_URL (прописана в CI).
Документация
| Файл | Содержимое |
|---|---|
| doc/algoritm.md | Алгоритм расчёта: налоговые правила GVK, формулы, UX-флоу, открытые вопросы |
| doc/parser.md | ТЗ модуля импорта IBKR: XML-формат, нормализация, архитектура |
| doc/how_make_xml.md | Инструкция пользователю: выгрузка Flex Query XML из IBKR |
Структура проекта
scripts/
├── fetch-rates.ts # Bun-скрипт: курсы USD/TRY с TCMB EVDS3 → public/data/usd_try_rates.json
├── fetch-yuufe.ts # Bun-скрипт: индекс Yİ-ÜFE с TCMB EVDS3 → public/data/yuufe.json
└── update-thresholds.ts # Bun-скрипт: обновление порогов MSİ в src/data/declarationThresholds.ts
public/
├── data/
│ ├── usd_try_rates.json # Предварительно загруженные курсы USD/TRY (генерируется скриптом, коммитится)
│ └── yuufe.json # Предварительно загруженный индекс Yİ-ÜFE (генерируется скриптом, коммитится)
└── samples/ # Тестовые IBKR XML (dev-режим)
src/
├── i18n/
│ ├── ru.ts # Русские строки — эталон, все ключи as const
│ ├── en.ts # Английский перевод (Record<TranslationKey, string>)
│ ├── tr.ts # Турецкий перевод (Record<TranslationKey, string>)
│ └── index.ts # Lang, t(lang, key, params?), useT() хук
├── components/
│ ├── Layout.tsx # Sticky header (логотип, версия, LanguageSwitcher, nav) + Outlet + footer
│ ├── LanguageSwitcher.tsx # Переключатель RU / EN / TR в шапке
│ ├── TaxResultsView.tsx # Результаты: год, 3 раздела, итог, прогноз налога
│ ├── ExportPanel.tsx # Шаг 7: Excel + HTML кнопки по годам, двойной экспорт
│ └── ui/ # shadcn/ui (генерируется через CLI)
├── data/
│ ├── yuufe.ts # Bundled TÜİK Yİ-ÜFE (2018-01 – 2025-04, база 2010=100)
│ ├── taxBrackets.ts # GVK Madde 103 прогрессивная шкала 2022–2026
│ └── declarationThresholds.ts # GVK Madde 86 пороги MSİ 2020–2026; обновляется скриптом
├── pages/
│ ├── HomePage.tsx
│ ├── CalculatorPage.tsx # 7-шаговый stepper (всё реализовано)
│ ├── InstructionsPage.tsx
│ ├── instructions/ # Вложенные страницы раздела Инструкции
│ ├── ContactsPage.tsx
│ └── NotFoundPage.tsx
├── services/
│ ├── ibkr/
│ │ ├── parser.ts # parseIBKRXml() → ParsedStatement | error
│ │ ├── validator.ts # validateFiles() → ValidationResult + FileStats[]
│ │ └── types.ts # IBKR domain-интерфейсы
│ ├── tax/
│ │ ├── types.ts # CapitalGainLine, DividendLine, TaxYearResult, TaxResult
│ │ ├── normalize.ts # RawAttrs[] → NormalizedOrder / NormalizedCashTx
│ │ ├── calculator.ts # calculateTax() — FIFO + Yİ-ÜFE + дивиденды
│ │ └── estimator.ts # estimateTax() — прогрессивный налог по GVK 103
│ ├── export/
│ │ ├── excel.ts # exportYearToExcel(yr, est, lang) — SheetJS, 4 листа
│ │ └── pdf.ts # downloadHtmlReport(yr, est, lang) — скачивает .html файл
│ ├── evds.ts # EvdsClient, evdsDateToIso, extractDailyRates/MonthlyIndex
│ ├── tcmb.ts # fetchRatesForYear (localStorage → static file → EVDS), parseTcmbCsv
│ ├── tuik.ts # fetchYiUfe (localStorage → static file → EVDS → bundled), mergeYiUfe
│ └── cache.ts # cacheGet/Set/Clear — localStorage TTL
├── store/
│ └── appStore.ts # Zustand: loadedFiles, tcmbRates, yiufeData, calcResult, lang
├── lib/
│ └── utils.ts # cn() утилита
├── env.d.ts # declare __APP_VERSION__, __APP_BUILD_DATE__ (Vite define)
├── App.tsx # createBrowserRouter + маршруты
├── main.tsx
└── index.css # Tailwind v4 + shadcn CSS-переменные
Маршруты
| Путь | Страница |
|---|---|
/ |
Главная |
/calculator |
Расчёт (7 шагов) |
/instructions/ibkr-export |
Выгрузка отчётов IBKR |
/instructions/calculator-guide |
Режим расчёта |
/instructions/results-guide |
Использование результатов |
/instructions/other |
Прочие инструкции |
/contacts |
Контакты |