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 11 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO;
12 12 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesRespVO;
13 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 15 import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
15 16 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO;
16 17 import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
... ... @@ -112,12 +113,28 @@ public class MesWmProductSalesController {
112 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 125 @Operation(summary = "执行拣货")
117 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 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 433 ErrorCode WM_PRODUCT_SALES_LINES_EMPTY = new ErrorCode(1_040_714_003, "销售出库单行不能为空");
434 434 ErrorCode WM_PRODUCT_SALES_CANNOT_SUBMIT = new ErrorCode(1_040_714_004, "当前状态不允许提交");
435 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 445 // ========== MES 仓库管理-发货通知单(1-040-720-000) ==========
445 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 2  
3 3 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4 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 6 import cn.iocoder.yudao.module.mes.enums.MesOrderStatusConstants;
6 7 import cn.iocoder.yudao.module.mes.service.wm.productsales.MesWmProductSalesService;
7 8 import lombok.AllArgsConstructor;
... ... @@ -20,8 +21,6 @@ import java.util.Arrays;
20 21 @AllArgsConstructor
21 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 33 * 对应方法:{@link MesWmProductSalesService#submitProductSales(Long)}
35 34 */
36 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 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 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO;
5 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 7 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO;
7 8 import jakarta.validation.Valid;
8 9  
... ... @@ -59,11 +60,26 @@ public interface MesWmProductSalesService {
59 60 void submitProductSales(Long id);
60 61  
61 62 /**
62   - * 执行拣货(待拣货 → 待出库)
  63 + * 校验销售出库单数量是否匹配
63 64 *
64 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 8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 9 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO;
10 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 12 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO;
12 13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO;
13 14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO;
... ... @@ -117,33 +118,53 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
117 118 .setId(id).setStatus(MesWmProductSalesStatusEnum.APPROVING.getStatus()));
118 119 }
119 120  
120   - // TODO @AI:前后端方法名,都改成 stock;保持整体都是对齐的;
121 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 124 List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(id);
132 125 for (MesWmProductSalesLineDO line : lines) {
133 126 List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListByLineId(line.getId());
134 127 if (CollUtil.isEmpty(details)) {
135   - throw exception(WM_PRODUCT_SALES_DETAILS_EMPTY);
  128 + return false;
136 129 }
137 130 BigDecimal totalDetailQty = CollectionUtils.getSumValue(details,
138 131 MesWmProductSalesDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO);
139 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 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 170 @Override
... ...