feat: 견적 산출 서비스 prices 테이블 연동
- Price 모델에 getCurrentPrice(), getSalesPriceByItemCode() 메서드 추가 - Price 모델에 STATUS_*, ITEM_TYPE_* 상수 추가 - QuoteCalculationService에 setTenantId(), getUnitPrice() 메서드 추가 - 스크린 품목 단가: 원단, 케이스, 브라켓, 인건비 prices 조회로 변경 - 철재 품목 단가: 철판, 용접, 표면처리, 가공비 prices 조회로 변경 - 모터 용량별 단가: 50W~300W prices 조회로 변경 - 모든 단가는 prices 조회 실패 시 기존 하드코딩 값을 fallback으로 사용
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Services\Quote;
|
||||
|
||||
use App\Models\Products\Price;
|
||||
use App\Models\Quote\Quote;
|
||||
use App\Services\Service;
|
||||
|
||||
@@ -13,10 +14,39 @@
|
||||
*/
|
||||
class QuoteCalculationService extends Service
|
||||
{
|
||||
private ?int $tenantId = null;
|
||||
|
||||
public function __construct(
|
||||
private FormulaEvaluatorService $formulaEvaluator
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 테넌트 ID 설정
|
||||
*/
|
||||
public function setTenantId(int $tenantId): self
|
||||
{
|
||||
$this->tenantId = $tenantId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 품목 코드로 단가 조회 (prices 테이블 연동)
|
||||
*
|
||||
* @param string $itemCode 품목 코드
|
||||
* @param float $fallback 조회 실패 시 기본값
|
||||
*/
|
||||
private function getUnitPrice(string $itemCode, float $fallback = 0): float
|
||||
{
|
||||
if (! $this->tenantId) {
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
$price = Price::getSalesPriceByItemCode($this->tenantId, $itemCode);
|
||||
|
||||
return $price > 0 ? $price : $fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 견적 자동산출 실행
|
||||
*
|
||||
@@ -209,6 +239,7 @@ private function generateScreenItems(array $inputs, array $outputs, int $qty): a
|
||||
$items = [];
|
||||
|
||||
// 1. 스크린 원단
|
||||
$fabricPrice = $this->getUnitPrice('SCR-FABRIC-001', 25000);
|
||||
$items[] = [
|
||||
'item_code' => 'SCR-FABRIC-001',
|
||||
'item_name' => '스크린 원단',
|
||||
@@ -216,13 +247,14 @@ private function generateScreenItems(array $inputs, array $outputs, int $qty): a
|
||||
'unit' => 'm²',
|
||||
'base_quantity' => 1,
|
||||
'calculated_quantity' => $outputs['AREA'] * $qty,
|
||||
'unit_price' => 25000,
|
||||
'total_price' => $outputs['AREA'] * $qty * 25000,
|
||||
'unit_price' => $fabricPrice,
|
||||
'total_price' => $outputs['AREA'] * $qty * $fabricPrice,
|
||||
'formula' => 'AREA * QTY',
|
||||
'formula_category' => 'material',
|
||||
];
|
||||
|
||||
// 2. 케이스
|
||||
$casePrice = $this->getUnitPrice('SCR-CASE-001', 85000);
|
||||
$items[] = [
|
||||
'item_code' => 'SCR-CASE-001',
|
||||
'item_name' => '알루미늄 케이스',
|
||||
@@ -230,8 +262,8 @@ private function generateScreenItems(array $inputs, array $outputs, int $qty): a
|
||||
'unit' => 'EA',
|
||||
'base_quantity' => 1,
|
||||
'calculated_quantity' => $qty,
|
||||
'unit_price' => 85000,
|
||||
'total_price' => $qty * 85000,
|
||||
'unit_price' => $casePrice,
|
||||
'total_price' => $qty * $casePrice,
|
||||
'formula' => 'QTY',
|
||||
'formula_category' => 'material',
|
||||
];
|
||||
@@ -252,6 +284,7 @@ private function generateScreenItems(array $inputs, array $outputs, int $qty): a
|
||||
];
|
||||
|
||||
// 4. 브라켓
|
||||
$bracketPrice = $this->getUnitPrice('SCR-BRACKET-001', 15000);
|
||||
$items[] = [
|
||||
'item_code' => 'SCR-BRACKET-001',
|
||||
'item_name' => '설치 브라켓',
|
||||
@@ -259,8 +292,8 @@ private function generateScreenItems(array $inputs, array $outputs, int $qty): a
|
||||
'unit' => 'SET',
|
||||
'base_quantity' => 2,
|
||||
'calculated_quantity' => 2 * $qty,
|
||||
'unit_price' => 15000,
|
||||
'total_price' => 2 * $qty * 15000,
|
||||
'unit_price' => $bracketPrice,
|
||||
'total_price' => 2 * $qty * $bracketPrice,
|
||||
'formula' => '2 * QTY',
|
||||
'formula_category' => 'material',
|
||||
];
|
||||
@@ -272,6 +305,7 @@ private function generateScreenItems(array $inputs, array $outputs, int $qty): a
|
||||
['min' => 10, 'max' => null, 'result' => 4],
|
||||
], 2);
|
||||
|
||||
$laborPrice = $this->getUnitPrice('LAB-INSTALL-001', 50000);
|
||||
$items[] = [
|
||||
'item_code' => 'LAB-INSTALL-001',
|
||||
'item_name' => '설치 인건비',
|
||||
@@ -279,8 +313,8 @@ private function generateScreenItems(array $inputs, array $outputs, int $qty): a
|
||||
'unit' => 'HR',
|
||||
'base_quantity' => $laborHours,
|
||||
'calculated_quantity' => $laborHours * $qty,
|
||||
'unit_price' => 50000,
|
||||
'total_price' => $laborHours * $qty * 50000,
|
||||
'unit_price' => $laborPrice,
|
||||
'total_price' => $laborHours * $qty * $laborPrice,
|
||||
'formula' => 'LABOR_HOURS * QTY',
|
||||
'formula_category' => 'labor',
|
||||
];
|
||||
@@ -295,16 +329,18 @@ private function generateSteelItems(array $inputs, array $outputs, int $qty): ar
|
||||
{
|
||||
$items = [];
|
||||
|
||||
// 재질별 단가
|
||||
$materialPrice = $this->formulaEvaluator->evaluateMapping($inputs['MATERIAL'], [
|
||||
// 재질별 품목코드 및 단가 조회
|
||||
$materialCode = 'STL-PLATE-'.strtoupper($inputs['MATERIAL']);
|
||||
$fallbackMaterialPrice = $this->formulaEvaluator->evaluateMapping($inputs['MATERIAL'], [
|
||||
['source' => 'ss304', 'result' => 4500],
|
||||
['source' => 'ss316', 'result' => 6500],
|
||||
['source' => 'galvanized', 'result' => 3000],
|
||||
], 4500);
|
||||
$materialPrice = $this->getUnitPrice($materialCode, $fallbackMaterialPrice);
|
||||
|
||||
// 1. 철판
|
||||
$items[] = [
|
||||
'item_code' => 'STL-PLATE-001',
|
||||
'item_code' => $materialCode,
|
||||
'item_name' => '철판 ('.$inputs['MATERIAL'].')',
|
||||
'specification' => sprintf('%.0f x %.0f x %.1f mm', $outputs['W1'], $outputs['H1'], $inputs['THICKNESS']),
|
||||
'unit' => 'kg',
|
||||
@@ -318,6 +354,7 @@ private function generateSteelItems(array $inputs, array $outputs, int $qty): ar
|
||||
|
||||
// 2. 용접
|
||||
$weldLength = ($outputs['W1'] + $outputs['H1']) * 2 / 1000; // m
|
||||
$weldPrice = $this->getUnitPrice('STL-WELD-001', 15000);
|
||||
$items[] = [
|
||||
'item_code' => 'STL-WELD-001',
|
||||
'item_name' => '용접 ('.$inputs['WELDING'].')',
|
||||
@@ -325,21 +362,23 @@ private function generateSteelItems(array $inputs, array $outputs, int $qty): ar
|
||||
'unit' => 'm',
|
||||
'base_quantity' => $weldLength,
|
||||
'calculated_quantity' => $weldLength * $qty,
|
||||
'unit_price' => 15000,
|
||||
'total_price' => $weldLength * $qty * 15000,
|
||||
'unit_price' => $weldPrice,
|
||||
'total_price' => $weldLength * $qty * $weldPrice,
|
||||
'formula' => 'WELD_LENGTH * QTY',
|
||||
'formula_category' => 'labor',
|
||||
];
|
||||
|
||||
// 3. 표면처리
|
||||
$finishPrice = $this->formulaEvaluator->evaluateMapping($inputs['FINISH'], [
|
||||
$finishCode = 'STL-FINISH-'.strtoupper($inputs['FINISH']);
|
||||
$fallbackFinishPrice = $this->formulaEvaluator->evaluateMapping($inputs['FINISH'], [
|
||||
['source' => 'hairline', 'result' => 8000],
|
||||
['source' => 'mirror', 'result' => 15000],
|
||||
['source' => 'matte', 'result' => 5000],
|
||||
], 8000);
|
||||
$finishPrice = $this->getUnitPrice($finishCode, $fallbackFinishPrice);
|
||||
|
||||
$items[] = [
|
||||
'item_code' => 'STL-FINISH-001',
|
||||
'item_code' => $finishCode,
|
||||
'item_name' => '표면처리 ('.$inputs['FINISH'].')',
|
||||
'specification' => sprintf('%.2f m²', $outputs['AREA'] * $qty),
|
||||
'unit' => 'm²',
|
||||
@@ -352,6 +391,7 @@ private function generateSteelItems(array $inputs, array $outputs, int $qty): ar
|
||||
];
|
||||
|
||||
// 4. 가공비
|
||||
$processPrice = $this->getUnitPrice('STL-PROCESS-001', 50000);
|
||||
$items[] = [
|
||||
'item_code' => 'STL-PROCESS-001',
|
||||
'item_name' => '가공비',
|
||||
@@ -359,8 +399,8 @@ private function generateSteelItems(array $inputs, array $outputs, int $qty): ar
|
||||
'unit' => 'EA',
|
||||
'base_quantity' => 1,
|
||||
'calculated_quantity' => $qty,
|
||||
'unit_price' => 50000,
|
||||
'total_price' => $qty * 50000,
|
||||
'unit_price' => $processPrice,
|
||||
'total_price' => $qty * $processPrice,
|
||||
'formula' => 'QTY',
|
||||
'formula_category' => 'labor',
|
||||
];
|
||||
@@ -400,17 +440,21 @@ private function calculateCosts(array $items): array
|
||||
}
|
||||
|
||||
/**
|
||||
* 모터 단가 조회
|
||||
* 모터 단가 조회 (prices 테이블 연동)
|
||||
*/
|
||||
private function getMotorPrice(string $capacity): int
|
||||
private function getMotorPrice(string $capacity): float
|
||||
{
|
||||
return match ($capacity) {
|
||||
// 용량별 품목코드 및 기본 단가
|
||||
$motorCode = 'SCR-MOTOR-'.$capacity;
|
||||
$fallbackPrice = match ($capacity) {
|
||||
'50W' => 120000,
|
||||
'100W' => 150000,
|
||||
'200W' => 200000,
|
||||
'300W' => 280000,
|
||||
default => 150000,
|
||||
};
|
||||
|
||||
return $this->getUnitPrice($motorCode, $fallbackPrice);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user