매출관리 출하건별 분리: shipment_log 기반으로 변경

- 매출관리 그리드: shipment_log 건별 1행 표시 (기존 sales_registration 합계 → 개별)
- 매출마감: 출하건(log_id) 단위로 마감 가능, 전체 마감 시 PROJECT_MGMT 상태 갱신
- 마감정보입력: shipment_log 컬럼에 저장 (기존 PROJECT_MGMT → shipment_log)
- 아마란스 전표 API: 기존 로직 유지, 데이터 소스만 shipment_log로 변경
- DB: shipment_log에 마감 관련 7개 컬럼 추가 + 기존 마감 데이터 마이그레이션
- 판매관리 영향 없음

Made-with: Cursor
This commit is contained in:
2026-03-09 14:43:46 +09:00
parent 766df4d06a
commit cb93f5e6cc
4 changed files with 460 additions and 65 deletions

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",
@@ -637,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

@@ -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);

View File

@@ -2359,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

@@ -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>
* 판매 정보 조회
@@ -991,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;
@@ -1007,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;
@@ -1016,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);
@@ -1075,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";
@@ -1096,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();
@@ -1105,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;
@@ -1123,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"));
@@ -1133,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()) {
@@ -1149,8 +1175,6 @@ public Map<String, Object> saveSaleRegistration(HttpServletRequest request, Map<
}
String slipTitle = customerName + "_" + itemSummary;
// 작성번호는 타임스탬프 기반으로 유니크하게 생성
int menuSq = generateMenuSq();
slipJson = slipApiClient.buildDomesticSlipJson(
@@ -1160,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()) {
@@ -1192,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);
@@ -1210,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() + "건 등록)");
@@ -1774,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;
}