Merge branch 'main' into feature/20251113-hj

This commit is contained in:
2025-11-14 09:51:05 +09:00
6 changed files with 341 additions and 13 deletions

View File

@@ -927,12 +927,17 @@
) AS MANAGER,
COALESCE(SR.incoterms, '') AS INCOTERMS,
T.SALES_STATUS,
T.OBJID::VARCHAR AS SALE_NO,
'ORIGINAL' AS RECORD_TYPE,
'' AS SPLIT_LOG_ID
FROM PROJECT_MGMT AS T
LEFT JOIN sales_registration SR ON T.PROJECT_NO = SR.project_no
WHERE 1 = 1
T.OBJID::VARCHAR AS SALE_NO,
'ORIGINAL' AS RECORD_TYPE,
'' AS SPLIT_LOG_ID,
-- 거래명세서 존재 여부
CASE WHEN EXISTS(
SELECT 1 FROM NSWOS100_TBL
WHERE OdOrderNo = T.PROJECT_NO
) THEN 'Y' ELSE 'N' END AS HAS_TRANSACTION_STATEMENT
FROM PROJECT_MGMT AS T
LEFT JOIN sales_registration SR ON T.PROJECT_NO = SR.project_no
WHERE 1 = 1
AND T.PROJECT_NO IS NOT NULL
AND T.PROJECT_NO != ''
<if test="orderType != null and orderType != ''">
@@ -1906,5 +1911,28 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
ORDER BY CIS.SERIAL_NO
</select>
<select id="getSavedTransactionStatement" parameterType="map" resultType="map">
/* salesNcollectMgmt.getSavedTransactionStatement - 저장된 거래명세서 조회 */
SELECT
SuVndCd,
IssueDt,
IssueNo,
IsNo,
ProdCd,
OdOrderNo,
ImItemId,
RmDueDt,
RmOrderQty,
RmRcptQty,
RmRemQty,
IsDt,
IsQty,
IsPrice,
IsAmount
FROM NSWOS100_TBL
WHERE OdOrderNo = #{projectNo}
ORDER BY IsNo
</select>
</mapper>

View File

@@ -187,6 +187,9 @@
return;
}
// 거래명세서 출력 버튼은 항상 새로 작성 (저장된 데이터 무시)
localStorage.setItem('loadSavedStatement', 'false');
// 같은 거래처인지 확인
var firstCustomer = selectedData[0].CUSTOMER;
@@ -328,7 +331,32 @@ var columns = [
formatter: "money", formatterParams: {thousand: ",", symbolAfter: "", precision: 2}
},
{headerHozAlign : 'center', hozAlign : 'center', width : '120', title : 'S/N', field : 'SERIAL_NO'},
{headerHozAlign : 'center', hozAlign : 'center', width : '120', title : '품번', field : 'PRODUCT_NO'}
{headerHozAlign : 'center', hozAlign : 'center', width : '120', title : '품번', field : 'PRODUCT_NO'},
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '거래명세서', field : 'TRANSACTION_STATEMENT',
formatter: function(cell) {
var data = cell.getRow().getData();
var projectNo = data.PROJECT_NO;
var hasStatement = data.HAS_TRANSACTION_STATEMENT === 'Y';
if(projectNo) {
if(hasStatement) {
return '<img src="/images/folder_blue.png" height="25px" width="25px" style="cursor:pointer;" title="거래명세서 보기">';
} else {
return '<img src="/images/file_empty.png" height="25px" width="25px" style="cursor:pointer;" title="거래명세서 작성">';
}
}
return '';
},
cellClick: function(e, cell) {
var data = cell.getRow().getData();
var projectNo = data.PROJECT_NO;
var hasStatement = data.HAS_TRANSACTION_STATEMENT === 'Y';
if(projectNo) {
fn_openTransactionStatementPopup(projectNo, hasStatement);
}
}
}
// {headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '제품구분', field : 'PRODUCT_TYPE'},
// {headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '국내/해외', field : 'NATION'},
// {headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '접수일', field : 'RECEIPT_DATE'},
@@ -428,6 +456,57 @@ function fn_search(){
}
// 출하일 상세 내역 팝업
// 거래명세서 팝업 열기 (단일 프로젝트)
function fn_openTransactionStatementPopup(projectNo, hasStatement) {
console.log("=== 거래명세서 팝업 열기 ===");
console.log("projectNo:", projectNo);
console.log("hasStatement:", hasStatement);
if(!projectNo) {
alert("프로젝트 번호가 없습니다.");
return;
}
// 흰색 폴더(저장 안 된 경우) 클릭 시 아무것도 안 함
if(!hasStatement) {
console.log("저장된 거래명세서가 없습니다. 팝업을 열지 않습니다.");
return;
}
// 해당 프로젝트의 데이터를 찾아서 localStorage에 저장
var allData = _tabulGrid.getData();
var projectData = allData.filter(function(row) {
return row.PROJECT_NO === projectNo;
});
if(projectData.length === 0) {
alert("프로젝트 데이터를 찾을 수 없습니다.");
return;
}
localStorage.setItem('transactionStatementData', JSON.stringify(projectData));
// 파란폴더(저장된 경우)만 여기까지 도달 → DB 데이터 로드
localStorage.setItem('loadSavedStatement', 'true');
// 거래명세서 팝업 열기
var url = "/salesMgmt/transactionStatementForm.do?projectNos=" + encodeURIComponent(projectNo);
var popup_width = 900;
var popup_height = 800;
var left = (screen.width - popup_width) / 2;
var top = (screen.height - popup_height) / 2;
var popup = window.open(url, "transactionStatement", "width=" + popup_width + ",height=" + popup_height + ",left=" + left + ",top=" + top + ",scrollbars=yes,resizable=yes");
// 팝업이 닫힐 때 그리드 새로고침
var checkPopup = setInterval(function() {
if(popup.closed) {
clearInterval(checkPopup);
console.log("거래명세서 팝업 닫힘 - 그리드 새로고침");
fn_search(); // 그리드 새로고침
}
}, 500);
}
function fn_openShippingDetail(projectNo) {
console.log("=== fn_openShippingDetail 호출 ===");
console.log("전달받은 projectNo:", projectNo);

View File

@@ -436,6 +436,26 @@ function fn_loadData() {
var gridData = JSON.parse(gridDataJson);
console.log("그리드 데이터:", gridData);
// 저장된 거래명세서를 불러올지 확인
var loadSaved = localStorage.getItem('loadSavedStatement') === 'true';
console.log("저장된 데이터 불러오기:", loadSaved);
if(loadSaved && gridData.length > 0) {
// 저장된 거래명세서 불러오기
fn_loadSavedStatement(gridData[0].PROJECT_NO, gridData);
return;
}
// 흰색 폴더 클릭 시 (저장 안 된 경우) - 빈 거래명세서
var loadEmpty = localStorage.getItem('loadSavedStatement') === 'empty';
console.log("빈 거래명세서 로드:", loadEmpty);
if(loadEmpty) {
// 빈 거래명세서 표시
fn_loadEmptyStatement(gridData);
return;
}
// S/N 정보 수집 (비고에 표시) - 비동기 처리
fn_collectSerialNumbers(gridData, function(serialNumbers) {
// 그리드 데이터를 거래명세서 형식으로 변환
@@ -477,6 +497,129 @@ function fn_loadData() {
// localStorage 정리
localStorage.removeItem('transactionStatementData');
localStorage.removeItem('loadSavedStatement');
}
// 빈 거래명세서 로드 (흰색 폴더 클릭 시)
function fn_loadEmptyStatement(gridData) {
console.log("=== 빈 거래명세서 로드 ===");
var firstItem = gridData[0];
// 고객사 정보만 채우기
$("#receiverName").text(firstItem.CUSTOMER || "");
// 품목 테이블 비우기 (빈 행 9개)
var tbody = $("#itemsBody");
tbody.empty();
for(var i = 0; i < 9; i++) {
var row = $("<tr>");
for(var j = 0; j < 6; j++) {
row.append($("<td contenteditable='true'>").html("&nbsp;"));
}
tbody.append(row);
}
// 합계 초기화
$("#totalQuantity").text("0");
$("#totalSupplyPrice").text("0");
$("#totalVat").text("0");
$("#totalText").text("0");
$("#totalNum").text("0");
// contenteditable 셀 변경 시 합계 자동 업데이트
fn_attachCellListeners();
// localStorage 정리
localStorage.removeItem('transactionStatementData');
localStorage.removeItem('loadSavedStatement');
}
// 저장된 거래명세서 불러오기
function fn_loadSavedStatement(projectNo, gridData) {
console.log("=== 저장된 거래명세서 불러오기 ===");
console.log("projectNo:", projectNo);
$.ajax({
url: '/salesMgmt/getSavedTransactionStatement.do',
type: 'POST',
data: { projectNo: projectNo },
success: function(response) {
if(response.success && response.data && response.data.length > 0) {
console.log("저장된 데이터:", response.data);
// 저장된 데이터를 거래명세서 폼에 채우기
var savedData = response.data;
var firstItem = gridData[0];
// 고객사 정보
$("#receiverName").text(firstItem.CUSTOMER || "");
// 품목 테이블 채우기
var tbody = $("#itemsBody");
tbody.empty();
var totalQuantity = 0;
var totalSupply = 0;
var totalVat = 0;
for(var i = 0; i < savedData.length; i++) {
var item = savedData[i];
var quantity = parseInt(item.isqty || item.ISQTY || 0);
var supply = parseInt(item.isamount || item.ISAMOUNT || 0);
var vat = supply * 0.1; // VAT 10%
totalQuantity += quantity;
totalSupply += supply;
totalVat += vat;
var row = $("<tr>");
row.append($("<td contenteditable='true' class='text-left'>").text(item.imitemid || item.IMITEMID || ""));
row.append($("<td contenteditable='true'>").text(item.prodcd || item.PRODCD || ""));
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))));
tbody.append(row);
}
// 빈 행 추가 (최소 9개 행 유지)
for(var i = savedData.length; i < 9; i++) {
var row = $("<tr>");
for(var j = 0; j < 6; j++) {
row.append($("<td contenteditable='true'>").html("&nbsp;"));
}
tbody.append(row);
}
// 합계 업데이트
$("#totalQuantity").text(totalQuantity);
$("#totalSupplyPrice").text(fn_num(totalSupply));
$("#totalVat").text(fn_num(Math.round(totalVat)));
var total = totalSupply + totalVat;
$("#totalText").text(Math.round(total).toString());
$("#totalNum").text(fn_num(Math.round(total)));
// contenteditable 셀 변경 시 합계 자동 업데이트
fn_attachCellListeners();
// localStorage 정리
localStorage.removeItem('transactionStatementData');
localStorage.removeItem('loadSavedStatement');
} else {
console.log("저장된 거래명세서가 없습니다. 새로 작성합니다.");
// 저장된 데이터가 없으면 새로 작성
localStorage.setItem('loadSavedStatement', 'false');
fn_loadData();
}
},
error: function() {
console.error("저장된 거래명세서 조회 실패");
alert("저장된 거래명세서를 불러오는데 실패했습니다.");
}
});
}
// S/N 정보 수집 함수 (비동기)
@@ -744,6 +887,20 @@ function fn_save() {
success: function(response) {
if(response && response.success) {
alert("저장되었습니다.");
// 부모 창(판매관리) 새로고침
if(window.opener && !window.opener.closed) {
console.log("부모 창 새로고침 시도");
try {
if(typeof window.opener.fn_search === 'function') {
window.opener.fn_search();
console.log("부모 창 새로고침 성공");
} else {
console.log("fn_search 함수를 찾을 수 없습니다.");
}
} catch(e) {
console.error("부모 창 새로고침 실패:", e);
}
}
} else {
alert("저장 실패: " + (response.message || "알 수 없는 오류"));
}

View File

@@ -989,4 +989,22 @@ public class SalesNcollectMgmtController {
return resultMap;
}
@RequestMapping(value = "/salesMgmt/getSavedTransactionStatement.do", method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> getSavedTransactionStatement(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
Map<String, Object> resultMap = new HashMap<String, Object>();
try {
List<Map<String, Object>> savedData = salesNcollectMgmtService.getSavedTransactionStatement(paramMap);
resultMap.put("success", true);
resultMap.put("data", savedData);
} catch(Exception e) {
e.printStackTrace();
resultMap.put("success", false);
resultMap.put("message", e.getMessage());
}
return resultMap;
}
}

View File

@@ -927,12 +927,17 @@
) AS MANAGER,
COALESCE(SR.incoterms, '') AS INCOTERMS,
T.SALES_STATUS,
T.OBJID::VARCHAR AS SALE_NO,
'ORIGINAL' AS RECORD_TYPE,
'' AS SPLIT_LOG_ID
FROM PROJECT_MGMT AS T
LEFT JOIN sales_registration SR ON T.PROJECT_NO = SR.project_no
WHERE 1 = 1
T.OBJID::VARCHAR AS SALE_NO,
'ORIGINAL' AS RECORD_TYPE,
'' AS SPLIT_LOG_ID,
-- 거래명세서 존재 여부
CASE WHEN EXISTS(
SELECT 1 FROM NSWOS100_TBL
WHERE OdOrderNo = T.PROJECT_NO
) THEN 'Y' ELSE 'N' END AS HAS_TRANSACTION_STATEMENT
FROM PROJECT_MGMT AS T
LEFT JOIN sales_registration SR ON T.PROJECT_NO = SR.project_no
WHERE 1 = 1
AND T.PROJECT_NO IS NOT NULL
AND T.PROJECT_NO != ''
<if test="orderType != null and orderType != ''">
@@ -1906,5 +1911,28 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
ORDER BY CIS.SERIAL_NO
</select>
<select id="getSavedTransactionStatement" parameterType="map" resultType="map">
/* salesNcollectMgmt.getSavedTransactionStatement - 저장된 거래명세서 조회 */
SELECT
SuVndCd,
IssueDt,
IssueNo,
IsNo,
ProdCd,
OdOrderNo,
ImItemId,
RmDueDt,
RmOrderQty,
RmRcptQty,
RmRemQty,
IsDt,
IsQty,
IsPrice,
IsAmount
FROM NSWOS100_TBL
WHERE OdOrderNo = #{projectNo}
ORDER BY IsNo
</select>
</mapper>

View File

@@ -1408,4 +1408,22 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
return result;
}
public List<Map<String, Object>> getSavedTransactionStatement(Map<String, Object> paramMap) {
SqlSession sqlSession = null;
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession();
result = sqlSession.selectList("salesNcollectMgmt.getSavedTransactionStatement", paramMap);
} catch(Exception e) {
e.printStackTrace();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
return result;
}
}