feat(mes): 新增工艺路线完整导入功能并优化相关验证
- 添加工艺路线批量导入模板和完整导入功能,支持多工作表导入
- 增加多个工艺路线相关错误码常量,完善错误处理机制
- 优化生产反馈服务,修复主键ID处理问题
- 完善工艺路线查询功能,支持按产品关键词模糊搜索
- 重构工序产出配置逻辑,支持按产品归属配置产出物料
- 添加工序默认报工仓库配置功能,集成仓库区域位置信息
- 实现工艺路线产品验证增强,确保物料类型和成品半成品分类正确
- 优化工序产出验证逻辑,支持中间工序和末道工序的不同配置规则
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- å·¥èºè·¯çº¿å¯¼å
¥æéï¼æ®é导å
¥å宿´è·¯çº¿æ¹é导å
¥å
±ç¨ mes:pro-route:importã |
| | | -- ä¼å
æå°âå·¥èºè®¾è®¡ / å·¥èºè·¯çº¿âèåï¼è¥ç®æ åºåªæçäº§æ¨¡åæ§èåï¼åæå°æ§èåã |
| | | -- å¯é夿§è¡ï¼ä»
èªå¨ææè¶
级管çåè§è²ï¼å
¶ä»è§è²è¯·å¨è§è²ç®¡ç䏿éå¾éã |
| | | |
| | | SET @route_menu_id := COALESCE( |
| | | (SELECT id FROM system_menu |
| | | WHERE component LIKE '%process-design/route%' AND type = 2 AND deleted = 0 |
| | | ORDER BY id LIMIT 1), |
| | | (SELECT id FROM system_menu |
| | | WHERE component LIKE '%mes/pro/route/index%' AND type = 2 AND deleted = 0 |
| | | ORDER BY id LIMIT 1) |
| | | ); |
| | | |
| | | SET @import_menu_id := ( |
| | | SELECT id FROM system_menu |
| | | WHERE permission = 'mes:pro-route:import' AND deleted = 0 |
| | | ORDER BY id LIMIT 1 |
| | | ); |
| | | SET @new_menu_id := (SELECT COALESCE(MAX(id), 0) + 1 FROM system_menu); |
| | | |
| | | INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, |
| | | component_name, status, visible, keep_alive, always_show, |
| | | creator, create_time, updater, update_time, deleted) |
| | | SELECT @new_menu_id, |
| | | 'å·¥èºè·¯çº¿å¯¼å
¥', 'mes:pro-route:import', 3, 6, @route_menu_id, |
| | | '', '#', '', NULL, 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0 |
| | | FROM DUAL |
| | | WHERE @route_menu_id IS NOT NULL AND @import_menu_id IS NULL; |
| | | |
| | | SET @import_menu_id := ( |
| | | SELECT id FROM system_menu |
| | | WHERE permission = 'mes:pro-route:import' AND deleted = 0 |
| | | ORDER BY id LIMIT 1 |
| | | ); |
| | | |
| | | INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id) |
| | | SELECT r.id, @import_menu_id, '1', NOW(), '1', NOW(), 0, r.tenant_id |
| | | FROM system_role r |
| | | WHERE r.id = 1 AND r.deleted = 0 AND @import_menu_id IS NOT NULL |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM system_role_menu |
| | | WHERE role_id = r.id AND menu_id = @import_menu_id |
| | | AND tenant_id = r.tenant_id AND deleted = 0 |
| | | ); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- å·¥èºè·¯çº¿å·¥åºäº§åºæäº§åå½å±åé»è®¤æ¥å·¥åºä½ï¼MySQL 8.xï¼ |
| | | -- å
æ§è¡æ¬èæ¬ï¼åé¨ç½²å¯¹åºåç«¯çæ¬ï¼åå²å·¥èºè·¯çº¿äº§åå¯ç¨åéè¡¥é½ä»åºãåºåºãåºä½ã |
| | | |
| | | CREATE TABLE IF NOT EXISTS `mes_pro_route_process_output` ( |
| | | `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ç¼å·', |
| | | `route_process_id` bigint NOT NULL COMMENT 'å·¥èºè·¯çº¿å·¥åºç¼å·', |
| | | `route_id` bigint NOT NULL COMMENT 'å·¥èºè·¯çº¿ç¼å·', |
| | | `process_id` bigint NOT NULL COMMENT 'å·¥åºç¼å·', |
| | | `product_id` bigint DEFAULT NULL COMMENT 'å½å±äº§åç¼å·ï¼ç©ºä¸ºéç¨', |
| | | `item_id` bigint NOT NULL COMMENT '产åºç©æç¼å·', |
| | | `sort` int DEFAULT NULL COMMENT 'æåº', |
| | | `remark` varchar(255) DEFAULT NULL COMMENT '夿³¨', |
| | | `creator` varchar(64) DEFAULT '' COMMENT 'å建è
', |
| | | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´', |
| | | `updater` varchar(64) DEFAULT '' COMMENT 'æ´æ°è
', |
| | | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´', |
| | | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å é¤', |
| | | PRIMARY KEY (`id`), |
| | | KEY `idx_route_process_id` (`route_process_id`), |
| | | KEY `idx_route_id` (`route_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='MES å·¥èºè·¯çº¿å·¥åºäº§åºå½å±'; |
| | | |
| | | -- å½å忝ä¿ççå·¥èºåæ°æ¨¡æ¿åå·¥åºæ¶çï¼ç®æ åºå¯è½å°æªæ§è¡å¯¹åºåå²è¿ç§»ã |
| | | SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_route_process' AND column_name = 'param_template_id'), |
| | | 'SELECT 1', 'ALTER TABLE mes_pro_route_process ADD COLUMN param_template_id BIGINT NULL COMMENT ''å·¥èºåæ°æ¨¡æ¿ç¼å·'' AFTER process_id'); |
| | | PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_route_process' AND column_name = 'yield_rate'), |
| | | 'SELECT 1', 'ALTER TABLE mes_pro_route_process ADD COLUMN yield_rate DECIMAL(10,4) NULL COMMENT ''å·¥åºæ¶çï¼äº§åº/æå
¥ï¼0~1ï¼'' AFTER param_template_id'); |
| | | PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_route_product' AND column_name = 'warehouse_id'), |
| | | 'SELECT 1', 'ALTER TABLE mes_pro_route_product ADD COLUMN warehouse_id BIGINT NULL COMMENT ''æ¥å·¥äº§åºä»åºID'' AFTER item_id'); |
| | | PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_route_product' AND column_name = 'location_id'), |
| | | 'SELECT 1', 'ALTER TABLE mes_pro_route_product ADD COLUMN location_id BIGINT NULL COMMENT ''æ¥å·¥äº§åºåºåºID'' AFTER warehouse_id'); |
| | | PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_route_product' AND column_name = 'area_id'), |
| | | 'SELECT 1', 'ALTER TABLE mes_pro_route_product ADD COLUMN area_id BIGINT NULL COMMENT ''æ¥å·¥äº§åºåºä½ID'' AFTER location_id'); |
| | | PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_work_order_process' AND column_name = 'param_template_id'), |
| | | 'SELECT 1', 'ALTER TABLE mes_pro_work_order_process ADD COLUMN param_template_id BIGINT NULL COMMENT ''å·¥èºåæ°æ¨¡æ¿ç¼å·'' AFTER process_id'); |
| | | PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_work_order_process' AND column_name = 'yield_rate'), |
| | | 'SELECT 1', 'ALTER TABLE mes_pro_work_order_process ADD COLUMN yield_rate DECIMAL(10,4) NULL COMMENT ''å·¥åºæ¶çï¼äº§åº/æå
¥ï¼0~1ï¼'' AFTER param_template_id'); |
| | | PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_work_order_process' AND column_name = 'warehouse_id'), |
| | | 'SELECT 1', 'ALTER TABLE mes_pro_work_order_process ADD COLUMN warehouse_id BIGINT NULL COMMENT ''æ¥å·¥äº§åºä»åºID'''); |
| | | PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_work_order_process' AND column_name = 'location_id'), |
| | | 'SELECT 1', 'ALTER TABLE mes_pro_work_order_process ADD COLUMN location_id BIGINT NULL COMMENT ''æ¥å·¥äº§åºåºåºID'''); |
| | | PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_work_order_process' AND column_name = 'area_id'), |
| | | 'SELECT 1', 'ALTER TABLE mes_pro_work_order_process ADD COLUMN area_id BIGINT NULL COMMENT ''æ¥å·¥äº§åºåºä½ID'''); |
| | | PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportExcelVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteCompleteImportWorkbook; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRoutePageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteCompleteImportService; |
| | | import cn.idev.excel.ExcelWriter; |
| | | import cn.idev.excel.FastExcelFactory; |
| | | import cn.iocoder.yudao.framework.common.util.http.HttpUtils; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.Parameters; |
| | |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; |
| | |
| | | |
| | | @Resource |
| | | private MesProRouteService routeService; |
| | | @Resource |
| | | private MesProRouteCompleteImportService completeImportService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建工èºè·¯çº¿") |
| | |
| | | ExcelUtils.write(response, "å·¥èºè·¯çº¿.xls", "æ°æ®", MesProRouteRespVO.class, data); |
| | | } |
| | | |
| | | @GetMapping("/get-import-template") |
| | | @Operation(summary = "è·å¾å·¥èºè·¯çº¿å¯¼å
¥æ¨¡æ¿") |
| | | @PreAuthorize("@ss.hasPermission('mes:pro-route:import')") |
| | | public void importTemplate(HttpServletResponse response) throws IOException { |
| | | // æå¨åå»ºå¯¼åº demo |
| | | List<MesProRouteImportExcelVO> list = Collections.singletonList( |
| | | MesProRouteImportExcelVO.builder() |
| | | .code("ROUTE001").name("示ä¾å·¥èºè·¯çº¿").description("").remark("").build() |
| | | ); |
| | | // è¾åº |
| | | ExcelUtils.write(response, "å·¥èºè·¯çº¿å¯¼å
¥æ¨¡æ¿.xls", "å·¥èºè·¯çº¿å表", MesProRouteImportExcelVO.class, list); |
| | | } |
| | | |
| | | @PostMapping("/import") |
| | | @Operation(summary = "导å
¥å·¥èºè·¯çº¿") |
| | | @Parameters({ |
| | | @Parameter(name = "file", description = "Excel æä»¶", required = true), |
| | | @Parameter(name = "updateSupport", description = "æ¯å¦æ¯ææ´æ°ï¼é»è®¤ä¸º false", example = "true") |
| | | }) |
| | | @PreAuthorize("@ss.hasPermission('mes:pro-route:import')") |
| | | public CommonResult<MesProRouteImportRespVO> importExcel(@RequestParam("file") MultipartFile file, |
| | | @RequestParam(value = "updateSupport", required = false, |
| | | defaultValue = "false") Boolean updateSupport) throws Exception { |
| | | List<MesProRouteImportExcelVO> list = ExcelUtils.read(file, MesProRouteImportExcelVO.class); |
| | | return success(routeService.importRouteList(list, updateSupport)); |
| | | } |
| | | |
| | | @GetMapping("/get-complete-import-template") |
| | | @Operation(summary = "è·å¾å®æ´å·¥èºè·¯çº¿æ¹é导å
¥æ¨¡æ¿") |
| | | @PreAuthorize("@ss.hasPermission('mes:pro-route:import')") |
| | | public void completeImportTemplate(HttpServletResponse response) throws IOException { |
| | | response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
| | | response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8("宿´å·¥èºè·¯çº¿å¯¼å
¥æ¨¡æ¿.xlsx")); |
| | | ExcelWriter writer = FastExcelFactory.write(response.getOutputStream()).autoCloseStream(false).build(); |
| | | try { |
| | | writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(0, "路线") |
| | | .head(MesProRouteCompleteImportWorkbook.RouteRow.class).build()); |
| | | writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(1, "产å") |
| | | .head(MesProRouteCompleteImportWorkbook.ProductRow.class).build()); |
| | | writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(2, "å·¥åº") |
| | | .head(MesProRouteCompleteImportWorkbook.ProcessRow.class).build()); |
| | | writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(3, "å·¥åºäº§åº") |
| | | .head(MesProRouteCompleteImportWorkbook.OutputRow.class).build()); |
| | | writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(4, "å·¥åºææ") |
| | | .head(MesProRouteCompleteImportWorkbook.InputRow.class).build()); |
| | | writer.write(List.of( |
| | | new MesProRouteCompleteImportWorkbook.GuideRow("路线", "æ¯è¡ä¸æ¡è·¯çº¿ï¼è·¯çº¿ç¼ç å¯ä¸ã宿´å¯¼å
¥ä»
æ°å¢ï¼å·²æç¼ç 伿¥éï¼å¯¼å
¥åé»è®¤åç¨ã"), |
| | | new MesProRouteCompleteImportWorkbook.GuideRow("产å", "路线ç¼ç ä¸è·¯çº¿è¡¨ä¸è´ï¼æ¯æ¡è·¯çº¿è³å°ä¸ä¸ªäº§åãç©æãä»åºãåºåºãåºä½å¡«åå·²æä¸å¡ç¼ç ï¼æ¶é´åä½å¡« MINUTE/HOUR/DAYã"), |
| | | new MesProRouteCompleteImportWorkbook.GuideRow("å·¥åº", "æ¯æ¡è·¯çº¿è³å°ä¸éå·¥åºï¼å·¥åºç¼ç 须已åå¨ï¼æåºå¯ä¸ãå¸å°åæ®µå¡«æ¯æå¦ï¼å
³ç³»å¡« 0=å¼å§-å¼å§ã1=ç»æ-ç»æã2=å¼å§-ç»æã3=ç»æ-å¼å§ã"), |
| | | new MesProRouteCompleteImportWorkbook.GuideRow("å·¥åºäº§åº", "æè·¯çº¿ç¼ç åå·¥åºç¼ç å
³èï¼å½å±äº§åç¼ç ç空表示å¤äº§åéç¨ãæ«éå·¥åºåºé
ç½®å
¨é¨æåã"), |
| | | new MesProRouteCompleteImportWorkbook.GuideRow("å·¥åºææ", "æè·¯çº¿ãå·¥åºåå½å±äº§åç¼ç å
³èï¼ææç©æé¡»å·²å¨è¯¥äº§å BOM ä¸ã") |
| | | ), FastExcelFactory.writerSheet(5, "å¡«å说æ") |
| | | .head(MesProRouteCompleteImportWorkbook.GuideRow.class).build()); |
| | | } finally { |
| | | writer.finish(); |
| | | } |
| | | } |
| | | |
| | | @PostMapping("/import-complete") |
| | | @Operation(summary = "æ¹é导å
¥å®æ´å·¥èºè·¯çº¿ï¼å¤å·¥ä½è¡¨ï¼") |
| | | @PreAuthorize("@ss.hasPermission('mes:pro-route:import')") |
| | | public CommonResult<MesProRouteImportRespVO> importComplete(@RequestParam("file") MultipartFile file) throws IOException { |
| | | MesProRouteCompleteImportWorkbook workbook = new MesProRouteCompleteImportWorkbook(); |
| | | workbook.setRoutes(readSheet(file, 0, MesProRouteCompleteImportWorkbook.RouteRow.class)); |
| | | workbook.setProducts(readSheet(file, 1, MesProRouteCompleteImportWorkbook.ProductRow.class)); |
| | | workbook.setProcesses(readSheet(file, 2, MesProRouteCompleteImportWorkbook.ProcessRow.class)); |
| | | workbook.setOutputs(readSheet(file, 3, MesProRouteCompleteImportWorkbook.OutputRow.class)); |
| | | workbook.setInputs(readSheet(file, 4, MesProRouteCompleteImportWorkbook.InputRow.class)); |
| | | return success(completeImportService.importRoutes(workbook)); |
| | | } |
| | | |
| | | private <T> List<T> readSheet(MultipartFile file, int index, Class<T> type) throws IOException { |
| | | try (InputStream input = file.getInputStream()) { |
| | | return FastExcelFactory.read(input, type, null).autoCloseStream(false).sheet(index).doReadSync(); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProProcessBomItemVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessOutputVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | |
| | | private MesProProcessService processService; |
| | | @Resource |
| | | private MesMdItemService mdItemService; |
| | | @Resource |
| | | private MesProRouteProductService routeProductService; |
| | | @Resource |
| | | private MesWmWarehouseService warehouseService; |
| | | @Resource |
| | | private MesWmWarehouseLocationService warehouseLocationService; |
| | | @Resource |
| | | private MesWmWarehouseAreaService warehouseAreaService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建工èºè·¯çº¿å·¥åº") |
| | |
| | | } |
| | | } |
| | | } |
| | | // å¡«å
å·¥èºè·¯çº¿äº§åä¸é
ç½®çé»è®¤æ¥å·¥ä»åºï¼ä¸åæ¶ä½ä¸ºå·¥åºé»è®¤å¼ï¼ |
| | | fillDefaultOutputWarehouse(result, productId); |
| | | return success(result); |
| | | } |
| | | |
| | | private void fillDefaultOutputWarehouse(List<MesProRouteProcessRespVO> list, Long productId) { |
| | | if (CollUtil.isEmpty(list)) { |
| | | return; |
| | | } |
| | | MesProRouteProductDO routeProduct = routeProductService.getRouteProductByItemId(productId); |
| | | if (routeProduct == null || routeProduct.getWarehouseId() == null) { |
| | | return; |
| | | } |
| | | MesWmWarehouseDO warehouse = warehouseService.getWarehouse(routeProduct.getWarehouseId()); |
| | | MesWmWarehouseLocationDO location = routeProduct.getLocationId() != null |
| | | ? warehouseLocationService.getWarehouseLocation(routeProduct.getLocationId()) : null; |
| | | MesWmWarehouseAreaDO area = routeProduct.getAreaId() != null |
| | | ? warehouseAreaService.getWarehouseArea(routeProduct.getAreaId()) : null; |
| | | String warehouseName = warehouse != null ? warehouse.getName() : null; |
| | | String locationName = location != null ? location.getName() : null; |
| | | String areaName = area != null ? area.getName() : null; |
| | | for (MesProRouteProcessRespVO vo : list) { |
| | | vo.setWarehouseId(routeProduct.getWarehouseId()); |
| | | vo.setWarehouseName(warehouseName); |
| | | vo.setLocationId(routeProduct.getLocationId()); |
| | | vo.setLocationName(locationName); |
| | | vo.setAreaId(routeProduct.getAreaId()); |
| | | vo.setAreaName(areaName); |
| | | } |
| | | } |
| | | |
| | | @GetMapping("/list-by-route") |
| | |
| | | } |
| | | Map<Long, MesMdItemDO> outputItemMap = allOutputItemIds.isEmpty() |
| | | ? Collections.emptyMap() : mdItemService.getItemMap(allOutputItemIds); |
| | | // 2.1 产åºå½å±é
ç½®ï¼productId 为空表示å¤äº§åéç¨ï¼ |
| | | Map<Long, List<MesProRouteProcessOutputVO>> outputsByRouteProcessId = new HashMap<>(); |
| | | Set<Long> routeProcessIds = convertSet(list, MesProRouteProcessDO::getId); |
| | | routeProcessIds.removeAll(Collections.singleton(null)); |
| | | if (CollUtil.isNotEmpty(routeProcessIds)) { |
| | | List<MesProRouteProcessOutputDO> outputList = routeProcessService |
| | | .getRouteProcessOutputListByRouteProcessIds(routeProcessIds); |
| | | if (CollUtil.isNotEmpty(outputList)) { |
| | | Set<Long> ownerItemIds = new HashSet<>(); |
| | | outputList.forEach(output -> { |
| | | if (output.getProductId() != null) { |
| | | ownerItemIds.add(output.getProductId()); |
| | | } |
| | | if (output.getItemId() != null) { |
| | | ownerItemIds.add(output.getItemId()); |
| | | } |
| | | }); |
| | | Map<Long, MesMdItemDO> ownerItemMap = mdItemService.getItemMap(ownerItemIds); |
| | | for (MesProRouteProcessOutputDO output : outputList) { |
| | | MesProRouteProcessOutputVO outputVO = new MesProRouteProcessOutputVO(); |
| | | outputVO.setId(output.getId()); |
| | | outputVO.setRouteProcessId(output.getRouteProcessId()); |
| | | outputVO.setProductId(output.getProductId()); |
| | | outputVO.setItemId(output.getItemId()); |
| | | outputVO.setSort(output.getSort()); |
| | | outputVO.setRemark(output.getRemark()); |
| | | MapUtils.findAndThen(ownerItemMap, output.getItemId(), |
| | | item -> outputVO.setItemCode(item.getCode()).setItemName(item.getName())); |
| | | MapUtils.findAndThen(ownerItemMap, output.getProductId(), |
| | | product -> outputVO.setProductName(product.getName())); |
| | | outputsByRouteProcessId.computeIfAbsent(output.getRouteProcessId(), |
| | | key -> new ArrayList<>()).add(outputVO); |
| | | } |
| | | } |
| | | } |
| | | // 3. æ¼æ¥ VO |
| | | List<MesProRouteProcessRespVO> result = BeanUtils.toBean(list, MesProRouteProcessRespVO.class); |
| | | for (int i = 0; i < result.size(); i++) { |
| | |
| | | vo.setOutputItemName(firstItem.getName()); |
| | | } |
| | | } |
| | | vo.setOutputs(outputsByRouteProcessId.getOrDefault(vo.getId(), Collections.emptyList())); |
| | | } |
| | | return result; |
| | | } |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| | | import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | |
| | | private MesMdItemService itemService; |
| | | @Resource |
| | | private MesMdUnitMeasureService unitMeasureService; |
| | | @Resource |
| | | private MesWmWarehouseService warehouseService; |
| | | @Resource |
| | | private MesWmWarehouseLocationService warehouseLocationService; |
| | | @Resource |
| | | private MesWmWarehouseAreaService warehouseAreaService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建工èºè·¯çº¿äº§å") |
| | |
| | | convertSet(list, MesProRouteProductDO::getItemId)); |
| | | Map<Long, MesMdUnitMeasureDO> unitMap = unitMeasureService.getUnitMeasureMap( |
| | | convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId)); |
| | | Map<Long, MesWmWarehouseDO> warehouseMap = warehouseService.getWarehouseMap( |
| | | convertSet(list, MesProRouteProductDO::getWarehouseId)); |
| | | Map<Long, MesWmWarehouseLocationDO> locationMap = warehouseLocationService.getWarehouseLocationMap( |
| | | convertSet(list, MesProRouteProductDO::getLocationId)); |
| | | Map<Long, MesWmWarehouseAreaDO> areaMap = warehouseAreaService.getWarehouseAreaMap( |
| | | convertSet(list, MesProRouteProductDO::getAreaId)); |
| | | // 2. æ¼æ¥ VO |
| | | return BeanUtils.toBean(list, MesProRouteProductRespVO.class, vo -> { |
| | | MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { |
| | |
| | | MapUtils.findAndThen(unitMap, item.getUnitMeasureId(), |
| | | unit -> vo.setUnitName(unit.getName())); |
| | | }); |
| | | MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); |
| | | MapUtils.findAndThen(locationMap, vo.getLocationId(), location -> vo.setLocationName(location.getName())); |
| | | MapUtils.findAndThen(areaMap, vo.getAreaId(), area -> vo.setAreaName(area.getName())); |
| | | }); |
| | | } |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo; |
| | | |
| | | import cn.idev.excel.annotation.ExcelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | /** å¤å·¥ä½è¡¨å®æ´å·¥èºè·¯çº¿å¯¼å
¥æ°æ®ï¼ææå
³èéè¿ä¸å¡ç¼ç å®ä½ã */ |
| | | @Data |
| | | public class MesProRouteCompleteImportWorkbook { |
| | | |
| | | private List<RouteRow> routes; |
| | | private List<ProductRow> products; |
| | | private List<ProcessRow> processes; |
| | | private List<OutputRow> outputs; |
| | | private List<InputRow> inputs; |
| | | |
| | | @Data |
| | | public static class RouteRow { |
| | | @ExcelProperty("路线ç¼ç ") private String routeCode; |
| | | @ExcelProperty("路线åç§°") private String name; |
| | | @ExcelProperty("路线说æ") private String description; |
| | | @ExcelProperty("夿³¨") private String remark; |
| | | } |
| | | |
| | | @Data |
| | | public static class ProductRow { |
| | | @ExcelProperty("路线ç¼ç ") private String routeCode; |
| | | @ExcelProperty("产åç©æç¼ç ") private String itemCode; |
| | | @ExcelProperty("ä»åºç¼ç ") private String warehouseCode; |
| | | @ExcelProperty("åºåºç¼ç ") private String locationCode; |
| | | @ExcelProperty("åºä½ç¼ç ") private String areaCode; |
| | | @ExcelProperty("ç产æ°é") private Integer quantity; |
| | | @ExcelProperty("çäº§ç¨æ¶") private BigDecimal productionTime; |
| | | @ExcelProperty("æ¶é´åä½") private String timeUnitType; |
| | | @ExcelProperty("夿³¨") private String remark; |
| | | } |
| | | |
| | | @Data |
| | | public static class ProcessRow { |
| | | @ExcelProperty("路线ç¼ç ") private String routeCode; |
| | | @ExcelProperty("å·¥åºç¼ç ") private String processCode; |
| | | @ExcelProperty("æåº") private Integer sort; |
| | | @ExcelProperty("å·¥èºåæ°æ¨¡æ¿ID") private Long paramTemplateId; |
| | | @ExcelProperty("å·¥åºæ¶ç") private BigDecimal yieldRate; |
| | | @ExcelProperty("ä¸ä¸ä¸å·¥åºå
³ç³»") private Integer linkType; |
| | | @ExcelProperty("å夿¶é´(åé)") private Integer prepareTime; |
| | | @ExcelProperty("çå¾
æ¶é´(åé)") private Integer waitTime; |
| | | @ExcelProperty("çç¹å¾é¢è²") private String colorCode; |
| | | @ExcelProperty("å
³é®å·¥åº(æ¯/å¦)") private String keyFlag; |
| | | @ExcelProperty("è´¨æ£å·¥åº(æ¯/å¦)") private String checkFlag; |
| | | @ExcelProperty("åå²(æ¯/å¦)") private String backflushFlag; |
| | | @ExcelProperty("夿³¨") private String remark; |
| | | } |
| | | |
| | | @Data |
| | | public static class OutputRow { |
| | | @ExcelProperty("路线ç¼ç ") private String routeCode; |
| | | @ExcelProperty("å·¥åºç¼ç ") private String processCode; |
| | | @ExcelProperty("产åºç©æç¼ç ") private String itemCode; |
| | | @ExcelProperty("å½å±äº§åç¼ç (空为éç¨)") private String productCode; |
| | | @ExcelProperty("æåº") private Integer sort; |
| | | @ExcelProperty("夿³¨") private String remark; |
| | | } |
| | | |
| | | @Data |
| | | public static class InputRow { |
| | | @ExcelProperty("路线ç¼ç ") private String routeCode; |
| | | @ExcelProperty("å·¥åºç¼ç ") private String processCode; |
| | | @ExcelProperty("å½å±äº§åç¼ç ") private String productCode; |
| | | @ExcelProperty("ææç©æç¼ç ") private String itemCode; |
| | | @ExcelProperty("ç¨ææ¯ä¾") private BigDecimal quantity; |
| | | @ExcelProperty("夿³¨") private String remark; |
| | | } |
| | | |
| | | @Data |
| | | public static class GuideRow { |
| | | @ExcelProperty("å·¥ä½è¡¨") private String sheet; |
| | | @ExcelProperty("å¡«å说æ") private String description; |
| | | |
| | | public GuideRow(String sheet, String description) { |
| | | this.sheet = sheet; |
| | | this.description = description; |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo; |
| | | |
| | | import cn.idev.excel.annotation.ExcelProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * MES å·¥èºè·¯çº¿ Excel 导å
¥ VO |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class MesProRouteImportExcelVO { |
| | | |
| | | @ExcelProperty("å·¥èºè·¯çº¿ç¼ç ") |
| | | private String code; |
| | | |
| | | @ExcelProperty("å·¥èºè·¯çº¿åç§°") |
| | | private String name; |
| | | |
| | | @ExcelProperty("å·¥èºè·¯çº¿è¯´æ") |
| | | private String description; |
| | | |
| | | @ExcelProperty("夿³¨") |
| | | private String remark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | @Schema(description = "管çåå° - MES å·¥èºè·¯çº¿å¯¼å
¥ Response VO") |
| | | @Data |
| | | @Builder |
| | | public class MesProRouteImportRespVO { |
| | | |
| | | @Schema(description = "å建æåçå·¥èºè·¯çº¿ç¼ç æ°ç»", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private List<String> createCodes; |
| | | |
| | | @Schema(description = "æ´æ°æåçå·¥èºè·¯çº¿ç¼ç æ°ç»", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private List<String> updateCodes; |
| | | |
| | | @Schema(description = "导å
¥å¤±è´¥çå·¥èºè·¯çº¿éåï¼key 为工èºè·¯çº¿ç¼ç ï¼value 为失败åå ", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private Map<String, String> failureCodes; |
| | | |
| | | } |
| | |
| | | @Schema(description = "å·¥èºè·¯çº¿åç§°", example = "æ å路线") |
| | | private String name; |
| | | |
| | | @Schema(description = "å
³è产ååç§°æç¼ç ï¼æ¨¡ç³æç´¢ï¼", example = "é»ææ¸æ") |
| | | private String productKeyword; |
| | | |
| | | @Schema(description = "ç¶æ", example = "0") |
| | | private Integer status; |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Schema(description = "管çåå° - MES å·¥èºè·¯çº¿å·¥åºäº§åºå½å± Response VO") |
| | | @Data |
| | | public class MesProRouteProcessOutputVO { |
| | | |
| | | @Schema(description = "ç¼å·", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "å·¥èºè·¯çº¿å·¥åºç¼å·", example = "1") |
| | | private Long routeProcessId; |
| | | |
| | | @Schema(description = "å½å±äº§åç¼å·ï¼ä¸ºç©ºè¡¨ç¤ºå¤äº§åéç¨") |
| | | private Long productId; |
| | | |
| | | @Schema(description = "å½å±äº§ååç§°") |
| | | private String productName; |
| | | |
| | | @Schema(description = "产åºç©æç¼å·", example = "100") |
| | | private Long itemId; |
| | | |
| | | @Schema(description = "产åºç©æç¼ç ") |
| | | private String itemCode; |
| | | |
| | | @Schema(description = "产åºç©æåç§°") |
| | | private String itemName; |
| | | |
| | | @Schema(description = "æåº", example = "1") |
| | | private Integer sort; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | | } |
| | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | import java.math.BigDecimal; |
| | | |
| | | @Schema(description = "管çåå° - MES å·¥èºè·¯çº¿å·¥åº Response VO") |
| | | @Data |
| | |
| | | |
| | | @Schema(description = "å·¥åºç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long processId; |
| | | |
| | | @Schema(description = "å·¥èºåæ°æ¨¡æ¿ç¼å·", example = "1") |
| | | private Long paramTemplateId; |
| | | |
| | | @Schema(description = "å·¥åºæ¶çï¼äº§åº/æå
¥ï¼0~1ï¼", example = "0.98") |
| | | private BigDecimal yieldRate; |
| | | |
| | |
| | | @Schema(description = "产åºäº§åç©æåç§°ï¼éå·æ¼æ¥ï¼", example = "产åA, 产åB") |
| | | private String outputItemNames; |
| | | |
| | | @Schema(description = "产åºç©æå½å±é
ç½®ï¼productId 为空表示å¤äº§åéç¨ï¼") |
| | | private List<MesProRouteProcessOutputVO> outputs; |
| | | |
| | | @Schema(description = "é»è®¤æ¥å·¥ä»åºç¼å·ï¼åèªå·¥èºè·¯çº¿äº§åçæ¥å·¥ä»åºé
ç½®ï¼", example = "10") |
| | | private Long warehouseId; |
| | | |
| | | @Schema(description = "é»è®¤æ¥å·¥ä»åºåç§°") |
| | | private String warehouseName; |
| | | |
| | | @Schema(description = "é»è®¤æ¥å·¥åºåºç¼å·", example = "110") |
| | | private Long locationId; |
| | | |
| | | @Schema(description = "é»è®¤æ¥å·¥åºåºåç§°") |
| | | private String locationName; |
| | | |
| | | @Schema(description = "é»è®¤æ¥å·¥åºä½ç¼å·", example = "210") |
| | | private Long areaId; |
| | | |
| | | @Schema(description = "é»è®¤æ¥å·¥åºä½åç§°") |
| | | private String areaName; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | import java.math.BigDecimal; |
| | | |
| | | @Schema(description = "管çåå° - MES å·¥èºè·¯çº¿å·¥åºæ°å¢/ä¿®æ¹ Request VO") |
| | | @Data |
| | |
| | | @NotNull(message = "å·¥èºè·¯çº¿ç¼å·ä¸è½ä¸ºç©º") |
| | | private Long routeId; |
| | | |
| | | @Schema(description = "å·¥åºç¼å·", example = "1") |
| | | @Schema(description = "å·¥åºç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "å·¥åºç¼å·ä¸è½ä¸ºç©º") |
| | | private Long processId; |
| | | |
| | | @Schema(description = "å·¥èºåæ°æ¨¡æ¿ç¼å·", example = "1") |
| | | private Long paramTemplateId; |
| | | |
| | | @Schema(description = "å·¥åºæ¶çï¼äº§åº/æå
¥ï¼0~1ï¼", example = "0.98") |
| | | private BigDecimal yieldRate; |
| | | |
| | | @Schema(description = "åºå·", example = "1") |
| | | @Schema(description = "åºå·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åºå·ä¸è½ä¸ºç©º") |
| | | private Integer sort; |
| | | |
| | | @Schema(description = "ä¸ä¸ä¸éå·¥åºå
³ç³»", example = "0") |
| | |
| | | @Schema(description = "产åºäº§åç©æç¼å·å表ï¼å¤éï¼ä¸ºç©ºæ¶é»è®¤ç¨å·¥åæåï¼", example = "[100, 101]") |
| | | private List<Long> outputItemIds; |
| | | |
| | | @Schema(description = "产åºç©æå½å±é
ç½®ï¼ä¼å
äº outputItemIdsï¼productId 为空表示å¤äº§åéç¨ï¼") |
| | | private List<OutputItem> outputs; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | | @Schema(description = "产åºç©æå½å±") |
| | | @Data |
| | | public static class OutputItem { |
| | | |
| | | @Schema(description = "å½å±äº§åç¼å·ï¼ä¸ºç©ºè¡¨ç¤ºå¤äº§åéç¨") |
| | | private Long productId; |
| | | |
| | | @Schema(description = "产åºç©æç¼å·", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private Long itemId; |
| | | |
| | | @Schema(description = "æåº") |
| | | private Integer sort; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | | } |
| | | |
| | | } |
| | |
| | | @Schema(description = "产åç©æç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long itemId; |
| | | |
| | | @Schema(description = "æ¥å·¥äº§åºä»åºç¼å·") |
| | | private Long warehouseId; |
| | | |
| | | @Schema(description = "æ¥å·¥äº§åºä»åºåç§°") |
| | | private String warehouseName; |
| | | |
| | | @Schema(description = "æ¥å·¥äº§åºåºåºç¼å·") |
| | | private Long locationId; |
| | | |
| | | @Schema(description = "æ¥å·¥äº§åºåºåºåç§°") |
| | | private String locationName; |
| | | |
| | | @Schema(description = "æ¥å·¥äº§åºåºä½ç¼å·") |
| | | private Long areaId; |
| | | |
| | | @Schema(description = "æ¥å·¥äº§åºåºä½åç§°") |
| | | private String areaName; |
| | | |
| | | @Schema(description = "产åç©æç¼ç ") |
| | | private String itemCode; |
| | | |
| | |
| | | @NotNull(message = "产åç©æç¼å·ä¸è½ä¸ºç©º") |
| | | private Long itemId; |
| | | |
| | | @Schema(description = "æ¥å·¥äº§åºä»åºç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "æ¥å·¥äº§åºä»åºä¸è½ä¸ºç©º") |
| | | private Long warehouseId; |
| | | |
| | | @Schema(description = "æ¥å·¥äº§åºåºåºç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "æ¥å·¥äº§åºåºåºä¸è½ä¸ºç©º") |
| | | private Long locationId; |
| | | |
| | | @Schema(description = "æ¥å·¥äº§åºåºä½ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "æ¥å·¥äº§åºåºä½ä¸è½ä¸ºç©º") |
| | | private Long areaId; |
| | | |
| | | @Schema(description = "ç产æ°é", example = "100") |
| | | private Integer quantity; |
| | | |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | |
| | | private MesProProcessService processService; |
| | | @Resource |
| | | private MesMdItemService itemService; |
| | | @Resource |
| | | private MesWmWarehouseService warehouseService; |
| | | @Resource |
| | | private MesWmWarehouseLocationService warehouseLocationService; |
| | | @Resource |
| | | private MesWmWarehouseAreaService warehouseAreaService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建工åå·¥åº") |
| | |
| | | } |
| | | Map<Long, MesMdItemDO> itemMap = CollUtil.isNotEmpty(allOutputItemIds) |
| | | ? itemService.getItemMap(allOutputItemIds) : Collections.emptyMap(); |
| | | // è·åæ¥å·¥ä»åº/åºåº/åºä½ |
| | | Set<Long> warehouseIds = convertSet(list, MesProWorkOrderProcessDO::getWarehouseId); |
| | | warehouseIds.removeAll(Collections.singleton(null)); |
| | | Set<Long> locationIds = convertSet(list, MesProWorkOrderProcessDO::getLocationId); |
| | | locationIds.removeAll(Collections.singleton(null)); |
| | | Set<Long> areaIds = convertSet(list, MesProWorkOrderProcessDO::getAreaId); |
| | | areaIds.removeAll(Collections.singleton(null)); |
| | | Map<Long, MesWmWarehouseDO> warehouseMap = CollUtil.isNotEmpty(warehouseIds) |
| | | ? warehouseService.getWarehouseMap(warehouseIds) : Collections.emptyMap(); |
| | | Map<Long, MesWmWarehouseLocationDO> locationMap = CollUtil.isNotEmpty(locationIds) |
| | | ? warehouseLocationService.getWarehouseLocationMap(locationIds) : Collections.emptyMap(); |
| | | Map<Long, MesWmWarehouseAreaDO> areaMap = CollUtil.isNotEmpty(areaIds) |
| | | ? warehouseAreaService.getWarehouseAreaMap(areaIds) : Collections.emptyMap(); |
| | | |
| | | // 2. æ¥è¯¢ææBOMï¼æå·¥åºåç»ï¼ |
| | | Set<Long> workOrderIds = convertSet(list, MesProWorkOrderProcessDO::getWorkOrderId); |
| | |
| | | MapUtils.findAndThen(processMap, vo.getNextProcessId(), process -> { |
| | | vo.setNextProcessName(process.getName()); |
| | | }); |
| | | // æ¥å·¥ä»åº/åºåº/åºä½åç§° |
| | | MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), |
| | | warehouse -> vo.setWarehouseName(warehouse.getName())); |
| | | MapUtils.findAndThen(locationMap, vo.getLocationId(), |
| | | location -> vo.setLocationName(location.getName())); |
| | | MapUtils.findAndThen(areaMap, vo.getAreaId(), |
| | | area -> vo.setAreaName(area.getName())); |
| | | // 产åºç©æï¼æ¯æå¤éï¼ |
| | | List<Long> ids = voOutputItemIdsMap.getOrDefault((long) i, Collections.emptyList()); |
| | | if (!ids.isEmpty()) { |
| | |
| | | @Schema(description = "产åºäº§åç©æåç§°ï¼éå·æ¼æ¥ï¼", example = "产åA, 产åB") |
| | | private String outputItemNames; |
| | | |
| | | @Schema(description = "æ¥å·¥ä»åºç¼å·", example = "10") |
| | | private Long warehouseId; |
| | | |
| | | @Schema(description = "æ¥å·¥ä»åºåç§°", example = "æåä»") |
| | | private String warehouseName; |
| | | |
| | | @Schema(description = "æ¥å·¥åºåºç¼å·", example = "110") |
| | | private Long locationId; |
| | | |
| | | @Schema(description = "æ¥å·¥åºåºåç§°", example = "æåä»éç¨åºåº") |
| | | private String locationName; |
| | | |
| | | @Schema(description = "æ¥å·¥åºä½ç¼å·", example = "210") |
| | | private Long areaId; |
| | | |
| | | @Schema(description = "æ¥å·¥åºä½åç§°", example = "æåä»é»è®¤åºä½") |
| | | private String areaName; |
| | | |
| | | @Schema(description = "夿³¨", example = "夿³¨") |
| | | @ExcelProperty("夿³¨") |
| | | private String remark; |
| | |
| | | @Schema(description = "产åºäº§åç©æç¼å·å表ï¼å¤éï¼ä¸ºç©ºæ¶é»è®¤ç¨å·¥åæåï¼", example = "[100, 101]") |
| | | private List<Long> outputItemIds; |
| | | |
| | | @Schema(description = "æ¥å·¥ä»åºç¼å·", example = "10") |
| | | private Long warehouseId; |
| | | |
| | | @Schema(description = "æ¥å·¥åºåºç¼å·", example = "110") |
| | | private Long locationId; |
| | | |
| | | @Schema(description = "æ¥å·¥åºä½ç¼å·", example = "210") |
| | | private Long areaId; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | import cn.iocoder.yudao.module.mes.enums.DictTypeConstants; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | |
| | | * å
³è {@link MesProProcessDO#getId()} |
| | | */ |
| | | private Long processId; |
| | | /** |
| | | * å·¥èºåæ°æ¨¡æ¿ç¼å· |
| | | */ |
| | | /** å·¥èºåæ°æ¨¡æ¿ç¼å· */ |
| | | private Long paramTemplateId; |
| | | /** |
| | | * å·¥åºæ¶çï¼äº§åº/æå
¥ï¼0~1ï¼ |
| | | * |
| | | * æäº§æ¶ææ¤æ¶ç仿«éå¾å忍åéå·¥åºçäº§åºæ°éï¼ä¸ºç©ºæ 1.0ï¼ä¸æ¾å¤§ï¼å¤ç |
| | | */ |
| | | /** å·¥åºæ¶çï¼äº§åº/æå
¥ï¼0~1ï¼ */ |
| | | private BigDecimal yieldRate; |
| | | /** |
| | | * åºå· |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.dataobject.pro.route; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | /** |
| | | * MES å·¥èºè·¯çº¿å·¥åºäº§åºå½å± DO |
| | | * |
| | | * åä¸éå·¥åºç产åºå¯ä»¥æäº§ååºåï¼å½å±äº§åä¸ºç©ºè¡¨ç¤ºè¯¥è·¯çº¿ä¸ææäº§åéç¨ã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @TableName("mes_pro_route_process_output") |
| | | @KeySequence("mes_pro_route_process_output_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class MesProRouteProcessOutputDO extends BaseDO { |
| | | |
| | | /** |
| | | * ç¼å· |
| | | */ |
| | | @TableId |
| | | private Long id; |
| | | /** |
| | | * å·¥èºè·¯çº¿å·¥åºç¼å· |
| | | * |
| | | * å
³è {@link MesProRouteProcessDO#getId()} |
| | | */ |
| | | private Long routeProcessId; |
| | | /** |
| | | * å·¥èºè·¯çº¿ç¼å· |
| | | * |
| | | * å
³è {@link MesProRouteDO#getId()} |
| | | */ |
| | | private Long routeId; |
| | | /** |
| | | * å·¥åºç¼å· |
| | | * |
| | | * å
³è {@link MesProProcessDO#getId()} |
| | | */ |
| | | private Long processId; |
| | | /** |
| | | * å½å±äº§åç¼å·ï¼ä¸ºç©ºè¡¨ç¤ºå¤äº§åéç¨ |
| | | * |
| | | * å
³è {@link MesMdItemDO#getId()} |
| | | */ |
| | | private Long productId; |
| | | /** |
| | | * 产åºç©æç¼å· |
| | | * |
| | | * å
³è {@link MesMdItemDO#getId()} |
| | | */ |
| | | private Long itemId; |
| | | /** |
| | | * æåº |
| | | */ |
| | | private Integer sort; |
| | | /** |
| | | * 夿³¨ |
| | | */ |
| | | private String remark; |
| | | |
| | | } |
| | |
| | | * å
³è {@link cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO#getId()} |
| | | */ |
| | | private Long itemId; |
| | | /** æ¥å·¥äº§åºä»åº */ |
| | | private Long warehouseId; |
| | | /** æ¥å·¥äº§åºåºåº */ |
| | | private Long locationId; |
| | | /** æ¥å·¥äº§åºåºä½ */ |
| | | private Long areaId; |
| | | /** |
| | | * ç产æ°é |
| | | */ |
| | |
| | | */ |
| | | private String outputItemId; |
| | | /** |
| | | * æ¥å·¥ä»åºç¼å· |
| | | * |
| | | * ä¸åæ¶ä»å·¥èºè·¯çº¿äº§å带å
¥ï¼å¯æå·¥è°æ´ï¼æ¥å·¥äº§åºææ¤ä»åºå
¥åº |
| | | * å
³è {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO#getId()} |
| | | */ |
| | | private Long warehouseId; |
| | | /** |
| | | * æ¥å·¥åºåºç¼å· |
| | | * |
| | | * å
³è {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO#getId()} |
| | | */ |
| | | private Long locationId; |
| | | /** |
| | | * æ¥å·¥åºä½ç¼å· |
| | | * |
| | | * å
³è {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO#getId()} |
| | | */ |
| | | private Long areaId; |
| | | /** |
| | | * 夿³¨ |
| | | */ |
| | | private String remark; |
| | |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRoutePageReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.List; |
| | |
| | | public interface MesProRouteMapper extends BaseMapperX<MesProRouteDO> { |
| | | |
| | | default PageResult<MesProRouteDO> selectPage(MesProRoutePageReqVO reqVO) { |
| | | return selectPage(reqVO, new LambdaQueryWrapperX<MesProRouteDO>() |
| | | LambdaQueryWrapperX<MesProRouteDO> query = new LambdaQueryWrapperX<MesProRouteDO>() |
| | | .likeIfPresent(MesProRouteDO::getCode, reqVO.getCode()) |
| | | .likeIfPresent(MesProRouteDO::getName, reqVO.getName()) |
| | | .eqIfPresent(MesProRouteDO::getStatus, reqVO.getStatus()) |
| | | .betweenIfPresent(MesProRouteDO::getCreateTime, reqVO.getCreateTime()) |
| | | .orderByDesc(MesProRouteDO::getId)); |
| | | .betweenIfPresent(MesProRouteDO::getCreateTime, reqVO.getCreateTime()); |
| | | if (StrUtil.isNotBlank(reqVO.getProductKeyword())) { |
| | | String keyword = reqVO.getProductKeyword().trim(); |
| | | query.apply("EXISTS (SELECT 1 FROM mes_pro_route_product rp " |
| | | + "JOIN mes_md_item item ON item.id = rp.item_id AND item.deleted = 0 " |
| | | + "WHERE rp.route_id = mes_pro_route.id AND rp.deleted = 0 " |
| | | + "AND (item.code LIKE CONCAT('%', {0}, '%') " |
| | | + "OR item.name LIKE CONCAT('%', {1}, '%')))", keyword, keyword); |
| | | } |
| | | return selectPage(reqVO, query.orderByDesc(MesProRouteDO::getId)); |
| | | } |
| | | |
| | | default MesProRouteDO selectByCode(String code) { |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.pro.route; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * MES å·¥èºè·¯çº¿å·¥åºäº§åºå½å± Mapper |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Mapper |
| | | public interface MesProRouteProcessOutputMapper extends BaseMapperX<MesProRouteProcessOutputDO> { |
| | | |
| | | default List<MesProRouteProcessOutputDO> selectListByRouteProcessId(Long routeProcessId) { |
| | | return selectList(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>() |
| | | .eq(MesProRouteProcessOutputDO::getRouteProcessId, routeProcessId) |
| | | .orderByAsc(MesProRouteProcessOutputDO::getSort)); |
| | | } |
| | | |
| | | default List<MesProRouteProcessOutputDO> selectListByRouteProcessIds(Collection<Long> routeProcessIds) { |
| | | return selectList(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>() |
| | | .in(MesProRouteProcessOutputDO::getRouteProcessId, routeProcessIds) |
| | | .orderByAsc(MesProRouteProcessOutputDO::getSort)); |
| | | } |
| | | |
| | | default List<MesProRouteProcessOutputDO> selectListByRouteId(Long routeId) { |
| | | return selectList(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>() |
| | | .eq(MesProRouteProcessOutputDO::getRouteId, routeId) |
| | | .orderByAsc(MesProRouteProcessOutputDO::getSort)); |
| | | } |
| | | |
| | | default void deleteByRouteProcessId(Long routeProcessId) { |
| | | delete(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>() |
| | | .eq(MesProRouteProcessOutputDO::getRouteProcessId, routeProcessId)); |
| | | } |
| | | |
| | | default void deleteByRouteId(Long routeId) { |
| | | delete(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>() |
| | | .eq(MesProRouteProcessOutputDO::getRouteId, routeId)); |
| | | } |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.List; |
| | |
| | | .orderByAsc(MesProWorkOrderProcessDO::getSort)); |
| | | } |
| | | |
| | | default MesProWorkOrderProcessDO selectByWorkOrderIdAndProcessId(Long workOrderId, Long processId) { |
| | | return selectOne(new LambdaQueryWrapperX<MesProWorkOrderProcessDO>() |
| | | .eq(MesProWorkOrderProcessDO::getWorkOrderId, workOrderId) |
| | | .eq(MesProWorkOrderProcessDO::getProcessId, processId) |
| | | .orderByAsc(MesProWorkOrderProcessDO::getSort) |
| | | .last("LIMIT 1")); |
| | | } |
| | | |
| | | default MesProWorkOrderProcessDO selectLastByWorkOrderId(Long workOrderId) { |
| | | return selectOne(new LambdaQueryWrapperX<MesProWorkOrderProcessDO>() |
| | | .eq(MesProWorkOrderProcessDO::getWorkOrderId, workOrderId) |
| | |
| | | .last("LIMIT 1")); |
| | | } |
| | | |
| | | /** |
| | | * æ´æ°æ¥å·¥ä»åºï¼å
许置空ï¼updateById ä¼å¿½ç¥ null åæ®µï¼æ
åç¬å¤çï¼ |
| | | */ |
| | | default void updateWarehouseById(Long id, Long warehouseId, Long locationId, Long areaId) { |
| | | update(null, new LambdaUpdateWrapper<MesProWorkOrderProcessDO>() |
| | | .set(MesProWorkOrderProcessDO::getWarehouseId, warehouseId) |
| | | .set(MesProWorkOrderProcessDO::getLocationId, locationId) |
| | | .set(MesProWorkOrderProcessDO::getAreaId, areaId) |
| | | .eq(MesProWorkOrderProcessDO::getId, id)); |
| | | } |
| | | |
| | | default void deleteByWorkOrderId(Long workOrderId) { |
| | | delete(new LambdaQueryWrapperX<MesProWorkOrderProcessDO>() |
| | | .eq(MesProWorkOrderProcessDO::getWorkOrderId, workOrderId)); |
| | |
| | | ErrorCode PRO_ROUTE_ENABLE_NO_KEY_PROCESS = new ErrorCode(1_040_501_003, "å·¥èºè·¯çº¿å¿
é¡»è¦æå
³é®å·¥åº"); |
| | | ErrorCode PRO_ROUTE_ENABLE_PRODUCT_NO_BOM = new ErrorCode(1_040_501_004, "产å {} æªé
置工åºç BOM æ¶è"); |
| | | ErrorCode PRO_ROUTE_IS_ENABLE = new ErrorCode(1_040_501_005, "å·¥èºè·¯çº¿å·²å¯ç¨ï¼ä¸å
许æä½"); |
| | | ErrorCode PRO_ROUTE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_040_501_006, "导å
¥å·¥èºè·¯çº¿æ°æ®ä¸è½ä¸ºç©º"); |
| | | // ========== MES ç产管ç-å·¥èºè·¯çº¿å·¥åºï¼1-040-501-100ï¼ ========== |
| | | ErrorCode PRO_ROUTE_PROCESS_NOT_EXISTS = new ErrorCode(1_040_501_100, "å·¥èºè·¯çº¿å·¥åºä¸åå¨"); |
| | | ErrorCode PRO_ROUTE_PROCESS_SORT_DUPLICATE = new ErrorCode(1_040_501_101, "åºå·å·²åå¨"); |
| | |
| | | // ========== MES ç产管ç-å·¥èºè·¯çº¿äº§åï¼1-040-501-200ï¼ ========== |
| | | ErrorCode PRO_ROUTE_PRODUCT_NOT_EXISTS = new ErrorCode(1_040_501_200, "å·¥èºè·¯çº¿äº§åä¸åå¨"); |
| | | ErrorCode PRO_ROUTE_PRODUCT_ITEM_DUPLICATE = new ErrorCode(1_040_501_201, "æ¤äº§åå·²é
ç½®äºå·¥èºè·¯çº¿"); |
| | | ErrorCode PRO_ROUTE_PRODUCT_ITEM_TYPE_INVALID = new ErrorCode(1_040_501_202, "ç©æã{}ãçä¸»æ°æ®ç±»ååMESåç±»åéæå/åæåï¼è¯·å
æ ¸å¯¹ç©æç±»å"); |
| | | ErrorCode PRO_ROUTE_PRODUCT_WAREHOUSE_REQUIRED = new ErrorCode(1_040_501_203, "产åã{}ãæªé
ç½®æ¥å·¥å
¥åºä»åºãåºåºååºä½"); |
| | | ErrorCode PRO_ROUTE_PROCESS_OUTPUT_REQUIRED = new ErrorCode(1_040_501_204, "å·¥åºã{}ãæªé
置产åºäº§å"); |
| | | ErrorCode PRO_ROUTE_PROCESS_OUTPUT_NOT_BOUND = new ErrorCode(1_040_501_205, "å·¥åºäº§åºäº§åã{}ãæªç»å®å°å½åå·¥èºè·¯çº¿"); |
| | | ErrorCode PRO_ROUTE_MIDDLE_OUTPUT_INVALID = new ErrorCode(1_040_501_206, "ä¸é´å·¥åºã{}ãåªè½äº§åºåæåå类产å"); |
| | | ErrorCode PRO_ROUTE_LAST_OUTPUT_INVALID = new ErrorCode(1_040_501_207, "æ«éå·¥åºã{}ãåªè½äº§åºæåå类产åï¼ä¸å¿
é¡»å
å«å
¨é¨ç»å®æå"); |
| | | ErrorCode PRO_ROUTE_LAST_OUTPUT_ATTRIBUTION_REQUIRED = new ErrorCode(1_040_501_208, "æ«éå·¥åºã{}ãçæå产åºå¿
é¡»æäº§åå½å±é
ç½®ï¼æ¯ä¸ªæåå½å±å°èªèº«ï¼ä¸è½ä½¿ç¨âéç¨â"); |
| | | // ========== MES ç产管ç-å·¥èºè·¯çº¿äº§åBOMï¼1-040-501-300ï¼ ========== |
| | | ErrorCode PRO_ROUTE_PRODUCT_BOM_NOT_EXISTS = new ErrorCode(1_040_501_300, "å·¥èºè·¯çº¿äº§å BOM ä¸åå¨"); |
| | | ErrorCode PRO_ROUTE_PRODUCT_BOM_DUPLICATE = new ErrorCode(1_040_501_301, "å½å BOM ç©æå¨æ¤å·¥åºå·²ç»é
ç½®è¿"); |
| | |
| | | // 1. æ ¡éª |
| | | MesProTaskDO task = validateFeedbackData(createReqVO); |
| | | |
| | | // 2. æå
¥ï¼èªå¨å¡«å
itemIdï¼ |
| | | // 2. æå
¥ï¼ä¸»é®ç±æ°æ®åºèªå¢ï¼å¿½ç¥è¯·æ±ä¸å¯è½æ®çç idï¼èªå¨å¡«å
itemIdï¼ |
| | | MesProFeedbackDO feedback = BeanUtils.toBean(createReqVO, MesProFeedbackDO.class) |
| | | .setId(null) |
| | | .setStatus(MesProFeedbackStatusEnum.PREPARE.getStatus()) |
| | | .setItemId(task.getItemId()); |
| | | feedbackMapper.insert(feedback); |
| | |
| | | } |
| | | Long routeId = routeProduct.getRouteId(); |
| | | |
| | | // 2. æ ¡éªå·¥èºè·¯çº¿æ¯å¦æå·¥åº |
| | | List<MesProRouteProcessDO> routeProcesses = routeProcessService.getRouteProcessListByRouteId(routeId); |
| | | // 2. æ ¡éªå·¥èºè·¯çº¿æ¯å¦æå·¥åºï¼æäº§åè¿æ»¤å
¶å®äº§åç产åºç©æï¼ |
| | | List<MesProRouteProcessDO> routeProcesses = routeProcessService |
| | | .getRouteProcessListByProductId(mps.getProductId()); |
| | | if (CollUtil.isEmpty(routeProcesses)) { |
| | | throw exception(PRO_ROUTE_ENABLE_NO_PROCESS); |
| | | } |
| | |
| | | .map(Long::valueOf).collect(Collectors.toList())); |
| | | } |
| | | process.setColorCode(rp.getColorCode()); |
| | | // 带å
¥å·¥èºè·¯çº¿äº§åé
ç½®çæ¥å·¥ä»åº |
| | | process.setWarehouseId(routeProduct.getWarehouseId()); |
| | | process.setLocationId(routeProduct.getLocationId()); |
| | | process.setAreaId(routeProduct.getAreaId()); |
| | | // 带å
¥å·¥åºææBOM |
| | | List<MesProRouteProductBomDO> routeBomItems = bomMap != null ? bomMap.get(rp.getProcessId()) : null; |
| | | if (CollUtil.isNotEmpty(routeBomItems)) { |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.pro.route; |
| | | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteCompleteImportWorkbook; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.product.MesProRouteProductSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.productbom.MesProRouteProductBomSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.process.MesProProcessMapper; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.support.TransactionTemplate; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Comparator; |
| | | import java.util.HashSet; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_ROUTE_IMPORT_LIST_IS_EMPTY; |
| | | |
| | | /** æè·¯çº¿åäºå¡æ¹é导å
¥å®æ´é
ç½®ï¼å¤±è´¥è·¯çº¿ä¸ä¼çä¸é¨å产åæå·¥åºã */ |
| | | @Service |
| | | public class MesProRouteCompleteImportService { |
| | | @Resource private TransactionTemplate transactionTemplate; |
| | | @Resource private MesProRouteService routeService; |
| | | @Resource private MesProRouteProductService productService; |
| | | @Resource private MesProRouteProcessService processService; |
| | | @Resource private MesProRouteProductBomService bomService; |
| | | @Resource private MesMdItemService itemService; |
| | | @Resource private MesProProcessMapper processMapper; |
| | | @Resource private MesWmWarehouseService warehouseService; |
| | | @Resource private MesWmWarehouseLocationService locationService; |
| | | @Resource private MesWmWarehouseAreaService areaService; |
| | | |
| | | public MesProRouteImportRespVO importRoutes(MesProRouteCompleteImportWorkbook book) { |
| | | if (book.getRoutes() == null || book.getRoutes().isEmpty()) { |
| | | throw exception(PRO_ROUTE_IMPORT_LIST_IS_EMPTY); |
| | | } |
| | | MesProRouteImportRespVO result = MesProRouteImportRespVO.builder() |
| | | .createCodes(new ArrayList<>()).updateCodes(new ArrayList<>()) |
| | | .failureCodes(new LinkedHashMap<>()).build(); |
| | | Set<String> seen = new HashSet<>(); |
| | | int rowNumber = 1; |
| | | for (MesProRouteCompleteImportWorkbook.RouteRow row : book.getRoutes()) { |
| | | rowNumber++; |
| | | String code = trim(row.getRouteCode()); |
| | | String key = StrUtil.isBlank(code) ? "路线表第 " + rowNumber + " è¡" : code; |
| | | if (StrUtil.isBlank(code) || StrUtil.isBlank(row.getName())) { |
| | | result.getFailureCodes().put(key, "路线ç¼ç ååç§°ä¸è½ä¸ºç©º"); |
| | | continue; |
| | | } |
| | | if (!seen.add(code)) { |
| | | result.getFailureCodes().put("路线表第 " + rowNumber + " è¡ï¼" + code + "ï¼", "导å
¥æä»¶ä¸è·¯çº¿ç¼ç éå¤"); |
| | | continue; |
| | | } |
| | | try { |
| | | transactionTemplate.execute(status -> { |
| | | importOne(book, row, code); |
| | | return null; |
| | | }); |
| | | result.getCreateCodes().add(code); |
| | | } catch (RuntimeException ex) { |
| | | result.getFailureCodes().put(key, ex.getMessage()); |
| | | } |
| | | } |
| | | Set<String> known = book.getRoutes().stream().map(r -> trim(r.getRouteCode())) |
| | | .collect(Collectors.toSet()); |
| | | collectUnknownRoutes(result.getFailureCodes(), known, "产å", book.getProducts() == null ? List.of() : |
| | | book.getProducts().stream().map(MesProRouteCompleteImportWorkbook.ProductRow::getRouteCode).toList()); |
| | | collectUnknownRoutes(result.getFailureCodes(), known, "å·¥åº", book.getProcesses() == null ? List.of() : |
| | | book.getProcesses().stream().map(MesProRouteCompleteImportWorkbook.ProcessRow::getRouteCode).toList()); |
| | | collectUnknownRoutes(result.getFailureCodes(), known, "å·¥åºäº§åº", book.getOutputs() == null ? List.of() : |
| | | book.getOutputs().stream().map(MesProRouteCompleteImportWorkbook.OutputRow::getRouteCode).toList()); |
| | | collectUnknownRoutes(result.getFailureCodes(), known, "å·¥åºææ", book.getInputs() == null ? List.of() : |
| | | book.getInputs().stream().map(MesProRouteCompleteImportWorkbook.InputRow::getRouteCode).toList()); |
| | | return result; |
| | | } |
| | | |
| | | private void importOne(MesProRouteCompleteImportWorkbook book, |
| | | MesProRouteCompleteImportWorkbook.RouteRow row, String code) { |
| | | List<MesProRouteCompleteImportWorkbook.ProductRow> products = rows(book.getProducts()).stream() |
| | | .filter(r -> code.equals(trim(r.getRouteCode()))).toList(); |
| | | List<MesProRouteCompleteImportWorkbook.ProcessRow> processes = rows(book.getProcesses()).stream() |
| | | .filter(r -> code.equals(trim(r.getRouteCode()))) |
| | | .sorted(Comparator.comparing(MesProRouteCompleteImportWorkbook.ProcessRow::getSort, |
| | | Comparator.nullsLast(Comparator.naturalOrder()))).toList(); |
| | | if (products.isEmpty() || processes.isEmpty()) { |
| | | throw new IllegalArgumentException("è³å°éè¦ä¸æ¡äº§ååä¸éå·¥åº"); |
| | | } |
| | | MesProRouteSaveReqVO route = new MesProRouteSaveReqVO(); |
| | | route.setCode(code); |
| | | route.setName(trim(row.getName())); |
| | | route.setDescription(row.getDescription()); |
| | | route.setRemark(row.getRemark()); |
| | | Long routeId = routeService.createRoute(route); |
| | | Set<String> productCodes = new HashSet<>(); |
| | | for (MesProRouteCompleteImportWorkbook.ProductRow product : products) { |
| | | String itemCode = trim(product.getItemCode()); |
| | | if (!productCodes.add(itemCode)) { |
| | | throw new IllegalArgumentException("产åç¼ç éå¤ï¼" + itemCode); |
| | | } |
| | | MesProRouteProductSaveReqVO request = new MesProRouteProductSaveReqVO(); |
| | | request.setRouteId(routeId); |
| | | request.setItemId(itemId(itemCode)); |
| | | MesWmWarehouseDO warehouse = warehouseService.getWarehouseByCode(trim(product.getWarehouseCode())); |
| | | MesWmWarehouseLocationDO location = locationService.getWarehouseLocationByCode(trim(product.getLocationCode())); |
| | | MesWmWarehouseAreaDO area = areaService.getWarehouseAreaByCode(trim(product.getAreaCode())); |
| | | if (warehouse == null || location == null || area == null) { |
| | | throw new IllegalArgumentException("产å " + itemCode + " çä»åºãåºåºæåºä½ç¼ç ä¸åå¨"); |
| | | } |
| | | request.setWarehouseId(warehouse.getId()); |
| | | request.setLocationId(location.getId()); |
| | | request.setAreaId(area.getId()); |
| | | request.setQuantity(product.getQuantity()); |
| | | request.setProductionTime(product.getProductionTime()); |
| | | request.setTimeUnitType(product.getTimeUnitType()); |
| | | request.setRemark(product.getRemark()); |
| | | productService.createRouteProduct(request); |
| | | } |
| | | Set<String> processCodes = new HashSet<>(); |
| | | Set<Integer> sorts = new HashSet<>(); |
| | | for (MesProRouteCompleteImportWorkbook.ProcessRow process : processes) { |
| | | String processCode = trim(process.getProcessCode()); |
| | | if (!processCodes.add(processCode) || process.getSort() == null || !sorts.add(process.getSort())) { |
| | | throw new IllegalArgumentException("å·¥åºç¼ç ææåºéå¤/为空ï¼" + processCode); |
| | | } |
| | | MesProProcessDO processDO = processMapper.selectByCode(processCode); |
| | | if (processDO == null) { |
| | | throw new IllegalArgumentException("å·¥åºç¼ç ä¸åå¨ï¼" + processCode); |
| | | } |
| | | MesProRouteProcessSaveReqVO request = new MesProRouteProcessSaveReqVO(); |
| | | request.setRouteId(routeId); |
| | | request.setProcessId(processDO.getId()); |
| | | request.setSort(process.getSort()); |
| | | request.setParamTemplateId(process.getParamTemplateId()); |
| | | request.setYieldRate(process.getYieldRate()); |
| | | request.setLinkType(process.getLinkType()); |
| | | request.setPrepareTime(process.getPrepareTime()); |
| | | request.setWaitTime(process.getWaitTime()); |
| | | request.setColorCode(process.getColorCode()); |
| | | request.setKeyFlag(flag(process.getKeyFlag(), processCode, "å
³é®å·¥åº")); |
| | | request.setCheckFlag(flag(process.getCheckFlag(), processCode, "è´¨æ£å·¥åº")); |
| | | request.setBackflushFlag(flag(process.getBackflushFlag(), processCode, "åå²")); |
| | | request.setRemark(process.getRemark()); |
| | | List<MesProRouteProcessSaveReqVO.OutputItem> outputs = new ArrayList<>(); |
| | | for (MesProRouteCompleteImportWorkbook.OutputRow output : rows(book.getOutputs())) { |
| | | if (!code.equals(trim(output.getRouteCode())) || !processCode.equals(trim(output.getProcessCode()))) { |
| | | continue; |
| | | } |
| | | MesProRouteProcessSaveReqVO.OutputItem value = new MesProRouteProcessSaveReqVO.OutputItem(); |
| | | value.setItemId(itemId(output.getItemCode())); |
| | | if (StrUtil.isNotBlank(output.getProductCode())) { |
| | | String ownerCode = trim(output.getProductCode()); |
| | | if (!productCodes.contains(ownerCode)) { |
| | | throw new IllegalArgumentException("产åºå½å±äº§åæªå
³è路线ï¼" + ownerCode); |
| | | } |
| | | value.setProductId(itemId(ownerCode)); |
| | | } |
| | | value.setSort(output.getSort()); |
| | | value.setRemark(output.getRemark()); |
| | | outputs.add(value); |
| | | } |
| | | request.setOutputs(outputs); |
| | | processService.createRouteProcess(request); |
| | | } |
| | | for (MesProRouteCompleteImportWorkbook.OutputRow output : rows(book.getOutputs())) { |
| | | if (code.equals(trim(output.getRouteCode())) && !processCodes.contains(trim(output.getProcessCode()))) { |
| | | throw new IllegalArgumentException("产åºå¼ç¨äºæªé
ç½®çå·¥åºï¼" + output.getProcessCode()); |
| | | } |
| | | } |
| | | for (MesProRouteCompleteImportWorkbook.InputRow input : rows(book.getInputs())) { |
| | | if (!code.equals(trim(input.getRouteCode()))) { |
| | | continue; |
| | | } |
| | | String processCode = trim(input.getProcessCode()); |
| | | String productCode = trim(input.getProductCode()); |
| | | if (!processCodes.contains(processCode) || !productCodes.contains(productCode)) { |
| | | throw new IllegalArgumentException("ææå¼ç¨äºæªé
ç½®çå·¥åºæäº§åï¼" + processCode + "/" + productCode); |
| | | } |
| | | MesProRouteProductBomSaveReqVO request = new MesProRouteProductBomSaveReqVO(); |
| | | request.setRouteId(routeId); |
| | | request.setProcessId(processMapper.selectByCode(processCode).getId()); |
| | | request.setProductId(itemId(productCode)); |
| | | request.setItemId(itemId(input.getItemCode())); |
| | | request.setQuantity(input.getQuantity()); |
| | | request.setRemark(input.getRemark()); |
| | | bomService.createRouteProductBom(request); |
| | | } |
| | | } |
| | | |
| | | private Long itemId(String code) { |
| | | String normalized = trim(code); |
| | | MesMdItemDO item = StrUtil.isBlank(normalized) ? null : itemService.getItemByCode(normalized); |
| | | if (item == null) { |
| | | throw new IllegalArgumentException("ç©æç¼ç ä¸åå¨ï¼" + normalized); |
| | | } |
| | | return item.getId(); |
| | | } |
| | | |
| | | private static boolean flag(String value, String processCode, String field) { |
| | | String normalized = trim(value); |
| | | if (StrUtil.isBlank(normalized) || "å¦".equals(normalized) |
| | | || "false".equalsIgnoreCase(normalized) || "0".equals(normalized)) { |
| | | return false; |
| | | } |
| | | if ("æ¯".equals(normalized) || "true".equalsIgnoreCase(normalized) || "1".equals(normalized)) { |
| | | return true; |
| | | } |
| | | throw new IllegalArgumentException("å·¥åº " + processCode + " ç" + field + "åªè½å¡«åæ¯æå¦"); |
| | | } |
| | | |
| | | private static String trim(String value) { |
| | | return StrUtil.trim(value); |
| | | } |
| | | |
| | | private static <T> List<T> rows(List<T> values) { |
| | | return values == null ? List.of() : values; |
| | | } |
| | | |
| | | private static void collectUnknownRoutes(Map<String, String> failures, Set<String> known, |
| | | String sheet, List<String> codes) { |
| | | for (String code : codes) { |
| | | if (!known.contains(trim(code))) { |
| | | failures.put(sheet + "表路线 " + code, "路线表ä¸ä¸å卿¤è·¯çº¿ç¼ç "); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | List<MesProRouteProcessDO> getRouteProcessListByProductId(Long productId); |
| | | |
| | | /** |
| | | * è·å¾å·¥åºå¨æå®äº§åä¸ç产åºç©æç¼å· |
| | | * |
| | | * æäº§åºå½å±è¿æ»¤ï¼å½å±ä¸ºç©ºï¼å¤äº§åéç¨ï¼æå½å±çäºæå®äº§åç产åºã |
| | | * æ å½å±æ°æ®æ¶è¿å {@code null}ï¼è°ç¨æ¹éèªè¡åéï¼æå½å±æ°æ®ä½è¯¥äº§åæ äº§åºæ¶è¿å空å表ã |
| | | * |
| | | * @param routeProcessId å·¥èºè·¯çº¿å·¥åºç¼å· |
| | | * @param productId 产åç¼å· |
| | | * @return 产åºç©æç¼å·åè¡¨ï¼ææåºï¼ï¼æ å½å±æ°æ®æ¶ä¸º null |
| | | */ |
| | | List<Long> getOutputItemIdsByRouteProcessId(Long routeProcessId, Long productId); |
| | | |
| | | /** |
| | | * æå·¥èºè·¯çº¿å·¥åºç¼å·æ¹éè·å¾äº§åºå½å±é
ç½® |
| | | * |
| | | * @param routeProcessIds å·¥èºè·¯çº¿å·¥åºç¼å·æ°ç» |
| | | * @return 产åºå½å±å表 |
| | | */ |
| | | List<cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO> |
| | | getRouteProcessOutputListByRouteProcessIds(Collection<Long> routeProcessIds); |
| | | |
| | | /** |
| | | * æäº§åè·å¾å·¥åºææBOM Map |
| | | * |
| | | * key = å·¥åºIDï¼value = 该工åºä¸çææBOMæç»å表 |
| | |
| | | package cn.iocoder.yudao.module.mes.service.pro.route; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.NumberUtil; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProcessMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProcessOutputMapper; |
| | | import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.context.annotation.Lazy; |
| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | |
| | | |
| | | @Resource |
| | | private MesProRouteProcessMapper routeProcessMapper; |
| | | @Resource |
| | | private MesProRouteProcessOutputMapper routeProcessOutputMapper; |
| | | |
| | | @Resource |
| | | @Lazy |
| | |
| | | |
| | | // 2. æå
¥ |
| | | MesProRouteProcessDO routeProcess = BeanUtils.toBean(createReqVO, MesProRouteProcessDO.class); |
| | | if (CollUtil.isNotEmpty(createReqVO.getOutputItemIds())) { |
| | | routeProcess.setOutputItemId( |
| | | createReqVO.getOutputItemIds().stream() |
| | | .map(String::valueOf) |
| | | .collect(Collectors.joining(","))); |
| | | } |
| | | routeProcess.setOutputItemId(resolveOutputItemId(createReqVO)); |
| | | routeProcessMapper.insert(routeProcess); |
| | | // 2.1 ä¿å产åºå½å±é
ç½® |
| | | saveRouteProcessOutputs(routeProcess, createReqVO); |
| | | |
| | | // 3. æ´æ°å·¥åºé¾ |
| | | rebuildProcessChain(createReqVO.getRouteId()); |
| | |
| | | public void updateRouteProcess(MesProRouteProcessSaveReqVO updateReqVO) { |
| | | // 1.0 å·²å¯ç¨çå·¥èºè·¯çº¿ï¼ä¸å
许æä½ |
| | | routeService.validateRouteNotEnable(updateReqVO.getRouteId()); |
| | | // 1.1 æ ¡éªåå¨ï¼é¨åæ´æ°ï¼å¦ä»
è°æ´äº§åºäº§åï¼æ¶æªä¼ çå¿
å¡«åæ®µç¨ç°æå¼åå¡« |
| | | // 1.1 æ ¡éªåå¨ï¼å
¼å®¹åªè°æ´äº§åºé
ç½®çé¨åæ´æ°è¯·æ± |
| | | MesProRouteProcessDO existing = validateRouteProcessExists(updateReqVO.getId()); |
| | | if (updateReqVO.getProcessId() == null) { |
| | | updateReqVO.setProcessId(existing.getProcessId()); |
| | |
| | | |
| | | // 2. æ´æ° |
| | | MesProRouteProcessDO updateObj = BeanUtils.toBean(updateReqVO, MesProRouteProcessDO.class); |
| | | if (CollUtil.isNotEmpty(updateReqVO.getOutputItemIds())) { |
| | | updateObj.setOutputItemId( |
| | | updateReqVO.getOutputItemIds().stream() |
| | | .map(String::valueOf) |
| | | .collect(Collectors.joining(","))); |
| | | } |
| | | // 空æ°ç»ä¹è¦æ¾å¼åå
¥ç©ºä¸²ï¼å¦å MyBatis-Plus é»è®¤å¿½ç¥ nullï¼ç¨æ·æ¸
空产åºååæ§é
ç½®ä»ä¼æ®çã |
| | | updateObj.setOutputItemId(resolveOutputItemId(updateReqVO)); |
| | | routeProcessMapper.updateById(updateObj); |
| | | // 2.1 è¦ç产åºå½å±é
ç½® |
| | | saveRouteProcessOutputs(updateObj, updateReqVO); |
| | | |
| | | // 3. é建工åºé¾ |
| | | rebuildProcessChain(updateReqVO.getRouteId()); |
| | |
| | | // 1.2 å·²å¯ç¨çå·¥èºè·¯çº¿ï¼ä¸å
许æä½ |
| | | routeService.validateRouteNotEnable(routeProcess.getRouteId()); |
| | | |
| | | // 2. å é¤ |
| | | // 2. å é¤ï¼å«äº§åºå½å±é
ç½®ï¼ |
| | | routeProcessMapper.deleteById(id); |
| | | routeProcessOutputMapper.deleteByRouteProcessId(id); |
| | | |
| | | // 3. é建工åºé¾ |
| | | rebuildProcessChain(routeProcess.getRouteId()); |
| | |
| | | return Collections.emptyList(); |
| | | } |
| | | // 2. è¿å该工èºè·¯çº¿çå·¥åºå表 |
| | | return routeProcessMapper.selectListByRouteId(routeProduct.getRouteId()); |
| | | List<MesProRouteProcessDO> list = routeProcessMapper.selectListByRouteId(routeProduct.getRouteId()); |
| | | // 3. æäº§åºå½å±è¿æ»¤ï¼éç¨äº§åº + å½å产åä¸å±äº§åº |
| | | Map<Long, List<MesProRouteProcessOutputDO>> outputMap = routeProcessOutputMapper |
| | | .selectListByRouteId(routeProduct.getRouteId()).stream() |
| | | .collect(Collectors.groupingBy(MesProRouteProcessOutputDO::getRouteProcessId)); |
| | | for (MesProRouteProcessDO process : list) { |
| | | List<MesProRouteProcessOutputDO> outputs = outputMap.get(process.getId()); |
| | | if (CollUtil.isEmpty(outputs)) { |
| | | // åéæ°æ®æªæåå½å±æ¶ï¼éåæ§çâåé¤å路线å
¶å®äº§åâé»è¾ |
| | | filterOtherProductOutputs(Collections.singletonList(process), routeProduct); |
| | | continue; |
| | | } |
| | | String filtered = outputs.stream() |
| | | .filter(output -> output.getProductId() == null || output.getProductId().equals(productId)) |
| | | .map(MesProRouteProcessOutputDO::getItemId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .map(String::valueOf) |
| | | .collect(Collectors.joining(",")); |
| | | process.setOutputItemId(filtered.isEmpty() ? null : filtered); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public List<Long> getOutputItemIdsByRouteProcessId(Long routeProcessId, Long productId) { |
| | | List<MesProRouteProcessOutputDO> outputs = routeProcessOutputMapper.selectListByRouteProcessId(routeProcessId); |
| | | if (CollUtil.isEmpty(outputs)) { |
| | | // æªé
置产åºå½å±ï¼åéæ°æ®ï¼è¿å nullï¼ä¾¿äºè°ç¨æ¹åéèåäº§åº |
| | | return null; |
| | | } |
| | | return outputs.stream() |
| | | .filter(output -> output.getProductId() == null || output.getProductId().equals(productId)) |
| | | .map(MesProRouteProcessOutputDO::getItemId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | @Override |
| | | public List<MesProRouteProcessOutputDO> getRouteProcessOutputListByRouteProcessIds(Collection<Long> routeProcessIds) { |
| | | if (CollUtil.isEmpty(routeProcessIds)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return routeProcessOutputMapper.selectListByRouteProcessIds(routeProcessIds); |
| | | } |
| | | |
| | | /** |
| | | * 计ç®å·¥åºäº§åºç©æçèå串ï¼éå·åéï¼å»éï¼ |
| | | * |
| | | * ä¼å
使ç¨äº§åºå½å±é
ç½®ï¼outputsï¼ï¼æªä¼ æ¶å
¼å®¹ outputItemIdsã |
| | | */ |
| | | private String resolveOutputItemId(MesProRouteProcessSaveReqVO reqVO) { |
| | | List<Long> itemIds; |
| | | if (CollUtil.isNotEmpty(reqVO.getOutputs())) { |
| | | itemIds = reqVO.getOutputs().stream() |
| | | .filter(Objects::nonNull) |
| | | .sorted(Comparator.comparing(MesProRouteProcessSaveReqVO.OutputItem::getSort, |
| | | Comparator.nullsLast(Comparator.naturalOrder()))) |
| | | .map(MesProRouteProcessSaveReqVO.OutputItem::getItemId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | } else if (CollUtil.isNotEmpty(reqVO.getOutputItemIds())) { |
| | | itemIds = reqVO.getOutputItemIds().stream() |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | } else { |
| | | itemIds = Collections.emptyList(); |
| | | } |
| | | return itemIds.stream().map(String::valueOf).collect(Collectors.joining(",")); |
| | | } |
| | | |
| | | /** |
| | | * è¦çä¿åå·¥åºäº§åºå½å±é
ç½® |
| | | */ |
| | | private void saveRouteProcessOutputs(MesProRouteProcessDO routeProcess, |
| | | MesProRouteProcessSaveReqVO reqVO) { |
| | | routeProcessOutputMapper.deleteByRouteProcessId(routeProcess.getId()); |
| | | if (CollUtil.isEmpty(reqVO.getOutputs()) && CollUtil.isEmpty(reqVO.getOutputItemIds())) { |
| | | return; |
| | | } |
| | | Set<Long> routeProductIds = routeProductService.getRouteProductListByRouteId(routeProcess.getRouteId()) |
| | | .stream().map(MesProRouteProductDO::getItemId).filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | List<MesProRouteProcessOutputDO> outputs = new ArrayList<>(); |
| | | Set<String> keys = new HashSet<>(); |
| | | int index = 0; |
| | | if (CollUtil.isNotEmpty(reqVO.getOutputs())) { |
| | | for (MesProRouteProcessSaveReqVO.OutputItem output : reqVO.getOutputs()) { |
| | | if (output == null || output.getItemId() == null) { |
| | | continue; |
| | | } |
| | | index++; |
| | | if (!keys.add(output.getProductId() + "_" + output.getItemId())) { |
| | | continue; |
| | | } |
| | | outputs.add(buildOutput(routeProcess, output.getProductId(), output.getItemId(), |
| | | output.getSort() != null ? output.getSort() : index, output.getRemark())); |
| | | } |
| | | } else { |
| | | // å
¼å®¹åªä¼ outputItemIds çè°ç¨æ¹ï¼äº§åºæ¯è·¯çº¿äº§åæ¶å½å±å°èªèº«ï¼å¦åè§ä¸ºéç¨ |
| | | for (Long itemId : reqVO.getOutputItemIds()) { |
| | | if (itemId == null) { |
| | | continue; |
| | | } |
| | | index++; |
| | | Long productId = routeProductIds.contains(itemId) ? itemId : null; |
| | | if (!keys.add(productId + "_" + itemId)) { |
| | | continue; |
| | | } |
| | | outputs.add(buildOutput(routeProcess, productId, itemId, index, null)); |
| | | } |
| | | } |
| | | if (CollUtil.isNotEmpty(outputs)) { |
| | | routeProcessOutputMapper.insertBatch(outputs); |
| | | } |
| | | } |
| | | |
| | | private MesProRouteProcessOutputDO buildOutput(MesProRouteProcessDO routeProcess, Long productId, |
| | | Long itemId, Integer sort, String remark) { |
| | | return MesProRouteProcessOutputDO.builder() |
| | | .routeProcessId(routeProcess.getId()) |
| | | .routeId(routeProcess.getRouteId()) |
| | | .processId(routeProcess.getProcessId()) |
| | | .productId(productId) |
| | | .itemId(itemId) |
| | | .sort(sort) |
| | | .remark(remark) |
| | | .build(); |
| | | } |
| | | |
| | | /** |
| | | * è¿æ»¤äº§åºç©æï¼åé¤åä¸å·¥èºè·¯çº¿ä¸å
¶å®äº§åç产åºï¼åªä¿çå½å产åç产åºååæåäº§åº |
| | | */ |
| | | private void filterOtherProductOutputs(List<MesProRouteProcessDO> list, MesProRouteProductDO routeProduct) { |
| | | if (CollUtil.isEmpty(list) || routeProduct.getItemId() == null) { |
| | | return; |
| | | } |
| | | Set<Long> otherProductIds = routeProductService.getRouteProductListByRouteId(routeProduct.getRouteId()) |
| | | .stream() |
| | | .map(MesProRouteProductDO::getItemId) |
| | | .filter(itemId -> itemId != null && !itemId.equals(routeProduct.getItemId())) |
| | | .collect(Collectors.toSet()); |
| | | if (otherProductIds.isEmpty()) { |
| | | return; |
| | | } |
| | | for (MesProRouteProcessDO process : list) { |
| | | String outputItemId = process.getOutputItemId(); |
| | | if (outputItemId == null || outputItemId.isBlank()) { |
| | | continue; |
| | | } |
| | | String filtered = Arrays.stream(outputItemId.split(",")) |
| | | .map(String::trim) |
| | | .filter(s -> !s.isEmpty()) |
| | | .filter(s -> !otherProductIds.contains(NumberUtil.parseLong(s, -1L))) |
| | | .collect(Collectors.joining(",")); |
| | | process.setOutputItemId(filtered.isEmpty() ? null : filtered); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | @Override |
| | | public void deleteRouteProcessByRouteId(Long routeId) { |
| | | routeProcessMapper.deleteByRouteId(routeId); |
| | | routeProcessOutputMapper.deleteByRouteId(routeId); |
| | | } |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.product.MesProRouteProductSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProductMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.md.MesMdItemTypeEnum; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | | import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | import cn.iocoder.yudao.module.mdm.enums.MdmItemTypeEnum; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.util.List; |
| | | import java.util.HashSet; |
| | | import java.util.Set; |
| | | import java.util.Objects; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; |
| | |
| | | @Resource |
| | | @Lazy |
| | | private MesProRouteProductBomService routeProductBomService; |
| | | @Resource |
| | | private MesMdItemService itemService; |
| | | @Resource |
| | | private MesMdItemTypeService itemTypeService; |
| | | @Resource |
| | | private MesWmWarehouseAreaService warehouseAreaService; |
| | | @Resource |
| | | private MdmItemApi mdmItemApi; |
| | | |
| | | @Override |
| | | public Long createRouteProduct(MesProRouteProductSaveReqVO createReqVO) { |
| | | // 1.0 å·²å¯ç¨çå·¥èºè·¯çº¿ï¼ä¸å
许æä½ |
| | | routeService.validateRouteNotEnable(createReqVO.getRouteId()); |
| | | // 1.1 æ ¡éªäº§åå¯ä¸æ§ï¼ä¸ä¸ªäº§ååªè½å
³è䏿¡å·¥èºè·¯çº¿ï¼ |
| | | // 1.1 æ ¡éªäº§ååå¨ãå¯ç¨ä¸ç±»åæ£ç¡® |
| | | validateProductItem(createReqVO.getItemId()); |
| | | // 1.2 æ ¡éªäº§åå¯ä¸æ§ï¼ä¸ä¸ªäº§ååªè½å
³è䏿¡å·¥èºè·¯çº¿ï¼ |
| | | validateItemUnique(null, createReqVO.getItemId()); |
| | | warehouseAreaService.validateWarehouseAreaExists(createReqVO.getWarehouseId(), |
| | | createReqVO.getLocationId(), createReqVO.getAreaId()); |
| | | |
| | | // 2. æå
¥ |
| | | MesProRouteProductDO routeProduct = BeanUtils.toBean(createReqVO, MesProRouteProductDO.class); |
| | |
| | | // 1.0 å·²å¯ç¨çå·¥èºè·¯çº¿ï¼ä¸å
许æä½ |
| | | routeService.validateRouteNotEnable(updateReqVO.getRouteId()); |
| | | // 1.1 æ ¡éªåå¨ |
| | | validateRouteProductExists(updateReqVO.getId()); |
| | | // 1.2 æ ¡éªäº§åå¯ä¸æ§ |
| | | MesProRouteProductDO existing = routeProductMapper.selectById(updateReqVO.getId()); |
| | | validateRouteProductExists(existing); |
| | | // åå²è·¯çº¿äº§åå¯è½éç¨æ§åç±»ï¼åªä¿®æ¹ä»ä½çé
ç½®æ¶ä¸éæ°æ¦æªåæç©æã |
| | | if (ObjUtil.notEqual(existing.getItemId(), updateReqVO.getItemId())) { |
| | | validateProductItem(updateReqVO.getItemId()); |
| | | } |
| | | // 1.3 æ ¡éªäº§åå¯ä¸æ§ |
| | | validateItemUnique(updateReqVO.getId(), updateReqVO.getItemId()); |
| | | warehouseAreaService.validateWarehouseAreaExists(updateReqVO.getWarehouseId(), |
| | | updateReqVO.getLocationId(), updateReqVO.getAreaId()); |
| | | |
| | | // 2. æ´æ° |
| | | MesProRouteProductDO updateObj = BeanUtils.toBean(updateReqVO, MesProRouteProductDO.class); |
| | |
| | | } |
| | | } |
| | | |
| | | private void validateProductItem(Long itemId) { |
| | | MesMdItemDO item = itemService.validateItemExistsAndEnable(itemId); |
| | | Long typeId = item.getItemTypeId(); |
| | | Set<Long> visited = new HashSet<>(); |
| | | while (typeId != null && !MesMdItemTypeDO.PARENT_ID_ROOT.equals(typeId) && visited.add(typeId)) { |
| | | MesMdItemTypeDO itemType = itemTypeService.getItemType(typeId); |
| | | if (itemType == null) { |
| | | break; |
| | | } |
| | | // å
¼å®¹æ§åç±»ï¼å¯è½æªè®¾ç½® PRODUCT æ è¯ï¼ä¹æªéç¨ FG/SF ç¼ç ã |
| | | if (MesMdItemTypeEnum.isProduct(itemType.getItemOrProduct()) |
| | | || "FG".equalsIgnoreCase(itemType.getCode()) |
| | | || "SF".equalsIgnoreCase(itemType.getCode()) |
| | | || isProductCategoryName(itemType.getName())) { |
| | | return; |
| | | } |
| | | typeId = itemType.getParentId(); |
| | | } |
| | | // MES å
¼å®¹åç±»å¯è½ä»æ 记为 ITEMï¼ä»¥ MDM ç©æä¸»æ°æ®çæå/åæåç±»åå
åºã |
| | | CommonResult<MdmItemRespDTO> mdmResult = mdmItemApi.getItemByCode(item.getCode()); |
| | | if (mdmResult != null && mdmResult.isSuccess() && mdmResult.getData() != null) { |
| | | MdmItemRespDTO mdmItem = mdmResult.getData(); |
| | | Integer mdmType = mdmItem.getItemType(); |
| | | // åå²åæ¥æ¾å°åç±» ID åå
¥ itemTypeï¼ä¸ categoryId ç¸åçå¼ä¸è½å½ä½ç±»åæä¸¾ã |
| | | if (!Objects.equals(mdmItem.getCategoryId(), mdmType == null ? null : mdmType.longValue()) |
| | | && (MdmItemTypeEnum.PRODUCT.getType().equals(mdmType) |
| | | || MdmItemTypeEnum.SEMI_FINISHED.getType().equals(mdmType))) { |
| | | return; |
| | | } |
| | | } |
| | | throw exception(PRO_ROUTE_PRODUCT_ITEM_TYPE_INVALID, item.getName()); |
| | | } |
| | | |
| | | private boolean isProductCategoryName(String name) { |
| | | return name != null && (name.contains("åæå") || name.contains("产æå") |
| | | || (name.contains("æå") && !name.contains("éæå"))); |
| | | } |
| | | |
| | | private void validateItemUnique(Long id, Long itemId) { |
| | | MesProRouteProductDO existing = routeProductMapper.selectByItemId(itemId); |
| | | if (existing == null) { |
| | |
| | | package cn.iocoder.yudao.module.mes.service.pro.route; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportExcelVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRoutePageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO; |
| | |
| | | return convertMap(getRouteList(ids), MesProRouteDO::getId); |
| | | } |
| | | |
| | | /** |
| | | * æ¹é导å
¥å·¥èºè·¯çº¿ |
| | | * |
| | | * @param importList 导å
¥å·¥èºè·¯çº¿å表 |
| | | * @param updateSupport æ¯å¦æ¯ææ´æ° |
| | | * @return 导å
¥ç»æ |
| | | */ |
| | | MesProRouteImportRespVO importRouteList(List<MesProRouteImportExcelVO> importList, |
| | | boolean updateSupport); |
| | | |
| | | } |
| | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.BooleanUtil; |
| | | import cn.hutool.core.util.ObjUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportExcelVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRoutePageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteMapper; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | | import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | import cn.iocoder.yudao.module.mdm.enums.MdmItemTypeEnum; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.Comparator; |
| | | import java.util.Arrays; |
| | | import java.util.Objects; |
| | | import java.util.stream.Collectors; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; |
| | |
| | | @Resource |
| | | @Lazy |
| | | private MesMdItemService itemService; |
| | | @Resource |
| | | private MesMdItemTypeService itemTypeService; |
| | | @Resource |
| | | private MesWmWarehouseAreaService warehouseAreaService; |
| | | @Resource |
| | | private MdmItemApi mdmItemApi; |
| | | |
| | | @Override |
| | | public Long createRoute(MesProRouteSaveReqVO createReqVO) { |
| | |
| | | // 3. ææäº§åå¿
é¡»é
ç½®äº BOM æ¶è |
| | | List<MesProRouteProductDO> productList = routeProductService.getRouteProductListByRouteId(routeId); |
| | | for (MesProRouteProductDO product : productList) { |
| | | MesMdItemDO item = itemService.validateItemExists(product.getItemId()); |
| | | if (product.getWarehouseId() == null || product.getLocationId() == null || product.getAreaId() == null) { |
| | | throw exception(PRO_ROUTE_PRODUCT_WAREHOUSE_REQUIRED, item.getName()); |
| | | } |
| | | warehouseAreaService.validateWarehouseAreaExists(product.getWarehouseId(), |
| | | product.getLocationId(), product.getAreaId()); |
| | | List<MesProRouteProductBomDO> bomList = routeProductBomService |
| | | .getRouteProductBomList(routeId, null, product.getItemId()); |
| | | if (CollUtil.isEmpty(bomList)) { |
| | | MesMdItemDO item = itemService.validateItemExists(product.getItemId()); |
| | | throw exception(PRO_ROUTE_ENABLE_PRODUCT_NO_BOM, item.getName()); |
| | | } |
| | | } |
| | | validateProcessOutputs(processList, productList); |
| | | } |
| | | |
| | | private void validateProcessOutputs(List<MesProRouteProcessDO> processList, |
| | | List<MesProRouteProductDO> productList) { |
| | | if (CollUtil.isEmpty(productList)) { |
| | | throw exception(PRO_ROUTE_LAST_OUTPUT_INVALID, processList.get(processList.size() - 1).getProcessId()); |
| | | } |
| | | Set<Long> boundItemIds = productList.stream().map(MesProRouteProductDO::getItemId).collect(Collectors.toSet()); |
| | | Set<Long> finishedItemIds = productList.stream() |
| | | .map(MesProRouteProductDO::getItemId) |
| | | .filter(itemId -> isItemUnderCategory(itemId, "FG")) |
| | | .collect(Collectors.toSet()); |
| | | MesProRouteProcessDO lastProcess = processList.stream() |
| | | .max(Comparator.comparing(MesProRouteProcessDO::getSort)).orElseThrow(); |
| | | for (MesProRouteProcessDO process : processList) { |
| | | Set<Long> outputIds = parseOutputItemIds(process.getOutputItemId()); |
| | | boolean last = process.getId().equals(lastProcess.getId()); |
| | | if (CollUtil.isEmpty(outputIds)) { |
| | | // ä¸é´å·¥åºä¸ä¸å®å½¢æå¯å
¥åºçç¬ç«åæåï¼å
许ä¸é
置产åºï¼æ«éå·¥åºå¿
é¡»äº§åºæåã |
| | | if (last) { |
| | | throw exception(PRO_ROUTE_LAST_OUTPUT_INVALID, process.getProcessId()); |
| | | } |
| | | continue; |
| | | } |
| | | for (Long outputId : outputIds) { |
| | | if (!boundItemIds.contains(outputId)) { |
| | | MesMdItemDO item = itemService.validateItemExists(outputId); |
| | | throw exception(PRO_ROUTE_PROCESS_OUTPUT_NOT_BOUND, item.getName()); |
| | | } |
| | | } |
| | | if (last) { |
| | | if (finishedItemIds.isEmpty() || !outputIds.equals(finishedItemIds)) { |
| | | throw exception(PRO_ROUTE_LAST_OUTPUT_INVALID, process.getProcessId()); |
| | | } |
| | | } else if (outputIds.stream().anyMatch(itemId -> !isItemUnderCategory(itemId, "SF"))) { |
| | | throw exception(PRO_ROUTE_MIDDLE_OUTPUT_INVALID, process.getProcessId()); |
| | | } |
| | | } |
| | | // 4. æ«éå·¥åºæå产åºå¿
é¡»æäº§åå½å±ï¼é¿å
âéç¨â产åºä¸²å°å
¶å®äº§åçå·¥å |
| | | List<MesProRouteProcessOutputDO> lastOutputs = routeProcessService |
| | | .getRouteProcessOutputListByRouteProcessIds(Collections.singletonList(lastProcess.getId())); |
| | | if (CollUtil.isNotEmpty(lastOutputs)) { |
| | | boolean attributionInvalid = lastOutputs.stream().anyMatch(output -> |
| | | output.getProductId() == null || !output.getProductId().equals(output.getItemId())); |
| | | Set<Long> attributedProducts = lastOutputs.stream() |
| | | .map(MesProRouteProcessOutputDO::getProductId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | if (attributionInvalid || !attributedProducts.containsAll(finishedItemIds)) { |
| | | throw exception(PRO_ROUTE_LAST_OUTPUT_ATTRIBUTION_REQUIRED, lastProcess.getProcessId()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private Set<Long> parseOutputItemIds(String value) { |
| | | if (StrUtil.isBlank(value)) { |
| | | return Collections.emptySet(); |
| | | } |
| | | return Arrays.stream(value.split(",")).map(String::trim).filter(StrUtil::isNotBlank) |
| | | .map(Long::valueOf).collect(Collectors.toSet()); |
| | | } |
| | | |
| | | private boolean isItemUnderCategory(Long itemId, String rootCode) { |
| | | MesMdItemDO item = itemService.validateItemExists(itemId); |
| | | MesMdItemTypeDO type = itemTypeService.getItemType(item.getItemTypeId()); |
| | | while (type != null) { |
| | | if ("SF".equalsIgnoreCase(type.getCode()) |
| | | || (type.getName() != null && type.getName().contains("åæå"))) { |
| | | return "SF".equals(rootCode); |
| | | } |
| | | if ("FG".equalsIgnoreCase(type.getCode()) || isFinishedCategoryName(type.getName())) { |
| | | return "FG".equals(rootCode); |
| | | } |
| | | if (type.getParentId() == null || MesMdItemTypeDO.PARENT_ID_ROOT.equals(type.getParentId())) { |
| | | break; |
| | | } |
| | | type = itemTypeService.getItemType(type.getParentId()); |
| | | } |
| | | // MES åç±»æªä½¿ç¨ FG/SF ç¼ç æ¶ï¼æ MDM ä¸»æ°æ®çç©æç±»åå¤å®ã |
| | | CommonResult<MdmItemRespDTO> mdmResult = mdmItemApi.getItemByCode(item.getCode()); |
| | | if (mdmResult == null || !mdmResult.isSuccess() || mdmResult.getData() == null) { |
| | | return false; |
| | | } |
| | | MdmItemRespDTO mdmItem = mdmResult.getData(); |
| | | Integer mdmType = mdmItem.getItemType(); |
| | | // åå²åæ¥å¯è½æåç±» ID åå¨ itemTypeï¼ä¸è½æåç±» 2/3 误å¤ä¸ºæå/åæåã |
| | | if (Objects.equals(mdmItem.getCategoryId(), mdmType == null ? null : mdmType.longValue())) { |
| | | return false; |
| | | } |
| | | return "FG".equals(rootCode) ? MdmItemTypeEnum.PRODUCT.getType().equals(mdmType) |
| | | : "SF".equals(rootCode) && MdmItemTypeEnum.SEMI_FINISHED.getType().equals(mdmType); |
| | | } |
| | | |
| | | private boolean isFinishedCategoryName(String name) { |
| | | return name != null && !name.contains("åæå") && !name.contains("éæå") |
| | | && (name.contains("产æå") || name.contains("æå")); |
| | | } |
| | | |
| | | @Override |
| | |
| | | return routeMapper.selectByIds(ids); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public MesProRouteImportRespVO importRouteList(List<MesProRouteImportExcelVO> importList, |
| | | boolean updateSupport) { |
| | | // 1. æ ¡éªé空 |
| | | if (CollUtil.isEmpty(importList)) { |
| | | throw exception(PRO_ROUTE_IMPORT_LIST_IS_EMPTY); |
| | | } |
| | | |
| | | // 2. éè¡å¤ç |
| | | MesProRouteImportRespVO respVO = MesProRouteImportRespVO.builder() |
| | | .createCodes(new ArrayList<>()).updateCodes(new ArrayList<>()) |
| | | .failureCodes(new LinkedHashMap<>()).build(); |
| | | AtomicInteger index = new AtomicInteger(1); |
| | | importList.forEach(importItem -> { |
| | | int currentIndex = index.getAndIncrement(); |
| | | String key = StrUtil.blankToDefault(importItem.getCode(), "第 " + currentIndex + " è¡"); |
| | | if (StrUtil.isBlank(importItem.getCode())) { |
| | | respVO.getFailureCodes().put(key, "å·¥èºè·¯çº¿ç¼ç ä¸è½ä¸ºç©º"); |
| | | return; |
| | | } |
| | | if (StrUtil.isBlank(importItem.getName())) { |
| | | respVO.getFailureCodes().put(key, "å·¥èºè·¯çº¿åç§°ä¸è½ä¸ºç©º"); |
| | | return; |
| | | } |
| | | // 2.1 夿ï¼å建 or æ´æ° |
| | | MesProRouteDO existRoute = routeMapper.selectByCode(importItem.getCode()); |
| | | if (existRoute == null) { |
| | | // 2.1.1 æ°å¢ï¼ä¸æå·¥å建ä¸è´ï¼é»è®¤ç¦ç¨ï¼éè¿å¯ç¨æ¥å£æ ¡éªåå¯ç¨ï¼ |
| | | MesProRouteDO route = BeanUtils.toBean(importItem, MesProRouteDO.class); |
| | | route.setId(null); |
| | | route.setStatus(CommonStatusEnum.DISABLE.getStatus()); |
| | | routeMapper.insert(route); |
| | | respVO.getCreateCodes().add(importItem.getCode()); |
| | | } else if (updateSupport) { |
| | | // 2.1.2 æ´æ°ï¼ç¶æç±å¯ç¨/ç¦ç¨æ¥å£åç¬ç»´æ¤ï¼å¯¼å
¥ä¸ä¿®æ¹ç¶æï¼ |
| | | MesProRouteDO updateObj = BeanUtils.toBean(importItem, MesProRouteDO.class); |
| | | updateObj.setId(existRoute.getId()); |
| | | updateObj.setCode(existRoute.getCode()); |
| | | routeMapper.updateById(updateObj); |
| | | respVO.getUpdateCodes().add(importItem.getCode()); |
| | | } else { |
| | | respVO.getFailureCodes().put(key, "å·¥èºè·¯çº¿ç¼ç å·²åå¨"); |
| | | } |
| | | }); |
| | | return respVO; |
| | | } |
| | | |
| | | } |
| | |
| | | if (itemId == null) { |
| | | MesProRouteProcessDO routeProcess = routeProcessService |
| | | .getRouteProcessByRouteIdAndProcessId(createReqVO.getRouteId(), createReqVO.getProcessId()); |
| | | if (routeProcess != null && routeProcess.getOutputItemId() != null) { |
| | | itemId = Long.valueOf(routeProcess.getOutputItemId().split(",")[0].trim()); |
| | | } else { |
| | | itemId = workOrder.getProductId(); |
| | | } |
| | | itemId = resolveTaskItemId(routeProcess, workOrder.getProductId()); |
| | | } |
| | | MesMdItemDO item = itemService.validateItemExists(itemId); |
| | | |
| | |
| | | throw exception(cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_ROUTE_ENABLE_NO_PROCESS); |
| | | } |
| | | return routeProcesses; |
| | | } |
| | | |
| | | /** å½å产åçä»»å¡äº§åºï¼ä¼å
使ç¨å·¥åºç产åå½å±é
ç½®ã */ |
| | | private Long resolveTaskItemId(MesProRouteProcessDO routeProcess, Long productId) { |
| | | if (routeProcess == null) { |
| | | return productId; |
| | | } |
| | | if (routeProcess.getId() != null) { |
| | | List<Long> outputIds = routeProcessService |
| | | .getOutputItemIdsByRouteProcessId(routeProcess.getId(), productId); |
| | | if (outputIds != null) { |
| | | return CollUtil.isNotEmpty(outputIds) ? outputIds.get(0) : productId; |
| | | } |
| | | } |
| | | String outputItemId = routeProcess.getOutputItemId(); |
| | | if (outputItemId != null && !outputItemId.isBlank()) { |
| | | return Long.valueOf(outputItemId.split(",")[0].trim()); |
| | | } |
| | | return productId; |
| | | } |
| | | |
| | | @Override |
| | |
| | | List<Long> taskIds = new java.util.ArrayList<>(); |
| | | for (MesProRouteProcessDO rp : routeProcesses) { |
| | | cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO proc = procMap.get(rp.getProcessId()); |
| | | Long itemId = rp.getOutputItemId() != null ? Long.valueOf(rp.getOutputItemId().split(",")[0].trim()) : workOrder.getProductId(); |
| | | Long itemId = resolveTaskItemId(rp, workOrder.getProductId()); |
| | | cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO item = itemService.validateItemExists(itemId); |
| | | MesProTaskDO task = new MesProTaskDO() |
| | | .setWorkOrderId(workOrder.getId()) |
| | |
| | | List<MesProWorkOrderProcessDO> getWorkOrderProcessListByWorkOrderId(Long workOrderId); |
| | | |
| | | /** |
| | | * æ ¹æ®å·¥åç¼å· + å·¥åºç¼å·è·å¾å·¥åå·¥åº |
| | | * |
| | | * @param workOrderId å·¥åç¼å· |
| | | * @param processId å·¥åºç¼å· |
| | | * @return å·¥åå·¥åº |
| | | */ |
| | | MesProWorkOrderProcessDO getWorkOrderProcessByWorkOrderIdAndProcessId(Long workOrderId, Long processId); |
| | | |
| | | /** |
| | | * æ ¹æ®å·¥åç¼å·è·å¾æåä¸éå·¥åº |
| | | * |
| | | * @param workOrderId å·¥åç¼å· |
| | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderBomMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderProcessMapper; |
| | | import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | |
| | | private MesProProcessService processService; |
| | | @Resource |
| | | private MesProRouteProcessService routeProcessService; |
| | | @Resource |
| | | private MesProRouteProductService routeProductService; |
| | | @Resource |
| | | private MesWmWarehouseAreaService warehouseAreaService; |
| | | @Resource |
| | | @Lazy |
| | | private MesProWorkOrderBomService workOrderBomService; |
| | |
| | | .collect(Collectors.joining(","))); |
| | | } |
| | | workOrderProcessMapper.updateById(updateObj); |
| | | // æ¥å·¥ä»åºå
许置空ï¼åç¬æ´æ° |
| | | workOrderProcessMapper.updateWarehouseById(updateReqVO.getId(), updateReqVO.getWarehouseId(), |
| | | updateReqVO.getLocationId(), updateReqVO.getAreaId()); |
| | | |
| | | // 3. é建工åºé¾ |
| | | rebuildProcessChain(updateReqVO.getWorkOrderId()); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public MesProWorkOrderProcessDO getWorkOrderProcessByWorkOrderIdAndProcessId(Long workOrderId, Long processId) { |
| | | return workOrderProcessMapper.selectByWorkOrderIdAndProcessId(workOrderId, processId); |
| | | } |
| | | |
| | | @Override |
| | | public MesProWorkOrderProcessDO getLastWorkOrderProcessByWorkOrderId(Long workOrderId) { |
| | | return workOrderProcessMapper.selectLastByWorkOrderId(workOrderId); |
| | | } |
| | |
| | | if (CollUtil.isEmpty(routeProcessList)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | // å·¥èºè·¯çº¿äº§åä¸é
ç½®çæ¥å·¥ä»åºä½ä¸ºå·¥åºé»è®¤å¼ï¼ä¸åæ¶å¯åä¿®æ¹ |
| | | MesProRouteProductDO routeProduct = routeProductService.getRouteProductByItemId(productId); |
| | | // 转æ¢ä¸ºå·¥åå·¥åº DOï¼ä¸è®¾ç½® workOrderIdï¼å端ä¿åæ¶åè®¾ç½®ï¼ |
| | | List<MesProWorkOrderProcessDO> processList = new ArrayList<>(routeProcessList.size()); |
| | | for (MesProRouteProcessDO routeProcess : routeProcessList) { |
| | |
| | | .checkFlag(routeProcess.getCheckFlag()) |
| | | .backflushFlag(routeProcess.getBackflushFlag()) |
| | | .outputItemId(routeProcess.getOutputItemId()) |
| | | .warehouseId(routeProduct != null ? routeProduct.getWarehouseId() : null) |
| | | .locationId(routeProduct != null ? routeProduct.getLocationId() : null) |
| | | .areaId(routeProduct != null ? routeProduct.getAreaId() : null) |
| | | .remark(routeProcess.getRemark()) |
| | | .build()); |
| | | } |
| | |
| | | updateObj.setOutputItemId(item.getOutputItemIds().stream().map(String::valueOf).collect(Collectors.joining(","))); |
| | | } |
| | | workOrderProcessMapper.updateById(updateObj); |
| | | // æ¥å·¥ä»åºå
许置空ï¼åç¬æ´æ° |
| | | workOrderProcessMapper.updateWarehouseById(item.getId(), item.getWarehouseId(), |
| | | item.getLocationId(), item.getAreaId()); |
| | | } |
| | | } |
| | | |
| | |
| | | throw exception(PRO_WORK_ORDER_PROCESS_DUPLICATE); |
| | | } |
| | | } |
| | | // 4. æ ¡éªå
³é®å·¥åºå¯ä¸ |
| | | // 4. æ ¡éªæ¥å·¥ä»åºå±çº§ï¼ä»åº/åºåº/åºä½åæ¶å¡«åæ¶æ ¡éªå½å±å
³ç³»ï¼ |
| | | if (reqVO.getWarehouseId() != null && reqVO.getLocationId() != null && reqVO.getAreaId() != null) { |
| | | warehouseAreaService.validateWarehouseAreaExists(reqVO.getWarehouseId(), |
| | | reqVO.getLocationId(), reqVO.getAreaId()); |
| | | } |
| | | // 5. æ ¡éªå
³é®å·¥åºå¯ä¸ |
| | | if (Boolean.TRUE.equals(reqVO.getKeyFlag())) { |
| | | for (MesProWorkOrderProcessDO process : list) { |
| | | if (reqVO.getId() != null && reqVO.getId().equals(process.getId())) { |
| | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDetailDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; |
| | |
| | | private MesWmProductProduceDetailService productProduceDetailService; |
| | | @Resource |
| | | private MesProWorkOrderService workOrderService; |
| | | @Resource |
| | | private MesProWorkOrderProcessService workOrderProcessService; |
| | | @Resource |
| | | private MesProRouteProductService routeProductService; |
| | | @Resource |
| | | private MesWmBatchService batchService; |
| | | @Resource |
| | |
| | | Long batchId = batch != null ? batch.getId() : null; |
| | | String batchCode = batch != null ? batch.getCode() : null; |
| | | |
| | | // 1.5 è·åèæçº¿è¾¹åº |
| | | MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE); |
| | | MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION); |
| | | MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA); |
| | | WarehouseTarget outputTarget = resolveOutputTarget(feedback.getWorkOrderId(), feedback.getProcessId(), feedback.getItemId()); |
| | | |
| | | // 3. æ ¹æ®æ¯å¦éè¦æ£éªåæ¯å¤ç |
| | | if (checkFlag) { |
| | |
| | | productProduceLineService.createProductProduceLine(unqualifiedLine); |
| | | MesWmProductProduceDetailDO unqualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode, |
| | | unqualifiedLine.getId(), unqualifiedQty, |
| | | virtualWarehouse.getId(), virtualLocation.getId(), virtualArea.getId()); |
| | | outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId()); |
| | | productProduceDetailService.createProductProduceDetail(unqualifiedDetail); |
| | | } |
| | | // 3.2.2 åæ ¼åè¡ + æç» |
| | |
| | | productProduceLineService.createProductProduceLine(qualifiedLine); |
| | | MesWmProductProduceDetailDO qualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode, |
| | | qualifiedLine.getId(), qualifiedQty, |
| | | virtualWarehouse.getId(), virtualLocation.getId(), virtualArea.getId()); |
| | | outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId()); |
| | | productProduceDetailService.createProductProduceDetail(qualifiedDetail); |
| | | } |
| | | } |
| | |
| | | if (produce == null) { |
| | | throw exception(WM_PRODUCT_PRODUCE_NOT_EXISTS); |
| | | } |
| | | // 1.2 è·åèæçº¿è¾¹åº |
| | | MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE); |
| | | MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION); |
| | | MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA); |
| | | |
| | | // 2. éè¿ sourceLineId ç´æ¥å®ä½å¾
æ£éªè¡ |
| | | MesWmProductProduceLineDO pendingLine = productProduceLineService.validateProductProduceLineExists(sourceLineId); |
| | | WarehouseTarget outputTarget = resolveOutputTarget(produce.getWorkOrderId(), produce.getProcessId(), pendingLine.getItemId()); |
| | | |
| | | // 3A. æ
åµä¸ï¼åå¨ä¸åæ ¼åæ°éï¼éè¦æåè¡ |
| | | if (unqualifiedQty != null && unqualifiedQty.compareTo(BigDecimal.ZERO) > 0) { |
| | |
| | | .produceId(produce.getId()).lineId(unqualifiedLine.getId()) |
| | | .itemId(pendingLine.getItemId()).quantity(unqualifiedQty) |
| | | .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()) |
| | | .warehouseId(virtualWarehouse.getId()).locationId(virtualLocation.getId()).areaId(virtualArea.getId()) |
| | | .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId()) |
| | | .build()); |
| | | // 3A.2 åæ ¼åï¼å¤ç¨åå¾
æ£è¡ï¼æ´æ°æ°éåç¶æ + æç» |
| | | pendingLine.setQuantity(ObjectUtil.defaultIfNull(qualifiedQty, BigDecimal.ZERO)); |
| | |
| | | .produceId(produce.getId()).lineId(pendingLine.getId()) |
| | | .itemId(pendingLine.getItemId()).quantity(qualifiedQty) |
| | | .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()) |
| | | .warehouseId(virtualWarehouse.getId()).locationId(virtualLocation.getId()).areaId(virtualArea.getId()) |
| | | .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId()) |
| | | .build()); |
| | | } |
| | | } else { |
| | |
| | | .produceId(produce.getId()).lineId(pendingLine.getId()) |
| | | .itemId(pendingLine.getItemId()).quantity(pendingLine.getQuantity()) |
| | | .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()) |
| | | .warehouseId(virtualWarehouse.getId()).locationId(virtualLocation.getId()).areaId(virtualArea.getId()) |
| | | .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId()) |
| | | .build()); |
| | | } |
| | | |
| | |
| | | finishProductProduce(produce.getId()); |
| | | } |
| | | |
| | | private WarehouseTarget resolveOutputTarget(Long workOrderId, Long processId, Long itemId) { |
| | | if (workOrderId != null && processId != null) { |
| | | MesProWorkOrderProcessDO process = workOrderProcessService |
| | | .getWorkOrderProcessByWorkOrderIdAndProcessId(workOrderId, processId); |
| | | if (process != null && process.getWarehouseId() != null |
| | | && process.getLocationId() != null && process.getAreaId() != null) { |
| | | areaService.validateWarehouseAreaExists(process.getWarehouseId(), process.getLocationId(), process.getAreaId()); |
| | | return new WarehouseTarget(process.getWarehouseId(), process.getLocationId(), process.getAreaId()); |
| | | } |
| | | } |
| | | MesProRouteProductDO product = itemId == null ? null : routeProductService.getRouteProductByItemId(itemId); |
| | | if (product != null && product.getWarehouseId() != null |
| | | && product.getLocationId() != null && product.getAreaId() != null) { |
| | | areaService.validateWarehouseAreaExists(product.getWarehouseId(), product.getLocationId(), product.getAreaId()); |
| | | return new WarehouseTarget(product.getWarehouseId(), product.getLocationId(), product.getAreaId()); |
| | | } |
| | | MesWmWarehouseDO warehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE); |
| | | MesWmWarehouseLocationDO location = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION); |
| | | MesWmWarehouseAreaDO area = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA); |
| | | return new WarehouseTarget(warehouse.getId(), location.getId(), area.getId()); |
| | | } |
| | | |
| | | private record WarehouseTarget(Long warehouseId, Long locationId, Long areaId) { } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.pro.route; |
| | | |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.product.MesProRouteProductSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProductMapper; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | | import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.junit.jupiter.api.extension.ExtendWith; |
| | | import org.mockito.InjectMocks; |
| | | import org.mockito.Mock; |
| | | import org.mockito.junit.jupiter.MockitoExtension; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; |
| | | import static org.junit.jupiter.api.Assertions.assertThrows; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | @ExtendWith(MockitoExtension.class) |
| | | class MesProRouteProductServiceImplTest { |
| | | |
| | | @InjectMocks |
| | | private MesProRouteProductServiceImpl service; |
| | | @Mock |
| | | private MesProRouteProductMapper routeProductMapper; |
| | | @Mock |
| | | private MesProRouteService routeService; |
| | | @Mock |
| | | private MesMdItemService itemService; |
| | | @Mock |
| | | private MesMdItemTypeService itemTypeService; |
| | | @Mock |
| | | private MesWmWarehouseAreaService warehouseAreaService; |
| | | @Mock |
| | | private MdmItemApi mdmItemApi; |
| | | |
| | | @Test |
| | | void updateWarehouseForExistingItemDoesNotRevalidateLegacyCategory() { |
| | | MesProRouteProductSaveReqVO request = request(88L, 815L); |
| | | MesProRouteProductDO existing = new MesProRouteProductDO().setId(88L).setItemId(815L); |
| | | when(routeProductMapper.selectById(88L)).thenReturn(existing); |
| | | when(routeProductMapper.selectByItemId(815L)).thenReturn(existing); |
| | | |
| | | assertDoesNotThrow(() -> service.updateRouteProduct(request)); |
| | | verifyNoInteractions(itemService, itemTypeService); |
| | | verify(routeProductMapper).updateById(any(MesProRouteProductDO.class)); |
| | | } |
| | | |
| | | @Test |
| | | void createAcceptsSemiFinishedItemInSfCategoryTree() { |
| | | MesProRouteProductSaveReqVO request = request(null, 815L); |
| | | when(itemService.validateItemExistsAndEnable(815L)) |
| | | .thenReturn(new MesMdItemDO().setId(815L).setName("å°è£
åæå").setItemTypeId(23L)); |
| | | when(itemTypeService.getItemType(23L)).thenReturn(new MesMdItemTypeDO() |
| | | .setId(23L).setCode("PACKAGED").setItemOrProduct("ITEM").setParentId(2L)); |
| | | when(itemTypeService.getItemType(2L)).thenReturn(new MesMdItemTypeDO() |
| | | .setId(2L).setCode("SF").setParentId(0L)); |
| | | |
| | | assertDoesNotThrow(() -> service.createRouteProduct(request)); |
| | | verify(routeProductMapper).insert(any(MesProRouteProductDO.class)); |
| | | } |
| | | |
| | | @Test |
| | | void createRejectsRawMaterialOutsideProductTree() { |
| | | MesProRouteProductSaveReqVO request = request(null, 815L); |
| | | when(itemService.validateItemExistsAndEnable(815L)) |
| | | .thenReturn(new MesMdItemDO().setId(815L).setName("åæ").setItemTypeId(23L)); |
| | | when(itemTypeService.getItemType(23L)).thenReturn(new MesMdItemTypeDO() |
| | | .setId(23L).setCode("RAW").setItemOrProduct("ITEM").setParentId(0L)); |
| | | |
| | | assertThrows(ServiceException.class, () -> service.createRouteProduct(request)); |
| | | verify(routeProductMapper, never()).insert(any(MesProRouteProductDO.class)); |
| | | } |
| | | |
| | | @Test |
| | | void createAcceptsNamedSemiFinishedInLegacyItemCategory() { |
| | | MesProRouteProductSaveReqVO request = request(null, 815L); |
| | | MesMdItemDO item = new MesMdItemDO().setId(815L).setCode("74000432") |
| | | .setName("é»ææ¸æåæå").setItemTypeId(23L); |
| | | when(itemService.validateItemExistsAndEnable(815L)).thenReturn(item); |
| | | when(itemTypeService.getItemType(23L)).thenReturn(new MesMdItemTypeDO() |
| | | .setId(23L).setCode("FOOD").setName("é»ææ¸æåæå") |
| | | .setItemOrProduct("ITEM").setParentId(0L)); |
| | | |
| | | assertDoesNotThrow(() -> service.createRouteProduct(request)); |
| | | verifyNoInteractions(mdmItemApi); |
| | | verify(routeProductMapper).insert(any(MesProRouteProductDO.class)); |
| | | } |
| | | |
| | | private static MesProRouteProductSaveReqVO request(Long id, Long itemId) { |
| | | MesProRouteProductSaveReqVO request = new MesProRouteProductSaveReqVO(); |
| | | request.setId(id); |
| | | request.setRouteId(44L); |
| | | request.setItemId(itemId); |
| | | request.setWarehouseId(24L); |
| | | request.setLocationId(9L); |
| | | request.setAreaId(9L); |
| | | return request; |
| | | } |
| | | } |