Commit Graph

4 Commits

Author SHA1 Message Date
Дмитрий 1be2d62f9e feat(supplier): group recompute + pause + source-change + root auto-link
Закрывает замечания заказчика (22.05.2026) по проектам/поставщику. Все 4 куска
имеют общий корень: online-синхронизация одного проекта работала с данными ЭТОГО
проекта, а не пересчитывала всю «группу» (проекты разных tenant'ов с одним
identifier) — отсюда переплата ×3 при изменении лимита, затирание регионов/дней
группы, неотправленная пауза, и осиротевшие проекты при смене источника.

1. Групповой пересчёт в SyncSupplierProjectJob::handleOnline (#1 при изменении,
   #2 дни, #3 регионы, C2/C3): union regions, computeOrder eligible,
   distributeForPlatform — те же расчёты, что в ночном syncGroup. Online и
   ночной теперь дают идентичный supplier-state, расхождение устранено.

2. Пауза #10:
   - ProjectController::toggleActive — диспатчит SyncSupplierProjectJob;
   - ProjectService::bulkPauseResume — диспатчит sync per project;
   - DTO status вычисляется из groupActive (paused когда группа без активных);
   - sp.inactive_since пишется при пересинке (для UI/DTO консистентности).

3. Смена источника #8/#9 в ProjectService::update:
   - до update снимается старый buildUniqueKeyAgnostic;
   - если изменился — отвязываем старые supplier_projects от этого project
     (pivot + legacy FK), DeleteSupplierProjectJob удаляет их у поставщика
     при отсутствии других потребителей, либо пересинкает агрегат.

4. Перенос auto-link корня из feat/root-domain-auto-link: новый
   App\Support\SupplierIdentifier::extractRootDomain + блоки auto-link в
   обоих джобах (online + nightly).

Тесты: TDD на каждый кусок. SyncSupplierProjectJobTest +2 (group recompute,
pause). ProjectUpdateDedupTest +1 (source detach + cleanup dispatch).
ProjectsActionsTest +2 (toggle + bulk pause dispatches).

Регрессия: 186/186 passed (Project/Plan5/Projects + Supplier), 502 assertions.

Деплой: дельтой на боевой (база = root-domain ветка; на боевом джобы СТАРЕЕ
main, deliver через копию изменённых файлов + config:cache + restart queue).

План: docs/superpowers/plans/2026-05-22-замечания-проекты-чеклист.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 16:52:30 +03:00
Дмитрий 7408bc4232 feat(projects): hard delete with deals-guard, replace archive
- ProjectService: add delete() with DB-level deals check (bypasses SoftDeletes
  scope via DB::table), captures supplier pivot IDs before cascade, dispatches
  DeleteSupplierProjectJob; add bulkDelete() private method; replace archive
  match arm with delete; remove archive() method
- ProjectController: destroy() calls delete() not archive(); update docblocks
- BulkProjectActionRequest: replace 'archive' with 'delete' in Rule::in for action
- Tests: ProjectDeleteTest (2 new TDD tests), ProjectsActionsTest updated
  (destroy → hard delete, 409-already-archived → 422-has-deals, bulk archive → bulk delete)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 08:08:33 +03:00
Дмитрий 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