diff --git a/WebContent/WEB-INF/classes/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml b/WebContent/WEB-INF/classes/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml index d56894a..bc4076e 100644 --- a/WebContent/WEB-INF/classes/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml +++ b/WebContent/WEB-INF/classes/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml @@ -1420,31 +1420,6 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC ) - - - + + + + + diff --git a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp index 3def3ef..fb281d4 100644 --- a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp +++ b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp @@ -134,35 +134,54 @@ function fn_openSaleRegPopupWithData(rowData){ console.log("=== fn_openSaleRegPopupWithData 호출 ==="); console.log("rowData:", rowData); - console.log("SALES_QUANTITY:", rowData.SALES_QUANTITY); - console.log("SALES_UNIT_PRICE:", rowData.SALES_UNIT_PRICE); - console.log("SALES_SUPPLY_PRICE:", rowData.SALES_SUPPLY_PRICE); - console.log("SALES_VAT:", rowData.SALES_VAT); - console.log("SALES_TOTAL_AMOUNT:", rowData.SALES_TOTAL_AMOUNT); - console.log("SALES_CURRENCY:", rowData.SALES_CURRENCY); - console.log("SALES_EXCHANGE_RATE:", rowData.SALES_EXCHANGE_RATE); var popup_width = 850; var popup_height = 550; - // 기본 파라미터 + // 기본 파라미터만 전달 (orderNo, saleNo) + // 잔량 계산은 Controller에서 자동으로 처리됨 var params = "orderNo=" + encodeURIComponent(rowData.PROJECT_NO); params += "&saleNo=" + (rowData.SALE_NO ? encodeURIComponent(rowData.SALE_NO) : ""); - // 그리드에서 표시되고 있는 금액 정보 전달 - if(rowData.SALES_QUANTITY !== undefined && rowData.SALES_QUANTITY !== null) params += "&salesQuantity=" + encodeURIComponent(rowData.SALES_QUANTITY); - if(rowData.SALES_UNIT_PRICE !== undefined && rowData.SALES_UNIT_PRICE !== null) params += "&salesUnitPrice=" + encodeURIComponent(rowData.SALES_UNIT_PRICE); - if(rowData.SALES_SUPPLY_PRICE !== undefined && rowData.SALES_SUPPLY_PRICE !== null) params += "&salesSupplyPrice=" + encodeURIComponent(rowData.SALES_SUPPLY_PRICE); - if(rowData.SALES_VAT !== undefined && rowData.SALES_VAT !== null) params += "&salesVat=" + encodeURIComponent(rowData.SALES_VAT); - if(rowData.SALES_TOTAL_AMOUNT !== undefined && rowData.SALES_TOTAL_AMOUNT !== null) params += "&salesTotalAmount=" + encodeURIComponent(rowData.SALES_TOTAL_AMOUNT); - if(rowData.SALES_CURRENCY) params += "&salesCurrency=" + encodeURIComponent(rowData.SALES_CURRENCY); - if(rowData.SALES_EXCHANGE_RATE !== undefined && rowData.SALES_EXCHANGE_RATE !== null) params += "&salesExchangeRate=" + encodeURIComponent(rowData.SALES_EXCHANGE_RATE); + // 금액 정보는 Controller에서 자동으로 불러옴 (수주 데이터 기반) + // URL 파라미터로 전달하지 않음 var url = "/salesMgmt/salesRegForm.do?" + params; console.log("최종 URL:", url); fn_centerPopup(popup_width, popup_height, url); } + // 거래명세서 출력 함수 + function fn_printTransactionStatement() { + var selectedData = _tabulGrid.getSelectedData(); + + if(selectedData.length === 0) { + alert("거래명세서를 출력할 항목을 선택해주세요."); + return; + } + + // 같은 거래처인지 확인 + var firstCustomer = selectedData[0].CUSTOMER; + + for(var i = 1; i < selectedData.length; i++) { + if(selectedData[i].CUSTOMER !== firstCustomer) { + alert("같은 거래처만 선택 가능합니다.\n선택한 거래처: " + firstCustomer + ", " + selectedData[i].CUSTOMER); + return; + } + } + + // 프로젝트 번호들을 수집 + var projectNos = selectedData.map(function(row) { + return row.PROJECT_NO; + }).join(','); + + // 새로 만든 거래명세서 팝업 열기 + var popup_width = 1000; + var popup_height = 800; + var url = "/salesMgmt/transactionStatementForm.do?projectNos=" + encodeURIComponent(projectNos); + fn_centerPopup(popup_width, popup_height, url); + } + function fn_FileRegist(objId, docType, docTypeName){ var popup_width = 800; var popup_height = 680; @@ -418,6 +437,7 @@ function fn_bulkRegister(){
+
diff --git a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesRegForm.jsp b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesRegForm.jsp index ee2cd8c..443388c 100644 --- a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesRegForm.jsp +++ b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesRegForm.jsp @@ -3,6 +3,8 @@ <%@ page import="com.pms.common.utils.*"%> <%@ page import="java.util.*"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> <%@include file="/init_new.jsp"%> <% PersonBean person = (PersonBean) session.getAttribute(Constants.PERSON_BEAN); @@ -72,13 +74,77 @@ self.close(); }); - // 저장 버튼 - $("#btnSave").click(function() { - fn_save(); - }); + // 저장 버튼 + $("#btnSave").click(function() { + fn_save(); }); + + // 품목이 여러 개인 경우 자동으로 첫 번째 품목 선택 - 주석처리: 품목은 하나만 존재 + /* + if($(".item-radio").length > 0) { + $(".item-radio").first().prop("checked", true); + fn_calculateSelectedItem(); + } + */ +}); - // 판매공급가액 계산 함수 +// === Phase 2: 품목 선택 관련 함수 (단일 선택) - 주석처리: 품목은 하나만 존재 === +/* +// 행 클릭 시 라디오 버튼 선택 +function fn_selectItem(itemObjid) { + $(".item-radio[value='" + itemObjid + "']").prop("checked", true); + fn_calculateSelectedItem(); +} + +// 선택한 품목의 정보를 입력 필드에 반영 +function fn_calculateSelectedItem() { + var selectedRadio = $(".item-radio:checked"); + + if(selectedRadio.length === 0) { + $("#selectedItemInfo").text("품목을 선택하세요"); + return; + } + + var quantity = parseFloat(selectedRadio.data("quantity")) || 0; + var unitPrice = parseFloat(selectedRadio.data("unit-price")) || 0; + var supplyPrice = parseFloat(selectedRadio.data("supply-price")) || 0; + var vat = parseFloat(selectedRadio.data("vat")) || 0; + var totalAmount = parseFloat(selectedRadio.data("total")) || 0; + + // 선택한 품목 정보 표시 + var itemObjid = selectedRadio.val(); + var itemRow = $("tr[data-item-objid='" + itemObjid + "']"); + var partNo = itemRow.find("td:eq(1)").text().trim(); + var partName = itemRow.find("td:eq(2)").text().trim(); + + $("#selectedItemInfo").html( + "" + partName + " (" + partNo + ") - " + + "수량: " + quantity.toLocaleString() + "개 | " + + "공급가액: " + supplyPrice.toLocaleString() + "원" + ); + + // 입력 필드에 자동 반영 + $("#salesQuantity").val(quantity); + $("#salesUnitPrice").val(unitPrice); + $("#salesSupplyPrice").val(supplyPrice); + $("#salesVat").val(vat); + $("#salesTotalAmount").val(totalAmount); + + // hidden 필드에 선택한 품목 OBJID 저장 + if($("#selectedItemObjid").length === 0) { + $("").attr({ + type: "hidden", + id: "selectedItemObjid", + name: "selectedItemObjid" + }).appendTo("form"); + } + $("#selectedItemObjid").val(itemObjid); +} +*/ + +// === 기존 함수들 === + +// 판매공급가액 계산 함수 function fn_calculateSupplyPrice() { var currency = $("#salesCurrency").val(); var exchangeRate = parseFloat($("#salesExchangeRate").val()) || 1; @@ -117,6 +183,7 @@ // 판매환종 초기값 설정 (견적환종과 동기화하되, 사용자가 변경 가능) function initializeSalesCurrency() { + // Controller에서 계산한 값 사용 (잔량 기반) var existingSalesCurrency = "${saleInfo.SALES_CURRENCY}"; var contractCurrency = "${orderInfo.SALES_CURRENCY}"; // 견적환종 (SALES_CURRENCY로 통일) var contractExchangeRate = "${orderInfo.SALES_EXCHANGE_RATE}"; // 견적환율 @@ -532,19 +599,32 @@ if(remainingQuantity > 0) { if(confirm("잔량 " + remainingQuantity + "개가 남았습니다. 계속 등록하시겠습니까?")) { + // 부모 창 새로고침 후 팝업 새로고침 + if(opener && opener.fn_search) { + console.log("분할 출하 계속 - 부모 창 fn_search 호출"); + opener.fn_search(); + } // 팝업 새로고침 (잔량으로 수량 자동 설정) location.reload(); } else { // 목록 새로고침 후 팝업 닫기 + console.log("분할 출하 중단 - 팝업 닫기"); if(opener && opener.fn_search) { + console.log("부모 창 fn_search 호출"); opener.fn_search(); + } else { + console.log("부모 창 fn_search 없음!"); } self.close(); } } else { // 목록 새로고침 후 팝업 닫기 + console.log("잔량 없음 - 팝업 닫기"); if(opener && opener.fn_search) { + console.log("부모 창 fn_search 호출"); opener.fn_search(); + } else { + console.log("부모 창 fn_search 없음!"); } self.close(); } @@ -648,6 +728,67 @@ + + <%-- + + + +
+ 📦 품목 선택 (${fn:length(projectItems)}개) - 하나만 선택 가능 + + + + + + + + + + + + + + + + + + + + + + + +
선택품번품명수량단가공급가액
+ + ${item.PART_NO}${item.PART_NAME} + + + + + +
+
+ 💡 선택한 품목: + + 품목을 선택하세요 + +
+
+ + +
+ --%> + @@ -660,11 +801,19 @@ - + - + + + + (잔량: ${saleInfo.SALES_QUANTITY}개) + + - + @@ -720,11 +869,15 @@ - + - + @@ -732,11 +885,15 @@ - + - + @@ -751,7 +908,9 @@ - + diff --git a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/transactionStatementForm.jsp b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/transactionStatementForm.jsp new file mode 100644 index 0000000..cbc17d0 --- /dev/null +++ b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/transactionStatementForm.jsp @@ -0,0 +1,674 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ page import="com.pms.common.utils.*"%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ page import="java.util.*" %> +<%@include file= "/init.jsp" %> + + + + +거래명세서 + + + + + + +
+ +
거래 명세서
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 납품일: + 수요일, 9월 24, 2025 + 등록 번호 + 314-81-75146 + +
+
성 명
+
이동현
+
+


+
+ 상호(법인명) + ㈜알피에스 +
+
+ ㈜OOO + 귀하 + +
+ 사업장주소 + 대전광역시 유성구 국제과학10로 8 +
+
+
+ 업 태 + 제조 + 종 목 + 금속절삭가공기계의 +
+
+ 아래와 같이 공급합니다. + +
+ 전화 번호 + TEL:042-602-3300/FAX:042-672 +
+
+ + +
+ (공급가액+세액) + 3300000 원정 + + + 3,300,000 +
+ + + + + + + + + + + + + + + +
품 명규 격수 량단 가공 급 가 액세 액
+ + +
+
<비고>
+
Spindle S/N : 187-1062, 1096, 1099
+
+ + + + + + + + +
+ + +
+ + + +
+ + + diff --git a/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java b/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java index 734ce9c..fb2e4b9 100644 --- a/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java +++ b/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java @@ -334,7 +334,9 @@ public class SalesNcollectMgmtController { if(orderData != null && orderData.get("SALES_SUPPLY_PRICE") != null) { // 수주 데이터를 saleInfo에 병합 (기존 S/N 등은 유지) if(saleInfo == null) saleInfo = new HashMap(); - saleInfo.put("SALES_QUANTITY", orderData.get("SALES_QUANTITY")); + // SALES_QUANTITY는 나중에 잔량으로 계산하므로 여기서는 설정하지 않음 + // saleInfo.put("SALES_QUANTITY", orderData.get("SALES_QUANTITY")); // 제거 + saleInfo.put("ORDER_QUANTITY", orderData.get("SALES_QUANTITY")); // 주문수량은 ORDER_QUANTITY로 저장 saleInfo.put("SALES_UNIT_PRICE", orderData.get("SALES_UNIT_PRICE")); saleInfo.put("SALES_SUPPLY_PRICE", orderData.get("SALES_SUPPLY_PRICE")); saleInfo.put("SALES_VAT", orderData.get("SALES_VAT")); @@ -371,11 +373,22 @@ public class SalesNcollectMgmtController { System.out.println("총 판매 수량 (모든 분할 출하 합계): " + totalSoldQuantity); System.out.println("잔량 (remainingQuantity): " + remainingQuantity); - // 잔량을 판매수량으로 설정 (정수) - saleInfo.put("SALES_QUANTITY", remainingQuantity > 0 ? remainingQuantity : orderQuantity); - System.out.println("설정 후 SALES_QUANTITY: " + saleInfo.get("SALES_QUANTITY")); + // 잔량을 판매수량으로 설정 (정수) + saleInfo.put("SALES_QUANTITY", remainingQuantity > 0 ? remainingQuantity : orderQuantity); + System.out.println("설정 후 SALES_QUANTITY: " + saleInfo.get("SALES_QUANTITY")); +} + + // 프로젝트의 모든 품목 조회 (Phase 2) - 주석처리: 품목은 하나만 존재 + /* + List> projectItems = null; + if(paramMap.get("orderNo") != null && !paramMap.get("orderNo").equals("")) { + projectItems = salesNcollectMgmtService.getProjectItems(paramMap); + System.out.println("=== 프로젝트 품목 조회 ==="); + System.out.println("품목 개수: " + (projectItems != null ? projectItems.size() : 0)); + request.setAttribute("projectItems", projectItems); } - + */ + request.setAttribute("saleInfo", saleInfo); // orderInfo로 견적 정보 전달 (saleInfo가 이미 모든 필요한 정보를 포함) @@ -502,6 +515,75 @@ public class SalesNcollectMgmtController { return "/salesmgmt/salesMgmt/shippingDetailPopup"; } + /** + * 거래명세서 폼 표시 + */ + @RequestMapping(value = "/salesMgmt/transactionStatementForm.do", method = RequestMethod.GET) + public String showTransactionStatementForm(HttpServletRequest request, @RequestParam Map paramMap) { + return "/salesmgmt/salesMgmt/transactionStatementForm"; + } + + /** + * 거래명세서 데이터 조회 + */ + @RequestMapping(value = "/salesMgmt/getTransactionStatementData.do", method = RequestMethod.POST) + @ResponseBody + public Map getTransactionStatementData(HttpServletRequest request, @RequestParam Map paramMap) { + Map resultMap = new HashMap(); + + try { + String projectNos = (String) paramMap.get("projectNos"); + + if(projectNos == null || projectNos.isEmpty()) { + resultMap.put("success", false); + resultMap.put("message", "프로젝트 번호가 없습니다."); + return resultMap; + } + + // 거래명세서 데이터 조회 + Map statementData = salesNcollectMgmtService.getTransactionStatementData(paramMap); + + resultMap.put("success", true); + resultMap.putAll(statementData); + + } catch (Exception e) { + e.printStackTrace(); + resultMap.put("success", false); + resultMap.put("message", "데이터 조회 중 오류가 발생했습니다: " + e.getMessage()); + } + + return resultMap; + } + + /** + * 거래명세서 저장 (차수 관리 없음) + */ + @RequestMapping(value = "/salesMgmt/saveTransactionStatement.do", method = RequestMethod.POST) + @ResponseBody + public Map saveTransactionStatement(HttpServletRequest request, @org.springframework.web.bind.annotation.RequestBody Map paramMap) { + Map resultMap = new HashMap(); + + try { + // 거래명세서는 차수 관리가 불필요하므로 단순히 성공 응답만 반환 + // 실제로는 PDF 생성 또는 로그 기록 등의 작업을 수행할 수 있음 + + System.out.println("=== 거래명세서 저장 ==="); + System.out.println("프로젝트 번호: " + paramMap.get("projectNos")); + System.out.println("납품일: " + paramMap.get("deliveryDate")); + System.out.println("비고: " + paramMap.get("noteContent")); + + resultMap.put("success", true); + resultMap.put("message", "저장되었습니다."); + + } catch (Exception e) { + e.printStackTrace(); + resultMap.put("success", false); + resultMap.put("message", "저장 중 오류가 발생했습니다: " + e.getMessage()); + } + + return resultMap; + } + /** *
 	 * 계약관리 목록 조회
diff --git a/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml b/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml
index 8de770e..bc4076e 100644
--- a/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml
+++ b/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml
@@ -1566,40 +1566,43 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
 	-->
 	
 	
 	
+	
+	
+	
+	
+	
 
 
diff --git a/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java b/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java
index c41b5b5..85490ff 100644
--- a/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java
+++ b/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java
@@ -308,10 +308,39 @@ public class SalesNcollectMgmtService {
 			}
 		}
 		
-		return CommonUtils.toUpperCaseMapKey(resultMap);
+	return CommonUtils.toUpperCaseMapKey(resultMap);
+}
+
+/**
+ * 프로젝트의 모든 품목 조회 - 주석처리: 품목은 하나만 존재
+ */
+/*
+public List> getProjectItems(Map paramMap) {
+	List> resultList = new ArrayList>();
+	SqlSession sqlSession = null;
+	
+	try {
+		sqlSession = SqlMapConfig.getInstance().getSqlSession();
+		resultList = sqlSession.selectList("salesNcollectMgmt.getProjectItems", paramMap);
+	} catch(Exception e) {
+		e.printStackTrace();
+	} finally {
+		if(sqlSession != null) {
+			sqlSession.close();
+		}
 	}
 	
-	public Map saveSaleRegistration(HttpServletRequest request, Map paramMap) {
+	// 대문자 키로 변환
+	List> upperCaseList = new ArrayList>();
+	for(Map item : resultList) {
+		upperCaseList.add(CommonUtils.toUpperCaseMapKey(item));
+	}
+	
+	return upperCaseList;
+}
+*/
+
+public Map saveSaleRegistration(HttpServletRequest request, Map paramMap) {
 		Map resultMap = new HashMap();
 		SqlSession sqlSession = null;
 		
@@ -321,18 +350,87 @@ public class SalesNcollectMgmtService {
 			PersonBean person = (PersonBean) request.getSession().getAttribute(Constants.PERSON_BEAN);
 			paramMap.put("cretEmpNo", person.getUserId());
 			
-			// sales_registration 테이블에 판매 데이터 저장 (ON CONFLICT로 자동 UPDATE)
+			String projectNo = (String) paramMap.get("orderNo");
+			System.out.println("=== saveSaleRegistration 시작 ===");
+			System.out.println("projectNo: " + projectNo);
+			System.out.println("salesQuantity: " + paramMap.get("salesQuantity"));
+			
+			// 기존 판매 데이터가 있는지 확인
+			Map checkParam = new HashMap();
+			checkParam.put("orderNo", projectNo);
+			Map existingSale = sqlSession.selectOne("salesNcollectMgmt.getSaleInfo", checkParam);
+			
+		System.out.println("existingSale: " + (existingSale != null ? "있음" : "없음"));
+		
+		// SALE_NO가 null이면 첫 판매, 있으면 분할 출하
+		Object saleNoObj = existingSale != null ? existingSale.get("SALE_NO") : null;
+		if(saleNoObj == null) saleNoObj = existingSale != null ? existingSale.get("sale_no") : null;
+		
+		System.out.println("SALE_NO 확인: " + saleNoObj);
+		
+		if(saleNoObj == null) {
+			// 첫 판매 등록: sales_registration에 INSERT
+			System.out.println("첫 판매 등록 - sales_registration에 INSERT");
 			sqlSession.insert("salesNcollectMgmt.insertSaleRegistration", paramMap);
+		} else {
+		// 분할 출하: shipment_log에 INSERT
+		System.out.println("분할 출하 - shipment_log에 INSERT");
+			
+			// existingSale은 이미 대문자 키로 변환되어 있음 (CommonUtils.toUpperCaseMapKey)
+			// 하지만 getSaleInfo에서는 변환 안 함 - 직접 확인
+			Object orderQtyObj = existingSale.get("ORDER_QUANTITY");
+			if(orderQtyObj == null) orderQtyObj = existingSale.get("order_quantity");
+			
+			// saleNoObj는 이미 위에서 선언됨 - 재사용
+			// Object saleNoObj = existingSale.get("SALE_NO"); // 중복 제거
+			// if(saleNoObj == null) saleNoObj = existingSale.get("sale_no"); // 이미 위에서 처리
+			
+			System.out.println("ORDER_QUANTITY: " + orderQtyObj);
+			System.out.println("SALE_NO: " + saleNoObj);
+				
+				if(orderQtyObj == null || saleNoObj == null) {
+					System.out.println("=== existingSale 전체 내용 ===");
+					for(Object key : existingSale.keySet()) {
+						System.out.println(key + ": " + existingSale.get(key));
+					}
+					throw new RuntimeException("ORDER_QUANTITY 또는 SALE_NO가 null입니다");
+				}
+				
+				paramMap.put("targetObjid", projectNo);
+				paramMap.put("originalQuantity", orderQtyObj);
+				
+				// 잔량 계산
+				int orderQuantity = Integer.parseInt(String.valueOf(orderQtyObj).split("\\.")[0]);
+				int salesQuantity = Integer.parseInt(String.valueOf(paramMap.get("salesQuantity")));
+				int remainingQuantity = orderQuantity - salesQuantity;
+				
+				System.out.println("orderQuantity: " + orderQuantity);
+				System.out.println("salesQuantity: " + salesQuantity);
+				System.out.println("remainingQuantity: " + remainingQuantity);
+				
+				paramMap.put("remainingQuantity", remainingQuantity);
+				paramMap.put("parentSaleNo", saleNoObj);
+				
+				System.out.println("shipment_log INSERT 직전 파라미터:");
+				System.out.println("  targetObjid: " + paramMap.get("targetObjid"));
+				System.out.println("  parentSaleNo: " + paramMap.get("parentSaleNo"));
+				System.out.println("  cretEmpNo: " + paramMap.get("cretEmpNo"));
+				
+				sqlSession.insert("salesNcollectMgmt.insertShipmentLog", paramMap);
+			}
 			
 			sqlSession.commit();
 			resultMap.put("result", true);
 			resultMap.put("msg", "저장되었습니다.");
+			System.out.println("=== 저장 성공 ===");
 		} catch(Exception e) {
 			if(sqlSession != null) {
 				sqlSession.rollback();
 			}
 			resultMap.put("result", false);
 			resultMap.put("msg", "저장 중 오류가 발생했습니다.");
+			System.out.println("=== 저장 실패 ===");
+			System.out.println("에러 메시지: " + e.getMessage());
 			e.printStackTrace();
 		} finally {
 			if(sqlSession != null) {
@@ -919,6 +1017,29 @@ public class SalesNcollectMgmtService {
 		return resultMap;
 	}
 	
+	/**
+	 * 모든 분할 출하의 총 판매 수량 조회
+	 * @param paramMap
+	 * @return Map
+	 */
+	public Map getTotalSalesQuantity(Map paramMap) {
+		SqlSession sqlSession = null;
+		Map result = null;
+		
+		try {
+			sqlSession = SqlMapConfig.getInstance().getSqlSession();
+			result = sqlSession.selectOne("salesNcollectMgmt.getTotalSalesQuantity", paramMap);
+		} catch(Exception e) {
+			e.printStackTrace();
+		} finally {
+			if(sqlSession != null) {
+				sqlSession.close();
+			}
+		}
+		
+		return result;
+	}
+	
 	/**
 	 * 출하일 상세 내역 조회
 	 * @param projectNo
@@ -941,4 +1062,87 @@ public class SalesNcollectMgmtService {
 		}
 		return resultList;
 	}
+	
+	/**
+	 * 거래명세서 데이터 조회
+	 * @param paramMap - projectNos (쉼표로 구분된 프로젝트 번호들)
+	 * @return Map
+	 */
+	public Map getTransactionStatementData(Map paramMap) {
+		SqlSession sqlSession = null;
+		Map resultMap = new HashMap();
+		
+		try {
+			sqlSession = SqlMapConfig.getInstance().getSqlSession();
+			
+			String projectNos = (String) paramMap.get("projectNos");
+			System.out.println("=== getTransactionStatementData 시작 ===");
+			System.out.println("projectNos: " + projectNos);
+			
+			String[] projectNoArray = projectNos.split(",");
+			
+			// 첫 번째 프로젝트의 고객 정보 조회
+			Map firstProjectParam = new HashMap();
+			firstProjectParam.put("projectNo", projectNoArray[0].trim());
+			System.out.println("고객 정보 조회 - projectNo: " + projectNoArray[0].trim());
+			
+			Map customerInfo = sqlSession.selectOne("salesNcollectMgmt.getCustomerInfoByProjectNo", firstProjectParam);
+			System.out.println("고객 정보 조회 결과: " + customerInfo);
+			
+			if(customerInfo != null) {
+				resultMap.put("customerName", customerInfo.get("CUSTOMER_NAME"));
+				resultMap.put("customerRegNo", customerInfo.get("CUSTOMER_REG_NO"));
+				resultMap.put("customerAddress", customerInfo.get("CUSTOMER_ADDRESS"));
+				resultMap.put("customerBusiness", customerInfo.get("CUSTOMER_BUSINESS"));
+				resultMap.put("customerType", customerInfo.get("CUSTOMER_TYPE"));
+				resultMap.put("customerContact", customerInfo.get("CUSTOMER_CONTACT"));
+				System.out.println("고객명: " + customerInfo.get("CUSTOMER_NAME"));
+			} else {
+				System.out.println("고객 정보가 null입니다!");
+			}
+			
+			// 공급자 정보 (회사 정보)
+			resultMap.put("supplierName", "㈜압피에스");
+			resultMap.put("supplierRegNo", "314-81-75146");
+			resultMap.put("supplierAddress", "대전광역시 유성구 국제과학10로 8");
+			resultMap.put("supplierBusiness", "제조");
+			resultMap.put("supplierType", "금속절삭가공기계의");
+			resultMap.put("supplierContact", "TEL:042-602-3300/FAX:042-672");
+			
+			// 품목 정보 조회
+			List> items = new ArrayList>();
+			for(String projectNo : projectNoArray) {
+				Map itemParam = new HashMap();
+				itemParam.put("projectNo", projectNo.trim());
+				System.out.println("품목 정보 조회 - projectNo: " + projectNo.trim());
+				
+				Map item = sqlSession.selectOne("salesNcollectMgmt.getTransactionStatementItem", itemParam);
+				System.out.println("품목 정보 조회 결과: " + item);
+				
+				if(item != null) {
+					items.add(item);
+				}
+			}
+			resultMap.put("items", items);
+			System.out.println("총 품목 개수: " + items.size());
+			
+			// 비고
+			resultMap.put("note", "아래와 같이 공급합니다.");
+			
+			System.out.println("=== resultMap 최종 ===");
+			System.out.println("customerName: " + resultMap.get("customerName"));
+			System.out.println("items size: " + ((List)resultMap.get("items")).size());
+			System.out.println("supplierName: " + resultMap.get("supplierName"));
+			
+		} catch (Exception e) {
+			System.out.println("=== 에러 발생 ===");
+			e.printStackTrace();
+		} finally {
+			if (sqlSession != null) {
+				sqlSession.close();
+			}
+		}
+		
+		return resultMap;
+	}
 }