영업관리 금액 소수점 2자리 표시 및 계산 정확성 개선

- 견적/주문서/판매관리 전 페이지 금액 필드 소수점 2자리 + 천단위 콤마 표시
- parseInt → parseFloat, Math.round 제거로 소수점 계산 정확성 확보
- estimateTemplate1 fn_save() 소수점 제거 버그 수정 (/[^0-9]/g → /[^0-9.]/g)
- contractMgmt.xml, salesNcollectMgmt.xml VARCHAR→NUMERIC CAST 시 REPLACE 안전 처리
- EST_TOTAL_AMOUNT_KRW 환율 변경 시 동적 재계산 (SQL 쿼리 수정)

Made-with: Cursor
This commit is contained in:
2026-03-13 17:36:06 +09:00
parent c0142b4787
commit e017520b60
10 changed files with 109 additions and 96 deletions

View File

@@ -369,13 +369,13 @@ $(function(){
fn_calculateTotal(); // 합계 재계산
});
// 단가 입력 완료 시 콤마 자동 추가
// 단가 입력 완료 시 포맷 적용
$(document).on("blur", ".item-price", function(){
var val = $(this).val().replace(/,/g, "");
if(!isNaN(val) && val !== "") {
$(this).val(addComma(val));
$(this).val(Number(val).toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 2}));
}
fn_calculateTotal(); // blur 시에도 합계 재계산
fn_calculateTotal();
});
// 단가 입력 시 숫자만 입력 가능하도록 제한 및 실시간 콤마 처리
@@ -384,8 +384,8 @@ $(function(){
var originalVal = $(this).val();
var commasBefore = (originalVal.substring(0, cursorPos).match(/,/g) || []).length;
// 콤마 제거 후 숫자가 아닌 문자 제거
var val = originalVal.replace(/,/g, "").replace(/[^0-9]/g, "");
// 콤마 제거 후 숫자와 소수점 외 문자 제거
var val = originalVal.replace(/,/g, "").replace(/[^0-9.]/g, "");
// 값 설정 (빈 문자열 포함)
if(val !== "") {
@@ -468,7 +468,7 @@ function fn_calculateAmount(row) {
var qty = row.find(".item-qty").val().replace(/,/g, "") || "0";
var price = row.find(".item-price").val().replace(/,/g, "") || "0";
var amount = parseInt(qty) * parseInt(price);
var amount = parseFloat(qty) * parseFloat(price);
if(!isNaN(amount)) {
var currencySymbol = getCurrencySymbol();
row.find(".item-amount").val(currencySymbol + addComma(amount));
@@ -482,9 +482,9 @@ function fn_calculateTotal() {
// 품목 행만 순회 (계 행, 원화환산 행, 비고 행, 참조사항 행, 회사명 행 제외)
$("#itemsTableBody tr").not(".total-row, .total-krw-row, .remarks-row, .notes-row, .footer-row").each(function(){
var amount = $(this).find(".item-amount").val() || "0";
// 모든 비숫자 문자 제거 (통화 기호, 콤마 등)
amount = amount.replace(/[^0-9]/g, "");
var numAmount = parseInt(amount) || 0;
// 모든 비숫자 문자 제거 (통화 기호, 콤마 등, 소수점은 유지)
amount = amount.replace(/[^0-9.]/g, "");
var numAmount = parseFloat(amount) || 0;
total += numAmount;
});
@@ -499,13 +499,13 @@ function fn_calculateTotal() {
// 원화환산 공급가액 계산
function fn_calculateTotalKRW(total) {
var totalKRW = total * g_exchangeRate;
$("#totalAmountKRW").text("₩" + addComma(Math.round(totalKRW)));
$("#totalAmountKRW").text("₩" + addComma(totalKRW));
}
// 콤마 추가
// 금액 포맷 (소수점 2자리 + 천단위 콤마)
function addComma(num) {
var regexp = /\B(?=(\d{3})+(?!\d))/g;
return num.toString().replace(regexp, ',');
if(num === '' || num === null || num === undefined) return '';
return Number(num).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
// 통화 기호 반환
@@ -1262,8 +1262,8 @@ function fn_save() {
specification: row.find(".item-spec").val() || "",
quantity: quantity.replace(/[^0-9]/g, ""), // 숫자만 추출
unit: row.find(".item-unit").val() || "",
unit_price: unitPrice.replace(/[^0-9]/g, ""), // 숫자만 추출
amount: amount.replace(/[^0-9]/g, ""), // 숫자만 추출
unit_price: unitPrice.replace(/[^0-9.]/g, ""),
amount: amount.replace(/[^0-9.]/g, ""),
note: row.find(".item-note").val() || ""
});
});

View File

@@ -403,11 +403,11 @@ $(function(){
}
}
// 숫자만 입력 가능하도록 제한 (unit_price, subtotal)
// 숫자와 소수점만 입력 가능하도록 제한 (unit_price, subtotal)
$(document).on("keypress", ".item-price, .subtotal-amount", function(e) {
// 숫자(0-9), 백스페이스, 삭제, 탭, 엔터, 콤마만 허용
var charCode = (e.which) ? e.which : e.keyCode;
if (charCode != 8 && charCode != 9 && charCode != 13 && charCode != 44 &&
// 숫자(0-9), 백스페이스, 삭제, 탭, 엔터, 콤마, 소수점(46) 허용
if (charCode != 8 && charCode != 9 && charCode != 13 && charCode != 44 && charCode != 46 &&
(charCode < 48 || charCode > 57)) {
e.preventDefault();
return false;
@@ -508,7 +508,7 @@ function fn_calculateAmount(row) {
var qty = row.find(".item-qty").val() || "1";
var price = row.find(".item-price").val().replace(/,/g, "") || "0";
var amount = parseInt(qty) * parseInt(price);
var amount = parseFloat(qty) * parseFloat(price);
if(!isNaN(amount)) {
row.find(".item-amount").val(addComma(amount));
}
@@ -520,7 +520,7 @@ function fn_calculateSubtotal(tbody) {
tbody.find("tr:not(.category-row):not(.subtotal-row)").each(function() {
var amount = $(this).find(".item-amount").val();
if(amount) {
var numAmount = parseInt(amount.replace(/,/g, "").replace(/-/g, ""));
var numAmount = parseFloat(amount.replace(/,/g, "").replace(/-/g, ""));
if(!isNaN(numAmount)) {
total += numAmount;
}
@@ -530,10 +530,10 @@ function fn_calculateSubtotal(tbody) {
tbody.find(".subtotal-amount").val(addComma(total));
}
// 콤마 추가
// 금액 포맷 (소수점 2자리 + 천단위 콤마)
function addComma(num) {
var regexp = /\B(?=(\d{3})+(?!\d))/g;
return num.toString().replace(regexp, ',');
if(num === '' || num === null || num === undefined) return '';
return Number(num).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
// 카테고리 추가
@@ -709,7 +709,7 @@ function fn_calculateTotal() {
// 초음파 CNC Machine의 AMOUNT 값 추가
var cncAmount = $(".category-section[data-category='cnc_machine'] .item-amount").val();
if(cncAmount && cncAmount !== "") {
var numVal = parseInt(cncAmount.replace(/,/g, ""));
var numVal = parseFloat(cncAmount.replace(/,/g, ""));
if(!isNaN(numVal)) {
total += numVal;
}
@@ -719,7 +719,7 @@ function fn_calculateTotal() {
$(".subtotal-amount").each(function() {
var val = $(this).val();
if(val && val !== "" && val !== "-") {
var numVal = parseInt(val.replace(/,/g, ""));
var numVal = parseFloat(val.replace(/,/g, ""));
if(!isNaN(numVal)) {
total += numVal;
}
@@ -1000,7 +1000,7 @@ function fn_save() {
// 원화 환산 (TOTAL_AMOUNT_KRW)
var totalAmountKrw = "";
if(totalAmount && totalAmount !== "" && !isNaN(totalAmount)) {
totalAmountKrw = Math.round(parseFloat(totalAmount) * exchangeRate).toString();
totalAmountKrw = (parseFloat(totalAmount) * exchangeRate).toFixed(2);
}
var formData = {

View File

@@ -374,7 +374,7 @@ function fn_renderOrderForm(info, items){
}
function fn_fmt(n){
return Number(n).toLocaleString();
return Number(n).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
</script>
</body>

View File

@@ -250,7 +250,7 @@ var columns = [
else if(currencyName.includes('유로') || currencyName === 'EUR') currencySymbol = '€';
else if(currencyName.includes('엔') || currencyName === 'JPY') currencySymbol = '¥';
else if(currencyName.includes('위안') || currencyName === 'CNY') currencySymbol = '¥';
return currencySymbol + Number(value).toLocaleString();
return currencySymbol + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 12. 부가세
@@ -266,7 +266,7 @@ var columns = [
else if(currencyName.includes('유로') || currencyName === 'EUR') currencySymbol = '€';
else if(currencyName.includes('엔') || currencyName === 'JPY') currencySymbol = '¥';
else if(currencyName.includes('위안') || currencyName === 'CNY') currencySymbol = '¥';
return currencySymbol + Number(value).toLocaleString();
return currencySymbol + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 13. 총액
@@ -282,7 +282,7 @@ var columns = [
else if(currencyName.includes('유로') || currencyName === 'EUR') currencySymbol = '€';
else if(currencyName.includes('엔') || currencyName === 'JPY') currencySymbol = '¥';
else if(currencyName.includes('위안') || currencyName === 'CNY') currencySymbol = '¥';
return currencySymbol + Number(value).toLocaleString();
return currencySymbol + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 14. 원화총액
@@ -290,7 +290,7 @@ var columns = [
formatter: function(cell) {
var value = cell.getValue();
if(!value || value === '' || value === '0') return '';
return '₩' + Number(value).toLocaleString();
return '₩' + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 15. 주문서첨부
@@ -421,7 +421,7 @@ function fn_calculateTotalFromGrid(){
}
console.log("✅ [주문서관리] 표시된 데이터 합계:", totalAmountKRW);
$("#totalAmount").text(Number(totalAmountKRW).toLocaleString());
$("#totalAmount").text(Number(totalAmountKRW).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}));
}
function _fnc_datepick(){

View File

@@ -53,7 +53,7 @@
// 숫자 입력 필드에 콤마 자동 추가 및 금액 계산
$(document).on("keyup", "input:text[numberOnly]", function() {
$(this).val(addComma($(this).val().replace(/[^0-9]/g, "")));
$(this).val(addComma($(this).val().replace(/[^0-9.]/g, "")));
var itemId = $(this).closest("tr").attr("id");
if(itemId) {
@@ -95,10 +95,10 @@
fn_loadContractItems();
});
// 콤마 추가 함수
// 금액 포맷 (소수점 2자리 + 천단위 콤마)
function addComma(data) {
if(!data) return '';
return data.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
if(!data && data !== 0) return '';
return Number(data).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
// 콤마 제거 함수
@@ -148,15 +148,15 @@
// 부가세 입력 가능 (기본값: 공급가액 × 10%)
// 공급가액 + 부가세 = 총액 (자동)
function fn_calculateItemAmount(itemId) {
var quantity = parseInt(removeComma($("#" + itemId + " .item-quantity").val())) || 0;
var unitPrice = parseInt(removeComma($("#" + itemId + " .item-unit-price").val())) || 0;
var quantity = parseFloat(removeComma($("#" + itemId + " .item-quantity").val())) || 0;
var unitPrice = parseFloat(removeComma($("#" + itemId + " .item-unit-price").val())) || 0;
// 공급가액 계산
var supplyPrice = quantity * unitPrice;
$("#" + itemId + " .item-supply-price").val(addComma(supplyPrice));
// 부가세 자동 계산 (공급가액의 10%)
var vat = Math.round(supplyPrice * 0.1);
var vat = supplyPrice * 0.1;
$("#" + itemId + " .item-vat").val(addComma(vat));
// 총액 계산
@@ -166,8 +166,8 @@
// 부가세 직접 입력 시 총액만 재계산
function fn_calculateTotalFromVat(itemId) {
var supplyPrice = parseInt(removeComma($("#" + itemId + " .item-supply-price").val())) || 0;
var vat = parseInt(removeComma($("#" + itemId + " .item-vat").val())) || 0;
var supplyPrice = parseFloat(removeComma($("#" + itemId + " .item-supply-price").val())) || 0;
var vat = parseFloat(removeComma($("#" + itemId + " .item-vat").val())) || 0;
// 총액 계산
var totalAmount = supplyPrice + vat;

View File

@@ -453,7 +453,7 @@ var columns = [
else if(currencyName.includes('유로') || currencyName === 'EUR') currencySymbol = '€';
else if(currencyName.includes('엔') || currencyName === 'JPY') currencySymbol = '¥';
else if(currencyName.includes('위안') || currencyName === 'CNY') currencySymbol = '¥';
return currencySymbol + Number(value).toLocaleString();
return currencySymbol + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 11. 공급가액 (환종에 따라 표기)
@@ -469,7 +469,7 @@ var columns = [
else if(currencyName.includes('유로') || currencyName === 'EUR') currencySymbol = '€';
else if(currencyName.includes('엔') || currencyName === 'JPY') currencySymbol = '¥';
else if(currencyName.includes('위안') || currencyName === 'CNY') currencySymbol = '¥';
return currencySymbol + Number(value).toLocaleString();
return currencySymbol + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 12. 부가세 (환종에 따라 표기)
@@ -485,7 +485,7 @@ var columns = [
else if(currencyName.includes('유로') || currencyName === 'EUR') currencySymbol = '€';
else if(currencyName.includes('엔') || currencyName === 'JPY') currencySymbol = '¥';
else if(currencyName.includes('위안') || currencyName === 'CNY') currencySymbol = '¥';
return currencySymbol + Number(value).toLocaleString();
return currencySymbol + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 13. 총액 (환종에 따라 표기)
@@ -501,7 +501,7 @@ var columns = [
else if(currencyName.includes('유로') || currencyName === 'EUR') currencySymbol = '€';
else if(currencyName.includes('엔') || currencyName === 'JPY') currencySymbol = '¥';
else if(currencyName.includes('위안') || currencyName === 'CNY') currencySymbol = '¥';
return currencySymbol + Number(value).toLocaleString();
return currencySymbol + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 14. 원화총액
@@ -509,7 +509,7 @@ var columns = [
formatter: function(cell) {
var value = cell.getValue();
if(!value || value === '' || value === '0') return '';
return '₩' + Number(value).toLocaleString();
return '₩' + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 15. 출하일
@@ -629,9 +629,9 @@ function fn_search(){
var vat = totals.total_vat || totals.TOTAL_VAT || 0;
var amount = totals.total_amount || totals.TOTAL_AMOUNT || 0;
$("#totalSupplyPrice").text(Number(supplyPrice).toLocaleString());
$("#totalVat").text(Number(vat).toLocaleString());
$("#totalAmount").text(Number(amount).toLocaleString());
$("#totalSupplyPrice").text(Number(supplyPrice).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}));
$("#totalVat").text(Number(vat).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}));
$("#totalAmount").text(Number(amount).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}));
} else {
$("#totalSupplyPrice").text("0");
$("#totalVat").text("0");

View File

@@ -358,7 +358,7 @@ var columns = [
else if(currencyName.includes('유로') || currencyName === 'EUR') currencySymbol = '€';
else if(currencyName.includes('엔') || currencyName === 'JPY') currencySymbol = '¥';
else if(currencyName.includes('위안') || currencyName === 'CNY') currencySymbol = '¥';
return currencySymbol + Number(value).toLocaleString();
return currencySymbol + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 13. 판매공급가액
@@ -374,7 +374,7 @@ var columns = [
else if(currencyName.includes('유로') || currencyName === 'EUR') currencySymbol = '€';
else if(currencyName.includes('엔') || currencyName === 'JPY') currencySymbol = '¥';
else if(currencyName.includes('위안') || currencyName === 'CNY') currencySymbol = '¥';
return currencySymbol + Number(value).toLocaleString();
return currencySymbol + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 14. 부가세
@@ -390,7 +390,7 @@ var columns = [
else if(currencyName.includes('유로') || currencyName === 'EUR') currencySymbol = '€';
else if(currencyName.includes('엔') || currencyName === 'JPY') currencySymbol = '¥';
else if(currencyName.includes('위안') || currencyName === 'CNY') currencySymbol = '¥';
return currencySymbol + Number(value).toLocaleString();
return currencySymbol + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 15. 판매총액
@@ -406,7 +406,7 @@ var columns = [
else if(currencyName.includes('유로') || currencyName === 'EUR') currencySymbol = '€';
else if(currencyName.includes('엔') || currencyName === 'JPY') currencySymbol = '¥';
else if(currencyName.includes('위안') || currencyName === 'CNY') currencySymbol = '¥';
return currencySymbol + Number(value).toLocaleString();
return currencySymbol + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 16. 판매원화총액
@@ -414,7 +414,7 @@ var columns = [
formatter: function(cell) {
var value = cell.getValue();
if(!value || value === '' || value === '0') return '';
return '₩' + Number(value).toLocaleString();
return '₩' + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 17. 잔량원화총액
@@ -422,7 +422,7 @@ var columns = [
formatter: function(cell) {
var value = cell.getValue();
if(!value || value === '' || value === '0') return '';
return '₩' + Number(value).toLocaleString();
return '₩' + Number(value).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
},
// 18. 수주상태
@@ -554,9 +554,9 @@ function fn_search(){
}
// 합계 표시
$("#totalSalesAmountKRW").text(Number(totalSalesAmountKRW).toLocaleString());
$("#shippedAmountKRW").text(Number(shippedAmountKRW).toLocaleString());
$("#notShippedAmountKRW").text(Number(notShippedAmountKRW).toLocaleString());
$("#totalSalesAmountKRW").text(Number(totalSalesAmountKRW).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}));
$("#shippedAmountKRW").text(Number(shippedAmountKRW).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}));
$("#notShippedAmountKRW").text(Number(notShippedAmountKRW).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}));
// 페이징 HTML 업데이트
if(response.PAGE_HTML){

View File

@@ -709,7 +709,7 @@ function fn_loadSavedStatement(projectObjid, gridData) {
row.append($("<td contenteditable='true'>").text(quantity));
row.append($("<td contenteditable='true' class='text-right'>").text(fn_num(item.isprice || item.ISPRICE || 0)));
row.append($("<td contenteditable='true' class='text-right'>").text(fn_num(supply)));
row.append($("<td contenteditable='true' class='text-right'>").text(fn_num(Math.round(vat))));
row.append($("<td contenteditable='true' class='text-right'>").text(fn_num(vat)));
tbody.append(row);
}
@@ -725,11 +725,11 @@ function fn_loadSavedStatement(projectObjid, gridData) {
// 합계 업데이트
$("#totalQuantity").text(totalQuantity);
$("#totalSupplyPrice").text(fn_num(totalSupply));
$("#totalVat").text(fn_num(Math.round(totalVat)));
$("#totalVat").text(fn_num(totalVat));
var total = totalSupply + totalVat;
$("#totalText").text(fn_numberToKorean(Math.round(total)));
$("#totalNum").text(fn_num(Math.round(total)));
$("#totalNum").text(fn_num(total));
// contenteditable 셀 변경 시 합계 자동 업데이트
fn_attachCellListeners();
@@ -963,7 +963,7 @@ function fn_recalculateTotal() {
}
function fn_num(n) {
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return Number(n).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
// 숫자를 한글로 변환하는 함수

View File

@@ -579,28 +579,32 @@
-- 최근 차수 견적서 합계 정보
,(SELECT TOTAL_AMOUNT FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = T.OBJID ORDER BY REGDATE DESC LIMIT 1) AS EST_TOTAL_AMOUNT
,CASE
WHEN T.EXCHANGE_RATE IS NOT NULL AND T.EXCHANGE_RATE != '' AND CAST(T.EXCHANGE_RATE AS NUMERIC) != 0
WHEN T.EXCHANGE_RATE IS NOT NULL AND T.EXCHANGE_RATE != ''
AND REPLACE(T.EXCHANGE_RATE, ',', '') ~ '^\d+\.?\d*$'
AND CAST(REPLACE(T.EXCHANGE_RATE, ',', '') AS NUMERIC) != 0
THEN ROUND(
CAST(COALESCE((SELECT TOTAL_AMOUNT FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = T.OBJID ORDER BY REGDATE DESC LIMIT 1), '0') AS NUMERIC)
* CAST(T.EXCHANGE_RATE AS NUMERIC), 2
CAST(REPLACE(COALESCE((SELECT TOTAL_AMOUNT FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = T.OBJID ORDER BY REGDATE DESC LIMIT 1), '0'), ',', '') AS NUMERIC)
* CAST(REPLACE(T.EXCHANGE_RATE, ',', '') AS NUMERIC), 2
)
ELSE CAST(COALESCE((SELECT TOTAL_AMOUNT_KRW FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = T.OBJID ORDER BY REGDATE DESC LIMIT 1), '0') AS NUMERIC)
ELSE CAST(REPLACE(COALESCE((SELECT TOTAL_AMOUNT_KRW FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = T.OBJID ORDER BY REGDATE DESC LIMIT 1), '0'), ',', '') AS NUMERIC)
END AS EST_TOTAL_AMOUNT_KRW
-- 견적수량 (ESTIMATE_TEMPLATE_ITEM의 수량 합계)
,(SELECT COALESCE(SUM(CAST(QUANTITY AS NUMERIC)), 0) FROM ESTIMATE_TEMPLATE_ITEM WHERE TEMPLATE_OBJID = (SELECT objid FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = T.OBJID ORDER BY REGDATE DESC LIMIT 1)) AS ESTIMATE_QUANTITY
,(SELECT COALESCE(SUM(CAST(REPLACE(QUANTITY, ',', '') AS NUMERIC)), 0) FROM ESTIMATE_TEMPLATE_ITEM WHERE TEMPLATE_OBJID = (SELECT objid FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = T.OBJID ORDER BY REGDATE DESC LIMIT 1)) AS ESTIMATE_QUANTITY
-- 수주 합계 정보 (CONTRACT_MGMT 테이블에 저장된 값 사용)
,T.ORDER_SUPPLY_PRICE AS ORDER_SUPPLY_PRICE_SUM
,T.ORDER_VAT AS ORDER_VAT_SUM
,T.ORDER_TOTAL_AMOUNT AS ORDER_TOTAL_AMOUNT_SUM
-- 수주수량 (CONTRACT_MGMT의 QUANTITY 또는 CONTRACT_ITEM 합계)
,COALESCE(
NULLIF(T.QUANTITY, '')::NUMERIC,
(SELECT COALESCE(SUM(CAST(ORDER_QUANTITY AS NUMERIC)), 0) FROM CONTRACT_ITEM WHERE CONTRACT_OBJID = T.OBJID AND STATUS = 'ACTIVE')
NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::NUMERIC,
(SELECT COALESCE(SUM(CAST(REPLACE(ORDER_QUANTITY, ',', '') AS NUMERIC)), 0) FROM CONTRACT_ITEM WHERE CONTRACT_OBJID = T.OBJID AND STATUS = 'ACTIVE')
) AS ORDER_QUANTITY
,CASE
WHEN T.ORDER_TOTAL_AMOUNT IS NOT NULL AND T.ORDER_TOTAL_AMOUNT != ''
,CASE
WHEN T.ORDER_TOTAL_AMOUNT IS NOT NULL AND T.ORDER_TOTAL_AMOUNT != ''
AND T.EXCHANGE_RATE IS NOT NULL AND T.EXCHANGE_RATE != ''
THEN CAST(T.ORDER_TOTAL_AMOUNT AS NUMERIC) * CAST(T.EXCHANGE_RATE AS NUMERIC)
AND REPLACE(T.ORDER_TOTAL_AMOUNT, ',', '') ~ '^\d+\.?\d*$'
AND REPLACE(T.EXCHANGE_RATE, ',', '') ~ '^\d+\.?\d*$'
THEN CAST(REPLACE(T.ORDER_TOTAL_AMOUNT, ',', '') AS NUMERIC) * CAST(REPLACE(T.EXCHANGE_RATE, ',', '') AS NUMERIC)
ELSE 0
END AS ORDER_TOTAL_AMOUNT_KRW
-- 품목 정보 요약
@@ -5732,7 +5736,7 @@ WHERE
SELECT
ETI.PART_OBJID,
PM.PART_NO,
CAST(COALESCE(NULLIF(ETI.UNIT_PRICE, ''), '0') AS NUMERIC) AS CURRENT_PRICE
CAST(REPLACE(COALESCE(NULLIF(ETI.UNIT_PRICE, ''), '0'), ',', '') AS NUMERIC) AS CURRENT_PRICE
FROM ESTIMATE_TEMPLATE ET
INNER JOIN ESTIMATE_TEMPLATE_ITEM ETI ON ET.OBJID = ETI.TEMPLATE_OBJID
LEFT JOIN PART_MNG PM ON ETI.PART_OBJID = PM.OBJID
@@ -5747,7 +5751,7 @@ WHERE
SELECT
CI.PART_OBJID,
COALESCE(PM.PART_NO, CI.PART_NO) AS PART_NO,
MAX(CAST(COALESCE(NULLIF(ETI.UNIT_PRICE, ''), '0') AS NUMERIC)) AS PREV_MAX_PRICE
MAX(CAST(REPLACE(COALESCE(NULLIF(ETI.UNIT_PRICE, ''), '0'), ',', '') AS NUMERIC)) AS PREV_MAX_PRICE
FROM CONTRACT_MGMT CM
INNER JOIN CONTRACT_ITEM CI ON CM.OBJID = CI.CONTRACT_OBJID AND CI.STATUS = 'ACTIVE'
LEFT JOIN PART_MNG PM ON CI.PART_OBJID = PM.OBJID

View File

@@ -860,7 +860,7 @@
AND CIS.SERIAL_NO IS NOT NULL),
SR.serial_no
) AS SERIAL_NO,
COALESCE(T.QUANTITY::numeric, 0) AS ORDER_QUANTITY,
COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0) AS ORDER_QUANTITY,
-- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT
COALESCE(
(SELECT CI.DUE_DATE
@@ -897,12 +897,15 @@
COALESCE(SR.sales_total_amount, 0) AS SALES_TOTAL_AMOUNT,
COALESCE(SR.sales_total_amount, 0) AS SALES_TOTAL_AMOUNT_KRW,
-- 잔량 계산: 수주수량 - sales_registration.sales_quantity
COALESCE(T.QUANTITY::numeric, 0) - COALESCE(SR.sales_quantity, 0) AS REMAINING_QUANTITY,
COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0) - COALESCE(SR.sales_quantity, 0) AS REMAINING_QUANTITY,
-- 잔량원화총액 계산: 잔량 * 판매단가
(COALESCE(T.QUANTITY::numeric, 0) - COALESCE(SR.sales_quantity, 0)) * COALESCE(SR.sales_unit_price, 0) AS REMAINING_AMOUNT_KRW,
(COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0) - COALESCE(SR.sales_quantity, 0)) * COALESCE(SR.sales_unit_price, 0) AS REMAINING_AMOUNT_KRW,
COALESCE(SR.sales_currency, T.CONTRACT_CURRENCY) AS SALES_CURRENCY,
CODE_NAME(COALESCE(SR.sales_currency, T.CONTRACT_CURRENCY)) AS SALES_CURRENCY_NAME,
COALESCE(SR.sales_exchange_rate, T.CONTRACT_PRICE_CURRENCY::numeric, 0) AS SALES_EXCHANGE_RATE,
COALESCE(SR.sales_exchange_rate,
CASE WHEN REPLACE(COALESCE(T.CONTRACT_PRICE_CURRENCY, ''), ',', '') ~ '^\d+\.?\d*$'
THEN REPLACE(T.CONTRACT_PRICE_CURRENCY, ',', '')::numeric ELSE 0 END,
0) AS SALES_EXCHANGE_RATE,
COALESCE(TO_CHAR(SR.shipping_date, 'YYYY-MM-DD'), '') AS SHIPPING_DATE,
-- 출하일 (분할출하 포함): 엑셀 다운로드용
COALESCE(
@@ -928,10 +931,10 @@
CASE
WHEN COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) = 0
THEN '미판매'
WHEN COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) >= COALESCE(T.QUANTITY::numeric, 0)
WHEN COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) >= COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0)
THEN '완판'
WHEN COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) > 0
AND COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) <![CDATA[<]]> COALESCE(T.QUANTITY::numeric, 0)
AND COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) <![CDATA[<]]> COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0)
THEN '분할판매'
ELSE ''
END AS SALES_STATUS,
@@ -1085,13 +1088,13 @@
<foreach item="item" collection="salesStatus">
<!-- 완판: 0900208 -->
<if test="'0900208'.equals(item)">
OR (COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) >= COALESCE(T.QUANTITY::numeric, 0)
AND COALESCE(T.QUANTITY::numeric, 0) > 0)
OR (COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) >= COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0)
AND COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0) > 0)
</if>
<!-- 분할판매: 0900209 -->
<if test="'0900209'.equals(item)">
OR (COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) > 0
AND COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) <![CDATA[<]]> COALESCE(T.QUANTITY::numeric, 0))
AND COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) <![CDATA[<]]> COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0))
</if>
<!-- 미판매: 0900210 -->
<if test="'0900210'.equals(item)">
@@ -1276,13 +1279,13 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
<foreach item="item" collection="salesStatus">
<!-- 완판: 0900208 -->
<if test="'0900208'.equals(item)">
OR (COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) >= COALESCE(T.QUANTITY::numeric, 0)
AND COALESCE(T.QUANTITY::numeric, 0) > 0)
OR (COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) >= COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0)
AND COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0) > 0)
</if>
<!-- 분할판매: 0900209 -->
<if test="'0900209'.equals(item)">
OR (COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) > 0
AND COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) <![CDATA[<]]> COALESCE(T.QUANTITY::numeric, 0))
AND COALESCE((SELECT SUM(sales_quantity) FROM sales_registration WHERE project_no LIKE T.PROJECT_NO || '%'), 0) <![CDATA[<]]> COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0))
</if>
<!-- 미판매: 0900210 -->
<if test="'0900210'.equals(item)">
@@ -1566,7 +1569,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
AND CIS.SERIAL_NO IS NOT NULL),
SR.serial_no
) AS SERIAL_NO,
COALESCE(T.QUANTITY::numeric, 0) AS ORDER_QUANTITY,
COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0) AS ORDER_QUANTITY,
-- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT
COALESCE(
(SELECT CI.DUE_DATE
@@ -1598,7 +1601,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
COALESCE(SR.shipping_order_status, '') AS SHIPPING_ORDER_STATUS,
-- 주문수량 (PROJECT_MGMT에서 가져오기) - 잔량 계산용
COALESCE(T.QUANTITY::NUMERIC, 0) AS ORDER_QUANTITY,
COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::NUMERIC, 0) AS ORDER_QUANTITY,
-- 판매수량 (sales_registration에서 가져오기) - 이미 판매한 수량
COALESCE(SR.sales_quantity, 0) AS SALES_QUANTITY,
@@ -1609,7 +1612,10 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
COALESCE(SR.sales_total_amount, 0) AS SALES_TOTAL_AMOUNT_KRW,
COALESCE(NULLIF(SR.sales_currency, ''), CM.CONTRACT_CURRENCY) AS SALES_CURRENCY,
CODE_NAME(COALESCE(NULLIF(SR.sales_currency, ''), CM.CONTRACT_CURRENCY)) AS SALES_CURRENCY_NAME,
COALESCE(NULLIF(SR.sales_exchange_rate, 0), NULLIF(CM.EXCHANGE_RATE, '')::numeric, 0) AS SALES_EXCHANGE_RATE,
COALESCE(NULLIF(SR.sales_exchange_rate, 0),
CASE WHEN REPLACE(COALESCE(CM.EXCHANGE_RATE, ''), ',', '') ~ '^\d+\.?\d*$'
THEN REPLACE(CM.EXCHANGE_RATE, ',', '')::numeric ELSE 0 END,
0) AS SALES_EXCHANGE_RATE,
COALESCE(TO_CHAR(SR.shipping_date, 'YYYY-MM-DD'), '') AS SHIPPING_DATE,
COALESCE(SR.shipping_method, '') AS SHIPPING_METHOD,
COALESCE(SR.manager_user_id, T.PM_USER_ID) AS MANAGER,
@@ -1649,7 +1655,8 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
-- 환종 정보
CM.CONTRACT_CURRENCY AS SALES_CURRENCY,
CODE_NAME(CM.CONTRACT_CURRENCY) AS SALES_CURRENCY_NAME,
COALESCE(CM.EXCHANGE_RATE::NUMERIC, 0) AS SALES_EXCHANGE_RATE,
COALESCE(CASE WHEN REPLACE(COALESCE(CM.EXCHANGE_RATE, ''), ',', '') ~ '^\d+\.?\d*$'
THEN REPLACE(CM.EXCHANGE_RATE, ',', '')::NUMERIC ELSE 0 END, 0) AS SALES_EXCHANGE_RATE,
-- 출하일 기본값: 오늘 날짜
TO_CHAR(CURRENT_DATE, 'YYYY-MM-DD') AS SHIPPING_DATE,
@@ -1696,7 +1703,8 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
-- 환종 정보
CM.CONTRACT_CURRENCY AS SALES_CURRENCY,
CODE_NAME(CM.CONTRACT_CURRENCY) AS SALES_CURRENCY_NAME,
COALESCE(NULLIF(CM.EXCHANGE_RATE, '')::NUMERIC, 0) AS SALES_EXCHANGE_RATE,
COALESCE(CASE WHEN REPLACE(COALESCE(CM.EXCHANGE_RATE, ''), ',', '') ~ '^\d+\.?\d*$'
THEN REPLACE(CM.EXCHANGE_RATE, ',', '')::NUMERIC ELSE 0 END, 0) AS SALES_EXCHANGE_RATE,
-- 출하일 기본값: 오늘 날짜
TO_CHAR(CURRENT_DATE, 'YYYY-MM-DD') AS SHIPPING_DATE,
@@ -2202,7 +2210,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
COALESCE(T.PART_NO, '') AS PART_NO,
COALESCE(T.PART_NAME, '') AS PART_NAME,
T.PART_OBJID,
COALESCE(T.QUANTITY::NUMERIC, 0) AS QUANTITY,
COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::NUMERIC, 0) AS QUANTITY,
CODE_NAME(T.CATEGORY_CD) AS ORDER_TYPE,
CODE_NAME(T.PRODUCT) AS PRODUCT_TYPE,
CODE_NAME(T.AREA_CD) AS NATION,
@@ -2215,9 +2223,10 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
COALESCE(CM.PO_NO, '') AS PO_NO,
COALESCE(CM.ORDER_DATE, '') AS ORDER_DATE,
COALESCE((SELECT SUM(SR.SALES_QUANTITY) FROM SALES_REGISTRATION SR WHERE SR.PROJECT_NO = T.PROJECT_NO), 0) AS SALES_QUANTITY,
COALESCE(T.QUANTITY::NUMERIC, 0) - COALESCE((SELECT SUM(SR.SALES_QUANTITY) FROM SALES_REGISTRATION SR WHERE SR.PROJECT_NO = T.PROJECT_NO), 0) AS REMAINING_QUANTITY,
COALESCE(NULLIF(CM.ORDER_UNIT_PRICE, '')::NUMERIC, 0) AS ORDER_UNIT_PRICE,
COALESCE(NULLIF(CM.EXCHANGE_RATE, '')::NUMERIC, 1) AS EXCHANGE_RATE,
COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::NUMERIC, 0) - COALESCE((SELECT SUM(SR.SALES_QUANTITY) FROM SALES_REGISTRATION SR WHERE SR.PROJECT_NO = T.PROJECT_NO), 0) AS REMAINING_QUANTITY,
COALESCE(NULLIF(REPLACE(CM.ORDER_UNIT_PRICE, ',', ''), '')::NUMERIC, 0) AS ORDER_UNIT_PRICE,
COALESCE(CASE WHEN REPLACE(COALESCE(CM.EXCHANGE_RATE, ''), ',', '') ~ '^\d+\.?\d*$'
THEN REPLACE(CM.EXCHANGE_RATE, ',', '')::NUMERIC ELSE 1 END, 1) AS EXCHANGE_RATE,
COALESCE(CM.CONTRACT_CURRENCY, '') AS CONTRACT_CURRENCY
FROM PROJECT_MGMT T
LEFT JOIN CONTRACT_MGMT CM ON CM.OBJID = T.CONTRACT_OBJID
@@ -2393,7 +2402,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID
AND CI.PART_OBJID = T.PART_OBJID AND CI.STATUS = 'ACTIVE'
), '') AS SERIAL_NO,
COALESCE(T.QUANTITY::numeric, 0) AS ORDER_QUANTITY,
COALESCE(NULLIF(REPLACE(T.QUANTITY, ',', ''), '')::numeric, 0) AS ORDER_QUANTITY,
(SELECT CM.PO_NO FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID) AS PO_NO,
COALESCE(T.CONTRACT_DATE, (SELECT CM.order_date FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID)) AS ORDER_DATE,
COALESCE(SL.split_quantity, 0) AS SALES_QUANTITY,