feat(mes): 增加盘号追溯功能和销售退货单优化
- 在ERP销售订单中新增盘号字段用于产品追溯
- 优化MES模块中的盘号管理,包括生产计划和工单的盘号快照
- 完善销售退货流程中的盘号匹配验证逻辑
- 增加退货批次与销售订单明细的盘号一致性校验
- 修复IPQC质检项值显示逻辑,空值显示为"未填写"
- 优化批次查询逻辑,精确匹配盘号避免大小写差异
- 新增多个退货相关错误码处理异常情况
- 完善退货单据中客户和创建者信息展示
| | |
| | | |
| | | å¾
åï¼å·¥èµè®¡ç®å
¬å¼å°æªä¸å®¢æ·ç¡®è®¤ï¼æ¬æ¬¡ä¸å®æ½å·¥èµè®¡ç®ï¼å¾
确认工åºã设å¤ãå·¥æ¶å®é¢åè¾
å©å·¥æ¶ç计ç®å£å¾ååæ¹é ã |
| | | |
| | | æ¥è¯¢æéæ¸
çå¾
åï¼å端已移é¤ä¸é¨å详æ
å
¥å£ãéå®è®¢åæ¥çåå·¥ä½å°æ¥å·¥è®°å½å¯¹æ¥è¯¢æé®æéçä¾èµï¼é¦é¡µç»è¥å¡çæé¡µé¢èåæ§å¶ãå
¶ä»æ¥è¯¢æéä»è¢«åçã设置ãé¦é¡µæ±æ»å跨页é¢å
¥å£å¼ç¨ï¼å¨é项宿æéæ¿ä»£å¹¶éªè¯ä¹åï¼ä¸è¦æ§è¡ `sql/mysql/remove_query_button_permissions.sql`ã |
| | | |
| | | çå·è¿½æº¯ï¼æ°ç产产åºå³ä½¿ç©ææªå¯ç¨æ®éæ¹æ¬¡ç®¡çï¼ä¹ä¼ä¸ºé空çå·å建å
é¨è¿½æº¯æ¹æ¬¡ï¼åºåä»ææ¹æ¬¡åå¼ï¼åºå页é¢åæ£è´§éæ©å¨æ¾ç¤ºçå·ï¼é宿£è´§ååºåºæ¶æ ¸å¯¹é宿ç»çå·ãå岿 çå·åºåä¿ç空å¼ï¼ä¸èªå¨æ¨æµææåï¼éå®æç»æçå·æ¶ï¼è¿ç±»æ§åºåä¸è½ç´æ¥ä½ä¸ºå¹é
åºååºåºï¼éå
æ ¸å®å¹¶å¤çå岿°æ®ãå
é¨è¿½æº¯æ¹æ¬¡åç°æåºä½æ¹æ¬¡æ··æ¾è§å约æï¼æ£å¼ä½¿ç¨åééªè¯ç°åºåºä½é
ç½®ã |
| | | |
| | | çå·å¾
æ ¸å®ï¼æ°å»ºæç¼è¾å
³èéå®è®¢åçåè´§éç¥åè¡æ¶ï¼é¡»æç¡®éæ©é宿ç»å¹¶æ ¡éªè®¢åä¸ç©æï¼å岿 å
³èè¡ä¸ä¼èªå¨æ¨æï¼å·²çæç䏿¸¸åå²åæ®ä»ééç¬æ ¸å®ã带çå·çéè´§æ¹æ¬¡é¡»å¨è¯¥éå®è®¢å䏿æç¡®å
³èé宿ç»ä¸å·²å®æçå®é
åºåºè®°å½ï¼å¹¶å¨ä¸æ¶æ¶ä¿çæ¹æ¬¡ï¼å°æªè®°å½å
·ä½åºåºæç»æç´¯è®¡å¯éæ°éï¼å®é
éè´§æ°é忥æºä»éæ ¸å®ãè°æ¨ä¸åºåè°æ´å·²ä¿çè¿½æº¯æ¹æ¬¡ï¼ä»éæ°æ®åºå页é¢ç«¯å°ç«¯éªè¯ï¼ä¸å¾æç©ææè¡é¡ºåºæ¨æµåå²å
³èã |
| | | |
| | | | ä½ç½® | å½åç¶æ | æ¹é 注æäºé¡¹ | |
| | | | --- | --- | --- | |
| | | | `MesProMpsController.mergeAndIssueMps` å `MesProMpsServiceImpl.mergeAndIssueMps` | å·²åç¨ï¼æ§å
¥å£è¿ååç¨æç¤º | æ°æ¥å£éæ¡æ¹éä¸åï¼åå²åå¹¶å·¥ååæç»ä»éä¿çæ¥è¯¢ååæ¶å
¼å®¹ã | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- æµè¯åºé¡µé¢èè°ï¼è¡¥å
ç©æææ¯åæ°åæ®µã |
| | | -- å
éæ©ç®æ æ°æ®åºï¼å·²å卿¶è·³è¿ï¼ä¸è¦çæ¢ææ°æ®ã |
| | | -- MySQL ç ALTER TABLE ä¼éå¼æäº¤ï¼æ§è¡åè¯·ç¡®è®¤æ°æ®åºåå¤ä»½ã |
| | | SET @mdm_ddl = IF( |
| | | EXISTS (SELECT 1 FROM information_schema.COLUMNS |
| | | WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'mdm_item' |
| | | AND COLUMN_NAME = 'technical_parameters'), |
| | | 'SELECT ''mdm_item.technical_parameters å·²åå¨ï¼è·³è¿'' AS message', |
| | | 'ALTER TABLE `mdm_item` ADD COLUMN `technical_parameters` TEXT NULL COMMENT ''ææ¯åæ°''' |
| | | ); |
| | | PREPARE mdm_stmt FROM @mdm_ddl; |
| | | EXECUTE mdm_stmt; |
| | | DEALLOCATE PREPARE mdm_stmt; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- åªè¯»ææ¥ï¼å
³èéå®è®¢åä½ç¼ºå°é宿ç»å
³èçåå²åæ®ï¼ä¸è½æç©æèªå¨çæµçå·ã |
| | | -- 请å¨ç®æ æ°æ®åºæ§è¡å¹¶éæ¡æ ¸å¯¹ï¼æ¬èæ¬ä¸ä¿®æ¹æ°æ®ã |
| | | SELECT n.id AS notice_id, n.code AS notice_code, n.sale_order_id, |
| | | l.id AS notice_line_id, l.item_id, l.quantity, l.sale_order_item_id |
| | | FROM mes_wm_sales_notice n |
| | | JOIN mes_wm_sales_notice_line l ON l.notice_id = n.id |
| | | WHERE n.deleted = b'0' AND l.deleted = b'0' |
| | | AND n.sale_order_id IS NOT NULL AND l.sale_order_item_id IS NULL |
| | | ORDER BY n.id, l.id; |
| | | |
| | | SELECT s.id AS sales_id, s.code AS sales_code, s.sale_order_id, |
| | | l.id AS sales_line_id, l.notice_line_id, l.item_id, l.quantity, l.sale_order_item_id |
| | | FROM mes_wm_product_sales s |
| | | JOIN mes_wm_product_sales_line l ON l.sales_id = s.id |
| | | WHERE s.deleted = b'0' AND l.deleted = b'0' |
| | | AND s.sale_order_id IS NOT NULL AND l.sale_order_item_id IS NULL |
| | | ORDER BY s.id, l.id; |
| | |
| | | // 4. ç»è£
ååº |
| | | ErpSaleOrderRespVO saleOrderVO = BeanUtils.toBean(saleOrder, ErpSaleOrderRespVO.class); |
| | | saleOrderVO.setItems(itemVOList); |
| | | saleOrderVO.setNeedProduction(itemVOList.stream() |
| | | .anyMatch(item -> Integer.valueOf(1).equals(item.getNeedProduction())) ? 1 : 0); |
| | | if (saleOrder.getCustomerId() != null) { |
| | | MapUtils.findAndThen(customerApi.getCustomerMap(List.of(saleOrder.getCustomerId())), |
| | | saleOrder.getCustomerId(), customer -> saleOrderVO.setCustomerName(customer.getName())); |
| | | } |
| | | if (saleOrder.getCreator() != null && saleOrder.getCreator().matches("\\d+")) { |
| | | AdminUserRespDTO creator = adminUserApi.getUser(Long.valueOf(saleOrder.getCreator())); |
| | | if (creator != null) { |
| | | saleOrderVO.setCreatorName(creator.getNickname()); |
| | | } |
| | | } |
| | | saleOrderVO.setAttachmentList(storageAttachmentApi.listAttachments("erp_sale_order", id)); |
| | | return success(saleOrderVO); |
| | | } |
| | |
| | | @Schema(description = "产ååç§°ï¼ä»
ä¾å¯¹ç
§ï¼å¹é
ä»ä»¥äº§åç¼ç 为åï¼") |
| | | private String productName; |
| | | |
| | | @ExcelProperty("çå·") |
| | | @Schema(description = "产åæç»çå·ï¼æå·¥å¡«åï¼æå¤128个å符") |
| | | private String reelNo; |
| | | |
| | | @ExcelProperty("æ°é") |
| | | @Schema(description = "äº§åæ°é", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private BigDecimal count; |
| | |
| | | } |
| | | ErpSaleOrderSaveReqVO.Item itemVO = new ErpSaleOrderSaveReqVO.Item(); |
| | | itemVO.setProductId(item.getId()); |
| | | if (row.getReelNo() != null && row.getReelNo().length() > 128) { |
| | | throw new IllegalArgumentException("产å[" + row.getProductCode() + "]ççå·ä¸è½è¶
è¿128个å符"); |
| | | } |
| | | itemVO.setReelNo(row.getReelNo()); |
| | | itemVO.setProductUnitId(item.getUnitMeasureId()); |
| | | // åä»·ï¼æªå¡«åæ¶åç©æçéå®ä»·ï¼ä¸é¡µé¢é产ååçé»è®¤å¼ä¸è´ï¼ |
| | | itemVO.setProductPrice(row.getProductPrice() != null ? row.getProductPrice() |
| | |
| | | String value = detail.getValue(); |
| | | String remark = detail.getRemark(); |
| | | // æ ¼å¼å为 "ææ åç§°:å¼" æ "ææ åç§°:å¼(夿³¨)" |
| | | String indicatorStr = indicatorName + ":" + value; |
| | | String indicatorStr = indicatorName + ":" + (StrUtil.isBlank(value) ? "æªå¡«å" : value); |
| | | if (StrUtil.isNotBlank(remark)) { |
| | | indicatorStr += "(" + remark + ")"; |
| | | } |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.qc.rqc; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | |
| | | @RequestMapping("/mes/qc/rqc") |
| | | @Validated |
| | | public class MesQcRqcController { |
| | | |
| | | @Resource |
| | | private MesWmBatchService batchService; |
| | | |
| | | @Resource |
| | | private MesQcRqcService rqcService; |
| | |
| | | convertSet(list, MesQcRqcDO::getInspectorUserId)); |
| | | // æ¹éæ¥è¯¢æ£éªé¡¹ææ |
| | | Map<Long, String> indicatorSummaryMap = buildIndicatorSummaryMap(list); |
| | | Map<String, MesWmBatchDO> batchMap = new HashMap<>(); |
| | | for (String code : convertSet(list, MesQcRqcDO::getBatchCode)) { |
| | | if (code != null && !code.isBlank()) batchMap.put(code, batchService.getBatchByCode(code)); |
| | | } |
| | | // æ¼è£
VO |
| | | return BeanUtils.toBean(list, MesQcRqcRespVO.class, vo -> { |
| | | findAndThen(batchMap, vo.getBatchCode(), batch -> vo.setReelNo(batch.getReelNo())); |
| | | findAndThen(itemMap, vo.getItemId(), item -> { |
| | | vo.setItemCode(item.getCode()).setItemName(item.getName()).setItemSpecification(item.getSpecification()); |
| | | findAndThen(unitMeasureMap, item.getUnitMeasureId(), |
| | |
| | | @Schema(description = "æ¥æºåæ®è¡ ID", example = "300") |
| | | private Long sourceLineId; |
| | | |
| | | @Schema(description = "éè´§æ¹æ¬¡çå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "æ¥æºåæ®ç¼ç ", example = "RT20250101001") |
| | | @ExcelProperty("æ¥æºåæ®ç¼ç ") |
| | | private String sourceDocCode; |
| | |
| | | @Schema(description = "æ¹æ¬¡ç¼ç ", requiredMode = Schema.RequiredMode.REQUIRED, example = "BATCH20250314001") |
| | | private String code; |
| | | |
| | | @Schema(description = "ç产追溯çå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "ç©æ ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long itemId; |
| | | |
| | |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | 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; |
| | |
| | | private MesWmWarehouseLocationService locationService; |
| | | @Resource |
| | | private MesWmWarehouseAreaService areaService; |
| | | @Resource |
| | | private MesWmBatchService batchService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建éå®åºåºæç»") |
| | |
| | | convertSet(list, MesWmProductSalesDetailDO::getLocationId)); |
| | | Map<Long, MesWmWarehouseAreaDO> areaMap = areaService.getWarehouseAreaMap( |
| | | convertSet(list, MesWmProductSalesDetailDO::getAreaId)); |
| | | Map<Long, MesWmBatchDO> batchMap = batchService.getBatchMap( |
| | | convertSet(list, MesWmProductSalesDetailDO::getBatchId)); |
| | | // 2. æå»ºç»æ |
| | | return BeanUtils.toBean(list, MesWmProductSalesDetailRespVO.class, vo -> { |
| | | MapUtils.findAndThen(batchMap, vo.getBatchId(), batch -> vo.setReelNo(batch.getReelNo())); |
| | | MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { |
| | | vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification()); |
| | | MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(), |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.wm.productsales; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderItemRespDTO; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.stream.Collectors; |
| | |
| | | private MdmItemApi mdmItemApi; |
| | | @Resource |
| | | private MdmUnitMeasureApi mdmUnitMeasureApi; |
| | | @Resource |
| | | private ErpSaleOrderApi saleOrderApi; |
| | | |
| | | |
| | | @PostMapping("/create") |
| | |
| | | Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureList( |
| | | convertSet(itemMap.values(), MdmItemRespDTO::getUnitMeasureId)).getCheckedData() |
| | | .stream().collect(Collectors.toMap(MdmUnitMeasureRespDTO::getId, e -> e)); |
| | | Map<Long, ErpSaleOrderItemRespDTO> saleItems = new HashMap<>(); |
| | | for (Long itemId : convertSet(list, MesWmProductSalesLineDO::getSaleOrderItemId)) { |
| | | if (itemId != null) saleItems.put(itemId, saleOrderApi.getSaleOrderItem(itemId).getCheckedData()); |
| | | } |
| | | // 2. æå»ºç»æ |
| | | return BeanUtils.toBean(list, MesWmProductSalesLineRespVO.class, vo -> { |
| | | MapUtils.findAndThen(saleItems, vo.getSaleOrderItemId(), item -> vo.setReelNo(item.getReelNo())); |
| | | MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { |
| | | vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification()); |
| | | MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(), |
| | |
| | | @Schema(description = "æ¹æ¬¡å·", example = "BATCH001") |
| | | private String batchCode; |
| | | |
| | | @Schema(description = "å®é
æ£è´§åºåçå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "ä»åºID", example = "1") |
| | | private Long warehouseId; |
| | | |
| | |
| | | @Schema(description = "åè´§éç¥åè¡ID", example = "1") |
| | | private Long noticeLineId; |
| | | |
| | | @Schema(description = "éå®è®¢åæç»ID") |
| | | private Long saleOrderItemId; |
| | | |
| | | @Schema(description = "é宿ç»çå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "ç©æID", example = "1") |
| | | private Long itemId; |
| | | |
| | |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesDetailDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | 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; |
| | |
| | | private MesWmWarehouseLocationService locationService; |
| | | @Resource |
| | | private MesWmWarehouseAreaService areaService; |
| | | @Resource |
| | | private MesWmBatchService batchService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建éå®éè´§æç»") |
| | |
| | | convertSet(list, MesWmReturnSalesDetailDO::getLocationId)); |
| | | Map<Long, MesWmWarehouseAreaDO> areaMap = areaService.getWarehouseAreaMap( |
| | | convertSet(list, MesWmReturnSalesDetailDO::getAreaId)); |
| | | Map<Long, MesWmBatchDO> batchMap = batchService.getBatchMap( |
| | | convertSet(list, MesWmReturnSalesDetailDO::getBatchId)); |
| | | // 2. æå»ºç»æ |
| | | return BeanUtils.toBean(list, MesWmReturnSalesDetailRespVO.class, vo -> { |
| | | MapUtils.findAndThen(batchMap, vo.getBatchId(), batch -> vo.setReelNo(batch.getReelNo())); |
| | | MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { |
| | | vo.setItemCode(item.getCode()).setItemName(item.getName()).setItemSpecification(item.getSpecification()); |
| | | MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(), |
| | |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi; |
| | | import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO; |
| | | |
| | |
| | | private MdmItemApi mdmItemApi; |
| | | @Resource |
| | | private MdmUnitMeasureApi mdmUnitMeasureApi; |
| | | @Resource |
| | | private MesWmBatchService batchService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建éå®éè´§åè¡") |
| | |
| | | Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureList( |
| | | convertSet(itemMap.values(), MdmItemRespDTO::getUnitMeasureId)).getCheckedData() |
| | | .stream().collect(Collectors.toMap(MdmUnitMeasureRespDTO::getId, e -> e)); |
| | | Map<Long, MesWmBatchDO> batchMap = batchService.getBatchMap( |
| | | convertSet(list, MesWmReturnSalesLineDO::getBatchId)); |
| | | // 2. æå»ºç»æ |
| | | return BeanUtils.toBean(list, MesWmReturnSalesLineRespVO.class, vo -> |
| | | return BeanUtils.toBean(list, MesWmReturnSalesLineRespVO.class, vo -> { |
| | | MapUtils.findAndThen(batchMap, vo.getBatchId(), batch -> vo.setReelNo(batch.getReelNo())); |
| | | MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { |
| | | vo.setItemCode(item.getCode()).setItemName(item.getName()).setItemSpecification(item.getSpecification()); |
| | | MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(), |
| | | unitMeasure -> vo.setItemUnit(unitMeasure.getName())); |
| | | })); |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | } |
| | |
| | | @Schema(description = "æ¹æ¬¡å·", example = "B001") |
| | | private String batchCode; |
| | | |
| | | @Schema(description = "䏿¶æ¹æ¬¡çå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "ä»åºID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long warehouseId; |
| | | |
| | |
| | | @Schema(description = "æ¹æ¬¡å·", example = "B001") |
| | | private String batchCode; |
| | | |
| | | @Schema(description = "éè´§æ¹æ¬¡çå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "è´¨éç¶æ", example = "åæ ¼") |
| | | private Integer qualityStatus; |
| | | |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.wm.salesnotice; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderItemRespDTO; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.stream.Collectors; |
| | |
| | | private MdmItemApi mdmItemApi; |
| | | @Resource |
| | | private MdmUnitMeasureApi mdmUnitMeasureApi; |
| | | @Resource |
| | | private ErpSaleOrderApi saleOrderApi; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建åè´§éç¥åè¡") |
| | |
| | | Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureList( |
| | | convertSet(itemMap.values(), MdmItemRespDTO::getUnitMeasureId)).getCheckedData() |
| | | .stream().collect(java.util.stream.Collectors.toMap(MdmUnitMeasureRespDTO::getId, e -> e)); |
| | | Map<Long, ErpSaleOrderItemRespDTO> saleItems = new HashMap<>(); |
| | | for (Long itemId : convertSet(list, MesWmSalesNoticeLineDO::getSaleOrderItemId)) { |
| | | if (itemId != null) saleItems.put(itemId, saleOrderApi.getSaleOrderItem(itemId).getCheckedData()); |
| | | } |
| | | // 2. æå»ºç»æ |
| | | return BeanUtils.toBean(list, MesWmSalesNoticeLineRespVO.class, vo -> { |
| | | MapUtils.findAndThen(saleItems, vo.getSaleOrderItemId(), item -> vo.setReelNo(item.getReelNo())); |
| | | MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { |
| | | vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification()); |
| | | MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(), |
| | |
| | | @Schema(description = "éå®è®¢åæç»ID", example = "1") |
| | | private Long saleOrderItemId; |
| | | |
| | | @Schema(description = "é宿ç»çå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "ç©æç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long itemId; |
| | | |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.wm.transfer; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | |
| | | @RequestMapping("/mes/wm/transfer-line") |
| | | @Validated |
| | | public class MesWmTransferLineController { |
| | | |
| | | @Resource |
| | | private MesWmBatchService batchService; |
| | | |
| | | @Resource |
| | | private MesWmTransferLineService transferLineService; |
| | |
| | | convertSet(list, MesWmTransferLineDO::getFromLocationId)); |
| | | Map<Long, MesWmWarehouseAreaDO> areaMap = areaService.getWarehouseAreaMap( |
| | | convertSet(list, MesWmTransferLineDO::getFromAreaId)); |
| | | Map<Long, MesWmBatchDO> batchMap = batchService.getBatchMap(convertSet(list, MesWmTransferLineDO::getBatchId)); |
| | | // æ¼æ¥æ°æ® |
| | | return BeanUtils.toBean(list, MesWmTransferLineRespVO.class, vo -> { |
| | | MapUtils.findAndThen(batchMap, vo.getBatchId(), batch -> vo.setReelNo(batch.getReelNo())); |
| | | MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { |
| | | vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification()); |
| | | MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(), |
| | |
| | | @Schema(description = "æ¹æ¬¡å·") |
| | | private String batchCode; |
| | | |
| | | @Schema(description = "è°æ¨æ¹æ¬¡çå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "ç§»åºä»åºç¼å·", example = "1") |
| | | private Long fromWarehouseId; |
| | | @Schema(description = "ç§»åºä»åºåç§°") |
| | |
| | | |
| | | // è¿å ID æå°çæ¹æ¬¡ |
| | | query.orderByAsc(MesWmBatchDO::getId); |
| | | if (batch.getReelNo() != null) { |
| | | // æ°æ®åºæåºè§åå¯è½å¿½ç¥å¤§å°åææ«å°¾ç©ºæ ¼ï¼çå·å¿
é¡»æè¾å
¥å¼ç²¾ç¡®å¹é
ã |
| | | return selectList(query).stream() |
| | | .filter(candidate -> batch.getReelNo().equals(candidate.getReelNo())) |
| | | .findFirst().orElse(null); |
| | | } |
| | | query.last("LIMIT 1"); |
| | | return selectOne(query); |
| | | } |
| | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.apache.ibatis.annotations.Select; |
| | | |
| | | import java.util.List; |
| | | |
| | |
| | | @Mapper |
| | | public interface MesWmProductSalesDetailMapper extends BaseMapperX<MesWmProductSalesDetailDO> { |
| | | |
| | | /** åªè®¤å¯è¯¥è®¢å已宿åºåºãä¸æç¡®å
³èé宿ç»çå®é
æ£è´§æ¹æ¬¡ã */ |
| | | @Select("SELECT COUNT(*) FROM mes_wm_product_sales_detail d " |
| | | + "JOIN mes_wm_product_sales s ON s.id = d.sales_id AND s.deleted = 0 " |
| | | + "JOIN mes_wm_product_sales_line l ON l.id = d.line_id AND l.sales_id = s.id AND l.deleted = 0 " |
| | | + "JOIN erp_sale_order_items i ON i.id = l.sale_order_item_id AND i.order_id = s.sale_order_id AND i.deleted = 0 " |
| | | + "JOIN mes_wm_batch b ON b.id = d.batch_id AND b.deleted = 0 AND b.item_id = d.item_id " |
| | | + "WHERE d.deleted = 0 AND s.sale_order_id = #{orderId} AND s.status = #{finishedStatus} " |
| | | + "AND d.item_id = #{itemId} AND l.item_id = #{itemId} AND i.product_id = #{itemId} " |
| | | + "AND BINARY i.reel_no = BINARY b.reel_no " |
| | | + "AND d.batch_id = #{batchId}") |
| | | Long countFinishedSaleBatch(@Param("orderId") Long orderId, @Param("itemId") Long itemId, |
| | | @Param("batchId") Long batchId, @Param("finishedStatus") Integer finishedStatus); |
| | | |
| | | default List<MesWmProductSalesDetailDO> selectListByLineId(Long lineId) { |
| | | return selectList(MesWmProductSalesDetailDO::getLineId, lineId); |
| | | } |
| | |
| | | ErrorCode WM_RETURN_SALES_LINE_NOT_EXISTS = new ErrorCode(1_040_713_100, "éå®éè´§åè¡ä¸åå¨"); |
| | | ErrorCode WM_RETURN_SALES_DETAIL_NOT_EXISTS = new ErrorCode(1_040_713_200, "éå®éè´§åæç»ä¸åå¨"); |
| | | ErrorCode WM_RETURN_SALES_DETAIL_QUANTITY_EXCEED = new ErrorCode(1_040_713_201, "䏿¶æç»æ»æ°éä¸è½è¶
è¿éè´§è¡æ°é"); |
| | | ErrorCode WM_RETURN_SALES_DETAIL_SOURCE_MISMATCH = new ErrorCode(1_040_713_202, "éè´§æç»çåæ®æç©æä¸éè´§è¡ä¸ä¸è´"); |
| | | ErrorCode WM_RETURN_SALES_DETAIL_BATCH_MISMATCH = new ErrorCode(1_040_713_203, "éè´§æç»æ¹æ¬¡ä¸éè´§è¡æ¹æ¬¡ä¸ä¸è´"); |
| | | ErrorCode WM_RETURN_SALES_REEL_MISMATCH = new ErrorCode(1_040_713_204, "è¯·éæ©çå·å±äºè¯¥éå®è®¢å产åæç»çæ¹æ¬¡"); |
| | | ErrorCode WM_RETURN_SALES_BATCH_NOT_SHIPPED = new ErrorCode(1_040_713_205, "æéçå·æ¹æ¬¡æªå¨è¯¥éå®è®¢åä¸å®æåºåºï¼ä¸è½ä½ä¸ºéè´§æ¥æº"); |
| | | |
| | | // ========== MES ä»åºç®¡ç-çç¹æ¹æ¡/ä»»å¡/ç»æï¼1-040-714-100ï¼ ========== |
| | | ErrorCode WM_STOCK_TAKING_PLAN_NOT_EXISTS = new ErrorCode(1_040_714_100, "çç¹æ¹æ¡ä¸åå¨"); |
| | |
| | | ErrorCode WM_SALES_NOTICE_LINE_NOT_EXISTS = new ErrorCode(1_040_720_010, "åè´§éç¥åè¡ä¸åå¨"); |
| | | ErrorCode WM_SALES_NOTICE_LINE_EMPTY = new ErrorCode(1_040_720_011, "åè´§éç¥åè¡ä¸ºç©ºï¼ä¸è½æäº¤"); |
| | | ErrorCode WM_SALES_NOTICE_LINE_NOT_MATCH = new ErrorCode(1_040_720_012, "åè´§éç¥åè¡ä¸å±äºæå®çåè´§éç¥å"); |
| | | ErrorCode WM_SALES_NOTICE_LINE_SALE_ITEM_REQUIRED = new ErrorCode(1_040_720_013, "å
³èéå®è®¢åçåè´§è¡å¿
须鿩éå®è®¢åæç»"); |
| | | ErrorCode WM_SALES_NOTICE_LINE_SALE_ITEM_MISMATCH = new ErrorCode(1_040_720_014, "éå®è®¢åæç»ä¸åè´§éç¥åæç©æä¸å¹é
"); |
| | | ErrorCode WM_SALES_NOTICE_SALE_ORDER_NOT_EXISTS = new ErrorCode(1_040_720_013, "éå®è®¢åä¸åå¨"); |
| | | ErrorCode WM_SALES_NOTICE_SALE_ORDER_ITEM_EMPTY = new ErrorCode(1_040_720_014, "éå®è®¢åæç»ä¸ºç©º"); |
| | | ErrorCode WM_SALES_NOTICE_ALREADY_EXISTS = new ErrorCode(1_040_720_015, "该éå®è®¢åå·²çæåè´§éç¥åï¼ä¸è½éå¤çæ"); |
| | |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import cn.iocoder.yudao.module.system.api.permission.PermissionApi; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsSaveReqVO; |
| | |
| | | @Resource |
| | | private MesProMpsMapper mpsMapper; |
| | | @Resource |
| | | @Lazy |
| | | private ErpSaleOrderApi saleOrderApi; |
| | | @Resource |
| | | private PermissionApi permissionApi; |
| | |
| | | |
| | | // æ´æ° |
| | | MesProMpsDO updateObj = BeanUtils.toBean(updateReqVO, MesProMpsDO.class); |
| | | // çå·æ¯åå»ºè®¡åæ¶çæ¥æºå¿«ç
§ï¼ç¼è¾å
¶ä»å段ä¸è½æ¹åå®ã |
| | | updateObj.setReelNo(null); |
| | | mpsMapper.updateById(updateObj); |
| | | } |
| | | |
| | |
| | | |
| | | // 3. æ´æ° |
| | | MesProWorkOrderDO updateObj = BeanUtils.toBean(updateReqVO, MesProWorkOrderDO.class); |
| | | // å·¥åçå·æ²¿ç¨å建æ¶çæ¥æºå¿«ç
§ï¼é¿å
ä¸è®¡ååå·²çæç䏿¸¸è®°å½ä¸ä¸è´ã |
| | | updateObj.setReelNo(null); |
| | | workOrderMapper.updateById(updateObj); |
| | | } |
| | | |
| | |
| | | .setTemplateId(templateId) |
| | | .setSourceDocCode(sourceDocCode) |
| | | .setStatus(MesQcStatusEnum.DRAFT.getStatus()); |
| | | if (Objects.equals(createReqVO.getSourceDocType(), MesBizTypeConstants.WM_RETURN_SALES) |
| | | && createReqVO.getSourceLineId() != null) { |
| | | var line = returnSalesLineService.validateReturnSalesLineExists(createReqVO.getSourceLineId()); |
| | | if (!Objects.equals(line.getReturnId(), createReqVO.getSourceDocId()) |
| | | || !Objects.equals(line.getItemId(), createReqVO.getItemId())) { |
| | | throw exception(WM_RETURN_SALES_DETAIL_SOURCE_MISMATCH); |
| | | } |
| | | rqc.setBatchCode(line.getBatchCode()); |
| | | } |
| | | rqcMapper.insert(rqc); |
| | | |
| | | // 3. 仿¨¡æ¿ææ èªå¨çææ£éªè¡ |
| | |
| | | @Override |
| | | public void updateRqc(MesQcRqcSaveReqVO updateReqVO) { |
| | | // 1.1 æ ¡éªåå¨ + èç¨¿ç¶æ |
| | | validateRqcStatusPrepare(updateReqVO.getId()); |
| | | MesQcRqcDO existingRqc = validateRqcStatusPrepare(updateReqVO.getId()); |
| | | // 1.2 æ ¡éªæ°æ® |
| | | validateRqcSaveData(updateReqVO); |
| | | |
| | |
| | | updateObj.setSourceDocType(null).setSourceDocId(null).setSourceLineId(null); // ä¸å
è®¸ä¿®æ¹æ¥æºåæ® |
| | | updateObj.setTemplateId(null); // ä¸å
è®¸ä¿®æ¹æ¨¡æ¿ |
| | | updateObj.setItemId(null); // ä¸å
许修æ¹ç©æ |
| | | if (Objects.equals(existingRqc.getSourceDocType(), MesBizTypeConstants.WM_RETURN_SALES) |
| | | && existingRqc.getSourceLineId() != null) updateObj.setBatchCode(null); |
| | | rqcMapper.updateById(updateObj); |
| | | } |
| | | |
| | |
| | | } else { |
| | | // åºåºæ¶æ ¡éªæ¹æ¬¡åå¨ |
| | | if (batchId != null) { |
| | | batchService.validateBatchExists(batchId, reqVO.getItemId()); |
| | | batchCode = batchService.validateBatchExists(batchId, reqVO.getItemId()).getCode(); |
| | | } |
| | | } |
| | | |
| | |
| | | * @return æ¹æ¬¡è®°å½ï¼ç©ææªå¯ç¨æ¹æ¬¡ç®¡çæ¶è¿å nullï¼ |
| | | */ |
| | | private MesWmBatchDO resolveBatch(MesWmMaterialStockAdjustReqVO reqVO, MesMdmItemDO item) { |
| | | // 1. ç©ææªå¯ç¨æ¹æ¬¡ç®¡çï¼è¿å null |
| | | if (Boolean.FALSE.equals(item.getIsBatchManaged())) { |
| | | return null; |
| | | } |
| | | |
| | | // 2. ä¼ å
¥ batchIdï¼æ ¡éªå¹¶è¿åå·²ææ¹æ¬¡ |
| | | // å³ä½¿ç©ææªå¯ç¨æ®éæ¹æ¬¡ç®¡çï¼ä¹è¦ä¿çå¹¶æ ¡éªçå·è¿½æº¯æ¹æ¬¡ã |
| | | if (reqVO.getBatchId() != null) { |
| | | return batchService.validateBatchExists(reqVO.getBatchId(), reqVO.getItemId()); |
| | | } |
| | | |
| | | // 1. æªæå®è¿½æº¯æ¹æ¬¡ä¸ç©ææªå¯ç¨æ¹æ¬¡ç®¡çï¼è¿å null |
| | | if (Boolean.FALSE.equals(item.getIsBatchManaged())) { |
| | | return null; |
| | | } |
| | | |
| | | // 3. æªä¼ batchIdï¼èªå¨åå»ºæ¹æ¬¡ |
| | | MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO() |
| | | .setItemId(reqVO.getItemId()) |
| | |
| | | @Override |
| | | public Long createProductSalesLine(MesWmProductSalesLineSaveReqVO createReqVO) { |
| | | // æ ¡éªæ°æ® |
| | | validateLineSaveData(createReqVO); |
| | | MesWmSalesNoticeLineDO noticeLine = validateLineSaveData(createReqVO); |
| | | |
| | | // æ°å¢ |
| | | MesWmProductSalesLineDO line = BeanUtils.toBean(createReqVO, MesWmProductSalesLineDO.class); |
| | | if (noticeLine != null) { |
| | | line.setSaleOrderItemId(noticeLine.getSaleOrderItemId()); |
| | | } |
| | | productSalesLineMapper.insert(line); |
| | | return line.getId(); |
| | | } |
| | |
| | | MesWmProductSalesLineDO oldLine = validateProductSalesLineExists(updateReqVO.getId()); |
| | | // æ ¡éªæ°æ® |
| | | updateReqVO.setSalesId(oldLine.getSalesId()); |
| | | validateLineSaveData(updateReqVO); |
| | | MesWmSalesNoticeLineDO noticeLine = validateLineSaveData(updateReqVO); |
| | | |
| | | // æ´æ° |
| | | MesWmProductSalesLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductSalesLineDO.class); |
| | | if (noticeLine != null) { |
| | | updateObj.setSaleOrderItemId(noticeLine.getSaleOrderItemId()); |
| | | } |
| | | productSalesLineMapper.updateById(updateObj); |
| | | |
| | | // æ£æ¥æ¯å¦éè¦å建 OQC æ£éªåï¼ä»ä¸éè¦æ£éªå为éè¦æ£éª |
| | |
| | | MesWmProductSalesLineDO line = new MesWmProductSalesLineDO(); |
| | | line.setId(updateReqVO.getId()); |
| | | line.setItemId(updateReqVO.getItemId()); |
| | | line.setSaleOrderItemId(updateObj.getSaleOrderItemId() != null |
| | | ? updateObj.getSaleOrderItemId() : oldLine.getSaleOrderItemId()); |
| | | line.setBatchCode(updateReqVO.getBatchCode()); |
| | | line.setQuantity(updateReqVO.getQuantity()); |
| | | |
| | |
| | | /** |
| | | * æ ¡éªä¿åæ¶çå
³èæ°æ® |
| | | */ |
| | | private void validateLineSaveData(MesWmProductSalesLineSaveReqVO reqVO) { |
| | | private MesWmSalesNoticeLineDO validateLineSaveData(MesWmProductSalesLineSaveReqVO reqVO) { |
| | | // æ ¡éªä¸»ååå¨ä¸ä¸ºèç¨¿ç¶æ |
| | | MesWmProductSalesDO sales = productSalesService.validateProductSalesExistsAndDraft(reqVO.getSalesId()); |
| | | // æ ¡éªç©æåå¨ |
| | | mdmItemService.validateItemExistsAndEnable(reqVO.getItemId()); |
| | | // æ ¡éªå
³èåè´§éç¥åè¡ï¼å卿§ + å½å± + åæ®µä¸è´æ§ï¼ |
| | | validateSalesNoticeLine(sales, reqVO); |
| | | MesWmSalesNoticeLineDO noticeLine = validateSalesNoticeLine(sales, reqVO); |
| | | // æ ¹æ® batchCode è§£æ batchId |
| | | fillBatchId(reqVO); |
| | | return noticeLine; |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param sales åºåºå |
| | | * @param reqVO åºåºè¡è¯·æ± |
| | | */ |
| | | private void validateSalesNoticeLine(MesWmProductSalesDO sales, MesWmProductSalesLineSaveReqVO reqVO) { |
| | | private MesWmSalesNoticeLineDO validateSalesNoticeLine(MesWmProductSalesDO sales, MesWmProductSalesLineSaveReqVO reqVO) { |
| | | Long noticeLineId = reqVO.getNoticeLineId(); |
| | | // æ
åµä¸ï¼å¦æåºåºåå
³èäºåè´§éç¥åï¼åå¿
é¡»å
³èåè´§éç¥åè¡ |
| | | if (sales.getNoticeId() != null) { |
| | |
| | | && ObjUtil.notEqual(reqVO.getBatchCode(), noticeLine.getBatchCode())) { |
| | | throw exception(WM_PRODUCT_SALES_LINE_NOTICE_LINE_BATCH_MISMATCH); |
| | | } |
| | | return; |
| | | return noticeLine; |
| | | } |
| | | |
| | | // æ
åµäºï¼å¦æåºåºå没æå
³èåè´§éç¥åï¼åä¸å
许å
³èåè´§éç¥åè¡ |
| | | if (noticeLineId != null) { |
| | | throw exception(WM_PRODUCT_SALES_LINE_SALES_NOTICE_LINE_NOT_ALLOWED); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | |
| | | @Override |
| | | public void updateReturnSalesDetail(MesWmReturnSalesDetailSaveReqVO updateReqVO) { |
| | | // æ ¡éªåå¨ |
| | | validateReturnSalesDetailExists(updateReqVO.getId()); |
| | | MesWmReturnSalesDetailDO oldDetail = validateReturnSalesDetailExists(updateReqVO.getId()); |
| | | if (ObjUtil.notEqual(oldDetail.getLineId(), updateReqVO.getLineId()) |
| | | || ObjUtil.notEqual(oldDetail.getReturnId(), updateReqVO.getReturnId())) { |
| | | throw exception(WM_RETURN_SALES_DETAIL_SOURCE_MISMATCH); |
| | | } |
| | | // æ ¡éªä¿åæ°æ® |
| | | validateReturnSalesDetailSaveData(updateReqVO); |
| | | |
| | |
| | | private void validateReturnSalesDetailSaveData(MesWmReturnSalesDetailSaveReqVO reqVO) { |
| | | // æ ¡éªç¶æ°æ®åå¨ |
| | | MesWmReturnSalesLineDO line = returnSalesLineService.validateReturnSalesLineExists(reqVO.getLineId()); |
| | | if (ObjUtil.notEqual(line.getReturnId(), reqVO.getReturnId()) |
| | | || ObjUtil.notEqual(line.getItemId(), reqVO.getItemId())) { |
| | | throw exception(WM_RETURN_SALES_DETAIL_SOURCE_MISMATCH); |
| | | } |
| | | // æ ¡éªéè´§å为å¾
䏿¶ç¶æ |
| | | validateReturnSalesStatusApproved(line.getReturnId()); |
| | | // 䏿¶ä½¿ç¨éè´§è¡çæ¹æ¬¡ï¼è¥è¡æ²¡ææå®æ¹æ¬¡ï¼å
许æ¾å¼éæ©å±äºè¯¥ç©æçæ¹æ¬¡ã |
| | | fillBatchId(reqVO, line); |
| | | // æ ¡éªä»åºãåºåºãåºä½çå
³èå
³ç³» |
| | | warehouseAreaService.validateWarehouseAreaExists( |
| | | reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId()); |
| | |
| | | // æ ¡éªæç»æ»æ°éä¸è¶
è¿è¡æ°éï¼æé¤èªèº«ï¼ |
| | | validateDetailQuantityNotExceed(reqVO.getLineId(), reqVO.getQuantity(), reqVO.getId(), line); |
| | | |
| | | // æ ¹æ® batchCode è§£æ batchIdï¼å¿
须卿··æ¾è§åæ ¡éªä¹åï¼å 为å®ä¾èµ batchIdï¼ |
| | | fillBatchId(reqVO); |
| | | } |
| | | |
| | | /** |
| | |
| | | /** |
| | | * æ ¹æ® batchCode è§£æå¹¶åå¡« batchId |
| | | */ |
| | | private void fillBatchId(MesWmReturnSalesDetailSaveReqVO reqVO) { |
| | | if (StrUtil.isBlank(reqVO.getBatchCode())) { |
| | | return; |
| | | private void fillBatchId(MesWmReturnSalesDetailSaveReqVO reqVO, MesWmReturnSalesLineDO line) { |
| | | MesWmBatchDO batch = null; |
| | | if (StrUtil.isNotBlank(reqVO.getBatchCode())) { |
| | | batch = batchService.getBatchByCode(reqVO.getBatchCode()); |
| | | if (batch == null) throw exception(WM_BATCH_NOT_EXISTS); |
| | | if (reqVO.getBatchId() != null && ObjUtil.notEqual(batch.getId(), reqVO.getBatchId())) { |
| | | throw exception(WM_RETURN_SALES_DETAIL_BATCH_MISMATCH); |
| | | } |
| | | } else if (reqVO.getBatchId() != null || line.getBatchId() != null) { |
| | | batch = batchService.validateBatchExists( |
| | | reqVO.getBatchId() != null ? reqVO.getBatchId() : line.getBatchId(), reqVO.getItemId()); |
| | | } |
| | | MesWmBatchDO batch = batchService.getBatchByCode(reqVO.getBatchCode()); |
| | | if (batch != null) { |
| | | if (ObjUtil.notEqual(batch.getItemId(), reqVO.getItemId()) |
| | | || (line.getBatchId() != null && ObjUtil.notEqual(line.getBatchId(), batch.getId()))) { |
| | | throw exception(WM_RETURN_SALES_DETAIL_BATCH_MISMATCH); |
| | | } |
| | | reqVO.setBatchId(batch.getId()); |
| | | reqVO.setBatchCode(batch.getCode()); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | 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.wm.returnsales.vo.line.MesWmReturnSalesLinePageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.line.MesWmReturnSalesLineSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnsales.MesWmReturnSalesLineMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesDetailMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnSalesStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | import jakarta.annotation.Resource; |
| | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.MD_ITEM_BATCH_REQUIRED; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_RETURN_SALES_LINE_NOT_EXISTS; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_RETURN_SALES_REEL_MISMATCH; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_RETURN_SALES_BATCH_NOT_SHIPPED; |
| | | |
| | | /** |
| | | * MES éå®éè´§åè¡ Service å®ç°ç±» |
| | |
| | | private MesMdmItemService mdmItemService; |
| | | @Resource |
| | | private MesWmBatchService batchService; |
| | | @Resource |
| | | private ErpSaleOrderApi saleOrderApi; |
| | | @Resource |
| | | private MesWmProductSalesDetailMapper productSalesDetailMapper; |
| | | @Resource |
| | | @Lazy |
| | | private MesWmReturnSalesDetailService returnSalesDetailService; |
| | |
| | | */ |
| | | private void validateLineSaveData(MesWmReturnSalesLineSaveReqVO reqVO) { |
| | | // æ ¡éªéè´§ååå¨ + èç¨¿ç¶æ |
| | | returnSalesService.validateReturnSalesExistsAndPrepare(reqVO.getReturnId()); |
| | | MesWmReturnSalesDO returnSales = returnSalesService.validateReturnSalesExistsAndPrepare(reqVO.getReturnId()); |
| | | // æ ¡éªç©æåå¨ + æ¹æ¬¡ç®¡ç |
| | | validateItemBatchManagement(reqVO.getReturnId(), reqVO.getItemId(), reqVO.getBatchId()); |
| | | MesWmBatchDO batch = validateItemBatchManagement(returnSales, reqVO.getItemId(), reqVO.getBatchId()); |
| | | if (batch != null) { |
| | | reqVO.setBatchCode(batch.getCode()); |
| | | } |
| | | if (returnSales.getSaleOrderId() != null) { |
| | | var items = saleOrderApi.getSaleOrderItemList(returnSales.getSaleOrderId()).getCheckedData(); |
| | | var reelNumbers = items.stream().filter(item -> Objects.equals(reqVO.getItemId(), item.getProductId())) |
| | | .map(item -> item.getReelNo()).filter(StrUtil::isNotBlank).toList(); |
| | | if (!reelNumbers.isEmpty() && (batch == null || !reelNumbers.contains(batch.getReelNo()) |
| | | || (StrUtil.isNotBlank(batch.getSalesOrderCode()) |
| | | && !Objects.equals(returnSales.getSalesOrderCode(), batch.getSalesOrderCode())))) { |
| | | throw exception(WM_RETURN_SALES_REEL_MISMATCH); |
| | | } |
| | | if (!reelNumbers.isEmpty() && productSalesDetailMapper.countFinishedSaleBatch( |
| | | returnSales.getSaleOrderId(), reqVO.getItemId(), batch.getId(), |
| | | MesWmProductSalesStatusEnum.FINISHED.getStatus()) == 0) { |
| | | throw exception(WM_RETURN_SALES_BATCH_NOT_SHIPPED); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | * @param itemId ç©æID |
| | | * @param batchId æ¹æ¬¡ID |
| | | */ |
| | | private void validateItemBatchManagement(Long returnId, Long itemId, Long batchId) { |
| | | private MesWmBatchDO validateItemBatchManagement(MesWmReturnSalesDO returnSales, Long itemId, Long batchId) { |
| | | MesMdmItemDO item = mdmItemService.validateItemExistsAndEnable(itemId); |
| | | // å¦æç©æå¯ç¨äºæ¹æ¬¡ç®¡çï¼åå¿
é¡»éæ©æ¹æ¬¡ |
| | | if (Boolean.TRUE.equals(item.getIsBatchManaged()) && batchId == null) { |
| | |
| | | // æ ¡éªæ¹æ¬¡åå¨ä¸å±äºå½åç©æåå®¢æ· |
| | | if (batchId != null) { |
| | | // ä»ç¶åè·å客æ·IDï¼æ ¡éªæ¹æ¬¡å½å± |
| | | MesWmReturnSalesDO returnSales = returnSalesService.validateReturnSalesExistsAndPrepare(returnId); |
| | | batchService.validateBatchExists(batchId, itemId, returnSales.getClientId(), null); |
| | | return batchService.validateBatchExists(batchId, itemId, returnSales.getClientId(), null); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | |
| | | .setItemId(sourceLine.getItemId()) |
| | | .setQuantity(unqualifiedQuantity) |
| | | .setBatchId(sourceLine.getBatchId()) |
| | | .setBatchCode(sourceLine.getBatchCode()) |
| | | .setRqcId(sourceLine.getRqcId()) |
| | | .setRqcCheckFlag(sourceLine.getRqcCheckFlag()) |
| | | .setQualityStatus(MesWmQualityStatusEnum.FAIL.getStatus()) |
| | |
| | | |
| | | import cn.hutool.core.util.ObjUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | 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.wm.salesnotice.vo.line.MesWmSalesNoticeLinePageReqVO; |
| | |
| | | private MesWmBatchService batchService; |
| | | @Resource |
| | | private MesMdmItemService mdmItemService; |
| | | @Resource |
| | | private ErpSaleOrderApi saleOrderApi; |
| | | |
| | | @Override |
| | | public Long createSalesNoticeLine(MesWmSalesNoticeLineSaveReqVO createReqVO) { |
| | |
| | | MesWmSalesNoticeLineDO line = validateSalesNoticeLineExists(updateReqVO.getId()); |
| | | // æ ¡éªæ°æ® |
| | | updateReqVO.setNoticeId(line.getNoticeId()); |
| | | if (updateReqVO.getSaleOrderItemId() == null) { |
| | | updateReqVO.setSaleOrderItemId(line.getSaleOrderItemId()); |
| | | } |
| | | validateLineSaveData(updateReqVO); |
| | | |
| | | // æ´æ° |
| | |
| | | private void validateLineSaveData(MesWmSalesNoticeLineSaveReqVO reqVO) { |
| | | // æ ¡éªç¶åæ®åå¨ä¸ä¸ºèç¨¿ç¶æ |
| | | validateNoticeStatusDraft(reqVO.getNoticeId()); |
| | | MesWmSalesNoticeDO notice = salesNoticeService.getSalesNotice(reqVO.getNoticeId()); |
| | | if (notice.getSaleOrderId() != null && reqVO.getSaleOrderItemId() == null) { |
| | | throw exception(WM_SALES_NOTICE_LINE_SALE_ITEM_REQUIRED); |
| | | } |
| | | if (reqVO.getSaleOrderItemId() != null) { |
| | | var saleItem = saleOrderApi.getSaleOrderItem(reqVO.getSaleOrderItemId()).getCheckedData(); |
| | | if (saleItem == null || !ObjUtil.equal(notice.getSaleOrderId(), saleItem.getOrderId()) |
| | | || !ObjUtil.equal(reqVO.getItemId(), saleItem.getProductId())) { |
| | | throw exception(WM_SALES_NOTICE_LINE_SALE_ITEM_MISMATCH); |
| | | } |
| | | } |
| | | // æ ¡éªç©æåå¨ |
| | | if (reqVO.getItemId() != null) { |
| | | mdmItemService.validateItemExistsAndEnable(reqVO.getItemId()); |
| | |
| | | public void testNonBatchManagedItemUsesDistinctTraceBatchesForReels() { |
| | | when(mdmItemService.validateItemExists(10L)).thenReturn(new MesMdmItemDO().setId(10L) |
| | | .setName("产å").setIsBatchManaged(false)); |
| | | when(autoCodeRecordService.generateAutoCode(anyString())).thenReturn("TRACE-1", "TRACE-2"); |
| | | when(autoCodeRecordService.generateAutoCode(anyString())).thenReturn("TRACE-1", "TRACE-2", "TRACE-3"); |
| | | MesWmBatchGenerateReqVO first = new MesWmBatchGenerateReqVO().setItemId(10L) |
| | | .setWorkOrderId(20L).setReelNo("PAN-1"); |
| | | MesWmBatchGenerateReqVO second = new MesWmBatchGenerateReqVO().setItemId(10L) |
| | |
| | | assertNotEquals(a.getId(), b.getId()); |
| | | assertEquals(a.getId(), batchService.getOrGenerateBatchCode(first).getId()); |
| | | assertEquals("PAN-2", batchMapper.selectById(b.getId()).getReelNo()); |
| | | MesWmBatchDO caseVariant = batchService.getOrGenerateBatchCode(new MesWmBatchGenerateReqVO() |
| | | .setItemId(10L).setWorkOrderId(20L).setReelNo("pan-1")); |
| | | assertNotEquals(a.getId(), caseVariant.getId()); |
| | | assertNull(batchService.getOrGenerateBatchCode(new MesWmBatchGenerateReqVO().setItemId(10L))); |
| | | } |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.wm.productsales; |
| | | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.line.MesWmProductSalesLineSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesLineMapper; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.salesnotice.MesWmSalesNoticeLineService; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.junit.jupiter.api.extension.ExtendWith; |
| | | import org.mockito.ArgumentCaptor; |
| | | import org.mockito.InjectMocks; |
| | | import org.mockito.Mock; |
| | | import org.mockito.junit.jupiter.MockitoExtension; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | import static org.mockito.ArgumentMatchers.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | @ExtendWith(MockitoExtension.class) |
| | | class MesWmProductSalesLineReelTest { |
| | | @InjectMocks private MesWmProductSalesLineServiceImpl service; |
| | | @Mock private MesWmProductSalesLineMapper mapper; |
| | | @Mock private MesWmProductSalesService salesService; |
| | | @Mock private MesWmSalesNoticeLineService noticeService; |
| | | @Mock private MesMdmItemService itemService; |
| | | @Mock private MesQcOqcService oqcService; |
| | | |
| | | private MesWmProductSalesLineSaveReqVO request() { |
| | | var req = new MesWmProductSalesLineSaveReqVO(); |
| | | req.setSalesId(1L); |
| | | req.setNoticeLineId(2L); |
| | | req.setItemId(3L); |
| | | req.setQuantity(BigDecimal.ONE); |
| | | when(salesService.validateProductSalesExistsAndDraft(1L)) |
| | | .thenReturn(MesWmProductSalesDO.builder().id(1L).noticeId(4L).build()); |
| | | when(noticeService.validateSalesNoticeLineExists(2L, 4L)) |
| | | .thenReturn(MesWmSalesNoticeLineDO.builder().id(2L).itemId(3L) |
| | | .quantity(BigDecimal.ONE).saleOrderItemId(5L).build()); |
| | | return req; |
| | | } |
| | | |
| | | @Test |
| | | void createRetainsSalesItemForReelGuard() { |
| | | service.createProductSalesLine(request()); |
| | | var line = ArgumentCaptor.forClass(MesWmProductSalesLineDO.class); |
| | | verify(mapper).insert(line.capture()); |
| | | assertEquals(5L, line.getValue().getSaleOrderItemId()); |
| | | } |
| | | |
| | | @Test |
| | | void enablingInspectionRetainsSalesItemForOqc() { |
| | | var req = request(); |
| | | req.setId(6L); |
| | | req.setOqcCheckFlag(true); |
| | | when(mapper.selectById(6L)).thenReturn(MesWmProductSalesLineDO.builder() |
| | | .id(6L).salesId(1L).oqcCheckFlag(false).build()); |
| | | when(salesService.getProductSales(1L)).thenReturn(MesWmProductSalesDO.builder().id(1L).build()); |
| | | when(oqcService.createOqcFromProductSales(eq(1L), any(), any(), any())).thenReturn(7L); |
| | | service.updateProductSalesLine(req); |
| | | var line = ArgumentCaptor.forClass(MesWmProductSalesLineDO.class); |
| | | verify(oqcService).createOqcFromProductSales(eq(1L), line.capture(), any(), any()); |
| | | assertEquals(5L, line.getValue().getSaleOrderItemId()); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.wm.returnsales; |
| | | |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesDetailMapper; |
| | | import org.apache.ibatis.annotations.Select; |
| | | import org.junit.jupiter.api.Test; |
| | | |
| | | import java.sql.DriverManager; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | |
| | | /** éªè¯å®é
åºåºå
³èæ¥è¯¢ä¸ä¼æå
¶ä»è®¢åãå¾
åºåºåæå
¶ä»çå·å½ä½éè´§æ¥æºã */ |
| | | class MesWmFinishedSaleBatchSqlTest { |
| | | @Test |
| | | void checksFinishedOrderItemAndBatch() throws Exception { |
| | | try (var c = DriverManager.getConnection("jdbc:h2:mem:reel_source;MODE=MySQL;DB_CLOSE_DELAY=-1")) { |
| | | var st = c.createStatement(); |
| | | st.execute("CREATE TABLE mes_wm_product_sales (id BIGINT, sale_order_id BIGINT, status INT, deleted INT)"); |
| | | st.execute("CREATE TABLE mes_wm_product_sales_line (id BIGINT, sales_id BIGINT, sale_order_item_id BIGINT, item_id BIGINT, deleted INT)"); |
| | | st.execute("CREATE TABLE mes_wm_product_sales_detail (id BIGINT, sales_id BIGINT, line_id BIGINT, item_id BIGINT, batch_id BIGINT, deleted INT)"); |
| | | st.execute("CREATE TABLE erp_sale_order_items (id BIGINT, order_id BIGINT, product_id BIGINT, reel_no VARCHAR(128), deleted INT)"); |
| | | st.execute("CREATE TABLE mes_wm_batch (id BIGINT, item_id BIGINT, reel_no VARCHAR(128), deleted INT)"); |
| | | st.execute("INSERT INTO mes_wm_product_sales VALUES (1, 7, 5, 0), (2, 7, 1, 0), (3, 8, 5, 0)"); |
| | | st.execute("INSERT INTO erp_sale_order_items VALUES (11, 7, 3, 'P1', 0), (12, 8, 3, 'P1', 0), (13, 7, 3, 'P2', 0)"); |
| | | st.execute("INSERT INTO mes_wm_batch VALUES (21, 3, 'P1', 0), (22, 3, 'P2', 0)"); |
| | | st.execute("INSERT INTO mes_wm_product_sales_line VALUES (31, 1, 11, 3, 0), (32, 2, 13, 3, 0), (33, 3, 12, 3, 0), (34, 1, 13, 3, 0)"); |
| | | st.execute("INSERT INTO mes_wm_product_sales_detail VALUES (41, 1, 31, 3, 21, 0), (42, 2, 32, 3, 22, 0), (43, 3, 33, 3, 21, 0), (44, 1, 34, 3, 21, 0)"); |
| | | String sql = MesWmProductSalesDetailMapper.class.getMethod("countFinishedSaleBatch", Long.class, |
| | | Long.class, Long.class, Integer.class).getAnnotation(Select.class).value()[0] |
| | | // H2 çå符串æ¯è¾é»è®¤åºå大å°åï¼MySQL ç BINARY è¯æ³å¨çå®åºåç¬éªè¯ã |
| | | .replace("BINARY i.reel_no", "i.reel_no").replace("BINARY b.reel_no", "b.reel_no") |
| | | .replace("#{orderId}", "?").replace("#{finishedStatus}", "?") |
| | | .replace("#{itemId}", "?").replace("#{batchId}", "?"); |
| | | try (var query = c.prepareStatement(sql)) { |
| | | query.setLong(1, 7); query.setInt(2, 5); query.setLong(3, 3); |
| | | query.setLong(4, 3); query.setLong(5, 3); query.setLong(6, 21); |
| | | try (var rows = query.executeQuery()) { rows.next(); assertEquals(1, rows.getInt(1)); } |
| | | query.setLong(6, 22); |
| | | try (var rows = query.executeQuery()) { rows.next(); assertEquals(0, rows.getInt(1)); } |
| | | } |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.wm.returnsales; |
| | | |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderItemRespDTO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.detail.MesWmReturnSalesDetailSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.line.MesWmReturnSalesLineSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnsales.MesWmReturnSalesDetailMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnsales.MesWmReturnSalesLineMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesDetailMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnSalesStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | | 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 java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.*; |
| | | import static org.mockito.ArgumentMatchers.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | @ExtendWith(MockitoExtension.class) |
| | | class MesWmReturnSalesReelTest { |
| | | @InjectMocks private MesWmReturnSalesDetailServiceImpl details; |
| | | @InjectMocks private MesWmReturnSalesLineServiceImpl lines; |
| | | @Mock private MesWmReturnSalesLineService lineService; |
| | | @Mock private MesWmReturnSalesService returnService; |
| | | @Mock private MesWmReturnSalesDetailMapper detailMapper; |
| | | @Mock private MesWmReturnSalesLineMapper lineMapper; |
| | | @Mock private MesWmProductSalesDetailMapper productSalesDetailMapper; |
| | | @Mock private MesWmWarehouseAreaService areaService; |
| | | @Mock private MesWmMaterialStockService stockService; |
| | | @Mock private MesWmBatchService batchService; |
| | | @Mock private MesMdmItemService itemService; |
| | | @Mock private ErpSaleOrderApi saleOrderApi; |
| | | |
| | | @Test |
| | | void resolvesBatchBeforeCheckingMixingAndKeepsSource() { |
| | | var req = new MesWmReturnSalesDetailSaveReqVO(); |
| | | req.setLineId(1L); req.setReturnId(2L); req.setItemId(3L); |
| | | req.setBatchCode("B1"); req.setQuantity(BigDecimal.ONE); req.setAreaId(4L); |
| | | when(lineService.validateReturnSalesLineExists(1L)).thenReturn(MesWmReturnSalesLineDO.builder() |
| | | .returnId(2L).itemId(3L).batchId(5L).quantity(BigDecimal.TEN).build()); |
| | | when(returnService.validateReturnSalesExists(2L)).thenReturn(MesWmReturnSalesDO.builder() |
| | | .status(MesWmReturnSalesStatusEnum.APPROVED.getStatus()).build()); |
| | | when(batchService.getBatchByCode("B1")).thenReturn(MesWmBatchDO.builder().id(5L).itemId(3L).code("B1").build()); |
| | | when(detailMapper.selectListByLineId(1L)).thenReturn(List.of()); |
| | | details.createReturnSalesDetail(req); |
| | | verify(stockService).checkAreaMixingRule(4L, 3L, 5L); |
| | | assertEquals(5L, req.getBatchId()); |
| | | } |
| | | |
| | | @Test |
| | | void rejectsDifferentBatchForSameReturnLine() { |
| | | var req = new MesWmReturnSalesDetailSaveReqVO(); |
| | | req.setLineId(1L); req.setReturnId(2L); req.setItemId(3L); req.setBatchCode("B2"); |
| | | when(lineService.validateReturnSalesLineExists(1L)).thenReturn(MesWmReturnSalesLineDO.builder() |
| | | .returnId(2L).itemId(3L).batchId(5L).build()); |
| | | when(returnService.validateReturnSalesExists(2L)).thenReturn(MesWmReturnSalesDO.builder() |
| | | .status(MesWmReturnSalesStatusEnum.APPROVED.getStatus()).build()); |
| | | when(batchService.getBatchByCode("B2")).thenReturn(MesWmBatchDO.builder().id(6L).itemId(3L).build()); |
| | | assertThrows(ServiceException.class, () -> details.createReturnSalesDetail(req)); |
| | | verifyNoInteractions(stockService); |
| | | } |
| | | |
| | | @Test |
| | | void returnOrderWithReelsRequiresMatchingBatch() { |
| | | var req = new MesWmReturnSalesLineSaveReqVO(); |
| | | req.setReturnId(2L); req.setItemId(3L); req.setBatchId(5L); |
| | | when(returnService.validateReturnSalesExistsAndPrepare(2L)).thenReturn(MesWmReturnSalesDO.builder() |
| | | .saleOrderId(7L).clientId(8L).build()); |
| | | when(itemService.validateItemExistsAndEnable(3L)).thenReturn(MesMdmItemDO.builder().isBatchManaged(false).build()); |
| | | var batch = MesWmBatchDO.builder().id(5L).itemId(3L).code("B1").reelNo("OTHER").build(); |
| | | when(batchService.validateBatchExists(5L, 3L, 8L, null)).thenReturn(batch); |
| | | var item = new ErpSaleOrderItemRespDTO(); item.setProductId(3L); item.setReelNo("REEL-1"); |
| | | when(saleOrderApi.getSaleOrderItemList(7L)).thenReturn(CommonResult.success(List.of(item))); |
| | | assertThrows(ServiceException.class, () -> lines.createReturnSalesLine(req)); |
| | | batch.setReelNo("REEL-1"); |
| | | when(productSalesDetailMapper.countFinishedSaleBatch(eq(7L), eq(3L), eq(5L), anyInt())).thenReturn(0L, 1L); |
| | | assertThrows(ServiceException.class, () -> lines.createReturnSalesLine(req)); |
| | | lines.createReturnSalesLine(req); |
| | | assertEquals("B1", req.getBatchCode()); |
| | | verify(lineMapper).insert(any(MesWmReturnSalesLineDO.class)); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.wm.salesnotice; |
| | | |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderItemRespDTO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.salesnotice.vo.line.MesWmSalesNoticeLineSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.salesnotice.MesWmSalesNoticeLineMapper; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.junit.jupiter.api.extension.ExtendWith; |
| | | import org.mockito.ArgumentCaptor; |
| | | import org.mockito.InjectMocks; |
| | | import org.mockito.Mock; |
| | | import org.mockito.junit.jupiter.MockitoExtension; |
| | | |
| | | import static cn.iocoder.yudao.module.mes.enums.wm.MesWmSalesNoticeStatusEnum.PREPARE; |
| | | import static org.junit.jupiter.api.Assertions.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | @ExtendWith(MockitoExtension.class) |
| | | class MesWmSalesNoticeLineReelTest { |
| | | @InjectMocks private MesWmSalesNoticeLineServiceImpl service; |
| | | @Mock private MesWmSalesNoticeService noticeService; |
| | | @Mock private MesWmSalesNoticeLineMapper mapper; |
| | | @Mock private ErpSaleOrderApi saleOrderApi; |
| | | @Mock private MesMdmItemService itemService; |
| | | |
| | | private MesWmSalesNoticeLineSaveReqVO request() { |
| | | var req = new MesWmSalesNoticeLineSaveReqVO(); |
| | | req.setNoticeId(1L); |
| | | req.setItemId(3L); |
| | | when(noticeService.getSalesNotice(1L)).thenReturn(MesWmSalesNoticeDO.builder() |
| | | .id(1L).saleOrderId(2L).status(PREPARE.getStatus()).build()); |
| | | return req; |
| | | } |
| | | |
| | | @Test |
| | | void linkedNoticeRequiresExplicitSalesItem() { |
| | | var req = request(); |
| | | assertThrows(ServiceException.class, () -> service.createSalesNoticeLine(req)); |
| | | verifyNoInteractions(saleOrderApi); |
| | | } |
| | | |
| | | @Test |
| | | void linkedNoticeRequiresMatchingOrderAndProduct() { |
| | | var req = request(); |
| | | req.setSaleOrderItemId(4L); |
| | | var item = new ErpSaleOrderItemRespDTO(); |
| | | item.setOrderId(2L); |
| | | item.setProductId(99L); |
| | | when(saleOrderApi.getSaleOrderItem(4L)).thenReturn(CommonResult.success(item)); |
| | | assertThrows(ServiceException.class, () -> service.createSalesNoticeLine(req)); |
| | | item.setProductId(3L); |
| | | service.createSalesNoticeLine(req); |
| | | var saved = ArgumentCaptor.forClass(MesWmSalesNoticeLineDO.class); |
| | | verify(mapper).insert(saved.capture()); |
| | | assertEquals(4L, saved.getValue().getSaleOrderItemId()); |
| | | } |
| | | } |
| | |
| | | primary: master |
| | | datasource: |
| | | master: |
| | | url: jdbc:mysql://localhost:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # æ¬å°èè°æµè¯åº |
| | | url: jdbc:mysql://36.213.150.237:10000/mom-ntsh?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # æ¬å°èè°æµè¯åº |
| | | username: root |
| | | password: 123456 |
| | | password: xd@123456.. |
| | | |
| | | # Redis é
ç½®ãRedisson é»è®¤çé
置足å¤ä½¿ç¨ï¼ä¸è¬ä¸éè¦è¿è¡è°ä¼ |
| | | data: |