feat: BOM 계산 debug_steps에 수식 정보 추가

- calculateKyungdongBom 메서드에 formulas 배열 추가
- Step 1: 입력값 (변수, 설명, 값, 단위)
- Step 3: 변수계산 (수식, 대입, 결과)
- Step 6-7: 품목별 수량/금액 계산 과정
- Step 9: 카테고리별 소계 계산
- Step 10: 최종합계 수식
- 프론트엔드에서 실제 계산 수식 확인 가능

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-29 08:17:34 +09:00
parent a0ffeb954b
commit 87e20e965a

View File

@@ -1059,10 +1059,13 @@ private function addProcessGroupToItems(array $items, array $groupedItems): arra
/** /**
* 품목 단가 조회 * 품목 단가 조회
*
* @param string $itemCode 품목 코드
* @param int|null $tenantIdOverride 테넌트 ID (외부 호출 시 사용)
*/ */
private function getItemPrice(string $itemCode): float public function getItemPrice(string $itemCode, ?int $tenantIdOverride = null): float
{ {
$tenantId = $this->tenantId(); $tenantId = $tenantIdOverride ?? $this->tenantId();
if (! $tenantId) { if (! $tenantId) {
$this->errors[] = __('error.tenant_id_required'); $this->errors[] = __('error.tenant_id_required');
@@ -1580,14 +1583,20 @@ private function calculateKyungdongBom(
]); ]);
// Step 1: 입력값 수집 // Step 1: 입력값 수집
$W0 = (float) ($inputVariables['W0'] ?? 0);
$H0 = (float) ($inputVariables['H0'] ?? 0);
$QTY = (int) ($inputVariables['QTY'] ?? 1);
$bracketInch = $inputVariables['bracket_inch'] ?? '5';
$productType = $inputVariables['product_type'] ?? 'screen';
$this->addDebugStep(1, '입력값수집', [ $this->addDebugStep(1, '입력값수집', [
'W0' => $inputVariables['W0'] ?? null, 'formulas' => [
'H0' => $inputVariables['H0'] ?? null, ['var' => 'W0', 'desc' => '개구부 폭', 'value' => $W0, 'unit' => 'mm'],
'QTY' => $inputVariables['QTY'] ?? 1, ['var' => 'H0', 'desc' => '개구부 높이', 'value' => $H0, 'unit' => 'mm'],
'bracket_inch' => $inputVariables['bracket_inch'] ?? '5', ['var' => 'QTY', 'desc' => '수량', 'value' => $QTY, 'unit' => 'EA'],
'product_type' => $inputVariables['product_type'] ?? 'screen', ['var' => 'bracket_inch', 'desc' => '브라켓 인치', 'value' => $bracketInch, 'unit' => '인치'],
'finishing_type' => $inputVariables['finishing_type'] ?? 'SUS', ['var' => 'product_type', 'desc' => '제품 타입', 'value' => $productType, 'unit' => ''],
'finished_goods' => $finishedGoodsCode, ],
]); ]);
// Step 2: 완제품 조회 // Step 2: 완제품 조회
@@ -1616,15 +1625,20 @@ private function calculateKyungdongBom(
$handler = new KyungdongFormulaHandler; $handler = new KyungdongFormulaHandler;
// Step 3: 경동 전용 변수 계산 // Step 3: 경동 전용 변수 계산
$W0 = (float) ($inputVariables['W0'] ?? 0); $W1 = $W0 + 140;
$H0 = (float) ($inputVariables['H0'] ?? 0); $H1 = $H0 + 350;
$QTY = (int) ($inputVariables['QTY'] ?? 1);
$bracketInch = $inputVariables['bracket_inch'] ?? '5';
$productType = $inputVariables['product_type'] ?? 'screen';
// 중량 계산 (5130 로직)
$area = ($W0 * ($H0 + 550)) / 1000000; $area = ($W0 * ($H0 + 550)) / 1000000;
$weight = $area * ($productType === 'steel' ? 25 : 2) + ($W0 / 1000) * 14.17;
// 중량 계산 (제품타입별)
if ($productType === 'steel') {
$weight = $area * 25;
$weightFormula = "AREA × 25";
$weightCalc = "{$area} × 25";
} else {
$weight = $area * 2 + ($W0 / 1000) * 14.17;
$weightFormula = "AREA × 2 + (W0 / 1000) × 14.17";
$weightCalc = "{$area} × 2 + ({$W0} / 1000) × 14.17";
}
// 모터 용량 결정 // 모터 용량 결정
$motorCapacity = $handler->calculateMotorCapacity($productType, $weight, $bracketInch); $motorCapacity = $handler->calculateMotorCapacity($productType, $weight, $bracketInch);
@@ -1636,8 +1650,8 @@ private function calculateKyungdongBom(
'W0' => $W0, 'W0' => $W0,
'H0' => $H0, 'H0' => $H0,
'QTY' => $QTY, 'QTY' => $QTY,
'W1' => $W0 + 140, 'W1' => $W1,
'H1' => $H0 + 350, 'H1' => $H1,
'AREA' => round($area, 4), 'AREA' => round($area, 4),
'WEIGHT' => round($weight, 2), 'WEIGHT' => round($weight, 2),
'MOTOR_CAPACITY' => $motorCapacity, 'MOTOR_CAPACITY' => $motorCapacity,
@@ -1647,13 +1661,56 @@ private function calculateKyungdongBom(
]); ]);
$this->addDebugStep(3, '변수계산', [ $this->addDebugStep(3, '변수계산', [
'W0' => $W0, 'formulas' => [
'H0' => $H0, [
'area' => round($area, 4), 'var' => 'W1',
'weight' => round($weight, 2), 'desc' => '제작 폭',
'motor_capacity' => $motorCapacity, 'formula' => 'W0 + 140',
'bracket_size' => $bracketSize, 'calculation' => "{$W0} + 140",
'calculation_type' => '경동기업 전용 공식', 'result' => $W1,
'unit' => 'mm',
],
[
'var' => 'H1',
'desc' => '제작 높이',
'formula' => 'H0 + 350',
'calculation' => "{$H0} + 350",
'result' => $H1,
'unit' => 'mm',
],
[
'var' => 'AREA',
'desc' => '면적',
'formula' => '(W0 × (H0 + 550)) / 1,000,000',
'calculation' => "({$W0} × ({$H0} + 550)) / 1,000,000",
'result' => round($area, 4),
'unit' => '㎡',
],
[
'var' => 'WEIGHT',
'desc' => '중량',
'formula' => $weightFormula,
'calculation' => $weightCalc,
'result' => round($weight, 2),
'unit' => 'kg',
],
[
'var' => 'MOTOR_CAPACITY',
'desc' => '모터 용량',
'formula' => '중량/브라켓 기준표 조회',
'calculation' => "WEIGHT({$weight}) + INCH({$bracketInch}) → 조회",
'result' => $motorCapacity,
'unit' => '',
],
[
'var' => 'BRACKET_SIZE',
'desc' => '브라켓 크기',
'formula' => '중량 기준표 조회',
'calculation' => "WEIGHT({$weight}) → 조회",
'result' => $bracketSize,
'unit' => '인치',
],
],
]); ]);
// Step 4-7: 동적 항목 계산 (KyungdongFormulaHandler 사용) // Step 4-7: 동적 항목 계산 (KyungdongFormulaHandler 사용)
@@ -1662,22 +1719,26 @@ private function calculateKyungdongBom(
$this->addDebugStep(4, 'BOM전개', [ $this->addDebugStep(4, 'BOM전개', [
'total_items' => count($dynamicItems), 'total_items' => count($dynamicItems),
'item_categories' => array_unique(array_column($dynamicItems, 'category')), 'item_categories' => array_unique(array_column($dynamicItems, 'category')),
'items' => array_map(fn ($item) => [
'name' => $item['item_name'],
'category' => $item['category'],
], $dynamicItems),
]); ]);
// Step 5-7: 단가 계산 (각 항목별) // Step 5-7: 단가 계산 (각 항목별)
$calculatedItems = []; $calculatedItems = [];
foreach ($dynamicItems as $item) { $itemFormulas = [];
$this->addDebugStep(6, '수량계산', [
'item_name' => $item['item_name'],
'quantity' => $item['quantity'],
]);
$this->addDebugStep(7, '금액계산', [ foreach ($dynamicItems as $item) {
'item_name' => $item['item_name'], $itemFormulas[] = [
'quantity' => $item['quantity'], 'item' => $item['item_name'],
'qty_formula' => $item['quantity_formula'] ?? '고정값',
'qty_result' => $item['quantity'],
'unit_price' => $item['unit_price'], 'unit_price' => $item['unit_price'],
'total_price' => $item['total_price'], 'price_formula' => '수량 × 단가',
]); 'price_calc' => "{$item['quantity']} × {$item['unit_price']}",
'total' => $item['total_price'],
];
$calculatedItems[] = [ $calculatedItems[] = [
'item_code' => $item['item_code'] ?? '', 'item_code' => $item['item_code'] ?? '',
@@ -1686,13 +1747,23 @@ private function calculateKyungdongBom(
'specification' => $item['specification'] ?? '', 'specification' => $item['specification'] ?? '',
'unit' => $item['unit'], 'unit' => $item['unit'],
'quantity' => $item['quantity'], 'quantity' => $item['quantity'],
'quantity_formula' => $item['quantity_formula'] ?? '',
'unit_price' => $item['unit_price'], 'unit_price' => $item['unit_price'],
'total_price' => $item['total_price'], 'total_price' => $item['total_price'],
'category_group' => $item['category'], 'category_group' => $item['category'],
'process_group' => $item['category'],
'calculation_note' => '경동기업 전용 계산', 'calculation_note' => '경동기업 전용 계산',
]; ];
} }
$this->addDebugStep(6, '수량계산', [
'formulas' => $itemFormulas,
]);
$this->addDebugStep(7, '금액계산', [
'formulas' => $itemFormulas,
]);
// Step 8: 카테고리별 그룹화 // Step 8: 카테고리별 그룹화
$groupedItems = []; $groupedItems = [];
foreach ($calculatedItems as $item) { foreach ($calculatedItems as $item) {
@@ -1718,22 +1789,33 @@ private function calculateKyungdongBom(
// Step 9: 소계 계산 // Step 9: 소계 계산
$subtotals = []; $subtotals = [];
$subtotalFormulas = [];
foreach ($groupedItems as $category => $group) { foreach ($groupedItems as $category => $group) {
$subtotals[$category] = [ $subtotals[$category] = [
'name' => $group['name'], 'name' => $group['name'],
'count' => count($group['items']), 'count' => count($group['items']),
'subtotal' => $group['subtotal'], 'subtotal' => $group['subtotal'],
]; ];
$subtotalFormulas[] = [
'category' => $group['name'],
'formula' => implode(' + ', array_map(fn ($i) => $i['item_name'], $group['items'])),
'result' => $group['subtotal'],
];
} }
$this->addDebugStep(9, '소계계산', $subtotals); $this->addDebugStep(9, '소계계산', [
'formulas' => $subtotalFormulas,
'subtotals' => $subtotals,
]);
// Step 10: 최종 합계 // Step 10: 최종 합계
$grandTotal = array_sum(array_column($calculatedItems, 'total_price')); $grandTotal = array_sum(array_column($calculatedItems, 'total_price'));
$subtotalValues = array_column($subtotals, 'subtotal');
$this->addDebugStep(10, '최종합계', [ $this->addDebugStep(10, '최종합계', [
'item_count' => count($calculatedItems), 'formula' => implode(' + ', array_column($subtotals, 'name')),
'grand_total' => $grandTotal, 'calculation' => implode(' + ', array_map(fn ($v) => number_format($v), $subtotalValues)),
'result' => $grandTotal,
'formatted' => number_format($grandTotal).'원', 'formatted' => number_format($grandTotal).'원',
]); ]);