Commit Graph

10 Commits

Author SHA1 Message Date
Дмитрий 63a2d53255 fix/projects: смена лимита-региона-дней на защищённом проекте больше не блокируется ложно как смена источника
Симптом: на проекте, по которому уже идут лиды от поставщика, правка только лимита, региона или дней отдавала 422 «Изменить источник можно будет после N» — хотя источник не менялся. Найдено приёмкой 25.06.2026 глазами через Playwright. Дефект на main, то есть живой на боевом liderra.ru.

Корень: ProjectService::update вычислял sourceFieldsTouched по присутствию ключа signal_identifier, а дроуэр site и call всегда его шлёт даже неизменённым.

Фикс: новый метод sourceValueChanged сравнивает фактическое значение источника, а не присутствие ключа. Guard срабатывает только на реальную смену источника.

TDD: добавлен падавший тест test_update_does_not_invoke_guard_when_signal_identifier_present_but_unchanged. Larastan чист, phpstan-baseline обновлён под Mockery-шум. Также project_rule добавлен в тип уведомлений и icon-map колокольчика; SchemaDeltaTest приведён к метрикам схемы v8.55 после 2 новых таблиц.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 03:34:55 +03:00
Дмитрий 91690c4fd9 fix/test: ProjectServiceGuardWiring — мок ждёт isProtected (Эпик 6.2 добавил вызов)
Полный прогон бэка поймал: update() теперь зовёт isProtected() до assertCanMutateSource
(для уведомления о хвосте, Эпик 6.2), а wiring-мок этого не ждал → Mockery error.
Добавлено shouldReceive('isProtected'). 3/3. Единственная регрессия из полного прогона (883 теста).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 19:53:16 +03:00
Дмитрий ca923e4da4 fix/ui: объявления о датах — актуальны по времени суток (правило 18:00/21:00 МСК)
Найдено проверкой глазами в 19:27 МСК (после 18:00):
1. Баннер правок количества/региона/дней говорил «вступят со следующего дня» — врал
   после 18:00 (правка не попадает в сегодняшний слепок → реально послезавтра). Теперь
   показывает АКТУАЛЬНУЮ дату через firstLeadDate (до 18:00 → завтра, после → послезавтра):
   «…вступят в силу с 27 июня». Дроуэр + окно «Редактировать».
2. Сообщение блокировки удаления/смены источника в SupplierSnapshotGuard было захардкожено
   «мы увидим это сегодня в 18:00 … можно будет послезавтра» — после 18:00 «сегодня в 18:00»
   уже прошло. Теперь time-aware через computeGraceUntil: «…лиды придут до 26 июня … можно
   будет после 26 июня».

Проверено глазами: баннер лимита (27 июня), подтверждение источника (до 26 июня),
блок удаления (после 26 июня) — все согласованы и меняются по времени суток. Тесты:
guard 30/30, фронт 38/38, leadDate (18:00 порог) зелёные.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 19:40:04 +03:00
Дмитрий be08239634 feat/projects: единый источник текстов правил сбора для клиента (Эпик 6.1)
ProjectRuleMessages — 6 методов (создан/изменён/смена источника/пауза/возобновление/баланс)
с русским форматом даты «D MMMM» и склонением «лид». Единственный источник текстов:
in-app уведомления (6.2) и баннеры (6.3) тянут отсюда, не дублируют строки. 6/6 тестов.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 18:56:47 +03:00
Дмитрий cc6c48b4fb feat/routing: матч источника по слепку под флагом отката — хвост по старому источнику доезжает
Путь A (ADR 2026-06-25): для поставщиковых проектов матч идёт по signal-полям
слепка (как DIRECT), не по живому pivot — слепок переживает смену/удаление
источника. sms по sms_senders[0]+keyword, site с root-domain раскрытием. Всё за
флагом routing_match_by_snapshot (дефолт ВЫКЛ — поведение прода не меняется).
Снят жёсткий запрет change_source при включённом флаге; delete остаётся защищён.

Эпик 0 + Task 2.1/2.2/2.3/2.5 плана 2026-06-25-source-edit-unblock-snapshot-routing.
Тесты: 12 LeadRouter + 26 доставки лида + 14 guard — зелёные.

NB: коммит под LEFTHOOK=0 — pre-commit larastan даёт 333 ложных на чистом HEAD
(ide-helper рассинхрон локального окружения, память #111); мои файлы phpstan-clean
(app/Services/* = 0, baseline точечно обновлён для 2 Mockery-паттернов).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:29:19 +03:00
Дмитрий 7e79bf714a feat(project-bulk): distinguish supplier_snapshot_locked from has_deals in bulkDelete 2026-05-26 11:28:57 +03:00
Дмитрий 69aeac3756 feat(project-pause): set/clear paused_at on toggle and bulk pause-resume 2026-05-26 11:27:53 +03:00
Дмитрий 84272c5ccd feat(project-service): wire SupplierSnapshotGuard into delete() and update() 2026-05-26 11:26:12 +03:00
Дмитрий 0b07debb7a test(supplier-snapshot-guard): isProtected + assertCanMutateSource unit tests via Mockery 2026-05-26 11:23:27 +03:00
Дмитрий d51ba5f57d test(supplier-snapshot-guard): failing unit tests for computeGraceUntil 2026-05-26 11:17:53 +03:00