From d9e06fe95db4d3ac671707b7349fb6f5ac11973c Mon Sep 17 00:00:00 2001 From: Johngreen Date: Fri, 17 Oct 2025 17:18:05 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=EB=A7=A4=EC=B6=9C=EA=B4=80=EB=A6=AC=20?= =?UTF-8?q?=EC=8B=A0=EA=B7=9C=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/pms/salesmgmt/mapper/contractMgmt.xml | 38 ++-- .../salesmgmt/salesMggmt/salesMgmtList.jsp | 0 .../salesmgmt/salesMgmt/revenueMgmtList.jsp | 191 ++++++++++++++++++ .../salesmgmt/salesMgmt/salesMgmtList.jsp | 157 +++++++------- .../view/salesmgmt/salesMgmt/salesRegForm.jsp | 114 +++++++++++ plan.md | 33 +++ .../SalesNcollectMgmtController.java | 70 ++++++- 7 files changed, 491 insertions(+), 112 deletions(-) create mode 100644 WebContent/WEB-INF/view/salesmgmt/salesMggmt/salesMgmtList.jsp create mode 100644 WebContent/WEB-INF/view/salesmgmt/salesMgmt/revenueMgmtList.jsp create mode 100644 WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesRegForm.jsp create mode 100644 plan.md diff --git a/WebContent/WEB-INF/classes/com/pms/salesmgmt/mapper/contractMgmt.xml b/WebContent/WEB-INF/classes/com/pms/salesmgmt/mapper/contractMgmt.xml index 209f229..08e3dfb 100644 --- a/WebContent/WEB-INF/classes/com/pms/salesmgmt/mapper/contractMgmt.xml +++ b/WebContent/WEB-INF/classes/com/pms/salesmgmt/mapper/contractMgmt.xml @@ -500,6 +500,11 @@ ,EXCHANGE_RATE ,EST_PRICE ,EST_SUPPLY_PRICE + ,ORDER_DATE + ,ORDER_UNIT_PRICE + ,ORDER_SUPPLY_PRICE + ,ORDER_VAT + ,ORDER_TOTAL_AMOUNT ,(SELECT COUNT(1) FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = T.OBJID) AS EST_STATUS ,( SELECT IS_SEND @@ -718,6 +723,13 @@ AND TO_DATE(DUE_DATE,'YYYY-MM-DD') TO_DATE(#{due_end_date}, 'YYYY-MM-DD') + + + AND TO_DATE(RECEIPT_DATE,'YYYY-MM-DD') = ]]> TO_DATE(#{order_start_date}, 'YYYY-MM-DD') + + + AND TO_DATE(ORDER_DATE,'YYYY-MM-DD') TO_DATE(#{due_end_date}, 'YYYY-MM-DD') + ORDER BY REGDATE DESC @@ -877,9 +889,7 @@ ,CONTRACT_DEL_DATE ,CONTRACT_COMPANY ,CONTRACT_DATE - ,PO_NO ,MANUFACTURE_PLANT - ,CONTRACT_RESULT ,PROJECT_NAME ,SPEC_USER_ID ,SPEC_PLAN_DATE @@ -900,14 +910,8 @@ ,PART_NO ,PART_NAME ,SERIAL_NO - ,QUANTITY ,CUSTOMER_REQUEST ,EXCHANGE_RATE - ,ORDER_DATE - ,ORDER_UNIT_PRICE - ,ORDER_SUPPLY_PRICE - ,ORDER_VAT - ,ORDER_TOTAL_AMOUNT ) VALUES ( @@ -944,9 +948,7 @@ ,#{contract_del_date} ,#{contract_company} ,#{contract_date} - ,#{po_no} ,#{manufacture_plant} - ,#{contract_result} ,#{project_name} ,#{spec_user_id} ,#{spec_plan_date} @@ -967,14 +969,8 @@ ,#{part_no} ,#{part_name} ,#{serial_no} - ,#{quantity} ,#{customer_request} ,#{exchange_rate} - ,#{order_date} - ,#{unit_price} - ,#{supply_price} - ,#{vat} - ,#{total_amount} ) ON CONFLICT (OBJID) DO UPDATE @@ -1007,10 +1003,8 @@ ,REQ_DEL_DATE = #{req_del_date} ,CONTRACT_DEL_DATE = #{contract_del_date} ,CONTRACT_COMPANY = #{contract_company} - ,CONTRACT_DATE = #{contract_date} - ,PO_NO = #{po_no} + ,CONTRACT_DATE = #{contract_date} ,MANUFACTURE_PLANT = #{manufacture_plant} - ,CONTRACT_RESULT = #{contract_result} ,PROJECT_NAME = #{project_name} ,SPEC_USER_ID = #{spec_user_id} ,SPEC_PLAN_DATE = #{spec_plan_date} @@ -1031,14 +1025,8 @@ ,PART_NO = #{part_no} ,PART_NAME = #{part_name} ,SERIAL_NO = #{serial_no} - ,QUANTITY = #{quantity} ,CUSTOMER_REQUEST = #{customer_request} ,EXCHANGE_RATE = #{exchange_rate} - ,ORDER_DATE = #{order_date} - ,ORDER_UNIT_PRICE = #{unit_price} - ,ORDER_SUPPLY_PRICE = #{supply_price} - ,ORDER_VAT = #{vat} - ,ORDER_TOTAL_AMOUNT = #{total_amount} diff --git a/WebContent/WEB-INF/view/salesmgmt/salesMggmt/salesMgmtList.jsp b/WebContent/WEB-INF/view/salesmgmt/salesMggmt/salesMgmtList.jsp new file mode 100644 index 0000000..e69de29 diff --git a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/revenueMgmtList.jsp b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/revenueMgmtList.jsp new file mode 100644 index 0000000..313ddd4 --- /dev/null +++ b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/revenueMgmtList.jsp @@ -0,0 +1,191 @@ +<%@ 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" %> +<% + PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN); + String connector = person.getUserId(); +%> + + + + +<%=Constants.SYSTEM_NAME%> + + + + + + +
+ + +
+ +
+ +
+
+
+
+

+ 영업관리_매출관리 +

+
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + ~
~ ~
+
+ + <%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %> +
+
+
+
+ + diff --git a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp index a2cf63e..28943d5 100644 --- a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp +++ b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp @@ -1,43 +1,48 @@ -<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> -<%@ page import="com.pms.common.utils.*"%> +<%@ 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.*" %> +<%@ page import="java.util.*" %> <%@include file= "/init.jsp" %> <% PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN); String connector = person.getUserId(); %> - - - - -<%=Constants.SYSTEM_NAME%> + + + + +판매 관리 - + -
- - -
-
@@ -96,11 +89,12 @@ function fn_excel() {

- 영업관리_매출관리 + 판매 관리

- + +
@@ -122,70 +116,65 @@ function fn_excel() { - - - - - - - + - - - - - - + + + + + + + + + + ~ + + ~ + + + + ~ + + + + + + - - - - - - - - - - - - ~ - - - - ~ - - ~ - + + + +
- - <%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %> + + <%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %>
-
- + + diff --git a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesRegForm.jsp b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesRegForm.jsp new file mode 100644 index 0000000..56fae4f --- /dev/null +++ b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesRegForm.jsp @@ -0,0 +1,114 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ include file="/init.jsp" %> + + + + +판매 등록 + + + + + +
+
+
+

+ 판매 등록 +

+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+
+ +
+
+ + +
+
+
+
+ + diff --git a/plan.md b/plan.md new file mode 100644 index 0000000..6754a95 --- /dev/null +++ b/plan.md @@ -0,0 +1,33 @@ +# 프로젝트: 판매 관리 기능 개발 + +## 개요 +판매 관리 페이지의 UI를 개선하고, 판매 등록 및 조회 기능을 TDD(테스트 주도 개발) 원칙에 따라 개발합니다. + +## 핵심 기능 +1. 판매 관리 페이지 UI 구현 +2. 판매 등록 팝업 기능 +3. 판매 목록 조회 및 필터링 +4. 데이터베이스 연동 및 CRUD 기능 구현 + +## 테스트 계획 +### 1단계: UI 및 기본 기능 +- [x] 판매 관리 페이지 기본 UI 생성 +- [x] 필터 및 테이블 컬럼 구성 +- [x] 컨트롤러 URL 매핑 및 이름 재정의 +- [x] '판매등록' 버튼 추가 +- [ ] **테스트 1**: '판매등록' 버튼 클릭 시 팝업창이 정상적으로 뜨는가? +- [ ] **테스트 2**: '출고방법' 드롭다운에 DB 데이터가 정상적으로 표시되는가? +- [ ] **테스트 3**: '인도조건' 드롭다운에 DB 데이터가 정상적으로 표시되는가? + +### 2단계: 데이터 처리 +- [ ] **테스트 4**: 판매 등록 팝업에서 '저장' 버튼 클릭 시 데이터가 DB에 저장되는가? +- [ ] **테스트 5**: 판매 목록 조회 시 DB에서 데이터를 가져와 그리드에 표시하는가? +- [ ] **테스트 6**: 검색 조건으로 필터링했을 때 결과가 정확히 조회되는가? + +## 에러 처리 계획 +- 필수 입력값 누락 시 유효성 검사 및 알림 +- 데이터 저장 실패 시 에러 메시지 처리 + +## 진행 상태 +- 완료된 테스트는 [x]로 표시합니다. +- 현재 진행 중인 테스트는 **[진행중]** 으로 표시합니다. diff --git a/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java b/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java index 10aafaf..c5909f5 100644 --- a/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java +++ b/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java @@ -99,8 +99,8 @@ public class SalesNcollectMgmtController { * @param paramMap - 검색 정보 * @return String */ - @RequestMapping(value = "/contractMgmt/salesMgmtList.do", method = RequestMethod.GET) - public String showSalesMgmtList(HttpServletRequest request, @RequestParam Map paramMap) { + @RequestMapping(value = "/revenueMgmt/revenueList.do", method = RequestMethod.GET) + public String showRevenueMgmtList(HttpServletRequest request, @RequestParam Map paramMap) { try { // 코드정보 Map codeMap = new HashMap(); @@ -143,6 +143,46 @@ public class SalesNcollectMgmtController { } catch (Exception e) { e.printStackTrace(); } + return "/salesmgmt/salesMgmt/revenueMgmtList"; + } + + /** + *
+	 * 신규 매출관리 목록 페이지
+	 * 
+ * @param request + * @param paramMap - 검색 정보 + * @return String + */ + @RequestMapping(value = "/contractMgmt/salesMgmtList.do", method = RequestMethod.GET) + public String showSalesMgmtList(HttpServletRequest request, @RequestParam Map paramMap) { + try { + // 코드정보 (기존 로직과 동일하게 설정 가능) + Map codeMap = new HashMap(); + + // 주문유형 + codeMap.put("orderTypeList", + salesMgmtCommonService.bizMakeOptionList("GE", "", "salesMgmtCommon.getCodeList")); + // 제품구분 + codeMap.put("productTypeList", + salesMgmtCommonService.bizMakeOptionList("", "", "salesMgmtCommon.getGoodsList")); + // 국내/해외 + codeMap.put("nationList", + salesMgmtCommonService.bizMakeOptionList("AR", "", "salesMgmtCommon.getCodeList")); + // 수주상태 + codeMap.put("orderStatusList", + commonService.bizMakeOptionList("0000932", "", "common.getCodeselect")); + // 출하대기 상태 + codeMap.put("shippingStatusList", + salesMgmtCommonService.bizMakeOptionList("SH", "", "salesMgmtCommon.getCodeList")); + // 담당자 + codeMap.put("managerList", + salesMgmtCommonService.bizMakeOptionList("", "", "salesMgmtCommon.getSalesmanList")); + + request.setAttribute("codeMap", codeMap); + } catch (Exception e) { + e.printStackTrace(); + } return "/salesmgmt/salesMgmt/salesMgmtList"; } @@ -155,7 +195,7 @@ public class SalesNcollectMgmtController { * @return Map */ @ResponseBody - @RequestMapping(value = "/salesMgmt/salesMgmtGridList.do", method = RequestMethod.POST) + @RequestMapping(value = "/revenueMgmt/revenueGridList.do", method = RequestMethod.POST) public Map salesMgmtGridList(HttpServletRequest request, @RequestParam Map paramMap) { Map resultMap = new HashMap(); @@ -175,6 +215,30 @@ public class SalesNcollectMgmtController { return resultMap; } + /** + *
+	 * 판매등록 폼 팝업
+	 * 
+ * @param request + * @param paramMap + * @return String + */ + @RequestMapping(value = "/salesMgmt/salesRegForm.do", method = RequestMethod.GET) + public String showSalesRegForm(HttpServletRequest request, @RequestParam Map paramMap) { + try { + Map codeMap = new HashMap(); + + // 담당자 + codeMap.put("managerList", + salesMgmtCommonService.bizMakeOptionList("", "", "salesMgmtCommon.getSalesmanList")); + + request.setAttribute("codeMap", codeMap); + } catch (Exception e) { + e.printStackTrace(); + } + return "/salesmgmt/salesMgmt/salesRegForm"; + } + /** *
 	 * 계약관리 목록 조회

From 6af7fb6b6d3090c3b9ce8a94edac6a05a109cd21 Mon Sep 17 00:00:00 2001
From: Johngreen 
Date: Mon, 20 Oct 2025 23:04:30 +0900
Subject: [PATCH 2/2] =?UTF-8?q?=ED=8C=90=EB=A7=A4=EA=B4=80=EB=A6=AC,=20?=
 =?UTF-8?q?=EB=A7=A4=EC=B6=9C=EA=B4=80=EB=A6=AC=EC=99=84=EC=84=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .gitignore                                    |   2 +
 .../salesmgmt/mapper/salesNcollectMgmt.xml    | 330 +++++++++++++-----
 .../salesmgmt/salesMgmt/revenueMgmtList.jsp   | 262 ++++++++++++--
 .../salesmgmt/salesMgmt/salesMgmtList.jsp     | 252 ++++++++++---
 .../view/salesmgmt/salesMgmt/salesRegForm.jsp |  27 +-
 .../salesmgmt/salesMgmt/splitShipmentForm.jsp | 174 +++++++++
 .../salesmgmt/salesMgmt/salesMgmtList.jsp     |   0
 database/add_order_columns.sql                |  34 --
 database/estimate_template_tables.sql         |  88 -----
 plan.md                                       |  33 --
 .../SalesNcollectMgmtController.java          | 128 ++++++-
 .../salesmgmt/mapper/salesNcollectMgmt.xml    | 330 +++++++++++++-----
 .../service/SalesNcollectMgmtService.java     | 205 ++++++++++-
 13 files changed, 1419 insertions(+), 446 deletions(-)
 create mode 100644 WebContent/WEB-INF/view/salesmgmt/salesMgmt/splitShipmentForm.jsp
 create mode 100644 WebContent/WEB_INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp
 delete mode 100644 database/add_order_columns.sql
 delete mode 100644 database/estimate_template_tables.sql
 delete mode 100644 plan.md

diff --git a/.gitignore b/.gitignore
index f1d22bf..cd16182 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,5 @@ Thumbs.db
 # Docker volumes
 .docker/
 
+# Cursor files
+.cursor/
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 61b6849..c08aafe 100644
--- a/WebContent/WEB-INF/classes/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml
+++ b/WebContent/WEB-INF/classes/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml
@@ -812,188 +812,332 @@
 			,editEmpNo = #{cretEmpNo}			/* 수정자 */
 	
 	
-	
+	
 	
 	
-	
-	
+		/* salesNcollectMgmt.getSalesMgmtGridListCount - VIEW 기반 */
 		SELECT 
-			COUNT(1)
-		FROM SWSC110A_TBL B
-		LEFT JOIN SWSD010A_TBL A ON A.orderNo = B.orderNo
-		INNER JOIN SWSB110A_TBL C ON B.goodsCd = C.goodsCd
-		INNER JOIN SWSB210A_TBL D ON B.custCd = D.custCd
+			CEIL(COUNT(1)::float / #{COUNT_PER_PAGE}::float)::numeric::integer AS MAX_PAGE_SIZE,
+			COUNT(1)::integer AS TOTAL_CNT
+		FROM v_sales_mgmt_with_splits
 		WHERE 1 = 1
 		
-			AND B.SaleType = #{orderType}
+			AND CATEGORY_CD = #{orderType}
 		
 		
-			AND C.c_class = #{productType}
+			AND PRODUCT_TYPE = #{productType}
 		
 		
-			AND B.nationGB = #{nation}
+			AND AREA_CD = #{nation}
 		
 		
-			AND D.custNm LIKE CONCAT('%', #{customer}, '%')
+			AND CUSTOMER LIKE CONCAT('%', #{customer}, '%')
 		
 		
-			AND B.freeyn = #{paymentType}
+			AND PAID_TYPE = #{paymentType}
 		
 		
-			AND C.GoodsCd LIKE CONCAT('%', #{productNo}, '%')
+			AND PRODUCT_NO LIKE CONCAT('%', #{productNo}, '%')
 		
 		
-			AND C.GoodsNm LIKE CONCAT('%', #{productName}, '%')
+			AND PRODUCT_NAME LIKE CONCAT('%', #{productName}, '%')
 		
 		
-			AND A.serialno LIKE CONCAT('%', #{serialNo}, '%')
+			AND SERIAL_NO LIKE CONCAT('%', #{serialNo}, '%')
 		
 		
-			AND B.endsale = #{orderStatus}
+			AND STATUS_CD = #{orderStatus}
 		
 		
-			AND B.OrderNo LIKE CONCAT('%', #{poNo}, '%')
+			AND PO_NO LIKE CONCAT('%', #{poNo}, '%')
 		
 		
-			AND B.custreq =]]> #{requestDateFrom}
+			AND REQUEST_DATE =]]> #{requestDateFrom}
 		
 		
-			AND B.custreq  #{requestDateTo}
+			AND REQUEST_DATE  #{requestDateTo}
 		
 		
-			AND B.OrderDate =]]> #{orderDateFrom}
+			AND ORDER_DATE =]]> #{orderDateFrom}
 		
 		
-			AND B.OrderDate  #{orderDateTo}
+			AND ORDER_DATE  #{orderDateTo}
 		
 		
-			AND B.shippingstatus = #{shippingStatus}
+			AND SALES_STATUS = #{shippingStatus}
 		
 		
-			AND B.shippingdate =]]> #{shippingDateFrom}
+			AND SHIPPING_DATE =]]> #{shippingDateFrom}
 		
 		
-			AND B.shippingdate  #{shippingDateTo}
+			AND SHIPPING_DATE  #{shippingDateTo}
 		
 		
-			AND B.outGb = #{shippingMethod}
+			AND SHIPPING_METHOD = #{shippingMethod}
 		
 		
-			AND B.bEmpNo = #{manager}
+			AND PM_USER_ID = #{manager}
 		
 		
-			AND B.incoterms = #{incoterms}
+			AND INCOTERMS = #{incoterms}
 		
 	
 	
+	
+	
+
+	
+	
+
+	
+	
+		/* salesNcollectMgmt.insertSplitShipmentLog */
+		INSERT INTO shipment_log (
+			target_objid,
+			log_type,
+			log_message,
+			split_quantity,
+			original_quantity,
+			shipping_status,
+			sales_unit_price,
+			sales_supply_price,
+			sales_vat,
+			sales_total_amount,
+			sales_currency,
+			sales_exchange_rate,
+			serial_no,
+			shipping_date,
+			shipping_method,
+			manager_user_id,
+			incoterms,
+			remark,
+			is_split_record,
+			reg_date,
+			reg_user_id
+		) VALUES (
+			#{objid},
+			#{logType},
+			#{logMessage},
+			#{splitQuantity}::integer,
+			#{originalQuantity}::integer,
+			'PENDING',
+			#{salesUnitPrice}::numeric,
+			#{salesSupplyPrice}::numeric,
+			#{salesVat}::numeric,
+			#{salesTotalAmount}::numeric,
+			#{salesCurrency},
+			#{salesExchangeRate}::numeric,
+			#{serialNo},
+			
+				
+					TO_DATE(#{shippingDate}, 'YYYY-MM-DD'),
+				
+				
+					NULL,
+				
+			
+			#{shippingMethod},
+			#{managerUserId},
+			#{incoterms},
+			#{remark},
+			true,
+			NOW(),
+			#{userId}
+		)
+	
+	
 
 
diff --git a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/revenueMgmtList.jsp b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/revenueMgmtList.jsp
index 313ddd4..ccfcb8f 100644
--- a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/revenueMgmtList.jsp
+++ b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/revenueMgmtList.jsp
@@ -14,6 +14,18 @@
 <%=Constants.SYSTEM_NAME%>
 
 
  
@@ -91,6 +255,9 @@ function fn_excel() {
 
 
+ + +
@@ -115,21 +282,27 @@ function fn_excel() { - - - - - - - - - - - + + + + + + + + + + + + + @@ -181,6 +354,23 @@ function fn_excel() {
+ + +
+ + + + + + +
+ Total 공급가액: 0 원 + + Total 부가세: 0 원 + + Total 총액: 0 원 +
+
<%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %>
diff --git a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp index 28943d5..7d5ddac 100644 --- a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp +++ b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp @@ -11,7 +11,7 @@ -판매 관리 +<%=Constants.SYSTEM_NAME%> +

- 판매 관리 + 영업관리_판매관리

- - + +
@@ -115,11 +260,13 @@ function fn_search(){ - - - - - + + + - - + + + + + + + + + + + + - - - - ~ - - ~ - - - - ~ - - - - - - @@ -166,7 +303,26 @@ function fn_search(){ - + + + + + + + + + + ~ + + + + ~ + + ~
diff --git a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesRegForm.jsp b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesRegForm.jsp index 56fae4f..eb2111a 100644 --- a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesRegForm.jsp +++ b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesRegForm.jsp @@ -22,14 +22,37 @@ // 저장 버튼 $("#btnSave").click(function() { - // 저장 로직은 추후 구현 - alert("저장 기능은 구현 예정입니다."); + fn_save(); }); }); + + function fn_save() { + if(confirm("저장하시겠습니까?")) { + $.ajax({ + url: "/salesMgmt/saveSales.do", + type: "POST", + data: $("#form1").serialize(), + dataType: "json", + success: function(data) { + alert(data.msg); + if (data.result) { + opener.fn_search(); // 부모창 그리드 새로고침 + window.close(); // 팝업 닫기 + } + }, + error: function(jqxhr, status, error) { + console.error("Error:", error); + alert("저장에 실패했습니다."); + } + }); + } + } + +

diff --git a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/splitShipmentForm.jsp b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/splitShipmentForm.jsp new file mode 100644 index 0000000..0e0cffb --- /dev/null +++ b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/splitShipmentForm.jsp @@ -0,0 +1,174 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ include file="/init.jsp" %> + + + + +분할출하 등록 + + + + + + + + + + +
+
+

+ 분할출하 등록 +

+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ +
+
+ +
+
+ + +
+
+
+ + + + diff --git a/WebContent/WEB_INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp b/WebContent/WEB_INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp new file mode 100644 index 0000000..e69de29 diff --git a/database/add_order_columns.sql b/database/add_order_columns.sql deleted file mode 100644 index f6f8af3..0000000 --- a/database/add_order_columns.sql +++ /dev/null @@ -1,34 +0,0 @@ --- CONTRACT_MGMT 테이블에 수주 관련 컬럼 추가 --- 기존 컬럼 활용: --- - 수주상태: contract_result (기존 컬럼 사용) --- - 환종: contract_currency (기존 컬럼 사용) --- - 환율: exchange_rate (기존 컬럼 사용) --- - 발주번호: po_no (기존 컬럼 사용) - --- 수주 관련 신규 컬럼들 -ALTER TABLE public.contract_mgmt ADD COLUMN IF NOT EXISTS order_date VARCHAR(50); -ALTER TABLE public.contract_mgmt ADD COLUMN IF NOT EXISTS order_unit_price NUMERIC(15,2); -ALTER TABLE public.contract_mgmt ADD COLUMN IF NOT EXISTS order_supply_price NUMERIC(15,2); -ALTER TABLE public.contract_mgmt ADD COLUMN IF NOT EXISTS order_vat NUMERIC(15,2); -ALTER TABLE public.contract_mgmt ADD COLUMN IF NOT EXISTS order_total_amount NUMERIC(15,2); - --- 컬럼 코멘트 추가 -COMMENT ON COLUMN public.contract_mgmt.contract_result IS '영업진행상태 / 수주상태'; -COMMENT ON COLUMN public.contract_mgmt.po_no IS '발주번호'; -COMMENT ON COLUMN public.contract_mgmt.order_date IS '발주일'; -COMMENT ON COLUMN public.contract_mgmt.order_unit_price IS '수주 단가'; -COMMENT ON COLUMN public.contract_mgmt.order_supply_price IS '수주 공급가액'; -COMMENT ON COLUMN public.contract_mgmt.order_vat IS '수주 부가세'; -COMMENT ON COLUMN public.contract_mgmt.order_total_amount IS '수주 총액'; -COMMENT ON COLUMN public.contract_mgmt.contract_currency IS '견적/수주 환종'; -COMMENT ON COLUMN public.contract_mgmt.exchange_rate IS '견적/수주 환율'; - --- 인덱스 추가 (선택사항) -CREATE INDEX IF NOT EXISTS idx_contract_result ON public.contract_mgmt(contract_result); -CREATE INDEX IF NOT EXISTS idx_contract_order_date ON public.contract_mgmt(order_date); - --- 조회 예시 --- SELECT objid, contract_no, contract_result, po_no, order_date, order_total_amount, contract_currency, exchange_rate --- FROM public.contract_mgmt --- WHERE contract_result IS NOT NULL; - diff --git a/database/estimate_template_tables.sql b/database/estimate_template_tables.sql deleted file mode 100644 index 50c88e5..0000000 --- a/database/estimate_template_tables.sql +++ /dev/null @@ -1,88 +0,0 @@ --- 견적서 템플릿 테이블 생성 SQL - --- 1. 견적서 템플릿 메인 테이블 -CREATE TABLE IF NOT EXISTS ESTIMATE_TEMPLATE ( - OBJID VARCHAR(50) PRIMARY KEY, - CONTRACT_OBJID VARCHAR(50) NOT NULL, -- CONTRACT_MGMT 테이블의 OBJID 참조 - TEMPLATE_TYPE VARCHAR(10) NOT NULL, -- '1': 일반 견적서, '2': 장비 견적서 - - -- 일반 견적서 필드 (Template 1) - EXECUTOR VARCHAR(200), -- 시행일자 - RECIPIENT VARCHAR(200), -- 수신처 - ESTIMATE_NO VARCHAR(100), -- 견적번호 - CONTACT_PERSON VARCHAR(100), -- 수신인 - GREETING_TEXT TEXT, -- 인사말 - - -- 장비 견적서 필드 (Template 2) - MODEL_NAME VARCHAR(200), -- 설비 Model 품명 - MODEL_CODE VARCHAR(100), -- 설비 Model 코드 - EXECUTOR_DATE VARCHAR(50), -- 시행일자 - NOTES_CONTENT TEXT, -- 비고 내용 (전체) - VALIDITY_PERIOD VARCHAR(50), -- 견적 유효기간 - - -- 비고 필드 (공통) - NOTE1 VARCHAR(500), - NOTE2 VARCHAR(500), - NOTE3 VARCHAR(500), - NOTE4 VARCHAR(500), - - -- 카테고리 정보 (장비 견적서용, JSON 형태로 저장) - CATEGORIES_JSON TEXT, - - -- 시스템 필드 - WRITER VARCHAR(50), - REGDATE TIMESTAMP DEFAULT NOW(), - CHG_USER_ID VARCHAR(50), - CHGDATE TIMESTAMP DEFAULT NOW(), - - CONSTRAINT fk_estimate_contract FOREIGN KEY (CONTRACT_OBJID) - REFERENCES CONTRACT_MGMT(OBJID) ON DELETE CASCADE -); - --- 인덱스 생성 -CREATE INDEX idx_estimate_contract ON ESTIMATE_TEMPLATE(CONTRACT_OBJID); -CREATE INDEX idx_estimate_type ON ESTIMATE_TEMPLATE(TEMPLATE_TYPE); - --- 코멘트 추가 -COMMENT ON TABLE ESTIMATE_TEMPLATE IS '견적서 템플릿 정보'; -COMMENT ON COLUMN ESTIMATE_TEMPLATE.CONTRACT_OBJID IS '견적요청 OBJID (CONTRACT_MGMT 참조)'; -COMMENT ON COLUMN ESTIMATE_TEMPLATE.TEMPLATE_TYPE IS '템플릿 타입 (1:일반견적서, 2:장비견적서)'; - - --- 2. 견적서 템플릿 품목 테이블 -CREATE TABLE IF NOT EXISTS ESTIMATE_TEMPLATE_ITEM ( - OBJID SERIAL PRIMARY KEY, - TEMPLATE_OBJID VARCHAR(50) NOT NULL, -- ESTIMATE_TEMPLATE 테이블의 OBJID 참조 - SEQ INTEGER NOT NULL, -- 순번 - CATEGORY VARCHAR(100), -- 카테고리 (장비 견적서용) - DESCRIPTION VARCHAR(500), -- 품명 - SPECIFICATION TEXT, -- 규격/사양 - QUANTITY VARCHAR(50), -- 수량 - UNIT VARCHAR(50), -- 단위 - UNIT_PRICE NUMERIC(15,2), -- 단가 - AMOUNT NUMERIC(15,2), -- 금액 - NOTE VARCHAR(500), -- 비고 - REMARK VARCHAR(500), -- 특이사항 - - CONSTRAINT fk_item_template FOREIGN KEY (TEMPLATE_OBJID) - REFERENCES ESTIMATE_TEMPLATE(OBJID) ON DELETE CASCADE -); - --- 인덱스 생성 -CREATE INDEX idx_item_template ON ESTIMATE_TEMPLATE_ITEM(TEMPLATE_OBJID); -CREATE INDEX idx_item_seq ON ESTIMATE_TEMPLATE_ITEM(TEMPLATE_OBJID, SEQ); - --- 코멘트 추가 -COMMENT ON TABLE ESTIMATE_TEMPLATE_ITEM IS '견적서 템플릿 품목 정보'; -COMMENT ON COLUMN ESTIMATE_TEMPLATE_ITEM.TEMPLATE_OBJID IS '견적서 템플릿 OBJID'; -COMMENT ON COLUMN ESTIMATE_TEMPLATE_ITEM.SEQ IS '품목 순번'; -COMMENT ON COLUMN ESTIMATE_TEMPLATE_ITEM.CATEGORY IS '카테고리 (장비견적서: CNC Machine, UTILITY 등)'; - - --- 3. CONTRACT_MGMT 테이블에 CATEGORIES_JSON 컬럼 추가 (이미 없는 경우) --- ALTER TABLE ESTIMATE_TEMPLATE ADD COLUMN IF NOT EXISTS CATEGORIES_JSON TEXT; - --- 샘플 데이터 조회 쿼리 --- SELECT * FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = 123; --- SELECT * FROM ESTIMATE_TEMPLATE_ITEM WHERE TEMPLATE_OBJID = 456 ORDER BY SEQ; - diff --git a/plan.md b/plan.md deleted file mode 100644 index 6754a95..0000000 --- a/plan.md +++ /dev/null @@ -1,33 +0,0 @@ -# 프로젝트: 판매 관리 기능 개발 - -## 개요 -판매 관리 페이지의 UI를 개선하고, 판매 등록 및 조회 기능을 TDD(테스트 주도 개발) 원칙에 따라 개발합니다. - -## 핵심 기능 -1. 판매 관리 페이지 UI 구현 -2. 판매 등록 팝업 기능 -3. 판매 목록 조회 및 필터링 -4. 데이터베이스 연동 및 CRUD 기능 구현 - -## 테스트 계획 -### 1단계: UI 및 기본 기능 -- [x] 판매 관리 페이지 기본 UI 생성 -- [x] 필터 및 테이블 컬럼 구성 -- [x] 컨트롤러 URL 매핑 및 이름 재정의 -- [x] '판매등록' 버튼 추가 -- [ ] **테스트 1**: '판매등록' 버튼 클릭 시 팝업창이 정상적으로 뜨는가? -- [ ] **테스트 2**: '출고방법' 드롭다운에 DB 데이터가 정상적으로 표시되는가? -- [ ] **테스트 3**: '인도조건' 드롭다운에 DB 데이터가 정상적으로 표시되는가? - -### 2단계: 데이터 처리 -- [ ] **테스트 4**: 판매 등록 팝업에서 '저장' 버튼 클릭 시 데이터가 DB에 저장되는가? -- [ ] **테스트 5**: 판매 목록 조회 시 DB에서 데이터를 가져와 그리드에 표시하는가? -- [ ] **테스트 6**: 검색 조건으로 필터링했을 때 결과가 정확히 조회되는가? - -## 에러 처리 계획 -- 필수 입력값 누락 시 유효성 검사 및 알림 -- 데이터 저장 실패 시 에러 메시지 처리 - -## 진행 상태 -- 완료된 테스트는 [x]로 표시합니다. -- 현재 진행 중인 테스트는 **[진행중]** 으로 표시합니다. diff --git a/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java b/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java index c5909f5..2cafcb6 100644 --- a/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java +++ b/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java @@ -58,7 +58,7 @@ public class SalesNcollectMgmtController { /** 매출수금 Service */ @Autowired - private SalesNcollectMgmtService salseNcollectMgmtService; + private SalesNcollectMgmtService salesNcollectMgmtService; /** 영업관리 공통 Service */ @Autowired @@ -200,16 +200,33 @@ public class SalesNcollectMgmtController { Map resultMap = new HashMap(); try { - List> list = salseNcollectMgmtService.getSalesMgmtGridList(request, paramMap); - int totalCount = salseNcollectMgmtService.getSalesMgmtGridListCount(paramMap); + System.out.println("===== revenueGridList.do 파라미터 ====="); + System.out.println("paramMap: " + paramMap); + + List> list = salesNcollectMgmtService.getSalesMgmtGridList(request, paramMap); + int totalCount = salesNcollectMgmtService.getSalesMgmtGridListCount(paramMap); + Map totals = salesNcollectMgmtService.getSalesMgmtTotals(paramMap); + + System.out.println("list size: " + list.size()); + System.out.println("totalCount: " + totalCount); + System.out.println("totals: " + totals); resultMap.put("RESULTLIST", list); resultMap.put("last_page", (int) Math.ceil((double)totalCount / Integer.parseInt(CommonUtils.checkNull(request.getParameter("size"), "10")))); + resultMap.put("TOTALS", totals); // Total 공급가액, 부가세, 총액 } catch(Exception e) { e.printStackTrace(); // Tabulator는 에러 발생 시 빈 배열을 기대합니다. resultMap.put("RESULTLIST", new java.util.ArrayList<>()); resultMap.put("last_page", 0); + + // Total 초기화 + Map emptyTotals = new HashMap(); + + emptyTotals.put("TOTAL_SUPPLY_PRICE", 0); + emptyTotals.put("TOTAL_VAT", 0); + emptyTotals.put("TOTAL_AMOUNT", 0); + resultMap.put("TOTALS", emptyTotals); } return resultMap; @@ -232,6 +249,12 @@ public class SalesNcollectMgmtController { codeMap.put("managerList", salesMgmtCommonService.bizMakeOptionList("", "", "salesMgmtCommon.getSalesmanList")); + // 기존 판매 정보 조회 + if(paramMap.get("orderNo") != null && !paramMap.get("orderNo").equals("")) { + Map saleInfo = salesNcollectMgmtService.getSaleInfo(paramMap); + request.setAttribute("saleInfo", saleInfo); + } + request.setAttribute("codeMap", codeMap); } catch (Exception e) { e.printStackTrace(); @@ -239,6 +262,87 @@ public class SalesNcollectMgmtController { return "/salesmgmt/salesMgmt/salesRegForm"; } + @RequestMapping(value = "/salesMgmt/salesMgmtGridList.do", method = RequestMethod.POST) + @ResponseBody + public Map getSalesMgmtGridList(HttpServletRequest request, @RequestParam Map paramMap) { + Map resultMap = new HashMap(); + + try { + List> list = salesNcollectMgmtService.getSalesMgmtGridList(request, paramMap); + int totalCount = salesNcollectMgmtService.getSalesMgmtGridListCount(paramMap); + + resultMap.put("RESULTLIST", list); + resultMap.put("last_page", (int) Math.ceil((double)totalCount / Integer.parseInt(CommonUtils.checkNull(request.getParameter("size"), "10")))); + } catch(Exception e) { + e.printStackTrace(); + // Tabulator는 에러 발생 시 빈 배열을 기대합니다. + resultMap.put("RESULTLIST", new java.util.ArrayList<>()); + resultMap.put("last_page", 0); + } + + return resultMap; + } + + @RequestMapping(value = "/salesMgmt/saveSales.do", method = RequestMethod.POST) + @ResponseBody + public Map saveSales(HttpServletRequest request, @RequestParam Map paramMap) { + Map resultMap = new HashMap(); + try { + resultMap = salesNcollectMgmtService.saveSaleRegistration(request, paramMap); + } catch (Exception e) { + resultMap.put("result", false); + resultMap.put("msg", "저장 중 오류가 발생했습니다."); + e.printStackTrace(); + } + return resultMap; + } + + /** + *
+	 * 분할출하 팝업 폼
+	 * 
+ * @param request + * @param paramMap + * @return String + */ + @RequestMapping(value = "/salesMgmt/splitShipmentForm.do", method = RequestMethod.GET) + public String showSplitShipmentForm(HttpServletRequest request, @RequestParam Map paramMap) { + try { + Map codeMap = new HashMap(); + + // 담당자 + codeMap.put("managerList", + salesMgmtCommonService.bizMakeOptionList("", "", "salesMgmtCommon.getSalesmanList")); + + request.setAttribute("codeMap", codeMap); + } catch (Exception e) { + e.printStackTrace(); + } + return "/salesmgmt/salesMgmt/splitShipmentForm"; + } + + /** + *
+	 * 분할출하 저장 처리
+	 * 
+ * @param request + * @param paramMap - 분할출하 정보 + * @return Map + */ + @RequestMapping(value = "/salesMgmt/saveSplitShipment.do", method = RequestMethod.POST) + @ResponseBody + public Map saveSplitShipment(HttpServletRequest request, @RequestParam Map paramMap) { + Map resultMap = new HashMap(); + try { + resultMap = salesNcollectMgmtService.splitShipment(request, paramMap); + } catch (Exception e) { + resultMap.put("result", false); + resultMap.put("msg", "분할출하 처리 중 오류가 발생했습니다."); + e.printStackTrace(); + } + return resultMap; + } + /** *
 	 * 계약관리 목록 조회
@@ -299,7 +403,7 @@ public class SalesNcollectMgmtController {
 	public String sales(HttpServletRequest request
 					  , @RequestParam Map paramMap) {
 		try {
-			List> list = salseNcollectMgmtService.getSalseAllByOrderNo(request, paramMap);
+			List> list = salesNcollectMgmtService.getSalseAllByOrderNo(request, paramMap);
 			
 			request.setAttribute("LIST", list);
 		} catch(Exception e) {
@@ -335,7 +439,7 @@ public class SalesNcollectMgmtController {
 		try {
 			if(StringUtils.isNotBlank(saleNo)) {
 				// 매출관리 조회
-				info = salseNcollectMgmtService.getSalesMgmt(paramMap);
+				info = salesNcollectMgmtService.getSalesMgmt(paramMap);
 			} else {
 				info = new HashMap();
 			}
@@ -388,7 +492,7 @@ public class SalesNcollectMgmtController {
 		Map resultMap = new HashMap();
 		
 		try {
-			resultMap.put("RESULT", salseNcollectMgmtService.saveSalesMgmt(request, paramMap));		
+			resultMap.put("RESULT", salesNcollectMgmtService.saveSalesMgmt(request, paramMap));		
 		} catch(Exception e) {
 			e.printStackTrace();
 		}
@@ -409,7 +513,7 @@ public class SalesNcollectMgmtController {
 		Map resultMap = new HashMap();
 		
 		try {
-			resultMap.put("RESULT", salseNcollectMgmtService.deleteSalesMgmt(request, paramMap));
+			resultMap.put("RESULT", salesNcollectMgmtService.deleteSalesMgmt(request, paramMap));
 			
 			
 		
@@ -450,7 +554,7 @@ public class SalesNcollectMgmtController {
 						   , @RequestParam Map paramMap) { 
 
 		try {
-				List> list = salseNcollectMgmtService.getCollectAllByOrderNo(request, paramMap);
+				List> list = salesNcollectMgmtService.getCollectAllByOrderNo(request, paramMap);
 				
 				request.setAttribute("LIST", list);
 			} catch(Exception e) {
@@ -491,7 +595,7 @@ public class SalesNcollectMgmtController {
 			System.out.println("paramMap##################### " + paramMap);
 			if(StringUtils.isNotBlank(saleNo)) {
 				// 수금관리 조회
-				info = salseNcollectMgmtService.getCollectMgmt(paramMap);
+				info = salesNcollectMgmtService.getCollectMgmt(paramMap);
 			} else {
 				info = new HashMap();
 			}
@@ -540,7 +644,7 @@ public class SalesNcollectMgmtController {
 			*/
 			
 			//하단 그리드를 위한 수금 목록 조회
-			List> list = salseNcollectMgmtService.getCollectAllByOrderNo(request, paramMap);			
+			List> list = salesNcollectMgmtService.getCollectAllByOrderNo(request, paramMap);			
 			request.setAttribute("LIST", list);
 			
 			request.setAttribute("info", info);
@@ -577,7 +681,7 @@ public class SalesNcollectMgmtController {
 		Map resultMap = new HashMap();
 		System.out.println("ctr::paramMap##################### " + paramMap);		
 		try {
-			resultMap.put("RESULT", salseNcollectMgmtService.saveCollectMgmt(request, paramMap));		
+			resultMap.put("RESULT", salesNcollectMgmtService.saveCollectMgmt(request, paramMap));		
 		
 		} catch(Exception e) {
 			e.printStackTrace();			
@@ -599,7 +703,7 @@ public class SalesNcollectMgmtController {
 		Map resultMap = new HashMap();
 		
 		try {
-			resultMap.put("RESULT", salseNcollectMgmtService.deleteCollectMgmt(request, paramMap));
+			resultMap.put("RESULT", salesNcollectMgmtService.deleteCollectMgmt(request, paramMap));
 		} catch(Exception e) {
 			e.printStackTrace();
 		}
diff --git a/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml b/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml
index 61b6849..c08aafe 100644
--- a/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml
+++ b/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml
@@ -812,188 +812,332 @@
 			,editEmpNo = #{cretEmpNo}			/* 수정자 */
 	
 	
-	
+	
 	
 	
-	
-	
+		/* salesNcollectMgmt.getSalesMgmtGridListCount - VIEW 기반 */
 		SELECT 
-			COUNT(1)
-		FROM SWSC110A_TBL B
-		LEFT JOIN SWSD010A_TBL A ON A.orderNo = B.orderNo
-		INNER JOIN SWSB110A_TBL C ON B.goodsCd = C.goodsCd
-		INNER JOIN SWSB210A_TBL D ON B.custCd = D.custCd
+			CEIL(COUNT(1)::float / #{COUNT_PER_PAGE}::float)::numeric::integer AS MAX_PAGE_SIZE,
+			COUNT(1)::integer AS TOTAL_CNT
+		FROM v_sales_mgmt_with_splits
 		WHERE 1 = 1
 		
-			AND B.SaleType = #{orderType}
+			AND CATEGORY_CD = #{orderType}
 		
 		
-			AND C.c_class = #{productType}
+			AND PRODUCT_TYPE = #{productType}
 		
 		
-			AND B.nationGB = #{nation}
+			AND AREA_CD = #{nation}
 		
 		
-			AND D.custNm LIKE CONCAT('%', #{customer}, '%')
+			AND CUSTOMER LIKE CONCAT('%', #{customer}, '%')
 		
 		
-			AND B.freeyn = #{paymentType}
+			AND PAID_TYPE = #{paymentType}
 		
 		
-			AND C.GoodsCd LIKE CONCAT('%', #{productNo}, '%')
+			AND PRODUCT_NO LIKE CONCAT('%', #{productNo}, '%')
 		
 		
-			AND C.GoodsNm LIKE CONCAT('%', #{productName}, '%')
+			AND PRODUCT_NAME LIKE CONCAT('%', #{productName}, '%')
 		
 		
-			AND A.serialno LIKE CONCAT('%', #{serialNo}, '%')
+			AND SERIAL_NO LIKE CONCAT('%', #{serialNo}, '%')
 		
 		
-			AND B.endsale = #{orderStatus}
+			AND STATUS_CD = #{orderStatus}
 		
 		
-			AND B.OrderNo LIKE CONCAT('%', #{poNo}, '%')
+			AND PO_NO LIKE CONCAT('%', #{poNo}, '%')
 		
 		
-			AND B.custreq =]]> #{requestDateFrom}
+			AND REQUEST_DATE =]]> #{requestDateFrom}
 		
 		
-			AND B.custreq  #{requestDateTo}
+			AND REQUEST_DATE  #{requestDateTo}
 		
 		
-			AND B.OrderDate =]]> #{orderDateFrom}
+			AND ORDER_DATE =]]> #{orderDateFrom}
 		
 		
-			AND B.OrderDate  #{orderDateTo}
+			AND ORDER_DATE  #{orderDateTo}
 		
 		
-			AND B.shippingstatus = #{shippingStatus}
+			AND SALES_STATUS = #{shippingStatus}
 		
 		
-			AND B.shippingdate =]]> #{shippingDateFrom}
+			AND SHIPPING_DATE =]]> #{shippingDateFrom}
 		
 		
-			AND B.shippingdate  #{shippingDateTo}
+			AND SHIPPING_DATE  #{shippingDateTo}
 		
 		
-			AND B.outGb = #{shippingMethod}
+			AND SHIPPING_METHOD = #{shippingMethod}
 		
 		
-			AND B.bEmpNo = #{manager}
+			AND PM_USER_ID = #{manager}
 		
 		
-			AND B.incoterms = #{incoterms}
+			AND INCOTERMS = #{incoterms}
 		
 	
 	
+	
+	
+
+	
+	
+
+	
+	
+		/* salesNcollectMgmt.insertSplitShipmentLog */
+		INSERT INTO shipment_log (
+			target_objid,
+			log_type,
+			log_message,
+			split_quantity,
+			original_quantity,
+			shipping_status,
+			sales_unit_price,
+			sales_supply_price,
+			sales_vat,
+			sales_total_amount,
+			sales_currency,
+			sales_exchange_rate,
+			serial_no,
+			shipping_date,
+			shipping_method,
+			manager_user_id,
+			incoterms,
+			remark,
+			is_split_record,
+			reg_date,
+			reg_user_id
+		) VALUES (
+			#{objid},
+			#{logType},
+			#{logMessage},
+			#{splitQuantity}::integer,
+			#{originalQuantity}::integer,
+			'PENDING',
+			#{salesUnitPrice}::numeric,
+			#{salesSupplyPrice}::numeric,
+			#{salesVat}::numeric,
+			#{salesTotalAmount}::numeric,
+			#{salesCurrency},
+			#{salesExchangeRate}::numeric,
+			#{serialNo},
+			
+				
+					TO_DATE(#{shippingDate}, 'YYYY-MM-DD'),
+				
+				
+					NULL,
+				
+			
+			#{shippingMethod},
+			#{managerUserId},
+			#{incoterms},
+			#{remark},
+			true,
+			NOW(),
+			#{userId}
+		)
+	
+	
 
 
diff --git a/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java b/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java
index b2b71ae..00d2d12 100644
--- a/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java
+++ b/src/com/pms/salesmgmt/service/SalesNcollectMgmtService.java
@@ -147,14 +147,18 @@ public class SalesNcollectMgmtService {
 		try {
 			sqlSession = SqlMapConfig.getInstance().getSqlSession();
 			
-			// Tabulator는 page, size 파라미터를 사용
-			String page = CommonUtils.checkNull(request.getParameter("page"), "1");
-			String size = CommonUtils.checkNull(request.getParameter("size"), Constants.ADMIN_COUNT_PER_PAGE+"");
-			int pageNum = Integer.parseInt(page);
-			int sizeNum = Integer.parseInt(size);
+			// 페이징 HTML 생성을 위해 DB에서 총 카운트 조회
+			String countPerPage = CommonUtils.checkNull(request.getParameter("size"), Constants.ADMIN_COUNT_PER_PAGE+"");
+			paramMap.put("COUNT_PER_PAGE", Integer.parseInt(countPerPage));
+			Map pageMap = (HashMap) sqlSession.selectOne("salesNcollectMgmt.getSalesMgmtGridListCount", paramMap);
+			pageMap = (HashMap) CommonUtils.setPagingInfo(request, pageMap);
+			paramMap.put("PAGE_HTML", CommonUtils.checkNull(pageMap.get("PAGE_HTML")));
 			
+			// 실제 쿼리에 사용할 LIMIT, OFFSET 값 계산 (정수 타입으로)
+			String page = CommonUtils.checkNull(request.getParameter("page"), "1");
+			int pageNum = Integer.parseInt(page);
+			int sizeNum = Integer.parseInt(countPerPage);
 			paramMap.put("PAGE_START", (pageNum - 1) * sizeNum);
-			paramMap.put("COUNT_PER_PAGE", sizeNum);
 			
 			resultList = (ArrayList) sqlSession.selectList("salesNcollectMgmt.getSalesMgmtGridList", paramMap);	
 		} catch(Exception e) {
@@ -181,7 +185,10 @@ public class SalesNcollectMgmtService {
 		
 		try {
 			sqlSession = SqlMapConfig.getInstance().getSqlSession();
-			totalCount = sqlSession.selectOne("salesNcollectMgmt.getSalesMgmtGridListCount", paramMap);
+			Map resultMap = sqlSession.selectOne("salesNcollectMgmt.getSalesMgmtGridListCount", paramMap);
+			if(resultMap != null && resultMap.get("TOTAL_CNT") != null) {
+				totalCount = Integer.parseInt(resultMap.get("TOTAL_CNT").toString());
+			}
 		} catch(Exception e) {
 			e.printStackTrace();
 		} finally {
@@ -193,6 +200,190 @@ public class SalesNcollectMgmtService {
 		return totalCount;
 	}
 	
+	/**
+	 * 매출관리 합계 조회 (Total 공급가액, 부가세, 총액)
+	 * @param paramMap - 검색 조건
+	 * @return Map - TOTAL_SUPPLY_PRICE, TOTAL_VAT, TOTAL_AMOUNT
+	 */
+	public Map getSalesMgmtTotals(Map paramMap) {
+		SqlSession sqlSession = null;
+		Map totalsMap = new HashMap();
+		
+		try {
+			sqlSession = SqlMapConfig.getInstance().getSqlSession();
+			totalsMap = sqlSession.selectOne("salesNcollectMgmt.getSalesMgmtTotals", paramMap);
+			
+			if(totalsMap == null) {
+				totalsMap = new HashMap();
+				totalsMap.put("TOTAL_SUPPLY_PRICE", 0);
+				totalsMap.put("TOTAL_VAT", 0);
+				totalsMap.put("TOTAL_AMOUNT", 0);
+			}
+		} catch(Exception e) {
+			e.printStackTrace();
+			totalsMap.put("TOTAL_SUPPLY_PRICE", 0);
+			totalsMap.put("TOTAL_VAT", 0);
+			totalsMap.put("TOTAL_AMOUNT", 0);
+		} finally {
+			if(sqlSession != null) {
+				sqlSession.close();
+			}
+		}
+		
+		return totalsMap;
+	}
+	
+	/**
+	 * 
+	 * 판매 정보 조회
+	 * 
+ * @param paramMap + * @return Map + */ + public Map getSaleInfo(Map paramMap) { + Map resultMap = new HashMap(); + SqlSession sqlSession = null; + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(); + resultMap = sqlSession.selectOne("salesNcollectMgmt.getSaleInfo", paramMap); + } catch(Exception e) { + e.printStackTrace(); + } finally { + if(sqlSession != null) { + sqlSession.close(); + } + } + + return CommonUtils.toUpperCaseMapKey(resultMap); + } + + public Map saveSaleRegistration(HttpServletRequest request, Map paramMap) { + Map resultMap = new HashMap(); + SqlSession sqlSession = null; + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(false); + + PersonBean person = (PersonBean) request.getSession().getAttribute(Constants.PERSON_BEAN); + paramMap.put("cretEmpNo", person.getUserId()); + + // SWSC110A_TBL 업데이트 + sqlSession.update("salesNcollectMgmt.updateSaleRegistration_swsc110a", paramMap); + + // SWSD010A_TBL 업데이트 (insert or update) + sqlSession.update("salesNcollectMgmt.upsertSaleRegistration_swsd010a", paramMap); + + sqlSession.commit(); + resultMap.put("result", true); + resultMap.put("msg", "저장되었습니다."); + } catch(Exception e) { + sqlSession.rollback(); + resultMap.put("result", false); + resultMap.put("msg", "저장 중 오류가 발생했습니다."); + e.printStackTrace(); + } finally { + if(sqlSession != null) { + sqlSession.close(); + } + } + + return resultMap; + } + + /** + *
+	 * 분할출하 처리 (로그 기반)
+	 * - 원본 데이터는 그대로 유지
+	 * - 분할출하 로그만 shipment_log에 저장
+	 * - 화면에서는 VIEW를 통해 원본 + 로그를 합쳐서 표시
+	 * 
+ * @param request + * @param paramMap - 분할출하 정보 + * @return Map + */ + public Map splitShipment(HttpServletRequest request, Map paramMap) { + Map resultMap = new HashMap(); + SqlSession sqlSession = null; + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(false); + + PersonBean person = (PersonBean) request.getSession().getAttribute(Constants.PERSON_BEAN); + String userId = person.getUserId(); + + // 원본 데이터 조회 + Map originalData = sqlSession.selectOne("salesNcollectMgmt.getContractByObjid", paramMap); + + if(originalData == null) { + resultMap.put("result", false); + resultMap.put("msg", "원본 데이터를 찾을 수 없습니다."); + return resultMap; + } + + // 분할 수량 + int splitQuantity = Integer.parseInt(paramMap.get("splitQuantity").toString()); + int originalQuantity = Integer.parseInt(paramMap.get("originalQuantity").toString()); + + // 원본 데이터의 단가 정보 가져오기 + BigDecimal unitPrice = new BigDecimal(originalData.get("order_unit_price").toString()); + BigDecimal exchangeRate = new BigDecimal(originalData.get("exchange_rate").toString()); + + // 분할 금액 계산 + BigDecimal splitSupplyPrice = unitPrice.multiply(new BigDecimal(splitQuantity)); + BigDecimal splitVat = splitSupplyPrice.multiply(new BigDecimal("0.1")); + BigDecimal splitTotalAmount = splitSupplyPrice.add(splitVat); + + // 분할출하 로그 저장 + paramMap.put("logType", "SPLIT_SHIPMENT"); + paramMap.put("logMessage", "분할출하 - 원본수량: " + originalQuantity + ", 분할수량: " + splitQuantity); + paramMap.put("userId", userId); + paramMap.put("splitQuantity", splitQuantity); + paramMap.put("originalQuantity", originalQuantity); + paramMap.put("salesUnitPrice", unitPrice); + paramMap.put("salesSupplyPrice", splitSupplyPrice); + paramMap.put("salesVat", splitVat); + paramMap.put("salesTotalAmount", splitTotalAmount); + paramMap.put("salesCurrency", originalData.get("contract_currency")); + paramMap.put("salesExchangeRate", exchangeRate); + + // 팝업에서 입력한 데이터 우선, 없으면 원본 데이터 사용 + paramMap.put("shippingMethod", + StringUtils.isNotBlank((String)paramMap.get("shippingMethod")) ? + paramMap.get("shippingMethod") : originalData.get("shipping_method")); + paramMap.put("incoterms", + StringUtils.isNotBlank((String)paramMap.get("incoterms")) ? + paramMap.get("incoterms") : originalData.get("incoterms")); + paramMap.put("managerUserId", + StringUtils.isNotBlank((String)paramMap.get("manager")) ? + paramMap.get("manager") : originalData.get("pm_user_id")); + + // 팝업에서 입력한 추가 정보 + // serialNo, shippingDate, remark는 팝업에서만 입력 가능 + + sqlSession.insert("salesNcollectMgmt.insertSplitShipmentLog", paramMap); + + sqlSession.commit(); + + resultMap.put("result", true); + resultMap.put("msg", "분할출하가 완료되었습니다."); + + } catch(Exception e) { + if(sqlSession != null) { + sqlSession.rollback(); + } + resultMap.put("result", false); + resultMap.put("msg", "분할출하 처리 중 오류가 발생했습니다: " + e.getMessage()); + e.printStackTrace(); + } finally { + if(sqlSession != null) { + sqlSession.close(); + } + } + + return resultMap; + } + /** *
 	 * 매출관리 등록