yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/invoice/CrmInvoiceMapper.java
@@ -104,7 +104,7 @@ default PageResult<CrmInvoiceDO> selectPageByCustomerId(CrmInvoicePageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX<CrmInvoiceDO>() .eq(CrmInvoiceDO::getCustomerId, reqVO.getCustomerId()) // 必须传递 .eqIfPresent(CrmInvoiceDO::getNo, reqVO.getNo()) .likeIfPresent(CrmInvoiceDO::getNo, reqVO.getNo()) .eqIfPresent(CrmInvoiceDO::getContractId, reqVO.getContractId()) .orderByDesc(CrmInvoiceDO::getId)); } @@ -116,7 +116,8 @@ CrmInvoiceDO::getId, userId, pageReqVO.getSceneType()); // 拼接自身的查询条件 query.selectAll(CrmInvoiceDO.class) .eqIfPresent(CrmInvoiceDO::getNo, pageReqVO.getNo()) .likeIfPresent(CrmInvoiceDO::getNo, pageReqVO.getNo()) .eqIfPresent(CrmInvoiceDO::getCustomerId, pageReqVO.getCustomerId()) .eqIfPresent(CrmInvoiceDO::getContractId, pageReqVO.getContractId()) .eqIfPresent(CrmInvoiceDO::getAuditStatus, pageReqVO.getAuditStatus()) .orderByDesc(CrmInvoiceDO::getId); yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
@@ -43,7 +43,7 @@ default PageResult<CrmReceivableDO> selectPageByCustomerId(CrmReceivablePageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivableDO>() .eq(CrmReceivableDO::getCustomerId, reqVO.getCustomerId()) // 必须传递 .eqIfPresent(CrmReceivableDO::getNo, reqVO.getNo()) .likeIfPresent(CrmReceivableDO::getNo, reqVO.getNo()) .eqIfPresent(CrmReceivableDO::getContractId, reqVO.getContractId()) .eqIfPresent(CrmReceivableDO::getPlanId, reqVO.getPlanId()) .orderByDesc(CrmReceivableDO::getId)); @@ -56,7 +56,8 @@ CrmReceivableDO::getId, userId, pageReqVO.getSceneType()); // 拼接自身的查询条件 query.selectAll(CrmReceivableDO.class) .eqIfPresent(CrmReceivableDO::getNo, pageReqVO.getNo()) .likeIfPresent(CrmReceivableDO::getNo, pageReqVO.getNo()) .eqIfPresent(CrmReceivableDO::getCustomerId, pageReqVO.getCustomerId()) .eqIfPresent(CrmReceivableDO::getPlanId, pageReqVO.getPlanId()) .eqIfPresent(CrmReceivableDO::getContractId, pageReqVO.getContractId()) .eqIfPresent(CrmReceivableDO::getAuditStatus, pageReqVO.getAuditStatus()) yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
@@ -47,6 +47,7 @@ import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderRespDTO; import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; import cn.iocoder.yudao.module.mdm.enums.MdmItemTypeEnum; import cn.iocoder.yudao.module.system.api.approval.ApprovalConfigApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; @@ -584,16 +585,18 @@ createReqDTO.setRemark("由合同【" + contract.getNo() + "】自动生成"); // 5. 构建订单项 Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap( convertSet(contractProducts, CrmContractProductDO::getItemId)); List<ErpSaleOrderCreateReqDTO.Item> items = contractProducts.stream() .map(cp -> { ErpSaleOrderCreateReqDTO.Item item = new ErpSaleOrderCreateReqDTO.Item(); item.setProductId(cp.getItemId()); item.setProductPrice(cp.getContractPrice()); item.setCount(cp.getCount()); // 合同产品默认需要生产:订单审批通过后才能自动创建 MPS, // 否则「合同 → 订单 → 生产」链路在合同来源的订单上会断掉。 // 若个别产品为外购/转售,可在销售订单明细中把「需要生产」改为否。 item.setNeedProduction(1); // 只有产品才进入生产计划;温控等外购物料可随合同进入销售订单, // 但不能创建 MPS,否则会导致整笔销售订单审批回滚。 MdmItemRespDTO mdmItem = itemMap.get(cp.getItemId()); item.setNeedProduction(mdmItem != null && MdmItemTypeEnum.isProduct(mdmItem.getItemType()) ? 1 : 0); return item; }) .toList(); yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java
@@ -30,6 +30,7 @@ import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; import cn.iocoder.yudao.module.mdm.enums.MdmItemTypeEnum; import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi; import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO; import cn.iocoder.yudao.module.mes.api.mps.MesProMpsApi; @@ -221,8 +222,22 @@ int createdCount = 0; int reusedCount = 0; int skippedCount = 0; for (ErpSaleOrderItemDO item : items) { if (item.getNeedProduction() == null || item.getNeedProduction() != 1) { continue; } MdmItemRespDTO mdmItem = mdmItemApi.getItem(item.getProductId()).getCheckedData(); if (mdmItem == null || !MdmItemTypeEnum.isProduct(mdmItem.getItemType())) { // 兼容修复前由合同生成的历史订单:当时所有明细都会被标记为需要生产。 // 非产品明细不应创建 MPS,同时纠正标记,避免后续发货状态一直等待生产完成。 saleOrderItemMapper.updateById(new ErpSaleOrderItemDO() .setId(item.getId()) .setNeedProduction(0)); skippedCount++; log.info("[createMpsForSaleOrder] 销售订单明细({}) 为非产品物料({}),跳过 MPS 并取消需要生产标记", item.getId(), item.getProductId()); continue; } @@ -241,7 +256,7 @@ } // 与销售订单审核共用事务;任一明细创建失败都会回滚审核状态和本次已创建的 MPS Long mpsId = createMpsForSaleOrderItem(saleOrder, item); Long mpsId = createMpsForSaleOrderItem(saleOrder, item, mdmItem); saleOrderItemMapper.updateById(new ErpSaleOrderItemDO() .setId(item.getId()) .setMpsId(mpsId)); @@ -254,31 +269,21 @@ .set(ErpSaleOrderDO::getProductionStatus, ErpSaleOrderProductionStatusEnum.NOT_FINISHED.getStatus()) .set(ErpSaleOrderDO::getProductionFinishTime, null)); } log.info("[createMpsForSaleOrder] 销售订单({}) 审核完成,创建 MPS {} 条,复用 MPS {} 条", orderId, createdCount, reusedCount); log.info("[createMpsForSaleOrder] 销售订单({}) 审核完成,创建 MPS {} 条,复用 MPS {} 条,跳过非产品 {} 条", orderId, createdCount, reusedCount, skippedCount); } /** * 为销售订单明细创建 MPS */ private Long createMpsForSaleOrderItem(ErpSaleOrderDO saleOrder, ErpSaleOrderItemDO item) { // 获取 MDM 物料信息(用于获取产品名称) String productName = null; try { MdmItemRespDTO mdmItem = mdmItemApi.getItem(item.getProductId()).getCheckedData(); if (mdmItem != null) { productName = mdmItem.getName(); } } catch (Exception e) { log.warn("[createMpsForSaleOrderItem] 获取 MDM 物料信息失败,productId={}", item.getProductId(), e); } private Long createMpsForSaleOrderItem(ErpSaleOrderDO saleOrder, ErpSaleOrderItemDO item, MdmItemRespDTO mdmItem) { MesProMpsCreateReqDTO createReqDTO = new MesProMpsCreateReqDTO(); createReqDTO.setSaleOrderId(saleOrder.getId()); createReqDTO.setSaleOrderNo(saleOrder.getNo()); createReqDTO.setSaleOrderItemId(item.getId()); createReqDTO.setMdmItemId(item.getProductId()); createReqDTO.setProductName(productName); createReqDTO.setProductName(mdmItem.getName()); createReqDTO.setQuantity(item.getCount()); createReqDTO.setClientId(saleOrder.getCustomerId()); createReqDTO.setRequestDate(formatRequestDate(saleOrder.getOrderTime())); yudao-module-mdm-api/src/main/java/cn/iocoder/yudao/module/mdm/enums/MdmItemTypeEnum.java
@@ -39,4 +39,11 @@ return null; } /** * 判断给定类型是否为可生产的产品。 */ public static boolean isProduct(Integer type) { return PRODUCT.getType().equals(type); } }