Commit d20a5f5442bed0d63775c54e4b4f6f4f17a81db0

Authored by YunaiV
1 parent 1ea8dd17

✨ feat(mes): 添加运单填写功能及相关状态管理

yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/MesWmProductSalesController.java
@@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; @@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
11 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO; 11 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO;
12 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesRespVO; 12 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesRespVO;
13 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO; 13 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO;
  14 +import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesShippingReqVO;
14 import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO; 15 import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
15 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO; 16 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO;
16 import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService; 17 import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
@@ -112,12 +113,28 @@ public class MesWmProductSalesController { @@ -112,12 +113,28 @@ public class MesWmProductSalesController {
112 return success(true); 113 return success(true);
113 } 114 }
114 115
115 - @PutMapping("/pick") 116 + @GetMapping("/check-quantity")
  117 + @Operation(summary = "校验销售出库单数量")
  118 + @Parameter(name = "id", description = "编号", required = true)
  119 + @PreAuthorize("@ss.hasPermission('mes:wm-product-sales:query')")
  120 + public CommonResult<Boolean> checkProductSalesQuantity(@RequestParam("id") Long id) {
  121 + return success(productSalesService.checkProductSalesQuantity(id));
  122 + }
  123 +
  124 + @PutMapping("/stock")
116 @Operation(summary = "执行拣货") 125 @Operation(summary = "执行拣货")
117 @Parameter(name = "id", description = "编号", required = true) 126 @Parameter(name = "id", description = "编号", required = true)
118 - @PreAuthorize("@ss.hasPermission('mes:wm-product-sales:pick')")  
119 - public CommonResult<Boolean> pickProductSales(@RequestParam("id") Long id) {  
120 - productSalesService.pickProductSales(id); 127 + @PreAuthorize("@ss.hasPermission('mes:wm-product-sales:stock')")
  128 + public CommonResult<Boolean> stockProductSales(@RequestParam("id") Long id) {
  129 + productSalesService.stockProductSales(id);
  130 + return success(true);
  131 + }
  132 +
  133 + @PutMapping("/shipping")
  134 + @Operation(summary = "填写运单")
  135 + @PreAuthorize("@ss.hasPermission('mes:wm-product-sales:shipping')")
  136 + public CommonResult<Boolean> shippingProductSales(@Valid @RequestBody MesWmProductSalesShippingReqVO reqVO) {
  137 + productSalesService.shippingProductSales(reqVO);
121 return success(true); 138 return success(true);
122 } 139 }
123 140
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesShippingReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - MES 销售出库单填写运单 Request VO")
  8 +@Data
  9 +public class MesWmProductSalesShippingReqVO {
  10 +
  11 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  12 + @NotNull(message = "编号不能为空")
  13 + private Long id;
  14 +
  15 + @Schema(description = "承运商", example = "顺丰快递")
  16 + private String carrier;
  17 +
  18 + @Schema(description = "运输单号", example = "SF1234567890")
  19 + private String shippingNumber;
  20 +
  21 +}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -433,13 +433,14 @@ public interface ErrorCodeConstants { @@ -433,13 +433,14 @@ public interface ErrorCodeConstants {
433 ErrorCode WM_PRODUCT_SALES_LINES_EMPTY = new ErrorCode(1_040_714_003, "销售出库单行不能为空"); 433 ErrorCode WM_PRODUCT_SALES_LINES_EMPTY = new ErrorCode(1_040_714_003, "销售出库单行不能为空");
434 ErrorCode WM_PRODUCT_SALES_CANNOT_SUBMIT = new ErrorCode(1_040_714_004, "当前状态不允许提交"); 434 ErrorCode WM_PRODUCT_SALES_CANNOT_SUBMIT = new ErrorCode(1_040_714_004, "当前状态不允许提交");
435 ErrorCode WM_PRODUCT_SALES_CANNOT_PICK = new ErrorCode(1_040_714_005, "当前状态不允许拣货"); 435 ErrorCode WM_PRODUCT_SALES_CANNOT_PICK = new ErrorCode(1_040_714_005, "当前状态不允许拣货");
436 - ErrorCode WM_PRODUCT_SALES_CANNOT_EXECUTE = new ErrorCode(1_040_714_006, "当前状态不允许执行出库");  
437 - ErrorCode WM_PRODUCT_SALES_CANNOT_CANCEL = new ErrorCode(1_040_714_007, "当前状态不允许取消");  
438 - ErrorCode WM_PRODUCT_SALES_DETAILS_EMPTY = new ErrorCode(1_040_714_008, "拣货明细不能为空");  
439 - ErrorCode WM_PRODUCT_SALES_DETAIL_QUANTITY_MISMATCH = new ErrorCode(1_040_714_009, "拣货数量与出库数量不匹配");  
440 - ErrorCode WM_PRODUCT_SALES_LINE_NOT_EXISTS = new ErrorCode(1_040_714_010, "销售出库单行不存在");  
441 - ErrorCode WM_PRODUCT_SALES_DETAIL_NOT_EXISTS = new ErrorCode(1_040_714_011, "销售出库明细不存在");  
442 - ErrorCode WM_PRODUCT_SALES_STOCK_INSUFFICIENT = new ErrorCode(1_040_714_012, "库存不足,无法拣货"); 436 + ErrorCode WM_PRODUCT_SALES_CANNOT_SHIPPING = new ErrorCode(1_040_714_006, "当前状态不允许填写运单");
  437 + ErrorCode WM_PRODUCT_SALES_CANNOT_EXECUTE = new ErrorCode(1_040_714_007, "当前状态不允许执行出库");
  438 + ErrorCode WM_PRODUCT_SALES_CANNOT_CANCEL = new ErrorCode(1_040_714_008, "当前状态不允许取消");
  439 + ErrorCode WM_PRODUCT_SALES_DETAILS_EMPTY = new ErrorCode(1_040_714_009, "拣货明细不能为空");
  440 + ErrorCode WM_PRODUCT_SALES_DETAIL_QUANTITY_MISMATCH = new ErrorCode(1_040_714_010, "拣货数量与出库数量不匹配");
  441 + ErrorCode WM_PRODUCT_SALES_LINE_NOT_EXISTS = new ErrorCode(1_040_714_011, "销售出库单行不存在");
  442 + ErrorCode WM_PRODUCT_SALES_DETAIL_NOT_EXISTS = new ErrorCode(1_040_714_012, "销售出库明细不存在");
  443 + ErrorCode WM_PRODUCT_SALES_STOCK_INSUFFICIENT = new ErrorCode(1_040_714_013, "库存不足,无法拣货");
443 444
444 // ========== MES 仓库管理-发货通知单(1-040-720-000) ========== 445 // ========== MES 仓库管理-发货通知单(1-040-720-000) ==========
445 ErrorCode WM_SALES_NOTICE_NOT_EXISTS = new ErrorCode(1_040_720_000, "发货通知单不存在"); 446 ErrorCode WM_SALES_NOTICE_NOT_EXISTS = new ErrorCode(1_040_720_000, "发货通知单不存在");
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmProductSalesStatusEnum.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mes.enums.wm; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mes.enums.wm;
2 2
3 import cn.iocoder.yudao.framework.common.core.ArrayValuable; 3 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO; 4 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesShippingReqVO;
5 import cn.iocoder.yudao.module.mes.enums.MesOrderStatusConstants; 6 import cn.iocoder.yudao.module.mes.enums.MesOrderStatusConstants;
6 import cn.iocoder.yudao.module.mes.service.wm.productsales.MesWmProductSalesService; 7 import cn.iocoder.yudao.module.mes.service.wm.productsales.MesWmProductSalesService;
7 import lombok.AllArgsConstructor; 8 import lombok.AllArgsConstructor;
@@ -20,8 +21,6 @@ import java.util.Arrays; @@ -20,8 +21,6 @@ import java.util.Arrays;
20 @AllArgsConstructor 21 @AllArgsConstructor
21 public enum MesWmProductSalesStatusEnum implements ArrayValuable<Integer> { 22 public enum MesWmProductSalesStatusEnum implements ArrayValuable<Integer> {
22 23
23 - // (草稿)提交 =》(待拣货)执行拣货 =》(待填写运单)填写运单;=》(待执行出库)执行出库=》(已完成)  
24 -  
25 /** 24 /**
26 * 草稿 25 * 草稿
27 * 26 *
@@ -34,12 +33,16 @@ public enum MesWmProductSalesStatusEnum implements ArrayValuable&lt;Integer&gt; { @@ -34,12 +33,16 @@ public enum MesWmProductSalesStatusEnum implements ArrayValuable&lt;Integer&gt; {
34 * 对应方法:{@link MesWmProductSalesService#submitProductSales(Long)} 33 * 对应方法:{@link MesWmProductSalesService#submitProductSales(Long)}
35 */ 34 */
36 APPROVING(MesOrderStatusConstants.APPROVING, "待拣货"), 35 APPROVING(MesOrderStatusConstants.APPROVING, "待拣货"),
37 - // TODO @AI:在 APPROVING、APPROVED 之间加一个 10,SHIPPING,待填写运单;执行填写运单方法:{@link MesWmProductSalesService#shippingProductSales(Long)}  
38 - // TODO @AI:pick 这个方法要改成 stock; 36 + /**
  37 + * 待填写运单
  38 + *
  39 + * 对应方法:{@link MesWmProductSalesService#shippingProductSales(MesWmProductSalesShippingReqVO)}
  40 + */
  41 + SHIPPING(10, "待填写运单"), // 10 是一个特殊的状态值,不在 MesOrderStatusConstants 中,单独定义
39 /** 42 /**
40 * 待执行出库 43 * 待执行出库
41 * 44 *
42 - * 对应方法:{@link MesWmProductSalesService#pickProductSales(Long)} 45 + * 对应方法:{@link MesWmProductSalesService#stockProductSales(Long)}
43 */ 46 */
44 APPROVED(MesOrderStatusConstants.APPROVED, "待执行出库"), 47 APPROVED(MesOrderStatusConstants.APPROVED, "待执行出库"),
45 /** 48 /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesService.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mes.service.wm.productsales; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mes.service.wm.productsales;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO; 4 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO;
5 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO; 5 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesShippingReqVO;
6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO; 7 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO;
7 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
8 9
@@ -59,11 +60,26 @@ public interface MesWmProductSalesService { @@ -59,11 +60,26 @@ public interface MesWmProductSalesService {
59 void submitProductSales(Long id); 60 void submitProductSales(Long id);
60 61
61 /** 62 /**
62 - * 执行拣货(待拣货 → 待出库) 63 + * 校验销售出库单数量是否匹配
63 * 64 *
64 * @param id 编号 65 * @param id 编号
  66 + * @return 是否匹配
65 */ 67 */
66 - void pickProductSales(Long id); 68 + boolean checkProductSalesQuantity(Long id);
  69 +
  70 + /**
  71 + * 执行拣货(待拣货 → 待填写运单)
  72 + *
  73 + * @param id 编号
  74 + */
  75 + void stockProductSales(Long id);
  76 +
  77 + /**
  78 + * 填写运单(待填写运单 → 待执行出库)
  79 + *
  80 + * @param reqVO 运单信息
  81 + */
  82 + void shippingProductSales(@Valid MesWmProductSalesShippingReqVO reqVO);
67 83
68 /** 84 /**
69 * 执行出库(待出库 → 已完成),扣减库存 85 * 执行出库(待出库 → 已完成),扣减库存
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO; 9 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO;
10 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO; 10 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO;
  11 +import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesShippingReqVO;
11 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO; 12 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO;
12 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO; 13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO;
13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO; 14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO;
@@ -117,33 +118,53 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService { @@ -117,33 +118,53 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
117 .setId(id).setStatus(MesWmProductSalesStatusEnum.APPROVING.getStatus())); 118 .setId(id).setStatus(MesWmProductSalesStatusEnum.APPROVING.getStatus()));
118 } 119 }
119 120
120 - // TODO @AI:前后端方法名,都改成 stock;保持整体都是对齐的;  
121 @Override 121 @Override
122 - @Transactional(rollbackFor = Exception.class)  
123 - public void pickProductSales(Long id) {  
124 - // 校验存在  
125 - MesWmProductSalesDO sales = validateProductSalesExists(id);  
126 - if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.APPROVING.getStatus(), sales.getStatus())) {  
127 - throw exception(WM_PRODUCT_SALES_CANNOT_PICK);  
128 - } 122 + public boolean checkProductSalesQuantity(Long id) {
129 // 校验每行明细数量之和是否等于行出库数量 123 // 校验每行明细数量之和是否等于行出库数量
130 - // TODO @AI:这里挪成独立的 checkQuantiy 接口,不在这个方法里,保持对齐;然后前端调用;类似 /Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/mes/wm/returnvendor/ReturnVendorForm.vue 这种;  
131 List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(id); 124 List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(id);
132 for (MesWmProductSalesLineDO line : lines) { 125 for (MesWmProductSalesLineDO line : lines) {
133 List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListByLineId(line.getId()); 126 List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListByLineId(line.getId());
134 if (CollUtil.isEmpty(details)) { 127 if (CollUtil.isEmpty(details)) {
135 - throw exception(WM_PRODUCT_SALES_DETAILS_EMPTY); 128 + return false;
136 } 129 }
137 BigDecimal totalDetailQty = CollectionUtils.getSumValue(details, 130 BigDecimal totalDetailQty = CollectionUtils.getSumValue(details,
138 MesWmProductSalesDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO); 131 MesWmProductSalesDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO);
139 if (line.getQuantity() != null && totalDetailQty.compareTo(line.getQuantity()) != 0) { 132 if (line.getQuantity() != null && totalDetailQty.compareTo(line.getQuantity()) != 0) {
140 - throw exception(WM_PRODUCT_SALES_DETAIL_QUANTITY_MISMATCH); 133 + return false;
141 } 134 }
142 } 135 }
  136 + return true;
  137 + }
  138 +
  139 + @Override
  140 + @Transactional(rollbackFor = Exception.class)
  141 + public void stockProductSales(Long id) {
  142 + // 校验存在
  143 + MesWmProductSalesDO sales = validateProductSalesExists(id);
  144 + if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.APPROVING.getStatus(), sales.getStatus())) {
  145 + throw exception(WM_PRODUCT_SALES_CANNOT_PICK);
  146 + }
  147 +
  148 + // 执行拣货(待拣货 → 待填写运单)
  149 + productSalesMapper.updateById(new MesWmProductSalesDO()
  150 + .setId(id).setStatus(MesWmProductSalesStatusEnum.SHIPPING.getStatus()));
  151 + }
  152 +
  153 + @Override
  154 + @Transactional(rollbackFor = Exception.class)
  155 + public void shippingProductSales(MesWmProductSalesShippingReqVO reqVO) {
  156 + // 校验存在
  157 + MesWmProductSalesDO sales = validateProductSalesExists(reqVO.getId());
  158 + if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.SHIPPING.getStatus(), sales.getStatus())) {
  159 + throw exception(WM_PRODUCT_SALES_CANNOT_SHIPPING);
  160 + }
143 161
144 - // 执行拣货(待拣货 → 待出库) 162 + // 填写运单(待填写运单 → 待执行出库)
145 productSalesMapper.updateById(new MesWmProductSalesDO() 163 productSalesMapper.updateById(new MesWmProductSalesDO()
146 - .setId(id).setStatus(MesWmProductSalesStatusEnum.APPROVED.getStatus())); 164 + .setId(reqVO.getId())
  165 + .setCarrier(reqVO.getCarrier())
  166 + .setShippingNumber(reqVO.getShippingNumber())
  167 + .setStatus(MesWmProductSalesStatusEnum.APPROVED.getStatus()));
147 } 168 }
148 169
149 @Override 170 @Override