From ea4f0bd4784dc7065540375b10aaacf0934f45a4 Mon Sep 17 00:00:00 2001 From: "han\\hanst" Date: Mon, 15 Dec 2025 15:00:53 +0800 Subject: [PATCH] =?UTF-8?q?CoDelServiceImpl=E6=8B=86=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ecss/controller/CoDelController.java | 34 +- .../ecss/service/CoDelExcelService.java | 50 + .../modules/ecss/service/CoDelService.java | 44 +- .../service/impl/CoDelExcelServiceImpl.java | 3886 +++++++++++++++++ .../ecss/service/impl/CoDelServiceImpl.java | 3537 +-------------- 5 files changed, 4030 insertions(+), 3521 deletions(-) create mode 100644 src/main/java/com/xujie/sys/modules/ecss/service/CoDelExcelService.java create mode 100644 src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelExcelServiceImpl.java diff --git a/src/main/java/com/xujie/sys/modules/ecss/controller/CoDelController.java b/src/main/java/com/xujie/sys/modules/ecss/controller/CoDelController.java index ff6e7941..d395867e 100644 --- a/src/main/java/com/xujie/sys/modules/ecss/controller/CoDelController.java +++ b/src/main/java/com/xujie/sys/modules/ecss/controller/CoDelController.java @@ -4,6 +4,7 @@ import com.xujie.sys.common.utils.PageUtils; import com.xujie.sys.common.utils.R; import com.xujie.sys.modules.ecss.data.*; import com.xujie.sys.modules.ecss.entity.*; +import com.xujie.sys.modules.ecss.service.CoDelExcelService; import com.xujie.sys.modules.ecss.service.CoDelService; import com.xujie.sys.modules.part.entity.PartInformationEntity; import com.xujie.sys.modules.part.entity.PartSubPropertiesValue; @@ -26,6 +27,9 @@ public class CoDelController { @Autowired private CoDelService coDelService; + @Autowired + private CoDelExcelService coDelExcelService; + @Autowired private PartInformationService partInformationService; @@ -62,7 +66,7 @@ public class CoDelController { @PostMapping("/previewExcel") public R previewExcel(@RequestParam(value = "file") MultipartFile file, @ModelAttribute EcssCoDelNotifyHeaderData data){ - Map result = coDelService.previewExcel(file, data); + Map result = coDelExcelService.previewExcel(file, data); return R.ok() .put("data", result.get("previewList")) .put("sheetErrors", result.get("sheetErrors")) @@ -75,13 +79,13 @@ public class CoDelController { @ModelAttribute EcssCoDelNotifyHeaderData data, @RequestParam(value = "deletedInvoices", required = false) String deletedInvoices, HttpServletRequest request){ - Map> resultMap = coDelService.saveEcssCoDelNotifyByExcel(file, data, deletedInvoices, request); + Map> resultMap = coDelExcelService.saveEcssCoDelNotifyByExcel(file, data, deletedInvoices, request); return R.ok().put("resultMap", resultMap); } @PostMapping("/modifyNotifyDetailByExcel") public R modifyNotifyDetailByExcel(@RequestParam(value = "file") MultipartFile file, @ModelAttribute EcssCoDelNotifyHeaderData data){ - coDelService.modifyNotifyDetailByExcel(file, data); + coDelExcelService.modifyNotifyDetailByExcel(file, data); return R.ok(); } @@ -301,7 +305,7 @@ public class CoDelController { public R saveCoDelPalletDataByExcel(@RequestParam(value = "file") MultipartFile file, @RequestParam(value = "palletRecords", required = false) String palletRecords, @ModelAttribute EcssCoDelNotifyHeaderData data){ - coDelService.saveCoDelPalletDataByExcel(file, data, palletRecords); + coDelExcelService.saveCoDelPalletDataByExcel(file, data, palletRecords); return R.ok(); } @@ -451,47 +455,47 @@ public class CoDelController { @PostMapping("/downloadDeclarationElements") public void downloadDeclarationElements(HttpServletResponse response, @RequestBody EcssDeclarationHeaderData data) { - coDelService.downloadDeclarationElements(response, data); + coDelExcelService.downloadDeclarationElements(response, data); } @PostMapping("/downloadDeclaration") public void downloadDeclaration(HttpServletResponse response, @RequestBody EcssDeclarationHeaderData data) { - coDelService.downloadDeclaration(response, data); + coDelExcelService.downloadDeclaration(response, data); } @PostMapping("/downloadInvoice") public void downloadInvoice(HttpServletResponse response, @RequestBody EcssDeclarationHeaderData data) { - coDelService.downloadInvoice(response, data); + coDelExcelService.downloadInvoice(response, data); } @PostMapping("/downloadPackingList") public void downloadPackingList(HttpServletResponse response, @RequestBody EcssDeclarationHeaderData data) { - coDelService.downloadPackingList(response, data); + coDelExcelService.downloadPackingList(response, data); } @PostMapping("/exportPackingTemplate") public void exportPackingTemplate(HttpServletResponse response, @RequestBody EcssCoDelNotifyHeaderData data) { - coDelService.exportPackingTemplate(response, data); + coDelExcelService.exportPackingTemplate(response, data); } @PostMapping("/downloadExportGoods") public void downloadExportGoods(HttpServletResponse response, @RequestBody EcssDeclarationHeaderData data) { - coDelService.downloadExportGoods(response, data); + coDelExcelService.downloadExportGoods(response, data); } @PostMapping("/downloadContract") public void downloadContract(HttpServletResponse response, @RequestBody EcssDeclarationHeaderData data) { - coDelService.downloadContract(response, data); + coDelExcelService.downloadContract(response, data); } @PostMapping("/downloadAll") public void downloadAll(HttpServletResponse response, @RequestBody EcssDeclarationHeaderData data) { - coDelService.downloadAll(response, data); + coDelExcelService.downloadAll(response, data); } @PostMapping("/downloadAllPdf") public void downloadAllPdf(HttpServletResponse response, @RequestBody EcssDeclarationHeaderData data) { - coDelService.downloadAllPdf(response, data); + coDelExcelService.downloadAllPdf(response, data); } @PostMapping("/saveOneClickPacking") @@ -546,7 +550,7 @@ public class CoDelController { @PostMapping("/saveWalMartOrderByExcel") public R saveWalMartOrderByExcel(@RequestParam(value = "file") MultipartFile file, @ModelAttribute EcssWalMartOrder data){ - String msg = coDelService.saveWalMartOrderByExcel(file, data); + String msg = coDelExcelService.saveWalMartOrderByExcel(file, data); return R.ok().put("errorMsg", msg); } @@ -677,7 +681,7 @@ public class CoDelController { } boolean isPreviewOnly = "true".equalsIgnoreCase(previewOnly); - Map result = coDelService.importPartPackageProperties(file, buNo, username, isPreviewOnly); + Map result = coDelExcelService.importPartPackageProperties(file, buNo, username, isPreviewOnly); if (isPreviewOnly) { // 预览模式:返回预览数据 diff --git a/src/main/java/com/xujie/sys/modules/ecss/service/CoDelExcelService.java b/src/main/java/com/xujie/sys/modules/ecss/service/CoDelExcelService.java new file mode 100644 index 00000000..a35d62ec --- /dev/null +++ b/src/main/java/com/xujie/sys/modules/ecss/service/CoDelExcelService.java @@ -0,0 +1,50 @@ +package com.xujie.sys.modules.ecss.service; + +import com.xujie.sys.modules.ecss.data.*; +import com.xujie.sys.modules.ecss.entity.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Map; + +public interface CoDelExcelService { + + + Map previewExcel(MultipartFile file, EcssCoDelNotifyHeaderData data); + Map> saveEcssCoDelNotifyByExcel(MultipartFile file, EcssCoDelNotifyHeaderData data, String deletedInvoices, HttpServletRequest request); + void saveCoDelPalletDataByExcel(MultipartFile file, EcssCoDelNotifyHeaderData data, String palletRecords); + + void downloadDeclarationElements(HttpServletResponse response, EcssDeclarationHeaderData data); + + void downloadDeclaration(HttpServletResponse response, EcssDeclarationHeaderData data); + + void downloadInvoice(HttpServletResponse response, EcssDeclarationHeaderData data); + + void downloadPackingList(HttpServletResponse response, EcssDeclarationHeaderData data); + + void exportPackingTemplate(HttpServletResponse response, EcssCoDelNotifyHeaderData data); + + void downloadExportGoods(HttpServletResponse response, EcssDeclarationHeaderData data); + + void downloadContract(HttpServletResponse response, EcssDeclarationHeaderData data); + + void downloadAll(HttpServletResponse response, EcssDeclarationHeaderData data); + + void downloadAllPdf(HttpServletResponse response, EcssDeclarationHeaderData data); + + String saveWalMartOrderByExcel(MultipartFile file, EcssWalMartOrder data); + + void modifyNotifyDetailByExcel(MultipartFile file, EcssCoDelNotifyHeaderData inData); + + /** + * @Description 导入物料包装属性(每卷数量、每箱卷数、每卷重量、箱重量、箱类型) + * @param file Excel文件 + * @param buNo 业务单元编码 + * @param username 用户名 + * @param previewOnly 是否仅预览 + * @return 预览数据或导入结果 + */ + Map importPartPackageProperties(MultipartFile file, String buNo, String username, boolean previewOnly); +} diff --git a/src/main/java/com/xujie/sys/modules/ecss/service/CoDelService.java b/src/main/java/com/xujie/sys/modules/ecss/service/CoDelService.java index a2cf3113..41ca4ef4 100644 --- a/src/main/java/com/xujie/sys/modules/ecss/service/CoDelService.java +++ b/src/main/java/com/xujie/sys/modules/ecss/service/CoDelService.java @@ -4,16 +4,9 @@ import com.xujie.sys.common.utils.PageUtils; import com.xujie.sys.modules.ecss.data.*; import com.xujie.sys.modules.ecss.entity.*; -import com.xujie.sys.modules.part.entity.ComponentPartData; import com.xujie.sys.modules.part.entity.PartInformationEntity; import com.xujie.sys.modules.part.entity.PartSubPropertiesValue; import com.xujie.sys.modules.part.entity.PartSubPropertiesValueData; -import org.apache.ibatis.annotations.Param; -import org.springframework.web.multipart.MultipartFile; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; import java.util.List; import java.util.Map; @@ -23,8 +16,6 @@ public interface CoDelService { PageUtils searchEcssCoDelNotifyHeaderForCK(EcssCoDelNotifyHeaderData data); PageUtils searchEcssCoDelNotifyHeaderForDanZheng(EcssCoDelNotifyHeaderData data); List searchEcssCoDelNotifyDetail(EcssCoDelNotifyHeaderData data); - Map previewExcel(MultipartFile file, EcssCoDelNotifyHeaderData data); - Map> saveEcssCoDelNotifyByExcel(MultipartFile file, EcssCoDelNotifyHeaderData data, String deletedInvoices, HttpServletRequest request); void updateEcssDelHeader(EcssCoDelNotifyHeaderData data); @@ -84,8 +75,6 @@ public interface CoDelService { //void deleteEmptyBoxAfterDetailDelete(EcssCoDelPalletData detailData); - void saveCoDelPalletDataByExcel(MultipartFile file, EcssCoDelNotifyHeaderData data, String palletRecords); - List searchEcssCoDelPalletHeaderData(EcssCoDelNotifyHeaderData inData); @@ -129,24 +118,6 @@ public interface CoDelService { List searchPackageList(EcssParamData data); - void downloadDeclarationElements(HttpServletResponse response, EcssDeclarationHeaderData data); - - void downloadDeclaration(HttpServletResponse response, EcssDeclarationHeaderData data); - - void downloadInvoice(HttpServletResponse response, EcssDeclarationHeaderData data); - - void downloadPackingList(HttpServletResponse response, EcssDeclarationHeaderData data); - - void exportPackingTemplate(HttpServletResponse response, EcssCoDelNotifyHeaderData data); - - void downloadExportGoods(HttpServletResponse response, EcssDeclarationHeaderData data); - - void downloadContract(HttpServletResponse response, EcssDeclarationHeaderData data); - - void downloadAll(HttpServletResponse response, EcssDeclarationHeaderData data); - - void downloadAllPdf(HttpServletResponse response, EcssDeclarationHeaderData data); - List getNotifyPartDetail(EcssDeclarationHeaderData inData); void saveOneClickPacking(EcssCoDelPalletHeaderData inData); @@ -165,8 +136,6 @@ public interface CoDelService { PageUtils searchWalMartOrderData(EcssWalMartOrder data); - String saveWalMartOrderByExcel(MultipartFile file, EcssWalMartOrder data); - void saveWalMartOrderData(EcssWalMartOrder data); void deleteWalMartOrder(EcssWalMartOrder data); @@ -175,10 +144,8 @@ public interface CoDelService { PageUtils queryPartListAll(PartInformationEntity data); - void modifyNotifyDetailByExcel(MultipartFile file, EcssCoDelNotifyHeaderData inData); - List> getCustomerTemplateList(Map params); - + /** * @Description 根据site、buNo、partNo获取物料包装属性(每卷数量、每箱卷数、箱重量) * @param site 站点 @@ -188,13 +155,4 @@ public interface CoDelService { */ Map getPartPackageProperties(String site, String buNo, String partNo); - /** - * @Description 导入物料包装属性(每卷数量、每箱卷数、每卷重量、箱重量、箱类型) - * @param file Excel文件 - * @param buNo 业务单元编码 - * @param username 用户名 - * @param previewOnly 是否仅预览 - * @return 预览数据或导入结果 - */ - Map importPartPackageProperties(MultipartFile file, String buNo, String username, boolean previewOnly); } diff --git a/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelExcelServiceImpl.java b/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelExcelServiceImpl.java new file mode 100644 index 00000000..b0459235 --- /dev/null +++ b/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelExcelServiceImpl.java @@ -0,0 +1,3886 @@ +package com.xujie.sys.modules.ecss.service.impl; + + +import com.aspose.cells.*; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.xujie.sys.common.utils.DateUtils; +import com.xujie.sys.common.utils.ExcelTemplate; +import com.xujie.sys.common.utils.MailUtil; +import com.xujie.sys.modules.ecss.data.*; +import com.xujie.sys.modules.ecss.dto.SheetErrorInfo; +import com.xujie.sys.modules.ecss.entity.*; +import com.xujie.sys.modules.ecss.mapper.CoDelMapper; +import com.xujie.sys.modules.ecss.service.CoDelExcelService; +import com.xujie.sys.modules.orderIssure.entity.PartData; +import com.xujie.sys.modules.part.entity.*; +import com.xujie.sys.modules.pms.data.MailSendAddressData; +import com.xujie.sys.modules.pms.data.SendMailRecord; +import com.xujie.sys.modules.pms.mapper.QcMapper; +import com.xujie.sys.modules.sys.entity.SysUserEntity; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +import org.apache.ibatis.session.SqlSession; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.shiro.SecurityUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Service +@Slf4j +public class CoDelExcelServiceImpl implements CoDelExcelService { + @Autowired + private QcMapper qcMapper; + @Autowired + private CoDelMapper coDelMapper; + @Autowired + SqlSession sqlSession; + + @Override + public Map previewExcel(MultipartFile file, EcssCoDelNotifyHeaderData inData) { + SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); + String site = coDelMapper.getSiteByBu(inData.getBuNo()); + List excelList = new ArrayList<>(); + List sheetErrors = new ArrayList<>(); + + try (InputStream is = file.getInputStream()) { + XSSFWorkbook workbook = new XSSFWorkbook(is); + importNotifyExcel(inData, workbook, site, currentUser, excelList, sheetErrors); + } catch (Exception e) { + throw new RuntimeException("文件解析失败:" + e.getMessage()); + } + + // 检查物料存在性 + Map> invalidMaterialsByInvoice = checkMaterialsExistence(excelList, site, currentUser.getUsername(), inData.getBuNo()); + + // 按发票号分组并汇总数据 + Map> groupedByInvoice = excelList.stream() + .collect(Collectors.groupingBy(EcssCoDelNotifyData::getCmcInvoice)); + + List> previewList = new ArrayList<>(); + + groupedByInvoice.forEach((invoice, dataList) -> { + Map summary = new HashMap<>(); + summary.put("cmcInvoice", invoice); + + // 格式化日期为字符串 + Date readyDate = dataList.get(0).getReadyDate(); + String formattedDate = readyDate != null ? DateUtils.format(readyDate, "yyyy-MM-dd") : ""; + summary.put("readyDate", formattedDate); + summary.put("totalQty", dataList.stream() + .map(EcssCoDelNotifyData::getQty) + .reduce(BigDecimal.ZERO, BigDecimal::add)); + summary.put("totalItems", dataList.size()); + summary.put("destination", dataList.get(0).getDestination()); + summary.put("shippingMode", dataList.get(0).getShippingMode()); + + // 检查是否已存在,如果已存在则跳过 + List existingHeaders = coDelMapper.checkIfHasHeader(invoice); + if (!existingHeaders.isEmpty()) { + return; // 跳过已存在的发票号 + } + + // 检查物料存在性 + List invalidMaterials = invalidMaterialsByInvoice.get(invoice); + boolean hasInvalidMaterials = invalidMaterials != null && !invalidMaterials.isEmpty(); + summary.put("hasInvalidMaterials", hasInvalidMaterials); + + if (hasInvalidMaterials) { + summary.put("invalidMaterials", invalidMaterials); + summary.put("invalidMaterialsText", "不存在的物料:" + String.join("、", invalidMaterials)); + } + + // 设置状态信息 + if (hasInvalidMaterials) { + summary.put("status", "物料不存在"); + summary.put("statusType", "error"); + } else { + summary.put("status", "可导入"); + summary.put("statusType", "success"); + } + + previewList.add(summary); + }); + + // 构建返回结果,包含预览数据和Sheet错误信息 + Map result = new HashMap<>(); + result.put("previewList", previewList); + result.put("sheetErrors", sheetErrors); + result.put("hasErrors", !sheetErrors.isEmpty()); + result.put("errorCount", sheetErrors.size()); + + return result; + } + + @Override + @Transactional + public Map> saveEcssCoDelNotifyByExcel(MultipartFile file, EcssCoDelNotifyHeaderData inData, String deletedInvoices, HttpServletRequest request) { + SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); + String site = coDelMapper.getSiteByBu(inData.getBuNo()); + List excelList = new ArrayList<>(); + List sheetErrors = new ArrayList<>(); + + try (InputStream is = file.getInputStream()) { + XSSFWorkbook workbook = new XSSFWorkbook(is); + importNotifyExcel(inData, workbook, site, currentUser, excelList, sheetErrors); + } catch (NullPointerException e) { + log.error("导入失败:{}", e.getMessage()); + throw new RuntimeException("导入失败:网络错误,请重新上传文档"); + } catch (Exception e) { + throw new RuntimeException("导入失败:" + e.getMessage()); + } + + // 处理删除的发票号 + if (deletedInvoices != null && !deletedInvoices.trim().isEmpty()) { + try { + // 解析删除的发票号列表 + List deletedInvoiceList = parseDeletedInvoices(deletedInvoices); + if (!deletedInvoiceList.isEmpty()) { + // 过滤掉被删除的发票号 + excelList = excelList.stream() + .filter(data -> !deletedInvoiceList.contains(data.getCmcInvoice())) + .collect(Collectors.toList()); + log.info("已过滤删除的发票号: {}", deletedInvoiceList); + } + } catch (Exception e) { + log.error("解析删除发票号列表失败: {}", e.getMessage()); + throw new RuntimeException("解析删除发票号列表失败:" + e.getMessage()); + } + } + + // 检查物料存在性并筛选掉包含不存在物料的发票 + Map> invalidMaterialsByInvoice = checkMaterialsExistence(excelList, site, currentUser.getUsername(), inData.getBuNo()); + if (!invalidMaterialsByInvoice.isEmpty()) { + // 过滤掉包含不存在物料的发票 + Set invalidInvoices = invalidMaterialsByInvoice.keySet(); + excelList = excelList.stream() + .filter(data -> !invalidInvoices.contains(data.getCmcInvoice())) + .collect(Collectors.toList()); + + // 记录被筛选掉的发票及其不存在的物料 + for (Map.Entry> entry : invalidMaterialsByInvoice.entrySet()) { + String invoice = entry.getKey(); + List invalidMaterials = entry.getValue(); + log.warn("发票号 {} 包含不存在的物料,已被筛选掉: {}", invoice, invalidMaterials); + } + } + + // 成功和失败列表 + List successList = new ArrayList<>(); + List failList = new ArrayList<>(); + + // 添加Sheet错误信息到失败列表 + for (SheetErrorInfo error : sheetErrors) { + if (error.getSkipped()) { + // 跳过类型的错误,作为警告信息 + failList.add("Sheet [" + error.getSheetName() + "] 已跳过:" + error.getErrorMessage()); + } else { + // 验证失败类型的错误 + failList.add("Sheet [" + error.getSheetName() + "] 错误:" + error.getErrorMessage()); + if (error.getErrorDetails() != null && !error.getErrorDetails().isEmpty()) { + // 限制显示前5个错误详情 + int limit = Math.min(5, error.getErrorDetails().size()); + for (int i = 0; i < limit; i++) { + failList.add(" - " + error.getErrorDetails().get(i)); + } + if (error.getErrorDetails().size() > 5) { + failList.add(" - ... 还有 " + (error.getErrorDetails().size() - 5) + " 个错误"); + } + } + } + } + + // 添加物料不存在的发票到失败列表 + for (Map.Entry> entry : invalidMaterialsByInvoice.entrySet()) { + String invoice = entry.getKey(); + List invalidMaterials = entry.getValue(); + String materialList = String.join("、", invalidMaterials); + failList.add("发票号:" + invoice + " 包含不存在的物料:" + materialList); + } + + // 使用 groupingBy 分组 + Map> groupedByReadyDateAndCmcInvoice = excelList.stream() + .collect(Collectors.groupingBy(data -> data.getReadyDate() + "-" + data.getCmcInvoice())); + + // 每个分组创建一个销售发货单 + groupedByReadyDateAndCmcInvoice.forEach((key, list) -> { + String cmcInvoice = list.get(0).getCmcInvoice(); + String transNo; + + // 检查是否已经存在 + List checkIfHasHeader = coDelMapper.checkIfHasHeader(cmcInvoice); + if (!checkIfHasHeader.isEmpty()) { + //failList.add("发票号:" + cmcInvoice + " 已经生成发货通知单"); + return; // 跳过当前分组,继续下一个 + } + + try { + // 新建头 + EcssCoDelNotifyHeader headerList = new EcssCoDelNotifyHeader(); + coDelMapper.updateTransNo(list.get(0).getSite(), "EC"); + transNo = coDelMapper.getTransNo(list.get(0).getSite(), "EC"); + + headerList.setDelNo(transNo); + headerList.setSite(list.get(0).getSite()); + headerList.setBuNo(list.get(0).getBuNo()); + headerList.setCustomerName(list.get(0).getCustomerName()); + headerList.setDestination(list.get(0).getDestination()); + headerList.setNotifyStatus(list.get(0).getNotifyStatus()); + headerList.setReadyDate(list.get(0).getReadyDate()); + headerList.setShippingMode(list.get(0).getShippingMode()); + + // 如果有按发票号分别设置的客户信息,则使用该信息;否则使用全局信息 + String customerNameKey = "customerName_" + cmcInvoice; + String localShipAddressKey = "localShipAddress_" + cmcInvoice; + String overseasShipperKey = "overseasShipper_" + cmcInvoice; + String overseasAddressKey = "overseasAddress_" + cmcInvoice; + String cnativeKey = "cnative_" + cmcInvoice; + String salesAreaKey = "salesArea_" + cmcInvoice; + + // 从请求参数中获取按发票号设置的客户信息 + String customerName = request.getParameter(customerNameKey); + String localShipAddress = request.getParameter(localShipAddressKey); + String overseasShipper = request.getParameter(overseasShipperKey); + String overseasAddress = request.getParameter(overseasAddressKey); + String cnative = request.getParameter(cnativeKey); + String salesArea = request.getParameter(salesAreaKey); + + // 如果按发票号的信息不存在,则使用全局信息 + headerList.setCustomerName(customerName != null ? customerName : inData.getCustomerName()); + headerList.setLocalShipAddress(localShipAddress != null ? localShipAddress : inData.getLocalShipAddress()); + headerList.setOverseasShipper(overseasShipper != null ? overseasShipper : inData.getOverseasShipper()); + headerList.setOverseasAddress(overseasAddress != null ? overseasAddress : inData.getOverseasAddress()); + headerList.setCnative(cnative != null ? cnative : inData.getCnative()); + headerList.setSalesArea(salesArea != null ? salesArea : inData.getSalesArea()); + headerList.setCmcInvoice(cmcInvoice); + headerList.setCreateBy(inData.getUsername()); + coDelMapper.saveEcssCoDelNotifyHeader(headerList); + + // 明细 + for (int i = 0; i < list.size(); i++) { + list.get(i).setDelNo(transNo); + list.get(i).setItemNo(i + 1); + } + coDelMapper.batchSaveEcssCoDelNotifyDetail(list); + + // 加入成功列表 + successList.add("发票号:" + cmcInvoice + " 导入成功"); + } catch (Exception e) { + failList.add("发票号:" + cmcInvoice + " 导入失败,原因:" + e.getMessage()); + } + }); + + // 返回结果 + Map> resultMap = new HashMap<>(); + resultMap.put("success", successList); + resultMap.put("fail", failList); + return resultMap; + } + + + /** + * 安全获取字符串列值(支持可选列) + * 如果列不存在,返回空字符串 + * + * @param row 数据行 + * @param columnMap 列索引映射 + * @param columnName 列名 + * @return 单元格字符串值,如果列不存在返回空字符串 + */ + private String getStringCellValueSafe(XSSFRow row, Map columnMap, String columnName) { + Integer columnIndex = columnMap.get(columnName); + if (columnIndex == null) { + return ""; // 列不存在,返回空字符串 + } + return getStringCellValue(row, columnIndex); + } + + /** + * 安全获取数字列值(支持可选列) + * 如果列不存在,返回null + * + * @param row 数据行 + * @param columnMap 列索引映射 + * @param columnName 列名 + * @return 单元格数字值,如果列不存在返回null + */ + private BigDecimal getNumericCellValueSafe(XSSFRow row, Map columnMap, String columnName) { + Integer columnIndex = columnMap.get(columnName); + if (columnIndex == null) { + return null; // 列不存在,返回null + } + return getNumericCellValueOrDefault(row, columnIndex); + } + + /** + * 判断单元格值是否有效(通用版本) + * 无效值包括:空值、"0"、Excel错误(#REF!、#N/A、#VALUE!等) + * + * @param cellValue 单元格字符串值 + * @return true=有效值,false=无效值 + */ + private boolean isValidCellValue(String cellValue) { + // 1. 空值或空字符串 + if (StringUtils.isBlank(cellValue)) { + return false; + } + + String trimmedValue = cellValue.trim(); + + // 2. 值为"0" + if ("0".equals(trimmedValue)) { + return false; + } + + // 3. Excel错误值(以#开头,如#REF!、#N/A、#VALUE!、#DIV/0!、#NAME?、#NUM!、#NULL!等) + return !trimmedValue.startsWith("#"); + + // 其他情况视为有效值 + } + + /** + * 判断CMC Invoice是否有效 + * 无效值包括:空值、"0"、Excel错误、长度超过20位 + * + * @param cellValue 单元格字符串值 + * @return true=有效值,false=无效值 + */ + private boolean isValidCmcInvoice(String cellValue) { + // 先进行通用验证 + if (!isValidCellValue(cellValue)) { + return false; + } + + // CMC Invoice特殊验证:长度不能超过20位 + String trimmedValue = cellValue.trim(); + return trimmedValue.length() <= 20; + } + + /** + * 解析Excel第一行,建立列名到列索引的映射关系 + * + * @param headerRow 第一行(表头行) + * @param sheetName Sheet名称(用于错误提示) + * @return 列名到列索引的映射Map + */ + private Map parseExcelHeader(XSSFRow headerRow, String sheetName) { + Map columnIndexMap = new HashMap<>(); + if (headerRow == null) { + throw new RuntimeException("Sheet [" + sheetName + "] 表头行不能为空!"); + } + + // 遍历第一行的所有单元格,建立列名到索引的映射 + int lastCellNum = headerRow.getLastCellNum(); + for (int i = 0; i < lastCellNum; i++) { + String columnName = getStringCellValue(headerRow, i); + if (StringUtils.isNotBlank(columnName)) { + // 去除空格并统一格式 + columnName = columnName.trim(); + columnIndexMap.put(columnName, i); + } + } + + // 注意:这里只解析表头,不验证必填列 + // 必填列的验证应该在确定要处理该 sheet 之后进行(即 Cargo Ready Date 列存在时) + return columnIndexMap; + } + + /** + * 导入通知单Excel + * + * @param inData 输入数据 + * @param workbook Excel工作簿 + * @param site 站点 + * @param currentUser 当前用户 + * @param excelList 解析后的数据列表 + * @param sheetErrors Sheet错误信息列表(收集所有错误) + */ + private void importNotifyExcel(EcssCoDelNotifyHeaderData inData, XSSFWorkbook workbook, String site, + SysUserEntity currentUser, List excelList, + List sheetErrors) { + for (int s = 0; s < workbook.getNumberOfSheets(); s++) { + // 读取工作表 + XSSFSheet sheet = workbook.getSheetAt(s); + String sheetName = sheet.getSheetName(); + SheetErrorInfo currentSheetError = null; // 当前Sheet的错误信息 + + try { + // 剔除空行 + deleteEmptyRow(sheet); + // 获取行数 + int rows = sheet.getPhysicalNumberOfRows(); + + if (rows < 4) { + continue; // 至少需要前2行、表头行(第3行)和一行数据(第4行) + } + + // 获取第三行(表头行) + XSSFRow headerRow = sheet.getRow(2); + if (headerRow == null) { + log.warn("Sheet [{}] 的第3行为空,跳过该Sheet", sheetName); + continue; + } + + // 解析表头行(第3行),获取列索引映射 + Map columnMap = parseExcelHeader(headerRow, sheetName); + + // 获取Cargo Ready Date列索引 + Integer cargoReadyDateIdx = columnMap.get("Cargo Ready Date"); + if (cargoReadyDateIdx == null) { + log.warn("Sheet [{}] 中未找到 'Cargo Ready Date' 列,跳过该Sheet", sheetName); + continue; + } + + // Cargo Ready Date 列存在,说明要处理这个 sheet,验证其他必填列 + String[] requiredColumns = { + "PO#", // 必填 + "PN", // 必填 + "Qty (pcs)", // 必填 + "Destination", // 必填 + "Shipping Mode", // 必填 + "Currency", // 必填 + "TP" // 必填 + }; + + List missingColumns = new ArrayList<>(); + for (String column : requiredColumns) { + if (!columnMap.containsKey(column)) { + missingColumns.add(column); + } + } + + if (!missingColumns.isEmpty()) { + currentSheetError = SheetErrorInfo.createError(sheetName, s, "MISSING_COLUMNS", + "表头缺少必填列: " + String.join(", ", missingColumns)); + sheetErrors.add(currentSheetError); + log.error("Sheet [{}] 表头缺少必填列,跳过该Sheet", sheetName); + continue; // 不抛异常,记录错误后跳过 + } + + // 扫描整个 sheet,如果发现"内销"则跳过该 sheet + boolean hasInternalSale = false; + for (int scanRow = 0; scanRow < rows; scanRow++) { + XSSFRow checkRow = sheet.getRow(scanRow); + if (checkRow == null) { + continue; + } + + // 遍历该行所有单元格 + int lastCellNum = checkRow.getLastCellNum(); + for (int scanCol = 0; scanCol < lastCellNum; scanCol++) { + XSSFCell cell = checkRow.getCell(scanCol); + if (cell != null && cell.getCellType() != CellType.BLANK) { + try { + String cellValue = getStringCellValue(checkRow, scanCol); + if (cellValue != null && cellValue.contains("内销")) { + hasInternalSale = true; + log.info("Sheet [{}] 在第{}行第{}列发现\"内销\",跳过该Sheet", sheetName, scanRow + 1, scanCol + 1); + break; + } + } catch (Exception e) { + // 忽略单元格读取异常,继续扫描其他单元格 + log.debug("Sheet [{}] 第{}行第{}列读取失败: {}", sheetName, scanRow + 1, scanCol + 1, e.getMessage()); + } + } + } + + if (hasInternalSale) { + break; + } + } + + // 如果发现内销,跳过该 sheet + if (hasInternalSale) { + continue; + } + + // 创建当前Sheet的错误收集器(如果还没创建的话) + if (currentSheetError == null) { + currentSheetError = new SheetErrorInfo(); + currentSheetError.setSheetName(sheetName); + currentSheetError.setSheetIndex(s); + currentSheetError.setErrorType("INVALID_DATA"); + currentSheetError.setErrorMessage("数据验证失败"); + currentSheetError.setSkipped(false); + currentSheetError.setErrorDetails(new ArrayList<>()); + } + + boolean hasRowError = false; // 标记当前Sheet是否有行错误 + + // 遍历每一行(从第四行开始,即索引3) + for (int j = 3; j < rows; j++) { + // 获得该行 + XSSFRow row = sheet.getRow(j); + if (row == null) { + continue; + } + + // 检查Cargo Ready Date列,如果没有值则跳过该行 + if (row.getCell(cargoReadyDateIdx) == null + || row.getCell(cargoReadyDateIdx).getCellType() == CellType.BLANK + || row.getCell(cargoReadyDateIdx).getCellType() == CellType.ERROR) { + log.debug("第{}行的Cargo Ready Date列为空,跳过该行", j+1); + continue; + } + + // 跳过内销数据(如果"内外销方式"列存在) + Integer saleTypeIdx = columnMap.get("内外销方式"); + if (saleTypeIdx != null && row.getCell(saleTypeIdx) != null + && getStringCellValue(row, saleTypeIdx).equals("内销")) { + continue; + } + + // 必填字段验证 - Cargo Ready Date已经在上面检查过了 + Integer poIdx = columnMap.get("PO#"); + if (row.getCell(poIdx) == null) { + currentSheetError.addErrorDetail("第" + (j+1) + "行的 [PO#] 列不能为空"); + hasRowError = true; + continue; // 跳过这一行,继续处理下一行 + } + + Integer pnIdx = columnMap.get("PN"); + if (row.getCell(pnIdx) == null) { + currentSheetError.addErrorDetail("第" + (j+1) + "行的 [PN] 列不能为空"); + hasRowError = true; + continue; + } + + Integer qtyIdx = columnMap.get("Qty (pcs)"); + if (row.getCell(qtyIdx) == null) { + currentSheetError.addErrorDetail("第" + (j+1) + "行的 [Qty (pcs)] 列不能为空"); + hasRowError = true; + continue; + } + + Integer destinationIdx = columnMap.get("Destination"); + if (row.getCell(destinationIdx) == null) { + currentSheetError.addErrorDetail("第" + (j+1) + "行的 [Destination] 列不能为空"); + hasRowError = true; + continue; + } + + Integer shippingModeIdx = columnMap.get("Shipping Mode"); + if (row.getCell(shippingModeIdx) == null) { + currentSheetError.addErrorDetail("第" + (j+1) + "行的 [Shipping Mode] 列不能为空"); + hasRowError = true; + continue; + } + + Integer currencyIdx = columnMap.get("Currency"); + if (row.getCell(currencyIdx) == null) { + currentSheetError.addErrorDetail("第" + (j+1) + "行的 [Currency] 列不能为空"); + hasRowError = true; + continue; + } + + Integer tpIdx = columnMap.get("TP"); + if (row.getCell(tpIdx) == null) { + currentSheetError.addErrorDetail("第" + (j+1) + "行的 [TP] 列不能为空"); + hasRowError = true; + continue; + } + + // 创建对象并为对象赋值 + EcssCoDelNotifyData task = new EcssCoDelNotifyData(); + task.setSite(site); // site + task.setBuNo(inData.getBuNo()); // bu + + // 解析日期 + Date readDate; + try { + LocalDate localDate = parseDateCell(row.getCell(cargoReadyDateIdx)); + String formatted = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + readDate = DateUtils.getDateByParten(formatted, "yyyy-MM-dd"); + } catch (Exception e) { + currentSheetError.addErrorDetail("第" + (j+1) + "行的 [Cargo Ready Date] 列格式有误: " + e.getMessage()); + hasRowError = true; + continue; + } + task.setReadyDate(readDate); + + // 使用动态索引读取数据(必填列) + task.setCustomerPO(getStringCellValue(row, poIdx)); + task.setPn(getStringCellValue(row, pnIdx)); + + // 读取Qty (pcs),捕获格式错误 + try { + task.setQty(getNumericCellValueOrDefault(row, qtyIdx, "Qty (pcs)")); + } catch (RuntimeException e) { + currentSheetError.addErrorDetail("第" + (j+1) + "行的 " + e.getMessage()); + hasRowError = true; + continue; + } + + task.setDestination(getStringCellValue(row, destinationIdx)); + task.setShippingMode(getStringCellValue(row, shippingModeIdx)); + task.setCurrency(getStringCellValue(row, currencyIdx)); + + // 读取TP,捕获格式错误 + try { + task.setTp(getNumericCellValueOrDefault(row, tpIdx, "TP")); + } catch (RuntimeException e) { + currentSheetError.addErrorDetail("第" + (j+1) + "行的 " + e.getMessage()); + hasRowError = true; + continue; + } + + // 处理CMC Invoice:如果CMC Invoice为空/0/Excel错误/超过20位则取Shipping Number,如果两者都为空则报错 + String cmcInvoiceValue = getStringCellValueSafe(row, columnMap, "CMC Invoice"); + String shippingNumberValue = getStringCellValueSafe(row, columnMap, "Shipping Number"); + + // 判断CMC Invoice是否为有效值(排除空值、"0"、Excel错误、长度超过20位) + boolean isCmcInvoiceValid = isValidCmcInvoice(cmcInvoiceValue); + // 判断Shipping Number是否为有效值(排除空值、"0"、Excel错误,允许超过20位) + boolean isShippingNumberValid = isValidCellValue(shippingNumberValue); + + if (!isCmcInvoiceValid && !isShippingNumberValid) { + currentSheetError.addErrorDetail("第" + (j+1) + "行的 [CMC Invoice] 和 [Shipping Number] 列不能同时为空"); + hasRowError = true; + continue; + } + + // 如果CMC Invoice无效(空/0/错误/超过20位),则使用Shipping Number + if (!isCmcInvoiceValid) { + task.setCmcInvoice(shippingNumberValue); + } else { + task.setCmcInvoice(cmcInvoiceValue); + } + task.setShippingNumber(shippingNumberValue); + + // 读取可选列(列不存在时返回空值或null) + task.setSalesOrder(getStringCellValueSafe(row, columnMap, "Sales Order")); + task.setLine(getStringCellValueSafe(row, columnMap, "Line#")); + task.setVersion(getStringCellValueSafe(row, columnMap, "Version#")); + task.setSaleslt(getStringCellValueSafe(row, columnMap, "Saleslt")); + task.setStatus(getStringCellValueSafe(row, columnMap, "Status")); + task.setFamily(getStringCellValueSafe(row, columnMap, "Family")); + task.setPartDescription(getStringCellValueSafe(row, columnMap, "Description")); + task.setManufacturerName(getStringCellValueSafe(row, columnMap, "Manufacturer Name")); + task.setCmcComment(getStringCellValueSafe(row, columnMap, "CMC Comment")); + task.setAwbBl(getStringCellValueSafe(row, columnMap, "AWB/ BL#")); + task.setForwarderInfo(getStringCellValueSafe(row, columnMap, "Forwarder Info")); + task.setSo(getStringCellValueSafe(row, columnMap, "SO")); + task.setUpc(getStringCellValueSafe(row, columnMap, "UPC")); + task.setRemark(getStringCellValueSafe(row, columnMap, "备注")); + task.setCategory(getStringCellValueSafe(row, columnMap, "Category")); + task.setQtyRoll(getStringCellValueSafe(row, columnMap, "QTY/ROLL")); + task.setQtyBox(getStringCellValueSafe(row, columnMap, "qty/box")); + task.setSaleType(getStringCellValueSafe(row, columnMap, "内外销方式")); // 内外销 + + task.setLt(getNumericCellValueSafe(row, columnMap, "LT (wks)")); + task.setTtlAmount(getNumericCellValueSafe(row, columnMap, "TTL Amount")); + task.setVat(getNumericCellValueSafe(row, columnMap, "VAT")); + task.setSumPrice(getNumericCellValueSafe(row, columnMap, "价税合计")); // 价税合计 + task.setRoll(getNumericCellValueSafe(row, columnMap, "Roll")); + + BigDecimal carton = getNumericCellValueSafe(row, columnMap, "BOX"); + task.setCarton(carton != null ? carton.setScale(1, RoundingMode.HALF_UP) : null); + + // 查询物料信息 + List parts = coDelMapper.getPartNo(site, task.getPn(), currentUser.getUsername(), inData.getBuNo()); + if (parts.isEmpty()) { + // 物料不存在时,设置 partNo 为 pn,后续会在批量检查阶段被筛选掉 + task.setPartNo(task.getPn()); + } else { + task.setPartNo(parts.get(0).getPartNo()); + } + + if (task.getQty().compareTo(BigDecimal.ZERO) == 0) { + task.setStatus("取消发货"); + } + task.setErpFlag("N"); + task.setNotifyStatus("已计划"); + task.setUsername(inData.getUsername()); + + // 物料存在性检查已移至批量处理阶段,此处不再单独检查 + excelList.add(task); + } + + // 在Sheet处理结束时,如果有行错误,添加到错误列表 + if (hasRowError && currentSheetError != null) { + sheetErrors.add(currentSheetError); + log.error("Sheet [{}] 处理完成,发现 {} 个数据错误", sheetName, currentSheetError.getErrorDetails().size()); + } + + } catch (Exception e) { + // 捕获异常并记录,不中断整个流程 + String errorMsg = e.getMessage(); + SheetErrorInfo errorInfo = SheetErrorInfo.createError(sheetName, s, "PARSE_ERROR", + "Sheet解析失败: " + (errorMsg != null ? errorMsg : e.getClass().getSimpleName())); + sheetErrors.add(errorInfo); + log.error("Sheet [{}] 处理异常: {}", sheetName, errorMsg, e); + // 继续处理下一个Sheet + } + } + + // 汇总日志 + log.info("=== Excel导入完成 === 成功处理 {} 条数据,发现 {} 个Sheet存在问题", excelList.size(), sheetErrors.size()); + } + + public static LocalDate parseDateCell(Cell cell) { + if (cell == null) return null; + + try { + CellType cellType = cell.getCellType(); + + // 处理数值类型(日期格式) + if (cellType == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell)) { + return cell.getDateCellValue().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } + // 处理字符串类型 + else if (cellType == CellType.STRING) { + String val = cell.getStringCellValue().trim(); + if (val.isEmpty()) return null; + return LocalDate.parse(val, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + } + // 处理公式类型 - 获取缓存结果 + else if (cellType == CellType.FORMULA) { + switch (cell.getCachedFormulaResultType()) { + case NUMERIC: + // 公式结果是数值类型且是日期格式 + if (DateUtil.isCellDateFormatted(cell)) { + return cell.getDateCellValue().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } + break; + case STRING: + // 公式结果是字符串类型 + String formulaVal = cell.getStringCellValue().trim(); + if (!formulaVal.isEmpty()) { + return LocalDate.parse(formulaVal, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + } + break; + default: + break; + } + } + } catch (Exception e) { + System.err.println("日期格式解析失败: " + cell.toString()); + } + + return null; // 无法解析,返回 null + } + + + public void sendMailUtil(String textHead,String text,String[] mailAddress,EcssCoDelNotifyHeaderData data) { + MailSendAddressData mailSendData = qcMapper.getSendMailFromAddress(); + MailUtil.sendMail(textHead, text, mailAddress, mailSendData); + // 保存邮件记录 + SendMailRecord mailRecord = new SendMailRecord(); + mailRecord.setSite(data.getSite()); + mailRecord.setBuNo(data.getBuNo()); + mailRecord.setDocumentNo(data.getDelNo()); + mailRecord.setSender(data.getCreateBy()); + mailRecord.setRecipient(Arrays.toString(mailAddress)); + mailRecord.setType("关务系统"); + qcMapper.saveSendMailRecord(mailRecord); + } + + private String getStringCellValue(XSSFRow row, int columnIndex) { + Cell cell = row.getCell(columnIndex); + if (cell == null || cell.getCellType() == CellType.BLANK) { + return ""; + } + + CellType cellType = cell.getCellType(); + + // 处理公式类型 - 获取缓存结果 + if (cellType == CellType.FORMULA) { + switch (cell.getCachedFormulaResultType()) { + case NUMERIC: + // 公式结果是数值类型 - 直接获取数值并转换为字符串 + // 检查是否是日期格式 + if (DateUtil.isCellDateFormatted(cell)) { + DataFormatter formatter = new DataFormatter(); + return formatter.formatCellValue(cell); + } else { + // 普通数值,转换为字符串(去除小数点后的零) + double numericValue = cell.getNumericCellValue(); + // 如果是整数,去掉.0 + if (numericValue == Math.floor(numericValue)) { + return String.valueOf((long) numericValue); + } else { + return String.valueOf(numericValue); + } + } + case STRING: + // 公式结果是字符串类型 + return cell.getStringCellValue(); + case BLANK: + return ""; + case BOOLEAN: + return String.valueOf(cell.getBooleanCellValue()); + default: + return ""; + } + } + + // 处理其他类型使用DataFormatter统一格式化 + DataFormatter formatter = new DataFormatter(); + return formatter.formatCellValue(cell); + } + + public static Integer getIntegerCellValue(XSSFRow row, int columnIndex) { + Cell cell = row.getCell(columnIndex); + if (cell == null || cell.getCellType() == CellType.BLANK) { + return null; + } + + CellType cellType = cell.getCellType(); + + // 处理数值类型 + if (cellType == CellType.NUMERIC) { + return (int) Math.round(cell.getNumericCellValue()); + } + + // 处理字符串类型 + if (cellType == CellType.STRING) { + try { + return Integer.parseInt(cell.getStringCellValue().trim()); + } catch (NumberFormatException e) { + return -1; + } + } + + // 处理公式类型 - 获取缓存结果 + if (cellType == CellType.FORMULA) { + switch (cell.getCachedFormulaResultType()) { + case NUMERIC: + return (int) Math.round(cell.getNumericCellValue()); + case STRING: + try { + return Integer.parseInt(cell.getStringCellValue().trim()); + } catch (NumberFormatException e) { + return -1; + } + default: + return -1; + } + } + + // 其他类型返回-1 + return -1; + } + + /** + * 获取数值单元格的值(无列名版本,保持向后兼容,遇到不支持的类型返回null) + */ + private BigDecimal getNumericCellValueOrDefault(XSSFRow row, int columnIndex) { + return getNumericCellValueOrDefault(row, columnIndex, null); + } + + /** + * 获取数值单元格的值 + * @param row Excel行 + * @param columnIndex 列索引 + * @param columnName 列名(用于错误提示,如 "Qty (pcs)"、"TP" 等必填列会抛出异常,其他列返回null) + * @return BigDecimal值 + */ + private BigDecimal getNumericCellValueOrDefault(XSSFRow row, int columnIndex, String columnName) { + Cell cell = row.getCell(columnIndex); + if (cell == null || cell.getCellType() == CellType.BLANK) { + return null; + } + + // 必填数字列列表 + boolean isRequiredColumn = columnName != null && + ("Qty (pcs)".equalsIgnoreCase(columnName) || "TP".equalsIgnoreCase(columnName)); + + switch (cell.getCellType()) { + case NUMERIC: + BigDecimal value = BigDecimal.valueOf(cell.getNumericCellValue()); + return value.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数 + case STRING: + try { + if (cell.getStringCellValue() == null || cell.getStringCellValue().isEmpty()) { + return null; + } + BigDecimal stringValue = new BigDecimal(cell.getStringCellValue()); + return stringValue.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数 + } catch (NumberFormatException e) { + if (isRequiredColumn) { + throw new RuntimeException("[" + columnName + "] 列无效的数值格式: " + cell.getStringCellValue()); + } + return null; + } + case FORMULA: + // 获取缓存结果 + switch (cell.getCachedFormulaResultType()) { + case NUMERIC: + BigDecimal formulaValue = BigDecimal.valueOf(cell.getNumericCellValue()); + return formulaValue.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数 + case STRING: + try { + if (cell.getStringCellValue() == null || cell.getStringCellValue().isEmpty()) { + return null; + } + BigDecimal stringValue = new BigDecimal(cell.getStringCellValue()); + return stringValue.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数 + } catch (NumberFormatException e) { + if (isRequiredColumn) { + throw new RuntimeException("[" + columnName + "] 列无效的数值格式: " + cell.getStringCellValue()); + } + return null; + } + default: + return null; + } + default: + // 如果是必填列,抛出异常提示 + if (isRequiredColumn) { + throw new RuntimeException("[" + columnName + "] 列不支持的单元格类型: " + cell.getCellType()); + } + // 其他非必填列返回null + return null; + } + } + + @Override + @Transactional + public void modifyNotifyDetailByExcel(MultipartFile file, EcssCoDelNotifyHeaderData inData) { + SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); + String site = coDelMapper.getSiteByBu(inData.getBuNo()); + List excelList = new ArrayList<>(); + List sheetErrors = new ArrayList<>(); + try { + // 转流 + InputStream is = file.getInputStream(); + // 读取工作簿 + XSSFWorkbook workbook = new XSSFWorkbook(is); + importNotifyExcel(inData, workbook, site, currentUser, excelList, sheetErrors); + + // 如果有Sheet错误,抛出异常提示用户 + if (!sheetErrors.isEmpty()) { + StringBuilder errorMsg = new StringBuilder("以下Sheet存在问题:\n"); + for (SheetErrorInfo error : sheetErrors) { + errorMsg.append("Sheet [").append(error.getSheetName()).append("]: ").append(error.getErrorMessage()).append("\n"); + } + throw new RuntimeException(errorMsg.toString()); + } + } catch (NullPointerException e) { + throw new RuntimeException("导入失败:网络错误,请重新上传文档"); + } catch (Exception e) { + throw new RuntimeException("导入失败:" + e.getMessage()); + } + // 过滤掉发票号不一致的数据 + excelList.removeIf(data -> !inData.getCmcInvoice().equals(data.getCmcInvoice())); + + // 检查过滤后是否还有数据 + if (excelList.isEmpty()) { + throw new RuntimeException("导入失败:Excel中没有与发货通知单["+inData.getCmcInvoice()+"]一致的发票号数据"); + } + + List notifyHeader = coDelMapper.checkIfHasHeader(excelList.get(0).getCmcInvoice()); + EcssCoDelNotifyHeaderData headerData; + if (notifyHeader.isEmpty()) { + throw new RuntimeException("发货通知单不存在,发票号:" + excelList.get(0).getCmcInvoice()); + } else { + headerData = notifyHeader.get(0); + } + + // 保存原始头表数据用于比较 + EcssCoDelNotifyHeaderData originalHeaderData = new EcssCoDelNotifyHeaderData(); + originalHeaderData.setReadyDate(headerData.getReadyDate()); + originalHeaderData.setShippingMode(headerData.getShippingMode()); + originalHeaderData.setDestination(headerData.getDestination()); + + List dbinData =coDelMapper.searchEcssCoDelNotifyDetail(headerData); + List dbData = new ArrayList<>(dbinData); + + // 更新头表字段(ReadyDate、ShippingMode、Destination) + if (!excelList.isEmpty()) { + EcssCoDelNotifyData firstExcelData = excelList.get(0); + headerData.setReadyDate(firstExcelData.getReadyDate()); + headerData.setShippingMode(firstExcelData.getShippingMode()); + headerData.setDestination(firstExcelData.getDestination()); + } + + // 先删除全部明细,再新建明细 + coDelMapper.deleteAllEcssDelDetail(headerData); + + // 智能判断改单逻辑:按PN分组汇总数量,避免因顺序变化导致误判 + // 构建原数据Map:按PN分组,计算每个PN的总数量 + Map originalPnQtyMap = new HashMap<>(); + for (EcssCoDelNotifyDetailData detail : dbData) { + String pn = detail.getPn(); + BigDecimal qty = detail.getQty(); + originalPnQtyMap.put(pn, originalPnQtyMap.getOrDefault(pn, BigDecimal.ZERO).add(qty)); + } + + // 构建新数据Map:按PN分组,计算每个PN的总数量 + Map newPnQtyMap = new HashMap<>(); + for (EcssCoDelNotifyData excelData : excelList) { + String pn = excelData.getPn(); + BigDecimal qty = excelData.getQty(); + newPnQtyMap.put(pn, newPnQtyMap.getOrDefault(pn, BigDecimal.ZERO).add(qty)); + } + + // 设置itemNo并判断每一行是否有变化(智能判断:按PN比较总数量) + for (int i = 0; i < excelList.size(); i++) { + excelList.get(i).setDelNo(headerData.getDelNo()); + excelList.get(i).setItemNo(i + 1); + + String pn = excelList.get(i).getPn(); + BigDecimal originalTotalQty = originalPnQtyMap.get(pn); + BigDecimal newTotalQty = newPnQtyMap.get(pn); + + // 判断是否有修改(按PN比较总数量,避免因顺序变化误判) + boolean isModified = false; + boolean isQtyModified = false; + + if (originalTotalQty == null) { + // 新增PN:原数据中不存在该PN + isModified = true; + isQtyModified = true; + log.info("检测到新增PN: {}, 数量: {}", pn, newTotalQty); + } else if (originalTotalQty.compareTo(newTotalQty) != 0) { + // 数量变更:PN存在但总数量不同 + isModified = true; + isQtyModified = true; + log.info("检测到PN数量变更: {}, 原数量: {}, 新数量: {}", pn, originalTotalQty, newTotalQty); + } + // 其他情况:PN和总数量都相同,无变化,isModified保持false + + // 只有真正有变化的才标记modifyFlag=true + excelList.get(i).setModifyFlag(isModified); + + // 设置数量变化标记 + if (isQtyModified) { + excelList.get(i).setModifyQtyFlag(true); + } + } + + // 检查是否有PN被删除(在原数据中存在但新数据中不存在) + boolean hasPnDeleted = false; + for (String originalPn : originalPnQtyMap.keySet()) { + if (!newPnQtyMap.containsKey(originalPn)) { + log.info("检测到PN被删除: {}, 原数量: {}", originalPn, originalPnQtyMap.get(originalPn)); + hasPnDeleted = true; + } + } + + // 检查是否有PN或Qty的修改 + boolean hasPnOrQtyModified = excelList.stream() + .anyMatch(item -> item.getModifyQtyFlag() != null && item.getModifyQtyFlag()); + + coDelMapper.batchSaveEcssCoDelNotifyDetail(excelList); + headerData.setModifyFlag(true); + // 更新头表字段,包括ReadyDate、ShippingMode、Destination + coDelMapper.updateEcssDelHeader(headerData); + List newData =coDelMapper.searchEcssCoDelNotifyDetail(headerData); + + // 构建邮件内容,包含头表字段变更和明细变更 + StringBuilder emailContent = generateModifyEmailContent(originalHeaderData, headerData, dbData, newData); + + String textHead = headerData.getDelNo()+"【发票:"+ headerData.getCmcInvoice()+"】改单"; + String[] mailAddress = coDelMapper.queryUsersByRoleName("关务仓库",headerData.getSite()).stream().map(SysUserEntity::getEmail).toArray(String[]::new); + + // 安全获取创建人邮箱,防止用户不存在或邮箱为空的空指针异常 + SysUserEntity creator = coDelMapper.queryByUserName(headerData.getCreateBy()); + String[] mailAddress2 = (creator != null && creator.getEmail() != null) ? + new String[]{creator.getEmail()} : new String[]{}; + + // 合并 + 去重 + String[] mailAddressAll = Stream.concat(Arrays.stream(mailAddress), Arrays.stream(mailAddress2)) + .filter(Objects::nonNull) // 防止 null + .filter(email -> !email.trim().isEmpty()) // 过滤空字符串 + .distinct() // 去重 + .toArray(String[]::new); + if (mailAddressAll.length>0) { + sendMailUtil(textHead, emailContent.toString(), mailAddressAll, headerData); + } + + // 改单导入后的状态处理逻辑 + // 只有在修改了PN/Qty或删除了PN时才执行状态处理 + if (hasPnOrQtyModified || hasPnDeleted) { + log.info("检测到PN/Qty修改或PN删除,执行状态处理逻辑"); + handleNotifyStatusAfterModify(headerData, true); + } else { + log.info("仅修改了其他字段(Destination、Shipping Mode、Currency、TP等),执行状态处理逻辑但不删除托盘数据"); + handleNotifyStatusAfterModify(headerData, false); + } + } + + /** + * 生成改单邮件内容,包含头表字段变更和明细变更 + */ + private StringBuilder generateModifyEmailContent(EcssCoDelNotifyHeaderData originalHeaderData, + EcssCoDelNotifyHeaderData newHeaderData, + List dbData, + List newData) { + StringBuilder emailContent = new StringBuilder(); + + // HTML头部和样式 + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + + // 邮件标题 + emailContent.append("

发货通知单改单通知

"); + emailContent.append("

发货通知单号:").append(newHeaderData.getDelNo()).append("

"); + emailContent.append("

发票号:").append(newHeaderData.getCmcInvoice()).append("

"); + emailContent.append("

操作时间:").append(DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss")).append("

"); + + // 检查头表字段变更 + StringBuilder headerChanges = new StringBuilder(); + boolean hasHeaderChanges = false; + + // 检查ReadyDate变更 + if (!Objects.equals(originalHeaderData.getReadyDate(), newHeaderData.getReadyDate())) { + hasHeaderChanges = true; + String originalDate = originalHeaderData.getReadyDate() != null ? + DateUtils.format(originalHeaderData.getReadyDate(), "yyyy-MM-dd") : "无"; + String newDate = newHeaderData.getReadyDate() != null ? + DateUtils.format(newHeaderData.getReadyDate(), "yyyy-MM-dd") : "无"; + headerChanges.append("
  • Ready Date: ") + .append(originalDate).append(" → ").append(newDate).append("
  • "); + } + + // 检查ShippingMode变更 + if (!Objects.equals(originalHeaderData.getShippingMode(), newHeaderData.getShippingMode())) { + hasHeaderChanges = true; + String originalMode = originalHeaderData.getShippingMode() != null ? originalHeaderData.getShippingMode() : "无"; + String newMode = newHeaderData.getShippingMode() != null ? newHeaderData.getShippingMode() : "无"; + headerChanges.append("
  • Shipping Mode: ") + .append(originalMode).append(" → ").append(newMode).append("
  • "); + } + + // 检查Destination变更 + if (!Objects.equals(originalHeaderData.getDestination(), newHeaderData.getDestination())) { + hasHeaderChanges = true; + String originalDest = originalHeaderData.getDestination() != null ? originalHeaderData.getDestination() : "无"; + String newDest = newHeaderData.getDestination() != null ? newHeaderData.getDestination() : "无"; + headerChanges.append("
  • Destination: ") + .append(originalDest).append(" → ").append(newDest).append("
  • "); + } + + // 如果有头表字段变更,添加到邮件内容 + if (hasHeaderChanges) { + emailContent.append("
    头表字段变更
    "); + emailContent.append("
      ").append(headerChanges).append("
    "); + } + + // 处理明细变更 - 生成对比表格 + emailContent.append("
    明细数据对比
    "); + emailContent.append(generateDetailComparisonTable(dbData, newData)); + + emailContent.append(""); + emailContent.append(""); + + return emailContent; + } + + /** + * 生成明细对比表格 + * @param dbData 原数据 + * @param newData 新数据 + * @return 表格HTML + */ + private String generateDetailComparisonTable(List dbData, + List newData) { + StringBuilder table = new StringBuilder(); + + // 注意:不能用pn作为key,因为同一个pn可能有多行记录 + // 应该用itemNo进行一对一匹配,比较每一行的变化 + Map dbMap = dbData.stream() + .collect(Collectors.toMap(EcssCoDelNotifyDetail::getItemNo, detail -> detail, (v1, v2) -> v1)); + Map newMap = newData.stream() + .collect(Collectors.toMap(EcssCoDelNotifyDetail::getItemNo, detail -> detail, (v1, v2) -> v1)); + + // 获取所有itemNo + Set allItemNos = new HashSet<>(); + allItemNos.addAll(dbMap.keySet()); + allItemNos.addAll(newMap.keySet()); + List sortedItemNos = new ArrayList<>(allItemNos); + sortedItemNos.sort(Integer::compareTo); + + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + + for (Integer itemNo : sortedItemNos) { + EcssCoDelNotifyDetailData oldDetail = dbMap.get(itemNo); + EcssCoDelNotifyDetailData newDetail = newMap.get(itemNo); + + table.append(""); + table.append(""); + + // 判断变更类型 + if (oldDetail == null) { + // 新增行 + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + } else if (newDetail == null) { + // 删除行 + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + } else { + // 判断各个字段的变化 + boolean pnChanged = !oldDetail.getPn().equals(newDetail.getPn()); + boolean qtyChanged = !oldDetail.getQty().equals(newDetail.getQty()); + boolean currencyChanged = !Objects.equals(oldDetail.getCurrency(), newDetail.getCurrency()); + boolean tpChanged = !Objects.equals(oldDetail.getTp(), newDetail.getTp()); + + // 构建变更说明 + List changes = new ArrayList<>(); + if (pnChanged) changes.add("换料"); + if (qtyChanged) changes.add("数量变更"); + if (currencyChanged) changes.add("Currency变更"); + if (tpChanged) changes.add("TP变更"); + String changeDesc = changes.isEmpty() ? "-" : String.join("、", changes); + + // PN列 + if (pnChanged) { + table.append(""); + } else { + table.append(""); + } + + // 数量列 + table.append(""); + if (qtyChanged) { + table.append(""); + } else { + table.append(""); + } + + // Currency列 + String oldCurrency = oldDetail.getCurrency() != null ? oldDetail.getCurrency() : "-"; + String newCurrency = newDetail.getCurrency() != null ? newDetail.getCurrency() : "-"; + table.append(""); + if (currencyChanged) { + table.append(""); + } else { + table.append(""); + } + + // TP列 + String oldTp = oldDetail.getTp() != null ? oldDetail.getTp().setScale(2, RoundingMode.HALF_UP).toString() : "-"; + String newTp = newDetail.getTp() != null ? newDetail.getTp().setScale(2, RoundingMode.HALF_UP).toString() : "-"; + table.append(""); + if (tpChanged) { + table.append(""); + } else { + table.append(""); + } + + // 变更说明列 + if (!changes.isEmpty()) { + table.append(""); + } else { + table.append(""); + } + } + + table.append(""); + } + + table.append("
    项次PN数量(原)数量(新)Currency(原)Currency(新)TP(原)TP(新)变更说明
    ").append(itemNo).append("").append(newDetail.getPn()).append("-").append(newDetail.getQty().setScale(0, RoundingMode.HALF_UP)).append("-").append(newDetail.getCurrency() != null ? newDetail.getCurrency() : "-").append("-").append(newDetail.getTp() != null ? newDetail.getTp().setScale(2, RoundingMode.HALF_UP).toString() : "-").append("新增").append(oldDetail.getPn()).append("").append(oldDetail.getQty().setScale(0, RoundingMode.HALF_UP)).append("-").append(oldDetail.getCurrency() != null ? oldDetail.getCurrency() : "-").append("-").append(oldDetail.getTp() != null ? oldDetail.getTp().setScale(2, RoundingMode.HALF_UP).toString() : "-").append("-已删除").append(oldDetail.getPn()).append(" → ").append(newDetail.getPn()).append("").append(newDetail.getPn()).append("").append(oldDetail.getQty().setScale(0, RoundingMode.HALF_UP)).append("").append(newDetail.getQty().setScale(0, RoundingMode.HALF_UP)).append("").append(newDetail.getQty().setScale(0, RoundingMode.HALF_UP)).append("").append(oldCurrency).append("").append(newCurrency).append("").append(newCurrency).append("").append(oldTp).append("").append(newTp).append("").append(newTp).append("").append(changeDesc).append("").append(changeDesc).append("
    "); + return table.toString(); + } + + @Override + @Transactional + public void saveCoDelPalletDataByExcel(MultipartFile file, EcssCoDelNotifyHeaderData inData, String palletRecords) { + // 先删除已存在的装箱数据 + clearPalletData(inData); + SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); + + List excelList = new ArrayList<>(); + try { + // 转流 + InputStream is = file.getInputStream(); + // 读取工作簿 + XSSFWorkbook workbook = new XSSFWorkbook(is); + // 读取工作表 + XSSFSheet sheet = workbook.getSheetAt(0); + // 剔除空行 + deleteEmptyRow(sheet); + // 获取行数 + int rows = sheet.getPhysicalNumberOfRows(); + // 遍历每一行(从第二行开始) + int groupSeqNo = 0; + BigDecimal boxQty = BigDecimal.ZERO; + BigDecimal grossWeight = BigDecimal.ZERO; + BigDecimal netWeight = BigDecimal.ZERO; + for (int j = 1; j < rows; j++) { + // 创建对象 + EcssCoDelPalletData excelData = new EcssCoDelPalletData(); + // 获得该行 + XSSFRow row = sheet.getRow(j); + if (row.getCell(1) != null && !inData.getCmcInvoice().equals(row.getCell(1).getStringCellValue())) { + continue; + } + if (row.getCell(6) == null) { + throw new RuntimeException("第" + j + "行的PN不能为空!"); + } + if (row.getCell(7) == null) { + throw new RuntimeException("第" + j + "行的数量不能为空!"); + } + // 为对象赋值 + excelData.setSite(inData.getSite()); // site + excelData.setBuNo(inData.getBuNo()); // bu + excelData.setDelNo(inData.getDelNo()); + excelData.setPoNo(getStringCellValue(row, 5)); + excelData.setPn(getStringCellValue(row, 6)); + List parts = coDelMapper.getPartNo(excelData.getSite(), excelData.getPn(),currentUser.getUsername(),inData.getBuNo()); + if (parts.isEmpty()) { + throw new RuntimeException("导入失败:物料:" + excelData.getPn() + "不存在!"); + } + excelData.setPartNo(parts.get(0).getPartNo()); + excelData.setQty(getNumericCellValueOrDefault(row, 7, "Qty (pcs)")); + excelData.setBoxQty(getNumericCellValueOrDefault(row, 2)); + excelData.setRolls(getNumericCellValueOrDefault(row, 8)); + excelData.setGrossWeight(getNumericCellValueOrDefault(row, 3)); + excelData.setNetWeight(getNumericCellValueOrDefault(row, 4)); + if (excelData.getBoxQty() == null && excelData.getGrossWeight() == null && excelData.getNetWeight() == null) { + excelData.setGroupSeqNo(groupSeqNo); + excelData.setBoxQty(boxQty); + excelData.setGrossWeight(grossWeight); + excelData.setNetWeight(netWeight); + } else { + groupSeqNo++; + excelData.setGroupSeqNo(groupSeqNo); + boxQty = excelData.getBoxQty(); + grossWeight = excelData.getGrossWeight(); + netWeight = excelData.getNetWeight(); + } + List checkPart = coDelMapper.checkPart(excelData.getSite(), excelData.getPartNo()); + if (checkPart.isEmpty()) { + throw new RuntimeException("导入失败:物料:" + excelData.getPartNo() + "在当前工厂不存在!"); + } + excelList.add(excelData); + } + } catch (NullPointerException e) { + throw new RuntimeException("导入失败:网络错误,请重新上传文档"); + } catch (Exception e) { + throw new RuntimeException("导入失败:" + e.getMessage()); + } + // 如果是沃尔玛订单,按照pn分组,同一个pn是一个EcssCoDelPalletHeaderData, + // 如果是非沃尔玛订单,按照序号分组,同一个序号是一个EcssCoDelPalletHeaderData, + Map> palletListMap = new HashMap<>(); + Map> palletListMap2 = new HashMap<>(); + for (EcssCoDelPalletData itemData : excelList){ + if (palletListMap2.containsKey(itemData.getGroupSeqNo())) { + palletListMap2.get(itemData.getGroupSeqNo()).add(itemData); + } else { + List palletDataList = new ArrayList<>(); + palletDataList.add(itemData); + palletListMap2.put(itemData.getGroupSeqNo(),palletDataList); + } + } + EcssWalMartOrder task = new EcssWalMartOrder(); + List headerList = new ArrayList<>(); + List boxList = new ArrayList<>(); + List detailList = new ArrayList<>(); + int seqNo = 0; + for (Map.Entry> entry : palletListMap2.entrySet()) { + seqNo++; + EcssCoDelBoxListData boxListData = new EcssCoDelBoxListData(); + boxListData.setSite(inData.getSite()); + boxListData.setBuNo(inData.getBuNo()); + boxListData.setDelNo(inData.getDelNo()); + boxListData.setItemNo(seqNo); + boxListData.setGrossWeight(entry.getValue().get(0).getGrossWeight()); + boxListData.setNetWeight(entry.getValue().get(0).getNetWeight()); + boxListData.setBoxQty(entry.getValue().get(0).getBoxQty()); + boxListData.setCreateBy(currentUser.getUsername()); + boxList.add(boxListData); + int i=0; + for (EcssCoDelPalletData thisData : entry.getValue()) { + EcssCoDelPalletDetailData detailData = new EcssCoDelPalletDetailData(); + detailData.setSite(thisData.getSite()); + detailData.setBuNo(thisData.getBuNo()); + detailData.setDelNo(thisData.getDelNo()); + detailData.setSeqNo(seqNo); + detailData.setItemNo(i + 1); + i++; + detailData.setPartNo(thisData.getPartNo()); + detailData.setPn(thisData.getPn()); + detailData.setQty(thisData.getQty()); + detailData.setPoNo(thisData.getPoNo()); + detailData.setBoxQty(thisData.getBoxQty()); + detailData.setRolls(thisData.getRolls()); + detailData.setCreateBy(currentUser.getUsername()); + detailList.add(detailData); + } + } + for (EcssCoDelBoxListData boxData : boxList) { + coDelMapper.saveCodelBoxList(boxData); + } + Map palletDetailMap = new HashMap<>(); + for (EcssCoDelPalletDetailData ecssCoDelPalletDetailData : detailList) { + coDelMapper.saveCodelPalletDetail(ecssCoDelPalletDetailData); + if (!palletDetailMap.containsKey(ecssCoDelPalletDetailData.getPn())) { + palletDetailMap.put(ecssCoDelPalletDetailData.getPn(), ecssCoDelPalletDetailData.getQty()); + } else { + palletDetailMap.put(ecssCoDelPalletDetailData.getPn(), palletDetailMap.get(ecssCoDelPalletDetailData.getPn()).add(ecssCoDelPalletDetailData.getQty())); + } + } + List ecssCoDelNotifyDetail = coDelMapper.searchEcssCoDelNotifyDetail(inData); + Map notifyDetailMap = new HashMap<>(); + for (EcssCoDelNotifyDetailData detailData : ecssCoDelNotifyDetail) { + if (!notifyDetailMap.containsKey(detailData.getPn())) { + notifyDetailMap.put(detailData.getPn(), detailData.getQty()); + } else { + notifyDetailMap.put(detailData.getPn(), notifyDetailMap.get(detailData.getPn()).add(detailData.getQty())); + } + } + for (Map.Entry entry : palletDetailMap.entrySet()) { + if (notifyDetailMap.get(entry.getKey()).compareTo(entry.getValue())!=0) { + throw new RuntimeException("物料["+entry.getKey()+"]的数量和发货通知单数量不一致!"); + } + } + // 处理栈板记录 + palletHeaderSave(inData.getSite(), inData.getBuNo(), inData.getDelNo(), palletRecords, currentUser); + + //将发货通知单modifyFlag变更为false + inData.setModifyFlag(false); + coDelMapper.updateEcssDelHeaderForModify(inData); + coDelMapper.updateEcssDelDetailForModify(inData); + } + + private void palletHeaderSave(String site,String buNo,String delNo, String palletRecords, SysUserEntity currentUser) { + List> palletRecordList = new ArrayList<>(); + if (palletRecords != null && !palletRecords.isEmpty()) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + palletRecordList = objectMapper.readValue(palletRecords, new TypeReference>>() {}); + } catch (Exception e) { + throw new RuntimeException("栈板记录数据格式错误: " + e.getMessage()); + } + } + /* EcssCoDelPalletHeaderData data = new EcssCoDelPalletHeaderData(); + data.setDelNo(delNo); + data.setSite(site); + data.setBuNo(buNo); + // 先删除已存在的栈板记录 + coDelMapper.deletePalletHeader(data);*/ + EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(site, delNo); + List palletHeaderDataList = coDelMapper.searchEcssCoDelPalletHeaderData(notifyHeader); + // 保存栈板头数据 + if (!palletRecordList.isEmpty()) { + int palletSeqNo = palletHeaderDataList.stream().map(EcssCoDelPalletHeaderData::getSeqNo) + .max(Integer::compareTo).orElse(0); // 获取当前最大的seqNo + for (Map palletRecord : palletRecordList) { + palletSeqNo++; + String palletNo = (String) palletRecord.get("pallet"); + Object palletQtyObj = palletRecord.get("palletQty"); + Object heightObj = palletRecord.get("height"); + Integer palletQty = null; + BigDecimal height = null; + + if (palletQtyObj instanceof Integer) { + palletQty = (Integer) palletQtyObj; + } else if (palletQtyObj instanceof String) { + try { + palletQty = Integer.parseInt((String) palletQtyObj); + } catch (NumberFormatException e) { + throw new RuntimeException("栈板托数格式错误: " + palletQtyObj); + } + } + + if (heightObj instanceof Number) { + height = new BigDecimal(heightObj.toString()); + } else if (heightObj instanceof String) { + try { + height = new BigDecimal((String) heightObj); + } catch (NumberFormatException e) { + throw new RuntimeException("栈板高度格式错误: " + heightObj); + } + } + + if (palletNo == null || palletNo.isEmpty()) { + throw new RuntimeException("第" + palletSeqNo + "行栈板不能为空"); + } + if (palletQty == null || palletQty <= 0) { + throw new RuntimeException("第" + palletSeqNo + "行托数必须大于0"); + } + if (height == null || height.compareTo(BigDecimal.ZERO) <= 0) { + throw new RuntimeException("第" + palletSeqNo + "行栈板高度必须大于0"); + } + + // 获取栈板信息(长宽高) + List palletDataList = coDelMapper.getPallet(site, buNo,palletNo); + EcssPalletData palletInfo; + if (palletDataList.isEmpty()) { + throw new RuntimeException("栈板信息不存在!"); + } else { + palletInfo = palletDataList.get(0); + } + // 创建栈板头数据 + EcssCoDelPalletHeaderData headerData = new EcssCoDelPalletHeaderData(); + headerData.setSite(site); + headerData.setBuNo(buNo); + headerData.setDelNo(delNo); + headerData.setSeqNo(palletSeqNo); + headerData.setPalletNo(delNo + "-" + String.format("%03d", headerData.getSeqNo())); + headerData.setPalletQty(palletQty); + headerData.setLength(palletInfo.getLength()); + headerData.setWidth(palletInfo.getWidth()); + headerData.setHeight(height); // 使用用户输入的高度 + headerData.setPallet(palletNo); + // 计算体积 + if (headerData.getLength() != null && headerData.getWidth() != null && headerData.getHeight() != null) { + headerData.setVolume(headerData.getLength() + .multiply(headerData.getWidth()) + .multiply(headerData.getHeight())); + } + + headerData.setPalletRemark(delNo + "-" + String.format("%03d", palletSeqNo)); + headerData.setCreateBy(currentUser.getUsername()); + // 保存栈板头数据 + coDelMapper.saveCodelPalletHeader(headerData); + } + } + } + + /** + * 导出申报要素 + */ + @Override + public void downloadDeclarationElements(HttpServletResponse response, EcssDeclarationHeaderData data) { + try { + ExcelTemplate template = ExcelTemplate.load(new ClassPathResource("templates/declaration-elements-template.xlsx").getInputStream()); + extractedElements(data, template); + try (XSSFWorkbook workbook = template.render(0)) { + workbook.write(response.getOutputStream()); + } + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment; filename=\"报关要素.xlsx\""); + response.flushBuffer(); + } catch (Exception e) { + log.error("导出报关要素异常{}", e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("报关导出异常:"+e.getMessage()); + } + } + /** + * 导出报关单 + */ + @Override + public void downloadDeclaration(HttpServletResponse response, EcssDeclarationHeaderData data) { + try { + ExcelTemplate template = ExcelTemplate.load(new ClassPathResource("templates/declaration-template.xlsx").getInputStream()); + extractedDeclaration(data, template); + try (XSSFWorkbook workbook = template.render(0)) { + workbook.write(response.getOutputStream()); + } + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment; filename=\"报关单.xlsx\""); + response.flushBuffer(); + } catch (Exception e) { + log.error("导出报关单异常{}", e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("报关导出异常:"+e.getMessage()); + } + } + + private String stringInput(String inString) { + if (StringUtils.isEmpty(inString)) { + return ""; + } else { + return inString; + } + } + /** + * 导出发票 + */ + @Override + public void downloadInvoice(HttpServletResponse response, EcssDeclarationHeaderData data) { + try { + EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); + // 硬标天线需要po(declaration-invoice2-template.xlsx),且要长, + // RFID和RF不需要po,合同带印章(declaration-invoice-seal-template.xlsx),发票号不带章declaration-invoice-template.xlsx + String xlsx = notifyHeader.getBuNo().equals("04-MHM") || notifyHeader.getBuNo().equals("02-Hardtag")? + "templates/declaration-invoice2-template.xlsx":"templates/declaration-invoice-template.xlsx"; + if (data.getShowWeight()!=null && data.getShowWeight()) { + xlsx = "templates/declaration-invoice-showWeight-template.xlsx"; + + } + ExcelTemplate template = ExcelTemplate.load(new ClassPathResource(xlsx).getInputStream()); + extractedInvoice(data, template, notifyHeader); + try (XSSFWorkbook workbook = template.render(0)) { + workbook.write(response.getOutputStream()); + } + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment; filename=\"发票.xlsx\""); + response.flushBuffer(); + } catch (Exception e) { + log.error("报关导出发票异常:{}", e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("报关导出异常:"+e.getMessage()); + } + } + /** + * 导出箱单 + */ + @Override + public void downloadPackingList(HttpServletResponse response, EcssDeclarationHeaderData data) { + try { + EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); + // RFID和RF不需要序号列(declaration-packingList-template.xlsx),其他需要序列号(declaration-packingList2-template.xlsx) + ExcelTemplate template = ExcelTemplate.load(new ClassPathResource( + notifyHeader.getBuNo().equals("03-RFID") || notifyHeader.getBuNo().equals("01-Label")? + "templates/declaration-packingList-template.xlsx":"templates/declaration-packingList2-template.xlsx").getInputStream()); + exportPackingList(data, template,notifyHeader,0); + try (XSSFWorkbook workbook = template.render(0)) { + workbook.write(response.getOutputStream()); + } + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment; filename=\"箱单.xlsx\""); + response.flushBuffer(); + } catch (Exception e) { + log.error("报关导出箱单异常:{}", e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("报关导出异常:"+e.getMessage()); + } + } + + /** + * 导出装箱数据导入模版 + */ + @Override + public void exportPackingTemplate(HttpServletResponse response, EcssCoDelNotifyHeaderData data) { + try { + ExcelTemplate template = ExcelTemplate.load(new ClassPathResource("templates/packing-template.xlsx").getInputStream()); + template.setBoxFlag(true); + // 获取装箱数据 + List> palletList = sqlSession.selectList("ecssMapper.searchCoDelPalletData", data); + if (palletList == null || palletList.isEmpty()) { + // 如果没有装箱数据,获取通知单明细数据 + palletList = sqlSession.selectList("ecssMapper.searchEcssCoDelNotifyDetail", data); + } + //计算总数和总箱数 + int totalQty = 0; + int totalBoxQty = 0; + for (Map item : palletList) { + Object qtyObj = item.get("qty"); + Object boxQtyObj = item.get("boxQty"); + int qty = 0; + int boxQty = 0; + if (qtyObj instanceof Number) { + qty = ((Number) qtyObj).intValue(); + } else if (qtyObj instanceof String) { + try { + qty = Integer.parseInt((String) qtyObj); + } catch (NumberFormatException e) { + qty = 0; // 或者根据需求处理异常 + } + } + if (boxQtyObj instanceof Number) { + boxQty = ((Number) boxQtyObj).intValue(); + } else if (boxQtyObj instanceof String) { + try { + boxQty = Integer.parseInt((String) boxQtyObj); + } catch (NumberFormatException e) { + boxQty = 0; // 或者根据需求处理异常 + } + } + totalQty += qty; + totalBoxQty += boxQty; + item.put("qty", qty); + item.put("boxQty", boxQty); + } + + template.addVar("totalQty", totalQty); + template.addVar("totalBox", totalBoxQty); + // 添加数据列表 + template.addListVarAll( palletList); + + try (XSSFWorkbook workbook = template.render(0)) { + workbook.write(response.getOutputStream()); + } + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment; filename=\"装箱数据导入模版.xlsx\""); + response.flushBuffer(); + } catch (Exception e) { + log.error("导出装箱数据模版异常:{}", e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("导出装箱数据模版异常:"+e.getMessage()); + } + } + + /** + * 导出出口货物委托书 + */ + @Override + public void downloadExportGoods(HttpServletResponse response, EcssDeclarationHeaderData data) { + try { + ExcelTemplate template = ExcelTemplate.load(new ClassPathResource("templates/export-goods-template.xlsx").getInputStream()); + extractedExportGoods(data, template); + try (XSSFWorkbook workbook = template.render(0)) { + workbook.write(response.getOutputStream()); + } + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment; filename=\"出口货物委托书.xlsx\""); + response.flushBuffer(); + } catch (Exception e) { + log.error("报关导出出口货物委托书异常:{}", e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("报关导出异常:"+e.getMessage()); + } + } + + /** + * 导出合同 + */ + @Override + public void downloadContract(HttpServletResponse response, EcssDeclarationHeaderData data) { + try { + ExcelTemplate template = ExcelTemplate.load(new ClassPathResource("templates/declaration-contract-template.xlsx").getInputStream()); + extractedContract(data, template); + try (XSSFWorkbook workbook = template.render(0)) { + workbook.write(response.getOutputStream()); + } + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment; filename=\"合同.xlsx\""); + response.flushBuffer(); + } catch (Exception e) { + log.error("报关导出合同异常:{}", e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("报关导出异常:"+e.getMessage()); + } + } + + @Override + public void downloadAll(HttpServletResponse response, EcssDeclarationHeaderData data) { + EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); + // RFID和RF和硬标天线使用的模版不一样 + // RFID和RF如果勾选了合同,导出需要带公章(declaration-all-seal-template.xlsx) + String xlsx; + if (notifyHeader.getBuNo().equals("03-RFID") || notifyHeader.getBuNo().equals("01-Label")) { + xlsx = "templates/declaration-all-template.xlsx"; + } else { + // 硬标天线需要po(declaration-all2-template.xlsx),且多一个合同sheet + xlsx = "templates/declaration-all2-template.xlsx"; + } + if (data.getShowWeight()!=null && data.getShowWeight()) { + xlsx = "templates/declaration-all-showWeight-template.xlsx"; + + } + XSSFWorkbook workbook = null; + try { + ExcelTemplate template = ExcelTemplate.load(new ClassPathResource(xlsx).getInputStream()); + + // 第一个sheet - 出口货物委托书 + extractedExportGoods(data, template); + workbook = template.render(0); + + // 第二个sheet - 发票 + template.clearAll(); + extractedInvoice(data, template, notifyHeader); + template.render(1); + + // 第三个sheet - 箱单 + template.clearAll(); + exportPackingList(data, template, notifyHeader, 0); + template.render(2); + + // 第四个sheet - 报关单 + template.clearAll(); + extractedDeclaration(data, template); + template.render(3); + + // 第五个sheet - 申报要素 + template.clearAll(); + extractedElements(data, template); + template.render(4); + + // 第六个sheet - 合同 (仅特定BU需要) + if (notifyHeader.getBuNo().equals("04-MHM") || notifyHeader.getBuNo().equals("02-Hardtag")) { + template.clearAll(); + extractedContract(data, template); + template.render(5); + } + + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment; filename=\"单证信息.xlsx\""); + workbook.write(response.getOutputStream()); + response.flushBuffer(); + } catch (IOException e) { + log.error("报关导出所有异常:{}", e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("报关导出异常:"+e.getMessage()); + } finally { + if (workbook != null) { + try { + workbook.close(); // 显式关闭资源 + } catch (IOException e) { + log.error("关闭资源异常{}", e.getMessage()); + e.printStackTrace(); + } + } + } + } + + /** + * 导出PDF格式的单证信息(不包含出口货物委托书) + */ + @Override + public void downloadAllPdf(HttpServletResponse response, EcssDeclarationHeaderData data) { + EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); + // RFID和RF和硬标天线使用的模版不一样 + // RFID和RF如果勾选了合同,导出需要带公章(declaration-all-seal-template.xlsx) + String xlsx = "templates/declaration-all-template-pdf.xlsx"; + if (data.getShowWeight()!=null && data.getShowWeight()) { + xlsx = "templates/declaration-all-showWeight-template-pdf.xlsx"; + + } + XSSFWorkbook excelWorkbook = null; + try { + ExcelTemplate template = ExcelTemplate.load(new ClassPathResource(xlsx).getInputStream()); + + // 第一个sheet - 发票(PDF导出时跳过出口货物委托书) + extractedInvoice(data, template, notifyHeader); + excelWorkbook = template.render(0); + + // 第二个sheet - 箱单 + template.clearAll(); + exportPackingList(data, template, notifyHeader, 0); + template.render(1); + + // 第三个sheet - 报关单 + template.clearAll(); + extractedDeclaration(data, template); + template.render(2); + + // 第四个sheet - 申报要素 + template.clearAll(); + extractedElements(data, template); + template.render(3); + + // 第五个sheet - 合同 (仅特定BU需要) + if (notifyHeader.getBuNo().equals("04-MHM") || notifyHeader.getBuNo().equals("02-Hardtag")) { + template.clearAll(); + extractedContract(data, template); + template.render(4); + } + + // 将Excel转换为PDF + byte[] pdfBytes = convertExcelToPdf(excelWorkbook); + + response.setContentType("application/pdf"); + response.setHeader("Content-Disposition", "attachment; filename=\"单证信息.pdf\""); + response.getOutputStream().write(pdfBytes); + response.flushBuffer(); + + } catch (Exception e) { + log.error("报关导出PDF异常:{}", e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("报关导出PDF异常:"+e.getMessage()); + } finally { + if (excelWorkbook != null) { + try { + excelWorkbook.close(); // 显式关闭资源 + } catch (IOException e) { + log.error("关闭资源异常{}", e.getMessage()); + e.printStackTrace(); + } + } + } + } + + /** + * 将Excel工作簿转换为PDF字节数组 + */ + private byte[] convertExcelToPdf(XSSFWorkbook excelWorkbook) throws Exception { + // 创建临时文件保存Excel + java.io.File tempExcelFile = java.io.File.createTempFile("temp_excel", ".xlsx"); + java.io.File tempPdfFile = java.io.File.createTempFile("temp_pdf", ".pdf"); + + try (java.io.FileOutputStream fos = new java.io.FileOutputStream(tempExcelFile)) { + excelWorkbook.write(fos); + } + + // 使用Aspose.Cells转换Excel为PDF + Workbook asposeWorkbook = new Workbook(tempExcelFile.getAbsolutePath()); + + // 设置PDF页面属性 + for (int i = 0; i < asposeWorkbook.getWorksheets().getCount(); i++) { + com.aspose.cells.Worksheet worksheet = asposeWorkbook.getWorksheets().get(i); + PageSetup pageSetup = worksheet.getPageSetup(); + + // 设置页面方向(纵向) + pageSetup.setOrientation(i==2?PageOrientationType.LANDSCAPE:PageOrientationType.PORTRAIT); + // 设置纸张大小(A4) + pageSetup.setPaperSize(PaperSizeType.PAPER_A_4); + // 设置缩放比例 + pageSetup.setZoom(85); + // 设置边距 + pageSetup.setLeftMargin(0.5); + pageSetup.setRightMargin(0.5); + pageSetup.setTopMargin(0.5); + pageSetup.setBottomMargin(0.5); + } + + // 保存为PDF + asposeWorkbook.save(tempPdfFile.getAbsolutePath(), SaveFormat.PDF); + + // 读取PDF文件为字节数组 + byte[] pdfBytes; + try (FileInputStream fis = new FileInputStream(tempPdfFile); + ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + byte[] buffer = new byte[1024]; + int len; + while ((len = fis.read(buffer)) != -1) { + bos.write(buffer, 0, len); + } + pdfBytes = bos.toByteArray(); + } + + + // 清理临时文件 + tempExcelFile.delete(); + tempPdfFile.delete(); + + return pdfBytes; + } + + private void extractedDeclaration(EcssDeclarationHeaderData data, ExcelTemplate template) { + template.setMoveSeal(true); + template.setDelRight(true); + EcssDeclarationHeaderData ecHeader = coDelMapper.getDeclarationHeader(data); + template.addVar("inputCode", stringInput(ecHeader.getInputCode())); + template.addVar("customsOfficeCode", stringInput(ecHeader.getCustomsOfficeCode())); + template.addVar("localShipper", stringInput(ecHeader.getLocalShipper())); + template.addVar("localShipAddress", stringInput(ecHeader.getLocalShipAddress())); + template.addVar("shipType", stringInput(ecHeader.getShipType())); + template.addVar("shipDate", ecHeader.getShipDate() != null ? DateUtils.format(ecHeader.getShipDate(), "yyyy-MM-dd") : ""); + template.addVar("submitDate", ecHeader.getSubmitDate() != null ? DateUtils.format(ecHeader.getSubmitDate(), "yyyy-MM-dd") : ""); + template.addVar("filingNo", stringInput(ecHeader.getFilingNo())); + template.addVar("overseasShipper", stringInput(ecHeader.getOverseasShipper())); + template.addVar("shippingMode", stringInput(ecHeader.getShippingMode())); + template.addVar("shippingData", stringInput(ecHeader.getShippingData())); + template.addVar("deliverNo", stringInput(ecHeader.getDeliverNo())); + template.addVar("salesPartner", stringInput(ecHeader.getSalesPartner())); + template.addVar("regulatoryMethod", stringInput(ecHeader.getRegulatoryMethod())); + template.addVar("dutyStatus", stringInput(ecHeader.getDutyStatus())); + template.addVar("permitNumber", stringInput(ecHeader.getPermitNumber())); + template.addVar("cmcInvoice", stringInput(ecHeader.getCmcInvoice())); + template.addVar("salesArea", stringInput(ecHeader.getSalesArea())); + template.addVar("receiveArea", stringInput(ecHeader.getReceiveArea())); + template.addVar("shippingPort", stringInput(ecHeader.getShippingPort())); + template.addVar("exitPort", stringInput(ecHeader.getExitPort())); + template.addVar("documents", stringInput(ecHeader.getDocuments())); + template.addVar("packageType", stringInput(ecHeader.getPackageType())); + EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); + List palletHeaderDataList = coDelMapper.searchEcssCoDelPalletHeaderData(notifyHeader); + // 总托数 + Integer totalPlt = palletHeaderDataList.stream() + .map(EcssCoDelPalletHeaderData::getPalletQty) + .filter(Objects::nonNull) // 防止空指针 + .reduce(0, Integer::sum); + template.addVar("packageQty", data.getPackageUnit()!=null&&data.getPackageUnit().equals("箱")?ecHeader.getBoxQty():totalPlt); + template.addVar("packageUnit", data.getPackageUnit()); + // 托盘重量=根据每个pallet的重量*数量累加 + BigDecimal palletWeight = BigDecimal.ZERO; + for (EcssCoDelPalletHeaderData palletHeader : palletHeaderDataList) { + if (palletHeader.getPallet() != null && !palletHeader.getPallet().isEmpty()) { + // 根据pallet编号查询EcssPallet信息 + List palletDataList = coDelMapper.getPallet(notifyHeader.getSite(), notifyHeader.getBuNo(), palletHeader.getPallet()); + if (!palletDataList.isEmpty()) { + EcssPalletData palletData = palletDataList.get(0); + if (palletData.getPalletWeight() != null && palletHeader.getPalletQty() != null) { + // 栈板重量 = 单个栈板重量 * 数量 + BigDecimal singlePalletWeight = palletData.getPalletWeight().multiply(BigDecimal.valueOf(palletHeader.getPalletQty())); + palletWeight = palletWeight.add(singlePalletWeight); + } + } + } + } + BigDecimal grossWeight = ecHeader.getGrossWeight() != null ? ecHeader.getGrossWeight() : BigDecimal.ZERO; + template.addVar("grossWeight", (grossWeight.add(palletWeight)).setScale(2, RoundingMode.HALF_UP)); + template.addVar("netWeight", ecHeader.getNetWeight() != null ? ecHeader.getNetWeight().setScale(2, RoundingMode.HALF_UP) : ""); + template.addVar("paymentType", stringInput(ecHeader.getPaymentType())); + template.addVar("shipPrice", ecHeader.getShipPrice() != null ? ecHeader.getShipPrice().setScale(2, RoundingMode.HALF_UP) : ""); + template.addVar("premium", ecHeader.getPremium() != null ? ecHeader.getPremium().setScale(2, RoundingMode.HALF_UP) : ""); + template.addVar("otherPrice", ecHeader.getOtherPrice() != null ? ecHeader.getOtherPrice().setScale(2, RoundingMode.HALF_UP) : ""); + template.addVar("specialRelationshipComfirm", StringUtils.isEmpty(ecHeader.getSpecialRelationshipComfirm()) ? "" : + ecHeader.getSpecialRelationshipComfirm().equals("Y") ? "是" : "否"); + template.addVar("priceInfluenceConfirm", StringUtils.isEmpty(ecHeader.getPriceInfluenceConfirm()) ? "" : + ecHeader.getPriceInfluenceConfirm().equals("Y") ? "是" : "否"); + template.addVar("royaltyPaymentsConfirm", StringUtils.isEmpty(ecHeader.getRoyaltyPaymentsConfirm()) ? "" : + ecHeader.getRoyaltyPaymentsConfirm().equals("Y") ? "是" : "否"); + template.addVar("remark", stringInput(ecHeader.getRemark())); + + List detailList = coDelMapper.exportDeclarationDetail(data); + for (int i = 0; i < detailList.size(); i++) { + Map eorder = detailList.get(i); + eorder.put("row_num", i + 1); + if (data.getDeclarationWeight()!=null && data.getDeclarationWeight().equals("显示")) { + eorder.put("net_weight", eorder.get("net_weight") != null ? eorder.get("net_weight").toString() : ""); + eorder.put("weight_unit", eorder.get("weight_unit") != null ? eorder.get("weight_unit").toString() : ""); + } else { + eorder.put("net_weight", ""); + eorder.put("weight_unit", ""); + } + if (eorder.get("currency") != null) { + String currency = stringInput((String) eorder.get("currency")); + if (currency.toUpperCase().startsWith("RMB")) { + currency = "CNY"; + } + eorder.put("currency", currency); + } + eorder.put("hs_code_desc", eorder.get("hs_code_desc")); + } + template.addListVarAll(detailList); + } + + private void extractedElements(EcssDeclarationHeaderData data, ExcelTemplate template) { + template.addVar("brandType", data.getBrandType()); + Map propertiesMap = new HashMap<>(); + if (data.getPropertiesList()!=null && !data.getPropertiesList().isEmpty()) { + for (int i = 0; i < data.getPropertiesList().size(); i++) { + String key = data.getPropertiesList().get(i).getHsCode()+"&"+ data.getPropertiesList().get(i).getHsCodeDesc(); + if (!propertiesMap.containsKey(key)) { + propertiesMap.put(key, data.getPropertiesList().get(i).getBrand()); + } + } + } + // 获取partNo列表 + List partNos = coDelMapper.getDeclarationElements(data); + List partNoList = partNos.stream().map(map -> map.get("partNo").toString()).collect(Collectors.toList()); + // 根据partNo获取hsCode + List hsCodes = coDelMapper.getHsCodeByPartNo(data.getSite(), partNoList); + Map hsCodeMap = new HashMap<>(); + // 用于收集每个hsCode对应的SKU列表 + Map> hsCodeSkuListMap = new HashMap<>(); + for (int i = 0; i < hsCodes.size(); i++) { + Map eorder = hsCodes.get(i); + String key = eorder.get("hsCode")+"&"+eorder.get("hsCodeDesc"); + String sku = eorder.get("sku") != null ? eorder.get("sku").toString() : ""; + + if (hsCodeMap.containsKey(key)) { + // 将SKU添加到列表中 + hsCodeSkuListMap.get(key).add(sku); + } else { + // hscode所有属性 + PartSubPropertiesValueData inData = new PartSubPropertiesValueData(); + inData.setSite(data.getSite()); + inData.setBuNo(eorder.get("BuNo")!=null?eorder.get("BuNo").toString():""); + inData.setCodeNo(eorder.get("codeNo")!=null?eorder.get("codeNo").toString():""); + inData.setPartNo(eorder.get("hsCodeDesc")!=null?eorder.get("hsCodeDesc").toString():""); + inData.setRecordType("ECSS"); + List propertiesList = coDelMapper.getPropertiesListByPartAndCodeNo(inData); + StringBuilder properties = new StringBuilder(); + for (int j = 0; j < propertiesList.size(); j++) { + if (propertiesList.get(j).getItemDesc().equals(data.getBrand()) && propertiesMap.containsKey(key) + && !StringUtils.isBlank(propertiesMap.get(key))) { + propertiesList.get(j).setTextValue(propertiesMap.get(key)); + } + if (j == propertiesList.size() - 1) { + properties.append(propertiesList.get(j).getItemDesc()).append(": ").append(propertiesList.get(j).getTextValue()); + } else { + properties.append(propertiesList.get(j).getItemDesc()).append(": ").append(propertiesList.get(j).getTextValue()).append("\n"); + } + } + eorder.put("properties",properties); + hsCodeMap.put(key, eorder); + // 初始化SKU列表 + List skuList = new ArrayList<>(); + skuList.add(sku); + hsCodeSkuListMap.put(key, skuList); + } + } + + // 格式化SKU列表:每3个一行 + List list = new ArrayList<>(hsCodeMap.values()); + for (int i = 0; i < list.size(); i++) { + Map fMap = list.get(i); + String key = fMap.get("hsCode")+"&"+fMap.get("hsCodeDesc"); + List skuList = hsCodeSkuListMap.get(key); + String formattedSku = formatSkuList(skuList); + fMap.put("sku", formattedSku); + fMap.put("content", "HS Code: "+fMap.get("hsCode")+ + "\n品名: "+(data.getHsCodeDescType()!=null&& data.getHsCodeDescType().equals("N")?fMap.get("hsCodeDescEn"):fMap.get("hsCodeDesc"))+ + "\n"+fMap.get("properties")+ + "\n型号: "+formattedSku); + } + template.addListVarAll(list); + } + + /** + * 格式化SKU列表,每3个一行,从第2行开始前面加17个空格 + * + * @param skuList SKU列表 + * @return 格式化后的SKU字符串,格式如: "10001,10002,10003\n 10004,10005,10006" + */ + private String formatSkuList(List skuList) { + if (skuList == null || skuList.isEmpty()) { + return ""; + } + + StringBuilder result = new StringBuilder(); + for (int i = 0; i < skuList.size(); i++) { + if (i > 0) { + // 每3个换行 + if (i % 3 == 0) { + // 换行后添加17个空格 + result.append("\n "); + } else { + // 否则用逗号分隔 + result.append(","); + } + } + result.append(skuList.get(i)); + } + + return result.toString(); + } + + private void extractedInvoice(EcssDeclarationHeaderData data, ExcelTemplate template, + EcssCoDelNotifyHeaderData notifyHeader) { + List notifyDetailList = data.getNotifyPartDetailList(); + for (EcssCoDelNotifyDetailData nData:notifyDetailList){ + coDelMapper.updateEcssCoDelNotifyDetail(nData); + } + if (data.getDeclarationNo()!=null) { + coDelMapper.updateEcssDeclarationHeader(data); + } + template.setMoveShape(true); + template.setCellStyle(true); + template.setRangeStyle(true); + template.setPriceRight(true); + template.setMoveSeal(true); + template.setInvoiceLie(data.getShowWeight() != null && data.getShowWeight()); + template.addVar("remark", stringInput(data.getFpremark())); + template.addVar("localShipper", notifyHeader.getCustomerName()); + template.addVar("localShipAddress", notifyHeader.getLocalShipAddress()); + template.addVar("ccusname", stringInput(notifyHeader.getOverseasShipper())); + template.addVar("cDeliverAdd", stringInput(notifyHeader.getOverseasAddress())); + template.addVar("invoiceTitle", data.getContractFlag()!=null && data.getContractFlag()? "合同" : "INVOICE No."); + template.addVar("cmc_invoice", notifyHeader.getCmcInvoice()); + template.addVar("dateStr", DateUtils.format(notifyHeader.getReadyDate(), "yyyy-MM-dd")); + List detailList = coDelMapper.exportEcssCoDelNotifyDetail(data); + String currency = "CNY"; + if (!detailList.isEmpty()) { + Object cur = detailList.get(0).get("currency"); + if (cur != null) { + String curStr = cur.toString().trim(); + if (curStr.toUpperCase().startsWith("RMB")) { + currency = "CNY"; + } else { + currency = curStr; + } + } + } + template.addVar("Currency", currency); + template.addVar("Incoterm", "EXW "); + template.addVar("shippingMode", stringInput(notifyHeader.getShippingMode())); + Map notifyDetailMap = notifyDetailList.stream().collect( + Collectors.toMap(EcssCoDelNotifyDetailData::getPartNo,e->e)); + // 装箱明细 - 按发货通知单明细item_no分组(用于获取每行的重量) + List palletDetailList = coDelMapper.exportCoDelPalletDetailGroupByItemNo(notifyHeader); + Map palletMap = palletDetailList.stream().collect(Collectors.toMap( o -> o.get("item_no"), o -> o)); + // 装箱数据 + List palletHeaderDataList = coDelMapper.searchEcssCoDelPalletHeaderData(notifyHeader);// 总托数 + Integer totalPlt = palletHeaderDataList.stream() + .map(EcssCoDelPalletHeaderData::getPalletQty) + .filter(Objects::nonNull) // 防止空指针 + .reduce(0, Integer::sum); + + Map poNoMap = new HashMap<>(); + for (int i = 0; i < detailList.size(); i++) { + Map eorder = detailList.get(i); + eorder.put("row_num", i + 1); + poNoMap.put(eorder.get("customerPO"), eorder.get("customerPO")); + } + StringBuilder ponos = new StringBuilder(); + poNoMap.forEach((key, value) -> ponos.append(key + " ")); + template.addVar("poNo", ponos); + BigDecimal allPrice = BigDecimal.valueOf(0.0); + List ndList = sqlSession.selectList("ecssMapper.searchEcssCoDelNotifyDetailList", notifyHeader); + for (int i = 0; i < ndList.size(); i++) { + Map eorder = ndList.get(i); + String partNo = (String) eorder.get("part_no"); + Integer itemNo = eorder.get("item_no") != null ? (Integer) eorder.get("item_no") : null; + eorder.put("row_num", i + 1); + EcssCoDelNotifyDetailData nodifyData = notifyDetailMap.get(partNo); + // 根据item_no获取该明细行对应的装箱重量 + Map pm = itemNo != null ? palletMap.get(itemNo) : null; + int totalQty = pm!=null && pm.get("total_qty")!=null?Integer.parseInt(pm.get("total_qty").toString()):0; + String lossratio = ""; + if (nodifyData!=null && nodifyData.getLossratio()!=null && !StringUtils.isBlank(nodifyData.getLossratio())) { + // 关务物料属性 + List propertiesValues = coDelMapper.getPropertiesListByTypeAndCodeNo( + notifyHeader.getSite(), "ECSSPART","BG001", notifyHeader.getBuNo(), partNo); + // 每一个物料加属性对应一个属性,用来下面遍历发货通知单明细时获取具体属性值 + Map> partNoAndItemNoMap = propertiesValues.stream() + .collect(Collectors.groupingBy(PartSubPropertiesValue::getPropertiesItemNo)); + lossratio = "\n" + (nodifyData.getEhundred()!=null && StringUtils.isNotEmpty(nodifyData.getEhundred()) + ?"E100-"+nodifyData.getEhundred():"") + +" "+"纯FSC纸重量:"+ + fscWeight(partNoAndItemNoMap,partNo, totalQty, nodifyData.getLossratio()); + } + String hsCodeDesc = data.getHsCodeDesc()!=null&& data.getHsCodeDesc()? + ("\n"+(data.getHsCodeDescTypeInvoice()!=null&& data.getHsCodeDescTypeInvoice().equals("N") + ?eorder.get("hsCodeDescEn") :eorder.get("hsCodeDesc"))):""; + eorder.put("designation", eorder.get("part_description") + "\n" + "PO:" + eorder.get("customerPO") + + (data.getHsCodeFlag()!=null && data.getHsCodeFlag()?" HS Code:" + eorder.get("hsCode"):"")+hsCodeDesc+ lossratio); + eorder.put("totalPrice", (((BigDecimal) eorder.get("qty")).multiply((BigDecimal) eorder.get("unitPrice"))). + setScale(2, RoundingMode.HALF_UP)); + allPrice = allPrice.add(((BigDecimal) eorder.get("qty")).multiply((BigDecimal) eorder.get("unitPrice"))); + if (data.getShowWeight()!=null && data.getShowWeight()) { + // 根据发货通知单明细查找装箱明细,获取重量和净重,保留2位小数 + eorder.put("grossWeight", pm != null && pm.get("gross_weight") != null ? + new BigDecimal(pm.get("gross_weight").toString()).setScale(2, RoundingMode.HALF_UP) : ""); + eorder.put("netWeight", pm != null && pm.get("net_weight") != null ? + new BigDecimal(pm.get("net_weight").toString()).setScale(2, RoundingMode.HALF_UP) : ""); + } + } + // RFID需要的 + if (notifyHeader.getBuNo().equals("01-Label") || notifyHeader.getBuNo().equals("03-RFID")) { + template.addVar("madein", stringInput(data.getOrigin())); + } + // 孟加拉需要的 + if (data.getMaterial()!=null && data.getMaterial()) { + template.addVar("RFIDBase", "RFID Base Material"); + template.addVar("HSCode", "& H.S.Code:"+stringInput(data.getHsCode())); + } + // 欧洲地区需要 + if (data.getPackaging()!=null && data.getPackaging()) { + template.addVar("packaging", "Non-reusable plastic packaging:"); + // 根据维护参数计算出KGS=总托数*plastic packaging维护参数 + BigDecimal totalKgs = data.getKgs()!=null?data.getKgs():BigDecimal.ZERO; + if (palletHeaderDataList.isEmpty()) { + totalKgs = BigDecimal.valueOf(0.1); + } + List list = coDelMapper.selectBoxList(notifyHeader); + BigDecimal totalCartons = BigDecimal.valueOf(0.0); + for (int m = 0; m < list.size(); m++) { + totalCartons = totalCartons.add(new BigDecimal(list.get(m).get("box_qty") != null ? + list.get(m).get("box_qty").toString() : "0")); + } + if (palletHeaderDataList.isEmpty()) { + totalPlt = totalCartons.setScale(0, RoundingMode.HALF_UP).intValue(); + } + template.addVar("KGS_qty", data.getKgs()!=null?(totalKgs.multiply(BigDecimal.valueOf(totalPlt))).setScale(2, RoundingMode.HALF_UP):""); + template.addVar("KGS", "KGS"); + } + // 新加的 货物明细单选框 勾选显示 + if (data.getGoodsLabel()!=null && data.getGoodsLabel()) { + template.addVar("nameLabel", "Name of goods"+(notifyHeader.getBuNo().equals("03-RFID")?" RFID LABEL":" RF LABEL")); + template.addVar("originLabel", "Origin of goods Made in China"); + template.addVar("sellerLabel", "Name of seller Checkpoint Commercial (Shanghai) Co.. Ltd."); + } + template.addVar("allPrice", allPrice.setScale(2, RoundingMode.HALF_UP)); + template.addListVarAll(ndList); + } + + private void exportPackingList(EcssDeclarationHeaderData data, ExcelTemplate template, + EcssCoDelNotifyHeaderData notifyHeader,int type) { + EcssDeclarationHeaderData ecHeader = coDelMapper.getDeclarationHeader(data); + List notifyDetailList; + if (type==0) { + coDelMapper.updateEcssDeclarationHeader(data); + notifyDetailList = data.getNotifyPartDetailList(); + for (EcssCoDelNotifyDetailData nData:notifyDetailList){ + coDelMapper.updateEcssCoDelNotifyDetail(nData); + } + } else { + notifyDetailList = coDelMapper.getNotifyPartDetail2(data); + } + Map notifyDetailMap = notifyDetailList.stream().collect( + Collectors.toMap(EcssCoDelNotifyDetailData::getPartNo,e->e)); + template.setCellStyle(true); + template.setRangeStyle(true); + template.setMoveSeal(true); + template.setIntRight(true); + if (notifyHeader.getBuNo().equals("03-RFID") || notifyHeader.getBuNo().equals("01-Label")){ + template.setCellStyle2(true); + } + template.addVar("remark", data.getXdremark()); + template.addVar("localShipper", notifyHeader.getCustomerName()); + template.addVar("localShipAddress", notifyHeader.getLocalShipAddress()); + template.addVar("ccusname", stringInput(notifyHeader.getOverseasShipper())); + template.addVar("cDeliverAdd", stringInput(notifyHeader.getOverseasAddress())); + template.addVar("dateStr", DateUtils.format(notifyHeader.getReadyDate(), "yyyy-MM-dd")); + template.addVar("cmc_invoice", notifyHeader.getCmcInvoice()); + Map poNoMap = new HashMap<>(); + // 装箱数据 + List palletHeaderDataList = coDelMapper.searchEcssCoDelPalletHeaderData(notifyHeader); + // 最高的栈板 + Optional palletHeaderData = palletHeaderDataList.isEmpty()? Optional.empty() + :palletHeaderDataList.stream().max(Comparator.comparing(EcssCoDelPalletHeaderData::getHeight)); + // 总托数 + Integer totalPlt = palletHeaderDataList.stream() + .map(EcssCoDelPalletHeaderData::getPalletQty) + .filter(Objects::nonNull) // 防止空指针 + .reduce(0, Integer::sum); + // 发货通知单明细 + List detailList = coDelMapper.exportEcssCoDelNotifyDetail(data); + // 获取poNo + for (int i = 0; i < detailList.size(); i++) { + Map eorder = detailList.get(i); + poNoMap.put(eorder.get("customerPO"), eorder.get("customerPO")); + } + StringBuilder ponos = new StringBuilder(); + poNoMap.forEach((key, value) -> ponos.append(key + " ")); + template.addVar("poNo", ponos); + List list = coDelMapper.selectBoxList(notifyHeader); + BigDecimal totalCartons = BigDecimal.valueOf(0.0); + BigDecimal grossWeight = BigDecimal.valueOf(0.0); + BigDecimal netWeight = BigDecimal.valueOf(0.0); + BigDecimal rolls = BigDecimal.valueOf(0.0); + int totalQty = 0; + List exportList = new ArrayList<>();//全部需要导出的明细 + EcssCoDelPalletHeaderData boxData = new EcssCoDelPalletHeaderData(); + boxData.setSite(data.getSite()); + boxData.setBuNo(notifyHeader.getBuNo()); + boxData.setDelNo(notifyHeader.getDelNo()); + for (int m = 0; m < list.size(); m++) { + boxData.setSeqNo(Integer.valueOf(list.get(m).get("item_no")!=null?list.get(m).get("item_no").toString():"")); + List checkList = coDelMapper.selectPalletDetailList(boxData); + // 记录当前箱子对应的exportList起始索引 + int boxStartIndex = exportList.size(); + + // 判断当前箱子所有明细的rolls是否全部为空或0 + boolean allDetailRollsEmptyOrZero = true; + for (int j = 0; j < checkList.size(); j++) { + Object rollsObj = checkList.get(j).get("rolls"); + if (rollsObj != null) { + BigDecimal rollsValue = (BigDecimal) rollsObj; + if (rollsValue.compareTo(BigDecimal.ZERO) != 0) { + allDetailRollsEmptyOrZero = false; + break; + } + } + } + // 获取box的rolls + BigDecimal boxRolls = list.get(m).get("rolls") != null ? + ((BigDecimal) list.get(m).get("rolls")).setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO; + + for (int i = 0; i < checkList.size(); i++) { + Map eorder = checkList.get(i); + String partNo = (String) eorder.get("part_no"); + int qty = ((BigDecimal)eorder.get("qty")).setScale(0, RoundingMode.HALF_UP).intValue(); + totalQty = totalQty + qty; + BigDecimal noCartons = list.get(m).get("box_qty")!=null?(BigDecimal) list.get(m).get("box_qty"):BigDecimal.ZERO; + eorder.put("total_qty", ((BigDecimal)eorder.get("qty")).setScale(0, RoundingMode.HALF_UP).intValue()); + // 合箱时:第一行显示数值,用于合并单元格居中显示 + boolean isFirstRow = (i == 0); + eorder.put("noCartons", isFirstRow ? noCartons.setScale(0, RoundingMode.HALF_UP) : ""); + eorder.put("gross_weight", isFirstRow ? ((BigDecimal) list.get(m).get("gross_weight")).setScale(2, RoundingMode.HALF_UP) : ""); + eorder.put("net_weight", isFirstRow ? ((BigDecimal) list.get(m).get("net_weight")).setScale(2, RoundingMode.HALF_UP) : ""); + + // rolls处理:如果明细的rolls全部为空或0,则取box的rolls;合箱时合并居中显示 + if (allDetailRollsEmptyOrZero) { + // 使用box的rolls,合箱时只在第一行显示(用于合并单元格居中) + eorder.put("rolls", isFirstRow ? boxRolls : ""); + if (isFirstRow) { + rolls = rolls.add(boxRolls); + } + } else { + // 使用明细的rolls + BigDecimal cuRolls = eorder.get("rolls") != null ? + ((BigDecimal) eorder.get("rolls")).setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO; + eorder.put("rolls", cuRolls); + rolls = rolls.add(cuRolls); + } + EcssCoDelNotifyDetailData nodifyData = notifyDetailMap.get(partNo); + // 只有RFID需要 + String lossratio=""; + if (notifyHeader.getBuNo().equals("03-RFID") && nodifyData!=null && StringUtils.isNotEmpty(nodifyData.getLossratio())) { + // 关务物料属性 + List propertiesValues = coDelMapper.getPropertiesListByTypeAndCodeNo( + notifyHeader.getSite(), "ECSSPART","BG001",notifyHeader.getBuNo(), partNo); + // 每一个物料加属性对应一个属性,用来下面遍历发货通知单明细时获取具体属性值 + Map> partNoAndItemNoMap = propertiesValues.stream() + .collect(Collectors.groupingBy(PartSubPropertiesValue::getPropertiesItemNo)); + lossratio = "\n" + (StringUtils.isNotEmpty(nodifyData.getEhundred())?"E100-"+nodifyData.getEhundred():"") + +" "+"纯FSC纸重量:"+ + fscWeight(partNoAndItemNoMap,partNo, qty, nodifyData.getLossratio()); + } + String hsCodeDesc = ""; + if (notifyHeader.getBuNo().equals("04-MHM") || notifyHeader.getBuNo().equals("02-Hardtag")) { + hsCodeDesc = "\n"+(data.getHsCodeDescType()!=null&& data.getHsCodeDescType().equals("N") + ?eorder.get("hsCodeDescEn") :eorder.get("hsCodeDesc")); + } + eorder.put("artNo", eorder.get("part_description") + "\n" + "PO:" + eorder.get("po_no") + + (data.getHsCodeFlag()!=null && data.getHsCodeFlag()?" HS Code:" + eorder.get("hsCode"):"")+ hsCodeDesc + lossratio + + (data.getUpc()!=null && data.getUpc()?"\n" + "UPC:"+ eorder.get("upc"):"") + + (data.getSo()!=null && data.getSo()?"\n" + "SO:"+eorder.get("so"):"")); + exportList.add(eorder); + } + // 如果当前箱子对应多个物料(合箱),添加合并单元格区域 + int boxEndIndex = exportList.size() - 1; + if (checkList.size() > 1) { + // 合并箱数列(第4列,索引4) + template.addMergeRegion(boxStartIndex, boxEndIndex, 4); + // 合并净重列(第6列,索引6) + template.addMergeRegion(boxStartIndex, boxEndIndex, 6); + // 合并毛重列(第7列,索引7) + template.addMergeRegion(boxStartIndex, boxEndIndex, 7); + // 如果明细rolls全为空/0,使用box的rolls时需要合并居中显示(第5列,索引5) + if (allDetailRollsEmptyOrZero) { + template.addMergeRegion(boxStartIndex, boxEndIndex, 8); + } + } + totalCartons = totalCartons.add(list.get(m).get("box_qty") !=null? new BigDecimal(list.get(m).get("box_qty").toString()) : BigDecimal.valueOf(0.0)); + grossWeight = grossWeight.add(list.get(m).get("gross_weight") !=null?new BigDecimal(list.get(m).get("gross_weight").toString()):BigDecimal.valueOf(0.0)); + netWeight = netWeight.add(list.get(m).get("net_weight") !=null?new BigDecimal(list.get(m).get("net_weight").toString()):BigDecimal.valueOf(0.0)); + } + // 托盘重量=根据每个pallet的重量*数量累加 + BigDecimal palletWeight = BigDecimal.ZERO; + for (EcssCoDelPalletHeaderData palletHeader : palletHeaderDataList) { + if (palletHeader.getPallet() != null && !palletHeader.getPallet().isEmpty()) { + // 根据pallet编号查询EcssPallet信息 + List palletDataList = coDelMapper.getPallet(notifyHeader.getSite(), notifyHeader.getBuNo(), palletHeader.getPallet()); + if (!palletDataList.isEmpty()) { + EcssPalletData palletData = palletDataList.get(0); + if (palletData.getPalletWeight() != null && palletHeader.getPalletQty() != null) { + // 栈板重量 = 单个栈板重量 * 数量 + BigDecimal singlePalletWeight = palletData.getPalletWeight().multiply(BigDecimal.valueOf(palletHeader.getPalletQty())); + palletWeight = palletWeight.add(singlePalletWeight); + } + } + } + } + template.addVar("Total_Cartons", totalCartons.setScale(0, RoundingMode.HALF_UP)); + template.addVar("Gross_Weight", (grossWeight.add(palletWeight)).setScale(2, RoundingMode.HALF_UP)); + template.addVar("Net_Weight", netWeight.setScale(2, RoundingMode.HALF_UP)); + template.addVar("total_weight", (grossWeight.add(palletWeight)).setScale(2, RoundingMode.HALF_UP)); + template.addVar("total_netweight", netWeight.setScale(2, RoundingMode.HALF_UP)); + template.addVar("total_box", totalCartons.setScale(0, RoundingMode.HALF_UP)); + template.addVar("total_qty", totalQty); + template.addVar("total_rolls", rolls.setScale(0, RoundingMode.HALF_UP)); + // 下面是可选的或者手动维护的 + // RFID需要的 + if (notifyHeader.getBuNo().equals("01-Label") || notifyHeader.getBuNo().equals("03-RFID")) { + String plt = "CTN"; + if (!palletHeaderDataList.isEmpty()) { + plt = "PLT"; + } else { + totalPlt = totalCartons.setScale(0, RoundingMode.HALF_UP).intValue(); + } + template.addVar("total_plt", totalPlt+plt); + template.addVar("total:", "total:"); + template.addVar("madein", stringInput(data.getOrigin())); + template.addVar("shippingNo", "shipping no"); + template.addVar("sp_cmc_invoice", notifyHeader.getCmcInvoice()); + // 如果palletWeight==0,不显示 + template.addVar("pallet_weight_name", palletWeight.compareTo(BigDecimal.ZERO)==0?"":"pallet weight:"); + template.addVar("pallet_weight", palletWeight.compareTo(BigDecimal.ZERO)==0?"":palletWeight.setScale(0, RoundingMode.HALF_UP)); + } + // 孟加拉需要的 + if (data.getMaterial()!=null && data.getMaterial()) { + template.addVar("RFIDBase", "RFID Base Material"); + template.addVar("HSCode", "& H.S.Code:"+stringInput(data.getHsCode())); + /* template.addVar("volume", !palletHeaderData.isPresent()?"":palletHeaderData.map(ecssCoDelPalletHeaderData -> + ecssCoDelPalletHeaderData.getLength().setScale(2, RoundingMode.HALF_UP) + + "*" + ecssCoDelPalletHeaderData.getWidth().setScale(2, RoundingMode.HALF_UP) + "*" + + ecssCoDelPalletHeaderData.getHeight().setScale(2, RoundingMode.HALF_UP) + "m").orElse(""));*/ + } + // 欧洲地区需要 + if (data.getPackaging()!=null && data.getPackaging()) { + template.addVar("packaging", "Non-reusable plastic packaging:"); + // 根据维护参数计算出KGS=总托数*plastic packaging维护参数 + BigDecimal totalKgs = data.getKgs()!=null?data.getKgs():BigDecimal.ZERO; + if (palletHeaderDataList.isEmpty()) { + totalKgs = BigDecimal.valueOf(0.1); + } + template.addVar("KGS_qty", data.getKgs()!=null?(totalKgs.multiply(BigDecimal.valueOf(totalPlt))).setScale(2, RoundingMode.HALF_UP):""); + template.addVar("KGS", "KGS"); + } + // 新加的 货物明细单选框 勾选显示 + if (data.getGoodsLabel()!=null && data.getGoodsLabel()) { + template.addVar("nameLabel", "Name of goods"+(notifyHeader.getBuNo().equals("03-RFID")?" RFID LABEL":" RF LABEL")); + template.addVar("originLabel", "Origin of goods Made in China"); + template.addVar("sellerLabel", "Name of seller Checkpoint Commercial (Shanghai) Co.. Ltd."); + } + template.addVar("Shipping_Mark", stringInput(data.getShippingMark())); + // 体积计算:优先使用栈板体积,栈板不存在时根据装箱明细的物料计算体积 + BigDecimal totalVolume = BigDecimal.ZERO; + if (!palletHeaderDataList.isEmpty()) { + // 计算所有栈板的体积总和 + for (EcssCoDelPalletHeaderData palletHeader : palletHeaderDataList) { + if (palletHeader.getLength() != null && palletHeader.getWidth() != null + && palletHeader.getHeight() != null && palletHeader.getPalletQty() != null) { + // 单个栈板体积 = 长 * 宽 * 高 * 数量 + BigDecimal singlePalletVolume = palletHeader.getLength() + .multiply(palletHeader.getWidth()) + .multiply(palletHeader.getHeight()) + .multiply(BigDecimal.valueOf(palletHeader.getPalletQty())); + totalVolume = totalVolume.add(singlePalletVolume); + } + } + totalVolume = totalVolume.setScale(2, RoundingMode.HALF_UP); + } else { + // 栈板不存在时,根据装箱明细的物料计算体积 + totalVolume = calculateVolumeByMaterials(notifyHeader); + } + template.addVar("Measurement", totalVolume); + if (data.getHighPalletFlag()!=null && data.getHighPalletFlag() && palletHeaderData.isPresent()) { + template.addVar("volume", palletHeaderData.map(ecssCoDelPalletHeaderData -> + ecssCoDelPalletHeaderData.getLength().setScale(2, RoundingMode.HALF_UP) + + "*" + ecssCoDelPalletHeaderData.getWidth().setScale(2, RoundingMode.HALF_UP) + "*" + + ecssCoDelPalletHeaderData.getHeight().setScale(2, RoundingMode.HALF_UP)).orElse("")); + } + template.addListVarAll(exportList); + } + + private void extractedExportGoods(EcssDeclarationHeaderData data, ExcelTemplate template) { + EcssDeclarationHeaderData ecHeader = coDelMapper.getDeclarationHeaderByDelNo(data); + // 发货通知单 + EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); + template.addVar("localShipper", notifyHeader.getCustomerName()); + template.addVar("localShipAddress", notifyHeader.getLocalShipAddress()); + template.addVar("ccusname", stringInput(notifyHeader.getOverseasShipper())); + template.addVar("cDeliverAdd", stringInput(notifyHeader.getOverseasAddress())); + template.addVar("shippingMode", stringInput(notifyHeader.getShippingMode())); + // 发货通知单明细 + List nodifyDetailList = coDelMapper.exportEcssCoDelNotifyDetail(data); + List rows = sqlSession.selectList("ecssMapper.getEcssCoDelNotifyDetailData", notifyHeader); + StringBuilder sb = new StringBuilder(); + StringBuilder sbEn = new StringBuilder(); + for (int i = 0; i < rows.size(); i++) { + if (i < rows.size() - 1) { + sb.append(rows.get( i).get("hsCodeDesc")).append("、"); + sbEn.append(rows.get( i).get("hsCodeDescEn")).append("、"); + } else { + sb.append(rows.get( i).get("hsCodeDesc")); + sbEn.append(rows.get( i).get("hsCodeDescEn")); + } + } + // 装箱明细 + List palletDetailList = coDelMapper.exportCoDelPalletDetail(notifyHeader); + int totalQty=palletDetailList.stream().mapToInt(o -> Integer.parseInt(o.get("total_qty").toString())).sum(); + Map palletMap = palletDetailList.stream().collect(Collectors.toMap( o -> o.get("part_no"), o -> o)); + template.addVar("hs_code_desc", stringInput(nodifyDetailList.get(0).get("hsCodeDescEn")!=null?sbEn.toString():"")); + String hsCodes = nodifyDetailList.stream() + .map(m -> Objects.toString(m.get("hsCode"), "")) + .filter(s -> !s.isEmpty()) // 去掉空值 + .distinct() // 去重 + .collect(Collectors.joining("、")); // 用换行拼接 + + template.addVar("hs_code", stringInput(hsCodes)); + Map poNoMap = new HashMap<>(); + BigDecimal ttlAmount = BigDecimal.ZERO; + for (Map map : nodifyDetailList) { + poNoMap.put(map.get("customerPO"), map.get("customerPO")); + ttlAmount = ttlAmount.add((BigDecimal) map.get("ttl_amount")); + } + StringBuilder ponos = new StringBuilder(); + poNoMap.forEach((key, value) -> ponos.append(key).append("\n")); + template.addVar("poNo", ponos); + template.addVar("shipping_port", stringInput(notifyHeader.getCnative())); + template.addVar("cmc_invoice", stringInput(notifyHeader.getCmcInvoice())); + // 导出时默认,可编辑的栏目 + template.addVar("sales_method", stringInput(data.getSalesMethod()));//贸易方式 + String currency = stringInput(data.getCurrency()); + if (currency.toUpperCase().startsWith("RMB")) { + currency = "CNY"; + } + template.addVar("currency", currency);//币制 + template.addVar("made_area", stringInput(data.getMadeArea()));//货物产地 + template.addVar("send_port", stringInput(data.getSendPort()));//发货港 + template.addVar("shipper", stringInput(data.getShipper()));//发货人 + template.addVar("voyage", notifyHeader.getBuNo().equals("01-Label") || notifyHeader.getBuNo().equals("03-RFID") + ?stringInput(notifyHeader.getCmcInvoice()) :stringInput(data.getVoyage()));//航次 + template.addVar("delivery_goods_date", stringInput(data.getDeliveryGoodsDate()));//提货日期 + template.addVar("shipping_date", stringInput(data.getShippingDate()));//船期 + // 装箱数据 + List palletHeaderDataList = coDelMapper.searchEcssCoDelPalletHeaderData(notifyHeader); + // 最高的栈板 + Optional palletHeaderData = palletHeaderDataList.isEmpty()? Optional.empty() : + palletHeaderDataList.stream().max(Comparator.comparing(EcssCoDelPalletHeaderData::getHeight)); + // 需要计算的 + // 总托数 + Integer totalPlt = palletHeaderDataList.stream() + .map(EcssCoDelPalletHeaderData::getPalletQty) + .filter(Objects::nonNull) // 防止空指针 + .reduce(0, Integer::sum); + template.addVar("quantity", ecHeader!=null?(totalPlt==0?ecHeader.getBoxQty()+"CTN("+ecHeader.getBoxQty()+"CTN)": + totalPlt+"PLT("+ecHeader.getBoxQty()+"CTN)"):""); + + template.addVar("price", ttlAmount.setScale(2, RoundingMode.HALF_UP)); + template.addVar("total_qty", totalQty); + // 托盘重量=根据每个pallet的重量*数量累加 + BigDecimal palletWeight = BigDecimal.ZERO; + for (EcssCoDelPalletHeaderData palletHeader : palletHeaderDataList) { + if (palletHeader.getPallet() != null && !palletHeader.getPallet().isEmpty()) { + // 根据pallet编号查询EcssPallet信息 + List palletDataList = coDelMapper.getPallet(notifyHeader.getSite(), notifyHeader.getBuNo(), palletHeader.getPallet()); + if (!palletDataList.isEmpty()) { + EcssPalletData palletData = palletDataList.get(0); + if (palletData.getPalletWeight() != null && palletHeader.getPalletQty() != null) { + // 栈板重量 = 单个栈板重量 * 数量 + BigDecimal singlePalletWeight = palletData.getPalletWeight().multiply(BigDecimal.valueOf(palletHeader.getPalletQty())); + palletWeight = palletWeight.add(singlePalletWeight); + } + } + } + } + template.addVar("net_weight", (ecHeader!=null&&ecHeader.getNetWeight() != null ? ecHeader.getNetWeight() : BigDecimal.ZERO). + setScale(2, RoundingMode.HALF_UP)); + template.addVar("gross_weight", (ecHeader!=null&&ecHeader.getGrossWeight() != null ? ecHeader.getGrossWeight() : BigDecimal.ZERO). + add(palletWeight).setScale(2, RoundingMode.HALF_UP)); + + // 体积计算:优先使用栈板体积,栈板不存在时根据装箱明细的物料计算体积 + BigDecimal totalVolume = BigDecimal.ZERO; + if (!palletHeaderDataList.isEmpty()) { + // 计算所有栈板的体积总和 + for (EcssCoDelPalletHeaderData palletHeader : palletHeaderDataList) { + if (palletHeader.getLength() != null && palletHeader.getWidth() != null + && palletHeader.getHeight() != null && palletHeader.getPalletQty() != null) { + // 单个栈板体积 = 长 * 宽 * 高 * 数量 + BigDecimal singlePalletVolume = palletHeader.getLength() + .multiply(palletHeader.getWidth()) + .multiply(palletHeader.getHeight()) + .multiply(BigDecimal.valueOf(palletHeader.getPalletQty())); + totalVolume = totalVolume.add(singlePalletVolume); + } + } + totalVolume = totalVolume.setScale(2, RoundingMode.HALF_UP); + } else { + // 栈板不存在时,根据装箱明细的物料计算体积 + totalVolume = calculateVolumeByMaterials(notifyHeader); + } + template.addVar("volume", totalVolume); + template.addVar("highest", palletHeaderData.isPresent() ?palletHeaderData.map(ecssCoDelPalletHeaderData -> + ecssCoDelPalletHeaderData.getLength().setScale(2, RoundingMode.HALF_UP) + + "*" + ecssCoDelPalletHeaderData.getWidth().setScale(2, RoundingMode.HALF_UP) + "*" + + ecssCoDelPalletHeaderData.getHeight().setScale(2, RoundingMode.HALF_UP)).orElse(""):""); + template.addVar("pickup_date", notifyHeader.getNotifyDate() != null ? + DateUtils.format(notifyHeader.getNotifyDate(), "yyyy-MM-dd") : ""); + List ecssContacts = coDelMapper.getEcssContacts(notifyHeader.getBuNo()); + if (!ecssContacts.isEmpty()) { + template.addVar("name1", ecssContacts.get(0).get("name")); + template.addVar("phone1", ecssContacts.get(0).get("phone")); + template.addVar("name2", ecssContacts.size()>1?ecssContacts.get(1).get("name"):""); + template.addVar("phone2", ecssContacts.size()>1?ecssContacts.get(1).get("phone"):""); + template.addVar("contacts", ecssContacts.get(0).get("name")+(ecssContacts.size()>1?"/"+ecssContacts.get(1).get("name"):"")); + } + } + + private void extractedContract(EcssDeclarationHeaderData data, ExcelTemplate template) { + // 发货通知单 + EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); + template.addVar("cmcInvoice", notifyHeader.getCmcInvoice()); + template.addVar("readyDate", DateUtils.format(notifyHeader.getReadyDate(), "yyyy/MM/dd")); + List notifyDetailGroup = coDelMapper.getNotifyDetailGroup(data); + BigDecimal totalQty = BigDecimal.ZERO; + BigDecimal totalAmount = BigDecimal.ZERO; + for (int i = 0; i < notifyDetailGroup.size(); i++) { + Map notifyDetail = notifyDetailGroup.get(i); + totalQty = totalQty.add((BigDecimal) notifyDetail.get("total_qty")); + totalAmount = totalAmount.add((BigDecimal) notifyDetail.get("ttl_amount")); + } + template.addVar("totalQty", totalQty); + template.addVar("totalAmount", totalAmount); + template.addListVarAll(notifyDetailGroup); + } + + /** + * 根据装箱明细的物料计算体积 + * 每种物料(每个pn是一种物料)维护的箱子的长*宽*高*箱数 + * + * @return 总体积 + */ + private BigDecimal calculateVolumeByMaterials(EcssCoDelNotifyHeaderData notifyHeader) { + List palletDetailList = coDelMapper.getCoDelPalletDetailGroupByPn(notifyHeader); + + if (palletDetailList == null || palletDetailList.isEmpty()) { + return BigDecimal.ZERO; + } + + try { + // 获取所有物料编号 + List pns = palletDetailList.stream() + .map(detail -> (String) detail.get("pn")) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + + if (pns.isEmpty()) { + return BigDecimal.ZERO; + } + + // 根据物料编号获取packageNo + List packageNoList = coDelMapper.getPackageNoByPn(notifyHeader.getSite(), pns); + Map partNoToPackageNoMap = packageNoList.stream() + .collect(Collectors.toMap( + map -> (String) map.get("pn"), + map -> (String) map.get("packageNo"), + (existing, replacement) -> existing // 处理重复key的情况 + )); + + // 获取所有packageNo对应的包装信息 + List packageNos = packageNoList.stream() + .map(map -> (String) map.get("packageNo")) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + + if (packageNos.isEmpty()) { + log.warn("未找到物料对应的包装信息,物料编号: {}", pns); + return BigDecimal.ZERO; + } + + // 获取包装信息(长宽高) + List packageDataList = coDelMapper.getPackageList(notifyHeader.getSite(), notifyHeader.getBuNo(), packageNos); + Map packageNoToDataMap = packageDataList.stream() + .collect(Collectors.toMap( + EcssPackageData::getPackageNo, + packageData -> packageData, + (existing, replacement) -> existing + )); + + // 计算总体积 + BigDecimal totalVolume = BigDecimal.ZERO; + for (Map detail : palletDetailList) { + String pn = (String) detail.get("pn"); + Object boxQtyObj = detail.get("box_qty"); + + if (pn == null || boxQtyObj == null) { + continue; + } + + // 获取箱数 + BigDecimal boxQty; + if (boxQtyObj instanceof BigDecimal) { + boxQty = (BigDecimal) boxQtyObj; + } else { + boxQty = new BigDecimal(boxQtyObj.toString()); + } + + // 获取包装信息 + String packageNo = partNoToPackageNoMap.get(pn); + if (packageNo == null) { + log.warn("物料 {} 未维护packageNo", pn); + continue; + } + + EcssPackageData packageData = packageNoToDataMap.get(packageNo); + if (packageData == null) { + log.warn("包装编号 {} 未找到对应的包装信息", packageNo); + continue; + } + + // 检查长宽高是否都有值 + if (packageData.getLength() == null || packageData.getWidth() == null || packageData.getHeight() == null) { + log.warn("包装编号 {} 的长宽高信息不完整", packageNo); + continue; + } + + // 计算该物料的体积:长*宽*高*箱数 + BigDecimal materialVolume = packageData.getLength() + .multiply(packageData.getWidth()) + .multiply(packageData.getHeight()) + .multiply(boxQty); + + totalVolume = totalVolume.add(materialVolume); + + log.debug("物料 {} 包装 {} 箱数 {} 单箱体积 {} 总体积 {}", + pn, packageNo, boxQty, + packageData.getLength().multiply(packageData.getWidth()).multiply(packageData.getHeight()), + materialVolume); + } + + return totalVolume.setScale(2, RoundingMode.HALF_UP); + + } catch (Exception e) { + log.error("计算物料体积时发生异常", e); + return BigDecimal.ZERO; + } + } + + /** + * 计算FSC纸重量 + */ + private BigDecimal fscWeight(Map> partNoAndItemNoMap, String partNo, + int totalQty, String lossratio) { + PartSubPropertiesValueData FSC001 = partNoAndItemNoMap.get("FSC001")==null?null:partNoAndItemNoMap.get("FSC001").get(0);//P距 + PartSubPropertiesValueData FSC002 = partNoAndItemNoMap.get("FSC002")==null?null:partNoAndItemNoMap.get("FSC002").get(0);//排数 + PartSubPropertiesValueData FSC003 = partNoAndItemNoMap.get("FSC003")==null?null:partNoAndItemNoMap.get("FSC003").get(0);//1米标签面积 + PartSubPropertiesValueData FSC004 = partNoAndItemNoMap.get("FSC004")==null?null:partNoAndItemNoMap.get("FSC004").get(0);//底纸宽度 + PartSubPropertiesValueData FSC005 = partNoAndItemNoMap.get("FSC005")==null?null:partNoAndItemNoMap.get("FSC005").get(0);//BOM分配张数 + PartSubPropertiesValueData FSC0012 = partNoAndItemNoMap.get("FSC001-02")==null?null:partNoAndItemNoMap.get("FSC001-02").get(0);//P距 + PartSubPropertiesValueData FSC0022 = partNoAndItemNoMap.get("FSC002-02")==null?null:partNoAndItemNoMap.get("FSC002-02").get(0);//排数 + PartSubPropertiesValueData FSC0032 = partNoAndItemNoMap.get("FSC003-02")==null?null:partNoAndItemNoMap.get("FSC003-02").get(0);//1米标签面积 + PartSubPropertiesValueData FSC0042 = partNoAndItemNoMap.get("FSC004-02")==null?null:partNoAndItemNoMap.get("FSC004-02").get(0);//底纸宽度 + PartSubPropertiesValueData FSC0052 = partNoAndItemNoMap.get("FSC005-02")==null?null:partNoAndItemNoMap.get("FSC005-02").get(0);//BOM分配张数 + BigDecimal fscWeight = BigDecimal.valueOf(0); + BigDecimal fscWeight2 = BigDecimal.valueOf(0); + // 千张理论用量(M)=((BOM分配张数/排数)*P距)/1000 + if (FSC001!=null && FSC002!=null && FSC003!=null && FSC004!=null && FSC005!=null) { + BigDecimal bom = FSC005.getNumValue()!=null?BigDecimal.valueOf(FSC005.getNumValue()):BigDecimal.ZERO; + BigDecimal row = FSC002.getNumValue()!=null?BigDecimal.valueOf(FSC002.getNumValue()):BigDecimal.ZERO; + BigDecimal area = FSC003.getNumValue()!=null?BigDecimal.valueOf(FSC003.getNumValue()):BigDecimal.ZERO; + BigDecimal width = FSC004.getNumValue()!=null?BigDecimal.valueOf(FSC004.getNumValue()):BigDecimal.ZERO; + BigDecimal pju = FSC001.getNumValue()!=null?BigDecimal.valueOf(FSC001.getNumValue()):BigDecimal.ZERO; + if (bom.compareTo(BigDecimal.ZERO)!=0 && row.compareTo(BigDecimal.ZERO)!=0 && area.compareTo(BigDecimal.ZERO)!=0 + && width.compareTo(BigDecimal.ZERO)!=0 && pju.compareTo(BigDecimal.ZERO)!=0) { + fscWeight = getFSC(totalQty, lossratio, bom, row, area, width, pju); + } + } + if (FSC0012!=null && FSC0022!=null && FSC0032!=null && FSC0042!=null && FSC0052!=null) { + BigDecimal bom = FSC0052.getNumValue()!=null?BigDecimal.valueOf(FSC0052.getNumValue()):BigDecimal.ZERO; + BigDecimal row = FSC0022.getNumValue()!=null?BigDecimal.valueOf(FSC0022.getNumValue()):BigDecimal.ZERO; + BigDecimal area = FSC0032.getNumValue()!=null?BigDecimal.valueOf(FSC0032.getNumValue()):BigDecimal.ZERO; + BigDecimal width = FSC0042.getNumValue()!=null?BigDecimal.valueOf(FSC0042.getNumValue()):BigDecimal.ZERO; + BigDecimal pju = FSC0012.getNumValue()!=null?BigDecimal.valueOf(FSC0012.getNumValue()):BigDecimal.ZERO; + if (bom.compareTo(BigDecimal.ZERO)!=0 && row.compareTo(BigDecimal.ZERO)!=0 && area.compareTo(BigDecimal.ZERO)!=0 + && width.compareTo(BigDecimal.ZERO)!=0 && pju.compareTo(BigDecimal.ZERO)!=0) { + fscWeight2 = getFSC(totalQty, lossratio, bom, row, area, width, pju); + } + } + return fscWeight.add(fscWeight2).setScale(2, RoundingMode.DOWN); + } + + private BigDecimal getFSC(int totalQty, String lossratio, + BigDecimal bom, BigDecimal row, BigDecimal area, BigDecimal width, BigDecimal pju) { + BigDecimal fscWeight; + BigDecimal qianzhang = ((bom.divide(row,6, RoundingMode.HALF_UP)).multiply(pju)). + divide(BigDecimal.valueOf(1000),6, RoundingMode.HALF_UP); + fscWeight = qianzhang.multiply(BigDecimal.valueOf(totalQty). + divide(BigDecimal.valueOf(1000),6, RoundingMode.HALF_UP)). + multiply(width.divide(BigDecimal.valueOf(1000),6, RoundingMode.HALF_UP)). + multiply(BigDecimal.valueOf(0.16)). + multiply(area.divide(width.divide(BigDecimal.valueOf(1000),6, RoundingMode.HALF_UP),6, RoundingMode.HALF_UP)). + multiply(BigDecimal.valueOf(Double.parseDouble(lossratio))); + return fscWeight; + } + + private void clearPalletData(EcssCoDelNotifyHeaderData inData) { + // 先删除已存在的装箱数据 + EcssCoDelPalletHeaderData data = new EcssCoDelPalletHeaderData(); + data.setDelNo(inData.getDelNo()); + data.setSite(inData.getSite()); + data.setBuNo(inData.getBuNo()); + coDelMapper.deleteBoxList(data); + coDelMapper.deletePalletDetail(data.getSite(),data.getBuNo(),data.getDelNo(),null,null); + } + + @Override + @Transactional + public String saveWalMartOrderByExcel(MultipartFile file, EcssWalMartOrder inData) { + SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); + String site = coDelMapper.getSiteByBu(inData.getBuNo()); + List excelList = new ArrayList<>(); + try { + // 转流 + InputStream is = file.getInputStream(); + // 读取工作簿 + XSSFWorkbook workbook = new XSSFWorkbook(is); + // 读取工作表 + XSSFSheet sheet = workbook.getSheetAt(0); + // 剔除空行 + deleteEmptyRow(sheet); + // 获取行数 + int rows = sheet.getPhysicalNumberOfRows(); + // 遍历每一行(从第二行开始) + StringBuilder sb = new StringBuilder("第"); + for (int j = 1; j < rows; j++) { + // 创建对象 + EcssWalMartOrder task = new EcssWalMartOrder(); + // 获得该行 + XSSFRow row = sheet.getRow(j); + if (row.getCell(0) == null ) { + throw new RuntimeException("第" + j + "行的SKU不能为空!"); + } + // 为对象赋值 + task.setSite(site); // site + task.setBuNo(inData.getBuNo()); // bu + task.setSku(getStringCellValue(row, 0)); + task.setSo(getStringCellValue(row, 1)); + task.setQty(getNumericCellValueOrDefault(row, 2, "Qty (pcs)")); + List orderDataList = sqlSession.selectList("ecssMapper" + "." + "searchWalMartOrderList", task); + if (!orderDataList.isEmpty()) { + if (sb.toString().length()>1) { + sb.append(",").append(j); + } else { + sb.append(j); + } + //throw new RuntimeException("第" + j + "行的SKU和数量已存在!"); + continue; + } + task.setRolls(getNumericCellValueOrDefault(row, 3)); + task.setGrossWeight(getNumericCellValueOrDefault(row, 4)); + task.setVerificationSheet(getStringCellValue(row, 5)); + task.setCreateBy(currentUser.getUserDisplay()); + excelList.add(task); + } + if (!excelList.isEmpty()) { + coDelMapper.batchSaveWalMartOrder(excelList); + } + if (sb.toString().equals("第")) { + sb= new StringBuilder(); + } else { + sb.append("行的SKU和数量已存在!"); + } + return sb.toString(); + } catch (Exception e) { + throw new RuntimeException("导入失败:" + e.getMessage()); + } + } + + private static void deleteEmptyRow(XSSFSheet sheet) { + // 从第三行开始遍历,检查每一行是否为空 + for (int i = 2; i <= sheet.getLastRowNum(); i++) { + XSSFRow row = sheet.getRow(i); + boolean isEmptyRow = true; + if (row != null) { + for (Cell cell : row) { + if (cell.getCellType() != CellType.BLANK && cell.getCellType() != CellType.ERROR && + !StringUtils.isBlank(String.valueOf(cell))) { + isEmptyRow = false; + break; + } + } + if (isEmptyRow) { + sheet.removeRow(row); // 删除空行 + } + } + } + } + + /** + * 检查物料存在性 + * @param excelList 导入的数据列表 + * @param site 工厂 + * @param username 用户名 + * @param buNo 业务单元 + * @return 返回包含不存在物料的发票号及其对应的不存在物料列表 + */ + private Map> checkMaterialsExistence(List excelList, String site, String username, String buNo) { + Map> invalidMaterialsByInvoice = new HashMap<>(); + + // 按发票号分组 + Map> groupedByInvoice = excelList.stream() + .collect(Collectors.groupingBy(EcssCoDelNotifyData::getCmcInvoice)); + + // 检查每个发票的物料 + for (Map.Entry> entry : groupedByInvoice.entrySet()) { + String invoice = entry.getKey(); + List invoiceData = entry.getValue(); + List invalidMaterials = new ArrayList<>(); + + // 检查该发票下的每个物料 + for (EcssCoDelNotifyData data : invoiceData) { + String pn = data.getPn(); + if (pn != null && !pn.trim().isEmpty()) { + // 使用 getPartNo 方法检查物料是否存在 + List parts = coDelMapper.getPartNo(site, pn, username, buNo); + if (parts.isEmpty()) { + // 物料不存在,添加到无效物料列表 + if (!invalidMaterials.contains(pn)) { + invalidMaterials.add(pn); + } + } + + // 测试用:如果物料号以 "TEST" 开头,强制认为不存在(用于测试功能) + if (pn.startsWith("TEST") && !invalidMaterials.contains(pn)) { + invalidMaterials.add(pn); + log.warn("测试:强制认为物料不存在: {} (发票号: {})", pn, invoice); + } + } + } + + // 如果该发票有不存在的物料,记录下来 + if (!invalidMaterials.isEmpty()) { + invalidMaterialsByInvoice.put(invoice, invalidMaterials); + } + } + + return invalidMaterialsByInvoice; + } + + /** + * 解析删除的发票号列表 + * @param deletedInvoices JSON格式的删除发票号字符串 + * @return 删除的发票号列表 + */ + private List parseDeletedInvoices(String deletedInvoices) { + if (deletedInvoices == null || deletedInvoices.trim().isEmpty()) { + return new ArrayList<>(); + } + + try { + ObjectMapper objectMapper = new ObjectMapper(); + // 解析JSON数组字符串为List + List invoiceList = objectMapper.readValue(deletedInvoices, + objectMapper.getTypeFactory().constructCollectionType(List.class, String.class)); + return invoiceList != null ? invoiceList : new ArrayList<>(); + } catch (Exception e) { + log.error("解析删除发票号JSON失败: {}, 原始数据: {}", e.getMessage(), deletedInvoices); + throw new RuntimeException("删除发票号数据格式错误"); + } + } + + /** + * 改单导入后处理通知单状态 + * + *

    处理逻辑:

    + *
      + *
    • 如果当前状态是已报关,删除对应的报关单
    • + *
    • 如果修改了PN/Qty或删除了PN:删除托盘数据,状态更新为"已下达"
    • + *
    • 如果仅修改了其他字段(Destination、Shipping Mode、Currency、TP等):不删除托盘数据,状态更新为"仓库已确认"
    • + *
    + * + * @param headerData 发货通知单头数据 + * @param isPnOrQtyModified 是否修改了PN/Qty或删除了PN + * @author AI Assistant + * @date 2024-10-09 + */ + private void handleNotifyStatusAfterModify(EcssCoDelNotifyHeaderData headerData, boolean isPnOrQtyModified) { + try { + log.info("开始处理改单导入后的状态更新,发货通知单号:{}, 当前状态:{}, PN/Qty是否修改:{}", + headerData.getDelNo(), headerData.getNotifyStatus(), isPnOrQtyModified); + + // 检查当前通知单状态 + String currentStatus = headerData.getNotifyStatus(); + + // 如果当前状态是已报关,需要删除对应的报关单 + if ("已报关".equals(currentStatus)) { + log.info("发货通知单{}状态为已报关,开始删除对应的报关单", headerData.getDelNo()); + + // 查找对应的报关单 + EcssDeclarationHeaderData declarationQuery = new EcssDeclarationHeaderData(); + declarationQuery.setSite(headerData.getSite()); + declarationQuery.setDelNo(headerData.getDelNo()); + + List declarations = coDelMapper.searchDeclarationHeader( + new Page(1, 1000), declarationQuery).getRecords(); + + // 删除找到的报关单 + for (EcssDeclarationHeaderData declaration : declarations) { + log.info("删除报关单,报关单号:{}", declaration.getDeclarationNo()); + coDelMapper.deleteDeclarationHeader(declaration); + coDelMapper.deleteDeclarationDetail(declaration); + } + + log.info("已删除发货通知单{}对应的{}个报关单", headerData.getDelNo(), declarations.size()); + } + + // 根据修改类型决定后续操作 + if (isPnOrQtyModified) { + // 修改了PN/Qty或删除了PN:需要删除托盘数据,状态更新为"已下达" + log.info("检测到PN/Qty修改或PN删除,删除托盘数据并将状态更新为已下达"); + headerData.setNotifyStatus("已下达"); + coDelMapper.changeEcssDelStatus(headerData); + clearPalletData(headerData); + EcssCoDelPalletHeaderData inData = new EcssCoDelPalletHeaderData(); + inData.setSite(headerData.getSite()); + inData.setBuNo(headerData.getBuNo()); + inData.setDelNo(headerData.getDelNo()); + coDelMapper.deletePalletHeader(inData); + log.info("发货通知单{}状态已更新为已下达,托盘数据已删除", headerData.getDelNo()); + } else { + // 仅修改了其他字段(Destination、Shipping Mode、Currency、TP等):不删除托盘数据,状态更新为"仓库已确认" + log.info("仅修改了其他字段(Destination、Shipping Mode、Currency、TP等),保留托盘数据并将状态更新为仓库已确认"); + headerData.setNotifyStatus("仓库已确认"); + coDelMapper.changeEcssDelStatus(headerData); + log.info("发货通知单{}状态已更新为仓库已确认,托盘数据已保留", headerData.getDelNo()); + } + + } catch (Exception e) { + log.error("处理改单导入后状态更新失败,发货通知单号:{}, 错误信息:{}", + headerData.getDelNo(), e.getMessage(), e); + throw new RuntimeException("改单导入后状态处理失败:" + e.getMessage()); + } + } + + /** + * 生成箱子列表对比表格 + * + * @param oldBoxList 计算前的箱子列表 + * @param newBoxList 计算后的箱子列表 + * @return HTML表格内容 + */ + private String generateBoxListComparisonTable(List oldBoxList, List newBoxList) { + StringBuilder table = new StringBuilder(); + + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + + // 创建映射以便对比 + Map oldBoxMap = new HashMap<>(); + for (Map box : oldBoxList) { + Integer itemNo = (Integer) box.get("item_no"); + oldBoxMap.put(itemNo, box); + } + + Map newBoxMap = new HashMap<>(); + for (Map box : newBoxList) { + Integer itemNo = (Integer) box.get("item_no"); + newBoxMap.put(itemNo, box); + } + + // 获取所有序号 + Set allItemNos = new HashSet<>(); + allItemNos.addAll(oldBoxMap.keySet()); + allItemNos.addAll(newBoxMap.keySet()); + + List sortedItemNos = new ArrayList<>(allItemNos); + sortedItemNos.sort(Integer::compareTo); + + for (Integer itemNo : sortedItemNos) { + Map oldBox = oldBoxMap.get(itemNo); + Map newBox = newBoxMap.get(itemNo); + + table.append(""); + table.append(""); + + // 毛重对比 + BigDecimal oldGrossWeight = oldBox != null ? (BigDecimal) oldBox.get("gross_weight") : BigDecimal.ZERO; + BigDecimal newGrossWeight = newBox != null ? (BigDecimal) newBox.get("gross_weight") : BigDecimal.ZERO; + table.append(""); + if (oldGrossWeight.compareTo(newGrossWeight) != 0) { + table.append(""); + } else { + table.append(""); + } + + // 净重对比 + BigDecimal oldNetWeight = oldBox != null ? (BigDecimal) oldBox.get("net_weight") : BigDecimal.ZERO; + BigDecimal newNetWeight = newBox != null ? (BigDecimal) newBox.get("net_weight") : BigDecimal.ZERO; + table.append(""); + if (oldNetWeight.compareTo(newNetWeight) != 0) { + table.append(""); + } else { + table.append(""); + } + + // 箱数对比 + BigDecimal oldBoxQty = oldBox != null ? (BigDecimal) oldBox.get("box_qty") : BigDecimal.ZERO; + BigDecimal newBoxQty = newBox != null ? (BigDecimal) newBox.get("box_qty") : BigDecimal.ZERO; + table.append(""); + if (oldBoxQty.compareTo(newBoxQty) != 0) { + table.append(""); + } else { + table.append(""); + } + + table.append(""); + } + + table.append("
    序号毛重(计算前)毛重(计算后)净重(计算前)净重(计算后)箱数(计算前)箱数(计算后)
    ").append(itemNo).append("").append(formatDecimal(oldGrossWeight)).append("").append(formatDecimal(newGrossWeight)).append("").append(formatDecimal(newGrossWeight)).append("").append(formatDecimal(oldNetWeight)).append("").append(formatDecimal(newNetWeight)).append("").append(formatDecimal(newNetWeight)).append("").append(formatDecimal(oldBoxQty)).append("").append(formatDecimal(newBoxQty)).append("").append(formatDecimal(newBoxQty)).append("
    "); + return table.toString(); + } + + /** + * 生成装箱明细对比表格 + * + * @param oldPalletDetailList 计算前的装箱明细列表 + * @param newPalletDetailList 计算后的装箱明细列表 + * @return HTML表格内容 + */ + private String generatePalletDetailComparisonTable(List oldPalletDetailList, + List newPalletDetailList) { + StringBuilder table = new StringBuilder(); + + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + table.append(""); + + // 创建映射以便对比 - 使用序号+项次作为唯一标识 + Map oldDetailMap = new HashMap<>(); + for (EcssCoDelPalletDetailData detail : oldPalletDetailList) { + String key = detail.getSeqNo() + "_" + detail.getItemNo(); + oldDetailMap.put(key, detail); + } + + Map newDetailMap = new HashMap<>(); + for (EcssCoDelPalletDetailData detail : newPalletDetailList) { + String key = detail.getSeqNo() + "_" + detail.getItemNo(); + newDetailMap.put(key, detail); + } + + // 获取所有键值 + Set allKeys = new HashSet<>(); + allKeys.addAll(oldDetailMap.keySet()); + allKeys.addAll(newDetailMap.keySet()); + + List sortedKeys = new ArrayList<>(allKeys); + sortedKeys.sort((a, b) -> { + String[] aParts = a.split("_"); + String[] bParts = b.split("_"); + int seqCompare = Integer.compare(Integer.parseInt(aParts[0]), Integer.parseInt(bParts[0])); + if (seqCompare != 0) return seqCompare; + return Integer.compare(Integer.parseInt(aParts[1]), Integer.parseInt(bParts[1])); + }); + + for (String key : sortedKeys) { + EcssCoDelPalletDetailData oldDetail = oldDetailMap.get(key); + EcssCoDelPalletDetailData newDetail = newDetailMap.get(key); + + String[] keyParts = key.split("_"); + Integer seqNo = Integer.parseInt(keyParts[0]); + Integer itemNo = Integer.parseInt(keyParts[1]); + + table.append(""); + table.append(""); + table.append(""); + + // 物料号和PN(取新数据或旧数据) + EcssCoDelPalletDetailData referenceDetail = newDetail != null ? newDetail : oldDetail; + table.append(""); + table.append(""); + + // 数量对比 + BigDecimal oldQty = oldDetail != null ? oldDetail.getQty() : BigDecimal.ZERO; + BigDecimal newQty = newDetail != null ? newDetail.getQty() : BigDecimal.ZERO; + table.append(""); + if (oldQty.compareTo(newQty) != 0) { + table.append(""); + } else { + table.append(""); + } + + // 箱数对比 + BigDecimal oldBoxQty = oldDetail != null ? oldDetail.getBoxQty() : BigDecimal.ZERO; + BigDecimal newBoxQty = newDetail != null ? newDetail.getBoxQty() : BigDecimal.ZERO; + table.append(""); + if (oldBoxQty.compareTo(newBoxQty) != 0) { + table.append(""); + } else { + table.append(""); + } + + // 卷数对比 + BigDecimal oldRolls = oldDetail != null ? oldDetail.getRolls() : BigDecimal.ZERO; + BigDecimal newRolls = newDetail != null ? newDetail.getRolls() : BigDecimal.ZERO; + table.append(""); + if (oldRolls.compareTo(newRolls) != 0) { + table.append(""); + } else { + table.append(""); + } + + table.append(""); + } + + table.append("
    序号项次物料号PN数量(计算前)数量(计算后)箱数(计算前)箱数(计算后)卷数(计算前)卷数(计算后)
    ").append(seqNo).append("").append(itemNo).append("").append(referenceDetail.getPartNo() != null ? referenceDetail.getPartNo() : "").append("").append(referenceDetail.getPn() != null ? referenceDetail.getPn() : "").append("").append(formatDecimal(oldQty)).append("").append(formatDecimal(newQty)).append("").append(formatDecimal(newQty)).append("").append(formatDecimal(oldBoxQty)).append("").append(formatDecimal(newBoxQty)).append("").append(formatDecimal(newBoxQty)).append("").append(formatDecimal(oldRolls)).append("").append(formatDecimal(newRolls)).append("").append(formatDecimal(newRolls)).append("
    "); + return table.toString(); + } + + /** + * 格式化BigDecimal数值显示 + * + * @param value 数值 + * @return 格式化后的字符串 + */ + private String formatDecimal(BigDecimal value) { + if (value == null) { + return "0"; + } + return value.stripTrailingZeros().toPlainString(); + } + + + /** + * 生成箱子变更邮件内容 + * + * @param notifyHeader 发货通知单头数据 + * @param oldBoxList 操作前的箱子列表 + * @param newBoxList 操作后的箱子列表 + * @param operation 操作类型 + * @param boxData 操作的箱子数据 + * @return HTML格式的邮件内容 + */ + private String generateBoxChangeEmailContent(EcssCoDelNotifyHeaderData notifyHeader, + List oldBoxList, + List newBoxList, + String operation, + Map boxData) { + StringBuilder emailContent = new StringBuilder(); + + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + + // 邮件标题 + emailContent.append("

    发货通知单箱子").append(operation).append("通知

    "); + emailContent.append("

    发货通知单号:").append(notifyHeader.getDelNo()).append("

    "); + emailContent.append("

    发票号:").append(notifyHeader.getCmcInvoice()).append("

    "); + emailContent.append("

    客户:").append(notifyHeader.getCustomerName()).append("

    "); + emailContent.append("

    操作时间:").append(DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss")).append("

    "); + emailContent.append("

    操作类型:").append(operation).append("箱子(序号:").append(boxData.get("item_no")).append(")

    "); + + // 箱子信息对比 + emailContent.append("
    箱子信息对比
    "); + emailContent.append(generateBoxListComparisonTable(oldBoxList, newBoxList)); + + emailContent.append(""); + emailContent.append(""); + + return emailContent.toString(); + } + + /** + * 生成箱明细变更邮件内容 + * + * @param notifyHeader 发货通知单头数据 + * @param oldPalletDetailList 操作前的装箱明细列表 + * @param newPalletDetailList 操作后的装箱明细列表 + * @param operation 操作类型 + * @param detailData 操作的明细数据 + * @return HTML格式的邮件内容 + */ + private String generateDetailChangeEmailContent(EcssCoDelNotifyHeaderData notifyHeader, + List oldPalletDetailList, + List newPalletDetailList, + String operation, + EcssCoDelPalletData detailData) { + StringBuilder emailContent = new StringBuilder(); + + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + emailContent.append(""); + + // 邮件标题 + emailContent.append("

    发货通知单箱明细").append(operation).append("通知

    "); + emailContent.append("

    发货通知单号:").append(notifyHeader.getDelNo()).append("

    "); + emailContent.append("

    发票号:").append(notifyHeader.getCmcInvoice()).append("

    "); + emailContent.append("

    客户:").append(notifyHeader.getCustomerName()).append("

    "); + emailContent.append("

    操作时间:").append(DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss")).append("

    "); + emailContent.append("

    操作类型:").append(operation).append("箱明细(序号:").append(detailData.getSeqNo()).append(",项次:").append(detailData.getItemNo()).append(")

    "); + + // 装箱明细对比 + emailContent.append("
    装箱明细对比
    "); + emailContent.append(generatePalletDetailComparisonTable(oldPalletDetailList, newPalletDetailList)); + + emailContent.append(""); + emailContent.append(""); + + return emailContent.toString(); + } + + /** + * @Description 导入物料包装属性(每卷数量、每箱卷数、每卷重量、箱重量、箱类型、FSC标签属性) + * @param file Excel文件 + * @param buNo 业务单元编码 + * @param username 用户名 + * @param previewOnly 是否仅预览 + * @return 预览数据或导入结果 + */ + @Override + @Transactional + public Map importPartPackageProperties(MultipartFile file, String buNo, String username, boolean previewOnly) { + Map result = new HashMap<>(); + List> previewList = new ArrayList<>(); + List> detailsList = new ArrayList<>(); + int successCount = 0; + int failCount = 0; + + try { + // 获取用户的site信息 + SysUserEntity sysUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); + String site = sysUser.getSite(); + + // 解析Excel文件 + XSSFWorkbook workbook = new XSSFWorkbook(file.getInputStream()); + XSSFSheet sheet = workbook.getSheetAt(0); + DataFormatter dataFormatter = new DataFormatter(); + + // 从第二行开始(跳过表头) + int lastRowNum = sheet.getLastRowNum(); + for (int rowNum = 1; rowNum <= lastRowNum; rowNum++) { + XSSFRow row = sheet.getRow(rowNum); + if (row == null) continue; + + Map rowData = new HashMap<>(); + rowData.put("rowNum", rowNum + 1); + + try { + // 读取Excel字段:物料编码 每卷重量 每卷数量 每箱卷数 Box No 箱重量(kg) 长(cm) 宽(cm) 高(cm) + String sku = getCellValueAsString(row.getCell(0), dataFormatter); + String rollWeightStr = getCellValueAsString(row.getCell(1), dataFormatter); + String rollQtyStr = getCellValueAsString(row.getCell(2), dataFormatter); + String boxRollsStr = getCellValueAsString(row.getCell(3), dataFormatter); + String boxNo = getCellValueAsString(row.getCell(4), dataFormatter); + String boxWeightStr = getCellValueAsString(row.getCell(5), dataFormatter); + String lengthStr = getCellValueAsString(row.getCell(6), dataFormatter); + String widthStr = getCellValueAsString(row.getCell(7), dataFormatter); + String heightStr = getCellValueAsString(row.getCell(8), dataFormatter); + + // 读取FSC标签属性字段:P距 排数 1米标签面积 底纸宽度(MM) BOM分配张数 P距-2 排数-2 1米标签面积-2 底纸宽度(MM)-2 BOM分配张数-2 + String fsc001Str = getCellValueAsString(row.getCell(9), dataFormatter); // P距 + String fsc002Str = getCellValueAsString(row.getCell(10), dataFormatter); // 排数 + String fsc003Str = getCellValueAsString(row.getCell(11), dataFormatter); // 1米标签面积 + String fsc004Str = getCellValueAsString(row.getCell(12), dataFormatter); // 底纸宽度(MM) + String fsc005Str = getCellValueAsString(row.getCell(13), dataFormatter); // BOM分配张数 + String fsc00102Str = getCellValueAsString(row.getCell(14), dataFormatter); // P距-2 + String fsc00202Str = getCellValueAsString(row.getCell(15), dataFormatter); // 排数-2 + String fsc00302Str = getCellValueAsString(row.getCell(16), dataFormatter); // 1米标签面积-2 + String fsc00402Str = getCellValueAsString(row.getCell(17), dataFormatter); // 底纸宽度(MM)-2 + String fsc00502Str = getCellValueAsString(row.getCell(18), dataFormatter); // BOM分配张数-2 + // 跳过空行 + if (StringUtils.isBlank(sku)) { + continue; + } + rowData.put("buNo", buNo); + rowData.put("sku", sku); + rowData.put("rollQty", rollQtyStr); + rowData.put("boxRolls", boxRollsStr); + rowData.put("rollWeight", rollWeightStr); + rowData.put("boxWeight", boxWeightStr); + rowData.put("boxNo", boxNo); + rowData.put("length", lengthStr); + rowData.put("width", widthStr); + rowData.put("height", heightStr); + + // FSC标签属性字段 + rowData.put("fsc001", fsc001Str); + rowData.put("fsc002", fsc002Str); + rowData.put("fsc003", fsc003Str); + rowData.put("fsc004", fsc004Str); + rowData.put("fsc005", fsc005Str); + rowData.put("fsc001-02", fsc00102Str); + rowData.put("fsc002-02", fsc00202Str); + rowData.put("fsc003-02", fsc00302Str); + rowData.put("fsc004-02", fsc00402Str); + rowData.put("fsc005-02", fsc00502Str); + + // 验证BU + if (StringUtils.isBlank(buNo)) { + rowData.put("hasError", true); + rowData.put("errorMsg", "BU不能为空"); + previewList.add(rowData); + continue; + } + + // 根据SKU查询物料信息(一个SKU可能对应多个part) + Map partParams = new HashMap<>(); + partParams.put("site", site); + partParams.put("buNo", buNo); + partParams.put("sku", sku); + List partList = coDelMapper.getPartListBySku(partParams); + + if (partList == null || partList.isEmpty()) { + rowData.put("hasError", true); + rowData.put("errorMsg", "物料编码不存在"); + previewList.add(rowData); + continue; + } + + // 收集所有partNo用于显示 + String partNos = partList.stream().map(PartData::getPartNo).collect(Collectors.joining(",")); + rowData.put("partNo", partNos); + rowData.put("partCount", partList.size()); + rowData.put("hasError", false); + + // 预览模式只返回数据,不执行导入 + if (previewOnly) { + previewList.add(rowData); + continue; + } + + // 导入模式:执行数据导入(为所有匹配的part设置属性) + StringBuilder successMsg = new StringBuilder(); + StringBuilder failMsg = new StringBuilder(); + boolean hasSuccess = false; + boolean hasFail = false; + int updatedPartCount = 0; + + // 处理属性值:每卷数量(ROLLQTY)、每箱卷数(BOXROLLS)、每卷重量(ROLLWEIGHT)、箱重量(BOXWEIGHT)、FSC标签属性 + String[] itemNos = {"ROLLQTY", "BOXROLLS", "ROLLWEIGHT", "BOXWEIGHT", + "FSC001", "FSC002", "FSC003", "FSC004", "FSC005", + "FSC001-02", "FSC002-02", "FSC003-02", "FSC004-02", "FSC005-02"}; + String[] itemValues = {rollQtyStr, boxRollsStr, rollWeightStr, boxWeightStr, + fsc001Str, fsc002Str, fsc003Str, fsc004Str, fsc005Str, + fsc00102Str, fsc00202Str, fsc00302Str, fsc00402Str, fsc00502Str}; + String[] itemNames = {"每卷数量", "每箱卷数", "每卷重量", "箱重量", + "P距", "排数", "1米标签面积", "底纸宽度(MM)", "BOM分配张数", + "P距-2", "排数-2", "1米标签面积-2", "底纸宽度(MM)-2", "BOM分配张数-2"}; + + // 为每个匹配的part设置属性 + for (PartData partData : partList) { + String partNo = partData.getPartNo(); + + for (int i = 0; i < itemNos.length; i++) { + String itemNo = itemNos[i]; + String itemValue = itemValues[i]; + String itemName = itemNames[i]; + + if (StringUtils.isNotBlank(itemValue)) { + try { + Double numValue = Double.parseDouble(itemValue); + boolean updated = updateOrInsertPartProperty(site, buNo, partNo, itemNo, numValue, username); + if (updated) { + hasSuccess = true; + } + } catch (NumberFormatException e) { + if (!failMsg.toString().contains(itemName + "格式错误")) { + failMsg.append(itemName).append("格式错误; "); + } + hasFail = true; + } + } + } + updatedPartCount++; + } + + if (hasSuccess) { + successMsg.append("属性更新成功(").append(updatedPartCount).append("个物料); "); + } + + // 处理箱类型:Box No(对应packageType)、长、宽、高 + // 箱类型只需要处理一次,然后为所有part更新packageNo + if (StringUtils.isNotBlank(boxNo)) { + try { + BigDecimal length = StringUtils.isNotBlank(lengthStr) ? new BigDecimal(lengthStr).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP) : null; + BigDecimal width = StringUtils.isNotBlank(widthStr) ? new BigDecimal(widthStr).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP) : null; + BigDecimal height = StringUtils.isNotBlank(heightStr) ? new BigDecimal(heightStr).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP) : null; + + // 检查箱类型是否存在(根据packageType),不存在则插入,返回packageNo + String packageNo = checkAndInsertPackageByType(site, buNo, boxNo, length, width, height, username); + + // 为所有匹配的part更新packageNo + for (PartData partData : partList) { + updatePartPackageNo(site, partData.getPartNo(), packageNo); + } + + successMsg.append("箱类型更新成功(").append(partList.size()).append("个物料); "); + hasSuccess = true; + } catch (Exception e) { + failMsg.append("箱类型处理失败: ").append(e.getMessage()).append("; "); + hasFail = true; + } + } + + // 记录结果 + Map detail = new HashMap<>(); + detail.put("sku", sku); + String partInfo = partList.size() > 1 ? "(共" + partList.size() + "个物料)" : ""; + if (hasSuccess && !hasFail) { + detail.put("success", true); + detail.put("message", "物料[" + sku + "]" + partInfo + " " + successMsg.toString()); + successCount++; + } else if (hasFail && !hasSuccess) { + detail.put("success", false); + detail.put("message", "物料[" + sku + "]" + partInfo + " " + failMsg.toString()); + failCount++; + } else if (hasSuccess && hasFail) { + detail.put("success", true); + detail.put("message", "物料[" + sku + "]" + partInfo + " 部分成功: " + successMsg.toString() + " 失败: " + failMsg.toString()); + successCount++; + } else { + detail.put("success", true); + detail.put("message", "物料[" + sku + "]" + partInfo + " 无需更新"); + successCount++; + } + detailsList.add(detail); + + } catch (Exception e) { + log.error("处理行{}时发生错误: {}", rowNum + 1, e.getMessage(), e); + rowData.put("hasError", true); + rowData.put("errorMsg", "处理失败: " + e.getMessage()); + if (previewOnly) { + previewList.add(rowData); + } else { + Map detail = new HashMap<>(); + detail.put("success", false); + detail.put("message", "行" + (rowNum + 1) + " 处理失败: " + e.getMessage()); + detailsList.add(detail); + failCount++; + } + } + } + + workbook.close(); + + } catch (Exception e) { + log.error("导入物料包装属性失败: {}", e.getMessage(), e); + throw new RuntimeException("导入失败: " + e.getMessage()); + } + + if (previewOnly) { + result.put("data", previewList); + } else { + result.put("successCount", successCount); + result.put("failCount", failCount); + result.put("details", detailsList); + } + + return result; + } + + /** + * 更新或插入物料属性值 + * @param site 站点 + * @param buNo 业务单元 + * @param partNo 物料编号 + * @param propertiesItemNo 属性项编号 (ROLLQTY/BOXROLLS/ROLLWEIGHT/BOXWEIGHT) + * @param numValue 数值 + * @param username 用户名 + * @return 是否成功 + */ + private boolean updateOrInsertPartProperty(String site, String buNo, String partNo, String propertiesItemNo, Double numValue, String username) { + String codeNo = "BG001"; + String recordType = "ECSSPART"; + + // 检查属性是否已存在 + Map checkParams = new HashMap<>(); + checkParams.put("site", site); + checkParams.put("buNo", buNo); + checkParams.put("partNo", partNo); + checkParams.put("codeNo", codeNo); + checkParams.put("recordType", recordType); + checkParams.put("propertiesItemNo", propertiesItemNo); + + Map existingProperty = coDelMapper.checkPartPropertyExists(checkParams); + + if (existingProperty != null) { + // 已存在,执行更新 + Map updateParams = new HashMap<>(); + updateParams.put("site", site); + updateParams.put("buNo", buNo); + updateParams.put("partNo", partNo); + updateParams.put("codeNo", codeNo); + updateParams.put("recordType", recordType); + updateParams.put("propertiesItemNo", propertiesItemNo); + updateParams.put("numValue", numValue); + coDelMapper.updatePartPropertyNumValue(updateParams); + } else { + // 不存在,执行插入 + // 获取当前最大的item_no + Map maxParams = new HashMap<>(); + maxParams.put("site", site); + maxParams.put("buNo", buNo); + maxParams.put("partNo", partNo); + maxParams.put("codeNo", codeNo); + maxParams.put("recordType", recordType); + Double maxItemNo = coDelMapper.getMaxItemNo(maxParams); + Double newItemNo = (maxItemNo == null) ? 1.0 : maxItemNo + 1.0; + + // 获取属性定义信息 + Map itemParams = new HashMap<>(); + itemParams.put("site", site); + itemParams.put("buNo", buNo); + itemParams.put("itemNo", propertiesItemNo); + itemParams.put("itemType", recordType); + Map itemInfo = coDelMapper.getPropertiesItemInfo(itemParams); + + // 插入新记录 + Map insertParams = new HashMap<>(); + insertParams.put("site", site); + insertParams.put("buNo", buNo); + insertParams.put("partNo", partNo); + insertParams.put("codeNo", codeNo); + insertParams.put("subCodeSeqNo", 1); + insertParams.put("subCodeDesc", itemInfo != null ? itemInfo.get("codeDesc") : "基本信息"); + insertParams.put("itemNo", newItemNo); + insertParams.put("propertiesItemNo", propertiesItemNo); + insertParams.put("textValue", null); + insertParams.put("numValue", numValue); + insertParams.put("recordType", recordType); + coDelMapper.insertPartProperty(insertParams); + } + + return true; + } + + /** + * 根据packageType检查并插入箱类型 + * @param site 站点 + * @param buNo 业务单元 + * @param packageType 箱类型(对应Excel中的Box No) + * @param length 长 + * @param width 宽 + * @param height 高 + * @param username 用户名 + * @return packageNo 箱编码(用于更新part表) + */ + private String checkAndInsertPackageByType(String site, String buNo, String packageType, BigDecimal length, BigDecimal width, BigDecimal height, String username) { + // 根据packageType检查箱类型是否存在 + Map checkParams = new HashMap<>(); + checkParams.put("site", site); + checkParams.put("buNo", buNo); + checkParams.put("packageType", packageType); + Map existingPackage = coDelMapper.checkPackageExistsByType(checkParams); + + String packageNo; + + if (existingPackage == null) { + // 不存在,插入新箱类型 + // 获取当前最大的packageNo并+1 + Map maxParams = new HashMap<>(); + maxParams.put("site", site); + maxParams.put("buNo", buNo); + String maxPackageNo = coDelMapper.getMaxPackageNo(maxParams); + + // 生成新的packageNo + if (StringUtils.isBlank(maxPackageNo)) { + packageNo = buNo.equals("01-Label")?"10001":buNo.equals("03-RFID")?"20001":"30001"; + } else { + // 尝试解析并递增 + try { + // 尝试纯数字递增 + int num = Integer.parseInt(maxPackageNo); + packageNo = String.valueOf(num + 1); + } catch (NumberFormatException e) { + // 无法解析,使用时间戳 + packageNo = (buNo.equals("01-Label")?"1":buNo.equals("03-RFID")?"2":"3") + System.currentTimeMillis(); + } + } + + // 计算体积 + BigDecimal volume = null; + if (length != null && width != null && height != null) { + volume = length.multiply(width).multiply(height); + } + + Map insertParams = new HashMap<>(); + insertParams.put("site", site); + insertParams.put("buNo", buNo); + insertParams.put("packageNo", packageNo); + insertParams.put("packageType", packageType); + insertParams.put("length", length); + insertParams.put("width", width); + insertParams.put("height", height); + insertParams.put("volume", volume); + insertParams.put("weight", null); + insertParams.put("remark", "导入创建"); + insertParams.put("createBy", username); + coDelMapper.insertPackage(insertParams); + } else { + // 已存在,获取现有的packageNo并更新长宽高 + packageNo = (String) existingPackage.get("packageNo"); + + Map updateParams = new HashMap<>(); + updateParams.put("site", site); + updateParams.put("buNo", buNo); + updateParams.put("packageType", packageType); + updateParams.put("length", length); + updateParams.put("width", width); + updateParams.put("height", height); + // 计算体积 + if (length != null && width != null && height != null) { + updateParams.put("volume", length.multiply(width).multiply(height)); + } + updateParams.put("updateBy", username); + coDelMapper.updatePackageDimensions(updateParams); + } + + return packageNo; + } + + /** + * 更新物料的packageNo + * @param site 站点 + * @param partNo 物料编号 + * @param packageNo 箱编码 + */ + private void updatePartPackageNo(String site, String partNo, String packageNo) { + Map updateParams = new HashMap<>(); + updateParams.put("site", site); + updateParams.put("partNo", partNo); + updateParams.put("packageNo", packageNo); + coDelMapper.updatePartPackageNo(updateParams); + } + + /** + * 获取单元格值为字符串 + */ + private String getCellValueAsString(Cell cell, DataFormatter dataFormatter) { + if (cell == null) { + return ""; + } + return dataFormatter.formatCellValue(cell).trim(); + } + +} diff --git a/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelServiceImpl.java b/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelServiceImpl.java index 992c9466..ad3a3268 100644 --- a/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelServiceImpl.java +++ b/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelServiceImpl.java @@ -4,17 +4,10 @@ package com.xujie.sys.modules.ecss.service.impl; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.xujie.sys.common.utils.DateUtils; -import com.xujie.sys.common.utils.ExcelTemplate; import com.xujie.sys.common.utils.MailUtil; import com.xujie.sys.common.utils.PageUtils; -import com.aspose.cells.Workbook; -import com.aspose.cells.SaveFormat; -import com.aspose.cells.PageSetup; -import com.aspose.cells.PageOrientationType; -import com.aspose.cells.PaperSizeType; import com.xujie.sys.modules.attrbute.entity.PropertyModelHeader; import com.xujie.sys.modules.ecss.data.*; -import com.xujie.sys.modules.ecss.dto.SheetErrorInfo; import com.xujie.sys.modules.ecss.entity.*; import com.xujie.sys.modules.ecss.mapper.CoDelMapper; import com.xujie.sys.modules.ecss.service.CoDelService; @@ -29,38 +22,17 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.ibatis.session.SqlSession; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.CellType; -import org.apache.poi.ss.usermodel.DataFormatter; -import org.apache.poi.ss.usermodel.DateUtil; -import org.apache.poi.xssf.usermodel.XSSFCell; -import org.apache.poi.xssf.usermodel.XSSFRow; -import org.apache.poi.xssf.usermodel.XSSFSheet; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.shiro.SecurityUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; import java.math.BigDecimal; import java.math.RoundingMode; -import java.text.ParseException; -import java.time.LocalDate; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; -import java.util.stream.Stream; @Service @Slf4j @@ -100,731 +72,6 @@ public class CoDelServiceImpl implements CoDelService { return coDelMapper.searchEcssCoDelNotifyDetail(data); } - @Override - public Map previewExcel(MultipartFile file, EcssCoDelNotifyHeaderData inData) { - SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); - String site = coDelMapper.getSiteByBu(inData.getBuNo()); - List excelList = new ArrayList<>(); - List sheetErrors = new ArrayList<>(); - - try (InputStream is = file.getInputStream()) { - XSSFWorkbook workbook = new XSSFWorkbook(is); - importNotifyExcel(inData, workbook, site, currentUser, excelList, sheetErrors); - } catch (Exception e) { - throw new RuntimeException("文件解析失败:" + e.getMessage()); - } - - // 检查物料存在性 - Map> invalidMaterialsByInvoice = checkMaterialsExistence(excelList, site, currentUser.getUsername(), inData.getBuNo()); - - // 按发票号分组并汇总数据 - Map> groupedByInvoice = excelList.stream() - .collect(Collectors.groupingBy(EcssCoDelNotifyData::getCmcInvoice)); - - List> previewList = new ArrayList<>(); - - groupedByInvoice.forEach((invoice, dataList) -> { - Map summary = new HashMap<>(); - summary.put("cmcInvoice", invoice); - - // 格式化日期为字符串 - Date readyDate = dataList.get(0).getReadyDate(); - String formattedDate = readyDate != null ? DateUtils.format(readyDate, "yyyy-MM-dd") : ""; - summary.put("readyDate", formattedDate); - summary.put("totalQty", dataList.stream() - .map(EcssCoDelNotifyData::getQty) - .reduce(BigDecimal.ZERO, BigDecimal::add)); - summary.put("totalItems", dataList.size()); - summary.put("destination", dataList.get(0).getDestination()); - summary.put("shippingMode", dataList.get(0).getShippingMode()); - - // 检查是否已存在,如果已存在则跳过 - List existingHeaders = coDelMapper.checkIfHasHeader(invoice); - if (!existingHeaders.isEmpty()) { - return; // 跳过已存在的发票号 - } - - // 检查物料存在性 - List invalidMaterials = invalidMaterialsByInvoice.get(invoice); - boolean hasInvalidMaterials = invalidMaterials != null && !invalidMaterials.isEmpty(); - summary.put("hasInvalidMaterials", hasInvalidMaterials); - - if (hasInvalidMaterials) { - summary.put("invalidMaterials", invalidMaterials); - summary.put("invalidMaterialsText", "不存在的物料:" + String.join("、", invalidMaterials)); - } - - // 设置状态信息 - if (hasInvalidMaterials) { - summary.put("status", "物料不存在"); - summary.put("statusType", "error"); - } else { - summary.put("status", "可导入"); - summary.put("statusType", "success"); - } - - previewList.add(summary); - }); - - // 构建返回结果,包含预览数据和Sheet错误信息 - Map result = new HashMap<>(); - result.put("previewList", previewList); - result.put("sheetErrors", sheetErrors); - result.put("hasErrors", !sheetErrors.isEmpty()); - result.put("errorCount", sheetErrors.size()); - - return result; - } - - @Override - @Transactional - public Map> saveEcssCoDelNotifyByExcel(MultipartFile file, EcssCoDelNotifyHeaderData inData, String deletedInvoices, HttpServletRequest request) { - SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); - String site = coDelMapper.getSiteByBu(inData.getBuNo()); - List excelList = new ArrayList<>(); - List sheetErrors = new ArrayList<>(); - - try (InputStream is = file.getInputStream()) { - XSSFWorkbook workbook = new XSSFWorkbook(is); - importNotifyExcel(inData, workbook, site, currentUser, excelList, sheetErrors); - } catch (NullPointerException e) { - log.error("导入失败:{}", e.getMessage()); - throw new RuntimeException("导入失败:网络错误,请重新上传文档"); - } catch (Exception e) { - throw new RuntimeException("导入失败:" + e.getMessage()); - } - - // 处理删除的发票号 - if (deletedInvoices != null && !deletedInvoices.trim().isEmpty()) { - try { - // 解析删除的发票号列表 - List deletedInvoiceList = parseDeletedInvoices(deletedInvoices); - if (!deletedInvoiceList.isEmpty()) { - // 过滤掉被删除的发票号 - excelList = excelList.stream() - .filter(data -> !deletedInvoiceList.contains(data.getCmcInvoice())) - .collect(Collectors.toList()); - log.info("已过滤删除的发票号: {}", deletedInvoiceList); - } - } catch (Exception e) { - log.error("解析删除发票号列表失败: {}", e.getMessage()); - throw new RuntimeException("解析删除发票号列表失败:" + e.getMessage()); - } - } - - // 检查物料存在性并筛选掉包含不存在物料的发票 - Map> invalidMaterialsByInvoice = checkMaterialsExistence(excelList, site, currentUser.getUsername(), inData.getBuNo()); - if (!invalidMaterialsByInvoice.isEmpty()) { - // 过滤掉包含不存在物料的发票 - Set invalidInvoices = invalidMaterialsByInvoice.keySet(); - excelList = excelList.stream() - .filter(data -> !invalidInvoices.contains(data.getCmcInvoice())) - .collect(Collectors.toList()); - - // 记录被筛选掉的发票及其不存在的物料 - for (Map.Entry> entry : invalidMaterialsByInvoice.entrySet()) { - String invoice = entry.getKey(); - List invalidMaterials = entry.getValue(); - log.warn("发票号 {} 包含不存在的物料,已被筛选掉: {}", invoice, invalidMaterials); - } - } - - // 成功和失败列表 - List successList = new ArrayList<>(); - List failList = new ArrayList<>(); - - // 添加Sheet错误信息到失败列表 - for (SheetErrorInfo error : sheetErrors) { - if (error.getSkipped()) { - // 跳过类型的错误,作为警告信息 - failList.add("Sheet [" + error.getSheetName() + "] 已跳过:" + error.getErrorMessage()); - } else { - // 验证失败类型的错误 - failList.add("Sheet [" + error.getSheetName() + "] 错误:" + error.getErrorMessage()); - if (error.getErrorDetails() != null && !error.getErrorDetails().isEmpty()) { - // 限制显示前5个错误详情 - int limit = Math.min(5, error.getErrorDetails().size()); - for (int i = 0; i < limit; i++) { - failList.add(" - " + error.getErrorDetails().get(i)); - } - if (error.getErrorDetails().size() > 5) { - failList.add(" - ... 还有 " + (error.getErrorDetails().size() - 5) + " 个错误"); - } - } - } - } - - // 添加物料不存在的发票到失败列表 - for (Map.Entry> entry : invalidMaterialsByInvoice.entrySet()) { - String invoice = entry.getKey(); - List invalidMaterials = entry.getValue(); - String materialList = String.join("、", invalidMaterials); - failList.add("发票号:" + invoice + " 包含不存在的物料:" + materialList); - } - - // 使用 groupingBy 分组 - Map> groupedByReadyDateAndCmcInvoice = excelList.stream() - .collect(Collectors.groupingBy(data -> data.getReadyDate() + "-" + data.getCmcInvoice())); - - // 每个分组创建一个销售发货单 - groupedByReadyDateAndCmcInvoice.forEach((key, list) -> { - String cmcInvoice = list.get(0).getCmcInvoice(); - String transNo; - - // 检查是否已经存在 - List checkIfHasHeader = coDelMapper.checkIfHasHeader(cmcInvoice); - if (!checkIfHasHeader.isEmpty()) { - //failList.add("发票号:" + cmcInvoice + " 已经生成发货通知单"); - return; // 跳过当前分组,继续下一个 - } - - try { - // 新建头 - EcssCoDelNotifyHeader headerList = new EcssCoDelNotifyHeader(); - coDelMapper.updateTransNo(list.get(0).getSite(), "EC"); - transNo = coDelMapper.getTransNo(list.get(0).getSite(), "EC"); - - headerList.setDelNo(transNo); - headerList.setSite(list.get(0).getSite()); - headerList.setBuNo(list.get(0).getBuNo()); - headerList.setCustomerName(list.get(0).getCustomerName()); - headerList.setDestination(list.get(0).getDestination()); - headerList.setNotifyStatus(list.get(0).getNotifyStatus()); - headerList.setReadyDate(list.get(0).getReadyDate()); - headerList.setShippingMode(list.get(0).getShippingMode()); - - // 如果有按发票号分别设置的客户信息,则使用该信息;否则使用全局信息 - String customerNameKey = "customerName_" + cmcInvoice; - String localShipAddressKey = "localShipAddress_" + cmcInvoice; - String overseasShipperKey = "overseasShipper_" + cmcInvoice; - String overseasAddressKey = "overseasAddress_" + cmcInvoice; - String cnativeKey = "cnative_" + cmcInvoice; - String salesAreaKey = "salesArea_" + cmcInvoice; - - // 从请求参数中获取按发票号设置的客户信息 - String customerName = request.getParameter(customerNameKey); - String localShipAddress = request.getParameter(localShipAddressKey); - String overseasShipper = request.getParameter(overseasShipperKey); - String overseasAddress = request.getParameter(overseasAddressKey); - String cnative = request.getParameter(cnativeKey); - String salesArea = request.getParameter(salesAreaKey); - - // 如果按发票号的信息不存在,则使用全局信息 - headerList.setCustomerName(customerName != null ? customerName : inData.getCustomerName()); - headerList.setLocalShipAddress(localShipAddress != null ? localShipAddress : inData.getLocalShipAddress()); - headerList.setOverseasShipper(overseasShipper != null ? overseasShipper : inData.getOverseasShipper()); - headerList.setOverseasAddress(overseasAddress != null ? overseasAddress : inData.getOverseasAddress()); - headerList.setCnative(cnative != null ? cnative : inData.getCnative()); - headerList.setSalesArea(salesArea != null ? salesArea : inData.getSalesArea()); - headerList.setCmcInvoice(cmcInvoice); - headerList.setCreateBy(inData.getUsername()); - coDelMapper.saveEcssCoDelNotifyHeader(headerList); - - // 明细 - for (int i = 0; i < list.size(); i++) { - list.get(i).setDelNo(transNo); - list.get(i).setItemNo(i + 1); - } - coDelMapper.batchSaveEcssCoDelNotifyDetail(list); - - // 加入成功列表 - successList.add("发票号:" + cmcInvoice + " 导入成功"); - } catch (Exception e) { - failList.add("发票号:" + cmcInvoice + " 导入失败,原因:" + e.getMessage()); - } - }); - - // 返回结果 - Map> resultMap = new HashMap<>(); - resultMap.put("success", successList); - resultMap.put("fail", failList); - return resultMap; - } - - - /** - * 安全获取字符串列值(支持可选列) - * 如果列不存在,返回空字符串 - * - * @param row 数据行 - * @param columnMap 列索引映射 - * @param columnName 列名 - * @return 单元格字符串值,如果列不存在返回空字符串 - */ - private String getStringCellValueSafe(XSSFRow row, Map columnMap, String columnName) { - Integer columnIndex = columnMap.get(columnName); - if (columnIndex == null) { - return ""; // 列不存在,返回空字符串 - } - return getStringCellValue(row, columnIndex); - } - - /** - * 安全获取数字列值(支持可选列) - * 如果列不存在,返回null - * - * @param row 数据行 - * @param columnMap 列索引映射 - * @param columnName 列名 - * @return 单元格数字值,如果列不存在返回null - */ - private BigDecimal getNumericCellValueSafe(XSSFRow row, Map columnMap, String columnName) { - Integer columnIndex = columnMap.get(columnName); - if (columnIndex == null) { - return null; // 列不存在,返回null - } - return getNumericCellValueOrDefault(row, columnIndex); - } - - /** - * 判断单元格值是否有效(通用版本) - * 无效值包括:空值、"0"、Excel错误(#REF!、#N/A、#VALUE!等) - * - * @param cellValue 单元格字符串值 - * @return true=有效值,false=无效值 - */ - private boolean isValidCellValue(String cellValue) { - // 1. 空值或空字符串 - if (StringUtils.isBlank(cellValue)) { - return false; - } - - String trimmedValue = cellValue.trim(); - - // 2. 值为"0" - if ("0".equals(trimmedValue)) { - return false; - } - - // 3. Excel错误值(以#开头,如#REF!、#N/A、#VALUE!、#DIV/0!、#NAME?、#NUM!、#NULL!等) - return !trimmedValue.startsWith("#"); - - // 其他情况视为有效值 - } - - /** - * 判断CMC Invoice是否有效 - * 无效值包括:空值、"0"、Excel错误、长度超过20位 - * - * @param cellValue 单元格字符串值 - * @return true=有效值,false=无效值 - */ - private boolean isValidCmcInvoice(String cellValue) { - // 先进行通用验证 - if (!isValidCellValue(cellValue)) { - return false; - } - - // CMC Invoice特殊验证:长度不能超过20位 - String trimmedValue = cellValue.trim(); - return trimmedValue.length() <= 20; - } - - /** - * 解析Excel第一行,建立列名到列索引的映射关系 - * - * @param headerRow 第一行(表头行) - * @param sheetName Sheet名称(用于错误提示) - * @return 列名到列索引的映射Map - */ - private Map parseExcelHeader(XSSFRow headerRow, String sheetName) { - Map columnIndexMap = new HashMap<>(); - if (headerRow == null) { - throw new RuntimeException("Sheet [" + sheetName + "] 表头行不能为空!"); - } - - // 遍历第一行的所有单元格,建立列名到索引的映射 - int lastCellNum = headerRow.getLastCellNum(); - for (int i = 0; i < lastCellNum; i++) { - String columnName = getStringCellValue(headerRow, i); - if (StringUtils.isNotBlank(columnName)) { - // 去除空格并统一格式 - columnName = columnName.trim(); - columnIndexMap.put(columnName, i); - } - } - - // 注意:这里只解析表头,不验证必填列 - // 必填列的验证应该在确定要处理该 sheet 之后进行(即 Cargo Ready Date 列存在时) - return columnIndexMap; - } - - /** - * 导入通知单Excel - * - * @param inData 输入数据 - * @param workbook Excel工作簿 - * @param site 站点 - * @param currentUser 当前用户 - * @param excelList 解析后的数据列表 - * @param sheetErrors Sheet错误信息列表(收集所有错误) - */ - private void importNotifyExcel(EcssCoDelNotifyHeaderData inData, XSSFWorkbook workbook, String site, - SysUserEntity currentUser, List excelList, - List sheetErrors) { - for (int s = 0; s < workbook.getNumberOfSheets(); s++) { - // 读取工作表 - XSSFSheet sheet = workbook.getSheetAt(s); - String sheetName = sheet.getSheetName(); - SheetErrorInfo currentSheetError = null; // 当前Sheet的错误信息 - - try { - // 剔除空行 - deleteEmptyRow(sheet); - // 获取行数 - int rows = sheet.getPhysicalNumberOfRows(); - - if (rows < 4) { - continue; // 至少需要前2行、表头行(第3行)和一行数据(第4行) - } - - // 获取第三行(表头行) - XSSFRow headerRow = sheet.getRow(2); - if (headerRow == null) { - log.warn("Sheet [{}] 的第3行为空,跳过该Sheet", sheetName); - continue; - } - - // 解析表头行(第3行),获取列索引映射 - Map columnMap = parseExcelHeader(headerRow, sheetName); - - // 获取Cargo Ready Date列索引 - Integer cargoReadyDateIdx = columnMap.get("Cargo Ready Date"); - if (cargoReadyDateIdx == null) { - log.warn("Sheet [{}] 中未找到 'Cargo Ready Date' 列,跳过该Sheet", sheetName); - continue; - } - - // Cargo Ready Date 列存在,说明要处理这个 sheet,验证其他必填列 - String[] requiredColumns = { - "PO#", // 必填 - "PN", // 必填 - "Qty (pcs)", // 必填 - "Destination", // 必填 - "Shipping Mode", // 必填 - "Currency", // 必填 - "TP" // 必填 - }; - - List missingColumns = new ArrayList<>(); - for (String column : requiredColumns) { - if (!columnMap.containsKey(column)) { - missingColumns.add(column); - } - } - - if (!missingColumns.isEmpty()) { - currentSheetError = SheetErrorInfo.createError(sheetName, s, "MISSING_COLUMNS", - "表头缺少必填列: " + String.join(", ", missingColumns)); - sheetErrors.add(currentSheetError); - log.error("Sheet [{}] 表头缺少必填列,跳过该Sheet", sheetName); - continue; // 不抛异常,记录错误后跳过 - } - - // 扫描整个 sheet,如果发现"内销"则跳过该 sheet - boolean hasInternalSale = false; - for (int scanRow = 0; scanRow < rows; scanRow++) { - XSSFRow checkRow = sheet.getRow(scanRow); - if (checkRow == null) { - continue; - } - - // 遍历该行所有单元格 - int lastCellNum = checkRow.getLastCellNum(); - for (int scanCol = 0; scanCol < lastCellNum; scanCol++) { - XSSFCell cell = checkRow.getCell(scanCol); - if (cell != null && cell.getCellType() != CellType.BLANK) { - try { - String cellValue = getStringCellValue(checkRow, scanCol); - if (cellValue != null && cellValue.contains("内销")) { - hasInternalSale = true; - log.info("Sheet [{}] 在第{}行第{}列发现\"内销\",跳过该Sheet", sheetName, scanRow + 1, scanCol + 1); - break; - } - } catch (Exception e) { - // 忽略单元格读取异常,继续扫描其他单元格 - log.debug("Sheet [{}] 第{}行第{}列读取失败: {}", sheetName, scanRow + 1, scanCol + 1, e.getMessage()); - } - } - } - - if (hasInternalSale) { - break; - } - } - - // 如果发现内销,跳过该 sheet - if (hasInternalSale) { - continue; - } - - // 创建当前Sheet的错误收集器(如果还没创建的话) - if (currentSheetError == null) { - currentSheetError = new SheetErrorInfo(); - currentSheetError.setSheetName(sheetName); - currentSheetError.setSheetIndex(s); - currentSheetError.setErrorType("INVALID_DATA"); - currentSheetError.setErrorMessage("数据验证失败"); - currentSheetError.setSkipped(false); - currentSheetError.setErrorDetails(new ArrayList<>()); - } - - boolean hasRowError = false; // 标记当前Sheet是否有行错误 - - // 遍历每一行(从第四行开始,即索引3) - for (int j = 3; j < rows; j++) { - // 获得该行 - XSSFRow row = sheet.getRow(j); - if (row == null) { - continue; - } - - // 检查Cargo Ready Date列,如果没有值则跳过该行 - if (row.getCell(cargoReadyDateIdx) == null - || row.getCell(cargoReadyDateIdx).getCellType() == CellType.BLANK - || row.getCell(cargoReadyDateIdx).getCellType() == CellType.ERROR) { - log.debug("第{}行的Cargo Ready Date列为空,跳过该行", j+1); - continue; - } - - // 跳过内销数据(如果"内外销方式"列存在) - Integer saleTypeIdx = columnMap.get("内外销方式"); - if (saleTypeIdx != null && row.getCell(saleTypeIdx) != null - && getStringCellValue(row, saleTypeIdx).equals("内销")) { - continue; - } - - // 必填字段验证 - Cargo Ready Date已经在上面检查过了 - Integer poIdx = columnMap.get("PO#"); - if (row.getCell(poIdx) == null) { - currentSheetError.addErrorDetail("第" + (j+1) + "行的 [PO#] 列不能为空"); - hasRowError = true; - continue; // 跳过这一行,继续处理下一行 - } - - Integer pnIdx = columnMap.get("PN"); - if (row.getCell(pnIdx) == null) { - currentSheetError.addErrorDetail("第" + (j+1) + "行的 [PN] 列不能为空"); - hasRowError = true; - continue; - } - - Integer qtyIdx = columnMap.get("Qty (pcs)"); - if (row.getCell(qtyIdx) == null) { - currentSheetError.addErrorDetail("第" + (j+1) + "行的 [Qty (pcs)] 列不能为空"); - hasRowError = true; - continue; - } - - Integer destinationIdx = columnMap.get("Destination"); - if (row.getCell(destinationIdx) == null) { - currentSheetError.addErrorDetail("第" + (j+1) + "行的 [Destination] 列不能为空"); - hasRowError = true; - continue; - } - - Integer shippingModeIdx = columnMap.get("Shipping Mode"); - if (row.getCell(shippingModeIdx) == null) { - currentSheetError.addErrorDetail("第" + (j+1) + "行的 [Shipping Mode] 列不能为空"); - hasRowError = true; - continue; - } - - Integer currencyIdx = columnMap.get("Currency"); - if (row.getCell(currencyIdx) == null) { - currentSheetError.addErrorDetail("第" + (j+1) + "行的 [Currency] 列不能为空"); - hasRowError = true; - continue; - } - - Integer tpIdx = columnMap.get("TP"); - if (row.getCell(tpIdx) == null) { - currentSheetError.addErrorDetail("第" + (j+1) + "行的 [TP] 列不能为空"); - hasRowError = true; - continue; - } - - // 创建对象并为对象赋值 - EcssCoDelNotifyData task = new EcssCoDelNotifyData(); - task.setSite(site); // site - task.setBuNo(inData.getBuNo()); // bu - - // 解析日期 - Date readDate; - try { - LocalDate localDate = parseDateCell(row.getCell(cargoReadyDateIdx)); - String formatted = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); - readDate = DateUtils.getDateByParten(formatted, "yyyy-MM-dd"); - } catch (Exception e) { - currentSheetError.addErrorDetail("第" + (j+1) + "行的 [Cargo Ready Date] 列格式有误: " + e.getMessage()); - hasRowError = true; - continue; - } - task.setReadyDate(readDate); - - // 使用动态索引读取数据(必填列) - task.setCustomerPO(getStringCellValue(row, poIdx)); - task.setPn(getStringCellValue(row, pnIdx)); - - // 读取Qty (pcs),捕获格式错误 - try { - task.setQty(getNumericCellValueOrDefault(row, qtyIdx, "Qty (pcs)")); - } catch (RuntimeException e) { - currentSheetError.addErrorDetail("第" + (j+1) + "行的 " + e.getMessage()); - hasRowError = true; - continue; - } - - task.setDestination(getStringCellValue(row, destinationIdx)); - task.setShippingMode(getStringCellValue(row, shippingModeIdx)); - task.setCurrency(getStringCellValue(row, currencyIdx)); - - // 读取TP,捕获格式错误 - try { - task.setTp(getNumericCellValueOrDefault(row, tpIdx, "TP")); - } catch (RuntimeException e) { - currentSheetError.addErrorDetail("第" + (j+1) + "行的 " + e.getMessage()); - hasRowError = true; - continue; - } - - // 处理CMC Invoice:如果CMC Invoice为空/0/Excel错误/超过20位则取Shipping Number,如果两者都为空则报错 - String cmcInvoiceValue = getStringCellValueSafe(row, columnMap, "CMC Invoice"); - String shippingNumberValue = getStringCellValueSafe(row, columnMap, "Shipping Number"); - - // 判断CMC Invoice是否为有效值(排除空值、"0"、Excel错误、长度超过20位) - boolean isCmcInvoiceValid = isValidCmcInvoice(cmcInvoiceValue); - // 判断Shipping Number是否为有效值(排除空值、"0"、Excel错误,允许超过20位) - boolean isShippingNumberValid = isValidCellValue(shippingNumberValue); - - if (!isCmcInvoiceValid && !isShippingNumberValid) { - currentSheetError.addErrorDetail("第" + (j+1) + "行的 [CMC Invoice] 和 [Shipping Number] 列不能同时为空"); - hasRowError = true; - continue; - } - - // 如果CMC Invoice无效(空/0/错误/超过20位),则使用Shipping Number - if (!isCmcInvoiceValid) { - task.setCmcInvoice(shippingNumberValue); - } else { - task.setCmcInvoice(cmcInvoiceValue); - } - task.setShippingNumber(shippingNumberValue); - - // 读取可选列(列不存在时返回空值或null) - task.setSalesOrder(getStringCellValueSafe(row, columnMap, "Sales Order")); - task.setLine(getStringCellValueSafe(row, columnMap, "Line#")); - task.setVersion(getStringCellValueSafe(row, columnMap, "Version#")); - task.setSaleslt(getStringCellValueSafe(row, columnMap, "Saleslt")); - task.setStatus(getStringCellValueSafe(row, columnMap, "Status")); - task.setFamily(getStringCellValueSafe(row, columnMap, "Family")); - task.setPartDescription(getStringCellValueSafe(row, columnMap, "Description")); - task.setManufacturerName(getStringCellValueSafe(row, columnMap, "Manufacturer Name")); - task.setCmcComment(getStringCellValueSafe(row, columnMap, "CMC Comment")); - task.setAwbBl(getStringCellValueSafe(row, columnMap, "AWB/ BL#")); - task.setForwarderInfo(getStringCellValueSafe(row, columnMap, "Forwarder Info")); - task.setSo(getStringCellValueSafe(row, columnMap, "SO")); - task.setUpc(getStringCellValueSafe(row, columnMap, "UPC")); - task.setRemark(getStringCellValueSafe(row, columnMap, "备注")); - task.setCategory(getStringCellValueSafe(row, columnMap, "Category")); - task.setQtyRoll(getStringCellValueSafe(row, columnMap, "QTY/ROLL")); - task.setQtyBox(getStringCellValueSafe(row, columnMap, "qty/box")); - task.setSaleType(getStringCellValueSafe(row, columnMap, "内外销方式")); // 内外销 - - task.setLt(getNumericCellValueSafe(row, columnMap, "LT (wks)")); - task.setTtlAmount(getNumericCellValueSafe(row, columnMap, "TTL Amount")); - task.setVat(getNumericCellValueSafe(row, columnMap, "VAT")); - task.setSumPrice(getNumericCellValueSafe(row, columnMap, "价税合计")); // 价税合计 - task.setRoll(getNumericCellValueSafe(row, columnMap, "Roll")); - - BigDecimal carton = getNumericCellValueSafe(row, columnMap, "BOX"); - task.setCarton(carton != null ? carton.setScale(1, RoundingMode.HALF_UP) : null); - - // 查询物料信息 - List parts = coDelMapper.getPartNo(site, task.getPn(), currentUser.getUsername(), inData.getBuNo()); - if (parts.isEmpty()) { - // 物料不存在时,设置 partNo 为 pn,后续会在批量检查阶段被筛选掉 - task.setPartNo(task.getPn()); - } else { - task.setPartNo(parts.get(0).getPartNo()); - } - - if (task.getQty().compareTo(BigDecimal.ZERO) == 0) { - task.setStatus("取消发货"); - } - task.setErpFlag("N"); - task.setNotifyStatus("已计划"); - task.setUsername(inData.getUsername()); - - // 物料存在性检查已移至批量处理阶段,此处不再单独检查 - excelList.add(task); - } - - // 在Sheet处理结束时,如果有行错误,添加到错误列表 - if (hasRowError && currentSheetError != null) { - sheetErrors.add(currentSheetError); - log.error("Sheet [{}] 处理完成,发现 {} 个数据错误", sheetName, currentSheetError.getErrorDetails().size()); - } - - } catch (Exception e) { - // 捕获异常并记录,不中断整个流程 - String errorMsg = e.getMessage(); - SheetErrorInfo errorInfo = SheetErrorInfo.createError(sheetName, s, "PARSE_ERROR", - "Sheet解析失败: " + (errorMsg != null ? errorMsg : e.getClass().getSimpleName())); - sheetErrors.add(errorInfo); - log.error("Sheet [{}] 处理异常: {}", sheetName, errorMsg, e); - // 继续处理下一个Sheet - } - } - - // 汇总日志 - log.info("=== Excel导入完成 === 成功处理 {} 条数据,发现 {} 个Sheet存在问题", excelList.size(), sheetErrors.size()); - } - - public static LocalDate parseDateCell(Cell cell) { - if (cell == null) return null; - - try { - CellType cellType = cell.getCellType(); - - // 处理数值类型(日期格式) - if (cellType == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell)) { - return cell.getDateCellValue().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); - } - // 处理字符串类型 - else if (cellType == CellType.STRING) { - String val = cell.getStringCellValue().trim(); - if (val.isEmpty()) return null; - return LocalDate.parse(val, DateTimeFormatter.ofPattern("yyyy-MM-dd")); - } - // 处理公式类型 - 获取缓存结果 - else if (cellType == CellType.FORMULA) { - switch (cell.getCachedFormulaResultType()) { - case NUMERIC: - // 公式结果是数值类型且是日期格式 - if (DateUtil.isCellDateFormatted(cell)) { - return cell.getDateCellValue().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); - } - break; - case STRING: - // 公式结果是字符串类型 - String formulaVal = cell.getStringCellValue().trim(); - if (!formulaVal.isEmpty()) { - return LocalDate.parse(formulaVal, DateTimeFormatter.ofPattern("yyyy-MM-dd")); - } - break; - default: - break; - } - } - } catch (Exception e) { - System.err.println("日期格式解析失败: " + cell.toString()); - } - - return null; // 无法解析,返回 null - } - - public void sendMailUtil(String textHead,String text,String[] mailAddress,EcssCoDelNotifyHeaderData data) { MailSendAddressData mailSendData = qcMapper.getSendMailFromAddress(); MailUtil.sendMail(textHead, text, mailAddress, mailSendData); @@ -905,166 +152,6 @@ public class CoDelServiceImpl implements CoDelService { } } - private String getStringCellValue(XSSFRow row, int columnIndex) { - Cell cell = row.getCell(columnIndex); - if (cell == null || cell.getCellType() == CellType.BLANK) { - return ""; - } - - CellType cellType = cell.getCellType(); - - // 处理公式类型 - 获取缓存结果 - if (cellType == CellType.FORMULA) { - switch (cell.getCachedFormulaResultType()) { - case NUMERIC: - // 公式结果是数值类型 - 直接获取数值并转换为字符串 - // 检查是否是日期格式 - if (DateUtil.isCellDateFormatted(cell)) { - DataFormatter formatter = new DataFormatter(); - return formatter.formatCellValue(cell); - } else { - // 普通数值,转换为字符串(去除小数点后的零) - double numericValue = cell.getNumericCellValue(); - // 如果是整数,去掉.0 - if (numericValue == Math.floor(numericValue)) { - return String.valueOf((long) numericValue); - } else { - return String.valueOf(numericValue); - } - } - case STRING: - // 公式结果是字符串类型 - return cell.getStringCellValue(); - case BLANK: - return ""; - case BOOLEAN: - return String.valueOf(cell.getBooleanCellValue()); - default: - return ""; - } - } - - // 处理其他类型使用DataFormatter统一格式化 - DataFormatter formatter = new DataFormatter(); - return formatter.formatCellValue(cell); - } - - public static Integer getIntegerCellValue(XSSFRow row, int columnIndex) { - Cell cell = row.getCell(columnIndex); - if (cell == null || cell.getCellType() == CellType.BLANK) { - return null; - } - - CellType cellType = cell.getCellType(); - - // 处理数值类型 - if (cellType == CellType.NUMERIC) { - return (int) Math.round(cell.getNumericCellValue()); - } - - // 处理字符串类型 - if (cellType == CellType.STRING) { - try { - return Integer.parseInt(cell.getStringCellValue().trim()); - } catch (NumberFormatException e) { - return -1; - } - } - - // 处理公式类型 - 获取缓存结果 - if (cellType == CellType.FORMULA) { - switch (cell.getCachedFormulaResultType()) { - case NUMERIC: - return (int) Math.round(cell.getNumericCellValue()); - case STRING: - try { - return Integer.parseInt(cell.getStringCellValue().trim()); - } catch (NumberFormatException e) { - return -1; - } - default: - return -1; - } - } - - // 其他类型返回-1 - return -1; - } - - /** - * 获取数值单元格的值(无列名版本,保持向后兼容,遇到不支持的类型返回null) - */ - private BigDecimal getNumericCellValueOrDefault(XSSFRow row, int columnIndex) { - return getNumericCellValueOrDefault(row, columnIndex, null); - } - - /** - * 获取数值单元格的值 - * @param row Excel行 - * @param columnIndex 列索引 - * @param columnName 列名(用于错误提示,如 "Qty (pcs)"、"TP" 等必填列会抛出异常,其他列返回null) - * @return BigDecimal值 - */ - private BigDecimal getNumericCellValueOrDefault(XSSFRow row, int columnIndex, String columnName) { - Cell cell = row.getCell(columnIndex); - if (cell == null || cell.getCellType() == CellType.BLANK) { - return null; - } - - // 必填数字列列表 - boolean isRequiredColumn = columnName != null && - ("Qty (pcs)".equalsIgnoreCase(columnName) || "TP".equalsIgnoreCase(columnName)); - - switch (cell.getCellType()) { - case NUMERIC: - BigDecimal value = BigDecimal.valueOf(cell.getNumericCellValue()); - return value.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数 - case STRING: - try { - if (cell.getStringCellValue() == null || cell.getStringCellValue().isEmpty()) { - return null; - } - BigDecimal stringValue = new BigDecimal(cell.getStringCellValue()); - return stringValue.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数 - } catch (NumberFormatException e) { - if (isRequiredColumn) { - throw new RuntimeException("[" + columnName + "] 列无效的数值格式: " + cell.getStringCellValue()); - } - return null; - } - case FORMULA: - // 获取缓存结果 - switch (cell.getCachedFormulaResultType()) { - case NUMERIC: - BigDecimal formulaValue = BigDecimal.valueOf(cell.getNumericCellValue()); - return formulaValue.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数 - case STRING: - try { - if (cell.getStringCellValue() == null || cell.getStringCellValue().isEmpty()) { - return null; - } - BigDecimal stringValue = new BigDecimal(cell.getStringCellValue()); - return stringValue.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数 - } catch (NumberFormatException e) { - if (isRequiredColumn) { - throw new RuntimeException("[" + columnName + "] 列无效的数值格式: " + cell.getStringCellValue()); - } - return null; - } - default: - return null; - } - default: - // 如果是必填列,抛出异常提示 - if (isRequiredColumn) { - throw new RuntimeException("[" + columnName + "] 列不支持的单元格类型: " + cell.getCellType()); - } - // 其他非必填列返回null - return null; - } - } - - @Override public void changeEcssDelStatus(EcssCoDelNotifyHeaderData data) { if (data.getNotifyStatus().equals("已下达")) { @@ -1233,397 +320,19 @@ public class CoDelServiceImpl implements CoDelService { } @Override - @Transactional - public void modifyNotifyDetailByExcel(MultipartFile file, EcssCoDelNotifyHeaderData inData) { - SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); - String site = coDelMapper.getSiteByBu(inData.getBuNo()); - List excelList = new ArrayList<>(); - List sheetErrors = new ArrayList<>(); - try { - // 转流 - InputStream is = file.getInputStream(); - // 读取工作簿 - XSSFWorkbook workbook = new XSSFWorkbook(is); - importNotifyExcel(inData, workbook, site, currentUser, excelList, sheetErrors); - - // 如果有Sheet错误,抛出异常提示用户 - if (!sheetErrors.isEmpty()) { - StringBuilder errorMsg = new StringBuilder("以下Sheet存在问题:\n"); - for (SheetErrorInfo error : sheetErrors) { - errorMsg.append("Sheet [").append(error.getSheetName()).append("]: ").append(error.getErrorMessage()).append("\n"); - } - throw new RuntimeException(errorMsg.toString()); - } - } catch (NullPointerException e) { - throw new RuntimeException("导入失败:网络错误,请重新上传文档"); - } catch (Exception e) { - throw new RuntimeException("导入失败:" + e.getMessage()); + public void updateEcssDel(EcssCoDelNotifyHeaderData data) { + List checkHeader = coDelMapper.checkEcssCoDelNotifyHeaderByDelNo(data.getSite(), data.getDelNo()); + if (checkHeader.isEmpty()) { + throw new RuntimeException("不存在该发货通知单请刷新界面"); } - // 过滤掉发票号不一致的数据 - excelList.removeIf(data -> !inData.getCmcInvoice().equals(data.getCmcInvoice())); + coDelMapper.updateEcssDel(data); + } - // 检查过滤后是否还有数据 - if (excelList.isEmpty()) { - throw new RuntimeException("导入失败:Excel中没有与发货通知单["+inData.getCmcInvoice()+"]一致的发票号数据"); - } - - List notifyHeader = coDelMapper.checkIfHasHeader(excelList.get(0).getCmcInvoice()); - EcssCoDelNotifyHeaderData headerData; - if (notifyHeader.isEmpty()) { - throw new RuntimeException("发货通知单不存在,发票号:" + excelList.get(0).getCmcInvoice()); - } else { - headerData = notifyHeader.get(0); - } - - // 保存原始头表数据用于比较 - EcssCoDelNotifyHeaderData originalHeaderData = new EcssCoDelNotifyHeaderData(); - originalHeaderData.setReadyDate(headerData.getReadyDate()); - originalHeaderData.setShippingMode(headerData.getShippingMode()); - originalHeaderData.setDestination(headerData.getDestination()); - - List dbinData =coDelMapper.searchEcssCoDelNotifyDetail(headerData); - List dbData = new ArrayList<>(dbinData); - - // 更新头表字段(ReadyDate、ShippingMode、Destination) - if (!excelList.isEmpty()) { - EcssCoDelNotifyData firstExcelData = excelList.get(0); - headerData.setReadyDate(firstExcelData.getReadyDate()); - headerData.setShippingMode(firstExcelData.getShippingMode()); - headerData.setDestination(firstExcelData.getDestination()); - } - - // 先删除全部明细,再新建明细 - coDelMapper.deleteAllEcssDelDetail(headerData); - - // 智能判断改单逻辑:按PN分组汇总数量,避免因顺序变化导致误判 - // 构建原数据Map:按PN分组,计算每个PN的总数量 - Map originalPnQtyMap = new HashMap<>(); - for (EcssCoDelNotifyDetailData detail : dbData) { - String pn = detail.getPn(); - BigDecimal qty = detail.getQty(); - originalPnQtyMap.put(pn, originalPnQtyMap.getOrDefault(pn, BigDecimal.ZERO).add(qty)); - } - - // 构建新数据Map:按PN分组,计算每个PN的总数量 - Map newPnQtyMap = new HashMap<>(); - for (EcssCoDelNotifyData excelData : excelList) { - String pn = excelData.getPn(); - BigDecimal qty = excelData.getQty(); - newPnQtyMap.put(pn, newPnQtyMap.getOrDefault(pn, BigDecimal.ZERO).add(qty)); - } - - // 设置itemNo并判断每一行是否有变化(智能判断:按PN比较总数量) - for (int i = 0; i < excelList.size(); i++) { - excelList.get(i).setDelNo(headerData.getDelNo()); - excelList.get(i).setItemNo(i + 1); - - String pn = excelList.get(i).getPn(); - BigDecimal originalTotalQty = originalPnQtyMap.get(pn); - BigDecimal newTotalQty = newPnQtyMap.get(pn); - - // 判断是否有修改(按PN比较总数量,避免因顺序变化误判) - boolean isModified = false; - boolean isQtyModified = false; - - if (originalTotalQty == null) { - // 新增PN:原数据中不存在该PN - isModified = true; - isQtyModified = true; - log.info("检测到新增PN: {}, 数量: {}", pn, newTotalQty); - } else if (originalTotalQty.compareTo(newTotalQty) != 0) { - // 数量变更:PN存在但总数量不同 - isModified = true; - isQtyModified = true; - log.info("检测到PN数量变更: {}, 原数量: {}, 新数量: {}", pn, originalTotalQty, newTotalQty); - } - // 其他情况:PN和总数量都相同,无变化,isModified保持false - - // 只有真正有变化的才标记modifyFlag=true - excelList.get(i).setModifyFlag(isModified); - - // 设置数量变化标记 - if (isQtyModified) { - excelList.get(i).setModifyQtyFlag(true); - } - } - - // 检查是否有PN被删除(在原数据中存在但新数据中不存在) - boolean hasPnDeleted = false; - for (String originalPn : originalPnQtyMap.keySet()) { - if (!newPnQtyMap.containsKey(originalPn)) { - log.info("检测到PN被删除: {}, 原数量: {}", originalPn, originalPnQtyMap.get(originalPn)); - hasPnDeleted = true; - } - } - - // 检查是否有PN或Qty的修改 - boolean hasPnOrQtyModified = excelList.stream() - .anyMatch(item -> item.getModifyQtyFlag() != null && item.getModifyQtyFlag()); - - coDelMapper.batchSaveEcssCoDelNotifyDetail(excelList); - headerData.setModifyFlag(true); - // 更新头表字段,包括ReadyDate、ShippingMode、Destination - coDelMapper.updateEcssDelHeader(headerData); - List newData =coDelMapper.searchEcssCoDelNotifyDetail(headerData); - - // 构建邮件内容,包含头表字段变更和明细变更 - StringBuilder emailContent = generateModifyEmailContent(originalHeaderData, headerData, dbData, newData); - - String textHead = headerData.getDelNo()+"【发票:"+ headerData.getCmcInvoice()+"】改单"; - String[] mailAddress = coDelMapper.queryUsersByRoleName("关务仓库",headerData.getSite()).stream().map(SysUserEntity::getEmail).toArray(String[]::new); - - // 安全获取创建人邮箱,防止用户不存在或邮箱为空的空指针异常 - SysUserEntity creator = coDelMapper.queryByUserName(headerData.getCreateBy()); - String[] mailAddress2 = (creator != null && creator.getEmail() != null) ? - new String[]{creator.getEmail()} : new String[]{}; - - // 合并 + 去重 - String[] mailAddressAll = Stream.concat(Arrays.stream(mailAddress), Arrays.stream(mailAddress2)) - .filter(Objects::nonNull) // 防止 null - .filter(email -> !email.trim().isEmpty()) // 过滤空字符串 - .distinct() // 去重 - .toArray(String[]::new); - if (mailAddressAll.length>0) { - sendMailUtil(textHead, emailContent.toString(), mailAddressAll, headerData); - } - - // 改单导入后的状态处理逻辑 - // 只有在修改了PN/Qty或删除了PN时才执行状态处理 - if (hasPnOrQtyModified || hasPnDeleted) { - log.info("检测到PN/Qty修改或PN删除,执行状态处理逻辑"); - handleNotifyStatusAfterModify(headerData, true); - } else { - log.info("仅修改了其他字段(Destination、Shipping Mode、Currency、TP等),执行状态处理逻辑但不删除托盘数据"); - handleNotifyStatusAfterModify(headerData, false); - } - } - - /** - * 生成改单邮件内容,包含头表字段变更和明细变更 - */ - private StringBuilder generateModifyEmailContent(EcssCoDelNotifyHeaderData originalHeaderData, - EcssCoDelNotifyHeaderData newHeaderData, - List dbData, - List newData) { - StringBuilder emailContent = new StringBuilder(); - - // HTML头部和样式 - emailContent.append(""); - emailContent.append(""); - emailContent.append(""); - emailContent.append(""); - emailContent.append(""); - emailContent.append(""); - emailContent.append(""); - - // 邮件标题 - emailContent.append("

    发货通知单改单通知

    "); - emailContent.append("

    发货通知单号:").append(newHeaderData.getDelNo()).append("

    "); - emailContent.append("

    发票号:").append(newHeaderData.getCmcInvoice()).append("

    "); - emailContent.append("

    操作时间:").append(DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss")).append("

    "); - - // 检查头表字段变更 - StringBuilder headerChanges = new StringBuilder(); - boolean hasHeaderChanges = false; - - // 检查ReadyDate变更 - if (!Objects.equals(originalHeaderData.getReadyDate(), newHeaderData.getReadyDate())) { - hasHeaderChanges = true; - String originalDate = originalHeaderData.getReadyDate() != null ? - DateUtils.format(originalHeaderData.getReadyDate(), "yyyy-MM-dd") : "无"; - String newDate = newHeaderData.getReadyDate() != null ? - DateUtils.format(newHeaderData.getReadyDate(), "yyyy-MM-dd") : "无"; - headerChanges.append("
  • Ready Date: ") - .append(originalDate).append(" → ").append(newDate).append("
  • "); - } - - // 检查ShippingMode变更 - if (!Objects.equals(originalHeaderData.getShippingMode(), newHeaderData.getShippingMode())) { - hasHeaderChanges = true; - String originalMode = originalHeaderData.getShippingMode() != null ? originalHeaderData.getShippingMode() : "无"; - String newMode = newHeaderData.getShippingMode() != null ? newHeaderData.getShippingMode() : "无"; - headerChanges.append("
  • Shipping Mode: ") - .append(originalMode).append(" → ").append(newMode).append("
  • "); - } - - // 检查Destination变更 - if (!Objects.equals(originalHeaderData.getDestination(), newHeaderData.getDestination())) { - hasHeaderChanges = true; - String originalDest = originalHeaderData.getDestination() != null ? originalHeaderData.getDestination() : "无"; - String newDest = newHeaderData.getDestination() != null ? newHeaderData.getDestination() : "无"; - headerChanges.append("
  • Destination: ") - .append(originalDest).append(" → ").append(newDest).append("
  • "); - } - - // 如果有头表字段变更,添加到邮件内容 - if (hasHeaderChanges) { - emailContent.append("
    头表字段变更
    "); - emailContent.append("
      ").append(headerChanges).append("
    "); - } - - // 处理明细变更 - 生成对比表格 - emailContent.append("
    明细数据对比
    "); - emailContent.append(generateDetailComparisonTable(dbData, newData)); - - emailContent.append(""); - emailContent.append(""); - - return emailContent; - } - - /** - * 生成明细对比表格 - * @param dbData 原数据 - * @param newData 新数据 - * @return 表格HTML - */ - private String generateDetailComparisonTable(List dbData, - List newData) { - StringBuilder table = new StringBuilder(); - - // 注意:不能用pn作为key,因为同一个pn可能有多行记录 - // 应该用itemNo进行一对一匹配,比较每一行的变化 - Map dbMap = dbData.stream() - .collect(Collectors.toMap(EcssCoDelNotifyDetail::getItemNo, detail -> detail, (v1, v2) -> v1)); - Map newMap = newData.stream() - .collect(Collectors.toMap(EcssCoDelNotifyDetail::getItemNo, detail -> detail, (v1, v2) -> v1)); - - // 获取所有itemNo - Set allItemNos = new HashSet<>(); - allItemNos.addAll(dbMap.keySet()); - allItemNos.addAll(newMap.keySet()); - List sortedItemNos = new ArrayList<>(allItemNos); - sortedItemNos.sort(Integer::compareTo); - - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - - for (Integer itemNo : sortedItemNos) { - EcssCoDelNotifyDetailData oldDetail = dbMap.get(itemNo); - EcssCoDelNotifyDetailData newDetail = newMap.get(itemNo); - - table.append(""); - table.append(""); - - // 判断变更类型 - if (oldDetail == null) { - // 新增行 - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - } else if (newDetail == null) { - // 删除行 - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - table.append(""); - } else { - // 判断各个字段的变化 - boolean pnChanged = !oldDetail.getPn().equals(newDetail.getPn()); - boolean qtyChanged = !oldDetail.getQty().equals(newDetail.getQty()); - boolean currencyChanged = !Objects.equals(oldDetail.getCurrency(), newDetail.getCurrency()); - boolean tpChanged = !Objects.equals(oldDetail.getTp(), newDetail.getTp()); - - // 构建变更说明 - List changes = new ArrayList<>(); - if (pnChanged) changes.add("换料"); - if (qtyChanged) changes.add("数量变更"); - if (currencyChanged) changes.add("Currency变更"); - if (tpChanged) changes.add("TP变更"); - String changeDesc = changes.isEmpty() ? "-" : String.join("、", changes); - - // PN列 - if (pnChanged) { - table.append(""); - } else { - table.append(""); - } - - // 数量列 - table.append(""); - if (qtyChanged) { - table.append(""); - } else { - table.append(""); - } - - // Currency列 - String oldCurrency = oldDetail.getCurrency() != null ? oldDetail.getCurrency() : "-"; - String newCurrency = newDetail.getCurrency() != null ? newDetail.getCurrency() : "-"; - table.append(""); - if (currencyChanged) { - table.append(""); - } else { - table.append(""); - } - - // TP列 - String oldTp = oldDetail.getTp() != null ? oldDetail.getTp().setScale(2, RoundingMode.HALF_UP).toString() : "-"; - String newTp = newDetail.getTp() != null ? newDetail.getTp().setScale(2, RoundingMode.HALF_UP).toString() : "-"; - table.append(""); - if (tpChanged) { - table.append(""); - } else { - table.append(""); - } - - // 变更说明列 - if (!changes.isEmpty()) { - table.append(""); - } else { - table.append(""); - } - } - - table.append(""); - } - - table.append("
    项次PN数量(原)数量(新)Currency(原)Currency(新)TP(原)TP(新)变更说明
    ").append(itemNo).append("").append(newDetail.getPn()).append("-").append(newDetail.getQty().setScale(0, RoundingMode.HALF_UP)).append("-").append(newDetail.getCurrency() != null ? newDetail.getCurrency() : "-").append("-").append(newDetail.getTp() != null ? newDetail.getTp().setScale(2, RoundingMode.HALF_UP).toString() : "-").append("新增").append(oldDetail.getPn()).append("").append(oldDetail.getQty().setScale(0, RoundingMode.HALF_UP)).append("-").append(oldDetail.getCurrency() != null ? oldDetail.getCurrency() : "-").append("-").append(oldDetail.getTp() != null ? oldDetail.getTp().setScale(2, RoundingMode.HALF_UP).toString() : "-").append("-已删除").append(oldDetail.getPn()).append(" → ").append(newDetail.getPn()).append("").append(newDetail.getPn()).append("").append(oldDetail.getQty().setScale(0, RoundingMode.HALF_UP)).append("").append(newDetail.getQty().setScale(0, RoundingMode.HALF_UP)).append("").append(newDetail.getQty().setScale(0, RoundingMode.HALF_UP)).append("").append(oldCurrency).append("").append(newCurrency).append("").append(newCurrency).append("").append(oldTp).append("").append(newTp).append("").append(newTp).append("").append(changeDesc).append("").append(changeDesc).append("
    "); - return table.toString(); - } - - @Override - public void updateEcssDel(EcssCoDelNotifyHeaderData data) { - List checkHeader = coDelMapper.checkEcssCoDelNotifyHeaderByDelNo(data.getSite(), data.getDelNo()); - if (checkHeader.isEmpty()) { - throw new RuntimeException("不存在该发货通知单请刷新界面"); - } - coDelMapper.updateEcssDel(data); - } - - @Override - public void cancerConfirmEcssDel(EcssCoDelNotifyHeaderData data) { - List checkHeader = coDelMapper.checkEcssCoDelNotifyHeaderByDelNo(data.getSite(), data.getDelNo()); - if (checkHeader.size() == 0) { - throw new RuntimeException("不存在该发货通知单请刷新界面"); + @Override + public void cancerConfirmEcssDel(EcssCoDelNotifyHeaderData data) { + List checkHeader = coDelMapper.checkEcssCoDelNotifyHeaderByDelNo(data.getSite(), data.getDelNo()); + if (checkHeader.size() == 0) { + throw new RuntimeException("不存在该发货通知单请刷新界面"); } if (!"仓库已确认".equals(checkHeader.get(0).getNotifyStatus())) { throw new RuntimeException("发货通知单状态不为仓库已确认,请刷新页面。"); @@ -1789,167 +498,6 @@ public class CoDelServiceImpl implements CoDelService { return coDelMapper.searchCoDelPalletDataNew(inData); } - @Override - @Transactional - public void saveCoDelPalletDataByExcel(MultipartFile file, EcssCoDelNotifyHeaderData inData, String palletRecords) { - // 先删除已存在的装箱数据 - clearPalletData(inData); - SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); - - List excelList = new ArrayList<>(); - try { - // 转流 - InputStream is = file.getInputStream(); - // 读取工作簿 - XSSFWorkbook workbook = new XSSFWorkbook(is); - // 读取工作表 - XSSFSheet sheet = workbook.getSheetAt(0); - // 剔除空行 - deleteEmptyRow(sheet); - // 获取行数 - int rows = sheet.getPhysicalNumberOfRows(); - // 遍历每一行(从第二行开始) - int groupSeqNo = 0; - BigDecimal boxQty = BigDecimal.ZERO; - BigDecimal grossWeight = BigDecimal.ZERO; - BigDecimal netWeight = BigDecimal.ZERO; - for (int j = 1; j < rows; j++) { - // 创建对象 - EcssCoDelPalletData excelData = new EcssCoDelPalletData(); - // 获得该行 - XSSFRow row = sheet.getRow(j); - if (row.getCell(1) != null && !inData.getCmcInvoice().equals(row.getCell(1).getStringCellValue())) { - continue; - } - if (row.getCell(6) == null) { - throw new RuntimeException("第" + j + "行的PN不能为空!"); - } - if (row.getCell(7) == null) { - throw new RuntimeException("第" + j + "行的数量不能为空!"); - } - // 为对象赋值 - excelData.setSite(inData.getSite()); // site - excelData.setBuNo(inData.getBuNo()); // bu - excelData.setDelNo(inData.getDelNo()); - excelData.setPoNo(getStringCellValue(row, 5)); - excelData.setPn(getStringCellValue(row, 6)); - List parts = coDelMapper.getPartNo(excelData.getSite(), excelData.getPn(),currentUser.getUsername(),inData.getBuNo()); - if (parts.isEmpty()) { - throw new RuntimeException("导入失败:物料:" + excelData.getPn() + "不存在!"); - } - excelData.setPartNo(parts.get(0).getPartNo()); - excelData.setQty(getNumericCellValueOrDefault(row, 7, "Qty (pcs)")); - excelData.setBoxQty(getNumericCellValueOrDefault(row, 2)); - excelData.setRolls(getNumericCellValueOrDefault(row, 8)); - excelData.setGrossWeight(getNumericCellValueOrDefault(row, 3)); - excelData.setNetWeight(getNumericCellValueOrDefault(row, 4)); - if (excelData.getBoxQty() == null && excelData.getGrossWeight() == null && excelData.getNetWeight() == null) { - excelData.setGroupSeqNo(groupSeqNo); - excelData.setBoxQty(boxQty); - excelData.setGrossWeight(grossWeight); - excelData.setNetWeight(netWeight); - } else { - groupSeqNo++; - excelData.setGroupSeqNo(groupSeqNo); - boxQty = excelData.getBoxQty(); - grossWeight = excelData.getGrossWeight(); - netWeight = excelData.getNetWeight(); - } - List checkPart = coDelMapper.checkPart(excelData.getSite(), excelData.getPartNo()); - if (checkPart.isEmpty()) { - throw new RuntimeException("导入失败:物料:" + excelData.getPartNo() + "在当前工厂不存在!"); - } - excelList.add(excelData); - } - } catch (NullPointerException e) { - throw new RuntimeException("导入失败:网络错误,请重新上传文档"); - } catch (Exception e) { - throw new RuntimeException("导入失败:" + e.getMessage()); - } - // 如果是沃尔玛订单,按照pn分组,同一个pn是一个EcssCoDelPalletHeaderData, - // 如果是非沃尔玛订单,按照序号分组,同一个序号是一个EcssCoDelPalletHeaderData, - Map> palletListMap = new HashMap<>(); - Map> palletListMap2 = new HashMap<>(); - for (EcssCoDelPalletData itemData : excelList){ - if (palletListMap2.containsKey(itemData.getGroupSeqNo())) { - palletListMap2.get(itemData.getGroupSeqNo()).add(itemData); - } else { - List palletDataList = new ArrayList<>(); - palletDataList.add(itemData); - palletListMap2.put(itemData.getGroupSeqNo(),palletDataList); - } - } - EcssWalMartOrder task = new EcssWalMartOrder(); - List headerList = new ArrayList<>(); - List boxList = new ArrayList<>(); - List detailList = new ArrayList<>(); - int seqNo = 0; - for (Map.Entry> entry : palletListMap2.entrySet()) { - seqNo++; - EcssCoDelBoxListData boxListData = new EcssCoDelBoxListData(); - boxListData.setSite(inData.getSite()); - boxListData.setBuNo(inData.getBuNo()); - boxListData.setDelNo(inData.getDelNo()); - boxListData.setItemNo(seqNo); - boxListData.setGrossWeight(entry.getValue().get(0).getGrossWeight()); - boxListData.setNetWeight(entry.getValue().get(0).getNetWeight()); - boxListData.setBoxQty(entry.getValue().get(0).getBoxQty()); - boxListData.setCreateBy(currentUser.getUsername()); - boxList.add(boxListData); - int i=0; - for (EcssCoDelPalletData thisData : entry.getValue()) { - EcssCoDelPalletDetailData detailData = new EcssCoDelPalletDetailData(); - detailData.setSite(thisData.getSite()); - detailData.setBuNo(thisData.getBuNo()); - detailData.setDelNo(thisData.getDelNo()); - detailData.setSeqNo(seqNo); - detailData.setItemNo(i + 1); - i++; - detailData.setPartNo(thisData.getPartNo()); - detailData.setPn(thisData.getPn()); - detailData.setQty(thisData.getQty()); - detailData.setPoNo(thisData.getPoNo()); - detailData.setBoxQty(thisData.getBoxQty()); - detailData.setRolls(thisData.getRolls()); - detailData.setCreateBy(currentUser.getUsername()); - detailList.add(detailData); - } - } - for (EcssCoDelBoxListData boxData : boxList) { - coDelMapper.saveCodelBoxList(boxData); - } - Map palletDetailMap = new HashMap<>(); - for (EcssCoDelPalletDetailData ecssCoDelPalletDetailData : detailList) { - coDelMapper.saveCodelPalletDetail(ecssCoDelPalletDetailData); - if (!palletDetailMap.containsKey(ecssCoDelPalletDetailData.getPn())) { - palletDetailMap.put(ecssCoDelPalletDetailData.getPn(), ecssCoDelPalletDetailData.getQty()); - } else { - palletDetailMap.put(ecssCoDelPalletDetailData.getPn(), palletDetailMap.get(ecssCoDelPalletDetailData.getPn()).add(ecssCoDelPalletDetailData.getQty())); - } - } - List ecssCoDelNotifyDetail = coDelMapper.searchEcssCoDelNotifyDetail(inData); - Map notifyDetailMap = new HashMap<>(); - for (EcssCoDelNotifyDetailData detailData : ecssCoDelNotifyDetail) { - if (!notifyDetailMap.containsKey(detailData.getPn())) { - notifyDetailMap.put(detailData.getPn(), detailData.getQty()); - } else { - notifyDetailMap.put(detailData.getPn(), notifyDetailMap.get(detailData.getPn()).add(detailData.getQty())); - } - } - for (Map.Entry entry : palletDetailMap.entrySet()) { - if (notifyDetailMap.get(entry.getKey()).compareTo(entry.getValue())!=0) { - throw new RuntimeException("物料["+entry.getKey()+"]的数量和发货通知单数量不一致!"); - } - } - // 处理栈板记录 - palletHeaderSave(inData.getSite(), inData.getBuNo(), inData.getDelNo(), palletRecords, currentUser); - - //将发货通知单modifyFlag变更为false - inData.setModifyFlag(false); - coDelMapper.updateEcssDelHeaderForModify(inData); - coDelMapper.updateEcssDelDetailForModify(inData); - } - private void palletHeaderSave(String site,String buNo,String delNo, String palletRecords, SysUserEntity currentUser) { List> palletRecordList = new ArrayList<>(); if (palletRecords != null && !palletRecords.isEmpty()) { @@ -2393,1386 +941,86 @@ public class CoDelServiceImpl implements CoDelService { @Override public void savePalletData(EcssPalletData inData) { SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); - String site = coDelMapper.getSiteByBu(inData.getBuNo()); - if (inData.getAddFlag() == 0) { - //0是新增 - List palletDataList = coDelMapper.getPallet(site,inData.getBuNo(),inData.getPalletNo()); - if(!palletDataList.isEmpty()){ - throw new RuntimeException("编码["+inData.getPalletNo()+"]已存在!"); - } - inData.setCreateBy(currentUser.getUsername()); - inData.setSite(site); - coDelMapper.insertPalletData(inData); - } else { - inData.setUpdateBy(currentUser.getUsername()); - inData.setSite(site); - coDelMapper.updatePalletData(inData); - } - } - - @Override - public void deletePallet(EcssPalletData data) { - coDelMapper.deletePallet(data); - } - - @Override - public PageUtils searchPackageData(EcssPackageData data) { - IPage resultList = coDelMapper.searchPackageData(new Page(data.getPage(), data.getLimit()), data); - return new PageUtils(resultList); - } - - @Override - public void savePackageData(EcssPackageData inData) { - SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); - String site = coDelMapper.getSiteByBu(inData.getBuNo()); - inData.setVolume(inData.getLength().multiply(inData.getWidth()).multiply(inData.getHeight())); - if (inData.getAddFlag() == 0) { - //0是新增 - List packageDataList = coDelMapper.getPackage(site,inData.getBuNo(),inData.getPackageNo()); - if(!packageDataList.isEmpty()){ - throw new RuntimeException("编码["+inData.getPackageNo()+"]已存在!"); - } - inData.setCreateBy(currentUser.getUsername()); - inData.setSite(site); - coDelMapper.insertPackageData(inData); - } else { - inData.setUpdateBy(currentUser.getUsername()); - inData.setSite(site); - coDelMapper.updatePackageData(inData); - } - } - - @Override - public void deleteEcssPackage(EcssPackageData data) { - coDelMapper.deleteEcssPackage(data); - } - - @Override - public List searchHsCodeList(EcssParamData data) { - return coDelMapper.searchHsCodeList(data); - } - - @Override - public List searchPalletList(EcssParamData data) { - return coDelMapper.searchPalletList(data); - } - - @Override - public List searchPackageList(EcssParamData data) { - return coDelMapper.searchPackageList(data); - } - - @Override - public List getNotifyPartDetail(EcssDeclarationHeaderData inData) { - return coDelMapper.getNotifyPartDetail(inData); - } - - /** - * 导出申报要素 - */ - @Override - public void downloadDeclarationElements(HttpServletResponse response, EcssDeclarationHeaderData data) { - try { - ExcelTemplate template = ExcelTemplate.load(new ClassPathResource("templates/declaration-elements-template.xlsx").getInputStream()); - extractedElements(data, template); - try (XSSFWorkbook workbook = template.render(0)) { - workbook.write(response.getOutputStream()); - } - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - response.setHeader("Content-Disposition", "attachment; filename=\"报关要素.xlsx\""); - response.flushBuffer(); - } catch (Exception e) { - log.error("导出报关要素异常{}", e.getMessage()); - e.printStackTrace(); - throw new RuntimeException("报关导出异常:"+e.getMessage()); - } - } - /** - * 导出报关单 - */ - @Override - public void downloadDeclaration(HttpServletResponse response, EcssDeclarationHeaderData data) { - try { - ExcelTemplate template = ExcelTemplate.load(new ClassPathResource("templates/declaration-template.xlsx").getInputStream()); - extractedDeclaration(data, template); - try (XSSFWorkbook workbook = template.render(0)) { - workbook.write(response.getOutputStream()); - } - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - response.setHeader("Content-Disposition", "attachment; filename=\"报关单.xlsx\""); - response.flushBuffer(); - } catch (Exception e) { - log.error("导出报关单异常{}", e.getMessage()); - e.printStackTrace(); - throw new RuntimeException("报关导出异常:"+e.getMessage()); - } - } - - private String stringInput(String inString) { - if (StringUtils.isEmpty(inString)) { - return ""; - } else { - return inString; - } - } - /** - * 导出发票 - */ - @Override - public void downloadInvoice(HttpServletResponse response, EcssDeclarationHeaderData data) { - try { - EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); - // 硬标天线需要po(declaration-invoice2-template.xlsx),且要长, - // RFID和RF不需要po,合同带印章(declaration-invoice-seal-template.xlsx),发票号不带章declaration-invoice-template.xlsx - String xlsx = notifyHeader.getBuNo().equals("04-MHM") || notifyHeader.getBuNo().equals("02-Hardtag")? - "templates/declaration-invoice2-template.xlsx":"templates/declaration-invoice-template.xlsx"; - if (data.getShowWeight()!=null && data.getShowWeight()) { - xlsx = "templates/declaration-invoice-showWeight-template.xlsx"; - - } - ExcelTemplate template = ExcelTemplate.load(new ClassPathResource(xlsx).getInputStream()); - extractedInvoice(data, template, notifyHeader); - try (XSSFWorkbook workbook = template.render(0)) { - workbook.write(response.getOutputStream()); - } - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - response.setHeader("Content-Disposition", "attachment; filename=\"发票.xlsx\""); - response.flushBuffer(); - } catch (Exception e) { - log.error("报关导出发票异常:{}", e.getMessage()); - e.printStackTrace(); - throw new RuntimeException("报关导出异常:"+e.getMessage()); - } - } - /** - * 导出箱单 - */ - @Override - public void downloadPackingList(HttpServletResponse response, EcssDeclarationHeaderData data) { - try { - EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); - // RFID和RF不需要序号列(declaration-packingList-template.xlsx),其他需要序列号(declaration-packingList2-template.xlsx) - ExcelTemplate template = ExcelTemplate.load(new ClassPathResource( - notifyHeader.getBuNo().equals("03-RFID") || notifyHeader.getBuNo().equals("01-Label")? - "templates/declaration-packingList-template.xlsx":"templates/declaration-packingList2-template.xlsx").getInputStream()); - exportPackingList(data, template,notifyHeader,0); - try (XSSFWorkbook workbook = template.render(0)) { - workbook.write(response.getOutputStream()); - } - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - response.setHeader("Content-Disposition", "attachment; filename=\"箱单.xlsx\""); - response.flushBuffer(); - } catch (Exception e) { - log.error("报关导出箱单异常:{}", e.getMessage()); - e.printStackTrace(); - throw new RuntimeException("报关导出异常:"+e.getMessage()); - } - } - - /** - * 导出装箱数据导入模版 - */ - @Override - public void exportPackingTemplate(HttpServletResponse response, EcssCoDelNotifyHeaderData data) { - try { - ExcelTemplate template = ExcelTemplate.load(new ClassPathResource("templates/packing-template.xlsx").getInputStream()); - template.setBoxFlag(true); - // 获取装箱数据 - List> palletList = sqlSession.selectList("ecssMapper.searchCoDelPalletData", data); - if (palletList == null || palletList.isEmpty()) { - // 如果没有装箱数据,获取通知单明细数据 - palletList = sqlSession.selectList("ecssMapper.searchEcssCoDelNotifyDetail", data); - } - //计算总数和总箱数 - int totalQty = 0; - int totalBoxQty = 0; - for (Map item : palletList) { - Object qtyObj = item.get("qty"); - Object boxQtyObj = item.get("boxQty"); - int qty = 0; - int boxQty = 0; - if (qtyObj instanceof Number) { - qty = ((Number) qtyObj).intValue(); - } else if (qtyObj instanceof String) { - try { - qty = Integer.parseInt((String) qtyObj); - } catch (NumberFormatException e) { - qty = 0; // 或者根据需求处理异常 - } - } - if (boxQtyObj instanceof Number) { - boxQty = ((Number) boxQtyObj).intValue(); - } else if (boxQtyObj instanceof String) { - try { - boxQty = Integer.parseInt((String) boxQtyObj); - } catch (NumberFormatException e) { - boxQty = 0; // 或者根据需求处理异常 - } - } - totalQty += qty; - totalBoxQty += boxQty; - item.put("qty", qty); - item.put("boxQty", boxQty); - } - - template.addVar("totalQty", totalQty); - template.addVar("totalBox", totalBoxQty); - // 添加数据列表 - template.addListVarAll( palletList); - - try (XSSFWorkbook workbook = template.render(0)) { - workbook.write(response.getOutputStream()); - } - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - response.setHeader("Content-Disposition", "attachment; filename=\"装箱数据导入模版.xlsx\""); - response.flushBuffer(); - } catch (Exception e) { - log.error("导出装箱数据模版异常:{}", e.getMessage()); - e.printStackTrace(); - throw new RuntimeException("导出装箱数据模版异常:"+e.getMessage()); - } - } - - /** - * 导出出口货物委托书 - */ - @Override - public void downloadExportGoods(HttpServletResponse response, EcssDeclarationHeaderData data) { - try { - ExcelTemplate template = ExcelTemplate.load(new ClassPathResource("templates/export-goods-template.xlsx").getInputStream()); - extractedExportGoods(data, template); - try (XSSFWorkbook workbook = template.render(0)) { - workbook.write(response.getOutputStream()); - } - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - response.setHeader("Content-Disposition", "attachment; filename=\"出口货物委托书.xlsx\""); - response.flushBuffer(); - } catch (Exception e) { - log.error("报关导出出口货物委托书异常:{}", e.getMessage()); - e.printStackTrace(); - throw new RuntimeException("报关导出异常:"+e.getMessage()); - } - } - - /** - * 导出合同 - */ - @Override - public void downloadContract(HttpServletResponse response, EcssDeclarationHeaderData data) { - try { - ExcelTemplate template = ExcelTemplate.load(new ClassPathResource("templates/declaration-contract-template.xlsx").getInputStream()); - extractedContract(data, template); - try (XSSFWorkbook workbook = template.render(0)) { - workbook.write(response.getOutputStream()); - } - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - response.setHeader("Content-Disposition", "attachment; filename=\"合同.xlsx\""); - response.flushBuffer(); - } catch (Exception e) { - log.error("报关导出合同异常:{}", e.getMessage()); - e.printStackTrace(); - throw new RuntimeException("报关导出异常:"+e.getMessage()); - } - } - - @Override - public void downloadAll(HttpServletResponse response, EcssDeclarationHeaderData data) { - EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); - // RFID和RF和硬标天线使用的模版不一样 - // RFID和RF如果勾选了合同,导出需要带公章(declaration-all-seal-template.xlsx) - String xlsx; - if (notifyHeader.getBuNo().equals("03-RFID") || notifyHeader.getBuNo().equals("01-Label")) { - xlsx = "templates/declaration-all-template.xlsx"; - } else { - // 硬标天线需要po(declaration-all2-template.xlsx),且多一个合同sheet - xlsx = "templates/declaration-all2-template.xlsx"; - } - if (data.getShowWeight()!=null && data.getShowWeight()) { - xlsx = "templates/declaration-all-showWeight-template.xlsx"; - - } - XSSFWorkbook workbook = null; - try { - ExcelTemplate template = ExcelTemplate.load(new ClassPathResource(xlsx).getInputStream()); - - // 第一个sheet - 出口货物委托书 - extractedExportGoods(data, template); - workbook = template.render(0); - - // 第二个sheet - 发票 - template.clearAll(); - extractedInvoice(data, template, notifyHeader); - template.render(1); - - // 第三个sheet - 箱单 - template.clearAll(); - exportPackingList(data, template, notifyHeader, 0); - template.render(2); - - // 第四个sheet - 报关单 - template.clearAll(); - extractedDeclaration(data, template); - template.render(3); - - // 第五个sheet - 申报要素 - template.clearAll(); - extractedElements(data, template); - template.render(4); - - // 第六个sheet - 合同 (仅特定BU需要) - if (notifyHeader.getBuNo().equals("04-MHM") || notifyHeader.getBuNo().equals("02-Hardtag")) { - template.clearAll(); - extractedContract(data, template); - template.render(5); - } - - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - response.setHeader("Content-Disposition", "attachment; filename=\"单证信息.xlsx\""); - workbook.write(response.getOutputStream()); - response.flushBuffer(); - } catch (IOException e) { - log.error("报关导出所有异常:{}", e.getMessage()); - e.printStackTrace(); - throw new RuntimeException("报关导出异常:"+e.getMessage()); - } finally { - if (workbook != null) { - try { - workbook.close(); // 显式关闭资源 - } catch (IOException e) { - log.error("关闭资源异常{}", e.getMessage()); - e.printStackTrace(); - } - } - } - } - - /** - * 导出PDF格式的单证信息(不包含出口货物委托书) - */ - @Override - public void downloadAllPdf(HttpServletResponse response, EcssDeclarationHeaderData data) { - EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); - // RFID和RF和硬标天线使用的模版不一样 - // RFID和RF如果勾选了合同,导出需要带公章(declaration-all-seal-template.xlsx) - String xlsx = "templates/declaration-all-template-pdf.xlsx"; - if (data.getShowWeight()!=null && data.getShowWeight()) { - xlsx = "templates/declaration-all-showWeight-template-pdf.xlsx"; - - } - XSSFWorkbook excelWorkbook = null; - try { - ExcelTemplate template = ExcelTemplate.load(new ClassPathResource(xlsx).getInputStream()); - - // 第一个sheet - 发票(PDF导出时跳过出口货物委托书) - extractedInvoice(data, template, notifyHeader); - excelWorkbook = template.render(0); - - // 第二个sheet - 箱单 - template.clearAll(); - exportPackingList(data, template, notifyHeader, 0); - template.render(1); - - // 第三个sheet - 报关单 - template.clearAll(); - extractedDeclaration(data, template); - template.render(2); - - // 第四个sheet - 申报要素 - template.clearAll(); - extractedElements(data, template); - template.render(3); - - // 第五个sheet - 合同 (仅特定BU需要) - if (notifyHeader.getBuNo().equals("04-MHM") || notifyHeader.getBuNo().equals("02-Hardtag")) { - template.clearAll(); - extractedContract(data, template); - template.render(4); - } - - // 将Excel转换为PDF - byte[] pdfBytes = convertExcelToPdf(excelWorkbook); - - response.setContentType("application/pdf"); - response.setHeader("Content-Disposition", "attachment; filename=\"单证信息.pdf\""); - response.getOutputStream().write(pdfBytes); - response.flushBuffer(); - - } catch (Exception e) { - log.error("报关导出PDF异常:{}", e.getMessage()); - e.printStackTrace(); - throw new RuntimeException("报关导出PDF异常:"+e.getMessage()); - } finally { - if (excelWorkbook != null) { - try { - excelWorkbook.close(); // 显式关闭资源 - } catch (IOException e) { - log.error("关闭资源异常{}", e.getMessage()); - e.printStackTrace(); - } - } - } - } - - /** - * 将Excel工作簿转换为PDF字节数组 - */ - private byte[] convertExcelToPdf(XSSFWorkbook excelWorkbook) throws Exception { - // 创建临时文件保存Excel - java.io.File tempExcelFile = java.io.File.createTempFile("temp_excel", ".xlsx"); - java.io.File tempPdfFile = java.io.File.createTempFile("temp_pdf", ".pdf"); - - try (java.io.FileOutputStream fos = new java.io.FileOutputStream(tempExcelFile)) { - excelWorkbook.write(fos); - } - - // 使用Aspose.Cells转换Excel为PDF - Workbook asposeWorkbook = new Workbook(tempExcelFile.getAbsolutePath()); - - // 设置PDF页面属性 - for (int i = 0; i < asposeWorkbook.getWorksheets().getCount(); i++) { - com.aspose.cells.Worksheet worksheet = asposeWorkbook.getWorksheets().get(i); - PageSetup pageSetup = worksheet.getPageSetup(); - - // 设置页面方向(纵向) - pageSetup.setOrientation(i==2?PageOrientationType.LANDSCAPE:PageOrientationType.PORTRAIT); - // 设置纸张大小(A4) - pageSetup.setPaperSize(PaperSizeType.PAPER_A_4); - // 设置缩放比例 - pageSetup.setZoom(85); - // 设置边距 - pageSetup.setLeftMargin(0.5); - pageSetup.setRightMargin(0.5); - pageSetup.setTopMargin(0.5); - pageSetup.setBottomMargin(0.5); - } - - // 保存为PDF - asposeWorkbook.save(tempPdfFile.getAbsolutePath(), SaveFormat.PDF); - - // 读取PDF文件为字节数组 - byte[] pdfBytes; - try (FileInputStream fis = new FileInputStream(tempPdfFile); - ByteArrayOutputStream bos = new ByteArrayOutputStream()) { - byte[] buffer = new byte[1024]; - int len; - while ((len = fis.read(buffer)) != -1) { - bos.write(buffer, 0, len); - } - pdfBytes = bos.toByteArray(); - } - - - // 清理临时文件 - tempExcelFile.delete(); - tempPdfFile.delete(); - - return pdfBytes; - } - - private void extractedDeclaration(EcssDeclarationHeaderData data, ExcelTemplate template) { - template.setMoveSeal(true); - template.setDelRight(true); - EcssDeclarationHeaderData ecHeader = coDelMapper.getDeclarationHeader(data); - template.addVar("inputCode", stringInput(ecHeader.getInputCode())); - template.addVar("customsOfficeCode", stringInput(ecHeader.getCustomsOfficeCode())); - template.addVar("localShipper", stringInput(ecHeader.getLocalShipper())); - template.addVar("localShipAddress", stringInput(ecHeader.getLocalShipAddress())); - template.addVar("shipType", stringInput(ecHeader.getShipType())); - template.addVar("shipDate", ecHeader.getShipDate() != null ? DateUtils.format(ecHeader.getShipDate(), "yyyy-MM-dd") : ""); - template.addVar("submitDate", ecHeader.getSubmitDate() != null ? DateUtils.format(ecHeader.getSubmitDate(), "yyyy-MM-dd") : ""); - template.addVar("filingNo", stringInput(ecHeader.getFilingNo())); - template.addVar("overseasShipper", stringInput(ecHeader.getOverseasShipper())); - template.addVar("shippingMode", stringInput(ecHeader.getShippingMode())); - template.addVar("shippingData", stringInput(ecHeader.getShippingData())); - template.addVar("deliverNo", stringInput(ecHeader.getDeliverNo())); - template.addVar("salesPartner", stringInput(ecHeader.getSalesPartner())); - template.addVar("regulatoryMethod", stringInput(ecHeader.getRegulatoryMethod())); - template.addVar("dutyStatus", stringInput(ecHeader.getDutyStatus())); - template.addVar("permitNumber", stringInput(ecHeader.getPermitNumber())); - template.addVar("cmcInvoice", stringInput(ecHeader.getCmcInvoice())); - template.addVar("salesArea", stringInput(ecHeader.getSalesArea())); - template.addVar("receiveArea", stringInput(ecHeader.getReceiveArea())); - template.addVar("shippingPort", stringInput(ecHeader.getShippingPort())); - template.addVar("exitPort", stringInput(ecHeader.getExitPort())); - template.addVar("documents", stringInput(ecHeader.getDocuments())); - template.addVar("packageType", stringInput(ecHeader.getPackageType())); - EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); - List palletHeaderDataList = coDelMapper.searchEcssCoDelPalletHeaderData(notifyHeader); - // 总托数 - Integer totalPlt = palletHeaderDataList.stream() - .map(EcssCoDelPalletHeaderData::getPalletQty) - .filter(Objects::nonNull) // 防止空指针 - .reduce(0, Integer::sum); - template.addVar("packageQty", data.getPackageUnit()!=null&&data.getPackageUnit().equals("箱")?ecHeader.getBoxQty():totalPlt); - template.addVar("packageUnit", data.getPackageUnit()); - // 托盘重量=根据每个pallet的重量*数量累加 - BigDecimal palletWeight = BigDecimal.ZERO; - for (EcssCoDelPalletHeaderData palletHeader : palletHeaderDataList) { - if (palletHeader.getPallet() != null && !palletHeader.getPallet().isEmpty()) { - // 根据pallet编号查询EcssPallet信息 - List palletDataList = coDelMapper.getPallet(notifyHeader.getSite(), notifyHeader.getBuNo(), palletHeader.getPallet()); - if (!palletDataList.isEmpty()) { - EcssPalletData palletData = palletDataList.get(0); - if (palletData.getPalletWeight() != null && palletHeader.getPalletQty() != null) { - // 栈板重量 = 单个栈板重量 * 数量 - BigDecimal singlePalletWeight = palletData.getPalletWeight().multiply(BigDecimal.valueOf(palletHeader.getPalletQty())); - palletWeight = palletWeight.add(singlePalletWeight); - } - } - } - } - BigDecimal grossWeight = ecHeader.getGrossWeight() != null ? ecHeader.getGrossWeight() : BigDecimal.ZERO; - template.addVar("grossWeight", (grossWeight.add(palletWeight)).setScale(2, RoundingMode.HALF_UP)); - template.addVar("netWeight", ecHeader.getNetWeight() != null ? ecHeader.getNetWeight().setScale(2, RoundingMode.HALF_UP) : ""); - template.addVar("paymentType", stringInput(ecHeader.getPaymentType())); - template.addVar("shipPrice", ecHeader.getShipPrice() != null ? ecHeader.getShipPrice().setScale(2, RoundingMode.HALF_UP) : ""); - template.addVar("premium", ecHeader.getPremium() != null ? ecHeader.getPremium().setScale(2, RoundingMode.HALF_UP) : ""); - template.addVar("otherPrice", ecHeader.getOtherPrice() != null ? ecHeader.getOtherPrice().setScale(2, RoundingMode.HALF_UP) : ""); - template.addVar("specialRelationshipComfirm", StringUtils.isEmpty(ecHeader.getSpecialRelationshipComfirm()) ? "" : - ecHeader.getSpecialRelationshipComfirm().equals("Y") ? "是" : "否"); - template.addVar("priceInfluenceConfirm", StringUtils.isEmpty(ecHeader.getPriceInfluenceConfirm()) ? "" : - ecHeader.getPriceInfluenceConfirm().equals("Y") ? "是" : "否"); - template.addVar("royaltyPaymentsConfirm", StringUtils.isEmpty(ecHeader.getRoyaltyPaymentsConfirm()) ? "" : - ecHeader.getRoyaltyPaymentsConfirm().equals("Y") ? "是" : "否"); - template.addVar("remark", stringInput(ecHeader.getRemark())); - - List detailList = coDelMapper.exportDeclarationDetail(data); - for (int i = 0; i < detailList.size(); i++) { - Map eorder = detailList.get(i); - eorder.put("row_num", i + 1); - if (data.getDeclarationWeight()!=null && data.getDeclarationWeight().equals("显示")) { - eorder.put("net_weight", eorder.get("net_weight") != null ? eorder.get("net_weight").toString() : ""); - eorder.put("weight_unit", eorder.get("weight_unit") != null ? eorder.get("weight_unit").toString() : ""); - } else { - eorder.put("net_weight", ""); - eorder.put("weight_unit", ""); - } - if (eorder.get("currency") != null) { - String currency = stringInput((String) eorder.get("currency")); - if (currency.toUpperCase().startsWith("RMB")) { - currency = "CNY"; - } - eorder.put("currency", currency); - } - eorder.put("hs_code_desc", eorder.get("hs_code_desc")); - } - template.addListVarAll(detailList); - } - - private void extractedElements(EcssDeclarationHeaderData data, ExcelTemplate template) { - template.addVar("brandType", data.getBrandType()); - Map propertiesMap = new HashMap<>(); - if (data.getPropertiesList()!=null && !data.getPropertiesList().isEmpty()) { - for (int i = 0; i < data.getPropertiesList().size(); i++) { - String key = data.getPropertiesList().get(i).getHsCode()+"&"+ data.getPropertiesList().get(i).getHsCodeDesc(); - if (!propertiesMap.containsKey(key)) { - propertiesMap.put(key, data.getPropertiesList().get(i).getBrand()); - } - } - } - // 获取partNo列表 - List partNos = coDelMapper.getDeclarationElements(data); - List partNoList = partNos.stream().map(map -> map.get("partNo").toString()).collect(Collectors.toList()); - // 根据partNo获取hsCode - List hsCodes = coDelMapper.getHsCodeByPartNo(data.getSite(), partNoList); - Map hsCodeMap = new HashMap<>(); - // 用于收集每个hsCode对应的SKU列表 - Map> hsCodeSkuListMap = new HashMap<>(); - for (int i = 0; i < hsCodes.size(); i++) { - Map eorder = hsCodes.get(i); - String key = eorder.get("hsCode")+"&"+eorder.get("hsCodeDesc"); - String sku = eorder.get("sku") != null ? eorder.get("sku").toString() : ""; - - if (hsCodeMap.containsKey(key)) { - // 将SKU添加到列表中 - hsCodeSkuListMap.get(key).add(sku); - } else { - // hscode所有属性 - PartSubPropertiesValueData inData = new PartSubPropertiesValueData(); - inData.setSite(data.getSite()); - inData.setBuNo(eorder.get("BuNo")!=null?eorder.get("BuNo").toString():""); - inData.setCodeNo(eorder.get("codeNo")!=null?eorder.get("codeNo").toString():""); - inData.setPartNo(eorder.get("hsCodeDesc")!=null?eorder.get("hsCodeDesc").toString():""); - inData.setRecordType("ECSS"); - List propertiesList = coDelMapper.getPropertiesListByPartAndCodeNo(inData); - StringBuilder properties = new StringBuilder(); - for (int j = 0; j < propertiesList.size(); j++) { - if (propertiesList.get(j).getItemDesc().equals(data.getBrand()) && propertiesMap.containsKey(key) - && !StringUtils.isBlank(propertiesMap.get(key))) { - propertiesList.get(j).setTextValue(propertiesMap.get(key)); - } - if (j == propertiesList.size() - 1) { - properties.append(propertiesList.get(j).getItemDesc()).append(": ").append(propertiesList.get(j).getTextValue()); - } else { - properties.append(propertiesList.get(j).getItemDesc()).append(": ").append(propertiesList.get(j).getTextValue()).append("\n"); - } - } - eorder.put("properties",properties); - hsCodeMap.put(key, eorder); - // 初始化SKU列表 - List skuList = new ArrayList<>(); - skuList.add(sku); - hsCodeSkuListMap.put(key, skuList); - } - } - - // 格式化SKU列表:每3个一行 - List list = new ArrayList<>(hsCodeMap.values()); - for (int i = 0; i < list.size(); i++) { - Map fMap = list.get(i); - String key = fMap.get("hsCode")+"&"+fMap.get("hsCodeDesc"); - List skuList = hsCodeSkuListMap.get(key); - String formattedSku = formatSkuList(skuList); - fMap.put("sku", formattedSku); - fMap.put("content", "HS Code: "+fMap.get("hsCode")+ - "\n品名: "+(data.getHsCodeDescType()!=null&& data.getHsCodeDescType().equals("N")?fMap.get("hsCodeDescEn"):fMap.get("hsCodeDesc"))+ - "\n"+fMap.get("properties")+ - "\n型号: "+formattedSku); - } - template.addListVarAll(list); - } - - /** - * 格式化SKU列表,每3个一行,从第2行开始前面加17个空格 - * - * @param skuList SKU列表 - * @return 格式化后的SKU字符串,格式如: "10001,10002,10003\n 10004,10005,10006" - */ - private String formatSkuList(List skuList) { - if (skuList == null || skuList.isEmpty()) { - return ""; - } - - StringBuilder result = new StringBuilder(); - for (int i = 0; i < skuList.size(); i++) { - if (i > 0) { - // 每3个换行 - if (i % 3 == 0) { - // 换行后添加17个空格 - result.append("\n "); - } else { - // 否则用逗号分隔 - result.append(","); - } - } - result.append(skuList.get(i)); - } - - return result.toString(); - } - - private void extractedInvoice(EcssDeclarationHeaderData data, ExcelTemplate template, - EcssCoDelNotifyHeaderData notifyHeader) { - List notifyDetailList = data.getNotifyPartDetailList(); - for (EcssCoDelNotifyDetailData nData:notifyDetailList){ - coDelMapper.updateEcssCoDelNotifyDetail(nData); - } - if (data.getDeclarationNo()!=null) { - coDelMapper.updateEcssDeclarationHeader(data); - } - template.setMoveShape(true); - template.setCellStyle(true); - template.setRangeStyle(true); - template.setPriceRight(true); - template.setMoveSeal(true); - template.setInvoiceLie(data.getShowWeight() != null && data.getShowWeight()); - template.addVar("remark", stringInput(data.getFpremark())); - template.addVar("localShipper", notifyHeader.getCustomerName()); - template.addVar("localShipAddress", notifyHeader.getLocalShipAddress()); - template.addVar("ccusname", stringInput(notifyHeader.getOverseasShipper())); - template.addVar("cDeliverAdd", stringInput(notifyHeader.getOverseasAddress())); - template.addVar("invoiceTitle", data.getContractFlag()!=null && data.getContractFlag()? "合同" : "INVOICE No."); - template.addVar("cmc_invoice", notifyHeader.getCmcInvoice()); - template.addVar("dateStr", DateUtils.format(notifyHeader.getReadyDate(), "yyyy-MM-dd")); - List detailList = coDelMapper.exportEcssCoDelNotifyDetail(data); - String currency = "CNY"; - if (!detailList.isEmpty()) { - Object cur = detailList.get(0).get("currency"); - if (cur != null) { - String curStr = cur.toString().trim(); - if (curStr.toUpperCase().startsWith("RMB")) { - currency = "CNY"; - } else { - currency = curStr; - } - } - } - template.addVar("Currency", currency); - template.addVar("Incoterm", "EXW "); - template.addVar("shippingMode", stringInput(notifyHeader.getShippingMode())); - Map notifyDetailMap = notifyDetailList.stream().collect( - Collectors.toMap(EcssCoDelNotifyDetailData::getPartNo,e->e)); - // 装箱明细 - 按发货通知单明细item_no分组(用于获取每行的重量) - List palletDetailList = coDelMapper.exportCoDelPalletDetailGroupByItemNo(notifyHeader); - Map palletMap = palletDetailList.stream().collect(Collectors.toMap( o -> o.get("item_no"), o -> o)); - // 装箱数据 - List palletHeaderDataList = coDelMapper.searchEcssCoDelPalletHeaderData(notifyHeader);// 总托数 - Integer totalPlt = palletHeaderDataList.stream() - .map(EcssCoDelPalletHeaderData::getPalletQty) - .filter(Objects::nonNull) // 防止空指针 - .reduce(0, Integer::sum); - - Map poNoMap = new HashMap<>(); - for (int i = 0; i < detailList.size(); i++) { - Map eorder = detailList.get(i); - eorder.put("row_num", i + 1); - poNoMap.put(eorder.get("customerPO"), eorder.get("customerPO")); - } - StringBuilder ponos = new StringBuilder(); - poNoMap.forEach((key, value) -> ponos.append(key + " ")); - template.addVar("poNo", ponos); - BigDecimal allPrice = BigDecimal.valueOf(0.0); - List ndList = sqlSession.selectList("ecssMapper.searchEcssCoDelNotifyDetailList", notifyHeader); - for (int i = 0; i < ndList.size(); i++) { - Map eorder = ndList.get(i); - String partNo = (String) eorder.get("part_no"); - Integer itemNo = eorder.get("item_no") != null ? (Integer) eorder.get("item_no") : null; - eorder.put("row_num", i + 1); - EcssCoDelNotifyDetailData nodifyData = notifyDetailMap.get(partNo); - // 根据item_no获取该明细行对应的装箱重量 - Map pm = itemNo != null ? palletMap.get(itemNo) : null; - int totalQty = pm!=null && pm.get("total_qty")!=null?Integer.parseInt(pm.get("total_qty").toString()):0; - String lossratio = ""; - if (nodifyData!=null && nodifyData.getLossratio()!=null && !StringUtils.isBlank(nodifyData.getLossratio())) { - // 关务物料属性 - List propertiesValues = coDelMapper.getPropertiesListByTypeAndCodeNo( - notifyHeader.getSite(), "ECSSPART","BG001", notifyHeader.getBuNo(), partNo); - // 每一个物料加属性对应一个属性,用来下面遍历发货通知单明细时获取具体属性值 - Map> partNoAndItemNoMap = propertiesValues.stream() - .collect(Collectors.groupingBy(PartSubPropertiesValue::getPropertiesItemNo)); - lossratio = "\n" + (nodifyData.getEhundred()!=null && StringUtils.isNotEmpty(nodifyData.getEhundred()) - ?"E100-"+nodifyData.getEhundred():"") - +" "+"纯FSC纸重量:"+ - fscWeight(partNoAndItemNoMap,partNo, totalQty, nodifyData.getLossratio()); - } - String hsCodeDesc = data.getHsCodeDesc()!=null&& data.getHsCodeDesc()? - ("\n"+(data.getHsCodeDescTypeInvoice()!=null&& data.getHsCodeDescTypeInvoice().equals("N") - ?eorder.get("hsCodeDescEn") :eorder.get("hsCodeDesc"))):""; - eorder.put("designation", eorder.get("part_description") + "\n" + "PO:" + eorder.get("customerPO") - + (data.getHsCodeFlag()!=null && data.getHsCodeFlag()?" HS Code:" + eorder.get("hsCode"):"")+hsCodeDesc+ lossratio); - eorder.put("totalPrice", (((BigDecimal) eorder.get("qty")).multiply((BigDecimal) eorder.get("unitPrice"))). - setScale(2, RoundingMode.HALF_UP)); - allPrice = allPrice.add(((BigDecimal) eorder.get("qty")).multiply((BigDecimal) eorder.get("unitPrice"))); - if (data.getShowWeight()!=null && data.getShowWeight()) { - // 根据发货通知单明细查找装箱明细,获取重量和净重,保留2位小数 - eorder.put("grossWeight", pm != null && pm.get("gross_weight") != null ? - new BigDecimal(pm.get("gross_weight").toString()).setScale(2, RoundingMode.HALF_UP) : ""); - eorder.put("netWeight", pm != null && pm.get("net_weight") != null ? - new BigDecimal(pm.get("net_weight").toString()).setScale(2, RoundingMode.HALF_UP) : ""); - } - } - // RFID需要的 - if (notifyHeader.getBuNo().equals("01-Label") || notifyHeader.getBuNo().equals("03-RFID")) { - template.addVar("madein", stringInput(data.getOrigin())); - } - // 孟加拉需要的 - if (data.getMaterial()!=null && data.getMaterial()) { - template.addVar("RFIDBase", "RFID Base Material"); - template.addVar("HSCode", "& H.S.Code:"+stringInput(data.getHsCode())); - } - // 欧洲地区需要 - if (data.getPackaging()!=null && data.getPackaging()) { - template.addVar("packaging", "Non-reusable plastic packaging:"); - // 根据维护参数计算出KGS=总托数*plastic packaging维护参数 - BigDecimal totalKgs = data.getKgs()!=null?data.getKgs():BigDecimal.ZERO; - if (palletHeaderDataList.isEmpty()) { - totalKgs = BigDecimal.valueOf(0.1); - } - List list = coDelMapper.selectBoxList(notifyHeader); - BigDecimal totalCartons = BigDecimal.valueOf(0.0); - for (int m = 0; m < list.size(); m++) { - totalCartons = totalCartons.add(new BigDecimal(list.get(m).get("box_qty") != null ? - list.get(m).get("box_qty").toString() : "0")); - } - if (palletHeaderDataList.isEmpty()) { - totalPlt = totalCartons.setScale(0, RoundingMode.HALF_UP).intValue(); - } - template.addVar("KGS_qty", data.getKgs()!=null?(totalKgs.multiply(BigDecimal.valueOf(totalPlt))).setScale(2, RoundingMode.HALF_UP):""); - template.addVar("KGS", "KGS"); - } - // 新加的 货物明细单选框 勾选显示 - if (data.getGoodsLabel()!=null && data.getGoodsLabel()) { - template.addVar("nameLabel", "Name of goods"+(notifyHeader.getBuNo().equals("03-RFID")?" RFID LABEL":" RF LABEL")); - template.addVar("originLabel", "Origin of goods Made in China"); - template.addVar("sellerLabel", "Name of seller Checkpoint Commercial (Shanghai) Co.. Ltd."); - } - template.addVar("allPrice", allPrice.setScale(2, RoundingMode.HALF_UP)); - template.addListVarAll(ndList); - } - - private void exportPackingList(EcssDeclarationHeaderData data, ExcelTemplate template, - EcssCoDelNotifyHeaderData notifyHeader,int type) { - EcssDeclarationHeaderData ecHeader = coDelMapper.getDeclarationHeader(data); - List notifyDetailList; - if (type==0) { - coDelMapper.updateEcssDeclarationHeader(data); - notifyDetailList = data.getNotifyPartDetailList(); - for (EcssCoDelNotifyDetailData nData:notifyDetailList){ - coDelMapper.updateEcssCoDelNotifyDetail(nData); - } - } else { - notifyDetailList = coDelMapper.getNotifyPartDetail2(data); - } - Map notifyDetailMap = notifyDetailList.stream().collect( - Collectors.toMap(EcssCoDelNotifyDetailData::getPartNo,e->e)); - template.setCellStyle(true); - template.setRangeStyle(true); - template.setMoveSeal(true); - template.setIntRight(true); - if (notifyHeader.getBuNo().equals("03-RFID") || notifyHeader.getBuNo().equals("01-Label")){ - template.setCellStyle2(true); - } - template.addVar("remark", data.getXdremark()); - template.addVar("localShipper", notifyHeader.getCustomerName()); - template.addVar("localShipAddress", notifyHeader.getLocalShipAddress()); - template.addVar("ccusname", stringInput(notifyHeader.getOverseasShipper())); - template.addVar("cDeliverAdd", stringInput(notifyHeader.getOverseasAddress())); - template.addVar("dateStr", DateUtils.format(notifyHeader.getReadyDate(), "yyyy-MM-dd")); - template.addVar("cmc_invoice", notifyHeader.getCmcInvoice()); - Map poNoMap = new HashMap<>(); - // 装箱数据 - List palletHeaderDataList = coDelMapper.searchEcssCoDelPalletHeaderData(notifyHeader); - // 最高的栈板 - Optional palletHeaderData = palletHeaderDataList.isEmpty()? Optional.empty() - :palletHeaderDataList.stream().max(Comparator.comparing(EcssCoDelPalletHeaderData::getHeight)); - // 总托数 - Integer totalPlt = palletHeaderDataList.stream() - .map(EcssCoDelPalletHeaderData::getPalletQty) - .filter(Objects::nonNull) // 防止空指针 - .reduce(0, Integer::sum); - // 发货通知单明细 - List detailList = coDelMapper.exportEcssCoDelNotifyDetail(data); - // 获取poNo - for (int i = 0; i < detailList.size(); i++) { - Map eorder = detailList.get(i); - poNoMap.put(eorder.get("customerPO"), eorder.get("customerPO")); - } - StringBuilder ponos = new StringBuilder(); - poNoMap.forEach((key, value) -> ponos.append(key + " ")); - template.addVar("poNo", ponos); - List list = coDelMapper.selectBoxList(notifyHeader); - BigDecimal totalCartons = BigDecimal.valueOf(0.0); - BigDecimal grossWeight = BigDecimal.valueOf(0.0); - BigDecimal netWeight = BigDecimal.valueOf(0.0); - BigDecimal rolls = BigDecimal.valueOf(0.0); - int totalQty = 0; - List exportList = new ArrayList<>();//全部需要导出的明细 - EcssCoDelPalletHeaderData boxData = new EcssCoDelPalletHeaderData(); - boxData.setSite(data.getSite()); - boxData.setBuNo(notifyHeader.getBuNo()); - boxData.setDelNo(notifyHeader.getDelNo()); - for (int m = 0; m < list.size(); m++) { - boxData.setSeqNo(Integer.valueOf(list.get(m).get("item_no")!=null?list.get(m).get("item_no").toString():"")); - List checkList = coDelMapper.selectPalletDetailList(boxData); - // 记录当前箱子对应的exportList起始索引 - int boxStartIndex = exportList.size(); - - // 判断当前箱子所有明细的rolls是否全部为空或0 - boolean allDetailRollsEmptyOrZero = true; - for (int j = 0; j < checkList.size(); j++) { - Object rollsObj = checkList.get(j).get("rolls"); - if (rollsObj != null) { - BigDecimal rollsValue = (BigDecimal) rollsObj; - if (rollsValue.compareTo(BigDecimal.ZERO) != 0) { - allDetailRollsEmptyOrZero = false; - break; - } - } - } - // 获取box的rolls - BigDecimal boxRolls = list.get(m).get("rolls") != null ? - ((BigDecimal) list.get(m).get("rolls")).setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO; - - for (int i = 0; i < checkList.size(); i++) { - Map eorder = checkList.get(i); - String partNo = (String) eorder.get("part_no"); - int qty = ((BigDecimal)eorder.get("qty")).setScale(0, RoundingMode.HALF_UP).intValue(); - totalQty = totalQty + qty; - BigDecimal noCartons = list.get(m).get("box_qty")!=null?(BigDecimal) list.get(m).get("box_qty"):BigDecimal.ZERO; - eorder.put("total_qty", ((BigDecimal)eorder.get("qty")).setScale(0, RoundingMode.HALF_UP).intValue()); - // 合箱时:第一行显示数值,用于合并单元格居中显示 - boolean isFirstRow = (i == 0); - eorder.put("noCartons", isFirstRow ? noCartons.setScale(0, RoundingMode.HALF_UP) : ""); - eorder.put("gross_weight", isFirstRow ? ((BigDecimal) list.get(m).get("gross_weight")).setScale(2, RoundingMode.HALF_UP) : ""); - eorder.put("net_weight", isFirstRow ? ((BigDecimal) list.get(m).get("net_weight")).setScale(2, RoundingMode.HALF_UP) : ""); - - // rolls处理:如果明细的rolls全部为空或0,则取box的rolls;合箱时合并居中显示 - if (allDetailRollsEmptyOrZero) { - // 使用box的rolls,合箱时只在第一行显示(用于合并单元格居中) - eorder.put("rolls", isFirstRow ? boxRolls : ""); - if (isFirstRow) { - rolls = rolls.add(boxRolls); - } - } else { - // 使用明细的rolls - BigDecimal cuRolls = eorder.get("rolls") != null ? - ((BigDecimal) eorder.get("rolls")).setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO; - eorder.put("rolls", cuRolls); - rolls = rolls.add(cuRolls); - } - EcssCoDelNotifyDetailData nodifyData = notifyDetailMap.get(partNo); - // 只有RFID需要 - String lossratio=""; - if (notifyHeader.getBuNo().equals("03-RFID") && nodifyData!=null && StringUtils.isNotEmpty(nodifyData.getLossratio())) { - // 关务物料属性 - List propertiesValues = coDelMapper.getPropertiesListByTypeAndCodeNo( - notifyHeader.getSite(), "ECSSPART","BG001",notifyHeader.getBuNo(), partNo); - // 每一个物料加属性对应一个属性,用来下面遍历发货通知单明细时获取具体属性值 - Map> partNoAndItemNoMap = propertiesValues.stream() - .collect(Collectors.groupingBy(PartSubPropertiesValue::getPropertiesItemNo)); - lossratio = "\n" + (StringUtils.isNotEmpty(nodifyData.getEhundred())?"E100-"+nodifyData.getEhundred():"") - +" "+"纯FSC纸重量:"+ - fscWeight(partNoAndItemNoMap,partNo, qty, nodifyData.getLossratio()); - } - String hsCodeDesc = ""; - if (notifyHeader.getBuNo().equals("04-MHM") || notifyHeader.getBuNo().equals("02-Hardtag")) { - hsCodeDesc = "\n"+(data.getHsCodeDescType()!=null&& data.getHsCodeDescType().equals("N") - ?eorder.get("hsCodeDescEn") :eorder.get("hsCodeDesc")); - } - eorder.put("artNo", eorder.get("part_description") + "\n" + "PO:" + eorder.get("po_no") - + (data.getHsCodeFlag()!=null && data.getHsCodeFlag()?" HS Code:" + eorder.get("hsCode"):"")+ hsCodeDesc + lossratio - + (data.getUpc()!=null && data.getUpc()?"\n" + "UPC:"+ eorder.get("upc"):"") - + (data.getSo()!=null && data.getSo()?"\n" + "SO:"+eorder.get("so"):"")); - exportList.add(eorder); - } - // 如果当前箱子对应多个物料(合箱),添加合并单元格区域 - int boxEndIndex = exportList.size() - 1; - if (checkList.size() > 1) { - // 合并箱数列(第4列,索引4) - template.addMergeRegion(boxStartIndex, boxEndIndex, 4); - // 合并净重列(第6列,索引6) - template.addMergeRegion(boxStartIndex, boxEndIndex, 6); - // 合并毛重列(第7列,索引7) - template.addMergeRegion(boxStartIndex, boxEndIndex, 7); - // 如果明细rolls全为空/0,使用box的rolls时需要合并居中显示(第5列,索引5) - if (allDetailRollsEmptyOrZero) { - template.addMergeRegion(boxStartIndex, boxEndIndex, 8); - } - } - totalCartons = totalCartons.add(list.get(m).get("box_qty") !=null? new BigDecimal(list.get(m).get("box_qty").toString()) : BigDecimal.valueOf(0.0)); - grossWeight = grossWeight.add(list.get(m).get("gross_weight") !=null?new BigDecimal(list.get(m).get("gross_weight").toString()):BigDecimal.valueOf(0.0)); - netWeight = netWeight.add(list.get(m).get("net_weight") !=null?new BigDecimal(list.get(m).get("net_weight").toString()):BigDecimal.valueOf(0.0)); - } - // 托盘重量=根据每个pallet的重量*数量累加 - BigDecimal palletWeight = BigDecimal.ZERO; - for (EcssCoDelPalletHeaderData palletHeader : palletHeaderDataList) { - if (palletHeader.getPallet() != null && !palletHeader.getPallet().isEmpty()) { - // 根据pallet编号查询EcssPallet信息 - List palletDataList = coDelMapper.getPallet(notifyHeader.getSite(), notifyHeader.getBuNo(), palletHeader.getPallet()); - if (!palletDataList.isEmpty()) { - EcssPalletData palletData = palletDataList.get(0); - if (palletData.getPalletWeight() != null && palletHeader.getPalletQty() != null) { - // 栈板重量 = 单个栈板重量 * 数量 - BigDecimal singlePalletWeight = palletData.getPalletWeight().multiply(BigDecimal.valueOf(palletHeader.getPalletQty())); - palletWeight = palletWeight.add(singlePalletWeight); - } - } - } - } - template.addVar("Total_Cartons", totalCartons.setScale(0, RoundingMode.HALF_UP)); - template.addVar("Gross_Weight", (grossWeight.add(palletWeight)).setScale(2, RoundingMode.HALF_UP)); - template.addVar("Net_Weight", netWeight.setScale(2, RoundingMode.HALF_UP)); - template.addVar("total_weight", (grossWeight.add(palletWeight)).setScale(2, RoundingMode.HALF_UP)); - template.addVar("total_netweight", netWeight.setScale(2, RoundingMode.HALF_UP)); - template.addVar("total_box", totalCartons.setScale(0, RoundingMode.HALF_UP)); - template.addVar("total_qty", totalQty); - template.addVar("total_rolls", rolls.setScale(0, RoundingMode.HALF_UP)); - // 下面是可选的或者手动维护的 - // RFID需要的 - if (notifyHeader.getBuNo().equals("01-Label") || notifyHeader.getBuNo().equals("03-RFID")) { - String plt = "CTN"; - if (!palletHeaderDataList.isEmpty()) { - plt = "PLT"; - } else { - totalPlt = totalCartons.setScale(0, RoundingMode.HALF_UP).intValue(); - } - template.addVar("total_plt", totalPlt+plt); - template.addVar("total:", "total:"); - template.addVar("madein", stringInput(data.getOrigin())); - template.addVar("shippingNo", "shipping no"); - template.addVar("sp_cmc_invoice", notifyHeader.getCmcInvoice()); - // 如果palletWeight==0,不显示 - template.addVar("pallet_weight_name", palletWeight.compareTo(BigDecimal.ZERO)==0?"":"pallet weight:"); - template.addVar("pallet_weight", palletWeight.compareTo(BigDecimal.ZERO)==0?"":palletWeight.setScale(0, RoundingMode.HALF_UP)); - } - // 孟加拉需要的 - if (data.getMaterial()!=null && data.getMaterial()) { - template.addVar("RFIDBase", "RFID Base Material"); - template.addVar("HSCode", "& H.S.Code:"+stringInput(data.getHsCode())); - /* template.addVar("volume", !palletHeaderData.isPresent()?"":palletHeaderData.map(ecssCoDelPalletHeaderData -> - ecssCoDelPalletHeaderData.getLength().setScale(2, RoundingMode.HALF_UP) - + "*" + ecssCoDelPalletHeaderData.getWidth().setScale(2, RoundingMode.HALF_UP) + "*" - + ecssCoDelPalletHeaderData.getHeight().setScale(2, RoundingMode.HALF_UP) + "m").orElse(""));*/ - } - // 欧洲地区需要 - if (data.getPackaging()!=null && data.getPackaging()) { - template.addVar("packaging", "Non-reusable plastic packaging:"); - // 根据维护参数计算出KGS=总托数*plastic packaging维护参数 - BigDecimal totalKgs = data.getKgs()!=null?data.getKgs():BigDecimal.ZERO; - if (palletHeaderDataList.isEmpty()) { - totalKgs = BigDecimal.valueOf(0.1); - } - template.addVar("KGS_qty", data.getKgs()!=null?(totalKgs.multiply(BigDecimal.valueOf(totalPlt))).setScale(2, RoundingMode.HALF_UP):""); - template.addVar("KGS", "KGS"); - } - // 新加的 货物明细单选框 勾选显示 - if (data.getGoodsLabel()!=null && data.getGoodsLabel()) { - template.addVar("nameLabel", "Name of goods"+(notifyHeader.getBuNo().equals("03-RFID")?" RFID LABEL":" RF LABEL")); - template.addVar("originLabel", "Origin of goods Made in China"); - template.addVar("sellerLabel", "Name of seller Checkpoint Commercial (Shanghai) Co.. Ltd."); - } - template.addVar("Shipping_Mark", stringInput(data.getShippingMark())); - // 体积计算:优先使用栈板体积,栈板不存在时根据装箱明细的物料计算体积 - BigDecimal totalVolume = BigDecimal.ZERO; - if (!palletHeaderDataList.isEmpty()) { - // 计算所有栈板的体积总和 - for (EcssCoDelPalletHeaderData palletHeader : palletHeaderDataList) { - if (palletHeader.getLength() != null && palletHeader.getWidth() != null - && palletHeader.getHeight() != null && palletHeader.getPalletQty() != null) { - // 单个栈板体积 = 长 * 宽 * 高 * 数量 - BigDecimal singlePalletVolume = palletHeader.getLength() - .multiply(palletHeader.getWidth()) - .multiply(palletHeader.getHeight()) - .multiply(BigDecimal.valueOf(palletHeader.getPalletQty())); - totalVolume = totalVolume.add(singlePalletVolume); - } - } - totalVolume = totalVolume.setScale(2, RoundingMode.HALF_UP); - } else { - // 栈板不存在时,根据装箱明细的物料计算体积 - totalVolume = calculateVolumeByMaterials(notifyHeader); - } - template.addVar("Measurement", totalVolume); - if (data.getHighPalletFlag()!=null && data.getHighPalletFlag() && palletHeaderData.isPresent()) { - template.addVar("volume", palletHeaderData.map(ecssCoDelPalletHeaderData -> - ecssCoDelPalletHeaderData.getLength().setScale(2, RoundingMode.HALF_UP) - + "*" + ecssCoDelPalletHeaderData.getWidth().setScale(2, RoundingMode.HALF_UP) + "*" - + ecssCoDelPalletHeaderData.getHeight().setScale(2, RoundingMode.HALF_UP)).orElse("")); - } - template.addListVarAll(exportList); - } - - private void extractedExportGoods(EcssDeclarationHeaderData data, ExcelTemplate template) { - EcssDeclarationHeaderData ecHeader = coDelMapper.getDeclarationHeaderByDelNo(data); - // 发货通知单 - EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); - template.addVar("localShipper", notifyHeader.getCustomerName()); - template.addVar("localShipAddress", notifyHeader.getLocalShipAddress()); - template.addVar("ccusname", stringInput(notifyHeader.getOverseasShipper())); - template.addVar("cDeliverAdd", stringInput(notifyHeader.getOverseasAddress())); - template.addVar("shippingMode", stringInput(notifyHeader.getShippingMode())); - // 发货通知单明细 - List nodifyDetailList = coDelMapper.exportEcssCoDelNotifyDetail(data); - List rows = sqlSession.selectList("ecssMapper.getEcssCoDelNotifyDetailData", notifyHeader); - StringBuilder sb = new StringBuilder(); - StringBuilder sbEn = new StringBuilder(); - for (int i = 0; i < rows.size(); i++) { - if (i < rows.size() - 1) { - sb.append(rows.get( i).get("hsCodeDesc")).append("、"); - sbEn.append(rows.get( i).get("hsCodeDescEn")).append("、"); - } else { - sb.append(rows.get( i).get("hsCodeDesc")); - sbEn.append(rows.get( i).get("hsCodeDescEn")); - } - } - // 装箱明细 - List palletDetailList = coDelMapper.exportCoDelPalletDetail(notifyHeader); - int totalQty=palletDetailList.stream().mapToInt(o -> Integer.parseInt(o.get("total_qty").toString())).sum(); - Map palletMap = palletDetailList.stream().collect(Collectors.toMap( o -> o.get("part_no"), o -> o)); - template.addVar("hs_code_desc", stringInput(nodifyDetailList.get(0).get("hsCodeDescEn")!=null?sbEn.toString():"")); - String hsCodes = nodifyDetailList.stream() - .map(m -> Objects.toString(m.get("hsCode"), "")) - .filter(s -> !s.isEmpty()) // 去掉空值 - .distinct() // 去重 - .collect(Collectors.joining("、")); // 用换行拼接 - - template.addVar("hs_code", stringInput(hsCodes)); - Map poNoMap = new HashMap<>(); - BigDecimal ttlAmount = BigDecimal.ZERO; - for (Map map : nodifyDetailList) { - poNoMap.put(map.get("customerPO"), map.get("customerPO")); - ttlAmount = ttlAmount.add((BigDecimal) map.get("ttl_amount")); - } - StringBuilder ponos = new StringBuilder(); - poNoMap.forEach((key, value) -> ponos.append(key).append("\n")); - template.addVar("poNo", ponos); - template.addVar("shipping_port", stringInput(notifyHeader.getCnative())); - template.addVar("cmc_invoice", stringInput(notifyHeader.getCmcInvoice())); - // 导出时默认,可编辑的栏目 - template.addVar("sales_method", stringInput(data.getSalesMethod()));//贸易方式 - String currency = stringInput(data.getCurrency()); - if (currency.toUpperCase().startsWith("RMB")) { - currency = "CNY"; - } - template.addVar("currency", currency);//币制 - template.addVar("made_area", stringInput(data.getMadeArea()));//货物产地 - template.addVar("send_port", stringInput(data.getSendPort()));//发货港 - template.addVar("shipper", stringInput(data.getShipper()));//发货人 - template.addVar("voyage", notifyHeader.getBuNo().equals("01-Label") || notifyHeader.getBuNo().equals("03-RFID") - ?stringInput(notifyHeader.getCmcInvoice()) :stringInput(data.getVoyage()));//航次 - template.addVar("delivery_goods_date", stringInput(data.getDeliveryGoodsDate()));//提货日期 - template.addVar("shipping_date", stringInput(data.getShippingDate()));//船期 - // 装箱数据 - List palletHeaderDataList = coDelMapper.searchEcssCoDelPalletHeaderData(notifyHeader); - // 最高的栈板 - Optional palletHeaderData = palletHeaderDataList.isEmpty()? Optional.empty() : - palletHeaderDataList.stream().max(Comparator.comparing(EcssCoDelPalletHeaderData::getHeight)); - // 需要计算的 - // 总托数 - Integer totalPlt = palletHeaderDataList.stream() - .map(EcssCoDelPalletHeaderData::getPalletQty) - .filter(Objects::nonNull) // 防止空指针 - .reduce(0, Integer::sum); - template.addVar("quantity", ecHeader!=null?(totalPlt==0?ecHeader.getBoxQty()+"CTN("+ecHeader.getBoxQty()+"CTN)": - totalPlt+"PLT("+ecHeader.getBoxQty()+"CTN)"):""); - - template.addVar("price", ttlAmount.setScale(2, RoundingMode.HALF_UP)); - template.addVar("total_qty", totalQty); - // 托盘重量=根据每个pallet的重量*数量累加 - BigDecimal palletWeight = BigDecimal.ZERO; - for (EcssCoDelPalletHeaderData palletHeader : palletHeaderDataList) { - if (palletHeader.getPallet() != null && !palletHeader.getPallet().isEmpty()) { - // 根据pallet编号查询EcssPallet信息 - List palletDataList = coDelMapper.getPallet(notifyHeader.getSite(), notifyHeader.getBuNo(), palletHeader.getPallet()); - if (!palletDataList.isEmpty()) { - EcssPalletData palletData = palletDataList.get(0); - if (palletData.getPalletWeight() != null && palletHeader.getPalletQty() != null) { - // 栈板重量 = 单个栈板重量 * 数量 - BigDecimal singlePalletWeight = palletData.getPalletWeight().multiply(BigDecimal.valueOf(palletHeader.getPalletQty())); - palletWeight = palletWeight.add(singlePalletWeight); - } - } - } - } - template.addVar("net_weight", (ecHeader!=null&&ecHeader.getNetWeight() != null ? ecHeader.getNetWeight() : BigDecimal.ZERO). - setScale(2, RoundingMode.HALF_UP)); - template.addVar("gross_weight", (ecHeader!=null&&ecHeader.getGrossWeight() != null ? ecHeader.getGrossWeight() : BigDecimal.ZERO). - add(palletWeight).setScale(2, RoundingMode.HALF_UP)); - - // 体积计算:优先使用栈板体积,栈板不存在时根据装箱明细的物料计算体积 - BigDecimal totalVolume = BigDecimal.ZERO; - if (!palletHeaderDataList.isEmpty()) { - // 计算所有栈板的体积总和 - for (EcssCoDelPalletHeaderData palletHeader : palletHeaderDataList) { - if (palletHeader.getLength() != null && palletHeader.getWidth() != null - && palletHeader.getHeight() != null && palletHeader.getPalletQty() != null) { - // 单个栈板体积 = 长 * 宽 * 高 * 数量 - BigDecimal singlePalletVolume = palletHeader.getLength() - .multiply(palletHeader.getWidth()) - .multiply(palletHeader.getHeight()) - .multiply(BigDecimal.valueOf(palletHeader.getPalletQty())); - totalVolume = totalVolume.add(singlePalletVolume); - } + String site = coDelMapper.getSiteByBu(inData.getBuNo()); + if (inData.getAddFlag() == 0) { + //0是新增 + List palletDataList = coDelMapper.getPallet(site,inData.getBuNo(),inData.getPalletNo()); + if(!palletDataList.isEmpty()){ + throw new RuntimeException("编码["+inData.getPalletNo()+"]已存在!"); } - totalVolume = totalVolume.setScale(2, RoundingMode.HALF_UP); + inData.setCreateBy(currentUser.getUsername()); + inData.setSite(site); + coDelMapper.insertPalletData(inData); } else { - // 栈板不存在时,根据装箱明细的物料计算体积 - totalVolume = calculateVolumeByMaterials(notifyHeader); - } - template.addVar("volume", totalVolume); - template.addVar("highest", palletHeaderData.isPresent() ?palletHeaderData.map(ecssCoDelPalletHeaderData -> - ecssCoDelPalletHeaderData.getLength().setScale(2, RoundingMode.HALF_UP) - + "*" + ecssCoDelPalletHeaderData.getWidth().setScale(2, RoundingMode.HALF_UP) + "*" - + ecssCoDelPalletHeaderData.getHeight().setScale(2, RoundingMode.HALF_UP)).orElse(""):""); - template.addVar("pickup_date", notifyHeader.getNotifyDate() != null ? - DateUtils.format(notifyHeader.getNotifyDate(), "yyyy-MM-dd") : ""); - List ecssContacts = coDelMapper.getEcssContacts(notifyHeader.getBuNo()); - if (!ecssContacts.isEmpty()) { - template.addVar("name1", ecssContacts.get(0).get("name")); - template.addVar("phone1", ecssContacts.get(0).get("phone")); - template.addVar("name2", ecssContacts.size()>1?ecssContacts.get(1).get("name"):""); - template.addVar("phone2", ecssContacts.size()>1?ecssContacts.get(1).get("phone"):""); - template.addVar("contacts", ecssContacts.get(0).get("name")+(ecssContacts.size()>1?"/"+ecssContacts.get(1).get("name"):"")); + inData.setUpdateBy(currentUser.getUsername()); + inData.setSite(site); + coDelMapper.updatePalletData(inData); } } - private void extractedContract(EcssDeclarationHeaderData data, ExcelTemplate template) { - // 发货通知单 - EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); - template.addVar("cmcInvoice", notifyHeader.getCmcInvoice()); - template.addVar("readyDate", DateUtils.format(notifyHeader.getReadyDate(), "yyyy/MM/dd")); - List notifyDetailGroup = coDelMapper.getNotifyDetailGroup(data); - BigDecimal totalQty = BigDecimal.ZERO; - BigDecimal totalAmount = BigDecimal.ZERO; - for (int i = 0; i < notifyDetailGroup.size(); i++) { - Map notifyDetail = notifyDetailGroup.get(i); - totalQty = totalQty.add((BigDecimal) notifyDetail.get("total_qty")); - totalAmount = totalAmount.add((BigDecimal) notifyDetail.get("ttl_amount")); - } - template.addVar("totalQty", totalQty); - template.addVar("totalAmount", totalAmount); - template.addListVarAll(notifyDetailGroup); + @Override + public void deletePallet(EcssPalletData data) { + coDelMapper.deletePallet(data); } - /** - * 根据装箱明细的物料计算体积 - * 每种物料(每个pn是一种物料)维护的箱子的长*宽*高*箱数 - * - * @return 总体积 - */ - private BigDecimal calculateVolumeByMaterials(EcssCoDelNotifyHeaderData notifyHeader) { - List palletDetailList = coDelMapper.getCoDelPalletDetailGroupByPn(notifyHeader); - - if (palletDetailList == null || palletDetailList.isEmpty()) { - return BigDecimal.ZERO; - } - - try { - // 获取所有物料编号 - List pns = palletDetailList.stream() - .map(detail -> (String) detail.get("pn")) - .filter(Objects::nonNull) - .distinct() - .collect(Collectors.toList()); - - if (pns.isEmpty()) { - return BigDecimal.ZERO; - } + @Override + public PageUtils searchPackageData(EcssPackageData data) { + IPage resultList = coDelMapper.searchPackageData(new Page(data.getPage(), data.getLimit()), data); + return new PageUtils(resultList); + } - // 根据物料编号获取packageNo - List packageNoList = coDelMapper.getPackageNoByPn(notifyHeader.getSite(), pns); - Map partNoToPackageNoMap = packageNoList.stream() - .collect(Collectors.toMap( - map -> (String) map.get("pn"), - map -> (String) map.get("packageNo"), - (existing, replacement) -> existing // 处理重复key的情况 - )); - - // 获取所有packageNo对应的包装信息 - List packageNos = packageNoList.stream() - .map(map -> (String) map.get("packageNo")) - .filter(Objects::nonNull) - .distinct() - .collect(Collectors.toList()); - - if (packageNos.isEmpty()) { - log.warn("未找到物料对应的包装信息,物料编号: {}", pns); - return BigDecimal.ZERO; + @Override + public void savePackageData(EcssPackageData inData) { + SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); + String site = coDelMapper.getSiteByBu(inData.getBuNo()); + inData.setVolume(inData.getLength().multiply(inData.getWidth()).multiply(inData.getHeight())); + if (inData.getAddFlag() == 0) { + //0是新增 + List packageDataList = coDelMapper.getPackage(site,inData.getBuNo(),inData.getPackageNo()); + if(!packageDataList.isEmpty()){ + throw new RuntimeException("编码["+inData.getPackageNo()+"]已存在!"); } + inData.setCreateBy(currentUser.getUsername()); + inData.setSite(site); + coDelMapper.insertPackageData(inData); + } else { + inData.setUpdateBy(currentUser.getUsername()); + inData.setSite(site); + coDelMapper.updatePackageData(inData); + } + } - // 获取包装信息(长宽高) - List packageDataList = coDelMapper.getPackageList(notifyHeader.getSite(), notifyHeader.getBuNo(), packageNos); - Map packageNoToDataMap = packageDataList.stream() - .collect(Collectors.toMap( - EcssPackageData::getPackageNo, - packageData -> packageData, - (existing, replacement) -> existing - )); - - // 计算总体积 - BigDecimal totalVolume = BigDecimal.ZERO; - for (Map detail : palletDetailList) { - String pn = (String) detail.get("pn"); - Object boxQtyObj = detail.get("box_qty"); - - if (pn == null || boxQtyObj == null) { - continue; - } - - // 获取箱数 - BigDecimal boxQty; - if (boxQtyObj instanceof BigDecimal) { - boxQty = (BigDecimal) boxQtyObj; - } else { - boxQty = new BigDecimal(boxQtyObj.toString()); - } - - // 获取包装信息 - String packageNo = partNoToPackageNoMap.get(pn); - if (packageNo == null) { - log.warn("物料 {} 未维护packageNo", pn); - continue; - } - - EcssPackageData packageData = packageNoToDataMap.get(packageNo); - if (packageData == null) { - log.warn("包装编号 {} 未找到对应的包装信息", packageNo); - continue; - } - - // 检查长宽高是否都有值 - if (packageData.getLength() == null || packageData.getWidth() == null || packageData.getHeight() == null) { - log.warn("包装编号 {} 的长宽高信息不完整", packageNo); - continue; - } - - // 计算该物料的体积:长*宽*高*箱数 - BigDecimal materialVolume = packageData.getLength() - .multiply(packageData.getWidth()) - .multiply(packageData.getHeight()) - .multiply(boxQty); - - totalVolume = totalVolume.add(materialVolume); + @Override + public void deleteEcssPackage(EcssPackageData data) { + coDelMapper.deleteEcssPackage(data); + } - log.debug("物料 {} 包装 {} 箱数 {} 单箱体积 {} 总体积 {}", - pn, packageNo, boxQty, - packageData.getLength().multiply(packageData.getWidth()).multiply(packageData.getHeight()), - materialVolume); - } + @Override + public List searchHsCodeList(EcssParamData data) { + return coDelMapper.searchHsCodeList(data); + } - return totalVolume.setScale(2, RoundingMode.HALF_UP); + @Override + public List searchPalletList(EcssParamData data) { + return coDelMapper.searchPalletList(data); + } - } catch (Exception e) { - log.error("计算物料体积时发生异常", e); - return BigDecimal.ZERO; - } + @Override + public List searchPackageList(EcssParamData data) { + return coDelMapper.searchPackageList(data); } - /** - * 计算FSC纸重量 - */ - private BigDecimal fscWeight(Map> partNoAndItemNoMap, String partNo, - int totalQty, String lossratio) { - PartSubPropertiesValueData FSC001 = partNoAndItemNoMap.get("FSC001")==null?null:partNoAndItemNoMap.get("FSC001").get(0);//P距 - PartSubPropertiesValueData FSC002 = partNoAndItemNoMap.get("FSC002")==null?null:partNoAndItemNoMap.get("FSC002").get(0);//排数 - PartSubPropertiesValueData FSC003 = partNoAndItemNoMap.get("FSC003")==null?null:partNoAndItemNoMap.get("FSC003").get(0);//1米标签面积 - PartSubPropertiesValueData FSC004 = partNoAndItemNoMap.get("FSC004")==null?null:partNoAndItemNoMap.get("FSC004").get(0);//底纸宽度 - PartSubPropertiesValueData FSC005 = partNoAndItemNoMap.get("FSC005")==null?null:partNoAndItemNoMap.get("FSC005").get(0);//BOM分配张数 - PartSubPropertiesValueData FSC0012 = partNoAndItemNoMap.get("FSC001-02")==null?null:partNoAndItemNoMap.get("FSC001-02").get(0);//P距 - PartSubPropertiesValueData FSC0022 = partNoAndItemNoMap.get("FSC002-02")==null?null:partNoAndItemNoMap.get("FSC002-02").get(0);//排数 - PartSubPropertiesValueData FSC0032 = partNoAndItemNoMap.get("FSC003-02")==null?null:partNoAndItemNoMap.get("FSC003-02").get(0);//1米标签面积 - PartSubPropertiesValueData FSC0042 = partNoAndItemNoMap.get("FSC004-02")==null?null:partNoAndItemNoMap.get("FSC004-02").get(0);//底纸宽度 - PartSubPropertiesValueData FSC0052 = partNoAndItemNoMap.get("FSC005-02")==null?null:partNoAndItemNoMap.get("FSC005-02").get(0);//BOM分配张数 - BigDecimal fscWeight = BigDecimal.valueOf(0); - BigDecimal fscWeight2 = BigDecimal.valueOf(0); - // 千张理论用量(M)=((BOM分配张数/排数)*P距)/1000 - if (FSC001!=null && FSC002!=null && FSC003!=null && FSC004!=null && FSC005!=null) { - BigDecimal bom = FSC005.getNumValue()!=null?BigDecimal.valueOf(FSC005.getNumValue()):BigDecimal.ZERO; - BigDecimal row = FSC002.getNumValue()!=null?BigDecimal.valueOf(FSC002.getNumValue()):BigDecimal.ZERO; - BigDecimal area = FSC003.getNumValue()!=null?BigDecimal.valueOf(FSC003.getNumValue()):BigDecimal.ZERO; - BigDecimal width = FSC004.getNumValue()!=null?BigDecimal.valueOf(FSC004.getNumValue()):BigDecimal.ZERO; - BigDecimal pju = FSC001.getNumValue()!=null?BigDecimal.valueOf(FSC001.getNumValue()):BigDecimal.ZERO; - if (bom.compareTo(BigDecimal.ZERO)!=0 && row.compareTo(BigDecimal.ZERO)!=0 && area.compareTo(BigDecimal.ZERO)!=0 - && width.compareTo(BigDecimal.ZERO)!=0 && pju.compareTo(BigDecimal.ZERO)!=0) { - fscWeight = getFSC(totalQty, lossratio, bom, row, area, width, pju); - } - } - if (FSC0012!=null && FSC0022!=null && FSC0032!=null && FSC0042!=null && FSC0052!=null) { - BigDecimal bom = FSC0052.getNumValue()!=null?BigDecimal.valueOf(FSC0052.getNumValue()):BigDecimal.ZERO; - BigDecimal row = FSC0022.getNumValue()!=null?BigDecimal.valueOf(FSC0022.getNumValue()):BigDecimal.ZERO; - BigDecimal area = FSC0032.getNumValue()!=null?BigDecimal.valueOf(FSC0032.getNumValue()):BigDecimal.ZERO; - BigDecimal width = FSC0042.getNumValue()!=null?BigDecimal.valueOf(FSC0042.getNumValue()):BigDecimal.ZERO; - BigDecimal pju = FSC0012.getNumValue()!=null?BigDecimal.valueOf(FSC0012.getNumValue()):BigDecimal.ZERO; - if (bom.compareTo(BigDecimal.ZERO)!=0 && row.compareTo(BigDecimal.ZERO)!=0 && area.compareTo(BigDecimal.ZERO)!=0 - && width.compareTo(BigDecimal.ZERO)!=0 && pju.compareTo(BigDecimal.ZERO)!=0) { - fscWeight2 = getFSC(totalQty, lossratio, bom, row, area, width, pju); - } - } - return fscWeight.add(fscWeight2).setScale(2, RoundingMode.DOWN); + @Override + public List getNotifyPartDetail(EcssDeclarationHeaderData inData) { + return coDelMapper.getNotifyPartDetail(inData); } - private BigDecimal getFSC(int totalQty, String lossratio, - BigDecimal bom, BigDecimal row, BigDecimal area, BigDecimal width, BigDecimal pju) { - BigDecimal fscWeight; - BigDecimal qianzhang = ((bom.divide(row,6, RoundingMode.HALF_UP)).multiply(pju)). - divide(BigDecimal.valueOf(1000),6, RoundingMode.HALF_UP); - fscWeight = qianzhang.multiply(BigDecimal.valueOf(totalQty). - divide(BigDecimal.valueOf(1000),6, RoundingMode.HALF_UP)). - multiply(width.divide(BigDecimal.valueOf(1000),6, RoundingMode.HALF_UP)). - multiply(BigDecimal.valueOf(0.16)). - multiply(area.divide(width.divide(BigDecimal.valueOf(1000),6, RoundingMode.HALF_UP),6, RoundingMode.HALF_UP)). - multiply(BigDecimal.valueOf(Double.parseDouble(lossratio))); - return fscWeight; + private String stringInput(String inString) { + if (StringUtils.isEmpty(inString)) { + return ""; + } else { + return inString; + } } /** @@ -4155,89 +1403,6 @@ public class CoDelServiceImpl implements CoDelService { coDelMapper.deleteWalMartOrder(data); } - @Override - @Transactional - public String saveWalMartOrderByExcel(MultipartFile file, EcssWalMartOrder inData) { - SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); - String site = coDelMapper.getSiteByBu(inData.getBuNo()); - List excelList = new ArrayList<>(); - try { - // 转流 - InputStream is = file.getInputStream(); - // 读取工作簿 - XSSFWorkbook workbook = new XSSFWorkbook(is); - // 读取工作表 - XSSFSheet sheet = workbook.getSheetAt(0); - // 剔除空行 - deleteEmptyRow(sheet); - // 获取行数 - int rows = sheet.getPhysicalNumberOfRows(); - // 遍历每一行(从第二行开始) - StringBuilder sb = new StringBuilder("第"); - for (int j = 1; j < rows; j++) { - // 创建对象 - EcssWalMartOrder task = new EcssWalMartOrder(); - // 获得该行 - XSSFRow row = sheet.getRow(j); - if (row.getCell(0) == null ) { - throw new RuntimeException("第" + j + "行的SKU不能为空!"); - } - // 为对象赋值 - task.setSite(site); // site - task.setBuNo(inData.getBuNo()); // bu - task.setSku(getStringCellValue(row, 0)); - task.setSo(getStringCellValue(row, 1)); - task.setQty(getNumericCellValueOrDefault(row, 2, "Qty (pcs)")); - List orderDataList = sqlSession.selectList("ecssMapper" + "." + "searchWalMartOrderList", task); - if (!orderDataList.isEmpty()) { - if (sb.toString().length()>1) { - sb.append(",").append(j); - } else { - sb.append(j); - } - //throw new RuntimeException("第" + j + "行的SKU和数量已存在!"); - continue; - } - task.setRolls(getNumericCellValueOrDefault(row, 3)); - task.setGrossWeight(getNumericCellValueOrDefault(row, 4)); - task.setVerificationSheet(getStringCellValue(row, 5)); - task.setCreateBy(currentUser.getUserDisplay()); - excelList.add(task); - } - if (!excelList.isEmpty()) { - coDelMapper.batchSaveWalMartOrder(excelList); - } - if (sb.toString().equals("第")) { - sb= new StringBuilder(); - } else { - sb.append("行的SKU和数量已存在!"); - } - return sb.toString(); - } catch (Exception e) { - throw new RuntimeException("导入失败:" + e.getMessage()); - } - } - - private static void deleteEmptyRow(XSSFSheet sheet) { - // 从第三行开始遍历,检查每一行是否为空 - for (int i = 2; i <= sheet.getLastRowNum(); i++) { - XSSFRow row = sheet.getRow(i); - boolean isEmptyRow = true; - if (row != null) { - for (Cell cell : row) { - if (cell.getCellType() != CellType.BLANK && cell.getCellType() != CellType.ERROR && - !StringUtils.isBlank(String.valueOf(cell))) { - isEmptyRow = false; - break; - } - } - if (isEmptyRow) { - sheet.removeRow(row); // 删除空行 - } - } - } - } - @Override public List getPropertiesListByDeclaration(EcssDeclarationHeaderData data) { // 获取partNo列表 @@ -5170,79 +2335,6 @@ public class CoDelServiceImpl implements CoDelService { return coDelMapper.getCustomerTemplateList(params); } - /** - * 检查物料存在性 - * @param excelList 导入的数据列表 - * @param site 工厂 - * @param username 用户名 - * @param buNo 业务单元 - * @return 返回包含不存在物料的发票号及其对应的不存在物料列表 - */ - private Map> checkMaterialsExistence(List excelList, String site, String username, String buNo) { - Map> invalidMaterialsByInvoice = new HashMap<>(); - - // 按发票号分组 - Map> groupedByInvoice = excelList.stream() - .collect(Collectors.groupingBy(EcssCoDelNotifyData::getCmcInvoice)); - - // 检查每个发票的物料 - for (Map.Entry> entry : groupedByInvoice.entrySet()) { - String invoice = entry.getKey(); - List invoiceData = entry.getValue(); - List invalidMaterials = new ArrayList<>(); - - // 检查该发票下的每个物料 - for (EcssCoDelNotifyData data : invoiceData) { - String pn = data.getPn(); - if (pn != null && !pn.trim().isEmpty()) { - // 使用 getPartNo 方法检查物料是否存在 - List parts = coDelMapper.getPartNo(site, pn, username, buNo); - if (parts.isEmpty()) { - // 物料不存在,添加到无效物料列表 - if (!invalidMaterials.contains(pn)) { - invalidMaterials.add(pn); - } - } - - // 测试用:如果物料号以 "TEST" 开头,强制认为不存在(用于测试功能) - if (pn.startsWith("TEST") && !invalidMaterials.contains(pn)) { - invalidMaterials.add(pn); - log.warn("测试:强制认为物料不存在: {} (发票号: {})", pn, invoice); - } - } - } - - // 如果该发票有不存在的物料,记录下来 - if (!invalidMaterials.isEmpty()) { - invalidMaterialsByInvoice.put(invoice, invalidMaterials); - } - } - - return invalidMaterialsByInvoice; - } - - /** - * 解析删除的发票号列表 - * @param deletedInvoices JSON格式的删除发票号字符串 - * @return 删除的发票号列表 - */ - private List parseDeletedInvoices(String deletedInvoices) { - if (deletedInvoices == null || deletedInvoices.trim().isEmpty()) { - return new ArrayList<>(); - } - - try { - ObjectMapper objectMapper = new ObjectMapper(); - // 解析JSON数组字符串为List - List invoiceList = objectMapper.readValue(deletedInvoices, - objectMapper.getTypeFactory().constructCollectionType(List.class, String.class)); - return invoiceList != null ? invoiceList : new ArrayList<>(); - } catch (Exception e) { - log.error("解析删除发票号JSON失败: {}, 原始数据: {}", e.getMessage(), deletedInvoices); - throw new RuntimeException("删除发票号数据格式错误"); - } - } - /** * 改单导入后处理通知单状态 * @@ -5651,53 +2743,6 @@ public class CoDelServiceImpl implements CoDelService { return value.stripTrailingZeros().toPlainString(); } - /** - * 发送箱子变更通知邮件 - * - * @param notifyHeader 发货通知单头数据 - * @param oldBoxList 操作前的箱子列表 - * @param operation 操作类型(修改/删除) - * @param boxData 操作的箱子数据 - * @author AI Assistant - * @date 2024-10-09 - */ - private void sendBoxChangeNotificationEmail(EcssCoDelNotifyHeaderData notifyHeader, - List oldBoxList, - String operation, - Map boxData) { - try { - log.info("开始发送箱子{}通知邮件,发货通知单号:{}", operation, notifyHeader.getDelNo()); - - // 获取操作后的装箱数据 - List newBoxList = coDelMapper.selectBoxList(notifyHeader); - - // 生成对比邮件内容 - String emailContent = generateBoxChangeEmailContent(notifyHeader, oldBoxList, newBoxList, operation, boxData); - - // 获取发货通知单创建人邮箱 - SysUserEntity creator = coDelMapper.queryByUserName(notifyHeader.getCreateBy()); - if (creator == null || StringUtils.isBlank(creator.getEmail())) { - log.warn("发货通知单创建人{}不存在或没有配置邮箱地址", notifyHeader.getCreateBy()); - return; - } - String creatorEmail = creator.getEmail(); - - // 发送邮件 - String subject = String.format("发货通知单%s【发票:%s】箱子%s通知", - notifyHeader.getDelNo(), notifyHeader.getCmcInvoice(), operation); - String[] mailAddress = {creatorEmail}; - - sendMailUtil(subject, emailContent, mailAddress, notifyHeader); - - log.info("箱子{}通知邮件发送成功,收件人:{}", operation, creatorEmail); - - } catch (Exception e) { - log.error("发送箱子{}通知邮件失败,发货通知单号:{}, 错误信息:{}", - operation, notifyHeader.getDelNo(), e.getMessage(), e); - // 邮件发送失败不影响主流程,只记录日志 - } - } - /** * 发送箱明细变更通知邮件 * @@ -6520,430 +3565,6 @@ public class CoDelServiceImpl implements CoDelService { return emailContent.toString(); } - /** - * @Description 导入物料包装属性(每卷数量、每箱卷数、每卷重量、箱重量、箱类型、FSC标签属性) - * @param file Excel文件 - * @param buNo 业务单元编码 - * @param username 用户名 - * @param previewOnly 是否仅预览 - * @return 预览数据或导入结果 - */ - @Override - @Transactional - public Map importPartPackageProperties(MultipartFile file, String buNo, String username, boolean previewOnly) { - Map result = new HashMap<>(); - List> previewList = new ArrayList<>(); - List> detailsList = new ArrayList<>(); - int successCount = 0; - int failCount = 0; - - try { - // 获取用户的site信息 - SysUserEntity sysUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); - String site = sysUser.getSite(); - - // 解析Excel文件 - XSSFWorkbook workbook = new XSSFWorkbook(file.getInputStream()); - XSSFSheet sheet = workbook.getSheetAt(0); - DataFormatter dataFormatter = new DataFormatter(); - - // 从第二行开始(跳过表头) - int lastRowNum = sheet.getLastRowNum(); - for (int rowNum = 1; rowNum <= lastRowNum; rowNum++) { - XSSFRow row = sheet.getRow(rowNum); - if (row == null) continue; - - Map rowData = new HashMap<>(); - rowData.put("rowNum", rowNum + 1); - - try { - // 读取Excel字段:物料编码 每卷重量 每卷数量 每箱卷数 Box No 箱重量(kg) 长(cm) 宽(cm) 高(cm) - String sku = getCellValueAsString(row.getCell(0), dataFormatter); - String rollWeightStr = getCellValueAsString(row.getCell(1), dataFormatter); - String rollQtyStr = getCellValueAsString(row.getCell(2), dataFormatter); - String boxRollsStr = getCellValueAsString(row.getCell(3), dataFormatter); - String boxNo = getCellValueAsString(row.getCell(4), dataFormatter); - String boxWeightStr = getCellValueAsString(row.getCell(5), dataFormatter); - String lengthStr = getCellValueAsString(row.getCell(6), dataFormatter); - String widthStr = getCellValueAsString(row.getCell(7), dataFormatter); - String heightStr = getCellValueAsString(row.getCell(8), dataFormatter); - - // 读取FSC标签属性字段:P距 排数 1米标签面积 底纸宽度(MM) BOM分配张数 P距-2 排数-2 1米标签面积-2 底纸宽度(MM)-2 BOM分配张数-2 - String fsc001Str = getCellValueAsString(row.getCell(9), dataFormatter); // P距 - String fsc002Str = getCellValueAsString(row.getCell(10), dataFormatter); // 排数 - String fsc003Str = getCellValueAsString(row.getCell(11), dataFormatter); // 1米标签面积 - String fsc004Str = getCellValueAsString(row.getCell(12), dataFormatter); // 底纸宽度(MM) - String fsc005Str = getCellValueAsString(row.getCell(13), dataFormatter); // BOM分配张数 - String fsc00102Str = getCellValueAsString(row.getCell(14), dataFormatter); // P距-2 - String fsc00202Str = getCellValueAsString(row.getCell(15), dataFormatter); // 排数-2 - String fsc00302Str = getCellValueAsString(row.getCell(16), dataFormatter); // 1米标签面积-2 - String fsc00402Str = getCellValueAsString(row.getCell(17), dataFormatter); // 底纸宽度(MM)-2 - String fsc00502Str = getCellValueAsString(row.getCell(18), dataFormatter); // BOM分配张数-2 - // 跳过空行 - if (StringUtils.isBlank(sku)) { - continue; - } - rowData.put("buNo", buNo); - rowData.put("sku", sku); - rowData.put("rollQty", rollQtyStr); - rowData.put("boxRolls", boxRollsStr); - rowData.put("rollWeight", rollWeightStr); - rowData.put("boxWeight", boxWeightStr); - rowData.put("boxNo", boxNo); - rowData.put("length", lengthStr); - rowData.put("width", widthStr); - rowData.put("height", heightStr); - - // FSC标签属性字段 - rowData.put("fsc001", fsc001Str); - rowData.put("fsc002", fsc002Str); - rowData.put("fsc003", fsc003Str); - rowData.put("fsc004", fsc004Str); - rowData.put("fsc005", fsc005Str); - rowData.put("fsc001-02", fsc00102Str); - rowData.put("fsc002-02", fsc00202Str); - rowData.put("fsc003-02", fsc00302Str); - rowData.put("fsc004-02", fsc00402Str); - rowData.put("fsc005-02", fsc00502Str); - - // 验证BU - if (StringUtils.isBlank(buNo)) { - rowData.put("hasError", true); - rowData.put("errorMsg", "BU不能为空"); - previewList.add(rowData); - continue; - } - - // 根据SKU查询物料信息(一个SKU可能对应多个part) - Map partParams = new HashMap<>(); - partParams.put("site", site); - partParams.put("buNo", buNo); - partParams.put("sku", sku); - List partList = coDelMapper.getPartListBySku(partParams); - - if (partList == null || partList.isEmpty()) { - rowData.put("hasError", true); - rowData.put("errorMsg", "物料编码不存在"); - previewList.add(rowData); - continue; - } - - // 收集所有partNo用于显示 - String partNos = partList.stream().map(PartData::getPartNo).collect(Collectors.joining(",")); - rowData.put("partNo", partNos); - rowData.put("partCount", partList.size()); - rowData.put("hasError", false); - - // 预览模式只返回数据,不执行导入 - if (previewOnly) { - previewList.add(rowData); - continue; - } - - // 导入模式:执行数据导入(为所有匹配的part设置属性) - StringBuilder successMsg = new StringBuilder(); - StringBuilder failMsg = new StringBuilder(); - boolean hasSuccess = false; - boolean hasFail = false; - int updatedPartCount = 0; - - // 处理属性值:每卷数量(ROLLQTY)、每箱卷数(BOXROLLS)、每卷重量(ROLLWEIGHT)、箱重量(BOXWEIGHT)、FSC标签属性 - String[] itemNos = {"ROLLQTY", "BOXROLLS", "ROLLWEIGHT", "BOXWEIGHT", - "FSC001", "FSC002", "FSC003", "FSC004", "FSC005", - "FSC001-02", "FSC002-02", "FSC003-02", "FSC004-02", "FSC005-02"}; - String[] itemValues = {rollQtyStr, boxRollsStr, rollWeightStr, boxWeightStr, - fsc001Str, fsc002Str, fsc003Str, fsc004Str, fsc005Str, - fsc00102Str, fsc00202Str, fsc00302Str, fsc00402Str, fsc00502Str}; - String[] itemNames = {"每卷数量", "每箱卷数", "每卷重量", "箱重量", - "P距", "排数", "1米标签面积", "底纸宽度(MM)", "BOM分配张数", - "P距-2", "排数-2", "1米标签面积-2", "底纸宽度(MM)-2", "BOM分配张数-2"}; - - // 为每个匹配的part设置属性 - for (PartData partData : partList) { - String partNo = partData.getPartNo(); - - for (int i = 0; i < itemNos.length; i++) { - String itemNo = itemNos[i]; - String itemValue = itemValues[i]; - String itemName = itemNames[i]; - - if (StringUtils.isNotBlank(itemValue)) { - try { - Double numValue = Double.parseDouble(itemValue); - boolean updated = updateOrInsertPartProperty(site, buNo, partNo, itemNo, numValue, username); - if (updated) { - hasSuccess = true; - } - } catch (NumberFormatException e) { - if (!failMsg.toString().contains(itemName + "格式错误")) { - failMsg.append(itemName).append("格式错误; "); - } - hasFail = true; - } - } - } - updatedPartCount++; - } - - if (hasSuccess) { - successMsg.append("属性更新成功(").append(updatedPartCount).append("个物料); "); - } - - // 处理箱类型:Box No(对应packageType)、长、宽、高 - // 箱类型只需要处理一次,然后为所有part更新packageNo - if (StringUtils.isNotBlank(boxNo)) { - try { - BigDecimal length = StringUtils.isNotBlank(lengthStr) ? new BigDecimal(lengthStr).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP) : null; - BigDecimal width = StringUtils.isNotBlank(widthStr) ? new BigDecimal(widthStr).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP) : null; - BigDecimal height = StringUtils.isNotBlank(heightStr) ? new BigDecimal(heightStr).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP) : null; - - // 检查箱类型是否存在(根据packageType),不存在则插入,返回packageNo - String packageNo = checkAndInsertPackageByType(site, buNo, boxNo, length, width, height, username); - - // 为所有匹配的part更新packageNo - for (PartData partData : partList) { - updatePartPackageNo(site, partData.getPartNo(), packageNo); - } - - successMsg.append("箱类型更新成功(").append(partList.size()).append("个物料); "); - hasSuccess = true; - } catch (Exception e) { - failMsg.append("箱类型处理失败: ").append(e.getMessage()).append("; "); - hasFail = true; - } - } - - // 记录结果 - Map detail = new HashMap<>(); - detail.put("sku", sku); - String partInfo = partList.size() > 1 ? "(共" + partList.size() + "个物料)" : ""; - if (hasSuccess && !hasFail) { - detail.put("success", true); - detail.put("message", "物料[" + sku + "]" + partInfo + " " + successMsg.toString()); - successCount++; - } else if (hasFail && !hasSuccess) { - detail.put("success", false); - detail.put("message", "物料[" + sku + "]" + partInfo + " " + failMsg.toString()); - failCount++; - } else if (hasSuccess && hasFail) { - detail.put("success", true); - detail.put("message", "物料[" + sku + "]" + partInfo + " 部分成功: " + successMsg.toString() + " 失败: " + failMsg.toString()); - successCount++; - } else { - detail.put("success", true); - detail.put("message", "物料[" + sku + "]" + partInfo + " 无需更新"); - successCount++; - } - detailsList.add(detail); - - } catch (Exception e) { - log.error("处理行{}时发生错误: {}", rowNum + 1, e.getMessage(), e); - rowData.put("hasError", true); - rowData.put("errorMsg", "处理失败: " + e.getMessage()); - if (previewOnly) { - previewList.add(rowData); - } else { - Map detail = new HashMap<>(); - detail.put("success", false); - detail.put("message", "行" + (rowNum + 1) + " 处理失败: " + e.getMessage()); - detailsList.add(detail); - failCount++; - } - } - } - - workbook.close(); - - } catch (Exception e) { - log.error("导入物料包装属性失败: {}", e.getMessage(), e); - throw new RuntimeException("导入失败: " + e.getMessage()); - } - - if (previewOnly) { - result.put("data", previewList); - } else { - result.put("successCount", successCount); - result.put("failCount", failCount); - result.put("details", detailsList); - } - - return result; - } - - /** - * 更新或插入物料属性值 - * @param site 站点 - * @param buNo 业务单元 - * @param partNo 物料编号 - * @param propertiesItemNo 属性项编号 (ROLLQTY/BOXROLLS/ROLLWEIGHT/BOXWEIGHT) - * @param numValue 数值 - * @param username 用户名 - * @return 是否成功 - */ - private boolean updateOrInsertPartProperty(String site, String buNo, String partNo, String propertiesItemNo, Double numValue, String username) { - String codeNo = "BG001"; - String recordType = "ECSSPART"; - - // 检查属性是否已存在 - Map checkParams = new HashMap<>(); - checkParams.put("site", site); - checkParams.put("buNo", buNo); - checkParams.put("partNo", partNo); - checkParams.put("codeNo", codeNo); - checkParams.put("recordType", recordType); - checkParams.put("propertiesItemNo", propertiesItemNo); - - Map existingProperty = coDelMapper.checkPartPropertyExists(checkParams); - - if (existingProperty != null) { - // 已存在,执行更新 - Map updateParams = new HashMap<>(); - updateParams.put("site", site); - updateParams.put("buNo", buNo); - updateParams.put("partNo", partNo); - updateParams.put("codeNo", codeNo); - updateParams.put("recordType", recordType); - updateParams.put("propertiesItemNo", propertiesItemNo); - updateParams.put("numValue", numValue); - coDelMapper.updatePartPropertyNumValue(updateParams); - } else { - // 不存在,执行插入 - // 获取当前最大的item_no - Map maxParams = new HashMap<>(); - maxParams.put("site", site); - maxParams.put("buNo", buNo); - maxParams.put("partNo", partNo); - maxParams.put("codeNo", codeNo); - maxParams.put("recordType", recordType); - Double maxItemNo = coDelMapper.getMaxItemNo(maxParams); - Double newItemNo = (maxItemNo == null) ? 1.0 : maxItemNo + 1.0; - - // 获取属性定义信息 - Map itemParams = new HashMap<>(); - itemParams.put("site", site); - itemParams.put("buNo", buNo); - itemParams.put("itemNo", propertiesItemNo); - itemParams.put("itemType", recordType); - Map itemInfo = coDelMapper.getPropertiesItemInfo(itemParams); - - // 插入新记录 - Map insertParams = new HashMap<>(); - insertParams.put("site", site); - insertParams.put("buNo", buNo); - insertParams.put("partNo", partNo); - insertParams.put("codeNo", codeNo); - insertParams.put("subCodeSeqNo", 1); - insertParams.put("subCodeDesc", itemInfo != null ? itemInfo.get("codeDesc") : "基本信息"); - insertParams.put("itemNo", newItemNo); - insertParams.put("propertiesItemNo", propertiesItemNo); - insertParams.put("textValue", null); - insertParams.put("numValue", numValue); - insertParams.put("recordType", recordType); - coDelMapper.insertPartProperty(insertParams); - } - - return true; - } - - /** - * 根据packageType检查并插入箱类型 - * @param site 站点 - * @param buNo 业务单元 - * @param packageType 箱类型(对应Excel中的Box No) - * @param length 长 - * @param width 宽 - * @param height 高 - * @param username 用户名 - * @return packageNo 箱编码(用于更新part表) - */ - private String checkAndInsertPackageByType(String site, String buNo, String packageType, BigDecimal length, BigDecimal width, BigDecimal height, String username) { - // 根据packageType检查箱类型是否存在 - Map checkParams = new HashMap<>(); - checkParams.put("site", site); - checkParams.put("buNo", buNo); - checkParams.put("packageType", packageType); - Map existingPackage = coDelMapper.checkPackageExistsByType(checkParams); - - String packageNo; - - if (existingPackage == null) { - // 不存在,插入新箱类型 - // 获取当前最大的packageNo并+1 - Map maxParams = new HashMap<>(); - maxParams.put("site", site); - maxParams.put("buNo", buNo); - String maxPackageNo = coDelMapper.getMaxPackageNo(maxParams); - - // 生成新的packageNo - if (StringUtils.isBlank(maxPackageNo)) { - packageNo = buNo.equals("01-Label")?"10001":buNo.equals("03-RFID")?"20001":"30001"; - } else { - // 尝试解析并递增 - try { - // 尝试纯数字递增 - int num = Integer.parseInt(maxPackageNo); - packageNo = String.valueOf(num + 1); - } catch (NumberFormatException e) { - // 无法解析,使用时间戳 - packageNo = (buNo.equals("01-Label")?"1":buNo.equals("03-RFID")?"2":"3") + System.currentTimeMillis(); - } - } - - // 计算体积 - BigDecimal volume = null; - if (length != null && width != null && height != null) { - volume = length.multiply(width).multiply(height); - } - - Map insertParams = new HashMap<>(); - insertParams.put("site", site); - insertParams.put("buNo", buNo); - insertParams.put("packageNo", packageNo); - insertParams.put("packageType", packageType); - insertParams.put("length", length); - insertParams.put("width", width); - insertParams.put("height", height); - insertParams.put("volume", volume); - insertParams.put("weight", null); - insertParams.put("remark", "导入创建"); - insertParams.put("createBy", username); - coDelMapper.insertPackage(insertParams); - } else { - // 已存在,获取现有的packageNo并更新长宽高 - packageNo = (String) existingPackage.get("packageNo"); - - Map updateParams = new HashMap<>(); - updateParams.put("site", site); - updateParams.put("buNo", buNo); - updateParams.put("packageType", packageType); - updateParams.put("length", length); - updateParams.put("width", width); - updateParams.put("height", height); - // 计算体积 - if (length != null && width != null && height != null) { - updateParams.put("volume", length.multiply(width).multiply(height)); - } - updateParams.put("updateBy", username); - coDelMapper.updatePackageDimensions(updateParams); - } - - return packageNo; - } - - /** - * 更新物料的packageNo - * @param site 站点 - * @param partNo 物料编号 - * @param packageNo 箱编码 - */ - private void updatePartPackageNo(String site, String partNo, String packageNo) { - Map updateParams = new HashMap<>(); - updateParams.put("site", site); - updateParams.put("partNo", partNo); - updateParams.put("packageNo", packageNo); - coDelMapper.updatePartPackageNo(updateParams); - } - /** * 发送总毛重调整通知邮件 * @@ -7143,14 +3764,4 @@ public class CoDelServiceImpl implements CoDelService { return emailContent.toString(); } - /** - * 获取单元格值为字符串 - */ - private String getCellValueAsString(Cell cell, DataFormatter dataFormatter) { - if (cell == null) { - return ""; - } - return dataFormatter.formatCellValue(cell).trim(); - } - }