From b643c9d494bad9e64f21383858c357f03f13316c Mon Sep 17 00:00:00 2001 From: hjjeong Date: Mon, 9 Mar 2026 11:35:53 +0900 Subject: [PATCH 01/10] =?UTF-8?q?=EC=B6=9C=ED=95=98=EC=A7=80=EC=8B=9C/?= =?UTF-8?q?=ED=8C=90=EB=A7=A4=EB=93=B1=EB=A1=9D=20=ED=86=B5=ED=95=A9=20?= =?UTF-8?q?=EB=B0=8F=20S/N=20=EB=A7=88=EC=8A=A4=ED=84=B0=20=EA=B8=B0?= =?UTF-8?q?=EC=A4=80=20=EC=A1=B0=ED=9A=8C=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 출하지시/판매등록 버튼: 항상 신규 shipment_log 생성 (기존 분할출하와 통합) - 출하일 상세 팝업: 행 클릭 시 해당 shipment_log 수정 가능 - S/N 조회: shipment_log 스냅샷 → CONTRACT_ITEM_SERIAL 마스터 기준으로 변경 - 수정 시 최신 S/N 표시, 저장 시 마스터에 sync Made-with: Cursor --- .../salesmgmt/mapper/salesNcollectMgmt.xml | 348 ++++++++++++++---- .../salesmgmt/salesMgmt/salesMgmtList.jsp | 27 +- .../view/salesmgmt/salesMgmt/salesRegForm.jsp | 39 +- .../salesMgmt/shippingDetailPopup.jsp | 35 +- .../SalesNcollectMgmtController.java | 20 +- .../salesmgmt/mapper/salesNcollectMgmt.xml | 113 +++++- .../service/SalesNcollectMgmtService.java | 142 ++++--- 7 files changed, 566 insertions(+), 158 deletions(-) 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 5c83660..9a65e1d 100644 --- a/WebContent/WEB-INF/classes/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml +++ b/WebContent/WEB-INF/classes/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml @@ -844,19 +844,22 @@ FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID) AS PAYMENT_TYPE, T.PART_NO AS PRODUCT_NO, T.PART_NAME AS PRODUCT_NAME, - -- S/N: 해당 품목의 시리얼 번호 (여러 개일 경우 "S/N 외 N건" 형식) - (SELECT - CASE - WHEN COUNT(*) = 0 THEN '' - WHEN COUNT(*) = 1 THEN MIN(CIS.SERIAL_NO) - ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건' - END - FROM CONTRACT_ITEM CI - LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND UPPER(CIS.STATUS) = 'ACTIVE' - WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID - AND CI.PART_OBJID = T.PART_OBJID - AND CI.STATUS = 'ACTIVE' - AND CIS.SERIAL_NO IS NOT NULL) AS SERIAL_NO, + -- S/N: CONTRACT_ITEM_SERIAL(마스터) 우선, 없으면 판매등록 텍스트 fallback (그리드 요약용) + COALESCE( + (SELECT + CASE + WHEN COUNT(*) = 0 THEN NULL + WHEN COUNT(*) = 1 THEN MIN(CIS.SERIAL_NO) + ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건' + END + FROM CONTRACT_ITEM CI + LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND UPPER(CIS.STATUS) = 'ACTIVE' + WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID + AND CI.PART_OBJID = T.PART_OBJID + AND CI.STATUS = 'ACTIVE' + AND CIS.SERIAL_NO IS NOT NULL), + SR.serial_no + ) AS SERIAL_NO, COALESCE(T.QUANTITY::numeric, 0) AS ORDER_QUANTITY, -- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT COALESCE( @@ -893,20 +896,10 @@ COALESCE(SR.sales_vat, 0) AS SALES_VAT, COALESCE(SR.sales_total_amount, 0) AS SALES_TOTAL_AMOUNT, COALESCE(SR.sales_total_amount, 0) AS SALES_TOTAL_AMOUNT_KRW, - -- 잔량 계산: 수주수량 - shipment_log의 split_quantity 합계 - COALESCE(T.QUANTITY::numeric, 0) - COALESCE( - (SELECT SUM(COALESCE(split_quantity, 0)) - FROM shipment_log - WHERE target_objid = T.PROJECT_NO), - 0 - ) AS REMAINING_QUANTITY, - -- 잔량원화총액 계산: (수주수량 - shipment_log 합계) * 판매단가 - (COALESCE(T.QUANTITY::numeric, 0) - COALESCE( - (SELECT SUM(COALESCE(split_quantity, 0)) - FROM shipment_log - WHERE target_objid = T.PROJECT_NO), - 0 - )) * COALESCE(SR.sales_unit_price, 0) AS REMAINING_AMOUNT_KRW, + -- 잔량 계산: 수주수량 - sales_registration.sales_quantity + COALESCE(T.QUANTITY::numeric, 0) - COALESCE(SR.sales_quantity, 0) AS REMAINING_QUANTITY, + -- 잔량원화총액 계산: 잔량 * 판매단가 + (COALESCE(T.QUANTITY::numeric, 0) - COALESCE(SR.sales_quantity, 0)) * COALESCE(SR.sales_unit_price, 0) AS REMAINING_AMOUNT_KRW, COALESCE(SR.sales_currency, T.CONTRACT_CURRENCY) AS SALES_CURRENCY, CODE_NAME(COALESCE(SR.sales_currency, T.CONTRACT_CURRENCY)) AS SALES_CURRENCY_NAME, COALESCE(SR.sales_exchange_rate, T.CONTRACT_PRICE_CURRENCY::numeric, 0) AS SALES_EXCHANGE_RATE, @@ -942,13 +935,14 @@ THEN '분할판매' ELSE '' END AS SALES_STATUS, + SR.sale_no AS SALES_REG_NO, T.OBJID::VARCHAR AS SALE_NO, 'ORIGINAL' AS RECORD_TYPE, '' AS SPLIT_LOG_ID, - -- 거래명세서 존재 여부 + -- 거래명세서 존재 여부 (LinkedProjectObjids에 해당 프로젝트 OBJID 포함 여부) CASE WHEN EXISTS( - SELECT 1 FROM NSWOS100_TBL - WHERE OdOrderNo = T.PROJECT_NO + SELECT 1 FROM NSWOS100_TBL + WHERE T.OBJID::VARCHAR = ANY(STRING_TO_ARRAY(LinkedProjectObjids, ',')) ) 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 @@ -1346,7 +1340,18 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC AND T.CUSTOMER_OBJID = #{customer_objid} - AND SR.serial_no LIKE '%' || #{serialNo} || '%' + AND ( + EXISTS ( + SELECT 1 FROM CONTRACT_ITEM CI + JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID + WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID + AND CI.PART_OBJID = T.PART_OBJID + AND CI.STATUS = 'ACTIVE' + AND UPPER(CIS.STATUS) = 'ACTIVE' + AND UPPER(CIS.SERIAL_NO) LIKE UPPER('%' || #{serialNo} || '%') + ) + OR SR.serial_no LIKE '%' || #{serialNo} || '%' + ) AND EXISTS (SELECT 1 FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID AND CM.PO_NO LIKE '%' || #{poNo} || '%') @@ -1550,19 +1555,17 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID) AS PAYMENT_TYPE, T.PART_NO AS PRODUCT_NO, T.PART_NAME AS PRODUCT_NAME, - -- S/N: 해당 품목의 시리얼 번호 (여러 개일 경우 "S/N 외 N건" 형식) - (SELECT - CASE - WHEN COUNT(*) = 0 THEN '' - WHEN COUNT(*) = 1 THEN MIN(CIS.SERIAL_NO) - ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건' - END - FROM CONTRACT_ITEM CI - LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND UPPER(CIS.STATUS) = 'ACTIVE' - WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID - AND CI.PART_OBJID = T.PART_OBJID - AND CI.STATUS = 'ACTIVE' - AND CIS.SERIAL_NO IS NOT NULL) AS SERIAL_NO, + -- S/N: CONTRACT_ITEM_SERIAL(마스터) 우선, 전체 콤마 리스트 (판매등록 폼 파싱용) + COALESCE( + (SELECT STRING_AGG(CIS.SERIAL_NO, ', ' ORDER BY CIS.SERIAL_NO) + FROM CONTRACT_ITEM CI + LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND UPPER(CIS.STATUS) = 'ACTIVE' + WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID + AND CI.PART_OBJID = T.PART_OBJID + AND CI.STATUS = 'ACTIVE' + AND CIS.SERIAL_NO IS NOT NULL), + SR.serial_no + ) AS SERIAL_NO, COALESCE(T.QUANTITY::numeric, 0) AS ORDER_QUANTITY, -- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT COALESCE( @@ -1648,8 +1651,8 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC CODE_NAME(CM.CONTRACT_CURRENCY) AS SALES_CURRENCY_NAME, COALESCE(CM.EXCHANGE_RATE::NUMERIC, 0) AS SALES_EXCHANGE_RATE, - -- 수주 날짜 - VARCHAR 타입이므로 그대로 사용 - CM.ORDER_DATE AS SHIPPING_DATE, + -- 출하일 기본값: 오늘 날짜 + TO_CHAR(CURRENT_DATE, 'YYYY-MM-DD') AS SHIPPING_DATE, -- 담당자 CM.PM_USER_ID AS MANAGER @@ -1662,7 +1665,6 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC CM.OBJID, CM.CONTRACT_CURRENCY, CM.EXCHANGE_RATE, - CM.ORDER_DATE, CM.PM_USER_ID @@ -1696,24 +1698,35 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC CODE_NAME(CM.CONTRACT_CURRENCY) AS SALES_CURRENCY_NAME, COALESCE(NULLIF(CM.EXCHANGE_RATE, '')::NUMERIC, 0) AS SALES_EXCHANGE_RATE, - -- 수주 날짜 - VARCHAR 타입이므로 그대로 사용 - CM.ORDER_DATE AS SHIPPING_DATE, - + -- 출하일 기본값: 오늘 날짜 + TO_CHAR(CURRENT_DATE, 'YYYY-MM-DD') AS SHIPPING_DATE, + -- 담당자 - CM.PM_USER_ID AS MANAGER - + CM.PM_USER_ID AS MANAGER, + + -- 기존 S/N (CONTRACT_ITEM_SERIAL 마스터에서 조회) + COALESCE(( + SELECT STRING_AGG(CIS2.SERIAL_NO, ',' ORDER BY CIS2.SEQ) + FROM CONTRACT_ITEM CI2 + JOIN CONTRACT_ITEM_SERIAL CIS2 ON CI2.OBJID = CIS2.ITEM_OBJID AND CIS2.STATUS = 'ACTIVE' + WHERE CI2.CONTRACT_OBJID = PM.CONTRACT_OBJID + AND CI2.PART_OBJID = PM.PART_OBJID + AND CI2.STATUS = 'ACTIVE' + ), '') AS SERIAL_NO + FROM PROJECT_MGMT PM INNER JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID LEFT JOIN CONTRACT_ITEM CI ON CM.OBJID::VARCHAR = CI.CONTRACT_OBJID AND UPPER(CI.STATUS) = 'ACTIVE' WHERE PM.PROJECT_NO = #{orderNo} - GROUP BY + GROUP BY CM.CONTRACT_NO, CM.OBJID, CM.CONTRACT_CURRENCY, CM.EXCHANGE_RATE, - CM.ORDER_DATE, CM.PM_USER_ID, - PM.QUANTITY + PM.QUANTITY, + PM.CONTRACT_OBJID, + PM.PART_OBJID @@ -1932,19 +1969,98 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC - + + + + + + + /* salesNcollectMgmt.updateShipmentLog - shipment_log 수정 */ + UPDATE shipment_log SET + split_quantity = #{salesQuantity}::integer, + shipping_status = #{shippingOrderStatus} + + , shipping_date = TO_DATE(#{shippingDate}, 'YYYY-MM-DD') + + + , shipping_method = #{shippingMethod} + + + , serial_no = #{serialNo} + + + , sales_unit_price = #{salesUnitPrice}::numeric + + + , sales_supply_price = #{salesSupplyPrice}::numeric + + + , sales_vat = #{salesVat}::numeric + + + , sales_total_amount = #{salesTotalAmount}::numeric + + + , sales_currency = #{salesCurrency} + + + , sales_exchange_rate = #{salesExchangeRate}::numeric + + + , manager_user_id = #{managerUserId} + + + , incoterms = #{incoterms} + + WHERE log_id = #{logId}::integer + + - + /* salesNcollectMgmt.saveTransactionStatement - 거래명세서 저장 */ INSERT INTO NSWOS100_TBL ( @@ -1995,6 +2111,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC ,IsQty /* 납품수량 */ ,IsPrice /* 납품단가 */ ,IsAmount /* 납품금액 */ + ,LinkedProjectObjids /* 연결된 프로젝트 OBJID 목록 */ ) VALUES ( #{suVndCd} /* 업체코드 */ ,#{issueDt} /* 작성일자 */ @@ -2011,6 +2128,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC ,COALESCE(#{isQty}, 0)::integer /* 납품수량 */ ,COALESCE(#{isPrice}, 0)::numeric /* 납품단가 */ ,COALESCE(#{isAmount}, 0)::numeric /* 납품금액 */ + ,#{linkedProjectObjids} /* 연결된 프로젝트 OBJID 목록 */ ) ON CONFLICT (SuVndCd, IssueDt, IssueNo, IsNo) DO UPDATE SET ProdCd = COALESCE(#{prodCd}, '') @@ -2024,6 +2142,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC ,IsQty = COALESCE(#{isQty}, 0)::integer ,IsPrice = COALESCE(#{isPrice}, 0)::numeric ,IsAmount = COALESCE(#{isAmount}, 0)::numeric + ,LinkedProjectObjids = #{linkedProjectObjids} @@ -2051,7 +2170,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC @@ -2142,6 +2262,102 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC LOADING_DATE = #{loadingDate} WHERE OBJID::VARCHAR = #{OBJID} - + + + + + + + + + + + + /* salesNcollectMgmt.updateSlipInfo - 전표 연동 정보 저장 */ + UPDATE PROJECT_MGMT + SET + SALES_STATUS = '완료', + SALES_DEADLINE_DATE = #{deadlineDate}, + SALES_SLIP_DATE = #{slipDate}, + SALES_SLIP_MENU_SQ = #{slipMenuSq} + WHERE OBJID::VARCHAR = #{OBJID} + + diff --git a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp index 0e7dbd9..2e76fcc 100644 --- a/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp +++ b/WebContent/WEB-INF/view/salesmgmt/salesMgmt/salesMgmtList.jsp @@ -620,19 +620,21 @@ function fn_openTransactionStatementPopup(projectNo, projectObjid, hasStatement) } function fn_openShippingDetail(projectNo) { - console.log("=== fn_openShippingDetail 호출 ==="); - console.log("전달받은 projectNo:", projectNo); - console.log("projectNo 타입:", typeof projectNo); - if(!projectNo) { alert("프로젝트 번호가 없습니다."); return; } - + var url = "/salesMgmt/shippingDetailPopup.do?projectNo=" + encodeURIComponent(projectNo); - console.log("팝업 URL:", url); - - window.open(url, "shippingDetailPopup", "width=800,height=600,scrollbars=yes,resizable=yes"); + var detailPopup = window.open(url, "shippingDetailPopup", "width=800,height=600,scrollbars=yes,resizable=yes"); + + // 팝업 닫힐 때 목록 새로고침 + var checkPopup = setInterval(function() { + if(detailPopup.closed) { + clearInterval(checkPopup); + fn_search(); + } + }, 500); } // 분할출하 함수 - 선택한 항목을 동일 프로젝트번호로 분할출하 @@ -702,13 +704,8 @@ function fn_bulkRegister(){ } } - if(selectedRow.SALES_REG_NO) { - // 기존 판매등록 수정 모드 - fn_openSaleRegPopup(selectedRow.PROJECT_NO, selectedRow.SALES_REG_NO); - } else { - // 신규 판매등록 - fn_openSaleRegPopupWithData(selectedRow); - } + // 항상 신규 출하 등록 (수정은 출하일 상세 팝업에서) + fn_openSaleRegPopupWithData(selectedRow); } +