V20260210 #154

Merged
hjjeong merged 4 commits from V20260210 into main 2026-02-27 03:27:08 +00:00
8 changed files with 609 additions and 161 deletions

View File

@@ -151,6 +151,28 @@ $(document).ready(function(){
document.form1.submit();
});
// 수주취소
$("#btnOrderCancel").click(function(){
var selectedData = _tabulGrid.getSelectedData();
if(selectedData.length < 1){
Swal.fire("수주취소할 행을 선택해주십시오.");
return false;
} else if(selectedData.length > 1){
Swal.fire("한번에 한개의 수주만 취소 가능합니다.");
return false;
}
var contractObjId = fnc_checkNull(selectedData[0].OBJID);
var orderQty = parseInt(selectedData[0].ORDER_QUANTITY) || 0;
if(orderQty === 0){
Swal.fire("수주 수량이 없는 건은 취소할 수 없습니다.");
return false;
}
fn_openOrderCancelPopup(contractObjId);
});
fn_search();
});
@@ -203,6 +225,14 @@ var columns = [
return Number(value).toLocaleString();
}
},
// 8-1. 수주취소
{headerHozAlign : 'center', hozAlign : 'right', minWidth : 60, widthGrow: 0.7, title : '수주취소', field : 'CANCEL_QTY_SUM',
formatter: function(cell) {
var value = cell.getValue();
if(!value || value === '' || value === 0 || value === '0') return '';
return "<span style='color:#e74c3c; font-weight:bold;'>" + Number(value).toLocaleString() + "</span>";
}
},
// 9. 유/무상
{headerHozAlign : 'center', hozAlign : 'center', minWidth : 45, widthGrow: 0.5, title : '유/무상', field : 'PAID_TYPE' },
// 10. 수주상태
@@ -656,6 +686,131 @@ function fn_showSerialNoPopup(serialNoString){
});
}
// 수주취소 팝업 - 품목별 취소 수량 입력
function fn_openOrderCancelPopup(contractObjId){
$.ajax({
url: "/contractMgmt/getContractItems.do",
type: "POST",
data: { contractObjId: contractObjId },
dataType: "json",
success: function(data){
if(data.result !== "success" || !data.items || data.items.length === 0){
Swal.fire("수주 품목 정보가 없습니다.");
return;
}
var items = data.items;
var html = '<div style="max-height: 400px; overflow-y: auto;">';
html += '<table style="width: 100%; border-collapse: collapse;">';
html += '<thead><tr style="background-color: #f0f0f0;">';
html += '<th style="border: 1px solid #ddd; padding: 8px;">품번</th>';
html += '<th style="border: 1px solid #ddd; padding: 8px;">품명</th>';
html += '<th style="border: 1px solid #ddd; padding: 8px;">수주수량</th>';
html += '<th style="border: 1px solid #ddd; padding: 8px;">취소수량</th>';
html += '</tr></thead><tbody>';
items.forEach(function(item, idx){
var objid = item.OBJID || item.objid || '';
var partNo = item.PART_NO || item.part_no || '';
var partName = item.PART_NAME || item.part_name || '';
var orderQty = item.ORDER_QUANTITY || item.order_quantity || '0';
var cancelQty = item.CANCEL_QTY || item.cancel_qty || '';
var orderQtyNum = parseInt(orderQty) || 0;
var maxCancel = orderQtyNum > 0 ? orderQtyNum - 1 : 0;
html += '<tr>';
html += '<td style="border: 1px solid #ddd; padding: 8px; text-align: center;">' + partNo + '</td>';
html += '<td style="border: 1px solid #ddd; padding: 8px; text-align: left;">' + partName + '</td>';
html += '<td style="border: 1px solid #ddd; padding: 8px; text-align: right;">' + (orderQtyNum > 0 ? Number(orderQtyNum).toLocaleString() : '-') + '</td>';
html += '<td style="border: 1px solid #ddd; padding: 8px; text-align: center;">';
if(orderQtyNum > 0){
html += '<input type="number" class="cancel-qty-input" data-objid="' + objid + '" data-order-qty="' + orderQtyNum + '" ';
html += 'value="' + (cancelQty || '') + '" min="0" max="' + maxCancel + '" ';
html += 'style="width: 80px; text-align: right; padding: 4px;" placeholder="0">';
} else {
html += '-';
}
html += '</td>';
html += '</tr>';
});
html += '</tbody></table>';
html += '<p style="margin-top: 10px; color: #888; font-size: 12px;">※ 전체 수량 취소는 불가하며, 부분 수량만 취소 가능합니다.</p>';
html += '</div>';
Swal.fire({
title: '수주취소 수량 입력',
html: html,
width: '700px',
showCancelButton: true,
confirmButtonText: '저장',
cancelButtonText: '닫기',
confirmButtonColor: '#e74c3c',
preConfirm: function(){
var inputs = document.querySelectorAll('.cancel-qty-input');
var itemObjIds = [];
var cancelQtys = [];
var orderQtys = [];
var hasError = false;
inputs.forEach(function(input){
var val = input.value.trim();
var orderQty = parseInt(input.getAttribute('data-order-qty'));
var cancelVal = val === '' ? 0 : parseInt(val);
if(cancelVal < 0){
Swal.showValidationMessage('취소 수량은 0 이상이어야 합니다.');
hasError = true;
return;
}
if(cancelVal >= orderQty){
Swal.showValidationMessage('취소 수량(' + cancelVal + ')은 수주수량(' + orderQty + ')보다 적어야 합니다.');
hasError = true;
return;
}
itemObjIds.push(input.getAttribute('data-objid'));
cancelQtys.push(cancelVal);
orderQtys.push(orderQty);
});
if(hasError) return false;
return { itemObjIds: itemObjIds, cancelQtys: cancelQtys, orderQtys: orderQtys };
}
}).then(function(result){
if(result.isConfirmed && result.value){
var val = result.value;
$.ajax({
url: "/contractMgmt/saveOrderCancelQty.do",
type: "POST",
data: {
itemObjIds: val.itemObjIds.join(","),
cancelQtys: val.cancelQtys.join(","),
orderQtys: val.orderQtys.join(",")
},
dataType: "json",
success: function(res){
if(res.result === "true"){
Swal.fire({ title: '저장 완료', text: res.msg, icon: 'success' });
fn_search();
} else {
Swal.fire({ title: '저장 실패', text: res.msg, icon: 'error' });
}
},
error: function(){
Swal.fire("수주취소 저장 중 오류가 발생했습니다.");
}
});
}
});
},
error: function(){
Swal.fire("품목 정보 조회 중 오류가 발생했습니다.");
}
});
}
//코드값을 받아와서 동적으로 selectbox 생성
function optionJobGroup(code){
var val=code;
@@ -790,11 +945,12 @@ function openProjectFormPopUp(objId){
<h2>
<span>영업관리_주문서관리</span>
</h2>
<div class="btnArea">
<input type="button" value="조회" class="plm_btns" id="btnSearch" name="btnSearch">
<input type="button" value="수주등록" class="plm_btns btnRegist">
<input type="button" value="결재상신" class="plm_btns" id="btnApproval">
</div>
<div class="btnArea">
<input type="button" value="조회" class="plm_btns" id="btnSearch" name="btnSearch">
<input type="button" value="수주등록" class="plm_btns btnRegist">
<input type="button" value="수주취소" class="plm_btns" id="btnOrderCancel" style="background-color:#e74c3c; color:#fff;">
<input type="button" value="결재상신" class="plm_btns" id="btnApproval">
</div>
</div>
<div id="plmSearchZon">
<!-- 검색필터: 주문유형, 발주번호, 고객사, 품번, 품명, S/N, 수주상태, 발주일(기간), 요청납기(기간) -->

View File

@@ -75,6 +75,7 @@ body, html {
<div class="header">
<h3 style="margin: 0 0 10px 0; float: left;">구매리스트</h3>
<div style="float: right;">
<input type="button" class="plm_btns excelIcon excelBtn" value="Excel Download" title="Excel Download">
<input type="button" value="견적요청서 생성" class="plm_btns" onclick="fn_createQuotationRequest();" style="margin-right: 5px; background-color: #4CAF50; border-color: #4CAF50;">
<input type="button" value="저장" class="plm_btns" onclick="fn_save();" style="margin-right: 5px;">
<input type="button" value="닫기" class="plm_btns" onclick="window.close();" style="margin-right: 5px;">
@@ -565,13 +566,11 @@ function fn_initGrid() {
title: '<span style="background-color: #FFFF00; padding: 2px 5px;">공급업체</span>',
field: 'VENDOR_PM',
editor: function(cell, onRendered, success, cancel, editorParams) {
// Select2 에디터 (가공업체와 동일한 목록 사용)
return createSelect2Editor(processingVendorList)(cell, onRendered, success, cancel, editorParams);
},
formatter: function(cell) {
var value = cell.getValue();
var value = (typeof cell.getValue === 'function') ? cell.getValue() : (cell.value || '');
if(!value) return '';
// processingVendorList에서 해당 값의 이름 찾기
for(var i = 0; i < processingVendorList.length; i++) {
if(processingVendorList[i].id == value) {
return processingVendorList[i].text;
@@ -686,9 +685,8 @@ function fn_initGrid() {
return createSelect2Editor(processingVendorList)(cell, onRendered, success, cancel, editorParams);
},
formatter: function(cell) {
var value = cell.getValue();
var value = (typeof cell.getValue === 'function') ? cell.getValue() : (cell.value || '');
if(!value) return '';
for(var i = 0; i < processingVendorList.length; i++) {
if(processingVendorList[i].id == value) {
return processingVendorList[i].text;
@@ -783,6 +781,32 @@ function fn_initGrid() {
return value;
}
},
// 35. 소재발주일
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 100,
title: '소재발주일',
field: 'MATERIAL_PO_DATE',
formatter: function(cell) {
var value = (typeof cell.getValue === 'function') ? cell.getValue() : (cell.value || '');
if(!value) return '';
return value.length >= 10 ? value.substring(0, 10) : value;
}
},
// 36. 소재입고일
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 100,
title: '소재입고일',
field: 'MATERIAL_RECEIPT_DATE',
formatter: function(cell) {
var value = (typeof cell.getValue === 'function') ? cell.getValue() : (cell.value || '');
if(!value) return '';
return value.length >= 10 ? value.substring(0, 10) : value;
}
},
// 34. 가공 품의서작성일
{
headerHozAlign: 'center',
@@ -791,14 +815,39 @@ function fn_initGrid() {
title: '가공품의서일',
field: 'PROCESSING_PROPOSAL_DATE',
formatter: function(cell) {
var value = cell.getValue();
var value = (typeof cell.getValue === 'function') ? cell.getValue() : (cell.value || '');
if(!value) return '';
// YYYY-MM-DD 형식으로 표시
if(value.length >= 10) {
return value.substring(0, 10);
}
return value;
}
},
// 37. 가공발주일
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 100,
title: '가공발주일',
field: 'PROCESSING_PO_DATE',
formatter: function(cell) {
var value = (typeof cell.getValue === 'function') ? cell.getValue() : (cell.value || '');
if(!value) return '';
return value.length >= 10 ? value.substring(0, 10) : value;
}
},
// 38. 가공입고일
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 100,
title: '가공입고일',
field: 'PROCESSING_RECEIPT_DATE',
formatter: function(cell) {
var value = (typeof cell.getValue === 'function') ? cell.getValue() : (cell.value || '');
if(!value) return '';
return value.length >= 10 ? value.substring(0, 10) : value;
}
}
];
@@ -875,8 +924,9 @@ function fn_loadFromMBom(callback) {
url: "/salesMng/getMBomForPurchaseList.do",
method: 'post',
data: {
PROJECT_MGMT_OBJID: mbomHeaderObjid, // MBOM_HEADER_OBJID를 직접 사용
bomReportObjId: bomReportObjid
PROJECT_MGMT_OBJID: mbomHeaderObjid,
bomReportObjId: bomReportObjid,
SALES_REQUEST_MASTER_OBJID: salesRequestMasterObjid
},
dataType: 'json',
success: function(data) {

View File

@@ -91,10 +91,10 @@
fn_bulkRegister();
});
// 거래명세서 출력 버튼 (주석처리됨)
// $("#btnTransactionStatement").click(function(){
// fn_printTransactionStatement();
// });
// 거래명세서 출력 버튼
$("#btnTransactionStatement").click(function(){
fn_printTransactionStatement();
});
// 분할출하 버튼
$("#btnSplitShipment").click(function(){
@@ -710,9 +710,7 @@ body {
</h2>
<div class="btnArea">
<input type="button" value="조회" class="plm_btns" id="btnSearch">
<%-- 거래명세서 버튼 주석처리
<input type="button" value="거래명세서 생성" class="plm_btns" id="btnTransactionStatement" style="background-color: #2196F3; color: white;">
--%>
<input type="button" value="출하지시/판매등록" class="plm_btns" id="btnBulkRegister" style="background-color: #4CAF50; color: white;">
<!-- <input type="button" value="분할출하" class="plm_btns" id="btnSplitShipment" style="background-color: #FF9800; color: white;"> -->
</div>

View File

@@ -3187,7 +3187,51 @@ UPDATE SET
0 AS GRAND_TOTAL_PRICE,
SRP.PROPOSAL_DATE,
NULL AS PROCESSING_PROPOSAL_DATE,
'SRP' AS DATA_SOURCE -- 데이터 소스 구분용
-- 소재발주일: 소재품번으로 매칭
(SELECT POM.MAIL_SEND_DATE
FROM PURCHASE_ORDER_MASTER POM
JOIN PURCHASE_ORDER_PART POP ON POM.OBJID = POP.PURCHASE_ORDER_MASTER_OBJID
JOIN SALES_REQUEST_MASTER SRM_PO ON POM.SALES_REQUEST_OBJID = SRM_PO.OBJID
WHERE COALESCE(SRP.RAW_MATERIAL_PART_NO, '') != ''
AND POP.PART_OBJID::VARCHAR = (SELECT PM2.OBJID::VARCHAR FROM PART_MNG PM2 WHERE PM2.PART_NO = SRP.RAW_MATERIAL_PART_NO LIMIT 1)
AND SRM_PO.PROJECT_NO = (SELECT PROJECT_NO FROM SALES_REQUEST_MASTER WHERE OBJID = SRP.SALES_REQUEST_MASTER_OBJID)
AND POM.MAIL_SEND_DATE IS NOT NULL
ORDER BY POM.MAIL_SEND_DATE DESC
LIMIT 1) AS MATERIAL_PO_DATE,
-- 소재입고일
(SELECT SUBSTRING(AP.RECEIPT_DATE, 1, 10)
FROM ARRIVAL_PLAN AP
JOIN PURCHASE_ORDER_PART POP ON AP.ORDER_PART_OBJID = POP.OBJID
JOIN PURCHASE_ORDER_MASTER POM ON POP.PURCHASE_ORDER_MASTER_OBJID = POM.OBJID
JOIN SALES_REQUEST_MASTER SRM_PO ON POM.SALES_REQUEST_OBJID = SRM_PO.OBJID
WHERE COALESCE(SRP.RAW_MATERIAL_PART_NO, '') != ''
AND POP.PART_OBJID::VARCHAR = (SELECT PM2.OBJID::VARCHAR FROM PART_MNG PM2 WHERE PM2.PART_NO = SRP.RAW_MATERIAL_PART_NO LIMIT 1)
AND SRM_PO.PROJECT_NO = (SELECT PROJECT_NO FROM SALES_REQUEST_MASTER WHERE OBJID = SRP.SALES_REQUEST_MASTER_OBJID)
AND AP.RECEIPT_DATE IS NOT NULL AND AP.RECEIPT_DATE != ''
ORDER BY AP.RECEIPT_DATE DESC
LIMIT 1) AS MATERIAL_RECEIPT_DATE,
-- 가공발주일: 부품 PART_OBJID로 직접 매칭
(SELECT POM.MAIL_SEND_DATE
FROM PURCHASE_ORDER_MASTER POM
JOIN PURCHASE_ORDER_PART POP ON POM.OBJID = POP.PURCHASE_ORDER_MASTER_OBJID
JOIN SALES_REQUEST_MASTER SRM_PO ON POM.SALES_REQUEST_OBJID = SRM_PO.OBJID
WHERE POP.PART_OBJID::VARCHAR = SRP.PART_OBJID::VARCHAR
AND SRM_PO.PROJECT_NO = (SELECT PROJECT_NO FROM SALES_REQUEST_MASTER WHERE OBJID = SRP.SALES_REQUEST_MASTER_OBJID)
AND POM.MAIL_SEND_DATE IS NOT NULL
ORDER BY POM.MAIL_SEND_DATE DESC
LIMIT 1) AS PROCESSING_PO_DATE,
-- 가공입고일
(SELECT SUBSTRING(AP.RECEIPT_DATE, 1, 10)
FROM ARRIVAL_PLAN AP
JOIN PURCHASE_ORDER_PART POP ON AP.ORDER_PART_OBJID = POP.OBJID
JOIN PURCHASE_ORDER_MASTER POM ON POP.PURCHASE_ORDER_MASTER_OBJID = POM.OBJID
JOIN SALES_REQUEST_MASTER SRM_PO ON POM.SALES_REQUEST_OBJID = SRM_PO.OBJID
WHERE POP.PART_OBJID::VARCHAR = SRP.PART_OBJID::VARCHAR
AND SRM_PO.PROJECT_NO = (SELECT PROJECT_NO FROM SALES_REQUEST_MASTER WHERE OBJID = SRP.SALES_REQUEST_MASTER_OBJID)
AND AP.RECEIPT_DATE IS NOT NULL AND AP.RECEIPT_DATE != ''
ORDER BY AP.RECEIPT_DATE DESC
LIMIT 1) AS PROCESSING_RECEIPT_DATE,
'SRP' AS DATA_SOURCE
FROM
SALES_REQUEST_PART SRP
LEFT JOIN PART_MNG PM ON SRP.PART_OBJID::VARCHAR = PM.OBJID::VARCHAR
@@ -3519,6 +3563,58 @@ SELECT
G.PROCESSING_UNIT_PRICE,
G.PROCESSING_TOTAL_PRICE,
G.GRAND_TOTAL_PRICE,
<if test="SALES_REQUEST_MASTER_OBJID != null and !''.equals(SALES_REQUEST_MASTER_OBJID)">
-- 소재발주일: 소재품번(RAW_MATERIAL_PART_NO)으로 PART_MNG OBJID 조회 후 매칭
(SELECT POM.MAIL_SEND_DATE
FROM PURCHASE_ORDER_MASTER POM
JOIN PURCHASE_ORDER_PART POP ON POM.OBJID = POP.PURCHASE_ORDER_MASTER_OBJID
JOIN SALES_REQUEST_MASTER SRM_PO ON POM.SALES_REQUEST_OBJID = SRM_PO.OBJID
WHERE COALESCE(G.RAW_MATERIAL_PART_NO, '') != ''
AND POP.PART_OBJID::VARCHAR = (SELECT PM2.OBJID::VARCHAR FROM PART_MNG PM2 WHERE PM2.PART_NO = G.RAW_MATERIAL_PART_NO LIMIT 1)
AND SRM_PO.PROJECT_NO = (SELECT PROJECT_NO FROM SALES_REQUEST_MASTER WHERE OBJID = #{SALES_REQUEST_MASTER_OBJID})
AND POM.MAIL_SEND_DATE IS NOT NULL
ORDER BY POM.MAIL_SEND_DATE DESC
LIMIT 1) AS MATERIAL_PO_DATE,
-- 소재입고일
(SELECT SUBSTRING(AP.RECEIPT_DATE, 1, 10)
FROM ARRIVAL_PLAN AP
JOIN PURCHASE_ORDER_PART POP ON AP.ORDER_PART_OBJID = POP.OBJID
JOIN PURCHASE_ORDER_MASTER POM ON POP.PURCHASE_ORDER_MASTER_OBJID = POM.OBJID
JOIN SALES_REQUEST_MASTER SRM_PO ON POM.SALES_REQUEST_OBJID = SRM_PO.OBJID
WHERE COALESCE(G.RAW_MATERIAL_PART_NO, '') != ''
AND POP.PART_OBJID::VARCHAR = (SELECT PM2.OBJID::VARCHAR FROM PART_MNG PM2 WHERE PM2.PART_NO = G.RAW_MATERIAL_PART_NO LIMIT 1)
AND SRM_PO.PROJECT_NO = (SELECT PROJECT_NO FROM SALES_REQUEST_MASTER WHERE OBJID = #{SALES_REQUEST_MASTER_OBJID})
AND AP.RECEIPT_DATE IS NOT NULL AND AP.RECEIPT_DATE != ''
ORDER BY AP.RECEIPT_DATE DESC
LIMIT 1) AS MATERIAL_RECEIPT_DATE,
-- 가공발주일: 부품 PART_OBJID로 직접 매칭
(SELECT POM.MAIL_SEND_DATE
FROM PURCHASE_ORDER_MASTER POM
JOIN PURCHASE_ORDER_PART POP ON POM.OBJID = POP.PURCHASE_ORDER_MASTER_OBJID
JOIN SALES_REQUEST_MASTER SRM_PO ON POM.SALES_REQUEST_OBJID = SRM_PO.OBJID
WHERE POP.PART_OBJID::VARCHAR = G.PART_OBJID::VARCHAR
AND SRM_PO.PROJECT_NO = (SELECT PROJECT_NO FROM SALES_REQUEST_MASTER WHERE OBJID = #{SALES_REQUEST_MASTER_OBJID})
AND POM.MAIL_SEND_DATE IS NOT NULL
ORDER BY POM.MAIL_SEND_DATE DESC
LIMIT 1) AS PROCESSING_PO_DATE,
-- 가공입고일
(SELECT SUBSTRING(AP.RECEIPT_DATE, 1, 10)
FROM ARRIVAL_PLAN AP
JOIN PURCHASE_ORDER_PART POP ON AP.ORDER_PART_OBJID = POP.OBJID
JOIN PURCHASE_ORDER_MASTER POM ON POP.PURCHASE_ORDER_MASTER_OBJID = POM.OBJID
JOIN SALES_REQUEST_MASTER SRM_PO ON POM.SALES_REQUEST_OBJID = SRM_PO.OBJID
WHERE POP.PART_OBJID::VARCHAR = G.PART_OBJID::VARCHAR
AND SRM_PO.PROJECT_NO = (SELECT PROJECT_NO FROM SALES_REQUEST_MASTER WHERE OBJID = #{SALES_REQUEST_MASTER_OBJID})
AND AP.RECEIPT_DATE IS NOT NULL AND AP.RECEIPT_DATE != ''
ORDER BY AP.RECEIPT_DATE DESC
LIMIT 1) AS PROCESSING_RECEIPT_DATE,
</if>
<if test="SALES_REQUEST_MASTER_OBJID == null or ''.equals(SALES_REQUEST_MASTER_OBJID)">
NULL AS MATERIAL_PO_DATE,
NULL AS MATERIAL_RECEIPT_DATE,
NULL AS PROCESSING_PO_DATE,
NULL AS PROCESSING_RECEIPT_DATE,
</if>
'MBOM' AS DATA_SOURCE
FROM GROUPED_BOM G
LEFT JOIN PART_MNG P ON P.OBJID::VARCHAR = G.PART_OBJID::VARCHAR

View File

@@ -2853,6 +2853,25 @@ public class ContractMgmtController {
return resultMap;
}
/**
* 수주취소 수량 저장
*/
@ResponseBody
@RequestMapping(value="/contractMgmt/saveOrderCancelQty.do", method=RequestMethod.POST)
public Map saveOrderCancelQty(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
Map resultMap = new HashMap();
try {
resultMap = contractMgmtService.saveOrderCancelQty(request, paramMap);
} catch (Exception e) {
e.printStackTrace();
resultMap.put("result", "false");
resultMap.put("msg", "수주취소 저장 중 오류가 발생했습니다.");
}
return resultMap;
}
@RequestMapping("/contractMgmt/FileRegistPopup.do")
public String FileRegistPopup(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
request.setAttribute("docType", CommonUtils.checkNull(paramMap.get("docType")));

View File

@@ -717,6 +717,16 @@
AND ORDER_QUANTITY != ''
AND ORDER_QUANTITY != '0'
) AS HAS_ORDER_DATA
-- 수주취소 수량 합계
,(
SELECT COALESCE(SUM(CAST(NULLIF(CANCEL_QTY, '') AS NUMERIC)), 0)
FROM CONTRACT_ITEM
WHERE CONTRACT_OBJID = T.OBJID
AND STATUS = 'ACTIVE'
AND CANCEL_QTY IS NOT NULL
AND CANCEL_QTY != ''
AND CANCEL_QTY != '0'
) AS CANCEL_QTY_SUM
FROM
CONTRACT_MGMT AS T
LEFT OUTER JOIN
@@ -5092,7 +5102,8 @@ WHERE
CI.ORDER_UNIT_PRICE,
CI.ORDER_SUPPLY_PRICE,
CI.ORDER_VAT,
CI.ORDER_TOTAL_AMOUNT
CI.ORDER_TOTAL_AMOUNT,
CI.CANCEL_QTY
FROM
CONTRACT_ITEM CI
LEFT JOIN PART_MNG PM ON CI.PART_OBJID = PM.OBJID
@@ -5117,7 +5128,8 @@ WHERE
CI.ORDER_UNIT_PRICE,
CI.ORDER_SUPPLY_PRICE,
CI.ORDER_VAT,
CI.ORDER_TOTAL_AMOUNT
CI.ORDER_TOTAL_AMOUNT,
CI.CANCEL_QTY
ORDER BY CI.SEQ
</select>
@@ -5133,6 +5145,16 @@ WHERE
WHERE OBJID = #{contractItemObjId}
</update>
<!-- 수주취소 수량 업데이트 -->
<update id="updateContractItemCancelQty" parameterType="map">
UPDATE CONTRACT_ITEM
SET
CANCEL_QTY = #{cancelQty},
CHGDATE = NOW(),
CHG_USER_ID = #{userId}
WHERE OBJID = #{itemObjId}
</update>
<!-- ====================================
품목 관리 쿼리
==================================== -->

View File

@@ -3865,4 +3865,89 @@ private String encodeImageToBase64(String imagePath) {
e.printStackTrace();
}
}
/**
* 수주취소 수량 저장
* 품목별로 부분 취소 수량을 업데이트한다. (전체 취소 불가, 부분만 가능)
*/
public Map<String, Object> saveOrderCancelQty(HttpServletRequest request, Map<String, Object> paramMap) {
Map<String, Object> resultMap = new HashMap<String, Object>();
SqlSession sqlSession = null;
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession();
HttpSession session = request.getSession();
PersonBean person = (PersonBean) session.getAttribute(Constants.PERSON_BEAN);
String userId = person.getUserId();
String itemObjIdsStr = CommonUtils.checkNull(paramMap.get("itemObjIds"));
String cancelQtysStr = CommonUtils.checkNull(paramMap.get("cancelQtys"));
String orderQtysStr = CommonUtils.checkNull(paramMap.get("orderQtys"));
if ("".equals(itemObjIdsStr) || "".equals(cancelQtysStr)) {
resultMap.put("result", "false");
resultMap.put("msg", "취소 수량 정보가 없습니다.");
return resultMap;
}
String[] itemObjIds = itemObjIdsStr.split(",");
String[] cancelQtys = cancelQtysStr.split(",");
String[] orderQtys = orderQtysStr.split(",");
for (int i = 0; i < itemObjIds.length; i++) {
String cancelQty = cancelQtys[i].trim();
String orderQty = orderQtys[i].trim();
// 빈 값이면 스킵
if ("".equals(cancelQty) || "0".equals(cancelQty)) {
// 취소수량 0이면 초기화
Map<String, Object> updateParam = new HashMap<String, Object>();
updateParam.put("itemObjId", itemObjIds[i].trim());
updateParam.put("cancelQty", "");
updateParam.put("userId", userId);
sqlSession.update("contractMgmt.updateContractItemCancelQty", updateParam);
continue;
}
int cancelQtyInt = Integer.parseInt(cancelQty);
int orderQtyInt = Integer.parseInt(orderQty);
// 전체 수량 취소 불가 (부분만 가능)
if (cancelQtyInt >= orderQtyInt) {
resultMap.put("result", "false");
resultMap.put("msg", "수주취소 수량은 수주수량(" + orderQtyInt + ")보다 적어야 합니다.");
sqlSession.rollback();
return resultMap;
}
if (cancelQtyInt < 0) {
resultMap.put("result", "false");
resultMap.put("msg", "수주취소 수량은 0 이상이어야 합니다.");
sqlSession.rollback();
return resultMap;
}
Map<String, Object> updateParam = new HashMap<String, Object>();
updateParam.put("itemObjId", itemObjIds[i].trim());
updateParam.put("cancelQty", cancelQty);
updateParam.put("userId", userId);
sqlSession.update("contractMgmt.updateContractItemCancelQty", updateParam);
}
sqlSession.commit();
resultMap.put("result", "true");
resultMap.put("msg", "수주취소 수량이 저장되었습니다.");
} catch (Exception e) {
e.printStackTrace();
if (sqlSession != null) sqlSession.rollback();
resultMap.put("result", "false");
resultMap.put("msg", "수주취소 저장 중 오류가 발생했습니다.");
} finally {
if (sqlSession != null) sqlSession.close();
}
return resultMap;
}
}

View File

@@ -2168,8 +2168,8 @@ public class ApprovalService {
private String buildOrderContentsHtml(Map orderInfo, List<Map> itemList){
StringBuilder html = new StringBuilder();
String contractNo = CommonUtils.checkNull(orderInfo.get("CONTRACT_NO"));
String orderDate = CommonUtils.checkNull(orderInfo.get("ORDER_DATE"));
String poNo = escapeHtml(CommonUtils.checkNull(orderInfo.get("PO_NO")));
String clientNm = escapeHtml(CommonUtils.checkNull(orderInfo.get("CLIENT_NM")));
String clientBusRegNo = escapeHtml(CommonUtils.checkNull(orderInfo.get("CLIENT_BUS_REG_NO")));
String clientCeoNm = escapeHtml(CommonUtils.checkNull(orderInfo.get("CLIENT_CEO_NM")));
@@ -2183,63 +2183,79 @@ public class ApprovalService {
String vat = CommonUtils.checkNull(orderInfo.get("ORDER_VAT"));
String totalAmount = CommonUtils.checkNull(orderInfo.get("ORDER_TOTAL_AMOUNT"));
String vatNote = escapeHtml(CommonUtils.checkNull(orderInfo.get("VAT_NOTE")));
String regDatetime = escapeHtml(CommonUtils.checkNull(orderInfo.get("REG_DATETIME")));
final String TH = "style='border:1px solid #999; padding:4px 8px; background-color:#f5f5f5; text-align:center; font-weight:bold; font-size:12px;'";
final String TD = "style='border:1px solid #999; padding:4px 8px; font-size:12px;'";
final String TD_C = "style='border:1px solid #999; padding:4px 8px; font-size:12px; text-align:center;'";
final String TD_R = "style='border:1px solid #999; padding:4px 8px; font-size:12px; text-align:right;'";
final String S = "border:1px solid #000; padding:2px 4px; font-size:11px; vertical-align:middle;";
final String LBL = S + " text-align:center; font-weight:bold; background-color:#e8faff;";
final String VL = LBL + " writing-mode:vertical-rl; letter-spacing:5px; font-size:12px;";
html.append("<div style='font-family:맑은 고딕; max-width:800px; margin:0 auto;'>");
html.append("<h2 style='text-align:center; letter-spacing:15px;'>주 문 서</h2>");
html.append("<div style='font-family:맑은 고딕,Malgun Gothic,sans-serif; max-width:860px; margin:0 auto;'>");
html.append("<h2 style='text-align:center; letter-spacing:25px; border-bottom:2px solid #000; padding-bottom:8px;'>주 문 서</h2>");
html.append("<p style='font-size:11px; margin:2px 0;'>주문일자 : ").append(escapeHtml(orderDate)).append("</p>");
html.append("<p style='font-size:11px; margin:2px 0;'>증빙번호 : ").append(poNo).append("</p>");
// 헤더
String poNo = escapeHtml(CommonUtils.checkNull(orderInfo.get("PO_NO")));
html.append("<p style='font-size:12px;'>주문일자 : ").append(escapeHtml(orderDate)).append("</p>");
html.append("<p style='font-size:12px;'>증빙번호 : ").append(poNo).append("</p>");
// 공급받는자/공급자 테이블
html.append("<table style='width:100%; border-collapse:collapse; margin-bottom:5px;'>");
html.append("<tr><td rowspan='4' " + TH + " style='width:25px; writing-mode:vertical-rl;'>공급받는자</td>");
html.append("<td " + TH + ">등록번호</td><td " + TD + " colspan='2'>").append(clientBusRegNo).append("</td>");
html.append("<td rowspan='4' " + TH + " style='width:25px; writing-mode:vertical-rl;'>공급자</td>");
html.append("<td " + TH + ">등록번호</td><td " + TD + " colspan='2'>314-81-75146</td></tr>");
html.append("<tr><td " + TH + ">상호</td><td " + TD + ">").append(clientNm).append("</td>");
html.append("<td " + TH + " style='width:35px;'>성명</td><td " + TD + ">").append(clientCeoNm).append("</td>");
html.append("<td " + TH + ">상호</td><td " + TD + ">주식회사알피에스본사</td>");
html.append("<td " + TH + " style='width:35px;'>성명</td><td " + TD + ">이동헌</td></tr>");
html.append("<tr><td " + TH + ">주소</td><td " + TD + " colspan='2'>").append(clientAddr).append("</td>");
html.append("<td " + TH + ">주소</td><td " + TD + " colspan='2'>대전광역시 유성구 국제과학10로 8(둔곡동)</td></tr>");
html.append("<tr><td " + TH + ">업태</td><td " + TD + ">").append(clientBusType).append("</td>");
html.append("<td " + TH + " style='width:35px;'>종목</td><td " + TD + ">").append(clientBusItem).append("</td>");
html.append("<td " + TH + ">업태</td><td " + TD + ">제조업</td>");
html.append("<td " + TH + " style='width:35px;'>종목</td><td " + TD + ">금속절삭가공기계,반도체제조용기계</td></tr>");
html.append("</table>");
// 납품처/담당자
html.append("<table style='width:100%; border-collapse:collapse; margin-bottom:5px;'>");
html.append("<tr><td " + TH + ">납품처</td><td " + TD + ">").append(clientNm).append("</td>");
html.append("<td " + TH + ">전화번호</td><td " + TD + ">").append(clientTelNo).append("</td>");
html.append("<td " + TH + ">팩스번호</td><td " + TD + ">").append(clientFaxNo).append("</td></tr>");
html.append("<tr><td " + TH + ">주소</td><td " + TD + ">").append(clientAddr).append("</td>");
html.append("<td " + TH + ">담당자</td><td " + TD + ">").append(writerName).append("</td>");
html.append("<td " + TH + ">C.P.번호</td><td " + TD + "></td></tr>");
html.append("</table>");
// 품목 테이블
html.append("<table style='width:100%; border-collapse:collapse;'>");
// 공급받는자 / 공급자 (10컬럼: 세로3% + 라벨8% + 값auto + 라벨6% + 값9% × 2세트)
html.append("<table style='width:100%; border-collapse:collapse; table-layout:fixed; margin-top:2px;'>");
html.append("<colgroup>");
html.append("<col style='width:3%;'><col style='width:8%;'><col style='width:20%;'><col style='width:6%;'><col style='width:9%;'>");
html.append("<col style='width:3%;'><col style='width:8%;'><col style='width:20%;'><col style='width:6%;'><col style='width:12%;'>");
html.append("</colgroup>");
// row1: 등록번호
html.append("<tr>");
html.append("<th " + TH + " style='width:30px;'>No.</th>");
html.append("<th " + TH + " style='width:90px;'>품번</th>");
html.append("<th " + TH + ">품명</th>");
html.append("<th " + TH + ">규격</th>");
html.append("<th " + TH + " style='width:40px;'>단위</th>");
html.append("<th " + TH + " style='width:75px;'>납기일</th>");
html.append("<th " + TH + " style='width:50px;'>수량</th>");
html.append("<th " + TH + " style='width:70px;'>단가</th>");
html.append("<th " + TH + " style='width:80px;'>금액</th>");
html.append("<td rowspan='4' style='").append(VL).append("'>공급받는자</td>");
html.append("<td style='").append(LBL).append("'>등록번호</td>");
html.append("<td colspan='3' style='").append(S).append("'>").append(clientBusRegNo).append("</td>");
html.append("<td rowspan='4' style='").append(VL).append("'>공 급 자</td>");
html.append("<td style='").append(LBL).append("'>등록번호</td>");
html.append("<td colspan='3' style='").append(S).append("'>314-81-75146</td>");
html.append("</tr>");
// row2: 상호/성명
html.append("<tr>");
html.append("<td style='").append(LBL).append("'>상 호</td><td style='").append(S).append("'>").append(clientNm).append("</td>");
html.append("<td style='").append(LBL).append("'>성명</td><td style='").append(S).append("'>").append(clientCeoNm).append("</td>");
html.append("<td style='").append(LBL).append("'>상 호</td><td style='").append(S).append("'>주식회사알피에스본사</td>");
html.append("<td style='").append(LBL).append("'>성명</td><td style='").append(S).append("'>이동헌</td>");
html.append("</tr>");
// row3: 주소
html.append("<tr>");
html.append("<td style='").append(LBL).append("'>주 소</td><td colspan='3' style='").append(S).append("'>").append(clientAddr).append("</td>");
html.append("<td style='").append(LBL).append("'>주 소</td><td colspan='3' style='").append(S).append("'>대전광역시 유성구 국제과학10로 8(둔곡동)</td>");
html.append("</tr>");
// row4: 업태/종목
html.append("<tr>");
html.append("<td style='").append(LBL).append("'>업 태</td><td style='").append(S).append("'>").append(clientBusType).append("</td>");
html.append("<td style='").append(LBL).append("'>종목</td><td style='").append(S).append("'>").append(clientBusItem).append("</td>");
html.append("<td style='").append(LBL).append("'>업 태</td><td style='").append(S).append("'>제조업</td>");
html.append("<td style='").append(LBL).append("'>종목</td><td style='").append(S).append(" font-size:9px;'>금속절삭가공기계,반도체제조용기계</td>");
html.append("</tr></table>");
// 납품처 / 담당자 (6컬럼)
html.append("<table style='width:100%; border-collapse:collapse; table-layout:fixed; margin-top:-1px;'>");
html.append("<colgroup><col style='width:9%;'><col><col style='width:9%;'><col style='width:16%;'><col style='width:9%;'><col style='width:16%;'></colgroup>");
html.append("<tr>");
html.append("<td style='").append(LBL).append("'>납 품 처</td><td style='").append(S).append("'>").append(clientNm).append("</td>");
html.append("<td style='").append(LBL).append("'>전화번호</td><td style='").append(S).append("'>").append(clientTelNo).append("</td>");
html.append("<td style='").append(LBL).append("'>팩스번호</td><td style='").append(S).append("'>").append(clientFaxNo).append("</td>");
html.append("</tr><tr>");
html.append("<td style='").append(LBL).append("'>주 소</td><td style='").append(S).append("'>").append(clientAddr).append("</td>");
html.append("<td style='").append(LBL).append("'>담 당 자</td><td style='").append(S).append("'>").append(writerName).append("</td>");
html.append("<td style='").append(LBL).append("'>C.P.번호</td><td style='").append(S).append("'></td>");
html.append("</tr></table>");
// 품목 테이블 (9컬럼)
html.append("<table style='width:100%; border-collapse:collapse; table-layout:fixed; margin-top:-1px;'>");
html.append("<colgroup><col style='width:4%;'><col style='width:11%;'><col style='width:20%;'><col style='width:18%;'>");
html.append("<col style='width:5%;'><col style='width:10%;'><col style='width:6%;'><col style='width:10%;'><col style='width:10%;'></colgroup>");
html.append("<tr>");
html.append("<th style='").append(LBL).append("'>No.</th>");
html.append("<th style='").append(LBL).append("'>품번</th>");
html.append("<th style='").append(LBL).append("'>품명</th>");
html.append("<th style='").append(LBL).append("'>규격</th>");
html.append("<th style='").append(LBL).append("'>단위</th>");
html.append("<th style='").append(LBL).append("'>납기일</th>");
html.append("<th style='").append(LBL).append("'>수량</th>");
html.append("<th style='").append(LBL).append("'>단가</th>");
html.append("<th style='").append(LBL).append("'>금액</th>");
html.append("</tr>");
int totalQty = 0;
@@ -2253,40 +2269,42 @@ public class ApprovalService {
String itemSupply = CommonUtils.checkNull(item.get("ORDER_SUPPLY_PRICE"));
html.append("<tr>");
html.append("<td " + TD_C + ">").append(i + 1).append("</td>");
html.append("<td " + TD_C + ">").append(escapeHtml(CommonUtils.checkNull(item.get("PART_NO")))).append("</td>");
html.append("<td " + TD + ">").append(escapeHtml(CommonUtils.checkNull(item.get("PART_NAME")))).append("</td>");
html.append("<td " + TD + ">").append(escapeHtml(CommonUtils.checkNull(item.get("SPEC")))).append("</td>");
html.append("<td " + TD_C + ">").append(escapeHtml(CommonUtils.checkNull(item.get("UNIT_NAME")))).append("</td>");
html.append("<td " + TD_C + ">").append(escapeHtml(CommonUtils.checkNull(item.get("DUE_DATE")))).append("</td>");
html.append("<td " + TD_R + ">").append(formatNumber(qty)).append("</td>");
html.append("<td " + TD_R + ">").append(formatNumber(unitPrice)).append("</td>");
html.append("<td " + TD_R + ">").append(formatNumber(itemSupply)).append("</td>");
html.append("<td style='").append(S).append(" text-align:center;'>").append(i + 1).append("</td>");
html.append("<td style='").append(S).append(" text-align:center;'>").append(escapeHtml(CommonUtils.checkNull(item.get("PART_NO")))).append("</td>");
html.append("<td style='").append(S).append("'>").append(escapeHtml(CommonUtils.checkNull(item.get("PART_NAME")))).append("</td>");
html.append("<td style='").append(S).append("'>").append(escapeHtml(CommonUtils.checkNull(item.get("SPEC")))).append("</td>");
html.append("<td style='").append(S).append(" text-align:center;'>").append(escapeHtml(CommonUtils.checkNull(item.get("UNIT_NAME")))).append("</td>");
html.append("<td style='").append(S).append(" text-align:center;'>").append(escapeHtml(CommonUtils.checkNull(item.get("DUE_DATE")))).append("</td>");
html.append("<td style='").append(S).append(" text-align:right;'>").append(formatNumber(qty)).append("</td>");
html.append("<td style='").append(S).append(" text-align:right;'>").append(formatNumber(unitPrice)).append("</td>");
html.append("<td style='").append(S).append(" text-align:right;'>").append(formatNumber(itemSupply)).append("</td>");
html.append("</tr>");
}
}
// 합계
html.append("<tr style='background-color:#ffffcc;'>");
html.append("<td " + TD_C + " colspan='6' style='font-weight:bold;'>합 계</td>");
html.append("<td " + TD_R + " style='font-weight:bold;'>").append(formatNumber(totalQty)).append("</td>");
html.append("<td " + TD_R + "></td>");
html.append("<td " + TD_R + " style='font-weight:bold;'>").append(formatNumber(supplyPrice)).append("</td>");
// 합계
html.append("<tr><td colspan='9' style='").append(S).append(" text-align:center; background-color:#ffffcc; font-weight:bold; letter-spacing:8px;'>합 계</td></tr>");
html.append("<tr>");
html.append("<td colspan='6' style='").append(S).append(" text-align:center; font-weight:bold;'></td>");
html.append("<td style='").append(S).append(" text-align:right; font-weight:bold;'>").append(formatNumber(totalQty)).append("</td>");
html.append("<td style='").append(S).append(" text-align:right;'></td>");
html.append("<td style='").append(S).append(" text-align:right; font-weight:bold;'>").append(formatNumber(supplyPrice)).append("</td>");
html.append("</tr></table>");
// 비고(합계 요약)
html.append("<table style='width:100%; border-collapse:collapse;'>");
html.append("<tr><td rowspan='3' " + TH + " style='width:40px; writing-mode:vertical-rl; letter-spacing:8px; font-size:13px;'>비 고</td>");
html.append("<td rowspan='3' " + TD + "></td>");
html.append("<td " + TH + " style='width:120px; letter-spacing:2px;'>공 급 가 액 합 계</td><td " + TD_R + " style='width:150px;'>").append(formatNumber(supplyPrice)).append("</td></tr>");
html.append("<tr><td " + TH + " style='letter-spacing:2px;'>부 가 가 치 세</td><td " + TD_R + ">").append(formatNumber(vat)).append("</td></tr>");
html.append("<tr><td " + TH + " style='letter-spacing:5px;'>총 계</td><td " + TD_R + " style='font-weight:bold;'>").append(formatNumber(totalAmount)).append("</td></tr>");
// 비고(합계 요약) - 4컬럼
html.append("<table style='width:100%; border-collapse:collapse; table-layout:fixed; margin-top:-1px;'>");
html.append("<colgroup><col style='width:5%;'><col><col style='width:15%;'><col style='width:19%;'></colgroup>");
html.append("<tr><td rowspan='3' style='").append(VL).append(" letter-spacing:8px; font-size:13px;'>비 고</td>");
html.append("<td rowspan='3' style='").append(S).append("'></td>");
html.append("<td style='").append(LBL).append(" letter-spacing:2px;'>공 급 가 액 합 계</td><td style='").append(S).append(" text-align:right;'>").append(formatNumber(supplyPrice)).append("</td></tr>");
html.append("<tr><td style='").append(LBL).append(" letter-spacing:2px;'>부 가 가 치 세</td><td style='").append(S).append(" text-align:right;'>").append(formatNumber(vat)).append("</td></tr>");
html.append("<tr><td style='").append(LBL).append(" letter-spacing:5px;'>총 계</td><td style='").append(S).append(" text-align:right; font-weight:bold;'>").append(formatNumber(totalAmount)).append("</td></tr>");
html.append("</table>");
// 하단 부가세 구분 + 날짜
html.append("<div style='display:flex; justify-content:space-between; padding:3px 5px; font-size:11px; border:1px solid #999; border-top:none;'>");
html.append("<span>").append(vatNote).append("</span>");
html.append("<span>").append(escapeHtml(CommonUtils.checkNull(orderInfo.get("REG_DATETIME")))).append("</span>");
html.append("</div>");
html.append("<table style='width:100%; border-collapse:collapse; margin-top:-1px;'><tr>");
html.append("<td style='").append(S).append(" border-top:none;'>").append(vatNote).append("</td>");
html.append("<td style='").append(S).append(" border-top:none; text-align:right;'>").append(regDatetime).append("</td>");
html.append("</tr></table>");
html.append("</div>");
@@ -2301,6 +2319,7 @@ public class ApprovalService {
String contractNo = CommonUtils.checkNull(orderInfo.get("CONTRACT_NO"));
String orderDate = CommonUtils.checkNull(orderInfo.get("ORDER_DATE"));
String poNo = CommonUtils.checkNull(orderInfo.get("PO_NO"));
String clientNm = CommonUtils.checkNull(orderInfo.get("CLIENT_NM"));
String clientBusRegNo = CommonUtils.checkNull(orderInfo.get("CLIENT_BUS_REG_NO"));
String clientCeoNm = CommonUtils.checkNull(orderInfo.get("CLIENT_CEO_NM"));
@@ -2319,55 +2338,54 @@ public class ApprovalService {
html.append("<!DOCTYPE html><html><head><meta charset='UTF-8'>");
html.append("<title>주문서 - ").append(contractNo).append("</title>");
html.append("<style>");
html.append("body { font-family: '맑은 고딕', sans-serif; font-size: 12px; margin: 20px 40px; }");
html.append(".title { text-align: center; font-size: 26px; font-weight: bold; letter-spacing: 18px; margin-bottom: 10px; }");
html.append(".header { font-size: 12px; margin-bottom: 8px; }");
html.append("table { width: 100%; border-collapse: collapse; margin-bottom: 5px; }");
html.append("th, td { border: 1px solid #000; padding: 3px 6px; font-size: 11px; }");
html.append("th { background-color: #f0f0f0; text-align: center; font-weight: bold; }");
html.append(".center { text-align: center; }");
html.append(".right { text-align: right; }");
html.append(".total-row td { background-color: #ffffcc; font-weight: bold; }");
html.append(".vl { writing-mode: vertical-rl; letter-spacing: 3px; width: 25px; text-align: center; font-weight: bold; background-color: #f0f0f0; }");
html.append("body { font-family:'맑은 고딕',sans-serif; font-size:12px; margin:15px 20px; }");
html.append("table { width:100%; border-collapse:collapse; }");
html.append("td,th { border:1px solid #000; padding:2px 4px; font-size:11px; vertical-align:middle; word-break:break-all; }");
html.append(".lbl { text-align:center; font-weight:bold; background-color:#e8faff; }");
html.append(".vl { text-align:center; font-weight:bold; background-color:#e8faff; writing-mode:vertical-rl; letter-spacing:5px; font-size:12px; }");
html.append(".tc { text-align:center; } .tr { text-align:right; }");
html.append("</style></head><body>");
html.append("<div class='title'>주 문 서</div>");
String poNo = CommonUtils.checkNull(orderInfo.get("PO_NO"));
html.append("<div class='header'>주문일자 : ").append(orderDate).append("</div>");
html.append("<div class='header'>증빙번호 : ").append(poNo).append("</div>");
html.append("<div style='max-width:860px; margin:0 auto;'>");
html.append("<div style='text-align:center; font-size:24px; font-weight:bold; letter-spacing:25px; padding:10px 0 8px 0; border-bottom:2px solid #000; margin-bottom:3px;'>주 문 서</div>");
html.append("<div style='font-size:11px; padding:2px 0;'>주문일자 : ").append(orderDate).append("</div>");
html.append("<div style='font-size:11px; padding:2px 0;'>증빙번호 : ").append(poNo).append("</div>");
// 공급받는자/공급자
html.append("<table>");
html.append("<tr><td class='vl' rowspan='4'>공<br/>급<br/>받<br/>는<br/>자</td>");
html.append("<th style='width:55px;'>등록번호</th><td colspan='2'>").append(clientBusRegNo).append("</td>");
html.append("<td class='vl' rowspan='4'>공<br/>급<br/>자</td>");
html.append("<th style='width:55px;'>등록번호</th><td colspan='2'>314-81-75146</td></tr>");
html.append("<tr><th>상 호</th><td>").append(clientNm).append("</td><th style='width:40px;'>성명</th><td>").append(clientCeoNm).append("</td>");
html.append("<th>상 호</th><td>주식회사알피에스본사</td><th style='width:40px;'>성명</th><td>이동헌</td></tr>");
html.append("<tr><th>주 소</th><td colspan='2'>").append(clientAddr).append("</td>");
html.append("<th>주 소</th><td colspan='2'>대전광역시 유성구 국제과학10로 8(둔곡동)</td></tr>");
html.append("<tr><th>업 태</th><td>").append(clientBusType).append("</td><th style='width:40px;'>종목</th><td>").append(clientBusItem).append("</td>");
html.append("<th>업 태</th><td>제조업</td><th style='width:40px;'>종목</th><td>금속절삭가공기계,반도체제조용기계</td></tr>");
// 공급받는자 / 공급자 (10컬럼)
html.append("<table style='margin-top:2px; table-layout:fixed;'>");
html.append("<colgroup>");
html.append("<col style='width:28px;'><col style='width:62px;'><col><col style='width:45px;'><col style='width:70px;'>");
html.append("<col style='width:28px;'><col style='width:62px;'><col><col style='width:45px;'><col style='width:90px;'>");
html.append("</colgroup>");
html.append("<tr>");
html.append("<td rowspan='4' class='vl'>공급받는자</td>");
html.append("<td class='lbl'>등록번호</td><td colspan='3'>").append(clientBusRegNo).append("</td>");
html.append("<td rowspan='4' class='vl'>공 급 자</td>");
html.append("<td class='lbl'>등록번호</td><td colspan='3'>314-81-75146</td></tr>");
html.append("<tr><td class='lbl'>상 호</td><td>").append(clientNm).append("</td><td class='lbl'>성명</td><td>").append(clientCeoNm).append("</td>");
html.append("<td class='lbl'>상 호</td><td>주식회사알피에스본사</td><td class='lbl'>성명</td><td>이동헌</td></tr>");
html.append("<tr><td class='lbl'>주 소</td><td colspan='3'>").append(clientAddr).append("</td>");
html.append("<td class='lbl'>주 소</td><td colspan='3'>대전광역시 유성구 국제과학10로 8(둔곡동)</td></tr>");
html.append("<tr><td class='lbl'>업 태</td><td>").append(clientBusType).append("</td><td class='lbl'>종목</td><td>").append(clientBusItem).append("</td>");
html.append("<td class='lbl'>업 태</td><td>제조업</td><td class='lbl'>종목</td><td style='font-size:9px;'>금속절삭가공기계,반도체제조용기계</td></tr>");
html.append("</table>");
// 납품처/담당자
html.append("<table>");
html.append("<tr><th style='width:70px;'>납 품 처</th><td>").append(clientNm).append("</td>");
html.append("<th style='width:70px;'>전화번호</th><td>").append(clientTelNo).append("</td>");
html.append("<th style='width:70px;'>팩스번호</th><td>").append(clientFaxNo).append("</td></tr>");
html.append("<tr><th>주 소</th><td>").append(clientAddr).append("</td>");
html.append("<th>담 당 자</th><td>").append(writerName).append("</td>");
html.append("<th>C.P.번호</th><td></td></tr>");
// 납품처 / 담당자 (6컬럼)
html.append("<table style='margin-top:-1px; table-layout:fixed;'>");
html.append("<colgroup><col style='width:70px;'><col><col style='width:70px;'><col style='width:130px;'><col style='width:70px;'><col style='width:130px;'></colgroup>");
html.append("<tr><td class='lbl'>납 품 처</td><td>").append(clientNm).append("</td>");
html.append("<td class='lbl'>전화번호</td><td>").append(clientTelNo).append("</td>");
html.append("<td class='lbl'>팩스번호</td><td>").append(clientFaxNo).append("</td></tr>");
html.append("<tr><td class='lbl'>주 소</td><td>").append(clientAddr).append("</td>");
html.append("<td class='lbl'>담 당 자</td><td>").append(writerName).append("</td>");
html.append("<td class='lbl'>C.P.번호</td><td></td></tr>");
html.append("</table>");
// 품목
html.append("<table>");
html.append("<tr><th style='width:30px;'>No.</th><th style='width:90px;'>품번</th><th>품명</th><th>규격</th>");
html.append("<th style='width:40px;'>단위</th><th style='width:75px;'>납기일</th>");
html.append("<th style='width:50px;'>수량</th><th style='width:70px;'>단가</th><th style='width:80px;'>금액</th></tr>");
// 품목 (9컬럼)
html.append("<table style='margin-top:-1px; table-layout:fixed;'>");
html.append("<colgroup><col style='width:32px;'><col style='width:85px;'><col><col><col style='width:38px;'><col style='width:78px;'><col style='width:48px;'><col style='width:68px;'><col style='width:78px;'></colgroup>");
html.append("<tr><th class='lbl'>No.</th><th class='lbl'>품번</th><th class='lbl'>품명</th><th class='lbl'>규격</th>");
html.append("<th class='lbl'>단위</th><th class='lbl'>납기일</th><th class='lbl'>수량</th><th class='lbl'>단가</th><th class='lbl'>금액</th></tr>");
int totalQty = 0;
if(itemList != null){
@@ -2378,37 +2396,41 @@ public class ApprovalService {
totalQty += qty;
html.append("<tr>");
html.append("<td class='center'>").append(i + 1).append("</td>");
html.append("<td class='center'>").append(CommonUtils.checkNull(item.get("PART_NO"))).append("</td>");
html.append("<td class='tc'>").append(i + 1).append("</td>");
html.append("<td class='tc'>").append(CommonUtils.checkNull(item.get("PART_NO"))).append("</td>");
html.append("<td>").append(CommonUtils.checkNull(item.get("PART_NAME"))).append("</td>");
html.append("<td>").append(CommonUtils.checkNull(item.get("SPEC"))).append("</td>");
html.append("<td class='center'>").append(CommonUtils.checkNull(item.get("UNIT_NAME"))).append("</td>");
html.append("<td class='center'>").append(CommonUtils.checkNull(item.get("DUE_DATE"))).append("</td>");
html.append("<td class='right'>").append(formatNumber(qty)).append("</td>");
html.append("<td class='right'>").append(formatNumber(CommonUtils.checkNull(item.get("ORDER_UNIT_PRICE")))).append("</td>");
html.append("<td class='right'>").append(formatNumber(CommonUtils.checkNull(item.get("ORDER_SUPPLY_PRICE")))).append("</td>");
html.append("<td class='tc'>").append(CommonUtils.checkNull(item.get("UNIT_NAME"))).append("</td>");
html.append("<td class='tc'>").append(CommonUtils.checkNull(item.get("DUE_DATE"))).append("</td>");
html.append("<td class='tr'>").append(formatNumber(qty)).append("</td>");
html.append("<td class='tr'>").append(formatNumber(CommonUtils.checkNull(item.get("ORDER_UNIT_PRICE")))).append("</td>");
html.append("<td class='tr'>").append(formatNumber(CommonUtils.checkNull(item.get("ORDER_SUPPLY_PRICE")))).append("</td>");
html.append("</tr>");
}
}
html.append("<tr class='total-row'><td colspan='6' class='center'>합 계</td>");
html.append("<td class='right'>").append(formatNumber(totalQty)).append("</td><td></td>");
html.append("<td class='right'>").append(formatNumber(supplyPrice)).append("</td></tr>");
// 합계
html.append("<tr><td colspan='9' style='text-align:center; background-color:#ffffcc; font-weight:bold; letter-spacing:8px;'>합 계</td></tr>");
html.append("<tr><td colspan='6' class='tc' style='font-weight:bold;'></td>");
html.append("<td class='tr' style='font-weight:bold;'>").append(formatNumber(totalQty)).append("</td><td class='tr'></td>");
html.append("<td class='tr' style='font-weight:bold;'>").append(formatNumber(supplyPrice)).append("</td></tr>");
html.append("</table>");
// 비고
html.append("<table>");
html.append("<tr><td class='vl' rowspan='3' style='letter-spacing:8px; font-size:13px;'>비 고</td>");
// 비고 (4컬럼)
html.append("<table style='margin-top:-1px; table-layout:fixed;'>");
html.append("<colgroup><col style='width:40px;'><col><col style='width:120px;'><col style='width:150px;'></colgroup>");
html.append("<tr><td rowspan='3' class='vl' style='letter-spacing:8px; font-size:13px;'>비 고</td>");
html.append("<td rowspan='3'></td>");
html.append("<th style='width:120px; letter-spacing:2px;'>공 급 가 액 합 계</th><td class='right' style='width:150px;'>").append(formatNumber(supplyPrice)).append("</td></tr>");
html.append("<tr><th style='letter-spacing:2px;'>부 가 가 치 세</th><td class='right'>").append(formatNumber(vat)).append("</td></tr>");
html.append("<tr><th style='letter-spacing:5px;'>총 계</th><td class='right' style='font-weight:bold;'>").append(formatNumber(totalAmount)).append("</td></tr>");
html.append("<td class='lbl' style='letter-spacing:2px;'>공 급 가 액 합 계</td><td class='tr'>").append(formatNumber(supplyPrice)).append("</td></tr>");
html.append("<tr><td class='lbl' style='letter-spacing:2px;'>부 가 가 치 세</td><td class='tr'>").append(formatNumber(vat)).append("</td></tr>");
html.append("<tr><td class='lbl' style='letter-spacing:5px;'>총 계</td><td class='tr' style='font-weight:bold;'>").append(formatNumber(totalAmount)).append("</td></tr>");
html.append("</table>");
// 하단 부가세 구분 + 날짜
html.append("<div style='display:flex; justify-content:space-between; padding:3px 5px; font-size:11px; border:1px solid #000; border-top:none;'>");
html.append("<span>").append(vatNote).append("</span>");
html.append("<span>").append(regDatetime).append("</span>");
html.append("</div>");
html.append("</div>");
html.append("</body></html>");