/** * 자재 카테고리별 구매 수량 계산 유틸리티 */ /** * 파이프 구매 수량 계산 * @param {number} totalLengthMm - 파이프 총 길이 (mm) * @param {number} quantity - BOM 수량 (개수) * @returns {object} 구매 계산 결과 */ export const calculatePipePurchase = (totalLengthMm, quantity) => { if (!totalLengthMm || totalLengthMm <= 0 || !quantity || quantity <= 0) { return { purchaseQuantity: 0, standardLength: 6000, cutLength: 0, calculation: '길이 정보 없음' }; } // 절단 여유분: 절단 개수만큼 3mm 추가 (백엔드와 동일) const cuttingLoss = quantity * 3; const requiredLength = totalLengthMm + cuttingLoss; // 6,000mm 단위로 올림 계산 const pipeCount = Math.ceil(requiredLength / 6000); return { purchaseQuantity: pipeCount, standardLength: 6000, cutLength: requiredLength, calculation: `${totalLengthMm}mm + ${cuttingLoss}mm(절단손실) = ${requiredLength}mm → ${pipeCount}본` }; }; /** * 볼트/너트 구매 수량 계산 * @param {number} bomQuantity - BOM 수량 * @returns {object} 구매 계산 결과 */ export const calculateBoltPurchase = (bomQuantity) => { if (!bomQuantity || bomQuantity <= 0) { return { purchaseQuantity: 0, calculation: '수량 정보 없음' }; } // +5% 여유분 후 4의 배수로 올림 const withMargin = bomQuantity * 1.05; const purchaseQuantity = Math.ceil(withMargin / 4) * 4; return { purchaseQuantity: purchaseQuantity, marginQuantity: Math.round(withMargin * 10) / 10, // 소수점 1자리 calculation: `${bomQuantity} × 1.05 = ${Math.round(withMargin * 10) / 10} → ${purchaseQuantity} SETS` }; }; /** * 가스켓 구매 수량 계산 * @param {number} bomQuantity - BOM 수량 * @returns {object} 구매 계산 결과 */ export const calculateGasketPurchase = (bomQuantity) => { if (!bomQuantity || bomQuantity <= 0) { return { purchaseQuantity: 0, calculation: '수량 정보 없음' }; } // 5의 배수로 올림 const purchaseQuantity = Math.ceil(bomQuantity / 5) * 5; return { purchaseQuantity: purchaseQuantity, calculation: `${bomQuantity} → ${purchaseQuantity} EA (5의 배수)` }; }; /** * 피팅/계기/밸브 구매 수량 계산 * @param {number} bomQuantity - BOM 수량 * @returns {object} 구매 계산 결과 */ export const calculateStandardPurchase = (bomQuantity) => { return { purchaseQuantity: bomQuantity || 0, calculation: `${bomQuantity || 0} EA (BOM 수량 그대로)` }; }; /** * 자재 카테고리별 구매 수량 계산 (통합 함수) * @param {object} material - 자재 정보 * @returns {object} 구매 계산 결과 */ export const calculatePurchaseQuantity = (material) => { const category = material.classified_category || material.category || ''; const bomQuantity = material.quantity || 0; switch (category.toUpperCase()) { case 'PIPE': // 파이프의 경우 길이 정보 필요 const lengthMm = material.pipe_details?.length_mm || 0; const totalLength = material.pipe_details?.total_length_mm || (lengthMm * bomQuantity); return { ...calculatePipePurchase(totalLength, bomQuantity), category: 'PIPE', unit: '본' }; case 'BOLT': case 'NUT': return { ...calculateBoltPurchase(bomQuantity), category: 'BOLT', unit: 'SETS' }; case 'GASKET': return { ...calculateGasketPurchase(bomQuantity), category: 'GASKET', unit: 'EA' }; case 'SUPPORT': // 서포트는 취합된 숫자 그대로 return { purchaseQuantity: bomQuantity, calculation: `${bomQuantity} EA (취합된 수량 그대로)`, category: 'SUPPORT', unit: 'EA' }; case 'FITTING': case 'INSTRUMENT': case 'VALVE': case 'FLANGE': default: return { ...calculateStandardPurchase(bomQuantity), category: category || 'STANDARD', unit: material.unit || 'EA' }; } }; /** * 자재 목록에 대한 구매 수량 계산 (일괄 처리) * @param {Array} materials - 자재 목록 * @returns {Array} 구매 계산 결과가 포함된 자재 목록 */ export const calculateBulkPurchase = (materials) => { return materials.map(material => ({ ...material, purchaseInfo: calculatePurchaseQuantity(material) })); };