Дмитрий
40202caf34
feat(projects-bulk): extend validation for 6 actions + scope
...
- BulkProjectActionRequest: add update_regions/update_days/update_limit actions, scope.filter, withValidator for ids-or-scope + delta/replace mutual exclusion
- ProjectBulkActionsTest: 4 new tests (3 pass, 1 todo pending Task 2 service handler)
- ProjectsActionsTest: update > 100 ids limit test to match new max:500
- phpstan-baseline: add 4 actingAs false-positive entries for new test file
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-12 14:38:59 +03:00
Дмитрий
458fa0b84d
feat(projects): Plan 5 Task 6 — destroy + sync + toggle-active + bulk endpoints
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-11 19:06:07 +03:00
Дмитрий
6238b8b580
feat(projects): Plan 5 Task 5 — update + UpdateProjectRequest + resync trigger
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-11 19:00:39 +03:00
Дмитрий
85f8e9e7a0
feat(jobs): Plan 5 Task 4 — SyncSupplierProjectJob full impl + ensureSupplierProject
...
- SyncSupplierProjectJob: replace stub with full implementation
(tries=3, backoff=[15,60,300]s; resolvePlatforms uppercase B1/B2/B3;
buildUniqueKey site/call→signal_identifier, sms B2→sender+keyword, B3→sender;
column name via strtolower($platform) to match schema snake_case)
- SupplierPortalClient: drop final modifier (Mockery testability);
add ensureSupplierProject() idempotent lookup-or-create wrapper
- Tests: 6 passing (site/call/sms-with-kw/sms-no-kw/exception/partial-failure);
DI fix via dispatchJobSync() helper resolving mock from container;
uppercase platform fixtures matching CHECK constraint B1/B2/B3;
last_error column absent from schema — partial-failure test uses sync_status only
- phpstan-baseline.neon: add $this->mock() Pest TestCase inference gaps
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-11 18:52:51 +03:00
Дмитрий
2ffbb49faa
fix(projects): Plan 5 Task 3 code-review fixes (2 Important + 2 Minor)
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-11 18:38:52 +03:00
Дмитрий
9d2e7270de
feat(projects): Plan 5 Task 3 — store + StoreProjectRequest + ProjectService::create
...
- StoreProjectRequest: 3-way conditional validation (site domain regex, call 7\d{10}, sms senders required)
- ProjectService::create(): max_projects limit check via Tenant.limits JSONB + dispatch SyncSupplierProjectJob
- ProjectController: constructor DI + store() method returning 201
- SyncSupplierProjectJob: stub (Task 4 полная реализация)
- POST /api/projects route inside auth:sanctum+tenant group (name projects.store)
- Migration add_limits_to_tenants: JSONB DEFAULT '{}' per-tenant limits column
- Tenant model: limits added to fillable + casts as array
- schema.sql/CHANGELOG: tenants.limits documented in v8.20
- phpstan-baseline: +8 actingAs entries for new test file
- Quirk: region_mode in request uses 'include'/'exclude' (schema CHECK) not 'all'/'whitelist' (plan spec typo)
- Quirk: Project::first() → Project::where('signal_identifier','x.ru')->latest()->first() (no RefreshDatabase, persistent test DB)
- 8/8 ProjectsStoreTest passed; 699/706 total (4 pre-existing failures unchanged)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-11 18:29:54 +03:00
Дмитрий
e242e7d7fc
fix(projects): Plan 5 Task 2 code-review fixes (2 Important + 2 Minor)
...
I-1/M-1: introduce resolvedSupplierProjects() private helper on Project
model; rewrite aggregateSyncStatus(), aggregateLastSyncedAt(),
getSupplierLinks() to read from eager-loaded supplierB1/B2/B3 relations
instead of SupplierProject::find() — eliminates up to 120 SELECTs/page.
I-2: aggregateLastSyncedAt() now uses sortBy(timestamp) instead of
Collection::min() on Carbon objects (string-comparison was unreliable).
M-2: add explanatory comment on intval+array_filter silent-drop behaviour
in the ?ids batch-fetch path.
M-3: new test — ?ids batch silently excludes foreign-tenant project IDs.
M-4: new test — show returns 200 for archived project (read preserved).
PHPStan baseline updated: 2 new test functions raise actingAs() count 7→9.
Tests: 9/9 passed (33 assertions). Larastan: 0 errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-11 18:15:36 +03:00
Дмитрий
35310b5517
feat(projects): Plan 5 Task 2 — index expanded (filters/search/pagination/ids) + show
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-11 18:08:01 +03:00
Дмитрий
144d4cbb98
feat(db): Plan 5 Task 1 — schema delta v8.19 → v8.20 + Project.archived_at
...
Schema delta (1 правка в db/schema.sql):
- projects + archived_at TIMESTAMPTZ NULL — soft archive flow (отличие от
is_active=false который = pause).
Метрики: 62 базовых таблицы / 117 индексов / 39 RLS (без изменений).
Сопутствующие правки:
- db/CHANGELOG_schema.md — v8.20 entry.
- app/Models/Project — fillable+casts: archived_at datetime + scopeActive +
scopeArchived (whereNull/whereNotNull archived_at).
- Migration guard: Schema::hasColumn() проверка перед ALTER TABLE — предотвращает
"duplicate column" после migrate:fresh (schema.sql v8.20 уже содержит колонку).
Tests:
- ArchivedAtTest.php — 2 it() блоков: archived_at колонка timestamptz + fillable/casts.
- pest --filter=ArchivedAtTest: 2/2 PASS (4 assertions, 485 ms).
- Full suite: 689/686+3 skipped/0 failed (2094 assertions, 84638 ms).
Quirk зафиксирован: Schema::getColumnType('projects', 'archived_at') → 'timestamptz'
(не 'timestamp') — PostgreSQL TIMESTAMPTZ → Doctrine/Laravel native type string.
План spec ожидал 'timestamp', скорректировано в тесте с комментарием.
Spec: docs/superpowers/specs/2026-05-10-claude-brain-extraction-design.md (Plan 5).
Plan: docs/superpowers/plans/2026-05-10-claude-brain-extraction.md Task 1.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-11 17:39:46 +03:00