feat(WEB): 수주등록 품목을 제품 모델+타입별 그룹핑 표시
- 견적의 calculation_inputs에서 productCode 정보를 수주등록으로 전달 - quote_items.note 파싱으로 floor/code 추출 (type_code/symbol 컬럼 부재 대응) - 제품코드(FG-KWE01-벽면형-SUS)에서 그룹핑 키(KWE01-SUS) 추출 - 그룹 내 동일 품목(item_code 기준) 수량/금액 합산하여 1행으로 표시 - quotes/actions.ts BomCalculationResult 타입을 types.ts와 일치시켜 TS 에러 해결
This commit is contained in:
@@ -120,6 +120,18 @@ interface ApiQuoteForSelect {
|
||||
grade?: string; // 등급 (A, B, C)
|
||||
} | null;
|
||||
items?: ApiQuoteItem[];
|
||||
calculation_inputs?: {
|
||||
items?: Array<{
|
||||
productCategory?: string;
|
||||
productCode?: string;
|
||||
productName?: string;
|
||||
openWidth?: string;
|
||||
openHeight?: string;
|
||||
quantity?: number;
|
||||
floor?: string;
|
||||
code?: string;
|
||||
}>;
|
||||
} | null;
|
||||
}
|
||||
|
||||
interface ApiQuoteItem {
|
||||
@@ -128,6 +140,7 @@ interface ApiQuoteItem {
|
||||
item_name: string;
|
||||
type_code?: string;
|
||||
symbol?: string;
|
||||
note?: string; // "5F FSS-01" 형태 (floor + code)
|
||||
specification?: string;
|
||||
// QuoteItem 모델 필드명 (calculated_quantity, total_price)
|
||||
calculated_quantity?: number;
|
||||
@@ -402,6 +415,18 @@ export interface QuotationForSelect {
|
||||
manager?: string; // 담당자
|
||||
contact?: string; // 연락처
|
||||
items?: QuotationItem[]; // 품목 내역
|
||||
calculationInputs?: {
|
||||
items?: Array<{
|
||||
productCategory?: string;
|
||||
productCode?: string;
|
||||
productName?: string;
|
||||
openWidth?: string;
|
||||
openHeight?: string;
|
||||
quantity?: number;
|
||||
floor?: string;
|
||||
code?: string;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface QuotationItem {
|
||||
@@ -643,6 +668,18 @@ function transformQuoteForSelect(apiData: ApiQuoteForSelect): QuotationForSelect
|
||||
manager: apiData.manager ?? undefined,
|
||||
contact: apiData.contact ?? apiData.client?.phone ?? undefined,
|
||||
items: apiData.items?.map(transformQuoteItemForSelect),
|
||||
calculationInputs: apiData.calculation_inputs ? {
|
||||
items: apiData.calculation_inputs.items?.map(item => ({
|
||||
productCategory: item.productCategory,
|
||||
productCode: item.productCode,
|
||||
productName: item.productName,
|
||||
openWidth: item.openWidth,
|
||||
openHeight: item.openHeight,
|
||||
quantity: item.quantity,
|
||||
floor: item.floor,
|
||||
code: item.code,
|
||||
})),
|
||||
} : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -655,12 +692,23 @@ function transformQuoteItemForSelect(apiItem: ApiQuoteItem): QuotationItem {
|
||||
// amount fallback: total_price → total_amount → 수량 * 단가 계산
|
||||
const amount = Number(apiItem.total_price ?? apiItem.total_amount ?? 0) || (quantity * unitPrice);
|
||||
|
||||
// note에서 floor+code 추출: "5F FSS-01" → type="5F", symbol="FSS-01"
|
||||
let typeFromNote = apiItem.type_code || '';
|
||||
let symbolFromNote = apiItem.symbol || '';
|
||||
if (!typeFromNote && !symbolFromNote && apiItem.note) {
|
||||
const noteParts = apiItem.note.trim().split(/\s+/);
|
||||
if (noteParts.length >= 2) {
|
||||
typeFromNote = noteParts[0];
|
||||
symbolFromNote = noteParts.slice(1).join(' ');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: String(apiItem.id),
|
||||
itemCode: apiItem.item_code || '',
|
||||
itemName: apiItem.item_name,
|
||||
type: apiItem.type_code || '',
|
||||
symbol: apiItem.symbol || '',
|
||||
type: typeFromNote,
|
||||
symbol: symbolFromNote,
|
||||
spec: apiItem.specification || '',
|
||||
quantity,
|
||||
unit: apiItem.unit || 'EA',
|
||||
|
||||
Reference in New Issue
Block a user