doc/sql/20260812_flat_bom_and_operation_pick.sql
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,121 @@ -- ============================================================================= -- æå¹³ BOM + å·¥åºç»ä¸é¢æ + æ¥å·¥é¢æé¨ç¦ æ°æ®åºè¿ç§»èæ¬ -- éç¨çæ¬ï¼2026-08-12 éæ±ï¼ç®åBOMé ç½® / å·¥èºè·¯çº¿æå·¥åºçæ / 颿䏿²å°å·¥åºæ¥å·¥ï¼ -- æ§è¡é¡ºåºï¼å æ§è¡æ¬èæ¬ï¼å åæ®µ + åå¡« + æ°æ®æ£æ¥ï¼ï¼ååå¸å端代ç ã -- 说æï¼ -- 1. æ¬èæ¬ä¸ç©çå é¤ technology_bom_structure / production_bom_structure ç -- åå²å±çº§ä¸æ°éåï¼parent_id / unit_quantity / demanded_quantity / unitï¼ï¼ -- ä» åæ¢ä½¿ç¨å ¶ä¸å¡è¯ä¹ï¼ä¿è¯åå²è®¢å䏿¥è¡¨å¯å溯ã -- 2. 第 3 é¨åæ¯âæ°æ®æ£æ¥âï¼éè¦äººå·¥ç¡®è®¤ååå³å®æ¯å¦æ§è¡å¯¹åºçæ¸ çè¯å¥ã -- ============================================================================= -- ----------------------------------------------------------------------------- -- 1. é¢æä¸»è®°å½å¢å å·¥åºå·¥åå½å±ï¼é¢æä»è®¢åçº§ä¸æ²å°å·¥åºï¼å·¥åï¼çº§ -- ----------------------------------------------------------------------------- ALTER TABLE production_order_pick ADD COLUMN production_operation_task_id BIGINT NULL COMMENT 'ç产工åidï¼å·¥åºä»»å¡ï¼ï¼é¢æå½å±çå·¥åºæ§è¡åå ' AFTER production_order_id; CREATE INDEX idx_pop_operation_task ON production_order_pick (production_operation_task_id); CREATE INDEX idx_pop_order_operation ON production_order_pick (production_order_id, technology_operation_id); -- åå¡«åå²é¢æçå·¥åå½å±ï¼ -- åªåå¡«è½å¤å¯ä¸ç¡®è®¤çè®°å½ï¼å䏿¡é¢æçæµæ°´éå·¥åIDå¯ä¸ï¼ã -- æ æ³å¯ä¸ç¡®è®¤çå岿°æ®ä¿æä¸º NULLï¼å®ä»¬ä»ä¼åºç°å¨è®¢åé¢ææ±æ»ä¸ï¼ -- ä½ä¸ä¼è¢«å½ä½æéå·¥åºç颿ï¼å æ¤ä¸ä¼é误解éå·¥åºæ¥å·¥ã UPDATE production_order_pick pop JOIN (SELECT pick_id, MIN(production_operation_task_id) AS task_id FROM production_order_pick_record WHERE production_operation_task_id IS NOT NULL GROUP BY pick_id HAVING COUNT(DISTINCT production_operation_task_id) = 1) r ON r.pick_id = pop.id SET pop.production_operation_task_id = r.task_id WHERE pop.production_operation_task_id IS NULL; -- ----------------------------------------------------------------------------- -- 2. æ¥å·¥æå ¥è®°å½æ¥æºé¢æï¼ä¿è¯åä¸ç¬é¢æåªè¢«ä¸æ¬¡æ¥å·¥å½éï¼é¿å éå¤æå ¥ -- ----------------------------------------------------------------------------- ALTER TABLE production_product_input ADD COLUMN production_order_pick_id BIGINT NULL COMMENT 'æ¥æºé¢æè®°å½idï¼production_order_pick.idï¼' AFTER product_model_id; -- å¯ä¸ç´¢å¼ï¼MySQL å 许å¤ä¸ª NULLï¼åå²æå ¥è®°å½ï¼æ æ¥æºé¢æï¼ä¸åå½±åï¼ -- æ°æ°æ®åä¿è¯ä¸æ¡é¢æä¸ä¼è¢«ä¸¤æ¡æ¥å·¥æå ¥éå¤å¼ç¨ã CREATE UNIQUE INDEX uk_ppi_production_order_pick ON production_product_input (production_order_pick_id); -- ----------------------------------------------------------------------------- -- 3. åå² BOM æ°æ®æ£æ¥ä¸æ¸ çï¼å æ¥ï¼åç±ä¸å¡ç¡®è®¤åæ§è¡æ¸ çï¼ -- ----------------------------------------------------------------------------- -- 3.1 æ£æ¥ï¼æ§ç BOM æåæ ¹èç¹ï¼æ ç¶èç¹ãæ å·¥åºï¼ä¸è§æ ¼çäº BOM æåè§æ ¼ï¼ã -- è¿äºè¡ä¸æ¯ç©æï¼æ°çæ¬æ¥è¯¢å·²å°å ¶è¿æ»¤ï¼å»ºè®®ç¡®è®¤åå é¤ã SELECT tbs.id, tbs.bom_id, tb.bom_no, tbs.product_model_id FROM technology_bom_structure tbs JOIN technology_bom tb ON tbs.bom_id = tb.id WHERE tbs.parent_id IS NULL AND tbs.operation_id IS NULL AND tbs.product_model_id = tb.product_model_id; -- 3.1 æ¸ çï¼ç¡®è®¤æ è¯¯åæ§è¡ï¼ -- DELETE tbs -- FROM technology_bom_structure tbs -- JOIN technology_bom tb ON tbs.bom_id = tb.id -- WHERE tbs.parent_id IS NULL -- AND tbs.operation_id IS NULL -- AND tbs.product_model_id = tb.product_model_id; -- 3.2 æ£æ¥ï¼ç¼ºå°æ¶èå·¥åºçç©æè¡ã -- æå¹³ BOM è¦æ±æ¯ä¸ªç©æé½å¿ é¡»æå®æ¶èå·¥åºï¼å¦å该 BOM æ æ³çæå·¥èºè·¯çº¿ï¼ -- å¿ é¡»å¨â产åç»æè¯¦æ â页é¢è¡¥é½ï¼ä¸è½é»è®¤ç»ä¸ä¸ªå·¥åºã SELECT tbs.id, tbs.bom_id, tb.bom_no, tbs.product_model_id FROM technology_bom_structure tbs JOIN technology_bom tb ON tbs.bom_id = tb.id WHERE tbs.operation_id IS NULL AND NOT (tbs.parent_id IS NULL AND tbs.product_model_id = tb.product_model_id); -- 3.3 æ£æ¥ï¼åä¸ BOM ä¸éå¤çâç©æè§æ ¼ + æ¶èå·¥åºâã -- æ°çæ¬ä¿åæ¶ä¼æç»éå¤é ç½®ï¼åå²é夿°æ®éè¦å åå¹¶ã SELECT bom_id, product_model_id, operation_id, COUNT(*) AS duplicate_count FROM technology_bom_structure WHERE operation_id IS NOT NULL GROUP BY bom_id, product_model_id, operation_id HAVING COUNT(*) > 1; -- 3.4 æ£æ¥ï¼åå²å¤å± BOM 䏿¾ä½ä¸ºâä¸ä¸éå·¥åºäº§åºâçä¸é´èç¹ã -- è¿äºè¡åæ¬ä»£è¡¨èªå¶åæå产åºï¼ä¸æ¯éè¦é¢ç¨çç©æã -- æå¹³ååå®ä»¬ä¼åæå¯é¢ç¨ç©æï¼éè¦ä¸å¡ç¡®è®¤æ¯å¦å é¤ã SELECT child.id, child.bom_id, tb.bom_no, child.product_model_id, child.operation_id FROM technology_bom_structure child JOIN technology_bom tb ON child.bom_id = tb.id WHERE EXISTS (SELECT 1 FROM technology_bom_structure sub WHERE sub.parent_id = child.id); -- 3.5 å¯éï¼å°åå²å±çº§é为ä¸çº§ï¼ç¡®è®¤ 3.1 / 3.4 å¤ç宿ååæ§è¡ï¼ã -- æ°ä»£ç ä¸å读å parent_id çä¸å¡è¯ä¹ï¼æ¬æ¥éª¤åªæ¯è®©æ°æ®ä¸æ¨¡åä¿æä¸è´ã -- UPDATE technology_bom_structure SET parent_id = NULL WHERE parent_id IS NOT NULL; -- 3.6 æ£æ¥ï¼åå²è®¢å BOM å¿«ç §ä¸çæåæ ¹èç¹ï¼å¿«ç §ä¿æç¬ç«ï¼é»è®¤ä¸æ¹åï¼ã -- æ°çæ¬é¢æå»ºè®®å·²æâå·¥åºé空 + è§æ ¼ä¸çäºæåè§æ ¼âè¿æ»¤ï¼åå²å¿«ç §å¯ä¿æåæ ·ã SELECT pbs.id, pbs.production_order_id, pbs.production_order_bom_id, pbs.product_model_id FROM production_bom_structure pbs JOIN production_order_bom pob ON pbs.production_order_bom_id = pob.id WHERE pbs.parent_id IS NULL AND pbs.technology_operation_id IS NULL AND pbs.product_model_id = pob.product_model_id; src/main/java/com/ruoyi/production/bean/dto/ProductionBomStructureDto.java
@@ -23,9 +23,10 @@ @Schema(description = "è§æ ¼åå·") private String model; private String tempId; private String parentTempId; private List<ProductionBomStructureDto> children; /** * 订åBOMå¿«ç §åæ ·æ¯æå¹³çâç©æè§æ ¼ + æ¶èå·¥åºâï¼æç»é¡¹åªéè¦ * idï¼æ´æ°æ¶ï¼ãproductModelIdãtechnologyOperationIdã */ @Schema(description = "æå¹³ç©ææç»å表") private List<ProductionBomStructureDto> detailList; } src/main/java/com/ruoyi/production/bean/dto/ProductionOrderPickDto.java
@@ -15,9 +15,6 @@ @Schema(description = "ç产订åID") private Long productionOrderId; @Schema(description = "ç产工åID") private Long productionOperationTaskId; @Schema(description = "æ¹å·") private String batchNo; src/main/java/com/ruoyi/production/bean/vo/ProductionBomStructureVo.java
@@ -5,8 +5,9 @@ import lombok.Data; import lombok.EqualsAndHashCode; import java.util.List; /** * 订åBOMå¿«ç §æç»å±ç¤ºå¯¹è±¡ï¼æå¹³ç»æï¼ä¸è¡ = ä¸ä¸ªç©æè§æ ¼ + å ¶æ¶èå·¥åºã */ @Data @EqualsAndHashCode(callSuper = true) public class ProductionBomStructureVo extends ProductionBomStructure { @@ -22,6 +23,4 @@ @Schema(description = "è§æ ¼åå·") private String model; private List<ProductionBomStructureVo> children; } src/main/java/com/ruoyi/production/bean/vo/ProductionOrderPickVo.java
@@ -21,12 +21,21 @@ @Schema(description = "é¢ç¨æ°é") private BigDecimal pickQuantity; @Schema(description = "å颿æ°éï¼é¢æ+è¡¥æ-éæï¼") private BigDecimal effectiveQuantity; @Schema(description = "æ¯å¦ä¸ºå·²é¢æè®°å½ï¼false 表示æ¥èªBOMçå¯é¢ç¨ç©æå»ºè®®") private Boolean picked; @Schema(description = "产ååç§°") private String productName; @Schema(description = "è§æ ¼åå·") private String model; @Schema(description = "å·¥åç¼å·") private String workOrderNo; @Schema(description = "æ¹å·") private List<String> batchNoList; } src/main/java/com/ruoyi/production/controller/ProductionBomStructureController.java
@@ -28,14 +28,20 @@ private ProductionBomStructureService productionBomStructureService; @GetMapping("/listByBomId/{bomId}") @Operation(summary = "æ ¹æ®BOMæ¥è¯¢ç产订åBOMç»ææ ") @Operation(summary = "æ ¹æ®è®¢åBOMæ¥è¯¢æå¹³ç©ææç»") public R<List<ProductionBomStructureVo>> listByBomId(@PathVariable Long bomId) { return R.ok(productionBomStructureService.listByBomId(bomId)); } @Log(title = "æ°å¢æä¿®æ¹BOMç»æ", businessType = BusinessType.UPDATE) @GetMapping("/listByOrderId/{productionOrderId}") @Operation(summary = "æ ¹æ®çäº§è®¢åæ¥è¯¢è®¢åBOMå¿«ç §çæå¹³ç©ææç»") public R<List<ProductionBomStructureVo>> listByOrderId(@PathVariable Long productionOrderId) { return R.ok(productionBomStructureService.listByOrderId(productionOrderId)); } @Log(title = "å ¨éä¿å订åBOMæå¹³ç©ææç»", businessType = BusinessType.UPDATE) @PostMapping("/addOrUpdateBomStructs") @Operation(summary = "æ°å¢æä¿®æ¹BOMç»æ") @Operation(summary = "å ¨éä¿å订åBOMæå¹³ç©ææç»") public R addProductionBomStructure(@RequestBody ProductionBomStructureDto productionBomStructureDto) { return R.ok(productionBomStructureService.addProductionBomStructure(productionBomStructureDto)); } src/main/java/com/ruoyi/production/controller/ProductionOrderPickController.java
@@ -48,5 +48,24 @@ public R<List<ProductionOrderPickVo>> listPickedDetail(@PathVariable Long productionOrderId) { return R.ok(productionOrderPickService.listPickedDetail(productionOrderId)); } @GetMapping("/taskMaterials/{productionOperationTaskId}") @Operation(summary = "æ¥è¯¢å·¥åºå·¥åçå¯é¢ç¨ç©æä¸å·²é¢æè®°å½") public R<List<ProductionOrderPickVo>> listTaskMaterials(@PathVariable Long productionOperationTaskId) { return R.ok(productionOrderPickService.listTaskMaterials(productionOperationTaskId)); } @Log(title = "å·¥åºé¢æ", businessType = BusinessType.INSERT) @PostMapping("/savePickByTask") @Operation(summary = "å·¥åºç»ä¸é¢æå¹¶ç«å³æ£ååºå") public R<Boolean> savePickByTask(@RequestBody ProductionOrderPickDto dto) { return R.ok(productionOrderPickService.savePickByTask(dto)); } @GetMapping("/taskPicked/{productionOperationTaskId}") @Operation(summary = "æ¥è¯¢å·¥åºå·¥åæ¯å¦å·²å卿æé¢æ") public R<Boolean> hasEffectivePick(@PathVariable Long productionOperationTaskId) { return R.ok(productionOrderPickService.hasEffectivePick(productionOperationTaskId)); } } src/main/java/com/ruoyi/production/mapper/ProductionOrderPickMapper.java
@@ -20,4 +20,9 @@ public interface ProductionOrderPickMapper extends BaseMapper<ProductionOrderPick> { List<ProductionOrderPickVo> listPickedDetailByOrderId(@Param("productionOrderId") Long productionOrderId); /** * æ¥è¯¢æä¸ªå·¥åºå·¥å已产ççé¢æè®°å½ã */ List<ProductionOrderPickVo> listPickedDetailByTaskId(@Param("productionOperationTaskId") Long productionOperationTaskId); } src/main/java/com/ruoyi/production/pojo/ProductionOrderPick.java
@@ -46,6 +46,9 @@ @Schema(description = "ç产订åid") private Long productionOrderId; @Schema(description = "ç产工åidï¼å·¥åºä»»å¡ï¼ï¼é¢æå½å±çå·¥åºæ§è¡åå ") private Long productionOperationTaskId; @Schema(description = "夿³¨") private String remark; src/main/java/com/ruoyi/production/pojo/ProductionProductInput.java
@@ -20,6 +20,9 @@ @Schema(description = "产åid") private Long productModelId; @Schema(description = "æ¥æºé¢æè®°å½idï¼production_order_pick.idï¼ï¼ä¿è¯åä¸ç¬é¢æåªè¢«ä¸æ¬¡æ¥å·¥å½é") private Long productionOrderPickId; @Schema(description = "æ°é") private BigDecimal quantity; src/main/java/com/ruoyi/production/service/ProductionBomStructureService.java
@@ -19,5 +19,10 @@ List<ProductionBomStructureVo> listByBomId(Long bomId); /** * æçäº§è®¢åæ¥è¯¢è®¢åBOMå¿«ç §çæå¹³ç©ææç»ã */ List<ProductionBomStructureVo> listByOrderId(Long productionOrderId); Boolean addProductionBomStructure(ProductionBomStructureDto productionBomStructureDto); } src/main/java/com/ruoyi/production/service/ProductionOrderPickService.java
@@ -22,4 +22,20 @@ Boolean updatePick(ProductionOrderPickDto dto); List<ProductionOrderPickVo> listPickedDetail(Long productionOrderId); /** * æ¥è¯¢æä¸ªå·¥åºå·¥åçé¢æç©ææ¸ åï¼ * æ¢å å«è®¢åBOMå¿«ç §ä¸åé ç»è¯¥å·¥åºçå¯é¢ç¨ç©æå»ºè®®ï¼ä¹å å«è¯¥å·¥å已产ççé¢æè®°å½ã */ List<ProductionOrderPickVo> listTaskMaterials(Long productionOperationTaskId); /** * å·¥åºç»ä¸é¢æï¼æå·¥åç´æ¥é¢æå¹¶ç«å³æ£ååºåï¼å¯éå¤é¢æã */ Boolean savePickByTask(ProductionOrderPickDto dto); /** * 夿æä¸ªå·¥åºå·¥åæ¯å¦å·²å卿æé¢æï¼ä»»ä¸ç©æå颿æ°é大äº0ï¼ã */ boolean hasEffectivePick(Long productionOperationTaskId); } src/main/java/com/ruoyi/production/service/ProductionOrderService.java
@@ -32,6 +32,11 @@ List<ProductionOrderPickVo> pick(Long productionOrderId); /** * æè®¢åBOMå¿«ç §ç»åºå¯é¢ç¨ç©æå»ºè®®ï¼technologyOperationId é空æ¶åªè¿å该工åºçç©æã */ List<ProductionOrderPickVo> pick(Long productionOrderId, Long technologyOperationId); ProductionOrderWorkOrderDetailVo getWorkOrderReportInspectDetail(ProductionOrderDto productionOrderDto); int updateOrder(ProductionOrderDto productionOrderDto); src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
@@ -3,6 +3,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.basic.mapper.ProductModelMapper; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.production.bean.dto.ProductionBomStructureDto; import com.ruoyi.production.bean.vo.ProductionBomStructureVo; @@ -31,7 +32,6 @@ import com.ruoyi.technology.pojo.TechnologyOperationParam; import com.ruoyi.technology.pojo.TechnologyParam; import lombok.RequiredArgsConstructor; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -61,123 +61,137 @@ private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper; private final ProductionOperationTaskMapper productionOperationTaskMapper; private final ProductionProductMainMapper productionProductMainMapper; private final ProductModelMapper productModelMapper; private final TechnologyOperationMapper technologyOperationMapper; private final TechnologyOperationParamMapper technologyOperationParamMapper; private final TechnologyParamMapper technologyParamMapper; /** * æ ¹æ®BOMæ¥è¯¢å¹¶ç»è£ ç»ææ ã * æè®¢åBOMæ¥è¯¢æå¹³ç©ææç»ã */ @Override public List<ProductionBomStructureVo> listByBomId(Long bomId) { // æBOMIDæ¥è¯¢çäº§ç»ææ°æ® List<ProductionBomStructureVo> list = productionBomStructureMapper.listByBomId(bomId); Map<Long, ProductionBomStructureVo> map = new HashMap<>(); for (ProductionBomStructureVo node : list) { node.setChildren(new ArrayList<>()); map.put(node.getId(), node); if (bomId == null) { return new ArrayList<>(); } List<ProductionBomStructureVo> tree = new ArrayList<>(); for (ProductionBomStructureVo node : list) { Long parentId = node.getParentId(); if (parentId == null || parentId == 0L) { tree.add(node); continue; } ProductionBomStructureVo parent = map.get(parentId); if (parent != null) { parent.getChildren().add(node); } } return tree; return productionBomStructureMapper.listByBomId(bomId); } /** * æçäº§è®¢åæ¥è¯¢è®¢åBOMå¿«ç §çæå¹³ç©ææç»ã */ @Override public List<ProductionBomStructureVo> listByOrderId(Long productionOrderId) { if (productionOrderId == null) { return new ArrayList<>(); } ProductionOrderBom orderBom = productionOrderBomMapper.selectOne( Wrappers.<ProductionOrderBom>lambdaQuery() .eq(ProductionOrderBom::getProductionOrderId, productionOrderId) .orderByDesc(ProductionOrderBom::getId) .last("limit 1")); if (orderBom == null || orderBom.getId() == null) { return new ArrayList<>(); } return listByBomId(orderBom.getId()); } /** * å ¨éåæ¥è®¢åBOMå¿«ç §çæå¹³ç©ææç»ï¼å¹¶æå ¶ä¸çå·¥åºé建订åå·¥èºè·¯çº¿å¿«ç §ä¸å·¥åã */ @Override @Transactional(rollbackFor = Exception.class) public Boolean addProductionBomStructure(ProductionBomStructureDto dto) { // æ°å¢ç产BOMç»æ // 读åå½å订åBOM主é®ï¼å¹¶æå端æ ç»ææå¹³æå表 Long orderBomId = dto.getProductionOrderBomId(); List<ProductionBomStructureDto> flatDtoList = new ArrayList<>(); flattenTree(dto.getChildren(), flatDtoList); if (orderBomId == null) { throw new ServiceException("订åBOM IDä¸è½ä¸ºç©º"); } ProductionOrderBom orderBom = productionOrderBomMapper.selectById(orderBomId); if (orderBom == null) { throw new ServiceException("订åBOMä¸åå¨"); } Long productionOrderId = dto.getProductionOrderId() != null ? dto.getProductionOrderId() : orderBom.getProductionOrderId(); // æ¥è¯¢æ°æ®åºå·²æç»æï¼ç¨äºåç»åå¢å æ¹å¯¹æ¯ List<ProductionBomStructureDto> detailList = dto.getDetailList() == null ? Collections.emptyList() : dto.getDetailList(); List<ProductionBomStructure> dbList = this.list(new LambdaQueryWrapper<ProductionBomStructure>() .eq(ProductionBomStructure::getProductionOrderBomId, orderBomId)); Set<Long> dbIds = dbList.stream().map(ProductionBomStructure::getId).collect(Collectors.toSet()); // æ¶éå端ä»ç¶åå¨çèç¹ID Set<Long> frontendIds = new HashSet<>(); for (ProductionBomStructureDto item : flatDtoList) { if (item.getId() != null) { frontendIds.add(item.getId()); } } // 计ç®éè¦å é¤çèç¹ï¼æ°æ®åºæãå端已å é¤ï¼ Set<Long> deleteIds = new HashSet<>(); for (ProductionBomStructure dbItem : dbList) { if (!frontendIds.contains(dbItem.getId())) { deleteIds.add(dbItem.getId()); } } // å å æå端已ç»ç§»é¤çèç¹ if (!deleteIds.isEmpty()) { this.removeByIds(deleteIds); } // ææ¯å¦æIDæå为æ°å¢åæ´æ°ï¼åæ¶ç¼åæ°å¢èç¹ç临æ¶IDæ å° Set<Long> submittedIds = new HashSet<>(); Set<String> relationKeys = new HashSet<>(); List<ProductionBomStructure> insertList = new ArrayList<>(); List<ProductionBomStructure> updateList = new ArrayList<>(); Map<String, ProductionBomStructure> tempEntityMap = new HashMap<>(); for (ProductionBomStructureDto item : flatDtoList) { for (ProductionBomStructureDto item : detailList) { Long productModelId = item.getProductModelId(); Long technologyOperationId = item.getTechnologyOperationId(); if (productModelId == null) { throw new ServiceException("è¯·éæ©ç©æè§æ ¼"); } if (productModelMapper.selectById(productModelId) == null) { throw new ServiceException("ç©æè§æ ¼ä¸åå¨"); } if (technologyOperationId == null) { throw new ServiceException("è¯·éæ©æ¶èå·¥åº"); } if (technologyOperationMapper.selectById(technologyOperationId) == null) { throw new ServiceException("æ¶èå·¥åºä¸åå¨"); } if (productModelId.equals(orderBom.getProductModelId())) { throw new ServiceException("æåè§æ ¼ä¸è½ä½ä¸ºèªèº«çç©æ"); } if (!relationKeys.add(productModelId + "#" + technologyOperationId)) { throw new ServiceException("åä¸ç©æè§æ ¼å¨åä¸å·¥åºä¸ä¸è½éå¤é ç½®"); } ProductionBomStructure entity = new ProductionBomStructure(); BeanUtils.copyProperties(item, entity); entity.setProductionOrderBomId(orderBomId); entity.setProductionOrderId(productionOrderId); entity.setProductModelId(productModelId); entity.setTechnologyOperationId(technologyOperationId); // æå¹³å¿«ç §ä¸å使ç¨å±çº§ä¸æ°éè¯ä¹ entity.setParentId(null); entity.setUnitQuantity(null); entity.setDemandedQuantity(null); entity.setUnit(null); if (item.getId() == null) { entity.setParentId(null); insertList.add(entity); tempEntityMap.put(item.getTempId(), entity); } else { if (!dbIds.contains(item.getId())) { throw new ServiceException("订åBOMç©ææç»ä¸å卿ä¸å±äºå½å订åBOM"); } entity.setId(item.getId()); submittedIds.add(item.getId()); updateList.add(entity); } } // æ¹éæ°å¢ï¼æ¿å°æ°æ®åºçæççå®ID Set<Long> deleteIds = new HashSet<>(dbIds); deleteIds.removeAll(submittedIds); if (!deleteIds.isEmpty()) { this.removeByIds(deleteIds); } if (!updateList.isEmpty()) { this.updateBatchById(updateList); } if (!insertList.isEmpty()) { this.saveBatch(insertList); } // æ°å¢èç¹äºæ¬¡ååç¶IDï¼åç«¯ä¼ çæ¯ä¸´æ¶ç¶IDï¼ List<ProductionBomStructure> parentFixList = new ArrayList<>(); for (ProductionBomStructureDto item : flatDtoList) { if (item.getId() == null && item.getParentTempId() != null) { ProductionBomStructure child = tempEntityMap.get(item.getTempId()); if (child == null) { continue; } ProductionBomStructure parent = tempEntityMap.get(item.getParentTempId()); // ç¶èç¹æ¯æ¬æ¬¡æ°å¢æ¶ï¼ç´æ¥ç¨æ°å¢åççå®IDï¼å¦ååé为åç«¯ä¼ å ¥ç¶ID Long realParentId = parent != null ? parent.getId() : Long.valueOf(item.getParentTempId()); child.setParentId(realParentId); parentFixList.add(child); } } // ååæ°å¢èç¹çç¶åå ³ç³» if (!parentFixList.isEmpty()) { this.updateBatchById(parentFixList); } // æ¹éæ´æ°å·²æèç¹ if (!updateList.isEmpty()) { this.updateBatchById(updateList); } syncDemandedQuantityAndTaskPlanQuantity(orderBomId, dto.getProductionOrderId()); syncRoutingAndTaskByStructure(orderBomId, productionOrderId); return true; } private void syncDemandedQuantityAndTaskPlanQuantity(Long orderBomId, Long productionOrderId) { /** * 订åBOMç©æåååï¼æå ¶å·¥åºéåé建订åå·¥èºè·¯çº¿å·¥åºå¿«ç §ä¸å·¥åè®¡åæ°éã */ private void syncRoutingAndTaskByStructure(Long orderBomId, Long productionOrderId) { if (orderBomId == null) { return; } @@ -194,55 +208,20 @@ return; } BigDecimal orderQuantity = defaultDecimal(productionOrder.getQuantity()); List<ProductionBomStructure> structureList = this.list( Wrappers.<ProductionBomStructure>lambdaQuery() .eq(ProductionBomStructure::getProductionOrderBomId, orderBomId) .orderByAsc(ProductionBomStructure::getId)); //忥鿱æ°é syncStructureDemandedQuantity(structureList, orderQuantity); Long rootProductModelId = orderBom.getProductModelId() != null ? orderBom.getProductModelId() : productionOrder.getProductModelId(); //忥ç产工èºè·¯çº¿ Long rootProductModelId = orderBom.getProductModelId() != null ? orderBom.getProductModelId() : productionOrder.getProductModelId(); // 忥ç产工èºè·¯çº¿å¿«ç § syncRoutingOperationsByBom(currentProductionOrderId, productionOrder, orderBom, structureList, rootProductModelId); //忥工å syncTaskPlanQuantity( currentProductionOrderId, structureList, orderQuantity, rootProductModelId); // 忥工åè®¡åæ°éï¼æå¹³BOMæ æ°éé ç½®ï¼è®¡åæ°éç»ä¸åè®¢åæ°é syncTaskPlanQuantity(currentProductionOrderId, TaskPlanQuantityUtil.resolveTaskPlanQuantity(productionOrder)); } private void syncStructureDemandedQuantity(List<ProductionBomStructure> structureList, BigDecimal orderQuantity) { if (structureList == null || structureList.isEmpty()) { return; } List<ProductionBomStructure> updateList = new ArrayList<>(); BigDecimal lastProcessDemandedQuantity = orderQuantity; for (ProductionBomStructure structure : structureList) { if (structure == null || structure.getId() == null) { continue; } BigDecimal demandedQuantity = lastProcessDemandedQuantity.multiply(defaultDecimal(structure.getUnitQuantity())); // if (compareDecimal(structure.getDemandedQuantity(), demandedQuantity) == 0) { // continue; // } ProductionBomStructure update = new ProductionBomStructure(); update.setId(structure.getId()); update.setDemandedQuantity(demandedQuantity); updateList.add(update); structure.setDemandedQuantity(demandedQuantity); lastProcessDemandedQuantity = demandedQuantity; } if (!updateList.isEmpty()) { this.updateBatchById(updateList); } } private void syncTaskPlanQuantity(Long productionOrderId, List<ProductionBomStructure> structureList, BigDecimal orderQuantity, Long rootProductModelId) { private void syncTaskPlanQuantity(Long productionOrderId, BigDecimal planQuantity) { List<ProductionOperationTask> taskList = productionOperationTaskMapper.selectList( Wrappers.<ProductionOperationTask>lambdaQuery() .eq(ProductionOperationTask::getProductionOrderId, productionOrderId) @@ -250,33 +229,10 @@ if (taskList == null || taskList.isEmpty()) { return; } Set<Long> routingOperationIds = taskList.stream() .map(ProductionOperationTask::getProductionOrderRoutingOperationId) .filter(Objects::nonNull) .collect(Collectors.toSet()); if (routingOperationIds.isEmpty()) { return; } Map<Long, ProductionOrderRoutingOperation> routingOperationMap = productionOrderRoutingOperationMapper .selectBatchIds(routingOperationIds) .stream() .filter(item -> item != null && item.getId() != null) .collect(Collectors.toMap(ProductionOrderRoutingOperation::getId, item -> item, (left, right) -> left)); // Keep task plan quantities aligned with the same order BOM snapshot demand used during snapshot creation. Map<String, BigDecimal> demandedQuantityMap = TaskPlanQuantityUtil.buildOperationDemandedQuantityMap(structureList, rootProductModelId); for (ProductionOperationTask task : taskList) { if (task == null || task.getId() == null || task.getProductionOrderRoutingOperationId() == null) { if (task == null || task.getId() == null) { continue; } ProductionOrderRoutingOperation routingOperation = routingOperationMap.get(task.getProductionOrderRoutingOperationId()); if (routingOperation == null) { continue; } BigDecimal planQuantity = resolveTaskPlanQuantity( routingOperation, demandedQuantityMap, orderQuantity, rootProductModelId); if (compareDecimal(task.getPlanQuantity(), planQuantity) == 0) { continue; } @@ -374,45 +330,30 @@ return orderRouting; } /** * æå¹³BOMæå·¥åºå»éçæææç订åå·¥åºåè¡¨ï¼ * 顺åºåç©ææç»ä¸å·¥åºé¦æ¬¡åºç°ç顺åºï¼äº§åºè§æ ¼åºå®ä¸ºè®¢åæåè§æ ¼ã */ private List<ProductionOrderRoutingOperation> buildDesiredRoutingOperationList(List<ProductionBomStructure> structureList, Long rootProductModelId) { if (structureList == null || structureList.isEmpty()) { return Collections.emptyList(); } Map<Long, ProductionBomStructure> structureById = structureList.stream() .filter(item -> item != null && item.getId() != null) .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left)); // æå»ºç¶-åæ å°å ³ç³» Map<Long, List<ProductionBomStructure>> treeMap = buildParentChildMap(structureList); // 使ç¨ååºéåæå»ºæä½å表ï¼å ååç¶ï¼ç¡®ä¿å·¥èºè·¯çº¿é¡ºåºæ£ç¡®ï¼ // ä½¿ç¨æ·±åº¦ä½ä¸ºæåºä¾æ®çè¾ å©ç»æ Map<String, ProductionBomStructure> operationMap = new LinkedHashMap<>(); Map<String, Integer> depthMap = new HashMap<>(); buildOperationListPostOrderWithDepth(null, treeMap, operationMap, depthMap, structureById, rootProductModelId, 1); // ææ·±åº¦æåºï¼æ·±åº¦å¤§çæåé¢ List<Map.Entry<String, ProductionBomStructure>> sortedEntries = new ArrayList<>(operationMap.entrySet()); sortedEntries.sort((a, b) -> { int depthCompare = Integer.compare( depthMap.getOrDefault(b.getKey(), 0), depthMap.getOrDefault(a.getKey(), 0)); if (depthCompare != 0) { return depthCompare; Set<Long> uniqueOperationIdList = new LinkedHashSet<>(); for (ProductionBomStructure structure : structureList) { if (structure == null || structure.getTechnologyOperationId() == null) { continue; } return 0; }); uniqueOperationIdList.add(structure.getTechnologyOperationId()); } List<ProductionOrderRoutingOperation> desiredOperationList = new ArrayList<>(); List<ProductionOrderRoutingOperation> desiredOperationList = new ArrayList<>(uniqueOperationIdList.size()); int dragSort = 1; for (Map.Entry<String, ProductionBomStructure> entry : sortedEntries) { ProductionBomStructure bomStructure = entry.getValue(); Long outputProductModelId = resolveOutputProductModelId(resolveOperationOutputNode(bomStructure, structureById), rootProductModelId); TechnologyOperation technologyOperation = getTechnologyOperation(bomStructure.getTechnologyOperationId()); for (Long operationId : uniqueOperationIdList) { TechnologyOperation technologyOperation = getTechnologyOperation(operationId); ProductionOrderRoutingOperation routingOperation = new ProductionOrderRoutingOperation(); routingOperation.setProductModelId(outputProductModelId); routingOperation.setTechnologyOperationId(bomStructure.getTechnologyOperationId()); routingOperation.setProductModelId(rootProductModelId); routingOperation.setTechnologyOperationId(operationId); routingOperation.setOperationName(technologyOperation == null ? null : technologyOperation.getName()); routingOperation.setIsQuality(technologyOperation == null ? null : technologyOperation.getIsQuality()); routingOperation.setIsProduction(technologyOperation == null ? null : technologyOperation.getIsProduction()); @@ -421,127 +362,6 @@ desiredOperationList.add(routingOperation); } return desiredOperationList; } private void buildOperationListPostOrderWithDepth(Long parentId, Map<Long, List<ProductionBomStructure>> treeMap, Map<String, ProductionBomStructure> operationMap, Map<String, Integer> depthMap, Map<Long, ProductionBomStructure> structureById, Long rootProductModelId, int currentDepth) { List<ProductionBomStructure> children = treeMap.get(parentId); if (children == null || children.isEmpty()) { return; } for (ProductionBomStructure child : children) { // å éå½å¤çåèç¹ buildOperationListPostOrderWithDepth(child.getId(), treeMap, operationMap, depthMap, structureById, rootProductModelId, currentDepth + 1); // åå¤çå½åèç¹ if (child.getTechnologyOperationId() != null) { Long outputProductModelId = resolveOutputProductModelId(resolveOperationOutputNode(child, structureById), rootProductModelId); String key = buildBomOperationDedupKey(child, outputProductModelId); // ä¿ç深度æå¤§çæä½ Integer existingDepth = depthMap.get(key); if (existingDepth == null || currentDepth > existingDepth) { operationMap.put(key, child); depthMap.put(key, currentDepth); } } } } private Map<Long, List<ProductionBomStructure>> buildParentChildMap(List<ProductionBomStructure> structureList) { Map<Long, List<ProductionBomStructure>> treeMap = new LinkedHashMap<>(); Map<Long, ProductionBomStructure> structureById = new HashMap<>(); // æå»ºç¶-åæ å°åIDæ å° for (ProductionBomStructure structure : structureList) { if (structure == null) continue; Long parentId = structure.getParentId(); treeMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(structure); if (structure.getId() != null) { structureById.put(structure.getId(), structure); } } // è®¡ç®æ¯ä¸ªèç¹ç深度ï¼ä»æ ¹èç¹å°å½åèç¹çè·ç¦»ï¼æ ¹èç¹æ·±åº¦ä¸º1ï¼ Map<Long, Integer> depthMap = new HashMap<>(); for (ProductionBomStructure structure : structureList) { if (structure == null || structure.getId() == null) continue; computeDepthFromRoot(structure.getId(), structureById, depthMap); } // 对æ¯ä¸ªç¶èç¹ä¸çåèç¹ææ·±åº¦ååºæåºï¼ææ·±å±çä¼å ï¼ for (Map.Entry<Long, List<ProductionBomStructure>> entry : treeMap.entrySet()) { List<ProductionBomStructure> children = entry.getValue(); children.sort((a, b) -> { // ä¼å ææ·±åº¦æåºï¼æ·±åº¦å¤§çæåé¢ï¼ææ·±å±ä¼å ï¼ int depthCompare = Integer.compare( depthMap.getOrDefault(b.getId(), 0), depthMap.getOrDefault(a.getId(), 0)); if (depthCompare != 0) { return depthCompare; } // 深度ç¸åæ¶æIDæåºä¿è¯ç¨³å®æ§ return Long.compare(a.getId(), b.getId()); }); } return treeMap; } /** * 计ç®èç¹æ·±åº¦ï¼ä»æ ¹èç¹å°å½åèç¹çè·ç¦»ï¼ * æ ¹èç¹æ·±åº¦ä¸º1ï¼æ¯åä¸ä¸å±æ·±åº¦å 1 */ private int computeDepthFromRoot(Long nodeId, Map<Long, ProductionBomStructure> structureById, Map<Long, Integer> depthMap) { if (depthMap.containsKey(nodeId)) { return depthMap.get(nodeId); } ProductionBomStructure structure = structureById.get(nodeId); if (structure == null) { depthMap.put(nodeId, 1); return 1; } Long parentId = structure.getParentId(); if (parentId == null || parentId == 0L) { // æ ¹èç¹æ·±åº¦ä¸º1 depthMap.put(nodeId, 1); return 1; } // åèç¹æ·±åº¦ = ç¶èç¹æ·±åº¦ + 1 int parentDepth = computeDepthFromRoot(parentId, structureById, depthMap); int depth = parentDepth + 1; depthMap.put(nodeId, depth); return depth; } private void buildOperationListPostOrder(Long parentId, Map<Long, List<ProductionBomStructure>> treeMap, Map<String, ProductionBomStructure> uniqueOperationMap, Map<Long, ProductionBomStructure> structureById, Long rootProductModelId) { List<ProductionBomStructure> children = treeMap.get(parentId); if (children == null || children.isEmpty()) { return; } for (ProductionBomStructure child : children) { // å éå½å¤çåèç¹ buildOperationListPostOrder(child.getId(), treeMap, uniqueOperationMap, structureById, rootProductModelId); // åå¤çå½åèç¹ if (child.getTechnologyOperationId() != null) { Long outputProductModelId = resolveOutputProductModelId(resolveOperationOutputNode(child, structureById), rootProductModelId); String key = buildBomOperationDedupKey(child, outputProductModelId); // å»éæ¶ä¿ç深度æå¤§çæä½ï¼ååºéåå éå°æ·±å±èç¹ï¼æä»¥ç´æ¥è¦çå³å¯ï¼ uniqueOperationMap.put(key, child); } } } private Map<String, Deque<ProductionOrderRoutingOperation>> buildExistingRoutingOperationBucketMap(List<ProductionOrderRoutingOperation> existingOperationList) { @@ -749,94 +569,8 @@ } } private Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> structureList, Long rootProductModelId) { if (structureList == null || structureList.isEmpty()) { return Collections.emptyMap(); } Map<Long, ProductionBomStructure> structureById = structureList.stream() .filter(item -> item != null && item.getId() != null) .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left)); Map<String, BigDecimal> demandedQuantityMap = new HashMap<>(); Set<String> mergedOutputNodeKeySet = new HashSet<>(); for (ProductionBomStructure bomStructure : structureList) { if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) { continue; } // Resolve the output node first, then read the output node demand for the task plan quantity. ProductionBomStructure outputNode = resolveOperationOutputNode(bomStructure, structureById); Long outputProductModelId = resolveOutputProductModelId(outputNode, rootProductModelId); if (outputProductModelId == null) { continue; } String mergedOutputNodeKey = buildOperationOutputNodeKey( bomStructure.getTechnologyOperationId(), outputNode == null ? null : outputNode.getId(), outputProductModelId); if (!mergedOutputNodeKeySet.add(mergedOutputNodeKey)) { continue; } // Multiple input rows can point to the same output node, so only count that output demand once. String key = buildOperationDemandedQuantityKey(bomStructure.getTechnologyOperationId(), outputProductModelId); demandedQuantityMap.merge(key, defaultDecimal(outputNode == null ? null : outputNode.getDemandedQuantity()), BigDecimal::add); } return demandedQuantityMap; } private BigDecimal resolveTaskPlanQuantity(ProductionOrderRoutingOperation routingOperation, Map<String, BigDecimal> demandedQuantityMap, BigDecimal orderQuantity, Long rootProductModelId) { if (routingOperation == null || demandedQuantityMap == null || demandedQuantityMap.isEmpty()) { return orderQuantity; } Long outputProductModelId = routingOperation.getProductModelId() != null ? routingOperation.getProductModelId() : rootProductModelId; String key = buildOperationDemandedQuantityKey( routingOperation.getTechnologyOperationId(), outputProductModelId); BigDecimal planQuantity = demandedQuantityMap.get(key); return planQuantity != null ? planQuantity : orderQuantity; } private String buildOperationDemandedQuantityKey(Long operationId, Long outputProductModelId) { return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId); } private String buildRoutingOperationBucketKey(Long operationId, Long outputProductModelId) { return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId); } private String buildBomOperationDedupKey(ProductionBomStructure bomStructure, Long outputProductModelId) { Long operationId = bomStructure == null ? null : bomStructure.getTechnologyOperationId(); Long parentId = bomStructure == null ? null : bomStructure.getParentId(); return operationId + "#" + outputProductModelId + "#" + parentId; } private String buildOperationOutputNodeKey(Long operationId, Long outputNodeId, Long outputProductModelId) { return String.valueOf(operationId) + "#" + String.valueOf(outputNodeId) + "#" + String.valueOf(outputProductModelId); } private ProductionBomStructure resolveOperationOutputNode(ProductionBomStructure bomStructure, Map<Long, ProductionBomStructure> structureById) { if (bomStructure == null) { return null; } // The root node is the first output node; other rows use their direct parent as the current operation output. if (bomStructure.getParentId() == null) { return bomStructure; } ProductionBomStructure parent = structureById.get(bomStructure.getParentId()); return parent != null ? parent : bomStructure; } private Long resolveOutputProductModelId(ProductionBomStructure outputNode, Long rootProductModelId) { if (outputNode == null) { return rootProductModelId; } return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId; } private TechnologyOperation getTechnologyOperation(Long technologyOperationId) { @@ -870,20 +604,6 @@ private int compareDecimal(BigDecimal left, BigDecimal right) { return defaultDecimal(left).compareTo(defaultDecimal(right)); } /** * å°æ å½¢ç»ææå¹³æå表ï¼ä¾¿äºç»ä¸ä¿åã */ private void flattenTree(List<ProductionBomStructureDto> source, List<ProductionBomStructureDto> result) { // æå¹³åå¤çæ if (source == null) { return; } for (ProductionBomStructureDto node : source) { result.add(node); flattenTree(node.getChildren(), result); } } } src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
@@ -10,14 +10,20 @@ import com.ruoyi.common.utils.StringUtils; import com.ruoyi.production.bean.dto.ProductionOrderPickDto; import com.ruoyi.production.bean.vo.ProductionOrderPickVo; import com.ruoyi.production.mapper.ProductionBomStructureMapper; import com.ruoyi.production.mapper.ProductionOperationTaskMapper; import com.ruoyi.production.mapper.ProductionOrderMapper; import com.ruoyi.production.mapper.ProductionOrderPickMapper; import com.ruoyi.production.mapper.ProductionOrderPickRecordMapper; import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper; import com.ruoyi.production.pojo.ProductionBomStructure; import com.ruoyi.production.pojo.ProductionOperationTask; import com.ruoyi.production.pojo.ProductionOrder; import com.ruoyi.production.pojo.ProductionOrderPick; import com.ruoyi.production.pojo.ProductionOrderPickRecord; import com.ruoyi.production.pojo.ProductionOrderRoutingOperation; import com.ruoyi.production.service.ProductionOrderPickService; import com.ruoyi.production.service.ProductionOrderService; import com.ruoyi.stock.dto.StockInventoryDto; import com.ruoyi.stock.mapper.StockInventoryMapper; import com.ruoyi.stock.pojo.StockInRecord; @@ -26,6 +32,8 @@ import com.ruoyi.stock.service.StockInventoryService; import com.ruoyi.stock.service.StockInRecordService; import com.ruoyi.stock.service.StockOutRecordService; import com.ruoyi.technology.mapper.TechnologyOperationMapper; import com.ruoyi.technology.pojo.TechnologyOperation; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -53,6 +61,10 @@ private final ProductionOrderMapper productionOrderMapper; private final ProductionOperationTaskMapper productionOperationTaskMapper; private final ProductionOrderPickRecordMapper productionOrderPickRecordMapper; private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper; private final ProductionBomStructureMapper productionBomStructureMapper; private final ProductionOrderService productionOrderService; private final TechnologyOperationMapper technologyOperationMapper; private final StockInventoryMapper stockInventoryMapper; private final StockInventoryService stockInventoryService; private final StockInRecordService stockInRecordService; @@ -83,6 +95,7 @@ // ä¿åé¢æä¸»è®°å½å¿«ç §ã ProductionOrderPick orderPick = new ProductionOrderPick(); orderPick.setProductionOrderId(resolvedDto.getProductionOrderId()); orderPick.setProductionOperationTaskId(resolvedDto.getProductionOperationTaskId()); orderPick.setProductModelId(resolvedDto.getProductModelId()); orderPick.setBatchNo(storedBatchNo); orderPick.setQuantity(resolvedDto.getPickQuantity()); @@ -191,7 +204,237 @@ List<ProductionOrderPickVo> detailList = baseMapper.listPickedDetailByOrderId(productionOrderId); fillBatchNoList(detailList); fillSelectableBatchNoList(detailList); fillPickedFlagAndEffectiveQuantity(detailList); return detailList; } /** * å·¥åºç©ææ¸ å = 订åBOMå¿«ç §ä¸å±äºè¯¥å·¥åºçå¯é¢ç¨ç©æå»ºè®® + 该工åå·²é¢æè®°å½ã * å»ºè®®è¡ picked=falseï¼id 为空ï¼ï¼åå²é¢æè¡ picked=trueã */ @Override public List<ProductionOrderPickVo> listTaskMaterials(Long productionOperationTaskId) { if (productionOperationTaskId == null) { return Collections.emptyList(); } TaskOperationContext context = resolveTaskOperationContext(productionOperationTaskId); List<ProductionOrderPickVo> result = new ArrayList<>(); // BOM å»ºè®®ç©æï¼æå½åå·¥åºè¿æ»¤ï¼ä¸å¸¦ä»»ä½éæ±æ°é List<ProductionOrderPickVo> suggestionList = productionOrderService.pick(context.productionOrderId(), context.technologyOperationId()); for (ProductionOrderPickVo suggestion : suggestionList) { suggestion.setId(null); suggestion.setPicked(false); suggestion.setProductionOrderId(context.productionOrderId()); suggestion.setProductionOperationTaskId(productionOperationTaskId); suggestion.setTechnologyOperationId(context.technologyOperationId()); if (StringUtils.isEmpty(suggestion.getOperationName())) { suggestion.setOperationName(context.operationName()); } suggestion.setWorkOrderNo(context.workOrderNo()); result.add(suggestion); } // 该工å已产ççé¢æè®°å½ List<ProductionOrderPickVo> pickedList = baseMapper.listPickedDetailByTaskId(productionOperationTaskId); fillBatchNoList(pickedList); fillSelectableBatchNoList(pickedList); fillPickedFlagAndEffectiveQuantity(pickedList); result.addAll(pickedList); return result; } /** * å·¥åºç»ä¸é¢æï¼ * 1) ç±å·¥ååæ¥è®¢åä¸å·¥åºï¼ä¸ä¿¡ä»»åç«¯ä¼ å ¥çå½å±åæ®µï¼ * 2) éè¡æ ¡éªç©æãæ°é䏿¹æ¬¡ï¼ * 3) å¤ç¨åºåºç³è¯· + ç«å³å®¡æ¹çåºåæ£åæµç¨ï¼ * 4) åå ¥é¢æä¸»è®°å½ä¸æµæ°´ï¼éå¤é¢æå³æ°å¢è®°å½ï¼ä¸åºååé¢ä¸è¡¥æã */ @Override @Transactional(rollbackFor = Exception.class) public Boolean savePickByTask(ProductionOrderPickDto dto) { if (dto == null) { throw new ServiceException("åæ°ä¸è½ä¸ºç©º"); } Long productionOperationTaskId = dto.getProductionOperationTaskId(); if (productionOperationTaskId == null) { throw new ServiceException("å·¥åIDä¸è½ä¸ºç©º"); } TaskOperationContext context = resolveTaskOperationContext(productionOperationTaskId); List<ProductionOrderPickDto> pickItems = resolvePickItems(dto).stream() .filter(Objects::nonNull) .filter(item -> item.getProductModelId() != null || item.getPickQuantity() != null) .collect(Collectors.toList()); if (pickItems.isEmpty()) { throw new ServiceException("请è³å°å¡«å䏿¡é¢æç©æ"); } for (int i = 0; i < pickItems.size(); i++) { int rowNo = i + 1; ProductionOrderPickDto resolvedDto = mergeDto(dto, pickItems.get(i)); if (resolvedDto.getProductModelId() == null) { throw new ServiceException("第" + rowNo + "è¡åæ°é误ï¼äº§åè§æ ¼ä¸è½ä¸ºç©º"); } if (resolvedDto.getPickQuantity() == null || resolvedDto.getPickQuantity().compareTo(BigDecimal.ZERO) <= 0) { throw new ServiceException("第" + rowNo + "è¡åæ°é误ï¼é¢ææ°éå¿ é¡»å¤§äº0"); } List<String> batchNoList = resolveBatchNoList(resolvedDto); String inventoryBatchNo = pickInventoryBatchNo(batchNoList); String storedBatchNo = formatBatchNoStorage(batchNoList); ProductionOrderPick orderPick = new ProductionOrderPick(); orderPick.setProductionOrderId(context.productionOrderId()); orderPick.setProductionOperationTaskId(productionOperationTaskId); orderPick.setProductModelId(resolvedDto.getProductModelId()); orderPick.setBatchNo(storedBatchNo); orderPick.setQuantity(resolvedDto.getPickQuantity()); orderPick.setRemark(resolvedDto.getRemark()); orderPick.setOperationName(context.operationName()); orderPick.setTechnologyOperationId(context.technologyOperationId()); // æå¹³BOMä¸é ç½®æ°éï¼é¢æä¸åæéæ±æ°é约æ orderPick.setDemandedQuantity(null); orderPick.setBom(isBomMaterial(context, resolvedDto.getProductModelId())); orderPick.setReturned(false); baseMapper.insert(orderPick); // ç«å³æ£ååºåï¼å¤±è´¥åæ´ä¸ªäºå¡åæ» subtractInventory(orderPick.getId(), resolvedDto.getProductModelId(), storedBatchNo, resolvedDto.getPickQuantity(), rowNo, PICK_STOCK_OUT_RECORD_TYPE); insertPickRecord(orderPick.getId(), context.productionOrderId(), productionOperationTaskId, resolvedDto.getProductModelId(), inventoryBatchNo, resolvedDto.getPickQuantity(), BigDecimal.ZERO, resolvedDto.getPickQuantity(), PICK_TYPE_NORMAL, resolvedDto.getRemark(), null); } return true; } /** * å½åå·¥åºæ¯å¦å·²æææé¢æï¼ä»»ä¸ç©æçå颿æ°éï¼é¢æ+è¡¥æ-éæï¼å¤§äº0ã */ @Override public boolean hasEffectivePick(Long productionOperationTaskId) { if (productionOperationTaskId == null) { return false; } List<ProductionOrderPick> pickList = baseMapper.selectList( Wrappers.<ProductionOrderPick>lambdaQuery() .eq(ProductionOrderPick::getProductionOperationTaskId, productionOperationTaskId)); for (ProductionOrderPick pick : pickList) { if (pick == null) { continue; } if (resolveEffectiveQuantity(pick).compareTo(BigDecimal.ZERO) > 0) { return true; } } return false; } /** * ç±å·¥åè§£æåºè®¢åãå·¥åºå¿«ç §ä¸å·¥åºåç§°ï¼å¹¶æ ¡éªå½å±å ³ç³»ã */ private TaskOperationContext resolveTaskOperationContext(Long productionOperationTaskId) { ProductionOperationTask task = productionOperationTaskMapper.selectById(productionOperationTaskId); if (task == null) { throw new ServiceException("å·¥åä¸åå¨"); } if (task.getProductionOrderId() == null) { throw new ServiceException("å·¥åæªå ³èç产订å"); } ProductionOrder productionOrder = productionOrderMapper.selectById(task.getProductionOrderId()); if (productionOrder == null) { throw new ServiceException("ç产订åä¸åå¨"); } ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(task.getProductionOrderRoutingOperationId()); if (routingOperation == null) { throw new ServiceException("å·¥å对åºç订åå·¥åºä¸åå¨"); } if (routingOperation.getProductionOrderId() != null && !Objects.equals(routingOperation.getProductionOrderId(), task.getProductionOrderId())) { throw new ServiceException("å·¥åä¸è®¢åå·¥åºå½å±ä¸ä¸è´"); } if (routingOperation.getTechnologyOperationId() == null) { throw new ServiceException("订åå·¥åºæªå ³èå·¥èºå·¥åº"); } String operationName = routingOperation.getOperationName(); if (StringUtils.isEmpty(operationName)) { TechnologyOperation technologyOperation = technologyOperationMapper.selectById(routingOperation.getTechnologyOperationId()); operationName = technologyOperation == null ? null : technologyOperation.getName(); } return new TaskOperationContext( task.getId(), task.getProductionOrderId(), routingOperation.getTechnologyOperationId(), operationName, task.getWorkOrderNo()); } /** * å¤æç©ææ¯å¦æ¥èªè®¢åBOMå¿«ç §ä¸çå½åå·¥åºã */ private boolean isBomMaterial(TaskOperationContext context, Long productModelId) { if (productModelId == null) { return false; } return productionBomStructureMapper.selectCount( Wrappers.<ProductionBomStructure>lambdaQuery() .eq(ProductionBomStructure::getProductionOrderId, context.productionOrderId()) .eq(ProductionBomStructure::getTechnologyOperationId, context.technologyOperationId()) .eq(ProductionBomStructure::getProductModelId, productModelId)) > 0; } private void fillPickedFlagAndEffectiveQuantity(List<ProductionOrderPickVo> detailList) { if (detailList == null || detailList.isEmpty()) { return; } for (ProductionOrderPickVo detail : detailList) { if (detail == null) { continue; } detail.setPicked(detail.getId() != null); detail.setEffectiveQuantity(resolveEffectiveQuantity(detail)); } } /** * å颿æ°é = 颿æ°é + è¡¥ææ°é - éææ°éã */ private BigDecimal resolveEffectiveQuantity(ProductionOrderPick pick) { if (pick == null) { return BigDecimal.ZERO; } return defaultDecimal(pick.getQuantity()) .add(defaultDecimal(pick.getFeedingQty())) .subtract(defaultDecimal(pick.getReturnQty())); } /** * å·¥å对åºç颿ä¸ä¸æï¼é¿å ä¾èµåç«¯ä¼ å ¥ç订å/å·¥åºå段ã */ private record TaskOperationContext(Long productionOperationTaskId, Long productionOrderId, Long technologyOperationId, String operationName, String workOrderNo) { } private void processDeletePickIds(ProductionOrderPickDto rootDto, @@ -294,6 +537,7 @@ ProductionOrderPick orderPick = new ProductionOrderPick(); orderPick.setProductionOrderId(dto.getProductionOrderId()); orderPick.setProductionOperationTaskId(dto.getProductionOperationTaskId()); orderPick.setProductModelId(dto.getProductModelId()); orderPick.setBatchNo(storedBatchNo); orderPick.setQuantity(dto.getPickQuantity()); src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationServiceImpl.java
@@ -137,38 +137,8 @@ throw new ServiceException("ç产订åä¸åå¨"); } // è·å订åBOM ProductionOrderBom orderBom = productionOrderBomMapper.selectOne( Wrappers.<ProductionOrderBom>lambdaQuery() .eq(ProductionOrderBom::getProductionOrderId, productionOrder.getId())); // ç¡®å®æ ¹äº§åè§æ ¼ID Long rootProductModelId = orderBom != null && orderBom.getProductModelId() != null ? orderBom.getProductModelId() : productionOrder.getProductModelId(); // è·åBOMç»æå表 List<ProductionBomStructure> orderBomStructureList = orderBom == null || orderBom.getId() == null ? Collections.emptyList() : productionBomStructureMapper.selectList( Wrappers.<ProductionBomStructure>lambdaQuery() .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId()) .orderByAsc(ProductionBomStructure::getId)); // æå»ºå·¥åºéæ±éæ å° Map<String, BigDecimal> operationDemandedQuantityMap = TaskPlanQuantityUtil.buildOperationDemandedQuantityMap(orderBomStructureList, rootProductModelId); // è·åå·¥èºè·¯çº¿å·¥åºï¼ç¨äºè®¡ç®è®¡åæ°éï¼ TechnologyRoutingOperation sourceOperation = technologyRoutingOperationMapper.selectById( updatedOperation.getTechnologyRoutingOperationId()); // å°åæ¥çç§ææ¹æ³æ¿æ¢ä¸ºè°ç¨å·¥å ·ç±» BigDecimal planQuantity = TaskPlanQuantityUtil.resolveTaskPlanQuantity( sourceOperation, operationDemandedQuantityMap, productionOrder, rootProductModelId); task.setPlanQuantity(planQuantity); // æå¹³BOMä¸é ç½®æ°éï¼å·¥åè®¡åæ°éç»ä¸åè®¢åæ°é task.setPlanQuantity(TaskPlanQuantityUtil.resolveTaskPlanQuantity(productionOrder)); task.setCompleteQuantity(BigDecimal.ZERO); task.setWorkOrderNo(generateNextTaskNo()); task.setStatus(2); src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
@@ -34,6 +34,7 @@ import com.ruoyi.quality.pojo.QualityInspectFile; import com.ruoyi.quality.pojo.QualityInspectParam; import com.ruoyi.production.service.ProductionOrderService; import com.ruoyi.production.util.TaskPlanQuantityUtil; import com.ruoyi.sales.mapper.SalesLedgerMapper; import com.ruoyi.sales.mapper.SalesLedgerProductMapper; import com.ruoyi.stock.mapper.StockInventoryMapper; @@ -250,18 +251,8 @@ .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId()) .orderByDesc(TechnologyRoutingOperation::getDragSort) .orderByDesc(TechnologyRoutingOperation::getId)); // Build task plan quantities from order BOM snapshot demand instead of recomputing from technology BOM units. Long rootProductModelId = orderBom != null && orderBom.getProductModelId() != null ? orderBom.getProductModelId() : productionOrder.getProductModelId(); List<ProductionBomStructure> orderBomStructureList = orderBom == null || orderBom.getId() == null ? Collections.emptyList() : productionBomStructureMapper.selectList( Wrappers.<ProductionBomStructure>lambdaQuery() .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId()) .orderByAsc(ProductionBomStructure::getId)); Map<String, BigDecimal> operationDemandedQuantityMap = buildOperationDemandedQuantityMap(orderBomStructureList, rootProductModelId); // æå¹³ BOM ä¸é ç½®æ°éï¼å·¥åè®¡åæ°éç»ä¸åè®¢åæ°éã BigDecimal taskPlanQuantity = TaskPlanQuantityUtil.resolveTaskPlanQuantity(productionOrder); Map<Long, String> operationNameMap = technologyOperationMapper.selectBatchIds( // éåå¤çæ°æ®å¹¶ç»è£ ç»æ routingOperations.stream() @@ -295,11 +286,7 @@ ProductionOperationTask task = new ProductionOperationTask(); task.setProductionOrderRoutingOperationId(targetOperation.getId()); task.setProductionOrderId(productionOrder.getId()); task.setPlanQuantity(resolveTaskPlanQuantity( sourceOperation, operationDemandedQuantityMap, productionOrder, rootProductModelId)); task.setPlanQuantity(taskPlanQuantity); task.setCompleteQuantity(BigDecimal.ZERO); task.setWorkOrderNo(generateNextTaskNo()); task.setStatus(2); @@ -334,87 +321,11 @@ return syncedParamCount; } private Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> bomStructures, Long rootProductModelId) { if (bomStructures == null || bomStructures.isEmpty()) { return Collections.emptyMap(); } Map<Long, ProductionBomStructure> structureById = bomStructures.stream() .filter(item -> item != null && item.getId() != null) .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left)); Map<String, BigDecimal> demandedQuantityMap = new HashMap<>(); Set<String> mergedOutputNodeKeySet = new HashSet<>(); for (ProductionBomStructure bomStructure : bomStructures) { if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) { continue; } // The BOM row points to the producing operation; task quantity should come from that operation's output node. ProductionBomStructure outputNode = resolveOperationOutputNode(bomStructure, structureById); Long outputProductModelId = resolveOutputProductModelId(outputNode, rootProductModelId); if (outputProductModelId == null) { continue; } String mergedOutputNodeKey = buildOperationOutputNodeKey( bomStructure.getTechnologyOperationId(), outputNode == null ? null : outputNode.getId(), outputProductModelId); if (!mergedOutputNodeKeySet.add(mergedOutputNodeKey)) { continue; } // demandedQuantity is already the order-level required output quantity for the current output node. BigDecimal demandedQuantity = defaultDecimal(outputNode == null ? null : outputNode.getDemandedQuantity()); String key = buildOperationDemandedQuantityKey(bomStructure.getTechnologyOperationId(), outputProductModelId); demandedQuantityMap.merge(key, demandedQuantity, BigDecimal::add); } return demandedQuantityMap; } private BigDecimal resolveTaskPlanQuantity(TechnologyRoutingOperation sourceOperation, Map<String, BigDecimal> operationDemandedQuantityMap, ProductionOrder productionOrder, Long rootProductModelId) { if (sourceOperation == null || operationDemandedQuantityMap == null || operationDemandedQuantityMap.isEmpty()) { return defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity()); } Long outputProductModelId = sourceOperation.getProductModelId() != null ? sourceOperation.getProductModelId() : rootProductModelId; String key = buildOperationDemandedQuantityKey(sourceOperation.getTechnologyOperationId(), outputProductModelId); BigDecimal planQuantity = operationDemandedQuantityMap.get(key); return planQuantity != null ? planQuantity : defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity()); } private String buildOperationDemandedQuantityKey(Long operationId, Long outputProductModelId) { return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId); } private String buildOperationOutputNodeKey(Long operationId, Long outputNodeId, Long outputProductModelId) { return String.valueOf(operationId) + "#" + String.valueOf(outputNodeId) + "#" + String.valueOf(outputProductModelId); } private ProductionBomStructure resolveOperationOutputNode(ProductionBomStructure bomStructure, Map<Long, ProductionBomStructure> structureById) { if (bomStructure == null) { return null; } // The root node is the first output node; child rows use their direct parent as the current operation output. if (bomStructure.getParentId() == null) { return bomStructure; } ProductionBomStructure parent = structureById.get(bomStructure.getParentId()); return parent != null ? parent : bomStructure; } private Long resolveOutputProductModelId(ProductionBomStructure outputNode, Long rootProductModelId) { if (outputNode == null) { return rootProductModelId; } return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId; } /** * åæ¥è®¢åBOMå¿«ç §ï¼æå¹³å¤å¶âç©æè§æ ¼ + æ¶èå·¥åºâå ³ç³»ã * ä¸åçææåæ ¹èç¹ï¼ä¹ä¸å计ç®åä½ç¨éåéæ±æ°éã */ private ProductionOrderBom syncProductionOrderBomSnapshot(ProductionOrder productionOrder, TechnologyRouting technologyRouting) { // åæ¥è®¢åBOMå¿«ç §ç»æ if (technologyRouting.getBomId() == null) { return null; } @@ -422,41 +333,32 @@ if (technologyBom == null) { throw new ServiceException("å·¥èºBOMä¸åå¨"); } // æ¥è¯¢å¹¶åå¤ä¸å¡æ°æ® // åªåçå®ç©æè¡ï¼å¿ é¡»ææ¶èå·¥åºï¼åå²æåæ ¹èç¹ï¼æ å·¥åºï¼ä¸å±äºç©æ List<TechnologyBomStructure> structureList = technologyBomStructureMapper.selectList( Wrappers.<TechnologyBomStructure>lambdaQuery() .eq(TechnologyBomStructure::getBomId, technologyBom.getId()) .isNotNull(TechnologyBomStructure::getOperationId) .orderByAsc(TechnologyBomStructure::getId)); // éåå¤çæ°æ®å¹¶ç»è£ ç»æ TechnologyBomStructure root = structureList.stream().filter(item -> item.getParentId() == null).findFirst().orElse(null); BigDecimal orderQuantity = defaultDecimal(productionOrder.getQuantity()); ProductionOrderBom orderBom = new ProductionOrderBom(); orderBom.setProductionOrderId(productionOrder.getId()); orderBom.setBomId(Long.valueOf(technologyBom.getId())); orderBom.setProductModelId(root != null ? root.getProductModelId() : productionOrder.getProductModelId()); // BOM 主表æ¬èº«å³ä»£è¡¨æåè§æ ¼ orderBom.setProductModelId(technologyBom.getProductModelId() != null ? technologyBom.getProductModelId() : productionOrder.getProductModelId()); orderBom.setRemark(technologyBom.getRemark()); orderBom.setBomNo(technologyBom.getBomNo()); orderBom.setVersion(technologyBom.getVersion()); // æä¹ åæè¾åºå¤çç»æ productionOrderBomMapper.insert(orderBom); Map<Long, Long> idMap = new HashMap<>(); BigDecimal lastProcessDemandedQuantity = orderQuantity; for (TechnologyBomStructure source : structureList) { // åèç¹ parentId éè¦æ å°ææ°å¿«ç §èç¹ idï¼æè½ä¿çåå§ BOM å±çº§ã ProductionBomStructure target = new ProductionBomStructure(); target.setProductionOrderId(productionOrder.getId()); target.setProductionOrderBomId(orderBom.getId()); target.setParentId(source.getParentId() == null ? null : idMap.get(source.getParentId())); target.setProductModelId(source.getProductModelId()); target.setTechnologyOperationId(source.getOperationId()); target.setUnitQuantity(source.getUnitQuantity()); target.setDemandedQuantity(lastProcessDemandedQuantity.multiply(source.getUnitQuantity())); target.setUnit(source.getUnit()); productionBomStructureMapper.insert(target); idMap.put(source.getId(), target.getId()); lastProcessDemandedQuantity = target.getDemandedQuantity(); } return orderBom; } @@ -1059,12 +961,20 @@ @Override public List<ProductionOrderPickVo> pick(Long productionOrderId) { // æ¥è¯¢è®¢åé¢æãææä¸éææç» return pick(productionOrderId, null); } /** * æè®¢åBOMå¿«ç §ç»åºå¯é¢ç¨ç©æå»ºè®®ã * æå¹³BOMåªæè¿°âç©æè§æ ¼ + æ¶èå·¥åºâï¼å æ¤è¿éä¸å计ç®éæ±æ°éï¼ * ä¼ å ¥å·¥åºIDæ¶åªè¿å该工åºçç©æã */ @Override public List<ProductionOrderPickVo> pick(Long productionOrderId, Long technologyOperationId) { if (productionOrderId == null) { return Collections.emptyList(); } // æ¥è¯¢å¹¶åå¤ä¸å¡æ°æ® ProductionOrderBom orderBom = productionOrderBomMapper.selectOne( Wrappers.<ProductionOrderBom>lambdaQuery() .eq(ProductionOrderBom::getProductionOrderId, productionOrderId) @@ -1074,67 +984,35 @@ return Collections.emptyList(); } // æ¥è¯¢å®æ´çBOMç»æï¼å æ¬æ ¹èç¹ï¼ï¼ç¨äºè®¡ç®å±çº§éæ±æ°é List<ProductionBomStructureVo> bomStructureList = productionBomStructureMapper.listByBomId(orderBom.getId()); if (bomStructureList == null || bomStructureList.isEmpty()) { return Collections.emptyList(); } // æ¥è¯¢ç产订åè·åè®¢åæ°é ProductionOrder productionOrder = productionOrderMapper.selectById(productionOrderId); BigDecimal orderQuantity = productionOrder != null ? defaultDecimal(productionOrder.getQuantity()) : BigDecimal.ZERO; // æå»ºæ å½¢ç»æå¹¶è®¡ç®å±çº§éæ±æ°é Map<Long, ProductionBomStructureVo> structureByIdMap = bomStructureList.stream() .filter(s -> s != null && s.getId() != null) .collect(Collectors.toMap(ProductionBomStructureVo::getId, s -> s)); // æå±çº§è®¡ç®éæ±æ°éï¼åçº§éæ±æ°é = ç¶çº§éæ±æ°é à å级åä½äº§åºæéæ°é for (ProductionBomStructureVo structure : bomStructureList) { if (structure == null) continue; if (structure.getParentId() == null || structure.getParentId() == 0) { // æ ¹èç¹ï¼éæ±æ°é = è®¢åæ°é structure.setDemandedQuantity(orderQuantity); } else { // åèç¹ï¼éæ±æ°é = ç¶çº§éæ±æ°é à å级åä½äº§åºæéæ°é ProductionBomStructureVo parent = structureByIdMap.get(structure.getParentId()); if (parent != null) { BigDecimal parentDemandedQty = defaultDecimal(parent.getDemandedQuantity()); BigDecimal unitQuantity = defaultDecimal(structure.getUnitQuantity()); structure.setDemandedQuantity(parentDemandedQty.multiply(unitQuantity)); } } } // æ¶é订åæåè§æ ¼IDï¼å å«ç产订åæåè§æ ¼ä¸BOMæ ¹èç¹è§æ ¼ï¼ç¨äºæé¤âèªå·±âè¿ä¸ç©æ // æåè§æ ¼ä¸éè¦é¢æï¼å ¼å®¹åå²å¿«ç §ä¸å¯è½åå¨çæåæ ¹èç¹ Set<Long> finishedProductModelIds = new HashSet<>(); if (productionOrder != null && productionOrder.getProductModelId() != null) { finishedProductModelIds.add(productionOrder.getProductModelId()); } bomStructureList.stream() .filter(s -> s != null && (s.getParentId() == null || s.getParentId() == 0)) .map(ProductionBomStructureVo::getProductModelId) .filter(Objects::nonNull) .forEach(finishedProductModelIds::add); if (orderBom.getProductModelId() != null) { finishedProductModelIds.add(orderBom.getProductModelId()); } // æ¶éææè¢«å¼ç¨ä¸ºç¶çº§çèç¹IDï¼BOM仿åä¸éå·¥åºå¾éé ç½®åä¸éå·¥åºï¼ // ä¸é´èç¹æ¯ä¸ä¸éå·¥åºç产åºï¼èªå¶ï¼ï¼åªææ¯æ¡åæ¯æéå±çå¶åèç¹ææ¯çæ£éè¦é¢ç¨çç©æã Set<Long> parentNodeIds = bomStructureList.stream() .filter(s -> s != null && s.getParentId() != null && s.getParentId() != 0) .map(ProductionBomStructureVo::getParentId) List<ProductionBomStructureVo> materialStructureList = bomStructureList.stream() .filter(Objects::nonNull) .collect(Collectors.toSet()); // è¿æ»¤åºå¶åèç¹ï¼å®é 颿项ï¼ï¼éæ ¹èç¹ãéä¸é´èç¹ï¼å¹¶æé¤ä¸è®¢åæååè§æ ¼çç©æï¼èªå¶æåæ éé¢æï¼ List<ProductionBomStructureVo> childStructureList = bomStructureList.stream() .filter(s -> s != null && s.getParentId() != null && s.getParentId() != 0) .filter(s -> s.getId() == null || !parentNodeIds.contains(s.getId())) .filter(s -> s.getProductModelId() == null || !finishedProductModelIds.contains(s.getProductModelId())) .filter(s -> s.getProductModelId() != null && !finishedProductModelIds.contains(s.getProductModelId())) .filter(s -> s.getTechnologyOperationId() != null) .filter(s -> technologyOperationId == null || technologyOperationId.equals(s.getTechnologyOperationId())) .collect(Collectors.toList()); if (materialStructureList.isEmpty()) { return Collections.emptyList(); } // éåå¤çæ°æ®å¹¶ç»è£ ç»æ List<Long> productModelIds = childStructureList.stream() List<Long> productModelIds = materialStructureList.stream() .map(ProductionBomStructureVo::getProductModelId) .filter(Objects::nonNull) .distinct() @@ -1160,31 +1038,26 @@ } Map<String, ProductionOrderPickVo> mergedPickMap = new LinkedHashMap<>(); for (ProductionBomStructureVo structure : childStructureList) { if (structure == null || structure.getProductModelId() == null) { for (ProductionBomStructureVo structure : materialStructureList) { Long productModelId = structure.getProductModelId(); String mergeKey = structure.getTechnologyOperationId() + "#" + productModelId; if (mergedPickMap.containsKey(mergeKey)) { continue; } Long productModelId = structure.getProductModelId(); String mergeKey = String.valueOf(structure.getTechnologyOperationId()) + "#" + productModelId; ProductionOrderPickVo vo = mergedPickMap.get(mergeKey); if (vo == null) { vo = new ProductionOrderPickVo(); vo.setProductModelId(productModelId); vo.setOperationName(structure.getOperationName()); vo.setTechnologyOperationId(structure.getTechnologyOperationId()); vo.setProductName(structure.getProductName()); vo.setModel(structure.getModel()); vo.setDemandedQuantity(BigDecimal.ZERO); vo.setUnit(structure.getUnit()); List<String> batchNoList = stockBatchNoMap.get(productModelId) == null ? Collections.emptyList() : new ArrayList<>(stockBatchNoMap.get(productModelId)); vo.setBatchNoList(batchNoList); vo.setStockQuantity(stockQuantityMap.getOrDefault(productModelId, BigDecimal.ZERO)); vo.setBom(true); mergedPickMap.put(mergeKey, vo); } vo.setDemandedQuantity(defaultDecimal(vo.getDemandedQuantity()).add(defaultDecimal(structure.getDemandedQuantity()))); ProductionOrderPickVo vo = new ProductionOrderPickVo(); vo.setProductModelId(productModelId); vo.setOperationName(structure.getOperationName()); vo.setTechnologyOperationId(structure.getTechnologyOperationId()); vo.setProductName(structure.getProductName()); vo.setModel(structure.getModel()); vo.setUnit(structure.getUnit()); List<String> batchNoList = stockBatchNoMap.get(productModelId) == null ? Collections.emptyList() : new ArrayList<>(stockBatchNoMap.get(productModelId)); vo.setBatchNoList(batchNoList); vo.setStockQuantity(stockQuantityMap.getOrDefault(productModelId, BigDecimal.ZERO)); vo.setBom(true); mergedPickMap.put(mergeKey, vo); } return new ArrayList<>(mergedPickMap.values()); } src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -16,11 +16,11 @@ import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.bean.BeanUtils; import com.ruoyi.procurementrecord.utils.StockUtils; import com.ruoyi.production.bean.dto.ProductStructureDto; import com.ruoyi.production.bean.dto.ProductionProductMainDto; import com.ruoyi.production.enums.ProductOrderStatusEnum; import com.ruoyi.production.mapper.*; import com.ruoyi.production.pojo.*; import com.ruoyi.production.service.ProductionOrderPickService; import com.ruoyi.production.service.ProductionProductMainService; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysUserMapper; @@ -65,10 +65,10 @@ private final ProductionAccountMapper productionAccountMapper; private final ProductionOperationTaskMapper productionOperationTaskMapper; private final ProductionOrderMapper productionOrderMapper; private final ProductionOrderBomMapper productionOrderBomMapper; private final ProductionBomStructureMapper productionBomStructureMapper; private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper; private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper; private final ProductionOrderPickMapper productionOrderPickMapper; private final ProductionOrderPickService productionOrderPickService; private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper; private final TechnologyOperationMapper technologyOperationMapper; private final StockUtils stockUtils; @@ -255,6 +255,11 @@ if (productionOrder == null) { throw new ServiceException("ç产订åä¸åå¨"); } // æ¥å·¥åç½®æ¡ä»¶ï¼å½åå·¥åºå¿ 须已ç»é¢æï¼ä»»ä¸ç©æå颿æ°é大äº0ï¼ã // è¯¥æ ¡éªå¨äºå¡å æ§è¡ï¼ç»è¿åç«¯ç´æ¥è°ç¨æ¥å£åæ ·åéã if (!productionOrderPickService.hasEffectivePick(taskId)) { throw new ServiceException("å½åå·¥åºæªé¢æï¼æ æ³æ¥å·¥"); } TechnologyRoutingOperation technologyRoutingOperation = technologyRoutingOperationMapper.selectById(routingOperation.getTechnologyRoutingOperationId()); TechnologyOperation technologyOperation = technologyRoutingOperation == null ? null : technologyOperationMapper.selectById(technologyRoutingOperation.getTechnologyOperationId()); @@ -274,24 +279,9 @@ productionProductMainMapper.insert(productionProductMain); syncOperationParamInputValue(dto, routingOperation.getId(), productionProductMain.getId()); List<ProductStructureDto> productStructureDtos = resolveInputStructures( productionOrder.getId(), routingOperation, productModel.getId()); // å¦ææ²¡æbomåèç¹äºï¼é£ä¹æå ¥å°±æ¯ä»æ¬èº« if (productStructureDtos.isEmpty()) { ProductStructureDto fallbackInput = new ProductStructureDto(); fallbackInput.setProductModelId(productModel.getId()); fallbackInput.setUnitQuantity(BigDecimal.ONE); productStructureDtos.add(fallbackInput); } for (ProductStructureDto item : productStructureDtos) { // å½åå®ç°æå·¥åºæåç´æ¥ä½ä¸ºæå ¥ï¼åç»è¥æ¥å ¥é¢æè®°å½å¯å¨è¿éæ¿æ¢æ¥æºã ProductionProductInput productionProductInput = new ProductionProductInput(); productionProductInput.setProductionProductMainId(productionProductMain.getId()); productionProductInput.setProductModelId(item.getProductModelId()); productionProductInput.setInputQuantity(item.getUnitQuantity().multiply(defaultDecimal(dto.getQuantity()))); productionProductInput.setQuantity(productionProductInput.getInputQuantity()); productionProductInputMapper.insert(productionProductInput); } // æ¥å·¥æå ¥æ¥æºäºå½åå·¥åºçå®é é¢æè®°å½ï¼ä¸åæ BOM åä½ç¨é à æ¥å·¥æ°éæ¨ç®ã // æ¯æ¡ææé¢æåªä¼è¢«é¦æ¬¡æ¥å·¥å½é䏿¬¡ï¼é夿¥å·¥ä¸ä¼éå¤è®¡å ¥å䏿¹ç©æã insertInputsFromTaskPicks(taskId, productionProductMain.getId()); ProductionProductOutput productionProductOutput = new ProductionProductOutput(); productionProductOutput.setProductionProductMainId(productionProductMain.getId()); @@ -503,62 +493,54 @@ return target; } private List<ProductStructureDto> resolveInputStructures(Long productionOrderId, ProductionOrderRoutingOperation routingOperation, Long outputProductModelId) { if (productionOrderId == null || routingOperation == null || routingOperation.getTechnologyOperationId() == null) { return new ArrayList<>(); /** * ç¨å½åå·¥åºå°æªè¢«æ¥å·¥å½éçå®é é¢æçææ¥å·¥æå ¥ã * æå ¥æ°éåå颿æ°éï¼é¢æ+è¡¥æ-éæï¼ï¼å¹¶è®°å½æ¥æºé¢æIDï¼ * ä¿è¯åä¸ç¬é¢æä¸ä¼å¨å¤æ¬¡æ¥å·¥ä¸è¢«éå¤è®¡å ¥ã */ private void insertInputsFromTaskPicks(Long productionOperationTaskId, Long productionProductMainId) { if (productionOperationTaskId == null || productionProductMainId == null) { return; } ProductionOrderBom orderBom = productionOrderBomMapper.selectOne( Wrappers.<ProductionOrderBom>lambdaQuery() .eq(ProductionOrderBom::getProductionOrderId, productionOrderId) .orderByDesc(ProductionOrderBom::getId) .last("limit 1")); if (orderBom == null || orderBom.getId() == null) { return new ArrayList<>(); List<ProductionOrderPick> taskPickList = productionOrderPickMapper.selectList( Wrappers.<ProductionOrderPick>lambdaQuery() .eq(ProductionOrderPick::getProductionOperationTaskId, productionOperationTaskId) .orderByAsc(ProductionOrderPick::getId)); if (taskPickList.isEmpty()) { return; } List<Long> pickIdList = taskPickList.stream() .map(ProductionOrderPick::getId) .filter(Objects::nonNull) .collect(Collectors.toList()); Set<Long> consumedPickIdSet = pickIdList.isEmpty() ? Collections.emptySet() : productionProductInputMapper.selectList( Wrappers.<ProductionProductInput>lambdaQuery() .in(ProductionProductInput::getProductionOrderPickId, pickIdList)) .stream() .map(ProductionProductInput::getProductionOrderPickId) .filter(Objects::nonNull) .collect(Collectors.toSet()); List<ProductionBomStructure> bomNodeList = productionBomStructureMapper.selectList( Wrappers.<ProductionBomStructure>lambdaQuery() .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId()) .orderByAsc(ProductionBomStructure::getId)); if (bomNodeList.isEmpty()) { return new ArrayList<>(); } Map<Long, ProductionBomStructure> nodeMap = bomNodeList.stream() .filter(item -> item != null && item.getId() != null) .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left)); Long currentOutputModelId = routingOperation.getProductModelId() != null ? routingOperation.getProductModelId() : outputProductModelId; Map<Long, BigDecimal> unitQtyByProductModel = new LinkedHashMap<>(); for (ProductionBomStructure node : bomNodeList) { if (node == null || node.getParentId() == null || node.getProductModelId() == null) { for (ProductionOrderPick pick : taskPickList) { if (pick == null || pick.getId() == null || consumedPickIdSet.contains(pick.getId())) { continue; } if (!Objects.equals(node.getTechnologyOperationId(), routingOperation.getTechnologyOperationId())) { BigDecimal effectiveQuantity = defaultDecimal(pick.getQuantity()) .add(defaultDecimal(pick.getFeedingQty())) .subtract(defaultDecimal(pick.getReturnQty())); if (effectiveQuantity.compareTo(BigDecimal.ZERO) <= 0) { continue; } ProductionBomStructure parent = nodeMap.get(node.getParentId()); if (parent == null || !Objects.equals(parent.getProductModelId(), currentOutputModelId)) { continue; } unitQtyByProductModel.merge(node.getProductModelId(), defaultDecimal(node.getUnitQuantity()), BigDecimal::add); ProductionProductInput productionProductInput = new ProductionProductInput(); productionProductInput.setProductionProductMainId(productionProductMainId); productionProductInput.setProductModelId(pick.getProductModelId()); productionProductInput.setProductionOrderPickId(pick.getId()); productionProductInput.setInputQuantity(effectiveQuantity); productionProductInput.setQuantity(effectiveQuantity); productionProductInputMapper.insert(productionProductInput); } List<ProductStructureDto> result = new ArrayList<>(); for (Map.Entry<Long, BigDecimal> entry : unitQtyByProductModel.entrySet()) { if (entry.getValue().compareTo(BigDecimal.ZERO) <= 0) { continue; } ProductStructureDto item = new ProductStructureDto(); item.setProductModelId(entry.getKey()); item.setUnitQuantity(entry.getValue()); result.add(item); } return result; } private Boolean removeProductMainByProductionTask(ProductionProductMain productionProductMain) { src/main/java/com/ruoyi/production/util/TaskPlanQuantityUtil.java
@@ -1,130 +1,23 @@ package com.ruoyi.production.util; import com.ruoyi.production.pojo.ProductionBomStructure; import com.ruoyi.production.pojo.ProductionOrder; import com.ruoyi.production.pojo.ProductionOrderRoutingOperation; import com.ruoyi.technology.pojo.TechnologyRoutingOperation; import lombok.experimental.UtilityClass; import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; /** * å·¥åè®¡åæ°é计ç®å·¥å ·ç±» * å·¥åè®¡åæ°é计ç®å·¥å ·ç±»ã * æå¹³ BOM åªç»´æ¤âç©æè§æ ¼ + æ¶èå·¥åºâï¼ä¸é ç½®ä»»ä½æ°éï¼ * å æ¤å·¥åºå·¥åçè®¡åæ°éç»ä¸åç产订åçä¸åæ°éã */ @UtilityClass public class TaskPlanQuantityUtil { /** * 计ç®å·¥åè®¡åæ°éï¼ä½¿ç¨ TechnologyRoutingOperationï¼ * æçäº§è®¢åæ°éè§£æå·¥åè®¡åæ°éã */ public BigDecimal resolveTaskPlanQuantity(TechnologyRoutingOperation sourceOperation, Map<String, BigDecimal> operationDemandedQuantityMap, ProductionOrder productionOrder, Long rootProductModelId) { if (sourceOperation == null || operationDemandedQuantityMap == null || operationDemandedQuantityMap.isEmpty()) { return defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity()); } Long outputProductModelId = sourceOperation.getProductModelId() != null ? sourceOperation.getProductModelId() : rootProductModelId; String key = buildOperationDemandedQuantityKey(sourceOperation.getTechnologyOperationId(), outputProductModelId); BigDecimal planQuantity = operationDemandedQuantityMap.get(key); return planQuantity != null ? planQuantity : defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity()); } /** * 计ç®å·¥åè®¡åæ°éï¼ä½¿ç¨ ProductionOrderRoutingOperationï¼ */ public BigDecimal resolveTaskPlanQuantity(ProductionOrderRoutingOperation routingOperation, Map<String, BigDecimal> demandedQuantityMap, BigDecimal orderQuantity, Long rootProductModelId) { if (routingOperation == null || demandedQuantityMap == null || demandedQuantityMap.isEmpty()) { return orderQuantity; } Long outputProductModelId = routingOperation.getProductModelId() != null ? routingOperation.getProductModelId() : rootProductModelId; String key = buildOperationDemandedQuantityKey(routingOperation.getTechnologyOperationId(), outputProductModelId); BigDecimal planQuantity = demandedQuantityMap.get(key); return planQuantity != null ? planQuantity : orderQuantity; } /** * æå»ºå·¥åºéæ±éæ å°è¡¨ */ public Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> bomStructures, Long rootProductModelId) { if (bomStructures == null || bomStructures.isEmpty()) { return Collections.emptyMap(); } Map<Long, ProductionBomStructure> structureById = new HashMap<>(); for (ProductionBomStructure item : bomStructures) { if (item != null && item.getId() != null) { structureById.put(item.getId(), item); } } Map<String, BigDecimal> demandedQuantityMap = new HashMap<>(); Set<String> mergedOutputNodeKeySet = new HashSet<>(); for (ProductionBomStructure bomStructure : bomStructures) { if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) { continue; } ProductionBomStructure outputNode = resolveOperationOutputNode(bomStructure, structureById); Long outputProductModelId = resolveOutputProductModelId(outputNode, rootProductModelId); if (outputProductModelId == null) { continue; } String mergedOutputNodeKey = buildOperationOutputNodeKey(bomStructure.getTechnologyOperationId(), outputNode == null ? null : outputNode.getId(), outputProductModelId); if (!mergedOutputNodeKeySet.add(mergedOutputNodeKey)) { continue; } BigDecimal demandedQuantity = defaultDecimal(outputNode == null ? null : outputNode.getDemandedQuantity()); String key = buildOperationDemandedQuantityKey(bomStructure.getTechnologyOperationId(), outputProductModelId); demandedQuantityMap.merge(key, demandedQuantity, BigDecimal::add); } return demandedQuantityMap; } /** * æå»ºå·¥åºéæ±ékey */ public String buildOperationDemandedQuantityKey(Long operationId, Long outputProductModelId) { return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId); } /** * æå»ºè¾åºèç¹key */ public String buildOperationOutputNodeKey(Long operationId, Long outputNodeId, Long outputProductModelId) { return String.valueOf(operationId) + "#" + String.valueOf(outputNodeId) + "#" + String.valueOf(outputProductModelId); } /** * è§£æå·¥åºè¾åºèç¹ */ public ProductionBomStructure resolveOperationOutputNode(ProductionBomStructure bomStructure, Map<Long, ProductionBomStructure> structureById) { if (bomStructure == null) { return null; } if (bomStructure.getParentId() == null) { return bomStructure; } ProductionBomStructure parent = structureById.get(bomStructure.getParentId()); return parent != null ? parent : bomStructure; } /** * è§£æè¾åºäº§åè§æ ¼ID */ public Long resolveOutputProductModelId(ProductionBomStructure outputNode, Long rootProductModelId) { if (outputNode == null) { return rootProductModelId; } return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId; public BigDecimal resolveTaskPlanQuantity(ProductionOrder productionOrder) { return defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity()); } /** src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
@@ -308,29 +308,30 @@ } int count = 0; StringBuilder stringBuffer = new StringBuilder(); // æå¹³BOMåªç»´æ¤âç©æè§æ ¼ + æ¶èå·¥åºâï¼ä¸é ç½®ç¨éï¼ // å æ¤è¿éåªæ ¡éªæ¯ä¸ªç©ææ¯å¦æå¯ç¨åºåï¼ä¸åæåä½ç¨éæ ¸ç®éæ±æ°éã for (TechnologyBomStructureVo structure : structureList) { if (structure.getParentId() == null) { if (structure == null || structure.getProductModelId() == null) { continue; } StockInventory stockInventory = stockInventoryMapper.selectOne( new QueryWrapper<StockInventory>().lambda().eq(StockInventory::getProductModelId, structure.getProductModelId())); if (stockInventory == null) { count++; stringBuffer.append(structure.getProductName()).append("-").append(structure.getModel()).append("åºåä¸è¶³").append(System.lineSeparator()); continue; List<StockInventory> stockList = stockInventoryMapper.selectList( new QueryWrapper<StockInventory>().lambda() .eq(StockInventory::getProductModelId, structure.getProductModelId())); BigDecimal available = BigDecimal.ZERO; for (StockInventory stockInventory : stockList) { if (stockInventory == null) { continue; } BigDecimal quantity = stockInventory.getQualitity() == null ? BigDecimal.ZERO : stockInventory.getQualitity(); BigDecimal locked = stockInventory.getLockedQuantity() == null ? BigDecimal.ZERO : stockInventory.getLockedQuantity(); available = available.add(quantity.subtract(locked)); } BigDecimal required = salesLedgerProduct.getQuantity().multiply(structure.getUnitQuantity() == null ? BigDecimal.ZERO : structure.getUnitQuantity()); BigDecimal remain = stockInventory.getQualitity() .subtract(stockInventory.getLockedQuantity()) .subtract(required) .divide(BigDecimal.ONE, 2, RoundingMode.CEILING); if (remain.compareTo(BigDecimal.ZERO) < 0) { if (available.compareTo(BigDecimal.ZERO) <= 0) { count++; stringBuffer.append(structure.getProductName()) .append("-") .append(structure.getModel()) .append("åºåä¸è¶³ï¼å°") .append(remain.abs()) .append("æ å¯ç¨åºå") .append(System.lineSeparator()); } } src/main/java/com/ruoyi/technology/bean/dto/BomImportDto.java
@@ -3,33 +3,32 @@ import com.ruoyi.framework.aspectj.lang.annotation.Excel; import lombok.Data; import java.math.BigDecimal; /** * BOM å¯¼å ¥å¯¼åºè¡ï¼æå¹³ç»æï¼ä¸è¡ = æå + ä¸ä¸ªç©æè§æ ¼ + è¯¥ç©æçæ¶èå·¥åºã * ä¸åå å«å±çº§ï¼ç¶é¡¹/å项ï¼ååä½ç¨éã */ @Data public class BomImportDto { @Excel(name = "ç¶é¡¹äº§åç¼å·") private String parentCode; @Excel(name = "æå产åç¼å·") private String productCode; @Excel(name = "ç¶é¡¹äº§ååç§°") private String parentName; @Excel(name = "æå产ååç§°") private String productName; @Excel(name = "ç¶é¡¹äº§åè§æ ¼") private String parentSpec; @Excel(name = "æå产åè§æ ¼") private String productSpec; @Excel(name = "å项产åç¼å·") private String childCode; @Excel(name = "ç©æäº§åç¼å·") private String materialCode; @Excel(name = "å项产ååç§°") private String childName; @Excel(name = "ç©æäº§ååç§°") private String materialName; @Excel(name = "å项产åè§æ ¼") private String childSpec; @Excel(name = "ç©æäº§åè§æ ¼") private String materialSpec; @Excel(name = "åä½ç¨é") private BigDecimal unitQty; @Excel(name = "ææå·¥åº") @Excel(name = "æ¶èå·¥åº") private String process; @Excel(name = "夿³¨") src/main/java/com/ruoyi/technology/bean/dto/TechnologyBomStructureDto.java
@@ -23,9 +23,10 @@ @Schema(description = "è§æ ¼åå·") private String model; private String tempId; private String parentTempId; private List<TechnologyBomStructureDto> children; /** * BOM åªç»´æ¤æå¹³çâç©æè§æ ¼ + æ¶èå·¥åºâå ³ç³»ï¼ä¸åæå±çº§åæ°éã * æç»é¡¹åªéè¦ idï¼æ´æ°æ¶ï¼ãproductModelIdãoperationIdã */ @Schema(description = "æå¹³ç©ææç»å表") private List<TechnologyBomStructureDto> detailList; } src/main/java/com/ruoyi/technology/bean/dto/TechnologyRoutingOperationDto.java
@@ -5,6 +5,8 @@ import lombok.Data; import lombok.EqualsAndHashCode; import java.util.List; @EqualsAndHashCode(callSuper = true) @Data @Schema(name = "TechnologyRoutingOperationDto对象", description = "å·¥èºè·¯çº¿å·¥åºæ¥è¯¢åæ°") @@ -12,4 +14,11 @@ @Schema(description = "å·¥èºè·¯çº¿idï¼å ¼å®¹æ§åæ° routeIdï¼") private Long routeId; /** * å·¥èºè·¯çº¿å·¥åºå ¨éä¿ååè¡¨ï¼ææäº¤é¡ºåºä½ä¸ºå·¥åºé¡ºåºï¼ * æ¯é¡¹åªéè¦ idï¼æ´æ°æ¶ï¼ä¸ technologyOperationIdã */ @Schema(description = "å·¥èºè·¯çº¿å·¥åºå表ï¼å ¨éä¿åï¼") private List<TechnologyRoutingOperationDto> operationList; } src/main/java/com/ruoyi/technology/bean/vo/TechnologyBomStructureVo.java
@@ -5,8 +5,9 @@ import lombok.Data; import lombok.EqualsAndHashCode; import java.util.List; /** * BOM æç»å±ç¤ºå¯¹è±¡ï¼æå¹³ç»æï¼ä¸è¡ = ä¸ä¸ªç©æè§æ ¼ + å ¶æ¶èå·¥åºã */ @Data @EqualsAndHashCode(callSuper = true) public class TechnologyBomStructureVo extends TechnologyBomStructure { @@ -25,6 +26,4 @@ @Schema(description = "产åç¼ç ") private String productCode; private List<TechnologyBomStructureVo> children; } src/main/java/com/ruoyi/technology/controller/TechnologyBomStructureController.java
@@ -22,14 +22,14 @@ private final TechnologyBomStructureService technologyBomStructureService; @PostMapping @Operation(summary = "æ°å¢æä¿®æ¹BOMç»æ") @Log(title = "æ°å¢æä¿®æ¹BOMç»æ", businessType = BusinessType.INSERT) @Operation(summary = "å ¨éä¿åBOMæå¹³ç©ææç»") @Log(title = "å ¨éä¿åBOMæå¹³ç©ææç»", businessType = BusinessType.INSERT) public R addOrUpdate(@RequestBody TechnologyBomStructureDto technologyBomStructureDto) { return R.ok(technologyBomStructureService.addTechnologyBomStructure(technologyBomStructureDto)); } @GetMapping("/listByBomId/{bomId}") @Operation(summary = "æ ¹æ®BOMæ¥è¯¢ç»ææ ") @Operation(summary = "æ ¹æ®BOMæ¥è¯¢æå¹³ç©ææç»") public R<List<TechnologyBomStructureVo>> listByBomId(@PathVariable Long bomId) { return R.ok(technologyBomStructureService.listByBomId(bomId)); } src/main/java/com/ruoyi/technology/controller/TechnologyRoutingOperationController.java
@@ -51,6 +51,13 @@ } @Log(title = "å·¥èºè·¯çº¿å·¥åº", businessType = BusinessType.UPDATE) @Operation(summary = "å ¨éä¿åå·¥èºè·¯çº¿å·¥åº") @PostMapping("/saveRouteOperations") public R saveRouteOperations(@RequestBody TechnologyRoutingOperationDto technologyRoutingOperationDto) { return R.ok(technologyRoutingOperationService.saveRouteOperations(technologyRoutingOperationDto)); } @Log(title = "å·¥èºè·¯çº¿å·¥åº", businessType = BusinessType.UPDATE) @Operation(summary = "ä¿®æ¹å·¥èºè·¯çº¿å·¥åº") @PutMapping public R edit(@RequestBody TechnologyRoutingOperation technologyRoutingOperation) { src/main/java/com/ruoyi/technology/service/TechnologyRoutingOperationService.java
@@ -20,6 +20,11 @@ boolean saveTechnologyRoutingOperation(TechnologyRoutingOperation technologyRoutingOperation); /** * å ¨éä¿åå·¥èºè·¯çº¿å·¥åºï¼ææäº¤é¡ºåºç¡®å®å·¥åºé¡ºåºï¼æªåä¼ çå·¥åºä¼è¢«å é¤ã */ boolean saveRouteOperations(TechnologyRoutingOperationDto dto); boolean removeTechnologyRoutingOperation(Long id); int sort(TechnologyRoutingOperation technologyRoutingOperation); src/main/java/com/ruoyi/technology/service/impl/TechnologyBomServiceImpl.java
@@ -1,6 +1,5 @@ package com.ruoyi.technology.service.impl; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; @@ -12,19 +11,18 @@ import com.ruoyi.basic.service.IProductService; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.bean.BeanUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.framework.web.domain.R; import com.ruoyi.technology.bean.dto.BomImportDto; import com.ruoyi.technology.bean.dto.TechnologyBomDto; import com.ruoyi.technology.bean.dto.TechnologyBomStructureDto; import com.ruoyi.technology.bean.vo.TechnologyBomStructureVo; import com.ruoyi.technology.bean.vo.TechnologyBomVo; import com.ruoyi.technology.mapper.TechnologyBomMapper; import com.ruoyi.technology.mapper.TechnologyBomStructureMapper; import com.ruoyi.technology.mapper.TechnologyOperationMapper; import com.ruoyi.technology.mapper.TechnologyRoutingMapper; import com.ruoyi.technology.pojo.TechnologyBom; import com.ruoyi.technology.pojo.TechnologyBomStructure; import com.ruoyi.technology.pojo.TechnologyOperation; import com.ruoyi.technology.pojo.TechnologyRouting; import com.ruoyi.technology.service.TechnologyBomService; import com.ruoyi.technology.service.TechnologyBomStructureService; @@ -34,10 +32,12 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.math.BigDecimal; import java.util.*; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @Service @RequiredArgsConstructor @@ -48,7 +48,7 @@ private final TechnologyBomStructureService technologyBomStructureService; private final TechnologyRoutingMapper technologyRoutingMapper; private final IProductService productService; private final TechnologyBomStructureMapper technologyBomStructureMapper; private final TechnologyOperationMapper technologyOperationMapper; /** * å页æ¥è¯¢BOMå表ã @@ -84,7 +84,7 @@ } /** * æ°å¢BOMå¹¶åå§åæ ¹ç»æèç¹ã * æ°å¢BOMãBOM主表æ¬èº«å³ä»£è¡¨æåè§æ ¼ï¼æç»è¡¨ä¸ååå ¥æåæ ¹èç¹ã */ @Override @Transactional(rollbackFor = Exception.class) @@ -96,12 +96,12 @@ } technologyBom.setBomNo("BM." + String.format("%05d", technologyBom.getId())); technologyBomMapper.updateById(technologyBom); initRootStructure(technologyBom.getId().longValue(), technologyBom.getProductModelId()); return R.ok(); } /** * ä¿®æ¹BOMï¼è§æ ¼å忶忥巿°å ³èç»æä¸è·¯çº¿æ°æ®ã * ä¿®æ¹BOMãæåè§æ ¼å忶忥巿°å·¥èºè·¯çº¿çæåè§æ ¼ï¼ * å¹¶æ¸ ç䏿°æåè§æ ¼å²çªçç©æè¡ï¼æåä¸è½ä½ä¸ºèªèº«ç©æï¼ï¼å ¶ä½ç©æ-å·¥åºå ³ç³»ä¿çã */ @Override @Transactional(rollbackFor = Exception.class) @@ -117,8 +117,8 @@ if (oldBom.getProductModelId() != null && !oldBom.getProductModelId().equals(technologyBom.getProductModelId())) { technologyRoutingMapper.updateProductModelByBomId(technologyBom.getProductModelId(), technologyBom.getId().longValue()); technologyBomStructureService.remove(Wrappers.<TechnologyBomStructure>lambdaQuery() .eq(TechnologyBomStructure::getBomId, technologyBom.getId().longValue())); initRootStructure(technologyBom.getId().longValue(), technologyBom.getProductModelId()); .eq(TechnologyBomStructure::getBomId, technologyBom.getId().longValue()) .eq(TechnologyBomStructure::getProductModelId, technologyBom.getProductModelId())); } if (technologyBom.getBomNo() == null) { technologyBom.setBomNo(oldBom.getBomNo()); @@ -160,19 +160,9 @@ } /** * åå§åBOMæ ¹èç¹ç»æã * å¯¼å ¥æå¹³BOMï¼æ¯è¡ = æå + ä¸ä¸ªç©æè§æ ¼ + æ¶èå·¥åºã * å䏿¬¡å¯¼å ¥çææè¡å¿ é¡»å±äºåä¸ä¸ªæåï¼æ§çç¶å/åä½ç¨é模æ¿ä¼è¢«æç¡®æç»ã */ private void initRootStructure(Long bomId, Long productModelId) { ProductModel productModel = productModelService.getById(productModelId); TechnologyBomStructure root = new TechnologyBomStructure(); root.setBomId(bomId); root.setParentId(null); root.setProductModelId(productModelId); root.setUnit(productModel.getUnit()); root.setUnitQuantity(BigDecimal.ONE); technologyBomStructureService.save(root); } @Override @Transactional(rollbackFor = Exception.class) public R uploadBom(MultipartFile file) { @@ -184,159 +174,135 @@ return R.fail("Excelè§£æå¤±è´¥"); } if (list == null || list.isEmpty()) return R.fail("æ°æ®ä¸ºç©º"); if (list == null || list.isEmpty()) { return R.fail("æ°æ®ä¸ºç©º"); } // å¤çå·¥åº list.forEach(dto -> { dto.setParentName(clean(dto.getParentName())); dto.setParentSpec(clean(dto.getParentSpec())); dto.setChildName(clean(dto.getChildName())); dto.setChildSpec(clean(dto.getChildSpec())); dto.setProductName(clean(dto.getProductName())); dto.setProductSpec(clean(dto.getProductSpec())); dto.setMaterialName(clean(dto.getMaterialName())); dto.setMaterialSpec(clean(dto.getMaterialSpec())); dto.setProcess(clean(dto.getProcess())); }); handleProcess(list); // Map<String, Long> processMap = productProcessService.list().stream() // .collect(Collectors.toMap(ProductProcess::getName, ProductProcess::getId, (k1, k2) -> k1)); // å建 BOM æ°æ® boolean templateMatched = list.stream().anyMatch(dto -> StringUtils.isNotBlank(dto.getProductName()) && StringUtils.isNotBlank(dto.getMaterialName())); if (!templateMatched) { return R.fail("BOMå¯¼å ¥æ¨¡æ¿å·²æ´æ°ä¸ºæå¹³ç»æï¼è¯·ä½¿ç¨ææ°æ¨¡æ¿ï¼æå产ååç§°/è§æ ¼ãç©æäº§ååç§°/è§æ ¼ãæ¶èå·¥åºï¼"); } BomImportDto first = list.get(0); ProductModel rootModel = findModel(first.getParentName(), first.getParentSpec()); ProductModel productModel = findModel(first.getProductName(), first.getProductSpec()); TechnologyBom bom = new TechnologyBom(); bom.setProductModelId(rootModel.getId()); bom.setProductModelId(productModel.getId()); bom.setVersion("1.0"); bom.setRemark(first.getRemark()); technologyBomMapper.insert(bom); bom.setBomNo("BM." + String.format("%05d", bom.getId())); technologyBomMapper.updateById(bom); // è®°å½å·²ç»æå ¥ç»æçèç¹ï¼Key = "åç§°+è§æ ¼", Value = structure_id Map<String, Long> treePathMap = new HashMap<>(); Map<String, Long> operationIdByName = new HashMap<>(); for (TechnologyOperation operation : technologyOperationMapper.selectList(null)) { if (StringUtils.isNotBlank(operation.getName())) { operationIdByName.putIfAbsent(operation.getName().trim(), operation.getId()); } } Set<String> relationKeys = new HashSet<>(); for (int i = 0; i < list.size(); i++) { BomImportDto dto = list.get(i); String parentKey = dto.getParentName() + "|" + dto.getParentSpec(); String childKey = dto.getChildName() + "|" + dto.getChildSpec(); //å¤çæ ¹èç¹,第ä¸è¡ä¸å项为空 if (i == 0 && StringUtils.isBlank(dto.getChildName())) { TechnologyBomStructure rootNode = new TechnologyBomStructure(); rootNode.setBomId(bom.getId().longValue()); rootNode.setParentId(null); // 顶屿²¡æç¶èç¹ rootNode.setProductModelId(rootModel.getId()); rootNode.setUnitQuantity(BigDecimal.ONE); rootNode.setUnit(rootModel.getUnit()); technologyBomStructureService.save(rootNode); treePathMap.put(parentKey, rootNode.getId()); continue; int rowNo = i + 2; if (StringUtils.isBlank(dto.getMaterialName())) { throw new ServiceException("å¯¼å ¥å¤±è´¥ï¼ç¬¬" + rowNo + "è¡ç¼ºå°ç©æäº§ååç§°"); } if (StringUtils.isNotBlank(dto.getProductName()) && !sameModel(dto, first)) { throw new ServiceException("å¯¼å ¥å¤±è´¥ï¼ç¬¬" + rowNo + "è¡çæåä¸ç¬¬ä¸è¡ä¸ä¸è´ï¼ä¸æ¬¡å¯¼å ¥åªè½ç»´æ¤ä¸ä¸ªBOM"); } if (StringUtils.isBlank(dto.getProcess())) { throw new ServiceException("å¯¼å ¥å¤±è´¥ï¼ç¬¬" + rowNo + "è¡ç¼ºå°æ¶èå·¥åº"); } Long operationId = operationIdByName.get(dto.getProcess()); if (operationId == null) { throw new ServiceException("å¯¼å ¥å¤±è´¥ï¼å·¥åºæªç»´æ¤[" + dto.getProcess() + "]ï¼ç¬¬" + rowNo + "è¡ï¼"); } // å¤çåå±çº§èç¹ // æ¾å°ç¶èç¹å¨æ°æ®åºéç ID Long parentStructureId = treePathMap.get(parentKey); if (parentStructureId == null) { // 妿 Map éæ¾ä¸å°ï¼è¯´æ Excel 顺åºä¹±äºæè æ°æ®æè¯¯ throw new ServiceException("å¯¼å ¥å¤±è´¥: ç¶é¡¹[" + dto.getParentName() + "]å¿ é¡»å¨å ¶å项ä¹åå®ä¹"); ProductModel materialModel = findModel(dto.getMaterialName(), dto.getMaterialSpec()); if (materialModel.getId().equals(productModel.getId())) { throw new ServiceException("å¯¼å ¥å¤±è´¥ï¼ç¬¬" + rowNo + "è¡æåè§æ ¼ä¸è½ä½ä¸ºèªèº«çç©æ"); } if (!relationKeys.add(materialModel.getId() + "#" + operationId)) { throw new ServiceException("å¯¼å ¥å¤±è´¥ï¼ç¬¬" + rowNo + "è¡ç©æä¸å·¥åºéå¤é ç½®"); } // è·åå项模åä¿¡æ¯ ProductModel childModel = findModel(dto.getChildName(), dto.getChildSpec()); // æå ¥ç»æè¡¨ TechnologyBomStructure node = new TechnologyBomStructure(); node.setBomId(bom.getId().longValue()); node.setParentId(parentStructureId); // ç¶èç¹ID node.setProductModelId(childModel.getId()); node.setUnitQuantity(dto.getUnitQty()); node.setUnit(childModel.getUnit()); // if (processMap.containsKey(dto.getProcess())) { // node.setProcessId(processMap.get(dto.getProcess())); // } node.setProductModelId(materialModel.getId()); node.setOperationId(operationId); technologyBomStructureService.save(node); // æå½åå项记å½å° Map,ä½ä¸ºä»¥åæ´æ·±å±çº§çç¶é¡¹æ¥æ¾ä¾æ® // åä¸ç¶é¡¹ä¸çååå项ä¸éè¦éå¤è®°å½ treePathMap.put(childKey, node.getId()); } return R.ok("BOMå¯¼å ¥æå"); } /** * å¯¼åºæå¹³BOMï¼æå + ç©æ + æ¶èå·¥åºã */ @Override public void exportBom(HttpServletResponse response, Long bomId) { if (bomId == null) { throw new ServiceException("BOM IDä¸è½ä¸ºç©º"); } TechnologyBom bom = technologyBomMapper.selectById(bomId); if (bom == null) { throw new ServiceException("BOMä¸åå¨"); } List<TechnologyBomStructureVo> treeData = technologyBomStructureService.listByBomId(bomId); if (treeData == null || treeData.isEmpty()) { List<TechnologyBomStructureVo> detailList = technologyBomStructureService.listByBomId(bomId); if (detailList == null || detailList.isEmpty()) { return; } // å°æ å½¢ç»ææå¹³å ä½¿ç¨ BFSç®æ³ 导åº,æå±çº§é¡ºåº List<BomImportDto> exportList = new ArrayList<>(); ProductModel productModel = productModelService.getById(bom.getProductModelId()); String productName = null; if (productModel != null && productModel.getProductId() != null) { Product product = productService.getById(productModel.getProductId()); productName = product == null ? null : product.getProductName(); } // Map<ID, Node> idMap ç¨äºæ¥æ¾ç¶èç¹ Map<Long, TechnologyBomStructureVo> idMap = new HashMap<>(); populateMap(treeData, idMap); // treeData ç第ä¸ä¸ªæ¯æ ¹èç¹ for (TechnologyBomStructureVo root : treeData) { // æ·»å æ ¹èç¹ BomImportDto rootRow = new BomImportDto(); rootRow.setParentName(root.getProductName()); rootRow.setParentSpec(root.getModel()); rootRow.setUnitQty(root.getUnitQuantity()); rootRow.setParentCode(root.getProductCode()); rootRow.setRemark(""); exportList.add(rootRow); // BFS éå-éå Queue<TechnologyBomStructureVo> queue = new LinkedList<>(); if (root.getChildren() != null) { queue.addAll(root.getChildren()); } while (!queue.isEmpty()) { TechnologyBomStructureVo child = queue.poll(); // æ¥æ¾ç¶èç¹ TechnologyBomStructureVo parent = idMap.get(child.getParentId()); if (parent == null) { // é¤äºæå¤å±èç¹,å ¶ä»èç¹çç¶ç±»è¯å®æ¯ä¸ä¼ä¸ºç©ºç continue; } BomImportDto row = new BomImportDto(); // ç¶ç±»ä¿¡æ¯ row.setParentName(parent.getProductName()); row.setParentSpec(parent.getModel()); // åç±»ä¿¡æ¯ row.setChildName(child.getProductName()); row.setChildSpec(child.getModel()); row.setUnitQty(child.getUnitQuantity()); row.setProcess(child.getOperationName()); row.setChildCode(child.getProductCode()); exportList.add(row); // å°åèç¹çåèç¹å å ¥éå-ä¸ä¸å± if (child.getChildren() != null && !child.getChildren().isEmpty()) { queue.addAll(child.getChildren()); } } List<BomImportDto> exportList = new ArrayList<>(detailList.size()); for (TechnologyBomStructureVo detail : detailList) { BomImportDto row = new BomImportDto(); row.setProductName(productName); row.setProductSpec(productModel == null ? null : productModel.getModel()); row.setProductCode(productModel == null ? null : productModel.getProductCode()); row.setMaterialName(detail.getProductName()); row.setMaterialSpec(detail.getModel()); row.setMaterialCode(detail.getProductCode()); row.setProcess(detail.getOperationName()); exportList.add(row); } ExcelUtil<BomImportDto> util = new ExcelUtil<>(BomImportDto.class); util.exportExcel(response, exportList, "BOMç»æå¯¼åº"); } /** * å¤å¶BOMï¼å¤å¶æåè§æ ¼ä¸å ¨é¨æå¹³ç©æ-å·¥åºå ³ç³»ã */ @Override @Transactional(rollbackFor = Exception.class) public R copy(TechnologyBom technologyBom) { if (technologyBom == null || technologyBom.getId() == null) { throw new ServiceException("BOM IDä¸è½ä¸ºç©º"); } TechnologyBom oldTechnologyBom = technologyBomMapper.selectById(technologyBom.getId()); List<TechnologyBomStructureVo> oldTechnologyBomStructureVos = technologyBomStructureService.listByBomId(technologyBom.getId().longValue()); //æ ¡éªäº§åè§æ ¼æ¯å¦åå¨ã if (oldTechnologyBom == null) { throw new ServiceException("BOMä¸åå¨"); } validateProductModel(oldTechnologyBom.getProductModelId()); TechnologyBom newTechnologyBom = new TechnologyBom(); newTechnologyBom.setProductModelId(oldTechnologyBom.getProductModelId()); newTechnologyBom.setVersion("FZ" + oldTechnologyBom.getVersion()); @@ -347,14 +313,30 @@ } newTechnologyBom.setBomNo("BM." + String.format("%05d", newTechnologyBom.getId())); technologyBomMapper.updateById(newTechnologyBom); //åå§åBOMæ ¹èç¹ç»æã initRootStructure(newTechnologyBom.getId().longValue(), newTechnologyBom.getProductModelId()); //æäº§åç»æéé¢çæ°æ®ä¹å ¨é¨é½å¤å¶ TechnologyBomStructureVo technologyBomStructureVo = oldTechnologyBomStructureVos.get(0); TechnologyBomStructureDto technologyBomStructureDto = convertTree(technologyBomStructureVo); technologyBomStructureDto.setBomId(newTechnologyBom.getId().longValue()); technologyBomStructureService.addTechnologyBomStructure(technologyBomStructureDto); List<TechnologyBomStructureVo> oldDetailList = technologyBomStructureService.listByBomId(technologyBom.getId().longValue()); if (oldDetailList != null && !oldDetailList.isEmpty()) { List<TechnologyBomStructure> newDetailList = new ArrayList<>(oldDetailList.size()); for (TechnologyBomStructureVo oldDetail : oldDetailList) { TechnologyBomStructure newDetail = new TechnologyBomStructure(); newDetail.setBomId(newTechnologyBom.getId().longValue()); newDetail.setProductModelId(oldDetail.getProductModelId()); newDetail.setOperationId(oldDetail.getOperationId()); newDetailList.add(newDetail); } technologyBomStructureService.saveBatch(newDetailList); } return R.ok(); } private boolean sameModel(BomImportDto row, BomImportDto first) { return StringUtils.equals(row.getProductName(), first.getProductName()) && StringUtils.equals(defaultBlank(row.getProductSpec()), defaultBlank(first.getProductSpec())); } private String defaultBlank(String value) { return value == null ? "" : value; } private ProductModel findModel(String name, String spec) { @@ -369,118 +351,8 @@ return model; } private void handleProcess(List<BomImportDto> list) { Set<String> processNames = list.stream() .map(BomImportDto::getProcess) .filter(StringUtils::isNotBlank) .collect(Collectors.toSet()); if (processNames.isEmpty()) { return; } // List<ProductProcess> exists = productProcessService.list( // new LambdaQueryWrapper<ProductProcess>().in(ProductProcess::getName, processNames) // ); // Set<String> existNames = exists.stream() // .map(ProductProcess::getName) // .collect(Collectors.toSet()); // // List<ProductProcess> needSave = processNames.stream() // .filter(n -> !existNames.contains(n)) // .map(n -> { // ProductProcess p = new ProductProcess(); // p.setName(n); // return p; // }) // .collect(Collectors.toList()); // // if (!needSave.isEmpty()) { // productProcessService.saveBatch(needSave); // needSave.forEach(p -> p.setNo("GX" + String.format("%08d", p.getId()))); // productProcessService.updateBatchById(needSave); // } } private String clean(String s) { if (s == null) return null; return s.replaceAll("[\\u00A0\\u3000]", "").trim(); } private void populateMap(List<TechnologyBomStructureVo> nodes, Map<Long, TechnologyBomStructureVo> map) { if (nodes == null || nodes.isEmpty()) { return; } for (TechnologyBomStructureVo node : nodes) { map.put(node.getId(), node); populateMap(node.getChildren(), map); } } /** * éå½è½¬æ¢æ å½¢ç»æ VO -> DTO * èªå¨çæèæ tempId / parentTempIdï¼ä¿è¯ä»»æå±çº§æ ç»ææ£ç¡® */ public static TechnologyBomStructureDto convertTree(TechnologyBomStructureVo vo) { if (vo == null) { return null; } TechnologyBomStructureDto realDto = convertNode(vo, "0"); // æ ¹èç¹ç¶ID=0ï¼çº¯æ°åï¼ TechnologyBomStructureDto rootDto = new TechnologyBomStructureDto(); rootDto.setTempId("0"); rootDto.setChildren(Collections.singletonList(realDto)); return rootDto; } /** * æ ¸å¿é彿¹æ³ * @param vo åå§èç¹ * @param parentTempId ç¶èç¹ çº¯æ°åID * @return 转æ¢åDTO */ private static TechnologyBomStructureDto convertNode(TechnologyBomStructureVo vo, String parentTempId) { if (vo == null) { return null; } TechnologyBomStructureDto dto = new TechnologyBomStructureDto(); BeanUtils.copyProperties(vo, dto); String currentTempId = getNumberId(); dto.setTempId(currentTempId); dto.setParentTempId(parentTempId); dto.setId(null); dto.setParentId(null); // ===================== éå½åèç¹ ===================== List<TechnologyBomStructureVo> voChildren = vo.getChildren(); if (CollUtil.isNotEmpty(voChildren)) { List<TechnologyBomStructureDto> dtoChildren = new ArrayList<>(); for (TechnologyBomStructureVo childVo : voChildren) { // åèç¹çç¶ID = å½åèç¹çæ°åID dtoChildren.add(convertNode(childVo, currentTempId)); } dto.setChildren(dtoChildren); } else { dto.setChildren(new ArrayList<>()); } return dto; } /** * çæ 13ä½ çº¯æ°åéæºIDï¼å®å ¨ãä¸éå¤ã髿§è½ï¼ */ private static String getNumberId() { // çæ 1000000000000 ~ 9999999999999 ä¹é´çæ°å long min = 1000000000000L; long max = 9999999999999L; long randomNum = ThreadLocalRandom.current().nextLong(min, max + 1); return String.valueOf(randomNum); } } src/main/java/com/ruoyi/technology/service/impl/TechnologyBomStructureServiceImpl.java
@@ -2,135 +2,149 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.basic.mapper.ProductModelMapper; import com.ruoyi.basic.pojo.ProductModel; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.technology.bean.dto.TechnologyBomStructureDto; import com.ruoyi.technology.bean.vo.TechnologyBomStructureVo; import com.ruoyi.technology.mapper.TechnologyBomMapper; import com.ruoyi.technology.mapper.TechnologyBomStructureMapper; import com.ruoyi.technology.mapper.TechnologyOperationMapper; import com.ruoyi.technology.pojo.TechnologyBom; import com.ruoyi.technology.pojo.TechnologyBomStructure; import com.ruoyi.technology.service.TechnologyBomStructureService; import lombok.RequiredArgsConstructor; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @Service @RequiredArgsConstructor public class TechnologyBomStructureServiceImpl extends ServiceImpl<TechnologyBomStructureMapper, TechnologyBomStructure> implements TechnologyBomStructureService { private final TechnologyBomStructureMapper technologyBomStructureMapper; private final TechnologyBomMapper technologyBomMapper; private final ProductModelMapper productModelMapper; private final TechnologyOperationMapper technologyOperationMapper; /** * ä¿åBOMç»ææ ï¼å¤çæ°å¢ãæ´æ°åå é¤èç¹ã * å ¨é忥 BOM çæå¹³ç©ææç»ï¼ä¸è¡åªè¡¨ç¤ºâæä¸ªç©æè§æ ¼å¨æéå·¥åºè¢«æ¶èâã * ä¸åæç¶åå±çº§ãåä½ç¨éãéæ±æ°éååä½é ç½®ã */ @Override @Transactional(rollbackFor = Exception.class) public Boolean addTechnologyBomStructure(TechnologyBomStructureDto dto) { Long bomId = dto.getBomId(); List<TechnologyBomStructureDto> flatDtoList = new ArrayList<>(); flattenTree(dto.getChildren(), flatDtoList); if (bomId == null) { throw new ServiceException("BOM IDä¸è½ä¸ºç©º"); } TechnologyBom bom = technologyBomMapper.selectById(bomId); if (bom == null) { throw new ServiceException("BOMä¸åå¨"); } List<TechnologyBomStructureDto> detailList = dto.getDetailList() == null ? Collections.emptyList() : dto.getDetailList(); List<TechnologyBomStructure> dbList = this.list(new LambdaQueryWrapper<TechnologyBomStructure>() .eq(TechnologyBomStructure::getBomId, bomId)); Set<Long> frontendIds = new HashSet<>(); for (TechnologyBomStructureDto item : flatDtoList) { if (item.getId() != null) { frontendIds.add(item.getId()); } } Set<Long> deleteIds = new HashSet<>(); Set<Long> dbIds = new HashSet<>(); for (TechnologyBomStructure dbItem : dbList) { if (!frontendIds.contains(dbItem.getId())) { deleteIds.add(dbItem.getId()); } } if (!deleteIds.isEmpty()) { this.removeByIds(deleteIds); dbIds.add(dbItem.getId()); } Set<Long> submittedIds = new HashSet<>(); Set<String> relationKeys = new HashSet<>(); List<TechnologyBomStructure> insertList = new ArrayList<>(); List<TechnologyBomStructure> updateList = new ArrayList<>(); Map<String, TechnologyBomStructure> tempEntityMap = new HashMap<>(); for (TechnologyBomStructureDto item : flatDtoList) { for (TechnologyBomStructureDto item : detailList) { Long productModelId = item.getProductModelId(); Long operationId = item.getOperationId(); validateMaterial(bom, productModelId); validateOperation(operationId); if (!relationKeys.add(productModelId + "#" + operationId)) { throw new ServiceException("åä¸ç©æè§æ ¼å¨åä¸å·¥åºä¸ä¸è½éå¤é ç½®"); } TechnologyBomStructure entity = new TechnologyBomStructure(); BeanUtils.copyProperties(item, entity); entity.setBomId(bomId); entity.setProductModelId(productModelId); entity.setOperationId(operationId); // æå¹³ BOM ä¸å使ç¨å±çº§åæ°éè¯ä¹ï¼åå²åç»ä¸ç½®ç©ºã entity.setParentId(null); entity.setUnitQuantity(null); entity.setDemandedQuantity(null); entity.setUnit(null); if (item.getId() == null) { entity.setParentId(null); insertList.add(entity); tempEntityMap.put(item.getTempId(), entity); } else { if (!dbIds.contains(item.getId())) { throw new ServiceException("BOMç©ææç»ä¸å卿ä¸å±äºå½åBOM"); } entity.setId(item.getId()); submittedIds.add(item.getId()); updateList.add(entity); } } if (!insertList.isEmpty()) { this.saveBatch(insertList); } List<TechnologyBomStructure> parentFixList = new ArrayList<>(); for (TechnologyBomStructureDto item : flatDtoList) { if (item.getId() == null && item.getParentTempId() != null) { TechnologyBomStructure child = tempEntityMap.get(item.getTempId()); if (child == null) { continue; } TechnologyBomStructure parent = tempEntityMap.get(item.getParentTempId()); Long realParentId = parent != null ? parent.getId() : Long.valueOf(item.getParentTempId()); child.setParentId(realParentId); parentFixList.add(child); } } if (!parentFixList.isEmpty()) { this.updateBatchById(parentFixList); Set<Long> deleteIds = new HashSet<>(dbIds); deleteIds.removeAll(submittedIds); if (!deleteIds.isEmpty()) { this.removeByIds(deleteIds); } if (!updateList.isEmpty()) { this.updateBatchById(updateList); } if (!insertList.isEmpty()) { this.saveBatch(insertList); } return true; } /** * æ ¹æ®BOMæ¥è¯¢å¹¶ç»è£ ç»ææ ã * æ ¹æ®BOMæ¥è¯¢æå¹³ç©ææç»ï¼æç¨³å®é¡ºåºè¿åã */ @Override public List<TechnologyBomStructureVo> listByBomId(Long bomId) { List<TechnologyBomStructureVo> list = technologyBomStructureMapper.listByBomId(bomId); Map<Long, TechnologyBomStructureVo> map = new HashMap<>(); for (TechnologyBomStructureVo node : list) { node.setChildren(new ArrayList<>()); map.put(node.getId(), node); if (bomId == null) { return new ArrayList<>(); } List<TechnologyBomStructureVo> tree = new ArrayList<>(); for (TechnologyBomStructureVo node : list) { Long parentId = node.getParentId(); if (parentId == null || parentId == 0L) { tree.add(node); continue; } TechnologyBomStructureVo parent = map.get(parentId); if (parent != null) { parent.getChildren().add(node); } } return tree; return technologyBomStructureMapper.listByBomId(bomId); } /** * å°æ å½¢ç»ææå¹³æå表ï¼ä¾¿äºç»ä¸ä¿åã * æ ¡éªç©æè§æ ¼ï¼å¿ é¡»åå¨ï¼ä¸ä¸è½æ¯ BOM èªèº«çæåè§æ ¼ã */ private void flattenTree(List<TechnologyBomStructureDto> source, List<TechnologyBomStructureDto> result) { if (source == null) { return; private void validateMaterial(TechnologyBom bom, Long productModelId) { if (productModelId == null) { throw new ServiceException("è¯·éæ©ç©æè§æ ¼"); } for (TechnologyBomStructureDto node : source) { result.add(node); flattenTree(node.getChildren(), result); ProductModel productModel = productModelMapper.selectById(productModelId); if (productModel == null) { throw new ServiceException("ç©æè§æ ¼ä¸åå¨"); } if (productModelId.equals(bom.getProductModelId())) { throw new ServiceException("æåè§æ ¼ä¸è½ä½ä¸ºèªèº«çç©æ"); } } /** * æ ¡éªæ¶èå·¥åºå¿ å¡«ä¸åå¨ã */ private void validateOperation(Long operationId) { if (operationId == null) { throw new ServiceException("è¯·éæ©æ¶èå·¥åº"); } if (technologyOperationMapper.selectById(operationId) == null) { throw new ServiceException("æ¶èå·¥åºä¸åå¨"); } } } src/main/java/com/ruoyi/technology/service/impl/TechnologyRoutingOperationServiceImpl.java
@@ -4,19 +4,30 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.technology.bean.dto.TechnologyRoutingOperationDto; import com.ruoyi.technology.bean.dto.TechnologyRoutingOperationParamSyncDto; import com.ruoyi.technology.bean.vo.TechnologyRoutingOperationVo; import com.ruoyi.technology.mapper.TechnologyOperationMapper; import com.ruoyi.technology.mapper.TechnologyRoutingMapper; import com.ruoyi.technology.mapper.TechnologyRoutingOperationMapper; import com.ruoyi.technology.mapper.TechnologyRoutingOperationParamMapper; import com.ruoyi.technology.pojo.TechnologyOperation; import com.ruoyi.technology.pojo.TechnologyRouting; import com.ruoyi.technology.pojo.TechnologyRoutingOperation; import com.ruoyi.technology.pojo.TechnologyRoutingOperationParam; import com.ruoyi.technology.service.TechnologyRoutingOperationParamService; import com.ruoyi.technology.service.TechnologyRoutingOperationService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @Service @Transactional(rollbackFor = Exception.class) @@ -26,6 +37,8 @@ private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper; private final TechnologyRoutingMapper technologyRoutingMapper; private final TechnologyOperationMapper technologyOperationMapper; private final TechnologyRoutingOperationParamMapper technologyRoutingOperationParamMapper; private final TechnologyRoutingOperationParamService technologyRoutingOperationParamService; @Override @@ -63,6 +76,93 @@ return saved; } /** * å ¨éä¿åå·¥èºè·¯çº¿å·¥åºï¼ * 1) ææäº¤é¡ºåºä½ä¸ºå·¥åºé¡ºåºï¼dragSort ä» 1 å¼å§ï¼ï¼ * 2) æ¯éå·¥åºåªå 许åºç°ä¸æ¬¡ï¼ä¸âæBOMå·¥åºå»éçæâä¿æä¸è´ï¼ * 3) å·¥åºäº§åºè§æ ¼åºå®ä¸ºå·¥èºè·¯çº¿çæåè§æ ¼ï¼ * 4) æªåä¼ çå·¥åºè¿ååæ°å¿«ç §ä¸å¹¶å é¤ã */ @Override public boolean saveRouteOperations(TechnologyRoutingOperationDto dto) { if (dto == null) { throw new ServiceException("åæ°ä¸è½ä¸ºç©º"); } Long technologyRoutingId = dto.getTechnologyRoutingId() != null ? dto.getTechnologyRoutingId() : dto.getRouteId(); if (technologyRoutingId == null) { throw new ServiceException("å·¥èºè·¯çº¿IDä¸è½ä¸ºç©º"); } TechnologyRouting technologyRouting = technologyRoutingMapper.selectById(technologyRoutingId); if (technologyRouting == null) { throw new ServiceException("å·¥èºè·¯çº¿ä¸åå¨"); } List<TechnologyRoutingOperationDto> operationList = dto.getOperationList() == null ? Collections.emptyList() : dto.getOperationList(); List<TechnologyRoutingOperation> dbList = this.list(Wrappers.<TechnologyRoutingOperation>lambdaQuery() .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRoutingId)); Set<Long> dbIds = dbList.stream().map(TechnologyRoutingOperation::getId).collect(Collectors.toSet()); Set<Long> submittedIds = new HashSet<>(); Set<Long> submittedOperationIds = new HashSet<>(); List<TechnologyRoutingOperation> insertList = new ArrayList<>(); List<TechnologyRoutingOperation> updateList = new ArrayList<>(); int dragSort = 1; for (TechnologyRoutingOperationDto item : operationList) { Long technologyOperationId = item.getTechnologyOperationId(); if (technologyOperationId == null) { throw new ServiceException("请为ææå·¥åºéæ©å·¥èºå·¥åº"); } TechnologyOperation technologyOperation = technologyOperationMapper.selectById(technologyOperationId); if (technologyOperation == null) { throw new ServiceException("å·¥èºå·¥åºä¸åå¨"); } if (!submittedOperationIds.add(technologyOperationId)) { throw new ServiceException("åä¸éå·¥åºä¸è½éå¤åºç°å¨å·¥èºè·¯çº¿ä¸ï¼" + technologyOperation.getName()); } TechnologyRoutingOperation entity = new TechnologyRoutingOperation(); entity.setTechnologyRoutingId(technologyRoutingId); entity.setProductModelId(technologyRouting.getProductModelId()); entity.setTechnologyOperationId(technologyOperationId); entity.setDragSort(dragSort++); entity.setIsQuality(technologyOperation.getIsQuality()); entity.setIsProduction(technologyOperation.getIsProduction()); entity.setType(technologyOperation.getType()); if (item.getId() == null) { insertList.add(entity); } else { if (!dbIds.contains(item.getId())) { throw new ServiceException("å·¥èºè·¯çº¿å·¥åºä¸å卿ä¸å±äºå½åå·¥èºè·¯çº¿"); } entity.setId(item.getId()); submittedIds.add(item.getId()); updateList.add(entity); } } Set<Long> deleteIds = new HashSet<>(dbIds); deleteIds.removeAll(submittedIds); if (!deleteIds.isEmpty()) { // å é¤å·¥åºæ¶åæ¶æ¸ çå ¶åæ°å¿«ç §ï¼é¿å çä¸å¤ç«æ°æ® technologyRoutingOperationParamMapper.delete(Wrappers.<TechnologyRoutingOperationParam>lambdaQuery() .in(TechnologyRoutingOperationParam::getTechnologyRoutingOperationId, deleteIds)); for (Long deleteId : deleteIds) { removeTechnologyRoutingOperation(deleteId); } } if (!updateList.isEmpty()) { this.updateBatchById(updateList); } for (TechnologyRoutingOperation insert : insertList) { saveTechnologyRoutingOperation(insert); } return true; } @Override public boolean removeTechnologyRoutingOperation(Long id) { TechnologyRoutingOperation technologyRoutingOperation = this.getById(id); src/main/java/com/ruoyi/technology/service/impl/TechnologyRoutingServiceImpl.java
@@ -20,10 +20,9 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @Service @@ -105,9 +104,10 @@ /** * æå½åéæ©ç BOM é建工èºè·¯çº¿å·¥åºï¼ * 1. å å é¤è¯¥å·¥èºè·¯çº¿ä¸å·²æå·¥åº * 2. 读å BOM ç»æä¸ç operation_id * 3. æ BOM åºç°é¡ºåºå»éåéæ°åå ¥å·¥èºè·¯çº¿å·¥åºè¡¨ * 1. å å é¤è¯¥å·¥èºè·¯çº¿ä¸å·²æå·¥åºååæ°å¿«ç § * 2. 读åæå¹³ BOM æç»ä¸ç operation_id * 3. æ BOM ä¸é¦æ¬¡åºç°ç顺åºå¯¹å·¥åºå»éï¼æ¯éå·¥åºçæä¸æ¡è·¯çº¿å·¥åº * 4. æ¯æ¡è·¯çº¿å·¥åºç产åºè§æ ¼åºå®ä¸ºå·¥èºè·¯çº¿çæåè§æ ¼ */ private void syncRoutingOperationsByBom(TechnologyRouting technologyRouting) { List<TechnologyRoutingOperation> oldRoutingOperationList = technologyRoutingOperationMapper.selectList( @@ -127,65 +127,32 @@ Wrappers.<TechnologyBomStructure>lambdaQuery() .eq(TechnologyBomStructure::getBomId, technologyRouting.getBomId()) .isNotNull(TechnologyBomStructure::getOperationId) .orderByDesc(TechnologyBomStructure::getId) .orderByAsc(TechnologyBomStructure::getId) ); if (bomStructures.isEmpty()) { throw new ServiceException("bom产åç»æä¸ºç©ºï¼"); } // åä¸ä¸ª BOM ä¸å¯è½éå¤å¼ç¨ç¸åå·¥åºï¼æç §ä¸ä¸å±çç¶èç¹çäº§åæ¯å¦ç¸ååå·¥åºæ¯å¦ç¸å Map<Long, TechnologyBomStructure> structureById = new HashMap<>(); // æå¹³ BOM ä¸å¤ä¸ªç©æå¯ä»¥æååä¸éå·¥åºï¼è¿éåªæå·¥åºå»é Set<Long> uniqueOperationIdList = new LinkedHashSet<>(); for (TechnologyBomStructure bomStructure : bomStructures) { if (bomStructure != null && bomStructure.getId() != null) { structureById.put(bomStructure.getId(), bomStructure); } } Map<String, TechnologyBomStructure> uniqueOperationMap = new LinkedHashMap<>(); for (TechnologyBomStructure bomStructure : bomStructures) { Long outputProductModelId = resolveOutputProductModelId(bomStructure, structureById, technologyRouting.getProductModelId()); uniqueOperationMap.putIfAbsent(buildBomOperationDedupKey(bomStructure, outputProductModelId), bomStructure); uniqueOperationIdList.add(bomStructure.getOperationId()); } int dragSort = 1; for (TechnologyBomStructure bomStructure : uniqueOperationMap.values()) { for (Long operationId : uniqueOperationIdList) { TechnologyRoutingOperation routingOperation = new TechnologyRoutingOperation(); routingOperation.setTechnologyRoutingId(technologyRouting.getId()); routingOperation.setProductModelId(resolveOutputProductModelId(bomStructure, structureById, technologyRouting.getProductModelId())); routingOperation.setTechnologyOperationId(bomStructure.getOperationId()); routingOperation.setProductModelId(technologyRouting.getProductModelId()); routingOperation.setTechnologyOperationId(operationId); routingOperation.setDragSort(dragSort++); TechnologyOperation technologyOperation = getOperation(bomStructure.getOperationId()); TechnologyOperation technologyOperation = getOperation(operationId); routingOperation.setIsQuality(technologyOperation != null ? technologyOperation.getIsQuality() : null); routingOperation.setIsProduction(technologyOperation != null ? technologyOperation.getIsProduction() : null); routingOperation.setType(technologyOperation != null ? technologyOperation.getType() : null); technologyRoutingOperationMapper.insert(routingOperation); syncRoutingOperationParams(routingOperation.getId(), bomStructure.getOperationId()); syncRoutingOperationParams(routingOperation.getId(), operationId); } } private String buildBomOperationDedupKey(TechnologyBomStructure bomStructure, Long outputProductModelId) { Long operationId = bomStructure == null ? null : bomStructure.getOperationId(); Long parentId = bomStructure == null ? null : bomStructure.getParentId(); return operationId + "#" + outputProductModelId + "#" + parentId; } private Long resolveOutputProductModelId(TechnologyBomStructure bomStructure, Map<Long, TechnologyBomStructure> structureById, Long routingProductModelId) { if (bomStructure == null) { return routingProductModelId; } Long parentId = bomStructure.getParentId(); if (parentId == null) { return routingProductModelId != null ? routingProductModelId : bomStructure.getProductModelId(); } TechnologyBomStructure parent = structureById.get(parentId); if (parent != null && parent.getProductModelId() != null) { return parent.getProductModelId(); } return routingProductModelId != null ? routingProductModelId : bomStructure.getProductModelId(); } private void syncRoutingOperationParams(Long technologyRoutingOperationId, Long technologyOperationId) { src/main/resources/mapper/production/ProductionBomStructureMapper.xml
@@ -44,7 +44,7 @@ LEFT JOIN product p ON pm.product_id = p.id LEFT JOIN technology_operation top1 ON pbs.technology_operation_id = top1.id WHERE pbs.parent_id IS NOT NULL pbs.technology_operation_id IS NOT NULL AND pbs.production_order_bom_id = #{bomId} ORDER BY pbs.id src/main/resources/mapper/production/ProductionOrderPickMapper.xml
@@ -11,6 +11,7 @@ <result column="create_time" property="createTime" /> <result column="update_time" property="updateTime" /> <result column="production_order_id" property="productionOrderId" /> <result column="production_operation_task_id" property="productionOperationTaskId" /> <result column="remark" property="remark" /> <result column="create_user" property="createUser" /> <result column="dept_id" property="deptId" /> @@ -24,19 +25,55 @@ <result column="is_bom" property="bom" /> </resultMap> <select id="listPickedDetailByOrderId" resultType="com.ruoyi.production.bean.vo.ProductionOrderPickVo"> select pop.*, pop.is_bom as bom, pop.is_returned as returned, pop.quantity as pickQuantity, p.product_name as productName, pm.model as model, pm.unit as unit <sql id="PickDetailColumns"> pop.id as id, pop.product_model_id as productModelId, pop.quantity as quantity, pop.quantity as pickQuantity, pop.batch_no as batchNo, pop.create_time as createTime, pop.update_time as updateTime, pop.production_order_id as productionOrderId, pop.production_operation_task_id as productionOperationTaskId, pop.remark as remark, pop.technology_operation_id as technologyOperationId, pop.demanded_quantity as demandedQuantity, pop.feeding_qty as feedingQty, pop.return_qty as returnQty, pop.actual_qty as actualQty, pop.is_returned as returned, pop.is_bom as bom, coalesce(pop.operation_name, top1.name) as operationName, p.product_name as productName, pm.model as model, pm.unit as unit, pot.work_order_no as workOrderNo </sql> <sql id="PickDetailJoins"> from production_order_pick pop left join product_model pm on pop.product_model_id = pm.id left join product p on pm.product_id = p.id left join product_model pm on pop.product_model_id = pm.id left join product p on pm.product_id = p.id left join technology_operation top1 on pop.technology_operation_id = top1.id left join production_operation_task pot on pop.production_operation_task_id = pot.id </sql> <!-- 订åçº§é¢ææ±æ»ï¼å å«è¯¥è®¢å䏿æå·¥åºçé¢æè®°å½ --> <select id="listPickedDetailByOrderId" resultType="com.ruoyi.production.bean.vo.ProductionOrderPickVo"> select <include refid="PickDetailColumns" /> <include refid="PickDetailJoins" /> where pop.production_order_id = #{productionOrderId} order by pop.create_time desc, pop.id desc </select> <!-- å·¥åºï¼å·¥åï¼çº§é¢æè®°å½ --> <select id="listPickedDetailByTaskId" resultType="com.ruoyi.production.bean.vo.ProductionOrderPickVo"> select <include refid="PickDetailColumns" /> <include refid="PickDetailJoins" /> where pop.production_operation_task_id = #{productionOperationTaskId} order by pop.create_time desc, pop.id desc </select> </mapper> src/main/resources/mapper/technology/TechnologyBomStructureMapper.xml
@@ -16,6 +16,10 @@ <result column="dept_id" property="deptId" /> </resultMap> <!-- æå¹³ BOM æç»ï¼ä¸è¡ = ä¸ä¸ªç©æè§æ ¼ + å ¶æ¶èå·¥åºã å岿°æ®ä¸ BOM æåæ ¹èç¹ï¼æ ç¶èç¹ãæ å·¥åºä¸è§æ ¼çäº BOM æåè§æ ¼ï¼ä¸æ¯ç©æï¼éè¦æé¤ã --> <select id="listByBomId" resultType="com.ruoyi.technology.bean.vo.TechnologyBomStructureVo"> select tbs.*, p.product_name as productName, @@ -24,10 +28,14 @@ top1.name as operationName, pm.product_code as productCode from technology_bom_structure tbs inner join technology_bom tb on tbs.bom_id = tb.id left join product_model pm on tbs.product_model_id = pm.id left join product p on pm.product_id = p.id left join technology_operation top1 on tbs.operation_id = top1.id where tbs.bom_id = #{bomId} and not (tbs.parent_id is null and tbs.operation_id is null and tbs.product_model_id = tb.product_model_id) order by tbs.id </select> </mapper>