feat: Items API BOM 데이터 확장 기능 추가
- GET /items/{id} 응답에 BOM 확장 데이터 포함
- child_item_code, child_item_name, unit, specification 필드 추가
- expandBomData() 메서드 구현 (ItemsService)
- Product 모델 bom 캐스팅 추가
This commit is contained in:
@@ -17,7 +17,7 @@ class Product extends Model
|
||||
protected $fillable = [
|
||||
'tenant_id', 'code', 'name', 'unit', 'category_id',
|
||||
'product_type', // 라벨/분류용
|
||||
'attributes', 'attributes_archive', 'options', 'description',
|
||||
'attributes', 'attributes_archive', 'options', 'bom', 'description',
|
||||
'is_sellable', 'is_purchasable', 'is_producible',
|
||||
// 하이브리드 구조: 최소 고정 필드
|
||||
'safety_stock', 'lead_time', 'is_variable_size',
|
||||
@@ -34,6 +34,7 @@ class Product extends Model
|
||||
'attributes' => 'array',
|
||||
'attributes_archive' => 'array',
|
||||
'options' => 'array',
|
||||
'bom' => 'array',
|
||||
'bending_details' => 'array',
|
||||
'certification_start_date' => 'date',
|
||||
'certification_end_date' => 'date',
|
||||
|
||||
@@ -136,6 +136,147 @@ private function processDynamicOptions(array &$data, string $sourceTable): void
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BOM 데이터에서 child_item_id, child_item_type, quantity만 추출
|
||||
*
|
||||
* @param array|null $bomData BOM 데이터 배열
|
||||
* @return array|null [{child_item_id, child_item_type, quantity}, ...]
|
||||
*/
|
||||
private function extractBomData(?array $bomData): ?array
|
||||
{
|
||||
if (empty($bomData)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$extracted = [];
|
||||
foreach ($bomData as $item) {
|
||||
if (! is_array($item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$childItemId = $item['child_item_id'] ?? null;
|
||||
$childItemType = $item['child_item_type'] ?? $item['ref_type'] ?? 'PRODUCT';
|
||||
$quantity = $item['quantity'] ?? null;
|
||||
|
||||
if ($childItemId === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// child_item_type 정규화 (PRODUCT/MATERIAL)
|
||||
$childItemType = strtoupper($childItemType);
|
||||
if (! in_array($childItemType, ['PRODUCT', 'MATERIAL'])) {
|
||||
$childItemType = 'PRODUCT';
|
||||
}
|
||||
|
||||
$extracted[] = [
|
||||
'child_item_id' => (int) $childItemId,
|
||||
'child_item_type' => $childItemType,
|
||||
'quantity' => $quantity !== null ? (float) $quantity : 1,
|
||||
];
|
||||
}
|
||||
|
||||
return empty($extracted) ? null : $extracted;
|
||||
}
|
||||
|
||||
/**
|
||||
* BOM 데이터 확장 (child_item 상세 정보 포함)
|
||||
*
|
||||
* DB에 저장된 [{child_item_id, child_item_type, quantity}] 형태를
|
||||
* [{child_item_id, child_item_type, child_item_code, child_item_name, quantity, unit, specification?}]
|
||||
* 형태로 확장하여 반환
|
||||
*
|
||||
* @param array $bomData BOM 데이터 배열 [{child_item_id, child_item_type, quantity}, ...]
|
||||
* @param int $tenantId 테넌트 ID
|
||||
* @return array 확장된 BOM 데이터
|
||||
*/
|
||||
private function expandBomData(array $bomData, int $tenantId): array
|
||||
{
|
||||
if (empty($bomData)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// child_item_type별로 ID 분리
|
||||
$productIds = [];
|
||||
$materialIds = [];
|
||||
|
||||
foreach ($bomData as $item) {
|
||||
$childId = $item['child_item_id'] ?? null;
|
||||
$childType = strtoupper($item['child_item_type'] ?? 'PRODUCT');
|
||||
|
||||
if ($childId === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($childType === 'MATERIAL') {
|
||||
$materialIds[] = $childId;
|
||||
} else {
|
||||
$productIds[] = $childId;
|
||||
}
|
||||
}
|
||||
|
||||
// Products에서 조회 (FG, PT)
|
||||
$products = collect([]);
|
||||
if (! empty($productIds)) {
|
||||
$products = Product::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->whereIn('id', $productIds)
|
||||
->get(['id', 'code', 'name', 'unit'])
|
||||
->keyBy('id');
|
||||
}
|
||||
|
||||
// Materials에서 조회 (SM, RM, CS)
|
||||
$materials = collect([]);
|
||||
if (! empty($materialIds)) {
|
||||
$materials = Material::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->whereIn('id', $materialIds)
|
||||
->get(['id', 'material_code', 'name', 'unit', 'specification'])
|
||||
->keyBy('id');
|
||||
}
|
||||
|
||||
// BOM 데이터 확장
|
||||
$expanded = [];
|
||||
foreach ($bomData as $item) {
|
||||
$childId = $item['child_item_id'] ?? null;
|
||||
$childType = strtoupper($item['child_item_type'] ?? 'PRODUCT');
|
||||
|
||||
if ($childId === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = [
|
||||
'child_item_id' => (int) $childId,
|
||||
'child_item_type' => $childType,
|
||||
'quantity' => $item['quantity'] ?? 1,
|
||||
];
|
||||
|
||||
// child_item_type에 따라 조회
|
||||
if ($childType === 'MATERIAL' && isset($materials[$childId])) {
|
||||
$material = $materials[$childId];
|
||||
$result['child_item_code'] = $material->material_code;
|
||||
$result['child_item_name'] = $material->name;
|
||||
$result['unit'] = $material->unit;
|
||||
if ($material->specification) {
|
||||
$result['specification'] = $material->specification;
|
||||
}
|
||||
} elseif ($childType === 'PRODUCT' && isset($products[$childId])) {
|
||||
$product = $products[$childId];
|
||||
$result['child_item_code'] = $product->code;
|
||||
$result['child_item_name'] = $product->name;
|
||||
$result['unit'] = $product->unit;
|
||||
} else {
|
||||
// 해당하는 품목이 없으면 null 처리
|
||||
$result['child_item_code'] = null;
|
||||
$result['child_item_name'] = null;
|
||||
$result['unit'] = null;
|
||||
}
|
||||
|
||||
$expanded[] = $result;
|
||||
}
|
||||
|
||||
return $expanded;
|
||||
}
|
||||
|
||||
/**
|
||||
* 통합 품목 조회 (materials + products UNION)
|
||||
*
|
||||
@@ -360,6 +501,11 @@ public function getItem(
|
||||
$data['item_type'] = $itemType;
|
||||
$data['type_code'] = $product->product_type;
|
||||
|
||||
// BOM 데이터 확장 (child_item 상세 정보 포함)
|
||||
if (! empty($data['bom'])) {
|
||||
$data['bom'] = $this->expandBomData($data['bom'], $tenantId);
|
||||
}
|
||||
|
||||
// 가격 정보 추가
|
||||
if ($includePrice) {
|
||||
$data['prices'] = $this->fetchPrices('PRODUCT', $id, $clientId, $priceDate);
|
||||
@@ -463,6 +609,9 @@ private function createProduct(array $data, int $tenantId, int $userId): Product
|
||||
// 동적 필드를 options에 병합
|
||||
$this->processDynamicOptions($data, 'products');
|
||||
|
||||
// BOM 데이터 처리 (child_item_id, quantity만 추출)
|
||||
$bomData = $this->extractBomData($data['bom'] ?? null);
|
||||
|
||||
$payload = $data;
|
||||
$payload['tenant_id'] = $tenantId;
|
||||
$payload['created_by'] = $userId;
|
||||
@@ -470,6 +619,7 @@ private function createProduct(array $data, int $tenantId, int $userId): Product
|
||||
$payload['is_sellable'] = $payload['is_sellable'] ?? true;
|
||||
$payload['is_purchasable'] = $payload['is_purchasable'] ?? false;
|
||||
$payload['is_producible'] = $payload['is_producible'] ?? false;
|
||||
$payload['bom'] = $bomData;
|
||||
|
||||
return Product::create($payload);
|
||||
}
|
||||
@@ -564,6 +714,11 @@ private function updateProduct(int $id, array $data): Product
|
||||
// 동적 필드를 options에 병합
|
||||
$this->processDynamicOptions($data, 'products');
|
||||
|
||||
// BOM 데이터 처리 (bom 키가 있을 때만)
|
||||
if (array_key_exists('bom', $data)) {
|
||||
$data['bom'] = $this->extractBomData($data['bom']);
|
||||
}
|
||||
|
||||
// item_type은 DB 필드가 아니므로 제거
|
||||
unset($data['item_type']);
|
||||
$data['updated_by'] = $userId;
|
||||
|
||||
Reference in New Issue
Block a user