Files
autocrm/app/Console/Commands/ImportLaborCatalogCommand.php
T
Vasyka cbf8deabf3 Fix labor table grouping colspan + auto-import catalog on deploy
- LaborResource: hide category column from data rows by default
  (toggleable) so group headers span all columns cleanly; the group
  header already shows the localized category name
- ImportLaborCatalogCommand: add --all flag to iterate every company,
  refactor into importForCompany() helper, keep idempotent (skips
  existing codes, updates only with --force)
- entrypoint.sh: run catalog:import-labors --all after each deploy so
  all 386 operations + 45 categories are seeded automatically

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-08 05:07:32 +00:00

170 lines
6.0 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\Central\Company;
use App\Models\Scopes\TenantScope;
use App\Models\Tenant\Labor;
use App\Models\Tenant\LaborCategory;
use App\Tenancy\TenantManager;
use Illuminate\Console\Command;
class ImportLaborCatalogCommand extends Command
{
protected $signature = 'catalog:import-labors
{company? : Company slug or numeric ID (omit with --all)}
{--all : Import for every company in the database}
{--file= : Path to JSON file (defaults to database/data/labor-catalog.json)}
{--force : Update existing records by code}';
protected $description = 'Import labor catalog (categories + operations) from JSON into labor_categories and labors tables';
public function handle(): int
{
$file = $this->resolveFile();
if (! $file) {
$this->error('JSON file not found. Use --file=/path/to/labor-catalog.json');
return 1;
}
$data = json_decode(file_get_contents($file), true);
if (! $data) {
$this->error('Failed to parse JSON file.');
return 1;
}
if ($this->option('all')) {
$companies = Company::all();
foreach ($companies as $company) {
$this->importForCompany($company, $data);
}
return 0;
}
$arg = $this->argument('company');
if (! $arg) {
$this->error('Provide a company slug/id, or use --all.');
return 1;
}
$company = is_numeric($arg)
? Company::find((int) $arg)
: Company::where('slug', $arg)->first();
if (! $company) {
$this->error("Company '{$arg}' not found.");
return 1;
}
$this->importForCompany($company, $data);
return 0;
}
private function resolveFile(): ?string
{
$file = $this->option('file');
if ($file && file_exists($file)) return $file;
foreach ([
database_path('data/labor-catalog.json'),
'/tmp/service/todo/psauto-katalog-rabot-full.json',
storage_path('app/labor-catalog.json'),
] as $path) {
if (file_exists($path)) return $path;
}
return null;
}
private function importForCompany(Company $company, array $data): void
{
app(TenantManager::class)->setCurrent($company);
$force = $this->option('force');
$allCats = $data['categories'] ?? [];
$operations = $data['operations'] ?? [];
$catMap = collect($allCats)->keyBy('id');
$usedCatIds = collect($operations)->pluck('service_category_id')->unique();
$leafCats = $catMap->only($usedCatIds->all());
$this->line("── {$company->name} (id={$company->id}) ── cats:{$leafCats->count()} ops:" . count($operations));
// ── 1. Labor categories ───────────────────────────────────────────
$catCodeToId = [];
$sortBase = 100;
foreach ($leafCats as $leaf) {
$parent = $catMap->get($leaf['parent_id']);
$nameRu = ($parent ? $parent['name'] . ' · ' : '') . $leaf['name'];
$code = $leaf['code'];
$existing = LaborCategory::withoutGlobalScope(TenantScope::class)
->where('company_id', $company->id)
->where('name', $code)
->first();
if ($existing) {
if ($force) {
$existing->update(['name_ru' => $nameRu]);
}
} else {
$existing = LaborCategory::withoutGlobalScope(TenantScope::class)->create([
'company_id' => $company->id,
'name' => $code,
'name_ro' => $nameRu,
'name_ru' => $nameRu,
'sort_order' => $sortBase + ($leaf['sort_order'] ?? 0),
]);
}
$catCodeToId[$leaf['id']] = $code;
$sortBase += 5;
}
// ── 2. Labors ─────────────────────────────────────────────────────
$created = 0;
$skipped = 0;
foreach ($operations as $op) {
$catKey = $catCodeToId[$op['service_category_id']] ?? null;
if (! $catKey) { $skipped++; continue; }
$existing = Labor::withoutGlobalScope(TenantScope::class)
->where('company_id', $company->id)
->where('code', $op['code'])
->exists();
if ($existing) {
if ($force) {
Labor::withoutGlobalScope(TenantScope::class)
->where('company_id', $company->id)
->where('code', $op['code'])
->update([
'name_ru' => $op['name'],
'hours' => round($op['norm_minutes'] / 60, 2),
'price' => round($op['base_price_minor'] / 100, 2),
]);
}
$skipped++;
continue;
}
Labor::withoutGlobalScope(TenantScope::class)->create([
'company_id' => $company->id,
'category' => $catKey,
'code' => $op['code'],
'name_ro' => $op['name'],
'name_ru' => $op['name'],
'pricing_mode' => 'hourly',
'hours' => round($op['norm_minutes'] / 60, 2),
'price' => round($op['base_price_minor'] / 100, 2),
'is_active' => (bool) ($op['is_active'] ?? true),
]);
$created++;
}
$this->line(" Created: {$created} Skipped/existing: {$skipped}");
}
}