Files
portal/docs/deploy/supplier-import-lkomega-runbook.md
T
Дмитрий 26999ca597 chore: working tree cleanup pre-llm-first-router merge
Три группы накопившихся auto-правок (НЕ ручные):

1. markdownlint --fix auto-format (~25 .md в docs/superpowers/, docs/security/marketing-vet.md, docs/adr/015, docs/deploy/lkomega-runbook): MD031/MD032 (blank lines around fence/list) + MD004 (bullet markers `+`→`-`). Содержательных текстовых правок 3: ADR-015 bullet, sprint5d-cleanup bullet, router-discipline trailing space.

2. lefthook 2.1.6 → 2.1.8 (package.json + lock): patch-bump, авто-резолвил npm.

3. Observer runtime (docs/observer/): episodes-2026-05.jsonl +420 строк (текущая активность мозга), STATUS.md regen, .pii-counters / .read-counter тики, +2026-05-24-brain-retro.md note.

Цель — разблокировать merge feat/llm-first-router → main (этап 0 плана постановки в боевой). Содержание ветки не трогает.
2026-05-25 14:23:11 +03:00

5.7 KiB

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 (новый)
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 (показать план, ничего не пишет)

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 — реальный прогон

cd /var/www/liderra/app && php artisan supplier:import-projects --tenant=info@lkomega.ru --commit

Вывод: Создано: проектов=N, supplier_projects=M, связок=K.

Шаг 3 — пост-проверка

# Число проектов под тенантом (подставить tenant_id info@lkomega.ru):
php artisan tinker --execute="echo App\Models\Project::on('pgsql_supplier')->where('tenant_id', <ID>)->count();"
  • Выборочно сверить 2–3 проекта: daily_limit_target = сумме площадок; регионы корректны (ГИБДД→Лидерра).

  • Проверить целостность площадок каждого проекта (см. оговорку ниже): каждый проект должен иметь столько связок project_supplier_links, сколько площадок было в группе (обычно 3).

    php artisan tinker --execute="App\Models\Project::on('pgsql_supplier')->where('tenant_id',<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 через ЛК или:

App\Models\Project::on('pgsql_supplier')->where('tenant_id', <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 искажает счётчики).