초기 커밋: 5130 레거시 시스템
- URL 하드코딩 → .env APP_URL 기반 동적 URL로 변경 - DB 연결 하드코딩 → .env 기반으로 변경 - MySQL strict mode DATE 오류 수정
This commit is contained in:
182
estimate/common/calculation.js
Normal file
182
estimate/common/calculation.js
Normal file
@@ -0,0 +1,182 @@
|
||||
// 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;
|
||||
}
|
||||
2535
estimate/common/common_addrowJS.php
Normal file
2535
estimate/common/common_addrowJS.php
Normal file
File diff suppressed because it is too large
Load Diff
220
estimate/common/common_screen.php
Normal file
220
estimate/common/common_screen.php
Normal file
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
function generateTableSection($id, $title, $badgeClass = 'bg-primary', $user_idtem= '', $process=null, $mode ) {
|
||||
|
||||
$sumColumns = range(0, 71);
|
||||
// echo $user_idtem . '</br>';
|
||||
echo "
|
||||
<div class='table-responsive mb-5'>
|
||||
<div class='d-flex align-items-center p-1 mt-1 mb-2'>
|
||||
<button type='button' id='estimateScreen_view' class='btn btn-primary btn-sm ms-2 me-3 mt-1'> <i class='bi bi-chevron-down'></i> </button>
|
||||
<span class='badge $badgeClass fs-6 me-3'>$title</span>
|
||||
<span class='text-primary fs-4 viewNoBtn add-row me-4' data-table='{$id}Table' style='margin-right: 5px;'>+</span>";
|
||||
if($process =='견적서')
|
||||
{
|
||||
echo "
|
||||
<button type='button' id='viewEstimateUnit' class='btn btn-secondary btn-sm me-2' data-table='{$id}Table' > <i class='bi bi-grid-3x3'></i> 단가표 </button> ";
|
||||
if($mode === 'view') {
|
||||
echo " <button type='button' id='viewEstimate' class='btn btn-dark btn-sm ' data-table='{$id}Table' > <i class='bi bi-card-heading'></i> 견적서 </button>
|
||||
<button type='button' id='viewEstimateDetail' class='btn btn-danger btn-sm ms-2' data-table='{$id}Table' > <i class='bi bi-card-heading'></i> 산출내역서 </button>
|
||||
";
|
||||
}
|
||||
}
|
||||
echo "
|
||||
</div>
|
||||
<div id='estimate_screenDiv' >
|
||||
<div class='d-flex row'>
|
||||
<table class='table table-bordered' id='{$id}Table'>
|
||||
<thead id='thead_{$id}'>
|
||||
<tr>
|
||||
<th rowspan='6'>행추가<br>삭제</th>
|
||||
<th rowspan='6'>일련<br>번호</th>
|
||||
<th rowspan='6'>층수</th>
|
||||
<th rowspan='6'>부호</th>
|
||||
<th rowspan='6'>제품명</th>
|
||||
<th rowspan='6'>종류</th>
|
||||
<th rowspan='4' colspan='2'>가이드레일</th>
|
||||
<th rowspan='4' colspan='2'>①오픈사이즈</th>
|
||||
<th rowspan='4' colspan='2'>②-1제작사이즈 <br> <span class='text-primary'> (스크린)</span></th>
|
||||
<th rowspan='4' colspan='2'>②-2제작사이즈 <br> <span class='text-danger'> (부자재)</span></th>
|
||||
<th rowspan='6'>③면적</th>
|
||||
<th rowspan='6'>④중량</th>
|
||||
<th rowspan='6'>셔터<br>수량</th>
|
||||
<th rowspan='4' colspan='3'> 연동제어기</th>
|
||||
<th rowspan='2' colspan='7'> ⑤모터</th>
|
||||
<th rowspan='1' colspan='36'> 절곡</th>
|
||||
<th rowspan='1' colspan='14'> 부자재</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan='2' colspan='13'>⑥가이드레일(한쌍)</th>
|
||||
<th rowspan='2' colspan='13'>⑦케이스(셔터박스)</th>
|
||||
<th rowspan='2' colspan='10'>⑧하단마감재</th>
|
||||
<th rowspan='2' colspan='8'>⑨감기샤프트</th>
|
||||
<th rowspan='2' colspan='4'>⑩각파이프</th>
|
||||
<th rowspan='4'>⑪환봉</th>
|
||||
<th rowspan='4'>⑫앵글</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan='4'> <span class='text-primary'> 모터 견적가 포함</span> </th>
|
||||
<th rowspan='4'> 전원 </th>
|
||||
<th rowspan='4'> 유/무선 </th>
|
||||
<th rowspan='4'> 용량 </th>
|
||||
<th rowspan='2' colspan='3'> 브라케트 </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan='1' colspan='6'>가이드레일 마감/보강재</th>
|
||||
<th rowspan='3'>하부base1</th>
|
||||
<th rowspan='3'>하부base2</th>
|
||||
<th colspan='5'> 레일용 연기차단재</th>
|
||||
<th colspan='12'> 케이스 </th>
|
||||
<th> 케이스용<br>연기차단재</th>
|
||||
<th colspan='3'>하장바</th>
|
||||
<th colspan='3'>엘바</th>
|
||||
<th colspan='3'>보강평철</th>
|
||||
<th colspan='1'>무게평철</th>
|
||||
<th rowspan='3'>인치</th>
|
||||
<th colspan='7'>원형파이프</th>
|
||||
<th rowspan='2' colspan='4'> 50*30*1.4T </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan='2'> 설치유형 </th>
|
||||
<th rowspan='2'> 마감유형 </th>
|
||||
<th rowspan='2'> 가로 </th>
|
||||
<th rowspan='2'> 높이 </th>
|
||||
<th rowspan='2'> 가로 </th>
|
||||
<th rowspan='2'> 높이 </th>
|
||||
<th rowspan='2'> 가로 </th>
|
||||
<th rowspan='2'> 높이 </th>
|
||||
<th rowspan='2'> 매립 </th>
|
||||
<th rowspan='2'> 노출 </th>
|
||||
<th rowspan='2'> 매립형 <br> 뒷박스 </th>
|
||||
<th rowspan='2'>가로*세로</th>
|
||||
<th rowspan='2'>사이즈<br>(인치)</th>
|
||||
<th rowspan='2'>받침용<br>앵글</th>
|
||||
<th rowspan='2'>size</th>
|
||||
<th rowspan='2'>2438</th>
|
||||
<th rowspan='2'>3000</th>
|
||||
<th rowspan='2'>3500</th>
|
||||
<th rowspan='2'>4000</th>
|
||||
<th rowspan='2'>4300</th>
|
||||
<th colspan='5'>W50</th>
|
||||
<th colspan='8'>셔터박스</th>
|
||||
<th rowspan='1'>상부덮개</th>
|
||||
<th colspan='3'>마구리</th>
|
||||
<th rowspan='2' colspan='1'>W80</th>
|
||||
<th colspan='3'>60*40</th>
|
||||
<th colspan='3'>60*17</th>
|
||||
<th colspan='3'>50</th>
|
||||
<th colspan='1'>12T</th>
|
||||
<th colspan='1'>연결 샤프트</th>
|
||||
<th colspan='3'>4</th>
|
||||
<th colspan='3'>5</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan='1'>2438</th>
|
||||
<th rowspan='1'>3000</th>
|
||||
<th rowspan='1'>3500</th>
|
||||
<th rowspan='1'>4000</th>
|
||||
<th rowspan='1'>4300</th>
|
||||
<th>규격 <br> (사이즈,전면밑,레일폭,점검구방향) </th>
|
||||
<th>size</th>
|
||||
<th>1219</th>
|
||||
<th>2438</th>
|
||||
<th>3000</th>
|
||||
<th>3500</th>
|
||||
<th>4000</th>
|
||||
<th>4150</th>
|
||||
<th>1219*389</th>
|
||||
<th>size</th>
|
||||
<th>날개</th>
|
||||
<th>수량</th>
|
||||
<th>size</th>
|
||||
<th>3000</th>
|
||||
<th>4000</th>
|
||||
<th>size</th>
|
||||
<th>3000</th>
|
||||
<th>4000</th>
|
||||
<th>size</th>
|
||||
<th>3000</th>
|
||||
<th>4000</th>
|
||||
<th>2000</th>
|
||||
<th>인치 / 길이 / 수량</th>
|
||||
<th>3000</th>
|
||||
<th>4500</th>
|
||||
<th>6000</th>
|
||||
<th>6000</th>
|
||||
<th>7000</th>
|
||||
<th>8200</th>
|
||||
<th>보강개수환산</th>
|
||||
<th>size</th>
|
||||
<th>3000</th>
|
||||
<th>6000</th>
|
||||
<th>3000</th>
|
||||
<th>2500</th>
|
||||
</tr> " ;
|
||||
|
||||
// $user_id가 'pro'일 경우 열 번호를 표시하는 th 행 추가 개발자 모드
|
||||
if ($user_idtem === 'pro') {
|
||||
echo "<tr> <th> </th> ";
|
||||
$j = 0;
|
||||
// 71에서 스크린제작사이즈 열 추가로 73
|
||||
for ($i = 1; $i <= 71; $i++) {
|
||||
if($i==10)
|
||||
{
|
||||
$j = 'col10_SW'; // 스크린제작치수 가로
|
||||
echo "<th class='text-primary'>$j</th>";
|
||||
$j = 'col11_SH'; // 스크린제작치수 높이
|
||||
echo "<th class='text-primary'>$j</th>";
|
||||
$j = 'col' . $i ;
|
||||
}
|
||||
else if($i==18)
|
||||
{
|
||||
$j = 'col18_brand'; // 계산서 발행 주체
|
||||
echo "<th class='text-primary'>$j</th>";
|
||||
$j = 'col' . $i ;
|
||||
echo "<th class='text-primary'>$j</th>";
|
||||
$j = 'col18_wireless';
|
||||
}
|
||||
else if($i>20) {
|
||||
$j = 'col' . ($i) ; // 18번 유/무선 모터 이후 숫자 맞춤
|
||||
if($i==47) {
|
||||
echo "<th class='text-primary'>$j</th>";
|
||||
$j = 'col45_wing' ; // 45번 마구리 날개여부)
|
||||
}
|
||||
}
|
||||
else
|
||||
$j = 'col' . $i ;
|
||||
|
||||
echo "<th class='text-primary'>$j</th>";
|
||||
}
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr> <th> </th> ";
|
||||
// 75에서 스크린제작사이즈 열 추가로 77
|
||||
for ($i = 2; $i <= 77; $i++) {
|
||||
echo "<th class='text-success'>$i</th>";
|
||||
}
|
||||
echo "</tr>";
|
||||
}
|
||||
echo "</thead>
|
||||
<tbody id='{$id}Group'>
|
||||
<!-- 자동생성 -->
|
||||
</tbody>
|
||||
<tfoot class='table-secondary'>
|
||||
<tr>
|
||||
<th class='text-end' colspan='14'> 합계 </th>";
|
||||
for ($i = 15; $i <= 77; $i++) {
|
||||
echo "<th class='text-center' id='totalCol{$i}'></th>";
|
||||
}
|
||||
|
||||
echo " </tr> ";
|
||||
echo " </tfoot>
|
||||
</table>
|
||||
<div class='d-flex row mt-3 mb-5'>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
";
|
||||
}
|
||||
|
||||
?>
|
||||
226
estimate/common/common_slat.php
Normal file
226
estimate/common/common_slat.php
Normal file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
// 철재 스라트
|
||||
function generateTableSection_slat($id, $title, $badgeClass = 'bg-success', $user_idtem= '', $process=null, $mode ) {
|
||||
// 합계가 필요한 열 번호 (0-based index)
|
||||
$sumColumns = range(0, 78);
|
||||
|
||||
// echo $user_idtem . '</br>';
|
||||
echo " <div class='table-responsive mb-5'>
|
||||
<div class='d-flex align-items-center p-1 mt-1 mb-2'>
|
||||
<button type='button' id='estimateslat_view' class='btn btn-success btn-sm ms-2 me-3 mt-1'> <i class='bi bi-chevron-down'></i> </button>
|
||||
<span class='badge $badgeClass fs-6 me-3'>$title</span>
|
||||
<span class='text-primary fs-4 viewNoBtn add-row-slat me-4' data-table='{$id}Table' style='margin-right: 5px;'>+</span> ";
|
||||
if($process =='견적서')
|
||||
{
|
||||
echo "
|
||||
<button type='button' id='viewEstimateSlatUnit' class='btn btn-secondary btn-sm me-2' data-table='{$id}Table' > <i class='bi bi-grid-3x3'></i> 단가표 </button> ";
|
||||
if($mode === 'view') {
|
||||
echo "<button type='button' id='viewEstimateSlat' class='btn btn-dark btn-sm ' data-table='{$id}Table' > <i class='bi bi-card-heading'></i> 견적서 </button>
|
||||
<button type='button' id='viewEstimateSlatDetail' class='btn btn-danger btn-sm ms-2' data-table='{$id}Table' > <i class='bi bi-card-heading'></i> 산출내역서 </button>
|
||||
";
|
||||
}
|
||||
}
|
||||
echo "
|
||||
</div>
|
||||
<div id='estimate_slatDiv' >
|
||||
<div class='d-flex row'>
|
||||
<table class='table table-bordered' id='{$id}Table'>
|
||||
<thead id='thead_{$id}'>
|
||||
<tr>
|
||||
<th rowspan='6'>행추가<br>삭제</th>
|
||||
<th rowspan='6'>일련<br>번호</th>
|
||||
<th rowspan='6'>층수</th>
|
||||
<th rowspan='6'>부호</th>
|
||||
<th rowspan='6'>제품명</th>
|
||||
<th rowspan='6'>투시창선택</th>
|
||||
<th rowspan='6'>종류</th>
|
||||
<th rowspan='4' colspan='2'>가이드레일</th>
|
||||
<th rowspan='4' colspan='2'>①오픈사이즈</th>
|
||||
<th rowspan='4' colspan='2'>②-1제작사이즈 <br> <span class='text-primary'> (철재)</span></th>
|
||||
<th rowspan='4' colspan='2'>②-2제작사이즈 <br> <span class='text-danger'> (부자재)</span></th>
|
||||
<th rowspan='6'>③면적</th>
|
||||
<th rowspan='6'>④중량</th>
|
||||
<th rowspan='6'>투시창<br>수량</th>
|
||||
<th rowspan='6'>셔터<br>수량</th>
|
||||
<th rowspan='4' colspan='3' >연동제어기</th>
|
||||
<th rowspan='2' colspan='7'>⑤모터</th>
|
||||
<th rowspan='1' colspan='36'>절곡</th>
|
||||
<th rowspan='1' colspan='21'>부자재</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan='2' colspan='8'>⑥가이드레일(한쌍)</th>
|
||||
<th rowspan='2' colspan='18'>⑦케이스(셔터박스)</th>
|
||||
<th rowspan='2' colspan='10'>⑧하단마감재</th>
|
||||
<th rowspan='2' colspan='13'>⑨감기샤프트</th>
|
||||
<th rowspan='2' colspan='4'>⑩각파이프</th>
|
||||
<th rowspan='4'>조인트바</th>
|
||||
<th rowspan='4'>앵글</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan='4'> <span class='text-primary'> 모터 견적가 포함</span> </th>
|
||||
<th rowspan='4'> 전원 </th>
|
||||
<th rowspan='4'> 유/무선 </th>
|
||||
<th rowspan='4'> 용량 </th>
|
||||
<th rowspan='2' colspan='3'> 브라케트 </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan='1' colspan='6'>가이드레일 마감/보강재</th>
|
||||
<th rowspan='3'>하부base1</th>
|
||||
<th rowspan='3'>하부base2</th>
|
||||
<th colspan='5' > 레일용 연기차단재 <br> W50</th>
|
||||
<th colspan='12'> 케이스 </th>
|
||||
<th> 케이스용<br>연기차단재</th>
|
||||
<th colspan='3'>하장바</th>
|
||||
<th colspan='7'>x</th>
|
||||
<th rowspan='3'>인치</th>
|
||||
<th colspan='12'>원형파이프</th>
|
||||
<th rowspan='2' colspan='4'> 50*30*1.4T </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan='2'> 설치유형 </th>
|
||||
<th rowspan='2'> 마감유형 </th>
|
||||
<th rowspan='2'> 가로 </th>
|
||||
<th rowspan='2'> 높이 </th>
|
||||
<th rowspan='2'> 가로 </th>
|
||||
<th rowspan='2'> 높이 </th>
|
||||
<th rowspan='2'> 가로 </th>
|
||||
<th rowspan='2'> 높이 </th>
|
||||
<th rowspan='2'> 매립 </th>
|
||||
<th rowspan='2'> 노출 </th>
|
||||
<th rowspan='2'> 매립형 <br> 뒷박스 </th>
|
||||
<th rowspan='2'>가로*세로</th>
|
||||
<th rowspan='2'>사이즈</th>
|
||||
<th rowspan='2'>받침용<br>앵글</th>
|
||||
<th rowspan='2'>size</th>
|
||||
<th rowspan='2'>2438</th>
|
||||
<th rowspan='2'>3000</th>
|
||||
<th rowspan='2'>3500</th>
|
||||
<th rowspan='2'>4000</th>
|
||||
<th rowspan='2'>4300</th>
|
||||
<th rowspan='2'>2438</th>
|
||||
<th rowspan='2'>3000</th>
|
||||
<th rowspan='2'>3500</th>
|
||||
<th rowspan='2'>4000</th>
|
||||
<th rowspan='2'>4300</th>
|
||||
<th colspan='8'>셔터박스</th>
|
||||
<th rowspan='1'>상부덮개</th>
|
||||
<th colspan='3'>마구리</th>
|
||||
<th colspan='1'>W80</th>
|
||||
<th colspan='3'>60*40</th>
|
||||
<th colspan='7'>x</th>
|
||||
<th colspan='1'>x</th>
|
||||
<th colspan='3'>4</th>
|
||||
<th colspan='3'>5</th>
|
||||
<th colspan='4'>6</th>
|
||||
<th colspan='1'>8</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>규격 <br> (사이즈,전면밑,레일폭,점검구방향) </th>
|
||||
<th>size</th>
|
||||
<th>1219</th>
|
||||
<th>2438</th>
|
||||
<th>3000</th>
|
||||
<th>3500</th>
|
||||
<th>4000</th>
|
||||
<th>4150</th>
|
||||
<th>1219*@</th>
|
||||
<th>size</th>
|
||||
<th>날개</th>
|
||||
<th>수량</th>
|
||||
<th>3000</th>
|
||||
<th>size</th>
|
||||
<th>3000</th>
|
||||
<th>4000</th>
|
||||
<th colspan='7'>x</th>
|
||||
<th>철재x</th>
|
||||
<th>3000</th>
|
||||
<th>4500</th>
|
||||
<th>6000</th>
|
||||
<th>6000</th>
|
||||
<th>7000</th>
|
||||
<th>8200</th>
|
||||
<th>3000</th>
|
||||
<th>6000</th>
|
||||
<th>7000</th>
|
||||
<th>8000</th>
|
||||
<th>8200</th>
|
||||
<th>보강개수환산</th>
|
||||
<th>size</th>
|
||||
<th>3000</th>
|
||||
<th>6000</th>
|
||||
<th>300</th>
|
||||
<th>2500</th>
|
||||
</tr> " ;
|
||||
|
||||
// $user_id가 'pro'일 경우 열 번호를 표시하는 th 행 추가
|
||||
if ($user_idtem === 'pro') {
|
||||
echo "<tr class='text-primary'><th></th>";
|
||||
|
||||
// 1) 기본 col1~col4
|
||||
for ($i = 1; $i <= 4; $i++) {
|
||||
echo "<th class='text-primary'>col{$i}</th>";
|
||||
}
|
||||
|
||||
// 2) col4 다음에 col4_quartz
|
||||
echo "<th class='text-primary'>col4_quartz</th>";
|
||||
|
||||
// 3) col5~col18
|
||||
for ($i = 5; $i <= 18; $i++) {
|
||||
if($i==10)
|
||||
{
|
||||
$j = 'col10_SW'; // 스크린제작치수 가로
|
||||
echo "<th class='text-primary'>$j</th>";
|
||||
$j = 'col11_SH'; // 스크린제작치수 높이
|
||||
echo "<th class='text-primary'>$j</th>";
|
||||
echo "<th class='text-primary'>col{$i}</th>";
|
||||
}
|
||||
else {
|
||||
echo "<th class='text-primary'>col{$i}</th>";
|
||||
}
|
||||
}
|
||||
|
||||
// 4) col19 이전에 추가할 3개
|
||||
echo "<th class='text-primary'>col19_brand</th>";
|
||||
echo "<th class='text-primary'>col19</th>";
|
||||
echo "<th class='text-primary'>col19_wireless</th>";
|
||||
|
||||
// 5) 나머지 col20~col46
|
||||
for ($i = 20; $i <= 46; $i++) {
|
||||
echo "<th class='text-primary'>col{$i}</th>";
|
||||
}
|
||||
echo "<th class='text-primary'>col46_wing</th>";
|
||||
|
||||
for ($i = 47; $i <= 77; $i++) {
|
||||
echo "<th class='text-primary'>col{$i}</th>";
|
||||
}
|
||||
echo "</tr>";
|
||||
echo "<tr> <th>idx </th> ";
|
||||
for ($i = 0; $i <= 82; $i++) {
|
||||
echo "<th class='text-success'>$i</th>";
|
||||
}
|
||||
echo "</tr>";
|
||||
|
||||
}
|
||||
echo "</thead>
|
||||
<tbody id='{$id}Group'>
|
||||
<!-- 자동생성 -->
|
||||
</tbody>
|
||||
<tfoot class='table-secondary'>
|
||||
<tr>
|
||||
<th class='text-end' colspan='15'> 합계 </th>";
|
||||
|
||||
for ($i = 16; $i <= 84; $i++) {
|
||||
echo "<th class='text-center' id='estimateSlattotalCol{$i}'></th>";
|
||||
}
|
||||
echo " </tr> ";
|
||||
echo " </tfoot>
|
||||
</table>
|
||||
<div class='d-flex row mt-3 mb-5'>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
";
|
||||
}
|
||||
|
||||
?>
|
||||
681
estimate/common/compare_lastJS.php
Normal file
681
estimate/common/compare_lastJS.php
Normal file
@@ -0,0 +1,681 @@
|
||||
<!-- 페이지 로딩 -->
|
||||
<script>
|
||||
var ajaxRequest_write = null;
|
||||
$(document).ready(function() {
|
||||
$('#loadingOverlay').hide(); // 로딩 오버레이 숨기기
|
||||
|
||||
var dataList = <?php echo json_encode($detailJson ?? []); ?>;
|
||||
|
||||
// JSON 데이터를 처리하기 전에 유효성 검사
|
||||
if (dataList && typeof dataList === 'string') {
|
||||
try {
|
||||
dataList = JSON.parse(dataList); // JSON 문자열을 객체로 변환
|
||||
} catch (e) {
|
||||
console.error('JSON parsing error: ', e);
|
||||
dataList = []; // 오류 발생 시 빈 배열로 초기화
|
||||
}
|
||||
}
|
||||
|
||||
// 배열인지 확인
|
||||
// console.log('dataList after JSON.parse check: ', dataList);
|
||||
if (!Array.isArray(dataList)) {
|
||||
dataList = [];
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#updateText").text('견적수정됨');
|
||||
}
|
||||
|
||||
// 테이블에 데이터 로드
|
||||
loadTableData('#detailTable', dataList);
|
||||
|
||||
// 셔터박스 오류메시지 화면에 표시
|
||||
var shutterboxMsg = <?= json_encode($shutterboxMsg) ?>;
|
||||
|
||||
// JavaScript로 처리
|
||||
if (shutterboxMsg) {
|
||||
var shutterboxDiv = document.getElementById("shutterboxMsg");
|
||||
shutterboxDiv.style.display = "block"; // 보이게 설정
|
||||
shutterboxDiv.innerHTML = shutterboxMsg; // 내용 설정
|
||||
}
|
||||
});
|
||||
|
||||
function loadTableData(tableBodySelector, dataList) {
|
||||
// console.log('loadTableData data: ', dataList); // 여기서 데이터 확인
|
||||
|
||||
var tableBody = $(tableBodySelector); // 테이블 본문 선택
|
||||
|
||||
// 데이터를 반복하면서 테이블에 행을 업데이트
|
||||
var count = 1;
|
||||
dataList.forEach(function(rowData, index) {
|
||||
var row = tableBody.find('tr').eq(index + count); // index에 맞는 tr을 가져옴
|
||||
if (row.length) {
|
||||
updateRowData(row, rowData, index);
|
||||
}
|
||||
// console.log('index data: ', index); // 여기서 데이터 확인
|
||||
// console.log('rowData data: ', rowData); // 여기서 데이터 확인
|
||||
// count++;
|
||||
});
|
||||
}
|
||||
|
||||
// 기존 행을 수정하는 함수
|
||||
function updateRowData(row, rowData, rowIndex) {
|
||||
// 수정해야 할 td 요소들을 선택하여 해당 값을 업데이트
|
||||
row.find('.su-input').val(rowData[0]); // 수량
|
||||
row.find('.area-length-input').val(rowData[2]); // 길이
|
||||
row.find('.area-price-input').val(rowData[3]); // 면적단가
|
||||
row.find('.unit-price-input').val(rowData[4]); // 단가
|
||||
|
||||
// 수정된 행에 동적 계산 함수 호출
|
||||
calculateRowTotal(row); // 필요 시 계산 함수 호출
|
||||
}
|
||||
|
||||
|
||||
function inputNumber(input) {
|
||||
const value = input.value.replace(/,/g, ''); // 콤마 제거
|
||||
input.value = parseInt(value, 10).toLocaleString(); // 다시 콤마 추가
|
||||
calculateRowTotal($(input).closest('tr')); // 행의 합계 다시 계산
|
||||
}
|
||||
|
||||
function generatePDF() {
|
||||
var title_message = '<?php echo $title_message; ?>';
|
||||
var workplace = '<?php echo $outworkplace; ?>';
|
||||
var deadline = '<?php echo $indate; ?>';
|
||||
var deadlineDate = new Date(deadline);
|
||||
var formattedDate = "(" + String(deadlineDate.getFullYear()).slice(-2) + "." + ("0" + (deadlineDate.getMonth() + 1)).slice(-2) + "." + ("0" + deadlineDate.getDate()).slice(-2) + ")";
|
||||
var result = 'KD' + title_message + '(' + workplace +')' + formattedDate + '.pdf';
|
||||
|
||||
var element = document.getElementById('content-to-print');
|
||||
|
||||
// 강제로 input 요소의 폰트 크기 변경
|
||||
var inputs = element.querySelectorAll('input');
|
||||
inputs.forEach(function(input) {
|
||||
input.style.fontSize = '6px'; // 모든 input 요소에 10px 폰트 크기 적용
|
||||
});
|
||||
|
||||
var opt = {
|
||||
margin: [15, 8, 17, 8], // Top, right, bottom, left margins
|
||||
filename: result,
|
||||
image: { type: 'jpeg', quality: 0.70 },
|
||||
html2canvas: { scale: 4 },
|
||||
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
|
||||
pagebreak: { mode: [''] }
|
||||
};
|
||||
|
||||
html2pdf().from(element).set(opt).save();
|
||||
}
|
||||
|
||||
// 이벤트 리스너 설정 및 계산 함수 호출
|
||||
function setupEventListeners() {
|
||||
document.querySelectorAll('input').forEach(input => {
|
||||
input.addEventListener('input', function () {
|
||||
const row = input.closest('tr');
|
||||
calculateRowTotal(row);
|
||||
calculateAllSubtotals();
|
||||
calculateGrandTotal();
|
||||
calculateRowTotalFirstTable();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 페이지 로드 시 초기 계산 및 이벤트 리스너 설정
|
||||
window.onload = function () {
|
||||
calculateAllSubtotals();
|
||||
calculateGrandTotal();
|
||||
setupEventListeners();
|
||||
calculateRowTotalFirstTable();
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
// 초기 상태에 따라 Estimateview와 listview를 보여주기/숨기기 설정
|
||||
if ($('#showEstimateCheckbox').is(':checked')) {
|
||||
$('.Estimateview').show();
|
||||
} else {
|
||||
$('.Estimateview').hide();
|
||||
}
|
||||
|
||||
if ($('#showlistCheckbox').is(':checked')) {
|
||||
$('.listview').show();
|
||||
} else {
|
||||
$('.listview').hide();
|
||||
}
|
||||
|
||||
// showEstimateCheckbox 체크박스 변경 시 Estimateview 보여주기/숨기기 설정
|
||||
$('#showEstimateCheckbox').on('change', function() {
|
||||
if ($(this).is(':checked')) {
|
||||
$('.Estimateview').show();
|
||||
} else {
|
||||
$('.Estimateview').hide();
|
||||
}
|
||||
});
|
||||
|
||||
// showlistCheckbox 체크박스 변경 시 listview 보여주기/숨기기 설정
|
||||
$('#showlistCheckbox').on('change', function() {
|
||||
if ($(this).is(':checked')) {
|
||||
$('.listview').show();
|
||||
} else {
|
||||
$('.listview').hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
var rowArray = <?= isset($row_array_json) ? $row_array_json : '[]' ?>;
|
||||
|
||||
if (Array.isArray(rowArray) && rowArray.length > 0) {
|
||||
rowArray.forEach(function (rowspanValue, index) {
|
||||
var cell = document.getElementById('dynamicRowspan-' + index);
|
||||
var cellCompare = document.getElementById('dynamicRowspanCompare-' + index);
|
||||
// console.log('index', index);
|
||||
if (cell && rowspanValue > 0) {
|
||||
cell.setAttribute('rowspan', rowspanValue);
|
||||
}
|
||||
if (cellCompare && rowspanValue > 0) {
|
||||
cellCompare.setAttribute('rowspan', rowspanValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 숫자 포맷팅 함수 (콤마 추가 및 소수점 둘째자리에서 반올림)
|
||||
function formatNumber(value) {
|
||||
// 소수점 둘째 자리에서 반올림
|
||||
const roundedValue = Math.round(value);
|
||||
// 콤마 추가 포맷팅
|
||||
return new Intl.NumberFormat().format(roundedValue);
|
||||
}
|
||||
|
||||
// 숫자에서 콤마를 제거하는 함수
|
||||
function cleanNumber(value) {
|
||||
// value가 null 또는 undefined인 경우 0을 반환하도록 처리
|
||||
if (!value) return 0;
|
||||
return parseFloat(value.replace(/,/g, '')) || 0;
|
||||
}
|
||||
|
||||
// 입력 필드에서 숫자를 포맷팅하는 함수
|
||||
function inputNumber(input) {
|
||||
const cursorPosition = input.selectionStart; // 현재 커서 위치를 저장
|
||||
const value = input.value.replace(/,/g, ''); // 입력값에서 숫자만 남기고 제거
|
||||
const formattedValue = Number(value).toLocaleString(); // 천 단위 콤마 추가
|
||||
input.value = formattedValue; // 포맷팅된 값으로 설정
|
||||
input.setSelectionRange(cursorPosition, cursorPosition); // 커서 위치 유지
|
||||
}
|
||||
|
||||
// 행별 합계 계산
|
||||
function calculateRowTotal(row) {
|
||||
// row를 jQuery 객체로 변환
|
||||
row = $(row);
|
||||
|
||||
// jQuery 객체에서 값을 가져옴
|
||||
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 ? cleanNumber(suInput.val()) : 1;
|
||||
const areaLength = areaLengthInput.length ? cleanNumber(areaLengthInput.val()) : 1;
|
||||
const areaPrice = areaPriceInput.length ? cleanNumber(areaPriceInput.val()) : 1;
|
||||
let unitPrice = unitPriceInput.length ? cleanNumber(unitPriceInput.val()) : 1;
|
||||
|
||||
const roundedAreaPrice = parseFloat(areaPrice.toFixed(2));
|
||||
|
||||
if (roundedAreaPrice > 0) {
|
||||
unitPrice = Math.ceil(areaLength * roundedAreaPrice); // 기본 수량 * 단가
|
||||
unitPriceInput.val(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(formatNumber(totalPrice));
|
||||
}
|
||||
|
||||
return totalPrice;
|
||||
}
|
||||
|
||||
|
||||
// 일련번호별 소계 계산 (jQuery 방식)
|
||||
function calculateSubtotalBySerial(serialNumber) {
|
||||
let subtotal = 0;
|
||||
const rows = $(`.calculation-row[data-serial="${serialNumber}"]`);
|
||||
|
||||
rows.each(function() {
|
||||
subtotal += calculateRowTotal($(this));
|
||||
});
|
||||
|
||||
const subtotalCells = $(`.subtotal-cell[data-serial="${serialNumber}"]`);
|
||||
if (subtotalCells.length > 0) {
|
||||
subtotalCells.each(function() {
|
||||
$(this).text(formatNumber(subtotal));
|
||||
});
|
||||
} else {
|
||||
console.error(`소계 셀을 찾을 수 없습니다. 일련번호: ${serialNumber}`);
|
||||
}
|
||||
|
||||
return subtotal;
|
||||
}
|
||||
|
||||
// 모든 일련번호별 소계 및 총합계 계산 (jQuery 방식)
|
||||
function calculateAllSubtotals() {
|
||||
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;
|
||||
}
|
||||
|
||||
// 총합계 계산 (jQuery 방식)
|
||||
function calculateGrandTotal() {
|
||||
const grandTotal = calculateAllSubtotals();
|
||||
const grandTotalCells = $('.grand-total');
|
||||
|
||||
if (grandTotalCells.length > 0) {
|
||||
grandTotalCells.each(function() {
|
||||
$(this).text(formatNumber(grandTotal));
|
||||
});
|
||||
$('#totalsum').text(formatNumber(grandTotal));
|
||||
var EstimateFirstSum = $("#EstimateFirstSum").val();
|
||||
var EstimateUpdatetSum = $("#EstimateUpdatetSum").val();
|
||||
$('#koreantotalsum').text(KoreanNumber(Math.ceil(grandTotal)));
|
||||
// 총금액에서 최초금액 추출하기
|
||||
if(grandTotal > 0 && EstimateFirstSum < 1)
|
||||
$("#EstimateFirstSum").val(formatNumber(grandTotal));
|
||||
else
|
||||
$("#EstimateUpdatetSum").val(formatNumber(grandTotal));
|
||||
// 차액계산
|
||||
if(cleanNumber($("#EstimateUpdatetSum").val())> 0)
|
||||
$("#EstimateDiffer").val(formatNumber(cleanNumber($("#EstimateUpdatetSum").val()) - cleanNumber($("#EstimateFirstSum").val())));
|
||||
else
|
||||
$("#EstimateDiffer").val(0);
|
||||
|
||||
// console.log('grand total : ', grandTotal);
|
||||
} else {
|
||||
console.error("전체 합계 셀을 찾을 수 없습니다. '.grand-total'이라는 클래스가 올바르게 설정되었는지 확인해주세요.");
|
||||
}
|
||||
}
|
||||
|
||||
// 첫 번째 테이블: 행별 합계 계산 (jQuery 방식)
|
||||
function calculateRowTotalFirstTable() {
|
||||
const rows = $('.calculation-firstrow');
|
||||
|
||||
rows.each(function() {
|
||||
const suInput = $(this).find('.total-su-input'); // 수량 입력 필드
|
||||
const subtotalCell = $(this).find('.subtotal-cell'); // 소계 셀
|
||||
const unitPriceCell = $(this).find('.total-unit-price'); // 단가 셀
|
||||
|
||||
const su = suInput ? cleanNumber(suInput.text()) : 1;
|
||||
const subtotal = subtotalCell ? cleanNumber(subtotalCell.text()) : 0;
|
||||
|
||||
let unitPrice = 0;
|
||||
if (su > 0) {
|
||||
unitPrice = subtotal / su; // 소계를 수량으로 나눠서 단가 계산
|
||||
}
|
||||
|
||||
if (unitPriceCell.length) {
|
||||
unitPriceCell.text(formatNumber(unitPrice)); // 단가 셀에 표시
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function KoreanNumber(number) {
|
||||
const koreanNumbers = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'];
|
||||
const koreanUnits = ['', '십', '백', '천'];
|
||||
const bigUnits = ['', '만', '억', '조'];
|
||||
|
||||
let result = '';
|
||||
let unitIndex = 0;
|
||||
let numberStr = String(number);
|
||||
|
||||
// 숫자가 0인 경우 '영원'을 반환
|
||||
if (number == 0) return '영원';
|
||||
|
||||
// 뒤에서부터 4자리씩 끊어서 처리
|
||||
while (numberStr.length > 0) {
|
||||
let chunk = numberStr.slice(-4); // 마지막 4자리
|
||||
numberStr = numberStr.slice(0, -4); // 나머지 숫자
|
||||
|
||||
let chunkResult = '';
|
||||
for (let i = 0; i < chunk.length; i++) {
|
||||
const digit = parseInt(chunk[i]);
|
||||
if (digit > 0) {
|
||||
chunkResult += koreanNumbers[digit] + koreanUnits[chunk.length - i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
if (chunkResult) {
|
||||
result = chunkResult + bigUnits[unitIndex] + result;
|
||||
}
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
// 불필요한 '일십', '일백', '일천' 등의 단위를 제거하고 '원'을 붙여 반환
|
||||
result = result.replace(/일(?=십|백|천)/g, '').trim();
|
||||
|
||||
return result + '';
|
||||
}
|
||||
|
||||
function removeAllButLastOccurrence(string, target) {
|
||||
// 마지막 '만'의 위치를 찾습니다
|
||||
const lastPos = string.lastIndexOf(target);
|
||||
|
||||
// 마지막 '만'이 없으면 원래 문자열을 반환합니다
|
||||
if (lastPos === -1) {
|
||||
return string;
|
||||
}
|
||||
|
||||
// 마지막 '만'을 제외한 모든 '만'을 제거합니다
|
||||
const beforeLastPos = string.slice(0, lastPos);
|
||||
const afterLastPos = string.slice(lastPos);
|
||||
|
||||
// '만'을 빈 문자열로 대체합니다
|
||||
const result = beforeLastPos.replace(new RegExp(target, 'g'), '') + afterLastPos;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// 저장 버튼 클릭 시 saveData 함수 호출
|
||||
$(".saveBtn").click(function() {
|
||||
saveData();
|
||||
});
|
||||
});
|
||||
|
||||
function saveData() {
|
||||
const myform = document.getElementById('board_form');
|
||||
let allValid = true;
|
||||
|
||||
if (!allValid) return;
|
||||
|
||||
var num = $("#num").val();
|
||||
$("#overlay").show();
|
||||
$("button").prop("disabled", true);
|
||||
|
||||
// 모드 설정 (insert 또는 modify)
|
||||
if ($("#mode").val() !== 'copy') {
|
||||
if (Number(num) < 1) {
|
||||
$("#mode").val('insert');
|
||||
} else {
|
||||
$("#mode").val('modify');
|
||||
}
|
||||
} else {
|
||||
$("#mode").val('insert');
|
||||
}
|
||||
|
||||
// 데이터 수집 (input 요소만 저장)
|
||||
let formData = [];
|
||||
$('#detailTable tbody tr').each(function() {
|
||||
let rowData = [];
|
||||
// 각 tr의 input 요소 순서대로 처리
|
||||
$(this).find('input, select').each(function() {
|
||||
let value = $(this).val();
|
||||
rowData.push(value); // input 값을 배열에 순서대로 추가
|
||||
});
|
||||
formData.push(rowData); // 각 행의 input 데이터를 배열에 추가
|
||||
});
|
||||
|
||||
// formData는 이제 각 행의 input 값들만 포함하는 배열입니다.
|
||||
console.log('formData:', formData);
|
||||
|
||||
|
||||
// JSON 문자열로 변환하여 form input에 설정
|
||||
let jsonString = JSON.stringify(formData);
|
||||
$('#detailJson').val(jsonString);
|
||||
|
||||
// console.log('detailJson', jsonString);
|
||||
|
||||
$("#estimateSurang").val('<?= $estimateSurang ?>'); // 견적수량 저장
|
||||
$("#estimateTotal").val($("#subtotal").text()); // 견적총액 저장
|
||||
console.log('$("#estimateSurang").val($("#subtotal").text())' , $("#estimateSurang").val());
|
||||
console.log('$("#estimateTotal").val($("#subtotal").text())' , $("#estimateTotal").val());
|
||||
|
||||
var form = $('#board_form')[0];
|
||||
var datasource = new FormData(form);
|
||||
|
||||
if (ajaxRequest_write !== null) {
|
||||
ajaxRequest_write.abort();
|
||||
}
|
||||
|
||||
showMsgModal(2); // 파일저장중
|
||||
|
||||
// Ajax 요청으로 서버에 데이터 전송
|
||||
ajaxRequest_write = $.ajax({
|
||||
enctype: 'multipart/form-data',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
cache: false,
|
||||
timeout: 600000,
|
||||
url: "/estimate/insert_detail.php", // 산출내역 저장
|
||||
type: "post",
|
||||
data: datasource,
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
// console.log(data);
|
||||
setTimeout(function() {
|
||||
$(opener.location).attr("href", "javascript:restorePageNumber();");
|
||||
setTimeout(function() {
|
||||
hideMsgModal();
|
||||
hideOverlay();
|
||||
}, 1500);
|
||||
}, 1000);
|
||||
|
||||
},
|
||||
error: function(jqxhr, status, error) {
|
||||
console.log(jqxhr, status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function generatePDF_server(callback) {
|
||||
var workplace = '<?php echo $title_message; ?>';
|
||||
var item = '<?php echo $emailTitle; ?>';
|
||||
var today = new Date();
|
||||
var formattedDate = "(" + String(today.getFullYear()).slice(-2) + "." + ("0" + (today.getMonth() + 1)).slice(-2) + "." + ("0" + today.getDate()).slice(-2) + ")";
|
||||
var result = 'KD' + item +'(' + workplace + ')' + formattedDate + '.pdf';
|
||||
|
||||
var element = document.getElementById('content-to-print');
|
||||
var opt = {
|
||||
margin: [10, 3, 12, 3], // Top, right, bottom, left margins
|
||||
filename: result,
|
||||
image: { type: 'jpeg', quality: 0.70 },
|
||||
html2canvas: { scale: 4 },
|
||||
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
|
||||
pagebreak: { mode: [''] }
|
||||
};
|
||||
|
||||
html2pdf().from(element).set(opt).output('datauristring').then(function (pdfDataUri) {
|
||||
var pdfBase64 = pdfDataUri.split(',')[1]; // Base64 인코딩된 PDF 데이터 추출
|
||||
var formData = new FormData();
|
||||
formData.append('pdf', pdfBase64);
|
||||
formData.append('filename', result);
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/email/save_pdf.php', // PDF 파일을 저장하는 PHP 파일
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function (response) {
|
||||
var res = JSON.parse(response);
|
||||
if (callback) {
|
||||
callback(res.filename); // 서버에 저장된 파일 경로를 콜백으로 전달
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
Swal.fire('Error', 'PDF 저장에 실패했습니다.', 'error');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var ajaxRequest = null;
|
||||
|
||||
function sendmail() {
|
||||
var secondordnum = '<?php echo $secondordnum; ?>'; // 서버에서 가져온 값
|
||||
var item = '<?php echo $emailTitle; ?>';
|
||||
|
||||
if (!secondordnum) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: '오류 알림',
|
||||
text: '발주처 코드가 없습니다.'
|
||||
});
|
||||
return; // 함수 종료
|
||||
}
|
||||
|
||||
if (typeof ajaxRequest !== 'undefined' && ajaxRequest !== null) {
|
||||
ajaxRequest.abort();
|
||||
}
|
||||
|
||||
ajaxRequest = $.ajax({
|
||||
type: 'POST',
|
||||
url: '/email/get_companyCode.php',
|
||||
data: { secondordnum: secondordnum },
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
console.log('response : ', response);
|
||||
if (response.error) {
|
||||
Swal.fire('Error', response.error, 'error');
|
||||
} else {
|
||||
var email = response.email;
|
||||
var vendorName = response.vendor_name;
|
||||
|
||||
Swal.fire({
|
||||
title: 'E메일 보내기',
|
||||
text: vendorName + ' Email 주소확인',
|
||||
icon: 'warning',
|
||||
input: 'text', // input 창을 텍스트 필드로 설정
|
||||
inputLabel: 'Email 주소 수정 가능',
|
||||
inputValue: email, // 기존 이메일 주소를 기본값으로 설정
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '보내기',
|
||||
cancelButtonText: '취소',
|
||||
reverseButtons: true,
|
||||
inputValidator: (value) => {
|
||||
if (!value) {
|
||||
return '이메일 주소를 입력해주세요!';
|
||||
}
|
||||
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailPattern.test(value)) {
|
||||
return '올바른 이메일 형식을 입력해주세요!';
|
||||
}
|
||||
}
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
const updatedEmail = result.value; // 입력된 이메일 주소 가져오기
|
||||
generatePDF_server(function(filename) {
|
||||
sendEmail(updatedEmail, vendorName, item, filename);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
Swal.fire('Error', '전송중 오류가 발생했습니다.', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sendEmail(recipientEmail, vendorName, item, filename) {
|
||||
if (typeof ajaxRequest !== 'undefined' && ajaxRequest !== null) {
|
||||
ajaxRequest.abort();
|
||||
}
|
||||
var today = new Date();
|
||||
var formattedDate = "(" + String(today.getFullYear()).slice(-2) + "." + ("0" + (today.getMonth() + 1)).slice(-2) + "." + ("0" + today.getDate()).slice(-2) + ")";
|
||||
|
||||
ajaxRequest = $.ajax({
|
||||
type: 'POST',
|
||||
url: '/email/send_email.php', // 이메일 전송을 처리하는 PHP 파일
|
||||
data: { email: recipientEmail, vendorName: vendorName, filename: filename, item: item, formattedDate: formattedDate },
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
Swal.fire('Success', '정상적으로 전송되었습니다.', 'success');
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
Swal.fire('Error', '전송에 실패했습니다. 확인바랍니다.', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// 재계산 버튼 클릭 이벤트
|
||||
$('.initialBtn').on('click', function() {
|
||||
// 재계산 확인 알림
|
||||
Swal.fire({
|
||||
title: '견적데이터 재계산',
|
||||
text: "견적 데이터를 재계산 하시겠습니까?",
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: '예, 재계산합니다',
|
||||
cancelButtonText: '취소'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
|
||||
$("#estimateTotal").val(0); // 견적총액 저장
|
||||
$("#EstimateFirstSum").val(0); // 견적초기금액
|
||||
$("#EstimateUpdatetSum").val(0); // 수정금액
|
||||
$("#EstimateDiffer").val(0); // 차액금액
|
||||
var form = $('#board_form')[0];
|
||||
var datasource = new FormData(form);
|
||||
|
||||
const initialData = JSON.stringify([]); // 빈 배열로 재계산
|
||||
|
||||
$('#detailJson').val(initialData);
|
||||
|
||||
// 재계산된 데이터 저장 요청
|
||||
$.ajax({
|
||||
enctype: 'multipart/form-data',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
cache: false,
|
||||
timeout: 600000,
|
||||
url: "/estimate/insert_detail.php",
|
||||
type: "post",
|
||||
data: datasource,
|
||||
dataType: "json",
|
||||
success: function(response) {
|
||||
Swal.fire({
|
||||
title: '재계산 완료',
|
||||
text: "모든 데이터가 재계산되었습니다.",
|
||||
icon: 'success',
|
||||
confirmButtonText: '확인'
|
||||
}).then(() => {
|
||||
hideMsgModal();
|
||||
// 페이지 새로고침
|
||||
location.reload();
|
||||
});
|
||||
},
|
||||
error: function(jqxhr, status, error) {
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: "재계산 중 오류가 발생했습니다.",
|
||||
icon: 'error',
|
||||
confirmButtonText: '확인'
|
||||
});
|
||||
console.log("AJAX Error: ", status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
102
estimate/common/compare_price_edit_table.php
Normal file
102
estimate/common/compare_price_edit_table.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . "/session.php");
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . "/estimate/fetch_unitprice.php");
|
||||
$pdo = db_connect();
|
||||
|
||||
// 스크린 원단 가격
|
||||
$price_screen = 0;
|
||||
|
||||
// 절곡판 단가 4종
|
||||
$price_egi_1_2t = 0; // EGI 1.15T, 1.2T
|
||||
$price_egi_1_6t = 0; // EGI 1.55T, 1.6T
|
||||
$price_sus_1_2t = 0;
|
||||
$price_sus_1_5t = 0;
|
||||
|
||||
// price_raw_materials 테이블에서 스크린 실리카 가격
|
||||
$query = "SELECT itemList FROM {$DB}.price_raw_materials WHERE is_deleted IS NULL OR is_deleted = 0 ORDER BY num DESC LIMIT 1";
|
||||
$stmt = $pdo->prepare($query);
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($row) {
|
||||
$itemList = json_decode($row['itemList'], true);
|
||||
$price_screen = slatPrice($itemList, '스크린', '실리카');
|
||||
}
|
||||
|
||||
// BDmodels 테이블에서 절곡판 단가 4종 로딩
|
||||
$query = "SELECT * FROM {$DB}.BDmodels WHERE seconditem = '절곡판' AND is_deleted IS NULL ORDER BY num DESC";
|
||||
$stmt = $pdo->prepare($query);
|
||||
$stmt->execute();
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$spec = $row['spec'] ?? '';
|
||||
$finishing = $row['finishing_type'] ?? '';
|
||||
$unitprice = intval(str_replace(',', '', $row['unitprice'] ?? 0));
|
||||
|
||||
if (in_array($spec, ['1.15T', '1.2T']) && $finishing === 'EGI') $price_egi_1_2t = $unitprice;
|
||||
if (in_array($spec, ['1.55T', '1.6T']) && $finishing === 'EGI') $price_egi_1_6t = $unitprice;
|
||||
if ($spec === '1.2T' && $finishing === 'SUS') $price_sus_1_2t = $unitprice;
|
||||
if ($spec === '1.5T' && $finishing === 'SUS') $price_sus_1_5t = $unitprice;
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="container mt-2">
|
||||
<h5 class="text-center mb-2">단가 수정 테이블 (스크린 / 절곡판)</h5>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered text-center align-middle table-sm">
|
||||
<thead class="table-secondary">
|
||||
<tr>
|
||||
<th>항목명</th>
|
||||
<th>단가 (원)</th>
|
||||
<th>단가 적용</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>스크린 원단 (㎡)</td>
|
||||
<td><input type="text" class="form-control text-end price-input" id="price_screen" value="<?= number_format($price_screen) ?>"></td>
|
||||
<td><button type="button" class="btn btn-outline-primary btn-sm apply-price" data-target="price_screen">적용</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>EGI 1.15T / 1.2T</td>
|
||||
<td><input type="text" class="form-control text-end price-input" id="price_egi_1_2t" value="<?= number_format($price_egi_1_2t) ?>"></td>
|
||||
<td><button type="button" class="btn btn-outline-primary btn-sm apply-price" data-target="price_egi_1_2t">적용</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>EGI 1.55T / 1.6T</td>
|
||||
<td><input type="text" class="form-control text-end price-input" id="price_egi_1_6t" value="<?= number_format($price_egi_1_6t) ?>"></td>
|
||||
<td><button type="button" class="btn btn-outline-primary btn-sm apply-price" data-target="price_egi_1_6t">적용</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SUS 1.2T</td>
|
||||
<td><input type="text" class="form-control text-end price-input" id="price_sus_1_2t" value="<?= number_format($price_sus_1_2t) ?>"></td>
|
||||
<td><button type="button" class="btn btn-outline-primary btn-sm apply-price" data-target="price_sus_1_2t">적용</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SUS 1.5T</td>
|
||||
<td><input type="text" class="form-control text-end price-input" id="price_sus_1_5t" value="<?= number_format($price_sus_1_5t) ?>"></td>
|
||||
<td><button type="button" class="btn btn-outline-primary btn-sm apply-price" data-target="price_sus_1_5t">적용</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.apply-price').on('click', function () {
|
||||
const targetId = $(this).data('target');
|
||||
const price = parseFloat($('#' + targetId).val().replace(/,/g, '')) || 0;
|
||||
|
||||
$('.unit-price-input').each(function () {
|
||||
const $this = $(this);
|
||||
if ($this.data('target') === targetId) {
|
||||
const row = $this.closest('tr');
|
||||
const su = parseFloat(row.find('.su-input').val().replace(/,/g, '')) || 0;
|
||||
const total = price * su;
|
||||
$this.val(price.toLocaleString());
|
||||
row.find('.total-price').text(total.toLocaleString());
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
207
estimate/common/estimate_compare_head.php
Normal file
207
estimate/common/estimate_compare_head.php
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php include $_SERVER['DOCUMENT_ROOT'] . '/load_header.php'; ?>
|
||||
<title> <?=$title_message?> </title>
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
$num = isset($_REQUEST['num']) ? $_REQUEST['num'] : '';
|
||||
$option = isset($_REQUEST['option']) ? $_REQUEST['option'] : ''; // 견적서와 산출서의 다른점을 표현하는 것
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . "/estimate/fetch_unitprice.php");
|
||||
$pdo = db_connect();
|
||||
try {
|
||||
$sql = "select * from {$DB}.{$tablename} where num = ? ";
|
||||
$stmh = $pdo->prepare($sql);
|
||||
$stmh->bindValue(1, $num, PDO::PARAM_STR);
|
||||
$stmh->execute();
|
||||
$count = $stmh->rowCount();
|
||||
if ($count < 1) {
|
||||
print "검색결과가 없습니다.<br>";
|
||||
} else {
|
||||
$row = $stmh->fetch(PDO::FETCH_ASSOC);
|
||||
include "_row.php";
|
||||
|
||||
// $korean = number_to_korean($totalprice);
|
||||
}
|
||||
} catch (PDOException $Exception) {
|
||||
print "오류: " . $Exception->getMessage();
|
||||
}
|
||||
// JSON 문자열을 PHP 배열로 디코딩합니다.
|
||||
if($major_category == '스크린')
|
||||
$decodedEstimateList = json_decode($estimateList, true);
|
||||
else
|
||||
$decodedEstimateList = json_decode($estimateSlatList, true);
|
||||
|
||||
|
||||
// 디코딩된 데이터가 배열인지 확인합니다.
|
||||
if (!is_array($decodedEstimateList)) {
|
||||
echo "데이터가 정상적이지 않습니다. 확인바랍니다.";
|
||||
exit;
|
||||
}
|
||||
|
||||
echo '<script>';
|
||||
echo 'var dataList = ' . json_encode($detailJson ?? []) . ';';
|
||||
echo '</script>';
|
||||
|
||||
$shutterboxMsg ='';
|
||||
|
||||
?>
|
||||
|
||||
<form id="board_form" name="board_form" method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" id="mode" name="mode" value="<?= isset($mode) ? $mode : '' ?>">
|
||||
<input type="hidden" id="num" name="num" value="<?= isset($num) ? $num : '' ?>">
|
||||
<input type="hidden" id="user_name" name="user_name" value="<?= isset($user_name) ? $user_name : '' ?>">
|
||||
<input type="hidden" id="update_log" name="update_log" value="<?= isset($update_log) ? $update_log : null ?>">
|
||||
<input type="hidden" id="tablename" name="tablename" value="<?= isset($tablename) ? $tablename : '' ?>">
|
||||
<input type="hidden" id="header" name="header" value="<?= isset($header) ? $header : '' ?>">
|
||||
<input type="hidden" id="detailJson" name="detailJson"> <!-- 라디오버튼 저장하려면 두개의 변수가 필요하다. -->
|
||||
<input type="hidden" id="estimateSurang" name="estimateSurang"> <!-- 견적가 수량 -->
|
||||
<input type="hidden" id="estimateTotal" name="estimateTotal"> <!-- 견적가 총액 -->
|
||||
|
||||
<div class="container-fluid mt-2">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="d-flex align-items-center justify-content-center mt-3 m-1 mb-4">
|
||||
<span class="badge bg-secondary me-5 fs-5"> <?=$major_category?> 인정제품 견적비교 </span>
|
||||
<button type="button" class="btn btn-dark btn-sm me-1 ms-1" onclick='location.reload();' > <i class="bi bi-arrow-clockwise"></i> </button>
|
||||
<span style="display:none;">
|
||||
<input type="checkbox" id="showEstimateCheckbox" <?php echo ($option == 'option') ? '' : 'checked'; ?>>
|
||||
<label for="showEstimateCheckbox" class="me-3">산출내역서 보이기</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<div class="d-flex align-items-center justify-content-end mt-3 m-1 mb-4">
|
||||
<button type="button" class="btn btn-secondary btn-sm ms-5" onclick="self.close();"> × 닫기 </button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="d-flex align-items-center justify-content-center mt-2 mb-0">
|
||||
<div class="alert alert-primary mb-2" role="alert">
|
||||
검사비는 제주도를 제외하고, 5만원으로 수정, '산출내역 저장'버튼을 누른 후 저장해야 금액이 확정됩니다.
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-end mt-0">
|
||||
<table class="table table-bordered mt-0 text-end w-auto">
|
||||
<thead class="table-secondary">
|
||||
<tr>
|
||||
<th class="text-center">최초 자동금액</th>
|
||||
<th class="text-center">수정 금액</th>
|
||||
<th class="text-center">차액</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="text" id="EstimateFirstSum" name="EstimateFirstSum" class="form-control text-end" readonly
|
||||
value="<?= isset($EstimateFirstSum) && $EstimateFirstSum != 0 ? number_format($EstimateFirstSum) : $EstimateFirstSum ?>">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" id="EstimateUpdatetSum" name="EstimateUpdatetSum" class="form-control text-end text-primary" readonly
|
||||
value="<?= isset($EstimateUpdatetSum) && $EstimateUpdatetSum != 0 ? number_format($EstimateUpdatetSum) : $EstimateUpdatetSum ?>">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" id="EstimateDiffer" name="EstimateDiffer" class="form-control text-end text-danger" readonly
|
||||
value="<?= isset($EstimateDiffer) && $EstimateDiffer != 0 ? number_format($EstimateDiffer) : $EstimateDiffer ?>">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="content-to-print">
|
||||
<br>
|
||||
<div class="container-fluid mt-1">
|
||||
<div class="d-flex align-items-center justify-content-center ">
|
||||
<table class="table table-sm" style="border-collapse: collapse;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center fw-bold" style="width:8%;" >작성일자</td>
|
||||
<td rowspan="2" class="text-center align-middle fw-bold fs-4" style="width:60%; border-top:none; border-bottom:none;" >
|
||||
<?php
|
||||
if($option!=='option')
|
||||
echo '<span class="badge bg-primary"> ' . $title_message . ' </span> </td>'; // <span id="updateText" class="text-danger"> </span>
|
||||
else
|
||||
echo '<span class="text-dark"> ' . $title_message_sub . ' </span> </td>';
|
||||
?>
|
||||
|
||||
<td class="text-center fw-bold" style="width:10%;" >견적번호</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center" > <?=$indate?> </td>
|
||||
<td class="text-center fw-bold text-primary" > <?=$pjnum?> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="d-flex align-items-center justify-content-center ">
|
||||
<table class="table" style="border-collapse: collapse;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center fw-bold yellowBold " style="width:10%;">업체명</td>
|
||||
<td class="text-center yellowBold " style="width:40%;"> <?=$secondord?> (귀하) </td>
|
||||
<td rowspan="5" class="text-center align-middle fw-bold" style="width:5%; border-top:none; border-bottom:none;" >공 급 자</td>
|
||||
<td class="text-center fw-bold lightgray " > 상호 </td>
|
||||
<td class="text-center fw-bold" colspan="3" >㈜ 경동기업 </td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center fw-bold">제품명</td>
|
||||
<td class="text-center" > <?=$subTitle?> </td>
|
||||
<td class="text-center fw-bold lightgray " style="width:10%;" >등록번호</td>
|
||||
<td class="text-center" style="width:10%;"> 139-87-00333 </td>
|
||||
<td class="text-center fw-bold lightgray " >대표자</td>
|
||||
<td class="text-center fw-bold">
|
||||
<div class="d-flex align-items-center justify-content-center ">
|
||||
이 경 호
|
||||
<!-- <img src="../img/daehanstamp.png" alt="도장" style="width:45px; height:45px;"> -->
|
||||
</div>
|
||||
</td>
|
||||
<tr>
|
||||
<td class="text-center fw-bold">현장명</td>
|
||||
<td class="text-center fw-bold" > <?=$outworkplace?></td>
|
||||
<td class="text-center fw-bold lightgray " > 사업장주소 </td>
|
||||
<td colspan="3" class="text-center"> 경기도 김포시 통진읍 옹정로 45-22</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center fw-bold">담당자</td>
|
||||
<td class="text-center"><?=$secondordman?></td>
|
||||
<td class="text-center fw-bold lightgray " > 업 태 </td>
|
||||
<td class="text-center" > 제조업 </td>
|
||||
<td class="text-center fw-bold lightgray " >종목</td>
|
||||
<td class="text-center" > 방화셔터, 금속창호 </td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center fw-bold">연락처</td>
|
||||
<td class="text-center"><?=$secondordmantel?></td>
|
||||
<td class="text-center fw-bold lightgray " > TEL. </td>
|
||||
<td class="text-center" > 031-983-5130</td>
|
||||
<td class="text-center fw-bold lightgray " > FAX </td>
|
||||
<td class="text-center" > 02-6911-6315 </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-center ">
|
||||
<table class="table" style="border-collapse: collapse;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center fw-bold" style="width:250px;" >
|
||||
합계금액(부가세 별도) <br>
|
||||
아래와 같이 견적합니다
|
||||
</td>
|
||||
<td class="text-center align-middle fs-6 fw-bold" style="width:50px;"> 금 </td>
|
||||
<td rowspan="5" class="text-end align-middle fw-bold fs-6" style="width:500px;" > <span id="koreantotalsum"> </span> </td>
|
||||
<td class="text-center fw-bold align-middle fs-6" style="width:50px;" > 원 </td>
|
||||
<td class="align-middle text-end fs-6 fw-bold" style="width:250px;"> ( ₩ <span id="totalsum"> </span> )</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
224
estimate/common/estimate_head.php
Normal file
224
estimate/common/estimate_head.php
Normal file
@@ -0,0 +1,224 @@
|
||||
<?php include $_SERVER['DOCUMENT_ROOT'] . '/load_header.php'; ?>
|
||||
<title> <?=$title_message?> </title>
|
||||
<link rel="stylesheet" href="css/style.css?v=<?=time()?>">
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
$num = isset($_REQUEST['num']) ? $_REQUEST['num'] : '';
|
||||
$option = isset($_REQUEST['option']) ? $_REQUEST['option'] : ''; // 견적서와 산출서의 다른점을 표현하는 것
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . "/estimate/fetch_unitprice.php");
|
||||
$pdo = db_connect();
|
||||
try {
|
||||
$sql = "select * from {$DB}.{$tablename} where num = ? ";
|
||||
$stmh = $pdo->prepare($sql);
|
||||
$stmh->bindValue(1, $num, PDO::PARAM_STR);
|
||||
$stmh->execute();
|
||||
$count = $stmh->rowCount();
|
||||
if ($count < 1) {
|
||||
print "검색결과가 없습니다.<br>";
|
||||
} else {
|
||||
$row = $stmh->fetch(PDO::FETCH_ASSOC);
|
||||
include "_row.php";
|
||||
|
||||
// $korean = number_to_korean($totalprice);
|
||||
}
|
||||
} catch (PDOException $Exception) {
|
||||
print "오류: " . $Exception->getMessage();
|
||||
}
|
||||
// JSON 문자열을 PHP 배열로 디코딩합니다.
|
||||
if($major_category == '스크린')
|
||||
$decodedEstimateList = json_decode($estimateList, true);
|
||||
else
|
||||
$decodedEstimateList = json_decode($estimateSlatList, true);
|
||||
|
||||
|
||||
// 디코딩된 데이터가 배열인지 확인합니다.
|
||||
if (!is_array($decodedEstimateList)) {
|
||||
echo "데이터가 정상적이지 않습니다. 확인바랍니다.";
|
||||
exit;
|
||||
}
|
||||
|
||||
// detailJson 변수가 제대로 설정되었는지 확인
|
||||
if (!isset($detailJson) || empty($detailJson)) {
|
||||
$detailJson = [];
|
||||
}
|
||||
|
||||
echo '<script>';
|
||||
echo 'var detailJsonData = ' . json_encode($detailJson) . ';';
|
||||
echo '</script>';
|
||||
|
||||
$shutterboxMsg ='';
|
||||
|
||||
?>
|
||||
|
||||
<form id="board_form" name="board_form" method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" id="mode" name="mode" value="<?= isset($mode) ? $mode : '' ?>">
|
||||
<input type="hidden" id="num" name="num" value="<?= isset($num) ? $num : '' ?>">
|
||||
<input type="hidden" id="user_name" name="user_name" value="<?= isset($user_name) ? $user_name : '' ?>">
|
||||
<input type="hidden" id="update_log" name="update_log" value="<?= isset($update_log) ? $update_log : null ?>">
|
||||
<input type="hidden" id="tablename" name="tablename" value="<?= isset($tablename) ? $tablename : '' ?>">
|
||||
<input type="hidden" id="header" name="header" value="<?= isset($header) ? $header : '' ?>">
|
||||
<input type="hidden" id="secondordnum" name="secondordnum" value="<?= isset($secondordnum) ? $secondordnum : '' ?>">
|
||||
<input type="hidden" id="detailJson" name="detailJson"> <!-- 라디오버튼 저장하려면 두개의 변수가 필요하다. -->
|
||||
<input type="hidden" id="estimateSurang" name="estimateSurang"> <!-- 견적가 수량 -->
|
||||
<input type="hidden" id="estimateTotal" name="estimateTotal"> <!-- 견적가 총액 -->
|
||||
<input type="hidden" id="option" name="option" value="<?= isset($option) ? $option : '' ?>">
|
||||
|
||||
<div class="container mt-2">
|
||||
<div class="d-flex align-items-center justify-content-end mt-3 m-1 mb-4">
|
||||
<span class="badge bg-secondary me-5 fs-5"> ( <?=$major_category?> ) </span>
|
||||
<input type="checkbox" id="showlistCheckbox" <?php echo ($option == 'option') ? '' : 'checked'; ?>>
|
||||
<label for="showlistCheckbox" class="me-3">소요자재</label>
|
||||
|
||||
<input type="checkbox" id="showEstimateCheckbox" <?php echo ($option == 'option') ? '' : 'checked'; ?>>
|
||||
<label for="showEstimateCheckbox" class="me-3">산출내역서</label>
|
||||
|
||||
<input type="checkbox" id="showVendorCheckbox" <?php echo ($option == 'option') ? 'checked' : ''; ?>>
|
||||
<label for="showVendorCheckbox" class="me-3">업체발송용</label>
|
||||
|
||||
<button type="button" class="btn btn-danger btn-sm mx-1 initialBtn" > <i class="bi bi-arrow-clockwise"></i> 재계산 </button>
|
||||
<button type="button" class="btn btn-dark btn-sm mx-1 saveBtn" > <i class="bi bi-floppy"></i> 산출내역 저장 </button>
|
||||
<button type="button" class="btn btn-dark btn-sm mx-1" onclick="generatePDF()"> PDF 저장 </button>
|
||||
<button type="button" class="btn btn-dark btn-sm mx-1" onclick="sendmail();"> <i class="bi bi-envelope-arrow-up"></i> 전송 </button>
|
||||
<button type="button" class="btn btn-secondary btn-sm ms-5" onclick="self.close();"> × 닫기 </button>
|
||||
</div>
|
||||
<br>
|
||||
<div class="d-flex align-items-center justify-content-center mt-2 mb-0">
|
||||
<div class="alert alert-primary mb-2" role="alert">
|
||||
검사비는 제주도를 제외하고, 5만원으로 수정, '산출내역 저장'버튼을 누른 후 저장해야 금액이 확정됩니다.
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-end mt-0">
|
||||
<table class="table table-bordered mt-0 text-end w-auto">
|
||||
<thead >
|
||||
<tr>
|
||||
<th colspan="5" class="text-end">
|
||||
<span class="text-danger me-5"> 💬 수정금액이 있으면 자동금액보다 수정금액이 우선됨.</span>
|
||||
<span class="text-secondary">공급가액 기준, VAT별도</span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr class="table-secondary">
|
||||
<th class="text-center">자동금액</th>
|
||||
<th class="text-center">수정 금액</th>
|
||||
<th class="text-center">차액</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="text" id="EstimateFirstSum" name="EstimateFirstSum" class="form-control text-end" readonly
|
||||
value="<?= isset($EstimateFirstSum) && $EstimateFirstSum != 0 ? number_format($EstimateFirstSum) : $EstimateFirstSum ?>">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" id="EstimateUpdatetSum" name="EstimateUpdatetSum" class="form-control text-end text-primary" readonly
|
||||
value="<?= isset($EstimateUpdatetSum) && $EstimateUpdatetSum != 0 ? number_format($EstimateUpdatetSum) : $EstimateUpdatetSum ?>">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" id="EstimateDiffer" name="EstimateDiffer" class="form-control text-end text-danger" readonly
|
||||
value="<?= isset($EstimateDiffer) && $EstimateDiffer != 0 ? number_format($EstimateDiffer) : $EstimateDiffer ?>">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="content-to-print">
|
||||
<br>
|
||||
<div class="container mt-1">
|
||||
<div class="d-flex align-items-center justify-content-center ">
|
||||
<table class="table table-sm" style="border-collapse: collapse;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center fw-bold" style="width:8%;" >작성일자</td>
|
||||
<td rowspan="2" class="text-center align-middle fw-bold fs-4" style="width:60%; border-top:none; border-bottom:none;" >
|
||||
<?php
|
||||
if($option!=='option')
|
||||
echo '<span class="badge bg-primary"> ' . $title_message . ' </span> </td>'; // <span id="updateText" class="text-danger"> </span>
|
||||
else
|
||||
echo '<span class="text-dark"> ' . $title_message_sub . ' </span> </td>';
|
||||
?>
|
||||
|
||||
<td class="text-center fw-bold" style="width:10%;" >견적번호</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center" > <?=$indate?> </td>
|
||||
<td class="text-center fw-bold text-primary" > <?=$pjnum?> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="d-flex align-items-center justify-content-center ">
|
||||
<table class="table" style="border-collapse: collapse;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center fw-bold yellowBold " style="width:10%;">업체명</td>
|
||||
<td class="text-center yellowBold " style="width:40%;"> <?=$secondord?> (귀하) </td>
|
||||
<td rowspan="5" class="text-center align-middle fw-bold" style="width:5%; border-top:none; border-bottom:none;" >공 급 자</td>
|
||||
<td class="text-center fw-bold lightgray " > 상호 </td>
|
||||
<td class="text-center fw-bold" colspan="3" >㈜ 경동기업 </td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center fw-bold">제품명</td>
|
||||
<td class="text-center" > <?=$subTitle?> </td>
|
||||
<td class="text-center fw-bold lightgray " style="width:10%;" >등록번호</td>
|
||||
<td class="text-center" style="width:10%;"> 139-87-00333 </td>
|
||||
<td class="text-center fw-bold lightgray " >대표자</td>
|
||||
<td class="text-center fw-bold">
|
||||
<div class="d-flex align-items-center justify-content-center ">
|
||||
이 경 호
|
||||
<!-- <img src="../img/daehanstamp.png" alt="도장" style="width:45px; height:45px;"> -->
|
||||
</div>
|
||||
</td>
|
||||
<tr>
|
||||
<td class="text-center fw-bold">현장명</td>
|
||||
<td class="text-center fw-bold" > <?=$outworkplace?></td>
|
||||
<td class="text-center fw-bold lightgray " > 사업장주소 </td>
|
||||
<td colspan="3" class="text-center"> 경기도 김포시 통진읍 옹정로 45-22</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center fw-bold">담당자</td>
|
||||
<td class="text-center"><?=$secondordman?></td>
|
||||
<td class="text-center fw-bold lightgray " > 업 태 </td>
|
||||
<td class="text-center" > 제조업 </td>
|
||||
<td class="text-center fw-bold lightgray " >종목</td>
|
||||
<td class="text-center" > 방화셔터, 금속창호 </td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center fw-bold">연락처</td>
|
||||
<td class="text-center"><?=$secondordmantel?></td>
|
||||
<td class="text-center fw-bold lightgray " > TEL. </td>
|
||||
<td class="text-center" > 031-983-5130</td>
|
||||
<td class="text-center fw-bold lightgray " > FAX </td>
|
||||
<td class="text-center" > 02-6911-6315 </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-center ">
|
||||
<table class="table" style="border-collapse: collapse;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<!--
|
||||
<td class="text-center fw-bold" style="width:250px;" >
|
||||
합계금액(부가세 별도) <br>
|
||||
아래와 같이 견적합니다
|
||||
<span class="text-danger"> (VAT 별도) </span>
|
||||
</td>
|
||||
<td class="text-center align-middle fs-6 fw-bold" style="width:50px;"> 금 </td>
|
||||
<td rowspan="5" class="text-end align-middle fw-bold fs-6" style="width:500px;" > <span id="koreantotalsum"> </span> </td>
|
||||
<td class="text-center fw-bold align-middle fs-6" style="width:50px;" > 원 </td>
|
||||
-->
|
||||
<td colspan="3" class="align-middle text-end fs-6 fw-bold" style="width:250px;"> ( ₩ <span id="totalsum"> </span> )
|
||||
<span class="text-danger"> (VAT 별도) </span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
752
estimate/common/lastJS.php
Normal file
752
estimate/common/lastJS.php
Normal file
@@ -0,0 +1,752 @@
|
||||
<!-- 페이지 로딩 -->
|
||||
<script>
|
||||
var ajaxRequest_write = null;
|
||||
$(document).ready(function() {
|
||||
$('#loadingOverlay').hide(); // 로딩 오버레이 숨기기
|
||||
|
||||
var dataList = <?php echo json_encode($detailJson ?? []); ?>;
|
||||
|
||||
// JSON 데이터를 처리하기 전에 유효성 검사
|
||||
if (dataList && typeof dataList === 'string') {
|
||||
try {
|
||||
dataList = JSON.parse(dataList); // JSON 문자열을 객체로 변환
|
||||
} catch (e) {
|
||||
console.error('JSON parsing error: ', e);
|
||||
dataList = []; // 오류 발생 시 빈 배열로 초기화
|
||||
}
|
||||
}
|
||||
|
||||
// 배열인지 확인
|
||||
// console.log('dataList after JSON.parse check: ', dataList);
|
||||
if (!Array.isArray(dataList)) {
|
||||
dataList = [];
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#updateText").text('견적수정됨');
|
||||
}
|
||||
|
||||
// 테이블에 데이터 로드
|
||||
loadTableData('#detailTable', dataList);
|
||||
|
||||
// 셔터박스 오류메시지 화면에 표시
|
||||
var shutterboxMsg = <?= json_encode($shutterboxMsg) ?>;
|
||||
|
||||
// JavaScript로 처리
|
||||
if (shutterboxMsg) {
|
||||
var shutterboxDiv = document.getElementById("shutterboxMsg");
|
||||
shutterboxDiv.style.display = "block"; // 보이게 설정
|
||||
shutterboxDiv.innerHTML = shutterboxMsg; // 내용 설정
|
||||
}
|
||||
});
|
||||
|
||||
function loadTableData(tableBodySelector, dataList) {
|
||||
console.log('loadTableData data: ', dataList); // 여기서 데이터 확인
|
||||
|
||||
var tableBody = $(tableBodySelector); // 테이블 본문 선택
|
||||
|
||||
// 데이터를 반복하면서 테이블에 행을 업데이트
|
||||
var count = 1;
|
||||
dataList.forEach(function(rowData, index) {
|
||||
var row = tableBody.find('tr').eq(index + count); // index에 맞는 tr을 가져옴
|
||||
if (row.length) {
|
||||
updateRowData(row, rowData, index);
|
||||
}
|
||||
// console.log('index data: ', index); // 여기서 데이터 확인
|
||||
// console.log('rowData data: ', rowData); // 여기서 데이터 확인
|
||||
// count++;
|
||||
});
|
||||
}
|
||||
|
||||
// 기존 행을 수정하는 함수
|
||||
function updateRowData(row, rowData, rowIndex) {
|
||||
// 수정해야 할 td 요소들을 선택하여 해당 값을 업데이트
|
||||
row.find('.su-input').val(rowData[0]); // 수량
|
||||
row.find('.area-length-input').val(rowData[2]); // 길이
|
||||
row.find('.area-price-input').val(rowData[3]); // 면적단가
|
||||
row.find('.unit-price-input').val(rowData[4]); // 단가
|
||||
|
||||
// 수정된 행에 동적 계산 함수 호출
|
||||
calculateRowTotal(row); // 필요 시 계산 함수 호출
|
||||
}
|
||||
|
||||
|
||||
function inputNumber(input) {
|
||||
const value = input.value.replace(/,/g, ''); // 콤마 제거
|
||||
input.value = parseInt(value, 10).toLocaleString(); // 다시 콤마 추가
|
||||
calculateRowTotal($(input).closest('tr')); // 행의 합계 다시 계산
|
||||
}
|
||||
|
||||
// 이벤트 리스너 설정 및 계산 함수 호출
|
||||
function setupEventListeners() {
|
||||
document.querySelectorAll('input').forEach(input => {
|
||||
input.addEventListener('input', function () {
|
||||
const row = input.closest('tr');
|
||||
calculateRowTotal(row);
|
||||
calculateAllSubtotals();
|
||||
calculateGrandTotal();
|
||||
calculateRowTotalFirstTable();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 페이지 로드 시 초기 계산 및 이벤트 리스너 설정
|
||||
window.onload = function () {
|
||||
calculateAllSubtotals();
|
||||
calculateGrandTotal();
|
||||
setupEventListeners();
|
||||
calculateRowTotalFirstTable();
|
||||
};
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
var rowArray = <?= isset($row_array_json) ? $row_array_json : '[]' ?>;
|
||||
|
||||
if (Array.isArray(rowArray) && rowArray.length > 0) {
|
||||
rowArray.forEach(function (rowspanValue, index) {
|
||||
var cell = document.getElementById('dynamicRowspan-' + index);
|
||||
console.log('index', index);
|
||||
if (cell && rowspanValue > 0) {
|
||||
cell.setAttribute('rowspan', rowspanValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 숫자 포맷팅 함수 (콤마 추가 및 소수점 둘째자리에서 반올림)
|
||||
function formatNumber(value) {
|
||||
const roundedValue = value;
|
||||
// 콤마 추가 포맷팅
|
||||
return new Intl.NumberFormat().format(roundedValue);
|
||||
}
|
||||
|
||||
// 숫자에서 콤마를 제거하는 함수
|
||||
function cleanNumber(value) {
|
||||
// value가 null 또는 undefined인 경우 0을 반환하도록 처리
|
||||
if (!value) return 0;
|
||||
return parseFloat(value.replace(/,/g, '')) || 0;
|
||||
}
|
||||
|
||||
// 입력 필드에서 숫자를 포맷팅하는 함수
|
||||
function inputNumber(input) {
|
||||
const cursorPosition = input.selectionStart; // 현재 커서 위치를 저장
|
||||
const value = input.value.replace(/,/g, ''); // 입력값에서 숫자만 남기고 제거
|
||||
const formattedValue = Number(value).toLocaleString(); // 천 단위 콤마 추가
|
||||
input.value = formattedValue; // 포맷팅된 값으로 설정
|
||||
input.setSelectionRange(cursorPosition, cursorPosition); // 커서 위치 유지
|
||||
}
|
||||
|
||||
// 세부내역 행별 합계 계산
|
||||
function calculateRowTotal(row) {
|
||||
// row를 jQuery 객체로 변환
|
||||
row = $(row);
|
||||
|
||||
// jQuery 객체에서 값을 가져옴
|
||||
const itemNameInput = row.find('.item-name');
|
||||
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 ? cleanNumber(suInput.val()) : 1;
|
||||
const areaLength = areaLengthInput.length ? cleanNumber(areaLengthInput.val()) : 1;
|
||||
const areaPrice = areaPriceInput.length ? cleanNumber(areaPriceInput.val()) : 1;
|
||||
let unitPrice = unitPriceInput.length ? cleanNumber(unitPriceInput.val()) : 1;
|
||||
|
||||
const roundedAreaPrice = parseFloat(areaPrice);
|
||||
|
||||
if (roundedAreaPrice > 0) {
|
||||
unitPrice = Math.round(Math.round(areaLength * roundedAreaPrice )); // 소수점 첫째자리 반올림
|
||||
unitPriceInput.val(formatNumber(unitPrice)); // 단가 업데이트
|
||||
}
|
||||
|
||||
let totalPrice = 0;
|
||||
if (!areaLength && !areaPrice) {
|
||||
totalPrice = Math.round(Math.round((su * unitPrice) ) );
|
||||
} else if (areaLength && !areaPrice) {
|
||||
totalPrice = Math.round(Math.round((areaLength * unitPrice * su) ) );
|
||||
} else {
|
||||
totalPrice = Math.round(Math.round((su * unitPrice) ) );
|
||||
}
|
||||
|
||||
const totalCell = row.find('.total-price');
|
||||
if (totalCell.length) {
|
||||
if(totalPrice>200)
|
||||
totalCell.text(formatNumber(totalPrice));
|
||||
}
|
||||
|
||||
console.log('totalPrice', totalPrice);
|
||||
console.log('itemNameInput', itemNameInput.text());
|
||||
console.log('suInput', suInput.val());
|
||||
console.log('areaLengthInput', areaLengthInput.val());
|
||||
console.log('areaPriceInput', areaPriceInput.val());
|
||||
console.log('unitPriceInput', unitPriceInput.val());
|
||||
|
||||
// return Math.round( parseInt(totalCell.text().replace(/,/g, '')) );
|
||||
return totalPrice;
|
||||
}
|
||||
|
||||
|
||||
// 일련번호별 소계 계산 (jQuery 방식)
|
||||
function calculateSubtotalBySerial(serialNumber) {
|
||||
let subtotal = 0; // 첫행은 0부터 시작이고 1로 시작한다. 그래서 -1로 시작한다.
|
||||
const rows = $(`.calculation-row[data-serial="${serialNumber}"]`);
|
||||
|
||||
console.log('calculateSubtotalBySerial rows', rows);
|
||||
|
||||
rows.each(function() {
|
||||
// Math.round()는 소수점 이하를 반올림 (예: 1.4 -> 1, 1.5 -> 2)
|
||||
// Math.ceil()은 소수점 이하를 무조건 올림 (예: 1.1 -> 2, 1.9 -> 2)
|
||||
// Math.floor()는 소수점 이하를 무조건 버림 (예: 1.1 -> 1, 1.9 -> 1)
|
||||
const rowTotal = calculateRowTotal($(this)); // 300 이하는 일련번호로 취급한다. 안전장치이다.
|
||||
if (rowTotal > 300) {
|
||||
subtotal += rowTotal;
|
||||
}
|
||||
console.log(' calculateSubtotalBySerial subtotal', subtotal);
|
||||
});
|
||||
|
||||
const subtotalCells = $(`.subtotal-cell[data-serial="${serialNumber}"]`);
|
||||
if (subtotalCells.length > 0) {
|
||||
subtotalCells.each(function() {
|
||||
$(this).text(formatNumber(subtotal));
|
||||
console.log(' subtotalCells 계산 : ', subtotal);
|
||||
});
|
||||
} else {
|
||||
console.error(`소계 셀을 찾을 수 없습니다. 일련번호: ${serialNumber}`);
|
||||
}
|
||||
|
||||
return subtotal;
|
||||
}
|
||||
|
||||
// 모든 일련번호별 소계 및 총합계 계산 (jQuery 방식)
|
||||
function calculateAllSubtotals() {
|
||||
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;
|
||||
}
|
||||
|
||||
// 총합계 계산 (jQuery 방식)
|
||||
function calculateGrandTotal() {
|
||||
const grandTotal = calculateAllSubtotals();
|
||||
const grandTotalCells = $('.grand-total');
|
||||
|
||||
if (grandTotalCells.length > 0) {
|
||||
grandTotalCells.each(function() {
|
||||
$(this).text(formatNumber(grandTotal));
|
||||
console.log('각 grandTotal', grandTotal);
|
||||
});
|
||||
$('#totalsum').text(formatNumber(grandTotal));
|
||||
var EstimateFirstSum = $("#EstimateFirstSum").val();
|
||||
var EstimateUpdatetSum = $("#EstimateUpdatetSum").val();
|
||||
$('#koreantotalsum').text(KoreanNumber(Math.round(grandTotal)));
|
||||
// 총금액에서 최초금액 추출하기
|
||||
if(grandTotal > 0 && EstimateFirstSum < 1)
|
||||
$("#EstimateFirstSum").val(formatNumber(grandTotal));
|
||||
else
|
||||
$("#EstimateUpdatetSum").val(formatNumber(grandTotal));
|
||||
// 차액계산
|
||||
if(cleanNumber($("#EstimateUpdatetSum").val())> 0)
|
||||
$("#EstimateDiffer").val(formatNumber(cleanNumber($("#EstimateUpdatetSum").val()) - cleanNumber($("#EstimateFirstSum").val())));
|
||||
else
|
||||
$("#EstimateDiffer").val(0);
|
||||
|
||||
// console.log('grand total : ', grandTotal);
|
||||
} else {
|
||||
console.error("전체 합계 셀을 찾을 수 없습니다. '.grand-total'이라는 클래스가 올바르게 설정되었는지 확인해주세요.");
|
||||
}
|
||||
}
|
||||
|
||||
// 첫 번째 테이블: 행별 합계 계산 (jQuery 방식)
|
||||
function calculateRowTotalFirstTable() {
|
||||
const rows = $('.calculation-firstrow');
|
||||
|
||||
rows.each(function() {
|
||||
const suInput = $(this).find('.total-su-input'); // 수량 입력 필드
|
||||
const subtotalCell = $(this).find('.subtotal-cell'); // 소계 셀
|
||||
const unitPriceCell = $(this).find('.total-unit-price-input'); // 단가 셀
|
||||
|
||||
const su = suInput ? cleanNumber(suInput.text()) : 1;
|
||||
const subtotal = subtotalCell ? cleanNumber(subtotalCell.text()) : 0;
|
||||
|
||||
let unitPrice = 0;
|
||||
if (su > 0) {
|
||||
unitPrice = subtotal / su; // 소계를 수량으로 나눠서 단가 계산
|
||||
}
|
||||
|
||||
if (unitPriceCell.length) {
|
||||
unitPriceCell.text(formatNumber(unitPrice)); // 단가 셀에 표시
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function KoreanNumber(number) {
|
||||
const koreanNumbers = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'];
|
||||
const koreanUnits = ['', '십', '백', '천'];
|
||||
const bigUnits = ['', '만', '억', '조'];
|
||||
|
||||
let result = '';
|
||||
let unitIndex = 0;
|
||||
let numberStr = String(number);
|
||||
|
||||
// 숫자가 0인 경우 '영원'을 반환
|
||||
if (number == 0) return '영원';
|
||||
|
||||
// 뒤에서부터 4자리씩 끊어서 처리
|
||||
while (numberStr.length > 0) {
|
||||
let chunk = numberStr.slice(-4); // 마지막 4자리
|
||||
numberStr = numberStr.slice(0, -4); // 나머지 숫자
|
||||
|
||||
let chunkResult = '';
|
||||
for (let i = 0; i < chunk.length; i++) {
|
||||
const digit = parseInt(chunk[i]);
|
||||
if (digit > 0) {
|
||||
chunkResult += koreanNumbers[digit] + koreanUnits[chunk.length - i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
if (chunkResult) {
|
||||
result = chunkResult + bigUnits[unitIndex] + result;
|
||||
}
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
// 불필요한 '일십', '일백', '일천' 등의 단위를 제거하고 '원'을 붙여 반환
|
||||
result = result.replace(/일(?=십|백|천)/g, '').trim();
|
||||
|
||||
return result + '';
|
||||
}
|
||||
|
||||
function removeAllButLastOccurrence(string, target) {
|
||||
// 마지막 '만'의 위치를 찾습니다
|
||||
const lastPos = string.lastIndexOf(target);
|
||||
|
||||
// 마지막 '만'이 없으면 원래 문자열을 반환합니다
|
||||
if (lastPos === -1) {
|
||||
return string;
|
||||
}
|
||||
|
||||
// 마지막 '만'을 제외한 모든 '만'을 제거합니다
|
||||
const beforeLastPos = string.slice(0, lastPos);
|
||||
const afterLastPos = string.slice(lastPos);
|
||||
|
||||
// '만'을 빈 문자열로 대체합니다
|
||||
const result = beforeLastPos.replace(new RegExp(target, 'g'), '') + afterLastPos;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// 저장 버튼 클릭 시 saveData 함수 호출
|
||||
$(".saveBtn").click(function() {
|
||||
saveData();
|
||||
});
|
||||
});
|
||||
|
||||
function saveData() {
|
||||
const myform = document.getElementById('board_form');
|
||||
let allValid = true;
|
||||
|
||||
if (!allValid) return;
|
||||
|
||||
var num = $("#num").val();
|
||||
$("#overlay").show();
|
||||
$("button").prop("disabled", true);
|
||||
|
||||
// 모드 설정 (insert 또는 modify)
|
||||
if ($("#mode").val() !== 'copy') {
|
||||
if (Number(num) < 1) {
|
||||
$("#mode").val('insert');
|
||||
} else {
|
||||
$("#mode").val('modify');
|
||||
}
|
||||
} else {
|
||||
$("#mode").val('insert');
|
||||
}
|
||||
|
||||
// 데이터 수집 (input 요소만 저장)
|
||||
let formData = [];
|
||||
$('#detailTable tbody tr').each(function() {
|
||||
let rowData = [];
|
||||
// 각 tr의 input 요소 순서대로 처리
|
||||
$(this).find('input, select').each(function() {
|
||||
let value = $(this).val();
|
||||
rowData.push(value); // input 값을 배열에 순서대로 추가
|
||||
});
|
||||
formData.push(rowData); // 각 행의 input 데이터를 배열에 추가
|
||||
});
|
||||
|
||||
// formData는 이제 각 행의 input 값들만 포함하는 배열입니다.
|
||||
console.log('formData:', formData);
|
||||
|
||||
|
||||
// JSON 문자열로 변환하여 form input에 설정
|
||||
let jsonString = JSON.stringify(formData);
|
||||
$('#detailJson').val(jsonString);
|
||||
|
||||
// console.log('detailJson', jsonString);
|
||||
|
||||
$("#estimateSurang").val('<?= $estimateSurang ?>'); // 견적수량 저장
|
||||
$("#estimateTotal").val($("#subtotal").text()); // 견적총액 저장
|
||||
// console.log('$("#estimateSurang").val($("#subtotal").text())' , $("#estimateSurang").val());
|
||||
// console.log('$("#estimateTotal").val($("#subtotal").text())' , $("#estimateTotal").val());
|
||||
|
||||
var form = $('#board_form')[0];
|
||||
var datasource = new FormData(form);
|
||||
|
||||
if (ajaxRequest_write !== null) {
|
||||
ajaxRequest_write.abort();
|
||||
}
|
||||
|
||||
showMsgModal(2); // 파일저장중
|
||||
|
||||
// Ajax 요청으로 서버에 데이터 전송
|
||||
ajaxRequest_write = $.ajax({
|
||||
enctype: 'multipart/form-data',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
cache: false,
|
||||
timeout: 600000,
|
||||
url: "/estimate/insert_detail.php", // 산출내역 저장
|
||||
type: "post",
|
||||
data: datasource,
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
// console.log(data);
|
||||
setTimeout(function() {
|
||||
$(opener.location).attr("href", "javascript:restorePageNumber();");
|
||||
setTimeout(function() {
|
||||
hideMsgModal();
|
||||
hideOverlay();
|
||||
}, 1500);
|
||||
}, 1000);
|
||||
|
||||
},
|
||||
error: function(jqxhr, status, error) {
|
||||
console.log(jqxhr, status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function generatePDF() {
|
||||
var title_message = '<?php echo $title_message; ?>';
|
||||
var workplace = '<?php echo $outworkplace; ?>';
|
||||
var deadline = '<?php echo $indate; ?>';
|
||||
var deadlineDate = new Date(deadline);
|
||||
var formattedDate = "(" + String(deadlineDate.getFullYear()).slice(-2) + "." + ("0" + (deadlineDate.getMonth() + 1)).slice(-2) + "." + ("0" + deadlineDate.getDate()).slice(-2) + ")";
|
||||
var result = 'KD' + title_message + '(' + workplace +')' + formattedDate + '.pdf';
|
||||
|
||||
var element = document.getElementById('content-to-print');
|
||||
var opt = {
|
||||
margin: [10, 3, 12, 3], // Top, right, bottom, left margins
|
||||
filename: result,
|
||||
image: { type: 'jpeg', quality: 1 },
|
||||
html2canvas: {
|
||||
scale: 3, // ★★★ 해상도(기본은 1) → 2~4 정도로 높이면 선명해짐 4는 용량이 매우 큽니다. 보통 3M 이상
|
||||
useCORS: true,
|
||||
scrollY: 0,
|
||||
scrollX: 0,
|
||||
windowWidth: document.body.scrollWidth,
|
||||
windowHeight: document.body.scrollHeight
|
||||
},
|
||||
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
|
||||
pagebreak: {
|
||||
mode: ['css', 'legacy'],
|
||||
avoid: ['tr', '.avoid-break'] // 핵심
|
||||
}
|
||||
};
|
||||
html2pdf().from(element).set(opt).save();
|
||||
}
|
||||
|
||||
function generatePDF_server(callback) {
|
||||
var workplace = '<?php echo $title_message; ?>';
|
||||
var item = '<?php echo $emailTitle; ?>';
|
||||
var today = new Date();
|
||||
var formattedDate = "(" + String(today.getFullYear()).slice(-2) + "." + ("0" + (today.getMonth() + 1)).slice(-2) + "." + ("0" + today.getDate()).slice(-2) + ")";
|
||||
var result = 'KD' + item +'(' + workplace + ')' + formattedDate + '.pdf';
|
||||
|
||||
var element = document.getElementById('content-to-print');
|
||||
var opt = {
|
||||
margin: [10, 3, 12, 3], // Top, right, bottom, left margins
|
||||
filename: result,
|
||||
image: { type: 'jpeg', quality: 1 },
|
||||
html2canvas: { scale: 3 },
|
||||
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
|
||||
pagebreak: { mode: [''] }
|
||||
};
|
||||
|
||||
html2pdf().from(element).set(opt).output('datauristring').then(function (pdfDataUri) {
|
||||
var pdfBase64 = pdfDataUri.split(',')[1]; // Base64 인코딩된 PDF 데이터 추출
|
||||
var formData = new FormData();
|
||||
formData.append('pdf', pdfBase64);
|
||||
formData.append('filename', result);
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/email/save_pdf.php', // PDF 파일을 저장하는 PHP 파일
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
dataType: 'json', // JSON 응답을 기대함
|
||||
success: function (response) {
|
||||
console.log('PDF save response:', response);
|
||||
|
||||
if (response.success && callback) {
|
||||
callback(response.filename); // 서버에 저장된 파일 경로를 콜백으로 전달
|
||||
} else {
|
||||
Swal.fire('Error', response.error || 'PDF 저장에 실패했습니다.', 'error');
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error('PDF save error:', xhr.responseText);
|
||||
Swal.fire('Error', 'PDF 저장에 실패했습니다.', 'error');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var ajaxRequest = null;
|
||||
|
||||
function sendmail() {
|
||||
var secondordnum = '<?php echo $secondordnum; ?>'; // 서버에서 가져온 값
|
||||
var item = '<?php echo $emailTitle; ?>';
|
||||
|
||||
console.log('secondordnum : ', secondordnum);
|
||||
|
||||
if (!secondordnum) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: '오류 알림',
|
||||
text: '발주처 코드가 없습니다.'
|
||||
});
|
||||
return; // 함수 종료
|
||||
}
|
||||
|
||||
if (typeof ajaxRequest !== 'undefined' && ajaxRequest !== null) {
|
||||
ajaxRequest.abort();
|
||||
}
|
||||
|
||||
ajaxRequest = $.ajax({
|
||||
type: 'POST',
|
||||
url: '/email/get_companyCode.php',
|
||||
data: { secondordnum: secondordnum },
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
console.log('response : ', response);
|
||||
if (response.error) {
|
||||
Swal.fire('Error', response.error, 'error');
|
||||
} else {
|
||||
var email = response.email;
|
||||
var vendorName = response.vendor_name;
|
||||
|
||||
Swal.fire({
|
||||
title: 'E메일 보내기',
|
||||
text: vendorName + ' Email 주소확인',
|
||||
icon: 'warning',
|
||||
input: 'text', // input 창을 텍스트 필드로 설정
|
||||
inputLabel: 'Email 주소 수정 가능',
|
||||
inputValue: email, // 기존 이메일 주소를 기본값으로 설정
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '보내기',
|
||||
cancelButtonText: '취소',
|
||||
reverseButtons: true,
|
||||
inputValidator: (value) => {
|
||||
if (!value) {
|
||||
return '이메일 주소를 입력해주세요!';
|
||||
}
|
||||
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailPattern.test(value)) {
|
||||
return '올바른 이메일 형식을 입력해주세요!';
|
||||
}
|
||||
}
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
const updatedEmail = result.value; // 입력된 이메일 주소 가져오기
|
||||
generatePDF_server(function(filename) {
|
||||
sendEmail(updatedEmail, vendorName, item, filename);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
Swal.fire('Error', '전송중 오류가 발생했습니다.', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sendEmail(recipientEmail, vendorName, item, filename) {
|
||||
if (typeof ajaxRequest !== 'undefined' && ajaxRequest !== null) {
|
||||
ajaxRequest.abort();
|
||||
}
|
||||
var today = new Date();
|
||||
var formattedDate = "(" + String(today.getFullYear()).slice(-2) + "." + ("0" + (today.getMonth() + 1)).slice(-2) + "." + ("0" + today.getDate()).slice(-2) + ")";
|
||||
|
||||
ajaxRequest = $.ajax({
|
||||
type: 'POST',
|
||||
url: '/email/send_email_alternative.php', // 대안 이메일 전송 파일 사용
|
||||
data: { email: recipientEmail, vendorName: vendorName, filename: filename, item: item, formattedDate: formattedDate },
|
||||
dataType: 'json', // JSON 응답을 기대함
|
||||
success: function(response) {
|
||||
console.log('Email response:', response);
|
||||
|
||||
if (response.success) {
|
||||
Swal.fire('Success', response.message || '정상적으로 전송되었습니다.', 'success');
|
||||
} else {
|
||||
// 앱 비밀번호가 필요한 경우 안내
|
||||
if (response.error && response.error.includes('앱 비밀번호')) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: '앱 비밀번호 필요',
|
||||
text: '네이버에서 앱 비밀번호를 요구하고 있습니다. 설정 가이드를 확인해주세요.',
|
||||
confirmButtonText: '가이드 보기',
|
||||
showCancelButton: true,
|
||||
cancelButtonText: '취소'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
window.open('/email/naver_app_password_guide.php', '_blank');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Swal.fire('Error', response.error || '전송에 실패했습니다.', 'error');
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('Email send error:', xhr.responseText);
|
||||
Swal.fire('Error', '전송에 실패했습니다. 확인바랍니다.', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// 재계산 버튼 클릭 이벤트
|
||||
$('.initialBtn').on('click', function() {
|
||||
// 재계산 확인 알림
|
||||
Swal.fire({
|
||||
title: '견적데이터 재계산',
|
||||
text: "견적 데이터를 재계산 하시겠습니까?",
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: '예, 재계산합니다',
|
||||
cancelButtonText: '취소'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
|
||||
$("#estimateTotal").val(0); // 견적총액 저장
|
||||
$("#EstimateFirstSum").val(0); // 견적초기금액
|
||||
$("#EstimateUpdatetSum").val(0); // 수정금액
|
||||
$("#EstimateDiffer").val(0); // 차액금액
|
||||
var form = $('#board_form')[0];
|
||||
var datasource = new FormData(form);
|
||||
|
||||
const initialData = JSON.stringify([]); // 빈 배열로 재계산
|
||||
|
||||
$('#detailJson').val(initialData);
|
||||
|
||||
// 재계산된 데이터 저장 요청
|
||||
$.ajax({
|
||||
enctype: 'multipart/form-data',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
cache: false,
|
||||
timeout: 600000,
|
||||
url: "/estimate/insert_detail.php",
|
||||
type: "post",
|
||||
data: datasource,
|
||||
dataType: "json",
|
||||
success: function(response) {
|
||||
Swal.fire({
|
||||
title: '재계산 완료',
|
||||
text: "모든 데이터가 재계산되었습니다.",
|
||||
icon: 'success',
|
||||
confirmButtonText: '확인'
|
||||
}).then(() => {
|
||||
hideMsgModal();
|
||||
// 페이지 새로고침
|
||||
location.reload();
|
||||
});
|
||||
},
|
||||
error: function(jqxhr, status, error) {
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: "재계산 중 오류가 발생했습니다.",
|
||||
icon: 'error',
|
||||
confirmButtonText: '확인'
|
||||
});
|
||||
console.log("AJAX Error: ", status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$(document).ready(function() {
|
||||
// 산출내역서 보이기/숨기기
|
||||
function toggleEstimateView() {
|
||||
$('#showEstimateCheckbox').is(':checked')
|
||||
? $('.Estimateview').show()
|
||||
: $('.Estimateview').hide();
|
||||
}
|
||||
|
||||
// 소요자재 보이기/숨기기 (vendor-send 토글 로직 제거)
|
||||
function toggleListView() {
|
||||
$('#showlistCheckbox').is(':checked')
|
||||
? $('.listview, .vendor-send, .Novendor-send').show()
|
||||
: $('.listview, .vendor-send, .Novendor-send').hide();
|
||||
}
|
||||
|
||||
// 업체발송용 영역 토글
|
||||
function toggleVendorDiv() {
|
||||
// 업체발송용 클릭시 나머지 두개의 체크박스를 해제한다.
|
||||
$('#showEstimateCheckbox').prop('checked', false);
|
||||
$('#showlistCheckbox').prop('checked', false);
|
||||
toggleEstimateView();
|
||||
toggleListView();
|
||||
if ($('#showVendorCheckbox').is(':checked')) {
|
||||
$('.vendor-send').show();
|
||||
$('.Novendor-send').hide();
|
||||
} else {
|
||||
$('.vendor-send').hide();
|
||||
$('.Novendor-send').show();
|
||||
}
|
||||
}
|
||||
|
||||
var option = $('#option').val(); // option은 견적서 화면에서 업체발송용으로 강제 지정
|
||||
|
||||
if (option === 'option') {
|
||||
// 초기 상태 반영
|
||||
toggleEstimateView();
|
||||
toggleListView();
|
||||
toggleVendorDiv();
|
||||
}
|
||||
|
||||
// 산출내역서 체크박스 이벤트
|
||||
$('#showEstimateCheckbox').on('change', toggleEstimateView);
|
||||
|
||||
// 소요자재 체크박스 이벤트
|
||||
$('#showlistCheckbox').on('change', function() {
|
||||
toggleListView();
|
||||
|
||||
// 소요자재 클릭 시 "업체발송용" 체크 해제 + 영역 토글
|
||||
$('#showVendorCheckbox').prop('checked', false);
|
||||
});
|
||||
|
||||
// 업체발송용 체크박스 이벤트
|
||||
$('#showVendorCheckbox').on('change', toggleVendorDiv);
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
263
estimate/common/output_head.php
Normal file
263
estimate/common/output_head.php
Normal file
@@ -0,0 +1,263 @@
|
||||
<?php include $_SERVER['DOCUMENT_ROOT'] . '/load_header.php';
|
||||
// 수주내역에서 거래명세표 표시
|
||||
$selectWork = '거래명세표';
|
||||
?>
|
||||
<title> <?=$title_message?> </title>
|
||||
<link rel="stylesheet" href="css/style.css?v=<?=time()?>">
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
$num = isset($_REQUEST['num']) ? $_REQUEST['num'] : '';
|
||||
// echo 'num: ' . $num . '<br>';
|
||||
// echo 'DB: ' . $DB . '<br>';
|
||||
// echo 'tablename: ' . $tablename . '<br>';
|
||||
$option = isset($_REQUEST['option']) ? $_REQUEST['option'] : 'option'; // 기본 option 견적서와 산출서의 다른점을 표현하는 것
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . "/estimate/fetch_unitprice.php");
|
||||
$pdo = db_connect();
|
||||
try {
|
||||
$sql = "select * from {$DB}.{$tablename} where num = ? ";
|
||||
$stmh = $pdo->prepare($sql);
|
||||
$stmh->bindValue(1, $num, PDO::PARAM_STR);
|
||||
$stmh->execute();
|
||||
$count = $stmh->rowCount();
|
||||
if ($count < 1) {
|
||||
print "검색결과가 없습니다.<br>";
|
||||
} else {
|
||||
$row = $stmh->fetch(PDO::FETCH_ASSOC);
|
||||
include $_SERVER['DOCUMENT_ROOT'] . "/output/_row.php";
|
||||
|
||||
// output_extra 불러오기
|
||||
$sql_extra = "SELECT * FROM {$DB}.output_extra WHERE parent_num = ?";
|
||||
$stmh_extra = $pdo->prepare($sql_extra);
|
||||
$stmh_extra->bindValue(1, $num, PDO::PARAM_STR);
|
||||
$stmh_extra->execute();
|
||||
$row_extra = $stmh_extra->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($row_extra) {
|
||||
include $_SERVER['DOCUMENT_ROOT'] . "/output/_row_extra.php";
|
||||
}
|
||||
|
||||
// echo '<pre> ';
|
||||
// print_r($estimateSlatList);
|
||||
// echo '</pre>';
|
||||
}
|
||||
} catch (PDOException $Exception) {
|
||||
print "오류: " . $Exception->getMessage();
|
||||
}
|
||||
// JSON 문자열을 PHP 배열로 디코딩합니다.
|
||||
if($major_category == '스크린')
|
||||
// $decodedEstimateList = json_decode($estimateList, true); // 견적서의 표현
|
||||
$decodedEstimateList = json_decode($estimateList, true);
|
||||
if($major_category == '철재')
|
||||
//$decodedEstimateList = json_decode($estimateSlatList, true); // 견적서의 표현
|
||||
$decodedEstimateList = json_decode($estimateSlatList, true);
|
||||
|
||||
// 디코딩된 데이터가 배열인지 확인합니다.
|
||||
if (!is_array($decodedEstimateList)) {
|
||||
echo "데이터가 정상적이지 않습니다. 확인바랍니다.";
|
||||
exit;
|
||||
}
|
||||
|
||||
echo '<script>';
|
||||
echo 'var dataList = ' . json_encode($detailJson ?? []) . ';';
|
||||
echo '</script>';
|
||||
|
||||
$shutterboxMsg ='';
|
||||
|
||||
// print 'ET_total : ' . $ET_total;
|
||||
|
||||
?>
|
||||
|
||||
<form id="board_form" name="board_form" method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" id="mode" name="mode" value="<?= isset($mode) ? $mode : '' ?>">
|
||||
<input type="hidden" id="num" name="num" value="<?= isset($num) ? $num : '' ?>">
|
||||
<input type="hidden" id="user_name" name="user_name" value="<?= isset($user_name) ? $user_name : '' ?>">
|
||||
<input type="hidden" id="update_log" name="update_log" value="<?= isset($update_log) ? $update_log : null ?>">
|
||||
<input type="hidden" id="tablename" name="tablename" value="<?= isset($tablename) ? $tablename : '' ?>">
|
||||
<input type="hidden" id="header" name="header" value="<?= isset($header) ? $header : '' ?>">
|
||||
<input type="hidden" id="detailJson" name="detailJson"> <!-- 라디오버튼 저장하려면 두개의 변수가 필요하다. -->
|
||||
<input type="hidden" id="estimateSurang" name="estimateSurang" value="<?= isset($estimateSurang) ? $estimateSurang : '' ?>"> <!-- 견적가 수량 -->
|
||||
<input type="hidden" id="estimateTotal" name="estimateTotal" value="<?= isset($estimateTotal) ? $estimateTotal : '' ?>"> <!-- 인정제품 금액 -->
|
||||
<input type="hidden" id="ET_unapproved" name="ET_unapproved" value="<?= isset($ET_unapproved) ? $ET_unapproved : '' ?>"> <!-- 비인정제품 금액 -->
|
||||
<input type="hidden" id="ET_total" name="ET_total" value="<?= isset($ET_total) ? $ET_total : '' ?>"> <!-- 총 금액 -->
|
||||
<input type="hidden" id="option" name="option" value="<?= isset($option) ? $option : '' ?>">
|
||||
|
||||
<div class="container mt-2">
|
||||
<div class="d-flex align-items-center justify-content-end mt-3 m-1 mb-4">
|
||||
<span class="badge bg-secondary me-5 fs-5"> ( <?=$major_category?> 거래명세표) </span>
|
||||
<input type="checkbox" id="showlistCheckbox" <?php echo ($option == 'option') ? '' : ''; ?>>
|
||||
<label for="showlistCheckbox" class="me-3">소요자재 </label>
|
||||
<input type="checkbox" id="showEstimateCheckbox" <?php echo ($option == 'option') ? '' : ''; ?>>
|
||||
<label for="showEstimateCheckbox" class="me-3">산출내역서</label>
|
||||
<input type="checkbox" id="showVendorCheckbox" <?php echo ($option == 'option') ? 'checked' : ''; ?>>
|
||||
<label for="showVendorCheckbox" class="me-3">업체발송용</label>
|
||||
|
||||
<button type="button" class="btn btn-danger btn-sm me-1 ms-1 initialBtn" > <i class="bi bi-arrow-clockwise"></i> 재계산 </button>
|
||||
<button type="button" class="btn btn-dark btn-sm ms-1 me-1 saveBtn" > <i class="bi bi-floppy"></i> 산출내역 저장 </button>
|
||||
<button type="button" class="btn btn-dark btn-sm ms-1 me-1" onclick="generatePDF()"> PDF 저장 </button>
|
||||
<button type="button" class="btn btn-dark btn-sm me-1" onclick="sendmail();"> <i class="bi bi-envelope-arrow-up"></i> 전송 </button>
|
||||
<button type="button" class="btn btn-secondary btn-sm ms-5" onclick="self.close();"> × 닫기 </button>
|
||||
</div>
|
||||
<br>
|
||||
<div class="row">
|
||||
<div class="col-5">
|
||||
<div class="d-flex align-items-center justify-content-center mt-2 mb-0">
|
||||
<div class="alert alert-primary mb-2" role="alert">
|
||||
검사비는 제주도를 제외하고, 5만원으로 수정, <br> '산출내역 저장'버튼을 누른 후 저장해야 금액이 확정됩니다.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-7">
|
||||
<div class="d-flex align-items-center justify-content-end mt-0">
|
||||
<table class="table table-bordered mt-0 text-end">
|
||||
<thead >
|
||||
<tr>
|
||||
<th colspan="6" class="text-end">
|
||||
<div class="d-flex align-items-center justify-content-end">
|
||||
<span class="mx-5"></span>할인율 적용</span>
|
||||
<input type="text" id="EstimateDiscountRate" name="EstimateDiscountRate" class="form-control text-end w100px"
|
||||
value="<?= isset($EstimateDiscountRate) ? $EstimateDiscountRate : '' ?>">
|
||||
%
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="6" class="text-end">
|
||||
<span class="text-danger me-5"> 💬 수정금액이 있으면 자동금액보다 수정금액이 우선됨.</span>
|
||||
<span class="text-danger"> (VAT포함)</span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr class="table-secondary">
|
||||
<th class="text-center">자동 견적금액</th>
|
||||
<th class="text-center">수정 견적금액</th>
|
||||
<th class="text-center">견적 차액 </th>
|
||||
<th class="text-center">견적확정액 </th>
|
||||
<th class="text-center">할인금액</th>
|
||||
<th class="text-center">최종 결정금액</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="text" id="EstimateFirstSum" name="EstimateFirstSum" class="form-control text-end" readonly
|
||||
value="<?= isset($EstimateFirstSum) && $EstimateFirstSum != 0 ? number_format($EstimateFirstSum) : $EstimateFirstSum ?>">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" id="EstimateUpdatetSum" name="EstimateUpdatetSum" class="form-control text-end text-primary" readonly
|
||||
value="<?= isset($EstimateUpdatetSum) && $EstimateUpdatetSum != 0 ? number_format($EstimateUpdatetSum) : $EstimateUpdatetSum ?>">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" id="EstimateDiffer" name="EstimateDiffer" class="form-control text-end text-danger" readonly
|
||||
value="<?= isset($EstimateDiffer) && $EstimateDiffer != 0 ? number_format($EstimateDiffer) : $EstimateDiffer ?>">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" id="EstimateFixAmount" name="EstimateFixAmount" class="form-control text-end text-dark" readonly
|
||||
value="<?= isset($EstimateFixAmount) && $EstimateFixAmount != 0 ? number_format($EstimateFixAmount) : $EstimateFixAmount ?>">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" id="EstimateDiscount" name="EstimateDiscount" class="form-control text-end text-primary" readonly
|
||||
value="<?= isset($EstimateDiscount) && $EstimateDiscount != 0 ? number_format($EstimateDiscount) : $EstimateDiscount ?>">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" id="EstimateFinalSum" name="EstimateFinalSum" class="form-control text-end fw-bold text-success" readonly
|
||||
value="<?= isset($EstimateFinalSum) && $EstimateFinalSum != 0 ? number_format($EstimateFinalSum) : $EstimateFinalSum ?>">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="content-to-print">
|
||||
<br>
|
||||
<div class="container mt-1">
|
||||
<div class="d-flex align-items-center justify-content-center ">
|
||||
<table class="table table-sm" style="border-collapse: collapse;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center align-middle fw-bold fs-4" style="border-top:none; border-bottom:none;" >
|
||||
<?php
|
||||
if($option!=='option')
|
||||
echo '<span class="text-dark"> ' . $title_message . ' </span> </td>'; // <span id="updateText" class="text-danger"> </span>
|
||||
else
|
||||
echo '<span class="text-dark"> ' . $title_message_sub . ' </span> </td>';
|
||||
?>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-center ">
|
||||
<table class="table" style="border-collapse: collapse;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center fw-bold yellowBold " style="width:10%;">업체명</td>
|
||||
<td class="text-center yellowBold " style="width:40%;"> <?=$secondord?> (귀하) </td>
|
||||
<td rowspan="5" class="text-center align-middle fw-bold" style="width:5%; border-top:none; border-bottom:none;" >공 급 자</td>
|
||||
<td class="text-center fw-bold lightgray " > 상호 </td>
|
||||
<td class="text-center fw-bold" colspan="3" >㈜ 경동기업 </td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center fw-bold">제품명</td>
|
||||
<td class="text-center" > <?=$subTitle?> </td>
|
||||
<td class="text-center fw-bold lightgray " style="width:10%;" >등록번호</td>
|
||||
<td class="text-center" style="width:10%;"> 139-87-00333 </td>
|
||||
<td class="text-center fw-bold lightgray " >대표자</td>
|
||||
<td class="text-center fw-bold">
|
||||
<div class="d-flex align-items-center justify-content-center ">
|
||||
이 경 호
|
||||
<!-- <img src="../img/daehanstamp.png" alt="도장" style="width:45px; height:45px;"> -->
|
||||
</div>
|
||||
</td>
|
||||
<tr>
|
||||
<td class="text-center fw-bold">현장명</td>
|
||||
<td class="text-center fw-bold" > <?=$outworkplace?></td>
|
||||
<td class="text-center fw-bold lightgray " > 사업장주소 </td>
|
||||
<td colspan="3" class="text-center"> 경기도 김포시 통진읍 옹정로 45-22</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center fw-bold">담당자</td>
|
||||
<td class="text-center"><?=$secondordman?></td>
|
||||
<td class="text-center fw-bold lightgray " > 업 태 </td>
|
||||
<td class="text-center" > 제조업 </td>
|
||||
<td class="text-center fw-bold lightgray " >종목</td>
|
||||
<td class="text-center" > 방화셔터, 금속창호 </td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center fw-bold">연락처</td>
|
||||
<td class="text-center"><?=$secondordmantel?></td>
|
||||
<td class="text-center fw-bold lightgray " > TEL. </td>
|
||||
<td class="text-center" > 031-983-5130</td>
|
||||
<td class="text-center fw-bold lightgray " > FAX </td>
|
||||
<td class="text-center" > 02-6911-6315 </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-center ">
|
||||
<table class="table" style="border-collapse: collapse;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<!-- <td class="text-center fw-bold" style="width:250px;" >
|
||||
합계 금액(VAT포함) <br>
|
||||
아래와 같이 계산합니다.
|
||||
</td>
|
||||
<td class="text-center align-middle fs-6 fw-bold" style="width:50px;"> 금 </td>
|
||||
<td rowspan="5" class="text-end align-middle fw-bold fs-6" style="width:500px;" > <span id="koreantotalsum"> </span> </td>
|
||||
<td class="text-center fw-bold align-middle fs-6" style="width:50px;" > 원 </td>
|
||||
<td class="align-middle text-end fs-6 fw-bold" style="width:250px;"> ( ₩ <span id="totalsum"> </span> )</td> -->
|
||||
|
||||
<td colspan="3" class="align-middle text-end fs-6 fw-bold" style="width:250px;"> ( ₩ <span id="totalsum"> </span> )
|
||||
<!-- 거래명세표는 VAT 포함) -->
|
||||
<span class="text-danger"> (VAT 포함) </span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
933
estimate/common/output_lastJS.php
Normal file
933
estimate/common/output_lastJS.php
Normal file
@@ -0,0 +1,933 @@
|
||||
<script>
|
||||
// 견적서의 lastJS를 변형해서 수주내역에서 인정제품의 거래명세표를 저장하기 위함이다.
|
||||
// 수주리스트의 인정제품 거래명세표로 활용되는 코드
|
||||
var ajaxRequest_write = null;
|
||||
$(document).ready(function() {
|
||||
$('#loadingOverlay').hide(); // 로딩 오버레이 숨기기
|
||||
|
||||
var dataList = <?php echo json_encode($detailJson ?? []); ?>;
|
||||
|
||||
// JSON 데이터를 처리하기 전에 유효성 검사
|
||||
if (dataList && typeof dataList === 'string') {
|
||||
try {
|
||||
dataList = JSON.parse(dataList); // JSON 문자열을 객체로 변환
|
||||
} catch (e) {
|
||||
console.error('JSON parsing error: ', e);
|
||||
dataList = []; // 오류 발생 시 빈 배열로 초기화
|
||||
}
|
||||
}
|
||||
|
||||
// 배열인지 확인
|
||||
// console.log('dataList after JSON.parse check: ', dataList);
|
||||
if (!Array.isArray(dataList)) {
|
||||
dataList = [];
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#updateText").text('견적수정됨');
|
||||
}
|
||||
|
||||
// 테이블에 데이터 로드
|
||||
loadTableData('#detailTable', dataList);
|
||||
|
||||
// 셔터박스 오류메시지 화면에 표시
|
||||
var shutterboxMsg = <?= json_encode($shutterboxMsg) ?>;
|
||||
|
||||
// JavaScript로 처리
|
||||
if (shutterboxMsg) {
|
||||
var shutterboxDiv = document.getElementById("shutterboxMsg");
|
||||
shutterboxDiv.style.display = "block"; // 보이게 설정
|
||||
shutterboxDiv.innerHTML = shutterboxMsg; // 내용 설정
|
||||
}
|
||||
});
|
||||
|
||||
function loadTableData(tableBodySelector, dataList) {
|
||||
console.log('loadTableData data: ', dataList); // 여기서 데이터 확인
|
||||
|
||||
var tableBody = $(tableBodySelector); // 테이블 본문 선택
|
||||
|
||||
// 데이터를 반복하면서 테이블에 행을 업데이트
|
||||
var count = 1;
|
||||
dataList.forEach(function(rowData, index) {
|
||||
var row = tableBody.find('tr').eq(index + count); // index에 맞는 tr을 가져옴
|
||||
if (row.length) {
|
||||
updateRowData(row, rowData, index);
|
||||
}
|
||||
// console.log('index data: ', index); // 여기서 데이터 확인
|
||||
// console.log('rowData data: ', rowData); // 여기서 데이터 확인
|
||||
// count++;
|
||||
});
|
||||
}
|
||||
|
||||
// 기존 행을 수정하는 함수
|
||||
function updateRowData(row, rowData, rowIndex) {
|
||||
// 수정해야 할 td 요소들을 선택하여 해당 값을 업데이트
|
||||
row.find('.su-input').val(rowData[0]); // 수량
|
||||
row.find('.area-length-input').val(rowData[2]); // 길이
|
||||
row.find('.area-price-input').val(rowData[3]); // 면적단가
|
||||
row.find('.unit-price-input').val(rowData[4]); // 단가
|
||||
|
||||
// 수정된 행에 동적 계산 함수 호출
|
||||
calculateRowTotal(row); // 필요 시 계산 함수 호출
|
||||
}
|
||||
|
||||
|
||||
function inputNumber(input) {
|
||||
const value = input.value.replace(/,/g, ''); // 콤마 제거
|
||||
input.value = parseInt(value, 10).toLocaleString(); // 다시 콤마 추가
|
||||
calculateRowTotal($(input).closest('tr')); // 행의 합계 다시 계산
|
||||
}
|
||||
|
||||
// 이벤트 리스너 설정 및 계산 함수 호출
|
||||
function setupEventListeners() {
|
||||
document.querySelectorAll('input').forEach(input => {
|
||||
input.addEventListener('input', function () {
|
||||
const row = input.closest('tr');
|
||||
calculateRowTotal(row);
|
||||
calculateAllSubtotals();
|
||||
calculateGrandTotal();
|
||||
calculateRowTotalFirstTable();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 페이지 로드 시 초기 계산 및 이벤트 리스너 설정
|
||||
window.onload = function () {
|
||||
calculateAllSubtotals();
|
||||
calculateGrandTotal();
|
||||
setupEventListeners();
|
||||
updateDiscountFooterRow();
|
||||
calculateEstimateAmounts();
|
||||
calculateRowTotalFirstTable();
|
||||
};
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
var rowArray = <?= isset($row_array_json) ? $row_array_json : '[]' ?>;
|
||||
|
||||
if (Array.isArray(rowArray) && rowArray.length > 0) {
|
||||
rowArray.forEach(function (rowspanValue, index) {
|
||||
var cell = document.getElementById('dynamicRowspan-' + index);
|
||||
console.log('index', index);
|
||||
if (cell && rowspanValue > 0) {
|
||||
cell.setAttribute('rowspan', rowspanValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 숫자 포맷팅 함수 (콤마 추가 및 소수점 둘째자리에서 반올림)
|
||||
function formatNumber(value) {
|
||||
// 소수점 둘째 자리에서 반올림
|
||||
const roundedValue = value;
|
||||
// 콤마 추가 포맷팅
|
||||
return new Intl.NumberFormat().format(roundedValue);
|
||||
}
|
||||
|
||||
// 숫자에서 콤마를 제거하는 함수
|
||||
function cleanNumber(value) {
|
||||
// value가 null 또는 undefined인 경우 0을 반환하도록 처리
|
||||
if (!value) return 0;
|
||||
return parseFloat(value.replace(/,/g, '')) || 0;
|
||||
}
|
||||
|
||||
// 입력 필드에서 숫자를 포맷팅하는 함수
|
||||
function inputNumber(input) {
|
||||
const cursorPosition = input.selectionStart; // 현재 커서 위치를 저장
|
||||
const value = input.value.replace(/,/g, ''); // 입력값에서 숫자만 남기고 제거
|
||||
const formattedValue = Number(value).toLocaleString(); // 천 단위 콤마 추가
|
||||
input.value = formattedValue; // 포맷팅된 값으로 설정
|
||||
input.setSelectionRange(cursorPosition, cursorPosition); // 커서 위치 유지
|
||||
}
|
||||
|
||||
// 세부내역 행별 합계 계산
|
||||
function calculateRowTotal(row) {
|
||||
// row를 jQuery 객체로 변환
|
||||
row = $(row);
|
||||
|
||||
// jQuery 객체에서 값을 가져옴
|
||||
const itemNameInput = row.find('.item-name');
|
||||
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 ? cleanNumber(suInput.val()) : 1;
|
||||
const areaLength = areaLengthInput.length ? cleanNumber(areaLengthInput.val()) : 1;
|
||||
const areaPrice = areaPriceInput.length ? cleanNumber(areaPriceInput.val()) : 1;
|
||||
let unitPrice = unitPriceInput.length ? cleanNumber(unitPriceInput.val()) : 1;
|
||||
|
||||
const roundedAreaPrice = parseFloat(areaPrice);
|
||||
|
||||
if (roundedAreaPrice > 0) {
|
||||
unitPrice = Math.round(Math.round(areaLength * roundedAreaPrice )); // 소수점 첫째자리 반올림
|
||||
unitPriceInput.val(formatNumber(unitPrice)); // 단가 업데이트
|
||||
}
|
||||
|
||||
let totalPrice = 0;
|
||||
if (!areaLength && !areaPrice) {
|
||||
totalPrice = Math.round(Math.round((su * unitPrice) ) );
|
||||
} else if (areaLength && !areaPrice) {
|
||||
totalPrice = Math.round(Math.round((areaLength * unitPrice * su) ) );
|
||||
} else {
|
||||
totalPrice = Math.round(Math.round((su * unitPrice) ) );
|
||||
}
|
||||
|
||||
const totalCell = row.find('.total-price');
|
||||
if (totalCell.length) {
|
||||
if(totalPrice>200)
|
||||
totalCell.text(formatNumber(totalPrice));
|
||||
}
|
||||
|
||||
console.log('totalPrice', totalPrice);
|
||||
console.log('itemNameInput', itemNameInput.text());
|
||||
console.log('suInput', suInput.val());
|
||||
console.log('areaLengthInput', areaLengthInput.val());
|
||||
console.log('areaPriceInput', areaPriceInput.val());
|
||||
console.log('unitPriceInput', unitPriceInput.val());
|
||||
|
||||
// return Math.round( parseInt(totalCell.text().replace(/,/g, '')) );
|
||||
return totalPrice;
|
||||
}
|
||||
|
||||
// 계산식 첫테이블의 일련번호별 소계 계산 (jQuery 방식)
|
||||
function calculateSubtotalBySerial(serialNumber) {
|
||||
let subtotal = 0;
|
||||
let vat = 0;
|
||||
let total = 0;
|
||||
const rows = $(`.calculation-row[data-serial="${serialNumber}"]`);
|
||||
|
||||
rows.each(function() {
|
||||
// Math.round()는 소수점 이하를 반올림 (예: 1.4 -> 1, 1.5 -> 2)
|
||||
// Math.ceil()은 소수점 이하를 무조건 올림 (예: 1.1 -> 2, 1.9 -> 2)
|
||||
// Math.floor()는 소수점 이하를 무조건 버림 (예: 1.1 -> 1, 1.9 -> 1)
|
||||
const rowTotal = calculateRowTotal($(this)); // 300 이하는 일련번호로 취급한다. 안전장치이다.
|
||||
if (rowTotal > 300) {
|
||||
subtotal += rowTotal;
|
||||
}
|
||||
console.log(' calculateSubtotalBySerial subtotal', subtotal);
|
||||
});
|
||||
|
||||
vat = Math.round(subtotal * 0.1);
|
||||
total = subtotal + vat;
|
||||
|
||||
const subtotalCells = $(`.subtotal-cell[data-serial="${serialNumber}"]`);
|
||||
const vatCells = $(`.vat-cell[data-serial="${serialNumber}"]`);
|
||||
const totalCells = $(`.subtotalamount-cell[data-serial="${serialNumber}"]`);
|
||||
if (subtotalCells.length > 0) {
|
||||
subtotalCells.each(function() {
|
||||
$(this).text(formatNumber(subtotal));
|
||||
});
|
||||
} else {
|
||||
console.error(`소계 셀을 찾을 수 없습니다. 일련번호: ${serialNumber}`);
|
||||
}
|
||||
if (vatCells.length > 0) {
|
||||
vatCells.each(function() {
|
||||
$(this).text(formatNumber(vat));
|
||||
});
|
||||
}
|
||||
if (totalCells.length > 0) {
|
||||
totalCells.each(function() {
|
||||
$(this).text(formatNumber(total));
|
||||
});
|
||||
}
|
||||
return subtotal;
|
||||
}
|
||||
|
||||
// 모든 일련번호별 소계 및 총합계 계산 (jQuery 방식)
|
||||
function calculateAllSubtotals() {
|
||||
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;
|
||||
}
|
||||
|
||||
// 숫자만 남기는 헬퍼
|
||||
function cleanNumber(str) {
|
||||
return Number(String(str).replace(/[^0-9.-]/g, '')) || 0;
|
||||
}
|
||||
|
||||
// 3자리마다 콤마 찍되, 먼저 소수점 이하는 반올림 처리
|
||||
function formatNumber(num) {
|
||||
// 1) 문자열이면 콤마 없애고 숫자로 변환
|
||||
const value = typeof num === 'string'
|
||||
? parseFloat(num.replace(/,/g, ''))
|
||||
: num;
|
||||
|
||||
// 2) 소수점 이하 반올림
|
||||
const rounded = Math.round(value || 0);
|
||||
|
||||
// 3) 천 단위 구분자 추가
|
||||
return rounded.toLocaleString();
|
||||
}
|
||||
|
||||
|
||||
// 1) grand total 계산 후, 차액까지 계산
|
||||
function calculateGrandTotal() {
|
||||
// 소수점 이하를 반올림해서 정수 와 원 단위만 남김
|
||||
const rawTotal = calculateAllSubtotals();
|
||||
const rawVat = Math.round(rawTotal * 0.1);
|
||||
const grandTotal = rawTotal;
|
||||
const rawSumTotal = grandTotal + rawVat;
|
||||
|
||||
let finalTotal = 0;
|
||||
if (rawVat > 0) {
|
||||
finalTotal = rawSumTotal;
|
||||
} else {
|
||||
finalTotal = grandTotal;
|
||||
}
|
||||
|
||||
const grandTotalCells = $('.grand-total');
|
||||
if (grandTotalCells.length > 0) {
|
||||
grandTotalCells.each(function() {
|
||||
$(this).text(formatNumber(grandTotal));
|
||||
});
|
||||
$('#totalsum').text(formatNumber(finalTotal));
|
||||
// 한글 금액 표기도 반올림된 정수로
|
||||
$('#koreantotalsum').text(KoreanNumber(finalTotal));
|
||||
|
||||
const firstSum = cleanNumber($("#EstimateFirstSum").val());
|
||||
if (grandTotal > 0 && rawVat> 0 && firstSum < 1) {
|
||||
$("#EstimateFirstSum").val(formatNumber(finalTotal));
|
||||
} else {
|
||||
$("#EstimateUpdatetSum").val(formatNumber(finalTotal));
|
||||
}
|
||||
|
||||
const updatedSum = cleanNumber($("#EstimateUpdatetSum").val());
|
||||
if (updatedSum > 0) {
|
||||
$("#EstimateDiffer").val(formatNumber(updatedSum - cleanNumber($("#EstimateFirstSum").val())));
|
||||
} else {
|
||||
$("#EstimateDiffer").val(0);
|
||||
}
|
||||
|
||||
} else {
|
||||
console.error("전체 합계 셀을 찾을 수 없습니다. '.grand-total' 클래스 확인하세요.");
|
||||
}
|
||||
|
||||
let grandVatCells = $('.grand-vat');
|
||||
if (grandVatCells.length > 0) {
|
||||
grandVatCells.each(function() {
|
||||
$(this).text(formatNumber(rawVat));
|
||||
});
|
||||
} else {
|
||||
console.error("전체 합계 셀을 찾을 수 없습니다. '.grand-vat' 클래스 확인하세요.");
|
||||
}
|
||||
|
||||
let grandSumCells = $('.grand-sum');
|
||||
if (grandSumCells.length > 0) {
|
||||
grandSumCells.each(function() {
|
||||
$(this).text(formatNumber(rawSumTotal));
|
||||
});
|
||||
} else {
|
||||
console.error("전체 합계 셀을 찾을 수 없습니다. '.grand-sum' 클래스 확인하세요.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 2) '견적확정액', '할인금액', '최종결정금액' 계산 함수
|
||||
function calculateEstimateAmounts() {
|
||||
// 1) 자동견적(=EstimateFirstSum) / 수동견적(=EstimateUpdatetSum) 값 가져오기
|
||||
const autoSum = cleanNumber($("#EstimateFirstSum").val());
|
||||
let manualSum = cleanNumber($("#EstimateUpdatetSum").val());
|
||||
|
||||
// ★ 수동견적과 자동견적이 같으면 수동견적을 지워서 나오지 않게
|
||||
if (manualSum > 0 && manualSum === autoSum) {
|
||||
manualSum = 0;
|
||||
$("#EstimateUpdatetSum").val('');
|
||||
}
|
||||
|
||||
// 2) 견적확정액 결정: 수동견적 있으면 수동, 없으면 자동
|
||||
const fixAmount = manualSum > 0 ? manualSum : autoSum;
|
||||
$("#EstimateFixAmount").val(formatNumber(fixAmount));
|
||||
|
||||
// 3) 할인금액 = 견적확정액 × 할인율 ÷ 100
|
||||
const discountRate = $("#EstimateDiscountRate").val();
|
||||
let discountAmt = fixAmount * discountRate / 100 ;
|
||||
|
||||
const subtotal = cleanNumber($('#subtotalamount').text());
|
||||
|
||||
let finalTotal = 0;
|
||||
let finalRounded = 0;
|
||||
if (discountRate > 0 ) {
|
||||
finalTotal = subtotal - discountAmt;
|
||||
// 1000원 미만 절사
|
||||
finalRounded = Math.floor(finalTotal / 1000) * 1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
finalTotal = subtotal ;
|
||||
finalRounded = finalTotal ;
|
||||
}
|
||||
|
||||
discountAmt += (finalTotal - finalRounded);
|
||||
|
||||
$("#EstimateDiscount").val(formatNumber(discountAmt));
|
||||
|
||||
// 4) 최종결정금액 = 견적확정액 – 할인금액
|
||||
const finalSum = fixAmount - discountAmt;
|
||||
$("#EstimateFinalSum").val(formatNumber(finalSum));
|
||||
|
||||
// → 할인/최종 합계 row 갱신
|
||||
updateDiscountFooterRow();
|
||||
|
||||
}
|
||||
|
||||
// 첫 번째 테이블: 행별 합계 계산 (jQuery 방식)
|
||||
function calculateRowTotalFirstTable() {
|
||||
const rows = $('.calculation-firstrow');
|
||||
|
||||
rows.each(function() {
|
||||
const suInput = $(this).find('.total-su-input'); // 수량 입력 필드
|
||||
const subtotalCell = $(this).find('.subtotal-cell'); // 소계 셀
|
||||
const unitPriceCell = $(this).find('.total-unit-price-input'); // 단가 셀
|
||||
|
||||
const su = suInput ? cleanNumber(suInput.text()) : 1;
|
||||
const subtotal = subtotalCell ? cleanNumber(subtotalCell.text()) : 0;
|
||||
|
||||
let unitPrice = 0;
|
||||
if (su > 0) {
|
||||
unitPrice = subtotal / su; // 소계를 수량으로 나눠서 단가 계산
|
||||
}
|
||||
|
||||
if (unitPriceCell.length) {
|
||||
unitPriceCell.text(formatNumber(unitPrice)); // 단가 셀에 표시
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function KoreanNumber(number) {
|
||||
const koreanNumbers = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'];
|
||||
const koreanUnits = ['', '십', '백', '천'];
|
||||
const bigUnits = ['', '만', '억', '조'];
|
||||
|
||||
let result = '';
|
||||
let unitIndex = 0;
|
||||
let numberStr = String(number);
|
||||
|
||||
// 숫자가 0인 경우 '영원'을 반환
|
||||
if (number == 0) return '영원';
|
||||
|
||||
// 뒤에서부터 4자리씩 끊어서 처리
|
||||
while (numberStr.length > 0) {
|
||||
let chunk = numberStr.slice(-4); // 마지막 4자리
|
||||
numberStr = numberStr.slice(0, -4); // 나머지 숫자
|
||||
|
||||
let chunkResult = '';
|
||||
for (let i = 0; i < chunk.length; i++) {
|
||||
const digit = parseInt(chunk[i]);
|
||||
if (digit > 0) {
|
||||
chunkResult += koreanNumbers[digit] + koreanUnits[chunk.length - i - 1];
|
||||
}
|
||||
}
|
||||
if (chunkResult) {
|
||||
result = chunkResult + bigUnits[unitIndex] + result;
|
||||
}
|
||||
unitIndex++;
|
||||
}
|
||||
// 불필요한 '일십', '일백', '일천' 등의 단위를 제거하고 '원'을 붙여 반환
|
||||
result = result.replace(/일(?=십|백|천)/g, '').trim();
|
||||
return result + '';
|
||||
}
|
||||
|
||||
function removeAllButLastOccurrence(string, target) {
|
||||
// 마지막 '만'의 위치를 찾습니다
|
||||
const lastPos = string.lastIndexOf(target);
|
||||
|
||||
// 마지막 '만'이 없으면 원래 문자열을 반환합니다
|
||||
if (lastPos === -1) {
|
||||
return string;
|
||||
}
|
||||
|
||||
// 마지막 '만'을 제외한 모든 '만'을 제거합니다
|
||||
const beforeLastPos = string.slice(0, lastPos);
|
||||
const afterLastPos = string.slice(lastPos);
|
||||
|
||||
// '만'을 빈 문자열로 대체합니다
|
||||
const result = beforeLastPos.replace(new RegExp(target, 'g'), '') + afterLastPos;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// 저장 버튼 클릭 시 saveData 함수 호출
|
||||
$(".saveBtn").click(function() {
|
||||
saveData();
|
||||
});
|
||||
});
|
||||
|
||||
function saveData() {
|
||||
const myform = document.getElementById('board_form');
|
||||
let allValid = true;
|
||||
|
||||
if (!allValid) return;
|
||||
|
||||
var num = $("#num").val();
|
||||
$("#overlay").show();
|
||||
$("button").prop("disabled", true);
|
||||
|
||||
// 모드 설정 (insert 또는 modify)
|
||||
if ($("#mode").val() !== 'copy') {
|
||||
if (Number(num) < 1) {
|
||||
$("#mode").val('insert');
|
||||
} else {
|
||||
$("#mode").val('modify');
|
||||
}
|
||||
} else {
|
||||
$("#mode").val('insert');
|
||||
}
|
||||
|
||||
// 데이터 수집 (input 요소만 저장)
|
||||
let formData = [];
|
||||
$('#detailTable tbody tr').each(function() {
|
||||
let rowData = [];
|
||||
// 각 tr의 input 요소 순서대로 처리
|
||||
$(this).find('input, select').each(function() {
|
||||
let value = $(this).val();
|
||||
rowData.push(value); // input 값을 배열에 순서대로 추가
|
||||
});
|
||||
formData.push(rowData); // 각 행의 input 데이터를 배열에 추가
|
||||
});
|
||||
|
||||
// formData는 이제 각 행의 input 값들만 포함하는 배열입니다.
|
||||
// console.log('formData:', formData);
|
||||
|
||||
// JSON 문자열로 변환하여 form input에 설정
|
||||
let jsonString = JSON.stringify(formData);
|
||||
$('#detailJson').val(jsonString);
|
||||
|
||||
// console.log('detailJson', jsonString);
|
||||
|
||||
|
||||
|
||||
$("#estimateSurang").val('<?= $estimateSurang ?>'); // 수량 저장
|
||||
$("#estimateTotal").val($("#EstimateFinalSum").val()); // 최종 결정금액 저장
|
||||
|
||||
// 총금액 계산 (콤마 제거 후 숫자로 변환)
|
||||
const unapproved = parseFloat(($("#ET_unapproved").val() || "0").replace(/,/g, ''));
|
||||
const approved = parseFloat(($("#estimateTotal").val() || "0").replace(/,/g, ''));
|
||||
const total = unapproved + approved;
|
||||
$("#ET_total").val(total.toLocaleString()); // 최종 결정금액 저장
|
||||
|
||||
if (window.opener && !window.opener.closed) {
|
||||
const $parent = window.opener.$; // 부모창의 jQuery 사용
|
||||
|
||||
// 수량은 있는 그대로
|
||||
if ($parent("#estimateSurang").length) {
|
||||
$parent("#estimateSurang").val($("#estimateSurang").val());
|
||||
}
|
||||
|
||||
// 금액들은 toLocaleString()으로 콤마 포함
|
||||
if ($parent("#estimateTotal").length) {
|
||||
$parent("#estimateTotal").val(approved.toLocaleString());
|
||||
}
|
||||
|
||||
if ($parent("#ET_unapproved").length) {
|
||||
$parent("#ET_unapproved").val(unapproved.toLocaleString());
|
||||
}
|
||||
|
||||
if ($parent("#ET_total").length) {
|
||||
$parent("#ET_total").val(total.toLocaleString());
|
||||
}
|
||||
}
|
||||
|
||||
var form = $('#board_form')[0];
|
||||
var datasource = new FormData(form);
|
||||
|
||||
if (ajaxRequest_write !== null) {
|
||||
ajaxRequest_write.abort();
|
||||
}
|
||||
|
||||
showMsgModal(2); // 파일저장중
|
||||
|
||||
// Ajax 요청으로 서버에 데이터 전송
|
||||
ajaxRequest_write = $.ajax({
|
||||
enctype: 'multipart/form-data',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
cache: false,
|
||||
timeout: 600000,
|
||||
url: "/estimate/insert_detail_output.php", // 산출내역 저장
|
||||
type: "post",
|
||||
data: datasource,
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
console.log(data);
|
||||
setTimeout(function() {
|
||||
$(opener.location).attr("href", "javascript:restorePageNumber();");
|
||||
setTimeout(function() {
|
||||
hideMsgModal();
|
||||
hideOverlay();
|
||||
window.close();
|
||||
}, 1000);
|
||||
}, 1000);
|
||||
},
|
||||
error: function(jqxhr, status, error) {
|
||||
console.log(jqxhr, status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function generatePDF() {
|
||||
var title_message = '<?php echo $title_message; ?>';
|
||||
var workplace = '<?php echo $outworkplace; ?>';
|
||||
var deadline = '<?php echo $indate; ?>';
|
||||
var deadlineDate = new Date(deadline);
|
||||
var formattedDate = "(" + String(deadlineDate.getFullYear()).slice(-2) + "." + ("0" + (deadlineDate.getMonth() + 1)).slice(-2) + "." + ("0" + deadlineDate.getDate()).slice(-2) + ")";
|
||||
var result = 'KD' + title_message + '(' + workplace +')' + formattedDate + '.pdf';
|
||||
|
||||
var element = document.getElementById('content-to-print');
|
||||
var opt = {
|
||||
margin: [10, 3, 12, 3], // Top, right, bottom, left margins
|
||||
filename: result,
|
||||
image: { type: 'jpeg', quality: 1 },
|
||||
html2canvas: {
|
||||
scale: 3, // ★★★ 해상도(기본은 1) → 2~4 정도로 높이면 선명해짐 4는 용량이 매우 큽니다. 보통 3M 이상
|
||||
useCORS: true,
|
||||
scrollY: 0,
|
||||
scrollX: 0,
|
||||
windowWidth: document.body.scrollWidth,
|
||||
windowHeight: document.body.scrollHeight
|
||||
},
|
||||
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
|
||||
pagebreak: {
|
||||
mode: ['css', 'legacy'],
|
||||
avoid: ['tr', '.avoid-break'] // 핵심
|
||||
}
|
||||
};
|
||||
html2pdf().from(element).set(opt).save();
|
||||
}
|
||||
|
||||
|
||||
function generatePDF_server(callback) {
|
||||
var workplace = '<?php echo $title_message; ?>';
|
||||
var item = '<?php echo $emailTitle; ?>';
|
||||
var today = new Date();
|
||||
var formattedDate = "(" + String(today.getFullYear()).slice(-2) + "." + ("0" + (today.getMonth() + 1)).slice(-2) + "." + ("0" + today.getDate()).slice(-2) + ")";
|
||||
var result = 'KD' + item +'(' + workplace + ')' + formattedDate + '.pdf';
|
||||
|
||||
var element = document.getElementById('content-to-print');
|
||||
var opt = {
|
||||
margin: [10, 3, 12, 3], // Top, right, bottom, left margins
|
||||
filename: result,
|
||||
image: { type: 'jpeg', quality: 0.70 },
|
||||
html2canvas: { scale: 4 },
|
||||
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
|
||||
pagebreak: { mode: [''] }
|
||||
};
|
||||
|
||||
html2pdf().from(element).set(opt).output('datauristring').then(function (pdfDataUri) {
|
||||
var pdfBase64 = pdfDataUri.split(',')[1]; // Base64 인코딩된 PDF 데이터 추출
|
||||
var formData = new FormData();
|
||||
formData.append('pdf', pdfBase64);
|
||||
formData.append('filename', result);
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/email/save_pdf.php', // PDF 파일을 저장하는 PHP 파일
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function (response) {
|
||||
var res = JSON.parse(response);
|
||||
if (callback) {
|
||||
callback(res.filename); // 서버에 저장된 파일 경로를 콜백으로 전달
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
Swal.fire('Error', 'PDF 저장에 실패했습니다.', 'error');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var ajaxRequest = null;
|
||||
|
||||
function sendmail() {
|
||||
var secondordnum = '<?php echo $secondordnum; ?>'; // 서버에서 가져온 값
|
||||
var item = '<?php echo $emailTitle; ?>';
|
||||
|
||||
if (!secondordnum) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: '오류 알림',
|
||||
text: '발주처 코드가 없습니다.'
|
||||
});
|
||||
return; // 함수 종료
|
||||
}
|
||||
|
||||
if (typeof ajaxRequest !== 'undefined' && ajaxRequest !== null) {
|
||||
ajaxRequest.abort();
|
||||
}
|
||||
|
||||
ajaxRequest = $.ajax({
|
||||
type: 'POST',
|
||||
url: '/email/get_companyCode.php',
|
||||
data: { secondordnum: secondordnum },
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
console.log('response : ', response);
|
||||
if (response.error) {
|
||||
Swal.fire('Error', response.error, 'error');
|
||||
} else {
|
||||
var email = response.email;
|
||||
var vendorName = response.vendor_name;
|
||||
|
||||
Swal.fire({
|
||||
title: 'E메일 보내기',
|
||||
text: vendorName + ' Email 주소확인',
|
||||
icon: 'warning',
|
||||
input: 'text', // input 창을 텍스트 필드로 설정
|
||||
inputLabel: 'Email 주소 수정 가능',
|
||||
inputValue: email, // 기존 이메일 주소를 기본값으로 설정
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '보내기',
|
||||
cancelButtonText: '취소',
|
||||
reverseButtons: true,
|
||||
inputValidator: (value) => {
|
||||
if (!value) {
|
||||
return '이메일 주소를 입력해주세요!';
|
||||
}
|
||||
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailPattern.test(value)) {
|
||||
return '올바른 이메일 형식을 입력해주세요!';
|
||||
}
|
||||
}
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
const updatedEmail = result.value; // 입력된 이메일 주소 가져오기
|
||||
generatePDF_server(function(filename) {
|
||||
sendEmail(updatedEmail, vendorName, item, filename);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
Swal.fire('Error', '전송중 오류가 발생했습니다.', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sendEmail(recipientEmail, vendorName, item, filename) {
|
||||
if (typeof ajaxRequest !== 'undefined' && ajaxRequest !== null) {
|
||||
ajaxRequest.abort();
|
||||
}
|
||||
var today = new Date();
|
||||
var formattedDate = "(" + String(today.getFullYear()).slice(-2) + "." + ("0" + (today.getMonth() + 1)).slice(-2) + "." + ("0" + today.getDate()).slice(-2) + ")";
|
||||
|
||||
ajaxRequest = $.ajax({
|
||||
type: 'POST',
|
||||
url: '/email/send_email.php', // 이메일 전송을 처리하는 PHP 파일
|
||||
data: { email: recipientEmail, vendorName: vendorName, filename: filename, item: item, formattedDate: formattedDate },
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
Swal.fire('Success', '정상적으로 전송되었습니다.', 'success');
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
Swal.fire('Error', '전송에 실패했습니다. 확인바랍니다.', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// 재계산 버튼 클릭 이벤트
|
||||
$('.initialBtn').on('click', function() {
|
||||
// 재계산 확인 알림
|
||||
Swal.fire({
|
||||
title: '견적데이터 재계산',
|
||||
text: "견적 데이터를 재계산 하시겠습니까?",
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: '예, 재계산합니다',
|
||||
cancelButtonText: '취소'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
|
||||
$("#estimateTotal").val(0); // 견적총액 저장
|
||||
$("#EstimateFirstSum").val(0); // 견적초기금액
|
||||
$("#EstimateUpdatetSum").val(0); // 수정금액
|
||||
$("#EstimateDiffer").val(0); // 차액금액
|
||||
$("#EstimateDiscountRate").val(0); // 할인율
|
||||
$("#EstimateDiscount").val(0); // 할인금액
|
||||
$("#EstimateFinalSum").val(0); // 최종결정금액
|
||||
var form = $('#board_form')[0];
|
||||
var datasource = new FormData(form);
|
||||
|
||||
const initialData = JSON.stringify([]); // 빈 배열로 재계산
|
||||
|
||||
$('#detailJson').val(initialData);
|
||||
|
||||
// 재계산된 데이터 저장 요청
|
||||
$.ajax({
|
||||
enctype: 'multipart/form-data',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
cache: false,
|
||||
timeout: 600000,
|
||||
url: "/estimate/insert_detail_output.php",
|
||||
type: "post",
|
||||
data: datasource,
|
||||
dataType: "json",
|
||||
success: function(response) {
|
||||
Swal.fire({
|
||||
title: '재계산 완료',
|
||||
text: "모든 데이터가 재계산되었습니다.",
|
||||
icon: 'success',
|
||||
confirmButtonText: '확인'
|
||||
}).then(() => {
|
||||
hideMsgModal();
|
||||
// 페이지 새로고침
|
||||
location.reload();
|
||||
});
|
||||
},
|
||||
error: function(jqxhr, status, error) {
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: "재계산 중 오류가 발생했습니다.",
|
||||
icon: 'error',
|
||||
confirmButtonText: '확인'
|
||||
});
|
||||
console.log("AJAX Error: ", status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ───────────────────────────────────────────────────
|
||||
// 할인/최종 합계 표시 함수 (이미 정의된 cleanNumber, formatNumber 사용)
|
||||
function updateDiscountFooterRow() {
|
||||
const rate = $('#EstimateDiscountRate').val();
|
||||
const subtotal = cleanNumber($('#subtotalamount').text());
|
||||
|
||||
// 기존 tfoot 제거
|
||||
$('#tableDetail tfoot').remove();
|
||||
|
||||
let discount = 0;
|
||||
let finalTotal = 0;
|
||||
let finalRounded = 0;
|
||||
|
||||
if (rate <= 0 ) {
|
||||
discount = 0;
|
||||
finalTotal = subtotal;
|
||||
finalRounded = finalTotal;
|
||||
} else {
|
||||
discount = Math.round(subtotal * Number(rate) / 100);
|
||||
finalTotal = subtotal - discount;
|
||||
finalRounded = Math.floor(finalTotal / 1000) * 1000;
|
||||
discount += (finalTotal - finalRounded);
|
||||
}
|
||||
|
||||
if (rate > 0) {
|
||||
const tfootHTML = `
|
||||
<tfoot>
|
||||
<tr class="bg-light">
|
||||
<td colspan="8" class="text-end fw-bold">할인금액</td>
|
||||
<td colspan="4" class="text-end text-danger"> - ${formatNumber(discount)}</td>
|
||||
</tr>
|
||||
<tr class="bg-secondary text-white">
|
||||
<td colspan="8" class="text-end fw-bold">최종 합계</td>
|
||||
<td colspan="4" class="text-end fw-bold">${formatNumber(finalRounded)}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
`;
|
||||
$('#tableDetail').append(tfootHTML);
|
||||
|
||||
// 화면 하단 두 요소에도 최종합계 반영
|
||||
$('#totalsum').text(formatNumber(finalRounded));
|
||||
$('#koreantotalsum').text(KoreanNumber(finalRounded));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ───────────────────────────────────────────────────
|
||||
// 페이지 로드 시 & 할인율 변경 시 바인딩만 추가
|
||||
$(document).ready(function() {
|
||||
setTimeout(function() {
|
||||
updateDiscountFooterRow();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
// 산출내역서 보이기/숨기기
|
||||
function toggleEstimateView() {
|
||||
$('#showEstimateCheckbox').is(':checked')
|
||||
? $('.Estimateview').show()
|
||||
: $('.Estimateview').hide();
|
||||
}
|
||||
|
||||
// 소요자재 보이기/숨기기 (vendor-send 토글 로직 제거)
|
||||
function toggleListView() {
|
||||
$('#showlistCheckbox').is(':checked')
|
||||
? $('.listview, .vendor-send, .Novendor-send').show()
|
||||
: $('.listview, .vendor-send, .Novendor-send').hide();
|
||||
}
|
||||
|
||||
// 업체발송용 영역 토글
|
||||
function toggleVendorDiv() {
|
||||
|
||||
// 업체발송용 클릭시 나머지 두개의 체크박스를 해제한다.
|
||||
$('#showEstimateCheckbox').prop('checked', false);
|
||||
$('#showlistCheckbox').prop('checked', false);
|
||||
toggleEstimateView();
|
||||
toggleListView();
|
||||
if ($('#showVendorCheckbox').is(':checked')) {
|
||||
$('.vendor-send').show();
|
||||
$('.Novendor-send').hide();
|
||||
} else {
|
||||
$('.vendor-send').hide();
|
||||
$('.Novendor-send').show();
|
||||
}
|
||||
}
|
||||
|
||||
var option = $('#option').val(); // option은 견적서 화면에서 업체발송용으로 강제 지정
|
||||
|
||||
if (option === 'option') {
|
||||
// 초기 상태 반영
|
||||
toggleEstimateView();
|
||||
toggleListView();
|
||||
toggleVendorDiv();
|
||||
}
|
||||
|
||||
// 산출내역서 체크박스 이벤트
|
||||
$('#showEstimateCheckbox').on('change', toggleEstimateView);
|
||||
|
||||
// 소요자재 체크박스 이벤트
|
||||
$('#showlistCheckbox').on('change', function() {
|
||||
toggleListView();
|
||||
|
||||
// 소요자재 클릭 시 "업체발송용" 체크 해제 + 영역 토글
|
||||
$('#showVendorCheckbox').prop('checked', false);
|
||||
});
|
||||
|
||||
// 업체발송용 체크박스 이벤트
|
||||
$('#showVendorCheckbox').on('change', toggleVendorDiv);
|
||||
|
||||
// 할인율 변경시
|
||||
$('#EstimateDiscountRate').on('input change', function() {
|
||||
updateDiscountFooterRow();
|
||||
calculateEstimateAmounts();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user