feat: [수주관리] convertToOrder 개소 파싱 로직 추가

- convertToOrder에서 calculation_inputs.items[] 파싱하여 floor_code/symbol_code 매핑
- resolveLocationMapping() 공통 메소드 추출 (note 파싱 1순위, formula_source 2순위)
- syncFromQuote와 동일한 2단계 파싱 로직으로 일관성 확보
- Exception → Throwable 변경 (동기화 실패 catch 범위 확대)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-06 19:40:14 +09:00
parent b6f1c817d8
commit 6dbcb5337d

View File

@@ -448,7 +448,7 @@ public function update(int $id, array $data): Quote
try {
$this->orderService->setContext($tenantId, $userId);
$this->orderService->syncFromQuote($quote, $quote->current_revision);
} catch (\Exception $e) {
} catch (\Throwable $e) {
// 수주 동기화 실패는 로그만 남기고 견적 수정은 성공 처리
Log::warning('Failed to sync order from quote', [
'quote_id' => $quote->id,
@@ -597,10 +597,14 @@ public function convertToOrder(int $id): Quote
$order->created_by = $userId;
$order->save();
// 수주 상세 품목 생성
// 수주 상세 품목 생성 (개소 매핑 포함)
$calculationInputs = $quote->calculation_inputs ?? [];
$productItems = $calculationInputs['items'] ?? [];
$serialIndex = 1;
foreach ($quote->items as $quoteItem) {
$orderItem = OrderItem::createFromQuoteItem($quoteItem, $order->id, $serialIndex);
$productMapping = $this->resolveLocationMapping($quoteItem, $productItems);
$orderItem = OrderItem::createFromQuoteItem($quoteItem, $order->id, $serialIndex, $productMapping);
$orderItem->created_by = $userId;
$orderItem->save();
$serialIndex++;
@@ -622,6 +626,45 @@ public function convertToOrder(int $id): Quote
});
}
/**
* 견적 품목에서 개소(층/부호) 매핑 정보 추출
*
* 1순위: note 필드 파싱 ("4F FSS-01" → floor_code:"4F", symbol_code:"FSS-01")
* 2순위: formula_source → calculation_inputs.items[] 매칭
*/
private function resolveLocationMapping(QuoteItem $quoteItem, array $productItems): array
{
$floorCode = null;
$symbolCode = null;
// 1순위: note에서 파싱
$note = trim($quoteItem->note ?? '');
if ($note !== '') {
$parts = preg_split('/\s+/', $note, 2);
$floorCode = $parts[0] ?? null;
$symbolCode = $parts[1] ?? null;
}
// 2순위: formula_source → calculation_inputs
if (empty($floorCode) && empty($symbolCode)) {
$productIndex = 0;
$formulaSource = $quoteItem->formula_source ?? '';
if (preg_match('/product_(\d+)/', $formulaSource, $matches)) {
$productIndex = (int) $matches[1];
}
if (isset($productItems[$productIndex])) {
$floorCode = $productItems[$productIndex]['floor'] ?? null;
$symbolCode = $productItems[$productIndex]['code'] ?? null;
} elseif (count($productItems) === 1) {
$floorCode = $productItems[0]['floor'] ?? null;
$symbolCode = $productItems[0]['code'] ?? null;
}
}
return ['floor_code' => $floorCode, 'symbol_code' => $symbolCode];
}
/**
* 수주번호 생성
* 형식: ORD-YYMMDD-NNN (예: ORD-260105-001)