// jQuery 의존성 확인 if (typeof jQuery === 'undefined') { console.error('jQuery is required for calculation.js'); } // 계산 로직을 위한 기본 클래스 class BaseCalculator { constructor() { this.cleanNumber = this.cleanNumber.bind(this); this.formatNumber = this.formatNumber.bind(this); } // 숫자만 남기는 헬퍼 cleanNumber(str) { return Number(String(str).replace(/[^0-9.-]/g, '')) || 0; } // 3자리마다 콤마 찍되, 먼저 소수점 이하는 반올림 처리 formatNumber(num) { const value = typeof num === 'string' ? parseFloat(num.replace(/,/g, '')) : num; const rounded = Math.round(value || 0); return rounded.toLocaleString(); } // 행별 합계 계산 (기본 구현) calculateRowTotal(row) { throw new Error('calculateRowTotal must be implemented by subclass'); } } // 견적서 계산 클래스 class EstimateCalculator extends BaseCalculator { calculateRowTotal(row) { row = $(row); const suInput = row.find('.su-input'); const areaLengthInput = row.find('.area-length-input'); const areaPriceInput = row.find('.area-price-input'); const unitPriceInput = row.find('.unit-price-input'); const su = suInput.length ? this.cleanNumber(suInput.val()) : 1; const areaLength = areaLengthInput.length ? this.cleanNumber(areaLengthInput.val()) : 1; const areaPrice = areaPriceInput.length ? this.cleanNumber(areaPriceInput.val()) : 1; let unitPrice = unitPriceInput.length ? this.cleanNumber(unitPriceInput.val()) : 1; const roundedAreaPrice = parseFloat(areaPrice.toFixed(2)); if (roundedAreaPrice > 0) { unitPrice = Math.ceil(areaLength * roundedAreaPrice); unitPriceInput.val(this.formatNumber(unitPrice)); } let totalPrice; if (!areaLength && !areaPrice) { totalPrice = su * unitPrice; } else if (areaLength && !areaPrice) { totalPrice = areaLength * unitPrice; } else { totalPrice = su * unitPrice; } const totalCell = row.find('.total-price'); if (totalCell.length) { totalCell.text(this.formatNumber(totalPrice)); } return totalPrice; } } // 거래명세표 계산 클래스 class TransactionCalculator extends BaseCalculator { calculateRowTotal(row) { row = $(row); const suInput = row.find('.su-input'); const unitPriceInput = row.find('.unit-price-input'); const supplyPriceCell = row.find('.supply-price'); const taxCell = row.find('.tax-price'); const totalCell = row.find('.total-price'); const su = suInput.length ? this.cleanNumber(suInput.val()) : 1; const unitPrice = unitPriceInput.length ? this.cleanNumber(unitPriceInput.val()) : 0; // 공급가액 계산 const supplyPrice = su * unitPrice; // 부가세 계산 (10%) const tax = Math.round(supplyPrice * 0.1); // 합계 계산 const totalPrice = supplyPrice + tax; // 각 셀에 값 표시 if (supplyPriceCell.length) { supplyPriceCell.text(this.formatNumber(supplyPrice)); } if (taxCell.length) { taxCell.text(this.formatNumber(tax)); } if (totalCell.length) { totalCell.text(this.formatNumber(totalPrice)); } return totalPrice; } } // 계산기 팩토리 class CalculatorFactory { static createCalculator(type) { switch(type) { case 'estimate': return new EstimateCalculator(); case 'transaction': return new TransactionCalculator(); default: throw new Error('Unknown calculator type'); } } } // 전역 계산기 인스턴스 let calculator = null; // 계산기 초기화 함수 function initializeCalculator(type) { calculator = CalculatorFactory.createCalculator(type); } // 기존 함수들을 새로운 클래스 기반으로 래핑 function calculateRowTotal(row) { if (!calculator) { console.error('Calculator not initialized'); return 0; } return calculator.calculateRowTotal(row); } function calculateSubtotalBySerial(serialNumber) { if (!calculator) { console.error('Calculator not initialized'); return 0; } let subtotal = 0; const rows = $(`.calculation-row[data-serial="${serialNumber}"]`); rows.each(function() { subtotal += calculator.calculateRowTotal($(this)); }); const subtotalCells = $(`.subtotal-cell[data-serial="${serialNumber}"]`); if (subtotalCells.length > 0) { subtotalCells.each(function() { $(this).text(calculator.formatNumber(subtotal)); }); } return subtotal; } function calculateAllSubtotals() { if (!calculator) { console.error('Calculator not initialized'); return 0; } let grandTotal = 0; const uniqueSerials = new Set(); $('.calculation-row').each(function() { uniqueSerials.add($(this).data('serial')); }); uniqueSerials.forEach(function(serialNumber) { grandTotal += calculateSubtotalBySerial(serialNumber); }); return grandTotal; }