From 6abb92c3219c765f6c566994dc015ed86493a01e Mon Sep 17 00:00:00 2001 From: tzy Date: Fri, 26 Sep 2025 14:51:02 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=87=E8=B4=AD=E8=AE=A2=E5=8D=95=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=8E=A8=E9=80=81=E8=87=B3=E4=BC=81=E4=B8=9A=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=9B=B4=E6=96=B020250916?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruoyi/common/poi/ExcelTemplateProc.java | 106 +++---- .../ruoyi/common/result/BOMUploadResult.java | 26 ++ .../ruoyi/common/utils/VersionComparator.java | 179 ++++++++++++ .../controller/BomDetailsController.java | 270 ++++++++++++++---- .../controller/EleMaterialsController.java | 12 + .../controller/ImMaterialController.java | 11 +- .../KingdeeWorkCenterDataController.java | 71 ++--- .../controller/PcRigidChainController.java | 166 +++++++++-- .../controller/ProcessOrderProController.java | 146 ++++++++-- .../controller/ProcessRouteController.java | 9 +- .../com/ruoyi/system/domain/BomDetails.java | 5 + .../com/ruoyi/system/domain/ImMaterial.java | 3 +- .../system/domain/dto/KindegeeLogDTO.java | 12 + .../domain/dto/ProcessRouteExcelDTO.java | 10 +- .../system/domain/vo/EleMaterialsVo.java | 2 +- .../ruoyi/system/domain/vo/ElecOutDataVO.java | 2 +- .../domain/vo/ElectricalMaterialBomVO.java | 93 ++++++ .../system/domain/vo/PcRigidChainVo.java | 2 +- .../java/com/ruoyi/system/runner/JdUtil.java | 1 - .../service/IProcessOrderProService.java | 4 +- .../service/impl/ImMaterialServiceImpl.java | 2 +- .../impl/MrpResultCheckServiceImpl.java | 5 +- .../impl/ProcessOrderProServiceImpl.java | 193 +++++++++++-- .../service/impl/ProcessRouteServiceImpl.java | 25 +- .../service/impl/SafetyStockServiceImpl.java | 5 +- .../impl/WorkProcedureServiceImpl.java | 2 +- .../EXCEL模板/生产及工艺计划模版.xlsx | Bin 18742 -> 18572 bytes .../resources/EXCEL模板/采购订单模板1.xlsx | Bin 11394 -> 11402 bytes 28 files changed, 1095 insertions(+), 267 deletions(-) create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/result/BOMUploadResult.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/utils/VersionComparator.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/KindegeeLogDTO.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ElectricalMaterialBomVO.java diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/poi/ExcelTemplateProc.java b/ruoyi-common/src/main/java/com/ruoyi/common/poi/ExcelTemplateProc.java index 6d30b38..1f7180d 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/poi/ExcelTemplateProc.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/poi/ExcelTemplateProc.java @@ -7,10 +7,12 @@ 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.springframework.core.io.ClassPathResource; import org.springframework.util.ResourceUtils; import java.io.*; +import java.nio.file.Files; import java.util.List; import java.util.Map; @@ -22,27 +24,29 @@ import java.util.Map; public class ExcelTemplateProc { /** - * @param templateFileName - * @param exportFilePathAndName - * @param staticDataMap - * @param dynamicDataMappingList - * @return void - * @description: 根据模版导出Excel入口 - * @author susu - * @date 2024/2/20 + * 根据模版导出Excel入口 + * @param templateFileName 模板文件路径,可以是classpath路径 或 本地绝对路径 + * @param exportFilePathAndName 导出文件完整路径 + * @param staticDataMap 静态数据 + * @param dynamicDataMappingList 动态数据 */ - public static void doExportExcelByTemplateProc(String templateFileName, String exportFilePathAndName, + public static void doExportExcelByTemplateProc(String templateFileName, + String exportFilePathAndName, Map staticDataMap, List dynamicDataMappingList) throws IOException { - /** - * 1. 从resources下加载模板并替换 - * 使用 ResourceUtils 加载文件 - */ - File file = ResourceUtils.getFile("classpath:"+templateFileName); - InputStream inputStream = new FileInputStream(file); + + InputStream inputStream; + + if (templateFileName.startsWith("/") || templateFileName.contains(":")) { + // 绝对路径 (Windows: D:/... Linux: /opt/...) + inputStream = Files.newInputStream(new File(templateFileName).toPath()); + } else { + // classpath 路径 + ClassPathResource resource = new ClassPathResource(templateFileName); + inputStream = resource.getInputStream(); + } Workbook workbook = dealAllSheetsByTemplate(inputStream, staticDataMap, dynamicDataMappingList); - // 2. 保存到本地 saveExportFile(workbook, exportFilePathAndName); } @@ -87,15 +91,15 @@ public class ExcelTemplateProc { Map staticDataMap, List dynamicDataMappingList) throws IOException { XSSFWorkbook workbook = new XSSFWorkbook(inputStream); - - + + // 处理所有sheet页 for (int sheetIndex = 0; sheetIndex < workbook.getNumberOfSheets(); sheetIndex++) { XSSFSheet sheet = workbook.getSheetAt(sheetIndex); // 按模板处理sheet页 dealSheetDataByTemplate(sheet, staticDataMap, dynamicDataMappingList); } - + return workbook; } @@ -134,7 +138,7 @@ public class ExcelTemplateProc { if (row == null) { continue; // 跳过空行 } - + // 添加调试信息:检查每一行是否有静态数据占位符 if (row.getFirstCellNum() != -1 && row.getLastCellNum() != -1) { for (int j = row.getFirstCellNum(); j < row.getLastCellNum(); j++) { @@ -147,10 +151,10 @@ public class ExcelTemplateProc { } } } - + // 先处理静态数据,再处理动态数据 dealTemplateDataRow(row, null, staticDataMap); - + DynamicDataMapping dynamicDataMapping = getDynamicRowDataByMatch(row, dynamicDataMappingList); if (dynamicDataMapping != null) { i = getTemplateLastRowIndexAfterDealTemplate(sheet, i, dynamicDataMapping); @@ -177,21 +181,21 @@ public class ExcelTemplateProc { if (dataMap == null || row == null) { return; } - + int firstCellNum = row.getFirstCellNum(); int lastCellNum = row.getLastCellNum(); - + // 检查是否有有效的单元格范围 if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) { return; } - + // 添加调试信息:显示静态数据处理 if (dataPrefix == null || dataPrefix.isEmpty()) { System.out.println("开始处理静态数据行: " + row.getRowNum() + ", dataMap=" + dataMap.toString()); } - - + + for (int i = firstCellNum; i < lastCellNum; i++) { XSSFCell cell = row.getCell(i); if (cell != null) { @@ -224,16 +228,16 @@ public class ExcelTemplateProc { if (StringUtils.isEmpty(cellValue)) { return; } - - - + + + boolean flag = false; dataPrefix = StringUtils.isEmpty(dataPrefix) ? "" : (dataPrefix + "."); for (Map.Entry entry : dataMap.entrySet()) { // 循环所有,因为可能一行有多个占位符 String cellTemplateStr = "{{" + dataPrefix + entry.getKey() + "}}"; - - + + if (cellValue.contains(cellTemplateStr)) { // 替换模版中单元格的数据 String replacementValue = entry.getValue() == null ? "" : entry.getValue().toString(); @@ -267,29 +271,29 @@ public class ExcelTemplateProc { if (dynamicDataMappingList == null || dynamicDataMappingList.size() < 1) { return null; } - + // 检查行是否为空 if (row == null) { return null; } - + int firstCellNum = row.getFirstCellNum(); int lastCellNum = row.getLastCellNum(); - + // 检查是否有有效的单元格范围 if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) { return null; } - + for (int j = firstCellNum; j < lastCellNum; j++) { XSSFCell cell = row.getCell(j); if (cell == null) { continue; } - + String value = cell.getStringCellValue(); if (value != null) { - + for (DynamicDataMapping dynamicData : dynamicDataMappingList) { if (value.startsWith("{{" + dynamicData.getDataId() + ".")) { return dynamicData; @@ -361,20 +365,20 @@ public class ExcelTemplateProc { if (row == null) { return false; } - + int firstCellNum = row.getFirstCellNum(); int lastCellNum = row.getLastCellNum(); - + if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) { return false; } - + for (int j = firstCellNum; j < lastCellNum; j++) { XSSFCell cell = row.getCell(j); if (cell == null) { continue; } - + String value = cell.getStringCellValue(); if (value != null && value.contains("{{") && value.contains("}}")) { return true; @@ -390,24 +394,24 @@ public class ExcelTemplateProc { if (dynamicDataMappingList == null || dynamicDataMappingList.size() < 1) { return false; } - + if (row == null) { return false; } - + int firstCellNum = row.getFirstCellNum(); int lastCellNum = row.getLastCellNum(); - + if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) { return false; } - + for (int j = firstCellNum; j < lastCellNum; j++) { XSSFCell cell = row.getCell(j); if (cell == null) { continue; } - + String value = cell.getStringCellValue(); if (value != null) { for (DynamicDataMapping dynamicData : dynamicDataMappingList) { @@ -427,20 +431,20 @@ public class ExcelTemplateProc { if (row == null) { return; } - + int firstCellNum = row.getFirstCellNum(); int lastCellNum = row.getLastCellNum(); - + if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) { return; } - + for (int j = firstCellNum; j < lastCellNum; j++) { XSSFCell cell = row.getCell(j); if (cell == null) { continue; } - + String value = cell.getStringCellValue(); if (value != null && value.contains("{{") && value.contains("}}")) { // 清理动态数据占位符,保留静态内容 diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/result/BOMUploadResult.java b/ruoyi-common/src/main/java/com/ruoyi/common/result/BOMUploadResult.java new file mode 100644 index 0000000..45f9c0b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/result/BOMUploadResult.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.result; + +import lombok.Data; + +@Data +public class BOMUploadResult { + private boolean success; // 是否成功 + private String id; // 金蝶内部ID + private String number; // 单据编号 + private String errorMessage; // 错误信息(失败时才有) + + public static BOMUploadResult success(String id, String number) { + BOMUploadResult result = new BOMUploadResult(); + result.setSuccess(true); + result.setId(id); + result.setNumber(number); + return result; + } + + public static BOMUploadResult fail(String errorMessage) { + BOMUploadResult result = new BOMUploadResult(); + result.setSuccess(false); + result.setErrorMessage(errorMessage); + return result; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/VersionComparator.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/VersionComparator.java new file mode 100644 index 0000000..d691228 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/VersionComparator.java @@ -0,0 +1,179 @@ +package com.ruoyi.common.utils; + +import java.util.Comparator; + +/** + * 版本号排序比较器 + * 支持类似 30AD.1, 30AD.2, 30AD.01.1, 30AD.01.2, 30AD.01A.1, 30AD.01A.2 的排序 + * + * 排序规则: + * 1. 先按主要部分排序(如 30AD) + * 2. 再按数字部分排序(如 1, 2, 01, 10) + * 3. 最后按字母部分排序(如 A, B) + * + * @author system + */ +public class VersionComparator implements Comparator { + + @Override + public int compare(String s1, String s2) { + if (s1 == null && s2 == null) { + return 0; + } + if (s1 == null) { + return -1; + } + if (s2 == null) { + return 1; + } + + // 按点分割字符串 + String[] parts1 = s1.split("\\."); + String[] parts2 = s2.split("\\."); + + int maxLength = Math.max(parts1.length, parts2.length); + + for (int i = 0; i < maxLength; i++) { + String part1 = i < parts1.length ? parts1[i] : ""; + String part2 = i < parts2.length ? parts2[i] : ""; + + int result = comparePart(part1, part2); + if (result != 0) { + return result; + } + } + + return 0; + } + + /** + * 比较单个部分 + * 支持数字和字母混合的比较 + */ + private int comparePart(String part1, String part2) { + if (part1.equals(part2)) { + return 0; + } + + // 如果其中一个为空,空的小于非空的 + if (part1.isEmpty()) { + return -1; + } + if (part2.isEmpty()) { + return 1; + } + + // 特殊处理:如果都是纯数字,按数值比较 + Integer num1 = parseNumber(part1); + Integer num2 = parseNumber(part2); + if (num1 != null && num2 != null) { + return num1.compareTo(num2); + } + + // 分离数字和字母部分 + String[] segments1 = splitNumberAndLetter(part1); + String[] segments2 = splitNumberAndLetter(part2); + + // 比较每个段 + int maxSegments = Math.max(segments1.length, segments2.length); + for (int i = 0; i < maxSegments; i++) { + String seg1 = i < segments1.length ? segments1[i] : ""; + String seg2 = i < segments2.length ? segments2[i] : ""; + + int result = compareSegment(seg1, seg2); + if (result != 0) { + return result; + } + } + + return 0; + } + + /** + * 将字符串分离为数字和字母段 + * 例如: "01A" -> ["01", "A"] + * "2" -> ["2"] + * "A" -> ["A"] + * "01FV" -> ["01", "FV"] + */ + private String[] splitNumberAndLetter(String str) { + if (str == null || str.isEmpty()) { + return new String[]{""}; + } + + StringBuilder result = new StringBuilder(); + boolean lastWasDigit = false; + + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + boolean isDigit = Character.isDigit(c); + + if (i > 0 && isDigit != lastWasDigit) { + result.append("|"); + } + + result.append(c); + lastWasDigit = isDigit; + } + + return result.toString().split("\\|"); + } + + /** + * 比较单个段 + */ + private int compareSegment(String seg1, String seg2) { + if (seg1.equals(seg2)) { + return 0; + } + + if (seg1.isEmpty()) { + return -1; + } + if (seg2.isEmpty()) { + return 1; + } + + // 尝试解析为数字 + Integer num1 = parseNumber(seg1); + Integer num2 = parseNumber(seg2); + + // 如果都是数字,按数字比较 + if (num1 != null && num2 != null) { + return num1.compareTo(num2); + } + + // 如果一个是数字,一个是字符串,数字优先 + if (num1 != null && num2 == null) { + return -1; + } + if (num1 == null && num2 != null) { + return 1; + } + + // 如果都是字符串,按字符串比较(忽略大小写) + return seg1.compareToIgnoreCase(seg2); + } + + /** + * 尝试将字符串解析为数字 + * 如果字符串只包含数字,则返回对应的Integer + * 否则返回null + */ + private Integer parseNumber(String str) { + if (str == null || str.isEmpty()) { + return null; + } + + try { + // 检查是否只包含数字 + if (str.matches("\\d+")) { + return Integer.parseInt(str); + } + } catch (NumberFormatException e) { + // 忽略异常,返回null + } + + return null; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/BomDetailsController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/BomDetailsController.java index e3d79d2..57d44de 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/BomDetailsController.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/BomDetailsController.java @@ -3,6 +3,9 @@ package com.ruoyi.system.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.bean.BeanUtil; import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -18,16 +21,20 @@ import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.core.validate.AddGroup; import com.ruoyi.common.core.validate.EditGroup; import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.result.BOMUploadResult; import com.ruoyi.common.utils.JdUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.system.domain.BomDetails; import com.ruoyi.system.domain.FigureSave; import com.ruoyi.system.domain.MaterialProperties; +import com.ruoyi.system.domain.ProcessOrderPro; import com.ruoyi.system.domain.bo.BomDetailsBo; import com.ruoyi.system.domain.bo.ProcessOrderProBo; import com.ruoyi.system.domain.dto.JdValidateBomDTO; import com.ruoyi.system.domain.dto.JdChildDTO; +import com.ruoyi.system.domain.dto.KindegeeLogDTO; import com.ruoyi.system.domain.vo.BomDetailsVo; +import com.ruoyi.system.domain.vo.ElectricalMaterialBomVO; import com.ruoyi.system.mapper.FigureSaveMapper; import com.ruoyi.system.mapper.ProcessOrderProMapper; import com.ruoyi.system.runner.JdUtil; @@ -49,6 +56,7 @@ import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.io.IOException; +import java.math.BigDecimal; import java.util.*; import static org.aspectj.bridge.MessageUtil.fail; @@ -354,12 +362,13 @@ public class BomDetailsController extends BaseController { @PostMapping("/updateFBMaterial") public R updateFBMaterial(@RequestBody List> bomDetailParams) { List bomDetailsList = new ArrayList<>(); - Set processedMaterials = new HashSet<>(); // 用于跟踪已处理的物料编码 + List logDTOS = new ArrayList<>(); List failedMaterials = new ArrayList<>(); // 用于跟踪处理失败的物料 String totalWeight = ""; // 遍历前端传来的数据 for (Map param : bomDetailParams) { String fnumber = param.get("fnumber"); // 物料编码 + String fname = param.get("fname"); // 物料编码 totalWeight = param.get("totalWeight"); // 生产令号 // 根据物料编码和生产令号查询 @@ -367,15 +376,9 @@ public class BomDetailsController extends BaseController { log.info("处理物料编码: {}, 生产令号: {}", fnumber, totalWeight); if (bomDetails != null && !bomDetails.isEmpty()) { //TODO 处理父级物料 - ProcessOrderProBo bo = iProcessOrderProService.selectByProjectNumber(totalWeight); + ProcessOrderPro bo = iProcessOrderProService.selectByProjectNumber(totalWeight); - List list = iFigureSaveService.selectByProCode(totalWeight); - if (list.size() > 0) { - for (FigureSave figureSave : list) { - String figureNumber = figureSave.getFigureNumber(); - List bomDetails1 = iBomDetailsService.selectByFNumberAndTotalWeight(figureNumber, totalWeight); - } - } + // 第一步:处理所有物料的保存 for (BomDetails material : bomDetails) { // 获取工艺表中的非委外工时 Double fbWorkTime = iProcessRouteService.getFbWorkTime(material); @@ -383,7 +386,6 @@ public class BomDetailsController extends BaseController { if (material.getPartNumber() != null && material.getName() != null && material.getUnitWeight().equals("否")) { String state = determineState(material); log.info("开始新增不存在的物料 ==> 物料图号: {}, 物料名称: {}", material.getPartNumber(), material.getName()); - try { int result = loadMaterialPreservation(material, state, fbWorkTime); if (result == 1) { @@ -405,69 +407,179 @@ public class BomDetailsController extends BaseController { } } - // 保存物料清单之前进行BOM校验 - if (validateBOM(fnumber, bomDetails)) { - failedMaterials.add(fnumber); + // 第二步:BOM校验和上传(在循环外部) + boolean needUpload = !validateBOM(fnumber, bomDetails); + if (needUpload) { + try { + // 物料清单保存方法 + BOMUploadResult bomUploadResult = FBloadBillOfMaterialsPreservation(bomDetails, bo); + if (bomUploadResult.isSuccess()) { + KindegeeLogDTO logDTO = new KindegeeLogDTO(); + logDTO.setProjectCode(bo.getProductionOrderNo()); + logDTO.setMaterialCode(fnumber); // 使用物料编码而不是单个物料的编码 + logDTO.setMaterialName(fname); + logDTO.setCode(bomUploadResult.getNumber()); + logDTO.setReason(bomUploadResult.getNumber()+"OK"); + logDTOS.add(logDTO); + } else { + KindegeeLogDTO logDTO = new KindegeeLogDTO(); + logDTO.setProjectCode(bo.getProductionOrderNo()); + logDTO.setMaterialCode(fnumber); + logDTO.setMaterialName(fname); + logDTO.setCode(bomUploadResult.getNumber()); + logDTO.setReason(bomUploadResult.getErrorMessage()); + logDTOS.add(logDTO); + } + bomDetailsList.addAll(bomDetails); + } catch (Exception e) { + log.error("保存BOM失败, 物料编码: {}, 错误: {}", fnumber, e.getMessage()); + failedMaterials.add(fnumber); + } + } else { + KindegeeLogDTO logDTO = new KindegeeLogDTO(); + logDTO.setProjectCode(bo.getProductionOrderNo()); + logDTO.setMaterialCode(fnumber); + logDTO.setMaterialName(fname); + logDTO.setCode("200"); + logDTO.setReason("BOM已存在且一致"); + logDTOS.add(logDTO); + log.info("BOM已存在且一致,物料编码: {},跳过保存", fnumber); } - - // 物料清单保存方法 - FBloadBillOfMaterialsPreservation(bomDetails, bo); - bomDetailsList.addAll(bomDetails); } } //更新项目进度 - ProcessOrderProBo processOrderProBo = iProcessOrderProService.selectByProjectNumber(totalWeight); + ProcessOrderPro processOrderProBo = iProcessOrderProService.selectByProjectNumber(totalWeight); + + processOrderProBo.setDrawingType( JSONUtil.toJsonStr(logDTOS)); processOrderProBo.setBomStatus(2L); - processOrderProMapper.selectById(processOrderProBo); + processOrderProMapper.updateById(processOrderProBo); // 返回处理结果 return R.ok("成功", bomDetailsList); } - private boolean validateBOM(String fnumber, List bomDetails) { List JDBomList = JdUtil.getSelectBomList(fnumber); - // 1. 判断BOM是否为空 if (JDBomList == null || JDBomList.isEmpty()) { - log.error("未在金蝶中找到相同的BOM,需要上传,物料编码: {}", fnumber); - return false; // BOM为空,需要上传 + log.error("未在金蝶中找到相同的BOM,需要上传,物料编码: {}", fnumber); + return false; } - for (JdValidateBomDTO jdBom : JDBomList) { - // 3. 检查子项数量是否一致 - if (jdBom.getChilds().size() != bomDetails.size()) { - continue; // 数量不一致,跳过这个BOM,继续检查下一个 + // 子项排序:按 PartNumber -> Name -> PartdiagramCode -> PartdiagramName 保证顺序唯一 + bomDetails.sort(Comparator.comparing(BomDetails::getPartNumber, Comparator.nullsFirst(String::compareTo)) + .thenComparing(BomDetails::getName, Comparator.nullsFirst(String::compareTo)) + .thenComparing(BomDetails::getPartdiagramCode, Comparator.nullsFirst(String::compareTo)) + .thenComparing(BomDetails::getPartdiagramName, Comparator.nullsFirst(String::compareTo))); + + log.info("开始验证BOM - 物料编码: {}, 金蝶BOM数量: {}, 输入BOM子项数量: {}", + fnumber, JDBomList.size(), bomDetails.size()); + + for (int bomIndex = 0; bomIndex < JDBomList.size(); bomIndex++) { + JdValidateBomDTO jdBom = JDBomList.get(bomIndex); + List childs = jdBom.getChilds(); + + log.debug("正在比较第{}个金蝶BOM - 子项数量: {}", bomIndex + 1, childs.size()); + + childs.sort(Comparator.comparing(JdChildDTO::getPartNumber, Comparator.nullsFirst(String::compareTo)) + .thenComparing(JdChildDTO::getName, Comparator.nullsFirst(String::compareTo)) + .thenComparing(JdChildDTO::getPartdiagramCode, Comparator.nullsFirst(String::compareTo)) + .thenComparing(JdChildDTO::getPartdiagramName, Comparator.nullsFirst(String::compareTo))); + + if (childs.size() != bomDetails.size()) { + log.warn("BOM子项数量不一致 - 物料编码: {}, 金蝶BOM子项数量: {}, 输入BOM子项数量: {}", + fnumber, childs.size(), bomDetails.size()); + continue; // 数量不一致,跳过当前金蝶BOM } - // 4. 比较每个子项的内容 boolean isMatch = true; - for (int i = 0; i < jdBom.getChilds().size(); i++) { - JdChildDTO jdChild = jdBom.getChilds().get(i); + List mismatchDetails = new ArrayList<>(); + + for (int i = 0; i < childs.size(); i++) { + JdChildDTO jdChild = childs.get(i); BomDetails inputBomDetail = bomDetails.get(i); - // 比较物料编码和名称 - if (!jdChild.getPartNumber().equals(inputBomDetail.getPartNumber()) || !jdChild.getName().equals(inputBomDetail.getName())) { - isMatch = false; - break; + List itemMismatches = new ArrayList<>(); + + // 字符串比较(null/空格统一处理) + if (!equalsStr(jdChild.getPartNumber(), inputBomDetail.getPartNumber())) { + itemMismatches.add(String.format("PartNumber [金蝶:'%s' vs 输入:'%s']", + jdChild.getPartNumber(), inputBomDetail.getPartNumber())); } - // 比较分子和分母 - if (!jdChild.getDenominator().equals(inputBomDetail.getDenominator()) || !jdChild.getQuantity().equals(inputBomDetail.getQuantity())) { + /* if (!equalsStr(jdChild.getName(), inputBomDetail.getName())) { + itemMismatches.add(String.format("Name [金蝶:'%s' vs 输入:'%s']", + jdChild.getName(), inputBomDetail.getName())); + } + */ + if (!equalsStr(jdChild.getPartdiagramCode(), inputBomDetail.getPartdiagramCode())) { + itemMismatches.add(String.format("PartdiagramCode [金蝶:'%s' vs 输入:'%s']", + jdChild.getPartdiagramCode(), inputBomDetail.getPartdiagramCode())); + } + + /* if (!equalsStr(jdChild.getPartdiagramName(), inputBomDetail.getPartdiagramName())) { + itemMismatches.add(String.format("PartdiagramName [金蝶:'%s' vs 输入:'%s']", + jdChild.getPartdiagramName(), inputBomDetail.getPartdiagramName())); + } +*/ + // 数值比较(BigDecimal + stripTrailingZeros) + BigDecimal jdQuantity = new BigDecimal(String.valueOf(jdChild.getQuantity())).stripTrailingZeros(); + BigDecimal inputQuantity = new BigDecimal(String.valueOf(inputBomDetail.getQuantity())).stripTrailingZeros(); + + BigDecimal jdDenominator = new BigDecimal(String.valueOf(jdChild.getDenominator())).stripTrailingZeros(); + BigDecimal inputDenominator = new BigDecimal(String.valueOf(inputBomDetail.getDenominator())).stripTrailingZeros(); + + if (jdQuantity.compareTo(inputQuantity) != 0) { + itemMismatches.add(String.format("Quantity [金蝶:%s vs 输入:%s]", + jdQuantity, inputQuantity)); + } + + if (jdDenominator.compareTo(inputDenominator) != 0) { + itemMismatches.add(String.format("Denominator [金蝶:%s vs 输入:%s]", + jdDenominator, inputDenominator)); + } + + // 如果有不匹配的字段,记录详细信息 + if (!itemMismatches.isEmpty()) { + String mismatchInfo = String.format("第%d项不匹配: %s", i + 1, String.join(", ", itemMismatches)); + mismatchDetails.add(mismatchInfo); isMatch = false; - break; + } + + // 调试日志 + log.debug("对比第{}项 -> PartNumber [{} vs {}], Name [{} vs {}], Code [{} vs {}], DiagramName [{} vs {}], Quantity [{} vs {}], Denominator [{} vs {}]", + i + 1, + jdChild.getPartNumber(), inputBomDetail.getPartNumber(), + jdChild.getName(), inputBomDetail.getName(), + jdChild.getPartdiagramCode(), inputBomDetail.getPartdiagramCode(), + jdChild.getPartdiagramName(), inputBomDetail.getPartdiagramName(), + jdQuantity, inputQuantity, + jdDenominator, inputDenominator); + } + + // 如果不匹配,打印详细的不一致信息 + if (!isMatch) { + log.warn("BOM不匹配详情 - 物料编码: {}", fnumber); + log.warn("金蝶BOM子项数量: {}, 输入BOM子项数量: {}", childs.size(), bomDetails.size()); + for (String mismatch : mismatchDetails) { + log.warn(" {}", mismatch); } } if (isMatch) { - // 找到匹配的BOM,无需上传 log.info("BOM完全相同,物料编码: {},无需上传", fnumber); - return true; + return true; // 找到匹配BOM } } - // 所有BOM都不匹配,需要上传 log.info("BOM不存在或不一致,物料编码: {},需要上传", fnumber); - return false; + return false; // 所有BOM都不匹配 + } + + // null/空格统一处理方法 + private boolean equalsStr(String a, String b) { + String s1 = (a == null || a.trim().isEmpty()) ? "" : a.trim(); + String s2 = (b == null || b.trim().isEmpty()) ? "" : b.trim(); + return s1.equals(s2); } @@ -589,7 +701,7 @@ public class BomDetailsController extends BaseController { } // FBOM物料清单保存 - public void FBloadBillOfMaterialsPreservation(List bomlist, ProcessOrderProBo bo) { + public BOMUploadResult FBloadBillOfMaterialsPreservation(List bomlist, ProcessOrderPro bo) { BomDetails bomDetails1 = bomlist.get(0); int verification = isMaterialVerification(bomDetails1.getFNumber(), bomDetails1.getFName()); @@ -648,7 +760,7 @@ public class BomDetailsController extends BaseController { List fTreeEntityList = new ArrayList<>(); for (BomDetails details : bomlist) { if (bomlist.isEmpty()) { - return; + return null; } // 创建FTreeEntity对象,并加入FTreeEntity数组 JsonObject fTreeEntityItem = new JsonObject(); @@ -673,7 +785,6 @@ public class BomDetailsController extends BaseController { } fTreeEntityItem.addProperty("FSupplyType", supplyType); } - fTreeEntityItem.addProperty("FMATERIALTYPE", "1"); // 创建FMATERIALIDCHILD对象,并加入FTreeEntity JsonObject fMaterialIdChild = new JsonObject(); @@ -708,30 +819,23 @@ public class BomDetailsController extends BaseController { fTreeEntityItem.add("FOWNERID", FOWNERID); FOWNERID.addProperty("FNumber", "GYS_070"); } - fTreeEntityList.add(fTreeEntityItem); } String jsonData = json.toString(); - log.info("打印json:" + jsonData); try { - // 业务对象标识 - String formId = "ENG_BOM"; - // 调用接口 - String resultJson = client.save(formId, jsonData); - - // 用于记录结果 - Gson gson = new Gson(); - // 对返回结果进行解析和校验 - RepoRet repoRet = gson.fromJson(resultJson, RepoRet.class); - if (repoRet.getResult().getResponseStatus().isIsSuccess()) { - log.debug("物料清单bom 保存成功===================>" + "图号:" + bomDetails1.getFNumber()); + String resultJson = new K3CloudApi().save("ENG_BOM", json.toString()); + // 直接用 Jackson 解析 + BOMUploadResult result = parseK3Response(resultJson); + if (result.isSuccess()) { + log.info("✅ BOM保存成功, 图号={}", bomDetails1.getFNumber()); } else { - log.error("物料清单bom 保存失败===================>" + "图号:" + bomDetails1.getFNumber()); + log.error("❌ BOM保存失败, 图号={}, 错误={}", bomDetails1.getFNumber(), result.getErrorMessage()); } + return result; } catch (Exception e) { - R.fail(e.getMessage()); + log.error("保存BOM异常,图号={}, 错误={}", bomDetails1.getFNumber(), e.getMessage(), e); + return BOMUploadResult.fail("保存BOM异常: " + e.getMessage()); } - // 输出生成的Json } /* @@ -2031,5 +2135,51 @@ public class BomDetailsController extends BaseController { } return 1; } + public BOMUploadResult parseK3Response(String jsonResponse) { + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(jsonResponse); + JsonNode result = root.path("Result"); + JsonNode responseStatus = result.path("ResponseStatus"); + + boolean isSuccess = responseStatus.path("IsSuccess").asBoolean(false); + if (isSuccess) { + String id = result.path("Id").asText(); + String number = result.path("Number").asText(); + return BOMUploadResult.success(id, number); + } else { + JsonNode errors = responseStatus.path("Errors"); + String errorMsg = (errors != null && errors.isArray() && !errors.isEmpty()) + ? errors.toString() + : "未知错误"; + return BOMUploadResult.fail(errorMsg); + } + } catch (Exception e) { + return BOMUploadResult.fail("解析返回信息失败: " + e.getMessage()); + } + } + + /** + * 导入BOM + * daorubaon + * + * @param file 导入文件 + */ + + @Log(title = "明细导入", businessType = BusinessType.IMPORT) + @SaCheckPermission("system:details:import") + @PostMapping(value = "/importData21", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R importData21(@RequestPart("file") MultipartFile file) throws Exception { + List electricalMaterialBomVOS = ExcelUtil.importExcel(file.getInputStream(), ElectricalMaterialBomVO.class); + for (ElectricalMaterialBomVO bomVO : electricalMaterialBomVOS) { + BomDetails bomDetails = new BomDetails(); + bomDetails.setTotalWeight(bomVO.getProductionOrderNo()); + bomDetails.setFNumber(bomVO.getDrawingNo()); + bomDetails.setFName(bomVO.getDrawingName()); + bomDetails.setPartdiagramCode(bomVO.getParentDrawingNo()); + bomDetails.setPartdiagramName(bomVO.getParentPart()); + } + return null; + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/EleMaterialsController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/EleMaterialsController.java index a965df1..889746c 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/EleMaterialsController.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/EleMaterialsController.java @@ -527,5 +527,17 @@ public class EleMaterialsController extends BaseController { return R.ok("更新成功"); } + @Log(title = "更新物料单重", businessType = BusinessType.IMPORT) + @SaCheckPermission("system:materials:importEleBom") + @PostMapping(value = "/importEleBom", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R importEleBom(@RequestParam("file") MultipartFile file) throws Exception { + String originalFilename = file.getOriginalFilename(); + log.info("读取文件名: " + originalFilename); + ExcelResult result = ExcelUtil.importExcelSheet1(file.getInputStream(), JdDanZhong.class, true); + List list = result.getList(); + + + return R.ok("更新成功"); + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ImMaterialController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ImMaterialController.java index 4f5921f..3707ac6 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ImMaterialController.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ImMaterialController.java @@ -270,7 +270,7 @@ public class ImMaterialController extends BaseController { @XxlJob("updateMaterials") public Boolean updateMaterials() throws Exception { - Date date = new Date(); + Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); List imMaterials = updateJdMaterial(sdf.format(date)); Boolean result = iImMaterialService.updateByFMid(imMaterials); @@ -284,14 +284,14 @@ public class ImMaterialController extends BaseController { json.addProperty("FormId", "BD_MATERIAL"); json.addProperty("FieldKeys", "FMATERIALID,FNumber,FName,FCategoryID.FNumber,F_HBYT_DZ,F_SVRI_Assistant.FNumber,FErpClsID,FBaseUnitId.FName,FModifyDate,FForbidStatus"); JsonArray filterString = new JsonArray(); - JsonObject filterObject = new JsonObject(); + /*JsonObject filterObject = new JsonObject(); filterObject.addProperty("FieldName", "FForbidStatus"); // 使用传入的 fieldName filterObject.addProperty("Compare", "105"); filterObject.addProperty("Value", "A"); filterObject.addProperty("Left", ""); filterObject.addProperty("Right", ""); filterObject.addProperty("Logic", 0); - filterString.add(filterObject); + filterString.add(filterObject);*/ JsonObject filterObject1 = new JsonObject(); filterObject1.addProperty("FieldName", "FCreateDate"); // 使用传入的 fieldName filterObject1.addProperty("Compare", "93"); @@ -301,7 +301,7 @@ public class ImMaterialController extends BaseController { filterObject1.addProperty("Logic", 1); filterString.add(filterObject1); JsonObject filterObject2 = new JsonObject(); - filterObject2.addProperty("FieldName", "FCreateDate"); // 使用传入的 fieldName + filterObject2.addProperty("FieldName", "FModifyDate"); // 使用传入的 fieldName filterObject2.addProperty("Compare", "93"); filterObject2.addProperty("Value", date); filterObject2.addProperty("Left", ""); @@ -356,7 +356,7 @@ public class ImMaterialController extends BaseController { //请求参数,要求为json字符串 JsonObject json = new JsonObject(); json.addProperty("FormId", "BD_MATERIAL"); - json.addProperty("FieldKeys", "FMATERIALID,FNumber,FName,FCategoryID.FNumber,F_HBYT_DZ,F_SVRI_Assistant.FNumber,FErpClsID,FBaseUnitId.FName,FModifyDate,FForbidStatus,FIsVmiBusiness"); + json.addProperty("FieldKeys", "FMATERIALID,FNumber,FName,F_HBYT_PP,FCategoryID.FNumber,F_HBYT_DZ,F_SVRI_Assistant.FNumber,FErpClsID,FBaseUnitId.FName,FModifyDate,FForbidStatus,FIsVmiBusiness"); JsonArray filterString = new JsonArray(); log.debug("构建查询条件..."); @@ -407,6 +407,7 @@ public class ImMaterialController extends BaseController { * 保存所有的物料编码 * @return List */ + @XxlJob("insertJDMaterial") @GetMapping("/insertJDMaterial") public List insertJDMaterial() throws Exception { List materialList = loadMaterial(); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/KingdeeWorkCenterDataController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/KingdeeWorkCenterDataController.java index 14297c0..3feff7d 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/KingdeeWorkCenterDataController.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/KingdeeWorkCenterDataController.java @@ -476,28 +476,28 @@ public class KingdeeWorkCenterDataController extends BaseController { // 转入 long transInQty = item.getLong("FTransInQty"); //转出小于转入数量 - if (transOutQty new HashMap<>()) - .put(materialCode, stock); + .put(materialCode, stock); break; } } @@ -706,9 +706,9 @@ public class KingdeeWorkCenterDataController extends BaseController { // 查找对应的安全库存配置 SafetyStock matchingSafetyStock = safetyStocks.stream() - .filter(s -> s.getMaterialCode().equals(materialCode)) - .findFirst() - .orElse(null); + .filter(s -> s.getMaterialCode().equals(materialCode)) + .findFirst() + .orElse(null); if (matchingSafetyStock == null) { log.warn("物料 {} 未找到对应的安全库存配置", materialCode); @@ -744,14 +744,14 @@ public class KingdeeWorkCenterDataController extends BaseController { int result = baseMapper.insert(newStock); if (result > 0) { log.info("成功补充物料到数据库: 物料编码={}, 组={}, 使用模板物料={}", - materialCode, groupType, template.getMaterialCode()); + materialCode, groupType, template.getMaterialCode()); allStocks.add(newStock); } else { log.error("补充物料到数据库失败: 物料编码={}, 组={}", materialCode, groupType); } } catch (Exception e) { log.error("补充物料到数据库失败: 物料编码={}, 组={}, 错误信息={}", - materialCode, groupType, e.getMessage(), e); + materialCode, groupType, e.getMessage(), e); } } } @@ -763,6 +763,7 @@ public class KingdeeWorkCenterDataController extends BaseController { log.info("物料处理完成,最终物料总数: {}", allStocks.size()); return allStocks; } + @Log(title = "金蝶安全库存数据", businessType = BusinessType.OTHER) @XxlJob("getKuCunTo40SB") public R getKuCunTo40SB() { @@ -775,13 +776,13 @@ public class KingdeeWorkCenterDataController extends BaseController { String materialCode = "40SB/L"; // 获取即时库存 double inventoryQty = Optional.ofNullable(JdUtil.getKuCun(materialCode)) - .map(list ->list.stream() + .map(list -> list.stream() .mapToDouble(JDInventoryDTO::getFBaseQty) .sum()) .orElse(0.0); // 查询子项物料的未领料量 - double fNoPickedQty = Optional.ofNullable(JdUtil.getWeiLingliao(materialCode)) - .map(list ->list.stream() + double fNoPickedQty = Optional.ofNullable(JdUtil.getWeiLingliao(materialCode)) + .map(list -> list.stream() .mapToDouble(JDProductionDTO::getFNoPickedQty) .sum()) .orElse(0.0); @@ -818,9 +819,8 @@ public class KingdeeWorkCenterDataController extends BaseController { .append("> **创建时间:** ").append(DateUtil.formatDateTime(new Date())).append("\n"); - - String part = markdownMsg.toString(); - wxRobotUtil.sendMarkdownMsgToWeChatGroup(part, robotId); + String part = markdownMsg.toString(); + wxRobotUtil.sendMarkdownMsgToWeChatGroup(part, robotId); return R.ok(); } @@ -1011,10 +1011,10 @@ public class KingdeeWorkCenterDataController extends BaseController { for (PurchaseRequestExcelDTO allOrder : allOrders) { String productionOrderNo = allOrder.getFUCHNText(); String deliveryDate = allOrder.getFArrivalDate(); - if (allOrder.getFCloseStatus().equals("A")){ + if (allOrder.getFCloseStatus().equals("A")) { allOrder.setFCloseStatus("未关闭"); } - if (allOrder.getFDocumentStatus().equals("C")){ + if (allOrder.getFDocumentStatus().equals("C")) { allOrder.setFDocumentStatus("已审核"); } @@ -1048,6 +1048,7 @@ public class KingdeeWorkCenterDataController extends BaseController { log.info("过滤完成,剩余数量: {}", filteredOrders.size()); return filteredOrders; } + @Log(title = "采购订单和采购申请单临期数据") @XxlJob("getPurchaseOrder2") @PostMapping("/getPurchaseOrder2") diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/PcRigidChainController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/PcRigidChainController.java index e12af75..f5f0032 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/PcRigidChainController.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/PcRigidChainController.java @@ -119,7 +119,7 @@ public class PcRigidChainController extends BaseController { } - @SaCheckPermission("system:pcRigidChain:importVariableData')") + @SaCheckPermission("system:pcRigidChain:importVariableData") @PostMapping("/importVariableData") public R> importVariableData(@RequestParam("file") MultipartFile file) throws Exception { if (file == null || file.isEmpty()) { @@ -131,8 +131,10 @@ public class PcRigidChainController extends BaseController { if (originalFilename == null || originalFilename.isEmpty()) { return R.fail("文件名不能为空"); } - String type = ""; + log.info("开始处理文件: {}, 文件大小: {} bytes", originalFilename, fileBytes.length); + + String type = ""; if (originalFilename.contains("30D")) { type = "30D"; } else if (originalFilename.contains("30S")) { @@ -153,28 +155,45 @@ public class PcRigidChainController extends BaseController { type = "125R"; } + log.info("检测到文件类型: {}", type); + //每个产品都会有 左,右,左+右 - List AxialDirection = Arrays.asList("R", "L", "L+R"); + List axialDirection = Arrays.asList("R", "L", "L+R"); ExcelReaderBuilder read = EasyExcel.read(new ByteArrayInputStream(fileBytes)); - List pcRigidChainsToUpdate = null; - for (ReadSheet readSheet : read.build().excelExecutor().sheetList()) { + List pcRigidChainsToUpdate = new ArrayList<>(); + + List sheetList = read.build().excelExecutor().sheetList(); + log.info("Excel文件包含 {} 个工作表", sheetList.size()); + + for (ReadSheet readSheet : sheetList) { + log.info("正在处理工作表: {} (第{}页)", readSheet.getSheetName(), readSheet.getSheetNo() + 1); + DefaultExcelListener excelListener = new DefaultExcelListener<>(true); EasyExcel.read(new ByteArrayInputStream(fileBytes), PcRigidChainVo.class, excelListener) .excelType(ExcelTypeEnum.XLS) .sheet(readSheet.getSheetNo()) - .headRowNumber(3) + .headRowNumber(4) .doRead(); List list = excelListener.getExcelResult().getList(); - pcRigidChainsToUpdate = new ArrayList<>(); + log.info("工作表 {} 解析完成,共读取 {} 行数据", readSheet.getSheetName(), list.size()); // 批量查询数据库中的记录 + log.debug("开始查询数据库中类型为 {} 的记录", type); List chains = iPcRigidChainService.selectPcRigidChainByType(type); + log.info("数据库中共找到 {} 条类型为 {} 的记录", chains.size(), type); + Map chainMap = chains.stream() .collect(Collectors.toMap(c -> c.getTypeName() + "_" + c.getAxialDirection(), c -> c)); + log.debug("数据库记录映射表构建完成,共 {} 个键值对", chainMap.size()); - for (String s : AxialDirection) { + int processedCount = 0; + int matchedCount = 0; + + for (String s : axialDirection) { + log.debug("开始处理轴向方向: {}", s); for (PcRigidChainVo pcRigidChainVO : list) { + processedCount++; Long vOne = pcRigidChainVO.getVOne(); String sheetName = readSheet.getSheetName(); String box = ""; @@ -187,40 +206,139 @@ public class PcRigidChainController extends BaseController { } String typeName = type + "/" + s + "-" + vOne + "/" + box; + String searchKey = typeName + "_" + s; - log.debug("此物料的完整图号:=====================>{}", typeName); + log.debug("处理记录 {}: 完整图号={}, 搜索键={}", processedCount, typeName, searchKey); // 从缓存中查找 - PcRigidChain dbChain = chainMap.get(typeName + "_" + s); + PcRigidChain dbChain = chainMap.get(searchKey); if (dbChain != null) { + matchedCount++; + log.info("找到匹配记录: {}", typeName); + + // 记录修改前的关键字段值 + log.info("修改前字段值:"); + log.info(" - ID: {}", dbChain.getId()); + log.info(" - Journey: {} -> {}", dbChain.getJourney(), vOne); + log.info(" - TypeName: {} -> {}", dbChain.getTypeName(), typeName); + log.info(" - Type: {} -> {}", dbChain.getType(), type); + log.info(" - Box: {} -> {}", dbChain.getBox(), box); + log.info(" - AxialDirection: {} -> {}", dbChain.getAxialDirection(), s); + + // 记录Excel中的关键字段值 + log.info("Excel数据关键字段:"); + log.info(" - VOne: {}", pcRigidChainVO.getVOne()); + log.info(" - VTwo: {}", pcRigidChainVO.getVTwo()); + log.info(" - VThree: {}", pcRigidChainVO.getVThree()); + log.info(" - LOne: {}", pcRigidChainVO.getLOne()); + log.info(" - LTwo: {}", pcRigidChainVO.getLTwo()); + log.info(" - LThree: {}", pcRigidChainVO.getLThree()); + log.info(" - SumWeight: {}", pcRigidChainVO.getSumWeight()); + log.info(" - ChainWeight: {}", pcRigidChainVO.getChainWeight()); + log.info(" - DynamicLoad: {}", pcRigidChainVO.getDynamicLoad()); + log.info(" - DeadLoad: {}", pcRigidChainVO.getDeadLoad()); + log.info(" - Speed: {}", pcRigidChainVO.getSpeed()); + log.info(" - Efficiency: {}", pcRigidChainVO.getEfficiency()); + log.info(" - ChainPitch: {}", pcRigidChainVO.getChainPitch()); + log.info(" - PitchRadius: {}", pcRigidChainVO.getPitchRadius()); + log.info(" - MinimumAltitude: {}", pcRigidChainVO.getMinimumAltitude()); + log.info(" - SingleMeterChainWeight: {}", pcRigidChainVO.getSingleMeterChainWeight()); + log.info(" - DrivingBoxWeight: {}", pcRigidChainVO.getDrivingBoxWeight()); + log.info(" - ChainBoxWeight: {}", pcRigidChainVO.getChainBoxWeight()); + log.info(" - Univalence: {}", pcRigidChainVO.getUnivalence()); + + // 执行字段复制和更新 BeanUtil.copyProperties(pcRigidChainVO, dbChain, "id"); dbChain.setJourney(vOne); dbChain.setTypeName(typeName); dbChain.setType(type); dbChain.setBox(box); dbChain.setAxialDirection(s); - // dbChain.setUpdateBy(SecurityUtils.getUsername()); dbChain.setCreateTime(new Date()); + dbChain.setUpdateTime(new Date()); + + // 记录修改后的关键字段值 + log.info("修改后字段值:"); + log.info(" - Journey: {}", dbChain.getJourney()); + log.info(" - TypeName: {}", dbChain.getTypeName()); + log.info(" - Type: {}", dbChain.getType()); + log.info(" - Box: {}", dbChain.getBox()); + log.info(" - AxialDirection: {}", dbChain.getAxialDirection()); + log.info(" - LOne: {}", dbChain.getLOne()); + log.info(" - LTwo: {}", dbChain.getLTwo()); + log.info(" - LThree: {}", dbChain.getLThree()); + log.info(" - SumWeight: {}", dbChain.getSumWeight()); + log.info(" - ChainWeight: {}", dbChain.getChainWeight()); + log.info(" - DynamicLoad: {}", dbChain.getDynamicLoad()); + log.info(" - DeadLoad: {}", dbChain.getDeadLoad()); + log.info(" - Speed: {}", dbChain.getSpeed()); + log.info(" - Efficiency: {}", dbChain.getEfficiency()); + log.info(" - ChainPitch: {}", dbChain.getChainPitch()); + log.info(" - PitchRadius: {}", dbChain.getPitchRadius()); + log.info(" - MinimumAltitude: {}", dbChain.getMinimumAltitude()); + log.info(" - SingleMeterChainWeight: {}", dbChain.getSingleMeterChainWeight()); + log.info(" - DrivingBoxWeight: {}", dbChain.getDrivingBoxWeight()); + log.info(" - ChainBoxWeight: {}", dbChain.getChainBoxWeight()); + log.info(" - Univalence: {}", dbChain.getUnivalence()); + log.info(" - CreateTime: {}", dbChain.getCreateTime()); + log.info(" - UpdateTime: {}", dbChain.getUpdateTime()); + pcRigidChainsToUpdate.add(dbChain); - } - } - } - - if (!pcRigidChainsToUpdate.isEmpty()) { - - for (PcRigidChain pcRigidChain : pcRigidChainsToUpdate) { - int i = pcRigidChainMapper.updateById(pcRigidChain); - if (i > 0) { - log.debug("物料:{}更新成功!!", pcRigidChain.getTypeName()); + log.info("物料: {} 已添加到更新列表", typeName); } else { - log.debug("物料:{}更新失败!!", pcRigidChain.getTypeName()); + log.warn("未找到对应的数据库记录: {}", searchKey); } } - } else { - return R.fail("没有找到要更新的数据"); } + + log.info("工作表 {} 处理完成 - 处理记录数: {}, 匹配成功数: {}", + readSheet.getSheetName(), processedCount, matchedCount); } + if (!pcRigidChainsToUpdate.isEmpty()) { + log.info("开始批量更新数据库,共 {} 条记录", pcRigidChainsToUpdate.size()); + int successCount = 0; + int failCount = 0; + + for (PcRigidChain pcRigidChain : pcRigidChainsToUpdate) { + log.info("正在更新物料: {}, ID: {}", pcRigidChain.getTypeName(), pcRigidChain.getId()); + // 记录更新前的关键字段值 + log.info("更新前数据库字段值:"); + log.info(" - Journey: {}", pcRigidChain.getJourney()); + log.info(" - LOne: {}", pcRigidChain.getLOne()); + log.info(" - LTwo: {}", pcRigidChain.getLTwo()); + log.info(" - LThree: {}", pcRigidChain.getLThree()); + log.info(" - SumWeight: {}", pcRigidChain.getSumWeight()); + log.info(" - ChainWeight: {}", pcRigidChain.getChainWeight()); + log.info(" - DynamicLoad: {}", pcRigidChain.getDynamicLoad()); + log.info(" - DeadLoad: {}", pcRigidChain.getDeadLoad()); + log.info(" - Speed: {}", pcRigidChain.getSpeed()); + log.info(" - Efficiency: {}", pcRigidChain.getEfficiency()); + log.info(" - ChainPitch: {}", pcRigidChain.getChainPitch()); + log.info(" - PitchRadius: {}", pcRigidChain.getPitchRadius()); + log.info(" - MinimumAltitude: {}", pcRigidChain.getMinimumAltitude()); + log.info(" - SingleMeterChainWeight: {}", pcRigidChain.getSingleMeterChainWeight()); + log.info(" - DrivingBoxWeight: {}", pcRigidChain.getDrivingBoxWeight()); + log.info(" - ChainBoxWeight: {}", pcRigidChain.getChainBoxWeight()); + log.info(" - Univalence: {}", pcRigidChain.getUnivalence()); + + int i = pcRigidChainMapper.updateById(pcRigidChain); + if (i > 0) { + successCount++; + log.info("物料:{} 更新成功!影响行数: {}", pcRigidChain.getTypeName(), i); + } else { + failCount++; + log.warn("物料:{} 更新失败!影响行数: {}", pcRigidChain.getTypeName(), i); + } + } + + log.info("批量更新完成 - 成功: {} 条, 失败: {} 条", successCount, failCount); + } else { + log.warn("没有找到要更新的数据"); + return R.fail("没有找到要更新的数据"); + } + + log.info("Excel导入处理完成,共处理 {} 条记录", pcRigidChainsToUpdate.size()); return R.ok("导入成功", pcRigidChainsToUpdate); } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessOrderProController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessOrderProController.java index 20806f2..964faee 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessOrderProController.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessOrderProController.java @@ -3,12 +3,14 @@ package com.ruoyi.system.controller; import java.io.*; import java.math.BigDecimal; import java.net.URLEncoder; +import java.text.SimpleDateFormat; import java.util.*; import com.alibaba.excel.EasyExcel; import com.ruoyi.common.excel.DefaultExcelListener; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.VersionComparator; import com.ruoyi.common.utils.file.SmbUtil; import com.ruoyi.common.poi.ExcelTemplateProc; import com.ruoyi.common.poi.DynamicDataMapping; @@ -60,16 +62,13 @@ import org.springframework.web.multipart.MultipartFile; @Slf4j @RequestMapping("/system/orderPro") public class ProcessOrderProController extends BaseController { - private final IProcessOrderProService iProcessOrderProService; @Autowired - private final ProductionOrderServiceImpl productionOrderService; private final IMrpResultCheckService iMrpResultCheckService; - private final IBomDetailsService iBomDetailsService; private final ProcessOrderProMapper processOrderProMapper; private final IImMaterialService imMaterialService; private final ISafetyStockService iSafetyStockService; - + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy年MM月dd日"); /** * 查询项目令号列表 */ @@ -344,7 +343,8 @@ public class ProcessOrderProController extends BaseController { public List getOverdueProjects() { return iProcessOrderProService.getOverdueProjects(); } - + @SaCheckPermission("system:orderPro:geMRPResults") + @Log(title = "获取MRP复核结果", businessType = BusinessType.OTHER) @PostMapping("/getMRPResults/{id}") public R> geMRPResults(@PathVariable Long id) { return iMrpResultCheckService.getMRPResults(id); @@ -371,7 +371,7 @@ public class ProcessOrderProController extends BaseController { // 1. 读取第一个sheet的数据list - 使用POI直接读取以保留空格 List allDataList = readExcelWithPOI(excelName); - + List routeList = readExcelPOIRoute(excelName); // 2. 读取原始表数据 List rawDataList = readRawDataTable(rawDataFile); @@ -391,7 +391,7 @@ public class ProcessOrderProController extends BaseController { if (materialCode != null) { String drawingNo = item.getDrawingNo(); String drawingName = item.getDrawingName(); - if (drawingNo != null && drawingName != null) { + if (drawingName != null) { ImMaterial material = imMaterialService.selectByCodeAndName(drawingNo, drawingName); if (material != null) { //判断是否是VMI物料 @@ -406,7 +406,7 @@ public class ProcessOrderProController extends BaseController { existingVmi.setQuantity(currentQuantity + itemQuantity); Integer currentBatchQuantity = existingVmi.getBatchQuantity() != null ? existingVmi.getBatchQuantity() : 0; - Integer itemBatchQuantity = item.getBatchQuantity() != null ? Integer.valueOf(item.getBatchQuantity()) : 0; + Integer itemBatchQuantity = item.getBatchQuantity() != null ? Integer.parseInt(item.getBatchQuantity()) : 0; existingVmi.setBatchQuantity(currentBatchQuantity + itemBatchQuantity); found = true; break; @@ -431,15 +431,44 @@ public class ProcessOrderProController extends BaseController { || materialCode.startsWith("009001") || materialCode.startsWith("009081") || (remark != null && remark.contains("外购"))) { // 过滤安全库存:如果属于安全库存,则进入工艺数据列表 - Boolean isSafeStock = iSafetyStockService.isSafeCode(materialCode); + Boolean isSafeStock = iSafetyStockService.isSafeCode(materialCode.trim()); if (isSafeStock) { // 属于安全库存,添加到工艺数据列表 processDataList.add(item); continue; // 已分类,跳过后续检查 } else { - // 不属于安全库存,添加到电气外包列表 - ElecOutDataVO elecData = convertToElecOutDataVO(item); - elecOutList.add(elecData); + // 不属于安全库存,检查是否已存在相同的DrawingNo + boolean found = false; + for (ElecOutDataVO existingElec : elecOutList) { + if (item.getDrawingNo() != null && item.getDrawingNo().equals(existingElec.getDrawingNo())) { + // 将数量和批次数量相加 + Double currentQuantity = existingElec.getQuantity() != null ? existingElec.getQuantity() : 0.0; + Double itemQuantity = item.getQuantity() != null ? item.getQuantity() : 0.0; + Double newQuantity = currentQuantity + itemQuantity; + existingElec.setQuantity(newQuantity); + + // 批次数量相加(String类型) + String currentBatchQuantity = existingElec.getBatchQuantity() != null ? (existingElec.getBatchQuantity()).toString() : "0"; + String itemBatchQuantity = item.getBatchQuantity() != null ? item.getBatchQuantity() : "0"; + try { + Integer currentBatch = Integer.valueOf(currentBatchQuantity); + Integer itemBatch = Integer.valueOf(itemBatchQuantity); + String newBatchQuantity = String.valueOf(currentBatch + itemBatch); + existingElec.setBatchQuantity(Integer.valueOf(newBatchQuantity)); + } catch (NumberFormatException e) { + // 如果转换失败,保持原值 + existingElec.setBatchQuantity(Integer.valueOf(currentBatchQuantity)); + } + found = true; + break; + } + } + + // 如果没有找到相同的DrawingNo,则添加新的电气外包数据 + if (!found) { + ElecOutDataVO elecData = convertToElecOutDataVO(item); + elecOutList.add(elecData); + } continue; // 已分类,跳过后续检查 } } @@ -463,7 +492,7 @@ public class ProcessOrderProController extends BaseController { // 检查是否已存在相同的DrawingNo boolean found = false; for (ProductionOrderVo existingProcess : processDataList) { - if (materialCode != null && materialCode.equals(existingProcess.getDrawingNo())) { + if (item.getDrawingNo() != null && item.getDrawingNo().equals(existingProcess.getDrawingNo())) { // 将数量和批次数量相加 Double currentQuantity = existingProcess.getQuantity() != null ? existingProcess.getQuantity() : 0.0; Double itemQuantity = item.getQuantity() != null ? item.getQuantity() : 0.0; @@ -491,12 +520,12 @@ public class ProcessOrderProController extends BaseController { if (!found) { processDataList.add(item); } - List excelDTOList = iProcessOrderProService.getRouteAndBomDetail( processDataList,orderPro); + } // 使用Excel模板文件 - String templatePath = "EXCEL模板/生产及工艺计划模版.xlsx"; + String templatePath = "D:/java/excel-template/生产及工艺计划模版.xlsx"; String outputPath = "D:/file/" + orderPro.getProductionOrderNo() + "生产及工艺计划表.xlsx"; // 准备模板数据 @@ -504,9 +533,29 @@ public class ProcessOrderProController extends BaseController { staticDataMap.put("productionOrderNo", orderPro.getProductionOrderNo()); staticDataMap.put("productionName", orderPro.getProductionName()); - // 添加静态数据调试信息 - log.info("静态数据: {}", staticDataMap); - + //获取工艺数据信息 + List excelDTOList = iProcessOrderProService.getRouteAndBomDetail(routeList,processDataList,orderPro); + excelDTOList.sort(Comparator.comparing( + ProcessRouteExcelDTO::getMaterial, + Comparator.nullsLast((m1, m2) -> { + // 总装部件优先 + boolean isTotal1 = "总装部件".equals(m1); + boolean isTotal2 = "总装部件".equals(m2); + + if (isTotal1 && !isTotal2) return -1; + if (!isTotal1 && isTotal2) return 1; + if (isTotal1 && isTotal2) return 0; + + // 其他材质按字母顺序排序 + if (m1 == null && m2 == null) return 0; + if (m1 == null) return 1; + if (m2 == null) return -1; + return m1.compareTo(m2); + }) + ).thenComparing( + ProcessRouteExcelDTO::getMaterialCode, + Comparator.nullsLast(new VersionComparator()) + )); // 准备动态数据映射 List dynamicDataMappingList = new ArrayList<>(); @@ -551,6 +600,11 @@ public class ProcessOrderProController extends BaseController { List> evoDataList = convertEVOProductsDataToMapList(evoProductsList); dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("EVOProductsDataVO", evoDataList)); } + // 添加伊特产品数据 + if (!excelDTOList.isEmpty()) { + List> evoRouteDataList = convertRouteDataToMapList(excelDTOList); + dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("ProcessRouteExcelDTO", evoRouteDataList)); + } // 使用模板导出Excel ExcelTemplateProc.doExportExcelByTemplateProc(templatePath, outputPath, staticDataMap, dynamicDataMappingList); @@ -584,6 +638,44 @@ public class ProcessOrderProController extends BaseController { } } + private List readExcelPOIRoute(String excelName) { + List resultList = new ArrayList<>(); + + try (FileInputStream fis = new FileInputStream(excelName); + XSSFWorkbook workbook = new XSSFWorkbook(fis)) { + + XSSFSheet sheet = workbook.getSheetAt(6); // 读取第一个sheet + + // 从第3行开始读取(headRowNumber=2,所以从第3行开始) + for (int rowIndex = 2; rowIndex <= sheet.getLastRowNum(); rowIndex++) { + XSSFRow row = sheet.getRow(rowIndex); + if (row == null) { + continue; + } + + ProcessRoute vo = new ProcessRoute(); + + // 根据列索引读取数据,保留原始空格 + vo.setMaterialCode(getCellValueAsString(row.getCell(0))); // 图号 + vo.setMaterialName(getCellValueAsString(row.getCell(1))); // 名称 + vo.setMaterial(getCellValueAsString(row.getCell(2))); // 数量 + vo.setDiscWeight(getCellValueAsDouble(row.getCell(3))); // 单重 + vo.setBatchQuantity(getCellValueAsLong(row.getCell(18))); // 批次数量 + vo.setUnitQuantity(getCellValueAsDouble(row.getCell(17))); // 批次数量 + + resultList.add(vo); + } + + log.info("使用POI读取Excel成功,共{}条记录", resultList.size()); + + } catch (Exception e) { + log.error("使用POI读取Excel失败", e); + throw new ServiceException("读取Excel文件失败: " + e.getMessage()); + } + + return resultList; + } + /** * 读取RawDataTable数据 */ @@ -850,7 +942,7 @@ public class ProcessOrderProController extends BaseController { elecData.setIndex(null); // ProductionOrder没有index字段 elecData.setDrawingNo(item.getDrawingNo()); elecData.setName(item.getDrawingName()); - elecData.setQuantity(item.getQuantity() != null ? item.getQuantity().intValue() : null); + elecData.setQuantity(Double.valueOf(item.getQuantity() != null ? item.getQuantity().intValue() : null)); elecData.setMaterial(item.getMaterial()); elecData.setUnitWeight(item.getSingleWeight() != null ? BigDecimal.valueOf(item.getSingleWeight()) : null); elecData.setTotalWeight(item.getTotalWeight() != null ? BigDecimal.valueOf(item.getTotalWeight()) : null); @@ -984,14 +1076,16 @@ public class ProcessOrderProController extends BaseController { } return mapList; } - + private String formatDate(Date date) { + return date == null ? "" : DATE_FORMAT.format(date); + } /** * 转换工艺VO为Map列表(用于模板) */ - private List> convertRouteDataToMapList(List routeDataList) { + private List> convertRouteDataToMapList(List routeDataList) { List> mapList = new ArrayList<>(); int index = 1; - for (ProcessRouteVo item : routeDataList) { + for (ProcessRouteExcelDTO item : routeDataList) { Map map = new HashMap<>(); map.put("index", index); map.put("routeDescription", item.getRouteDescription()); // 生产令号 @@ -1015,10 +1109,10 @@ public class ProcessOrderProController extends BaseController { map.put("unitQuantity", item.getUnitQuantity()); // 单台数量 map.put("batchQuantity", item.getBatchQuantity()); // 本批数量 map.put("firstBatchQuantity", item.getFirstBatchQuantity()); // 首批数量 - map.put("planStartTime", item.getPlanStartTime()); // 计划开始时间 - map.put("planEndTime", item.getPlanEndTime()); // 计划结束时间 - map.put("xuStartTime", item.getXuStartTime()); // 序开始时间 - map.put("xuEndTime", item.getXuEndTime()); // 序结束时间 + map.put("planStartTime", formatDate(item.getPlanStartTime())); + map.put("planEndTime", formatDate(item.getPlanEndTime())); + map.put("xuStartTime", formatDate(item.getXuStartTime())); + map.put("xuEndTime", formatDate(item.getXuEndTime())); mapList.add(map); index++; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessRouteController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessRouteController.java index c852fb9..f15f33b 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessRouteController.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ProcessRouteController.java @@ -27,6 +27,7 @@ import com.ruoyi.system.domain.vo.*; import com.ruoyi.system.jdmain.rouplan.Model; import com.ruoyi.system.mapper.BomDetailsMapper; import com.ruoyi.system.mapper.MaterialBomMapper; +import com.ruoyi.system.mapper.ProcessOrderProMapper; import com.ruoyi.system.mapper.ProcessRouteMapper; import com.ruoyi.system.runner.JdUtil; import com.ruoyi.system.service.IBomDetailsService; @@ -70,6 +71,7 @@ public class ProcessRouteController extends BaseController { @Autowired MaterialBomMapper materialBomMapper; private final IBomDetailsService iBomDetailsService; + private final ProcessOrderProMapper proMapper; private Long generateUniqueParentId(Long originalId) { return originalId + 1000; } @@ -393,7 +395,7 @@ public class ProcessRouteController extends BaseController { if (!route.isEmpty()){ return R.fail("项目 "+productionOrderNo+"已导入过工艺 ,请先清空再上传"); } - if (iProcessRouteService.saveData(result.getList(), list)) { + if (iProcessRouteService.saveData(list1, list)) { return R.ok("上传物料成功"); } else { return R.fail("导入失败"); @@ -475,8 +477,9 @@ public class ProcessRouteController extends BaseController { public void generatePDFs(String rooteProdet, HttpServletResponse response) throws IOException { // 调用服务层方法生成 ZIP 文件并获取其路径 String zipFilePath = iProcessRouteService.generatePDFs(rooteProdet); - System.out.println("ZIP 文件路径: " + zipFilePath); - + ProcessOrderPro processOrderPro = proMapper.selectByProjectNumber(rooteProdet); + processOrderPro.setBomStatus(5L); + proMapper.updateById(processOrderPro); // 读取文件为字节数组 File zipFile = new File(zipFilePath); if (!zipFile.exists()) { diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/BomDetails.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/BomDetails.java index c482c5c..73ea2a7 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/BomDetails.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/BomDetails.java @@ -36,16 +36,19 @@ public class BomDetails extends BaseEntity { */ @JsonProperty("F_HBYT_BJMC") private String partdiagramName; + /** * 父级物料编码 */ @JsonProperty("FMATERIALID.FNumber") private String fNumber; + /** * 父级物料名称 */ @JsonProperty("FITEMNAME") private String fName; + /** * 子项物料单位 */ @@ -57,11 +60,13 @@ public class BomDetails extends BaseEntity { */ @JsonProperty("FMATERIALIDCHILD.FNumber") private String partNumber; + /** *子项物料名称 */ @JsonProperty("FCHILDITEMNAME") private String name; + /** *属性 */ diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/ImMaterial.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/ImMaterial.java index b3fce81..27851fe 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/ImMaterial.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/ImMaterial.java @@ -86,7 +86,8 @@ public class ImMaterial extends BaseEntity { /** * 搜索次数 */ - private Long searchCount; + @JsonProperty("F_HBYT_PP") + private String searchCount; /** * 金蝶修改时间当日 */ diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/KindegeeLogDTO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/KindegeeLogDTO.java new file mode 100644 index 0000000..ae3f6f9 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/KindegeeLogDTO.java @@ -0,0 +1,12 @@ +package com.ruoyi.system.domain.dto; + +import lombok.Data; + +@Data +public class KindegeeLogDTO { + private String projectCode; + private String materialCode; + private String materialName; + private String code; + private String reason; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/ProcessRouteExcelDTO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/ProcessRouteExcelDTO.java index b3d5e5f..b889538 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/ProcessRouteExcelDTO.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/ProcessRouteExcelDTO.java @@ -6,8 +6,6 @@ import java.util.Date; @Data public class ProcessRouteExcelDTO { - - private Long id; /** @@ -35,7 +33,7 @@ public class ProcessRouteExcelDTO { /** * 单重KG */ - private Double discWeight; + private String discWeight; /** * 材料BOM物料编码 */ @@ -59,7 +57,7 @@ public class ProcessRouteExcelDTO { /** * 材料BOM用量 */ - private Double discUsage; + private String discUsage; /** * 材料BOM单位 */ @@ -103,13 +101,13 @@ public class ProcessRouteExcelDTO { /** * 单台数量 */ - private Long unitQuantity; + private String unitQuantity; /** * 本批数量 */ - private Long batchQuantity; + private String batchQuantity; /** * 首批数量 diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/EleMaterialsVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/EleMaterialsVo.java index 0755bec..cfea510 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/EleMaterialsVo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/EleMaterialsVo.java @@ -73,7 +73,7 @@ public class EleMaterialsVo { /** * 物料值 */ - @ExcelProperty(value = "总工时") + @ExcelProperty(value = "工时") private String materialValue; diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ElecOutDataVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ElecOutDataVO.java index 5bfc17b..25f0adf 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ElecOutDataVO.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ElecOutDataVO.java @@ -21,7 +21,7 @@ public class ElecOutDataVO { private String name; /** 数量 */ - private Integer quantity; + private Double quantity; /** 材料 */ private String material; diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ElectricalMaterialBomVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ElectricalMaterialBomVO.java new file mode 100644 index 0000000..9edc78e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ElectricalMaterialBomVO.java @@ -0,0 +1,93 @@ +package com.ruoyi.system.domain.vo; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; +/** + * 电气物料BOM + * 2025-09-25 + */ +@Data +public class ElectricalMaterialBomVO { + + /** + * ID + */ + private Long id; + + /** + * 生产令号 + */ + @ExcelProperty(value = "生产令号") + private String productionOrderNo; + + /** + * 生产图号 + */ + @ExcelProperty(value = "物料编码") + private String drawingNo; + + /** + * 生产名称 + */ + @ExcelProperty(value = "物料名称") + private String drawingName; + /** + * 型号 + */ + @ExcelProperty(value = "型号") + private String Model; + + /** + * 品牌 + */ + @ExcelProperty(value = "品牌") + private Double Brand; + + /** + * 单台数量 + */ + @ExcelProperty(value = "单台数量") + private Double quantity; + /** + * 批次数量 + */ + @ExcelProperty(value = "批次数量") + private String batchQuantity; + + /** + * 单位 + */ + @ExcelProperty(value = "单位") + private Double unit; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + + /** + * 部件名称 + */ + @ExcelProperty(value = "上级部件") + private String parentPart; + + /** + * 部件图号 + */ + @ExcelProperty(value = "上级部件图号") + private String parentDrawingNo; + + /** + * 主产品图号 + */ + @ExcelProperty(value = "主产品图号") + private String mainProducts; + + /** + * 主产品名称 + */ + @ExcelProperty(value = "主产品名称") + private String mainProductsName; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/PcRigidChainVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/PcRigidChainVo.java index f43e034..7d64fd6 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/PcRigidChainVo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/PcRigidChainVo.java @@ -52,7 +52,7 @@ public class PcRigidChainVo extends BaseEntity private String box; /** 行程(mm) */ - @ExcelProperty( "{V1}") + //@ExcelProperty( "{V1}") private Long journey; /** 标记号 */ diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/runner/JdUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/runner/JdUtil.java index ec40e3b..ffb28d6 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/runner/JdUtil.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/runner/JdUtil.java @@ -1528,7 +1528,6 @@ public class JdUtil { needUpdateFields.add("FMATERIALID"); needUpdateFields.add("FStandHourUnitId"); - // 创建 NeedReturnFields 数组 JsonArray needReturnFields = new JsonArray(); needReturnFields.add("SubHeadEntity5_FEntryId"); needReturnFields.add("FStdLaborProcessTime"); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/IProcessOrderProService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/IProcessOrderProService.java index 29861f8..4c25b20 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/IProcessOrderProService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/IProcessOrderProService.java @@ -58,7 +58,7 @@ public interface IProcessOrderProService { List selectProList(ProcessOrderProBo bo); - ProcessOrderProBo selectByProjectNumber(String routeDescription); + ProcessOrderPro selectByProjectNumber(String routeDescription); void batchUpdateProjectTimeRanges(); List selectByProjectNumbers(Set routeDescSet); @@ -78,5 +78,5 @@ public interface IProcessOrderProService { List getOverdueProjects(); - List getRouteAndBomDetail(List processDataList,ProcessOrderPro orderPro); + List getRouteAndBomDetail(List routlist,List processDataList,ProcessOrderPro orderPro); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ImMaterialServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ImMaterialServiceImpl.java index b46a92d..d1569a2 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ImMaterialServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ImMaterialServiceImpl.java @@ -726,7 +726,7 @@ public class ImMaterialServiceImpl implements IImMaterialService { */ @Override public Boolean insertJDMaterial(List materialList) { - if (materialList.size()<0 || materialList.isEmpty()){ + if (materialList.isEmpty()){ return false; } return baseMapper.insertBatch(materialList); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/MrpResultCheckServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/MrpResultCheckServiceImpl.java index 1c63b35..b6093a9 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/MrpResultCheckServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/MrpResultCheckServiceImpl.java @@ -37,11 +37,9 @@ import java.util.*; public class MrpResultCheckServiceImpl implements IMrpResultCheckService { private final MrpResultCheckMapper baseMapper; - private final FigureSaveMapper figureSaveMapper; private final ProcessOrderProMapper processOrderProMapper; private final IMaterialBomService iMaterialBomService; private final IProductionOrderService iProductionOrderService; - private final IBomDetailsService iBomDetailsService; private final IProcessRouteService iProcessRouteService; /** * 查询MRP运算结果复查 @@ -281,7 +279,8 @@ public class MrpResultCheckServiceImpl implements IMrpResultCheckService { baseMapper.insert(vo); } - + processOrderPro.setBomStatus(4L); + processOrderProMapper.updateById(processOrderPro); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ProcessOrderProServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ProcessOrderProServiceImpl.java index aa2e4d8..e75191d 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ProcessOrderProServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ProcessOrderProServiceImpl.java @@ -2,6 +2,7 @@ package com.ruoyi.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.alibaba.fastjson.JSONObject; +import java.math.BigDecimal; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.PdfReader; @@ -28,13 +29,14 @@ import com.ruoyi.system.domain.vo.ProductionOrderVo; import com.ruoyi.system.listener.FileToZip; import com.ruoyi.system.listener.SmbUtils; import com.ruoyi.system.mapper.FigureSaveMapper; -import com.ruoyi.system.mapper.MaterialBomMapper; import com.ruoyi.system.mapper.ProcessRouteMapper; import com.ruoyi.system.service.*; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.springframework.beans.BeanUtils; +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ruoyi.system.domain.bo.ProcessOrderProBo; @@ -138,6 +140,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService { lqw.eq(bo.getProjectEndTime() != null, ProcessOrderPro::getProjectEndTime, bo.getProjectEndTime()); lqw.eq(StringUtils.isNotBlank(bo.getUnit()), ProcessOrderPro::getUnit, bo.getUnit()); lqw.eq(bo.getQuantity() != null, ProcessOrderPro::getQuantity, bo.getQuantity()); + lqw.eq(bo.getBomStatus() != null, ProcessOrderPro::getBomStatus, bo.getBomStatus()); lqw.eq(bo.getIsEnterpriseStandard() != null, ProcessOrderPro::getIsEnterpriseStandard, bo.getIsEnterpriseStandard()); lqw.eq(bo.getDrawingPath() != null, ProcessOrderPro::getDrawingPath, bo.getDrawingPath()); //按照创建时间排序,最新的在顶端 @@ -245,18 +248,10 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService { * @return */ @Override - public ProcessOrderProBo selectByProjectNumber(String routeDescription) { + public ProcessOrderPro selectByProjectNumber(String routeDescription) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ProcessOrderPro::getProductionOrderNo, routeDescription); - ProcessOrderPro processOrderPro = baseMapper.selectOne(wrapper); - ProcessOrderProBo processOrderProBo = new ProcessOrderProBo(); - - // 添加null检查 - if (processOrderPro != null) { - BeanUtils.copyProperties(processOrderPro, processOrderProBo); - } - - return processOrderProBo; + return baseMapper.selectOne(wrapper); } @@ -288,7 +283,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService { // 更新至计划模块中 if (earliestStartTime != null && latestEndTime != null) { - ProcessOrderProBo processOrderPro = selectByProjectNumber(projectNumber); + ProcessOrderPro processOrderPro = selectByProjectNumber(projectNumber); if (processOrderPro != null) { processOrderPro.setPlanStartTime(earliestStartTime); @@ -359,7 +354,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService { pwProductionBill.setProduct(productInfos); // 6. 调用API - return callDrawingApi(pwProductionBill,orderPro); + return callDrawingApi(pwProductionBill, orderPro); } @@ -533,7 +528,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService { datainfo.setG20(String.valueOf(rigidChain.getGTwenty())); } - private String callDrawingApi(PwProductionBill pwProductionBill,ProcessOrderProBo orderPro) { + private String callDrawingApi(PwProductionBill pwProductionBill, ProcessOrderProBo orderPro) { try { String drawingDate = JSONObject.toJSONString(pwProductionBill); log.info("请求出图报文=====>{}", drawingDate); @@ -557,7 +552,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService { // 判断完成条件processstate=4 表示完成 if ("idle".equals(currentStatus) && processState == 4) { ProcessOrderPro processOrderPro = new ProcessOrderPro(); - BeanUtils.copyProperties(orderPro,processOrderPro); + BeanUtils.copyProperties(orderPro, processOrderPro); processOrderPro.setBomStatus(1L);// baseMapper.updateById(processOrderPro); @@ -1072,7 +1067,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService { OverdueProjectVo projectVo = new OverdueProjectVo(); projectVo.setProjectCode(processOrderPro.getProductionName()); - // projectVo.setDesigner(processOrderPro.getProgramDesigner()); + // projectVo.setDesigner(processOrderPro.getProgramDesigner()); projectVo.setStartDate(processOrderPro.getPlanStartTime().toString()); projectVo.setPlanDate(processOrderPro.getPlanEndTime().toString()); @@ -1106,6 +1101,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService { /** * 递归删除目录及其内容 + * * @param directory 要删除的目录 * @throws IOException 删除失败时抛出异常 */ @@ -1128,21 +1124,170 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService { } } } + /** * 将工艺部分 材料BOM部分 合并成ProcessRouteExcelDTO + * * @return */ @Override - public List getRouteAndBomDetail(List processDataList,ProcessOrderPro orderPro) { - String proRoot = orderPro.getProductionOrderNo(); - //工艺部分 - List processRoutes = iProcessRouteService.selectByProjectNumber(proRoot); - List materialBoms = materialBomService.selectByProCode(proRoot); - for (ProductionOrderVo productionOrderVo : processDataList) { - ProcessRouteExcelDTO processRouteExcelDTO = new ProcessRouteExcelDTO(); + public List getRouteAndBomDetail(List routlist,List processDataList, ProcessOrderPro orderPro) { + String proRoot = orderPro.getProductionOrderNo(); + + // 查出所有工艺路线和BOM + List allRoutes = iProcessRouteService.selectByProjectNumber(proRoot); + List allBoms = materialBomService.selectByProCode(proRoot); + + // 建立 routlist 的 Map 方便查找 + Map routeMap1 = routlist.stream() + .filter(r -> r.getMaterialCode() != null) + .collect(Collectors.toMap(ProcessRoute::getMaterialCode, r -> r)); + + // 合并数据 + for (ProcessRoute route : allRoutes) { + ProcessRoute source = routeMap1.get(route.getMaterialCode()); + if (source != null) { + copyPropertiesIgnoreNull(source, route); // 用 routlist 的非空字段补充 allRoutes + } + } + List resultList = new ArrayList<>(); + + // 规范化 key + Function normalize = s -> s == null ? "" : s.trim().toUpperCase(); + + // 按 materialCode 分组(工艺路线) + Map> routeMap = allRoutes.stream() + .collect(Collectors.groupingBy(r -> normalize.apply(r.getMaterialCode()))); + + // 按 parentMaterialCode 分组(BOM) + Map> bomMap = allBoms.stream() + .collect(Collectors.groupingBy(b -> normalize.apply(b.getParentMaterialCode()))); + + // 把订单数据做成 Map + Map orderMap = processDataList.stream() + .collect(Collectors.toMap(vo -> normalize.apply(vo.getDrawingNo()), vo -> vo, (a, b) -> a)); + + // 合并 material keys + LinkedHashSet materialKeys = new LinkedHashSet<>(); + materialKeys.addAll(routeMap.keySet()); + orderMap.keySet().stream() + .filter(key -> !materialKeys.contains(key)) + .forEach(materialKeys::add); + + for (String key : materialKeys) { + List processRoutes = routeMap.getOrDefault(key, Collections.emptyList()); + List materialBoms = bomMap.getOrDefault(key, Collections.emptyList()); + ProductionOrderVo orderVo = orderMap.get(key); + + // 优先从 processRoutes 取值,其次才用 orderVo,最后才用 BOM 或 key + String originalMaterialCode = !processRoutes.isEmpty() + ? processRoutes.get(0).getMaterialCode().trim() + : (orderVo != null + ? (orderVo.getDrawingNo() != null ? orderVo.getDrawingNo().trim() : null) + : (!materialBoms.isEmpty() + ? (materialBoms.get(0).getParentMaterialCode() != null ? materialBoms.get(0).getParentMaterialCode().trim() : null) + : key)); + + String originalMaterialName = !processRoutes.isEmpty() + ? (processRoutes.get(0).getMaterialName() != null ? processRoutes.get(0).getMaterialName().trim() : null) + : (orderVo != null ? (orderVo.getDrawingName() != null ? orderVo.getDrawingName().trim() : null) : null); + + String originalMaterial = !processRoutes.isEmpty() + ? (processRoutes.get(0).getMaterial() != null ? processRoutes.get(0).getMaterial().trim() : null) + : (orderVo != null ? (orderVo.getMaterial() != null ? orderVo.getMaterial().trim() : null) : null); + + Double originalUnitWeight = !processRoutes.isEmpty() ? processRoutes.get(0).getDiscWeight() : (orderVo != null ? orderVo.getSingleWeight() : null); + // 🔹 新增两个数量字段 + Double unitQuantity = !processRoutes.isEmpty() + ? processRoutes.get(0).getUnitQuantity() + : (orderVo != null ? orderVo.getQuantity() : null); + + Double batchQuantity = !processRoutes.isEmpty() + ? (processRoutes.get(0).getBatchQuantity() != null ? processRoutes.get(0).getBatchQuantity() : 0) + : (orderVo != null + ? (orderVo.getBatchQuantity() != null + ? Double.valueOf(orderVo.getBatchQuantity()) + : null) + : null); + + int maxSize = Math.max(processRoutes.size(), materialBoms.size()); + if (maxSize == 0 && orderVo != null) maxSize = 1; + + for (int i = 0; i < maxSize; i++) { + ProcessRouteExcelDTO dto = new ProcessRouteExcelDTO(); + + // 补充名称/材质/单重字段 + dto.setMaterialCode(originalMaterialCode); + dto.setMaterialName(originalMaterialName); + dto.setMaterial(originalMaterial); + dto.setDiscWeight(String.valueOf(originalUnitWeight)); + dto.setUnitQuantity(String.valueOf(unitQuantity)); + dto.setBatchQuantity(String.valueOf(batchQuantity)); + + // 订单数量信息 + if (orderVo != null) { + dto.setRouteDescription(orderVo.getProductionOrderNo()); + dto.setUnitQuantity(String.valueOf(orderVo.getQuantity())); + dto.setBatchQuantity(orderVo.getBatchQuantity()); + } + + // 工艺部分 + if (i < processRoutes.size()) { + ProcessRoute route = processRoutes.get(i); + dto.setProcessNo(route.getProcessNo()); + dto.setProcessName(route.getProcessName()); + dto.setWorkCenter(route.getWorkCenter()); + dto.setProcessDescription(route.getProcessDescription()); + dto.setProcessControl(route.getProcessControl()); + dto.setActivityDuration(route.getActivityDuration()); + dto.setActivityUnit(route.getActivityUnit()); + dto.setXuStartTime(route.getXuStartTime()); + dto.setXuEndTime(route.getXuEndTime()); + } + + // BOM 部分 + if (i < materialBoms.size()) { + MaterialBom bom = materialBoms.get(i); + dto.setRawMaterialCode(bom.getMaterialCode()); + dto.setRawMaterialName(bom.getMaterialName()); + dto.setBomMaterial(bom.getMaterialType()); + dto.setBomUnit(bom.getUnit()); + + // 单位转换:如果单位为mm,则数值乘以1000 + String quantity = bom.getQuantity(); + if ("mm".equals(bom.getUnit()) && quantity != null) { + try { + BigDecimal originalValue = new BigDecimal(quantity); + BigDecimal convertedValue = originalValue.multiply(new BigDecimal("1000")); + dto.setDiscUsage(convertedValue.toString()); + } catch (NumberFormatException e) { + // 如果转换失败,使用原值 + dto.setDiscUsage(quantity); + } + } else { + dto.setDiscUsage(quantity); + } + } + resultList.add(dto); + } } - return Collections.emptyList(); + return resultList; } + public static String[] getNullPropertyNames(Object source) { + BeanWrapper src = new BeanWrapperImpl(source); + java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors(); + + Set emptyNames = new HashSet<>(); + for (java.beans.PropertyDescriptor pd : pds) { + Object srcValue = src.getPropertyValue(pd.getName()); + if (srcValue == null) emptyNames.add(pd.getName()); + } + return emptyNames.toArray(new String[0]); + } + + public static void copyPropertiesIgnoreNull(Object src, Object target) { + BeanUtils.copyProperties(src, target, getNullPropertyNames(src)); + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ProcessRouteServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ProcessRouteServiceImpl.java index 905c436..aaf0fe1 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ProcessRouteServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ProcessRouteServiceImpl.java @@ -569,19 +569,7 @@ public class ProcessRouteServiceImpl implements IProcessRouteService { saveBomDetails(bomDetailsVos); List routeArrayList = new ArrayList<>(); Map inventoryCache = new HashMap<>(); - // 批量查询所有需要的库存信息 - /* for (ProcessRoute processRoute : processRoutes) { - String materialCode = processRoute.getMaterialCode(); - if (!inventoryCache.containsKey(materialCode)) { - try { - Double kucun = JdUtil.getKeyong(materialCode); - inventoryCache.put(materialCode, kucun); - } catch (Exception e) { - log.error("查询库存失败: {}", materialCode, e); - inventoryCache.put(materialCode, 0.0); - } - } - }*/ + boolean allEmpty = processRoutes.stream().allMatch(route -> route.getProcessNo() == null && route.getProcessName() == null); //获取工艺中所有的时间 挑选出最早的时间和最晚的时间 @@ -622,8 +610,7 @@ public class ProcessRouteServiceImpl implements IProcessRouteService { for (ProcessRoute processRoute : processRoutes) { String materialCode = processRoute.getMaterialCode(); Double kucun = inventoryCache.get(materialCode); - boolean isDuplicateWelding = "组焊件".equals(processRoute.getMaterial()) - && duplicateWeldingMaterials.contains(materialCode); + boolean isDuplicateWelding = "组焊件".equals(processRoute.getMaterial()) && duplicateWeldingMaterials.contains(materialCode); if (isDuplicateWelding && processedWeldingMaterials.contains(materialCode)) { continue; @@ -663,7 +650,6 @@ public class ProcessRouteServiceImpl implements IProcessRouteService { defaultRoute.setProcessControl(null); defaultRoute.setActivityDuration(null); defaultRoute.setActivityUnit("分"); - defaultRoute.setFirstBatchQuantity(kucun); defaultRoute.setCreateTime(new Date()); defaultRoute.setUpdateTime(new Date()); routeArrayList.add(defaultRoute); @@ -696,11 +682,14 @@ public class ProcessRouteServiceImpl implements IProcessRouteService { } } else { for (ProcessRoute processRoute : processRoutes) { - if (processRoute.getProcessNo() != null || processRoute.getProcessName() != null) { + if (processRoute.getProcessNo() != null + ||processRoute.getWorkCenter()!= null + ||processRoute.getProcessName() != null + ||processRoute.getMaterial().equals("总装部件")) { processRoute.setActivityUnit("分"); processRoute.setCreateTime(new Date()); processRoute.setUpdateTime(new Date()); - processRoute.setFirstBatchQuantity(processRoute.getFirstBatchQuantity()); + processRoute.setBatchQuantity(processRoute.getBatchQuantity()); processRoute.setUnitQuantity(processRoute.getUnitQuantity()); routeArrayList.add(processRoute); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SafetyStockServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SafetyStockServiceImpl.java index e523efa..0420cfe 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SafetyStockServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SafetyStockServiceImpl.java @@ -158,9 +158,8 @@ public class SafetyStockServiceImpl implements ISafetyStockService { public Boolean isSafeCode(String materialCode) { LambdaQueryWrapper safetyStockLambdaQueryWrapper = new LambdaQueryWrapper<>(); safetyStockLambdaQueryWrapper.eq(SafetyStock::getMaterialCode,materialCode); - SafetyStock safetyStock = baseMapper.selectOne(safetyStockLambdaQueryWrapper); - return safetyStock != null; - + List safetyStocks = baseMapper.selectList(safetyStockLambdaQueryWrapper); + return !safetyStocks.isEmpty(); } /** diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WorkProcedureServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WorkProcedureServiceImpl.java index 054de3c..f92d76a 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WorkProcedureServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WorkProcedureServiceImpl.java @@ -79,7 +79,7 @@ public class WorkProcedureServiceImpl implements IWorkProcedureService { parentTask.setParent(null); // 获取工艺订单信息 - ProcessOrderProBo processOrderProBo = iProcessOrderProService.selectByProjectNumber(processRoute.getRouteDescription()); + ProcessOrderPro processOrderProBo = iProcessOrderProService.selectByProjectNumber(processRoute.getRouteDescription()); Date start = processOrderProBo.getPlanStartTime(); Date end = processOrderProBo.getPlanEndTime(); diff --git a/ruoyi-system/src/main/resources/EXCEL模板/生产及工艺计划模版.xlsx b/ruoyi-system/src/main/resources/EXCEL模板/生产及工艺计划模版.xlsx index b0ff69259ff635fcaa869f9a4da5f45108532c02..82479b538f6c1a7ebf4066c943d2fbce66c3d965 100644 GIT binary patch delta 12601 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 delta 12786 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 diff --git a/ruoyi-system/src/main/resources/EXCEL模板/采购订单模板1.xlsx b/ruoyi-system/src/main/resources/EXCEL模板/采购订单模板1.xlsx index d824baadd77416b913f0d55892dd16d0f6dacb9f..45fe0f5b60b5eb789079e7fba91a29209e6d8eab 100644 GIT binary patch delta 2033 zcmVCVmIfJCuU1+5j8>WKWuyh|zd! z+S%P^X(4Tu<=b0e7uo2+g&6`QHZNd=w}5-pa|?wr$j=Lkypv;Pi$+7P>~5vxdG;CQ``zAwY1Lk zRfcCuf5nPf_v8=0mLTrBV#hkjMDXvv2M+m1Du}=*qGSG}@|NB|lq*yuadH{PHzBzw zNkYkj#_>l#_T+t^aBQf<@c$4OB}u7=rdKoK5l{@dPk%-64YLpf69o$BvQOHC0{{T1 z3X{+T8GkY^cx`NzR#|VOFc5xUY5xQAdjUg^CJL##NzUBmG~*#lJcLaf88q=(5GGp>{rUBne2w66&FM z%F)gA>z7H+vRA&NR2ZNk;U3xvk&Z2k`#TyFZhx|rCa|+%Sxf}HW?PG=86|-t#gXOM z_P~lsn4m(#IJ-m#+iXIg&Y8cDX(9?G8I6bl|M@OVd8O?wxJX}+ksQui`E%mpyx6Jr z;a<=7_9;i!sBzBNZz<{sR!p+jeR>6oQ#jl@j6(6Qj$1iI#Zgb5 z=YM9Na??*rFHQo4hZYsHJr^vV6G6~)qB8I!Ga$|MJBcByFEWu1vZj+Dgt*FLFfzLF z&@CQ6yHc(=e?pq`lV?Ma^$U%tFK7V8gA7>|YsOwBa0Mb;T2lb2k@zA!&>4*)56!Uy zh4oJvkHVhf5R@~KhjFBwhqbbPZ93<(1qGrwu$CO>qXc7^gNh>`D$>xzAEnqTYD-nJ?6&D7 zW4Qs72#QRWr2{?`r>hGU+8`xzfZ{R6&@aA(-vVBWkO3qP(!&%FC)NQv!!IG7LdcdqiNUoNgEquz zB4%xb+eFOUh+Y$M*GAw`6S8PS9J>j*?||SYWZ8xcnut{!;b>VW8J>wp}Sq8j|MfWWY;L+5JIeX1bUP=MQ8W~8q!Sk;WhHCqg) zm}XmbA#?Z`*e9S?PM}&^e*-{E%L}^Rfa^0QTl!40ZJ2N)q8p&v<`7)Viu<>C5iC_h zY|R9GeyGPCd;(IKR<^uiHxP`8>IDoGdkZC$_UdoU>3_4(3!w;q#VCE+Bmw{ca|8eY z4*&oFcx*3sZ*ps5Z*OZZcx`NrRKbqZFc7^X@eh&jt&_CbZL6lMcH4-RwkuRZ3n%0z zlg8S_j%;^RggEbs8~gxw-~bX|geZT3lQi93EkZdtetz@DGjHPI*)C0t8xWFm<|362m3#r2x=YeNe#>7rPvY=E&0Vn~T> z^9-0iiMU9K(p#~?G7~^T83U*^!M16h;FM5?Dusb)qQRqx1~BEpHU*|C5djHN`uZ}a znXJ{qb(!d>nZvrBXR;kI3`Lx6;R6MYh7nC*S@pmmS$0K#QeEjTK}JHPnuSzB=%S9^ z@*M5}2N`0UO|~@Ar~Qs;BRo2`<+(6)7on2!V=7mNeifvmZ%YcfyyF*JBd09n`HF@r z)?PT4t=*`wXFxZxa?xq0r!f*JdILULYgVC$?+KP=YvFP=SXs)S+uQw*FZ-{be|`9* z?JOL)(08_fbO8gZKg2~?>O7A5{qg($+s}t#_PwyOW?bhCND>r<>!sB1Tc+JFE(kky zDOI@|TR~m)ZsPPNW~Xnrx}N2=x>nz8O(rMZmNWG_&Z#v$pE=(9+RR;{7#+Q=XDp?G z;F3qG9q<%aqw#3UtR60-fvBy}!#f&QYJ&px4(Jg_j4I&Jw+9IZi)m5HA^LyB7y9Q( zpdr~?HYA>3&HZ^p?&ZvXv+|mf$MeZ_+?+pNEXJ?>*{eDQ{%1q6_FPY9?L4mEi_u@R zsurIS0dbQ`C_n+OlZz-b0n(GdD3b+ks%{lTlUFHJ0S}X`DL?^glkF)m0s503DmDQj zlT|850Unc`DnS9Vliw;j0nU>eD>ec3lTj-s8^tJn+9U!10CNNY01p5F000000096X z0001Claebu0gIE*D?0+W1d}lc6_X(>6&vWXPuhe7005{8000;O0000000031AOHXW P6q8XbD+VVh00000^(d|5 delta 2046 zcmV{Z&6?! z(}IHOfbiA2Mp_xh)zhsM`ZnJ${rv%h7uHLXK@_G_{ZzODEsGwUYR(R zbZzZ-I>nu`1s}xGeYA2kHcb;Xvp$B;ALIAU(@VcXw4 zA1=w;#p_qIQWTGJ&$)7-A(L~mS1P~uJQ?nJ%A_lDp5cGZRum~ycr13F%nQyULrN3R zAB`@(l*JjTG+Y-i(IK|mIOHo49#WpEN=d;Jrr^Ka$GL2@Jw-3l7Zt3;UK@YTTwGOe zT77if^Dm9VLP)VyZYWZ(a;?A1sWE_YuOq%?heRz!@+*Ii)P7E;q>+6U|Atr&BjL|2^ycWlhK0&?o;vN7{{b ztTliAqU$??@=WA0jtm`ZWBtnYpU)Nw#Bi|I9RFVlrkI0}`Uv0J@rOR-qO0V-kMK`H z>7)$JXv(&2%E7V?+Oh=>DZ{l*nQsO2eFeenIzcS|mIDwg4yyq}R)5?KXtBy?Jpg~P z5_{V>6j+J98-Q4e-3&nNv<*N`Nihw6UqLYJ+Rz17bU!wbW+>qF))~1l2drtv>X@yD zQ_72-Iglkj1`Zk2>Iu|J+iw80v_7EQ7dSq1w&R~zv5Pb5Bzz0%$eqBoqB?)82PJYd z#2$pg=ZCr7;}ei$T8;D(yA?t;4=@Z+y;~chw%2}RF8%_L9<%HVg9v{$@{Q{n0ssJD z1ONaJ0001ZY%h0ja%*C5Z)+}iZETHHO>fgc5WOSuAFRD6>yM-)QJgAnVo|B7P%tT+ z(8eCetJ=HP?xs$KIQPU2{s4F20203lQT_#MJ5E~_A)nSee(%l9o3Y2oyDT-XKuF4& zi>$7R3}7LTDBHN`Rp5Vj2FQ?#u!y9bfs3v|qT|WaC*y*P%Qfehh8AGbMTt`R2xA#0 zkP+GCIWYYu<{~3XU&RK?TmXq=0-&-K+opMhGeQ}v6-J_s29IMJ!i7rdti_}KPP{g?sS(TBPCMJBdQ>B zk)yA?fV)5+8Dg7zTbk;(L&vlco*Zm>Aq?#z)KWo0<;C8w5>)!OqEN^?e0enr$|7Do z2f9g=9){x#^f*YU=oJKHts^BF-(y!jYL)6VSXK9*o15DoUv9sC{`KLLo?U78sV>mc z)sCos5T{Y4^O%40`{Vc9Z$BT>?0dATLEPjFNg9@2>Z8&hTBbcL3xu6Ild3w6t)MP? z=k*7k=?v{o&$s+e&l;K?&pYaM`ZM3@KeJ{h^S=MSncS{aj1Plr7Am8m;F8Cx8}ba- zd+=zbtO2grK-5;~O`VKuwNV-M_t8Tzt^<4D9wZnoXJtJrd-nf;FZGv_LQAl}Y6(0! zUj&Pm*t2=?_QG!qo-VxEv^{>hTu$Ev^VdxZ{7*x%p1I!4X7afCIZXZnv$7VS5dmqF zQYb(HrjwH>GXctz$0(Bp?sVszMU!7CQ~?l^v?)LbYXkrQcx*3|tScOo7b*?`@{=Yi zHUSxvT`ETbAd{skK>@0h=PEh@(32u7HUjqxlMxIRlW!{!8#VHc>ly+80AK_F01p5F z000000096X0001ElbS0$0gRK^D?0+X1d}ib6_Y6}6&z0i|00D0005{8000;O00000 c00031AOHXW6$q0s2pW@gED#1CC;$Ke0E*tSOaK4?