← К списку версий
v0.90.0
01.10.2026
MINOR
Настоящие даты закрытия задач и запрет пустых месячных писем
ТЗ 270 «Месячные письма приходят пустыми». Найдено две причины. Первая: экспорт платформы задач /api/export/full вообще не отдаёт дату закрытия задачи — синхронизация её никогда не заполняла, поэтому 667 закрытых задач оставались без даты, а отчётный период — это диапазон дат, и задача без даты не попадала ни в один месяц. Добавлена команда tasks:import-completion-dates, которая берёт completed_date из БД платформы напрямую (подключение task_platform) и сверяет задачи по внешнему идентификатору; на TEST проставлено 460 настоящих дат (162 за июль, 95 за август, 123 за сентябрь), снесено 60 выдуманных дат, поправлено 3. Вторая: пустое письмо всё равно уходило клиенту. Добавлен гейт наполнения — письмо, в котором нет ни задач, закрытых внутри отчётного месяца, ни задач в работе, не отправляется, а остаётся в черновиках с причиной; тело письма пересобирается по свежим данным проекта прямо перед постановкой в очередь, потому что буфер готовится за месяцы вперёд. Версия v0.90.0.
Почему письма были пустыми
- Экспорт не отдаёт дату закрытия - В блоке «задачи» ответа /api/export/full (task.ipd 2.52.5) ключа даты закрытия нет, тогда как у проектов он есть. Синхронизация completed_at в my.ipd не заполнялась никогда: 667 закрытых задач оставались без даты, а месячный отчёт отбирает задачи по диапазону дат — задача без даты не попадает ни в один месяц.
- Даты, выдуманные моделью - App\Models\Task при переходе в статус completed сам проставляет completed_at = now(). У задач, закрытых в MY IPD без даты с платформы, completed_at совпадал с updated_at — это отметка синхронизации, а не момент выполнения. Такие даты команда сносит, а даты, проставленные вручную (completed_at != updated_at), не трогает.
- Пустое письмо уходило клиенту - Даже когда задач в периоде не было, месячное письмо всё равно попадало в очередь на отправку. На проде 01.10.2026 ушло 5 месячных писем, из них 4 содержали нули.
Что сделано
- Команда tasks:import-completion-dates - Берёт настоящие completed_date из БД платформы напрямую (подключение task_platform в config/database.php, параметры TASK_PLATFORM_DB_* в .env). Сверяет задачи по tasks.external_id = платформа tasks.task_id. Опции: --project (один проект), --dry-run. Платформа — источник истины: есть дата — проставляем/исправляем, нет даты — сносим выдуманную (равную updated_at).
- Даты ставятся поштучно по своим значениям - Задачи группируются по дате и обновляются отдельным UPDATE на каждую дату. Одним общим UPDATE всем ушла бы дата первой задачи — из-за такой ошибки первый прогон дал всем 397 задач 27.07.2026 и был откачен по бэкапу.
- Гейт наполнения месячного письма - Новый сервис App\Services\Reports\ReportContentCheck считает наполнение по живым данным проекта: задачи, закрытые внутри периода, и задачи в работе, видимые клиенту. Если обе цифры нулевые — письмо не отправляется: mail:send-monthly кладёт его в черновик с причиной, email:send-prepared отменяет отправку, email:rebuild-monthly показывает «Пустых отчётов: N». Счётчики проекта (tasks_total, progress_pct) в проверке не участвуют — они устаревают и показывали нули при полном отчёте.
- Письмо пересобирается перед отправкой - Буфер писем готовится на месяцы вперёд, и задачи клиента закрываются уже после сборки. Перед постановкой в очередь тело, тема и metadata письма пересобираются по свежим данным проекта (MonthlyDigestService::refresh), в metadata пишется content с числами закрытых и идущих в работу задач.
Что важно знать
- Пустой отчёт — это нормальное состояние - Для будущих месяцев (ноябрь 2026 и далее) отчёт закономерно пуст: данных за отчётный месяц ещё нет. Письмо остаётся в буфере и наполнится, когда клиенту закроют задачи; отправка заблокирована только если к дате отправки наполнения так и не появится.
- Задание платформы не менялось - Правки только в my.ipd. task.ipd не трогался: даты читаются из его локальной копии БД в режиме чтения.
- Что сделано на TEST - Проставлено 460 дат закрытия: июль 2026 — 162, август — 95, сентябрь — 123; снесено 60 отметок синхронизации, исправлено 3. Месячные письма с 01.10.2026 по 01.03.2027 пересобраны (60 писем), в пересборке видно «закрыто N, в работе M» по каждому клиенту.
- Бэкапы - Перед заливкой дат: storage/app/private/tasks_before_completion_dates_2026-10-01_101513.sql. Перед пересборкой месячных: storage/app/private/prepared_emails_monthly_before_rebuild_2026-10-01_082408.sql.
Результат
- Месячный отчёт показывает реально закрытые за месяц задачи, и клиент не получает письмо с нулями: пустой отчёт остаётся в черновиках с причиной.