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}"); } }