Merge pull request 'V20260210' (#163) from V20260210 into main

Reviewed-on: #163
This commit was merged in pull request #163.
This commit is contained in:
2026-03-09 08:04:48 +00:00
23 changed files with 1386 additions and 341 deletions

View File

@@ -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}
</if>
<if test="serialNo != null and serialNo != ''">
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} || '%'
)
</if>
<if test="poNo != null and poNo != ''">
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
</select>
@@ -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
</select>
<!--
@@ -1920,7 +1933,31 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
WHERE target_objid = #{projectNo}
),
shipping_order_status = '출하지시'
WHERE sale_no = #{saleNo}
<if test="managerUserId != null and managerUserId != ''">
, manager_user_id = #{managerUserId}
</if>
<if test="incoterms != null and incoterms != ''">
, incoterms = #{incoterms}
</if>
<if test="serialNo != null and serialNo != ''">
, serial_no = #{serialNo}
</if>
<if test="shippingMethod != null and shippingMethod != ''">
, shipping_method = #{shippingMethod}
</if>
<if test="shippingDate != null and shippingDate != ''">
, shipping_date = TO_DATE(#{shippingDate}, 'YYYY-MM-DD')
</if>
<if test="salesUnitPrice != null and salesUnitPrice != ''">
, sales_unit_price = #{salesUnitPrice}::numeric
</if>
<if test="salesCurrency != null and salesCurrency != ''">
, sales_currency = #{salesCurrency}
</if>
<if test="salesExchangeRate != null and salesExchangeRate != ''">
, sales_exchange_rate = #{salesExchangeRate}::numeric
</if>
WHERE sale_no = #{saleNo}::integer
</update>
<!-- PROJECT_MGMT의 OBJID 조회 (shipment_log의 target_objid로 사용) -->
@@ -1932,19 +1969,98 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
<!-- 출하일 상세 내역 조회 (모든 분할 출하 포함) -->
<select id="getShippingDetailList" parameterType="map" resultType="map">
/* salesNcollectMgmt.getShippingDetailList - shipment_log에서 모든 분할 출하 조회 */
SELECT
SELECT
SL.log_id,
COALESCE(TO_CHAR(SL.shipping_date, 'YYYY-MM-DD'), '미등록') AS shipping_date,
COALESCE(SL.split_quantity, 0) AS shipping_quantity,
COALESCE(SL.shipping_status, '미등록') AS shipping_order_status,
COALESCE(SL.serial_no, '-') AS serial_no,
COALESCE((
SELECT STRING_AGG(CIS.SERIAL_NO, ',' ORDER BY CIS.SEQ)
FROM CONTRACT_ITEM CI
JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND CIS.STATUS = 'ACTIVE'
JOIN PROJECT_MGMT PM ON PM.CONTRACT_OBJID = CI.CONTRACT_OBJID AND PM.PART_OBJID = CI.PART_OBJID
WHERE PM.PROJECT_NO = SL.target_objid AND CI.STATUS = 'ACTIVE'
), '-') AS serial_no,
SL.target_objid AS project_no,
'분할 출하 ' || SL.log_id AS source,
TO_CHAR(SL.reg_date, 'YYYY-MM-DD HH24:MI:SS') AS reg_date
FROM shipment_log SL
FROM shipment_log SL
WHERE SL.target_objid = #{projectNo}
ORDER BY SL.shipping_date DESC, SL.log_id DESC
</select>
<!-- shipment_log 단건 조회 (수정 팝업용) -->
<select id="getShipmentLogById" parameterType="map" resultType="map">
/* salesNcollectMgmt.getShipmentLogById - shipment_log 단건 조회 */
SELECT
SL.log_id,
SL.target_objid,
COALESCE(TO_CHAR(SL.shipping_date, 'YYYY-MM-DD'), '') AS SHIPPING_DATE,
COALESCE(SL.split_quantity, 0) AS SALES_QUANTITY,
COALESCE(SL.shipping_status, '') AS SHIPPING_ORDER_STATUS,
COALESCE(SL.shipping_method, '') AS SHIPPING_METHOD,
COALESCE((
SELECT STRING_AGG(CIS.SERIAL_NO, ',' ORDER BY CIS.SEQ)
FROM CONTRACT_ITEM CI
JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND CIS.STATUS = 'ACTIVE'
JOIN PROJECT_MGMT PM ON PM.CONTRACT_OBJID = CI.CONTRACT_OBJID AND PM.PART_OBJID = CI.PART_OBJID
WHERE PM.PROJECT_NO = SL.target_objid AND CI.STATUS = 'ACTIVE'
), '') AS SERIAL_NO,
COALESCE(SL.sales_unit_price, 0) AS SALES_UNIT_PRICE,
COALESCE(SL.sales_supply_price, 0) AS SALES_SUPPLY_PRICE,
COALESCE(SL.sales_vat, 0) AS SALES_VAT,
COALESCE(SL.sales_total_amount, 0) AS SALES_TOTAL_AMOUNT,
COALESCE(SL.sales_currency, '') AS SALES_CURRENCY,
COALESCE(SL.sales_exchange_rate, 0) AS SALES_EXCHANGE_RATE,
COALESCE(SL.manager_user_id, '') AS MANAGER,
COALESCE(SL.incoterms, '') AS INCOTERMS,
COALESCE(SL.original_quantity, 0) AS ORDER_QUANTITY,
COALESCE(SL.remaining_quantity, 0) AS REMAINING_QUANTITY
FROM shipment_log SL
WHERE SL.log_id = #{logId}::integer
</select>
<!-- shipment_log 수정 -->
<update id="updateShipmentLog" parameterType="map">
/* salesNcollectMgmt.updateShipmentLog - shipment_log 수정 */
UPDATE shipment_log SET
split_quantity = #{salesQuantity}::integer,
shipping_status = #{shippingOrderStatus}
<if test="shippingDate != null and shippingDate != ''">
, shipping_date = TO_DATE(#{shippingDate}, 'YYYY-MM-DD')
</if>
<if test="shippingMethod != null and shippingMethod != ''">
, shipping_method = #{shippingMethod}
</if>
<if test="serialNo != null and serialNo != ''">
, serial_no = #{serialNo}
</if>
<if test="salesUnitPrice != null and salesUnitPrice != ''">
, sales_unit_price = #{salesUnitPrice}::numeric
</if>
<if test="salesSupplyPrice != null and salesSupplyPrice != ''">
, sales_supply_price = #{salesSupplyPrice}::numeric
</if>
<if test="salesVat != null and salesVat != ''">
, sales_vat = #{salesVat}::numeric
</if>
<if test="salesTotalAmount != null and salesTotalAmount != ''">
, sales_total_amount = #{salesTotalAmount}::numeric
</if>
<if test="salesCurrency != null and salesCurrency != ''">
, sales_currency = #{salesCurrency}
</if>
<if test="salesExchangeRate != null and salesExchangeRate != ''">
, sales_exchange_rate = #{salesExchangeRate}::numeric
</if>
<if test="managerUserId != null and managerUserId != ''">
, manager_user_id = #{managerUserId}
</if>
<if test="incoterms != null and incoterms != ''">
, incoterms = #{incoterms}
</if>
WHERE log_id = #{logId}::integer
</update>
<!-- 거래명세서 - 고객 정보 조회 -->
<select id="getCustomerInfoByProjectNo" parameterType="map" resultType="map">
/* salesNcollectMgmt.getCustomerInfoByProjectNo - 프로젝트 번호로 고객 정보 조회 */
@@ -1976,7 +2092,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
WHERE PM.PROJECT_NO = #{projectNo}
</select>
<!-- 거래명세서 저장 - NSWOS100_TBL 테이블 사용 -->
<!-- 거래명세서 저장 - NSWOS100_TBL 테이블 사용 (ON CONFLICT DO UPDATE) -->
<insert id="saveTransactionStatement" parameterType="map">
/* 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}
</insert>
<!-- 거래명세서 번호 생성 -->
@@ -2051,7 +2170,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
<select id="getSavedTransactionStatement" parameterType="map" resultType="map">
/* salesNcollectMgmt.getSavedTransactionStatement - 저장된 거래명세서 조회 */
SELECT
SELECT
SuVndCd,
IssueDt,
IssueNo,
@@ -2066,9 +2185,10 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
IsDt,
IsQty,
IsPrice,
IsAmount
IsAmount,
LinkedProjectObjids
FROM NSWOS100_TBL
WHERE OdOrderNo = #{projectNo}
WHERE #{projectObjid} = ANY(STRING_TO_ARRAY(LinkedProjectObjids, ','))
ORDER BY IsNo
</select>
@@ -2142,6 +2262,102 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
LOADING_DATE = #{loadingDate}
WHERE OBJID::VARCHAR = #{OBJID}
</update>
<!--
/**
* 매출마감 전표연동용 데이터 조회
* OBJID 리스트로 전표 생성에 필요한 모든 정보를 일괄 조회한다.
* @since 2026.02.24
* @author system
* @version 1.0
**/
-->
<select id="getErpAccountCode" parameterType="map" resultType="com.pms.common.UpperKeyMap">
/* salesNcollectMgmt.getErpAccountCode - ERP 계정과목코드 조회 */
SELECT acct_cd AS ACCT_CD, acct_nm AS ACCT_NM
FROM erp_acct_code
WHERE co_cd = #{coCd}
AND acct_nm = #{acctNm}
LIMIT 1
</select>
<select id="getSlipDataForDeadline" parameterType="map" resultType="com.pms.common.UpperKeyMap">
/* salesNcollectMgmt.getSlipDataForDeadline - 매출마감 전표연동용 데이터 조회 */
SELECT
PM.OBJID,
PM.PROJECT_NO,
PM.AREA_CD,
PM.CUSTOMER_OBJID,
PM.PART_NO,
PM.PART_NAME,
COALESCE(PM.TAX_TYPE, '') AS TAX_TYPE,
COALESCE(PM.TAX_INVOICE_DATE, '') AS TAX_INVOICE_DATE,
COALESCE(PM.EXPORT_DECL_NO, '') AS EXPORT_DECL_NO,
COALESCE(PM.LOADING_DATE, '') AS LOADING_DATE,
COALESCE(PM.SALES_STATUS, '') AS SALES_STATUS,
/* 거래처 정보: ERP 연동 거래처(C_ 접두어)에서 client_cd 조회 */
CASE WHEN PM.CUSTOMER_OBJID LIKE 'C_%' THEN
(SELECT C.CLIENT_CD FROM CLIENT_MNG C WHERE 'C_' || C.OBJID::VARCHAR = PM.CUSTOMER_OBJID)
ELSE NULL END AS ERP_CLIENT_CD,
CASE WHEN PM.CUSTOMER_OBJID LIKE 'C_%' THEN
(SELECT C.CLIENT_NM FROM CLIENT_MNG C WHERE 'C_' || C.OBJID::VARCHAR = PM.CUSTOMER_OBJID)
ELSE
(SELECT S.SUPPLY_NAME FROM SUPPLY_MNG S WHERE S.OBJID::VARCHAR = PM.CUSTOMER_OBJID::VARCHAR)
END AS CUSTOMER_NAME,
CASE WHEN PM.CUSTOMER_OBJID LIKE 'C_%' THEN
(SELECT C.BUS_REG_NO FROM CLIENT_MNG C WHERE 'C_' || C.OBJID::VARCHAR = PM.CUSTOMER_OBJID)
ELSE NULL END AS BUS_REG_NO,
/* 판매 금액 정보 */
COALESCE(SR.SALES_SUPPLY_PRICE, 0) AS SALES_SUPPLY_PRICE,
COALESCE(SR.SALES_VAT, 0) AS SALES_VAT,
COALESCE(SR.SALES_TOTAL_AMOUNT, 0) AS SALES_TOTAL_AMOUNT,
/* 환종/환율 (해외) */
COALESCE(SR.SALES_CURRENCY, PM.CONTRACT_CURRENCY) AS SALES_CURRENCY,
COALESCE(SR.SALES_EXCHANGE_RATE, 0) AS SALES_EXCHANGE_RATE,
/* 외화 총액 계산: 원화총액 / 환율 (환율이 0이 아닐 때) */
CASE WHEN COALESCE(SR.SALES_EXCHANGE_RATE, 0) > 0 THEN
ROUND(COALESCE(SR.SALES_TOTAL_AMOUNT, 0)::NUMERIC / SR.SALES_EXCHANGE_RATE::NUMERIC, 2)
ELSE 0 END AS FOREIGN_AMOUNT
FROM PROJECT_MGMT PM
LEFT JOIN SALES_REGISTRATION SR ON PM.PROJECT_NO = SR.PROJECT_NO
WHERE PM.OBJID::VARCHAR = #{OBJID}
</select>
<!--
/**
* 매출마감 전표번호 시퀀스 조회
* 동일 일자의 최대 작성번호를 조회하여 다음 번호를 부여한다.
* @since 2026.02.24
* @author system
* @version 1.0
**/
-->
<select id="getNextSlipMenuSq" parameterType="map" resultType="int">
/* salesNcollectMgmt.getNextSlipMenuSq - 다음 전표 작성번호 조회 */
SELECT COALESCE(MAX(SLIP_MENU_SQ), 0) + 1
FROM PROJECT_MGMT
WHERE SALES_SLIP_DATE = #{slipDate}
AND SALES_SLIP_MENU_SQ IS NOT NULL
</select>
<!--
/**
* 매출마감 완료 후 전표 정보 저장
* @since 2026.02.24
* @author system
* @version 1.0
**/
-->
<update id="updateSlipInfo" parameterType="map">
/* 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}
</update>
</mapper>

View File

@@ -467,7 +467,8 @@ var columns = [
},
cellClick:function(e, cell){
var serialNo = fnc_checkNull(cell.getData().SERIAL_NO);
fn_showSerialNoPopup(serialNo);
var contractObjId = fnc_checkNull(cell.getData().OBJID);
fn_showSerialNoPopup(serialNo, contractObjId);
}
},
// 17. 품번
@@ -784,29 +785,49 @@ function fn_openEstimateTemplate(objId, templateType){
}
// S/N 목록 팝업 표시
function fn_showSerialNoPopup(serialNoString){
function fn_showSerialNoPopup(serialNoString, contractObjId){
if(!serialNoString || serialNoString === ''){
Swal.fire("S/N 정보가 없습니다.");
return;
}
// 쉼표로 구분된 S/N을 배열로 변환
var serialNumbers = serialNoString.split(',').map(function(sn){ return sn.trim(); });
// HTML 리스트 생성
// "외 N건" 형식이면 서버에서 전체 S/N 조회
if(serialNoString.indexOf('외') > -1 && serialNoString.indexOf('') > -1 && contractObjId){
$.ajax({
url: '/contractMgmt/getAllSerialNumbers.do',
type: 'POST',
data: { contractObjId: contractObjId },
dataType: 'json',
success: function(response){
if(response.result === 'success' && response.serialNumbers && response.serialNumbers.length > 0){
fn_displaySerialNoList(response.serialNumbers);
} else {
fn_displaySerialNoList([serialNoString]);
}
},
error: function(){
fn_displaySerialNoList([serialNoString]);
}
});
} else {
var serialNumbers = serialNoString.split(',').map(function(sn){ return sn.trim(); }).filter(function(sn){ return sn !== ''; });
fn_displaySerialNoList(serialNumbers);
}
}
// S/N 리스트 팝업 표시 (공통)
function fn_displaySerialNoList(serialNumbers){
var listHtml = '<div style="text-align: left; max-height: 400px; overflow-y: auto;">';
listHtml += '<ol style="padding-left: 20px; margin: 10px 0;">';
serialNumbers.forEach(function(sn, index){
serialNumbers.forEach(function(sn){
listHtml += '<li style="padding: 5px 0; font-size: 14px;">' + sn + '</li>';
});
listHtml += '</ol>';
listHtml += '</div>';
// SweetAlert2로 팝업 표시
Swal.fire({
title: 'S/N 목록',
html: listHtml,
icon: 'info',
width: 500,
confirmButtonText: '확인',
confirmButtonColor: '#3085d6'

View File

@@ -288,6 +288,13 @@
}
}
// 결재여부 체크박스 토글 (단일 선택)
function fn_toggleApprovalRequired(clicked) {
$("input[name='approval_required_chk']").prop("checked", false);
$(clicked).prop("checked", true);
$("#approval_required").val($(clicked).val());
}
function fn_save() {
if (fnc_valitate("form1")) {
// 품목 유효성 검사
@@ -1990,6 +1997,25 @@
<input type="text" name="exchange_rate" id="exchange_rate" reqTitle="환율" value="${info.EXCHANGE_RATE}" />
</td>
</tr>
<!-- 세번째 행: 결재여부 -->
<tr>
<td class="input_title"><label>결재여부 <span style="color:red;">*</span></label></td>
<td colspan="7">
<label style="margin-right:15px; cursor:pointer;">
<input type="checkbox" name="approval_required_chk" value="Y"
${info.APPROVAL_REQUIRED eq 'Y' ? 'checked' : ''}
onchange="fn_toggleApprovalRequired(this)" /> 필요
</label>
<label style="cursor:pointer;">
<input type="checkbox" name="approval_required_chk" value="N"
${info.APPROVAL_REQUIRED ne 'Y' ? 'checked' : ''}
onchange="fn_toggleApprovalRequired(this)" /> 불필요
</label>
<input type="hidden" name="approval_required" id="approval_required"
value="${info.APPROVAL_REQUIRED ne 'Y' ? 'N' : 'Y'}" />
</td>
</tr>
</table>
</td>
</tr>

View File

@@ -355,7 +355,8 @@ var columns = [
},
cellClick:function(e, cell){
var serialNo = fnc_checkNull(cell.getData().SERIAL_NO);
fn_showSerialNoPopup(serialNo);
var contractObjId = fnc_checkNull(cell.getData().OBJID);
fn_showSerialNoPopup(serialNo, contractObjId);
}
},
// 21. 품번
@@ -657,29 +658,49 @@ function fn_openEstimateTemplate(objId, templateType){
}
// S/N 목록 팝업 표시
function fn_showSerialNoPopup(serialNoString){
function fn_showSerialNoPopup(serialNoString, contractObjId){
if(!serialNoString || serialNoString === ''){
Swal.fire("S/N 정보가 없습니다.");
return;
}
// 쉼표로 구분된 S/N을 배열로 변환
var serialNumbers = serialNoString.split(',').map(function(sn){ return sn.trim(); });
// HTML 리스트 생성
// "외 N건" 형식이면 서버에서 전체 S/N 조회
if(serialNoString.indexOf('외') > -1 && serialNoString.indexOf('') > -1 && contractObjId){
$.ajax({
url: '/contractMgmt/getAllSerialNumbers.do',
type: 'POST',
data: { contractObjId: contractObjId },
dataType: 'json',
success: function(response){
if(response.result === 'success' && response.serialNumbers && response.serialNumbers.length > 0){
fn_displaySerialNoList(response.serialNumbers);
} else {
fn_displaySerialNoList([serialNoString]);
}
},
error: function(){
fn_displaySerialNoList([serialNoString]);
}
});
} else {
var serialNumbers = serialNoString.split(',').map(function(sn){ return sn.trim(); }).filter(function(sn){ return sn !== ''; });
fn_displaySerialNoList(serialNumbers);
}
}
// S/N 리스트 팝업 표시 (공통)
function fn_displaySerialNoList(serialNumbers){
var listHtml = '<div style="text-align: left; max-height: 400px; overflow-y: auto;">';
listHtml += '<ol style="padding-left: 20px; margin: 10px 0;">';
serialNumbers.forEach(function(sn, index){
serialNumbers.forEach(function(sn){
listHtml += '<li style="padding: 5px 0; font-size: 14px;">' + sn + '</li>';
});
listHtml += '</ol>';
listHtml += '</div>';
// SweetAlert2로 팝업 표시
Swal.fire({
title: 'S/N 목록',
html: listHtml,
icon: 'info',
width: 500,
confirmButtonText: '확인',
confirmButtonColor: '#3085d6'

View File

@@ -957,6 +957,9 @@ function fn_initGrid() {
function fn_preprocessBomData(dataList) {
if(!dataList || dataList.length === 0) return dataList;
// 레벨별 항목수량 추적 (제작수량 누적 곱 계산용)
var parentItemQtyByLevel = {};
dataList.forEach(function(row) {
// 필드명 정규화 (소문자 → 대문자 복사, 기존 대문자 유지)
if(row.part_unit_qty !== undefined && row.PART_UNIT_QTY === undefined) {
@@ -985,13 +988,27 @@ function fn_initGrid() {
console.log("전처리 - PART_NO:", row.PART_NO, "소재품번:", row.RAW_MATERIAL_NO, "소재소요량(DB):", savedRequiredQty, "소재소요량(최종):", row.REQUIRED_QTY);
// 제작수량: 저장된 값이 있으면 유지, 없으면 항목수량 × 총생산수량으로 자동계산
// 제작수량: 상위 항목수량을 누적 곱하여 계산
var level = parseInt(row.LEVEL) || 1;
var itemQty = parseFloat(row.ITEM_QTY || row.item_qty) || 0;
// 현재 레벨의 항목수량 저장 (하위 레벨 계산에 사용)
parentItemQtyByLevel[level] = itemQty;
// 현재 레벨보다 깊은 레벨 데이터 제거 (형제 노드 전환 시 오염 방지)
for(var l = level + 1; l <= _maxLevel; l++) {
delete parentItemQtyByLevel[l];
}
var savedProdQty = parseFloat(row.PRODUCTION_QTY) || 0;
if(savedProdQty > 0) {
row.PRODUCTION_QTY = savedProdQty;
} else {
var itemQty = parseFloat(row.ITEM_QTY || row.item_qty) || 0;
row.PRODUCTION_QTY = itemQty * totalProductionQty;
// 최상위(1)부터 현재 레벨까지 항목수량 누적 곱 × 총생산수량
var cumulativeItemQty = 1;
for(var l = 1; l <= level; l++) {
cumulativeItemQty *= (parentItemQtyByLevel[l] || 1);
}
row.PRODUCTION_QTY = cumulativeItemQty * totalProductionQty;
}
// 소재발주수량: 저장된 값이 있으면 유지, 없으면 계산

View File

@@ -144,26 +144,15 @@ $(document).ready(function(){
fn_search();
});
// 프로젝트별 파트 목록 캐시
var _PART_LIST_CACHE = {};
// 프로젝트번호로 MBOM 파트 목록 조회
// 프로젝트번호로 MBOM 파트 목록 조회 (미선택 시 전체 조회)
function fn_getPartListByProject(projectNo) {
if (!projectNo) return [{"CODE": "", "NAME": "선택"}];
var param = {"sqlId": "common.getMbomPartListByProjectNo"};
if (projectNo) param.PROJECT_NO = projectNo;
// 캐시에 있으면 캐시에서 반환
if (_PART_LIST_CACHE[projectNo]) {
return _PART_LIST_CACHE[projectNo];
}
// 서버에서 조회
var partList = [{"CODE": "", "NAME": "선택"}].concat(
fnc_getJsonAllDataListBySqlId({"sqlId": "common.getMbomPartListByProjectNo", "PROJECT_NO": projectNo})
fnc_getJsonAllDataListBySqlId(param)
);
// 캐시에 저장
_PART_LIST_CACHE[projectNo] = partList;
return partList;
}
@@ -207,30 +196,32 @@ function fn_select2Editor(cell, onRendered, success, cancel, editorParams) {
$(select).select2("open");
});
var isCleared = false;
var isCompleted = false;
$(select).on("select2:select", function(e) {
if(isCompleted) return;
isCompleted = true;
var selectedVal = $(select).val() || "";
if(typeof editorParams.onSelect === "function") {
editorParams.onSelect(cell, selectedVal);
}
try { $(select).select2("destroy"); } catch(e) {}
success(selectedVal);
});
$(select).on("select2:clear", function(e) {
isCleared = true;
if(typeof editorParams.onSelect === "function") {
editorParams.onSelect(cell, "");
}
if(isCompleted) return;
isCompleted = true;
var $sel = $(select);
setTimeout(function() {
$(select).select2("close");
try { $sel.select2("destroy"); } catch(e) {}
success("");
}, 0);
}, 50);
});
$(select).on("select2:close", function() {
if(isCleared) return;
success($(select).val() || "");
if(isCompleted) return;
isCompleted = true;
var val = $(select).val() || "";
try { $(select).select2("destroy"); } catch(e) {}
success(val);
});
return container;
@@ -270,21 +261,7 @@ function fn_search(){
editor: fn_select2Editor,
editorParams: {
valueId: "CODE", labelId: "NAME", values: _PROJECT_LIST,
placeholder: "프로젝트 검색...",
onSelect: function(cell, selectedValue) {
var productName = "";
for(var i = 0; i < _PROJECT_LIST.length; i++) {
if(_PROJECT_LIST[i].CODE == selectedValue) {
productName = _PROJECT_LIST[i].PRODUCT_NAME || "";
break;
}
}
if(selectedValue) fn_getPartListByProject(selectedValue);
cell.getRow().update({
"PART_OBJID": "", "PART_NO": "", "PART_NAME": "",
"PRODUCT_NAME": productName
});
}
placeholder: "프로젝트 검색..."
},
formatter: function(cell) {
return fnc_customSelectFormatter(cell, {valueId:"CODE", labelId:"NAME", values:_PROJECT_LIST});
@@ -412,11 +389,28 @@ function fn_search(){
var row = cell.getRow();
var field = cell.getField();
// 프로젝트 변경 시 제품구분 설정 + 파트 초기화
if (field === 'PROJECT_OBJID') {
var projectObjid = cell.getValue() || "";
var productName = "";
for(var i = 0; i < _PROJECT_LIST.length; i++) {
if(_PROJECT_LIST[i].CODE == projectObjid) {
productName = _PROJECT_LIST[i].PRODUCT_NAME || "";
break;
}
}
fn_getPartListByProject(projectObjid);
row.update({
"PART_OBJID": "", "PART_NO": "", "PART_NAME": "",
"PRODUCT_NAME": productName
});
}
// 파트 선택 시 품번/품명 자동 설정
if (field === 'PART_OBJID') {
var partObjid = cell.getValue();
var projectObjid = row.getData().PROJECT_OBJID;
if (partObjid && projectObjid) {
if (partObjid) {
var partList = fn_getPartListByProject(projectObjid);
for (var i = 0; i < partList.length; i++) {
if (partList[i].CODE == partObjid) {

View File

@@ -39,7 +39,7 @@
background: #f9f9f9;
font-weight: bold;
text-align: center;
width: 120px;
width: 70px;
}
.item-table {
width: 100%;

View File

@@ -78,10 +78,10 @@
return false;
}
// 선택된 OBJID 목록
var objIdList = [];
// 선택된 LOG_ID 목록
var logIdList = [];
for(var i = 0; i < targetObj.length; i++){
objIdList.push(fnc_checkNull(targetObj[i].OBJID));
logIdList.push(fnc_checkNull(targetObj[i].LOG_ID));
}
// 단건 선택 시 기존 마감정보 불러오기
@@ -164,7 +164,7 @@
url: "/revenueMgmt/saveDeadlineInfo.do",
type: "POST",
data: {
"objIdList": objIdList.join(','),
"logIdList": logIdList.join(','),
"taxType": formData.taxType,
"taxInvoiceDate": formData.taxInvoiceDate,
"exportDeclNo": formData.exportDeclNo,
@@ -286,17 +286,17 @@
}).then(function(result) {
if (result.isConfirmed) {
var deadlineDate = result.value;
var objIdList = [];
var logIdList = [];
for(var i = 0; i < targetObj.length; i++){
objIdList.push(fnc_checkNull(targetObj[i].OBJID));
logIdList.push(fnc_checkNull(targetObj[i].LOG_ID));
}
$.ajax({
url: "/salesNcollectMgmt/salesDeadlineConfirm.do",
type: "POST",
data: {
"objIdList": objIdList.join(','),
"logIdList": logIdList.join(','),
"deadlineDate": deadlineDate
},
dataType: "json",
@@ -332,7 +332,13 @@ var columns = [
// 컬럼 순서: 프로젝트번호, 주문유형, 매출마감, 발주일, 발주번호, 고객사, 제품구분, 품명, 수량, 단가, 공급가액, 부가세, 총액, 원화총액, 출하일, 국내/해외, 환종, 환율, S/N, 품번
{formatter:"rowSelection", titleFormatter:"rowSelection", hozAlign:"center", headerSort:false, width:40, frozen:true},
// 1. 프로젝트번호
{headerHozAlign : 'center', hozAlign : 'center', width : '130', title : '프로젝트번호', field : 'PROJECT_NO', frozen : true},
{headerHozAlign : 'center', hozAlign : 'center', width : '130', title : '프로젝트번호', field : 'PROJECT_NO', frozen : true,
formatter: fnc_createGridAnchorTag,
cellClick: function(e, cell){
var orderNo = cell.getData().PROJECT_NO;
fn_openSaleRegPopup(orderNo, "detail");
}
},
// 2. 주문유형
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '주문유형', field : 'ORDER_TYPE'},
// 3. 매출마감
@@ -605,6 +611,13 @@ function fn_excel() {
return false;
}
function fn_openSaleRegPopup(orderNo, saleNo){
var popup_width = 850;
var popup_height = 520;
var url = "/salesMgmt/salesRegForm.do?orderNo=" + encodeURIComponent(orderNo) + "&saleNo=" + (saleNo ? encodeURIComponent(saleNo) : "");
fn_centerPopup(popup_width, popup_height, url);
}
function fn_FileRegist(objId, docType, docTypeName){
var popup_width = 800;
var popup_height = 680;
@@ -624,7 +637,7 @@ function fn_FileRegist(objId, docType, docTypeName){
<form name="form1" id="form1" method="post">
<input type="hidden" name="actionType" id="actionType">
<input type="hidden" name="shippingDateRequired" value="Y"> <!-- 출하일 필수 -->
<input type="hidden" name="revenueMode" value="Y"> <!-- 매출관리 모드: shipment_log 기반 -->
<!-- 과세구분 코드 - 매출마감용 (과세매출/수출만 표시) -->
<select id="hiddenTaxTypeList" style="display:none;">
<option value="">선택</option>

View File

@@ -175,7 +175,7 @@
function fn_openSaleRegPopup(orderNo, saleNo){
var popup_width = 850;
var popup_height = 400;
var popup_height = 520;
// 한글 프로젝트 번호 인코딩 처리
var url = "/salesMgmt/salesRegForm.do?orderNo=" + encodeURIComponent(orderNo) + "&saleNo=" + (saleNo ? encodeURIComponent(saleNo) : "");
fn_centerPopup(popup_width, popup_height, url);
@@ -302,14 +302,18 @@ var columns = [
// 5. 요청납기
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '요청납기', field : 'REQUEST_DATE'},
// 6. 출하일
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '출하일', field : 'SHIPPING_DATE_WITH_COUNT',
{headerHozAlign : 'center', hozAlign : 'center', width : '120', title : '출하일', field : 'SHIPPING_DATE_WITH_COUNT',
formatter: function(cell) {
var value = cell.getValue();
var displayText = '';
if(!value || value.trim() === '') {
var data = cell.getRow().getData();
return data.SHIPPING_DATE || '';
displayText = data.SHIPPING_DATE || '';
} else {
displayText = value;
}
return value;
if(!displayText) return '';
return '<a href="javascript:void(0);" style="color:#2196F3;">' + displayText + '</a>';
},
cellClick: function(e, cell) {
var projectNo = cell.getRow().getData().PROJECT_NO;
@@ -620,19 +624,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 +708,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);
}
</script>
<style>

View File

@@ -596,54 +596,46 @@ function fn_calculateSelectedItem() {
dataType : "json",
success : function(data) {
alert(data.msg);
// 저장 후 잔량 확인을 위해 서버에 다시 조회
// 수정 모드(logId)면 바로 닫기 (부모 팝업이 자동 새로고침)
var logIdVal = $("input[name='logId']").val();
if(logIdVal && logIdVal.trim() !== '') {
self.close();
return;
}
// 신규 등록 모드: 저장 후 잔량 확인
$.ajax({
url: "/salesMgmt/salesRegForm.do",
type: "GET",
data: { orderNo: "${param.orderNo}" },
dataType: "html",
success: function(response) {
// 응답에서 SALES_QUANTITY 추출 (잔량)
var match = response.match(/SALES_QUANTITY:\s*(\d+)/);
var remainingQuantity = match ? parseInt(match[1]) : 0;
console.log("서버에서 계산한 잔량:", remainingQuantity);
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();
}
},
error: function() {
// 에러 시 그냥 팝업 닫기
if(opener && opener.fn_search) {
opener.fn_search();
}
@@ -680,14 +672,19 @@ function fn_calculateSelectedItem() {
<body>
<form name="form1" id="form1" action="" method="post">
<input type="hidden" name="orderNo" value="${param.orderNo}">
<input type="hidden" name="saleNo" value="${param.saleNo}">
<input type="hidden" name="logId" value="${logId}">
<!-- serialNoList는 name 속성 없이 id만 사용 (클라이언트 전용) -->
<input type="hidden" id="serialNoList" value="${saleInfo.SERIAL_NO_LIST}" />
<section class="business_popup_min_width">
<div class="plm_menu_name">
<h2>
<span>판매 등록 (주문번호: ${param.orderNo})</span>
<span>
<c:choose>
<c:when test="${not empty logId}">출하 수정 (주문번호: ${param.orderNo})</c:when>
<c:otherwise>판매 등록 (주문번호: ${param.orderNo})</c:otherwise>
</c:choose>
</span>
</h2>
</div>

View File

@@ -13,6 +13,7 @@ body {
margin: 0;
padding: 20px;
font-family: 'Malgun Gothic', sans-serif;
width: auto !important;
}
.popup-header {
font-size: 18px;
@@ -38,6 +39,15 @@ body {
border: 1px solid #ddd;
text-align: center;
}
.shipping-date-link {
color: #2196F3;
text-decoration: underline;
cursor: pointer;
}
.shipping-date-link:hover {
color: #1565C0;
font-weight: bold;
}
.btn-close {
padding: 8px 20px;
background-color: #666;
@@ -54,6 +64,24 @@ body {
margin-top: 20px;
}
</style>
<script>
function fn_openEditPopup(logId, orderNo) {
var popup_width = 850;
var popup_height = 420;
var url = "/salesMgmt/salesRegForm.do?orderNo=" + encodeURIComponent(orderNo) + "&logId=" + logId;
var left = (screen.width - popup_width) / 2;
var top = (screen.height - popup_height) / 2;
var editPopup = window.open(url, "salesRegEdit_" + logId,
"width=" + popup_width + ",height=" + popup_height + ",left=" + left + ",top=" + top + ",scrollbars=yes,resizable=yes");
var checkPopup = setInterval(function() {
if(editPopup.closed) {
clearInterval(checkPopup);
location.reload();
}
}, 500);
}
</script>
</head>
<body>
<div class="popup-header">
@@ -79,7 +107,12 @@ body {
<c:otherwise>
<c:forEach items="${shippingList}" var="item">
<tr>
<td>${item.shipping_date}</td>
<td>
<span class="shipping-date-link"
onclick="fn_openEditPopup('${item.log_id}', '${projectNo}')">
${item.shipping_date}
</span>
</td>
<td>${item.shipping_quantity}</td>
<td>${item.shipping_order_status}</td>
<td>${item.serial_no}</td>

View File

@@ -423,6 +423,33 @@ body {
border-right: none;
}
/* datepicker 팝업이 페이지 리셋 스타일에 영향받지 않도록 보호 */
.ui-datepicker {
z-index: 9999 !important;
background: #fff;
border: 1px solid #ccc;
padding: 5px;
font-size: 9pt;
}
.ui-datepicker th,
.ui-datepicker td {
padding: 3px;
text-align: center;
}
.ui-datepicker td a,
.ui-datepicker td span {
display: block;
padding: 3px;
text-decoration: none;
}
.ui-datepicker .ui-datepicker-header {
padding: 5px;
text-align: center;
}
/* 버튼 */
.button-area {
text-align: center;
@@ -448,39 +475,68 @@ body {
color: white;
}
</style>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
$(document).ready(function(){
var today = new Date();
// yyyyMMdd 또는 yyyy-MM-dd 형식의 날짜를 "YYYY년 M월 D일 X요일" 형식으로 표시
function fn_setDeliveryDate(dateStr) {
if(!dateStr) return;
var year, month, day;
if(dateStr.includes("-")) {
var parts = dateStr.split("-");
year = parseInt(parts[0]);
month = parseInt(parts[1]);
day = parseInt(parts[2]);
} else if(dateStr.length >= 8) {
year = parseInt(dateStr.substring(0, 4));
month = parseInt(dateStr.substring(4, 6));
day = parseInt(dateStr.substring(6, 8));
} else {
return;
}
if(isNaN(year) || isNaN(month) || isNaN(day)) return;
var date = new Date(year, month - 1, day);
var days = ['일', '월', '화', '수', '목', '금', '토'];
var dateStr = today.getFullYear() + "년 " + (today.getMonth() + 1) + "월 " + today.getDate() + "일 " + days[today.getDay()] + "요일";
var displayStr = year + "년 " + month + "월 " + day + "일 " + days[date.getDay()] + "요일";
// 표시용 날짜 (한글)
$("#deliveryDate").text(dateStr);
$("#deliveryDate").text(displayStr);
// 전송용 날짜 (YYYY-MM-DD) - hidden input에 저장
var year = today.getFullYear();
var month = String(today.getMonth() + 1).padStart(2, '0');
var day = String(today.getDate()).padStart(2, '0');
var isoDate = year + "-" + month + "-" + day;
var isoDate = year + "-" + String(month).padStart(2, '0') + "-" + String(day).padStart(2, '0');
$("#deliveryDateISO").val(isoDate);
}
$(document).ready(function(){
// 기본값: 오늘 날짜
var today = new Date();
var todayISO = today.getFullYear() + "-" + String(today.getMonth() + 1).padStart(2, '0') + "-" + String(today.getDate()).padStart(2, '0');
fn_setDeliveryDate(todayISO);
// datepicker 초기화
$("#deliveryDatePicker").datepicker({
changeMonth: true,
changeYear: true,
dateFormat: 'yy-mm-dd',
onSelect: function(dateText) {
fn_setDeliveryDate(dateText);
}
});
// 달력 아이콘 클릭 시 datepicker 표시
$("#deliveryDateCalBtn").on('click', function(e) {
e.preventDefault();
$("#deliveryDatePicker").datepicker("show");
});
fn_loadData();
// 날짜 수정 시 ISO 형식도 업데이트
// 날짜 직접 수정 시 ISO 형식도 업데이트
$("#deliveryDate").on('blur', function() {
var dateText = $(this).text().trim();
// 날짜 파싱 시도
try {
// "2025년 3월 5일 수요일" 형식 파싱
var match = dateText.match(/(\d{4})년\s*(\d+)월\s*(\d+)일/);
if (match) {
var year = match[1];
var month = String(match[2]).padStart(2, '0');
var day = String(match[3]).padStart(2, '0');
var isoDate = year + "-" + month + "-" + day;
$("#deliveryDateISO").val(isoDate);
console.log("날짜 업데이트: " + dateText + " → " + isoDate);
if(match) {
fn_setDeliveryDate(match[1] + "-" + match[2] + "-" + match[3]);
}
} catch(e) {
console.error("날짜 파싱 실패:", e);
@@ -623,6 +679,12 @@ function fn_loadSavedStatement(projectObjid, gridData) {
// 고객사 정보
$("#receiverName").text(firstItem.CUSTOMER || "");
// 저장된 납품일 복원
var savedIsDt = savedData[0].isdt || savedData[0].ISDT || savedData[0].IsDt || "";
if(savedIsDt && savedIsDt.length >= 8) {
fn_setDeliveryDate(String(savedIsDt));
}
// 품목 테이블 채우기
var tbody = $("#itemsBody");
tbody.empty();
@@ -1041,6 +1103,8 @@ function fn_close() {
<td class="date-label-cell">납품일</td>
<td class="date-value-cell" colspan="4">
<span id="deliveryDate" contenteditable="true">2025년 3월 5일 수요일</span>
<img id="deliveryDateCalBtn" src="/images/date_icon.png" alt="달력" class="no-print" style="width:16px;height:16px;cursor:pointer;vertical-align:middle;margin-left:8px;">
<input type="text" id="deliveryDatePicker" readonly style="width:1px;height:1px;border:none;padding:0;margin:0;opacity:0;overflow:hidden;">
<input type="hidden" id="deliveryDateISO" value="">
</td>
<td class="supplier-label-cell" rowspan="5">

View File

@@ -206,9 +206,10 @@ public class EmployeeApiClient {
json.append(",\"pId\":\"\"");
json.append("}");
// body 섹션
// body 섹션 - extraColumns로 이메일/전화번호 등 추가 필드 요청
json.append(",\"body\":{");
json.append("\"coCd\":\"").append(escapeJson(coCd)).append("\"");
json.append(",\"extraColumns\":[\"emalAdd\",\"outemalAdd\",\"emgcTel\",\"tel\",\"joinDt\"]");
json.append("}");
json.append("}");

View File

@@ -487,7 +487,8 @@ public class Constants {
/* SMTP 메일정보 - 영업팀 (견적서 등) */
public static final String SMTP_USER_SALES = "sales@rps-korea.com";
public static final String SMTP_USER_PW_SALES = "rpstech6125!!";
//public static final String SMTP_USER_PW_SALES = "rpstech6125!!";
public static final String SMTP_USER_PW_SALES = "rps6125!!";
public static final String SMTP_HOST_SALES = "erp.rps-korea.com";
public static final int SMTP_PORT_SALES = 25;

View File

@@ -114,9 +114,10 @@
data_type = #{data_type}
</insert>
<!-- 사원 정보 UPSERT (실제 JSON 응답 기준) -->
<!-- 사원 정보 UPSERT (sabun unique 인덱스 기준) -->
<insert id="upsertEmployee" parameterType="map">
INSERT INTO user_info (
user_id,
sabun,
empseq,
user_password,
@@ -128,6 +129,7 @@
position_name,
rank,
email,
tel,
cell_phone,
user_type,
user_type_name,
@@ -136,6 +138,7 @@
data_type,
regdate
) VALUES (
#{sabun},
#{sabun},
#{empseq},
#{user_password},
@@ -147,11 +150,12 @@
#{position_name},
#{rank},
#{email},
#{tel},
#{cell_phone},
#{user_type},
#{user_type_name},
#{status},
CASE
CASE
WHEN #{end_date} IS NULL OR #{end_date} = '' THEN NULL
ELSE TO_TIMESTAMP(#{end_date}, 'YYYYMMDD')
END,
@@ -168,11 +172,12 @@
position_name = #{position_name},
rank = #{rank},
email = #{email},
tel = #{tel},
cell_phone = #{cell_phone},
user_type = #{user_type},
user_type_name = #{user_type_name},
status = #{status},
end_date = CASE
end_date = CASE
WHEN #{end_date} IS NULL OR #{end_date} = '' THEN NULL
ELSE TO_TIMESTAMP(#{end_date}, 'YYYYMMDD')
END,

View File

@@ -3407,17 +3407,32 @@ ORDER BY COALESCE(T.REVISION, '1.0')
<!-- 프로젝트 OBJID로 MBOM 파트 목록 조회 -->
<select id="getMbomPartListByProjectNo" parameterType="map" resultType="map">
SELECT PM.OBJID AS CODE
, PM.PART_NO || ' - ' || PM.PART_NAME AS NAME
, PM.OBJID AS CODE_ID
, PM.PART_NO || ' - ' || PM.PART_NAME AS CODE_NAME
, PM.PART_NO
, PM.PART_NAME
FROM MBOM_DETAIL MD
INNER JOIN MBOM_HEADER MH ON MH.OBJID = MD.MBOM_HEADER_OBJID
INNER JOIN PROJECT_MGMT PJ ON PJ.OBJID = MH.PROJECT_OBJID
INNER JOIN PART_MNG PM ON PM.OBJID::VARCHAR = MD.PART_OBJID
WHERE PJ.OBJID = #{PROJECT_NO}
ORDER BY PM.PART_NO
<choose>
<when test="PROJECT_NO != null and PROJECT_NO != ''">
SELECT PM.OBJID AS CODE
, PM.PART_NO || ' - ' || PM.PART_NAME AS NAME
, PM.OBJID AS CODE_ID
, PM.PART_NO || ' - ' || PM.PART_NAME AS CODE_NAME
, PM.PART_NO
, PM.PART_NAME
FROM MBOM_DETAIL MD
INNER JOIN MBOM_HEADER MH ON MH.OBJID = MD.MBOM_HEADER_OBJID
INNER JOIN PROJECT_MGMT PJ ON PJ.OBJID = MH.PROJECT_OBJID
INNER JOIN PART_MNG PM ON PM.OBJID::VARCHAR = MD.PART_OBJID
WHERE PJ.OBJID = #{PROJECT_NO}
ORDER BY PM.PART_NO
</when>
<otherwise>
SELECT PM.OBJID AS CODE
, PM.PART_NO || ' - ' || PM.PART_NAME AS NAME
, PM.OBJID AS CODE_ID
, PM.PART_NO || ' - ' || PM.PART_NAME AS CODE_NAME
, PM.PART_NO
, PM.PART_NAME
FROM PART_MNG PM
WHERE PM.STATUS = 'release'
ORDER BY PM.PART_NO
</otherwise>
</choose>
</select>
</mapper>

View File

@@ -2853,6 +2853,27 @@ public class ContractMgmtController {
return resultMap;
}
/**
* 영업건(CONTRACT)의 전체 S/N 목록 조회
*/
@ResponseBody
@RequestMapping(value="/contractMgmt/getAllSerialNumbers.do", method=RequestMethod.POST)
public Map getAllSerialNumbersByContract(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
Map resultMap = new HashMap();
try {
List<String> serialNumbers = contractMgmtService.getAllSerialNumbersByContract(paramMap);
resultMap.put("result", "success");
resultMap.put("serialNumbers", serialNumbers);
} catch (Exception e) {
e.printStackTrace();
resultMap.put("result", "error");
resultMap.put("message", e.getMessage());
}
return resultMap;
}
/**
* 수주취소 수량 저장
*/

View File

@@ -214,18 +214,14 @@ public class SalesNcollectMgmtController {
@RequestMapping(value = "/revenueMgmt/revenueGridList.do", method = RequestMethod.POST)
public Map<String, Object> salesMgmtGridList(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
try {
// commonService.selectListPagingNew를 사용하여 페이지네이션 처리
commonService.selectListPagingNew("salesNcollectMgmt.getSalesMgmtGridList", request, paramMap);
// Total 합계 조회 (기존 로직 유지)
Map<String, Object> totals = salesNcollectMgmtService.getSalesMgmtTotals(paramMap);
commonService.selectListPagingNew("salesNcollectMgmt.getRevenueMgmtGridList", request, paramMap);
Map<String, Object> totals = salesNcollectMgmtService.getRevenueMgmtTotals(paramMap);
paramMap.put("TOTALS", totals);
} catch(Exception e) {
e.printStackTrace();
// 에러 발생 시 빈 데이터 설정
paramMap.put("RESULTLIST", new java.util.ArrayList<>());
// Total 초기화
Map<String, Object> emptyTotals = new HashMap<String, Object>();
emptyTotals.put("TOTAL_SUPPLY_PRICE", 0);
emptyTotals.put("TOTAL_VAT", 0);
@@ -352,6 +348,22 @@ public class SalesNcollectMgmtController {
return "/salesmgmt/salesMgmt/projectDetailView";
}
}
// logId가 있으면 shipment_log 수정 모드
else if(paramMap.get("logId") != null && !paramMap.get("logId").equals("")) {
System.out.println("=== shipment_log 수정 모드 (logId: " + paramMap.get("logId") + ") ===");
saleInfo = salesNcollectMgmtService.getShipmentLogById((String) paramMap.get("logId"));
if(saleInfo != null) {
salesCurrency = CommonUtils.nullToEmpty((String)saleInfo.get("SALES_CURRENCY"));
request.setAttribute("logId", paramMap.get("logId"));
System.out.println("=== shipment_log 조회 완료 ===");
System.out.println("LOG_ID: " + paramMap.get("logId"));
System.out.println("SALES_QUANTITY: " + saleInfo.get("SALES_QUANTITY"));
System.out.println("SHIPPING_DATE: " + saleInfo.get("SHIPPING_DATE"));
System.out.println("SERIAL_NO: " + saleInfo.get("SERIAL_NO"));
}
}
// saleNo가 있지만 "detail"이 아니면 판매등록 수정 모드 - getSaleInfo로 S/N 포함 조회
else if(paramMap.get("saleNo") != null && !paramMap.get("saleNo").equals("")) {
System.out.println("=== 판매등록 수정 모드 ===");
@@ -359,10 +371,10 @@ public class SalesNcollectMgmtController {
saleInfoParam.put("orderNo", paramMap.get("orderNo"));
saleInfoParam.put("saleNo", paramMap.get("saleNo"));
saleInfo = salesNcollectMgmtService.getSaleInfo(saleInfoParam);
if(saleInfo != null) {
salesCurrency = CommonUtils.nullToEmpty((String)saleInfo.get("SALES_CURRENCY"));
System.out.println("=== 판매등록 수정 모드 조회 완료 ===");
System.out.println("SALE_NO: " + saleInfo.get("SALE_NO"));
System.out.println("ORDER_QUANTITY: " + saleInfo.get("ORDER_QUANTITY"));

View File

@@ -561,9 +561,10 @@
WHEN AMR.STATUS = 'reject' THEN '반려'
WHEN AMR.STATUS = 'create' THEN '작성중'
WHEN AMR.STATUS = 'notRequired' THEN '결재불필요'
WHEN COALESCE(T.APPROVAL_REQUIRED, 'N') = 'N' THEN '결재불필요'
ELSE ''
END AS APPR_STATUS
,COALESCE(AMR.STATUS, '') AS AMARANTH_STATUS
,COALESCE(AMR.STATUS, CASE WHEN COALESCE(T.APPROVAL_REQUIRED, 'N') = 'N' THEN 'notRequired' ELSE '' END) AS AMARANTH_STATUS
,CASE
WHEN AMR_ORDER.STATUS = 'complete' THEN '결재완료'
WHEN AMR_ORDER.STATUS = 'inProcess' THEN '결재중'
@@ -1285,6 +1286,7 @@
,QUANTITY
,CUSTOMER_REQUEST
,EXCHANGE_RATE
,APPROVAL_REQUIRED
)
VALUES
(
@@ -1345,6 +1347,7 @@
,#{quantity}
,#{customer_request}
,#{exchange_rate}
,COALESCE(#{approval_required}, 'N')
)
ON CONFLICT (OBJID) DO
UPDATE
@@ -1402,6 +1405,7 @@
,QUANTITY = #{quantity}
,CUSTOMER_REQUEST = #{customer_request}
,EXCHANGE_RATE = #{exchange_rate}
,APPROVAL_REQUIRED = COALESCE(#{approval_required}, 'N')
</update>
<update id="saveContractMgmtInfo_old" parameterType="map">
@@ -1792,6 +1796,7 @@ SELECT
,A.QUANTITY
,A.CUSTOMER_REQUEST
,A.EXCHANGE_RATE
,COALESCE(A.APPROVAL_REQUIRED, 'N') AS APPROVAL_REQUIRED
,A.ORDER_DATE
,A.ORDER_UNIT_PRICE
,A.ORDER_SUPPLY_PRICE
@@ -5691,6 +5696,18 @@ WHERE
WHERE ITEM_OBJID = #{itemObjId}
</delete>
<!-- 영업건(CONTRACT)의 전체 S/N 조회 -->
<select id="getAllSerialNumbersByContract" parameterType="map" resultType="string">
SELECT CIS.SERIAL_NO
FROM CONTRACT_ITEM CI
JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND CIS.STATUS = 'ACTIVE'
WHERE CI.CONTRACT_OBJID = #{contractObjId}
AND CI.STATUS = 'ACTIVE'
AND CIS.SERIAL_NO IS NOT NULL
AND CIS.SERIAL_NO != ''
ORDER BY CI.SEQ, CIS.SEQ
</select>
<!-- 고객사 담당자 정보 조회 -->
<select id="getCustomerContactInfo" parameterType="map" resultType="map">
SELECT

View File

@@ -1700,21 +1700,33 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
-- 출하일 기본값: 오늘 날짜
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.PM_USER_ID,
PM.QUANTITY
PM.QUANTITY,
PM.CONTRACT_OBJID,
PM.PART_OBJID
</select>
<!--
@@ -1945,7 +1957,7 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
<if test="salesExchangeRate != null and salesExchangeRate != ''">
, sales_exchange_rate = #{salesExchangeRate}::numeric
</if>
WHERE sale_no = #{saleNo}
WHERE sale_no = #{saleNo}::integer
</update>
<!-- PROJECT_MGMT의 OBJID 조회 (shipment_log의 target_objid로 사용) -->
@@ -1957,19 +1969,98 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
<!-- 출하일 상세 내역 조회 (모든 분할 출하 포함) -->
<select id="getShippingDetailList" parameterType="map" resultType="map">
/* salesNcollectMgmt.getShippingDetailList - shipment_log에서 모든 분할 출하 조회 */
SELECT
SELECT
SL.log_id,
COALESCE(TO_CHAR(SL.shipping_date, 'YYYY-MM-DD'), '미등록') AS shipping_date,
COALESCE(SL.split_quantity, 0) AS shipping_quantity,
COALESCE(SL.shipping_status, '미등록') AS shipping_order_status,
COALESCE(SL.serial_no, '-') AS serial_no,
COALESCE((
SELECT STRING_AGG(CIS.SERIAL_NO, ',' ORDER BY CIS.SEQ)
FROM CONTRACT_ITEM CI
JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND CIS.STATUS = 'ACTIVE'
JOIN PROJECT_MGMT PM ON PM.CONTRACT_OBJID = CI.CONTRACT_OBJID AND PM.PART_OBJID = CI.PART_OBJID
WHERE PM.PROJECT_NO = SL.target_objid AND CI.STATUS = 'ACTIVE'
), '-') AS serial_no,
SL.target_objid AS project_no,
'분할 출하 ' || SL.log_id AS source,
TO_CHAR(SL.reg_date, 'YYYY-MM-DD HH24:MI:SS') AS reg_date
FROM shipment_log SL
FROM shipment_log SL
WHERE SL.target_objid = #{projectNo}
ORDER BY SL.shipping_date DESC, SL.log_id DESC
</select>
<!-- shipment_log 단건 조회 (수정 팝업용) -->
<select id="getShipmentLogById" parameterType="map" resultType="map">
/* salesNcollectMgmt.getShipmentLogById - shipment_log 단건 조회 */
SELECT
SL.log_id,
SL.target_objid,
COALESCE(TO_CHAR(SL.shipping_date, 'YYYY-MM-DD'), '') AS SHIPPING_DATE,
COALESCE(SL.split_quantity, 0) AS SALES_QUANTITY,
COALESCE(SL.shipping_status, '') AS SHIPPING_ORDER_STATUS,
COALESCE(SL.shipping_method, '') AS SHIPPING_METHOD,
COALESCE((
SELECT STRING_AGG(CIS.SERIAL_NO, ',' ORDER BY CIS.SEQ)
FROM CONTRACT_ITEM CI
JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND CIS.STATUS = 'ACTIVE'
JOIN PROJECT_MGMT PM ON PM.CONTRACT_OBJID = CI.CONTRACT_OBJID AND PM.PART_OBJID = CI.PART_OBJID
WHERE PM.PROJECT_NO = SL.target_objid AND CI.STATUS = 'ACTIVE'
), '') AS SERIAL_NO,
COALESCE(SL.sales_unit_price, 0) AS SALES_UNIT_PRICE,
COALESCE(SL.sales_supply_price, 0) AS SALES_SUPPLY_PRICE,
COALESCE(SL.sales_vat, 0) AS SALES_VAT,
COALESCE(SL.sales_total_amount, 0) AS SALES_TOTAL_AMOUNT,
COALESCE(SL.sales_currency, '') AS SALES_CURRENCY,
COALESCE(SL.sales_exchange_rate, 0) AS SALES_EXCHANGE_RATE,
COALESCE(SL.manager_user_id, '') AS MANAGER,
COALESCE(SL.incoterms, '') AS INCOTERMS,
COALESCE(SL.original_quantity, 0) AS ORDER_QUANTITY,
COALESCE(SL.remaining_quantity, 0) AS REMAINING_QUANTITY
FROM shipment_log SL
WHERE SL.log_id = #{logId}::integer
</select>
<!-- shipment_log 수정 -->
<update id="updateShipmentLog" parameterType="map">
/* salesNcollectMgmt.updateShipmentLog - shipment_log 수정 */
UPDATE shipment_log SET
split_quantity = #{salesQuantity}::integer,
shipping_status = #{shippingOrderStatus}
<if test="shippingDate != null and shippingDate != ''">
, shipping_date = TO_DATE(#{shippingDate}, 'YYYY-MM-DD')
</if>
<if test="shippingMethod != null and shippingMethod != ''">
, shipping_method = #{shippingMethod}
</if>
<if test="serialNo != null and serialNo != ''">
, serial_no = #{serialNo}
</if>
<if test="salesUnitPrice != null and salesUnitPrice != ''">
, sales_unit_price = #{salesUnitPrice}::numeric
</if>
<if test="salesSupplyPrice != null and salesSupplyPrice != ''">
, sales_supply_price = #{salesSupplyPrice}::numeric
</if>
<if test="salesVat != null and salesVat != ''">
, sales_vat = #{salesVat}::numeric
</if>
<if test="salesTotalAmount != null and salesTotalAmount != ''">
, sales_total_amount = #{salesTotalAmount}::numeric
</if>
<if test="salesCurrency != null and salesCurrency != ''">
, sales_currency = #{salesCurrency}
</if>
<if test="salesExchangeRate != null and salesExchangeRate != ''">
, sales_exchange_rate = #{salesExchangeRate}::numeric
</if>
<if test="managerUserId != null and managerUserId != ''">
, manager_user_id = #{managerUserId}
</if>
<if test="incoterms != null and incoterms != ''">
, incoterms = #{incoterms}
</if>
WHERE log_id = #{logId}::integer
</update>
<!-- 거래명세서 - 고객 정보 조회 -->
<select id="getCustomerInfoByProjectNo" parameterType="map" resultType="map">
/* salesNcollectMgmt.getCustomerInfoByProjectNo - 프로젝트 번호로 고객 정보 조회 */
@@ -2268,5 +2359,372 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
WHERE OBJID::VARCHAR = #{OBJID}
</update>
<!-- ==================== 매출관리 출하건별 분리 쿼리 ==================== -->
<!-- 매출관리 그리드 목록 - shipment_log 기반 (출하건별 1행) -->
<select id="getRevenueMgmtGridList" parameterType="map" resultType="map">
/* salesNcollectMgmt.getRevenueMgmtGridList - shipment_log 기반 매출관리 조회 */
SELECT
T.OBJID,
T.PROJECT_NO,
T.CONTRACT_OBJID,
SL.log_id AS LOG_ID,
COALESCE(SL.sales_deadline_date, '') AS SALES_DEADLINE_DATE,
COALESCE(SL.tax_type, '') AS TAX_TYPE,
COALESCE(CODE_NAME(SL.tax_type), '') AS TAX_TYPE_NAME,
COALESCE(SL.tax_invoice_date, '') AS TAX_INVOICE_DATE,
COALESCE(SL.export_decl_no, '') AS EXPORT_DECL_NO,
COALESCE(SL.loading_date, '') AS LOADING_DATE,
CODE_NAME(T.CATEGORY_CD) AS ORDER_TYPE,
CODE_NAME(T.PRODUCT) AS PRODUCT_TYPE,
CODE_NAME(T.AREA_CD) AS NATION,
CASE
WHEN T.CUSTOMER_OBJID LIKE 'C_%' THEN
(SELECT CLIENT_NM FROM CLIENT_MNG AS C WHERE 'C_' || C.OBJID::VARCHAR = T.CUSTOMER_OBJID)
ELSE
(SELECT SUPPLY_NAME FROM SUPPLY_MNG AS O WHERE O.OBJID::VARCHAR = T.CUSTOMER_OBJID::VARCHAR)
END AS CUSTOMER,
T.PART_NO AS PRODUCT_NO,
T.PART_NAME AS PRODUCT_NAME,
COALESCE((
SELECT STRING_AGG(CIS.SERIAL_NO, ',' ORDER BY CIS.SEQ)
FROM CONTRACT_ITEM CI
JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND CIS.STATUS = 'ACTIVE'
WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID
AND CI.PART_OBJID = T.PART_OBJID AND CI.STATUS = 'ACTIVE'
), '') AS SERIAL_NO,
COALESCE(T.QUANTITY::numeric, 0) AS ORDER_QUANTITY,
(SELECT CM.PO_NO FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID) AS PO_NO,
COALESCE(T.CONTRACT_DATE, (SELECT CM.order_date FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID)) AS ORDER_DATE,
COALESCE(SL.split_quantity, 0) AS SALES_QUANTITY,
COALESCE(SL.sales_unit_price, 0) AS SALES_UNIT_PRICE,
COALESCE(SL.sales_supply_price, 0) AS SALES_SUPPLY_PRICE,
COALESCE(SL.sales_vat, 0) AS SALES_VAT,
COALESCE(SL.sales_total_amount, 0) AS SALES_TOTAL_AMOUNT,
COALESCE(SL.sales_total_amount, 0) AS SALES_TOTAL_AMOUNT_KRW,
COALESCE(SL.sales_currency, T.CONTRACT_CURRENCY) AS SALES_CURRENCY,
CODE_NAME(COALESCE(SL.sales_currency, T.CONTRACT_CURRENCY)) AS SALES_CURRENCY_NAME,
COALESCE(SL.sales_exchange_rate, 0) AS SALES_EXCHANGE_RATE,
COALESCE(TO_CHAR(SL.shipping_date, 'YYYY-MM-DD'), '') AS SHIPPING_DATE,
COALESCE(SL.shipping_method, '') AS SHIPPING_METHOD,
COALESCE(
(SELECT USER_NAME FROM USER_INFO WHERE USER_ID = SL.manager_user_id),
(SELECT USER_NAME FROM USER_INFO WHERE USER_ID = T.PM_USER_ID)
) AS MANAGER,
COALESCE(SL.incoterms, '') AS INCOTERMS,
CASE
WHEN SL.sales_deadline_date IS NOT NULL AND SL.sales_deadline_date != '' THEN '완료'
ELSE ''
END AS SALES_STATUS
FROM PROJECT_MGMT AS T
INNER JOIN shipment_log SL ON SL.target_objid = T.PROJECT_NO
WHERE 1 = 1
AND T.PROJECT_NO IS NOT NULL
AND T.PROJECT_NO != ''
AND EXISTS (
SELECT 1 FROM CONTRACT_MGMT CM
WHERE CM.OBJID = T.CONTRACT_OBJID
AND CODE_NAME(CM.CONTRACT_RESULT) IN ('수주(FCST)', '수주')
)
<if test="orderType != null and orderType != ''">
AND T.CATEGORY_CD = #{orderType}
</if>
<if test="productType != null and productType != ''">
AND T.PRODUCT = #{productType}
</if>
<if test="nation != null and nation != ''">
AND T.AREA_CD = #{nation}
</if>
<if test="customer_objid != null and customer_objid != ''">
AND T.CUSTOMER_OBJID = #{customer_objid}
</if>
<if test="(search_partNo != null and search_partNo != '') or (search_partName != null and search_partName != '')">
AND (
<if test="search_partNo != null and search_partNo != ''">
T.PART_NO = #{search_partNo}
</if>
<if test="(search_partNo != null and search_partNo != '') and (search_partName != null and search_partName != '')">
OR
</if>
<if test="search_partName != null and search_partName != ''">
T.PART_NAME = #{search_partName}
</if>
)
</if>
<if test="serialNo != null and 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.SERIAL_NO) LIKE UPPER(CONCAT('%', #{serialNo}, '%'))
AND UPPER(CIS.STATUS) = 'ACTIVE'
)
</if>
<if test="poNo != null and poNo != ''">
AND EXISTS (
SELECT 1 FROM CONTRACT_MGMT CM
WHERE CM.OBJID = T.CONTRACT_OBJID
AND UPPER(CM.PO_NO) LIKE UPPER(CONCAT('%', #{poNo}, '%'))
)
</if>
<if test="orderDateFrom != null and orderDateFrom != ''">
AND EXISTS (
SELECT 1 FROM CONTRACT_MGMT CM
WHERE CM.OBJID = T.CONTRACT_OBJID
AND TO_DATE(CM.ORDER_DATE, 'YYYY-MM-DD') <![CDATA[>=]]> TO_DATE(#{orderDateFrom}, 'YYYY-MM-DD')
)
</if>
<if test="orderDateTo != null and orderDateTo != ''">
AND EXISTS (
SELECT 1 FROM CONTRACT_MGMT CM
WHERE CM.OBJID = T.CONTRACT_OBJID
AND TO_DATE(CM.ORDER_DATE, 'YYYY-MM-DD') <![CDATA[<=]]> TO_DATE(#{orderDateTo}, 'YYYY-MM-DD')
)
</if>
<if test="shippingDateFrom != null and shippingDateFrom != ''">
AND SL.shipping_date <![CDATA[>=]]> TO_DATE(#{shippingDateFrom}, 'YYYY-MM-DD')
</if>
<if test="shippingDateTo != null and shippingDateTo != ''">
AND SL.shipping_date <![CDATA[<=]]> TO_DATE(#{shippingDateTo}, 'YYYY-MM-DD')
</if>
<if test="salesDeadlineFrom != null and salesDeadlineFrom != ''">
AND SL.sales_deadline_date IS NOT NULL
AND TO_DATE(SL.sales_deadline_date, 'YYYY-MM-DD') <![CDATA[>=]]> TO_DATE(#{salesDeadlineFrom}, 'YYYY-MM-DD')
</if>
<if test="salesDeadlineTo != null and salesDeadlineTo != ''">
AND SL.sales_deadline_date IS NOT NULL
AND TO_DATE(SL.sales_deadline_date, 'YYYY-MM-DD') <![CDATA[<=]]> TO_DATE(#{salesDeadlineTo}, 'YYYY-MM-DD')
</if>
ORDER BY SL.shipping_date DESC, SL.log_id DESC
LIMIT #{COUNT_PER_PAGE} OFFSET (#{PAGE_INDEX} - 1) * #{COUNT_PER_PAGE}
</select>
<!-- 매출관리 그리드 목록 개수 - shipment_log 기반 -->
<select id="getRevenueMgmtGridListCount" parameterType="map" resultType="map">
/* salesNcollectMgmt.getRevenueMgmtGridListCount - shipment_log 기반 개수 */
SELECT
CEIL(COUNT(1)::float / #{COUNT_PER_PAGE}::float)::numeric::integer AS MAX_PAGE_SIZE,
COUNT(1)::integer AS TOTAL_CNT
FROM PROJECT_MGMT AS T
INNER JOIN shipment_log SL ON SL.target_objid = T.PROJECT_NO
WHERE 1 = 1
AND T.PROJECT_NO IS NOT NULL
AND T.PROJECT_NO != ''
AND EXISTS (
SELECT 1 FROM CONTRACT_MGMT CM
WHERE CM.OBJID = T.CONTRACT_OBJID
AND CODE_NAME(CM.CONTRACT_RESULT) IN ('수주(FCST)', '수주')
)
<if test="orderType != null and orderType != ''">
AND T.CATEGORY_CD = #{orderType}
</if>
<if test="productType != null and productType != ''">
AND T.PRODUCT = #{productType}
</if>
<if test="nation != null and nation != ''">
AND T.AREA_CD = #{nation}
</if>
<if test="customer_objid != null and customer_objid != ''">
AND T.CUSTOMER_OBJID = #{customer_objid}
</if>
<if test="(search_partNo != null and search_partNo != '') or (search_partName != null and search_partName != '')">
AND (
<if test="search_partNo != null and search_partNo != ''">
T.PART_NO = #{search_partNo}
</if>
<if test="(search_partNo != null and search_partNo != '') and (search_partName != null and search_partName != '')">
OR
</if>
<if test="search_partName != null and search_partName != ''">
T.PART_NAME = #{search_partName}
</if>
)
</if>
<if test="serialNo != null and 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.SERIAL_NO) LIKE UPPER(CONCAT('%', #{serialNo}, '%'))
AND UPPER(CIS.STATUS) = 'ACTIVE'
)
</if>
<if test="poNo != null and poNo != ''">
AND EXISTS (
SELECT 1 FROM CONTRACT_MGMT CM
WHERE CM.OBJID = T.CONTRACT_OBJID
AND UPPER(CM.PO_NO) LIKE UPPER(CONCAT('%', #{poNo}, '%'))
)
</if>
<if test="orderDateFrom != null and orderDateFrom != ''">
AND EXISTS (
SELECT 1 FROM CONTRACT_MGMT CM
WHERE CM.OBJID = T.CONTRACT_OBJID
AND TO_DATE(CM.ORDER_DATE, 'YYYY-MM-DD') <![CDATA[>=]]> TO_DATE(#{orderDateFrom}, 'YYYY-MM-DD')
)
</if>
<if test="orderDateTo != null and orderDateTo != ''">
AND EXISTS (
SELECT 1 FROM CONTRACT_MGMT CM
WHERE CM.OBJID = T.CONTRACT_OBJID
AND TO_DATE(CM.ORDER_DATE, 'YYYY-MM-DD') <![CDATA[<=]]> TO_DATE(#{orderDateTo}, 'YYYY-MM-DD')
)
</if>
<if test="shippingDateFrom != null and shippingDateFrom != ''">
AND SL.shipping_date <![CDATA[>=]]> TO_DATE(#{shippingDateFrom}, 'YYYY-MM-DD')
</if>
<if test="shippingDateTo != null and shippingDateTo != ''">
AND SL.shipping_date <![CDATA[<=]]> TO_DATE(#{shippingDateTo}, 'YYYY-MM-DD')
</if>
<if test="salesDeadlineFrom != null and salesDeadlineFrom != ''">
AND SL.sales_deadline_date IS NOT NULL
AND TO_DATE(SL.sales_deadline_date, 'YYYY-MM-DD') <![CDATA[>=]]> TO_DATE(#{salesDeadlineFrom}, 'YYYY-MM-DD')
</if>
<if test="salesDeadlineTo != null and salesDeadlineTo != ''">
AND SL.sales_deadline_date IS NOT NULL
AND TO_DATE(SL.sales_deadline_date, 'YYYY-MM-DD') <![CDATA[<=]]> TO_DATE(#{salesDeadlineTo}, 'YYYY-MM-DD')
</if>
</select>
<!-- 매출관리 합계 조회 - shipment_log 기반 -->
<select id="getRevenueMgmtTotals" parameterType="map" resultType="map">
/* salesNcollectMgmt.getRevenueMgmtTotals - shipment_log 기반 합계 */
SELECT
COALESCE(SUM(COALESCE(SL.sales_supply_price, 0)), 0)::numeric AS TOTAL_SUPPLY_PRICE,
COALESCE(SUM(COALESCE(SL.sales_vat, 0)), 0)::numeric AS TOTAL_VAT,
COALESCE(SUM(COALESCE(SL.sales_total_amount, 0)), 0)::numeric AS TOTAL_AMOUNT
FROM PROJECT_MGMT T
INNER JOIN shipment_log SL ON SL.target_objid = T.PROJECT_NO
WHERE 1 = 1
AND T.PROJECT_NO IS NOT NULL AND T.PROJECT_NO != ''
AND EXISTS (
SELECT 1 FROM CONTRACT_MGMT CM
WHERE CM.OBJID = T.CONTRACT_OBJID
AND CODE_NAME(CM.CONTRACT_RESULT) IN ('수주(FCST)', '수주')
)
<if test="orderType != null and orderType != ''">
AND T.CATEGORY_CD = #{orderType}
</if>
<if test="productType != null and productType != ''">
AND T.PRODUCT = #{productType}
</if>
<if test="nation != null and nation != ''">
AND T.AREA_CD = #{nation}
</if>
<if test="customer_objid != null and customer_objid != ''">
AND T.CUSTOMER_OBJID = #{customer_objid}
</if>
<if test="serialNo != null and 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} || '%')
)
</if>
<if test="poNo != null and poNo != ''">
AND EXISTS (SELECT 1 FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID AND CM.PO_NO LIKE '%' || #{poNo} || '%')
</if>
<if test="shippingDateFrom != null and shippingDateFrom != ''">
AND SL.shipping_date <![CDATA[>=]]> TO_DATE(#{shippingDateFrom}, 'YYYY-MM-DD')
</if>
<if test="shippingDateTo != null and shippingDateTo != ''">
AND SL.shipping_date <![CDATA[<=]]> TO_DATE(#{shippingDateTo}, 'YYYY-MM-DD')
</if>
<if test="salesDeadlineFrom != null and salesDeadlineFrom != ''">
AND SL.sales_deadline_date IS NOT NULL
AND TO_DATE(SL.sales_deadline_date, 'YYYY-MM-DD') <![CDATA[>=]]> TO_DATE(#{salesDeadlineFrom}, 'YYYY-MM-DD')
</if>
<if test="salesDeadlineTo != null and salesDeadlineTo != ''">
AND SL.sales_deadline_date IS NOT NULL
AND TO_DATE(SL.sales_deadline_date, 'YYYY-MM-DD') <![CDATA[<=]]> TO_DATE(#{salesDeadlineTo}, 'YYYY-MM-DD')
</if>
</select>
<!-- 매출마감 전표연동용 데이터 조회 - shipment_log 기반 (log_id로 조회) -->
<select id="getSlipDataForDeadlineByLogId" parameterType="map" resultType="com.pms.common.UpperKeyMap">
/* salesNcollectMgmt.getSlipDataForDeadlineByLogId - 출하건별 전표 데이터 조회 */
SELECT
PM.OBJID,
PM.PROJECT_NO,
PM.AREA_CD,
PM.CUSTOMER_OBJID,
PM.PART_NO,
PM.PART_NAME,
SL.log_id AS LOG_ID,
COALESCE(SL.tax_type, '') AS TAX_TYPE,
COALESCE(SL.tax_invoice_date, '') AS TAX_INVOICE_DATE,
COALESCE(SL.export_decl_no, '') AS EXPORT_DECL_NO,
COALESCE(SL.loading_date, '') AS LOADING_DATE,
COALESCE(SL.sales_deadline_date, '') AS SALES_STATUS,
CASE WHEN PM.CUSTOMER_OBJID LIKE 'C_%' THEN
(SELECT C.CLIENT_CD FROM CLIENT_MNG C WHERE 'C_' || C.OBJID::VARCHAR = PM.CUSTOMER_OBJID)
ELSE NULL END AS ERP_CLIENT_CD,
CASE WHEN PM.CUSTOMER_OBJID LIKE 'C_%' THEN
(SELECT C.CLIENT_NM FROM CLIENT_MNG C WHERE 'C_' || C.OBJID::VARCHAR = PM.CUSTOMER_OBJID)
ELSE
(SELECT S.SUPPLY_NAME FROM SUPPLY_MNG S WHERE S.OBJID::VARCHAR = PM.CUSTOMER_OBJID::VARCHAR)
END AS CUSTOMER_NAME,
CASE WHEN PM.CUSTOMER_OBJID LIKE 'C_%' THEN
(SELECT C.BUS_REG_NO FROM CLIENT_MNG C WHERE 'C_' || C.OBJID::VARCHAR = PM.CUSTOMER_OBJID)
ELSE NULL END AS BUS_REG_NO,
COALESCE(SL.sales_supply_price, 0) AS SALES_SUPPLY_PRICE,
COALESCE(SL.sales_vat, 0) AS SALES_VAT,
COALESCE(SL.sales_total_amount, 0) AS SALES_TOTAL_AMOUNT,
COALESCE(SL.sales_currency, PM.CONTRACT_CURRENCY) AS SALES_CURRENCY,
COALESCE(SL.sales_exchange_rate, 0) AS SALES_EXCHANGE_RATE,
CASE WHEN COALESCE(SL.sales_exchange_rate, 0) > 0 THEN
ROUND(COALESCE(SL.sales_total_amount, 0)::NUMERIC / SL.sales_exchange_rate::NUMERIC, 2)
ELSE 0 END AS FOREIGN_AMOUNT
FROM shipment_log SL
INNER JOIN PROJECT_MGMT PM ON PM.PROJECT_NO = SL.target_objid
WHERE SL.log_id = #{logId}::integer
</select>
<!-- shipment_log 마감정보 저장 -->
<update id="saveShipmentLogDeadlineInfo" parameterType="map">
/* salesNcollectMgmt.saveShipmentLogDeadlineInfo - 출하건별 마감정보 저장 */
UPDATE shipment_log SET
tax_type = #{taxType}
<if test="taxInvoiceDate != null and taxInvoiceDate != ''">, tax_invoice_date = #{taxInvoiceDate}</if>
<if test="exportDeclNo != null">, export_decl_no = #{exportDeclNo}</if>
<if test="loadingDate != null and loadingDate != ''">, loading_date = #{loadingDate}</if>
WHERE log_id = #{logId}::integer
</update>
<!-- shipment_log 전표 정보 저장 + 마감 처리 -->
<update id="updateShipmentLogSlipInfo" parameterType="map">
/* salesNcollectMgmt.updateShipmentLogSlipInfo - 출하건별 전표 연동 정보 저장 */
UPDATE shipment_log SET
sales_deadline_date = #{deadlineDate},
sales_slip_date = #{slipDate},
sales_slip_menu_sq = #{slipMenuSq}
WHERE log_id = #{logId}::integer
</update>
<!-- 프로젝트의 모든 shipment_log 마감 여부 확인 -->
<select id="checkAllShipmentLogClosed" parameterType="map" resultType="int">
/* salesNcollectMgmt.checkAllShipmentLogClosed - 미마감 건수 조회 */
SELECT COUNT(1)
FROM shipment_log
WHERE target_objid = #{projectNo}
AND (sales_deadline_date IS NULL OR sales_deadline_date = '')
</select>
<!-- 프로젝트 전체 마감 시 PROJECT_MGMT 상태 업데이트 -->
<update id="updateProjectSalesStatus" parameterType="map">
/* salesNcollectMgmt.updateProjectSalesStatus - 모든 출하건 마감 시 프로젝트 '완료' 처리 */
UPDATE PROJECT_MGMT
SET SALES_STATUS = '완료',
SALES_DEADLINE_DATE = #{deadlineDate}
WHERE PROJECT_NO = #{projectNo}
</update>
</mapper>

View File

@@ -2490,7 +2490,27 @@ private String encodeImageToBase64(String imagePath) {
return items;
}
/**
* 영업건(CONTRACT)의 전체 S/N 목록 조회
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public List<String> getAllSerialNumbersByContract(Map<String, Object> paramMap){
SqlSession sqlSession = null;
List<String> result = new ArrayList<String>();
try{
sqlSession = SqlMapConfig.getInstance().getSqlSession();
result = sqlSession.selectList("contractMgmt.getAllSerialNumbersByContract", paramMap);
}catch(Exception e){
e.printStackTrace();
}finally{
if(sqlSession != null) sqlSession.close();
}
return result;
}
/**
* 주문서관리 Total 합계 조회
* @param request

View File

@@ -237,7 +237,38 @@ public class SalesNcollectMgmtService {
return totalsMap;
}
/**
* 매출관리 합계 조회 (shipment_log 기반)
*/
public Map<String, Object> getRevenueMgmtTotals(Map<String, Object> paramMap) {
SqlSession sqlSession = null;
Map<String, Object> totalsMap = new HashMap<String, Object>();
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession();
totalsMap = sqlSession.selectOne("salesNcollectMgmt.getRevenueMgmtTotals", paramMap);
if(totalsMap == null) {
totalsMap = new HashMap<String, Object>();
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;
}
/**
* <pre>
* 판매 정보 조회
@@ -347,56 +378,68 @@ public List<Map<String, Object>> getProjectItems(Map<String, Object> paramMap) {
public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<String, Object> paramMap) {
Map<String, Object> resultMap = new HashMap<String, Object>();
SqlSession sqlSession = null;
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
PersonBean person = (PersonBean) request.getSession().getAttribute(Constants.PERSON_BEAN);
paramMap.put("cretEmpNo", person.getUserId());
String projectNo = (String) paramMap.get("orderNo");
String saleNo = (String) paramMap.get("saleNo");
String logId = (String) paramMap.get("logId");
System.out.println("=== saveSaleRegistration 시작 ===");
System.out.println("projectNo: " + projectNo);
System.out.println("saleNo (파라미터): " + saleNo);
System.out.println("logId: " + logId);
System.out.println("salesQuantity: " + paramMap.get("salesQuantity"));
// 폼 필드명(manager) → SQL 파라미터명(managerUserId) 매핑
if(paramMap.get("manager") != null && !"".equals(paramMap.get("manager"))) {
paramMap.put("managerUserId", paramMap.get("manager"));
}
System.out.println("serialNo: " + paramMap.get("serialNo"));
System.out.println("manager: " + paramMap.get("manager"));
// saleNo가 있으면 수정 모드 → sales_registration 직접 UPDATE
if(saleNo != null && !"".equals(saleNo)) {
System.out.println("=== 수정 모드: updateSaleRegistration ===");
// logId가 있으면 기존 shipment_log 수정
if(logId != null && !"".equals(logId)) {
System.out.println("=== shipment_log 수정 모드 (logId: " + logId + ") ===");
paramMap.put("shippingOrderStatus", "출하지시");
sqlSession.update("salesNcollectMgmt.updateSaleRegistration", paramMap);
sqlSession.update("salesNcollectMgmt.updateShipmentLog", paramMap);
} else {
// 신규 등록 모드
System.out.println("=== 신규 등록 모드 ===");
// 1. 해당 프로젝트의 sales_registration 레코드 확인
// 신규 등록 → shipment_log에 INSERT
System.out.println("=== 신규 출하 등록 → shipment_log INSERT ===");
// target_objid에 PROJECT_NO를 직접 저장 (기존 데이터와 동일한 형식)
Map<String, Object> logParam = new HashMap<String, Object>();
logParam.put("targetObjid", projectNo);
logParam.put("salesQuantity", paramMap.get("salesQuantity"));
logParam.put("originalQuantity", paramMap.get("orderQuantity"));
logParam.put("remainingQuantity", paramMap.get("remainingQuantity"));
logParam.put("shippingOrderStatus", "출하지시");
logParam.put("shippingDate", paramMap.get("shippingDate"));
logParam.put("shippingMethod", paramMap.get("shippingMethod"));
logParam.put("salesUnitPrice", paramMap.get("salesUnitPrice"));
logParam.put("salesSupplyPrice", paramMap.get("salesSupplyPrice"));
logParam.put("salesVat", paramMap.get("salesVat"));
logParam.put("salesTotalAmount", paramMap.get("salesTotalAmount"));
logParam.put("salesCurrency", paramMap.get("salesCurrency"));
logParam.put("salesExchangeRate", paramMap.get("salesExchangeRate"));
logParam.put("managerUserId", paramMap.get("managerUserId"));
logParam.put("incoterms", paramMap.get("incoterms"));
logParam.put("serialNo", paramMap.get("serialNo"));
logParam.put("parentSaleNo", null);
logParam.put("cretEmpNo", paramMap.get("cretEmpNo"));
sqlSession.insert("salesNcollectMgmt.insertShipmentLog", logParam);
System.out.println("shipment_log INSERT 완료");
// sales_registration이 없으면 기본 레코드 생성
Map<String, Object> checkParam = new HashMap<String, Object>();
checkParam.put("orderNo", projectNo);
Map<String, Object> existingSale = sqlSession.selectOne("salesNcollectMgmt.getSaleInfo", checkParam);
Object saleNoObj = null;
if(existingSale != null) {
saleNoObj = existingSale.get("SALE_NO");
if(saleNoObj == null) saleNoObj = existingSale.get("sale_no");
}
System.out.println("existingSale: " + (existingSale != null ? "있음" : "없음"));
System.out.println("SALE_NO: " + saleNoObj);
// 2. sales_registration 레코드가 없으면 생성
if(saleNoObj == null) {
System.out.println("sales_registration 레코드 생성 (첫 판매)");
Map<String, Object> baseRecord = new HashMap<String, Object>();
baseRecord.put("orderNo", projectNo);
baseRecord.put("salesQuantity", paramMap.get("salesQuantity"));
@@ -408,31 +451,51 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
baseRecord.put("salesExchangeRate", paramMap.get("salesExchangeRate"));
baseRecord.put("shippingDate", paramMap.get("shippingDate"));
baseRecord.put("shippingMethod", paramMap.get("shippingMethod"));
baseRecord.put("managerUserId", paramMap.get("manager"));
baseRecord.put("managerUserId", paramMap.get("managerUserId"));
baseRecord.put("incoterms", paramMap.get("incoterms"));
baseRecord.put("serialNo", paramMap.get("serialNo"));
baseRecord.put("shippingOrderStatus", "출하지시");
baseRecord.put("cretEmpNo", paramMap.get("cretEmpNo"));
sqlSession.insert("salesNcollectMgmt.insertSaleRegistration", baseRecord);
System.out.println("sales_registration INSERT 완료");
} else {
// 이미 레코드가 있으면 UPDATE
System.out.println("기존 레코드 존재 → updateSaleRegistration");
paramMap.put("shippingOrderStatus", "출하지시");
sqlSession.update("salesNcollectMgmt.updateSaleRegistration", paramMap);
System.out.println("sales_registration INSERT 완료 (첫 판매)");
}
}
// S/N → CONTRACT_ITEM_SERIAL 동기화
// shipment_log 합계로 sales_registration 집계 업데이트 (PROJECT_NO 기준)
Map<String, Object> aggParam = new HashMap<String, Object>();
aggParam.put("projectNo", projectNo);
aggParam.put("managerUserId", paramMap.get("managerUserId"));
aggParam.put("incoterms", paramMap.get("incoterms"));
aggParam.put("shippingMethod", paramMap.get("shippingMethod"));
aggParam.put("shippingDate", paramMap.get("shippingDate"));
aggParam.put("salesUnitPrice", paramMap.get("salesUnitPrice"));
aggParam.put("salesCurrency", paramMap.get("salesCurrency"));
aggParam.put("salesExchangeRate", paramMap.get("salesExchangeRate"));
aggParam.put("serialNo", paramMap.get("serialNo"));
// sale_no 조회
Map<String, Object> checkParam2 = new HashMap<String, Object>();
checkParam2.put("orderNo", projectNo);
Map<String, Object> saleReg = sqlSession.selectOne("salesNcollectMgmt.getSaleInfo", checkParam2);
if(saleReg != null) {
Object saleNoVal = saleReg.get("SALE_NO");
if(saleNoVal == null) saleNoVal = saleReg.get("sale_no");
if(saleNoVal != null) {
aggParam.put("saleNo", saleNoVal.toString());
sqlSession.update("salesNcollectMgmt.updateSalesQuantityFromShipmentLog", aggParam);
System.out.println("sales_registration 집계 업데이트 완료");
}
}
// S/N → CONTRACT_ITEM_SERIAL 동기화 (S/N은 프로젝트 단위로 동일)
String serialNo = CommonUtils.nullToEmpty((String) paramMap.get("serialNo"));
String writer = CommonUtils.nullToEmpty((String) paramMap.get("cretEmpNo"));
if(projectNo != null && !projectNo.isEmpty()) {
SerialNoSyncUtil.syncSerialToContractItemSerial(
sqlSession, projectNo, serialNo, writer);
}
sqlSession.commit();
resultMap.put("result", true);
resultMap.put("msg", "저장되었습니다.");
@@ -451,7 +514,7 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
sqlSession.close();
}
}
return resultMap;
}
@@ -959,12 +1022,12 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
Map resultMap = new HashMap();
SqlSession sqlSession = null;
try {
System.out.println("===== 매출마감 + 아마란스 전표연동 시작 =====");
System.out.println("===== 매출마감 + 아마란스 전표연동 시작 (shipment_log 기반) =====");
String objIdListStr = CommonUtils.checkNull(paramMap.get("objIdList"));
String logIdListStr = CommonUtils.checkNull(paramMap.get("logIdList"));
String deadlineDate = CommonUtils.checkNull(paramMap.get("deadlineDate"));
if (objIdListStr == null || objIdListStr.isEmpty()) {
if (logIdListStr == null || logIdListStr.isEmpty()) {
resultMap.put("result", false);
resultMap.put("msg", "선택된 항목이 없습니다.");
return resultMap;
@@ -975,8 +1038,8 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
return resultMap;
}
String[] targetObjIdList = objIdListStr.split(",");
if (targetObjIdList == null || targetObjIdList.length == 0) {
String[] targetLogIdList = logIdListStr.split(",");
if (targetLogIdList == null || targetLogIdList.length == 0) {
resultMap.put("result", false);
resultMap.put("msg", "선택된 항목이 없습니다.");
return resultMap;
@@ -984,39 +1047,36 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
// 1) 선택된 항목들의 전표 데이터 조회
// 1) 선택된 shipment_log 항목들의 전표 데이터 조회
List<Map<String, Object>> slipDataList = new ArrayList<Map<String, Object>>();
for (int i = 0; i < targetObjIdList.length; i++) {
String objId = CommonUtils.checkNull(targetObjIdList[i]);
if (objId.isEmpty()) continue;
for (int i = 0; i < targetLogIdList.length; i++) {
String logId = CommonUtils.checkNull(targetLogIdList[i]);
if (logId.isEmpty()) continue;
HashMap queryParam = new HashMap();
queryParam.put("OBJID", objId);
Map<String, Object> slipData = sqlSession.selectOne("salesNcollectMgmt.getSlipDataForDeadline", queryParam);
queryParam.put("logId", logId);
Map<String, Object> slipData = sqlSession.selectOne("salesNcollectMgmt.getSlipDataForDeadlineByLogId", queryParam);
if (slipData == null) {
resultMap.put("result", false);
resultMap.put("msg", "프로젝트 정보를 찾을 수 없습니다. (OBJID: " + objId + ")");
resultMap.put("msg", "출하 정보를 찾을 수 없습니다. (LOG_ID: " + logId + ")");
return resultMap;
}
// 이미 마감 완료된 건 체크
String salesStatus = CommonUtils.checkNull(slipData.get("SALES_STATUS"));
if ("완료".equals(salesStatus)) {
if (salesStatus != null && !salesStatus.isEmpty()) {
resultMap.put("result", false);
resultMap.put("msg", "이미 매출마감 완료된 건이 포함되어 있습니다. (" + slipData.get("PROJECT_NO") + ")");
resultMap.put("msg", "이미 매출마감 완료된 건이 포함되어 있습니다. (" + slipData.get("PROJECT_NO") + " / LOG_ID: " + logId + ")");
return resultMap;
}
// 마감정보 등록 여부 체크
String taxType = CommonUtils.checkNull(slipData.get("TAX_TYPE"));
if (taxType.isEmpty()) {
resultMap.put("result", false);
resultMap.put("msg", "마감정보를 먼저 등록해주세요. (" + slipData.get("PROJECT_NO") + ")");
resultMap.put("msg", "마감정보를 먼저 등록해주세요. (" + slipData.get("PROJECT_NO") + " / LOG_ID: " + logId + ")");
return resultMap;
}
// ERP 거래처코드 체크
String erpClientCd = CommonUtils.checkNull(slipData.get("ERP_CLIENT_CD"));
if (erpClientCd.isEmpty()) {
resultMap.put("result", false);
@@ -1043,7 +1103,7 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
groupByCustomer.get(customerKey).add(data);
}
// 3) DB에서 계정과목 코드 조회 (erp_acct_code → api11A02로 동기화된 값)
// 3) DB에서 계정과목 코드 조회
SalesSlipApiClient slipApiClient = new SalesSlipApiClient();
String erpBaseUrl = "https://erp.rps-korea.com";
@@ -1064,6 +1124,7 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
);
int successCount = 0;
java.util.Set<String> processedProjects = new java.util.HashSet<String>();
for (Map.Entry<String, List<Map<String, Object>>> entry : groupByCustomer.entrySet()) {
List<Map<String, Object>> customerItems = entry.getValue();
@@ -1073,10 +1134,8 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
String customerName = CommonUtils.checkNull(firstItem.get("CUSTOMER_NAME"));
String areaCd = CommonUtils.checkNull(firstItem.get("AREA_CD"));
String taxType = CommonUtils.checkNull(firstItem.get("TAX_TYPE"));
// 국내/해외는 프로젝트의 AREA_CD로 판단, 세무구분(taxFg)은 과세구분(TAX_TYPE)으로 전달
boolean isDomestic = "0001220".equals(areaCd);
// 금액 합산
long totalSupplyPrice = 0;
long totalVat = 0;
long totalAmount = 0;
@@ -1091,9 +1150,10 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
if (itemNames.length() > 0) itemNames.append(", ");
itemNames.append(CommonUtils.checkNull(item.get("PART_NAME")));
processedProjects.add(CommonUtils.checkNull(item.get("PROJECT_NO")));
}
// 적요: 다건이면 "품명 외 N건"
String itemSummary;
if (customerItems.size() == 1) {
itemSummary = CommonUtils.checkNull(firstItem.get("PART_NAME"));
@@ -1101,13 +1161,11 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
itemSummary = CommonUtils.checkNull(firstItem.get("PART_NAME")) + "" + (customerItems.size() - 1) + "";
}
// 전표 JSON 생성
String slipJson;
String slipDate;
String taxFg = SalesSlipApiClient.convertTaxType(taxType);
if (isDomestic) {
// 국내: 세금계산서발행일 기준
String taxInvoiceDate = CommonUtils.checkNull(firstItem.get("TAX_INVOICE_DATE"));
slipDate = taxInvoiceDate.replace("-", "");
if (slipDate.isEmpty()) {
@@ -1117,8 +1175,6 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
}
String slipTitle = customerName + "_" + itemSummary;
// 작성번호는 타임스탬프 기반으로 유니크하게 생성
int menuSq = generateMenuSq();
slipJson = slipApiClient.buildDomesticSlipJson(
@@ -1128,25 +1184,20 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
itemSummary, taxFg, slipDate
);
// API 호출
String apiResponse = slipApiClient.registerSalesSlip(erpBaseUrl, slipJson);
System.out.println("[매출마감] 국내 전표 API 응답: " + apiResponse);
// 응답 검증
validateApiResponse(apiResponse, customerName);
// DB 업데이트: 각 항목에 전표 정보 저장
for (Map<String, Object> item : customerItems) {
HashMap updateParam = new HashMap();
updateParam.put("OBJID", CommonUtils.checkNull(item.get("OBJID")));
updateParam.put("logId", CommonUtils.checkNull(item.get("LOG_ID")));
updateParam.put("deadlineDate", deadlineDate);
updateParam.put("slipDate", slipDate);
updateParam.put("slipMenuSq", menuSq);
sqlSession.update("salesNcollectMgmt.updateSlipInfo", updateParam);
sqlSession.update("salesNcollectMgmt.updateShipmentLogSlipInfo", updateParam);
}
} else {
// 해외: 선적일자 기준
String loadingDate = CommonUtils.checkNull(firstItem.get("LOADING_DATE"));
slipDate = loadingDate.replace("-", "");
if (slipDate.isEmpty()) {
@@ -1160,7 +1211,7 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
String exportDeclNo = CommonUtils.checkNull(firstItem.get("EXPORT_DECL_NO"));
String exchCd = SalesSlipApiClient.convertCurrencyCode(salesCurrency);
String slipTitle = customerName + "_" + itemSummary
String slipTitle = customerName + "_" + itemSummary
+ "_" + String.format("%.2f", totalForeignAmount)
+ "_@" + String.format("%.2f", exchangeRate);
@@ -1178,22 +1229,35 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
String apiResponse = slipApiClient.registerSalesSlip(erpBaseUrl, slipJson);
System.out.println("[매출마감] 해외 전표 API 응답: " + apiResponse);
validateApiResponse(apiResponse, customerName);
for (Map<String, Object> item : customerItems) {
HashMap updateParam = new HashMap();
updateParam.put("OBJID", CommonUtils.checkNull(item.get("OBJID")));
updateParam.put("logId", CommonUtils.checkNull(item.get("LOG_ID")));
updateParam.put("deadlineDate", deadlineDate);
updateParam.put("slipDate", slipDate);
updateParam.put("slipMenuSq", menuSq);
sqlSession.update("salesNcollectMgmt.updateSlipInfo", updateParam);
sqlSession.update("salesNcollectMgmt.updateShipmentLogSlipInfo", updateParam);
}
}
successCount += customerItems.size();
}
// 프로젝트별 전체 마감 여부 확인 → 모든 출하건 마감 시 PROJECT_MGMT도 '완료' 처리
for (String projectNo : processedProjects) {
HashMap checkParam = new HashMap();
checkParam.put("projectNo", projectNo);
int unclosedCount = (Integer) sqlSession.selectOne("salesNcollectMgmt.checkAllShipmentLogClosed", checkParam);
if (unclosedCount == 0) {
HashMap pmUpdateParam = new HashMap();
pmUpdateParam.put("projectNo", projectNo);
pmUpdateParam.put("deadlineDate", deadlineDate);
sqlSession.update("salesNcollectMgmt.updateProjectSalesStatus", pmUpdateParam);
System.out.println("[매출마감] 프로젝트 전체 마감 완료: " + projectNo);
}
}
sqlSession.commit();
resultMap.put("result", true);
resultMap.put("msg", successCount + "건의 매출마감이 완료되었습니다. (전표 " + groupByCustomer.size() + "건 등록)");
@@ -1388,7 +1452,33 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
}
return resultList;
}
/**
* shipment_log 단건 조회 (수정 팝업용)
* @param logId
* @return Map
*/
public Map<String, Object> getShipmentLogById(String logId) {
SqlSession sqlSession = null;
Map<String, Object> result = null;
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession();
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("logId", logId);
result = sqlSession.selectOne("salesNcollectMgmt.getShipmentLogById", paramMap);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
if(result != null) {
result = CommonUtils.toUpperCaseMapKey(result);
}
return result;
}
/**
* 거래명세서 데이터 조회
* @param paramMap - projectNos (쉼표로 구분된 프로젝트 번호들)
@@ -1716,43 +1806,43 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
*/
public Map saveDeadlineInfo(HttpServletRequest request, Map paramMap) {
Map resultMap = new HashMap();
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
SqlSession sqlSession = null;
try {
String objIdListStr = CommonUtils.checkNull(paramMap.get("objIdList"));
String logIdListStr = CommonUtils.checkNull(paramMap.get("logIdList"));
String taxType = CommonUtils.checkNull(paramMap.get("taxType"));
String taxInvoiceDate = CommonUtils.checkNull(paramMap.get("taxInvoiceDate"));
String exportDeclNo = CommonUtils.checkNull(paramMap.get("exportDeclNo"));
String loadingDate = CommonUtils.checkNull(paramMap.get("loadingDate"));
if (objIdListStr == null || objIdListStr.isEmpty()) {
if (logIdListStr == null || logIdListStr.isEmpty()) {
resultMap.put("result", false);
resultMap.put("msg", "선택된 항목이 없습니다.");
return resultMap;
}
String[] targetObjIdList = objIdListStr.split(",");
sqlSession = SqlMapConfig.getInstance().getSqlSession();
String[] targetLogIdList = logIdListStr.split(",");
sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
for (int i = 0; i < targetObjIdList.length; i++) {
for (int i = 0; i < targetLogIdList.length; i++) {
HashMap sqlParamMap = new HashMap();
sqlParamMap.put("OBJID", CommonUtils.checkNull(targetObjIdList[i]));
sqlParamMap.put("logId", CommonUtils.checkNull(targetLogIdList[i]));
sqlParamMap.put("taxType", taxType);
sqlParamMap.put("taxInvoiceDate", taxInvoiceDate);
sqlParamMap.put("exportDeclNo", exportDeclNo);
sqlParamMap.put("loadingDate", loadingDate);
sqlSession.update("salesNcollectMgmt.saveDeadlineInfo", sqlParamMap);
sqlSession.update("salesNcollectMgmt.saveShipmentLogDeadlineInfo", sqlParamMap);
}
sqlSession.commit();
resultMap.put("result", true);
resultMap.put("msg", targetObjIdList.length + "건의 마감정보가 저장되었습니다.");
resultMap.put("msg", targetLogIdList.length + "건의 마감정보가 저장되었습니다.");
} catch (Exception e) {
resultMap.put("result", false);
resultMap.put("msg", "마감정보 저장 중 오류가 발생했습니다.");
sqlSession.rollback();
if (sqlSession != null) sqlSession.rollback();
e.printStackTrace();
} finally {
sqlSession.close();
if (sqlSession != null) sqlSession.close();
}
return resultMap;
}

View File

@@ -776,21 +776,17 @@ public class BatchService extends BaseService {
System.out.println("====================================");
for (Map<String, Object> emp : empList) {
// 처음 10건만 로그 출력
if (processCount < 10) {
System.out.println("[사원 " + (processCount + 1) + "]");
System.out.println(" - USER_ID: " + emp.get("user_id"));
System.out.println(" - SABUN: " + emp.get("sabun"));
System.out.println(" - USER_NAME: " + emp.get("user_name"));
System.out.println(" - USER_NAME_ENG: " + emp.get("user_name_eng"));
System.out.println(" - DEPT_CODE: " + emp.get("dept_code"));
System.out.println(" - DEPT_NAME: " + emp.get("dept_name"));
System.out.println(" - EMAIL: " + emp.get("email"));
System.out.println(" - CELL_PHONE: " + emp.get("cell_phone"));
System.out.println(" - STATUS: " + emp.get("status"));
System.out.println(" - TEL: " + emp.get("tel"));
System.out.println("---");
}
// UPSERT 실행
sqlSession.insert("batch.upsertEmployee", emp);
processCount++;
}
@@ -1314,7 +1310,13 @@ public class BatchService extends BaseService {
emp.put("position_code", extractJsonValue(json, "hclsCd")); // 직급코드 (hclsCd)
emp.put("position_name", extractJsonValue(json, "hclsNm")); // 직급명 (hclsNm)
emp.put("rank", extractJsonValue(json, "hrspNm")); // 직책명 (hrspNm - 사원, 대리 등)
emp.put("email", extractJsonValue(json, "emalAdd")); // 이메일 (emalAdd)
// 이메일: 급여이메일(emalAdd) 우선, 없으면 외부이메일(outemalAdd) 사용
String email = extractJsonValue(json, "emalAdd");
if (email == null || email.isEmpty()) {
email = extractJsonValue(json, "outemalAdd");
}
emp.put("email", email);
emp.put("tel", extractJsonValue(json, "tel")); // 전화번호 (tel)
emp.put("cell_phone", extractJsonValue(json, "emgcTel")); // 휴대폰 (emgcTel)
emp.put("user_type", extractJsonValue(json, "enrlFg")); // 사용자유형 (enrlFg - J01:재직)
emp.put("user_type_name", extractJsonValue(json, "enrlNm")); // 사용자유형명 (enrlNm)