# Runbook: импорт проектов lkomega → info@lkomega.ru Разовая операция на боевом liderra.ru (`111.88.246.137`). Усыновляет активные проекты поставщика crm.bp-gr.ru (аккаунт lkomega) как проекты Лидерры под тенантом info@lkomega.ru. **Портал не трогается** (никаких save/update/delete). Plan: `docs/superpowers/plans/2026-05-22-supplier-projects-import-lkomega.md` Spec: `docs/superpowers/specs/2026-05-22-supplier-projects-import-lkomega-design.md` ## Деплой команды Скопировать на сервер в `/var/www/liderra/app` (бэкап заменяемого `SupplierRegions.php`): - `app/Support/SupplierRegions.php` (изменён — добавлен `mapFromSupplier`) - `app/Services/Supplier/Import/SupplierImportMapper.php` (новый) - `app/Services/Supplier/Import/SupplierProjectImporter.php` (новый) - `app/Console/Commands/ImportSupplierProjectsCommand.php` (новый) ```bash cp /var/www/liderra/app/app/Support/SupplierRegions.php /var/www/liderra/app/app/Support/SupplierRegions.php.bak-$(date +%Y%m%d-%H%M%S) # scp 4 файла... cd /var/www/liderra/app && php artisan optimize:clear # сброс кэша команд/конфига ``` Команда — не очередь/воркер, `queue:restart` не нужен. ## Шаг 1 — dry-run (показать план, ничего не пишет) ```bash cd /var/www/liderra/app && php artisan supplier:import-projects --tenant=info@lkomega.ru ``` Вывод: число проектов к созданию, таблица (тип / идентификатор[маскирован] / тег / регионы / лимит / площадки B1:id …), список пропусков (`unsupported_source` для dop2, `regions_exclude`, `sms_unparseable`, `already_exists`). **Показать заказчику → получить «ок».** ## Шаг 2 — реальный прогон ```bash cd /var/www/liderra/app && php artisan supplier:import-projects --tenant=info@lkomega.ru --commit ``` Вывод: `Создано: проектов=N, supplier_projects=M, связок=K.` ## Шаг 3 — пост-проверка ```bash # Число проектов под тенантом (подставить tenant_id info@lkomega.ru): php artisan tinker --execute="echo App\Models\Project::on('pgsql_supplier')->where('tenant_id', )->count();" ``` - Выборочно сверить 2–3 проекта: `daily_limit_target` = сумме площадок; регионы корректны (ГИБДД→Лидерра). - **Проверить целостность площадок каждого проекта** (см. оговорку ниже): каждый проект должен иметь столько связок `project_supplier_links`, сколько площадок было в группе (обычно 3). ```bash php artisan tinker --execute="App\Models\Project::on('pgsql_supplier')->where('tenant_id',)->get()->each(fn(\$p)=>print(\$p->id.': '.\$p->supplierProjects()->count().PHP_EOL));" ``` - Подтвердить, что на портале crm.bp-gr.ru **НЕ появилось новых проектов** (команда его не дёргает). ## Атомарность `commit()` оборачивает запись **каждого проекта в отдельную транзакцию** на проде (`DB::connection('pgsql_supplier')->transaction(...)` — Project + все `supplier_projects` + все pivot-связки группы атомарно). Сбой посреди группы → транзакция откатывается → ни проекта, ни partial-связок не остаётся, БД консистентна. Уже созданные ДО сбоя проекты сохраняются (per-group, не per-run). В прод-команде это включается автоматически: гейт `getPdo()->inTransaction()` — false на проде → BEGIN/COMMIT per item; true только под тестовым харнессом `SharesSupplierPdo` (общий PDO уже в транзакции) → внутренний BEGIN пропускается, чтобы избежать «already active transaction» в Pest. При ошибке посреди прогона — просто запустить `--commit` повторно: идемпотентность (`already_exists` по tenant+signal + `firstOrCreate` по `(platform, unique_key, subject_code)`) пропустит уже импортированные проекты и до-создаст оставшиеся. ## Откат Импортированные проекты под тенантом — soft-archive через ЛК или: ```php App\Models\Project::on('pgsql_supplier')->where('tenant_id', ) ->update(['is_active' => false, 'archived_at' => now()]); ``` `supplier_projects`/pivot можно оставить (они указывают на реальные портальные проекты, их используют и другие потоки). ## NB про среду - На worktree-сборке 2 теста `SupplierPortalClient*Test` падают из-за отсутствия node-модуля `playwright` — это известный worktree-only квирк (не регрессия), на боевом/основном checkout с `node_modules` они зелёные. - Larastan: production-код чист; test-only `TestCall`/Mockery (квирк #25) добавляются в `phpstan-baseline.neon` на чистом checkout при интеграции (не из worktree — там дрейф ide-helper искажает счётчики).