feat: 견적 BOM 자재 조회 기능 개선

- FormulaEvaluatorService: 품목마스터에서 규격/단위 조회 (getItemSpecAndUnit)
- QuoteService: 저장된 calculation_inputs로 BOM 자재 계산 (calculateBomMaterials)
- 견적 조회 시 bom_materials 데이터 자동 포함

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-06 13:28:10 +09:00
parent 1410cf725a
commit 31c6eced27
2 changed files with 130 additions and 11 deletions

View File

@@ -3,6 +3,7 @@
namespace App\Services\Quote;
use App\Models\CategoryGroup;
use App\Models\Items\Item;
use App\Models\Products\Price;
use App\Models\Quote\QuoteFormula;
use App\Services\Service;
@@ -479,11 +480,16 @@ public function executeAll(Collection $formulasByCategory, array $inputVariables
? $this->evaluate($item->unit_price_formula)
: $this->getItemPrice($item->item_code);
// 품목마스터에서 규격/단위 조회 (우선순위: 품목마스터 > 수식품목)
$itemMasterData = $this->getItemSpecAndUnit($item->item_code);
$specification = $itemMasterData['specification'] ?? $item->specification;
$unit = $itemMasterData['unit'] ?? $item->unit ?? 'EA';
$items[] = [
'item_code' => $item->item_code,
'item_name' => $item->item_name,
'specification' => $item->specification,
'unit' => $item->unit,
'specification' => $specification,
'unit' => $unit,
'quantity' => $quantity,
'unit_price' => $unitPrice,
'total_price' => $quantity * $unitPrice,
@@ -729,6 +735,8 @@ public function calculateBomWithDebug(
'item_code' => $bomItem['item_code'],
'item_name' => $bomItem['item_name'],
'item_category' => $itemCategory,
'specification' => $bomItem['specification'] ?? null,
'unit' => $bomItem['unit'] ?? 'EA',
'quantity' => $displayQuantity,
'quantity_formula' => $quantityFormula,
'base_price' => $basePrice,
@@ -1071,6 +1079,41 @@ private function getItemCategory(string $itemCode, int $tenantId): string
return $category ?? '기타';
}
/**
* 품목마스터에서 규격(specification)과 단위(unit) 조회
*
* @param string $itemCode 품목 코드
* @return array ['specification' => string|null, 'unit' => string]
*/
private function getItemSpecAndUnit(string $itemCode): array
{
$tenantId = $this->tenantId();
if (! $tenantId) {
return ['specification' => null, 'unit' => 'EA'];
}
// Items 테이블에서 기본 정보 조회
$item = Item::where('tenant_id', $tenantId)
->where('code', $itemCode)
->whereNull('deleted_at')
->with('details')
->first();
if (! $item) {
return ['specification' => null, 'unit' => 'EA'];
}
// specification은 ItemDetail에서, unit은 Item에서 가져옴
$specification = $item->details?->specification ?? null;
$unit = $item->unit ?? 'EA';
return [
'specification' => $specification,
'unit' => $unit,
];
}
/**
* BOM 전개 (수량 수식 포함)
*/
@@ -1102,18 +1145,22 @@ private function expandBomWithFormulas(array $finishedGoods, array $variables, i
$quantityFormula = $bomEntry['quantityFormula'] ?? $bomEntry['quantity'] ?? '1';
if ($childItemId) {
// ID 기반 조회
// ID 기반 조회 (item_details 조인하여 specification 포함)
$childItem = DB::table('items')
->where('tenant_id', $tenantId)
->where('id', $childItemId)
->whereNull('deleted_at')
->leftJoin('item_details', 'items.id', '=', 'item_details.item_id')
->where('items.tenant_id', $tenantId)
->where('items.id', $childItemId)
->whereNull('items.deleted_at')
->select('items.*', 'item_details.specification')
->first();
} elseif ($childItemCode) {
// 코드 기반 조회
// 코드 기반 조회 (item_details 조인하여 specification 포함)
$childItem = DB::table('items')
->where('tenant_id', $tenantId)
->where('code', $childItemCode)
->whereNull('deleted_at')
->leftJoin('item_details', 'items.id', '=', 'item_details.item_id')
->where('items.tenant_id', $tenantId)
->where('items.code', $childItemCode)
->whereNull('items.deleted_at')
->select('items.*', 'item_details.specification')
->first();
} else {
continue;
@@ -1127,6 +1174,7 @@ private function expandBomWithFormulas(array $finishedGoods, array $variables, i
'process_type' => $childItem->process_type,
'quantity_formula' => (string) $quantityFormula,
'unit' => $childItem->unit,
'specification' => $childItem->specification,
];
// 재귀적 BOM 전개 (반제품인 경우)