228 lines
8.2 KiB
PHP
228 lines
8.2 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Console\Commands;
|
||
|
|
|
||
|
|
use App\Models\BendingItem;
|
||
|
|
use App\Models\BendingDataRow;
|
||
|
|
use Illuminate\Console\Command;
|
||
|
|
use Illuminate\Support\Facades\DB;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* chandj.bending 누락분 → bending_items + bending_data 직접 이관
|
||
|
|
*
|
||
|
|
* 실행: php artisan bending:import-missing [--dry-run] [--tenant_id=287]
|
||
|
|
*/
|
||
|
|
class BendingImportMissing extends Command
|
||
|
|
{
|
||
|
|
protected $signature = 'bending:import-missing
|
||
|
|
{--tenant_id=287 : 테넌트 ID}
|
||
|
|
{--dry-run : 실행하지 않고 미리보기만}';
|
||
|
|
|
||
|
|
protected $description = 'chandj.bending 누락분 → bending_items 직접 이관';
|
||
|
|
|
||
|
|
private int $tenantId;
|
||
|
|
|
||
|
|
public function handle(): int
|
||
|
|
{
|
||
|
|
$this->tenantId = (int) $this->option('tenant_id');
|
||
|
|
$dryRun = $this->option('dry-run');
|
||
|
|
|
||
|
|
$existingNums = BendingItem::where('tenant_id', $this->tenantId)
|
||
|
|
->whereNotNull('legacy_bending_id')
|
||
|
|
->pluck('legacy_bending_id')
|
||
|
|
->toArray();
|
||
|
|
|
||
|
|
$missing = DB::connection('chandj')->table('bending')
|
||
|
|
->where(function ($q) {
|
||
|
|
$q->whereNull('is_deleted')->orWhere('is_deleted', 0);
|
||
|
|
})
|
||
|
|
->whereNotIn('num', $existingNums)
|
||
|
|
->orderBy('num')
|
||
|
|
->get();
|
||
|
|
|
||
|
|
$this->info("누락분: {$missing->count()}건 (이미 매핑: " . count($existingNums) . "건)");
|
||
|
|
|
||
|
|
if ($dryRun) {
|
||
|
|
$this->preview($missing);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
$success = 0;
|
||
|
|
$bdCount = 0;
|
||
|
|
$errors = 0;
|
||
|
|
|
||
|
|
DB::transaction(function () use ($missing, &$success, &$bdCount, &$errors) {
|
||
|
|
foreach ($missing as $row) {
|
||
|
|
try {
|
||
|
|
$bi = $this->importItem($row);
|
||
|
|
$bd = $this->importBendingData($bi, $row);
|
||
|
|
$bdCount += $bd;
|
||
|
|
$success++;
|
||
|
|
} catch (\Throwable $e) {
|
||
|
|
$this->error(" ❌ #{$row->num} {$row->itemName}: {$e->getMessage()}");
|
||
|
|
$errors++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
$this->newLine();
|
||
|
|
$this->info("완료: {$success}건 이관, {$bdCount}건 전개도, {$errors}건 오류");
|
||
|
|
|
||
|
|
return $errors > 0 ? 1 : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
private function importItem(object $row): BendingItem
|
||
|
|
{
|
||
|
|
$code = $this->generateCode($row);
|
||
|
|
|
||
|
|
$bi = BendingItem::create([
|
||
|
|
'tenant_id' => $this->tenantId,
|
||
|
|
'code' => $code,
|
||
|
|
'legacy_code' => "CHANDJ-{$row->num}",
|
||
|
|
'legacy_bending_id' => $row->num,
|
||
|
|
'item_name' => $row->itemName ?: "부품#{$row->num}",
|
||
|
|
'item_sep' => $this->clean($row->item_sep),
|
||
|
|
'item_bending' => $this->clean($row->item_bending),
|
||
|
|
'material' => $this->clean($row->material),
|
||
|
|
'item_spec' => $this->clean($row->item_spec),
|
||
|
|
'model_name' => $this->clean($row->model_name ?? null),
|
||
|
|
'model_UA' => $this->clean($row->model_UA ?? null),
|
||
|
|
'rail_width' => $this->toNum($row->rail_width ?? null),
|
||
|
|
'exit_direction' => $this->clean($row->exit_direction ?? null),
|
||
|
|
'box_width' => $this->toNum($row->box_width ?? null),
|
||
|
|
'box_height' => $this->toNum($row->box_height ?? null),
|
||
|
|
'front_bottom' => $this->toNum($row->front_bottom_width ?? null),
|
||
|
|
'options' => $this->buildOptions($row),
|
||
|
|
'is_active' => true,
|
||
|
|
'created_by' => 1,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$this->line(" ✅ #{$row->num} → {$bi->id} ({$row->itemName}) [{$code}]");
|
||
|
|
|
||
|
|
return $bi;
|
||
|
|
}
|
||
|
|
|
||
|
|
private function importBendingData(BendingItem $bi, object $row): int
|
||
|
|
{
|
||
|
|
$inputs = json_decode($row->inputList ?? '[]', true) ?: [];
|
||
|
|
if (empty($inputs)) {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
$rates = json_decode($row->bendingrateList ?? '[]', true) ?: [];
|
||
|
|
$sums = json_decode($row->sumList ?? '[]', true) ?: [];
|
||
|
|
$colors = json_decode($row->colorList ?? '[]', true) ?: [];
|
||
|
|
$angles = json_decode($row->AList ?? '[]', true) ?: [];
|
||
|
|
|
||
|
|
$count = count($inputs);
|
||
|
|
for ($i = 0; $i < $count; $i++) {
|
||
|
|
$input = (float) ($inputs[$i] ?? 0);
|
||
|
|
$rate = (string) ($rates[$i] ?? '');
|
||
|
|
$afterRate = ($rate !== '') ? $input + (float) $rate : $input;
|
||
|
|
|
||
|
|
BendingDataRow::create([
|
||
|
|
'bending_item_id' => $bi->id,
|
||
|
|
'sort_order' => $i + 1,
|
||
|
|
'input' => $input,
|
||
|
|
'rate' => $rate !== '' ? $rate : null,
|
||
|
|
'after_rate' => $afterRate,
|
||
|
|
'sum' => (float) ($sums[$i] ?? 0),
|
||
|
|
'color' => (bool) ($colors[$i] ?? false),
|
||
|
|
'a_angle' => (bool) ($angles[$i] ?? false),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
return $count;
|
||
|
|
}
|
||
|
|
|
||
|
|
private function generateCode(object $row): string
|
||
|
|
{
|
||
|
|
$bending = $row->item_bending ?? '';
|
||
|
|
$sep = $row->item_sep ?? '';
|
||
|
|
$material = $row->material ?? '';
|
||
|
|
$name = $row->itemName ?? '';
|
||
|
|
|
||
|
|
$prodCode = match (true) {
|
||
|
|
$bending === '케이스' => 'C',
|
||
|
|
$bending === '하단마감재' && str_contains($sep, '철재') => 'T',
|
||
|
|
$bending === '하단마감재' => 'B',
|
||
|
|
$bending === '가이드레일' && str_contains($sep, '철재') => 'R',
|
||
|
|
$bending === '가이드레일' => 'R',
|
||
|
|
$bending === '마구리' => 'X',
|
||
|
|
$bending === 'L-BAR' => 'L',
|
||
|
|
$bending === '연기차단재' => 'G',
|
||
|
|
default => 'Z',
|
||
|
|
};
|
||
|
|
|
||
|
|
$specCode = match (true) {
|
||
|
|
str_contains($name, '전면') => 'F',
|
||
|
|
str_contains($name, '린텔') => 'L',
|
||
|
|
str_contains($name, '점검') => 'P',
|
||
|
|
str_contains($name, '후면') => 'B',
|
||
|
|
str_contains($name, '상부') || str_contains($name, '덮개') => 'X',
|
||
|
|
str_contains($name, '본체') => 'M',
|
||
|
|
str_contains($name, 'C형') || str_contains($name, '-C') => 'C',
|
||
|
|
str_contains($name, 'D형') || str_contains($name, '-D') => 'D',
|
||
|
|
str_contains($name, '마감') && str_contains($material, 'SUS') => 'S',
|
||
|
|
str_contains($material, 'SUS') => 'S',
|
||
|
|
str_contains($material, 'EGI') => 'E',
|
||
|
|
default => 'Z',
|
||
|
|
};
|
||
|
|
|
||
|
|
$date = $row->registration_date ?? now()->format('Y-m-d');
|
||
|
|
$dateCode = date('ymd', strtotime($date));
|
||
|
|
|
||
|
|
$base = "{$prodCode}{$specCode}{$dateCode}";
|
||
|
|
|
||
|
|
// 중복 방지 일련번호
|
||
|
|
$seq = 1;
|
||
|
|
while (BendingItem::where('tenant_id', $this->tenantId)
|
||
|
|
->where('code', $seq === 1 ? $base : "{$base}-" . str_pad($seq, 2, '0', STR_PAD_LEFT))
|
||
|
|
->whereNull('length_code')
|
||
|
|
->exists()) {
|
||
|
|
$seq++;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $seq === 1 ? $base : "{$base}-" . str_pad($seq, 2, '0', STR_PAD_LEFT);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function buildOptions(object $row): ?array
|
||
|
|
{
|
||
|
|
$opts = [];
|
||
|
|
if (! empty($row->memo)) $opts['memo'] = $row->memo;
|
||
|
|
if (! empty($row->author)) $opts['author'] = $row->author;
|
||
|
|
if (! empty($row->search_keyword)) $opts['search_keyword'] = $row->search_keyword;
|
||
|
|
if (! empty($row->registration_date)) $opts['registration_date'] = (string) $row->registration_date;
|
||
|
|
|
||
|
|
return empty($opts) ? null : $opts;
|
||
|
|
}
|
||
|
|
|
||
|
|
private function preview($missing): void
|
||
|
|
{
|
||
|
|
$grouped = $missing->groupBy(fn ($r) => ($r->item_bending ?: '미분류') . '/' . ($r->item_sep ?: '미분류'));
|
||
|
|
$this->table(['분류', '건수'], $grouped->map(fn ($g, $k) => [$k, $g->count()])->values());
|
||
|
|
|
||
|
|
$this->newLine();
|
||
|
|
$headers = ['num', 'itemName', 'item_sep', 'item_bending', 'material', 'has_bd'];
|
||
|
|
$rows = $missing->take(15)->map(fn ($r) => [
|
||
|
|
$r->num,
|
||
|
|
mb_substr($r->itemName ?? '', 0, 25),
|
||
|
|
$r->item_sep ?? '-',
|
||
|
|
$r->item_bending ?? '-',
|
||
|
|
mb_substr($r->material ?? '-', 0, 12),
|
||
|
|
! empty(json_decode($r->inputList ?? '[]', true)) ? '✅' : '❌',
|
||
|
|
]);
|
||
|
|
$this->table($headers, $rows);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function clean(?string $v): ?string
|
||
|
|
{
|
||
|
|
return ($v === null || $v === '' || $v === 'null') ? null : trim($v);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function toNum(mixed $v): ?float
|
||
|
|
{
|
||
|
|
return ($v === null || $v === '' || $v === 'null') ? null : (float) $v;
|
||
|
|
}
|
||
|
|
}
|