Commit Graph

2 Commits

Author SHA1 Message Date
Дмитрий 19644a1d36 feat(slepok): Task 2.8 — ProjectService exposes applies_from after slepok-sensitive update
ProjectService::update() теперь возвращает Project с dynamic applies_from
attribute (CarbonImmutable | null), который ProjectResource подхватит для UI
(«изменения вступят в силу с DD.MM 21:00»).

Логика: для каждого изменённого поля из SupplierSnapshotGuard::SLEPOK_SENSITIVE_FIELDS
вычисляется максимум appliesFrom() — slepok-инвариант (до 18:00 МСК = today 21:00,
после = tomorrow 21:00). NULL = применяется немедленно (none changed / no supplier links).

Plan: docs/superpowers/plans/2026-05-26-slepok-routing-protection.md §Task 2.8
Spec: docs/superpowers/specs/2026-05-26-slepok-routing-protection-design.md §4.2.5

Tests: tests/Feature/Services/Project/ProjectServiceAppliesFromTest.php — 4/4 PASS.
ProjectService regression — 7/7 PASS.
2026-05-28 06:49:43 +03:00
Дмитрий 83e0cab8cb feat(slepok): Task 2.7 — SupplierSnapshotGuard::appliesFrom() API
Возвращает CarbonImmutable когда правка slepok-sensitive поля вступит в силу:
  правка до 18:00 МСК → сегодня в 21:00 МСК
  правка с 18:00 МСК и позже → завтра в 21:00 МСК

Возвращает null когда правка применяется немедленно:
  - поле не slepok-sensitive (вне 7 полей SLEPOK_SENSITIVE_FIELDS), либо
  - проект не связан с поставщиком (нет project_supplier_links)

7 slepok-sensitive полей: is_active, daily_limit_target, delivery_days_mask,
regions, signal_identifier, sms_senders, sms_keyword.

Spec §4.2.5. Используется ProjectService (Task 2.8) для прикрепления к
UI-ответу метки «изменения вступят в силу с DD.MM HH:MM МСК».

Plan: docs/superpowers/plans/2026-05-26-slepok-routing-protection.md §Task 2.7

NB plan-bug: оригинальные тесты в плане использовали Project::factory()->make()
с id=null, что приводило к WHERE project_id IS NULL → 0 совпадений. Заменил
на ->create() для реального id (factory default signal_type=null nullable в
projects table, не блокирует create()).

Tests added:
- tests/Feature/Services/Project/SupplierSnapshotGuardAppliesFromTest.php
  (11 tests including dataset-driven для 7 полей, 11/11 isolated PASS).
2026-05-28 06:43:48 +03:00