feat: SWG 가스켓 전체 구성 정보 표시 개선
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled

- H/F/I/O SS304/GRAPHITE/CS/CS 패턴에서 4개 구성요소 모두 표시
- 기존 SS304 + GRAPHITE → SS304/GRAPHITE/CS/CS로 완전한 구성 표시
- 외부링/필러/내부링/추가구성 모든 정보 포함
- 구매수량 계산 모달에서 정확한 재질 정보 확인 가능
This commit is contained in:
Hyungi Ahn
2025-08-30 14:23:01 +09:00
parent 78d90c7a8f
commit 4f8e395f87
84 changed files with 16297 additions and 2161 deletions

View File

@@ -21,21 +21,44 @@ const groupMaterialsByCategory = (materials) => {
/**
* 동일한 자재끼리 합치기 (자재 설명 + 사이즈 기준)
* 엑셀 내보내기용 특별 처리:
* - PIPE: 끝단 정보 제거 (BOE-POE, POE-TOE 등)
* - NIPPLE: 길이별 구분 (75mm, 100mm 등)
*/
const consolidateMaterials = (materials, isComparison = false) => {
const consolidated = {};
materials.forEach(material => {
const category = material.classified_category || material.category || 'UNCATEGORIZED';
const description = material.original_description || material.description || '';
let description = material.original_description || material.description || '';
const sizeSpec = material.size_spec || '';
// 그룹화 키: 카테고리 + 자재설명 + 사이즈
const groupKey = `${category}|${description}|${sizeSpec}`;
// 파이프 끝단 정보 제거 (엑셀 내보내기용)
if (category === 'PIPE') {
description = description
.replace(/\s+(BOE-POE|POE-TOE|BOE-TOE|BOE-BBE|BBE-POE|BBE-BBE|POE-POE|TOE-TOE)\s*$/i, '')
.replace(/\s+(BOE|POE|TOE|BBE)\s*-\s*(BOE|POE|TOE|BBE)\s*$/i, '')
.trim();
}
// 니플의 경우 길이 정보를 그룹화 키에 포함
let lengthInfo = '';
if (category === 'FITTING' && description.toLowerCase().includes('nipple')) {
const lengthMatch = description.match(/(\d+)\s*mm/i);
if (lengthMatch) {
lengthInfo = `_${lengthMatch[1]}mm`;
}
}
// 그룹화 키: 카테고리 + 정제된자재설명 + 사이즈 + 길이정보
const groupKey = `${category}|${description}|${sizeSpec}${lengthInfo}`;
if (!consolidated[groupKey]) {
consolidated[groupKey] = {
...material,
// 정제된 설명으로 덮어쓰기
original_description: description,
description: description,
quantity: 0,
totalLength: 0, // 파이프용
itemCount: 0, // 파이프 개수
@@ -99,12 +122,23 @@ const formatMaterialForExcel = (material, includeComparison = false) => {
const category = material.classified_category || material.category || '-';
const isPipe = category === 'PIPE';
// 엑셀용 자재 설명 정제
let cleanDescription = material.original_description || material.description || '-';
// 파이프 끝단 정보 제거
if (category === 'PIPE') {
cleanDescription = cleanDescription
.replace(/\s+(BOE-POE|POE-TOE|BOE-TOE|BOE-BBE|BBE-POE|BBE-BBE|POE-POE|TOE-TOE)\s*$/i, '')
.replace(/\s+(BOE|POE|TOE|BBE)\s*-\s*(BOE|POE|TOE|BBE)\s*$/i, '')
.trim();
}
// 구매 수량 계산
const purchaseInfo = calculatePurchaseQuantity(material);
const base = {
'카테고리': category,
'자재 설명': material.original_description || material.description || '-',
'자재 설명': cleanDescription,
'사이즈': material.size_spec || '-'
};

View File

@@ -4,12 +4,12 @@
/**
* 파이프 구매 수량 계산
* @param {number} lengthMm - 파이프 총 길이 (mm)
* @param {number} totalLengthMm - 파이프 총 길이 (mm)
* @param {number} quantity - BOM 수량 (개수)
* @returns {object} 구매 계산 결과
*/
export const calculatePipePurchase = (lengthMm, quantity) => {
if (!lengthMm || lengthMm <= 0 || !quantity || quantity <= 0) {
export const calculatePipePurchase = (totalLengthMm, quantity) => {
if (!totalLengthMm || totalLengthMm <= 0 || !quantity || quantity <= 0) {
return {
purchaseQuantity: 0,
standardLength: 6000,
@@ -18,17 +18,18 @@ export const calculatePipePurchase = (lengthMm, quantity) => {
};
}
// 절단 여유분: 조각마다 2mm 추가
const cutLength = lengthMm + (quantity * 2);
// 절단 여유분: 절단 개수만큼 3mm 추가 (백엔드와 동일)
const cuttingLoss = quantity * 3;
const requiredLength = totalLengthMm + cuttingLoss;
// 6,000mm 단위로 올림 계산
const pipeCount = Math.ceil(cutLength / 6000);
const pipeCount = Math.ceil(requiredLength / 6000);
return {
purchaseQuantity: pipeCount,
standardLength: 6000,
cutLength: cutLength,
calculation: `${lengthMm}mm + ${quantity * 2}mm(여유분) = ${cutLength}mm → ${pipeCount}`
cutLength: requiredLength,
calculation: `${totalLengthMm}mm + ${cuttingLoss}mm(절단손실) = ${requiredLength}mm → ${pipeCount}`
};
};