Files
sam-api/app/Services/BendingCodeService.php
강영보 7cd0aab884 refactor: [절곡] bending_items code를 'BD' 단일값으로 통일
- bending_items.code: prod+spec(CP,RS 등) → 'BD' (절곡품 공통 접두사)
- bending_models 패턴(GR/SB/BB)과 동일 레벨로 통일
- import 스크립트 3개 + BendingCodeService.resolveItem 수정
- 마이그레이션 SQL 업데이트
- DB 266건 업데이트 완료
2026-03-21 15:29:05 +09:00

206 lines
7.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Services;
use App\Models\BendingItem;
use App\Models\Orders\Order;
class BendingCodeService extends Service
{
// =========================================================================
// 제품 코드 (7종)
// =========================================================================
public const PRODUCTS = [
['code' => 'R', 'name' => '가이드레일(벽면형)'],
['code' => 'S', 'name' => '가이드레일(측면형)'],
['code' => 'G', 'name' => '연기차단재'],
['code' => 'B', 'name' => '하단마감재(스크린)'],
['code' => 'T', 'name' => '하단마감재(철재)'],
['code' => 'L', 'name' => 'L-Bar'],
['code' => 'C', 'name' => '케이스'],
];
// =========================================================================
// 종류 코드 + 사용 가능 제품
// 경동기업 재공품 LOT 채번 규칙 기준 (2026-03 최신)
// =========================================================================
public const SPECS = [
['code' => 'M', 'name' => '본체', 'products' => ['R']],
['code' => 'M', 'name' => '본체디딤', 'products' => ['S']],
['code' => 'T', 'name' => '본체(철재)', 'products' => ['R', 'S']],
['code' => 'C', 'name' => 'C형', 'products' => ['R', 'S']],
['code' => 'D', 'name' => 'D형', 'products' => ['R', 'S']],
['code' => 'S', 'name' => 'SUS마감재', 'products' => ['R', 'B', 'T']],
['code' => 'S', 'name' => 'SUS마감재(3)', 'products' => ['S']],
['code' => 'U', 'name' => 'SUS마감재(3)', 'products' => ['S']],
['code' => 'W', 'name' => '본체(L120)', 'products' => ['R', 'S']],
['code' => 'F', 'name' => 'SUS마감재(L120)', 'products' => ['R', 'S']],
['code' => 'E', 'name' => 'EGI', 'products' => ['B', 'T']],
['code' => 'I', 'name' => '화이바원단(W50)', 'products' => ['G']],
['code' => 'H', 'name' => '화이바원단(W80)', 'products' => ['G']],
['code' => 'A', 'name' => '스크린용', 'products' => ['L']],
['code' => 'F', 'name' => '전면부', 'products' => ['C']],
['code' => 'P', 'name' => '점검구', 'products' => ['C']],
['code' => 'L', 'name' => '린텔부', 'products' => ['C']],
['code' => 'B', 'name' => '후면코너부', 'products' => ['C']],
];
// =========================================================================
// 모양&길이 코드
// 연기차단재(G)는 SMOKE_BARRIER, 그 외는 GENERAL 사용
// 신규 길이 발생 시 코드 추가 가능 (확장형)
// =========================================================================
public const LENGTHS_SMOKE_BARRIER = [
['code' => '53', 'name' => 'W50 × 3000'],
['code' => '54', 'name' => 'W50 × 4000'],
['code' => '83', 'name' => 'W80 × 3000'],
['code' => '84', 'name' => 'W80 × 4000'],
];
public const LENGTHS_GENERAL = [
['code' => '06', 'name' => '610'],
['code' => '12', 'name' => '1219'],
['code' => '17', 'name' => '1750'],
['code' => '20', 'name' => '2000'],
['code' => '24', 'name' => '2438'],
['code' => '30', 'name' => '3000'],
['code' => '35', 'name' => '3500'],
['code' => '40', 'name' => '4000'],
['code' => '41', 'name' => '4150'],
['code' => '42', 'name' => '4200'],
['code' => '43', 'name' => '4300'],
['code' => '45', 'name' => '4500'],
];
// =========================================================================
// 제품+종류 → 원자재(재질) 매핑
// =========================================================================
public const MATERIAL_MAP = [
// 연기차단재
'G:I' => '화이바원단',
'G:H' => '화이바원단',
// 하단마감재(스크린)
'B:S' => 'SUS 1.2T',
'B:E' => 'EGI 1.55T',
// 하단마감재(철재)
'T:S' => 'SUS 1.2T',
'T:E' => 'EGI 1.55T',
// L-Bar
'L:A' => 'EGI 1.55T',
// 가이드레일(벽면형)
'R:M' => 'EGI 1.55T',
'R:T' => 'EGI 1.55T',
'R:C' => 'EGI 1.55T',
'R:D' => 'EGI 1.55T',
'R:S' => 'SUS 1.2T',
'R:W' => 'EGI 1.55T',
'R:F' => 'SUS 1.2T',
// 가이드레일(측면형)
'S:M' => 'EGI 1.55T',
'S:T' => 'EGI 1.55T',
'S:C' => 'EGI 1.55T',
'S:D' => 'EGI 1.55T',
'S:S' => 'SUS 1.2T',
'S:U' => 'SUS 1.2T',
'S:W' => 'EGI 1.55T',
'S:F' => 'SUS 1.2T',
// 케이스
'C:F' => 'EGI 1.55T',
'C:P' => 'EGI 1.55T',
'C:L' => 'EGI 1.55T',
'C:B' => 'EGI 1.55T',
];
/**
* 코드맵 전체 반환 (프론트엔드 드롭다운 구성용)
*/
public function getCodeMap(): array
{
return [
'products' => self::PRODUCTS,
'specs' => self::SPECS,
'lengths' => [
'smoke_barrier' => self::LENGTHS_SMOKE_BARRIER,
'general' => self::LENGTHS_GENERAL,
],
'material_map' => self::MATERIAL_MAP,
];
}
/**
* 드롭다운 선택 조합 → bending_items 품목 매핑 조회
*
* legacy_code 패턴: BD-{prod}{spec}-{length} (예: BD-CP-30)
*/
public function resolveItem(string $prodCode, string $specCode, string $lengthCode): ?array
{
// 1차: lot_no 앞 2자리(prod+spec) + length_code로 조회
$item = BendingItem::where('tenant_id', $this->tenantId())
->where('lot_no', 'like', "{$prodCode}{$specCode}%")
->where('length_code', $lengthCode)
->where('is_active', true)
->first();
// 2차: legacy_code 폴백
if (! $item) {
$legacyCode = "BD-{$prodCode}{$specCode}-{$lengthCode}";
$item = BendingItem::where('tenant_id', $this->tenantId())
->where('legacy_code', $legacyCode)
->where('is_active', true)
->first();
}
if (! $item) {
return null;
}
return [
'item_id' => $item->id,
'item_code' => $item->lot_no ?? $item->code,
'item_name' => $item->item_name,
'specification' => $item->item_spec,
'unit' => 'EA',
];
}
/**
* LOT 번호 생성 (일련번호 없음 — 같은 날 같은 조합은 동일 LOT)
*
* 예: prod='C', spec='L', length='30', date='2026-03-18' → 'CL6318-30'
*/
public function generateLotNumber(string $prodCode, string $specCode, string $lengthCode, string $date): string
{
$dateCode = self::generateDateCode($date);
return "{$prodCode}{$specCode}{$dateCode}-{$lengthCode}";
}
/**
* 날짜 → 4자리 날짜코드
*
* 2026-03-17 → '6317'
* 2026-10-05 → '6A05'
*/
public static function generateDateCode(string $date): string
{
$dt = \Carbon\Carbon::parse($date);
$year = $dt->year % 10;
$month = $dt->month;
$day = $dt->day;
$monthCode = $month >= 10
? chr(55 + $month) // 10=A, 11=B, 12=C
: (string) $month;
return $year.$monthCode.str_pad($day, 2, '0', STR_PAD_LEFT);
}
/**
* 제품+종류 → 원자재(재질) 반환
*/
public static function getMaterial(string $prodCode, string $specCode): ?string
{
return self::MATERIAL_MAP["{$prodCode}:{$specCode}"] ?? null;
}
}