diff --git a/src/components/production/WorkOrders/documents/SlatWorkLogContent.tsx b/src/components/production/WorkOrders/documents/SlatWorkLogContent.tsx index 1da4edff..8a6e61ec 100644 --- a/src/components/production/WorkOrders/documents/SlatWorkLogContent.tsx +++ b/src/components/production/WorkOrders/documents/SlatWorkLogContent.tsx @@ -50,6 +50,9 @@ export function SlatWorkLogContent({ data: order, materialLots = [] }: SlatWorkL // 숫자 천단위 콤마 포맷 const fmt = (v?: number) => v != null ? formatNumber(v) : '-'; + // 소수점: 정수면 소수점 없이, 소수면 소수점 1자리까지 + const fmtDec = (v: number) => Number.isInteger(v) ? String(v) : v.toFixed(1); + // floorCode에서 부호 추출: "1층/FSS-01" → "FSS-01" const getSymbolCode = (floorCode?: string) => { if (!floorCode || floorCode === '-') return '-'; @@ -176,7 +179,7 @@ export function SlatWorkLogContent({ data: order, materialLots = [] }: SlatWorkL 제작사이즈(mm) - 미미제외 조인트바
수량 코일
사용량 - 설치홈/
부호 + 층/
부호 가로 @@ -201,7 +204,7 @@ export function SlatWorkLogContent({ data: order, materialLots = [] }: SlatWorkL {fmt(item.height)} {slatCount > 0 ? fmt(slatCount) : '-'} {jointBar > 0 ? fmt(jointBar) : '-'} - {coilUsage > 0 ? coilUsage.toFixed(1) : '-'} + {coilUsage > 0 ? fmtDec(coilUsage) : '-'} {getSymbolCode(item.floorCode)} ); @@ -221,7 +224,7 @@ export function SlatWorkLogContent({ data: order, materialLots = [] }: SlatWorkL 생산량 합계 [m²] - {totalCoilUsage > 0 ? totalCoilUsage.toFixed(1) : ''} + {totalCoilUsage > 0 ? fmtDec(totalCoilUsage) : ''} 조인트바 합계 {totalJointBar > 0 ? fmt(totalJointBar) : ''} diff --git a/src/components/quotes/LocationDetailPanel.tsx b/src/components/quotes/LocationDetailPanel.tsx index 95423246..8589b573 100644 --- a/src/components/quotes/LocationDetailPanel.tsx +++ b/src/components/quotes/LocationDetailPanel.tsx @@ -337,6 +337,7 @@ export function LocationDetailPanel({ onUpdateLocation(location.id, { productCode: value, productName: product?.item_name || value, + itemCategory: product?.item_category, }); }} disabled={disabled} diff --git a/src/components/quotes/LocationListPanel.tsx b/src/components/quotes/LocationListPanel.tsx index ebb5a7ca..080601aa 100644 --- a/src/components/quotes/LocationListPanel.tsx +++ b/src/components/quotes/LocationListPanel.tsx @@ -152,6 +152,7 @@ export function LocationListPanel({ openHeight: parseFloat(formData.openHeight) || 0, productCode: formData.productCode, productName: product?.item_name || formData.productCode, + itemCategory: product?.item_category, quantity: parseInt(formData.quantity) || 1, guideRailType: formData.guideRailType, motorPower: formData.motorPower, diff --git a/src/components/quotes/QuoteRegistration.tsx b/src/components/quotes/QuoteRegistration.tsx index fce69942..e8ea1501 100644 --- a/src/components/quotes/QuoteRegistration.tsx +++ b/src/components/quotes/QuoteRegistration.tsx @@ -68,6 +68,7 @@ export interface LocationItem { openHeight: number; // 세로 (오픈사이즈 H) productCode: string; // 제품코드 productName: string; // 제품명 + itemCategory?: string; // 품목 카테고리 (스크린/철재) quantity: number; // 수량 guideRailType: string; // 가이드레일 설치 유형 motorPower: string; // 모터 전원 diff --git a/src/components/quotes/types.ts b/src/components/quotes/types.ts index 373de7b7..96506679 100644 --- a/src/components/quotes/types.ts +++ b/src/components/quotes/types.ts @@ -36,13 +36,28 @@ export const QUOTE_STATUS_COLORS: Record = { }; // ===== 제품 카테고리 ===== -export type ProductCategory = 'screen' | 'steel'; +export type ProductCategory = 'SCREEN' | 'STEEL'; -export const PRODUCT_CATEGORY_LABELS: Record = { +export const PRODUCT_CATEGORY_LABELS: Record = { + SCREEN: '스크린', + STEEL: '철재', screen: '스크린', steel: '철재', }; +/** item_category(한글) → product_category 변환 (DB는 대문자) */ +export function itemCategoryToProductCategory(itemCategory?: string): ProductCategory { + if (itemCategory === '철재') return 'STEEL'; + return 'SCREEN'; +} + +/** 개소 목록에서 견적 전체의 product_category 결정 */ +function deriveProductCategory(locations: LocationItem[]): ProductCategory { + const categories = new Set(locations.map(loc => itemCategoryToProductCategory(loc.itemCategory))); + if (categories.size === 1) return categories.values().next().value!; + return itemCategoryToProductCategory(locations[0]?.itemCategory); +} + // ===== 견적 품목 ===== export interface QuoteItem { id: string; @@ -104,6 +119,8 @@ export interface Quote { // 자동산출 입력값 타입 export interface CalculationInputItem { productCategory?: string; + itemCategory?: string; // 품목 카테고리 원본 (스크린/철재) - round-trip용 + productCode?: string; // 제품코드 productName?: string; openWidth?: string; openHeight?: string; @@ -246,7 +263,7 @@ export function transformApiToFrontend(apiData: QuoteApiData): Quote { // API 실제 필드명(manager, contact) 우선, 레거시 필드명(manager_name, manager_contact) 폴백 managerName: apiData.manager || apiData.manager_name || undefined, managerContact: apiData.contact || apiData.manager_contact || undefined, - productCategory: apiData.product_category, + productCategory: (apiData.product_category?.toUpperCase() || 'SCREEN') as ProductCategory, quantity: apiData.quantity || 0, unitSymbol: apiData.unit_symbol || undefined, supplyAmount: parseFloat(String(apiData.supply_amount)) || 0, @@ -669,6 +686,7 @@ export interface LocationItem { openHeight: number; // 세로 (오픈사이즈 H) productCode: string; // 제품코드 productName: string; // 제품명 + itemCategory?: string; // 품목 카테고리 (스크린/철재) quantity: number; // 수량 guideRailType: string; // 가이드레일 설치 유형 motorPower: string; // 모터 전원 @@ -734,7 +752,8 @@ export function transformV2ToApi( const calculationInputs: CalculationInputs & { bomResults?: BomCalculationResult[] } = { items: data.locations.map(loc => ({ - productCategory: 'screen', // TODO: 동적으로 결정 + productCategory: itemCategoryToProductCategory(loc.itemCategory), + itemCategory: loc.itemCategory, // round-trip용 productCode: loc.productCode, // BOM 재계산용 productName: loc.productName, openWidth: String(loc.openWidth), @@ -876,7 +895,7 @@ export function transformV2ToApi( contact: data.contact || null, completion_date: data.dueDate || null, remarks: data.remarks || null, - product_category: 'screen', // TODO: 동적으로 결정 + product_category: deriveProductCategory(data.locations), quantity: data.locations.reduce((sum, loc) => sum + loc.quantity, 0), unit_symbol: '개소', total_amount: grandTotal, @@ -948,6 +967,7 @@ export function transformApiToV2(apiData: QuoteApiData): QuoteFormDataV2 { openHeight: parseInt(ci.openHeight || '0', 10), productCode: (ci as { productCode?: string }).productCode || bomResult?.finished_goods?.code || '', productName: ci.productName || '', + itemCategory: ci.itemCategory, quantity: qty, guideRailType: ci.guideRailType || 'wall', motorPower: ci.motorPower || 'single', @@ -1129,7 +1149,7 @@ export function transformFormDataToApi(formData: QuoteFormData): Record sum + item.quantity, 0), unit_symbol: formData.unitSymbol || '개소', // 선택한 제품의 단위 또는 기본값 total_amount: grandTotal,