Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
- 서포트 카테고리 UI 개선: 좌우 스크롤, 헤더/본문 동기화, 가운데 정렬 - 동일 항목 합산 기능 구현 (Type + Size + Grade 기준) - 헤더 구조 변경: 압력/스케줄 제거, 구매수량 단일화, User Requirements 추가 - 우레탄 블럭슈 두께 정보(40t, 27t) Material Grade에 포함 - 서포트 수량 계산 수정: 취합된 숫자 그대로 표시 (4의 배수 계산 제거) - 서포트 분류 로직 개선: CLAMP, U-BOLT, URETHANE BLOCK SHOE 등 정확한 분류 - 백엔드 서포트 분류기에 User Requirements 추출 기능 추가 - 엑셀 내보내기에 서포트 카테고리 처리 로직 추가
161 lines
4.4 KiB
JavaScript
161 lines
4.4 KiB
JavaScript
/**
|
||
* 자재 카테고리별 구매 수량 계산 유틸리티
|
||
*/
|
||
|
||
/**
|
||
* 파이프 구매 수량 계산
|
||
* @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)
|
||
}));
|
||
};
|