Commit Graph

387 Commits

Author SHA1 Message Date
Дмитрий 305264be5b ci(deptrac): wire deptrac as lefthook pre-commit job 10
Job 10 runs `deptrac analyse` (root: app/) when staged app/**/*.php
changes — the layer-dependency gate. Red-green verified: a
Model→Service dependency is flagged (DependsOnDisallowedLayer,
exit 1); a clean tree exits 0. app/.gitignore += /.deptrac.cache.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 11:13:27 +03:00
Дмитрий d971f9cd59 feat(deptrac): layer model + ruleset config + ADR-005
app/deptrac.yaml — 13 layers (Controller/Service/Model/Job/…),
conservative ruleset enforcing inward/upward-violating directions.
First `deptrac analyse`: 0 violations / 481 allowed / 977 uncovered
— the codebase already conforms, so no baseline file is needed.
ADR-005 records the decision.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 11:09:24 +03:00
Дмитрий 65061f640e build(deptrac): add deptrac 4.6.1 as a composer dev-dependency (DT1)
deptrac/deptrac ^4.6 + 5 transitive deps (symfony/config,
dependency-injection, var-exporter; phpdocumentor/graphviz;
jetbrains/phpstorm-stubs). Primary DT1 path — composer dev-dep;
no PHAR fallback needed (resolver clean, 0 security advisories).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 11:04:45 +03:00
Дмитрий 550dfd0d44 fix(projects): C9 code-review findings — ProjectResource отдаёт regions[] + покрытие
C1: ProjectResource не возвращал regions → edit-диалог/drawer затирали
    сохранённые регионы при сохранении. +поле в toArray().
C2: +integration-тест outbound regions[] через полный SyncSupplierProjectsJob::handle().
I1: расскип NewProjectDialog payload-теста (regions в POST).
I2: assert data.regions в ProjectsStore/UpdateTest (ловит C1 на backend-уровне).
I4: docblock — bulkUpdateRegions legacy (region_mask, не влияет на outbound до Plan 6.5).
M1: CHANGELOG v8.22 — исправлен неверный пример регионов (Москва=82).

Регрессия: Pest 905/902/3sk/0, Vitest 104f/884/3sk/0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 10:05:32 +03:00
Дмитрий c847c02441 merge: C9 — Plan 6 регионы субъект-уровня в портал
# Conflicts:
#	app/tests/Feature/Plan4/Schema/SchemaDeltaTest.php
#	db/CHANGELOG_schema.md
#	db/schema.sql
2026-05-17 09:30:21 +03:00
Дмитрий b11bd1a2f8 merge: Sprint 3E (D6/D7 — убрать placeholder-вкладки SettingsView) в портал 2026-05-17 09:03:21 +03:00
Дмитрий fb033886f0 chore(cleanup): снять устаревший MDI clearable-workaround (CTO-19 tail) — Sprint 6 I5 2026-05-17 08:18:44 +03:00
Дмитрий 73351c8004 fix(a11y): aria-label с ключом на edit-кнопках AdminSystem — Sprint 6 G9 2026-05-17 08:18:44 +03:00
Дмитрий 5a1aa36837 docs(polling): семантический doc-комментарий вместо списка call-site'ов — Sprint 6 F4 review-fixup 2026-05-17 08:18:44 +03:00
Дмитрий 05183c32d7 refactor(polling): вынести интервалы в constants/polling.ts — Sprint 6 F4 2026-05-17 08:18:44 +03:00
Дмитрий e70a21fe83 test(admin): явный stubEnv DEV=true в dev-баннер тесте — Sprint 6 B6 review-fixup 2026-05-17 08:18:43 +03:00
Дмитрий a31746c157 feat(admin): DEV-only баннер о застабленном auth-gate — Sprint 6 B6 2026-05-17 08:18:43 +03:00
Дмитрий 5a536c84ce fix(a11y): focus-visible ring + keyboard-activation тест на eye-toggle — Sprint 6 A9 review-fixup 2026-05-17 08:18:43 +03:00
Дмитрий 85641c3136 fix(a11y): accessible eye-toggle на полях пароля — Sprint 6 A9 2026-05-17 08:18:43 +03:00
Дмитрий 4f27bdf468 test(deals): T1 fixup — DealsListIntegration/KanbanRedesign под I3 (убран MOCK_DEALS-fallback) 2026-05-17 07:13:51 +03:00
Дмитрий ff96e2f7ad fix(admin): I4 — devPlainCode-баннер за import.meta.env.DEV 2026-05-17 07:13:51 +03:00
Дмитрий 07d2e9705e fix(admin): I3 — убрать mock fallback в System/Tenants 2026-05-17 07:13:50 +03:00
Дмитрий aed9475c26 fix(admin): I3 — убрать mockAdmin fallback в Billing/Incidents 2026-05-17 07:13:50 +03:00
Дмитрий 347f54957d fix(deals): I3 — убрать mock-fallback в NewDealDialog/DealDetailDrawer 2026-05-17 07:13:50 +03:00
Дмитрий 97a7b7fe09 test(deals): T1 review-fixup — I3-тесты через onMounted-путь 2026-05-17 07:13:50 +03:00
Дмитрий 34f552552f fix(deals): I3 — убрать MOCK_DEALS fallback в DealsView/KanbanView 2026-05-17 07:13:50 +03:00
Дмитрий b23412bba3 style(admin): Sprint 5C — pint-fix AdminPricingTiersControllerTest 2026-05-17 05:24:44 +03:00
Дмитрий 1692ee500d fix(admin): G7 review-fixup — сброс effective_from при открытии редактора + boundary-тест 2026-05-17 05:24:44 +03:00
Дмитрий 7f385b04bb feat(admin): G10 — браузерный confirm() удаления сетки → v-dialog 2026-05-17 05:24:44 +03:00
Дмитрий e9c215a1e0 feat(admin): G7 — выбор effective_from тарифной сетки через date-picker 2026-05-17 05:24:44 +03:00
Дмитрий 9c968a1f6d refactor(admin): G3 — pricing-tiers/suppliers вьюхи на типизированный api/admin.ts 2026-05-17 05:24:44 +03:00
Дмитрий e2ef0b40a5 feat(billing): E4 — убрать mock pending-баннер (нет платёжного шлюза до Б-1) 2026-05-17 05:24:44 +03:00
Дмитрий 0b146e48b9 feat(billing): E2 — disabled+tooltip на кнопках Автопополнение/Сменить тариф 2026-05-17 05:24:43 +03:00
Дмитрий 2b14071fda style(ui): Sprint 5B — prettier-формат затронутых файлов
Регрессия full: prettier --check на 5 файлах, тронутых Sprint 5B
(T2/T3/T4). Whitespace-only, 0 изменений поведения — Vitest 67/67
на затронутых спеках. Pre-existing prettier-дрейф 28 НЕ-5B файлов
оставлен (вне scope спринта).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 04:03:36 +03:00
Дмитрий e40a575a5a feat(ui): C6 — degradation-alert в NewDealDialog при провале загрузки списков 2026-05-17 03:48:39 +03:00
Дмитрий c50b8d8c22 fix(ui): C3 — сброс toast-текста + типизация теста (review-fixup) 2026-05-17 03:44:50 +03:00
Дмитрий d324d0e20c feat(ui): C3 — кнопка «Экспорт» в шапке DealsView экспортирует весь список 2026-05-17 03:39:32 +03:00
Дмитрий 4584eb95c0 fix(ui): B3 — ⌘K open-only + DOM-тесты палитры (review-fixup) 2026-05-17 03:33:51 +03:00
Дмитрий dc3773b4fb feat(ui): B3 — минимальная ⌘K command-palette навигации 2026-05-17 03:28:05 +03:00
Дмитрий d8d60ae0a0 docs(ui): B2 — актуализация комментариев AppSidebar (review-fixup)
Code-quality review T1: stale JSDoc «Counts — mock» теперь ложный
(count live из API); +поясняющий комментарий к null→undefined цепочке.
Comment-only, 0 изменений поведения. Vitest 6/6 green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 03:20:02 +03:00
Дмитрий 84fbd56215 feat(ui): B2 — счётчик «Сделки» в сайдбаре из API вместо хардкода 2026-05-17 03:14:13 +03:00
Дмитрий 5c196c4bca feat(api): B2 — count_only параметр на GET /api/deals 2026-05-17 03:11:03 +03:00
Дмитрий 2688dde8c7 fix(dev): A8 review — production-guard в DemoSeeder + точность README/теста (Sprint 5A) 2026-05-17 02:23:26 +03:00
Дмитрий b7f7fdb8ee feat(dev): A8 — composer demo:seed + README демо-данные + idempotency-тест (Sprint 5A) 2026-05-17 02:23:26 +03:00
Дмитрий 228f6dc031 fix(auth): A6 review — ранний return при redirect на /login (Sprint 5A) 2026-05-17 02:23:26 +03:00
Дмитрий eaac2cf329 feat(auth): A6 — реальный обратный отсчёт TOTP-окна в 2FA (Sprint 5A) 2026-05-17 02:23:26 +03:00
Дмитрий 7e7ccfeedf test(auth): A5 — regression generic fallback ForgotPassword (Sprint 5A) 2026-05-17 02:23:26 +03:00
Дмитрий 35ef205c0f feat(auth): A4 — ResetPassword ошибка несовпадения паролей (Sprint 5A) 2026-05-17 02:23:26 +03:00
Дмитрий 7efab319e7 feat(auth): A1 — Yandex 360 SSO disabled + tooltip (Sprint 5A) 2026-05-17 02:23:26 +03:00
Дмитрий 0456f4f98a fix(import): final review — /import в явный список Route::view
Final review (🟢 low): SPA-маршрут /import работал через Route::fallback,
но все остальные app-маршруты перечислены явно в Route::view-блоке
(CLAUDE.md документирует явный список как намеренный паттерн — catch-all
перехватывал бы _test/* runtime-роуты Pest). /import добавлен в список
для консистентности и устойчивости.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:33:15 +03:00
Дмитрий 321cf2b22c fix(import): final review — tenant-изоляция import_unknown_statuses под BYPASSRLS
Final review нашёл: HistoricalImportService::loadStatusOverrides и
persistUnknownStatuses запрашивали import_unknown_statuses без явного
where(tenant_id), полагаясь на RLS через SET LOCAL. Но queue worker на prod
работает под crm_supplier_worker — BYPASSRLS-роль (00_create_roles.sql §5),
SET LOCAL не фильтрует → cross-tenant утечка: импорт тенанта A мог подхватить
resolved-маппинг тенанта B и инкрементировать его occurrences.

Добавлен явный where(tenant_id) в обе выборки (конвенция defense-in-depth
00_create_roles.sql:64 — WHERE-фильтры обязательны под BYPASSRLS). +тест
cross-tenant изоляции (red-green verified: без фикса 'Архив' тенанта A
получал status 'closed' из чужого маппинга).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:31:56 +03:00
Дмитрий 4c92d564de feat(import): маршрут /import + сайдбар + инструкция H9
- router/index.ts: добавлен маршрут /import (name=import, layout=app,
  requiresAuth=true, transition=ld-route-fadeup, devIndex=29)
- AppSidebar.vue: пункт «Импорт данных» (mdi-database-import-outline)
  добавлен в группу «Работа» следом за Дашборд
- router.spec.ts: TDD-кейс маршрута /import (layout=app, requiresAuth=true)
- docs/Как_перенести_данные_из_crm-bp-gr.md: инструкция H9 (4 шага + таблица ошибок)
- cspell-words.txt: добавлены формы глагола «замапить»

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:14:04 +03:00
Дмитрий a500882c55 refactor(import): Task 10 code-review — POLL_INTERVAL_MS константа
Code-review Task 10 (🟡): магическое число 2000 (интервал polling'а) вынесено
в именованную константу POLL_INTERVAL_MS — паттерн файла (как в DashboardView).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:08:33 +03:00
Дмитрий e8d0b61bdd feat(import): ImportView — экран импорта CSV
TDD: spec (3 tests) first, then component.
ImportView.vue: upload form + polling + history table + unknown-statuses banner.
Uses api/imports (uploadImport/listImports/getImport/getUnknownStatuses).
setInterval callback wrapped in named async fn (pollOnce) — no eslint-disable needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:05:15 +03:00
Дмитрий 4cccf9ef22 feat(import): api/imports.ts + UnknownStatusesDialog (wizard маппинга)
- api/imports.ts: типы ImportLogResource/UnknownStatus/StatusMapping,
  функции uploadImport/listImports/getImport/getUnknownStatuses/resolveUnknownStatuses
  (apiClient из ./client, стиль api/dashboard.ts)
- UnknownStatusesDialog.vue: wizard маппинга незамапленных статусов воронки
  (ТЗ §6.4/§6.6), 14 канонических slug, defineExpose(selection, save)
- Vitest 3/3 (tests/Frontend/UnknownStatusesDialog.spec.ts)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 19:58:33 +03:00