Commit f5e56fd98e76229ebfeb3ea4d6b8381af0fe27a5

Authored by YunaiV
1 parent 1c16ad01

✨ feat(mes): 新增库存记录ID和收货地址字段

在销售出库相关的多个数据对象中新增库存记录ID和收货地址字段,以支持更全面的出库信息管理。这些字段将帮助在出库单中更好地追踪库存和收货信息,提高系统的可用性和用户体验。
Showing 13 changed files with 136 additions and 8 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/MesWmProductSalesController.java
... ... @@ -13,9 +13,15 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProd
13 13 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO;
14 14 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesShippingReqVO;
15 15 import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
  16 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
16 17 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO;
  18 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO;
  19 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO;
17 20 import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
  21 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  22 +import cn.iocoder.yudao.module.mes.service.wm.productsales.MesWmProductSalesLineService;
18 23 import cn.iocoder.yudao.module.mes.service.wm.productsales.MesWmProductSalesService;
  24 +import cn.iocoder.yudao.module.mes.service.wm.salesnotice.MesWmSalesNoticeService;
19 25 import io.swagger.v3.oas.annotations.Operation;
20 26 import io.swagger.v3.oas.annotations.Parameter;
21 27 import io.swagger.v3.oas.annotations.tags.Tag;
... ... @@ -27,12 +33,11 @@ import org.springframework.validation.annotation.Validated;
27 33 import org.springframework.web.bind.annotation.*;
28 34  
29 35 import java.io.IOException;
30   -import java.util.Collections;
31   -import java.util.List;
32   -import java.util.Map;
  36 +import java.util.*;
33 37  
34 38 import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
35 39 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  40 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
36 41 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
37 42  
38 43 @Tag(name = "管理后台 - MES 销售出库单")
... ... @@ -46,6 +51,12 @@ public class MesWmProductSalesController {
46 51  
47 52 @Resource
48 53 private MesMdClientService clientService;
  54 + @Resource
  55 + private MesMdItemService itemService;
  56 + @Resource
  57 + private MesWmProductSalesLineService productSalesLineService;
  58 + @Resource
  59 + private MesWmSalesNoticeService salesNoticeService;
49 60  
50 61 @PostMapping("/create")
51 62 @Operation(summary = "创建销售出库单")
... ... @@ -156,6 +167,35 @@ public class MesWmProductSalesController {
156 167 return success(true);
157 168 }
158 169  
  170 + // TODO @AI:是不是有需要?有需要在提供;
  171 + @GetMapping("/get-item")
  172 + @Operation(summary = "根据客户 ID 获取曾出库的产品清单(去重)")
  173 + @Parameter(name = "clientId", description = "客户 ID", required = true, example = "1")
  174 + @PreAuthorize("@ss.hasPermission('mes:wm-product-sales:query')")
  175 + public CommonResult<List<MesMdItemDO>> getItemByClientId(@RequestParam("clientId") Long clientId) {
  176 + // 1. 查询该客户的所有出库单
  177 + List<MesWmProductSalesDO> salesList = productSalesService.getProductSalesListByClientId(clientId);
  178 + if (CollUtil.isEmpty(salesList)) {
  179 + return success(Collections.emptyList());
  180 + }
  181 + // 2. 查询所有行,提取去重的 itemId
  182 + List<Long> salesIds = convertList(salesList, MesWmProductSalesDO::getId);
  183 + Set<Long> itemIds = new LinkedHashSet<>();
  184 + for (Long salesId : salesIds) {
  185 + List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(salesId);
  186 + for (MesWmProductSalesLineDO line : lines) {
  187 + if (line.getItemId() != null) {
  188 + itemIds.add(line.getItemId());
  189 + }
  190 + }
  191 + }
  192 + if (itemIds.isEmpty()) {
  193 + return success(Collections.emptyList());
  194 + }
  195 + // 3. 返回物料列表
  196 + return success(new ArrayList<>(itemService.getItemMap(itemIds).values()));
  197 + }
  198 +
159 199 // ==================== 拼接 VO ====================
160 200  
161 201 private List<MesWmProductSalesRespVO> buildRespVOList(List<MesWmProductSalesDO> list) {
... ... @@ -165,10 +205,14 @@ public class MesWmProductSalesController {
165 205 // 1. 获得关联数据
166 206 Map<Long, MesMdClientDO> clientMap = clientService.getClientMap(
167 207 convertSet(list, MesWmProductSalesDO::getClientId));
  208 + Map<Long, MesWmSalesNoticeDO> noticeMap = salesNoticeService.getSalesNoticeMap(
  209 + convertSet(list, MesWmProductSalesDO::getNoticeId));
168 210 // 2. 构建结果
169 211 return BeanUtils.toBean(list, MesWmProductSalesRespVO.class, vo -> {
170   - MapUtils.findAndThen(clientMap, vo.getClientId(),
171   - client -> vo.setClientName(client.getName()));
  212 + MapUtils.findAndThen(clientMap, vo.getClientId(), client ->
  213 + vo.setClientName(client.getName()).setClientCode(client.getCode()));
  214 + MapUtils.findAndThen(noticeMap, vo.getNoticeId(),
  215 + notice -> vo.setNoticeCode(notice.getNoticeCode()));
172 216 });
173 217 }
174 218  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesRespVO.java
... ... @@ -58,6 +58,10 @@ public class MesWmProductSalesRespVO {
58 58 @ExcelProperty("联系方式")
59 59 private String contactTelephone;
60 60  
  61 + @Schema(description = "收货地址", example = "北京市朝阳区XX路XX号")
  62 + @ExcelProperty("收货地址")
  63 + private String address;
  64 +
61 65 @Schema(description = "承运商", example = "顺丰快递")
62 66 @ExcelProperty("承运商")
63 67 private String carrier;
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesSaveReqVO.java
... ... @@ -42,6 +42,9 @@ public class MesWmProductSalesSaveReqVO {
42 42 @Schema(description = "联系方式", example = "13800138000")
43 43 private String contactTelephone;
44 44  
  45 + @Schema(description = "收货地址", example = "北京市朝阳区XX路XX号")
  46 + private String address;
  47 +
45 48 @Schema(description = "承运商", example = "顺丰快递")
46 49 private String carrier;
47 50  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/detail/MesWmProductSalesDetailRespVO.java
... ... @@ -37,6 +37,9 @@ public class MesWmProductSalesDetailRespVO {
37 37 @Schema(description = "拣货数量", example = "50")
38 38 private BigDecimal quantity;
39 39  
  40 + @Schema(description = "库存记录ID", example = "1")
  41 + private Long materialStockId;
  42 +
40 43 @Schema(description = "批次ID", example = "1")
41 44 private Long batchId;
42 45  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/detail/MesWmProductSalesDetailSaveReqVO.java
... ... @@ -29,6 +29,9 @@ public class MesWmProductSalesDetailSaveReqVO {
29 29 @NotNull(message = "拣货数量不能为空")
30 30 private BigDecimal quantity;
31 31  
  32 + @Schema(description = "库存记录ID", example = "1")
  33 + private Long materialStockId;
  34 +
32 35 @Schema(description = "批次ID", example = "1")
33 36 private Long batchId;
34 37  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesDO.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO;
5 6 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum;
6 7 import com.baomidou.mybatisplus.annotation.KeySequence;
7 8 import com.baomidou.mybatisplus.annotation.TableId;
... ... @@ -56,7 +57,7 @@ public class MesWmProductSalesDO extends BaseDO {
56 57 /**
57 58 * 发货通知单 ID
58 59 *
59   - * 关联 {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO#getId()}
  60 + * 关联 {@link MesWmSalesNoticeDO#getId()}
60 61 */
61 62 private Long noticeId;
62 63  
... ... @@ -68,6 +69,11 @@ public class MesWmProductSalesDO extends BaseDO {
68 69 * 联系电话
69 70 */
70 71 private String contactTelephone;
  72 + // TODO @AI:改成 contactAddress
  73 + /**
  74 + * 收货地址
  75 + */
  76 + private String address;
71 77  
72 78 /**
73 79 * 承运商
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesDetailDO.java
... ... @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
5 7 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
6 8 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
7 9 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
... ... @@ -55,9 +57,18 @@ public class MesWmProductSalesDetailDO extends BaseDO {
55 57 */
56 58 private BigDecimal quantity;
57 59 /**
58   - * 批次ID
  60 + * 库存记录ID
  61 + *
  62 + * 关联 {@link MesWmMaterialStockDO#getId()}
  63 + */
  64 + private Long materialStockId;
  65 + /**
  66 + * 批次 ID
  67 + *
  68 + * 关联 {@link MesWmBatchDO#getId()}
59 69 */
60 70 private Long batchId;
  71 + // TODO @AI:增加 batchCode 字段
61 72 /**
62 73 * 仓库ID
63 74 *
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -554,6 +554,7 @@ public interface ErrorCodeConstants {
554 554 ErrorCode WM_PRODUCT_SALES_LINE_NOT_EXISTS = new ErrorCode(1_040_714_011, "销售出库单行不存在");
555 555 ErrorCode WM_PRODUCT_SALES_DETAIL_NOT_EXISTS = new ErrorCode(1_040_714_012, "销售出库明细不存在");
556 556 ErrorCode WM_PRODUCT_SALES_STOCK_INSUFFICIENT = new ErrorCode(1_040_714_013, "库存不足,无法拣货");
  557 + ErrorCode WM_PRODUCT_SALES_LINE_QUANTITY_INVALID = new ErrorCode(1_040_714_014, "出库数量必须大于 0");
557 558  
558 559 // ========== MES 仓库管理-杂项出库单(1-040-715-000) ==========
559 560 ErrorCode WM_MISC_ISSUE_NOT_EXISTS = new ErrorCode(1_040_715_000, "杂项出库单不存在");
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java
... ... @@ -31,7 +31,8 @@ public enum MesMdAutoCodeRuleCodeEnum {
31 31 QC_RQC_CODE("QC_RQC_CODE", "退货检验单编码"),
32 32 WM_WAREHOUSE_CODE("WM_WAREHOUSE_CODE", "仓库编码"),
33 33 WM_LOCATION_CODE("WM_LOCATION_CODE", "库区编码"),
34   - WM_AREA_CODE("WM_AREA_CODE", "库位编码");
  34 + WM_AREA_CODE("WM_AREA_CODE", "库位编码"),
  35 + WM_PRODUCT_SALES_CODE("WM_PRODUCT_SALES_CODE", "销售出库单编码");
35 36  
36 37 /**
37 38 * 规则代码
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineService.java
... ... @@ -77,4 +77,12 @@ public interface MesWmProductSalesLineService {
77 77 */
78 78 void updateProductSalesLineWhenOqcFinish(Long id, Long oqcId, Integer checkResult);
79 79  
  80 + /**
  81 + * 更新行的质量状态
  82 + *
  83 + * @param id 行 ID
  84 + * @param qualityStatus 质量状态
  85 + */
  86 + void updateProductSalesLineQualityStatus(Long id, Integer qualityStatus);
  87 +
80 88 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineServiceImpl.java
... ... @@ -154,5 +154,11 @@ public class MesWmProductSalesLineServiceImpl implements MesWmProductSalesLineSe
154 154 }
155 155 }
156 156  
  157 + @Override
  158 + public void updateProductSalesLineQualityStatus(Long id, Integer qualityStatus) {
  159 + productSalesLineMapper.updateById(new MesWmProductSalesLineDO()
  160 + .setId(id).setQualityStatus(qualityStatus));
  161 + }
  162 +
157 163 }
158 164  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
... ... @@ -15,6 +15,7 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSa
15 15 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesMapper;
16 16 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
17 17 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum;
  18 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
18 19 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
19 20 import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
20 21 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
... ... @@ -57,6 +58,7 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
57 58  
58 59 @Override
59 60 public Long createProductSales(MesWmProductSalesSaveReqVO createReqVO) {
  61 + // TODO @AI:增加 validateXXXSaveData;
60 62 // 校验编码唯一
61 63 validateCodeUnique(null, createReqVO.getCode());
62 64 // 校验客户存在
... ... @@ -73,6 +75,7 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
73 75 public void updateProductSales(MesWmProductSalesSaveReqVO updateReqVO) {
74 76 // 校验存在 + 草稿状态
75 77 validateProductSalesExistsAndDraft(updateReqVO.getId());
  78 + // TODO @AI:增加 validateXXXSaveData;(校验数据)
76 79 // 校验编码唯一
77 80 validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
78 81 // 校验客户存在
... ... @@ -116,11 +119,25 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
116 119 if (CollUtil.isEmpty(lines)) {
117 120 throw exception(WM_PRODUCT_SALES_LINES_EMPTY);
118 121 }
  122 + // 校验每行出库数量 > 0
  123 + for (MesWmProductSalesLineDO line : lines) {
  124 + if (line.getQuantity() == null || BigDecimal.ZERO.compareTo(line.getQuantity()) >= 0) {
  125 + throw exception(WM_PRODUCT_SALES_LINE_QUANTITY_INVALID);
  126 + }
  127 + }
119 128  
120 129 // 检查所有行的 oqcCheckFlag:如果有需要 OQC 检验的行,进入待检测状态
121 130 boolean needOqc = CollectionUtils.anyMatch(lines,
122 131 line -> Boolean.TRUE.equals(line.getOqcCheckFlag()));
123 132 if (needOqc) {
  133 + // 初始化 OQC 行的质量状态为"待检验"
  134 + // TODO @AI:改成批量更新接口?productSalesLineService 提供一个?【对齐:这个是否为必须?!】
  135 + for (MesWmProductSalesLineDO line : lines) {
  136 + if (Boolean.TRUE.equals(line.getOqcCheckFlag())) {
  137 + productSalesLineService.updateProductSalesLineQualityStatus(
  138 + line.getId(), MesWmQualityStatusEnum.PENDING.getStatus());
  139 + }
  140 + }
124 141 // 需要检验,进入待检测
125 142 productSalesMapper.updateById(new MesWmProductSalesDO()
126 143 .setId(id).setStatus(MesWmProductSalesStatusEnum.CONFIRMED.getStatus()));
... ... @@ -157,6 +174,14 @@ public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
157 174 if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.APPROVING.getStatus(), sales.getStatus())) {
158 175 throw exception(WM_PRODUCT_SALES_CANNOT_PICK);
159 176 }
  177 + // 校验每行至少有拣货明细
  178 + List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(id);
  179 + for (MesWmProductSalesLineDO line : lines) {
  180 + List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListByLineId(line.getId());
  181 + if (CollUtil.isEmpty(details)) {
  182 + throw exception(WM_PRODUCT_SALES_DETAILS_EMPTY);
  183 + }
  184 + }
160 185  
161 186 // 执行拣货(待拣货 → 待填写运单)
162 187 productSalesMapper.updateById(new MesWmProductSalesDO()
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeService.java
... ... @@ -8,6 +8,9 @@ import jakarta.validation.Valid;
8 8  
9 9 import java.util.Collection;
10 10 import java.util.List;
  11 +import java.util.Map;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
11 14  
12 15 /**
13 16 * MES 发货通知单 Service 接口
... ... @@ -68,6 +71,16 @@ public interface MesWmSalesNoticeService {
68 71 List<MesWmSalesNoticeDO> getSalesNoticeList(Collection<Long> ids);
69 72  
70 73 /**
  74 + * 获得发货通知单 Map
  75 + *
  76 + * @param ids 编号列表
  77 + * @return 发货通知单 Map
  78 + */
  79 + default Map<Long, MesWmSalesNoticeDO> getSalesNoticeMap(Collection<Long> ids) {
  80 + return convertMap(getSalesNoticeList(ids), MesWmSalesNoticeDO::getId);
  81 + }
  82 +
  83 + /**
71 84 * 按状态获得发货通知单列表
72 85 *
73 86 * @param status 状态
... ...