- PrefixResolver: 제품코드×마감재질→LOT prefix 결정 + BD-XX-NN 코드 생성 - DynamicBomEntry DTO: dynamic_bom JSON 항목 타입 안전 관리 - BendingInfoBuilder 확장: build() 리턴 변경 + buildDynamicBomForItem() 추가 - OrderService: 작업지시 생성 시 per-item dynamic_bom 자동 저장 - WorkOrderService.getMaterials(): dynamic_bom 우선 체크 + N+1 배치 최적화 - WorkOrderService.registerMaterialInput(): work_order_item_id 분기 라우팅 통일 - 단위 테스트 58개 + 통합 테스트 6개 (64 tests / 293 assertions) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
102 lines
3.1 KiB
PHP
102 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace App\DTOs\Production;
|
|
|
|
use InvalidArgumentException;
|
|
|
|
/**
|
|
* dynamic_bom JSON 항목 DTO
|
|
*
|
|
* work_order_items.options.dynamic_bom 배열의 각 엔트리를 표현
|
|
*/
|
|
class DynamicBomEntry
|
|
{
|
|
public function __construct(
|
|
public readonly int $child_item_id,
|
|
public readonly string $child_item_code,
|
|
public readonly string $lot_prefix,
|
|
public readonly string $part_type,
|
|
public readonly string $category,
|
|
public readonly string $material_type,
|
|
public readonly int $length_mm,
|
|
public readonly int|float $qty,
|
|
) {}
|
|
|
|
/**
|
|
* 배열에서 DTO 생성
|
|
*/
|
|
public static function fromArray(array $data): self
|
|
{
|
|
self::validate($data);
|
|
|
|
return new self(
|
|
child_item_id: (int) $data['child_item_id'],
|
|
child_item_code: (string) $data['child_item_code'],
|
|
lot_prefix: (string) $data['lot_prefix'],
|
|
part_type: (string) $data['part_type'],
|
|
category: (string) $data['category'],
|
|
material_type: (string) $data['material_type'],
|
|
length_mm: (int) $data['length_mm'],
|
|
qty: $data['qty'],
|
|
);
|
|
}
|
|
|
|
/**
|
|
* DTO → 배열 변환 (JSON 저장용)
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
return [
|
|
'child_item_id' => $this->child_item_id,
|
|
'child_item_code' => $this->child_item_code,
|
|
'lot_prefix' => $this->lot_prefix,
|
|
'part_type' => $this->part_type,
|
|
'category' => $this->category,
|
|
'material_type' => $this->material_type,
|
|
'length_mm' => $this->length_mm,
|
|
'qty' => $this->qty,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 필수 필드 검증
|
|
*
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
public static function validate(array $data): bool
|
|
{
|
|
$required = ['child_item_id', 'child_item_code', 'lot_prefix', 'part_type', 'category', 'material_type', 'length_mm', 'qty'];
|
|
|
|
foreach ($required as $field) {
|
|
if (! array_key_exists($field, $data) || $data[$field] === null) {
|
|
throw new InvalidArgumentException("DynamicBomEntry: '{$field}' is required");
|
|
}
|
|
}
|
|
|
|
if ((int) $data['child_item_id'] <= 0) {
|
|
throw new InvalidArgumentException('DynamicBomEntry: child_item_id must be positive');
|
|
}
|
|
|
|
$validCategories = ['guideRail', 'bottomBar', 'shutterBox', 'smokeBarrier'];
|
|
if (! in_array($data['category'], $validCategories, true)) {
|
|
throw new InvalidArgumentException('DynamicBomEntry: category must be one of: '.implode(', ', $validCategories));
|
|
}
|
|
|
|
if ($data['qty'] <= 0) {
|
|
throw new InvalidArgumentException('DynamicBomEntry: qty must be positive');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* DynamicBomEntry 배열 → JSON 저장용 배열 변환
|
|
*
|
|
* @param DynamicBomEntry[] $entries
|
|
*/
|
|
public static function toArrayList(array $entries): array
|
|
{
|
|
return array_map(fn (self $e) => $e->toArray(), $entries);
|
|
}
|
|
}
|