Browse Source

Merge branch 'hansae_sales' into style

jmh 4 years ago
parent
commit
61e1a17f26

+ 82 - 0
src/main/java/com/style24/admin/biz/dao/TsaSettleDao.java

@@ -1,14 +1,18 @@
 package com.style24.admin.biz.dao;
 
 import java.util.Collection;
+import java.util.Map;
 
 import com.style24.core.support.annotation.ShopDs;
 import com.style24.persistence.domain.AflinkFee;
 import com.style24.persistence.domain.DelvFeeSettle;
+import com.style24.persistence.domain.Erp;
 import com.style24.persistence.domain.GiftcardSettle;
 import com.style24.persistence.domain.GoodsSettle;
 import com.style24.persistence.domain.SettleConfirm;
 
+import com.gagaframework.web.parameter.GagaMap;
+
 /**
  * 정산 Dao
  *
@@ -113,4 +117,82 @@ public interface TsaSettleDao {
 	 */
 	Collection<AflinkFee> getAfLinkFeeList(AflinkFee afLinkFee);
 
+	/**
+	 * 한세ERP매출반영 목록
+	 * @param erp - 한세ERP 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	Collection<Erp> getHansaeSalesUploadList(Erp erp);
+
+	/**
+	 * 한세스타일매핑 정보
+	 * @param erp - 한세ERP 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	Erp getHansaeStyleMapping(Erp erp);
+
+	/**
+	 * 한세스타일매핑 정보 등록
+	 * @param erp - 한세ERP 정보
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	void saveHansaeStyleMapping(Erp erp);
+
+	/**
+	 * 매출반영 실패건 ERP 정보로 Update
+	 * @param erp - 한세ERP 정보
+	 * @return 처리건수
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	int updateFailedSalesUploadListToErpInfo(Erp erp);
+
+	/**
+	 * 매출반영 실패한 브랜드 목록
+	 * @param erp - 한세ERP 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	Collection<String> getFailedSalesUploadBrandList(Erp erp);
+
+	/**
+	 * 매출반영 실패한 목록
+	 * @param erp - 한세ERP 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	Collection<GagaMap> getFailedSalesUploadList(Erp erp);
+
+	/**
+	 * 매출반영I/F번호 조회
+	 * @param erpGb - ERP구분(hsmk:한세MK, hsdr:한세드림)
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	String getSalesUploadInterfaceNo(String erpGb);
+
+	/**
+	 * 매출반영목록 I/F번호 Update
+	 * @param paramMap - ERP 정보
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	void updateSalesUploadListInterfaceNo(GagaMap paramMap);
+
+	/**
+	 * 매출반영결과 처리
+	 * @param paramMap - 매출결과 정보
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	void updateSalesUploadResult(Map<String, Object> paramMap);
+
 }

+ 128 - 0
src/main/java/com/style24/admin/biz/service/TsaSettleService.java

@@ -1,6 +1,9 @@
 package com.style24.admin.biz.service;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -13,6 +16,7 @@ import com.style24.admin.support.env.TsaConstants;
 import com.style24.admin.support.security.session.TsaSession;
 import com.style24.persistence.domain.AflinkFee;
 import com.style24.persistence.domain.DelvFeeSettle;
+import com.style24.persistence.domain.Erp;
 import com.style24.persistence.domain.GiftcardSettle;
 import com.style24.persistence.domain.GoodsSettle;
 import com.style24.persistence.domain.SettleConfirm;
@@ -196,4 +200,128 @@ public class TsaSettleService {
 		return settleDao.getAfLinkFeeList(afLinkFee);
 	}
 
+	/**
+	 * 한세ERP매출반영 목록
+	 * @param erp - 한세ERP 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	public Collection<Erp> getHansaeSalesUploadList(Erp erp) {
+		return settleDao.getHansaeSalesUploadList(erp);
+	}
+
+	/**
+	 * 한세스타일매핑 정보
+	 * @param erp - 한세ERP 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	public Erp getHansaeStyleMapping(Erp erp) {
+		return settleDao.getHansaeStyleMapping(erp);
+	}
+
+	/**
+	 * 한세스타일매핑 정보 등록
+	 * @param erp - 한세ERP 정보
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	@Transactional("shopTxnManager")
+	public void saveHansaeStyleMapping(Erp erp) {
+		erp.setRegNo(TsaSession.getInfo().getUserNo());
+		erp.setUpdNo(TsaSession.getInfo().getUserNo());
+		settleDao.saveHansaeStyleMapping(erp);
+	}
+
+	/**
+	 * 매출반영 실패건 ERP 정보로 Update
+	 * @param erp - 한세ERP 정보
+	 * @return 처리건수
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	@Transactional("shopTxnManager")
+	public int updateFailedSalesUploadListToErpInfo(Erp erp) {
+		return settleDao.updateFailedSalesUploadListToErpInfo(erp);
+	}
+
+	/**
+	 * 매출반영 실패한 브랜드 목록
+	 * @param erp - 한세ERP 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	public Collection<String> getFailedSalesUploadBrandList(Erp erp) {
+		return settleDao.getFailedSalesUploadBrandList(erp);
+	}
+
+	/**
+	 * 매출반영 실패한 목록
+	 * @param erp - 한세ERP 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	public Collection<GagaMap> getFailedSalesUploadList(Erp erp) {
+		return settleDao.getFailedSalesUploadList(erp);
+	}
+
+	/**
+	 * 매출반영I/F번호 조회
+	 * @param erpGb - ERP구분(hsmk:한세MK, hsdr:한세드림)
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	public String getSalesUploadInterfaceNo(String erpGb) {
+		return settleDao.getSalesUploadInterfaceNo(erpGb);
+	}
+
+	/**
+	 * 매출반영목록 I/F번호 Update
+	 * @param paramMap - ERP 정보
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	@Transactional("shopTxnManager")
+	public void updateSalesUploadListInterfaceNo(GagaMap paramMap) {
+		settleDao.updateSalesUploadListInterfaceNo(paramMap);
+	}
+
+	/**
+	 * 매출반영결과 처리
+	 * @param erpGb - ERP구분(hsmk:한세MK, hsdr:한세드림)
+	 * @param paramMap - 결과 정보
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	@SuppressWarnings("unchecked")
+	@Transactional("shopTxnManager")
+	public void updateSalesUploadResult(String erpGb, GagaMap salesMap) {
+		if (salesMap.get("succList") != null) {
+			// 성공건 처리
+			List<Map<String, Object>> succList = (ArrayList<Map<String, Object>>)salesMap.get("succList");
+			if (succList != null && !succList.isEmpty()) {
+				for (Map<String, Object> dataMap : succList) {
+					dataMap.put("ERP_GB", erpGb);
+					settleDao.updateSalesUploadResult(dataMap);
+				}
+			}
+		}
+
+		if (salesMap.get("failList") != null) {
+			// 실패건 처리
+			List<Map<String, Object>> failList = (ArrayList<Map<String, Object>>)salesMap.get("failList");
+			if (failList != null && !failList.isEmpty()) {
+				for (Map<String, Object> dataMap : failList) {
+					dataMap.put("ERP_GB", erpGb);
+					settleDao.updateSalesUploadResult(dataMap);
+				}
+			}
+		}
+	}
+
 }

+ 152 - 0
src/main/java/com/style24/admin/biz/web/TsaSettleController.java

@@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.servlet.ModelAndView;
 
@@ -23,9 +24,11 @@ import com.style24.admin.biz.service.TsaRendererService;
 import com.style24.admin.biz.service.TsaSettleService;
 import com.style24.admin.support.controller.TsaBaseController;
 import com.style24.admin.support.security.session.TsaSession;
+import com.style24.core.biz.thirdparty.HansaeErp;
 import com.style24.core.support.message.TscMessageByLocale;
 import com.style24.persistence.domain.AflinkFee;
 import com.style24.persistence.domain.DelvFeeSettle;
+import com.style24.persistence.domain.Erp;
 import com.style24.persistence.domain.GiftcardSettle;
 import com.style24.persistence.domain.GoodsSettle;
 import com.style24.persistence.domain.SettleConfirm;
@@ -64,6 +67,9 @@ public class TsaSettleController extends TsaBaseController {
 	@Autowired
 	private Environment env;
 
+	@Autowired
+	private HansaeErp hansaeErp;
+
 	/**
 	 * 상품정산 화면
 	 * @return
@@ -415,4 +421,150 @@ public class TsaSettleController extends TsaBaseController {
 		return settleService.getAfLinkFeeList(aflinkFee);
 	}
 
+	/**
+	 * 한세ERP매출반영 화면
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	@GetMapping("/hansae/sales/upload/form")
+	public ModelAndView hansaeSalesUploadForm() {
+		ModelAndView mav = new ModelAndView();
+
+		mav.setViewName("settle/HansaeSalesUploadForm");
+
+		return mav;
+	}
+
+	/**
+	 * 한세ERP매출반영 목록
+	 * @param erp - 한세ERP 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	@PostMapping("/hansae/sales/upload/list")
+	@ResponseBody
+	public Collection<Erp> getHansaeSalesUploadList(@RequestBody Erp erp) {
+		return settleService.getHansaeSalesUploadList(erp);
+	}
+
+	/**
+	 * 한세스타일매핑 화면
+	 * @param erpGb - ERP구분(hsmk: 한세MK, hsdr: 한세드림)
+	 * @param cdStyle - 스타일코드
+	 * @param color - 색상코드
+	 * @param sizeCd - 사이즈코드
+	 * @param dsError - 실패메시지
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	@GetMapping("/hansae/style/mapping/form")
+	public ModelAndView hansaeStyleMappingForm(@RequestParam(value = "erpGb") String erpGb, @RequestParam(value = "cdStyle") String cdStyle, @RequestParam(value = "cdColor") String cdColor, @RequestParam(value = "cdSize") String cdSize, @RequestParam(value = "dsError") String dsError) {
+		ModelAndView mav = new ModelAndView();
+
+		Erp erp = new Erp();
+		erp.setErpGb(erpGb);
+		erp.setCdStyle(cdStyle);
+		erp.setCdColor(cdColor);
+		erp.setCdSize(cdSize);
+
+		Erp styleInfo = settleService.getHansaeStyleMapping(erp);
+
+		if (styleInfo == null) {
+			styleInfo = new Erp();
+			styleInfo.setErpGb(erpGb);
+			styleInfo.setCdStyle(cdStyle);
+			styleInfo.setCdColor(cdColor);
+			styleInfo.setCdSize(cdSize);
+		}
+
+		styleInfo.setDsError(dsError);
+
+		mav.addObject("styleInfo", styleInfo);
+
+		mav.setViewName("settle/HansaeStyleMappingForm");
+
+		return mav;
+	}
+
+	/**
+	 * 한세스타일매핑 저장 처리
+	 * @param erp - 한세ERP 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	@PostMapping("/hansae/style/mapping/save")
+	@ResponseBody
+	public GagaResponse saveHansaeStyleMapping(@RequestBody Erp erp) {
+		settleService.saveHansaeStyleMapping(erp);
+		return super.ok(message.getMessage("SUCC_0001"));
+	}
+
+	/**
+	 * 한세ERP 매출실패건 업로드 처리
+	 * @param erp - 한세ERP 정보
+	 * @return
+	 * @author gagamel
+	 * @since 2021. 11. 1
+	 */
+	@PostMapping("/hansae/failedSales/upload")
+	@ResponseBody
+	public GagaResponse uploadHansaeFailedSales(@RequestBody Erp erp) {
+		log.info("{}", erp);
+
+		// 매출반영 실패한 브랜드 목록 (브랜드 단위로 처리를 위해)
+		Collection<String> brandList = settleService.getFailedSalesUploadBrandList(erp);
+		if (brandList == null || brandList.isEmpty()) {
+			throw new IllegalStateException((HansaeErp.ErpGb.HANSAE_MK.value().equals(erp.getErpGb()) ? "한세MK" : "한세드림")
+				+ "의 판매기간(" + erp.getStartDt() + "~" + erp.getEndDt() + ") 내에 매출반영 실패건이 없습니다.");
+		}
+
+		// 매출반영 실패건 ERP 옵션 정보로 Update
+		int updCnt = settleService.updateFailedSalesUploadListToErpInfo(erp);
+//		if (updCnt == 0) {
+//			throw new IllegalStateException("매출반영 실패건 중 ERP 옵션 정보가 변경된 건이 없습니다.");
+//		}
+
+		for (String brandCd : brandList) {
+			erp.setBrandCd(brandCd);
+
+			// 매출반영 실패한 목록 (브랜드 단위로 조회)
+			Collection<GagaMap> uploadList = settleService.getFailedSalesUploadList(erp);
+
+			if (uploadList == null || uploadList.isEmpty()) {
+				log.error("{}-{} 브랜드의 매출반영 실패한 데이터가 없습니다.", erp.getErpGb(), brandCd);
+				continue;
+			}
+
+			// 매출반영I/F번호 조회
+			String noIf = settleService.getSalesUploadInterfaceNo(erp.getErpGb());
+			log.info("매출반영I/F번호: {}", noIf);
+
+			for (GagaMap dataMap : uploadList) {
+				// 매출반영목록 I/F번호 Update
+				dataMap.setString("ERP_GB", erp.getErpGb());
+				dataMap.setString("NO_IF", noIf);
+				settleService.updateSalesUploadListInterfaceNo(dataMap);
+			}
+
+//			// 매출업로드
+//			GagaMap salesMap = hansaeErp.uploadErpSales(erp.getErpGb(), uploadList);
+//
+//			if (salesMap == null || salesMap.isEmpty()) {
+//				log.error("{}-{} 브랜드의 매출반영결과 데이터가 없습니다. 한세 ERP 시스템담당자에게 문의해 주세요.", erp.getErpGb(), brandCd);
+//				continue;
+//			}
+//
+//			// 매출반영결과 처리
+//			settleService.updateSalesUploadResult(erp.getErpGb(), salesMap);
+
+			log.info("{}-{} 브랜드의 매출반영 업로드 성공", erp.getErpGb(), brandCd);
+		}
+
+		return super.ok(message.getMessage("SUCC_0001"));
+	}
+
 }

+ 206 - 0
src/main/java/com/style24/persistence/mybatis/shop/TsaSettle.xml

@@ -793,5 +793,211 @@
 		GROUP  BY AF_CHANNEL, AF_LINK_CD, AF_LINK_NM, OCCUR_DT, ORD_NO, ORD_DTL_STAT
 		ORDER  BY AF_CHANNEL, AF_LINK_CD, AF_LINK_NM, OCCUR_DT, ORD_NO, ORD_DTL_STAT
 	</select>
+	
+	<!-- 한세ERP매출반영 목록 -->
+	<select id="getHansaeSalesUploadList" parameterType="Erp" resultType="Erp">
+		/* TsaSettle.getHansaeSalesUploadList */
+		SELECT ERP_GB                                     /*ERP구분*/
+		     , DATE_FORMAT(DT_SALE,'%Y-%m-%d') AS DT_SALE /*판매일자*/
+		     , TP_SALE                                    /*판매구분(1:판매, 2:환불)*/
+		     , CD_STYLE                                   /*스타일코드*/
+		     , CD_COLOR                                   /*색상코드*/
+		     , CD_SIZE                                    /*사이즈코드*/
+		     , BRAND_CD                                   /*브랜드코드*/
+		     , QT_SALE                                    /*판매수량*/
+		     , AM_ACSALE                                  /*실판매금액*/
+		     , DS_REMARK                                  /*비고*/
+		     , NO_IF                                      /*I/F번호*/
+		     , CD_SALEBILL                                /*판매전표번호*/
+		     , DS_ERROR                                   /*실패메시지*/
+		     , CASE WHEN DS_ERROR IS NOT NULL
+		                 AND
+		                 IFNULL((SELECT COUNT(*)
+		                         FROM   TB_HANSAE_STYLE
+		                         WHERE  ERP_GB = HS.ERP_GB
+		                         AND    CD_STYLE = HS.CD_STYLE
+		                         AND    CD_COLOR = HS.CD_COLOR
+		                         AND    CD_SIZE = HS.CD_SIZE
+		                        ),0) > 0 THEN 'Y'
+		            ELSE
+		                'N'
+		       END                             MAPPING_YN /*매핑등록여부*/
+		FROM   TB_HANSAE_SALES HS
+		WHERE  ERP_GB = #{erpGb}
+		AND    DT_SALE BETWEEN REPLACE(#{startDt},'-','') AND REPLACE(#{endDt},'-','')
+		<if test='status == "S"'> <!-- 성공건 -->
+		AND    CD_SALEBILL IS NOT NULL
+		AND    DS_ERROR IS NULL
+		</if>
+		<if test='status == "F"'> <!-- 실패건 -->
+		AND    CD_SALEBILL IS NULL
+		AND    DS_ERROR IS NOT NULL
+		</if>
+		ORDER  BY ERP_GB, DT_SALE, BRAND_CD, CD_STYLE, CD_COLOR, CD_SIZE
+	</select>
+	
+	<!-- 한세스타일매핑 정보 -->
+	<select id="getHansaeStyleMapping" parameterType="Erp" resultType="Erp">
+		/* TsaSettle.getHansaeStyleMapping */
+		SELECT ERP_GB        /*ERP구분*/
+		     , CD_STYLE      /*스타일코드*/
+		     , CD_COLOR      /*색상코드*/
+		     , CD_SIZE       /*사이즈코드*/
+		     , ERP_CD_STYLE  /*ERP스타일코드*/
+		     , ERP_CD_COLOR  /*ERP색상코드*/
+		     , ERP_CD_SIZE   /*ERP사이즈코드*/
+		FROM   TB_HANSAE_STYLE
+		WHERE  ERP_GB = #{erpGb}
+		AND    CD_STYLE = #{cdStyle}
+		AND    CD_COLOR = #{cdColor}
+		AND    CD_SIZE = #{cdSize}
+	</select>
+	
+	<!-- 한세스타일매핑 정보 등록 -->
+	<update id="saveHansaeStyleMapping" parameterType="Erp">
+		/* TsaSettle.saveHansaeStyleMapping */
+		INSERT INTO TB_HANSAE_STYLE (
+		       ERP_GB
+		     , CD_STYLE
+		     , CD_COLOR
+		     , CD_SIZE
+		     , ERP_CD_STYLE
+		     , ERP_CD_COLOR
+		     , ERP_CD_SIZE
+		     , REG_NO
+		     , REG_DT
+		     , UPD_NO
+		     , UPD_DT
+		)
+		VALUES (
+		       #{erpGb}
+		     , #{cdStyle}
+		     , #{cdColor}
+		     , #{cdSize}
+		     , #{erpCdStyle}
+		     , #{erpCdColor}
+		     , #{erpCdSize}
+		     , #{regNo}
+		     , NOW()
+		     , #{updNo}
+		     , NOW()
+		)
+		ON DUPLICATE KEY UPDATE
+		       ERP_CD_STYLE = #{erpCdStyle}
+		     , ERP_CD_COLOR = #{erpCdColor}
+		     , ERP_CD_SIZE = #{erpCdSize}
+		     , UPD_NO = #{updNo}
+		     , UPD_DT = NOW()
+	</update>
+	
+	<!-- 매출반영 실패건 ERP 정보로 Update -->
+	<update id="updateFailedSalesUploadListToErpInfo" parameterType="Erp">
+		/* TsaSettle.updateFailedSalesUploadListToErpInfo */
+		UPDATE TB_HANSAE_SALES HS
+		SET    CD_STYLE = IFNULL((SELECT ERP_CD_STYLE
+		                          FROM   TB_HANSAE_STYLE
+		                          WHERE  ERP_GB = HS.ERP_GB
+		                          AND    CD_STYLE = HS.CD_STYLE
+		                          AND    CD_COLOR = HS.CD_COLOR
+		                          AND    CD_SIZE = HS.CD_SIZE
+		                         ),CD_STYLE)
+		     , CD_COLOR = IFNULL((SELECT ERP_CD_COLOR
+		                          FROM   TB_HANSAE_STYLE
+		                          WHERE  ERP_GB = HS.ERP_GB
+		                          AND    CD_STYLE = HS.CD_STYLE
+		                          AND    CD_COLOR = HS.CD_COLOR
+		                          AND    CD_SIZE = HS.CD_SIZE
+		                         ),CD_COLOR)
+		     , CD_SIZE = IFNULL((SELECT ERP_CD_SIZE
+		                         FROM   TB_HANSAE_STYLE
+		                         WHERE  ERP_GB = HS.ERP_GB
+		                         AND    CD_STYLE = HS.CD_STYLE
+		                         AND    CD_COLOR = HS.CD_COLOR
+		                         AND    CD_SIZE = HS.CD_SIZE
+		                        ),CD_SIZE)
+		WHERE  ERP_GB = #{erpGb}
+		AND    DT_SALE BETWEEN REPLACE(#{startDt},'-','') AND REPLACE(#{endDt},'-','')
+		AND    CD_SALEBILL IS NULL /*매출반영후실패한넘*/
+		AND    DS_ERROR IS NOT NULL /*매출반영후실패한넘*/
+		AND    EXISTS (SELECT 1
+		               FROM   TB_HANSAE_STYLE
+		               WHERE  ERP_GB = HS.ERP_GB
+		               AND    CD_STYLE = HS.CD_STYLE
+		               AND    CD_COLOR = HS.CD_COLOR
+		               AND    CD_SIZE = HS.CD_SIZE
+		              )
+	</update>
+	
+	<!-- 매출반영 실패한 브랜드 목록 -->
+	<select id="getFailedSalesUploadBrandList" parameterType="Erp" resultType="String">
+		/* TsaSettle.getFailedSalesUploadBrandList */
+		SELECT DISTINCT BRAND_CD
+		FROM   TB_HANSAE_SALES
+		WHERE  ERP_GB = #{erpGb}
+		AND    DT_SALE BETWEEN REPLACE(#{startDt},'-','') AND REPLACE(#{endDt},'-','')
+		AND    CD_SALEBILL IS NULL /*매출반영후실패한넘*/
+		AND    DS_ERROR IS NOT NULL /*매출반영후실패한넘*/
+		ORDER  BY BRAND_CD
+	</select>
+	
+	<!-- 매출반영 실패한 목록 -->
+	<select id="getFailedSalesUploadList" parameterType="Erp" resultType="paramMap">
+		/* TsaSettle.getFailedSalesUploadList */
+		SELECT DT_SALE
+		     , TP_SALE
+		     , CD_STYLE
+		     , CD_COLOR
+		     , CD_SIZE
+		     , QT_SALE
+		     , AM_ACSALE
+		     , DS_REMARK
+		FROM   TB_HANSAE_SALES
+		WHERE  ERP_GB = #{erpGb}
+		AND    DT_SALE BETWEEN REPLACE(#{startDt},'-','') AND REPLACE(#{endDt},'-','')
+		AND    BRAND_CD = #{brandCd}
+		AND    CD_SALEBILL IS NULL /*매출반영후실패한넘*/
+		AND    DS_ERROR IS NOT NULL /*매출반영후실패한넘*/
+		ORDER  BY DT_SALE, TP_SALE, CD_STYLE, CD_COLOR, CD_SIZE
+	</select>
+	
+	<!-- 매출반영I/F번호 조회 -->
+	<select id="getSalesUploadInterfaceNo" parameterType="String" resultType="String">
+		/* TsaSettle.getSalesUploadInterfaceNo */
+		SELECT CONCAT(DATE_FORMAT(NOW(),'%Y%m%d%H%i%S')
+		             ,LPAD(IFNULL((SELECT CAST(MAX(SUBSTRING(NO_IF,15)) AS UNSIGNED)
+		                           FROM   TB_HANSAE_SALES
+		                           WHERE  ERP_GB = #{erpGb}
+		                           AND    NO_IF LIKE CONCAT(DATE_FORMAT(NOW(),'%Y%m%d'),'%')
+		                          ),0) + 1,4,'0')) AS NO_IF
+		FROM   DUAL
+	</select>
+	
+	<!-- 매출반영목록 I/F번호 Update -->
+	<update id="updateSalesUploadListInterfaceNo" parameterType="paramMap">
+		/* TsaSettle.updateSalesUploadListInterfaceNo */
+		UPDATE TB_HANSAE_SALES
+		SET    NO_IF = #{NO_IF}
+		WHERE  ERP_GB = #{ERP_GB}
+		AND    DT_SALE = #{DT_SALE}
+		AND    TP_SALE = #{TP_SALE}
+		AND    CD_STYLE = #{CD_STYLE}
+		AND    CD_COLOR = #{CD_COLOR}
+		AND    CD_SIZE = #{CD_SIZE}
+	</update>
+	
+	<!-- 매출반영결과 처리 -->
+	<update id="updateSalesUploadResult" parameterType="paramMap">
+		/* TsaSettle.updateSalesUploadResult */
+		UPDATE TB_HANSAE_SALES
+		SET    CD_SALEBILL = #{CD_SALEBILL}
+		     , DS_ERROR = #{DS_ERROR}
+		     , NO_IF = CASE WHEN LENGTH(#{DS_ERROR}) > 0 THEN NULL ELSE #{NO_IF} END
+		     , UPD_DT = NOW()
+		WHERE  ERP_GB = #{ERP_GB}
+		AND    NO_IF = #{NO_IF}
+		AND    CD_STYLE = #{CD_STYLE}
+		AND    CD_COLOR = #{CD_COLOR}
+		AND    CD_SIZE = #{CD_SIZE}
+	</update>
 
 </mapper>

+ 2 - 2
src/main/java/com/style24/persistence/mybatis/shop/TsaStatistics.xml

@@ -195,8 +195,8 @@
 		                                          AND OD.SUPPLY_COMP_CD = TB2.TMTB_SUPPLY_CD
 		        LEFT OUTER JOIN TB_EXTMALL_USAC_PRICE EUP ON ODIH.ORD_DTL_ITEM_HST_SQ = EUP.ORD_DTL_ITEM_HST_SQ
 		        WHERE  1 = 1
-		        AND    ODIH.REG_DT <![CDATA[>=]]> STR_TO_DATE(#{occurDt},'%Y-%m-%d')
-		        AND    ODIH.REG_DT <![CDATA[<]]> DATE_ADD(STR_TO_DATE(#{occurDt},'%Y-%m-%d'), INTERVAL 1 DAY)
+		        AND    ODIH.REG_DT <![CDATA[>=]]> STR_TO_DATE(#{startDt},'%Y-%m-%d')
+		        AND    ODIH.REG_DT <![CDATA[<]]> DATE_ADD(STR_TO_DATE(#{endDt},'%Y-%m-%d'), INTERVAL 1 DAY)
 		        AND    ODIH.ORD_DTL_STAT IN ('G720_10','G720_30','G720_40','G720_50','G720_60') /*판매-결제완료,환입-취소완료,환입-품절취소,환입-반품완료,환입-교환완료*/
 		       ) ODIH
 		ORDER  BY ODIH.ORD_DTL_ITEM_HST_SQ

+ 18 - 1
src/main/webapp/WEB-INF/views/settle/DeliveryFeeSettleForm.html

@@ -174,9 +174,26 @@
 		if (!gagajf.validation($('#searchForm')))
 			return false;
 		
-		gagaAgGrid.fetch($('#searchForm').prop('action'), gridOptions, '#searchForm');
+		gagaAgGrid.fetch($('#searchForm').prop('action'), gridOptions, '#searchForm', fnCreateTotal);
 	});
 	
+	// 합계 생성
+	var fnCreateTotal = function() {
+		var delvFee = 0;
+
+		gridOptions.api.forEachNode(function(rowNode, index) {
+			if (!rowNode.group) {
+				if (typeof rowNode.data.delvFee =='number') { delvFee += rowNode.data.delvFee; }
+			}
+		});
+
+		var data = {
+			occurDt: null, supplyVendorNm: null, distributionGbNm: null, delvFeeGbNm: null, ordNo: '합계', delvFee:  delvFee
+		};
+
+		gagaAgGrid.setPinnedRowData(gridOptions, data, 'top');
+	}
+	
 	// 초기화 클릭시
 	$('#btnInit').on('click', function() {
 		$('#searchForm')[0].reset();

+ 34 - 0
src/main/webapp/WEB-INF/views/settle/GiftcardSettleForm.html

@@ -179,9 +179,36 @@
 		
 		gagaAgGrid.fetch($('#searchForm').prop('action'), gridOptions, '#searchForm', function() {
 			fnShowOrHideColumn();
+			fnCreateTotal();
 		});
 	});
 	
+	// 합계 생성
+	var fnCreateTotal = function() {
+		var chgGfcdAmt = 0;  // 최초등록금액
+		var usGfcdAmt = 0;   // 누적사용금액
+		var useGfcdAmt = 0;  // 정산월사용금액
+		var cnclGfcdAmt = 0; // 정산월취소금액
+		var rmGfcdAmt = 0;   // 잔액
+
+		gridOptions.api.forEachNode(function(rowNode, index) {
+			if (!rowNode.group) {
+				if (typeof rowNode.data.chgGfcdAmt =='number') { chgGfcdAmt += rowNode.data.chgGfcdAmt; }
+				if (typeof rowNode.data.usGfcdAmt =='number') { usGfcdAmt += rowNode.data.usGfcdAmt; }
+				if (typeof rowNode.data.useGfcdAmt =='number') { useGfcdAmt += rowNode.data.useGfcdAmt; }
+				if (typeof rowNode.data.cnclGfcdAmt =='number') { cnclGfcdAmt += rowNode.data.cnclGfcdAmt; }
+				if (typeof rowNode.data.rmGfcdAmt =='number') { rmGfcdAmt += rowNode.data.rmGfcdAmt; }
+			}
+		});
+
+		var data = {
+			gfcdNo: null, regDt: null, useExpDate: null, availYn: null, custId: '합계',
+			chgGfcdAmt:  chgGfcdAmt, usGfcdAmt:  usGfcdAmt, useGfcdAmt:  useGfcdAmt, cnclGfcdAmt:  cnclGfcdAmt, rmGfcdAmt:  rmGfcdAmt
+		};
+
+		gagaAgGrid.setPinnedRowData(gridOptions, data, 'top');
+	}
+	
 	// 초기화 클릭시
 	$('#btnInit').on('click', function() {
 		$('#searchForm')[0].reset();
@@ -209,6 +236,13 @@
 		
 		fnShowOrHideColumn();
 		gridOptions.api.setRowData();
+		
+		var data = {
+			gfcdNo: null, regDt: null, useExpDate: null, availYn: null, custId: null,
+			chgGfcdAmt: null, usGfcdAmt: null, useGfcdAmt: null, cnclGfcdAmt: null, rmGfcdAmt: null
+		};
+
+		gagaAgGrid.setPinnedRowData(gridOptions, data, 'top');
 	});
 	
 	var fnShowOrHideColumn = function() {

+ 66 - 2
src/main/webapp/WEB-INF/views/settle/GoodsSettleForm.html

@@ -266,7 +266,7 @@
 		},
 		{
 			headerName: "판매수수료율(%)", field: "sellFeeRate", width: 120, cellClass: 'text-center',
-			cellRenderer: function (params) { return params.value + '%'; }
+			cellRenderer: function (params) { return gagajf.isNull(params.value) ? '' : params.value + '%'; }
 		},
 		{
 			headerName: "수수료", field: "sellFeeAmt", width: 100, cellClass: 'text-right',
@@ -365,9 +365,73 @@
 		if (!gagajf.validation($('#searchForm')))
 			return false;
 		
-		gagaAgGrid.fetch($('#searchForm').prop('action'), gridOptions, '#searchForm');
+		gagaAgGrid.fetch($('#searchForm').prop('action'), gridOptions, '#searchForm', fnCreateTotal);
 	});
 	
+	// 합계 생성
+	var fnCreateTotal = function() {
+// 		var mallPrice = 0;           // 판매가
+// 		var sellPrice = 0;           // 정산판매가
+		var sellQty = 0;             // 판매수량
+		var sellAmt = 0;             // 판매총액
+		var cpnDcAmt = 0;            // 쿠폰할인금액
+		var cpn1DcAmt = 0;           // 즉시사용쿠폰금액
+		var goodsCpnDcAmt = 0;       // 상품쿠폰사용금액
+		var cartCpnDcAmt = 0;        // 주문서쿠폰사용금액
+		var pntDcAmt = 0;            // 포인트사용금액
+		var tmtbDcAmt = 0;           // 다다익선할인금액
+		var selfTmtbDcAmt = 0;       // 자사다다익선분담액
+		var supplyCompTmtbDcAmt = 0; // 입점다다익선분담액
+		var gfcdUseAmt = 0;          // 상품권사용금액
+		var selfCpnDcAmt = 0;        // 자사쿠폰분담액
+		var supplyCompCpnDcAmt = 0;  // 입점쿠폰분담액
+		var realSellPrice = 0;       // 실판매가
+ 		var realSellAmt = 0;         // 상품총액
+		var sellFeeAmt = 0;          // 수수료
+		var settleAmt = 0;           // 정산대상액
+
+		gridOptions.api.forEachNode(function(rowNode, index) {
+			if (!rowNode.group) {
+// 				if (typeof rowNode.data.mallPrice == 'number') { mallPrice += rowNode.data.mallPrice; }
+// 				if (typeof rowNode.data.sellPrice =='number') { sellPrice += rowNode.data.sellPrice; }
+				if (typeof rowNode.data.sellQty =='number') { sellQty += rowNode.data.sellQty; }
+				if (typeof rowNode.data.sellAmt =='number') { sellAmt += rowNode.data.sellAmt; }
+				if (typeof rowNode.data.cpnDcAmt =='number') { cpnDcAmt += rowNode.data.cpnDcAmt; }
+				if (typeof rowNode.data.cpn1DcAmt =='number') { cpn1DcAmt += rowNode.data.cpn1DcAmt; }
+				if (typeof rowNode.data.goodsCpnDcAmt =='number') { goodsCpnDcAmt += rowNode.data.goodsCpnDcAmt; }
+				if (typeof rowNode.data.cartCpnDcAmt =='number') { cartCpnDcAmt += rowNode.data.cartCpnDcAmt; }
+				if (typeof rowNode.data.pntDcAmt =='number') { pntDcAmt += rowNode.data.pntDcAmt; }
+				if (typeof rowNode.data.tmtbDcAmt =='number') { tmtbDcAmt += rowNode.data.tmtbDcAmt; }
+				if (typeof rowNode.data.selfTmtbDcAmt =='number') { selfTmtbDcAmt += rowNode.data.selfTmtbDcAmt; }
+				if (typeof rowNode.data.supplyCompTmtbDcAmt =='number') { supplyCompTmtbDcAmt += rowNode.data.supplyCompTmtbDcAmt; }
+				if (typeof rowNode.data.gfcdUseAmt =='number') { gfcdUseAmt += rowNode.data.gfcdUseAmt; }
+				if (typeof rowNode.data.selfCpnDcAmt =='number') { selfCpnDcAmt += rowNode.data.selfCpnDcAmt; }
+				if (typeof rowNode.data.supplyCompCpnDcAmt =='number') { supplyCompCpnDcAmt += rowNode.data.supplyCompCpnDcAmt; }
+				if (typeof rowNode.data.realSellPrice =='number') { realSellPrice += rowNode.data.realSellPrice; }
+				if (typeof rowNode.data.realSellAmt =='number') { realSellAmt += rowNode.data.realSellAmt; }
+				if (typeof rowNode.data.sellFeeAmt =='number') { sellFeeAmt += rowNode.data.sellFeeAmt; }
+				if (typeof rowNode.data.settleAmt =='number') { settleAmt += rowNode.data.settleAmt; }
+			}
+		});
+
+		var data = {
+			ordNo: null, ordDtlNo: null, mallGbNm: null, extmallOrderId: null, extmallNm: null,
+			supplyVendorNm: null, distributionGbNm: null, settleDayNm: null,
+			ordDt: null, settleGbNm: null, occurDt: null,
+			goodsCd: null, goodsNm: null, brandEnm: null, mdNm: null, itemCd: null, optCd1: null, optCd2: '합계',
+// 			mallPrice:  mallPrice, sellPrice:  sellPrice,
+			mallPrice:  null, sellPrice:  null,
+			sellQty:  sellQty, sellAmt:  sellAmt,
+			cpnDcAmt:  cpnDcAmt, cpn1DcAmt: cpn1DcAmt, goodsCpnDcAmt:  goodsCpnDcAmt, cartCpnDcAmt:  cartCpnDcAmt,
+			pntDcAmt:  pntDcAmt, tmtbDcAmt: tmtbDcAmt, selfTmtbDcAmt:  selfTmtbDcAmt, supplyCompTmtbDcAmt:  supplyCompTmtbDcAmt,
+			gfcdUseAmt:  gfcdUseAmt, selfCpnDcAmt:  selfCpnDcAmt, supplyCompCpnDcAmt:  supplyCompCpnDcAmt,
+			realSellPrice:  realSellPrice, realSellAmt:  realSellAmt,
+			sellFeeRate: null, sellFeeAmt:  sellFeeAmt, settleAmt:  settleAmt
+		};
+
+		gagaAgGrid.setPinnedRowData(gridOptions, data, 'top');
+	}
+	
 	// 초기화 클릭시
 	$('#btnInit').on('click', function() {
 		$('#searchForm')[0].reset();

+ 279 - 0
src/main/webapp/WEB-INF/views/settle/HansaeSalesUploadForm.html

@@ -0,0 +1,279 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : HansaeSalesUploadForm.html
+ * @desc    : 한세ERP매출반영 Page
+ *============================================================================
+ * STYLE24
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2021.11.01   gagamel     최초 작성
+ *******************************************************************************
+ -->
+	<div id="main">
+		<!-- 메인타이틀 영역 -->
+		<div class="main-title">
+		</div>
+		<!-- //메인타이틀 영역 -->
+		
+		<!-- 메뉴 설명 -->
+		<div class="infoBox menu-desc">
+		</div>
+		<!-- //메뉴 설명 -->
+		
+		<!-- 검색조건 영역 -->
+		<div class="panelStyle">
+			<form id="searchForm" name="searchForm" action="#" th:action="@{'/settle/hansae/sales/upload/list'}" onsubmit="$('#btnSearch').trigger('click'); return false;">
+				<table class="frmStyle" aria-describedby="검색조건">
+					<colgroup>
+						<col style="width:10%;"/>
+						<col style="width:15%;"/>
+						<col style="width:10%;"/>
+						<col/>
+					</colgroup>
+					<tr>
+						<th>ERP구분<i class="required" title="필수" aria-hidden="true"></i></th>
+						<td>
+							<select name="erpGb">
+								<option value="hsmk">[hsmk] 한세MK</option>
+								<option value="hsdr">[hsdr] 한세드림</option>
+							</select>
+						</td>
+						<th>판매기간<i class="required" title="필수" aria-hidden="true"></i></th>
+						<td id="terms">
+							<span class="nowrap">
+								<input name="startDt" id="startDt" type="text" class="w80 schDate" maxlength="8" required="required" data-valid-name="판매시작일"/>
+								~
+								<input name="endDt" id="endDt" type="text" class="w80 schDate" maxlength="8" required="required" data-valid-name="판매종료일"/>
+							</span>
+							<button type="button" class="btn btn-default btn-sm btnToday" onclick="gagajf.setDate('#terms', 'startDt', 'endDt', 't');">오늘</button>
+							<button type="button" class="btn btn-default btn-sm btnYesterday" onclick="gagajf.setDate('#terms', 'startDt', 'endDt', 'y');">어제</button>
+							<button type="button" class="btn btn-default btn-sm" onclick="gagajf.setDate('#terms', 'startDt', 'endDt', '7d');">최근한주</button>
+							<button type="button" class="btn btn-default btn-sm" onclick="gagajf.setDate('#terms', 'startDt', 'endDt', 'tw');">이번주</button>
+							<button type="button" class="btn btn-default btn-sm" onclick="gagajf.setDate('#terms', 'startDt', 'endDt', 'pw');">지난주</button>
+							<button type="button" class="btn btn-default btn-sm" onclick="gagajf.setDate('#terms', 'startDt', 'endDt', '1m');">최근한달</button>
+							<button type="button" class="btn btn-default btn-sm" onclick="gagajf.setDate('#terms', 'startDt', 'endDt', 'tm');">이번달</button>
+							<button type="button" class="btn btn-default btn-sm" onclick="gagajf.setDate('#terms', 'startDt', 'endDt', 'pm');">지난달</button>
+							<span class="infoTxt cRed"><i class="fa fa-info-circle" aria-hidden="true"></i>(한달 한도)</span>
+						</td>
+					</tr>
+					<tr>
+						<th>업로드결과</th>
+						<td colspan="3">
+							<select name="status">
+								<option value="">[전체]</option>
+								<option value="S">[S] 성공</option>
+								<option value="F">[F] 실패</option>
+							</select>
+						</td>
+					</tr>
+				</table>
+				
+				<ul class="panelBar">
+					<li class="center">
+						<button type="button" class="btn btn-base btn-lg" id="btnSearch">조회</button>
+						<button type="button" class="btn btn-gray btn-lg" onclick="$('#searchForm')[0].reset();">초기화</button>
+					</li>
+				</ul>
+			</form>
+		</div>
+		<!-- 검색조건 영역 -->
+
+		<!-- 리스트 영역 -->
+		<div class="panelStyle">
+			<!-- 버튼 배치 영역 -->
+			<ul class="panelBar">
+				<li>
+					<p>
+<!-- 						<span class="infoTxt cBlue"><i class="fa fa-info-circle" aria-hidden="true"></i>매출반영이 아직 전송되지 않은 건은 <strong>[미전송건업로드]</strong> 버튼을 클릭해 전송하세요. <u>(전일자 매출은 배치를 통해 자동으로 ERP로 전송됩니다.)</u></span><br/> -->
+						<span class="infoTxt cRed"><i class="fa fa-info-circle" aria-hidden="true"></i>실패건들은 <u>"실패메시지"를 클릭해 옵션을 올바르게 변경</u>하고 그 후 <strong>[실패건업로드]</strong> 버튼을 클릭해 재전송 하세요.</span>
+					</p>
+				</li>
+				<li class="right">
+<!-- 					<button type="button" class="btn btn-base btn-lg" id="btnSendNoUploadedSales">미전송건업로드</button> -->
+					<button type="button" class="btn btn-warning btn-lg" id="btnResendFailSales">실패건업로드</button>
+					<button type="button" class="btn btn-default btn-lg" id="btnExcel">엑셀다운로드</button>
+				</li>
+			</ul>
+			<!-- //버튼 배치 영역 -->
+			
+			<div id="gridList" style="width: 100%; height: 570px" class="ag-theme-balham"></div>
+		</div>
+		<!-- //리스트 영역 -->
+	</div>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	let columnDefs = [
+// 		{
+// 			width: 40, minWidth: 40, cellClass: 'text-center', headerCheckboxSelection: true, checkboxSelection: true, filter: false,
+// 			checkboxSelection: function (params) { return !gagajf.isNull(params.data.dsError) ? true : false; }
+// 		},
+		{
+			headerName: "ERP구분", field: "erpGb", width: 80, cellClass: 'text-center',
+			cellRenderer: function(params) { return params.value == 'hsmk' ? '한세MK' : '한세드림'; }
+		},
+		{ headerName: "판매일자", field: "dtSale", width: 100, cellClass: 'text-center' },
+		{
+			headerName: "판매구분", field: "tpSale", width: 80, cellClass: 'text-center',
+			cellRenderer: function(params) { return params.value == '1' ? '판매' : '환불'; }
+		},
+		{ headerName: "스타일코드", field: "cdStyle", width: 120, cellClass: 'text-center' },
+		{ headerName: "색상코드", field: "cdColor", width: 80, cellClass: 'text-center' },
+		{ headerName: "사이즈코드", field: "cdSize", width: 90, cellClass: 'text-center' },
+		{
+			headerName: "판매수량", field: "qtSale", width: 100, cellClass: 'text-center',
+			cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value); },
+			cellStyle : function(params) { if (Number(params.value) < 0) return { 'color' : 'red' } }
+		},
+		{
+			headerName: "실판매금액", field: "amAcsale", width: 100, cellClass: 'text-right',
+			cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value); },
+			cellStyle : function(params) { if (Number(params.value) < 0) return { 'color' : 'red' } }
+		},
+		{ headerName: "I/F번호", field: "noIf", width: 150, cellClass: 'text-center' },
+		{ headerName: "판매전표번호", field: "cdSalebill", width: 150, cellClass: 'text-center' },
+		{ 
+			headerName: "실패메시지", field: "dsError", width: 300,
+			cellRenderer: function(params) { return gagajf.isNull(params.value) ? '' : ('<a href="javascript:void(0);">' + params.value + '</a>'); }
+		},
+		{ 
+			headerName: "매핑여부", field: "mappingYn", width: 80, cellClass: 'text-center',
+			cellRenderer: function(params) {
+				if (gagajf.isNull(params.data.dsError)) return '';
+				return params.value == 'Y' ? 'Y' : '';
+			}
+		},
+		{ headerName: "반영일시", field: "regDt", width: 150, cellClass: 'text-center' }
+	];
+
+	let gridOptions = gagaAgGrid.getGridOptions(columnDefs);
+
+	// 셀 클릭 이벤트
+	gridOptions.onCellClicked = function(event) {
+		if (event.colDef.field != 'dsError')
+			return;
+		
+		if (gagajf.isNull(event.data.dsError))
+			return;
+		
+		let actionUrl = '/settle/hansae/style/mapping/form'
+				+ '?erpGb=' + event.data.erpGb
+				+ '&cdStyle=' + encodeURIComponent(event.data.cdStyle)
+				+ '&cdColor=' + encodeURIComponent(event.data.cdColor)
+				+ '&cdSize=' + encodeURIComponent(event.data.cdSize)
+				+ '&dsError=' + encodeURIComponent(event.data.dsError);
+		cfnOpenModalPopup(actionUrl, 'popupHansaeStyleMapping');
+	}
+	
+	// 검색
+	$('#btnSearch').on('click', function() {
+		// 입력 값 체크
+		if (!gagajf.validation('#searchForm'))
+			return false;
+		
+		let fromDate = $('#searchForm input[name=startDt]').val().toDate('YYYY-MM-DD');
+		let toDate = $('#searchForm input[name=endDt]').val().toDate('YYYY-MM-DD');
+		
+		if (fromDate > toDate) {
+			mcxDialog.alert("시작일자는 종료일자 보다 클 수 없습니다.");
+			$('#searchForm input[name=endDt]').focus();
+			return false;
+		}
+		
+		var iDays = Math.ceil((toDate - fromDate) / (60 * 60 * 24 * 1000));
+		if (iDays >= 31) {
+			mcxDialog.alert("최대 31일까지 조회할 수 있습니다.");
+			return;
+		}
+		
+		gagaAgGrid.fetch($('#searchForm').prop('action'), gridOptions, '#searchForm');
+	});
+	
+	// 미전송건업로드
+	$('#btnSendNoUploadedSales').on('click', function() {
+		
+	});
+	
+	// 실패건업로드
+	$('#btnResendFailSales').on('click', function() {
+		let allData = gagaAgGrid.getAllRowData(gridOptions);
+		
+		let isFailData = false;
+		allData.forEach(function(item, idx) {
+			if (!gagajf.isNull(item.dsError)) {
+				isFailData = true;
+				return false;
+			}
+		});
+		
+		if (!isFailData) {
+			mcxDialog.alert("매출반영할 실패건이 없습니다.");
+			return false;
+		}
+		
+		if (gagajf.isNull($('#startDt').val())) {
+			mcxDialog.alertC('판매시작일자를 입력해 주세요.', {
+				sureBtnText: "확인",
+				sureBtnClick: function() {
+					$('#startDt').focus();
+				}
+			});
+			return;
+		}
+		
+		if (gagajf.isNull($('#endDt').val())) {
+			mcxDialog.alertC('판매종료일자를 입력해 주세요.', {
+				sureBtnText: "확인",
+				sureBtnClick: function() {
+					$('#endDt').focus();
+				}
+			});
+			return;
+		}
+		
+		let msg =  ($('#searchForm select[name=erpGb]').val() == 'hsmk' ? '한세MK' : '한세드림') + '의'
+				+ ' 판매기간 ' + $('#startDt').val().toDate('YYYY-MM-DD').format('YYYY-MM-DD') + '~' + $('#endDt').val().toDate('YYYY-MM-DD').format('YYYY-MM-DD') + ' 동안의 매출반영 실패건을 업로드합니다.<br/>'
+				+ '매출반영은 몇 분이 소요될 수 있습니다. 계속 진행하시겠습니까?';
+		
+		mcxDialog.confirm(msg, {
+			cancelBtnText: "취소",
+			sureBtnText: "확인",
+			sureBtnClick: function() {
+				let jsonData = JSON.stringify({
+					erpGb : $('#searchForm select[name=erpGb]').val(),
+					startDt : $('#startDt').val(),
+					endDt : $('#endDt').val()
+				});
+				
+				gagajf.ajaxJsonSubmit('/settle/hansae/failedSales/upload', jsonData, function() {
+					$('#btnSearch').trigger('click');
+				});
+			}
+		});
+	});
+	
+	// 엑셀다운로드
+	$('#btnExcel').on('click', function() {
+		if (gridOptions.api.getDisplayedRowCount() <= 0) {
+			mcxDialog.alert("조회된 데이터가 없습니다. 조회 후 다운로드 하세요.");
+			return false;
+		}
+		
+		gagaAgGrid.exportToExcel('한세ERP매출반영 목록', gridOptions);
+	});
+	
+	$(document).ready(function() {
+		gagajf.setDate('#terms', 'startDt', 'endDt', 't');
+		
+		// Create a agGrid
+		gagaAgGrid.createGrid('gridList', gridOptions);
+	});
+/*]]>*/
+</script>
+
+</html>

+ 122 - 0
src/main/webapp/WEB-INF/views/settle/HansaeStyleMappingForm.html

@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html lang="ko"
+	xmlns:th="http://www.thymeleaf.org">
+<!--
+ *******************************************************************************
+ * @source  : HansaeStyleMappingForm.html
+ * @desc    : 한세ERP스타일매핑 팝업 Page
+ *============================================================================
+ * STYLE24
+ * Copyright(C) 2020 TSIT, All rights reserved.
+ *============================================================================
+ * VER  DATE         AUTHOR      DESCRIPTION
+ * ===  ===========  ==========  =============================================
+ * 1.0  2021.11.01   gagamel     최초 작성
+ *******************************************************************************
+ -->
+<div class="modalPopup" data-width="900" id="popupHansaeStyleMapping">
+	<div class="panelStyle">
+		<!-- TITLE -->
+		<div class="panelTitle">
+			<strong>한세ERP 스타일 매핑</strong>
+			<button type="button" class="close" onclick="uifnPopupClose('popupHansaeStyleMapping');"><em class="fa fa-times"></em></button>
+		</div>
+		<!-- //TITLE -->
+		
+		<!-- CONTENT -->
+		<div class="panelContent">
+			<form id="styleMappingForm" name="styleMappingForm" action="#" th:action="@{'/settle/hansae/style/mapping/save'}" th:method="post">
+				<table class="frmStyle" aria-describedby="상세폼">
+					<colgroup>
+						<col style="width:10%;"/>
+						<col/>
+						<col style="width:10%;"/>
+						<col style="width:15%;"/>
+						<col style="width:10%;"/>
+						<col style="width:15%;"/>
+					</colgroup>
+					<tbody>
+						<tr>
+							<th>ERP구분</th>
+							<td colspan="5">
+								<select name="erpGb">
+									<option value="hsmk" th:selected="${styleInfo.erpGb == 'hsmk'}">[hsmk] 한세MK</option>
+									<option value="hsdr" th:selected="${styleInfo.erpGb == 'hsdr'}">[hsdr] 한세드림</option>
+								</select>
+							</td>
+						</tr>
+						<tr>
+							<th>스타일코드</th>
+							<td>
+								<input type="text" name="cdStyle" maxlength="20" readonly="readonly" th:value="${styleInfo.cdStyle}"/>
+							</td>
+							<th>색상코드</th>
+							<td>
+								<input type="text" name="cdColor" maxlength="20" readonly="readonly" th:value="${styleInfo.cdColor}"/>
+							</td>
+							<th>사이즈코드</th>
+							<td>
+								<input type="text" name="cdSize" maxlength="20" readonly="readonly" th:value="${styleInfo.cdSize}"/>
+							</td>
+						</tr>
+						<tr>
+							<th>ERP스타일코드<i class="required" title="필수" aria-hidden="true"></i></th>
+							<td>
+								<input type="text" name="erpCdStyle" maxlength="20" th:value="${styleInfo?.erpCdStyle}" required="required" data-valid-name="ERP스타일코드"/>
+							</td>
+							<th>ERP색상코드<i class="required" title="필수" aria-hidden="true"></i></th>
+							<td>
+								<input type="text" name="erpCdColor" maxlength="20" th:value="${styleInfo?.erpCdColor}" required="required" data-valid-name="ERP색상코드"/>
+							</td>
+							<th>ERP사이즈코드<i class="required" title="필수" aria-hidden="true"></i></th>
+							<td>
+								<input type="text" name="erpCdSize" maxlength="20" th:value="${styleInfo?.erpCdSize}" required="required" data-valid-name="ERP사이즈코드"/>
+							</td>
+						</tr>
+						<tr>
+							<th>실패메시지</th>
+							<td colspan="5" th:utext="${#strings.replace(#strings.replace(styleInfo.dsError,'&amplt;','<'),'&ampgt;','>')}"></td>
+						</tr>
+					</tbody>
+				</table>
+			</form>
+		</div>
+		<!-- //CONTENT -->
+
+		<!-- 버튼 배치 영역 -->
+		<ul class="panelBar">
+			<li class="right">
+				<button type="button" class="btn btn-info btn-lg" id="btnSaveStyleMapping">저장</button>
+			</li>
+		</ul>
+		<!-- //버튼 배치 영역 -->
+	</div>
+</div>
+
+<script th:inline="javascript">
+/*<![CDATA[*/
+	// 저장
+	$('#btnSaveStyleMapping').on('click', function() {
+		// 입력 값 체크
+		if (!gagajf.validation('#styleMappingForm'))
+			return false;
+		
+		mcxDialog.confirm("저장하시겠습니까?", {
+			cancelBtnText: "취소",
+			sureBtnText: "확인",
+			sureBtnClick: function() {
+				gagajf.ajaxFormSubmit($('#styleMappingForm').prop('action'), '#styleMappingForm', function() {
+					uifnPopupClose('popupHansaeStyleMapping');
+					$('#btnSearch').trigger('click');
+				});
+			}
+		});
+	});
+	
+	$(document).ready(function() {
+		
+	});
+/*]]>*/
+</script>
+
+</html>

+ 98 - 4
src/main/webapp/WEB-INF/views/statistics/DailyTradingForm.html

@@ -35,8 +35,19 @@
 					</colgroup>
 					<tr>
 						<th>발생일<i class="required" title="필수" aria-hidden="true"></i></th>
-						<td>
-							<input type="text" class="schDate w80" name="occurDt" maxlength="10" th:value="${#calendars.format(#calendars.createNow(), 'yyyy-MM-dd')}" required="required" data-valid-type="calendar" data-valid-name="발생일"/>
+						<td id="terms">
+							<span class="nowrap">
+								<input name="startDt" id="startDt" type="text" class="w80 schDate" maxlength="8" required="required" data-valid-name="발생시작일"/>
+								~
+								<input name="endDt" id="endDt" type="text" class="w80 schDate" maxlength="8" required="required" data-valid-name="발생종료일"/>
+							</span>
+							<button type="button" class="btn btn-default btn-sm btnToday" onclick="gagajf.setDate('#terms', 'startDt', 'endDt', 't');">오늘</button>
+							<button type="button" class="btn btn-default btn-sm btnYesterday" onclick="gagajf.setDate('#terms', 'startDt', 'endDt', 'y');">어제</button>
+							<button type="button" class="btn btn-default btn-sm" onclick="gagajf.setDate('#terms', 'startDt', 'endDt', '7d');">최근한주</button>
+							<button type="button" class="btn btn-default btn-sm" onclick="gagajf.setDate('#terms', 'startDt', 'endDt', 'tw');">이번주</button>
+							<button type="button" class="btn btn-default btn-sm" onclick="gagajf.setDate('#terms', 'startDt', 'endDt', 'pw');">지난주</button>
+							<span class="infoTxt cRed marL10"><i class="fa fa-info-circle" aria-hidden="true"></i>(일주일 한도)</span>
+<!-- 							<input type="text" class="schDate w80" name="occurDt" maxlength="10" th:value="${#calendars.format(#calendars.createNow(), 'yyyy-MM-dd')}" required="required" data-valid-type="calendar" data-valid-name="발생일"/> -->
 						</td>
 					</tr>
 				</table>
@@ -178,7 +189,7 @@
 		},
 		{
 			headerName: "수수료율(%)", field: "sellFeeRate", width: 120, cellClass: 'text-center',
-			cellRenderer: function (params) { return params.value + '%'; }
+			cellRenderer: function (params) { return gagajf.isNull(params.value) ? '' : params.value + '%'; }
 		},
 		{
 			headerName: "수수료", field: "sellFeeAmt", width: 100, cellClass: 'text-right',
@@ -196,15 +207,98 @@
 		if (!gagajf.validation($('#searchForm')))
 			return false;
 		
-		gagaAgGrid.fetch($('#searchForm').prop('action'), gridOptions, '#searchForm');
+		var fromDate = $('#searchForm input[name=startDt]').val().toDate('YYYY-MM-DD');
+		var toDate = $('#searchForm input[name=endDt]').val().toDate('YYYY-MM-DD');
+		
+		if (fromDate > toDate) {
+			mcxDialog.alert("시작일자는 종료일자 보다 클 수 없습니다.");
+			$('#searchForm input[name=endDt]').focus();
+			return false;
+		}
+		
+		var iDays = Math.ceil((toDate - fromDate) / (60 * 60 * 24 * 1000));
+		if (iDays >= 7) {
+			mcxDialog.alert("최대 7일까지 조회할 수 있습니다.");
+			return;
+		}
+		
+		gagaAgGrid.fetch($('#searchForm').prop('action'), gridOptions, '#searchForm', fnCreateTotal);
 	});
 	
+	// 합계 생성
+	var fnCreateTotal = function() {
+// 		var sellPrice = 0;           // 판매가
+		var sellQty = 0;             // 판매수량
+		var sellAmt = 0;             // 판매금액
+		var cpnDcAmt = 0;            // 쿠폰할인금액
+		var cpn1DcAmt = 0;           // 즉시사용쿠폰금액
+		var goodsCpnDcAmt = 0;       // 상품쿠폰사용금액
+		var cartCpnDcAmt = 0;        // 주문서쿠폰사용금액
+		var pntDcAmt = 0;            // 포인트사용금액
+		var tmtbDcAmt = 0;           // 다다익선할인금액
+		var selfTmtbDcAmt = 0;       // 자사다다익선분담액
+		var supplyCompTmtbDcAmt = 0; // 입점다다익선분담액
+		var gfcdUseAmt = 0;          // 상품권사용금액
+		var selfCpnDcAmt = 0;        // 자사쿠폰분담액
+		var supplyCompCpnDcAmt = 0;  // 입점쿠폰분담액
+		var realSellPrice = 0;       // 실판매가
+ 		var realSellAmt = 0;         // 실판매금액
+		var sellFeeAmt = 0;          // 수수료
+		var settleAmt = 0;           // 정산대상액
+
+		gridOptions.api.forEachNode(function(rowNode, index) {
+			if (!rowNode.group) {
+// 				if (typeof rowNode.data.sellPrice =='number') { sellPrice += rowNode.data.sellPrice; }
+				if (typeof rowNode.data.sellQty =='number') { sellQty += rowNode.data.sellQty; }
+				if (typeof rowNode.data.sellAmt =='number') { sellAmt += rowNode.data.sellAmt; }
+				if (typeof rowNode.data.cpnDcAmt =='number') { cpnDcAmt += rowNode.data.cpnDcAmt; }
+				if (typeof rowNode.data.cpn1DcAmt =='number') { cpn1DcAmt += rowNode.data.cpn1DcAmt; }
+				if (typeof rowNode.data.goodsCpnDcAmt =='number') { goodsCpnDcAmt += rowNode.data.goodsCpnDcAmt; }
+				if (typeof rowNode.data.cartCpnDcAmt =='number') { cartCpnDcAmt += rowNode.data.cartCpnDcAmt; }
+				if (typeof rowNode.data.pntDcAmt =='number') { pntDcAmt += rowNode.data.pntDcAmt; }
+				if (typeof rowNode.data.tmtbDcAmt =='number') { tmtbDcAmt += rowNode.data.tmtbDcAmt; }
+				if (typeof rowNode.data.selfTmtbDcAmt =='number') { selfTmtbDcAmt += rowNode.data.selfTmtbDcAmt; }
+				if (typeof rowNode.data.supplyCompTmtbDcAmt =='number') { supplyCompTmtbDcAmt += rowNode.data.supplyCompTmtbDcAmt; }
+				if (typeof rowNode.data.gfcdUseAmt =='number') { gfcdUseAmt += rowNode.data.gfcdUseAmt; }
+				if (typeof rowNode.data.selfCpnDcAmt =='number') { selfCpnDcAmt += rowNode.data.selfCpnDcAmt; }
+				if (typeof rowNode.data.supplyCompCpnDcAmt =='number') { supplyCompCpnDcAmt += rowNode.data.supplyCompCpnDcAmt; }
+				if (typeof rowNode.data.realSellPrice =='number') { realSellPrice += rowNode.data.realSellPrice; }
+				if (typeof rowNode.data.realSellAmt =='number') { realSellAmt += rowNode.data.realSellAmt; }
+				if (typeof rowNode.data.sellFeeAmt =='number') { sellFeeAmt += rowNode.data.sellFeeAmt; }
+				if (typeof rowNode.data.settleAmt =='number') { settleAmt += rowNode.data.settleAmt; }
+			}
+		});
+
+		var data = {
+			ordNo: null, ordDtlNo: null, sellGb: null,
+			mallGbNm: null, extmallOrderId: null, extmallNm: null,
+			afChannelNm: null, afLinkNm: null,
+			distributionGbNm: null, supplyCompNm: null, supplyVendorNm: null,
+			ordDt: null, occurDt: null,
+			brandEnm: null, goodsCd: null, goodsNm: null, itemCd: null, optCd1: null, optCd2: null,
+			itemkindNm: null, itemkindNm1: null, itemkindNm2: null, itemkindNm3: null, itemkindNm4: '합계',
+// 			sellPrice:  sellPrice,
+			sellPrice:  null,
+			sellQty:  sellQty, sellAmt:  sellAmt,
+			cpnDcAmt:  cpnDcAmt, cpn1DcAmt: cpn1DcAmt, goodsCpnDcAmt:  goodsCpnDcAmt, cartCpnDcAmt:  cartCpnDcAmt,
+			pntDcAmt:  pntDcAmt, tmtbDcAmt: tmtbDcAmt, selfTmtbDcAmt:  selfTmtbDcAmt, supplyCompTmtbDcAmt:  supplyCompTmtbDcAmt,
+			gfcdUseAmt:  gfcdUseAmt, selfCpnDcAmt:  selfCpnDcAmt, supplyCompCpnDcAmt:  supplyCompCpnDcAmt,
+			realSellPrice:  realSellPrice, realSellAmt:  realSellAmt,
+			sellFeeRate: null, sellFeeAmt:  sellFeeAmt, settleAmt:  settleAmt,
+			mdNm: null
+		};
+
+		gagaAgGrid.setPinnedRowData(gridOptions, data, 'top');
+	}
+	
 	// 엑셀다운로드
 	$('#btnExcel').on('click', function() {
 		gagaAgGrid.exportToExcel('일일거래내역', gridOptions);
 	});
 	
 	$(document).ready(function() {
+		gagajf.setDate('#terms', 'startDt', 'endDt', 't');
+		
 		// Create a agGrid
 		gagaAgGrid.createGrid('gridList', gridOptions);
 	});