| sql/mysql/fix_sale_order_mps_link.sql | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/mps/MesProMpsMapper.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
sql/mysql/fix_sale_order_mps_link.sql
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,103 @@ -- ============================================================ -- éå®è®¢å -> ç产主计åï¼MPSï¼é¾è·¯æ°æ®æ£æ¥ä¸ä¿®å¤ -- -- éç¨åºæ¯ï¼éå®è®¢åå·²ç»å®¡æ ¸ï¼ä½å¾éâéè¦ç产âçæç»æ²¡æçæ MPSã -- 代ç åå¸åï¼æ°å®¡æ ¸åæ®ä¼å¨ MPS åå»ºå¤±è´¥æ¶æ´ä½åæ»å¹¶è¿åæç¡®é误ã -- ============================================================ -- 1. æ£æ¥ï¼éè¦ç产çé宿ç»ï¼æ¯å¦å·²ç»åæ¥ä¸º MES 产åã -- mes_item_id 为空表示没æ MES è®°å½ï¼item_or_product 䏿¯ PRODUCT 表示ä¸è½ç¨äº MPSã SELECT so.id AS sale_order_id, so.no AS sale_order_no, soi.id AS sale_order_item_id, mdm.id AS mdm_item_id, mdm.code AS item_code, mdm.name AS item_name, mi.id AS mes_item_id, mi.mdm_item_id AS mes_mdm_item_id, mit.item_or_product AS item_or_product, mi.status AS mes_item_status FROM erp_sale_order so JOIN erp_sale_order_items soi ON soi.order_id = so.id AND soi.deleted = b'0' JOIN mdm_item mdm ON mdm.id = soi.product_id AND mdm.deleted = b'0' LEFT JOIN mes_md_item mi ON mi.mdm_item_id = mdm.id AND mi.deleted = b'0' LEFT JOIN mes_md_item_type mit ON mit.id = mi.item_type_id AND mit.deleted = b'0' WHERE so.deleted = b'0' AND soi.need_production = 1 AND (mi.id IS NULL OR COALESCE(mi.status, 1) <> 0 OR COALESCE(mit.item_or_product, '') <> 'PRODUCT') ORDER BY so.id, soi.id; -- 2. ä¿®å¤ï¼åå² MES ç©ææç¼ç å·²åå¨ãä½ mdm_item_id 为空æ¶ï¼è¡¥é½ MDM å ³èã -- mdm_item.code æ¯å ¨å±å¯ä¸ç¼ç ï¼ä» æ´æ°ä¸¤è¾¹é½æªå é¤ãå ³è为空ä¸ç¼ç ç¸åçæ°æ®ã UPDATE mes_md_item mi JOIN mdm_item mdm ON mdm.code = mi.code AND mdm.deleted = b'0' LEFT JOIN mes_md_item linked ON linked.mdm_item_id = mdm.id AND linked.deleted = b'0' SET mi.mdm_item_id = mdm.id, mi.updater = '1', mi.update_time = NOW() WHERE mi.deleted = b'0' AND mi.mdm_item_id IS NULL AND linked.id IS NULL; -- 3. ä¿®å¤ï¼MPS å·²åå¨ä½é宿ç»ç mps_id æªåå¡«æ¶ï¼è¡¥é½å ³èã -- 已忶 MPSï¼status=3ï¼ä¸åå¡«ã UPDATE erp_sale_order_items soi JOIN ( SELECT sale_order_item_id, MAX(id) AS mps_id FROM mes_pro_mps WHERE deleted = b'0' AND status <> 3 AND sale_order_item_id IS NOT NULL GROUP BY sale_order_item_id ) mps ON mps.sale_order_item_id = soi.id SET soi.mps_id = mps.mps_id, soi.updater = '1', soi.update_time = NOW() WHERE soi.deleted = b'0' AND (soi.mps_id IS NULL OR soi.mps_id <> mps.mps_id); -- 4. æ£æ¥ï¼å·²ç»å®¡æ ¸ãéè¦ç产ï¼ä½ä»æ²¡æææ MPS çåå²è®¢åã SELECT so.id AS sale_order_id, so.no AS sale_order_no, soi.id AS sale_order_item_id, mdm.code AS item_code, mdm.name AS item_name, soi.count AS plan_quantity FROM erp_sale_order so JOIN erp_sale_order_items soi ON soi.order_id = so.id AND soi.deleted = b'0' JOIN mdm_item mdm ON mdm.id = soi.product_id AND mdm.deleted = b'0' LEFT JOIN mes_pro_mps mps ON mps.sale_order_item_id = soi.id AND mps.deleted = b'0' AND mps.status <> 3 WHERE so.deleted = b'0' AND so.status = 20 AND soi.need_production = 1 AND mps.id IS NULL ORDER BY so.id, soi.id; -- 5. åå²åæ®éè¯ï¼æåæ§è¡ï¼ï¼å 确认第 1ã4 æ¥æ£æ¥ç»æå·²ç»æ£å¸¸ï¼åå¡«å订å IDã -- å°è¯¥è®¢åéåâå¾ å®¡æ ¸(10)âï¼ç¶åå¨é¡µé¢éæ°ç¹å»â审æ¹âï¼æ°ä»£ç ä¼èªå¨çæ MPSã SET @sale_order_id = 0; -- æ¹ä¸ºéè¦éè¯çéå®è®¢å IDï¼0 è¡¨ç¤ºä¸æ§è¡ä»»ä½æ´æ° UPDATE erp_sale_order so SET so.status = 10, so.updater = '1', so.update_time = NOW() WHERE so.id = @sale_order_id AND @sale_order_id > 0 AND so.deleted = b'0' AND so.status = 20 AND COALESCE(so.out_count, 0) = 0 AND EXISTS ( SELECT 1 FROM erp_sale_order_items soi LEFT JOIN mes_pro_mps mps ON mps.sale_order_item_id = soi.id AND mps.deleted = b'0' AND mps.status <> 3 WHERE soi.order_id = so.id AND soi.deleted = b'0' AND soi.need_production = 1 AND mps.id IS NULL ); yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java
@@ -31,17 +31,14 @@ import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO; import cn.iocoder.yudao.module.mes.api.mps.MesProMpsApi; import cn.iocoder.yudao.module.mes.api.mps.dto.MesProMpsCreateReqDTO; import cn.iocoder.yudao.module.mes.api.mps.dto.MesProMpsRespDTO; import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; @@ -67,6 +64,8 @@ @Validated @Slf4j public class ErpSaleOrderServiceImpl implements ErpSaleOrderService { private static final Integer MPS_STATUS_CANCELED = 3; @Resource private ErpSaleOrderMapper saleOrderMapper; @@ -94,22 +93,6 @@ private ErpAccountMapper accountMapper; @Resource private MesProMpsApi mpsApi; @Resource private PlatformTransactionManager transactionManager; /** * MPS å建使ç¨ç¬ç«äºå¡ï¼REQUIRES_NEWï¼ï¼ * å®¡æ ¸æ¶èªå¨å建 MPS å±äºå°½åè为çèå¨ï¼å¤±è´¥ä» è®°å½æ¥å¿å¹¶åæ»èªèº«ï¼ * ä¸è½æ±¡æéå®è®¢åå®¡æ ¸äºå¡ï¼å¦åå å±å¼å¸¸ä¼æå¤å±äºå¡æ 记为 rollback-onlyï¼å¯¼è´æ´ä¸ªå®¡æ ¸è¢«åæ»ï¼ */ private TransactionTemplate mpsTransactionTemplate; @PostConstruct public void initMpsTransactionTemplate() { mpsTransactionTemplate = new TransactionTemplate(transactionManager); mpsTransactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); } @Resource private StorageAttachmentApi storageAttachmentApi; @@ -237,31 +220,36 @@ return; } int mpsCount = 0; int createdCount = 0; int reusedCount = 0; for (ErpSaleOrderItemDO item : items) { if (item.getNeedProduction() == null || item.getNeedProduction() != 1) { continue; } try { // ç¬ç«äºå¡å建 MPSï¼å¤±è´¥åªåæ» MPS èªèº«ï¼ä¸å½±åå®¡æ ¸äºå¡ Long mpsId = mpsTransactionTemplate.execute(txStatus -> { Long newMpsId = createMpsForSaleOrderItem(saleOrder, item); if (newMpsId != null) { // æ´æ°éå®è®¢åæç»ç MPS å ³è saleOrderItemMapper.updateById(new ErpSaleOrderItemDO() .setId(item.getId()) .setMpsId(newMpsId)); } return newMpsId; }); if (mpsId != null) { mpsCount++; // åä¸é宿ç»åªä¿ç䏿¡ææ MPSï¼é¿å åå®¡æ ¸å忬¡å®¡æ ¸äº§çéå¤è®¡å MesProMpsRespDTO existingMps = mpsApi.getMpsBySaleOrderItemId(item.getId()); if (existingMps != null && ObjectUtil.notEqual(existingMps.getStatus(), MPS_STATUS_CANCELED)) { if (ObjectUtil.notEqual(item.getMpsId(), existingMps.getId())) { saleOrderItemMapper.updateById(new ErpSaleOrderItemDO() .setId(item.getId()) .setMpsId(existingMps.getId())); } } catch (Exception e) { log.error("[createMpsForSaleOrder] å建 MPS 失败ï¼saleOrderItemId={}", item.getId(), e); reusedCount++; log.info("[createMpsForSaleOrder] éå®è®¢åæç»({}) å·²å卿æ MPS({})ï¼è·³è¿éå¤å建", item.getId(), existingMps.getId()); continue; } // ä¸éå®è®¢åå®¡æ ¸å ±ç¨äºå¡ï¼ä»»ä¸æç»å建失败é½ä¼åæ»å®¡æ ¸ç¶æåæ¬æ¬¡å·²å建ç MPS Long mpsId = createMpsForSaleOrderItem(saleOrder, item); saleOrderItemMapper.updateById(new ErpSaleOrderItemDO() .setId(item.getId()) .setMpsId(mpsId)); createdCount++; } log.info("[createMpsForSaleOrder] éå®è®¢å({}) å®¡æ ¸å®æï¼å建 MPS {} æ¡", orderId, mpsCount); log.info("[createMpsForSaleOrder] éå®è®¢å({}) å®¡æ ¸å®æï¼å建 MPS {} æ¡ï¼å¤ç¨ MPS {} æ¡", orderId, createdCount, reusedCount); } /** yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/mps/MesProMpsMapper.java
@@ -64,14 +64,20 @@ } default MesProMpsDO selectBySaleOrderItemId(Long saleOrderItemId) { return selectOne(MesProMpsDO::getSaleOrderItemId, saleOrderItemId); return selectOne(new LambdaQueryWrapperX<MesProMpsDO>() .eq(MesProMpsDO::getSaleOrderItemId, saleOrderItemId) .orderByDesc(MesProMpsDO::getId) .last("LIMIT 1")); } default List<MesProMpsDO> selectListBySaleOrderItemIds(Collection<Long> saleOrderItemIds) { if (saleOrderItemIds == null || saleOrderItemIds.isEmpty()) { return Collections.emptyList(); } return selectList(MesProMpsDO::getSaleOrderItemId, saleOrderItemIds); // åä¸é宿ç»å¯è½åå¨âæ§è®¡åå·²åæ¶ãæ°è®¡åéæ°å建âçåå²è®°å½ï¼æ ID ååºï¼è°ç¨æ¹åæå䏿¡å³ææ°è®¡å return selectList(new LambdaQueryWrapperX<MesProMpsDO>() .in(MesProMpsDO::getSaleOrderItemId, saleOrderItemIds) .orderByAsc(MesProMpsDO::getId)); } /**