Commit Graph

305 Commits

Author SHA1 Message Date
abd5d4b65f chore(ci+logs): eslint в CI, npm audit, logger с уровнями, retention error_logs
Шаги 1.6 и 1.7 плана production-готовности:
- eslint flat-config (баг-ловушки, легаси warn), lint блокирующий в pr-check
- npm audit --audit-level=high в CI (отчёт)
- фикс реального бага: условный useRef в TaskTitleInline
- server/utils/logger.ts (LOG_LEVEL/LOG_FORMAT) в 5 горячих местах
- error_logs retention 30 дней (worker), redactSensitive в captureErrorLog
- 0 errors lint, vitest 96/96
2026-09-08 00:11:38 +03:00
6eaca79cc6 perf(ui): виртуализация реестра задач и чата (@tanstack/react-virtual)
Шаг 1.3 плана production-готовности:
- TaskRegistry: виртуальные строки с динамической высотой, порог 30
- TaskChat: виртуальные сообщения, порог 100, автоскролл/scrollToMessage
- выделение/сортировка/поиск/inline-редакторы работают по полному массиву
- check/build чисто, vitest 96/96
2026-09-07 23:46:42 +03:00
667a321341 perf(sse+tasks): индекс SSE-соединений, getTaskTree на recursive CTE, батчи N+1
Шаги 1.4 и 1.5 плана production-готовности:
- SseConnectionIndex (byOrg/byUser), один heartbeat-таймер, протокол SSE не тронут
- getTaskTree/getTaskParentChain: WITH RECURSIVE CTE (было 1+2N запросов)
- sync initial/delta: Promise.all по формам
- embedding queue/reindex: батч-предзагрузки вместо поштучных запросов
- 8 новых тестов (96/96)
2026-09-07 23:29:21 +03:00
9528a8123a perf(directories): SQL-фильтрация/пагинация справочников, N+1, лимиты column-values
Шаги 1.1 и 1.2 плана production-готовности:
- getDataTableRowsPaged: фильтры/поиск/сортировка/пагинация в SQL (values->>N)
- getDataTablesWithAccess и list_directories без N+1
- /column-values: limit (дефолт 20, кап 1000), убраны debug-логи с горячего пути
- tree-режим и legacy-путь TableEditor сохранены 1:1
- 10 новых тестов (88/88)
2026-09-07 23:09:15 +03:00
701af06290 feat(billing): PaymentProvider + Mock, self-service пополнение баланса
Шаг 0.13 плана production-готовности:
- интерфейс PaymentProvider (createPayment/verifyWebhook/getPaymentStatus)
- MockPaymentProvider с настоящим webhook (sha256-подпись, идемпотентность)
- applySucceededPayment: транзакция с guard, credit, авто-снятие billingBlocked
- роуты payments + confirm-test (только mock) + публичный webhook
- Billing.tsx: Пополнить/Подтвердить (тест), фикс setLocation в рендере
- миграция 0080 billing_payments, 13 новых тестов (78/78)
2026-09-07 22:45:30 +03:00
23c49adcd1 perf(access): TTL-кэш getAccessibleTaskIds с точечной инвалидацией
Шаг 0.12 плана production-готовности:
- accessibleTasksCache (45с) per userId:orgId внутри storage-метода
- инвалидация в storage-слое: роли, делегирования, form access,
  assignees, createTask/updateTask, appRole пользователя
- 9 новых юнит-тестов (65/65 зелёные)
2026-09-07 22:27:33 +03:00
82d577c797 docs(log): регрессия поиска после 0.10/0.9 пройдена (прод) 2026-09-07 22:11:27 +03:00
a27766a38b perf(tasks): лимиты/пагинация /api/tasks и дельта-синк в SQL
Шаги 0.10 и 0.9 плана production-готовности:
- дефолт limit 50, кап 200; списковые ответы без description
- курсорная пагинация (updated_at, id) + nextCursor
- since-фильтр дельта-синка перенесён в SQL (updated_at >= since)
- embedding pre-count через COUNT(*) вместо выгрузки миллиона строк
- 11 новых юнит-тестов (56/56 зелёные)

Инвариант поиска сохранён: search-ветка /api/tasks?search= не изменена
2026-09-07 22:01:25 +03:00
0a60c6abf1 docs(log): шаги 0.5/0.2 — разбор инцидента с досрочным деплоем Dockerfile
Прод восстановлен (5426c70): app healthy, uid=1001(app), health 200,
миграция 0079 применена, trgm-индексы на месте
2026-09-07 21:45:51 +03:00
5426c70f1f fix(deploy): vite.config импортируется по вычисляемому URL — esbuild без --splitting
esbuild без --splitting инлайнит статический import('../vite.config') в бандл
и поднимает его импорты (vite, @vitejs/plugin-react) на верхний уровень —
prod падал с ERR_MODULE_NOT_FOUND при старте. Динамический import по
вычисляемому URL esbuild не анализирует и оставляет рантайму (dev-only).

Проверено: dist/index.js содержит 0 статических импортов vite, бандл стартует,
подключается к БД и доходит до инициализации таблиц
2026-09-07 21:33:58 +03:00
9fc6ad1bd1 fix(deploy): prod-бандл без devDependencies — динамический импорт vite в setupVite
Шаги 0.5/0.2 плана production-готовности (досрочно ушли с a54ec77):
- Dockerfile: npm ci --omit=dev вместо копирования node_modules из builder,
  пользователь app (USER app), HOME=/tmp, npm_config_cache=/tmp/.npm
- docker/Dockerfile.documents: USER app, HOME=/tmp
- server/vite.ts: vite и vite.config импортируются динамически внутри setupVite —
  иначе prod-бандл падал с ERR_MODULE_NOT_FOUND (@vitejs/plugin-react)
- package.json: vitest/supertest/@types/* (16 пакетов) перенесены
  в devDependencies — именно vitest в dependencies тянул vite/esbuild/tsx в прод-образ

Проверено локально: образ собирается, uid=1001(app), /app/data доступна на запись,
бандл стартует без devDeps (падает только на отсутствии JWT-секретов окружения)
2026-09-07 21:22:27 +03:00
a54ec775dd perf(db): миграция 0079 — pg_trgm GIN-индексы поиска и горячие индексы
Шаг 0.11 плана production-готовности:
- триграммные GIN на tasks.title/description (ilike '%…%' без смены семантики)
- tasks(organization_id, updated_at DESC), tasks(form_id, updated_at)
- field_history(task_id), users(email)

Проверено на 100k задач: Seq Scan 77.5ms → Bitmap Index Scan 4.8ms (~16x)
2026-09-07 21:09:50 +03:00
cf6f937108 security(users): SafeUser — passwordHash/токены не уходят клиенту
Шаг 0.8 плана production-готовности:
- shared/schema.ts: тип SafeUser + safeUserColumns
- getUsersByOrganization, listUsers, searchUsers, getUsersByRole,
  getUsersByOrgRoleId, documents getUser — без чувствительных колонок
- sync (initial/delta), автоматизации ctx.users.list, MCP list_users — sanitized
2026-09-07 21:03:00 +03:00
26419f395f security(errors): 5xx без утечки err.message клиенту
Шаг 0.7 плана production-готовности:
- глобальный error handler: нейтральный текст + код инцидента, детали в лог
- route-уровень: зачистка err.message/String(err) в 5xx по 12 файлам,
  оригинальные ошибки логируются с контекстом
2026-09-07 20:42:39 +03:00
b3d7923b23 test(ci): скрипт test, тесты в pr-check, фикс sporadic 403 в trackUserActivity
Шаг A3 (из 1.6) плана production-готовности:
- package.json: скрипт "test": "vitest run"
- pr-check.yml: шаг Tests после type check
- server/utils/userActivity.ts: try/catch в trackUserActivity — синхронная
  ошибка трекинга активности больше не срывает authenticateToken (ложный 403
  на первом запросе пользователя за минуту)
- tests/auto-transitions.test.ts: 3 устаревших теста обновлены под намеренное
  поведение (forward-only position, синк assignee, аудит вместо системного сообщения)

45/45 тестов зелёные, npm run check чисто.
2026-09-07 20:28:44 +03:00
76a0d859a5 chore(docs): add implementation log
Шаги 0.0 и 0.15 плана доведения до production-готовности:
сквозной журнал исполнения плана с шапкой-контекстом и инвариантом поиска.
2026-09-07 20:13:37 +03:00
a2b231d876 fix(history): московское время и ФИО вместо user ID в истории задачи 2026-09-07 16:55:51 +03:00
9faef8fcf5 SDD: отчёт task 5 — фиксированные цвета графиков DI2; выбор колонок доходов/расходов уже реализован 2026-09-07 00:56:16 +03:00
134b825da5 DI2: фиксированные цвета серий графиков — не зависят от скина/темы
По решению владельца цвета серий (категории, контрагенты, классы просрочки,
прибыль/убыток) должны быть стабильными: одинаковый цвет = одни и те же данные.
Возвращены исходные палитры DI2 (до рестайла 968010c):
- income/expenses-overview, salary: 20 цветов hex (#2563eb, #e11d48, ...)
- calculations: income/expense/profit/tax/toPay/cumulative + HIGHLIGHT_COLORS
- debt: OVERDUE_COLORS по классам (cls0..cls365plus), ДЗ/ПДЗ, фолбэк #94a3b8
- planning: PIE_COLORS hsl, paid/debt, totalDebt/totalOverdue, прогноз/факт, легенда
- profitability: income/expense/profit (hsl из оригинала)
- income-categories: TAG_COLORS (8 hex)
- dashboard: палитра из исходной темы DI2 (hsl(210 85% 42%) и т.д.), долг hsl(0 72% 51%)

Не тронуты: цвета текста/осей/сеток/тултипов/фонов (семантические токены скина).
2026-09-07 00:55:32 +03:00
d36a13aa36 SDD: леджер и отчёт этапа 4 (проверки + документация di2-restyle) 2026-09-07 00:28:13 +03:00
ffa8d83fa7 SDD: леджер — этап 3 принят 2026-09-07 00:17:23 +03:00
47b785edcd DI2 restyle: отчёт этапа 3 (визуальный рестайл) 2026-09-07 00:06:47 +03:00
968010c411 DI2: визуальный рестайл — графики и цвета по токенам скина, таблицы по UI Style Guide, мобильная нижняя панель, переключатель темы
- recharts: все hex/hsl палитры → var(--chart-1..5), семантика → var(--info/success/warning/destructive)
- палитровые Tailwind-классы (red/blue/green/amber и т.д.) → семантические классы через color-mix (Tailwind v3 не генерирует alpha для var-цветов)
- ui/table.tsx: шапка по гайду (bg-muted/50, text-[12px]), строки h-[var(--table-row-height,40px)], ячейки px-3, hover muted 40%
- lib/field-styles.ts (порт из основного клиента) + fieldControlClass в column-filter
- app-sidebar: брендинг «ОмегаПром / Финансы», компактные отступы
- bottom-nav.tsx: мобильная нижняя панель (Обзор/Разделы/Настройки) по референсу FinanceBottomNav, safe-area-bottom в index.css
- переключатель темы Sun/Moon в хедере (useDi2Theme), SidebarTrigger скрыт на мобильном
- заголовки страниц text-lg font-semibold
2026-09-07 00:04:49 +03:00
ba0d3ac85a SDD: леджер — этап 2 принят 2026-09-06 23:42:08 +03:00
0834356286 DI2: отчёт по этапу 2 (мост тем/скинов iistwin) 2026-09-06 23:33:18 +03:00
b4579340ff DI2: мост тем/скинов iistwin — Di2ThemeProvider, общий generated-skins.css, токены в формате основного клиента
- vite.config.di2.ts + client-di2/tsconfig.json: алиас @skin → client/src/themes (tokens, skins, generated-skins.css — единый источник токенов)
- client-di2/src/themes/Di2ThemeProvider.tsx: порт логики ThemeProvider основного клиента (localStorage['skin'] → скин организации → 'iistwin'; runtime CSS кастомных скинов через generateSkinCss; data-skin/data-theme/class light|dark на <html>; контекст с toggleTheme). Ключи 'skin'/'theme' общие с iistwin — выбор синхронизируется между приложениями
- main.tsx: обёртка <Di2ThemeProvider> вокруг App
- tailwind.config.ts DI2: цвета hsl(var(--x) / <alpha-value>) → var(--x), радиусы → var(--radius) с каскадом calc, добавлены warning/success/info
- client-di2/src/index.css: собственные HSL-тройки заменены fallback-блоками основного клиента (до гидратации), первой строкой @import общего generated-skins.css; DI2-only переменные (--card-border, --popover-border, *-border, --button-outline, --badge-outline) переопределены через color-mix от семантических токенов; удалены --opaque-button-border-intensity и hsl(from ...) relative-color вычисления; жёсткие --font-sans: Open Sans убраны (шрифты эмитит generateSkinCss из токенов скина)
- client-di2/index.html: lang=ru, viewport без maximum-scale + interactive-widget=resizes-content, мега-link Google Fonts (23 семейства) заменён на Inter + DM Mono как в основном клиенте
- Alpha-модификаторы семантических цветов (166 мест: bg-muted/50, border-primary/30 и т.п.) заменены на arbitrary values color-mix(in oklab, var(--x) NN%, transparent) — Tailwind v3 молча не генерирует /NN для var()-цветов; палитровые цвета (green-500/10 и т.п.) не тронуты
2026-09-06 23:31:43 +03:00
10a2c1a629 SDD: леджер — этап 1 принят 2026-09-06 22:04:22 +03:00
4c260f2aaf DI2: отчёт по этапу 1 (удаление старого модуля финансов) 2026-09-06 21:55:21 +03:00
66f4e5ef53 DI2: удаление старого модуля финансов — пул БД и аудит перенесены в finance-di2
- server/finance/ (19 файлов) удалён: пул финансовой БД перенесён в server/finance-di2/db-core.ts, db-client.ts теперь реэкспортирует из ./db-core
- Шедулер аудита перенесён в server/finance-di2/audit-scheduler.ts (03:00 МСК, setInterval, без node-cron), запуск из registerDi2Routes с защитой от повторного старта
- История аудита DI2 теперь персистентна: загрузка/запись data/finance-audit-history.json в audit-agent.ts (порт из удалённого server/finance/audit.ts)
- registerFinanceRoutes убран из server/routes/index.ts, registerDi2Routes остался на его месте
- Клиент: удалены pages/finance/, components/finance/, hooks/useFinance.ts, hooks/useColumnLabels.ts, lib/format.ts, pages/salary.tsx; роуты /finance*, /income*, /expenses*, /salary и lazy-импорты убраны из App.tsx
- Навигация: «Финансы» в Sidebar и MobileBottomNav ведёт на субапп /di2 (внешняя ссылка), гейт moduleAccess.finance + finance.view сохранён
- index.css: удалён блок .hover-elevate/.hover-elevate-2/.active-elevate (использовался только старым модульным DataTable)
- FinanceBottomNav.tsx сохранён как референс для этапа 3 (.superpowers/sdd/di2-restyle/FinanceBottomNav.reference.tsx)
- Не тронуто: client-di2/, сиды прав finance.view/manage, finance_access (module-access)
2026-09-06 21:53:47 +03:00
6bcf4dfdfc DI2: навигация сайдбара на wouter Link — base /di2 подставляется в href и клики не покидают субапп (прямые <a href> уводили на iistwin) 2026-09-06 20:53:44 +03:00
f993c4d1f0 DI2: дашборд клиента ходил на старый /api/dashboard (fetch-шаблон не был переименован) — CRM-формат ломал рендер. Фикс URL на /api/di2/dashboard 2026-09-06 20:40:42 +03:00
2b3e7682e3 Удаление временного SDD-воркспейса di2-embed (записи перенесены в Obsidian) 2026-09-06 20:27:53 +03:00
49578ab442 DI2 фикс коллизии: дашборд DI2 переименован в /api/di2/dashboard
DI2-роутер регистрируется перед основным dashboard.routes.ts и перехватывал
GET /api/dashboard (403 без finance.manage и неверный JSON — live-регрессия
главного дашборда CRM). Роут переименован, в DI2_PREFIXES /api/dashboard
заменён на /api/di2, клиент client-di2 ходит на новый путь (fetch + queryKey
+ prefetch в app-sidebar). Форматы старого модуля финансов не тронуты.
2026-09-06 19:58:24 +03:00
23ed1ecf3d DI2 внедрение, таск 2: клиентский субапп Data-Insight2 (client-di2)
- client-di2/: копия фронтенда DI2 + shared/schema.ts, main.tsx обёрнут в wouter Router base=/di2, favicon относительный
- client-di2/tailwind.config.ts (свои токены DI2) подключён через @config в index.css — иначе postcss взял бы корневой конфиг iistwin
- vite.config.di2.ts: base /di2/, root client-di2, outDir dist/public-di2
- package.json: build/check через && включают сборку и typecheck di2 (build:di2, check:di2), новых зависимостей нет
- Навигация финансов: ссылка «Data-Insight (полная версия)» → /di2 (target=_blank) в FinanceSidebar и FinanceBottomNav
- Типовые фиксы предсуществующих ошибок DI2 (casts, ruleLogic в mutation-типах, Set<string>)
2026-09-06 19:46:28 +03:00
254950f151 DI2 внедрение, таск 1: серверная интеграция Data-Insight2 (server/finance-di2)
- Копия DI2-сервера в server/finance-di2/ с правками: schema/db-client/cache/table-config/override-tables/google-auth/audit-agent/routes
- db-client — обёртка над существующим пулом server/finance/db-client (второй пул не создаётся, добавлен экспорт isConnectionError)
- ai-config.ts — чистое IO конфигов из ai-agent.ts без Telegram-поллинга; роуты /api/ai/toggle и /api/ai/status удалены, без compression/startAuditScheduler/autoStartIfEnabled
- Обёртка registerDi2Routes с auth-gate (authenticateToken + finance.manage), регистрация строго перед registerFinanceRoutes
- Статика /di2 из dist/public-di2 перед веткой vite/static (dev и prod)
- Фикс предсуществующего бага DI2: buildGlobalExclusionConditions без cats в /api/profitability
2026-09-06 19:27:43 +03:00
a60a75eda0 feat(profile): read-first редизайн страницы пользователя, per-field как в задачах
- Раскладка: шапка-профиль → Основное/Контакты → Роль и доступ/Поля+Замещение → вторичные разделы чипами
- ProfileFieldRow: двойной клик/карандаш, редактор в слоте, Esc, optimistic-кэш, все типы полей
- Замещение: надпись + ⊕ popover, список под строкой
- Удалены RHF-форма, zod-схема и кнопки «Сохранить»
- server: PATCH /api/users/:id (частичное обновление, admin-градация, аудит)
- server: history-number в REST PATCH пишет историю + пересчёт средней
2026-09-06 18:47:05 +03:00
ddc2bd9491 Поля профиля с options.readOnly: запрет редактирования «Оценки» и «Средней оценки»
- UI: read-only поля отображаются текстом (у history-number остаётся кнопка графика),
  исключены из сохранения; кнопка «Сохранить» скрыта, если все поля вкладки read-only
- Сервер: POST/PATCH field-values профиля отклоняют запись в поле с options.readOnly (400);
  автоматизации пишут через storage напрямую и не затрагиваются
- Данные: options.readOnly=true у полей «Оценка» и «Средняя оценка»
2026-09-06 15:08:23 +03:00
b070f862c5 Sandbox ctx.tasks.setFieldValue: передача formId в createTaskFieldValue
task_field_values.form_id NOT NULL — insert без formId падал, значение поля не создавалось
2026-09-06 13:46:39 +03:00
2960879fa9 Sandbox: ctx.tasks.setFieldValue искал несуществующий fieldCode + логирование триггерных автоматизаций
- automation-worker-code: getFormFields возвращает поле code (не fieldCode) — поиск поля всегда завершался ошибкой 'Field ... not found', ошибка терялась
- automation.routes: результаты runAutomationsByTrigger (логи, ошибки) теперь пишутся в консоль сервиса с префиксом [automation]
2026-09-06 13:37:44 +03:00
0bde7764b1 MCP create_task: создание исполнителя в task_assignees при assigned_to
Раньше assigned_to писался только в legacy-колонку tasks.assigned_to, а запись
в task_assignees (модель исполнителей) не создавалась — из-за этого автоматизации,
читающие ctx.tasks.getAssignees (например «Ответственный в состав бригады»),
видели пустой список исполнителей для задач, созданных через MCP
2026-09-06 13:10:27 +03:00
ec86fa4c11 Рейтинг сотрудников: history-number поле = последняя оценка, среднее за месяц — в поле options.avgFieldCode
- setUserProfileFieldValueFromAutomation: для history-number значением поля становится переданное число (последняя оценка), а не среднее
- recomputeUserFieldMonthlyAverage: среднее за текущий месяц пишется в поле, указанное в options.avgFieldCode history-number поля (если не задано — в само поле, обратная совместимость)
- Автоматизация «Оценка сотрудникам» (форма 11): сумма трёх числовых полей «Оценка 1/2/3» вместо одного select-поля
- Документация sandbox обновлена во всех четырёх местах (UI, MCP, Obsidian, AGENTS.md)
2026-09-06 11:55:15 +03:00
a7a14d947a Финансы: деревья категорий как в Data-Insight2 — группировка авто/ручные, свёрнуто по умолчанию, полосы сумм, нераспределённые 2026-09-06 09:53:45 +03:00
2d137332da Финансы: дебиторка по умолчанию читает из таблицы prosrochennayadebitorskayazadolzhennost — view vw_debt_active отсутствует во внешней БД 2026-09-06 09:40:56 +03:00
4b34a4bea5 Финансы: мобильная нижняя навигация (Обзор / Разделы / Настройки) вместо боковой панели на телефоне 2026-09-05 17:59:34 +03:00
ccfc396d40 CI: NODE_OPTIONS --max-old-space-size=6144 для tsc — googleapis тянет тяжёлые .d.ts, Type check падал с OOM 2026-09-05 15:39:58 +03:00
7a8db03163 Финансы: дебиторка (vw_debt_active), расчёты НДС/налога на прибыль, аудит целостности с автопланировщиком, синхронизация зарплат из Google Sheets, миграция настроек Data-Insight2 2026-09-05 15:07:36 +03:00
d2c993b613 fix: мультивыбор user/роль — мгновенное обновление выбора (галки/чипы) в карточке задачи 2026-09-05 12:33:14 +03:00
005a51e367 fix: MCP update_task_status запускает автоматизации task.status_changed (раньше обходил executeTaskTransition) 2026-09-04 20:32:06 +03:00
ba723aa0b7 feat: ctx.users.recalcFieldAverage + документация автопересчёта среднего history-number 2026-09-04 20:14:51 +03:00
6aececeb4a feat: автоматический пересчёт среднего для history-number в sandbox
- setFieldValue для history-number: переданное число пишется в историю, значением поля становится среднее за текущий месяц (автопересчёт)
- ctx.users.recalcFieldAverage(userId, fieldCodeOrId): принудительный пересчёт среднего за месяц (для schedule-автоматизаций; нет записей — поле очищается)
- документация обновлена (Automations.tsx, mcp.ts)
2026-09-04 20:09:27 +03:00