deca81c2d7
CsvReconcileJob — hourly резерв-канал приёма лидов через CSV-экспорт поставщика: - Cache::lock 600s (overlap protection). - Окно [now-25h, now] (запас 1ч над hourly cron). - INSERT supplier_csv_reconcile_log status='running' → 'ok' | 'drift_alert' | 'failed'. - Missing vids → INSERT supplier_leads (platform extracted из project, source='csv_recovery', recovered_from_csv_at=now) + dispatch RouteSupplierLeadJob. - Drift > 5% → CsvDriftAlertMail на services.supplier.alert_email. - UNIQUE-vid conflict → log + skip (idempotency). - На SupplierTransientException/любой Throwable → status='failed', error_message, rethrow. CsvDriftAlertMail + blade-template emails/csv_drift_alert. routes/console.php — Schedule::job(new CsvReconcileJob)->hourly(). config/services.php — supplier.alert_email default 'ops@liderra.ru'. 6 integration tests (CsvReconcileJobTest) + Schedule registration test (через Http::fake + Bus::fake + Mail::fake + SharesSupplierPdo trait для cross-connection). Parallel-test race fix: putSupplierSession() вызывается прямо перед SUT, потому что Sync/Cleanup tests'ы в afterEach делают forget('supplier:session'), а в --parallel режиме воркеры делят Redis DB+prefix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>