Files
portal/app/app/Console/Commands/SupplierDeadlineWatchCommand.php
T
Дмитрий b28f858419 fix(supplier): метка канала B1_/B2_/B3_ не стирается при обновлении заказа
Прод-инцидент 11-12.07.2026: робот итоговой проверки слал письма «нет заказа у
поставщика» на строки, которые в кабинете ЕСТЬ, включены и с верным лимитом.

Корень: кабинет дописывает метку канала к имени строки только при СОЗДАНИИ, а при
обновлении сохраняет имя ровно как прислали. Наш ежедневный updateProject слал голый
uniqueKey и каждый прогон стирал метку. Последствия:
  - итоговая проверка выводила площадку из префикса имени и переставала узнавать
    строку -> ложное missing 11.07 и 12.07;
  - лид от такой строки приходил с project без метки -> webhook не мог определить
    канал и писал platform=DIRECT вместо B1/B2/B3, то есть терялась атрибуция канала.

Что сделано:
  - SupplierPortalClient::toPayload — на update имя уходит с меткой канала; на create
    остаётся голым, там метку ставит сам кабинет и один save с тремя флагами рождает
    три строки, общего префикса у них нет.
  - VerifySupplierOrderJob::normalizeLive — площадка берётся из служебного поля src
    rt/bl/mt, а не из префикса имени; сверка больше не зависит от имени вообще.
  - Новая разовая команда supplier:repair-project-names — возвращает метку строкам,
    у которых её уже стёрли. Payload собирается ИЗ ЖИВОЙ строки кабинета, меняется
    ровно одно поле name; по умолчанию сухой прогон, запись только с --apply.

Ветка пересобрана на gitea/main — закрывает follow-up «фича итоговой проверки заказа
не сведена в main». Попутно возвращён CsvReconcileJobTest, отставший от кода после
сведения main 09.07: он не фейкал fetchDeliveredLeads и падал 9 из 11.

Боевой liderra.ru: выкачено, починена 81 строка, робот показывает 0 расхождений
138 наших строк вместо 57. Двум лидам восстановлен канал по журналу выдач поставщика.

Тесты: Pest supplier 277/277, Pint clean, Larastan 0 новых ошибок.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 19:14:22 +03:00

53 lines
2.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\Console\Commands;
use App\Mail\SupplierDeadlineWarningMail;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
/**
* Сторож дедлайна поставщика: если робот SyncSupplierProjectsJob к порогу не закончил —
* письмо. yellow=20:00, red=20:40 (МСК). Ловит и медленную работу, и зависание, и
* несработавший планировщик (за сегодня нет завершённого запуска).
*
* Spec: docs/superpowers/specs/2026-07-09-supplier-order-verification-and-deadline-watch-design.md §5
*/
final class SupplierDeadlineWatchCommand extends Command
{
protected $signature = 'supplier:deadline-watch {level : yellow|red}';
protected $description = 'Проверить, успел ли робот-заказчик до дедлайна поставщика (21:00 МСК)';
public function handle(): int
{
/** @var 'yellow'|'red' $level */
$level = $this->argument('level') === 'red' ? 'red' : 'yellow';
$today = Carbon::today('Europe/Moscow')->toDateString();
$finished = DB::connection('pgsql_supplier')->table('supplier_sync_runs')
->whereRaw("(started_at AT TIME ZONE 'Europe/Moscow')::date = ?", [$today])
->whereNotNull('finished_at')
->where('status', '!=', 'aborted')
->exists();
if ($finished) {
$this->info('OK: робот сегодня завершил заказ.');
return self::SUCCESS;
}
$reason = 'За сегодня нет завершённого (не aborted) запуска SyncSupplierProjectsJob с finished_at.';
Mail::to((string) config('services.supplier.alert_email'))
->queue(new SupplierDeadlineWarningMail($level, $reason));
$this->warn("{$level}: робот не закончил — письмо отправлено.");
return self::SUCCESS;
}
}