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 571eaced..c0f13736 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 @@ -613,4 +613,48 @@ public class CoDelController { return R.ok().put("data", result); } + + /** + * @Description 导入物料包装属性(每卷数量、每箱卷数、每卷重量、箱重量、箱类型) + * @Title importPartPackageProperties + * @param file Excel文件 + * @param buNo 业务单元编码 + * @param username 用户名 + * @param previewOnly 是否仅预览(true=预览,false=导入) + * @author System + * @date 2025-12-03 + * @return R 导入结果或预览数据 + */ + @PostMapping("/importPartPackageProperties") + @ResponseBody + public R importPartPackageProperties(@RequestParam(value = "file") MultipartFile file, + @RequestParam(value = "buNo") String buNo, + @RequestParam(value = "username") String username, + @RequestParam(value = "previewOnly", defaultValue = "false") String previewOnly) { + try { + // 参数校验 + if (buNo == null || buNo.trim().isEmpty()) { + return R.error("业务单元(buNo)不能为空"); + } + if (file == null || file.isEmpty()) { + return R.error("请上传Excel文件"); + } + + boolean isPreviewOnly = "true".equalsIgnoreCase(previewOnly); + Map result = coDelService.importPartPackageProperties(file, buNo, username, isPreviewOnly); + + if (isPreviewOnly) { + // 预览模式:返回预览数据 + return R.ok().put("data", result.get("data")); + } else { + // 导入模式:返回导入结果 + return R.ok() + .put("successCount", result.get("successCount")) + .put("failCount", result.get("failCount")) + .put("details", result.get("details")); + } + } catch (Exception e) { + return R.error("导入失败:" + e.getMessage()); + } + } } diff --git a/src/main/java/com/xujie/sys/modules/ecss/mapper/CoDelMapper.java b/src/main/java/com/xujie/sys/modules/ecss/mapper/CoDelMapper.java index df5b942f..f36ed096 100644 --- a/src/main/java/com/xujie/sys/modules/ecss/mapper/CoDelMapper.java +++ b/src/main/java/com/xujie/sys/modules/ecss/mapper/CoDelMapper.java @@ -265,4 +265,66 @@ public interface CoDelMapper { // 获取客户模板列表 List> getCustomerTemplateList(Map params); + + // ========== 物料包装属性导入相关方法 ========== + + /** + * 根据SKU获取物料信息(一个SKU可能对应多个part) + */ + List getPartListBySku(Map params); + + /** + * 检查物料属性是否存在 + */ + Map checkPartPropertyExists(Map params); + + /** + * 根据packageType检查箱类型是否存在 + */ + Map checkPackageExistsByType(Map params); + + /** + * 获取箱类型的最大packageNo编号 + */ + String getMaxPackageNo(Map params); + + /** + * 更新物料属性数值 + */ + void updatePartPropertyNumValue(Map params); + + /** + * 获取物料属性的最大item_no + */ + Double getMaxItemNo(Map params); + + /** + * 获取属性定义信息 + */ + Map getPropertiesItemInfo(Map params); + + /** + * 插入物料属性 + */ + void insertPartProperty(Map params); + + /** + * 检查箱类型是否存在 + */ + Map checkPackageExists(Map params); + + /** + * 插入箱类型 + */ + void insertPackage(Map params); + + /** + * 更新箱类型尺寸 + */ + void updatePackageDimensions(Map params); + + /** + * 更新物料的packageNo + */ + void updatePartPackageNo(Map params); } 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 a63f8379..d36eac73 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 @@ -175,4 +175,14 @@ public interface CoDelService { * @return 包装属性信息Map */ 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/CoDelServiceImpl.java b/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelServiceImpl.java index b8827364..630e8384 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 @@ -2981,7 +2981,11 @@ public class CoDelServiceImpl implements CoDelService { Map palletMap = palletDetailList.stream().collect(Collectors.toMap( o -> o.get("item_no"), o -> o)); // 装箱数据 List palletHeaderDataList = coDelMapper.searchEcssCoDelPalletHeaderData(notifyHeader);// 总托数 - int totalPlt = palletHeaderDataList.isEmpty()?0:palletHeaderDataList.get(0).getPalletQty(); + 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); @@ -3048,6 +3052,15 @@ public class CoDelServiceImpl implements CoDelService { 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"); } @@ -5746,4 +5759,408 @@ public class CoDelServiceImpl implements CoDelService { return emailContent.toString(); } + /** + * @Description 导入物料包装属性(每卷数量、每箱卷数、每卷重量、箱重量、箱类型) + * @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字段:物料编码、每卷数量、每箱卷数、每卷重量、箱重量(kg)、Box No、长(米)、宽(米)、高(米) + String sku = getCellValueAsString(row.getCell(0), dataFormatter); + String rollQtyStr = getCellValueAsString(row.getCell(1), dataFormatter); + String boxRollsStr = getCellValueAsString(row.getCell(2), dataFormatter); + String rollWeightStr = getCellValueAsString(row.getCell(3), dataFormatter); + String boxWeightStr = getCellValueAsString(row.getCell(4), dataFormatter); + String boxNo = 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); + // 跳过空行 + 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); + + // 验证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) + String[] itemNos = {"ROLLQTY", "BOXROLLS", "ROLLWEIGHT", "BOXWEIGHT"}; + String[] itemValues = {rollQtyStr, boxRollsStr, rollWeightStr, boxWeightStr}; + String[] itemNames = {"每卷数量", "每箱卷数", "每卷重量", "箱重量"}; + + // 为每个匹配的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/resources/mapper/ecss/CoDelMapper.xml b/src/main/resources/mapper/ecss/CoDelMapper.xml index ac2332db..76e059d6 100644 --- a/src/main/resources/mapper/ecss/CoDelMapper.xml +++ b/src/main/resources/mapper/ecss/CoDelMapper.xml @@ -1356,4 +1356,126 @@ left join ecss_CoDelNotifyHeader noHeader on a.site=noHeader.site and a.delNo=no ORDER BY template_name + + + + + + + + + + + + UPDATE part_sub_properties_value + SET num_value = #{numValue} + WHERE site = #{site} + AND bu_no = #{buNo} + AND part_no = #{partNo} + AND code_no = #{codeNo} + AND record_type = #{recordType} + AND properties_item_no = #{propertiesItemNo} + + + + + + + + + + + INSERT INTO part_sub_properties_value + (part_no, site, bu_no, code_no, sub_code_seq_no, sub_code_desc, item_no, properties_item_no, text_value, num_value, record_type) + VALUES + (#{partNo}, #{site}, #{buNo}, #{codeNo}, #{subCodeSeqNo}, #{subCodeDesc}, #{itemNo}, #{propertiesItemNo}, #{textValue}, #{numValue}, #{recordType}) + + + + + + + + + + + + + + INSERT INTO ecss_package + (Site, BuNo, package_no, package_type, length, width, height, volume, weight, Remark, CreateBy, CreateDate) + VALUES + (#{site}, #{buNo}, #{packageNo}, #{packageType}, #{length}, #{width}, #{height}, #{volume}, #{weight}, #{remark}, #{createBy}, GETDATE()) + + + + + UPDATE ecss_package + SET length = #{length}, + width = #{width}, + height = #{height}, + volume = #{volume}, + UpdateBy = #{updateBy}, + UpdateDate = GETDATE() + WHERE site = #{site} + AND BuNo = #{buNo} + AND package_type = #{packageType} + + + + + UPDATE part + SET packageNo = #{packageNo} + WHERE site = #{site} + AND part_no = #{partNo} +