Commit 0c3c271c54189712d22fe10de8efed869c47f4c6
1 parent
3cb4adf5
✨ feat(sales): 新增发货通知单行ID字段及相关校验逻辑
在销售出库单行中新增发货通知单行ID字段,并实现相关的校验逻辑,以确保出库单与发货通知单行之间的关联性和一致性。此变更提升了系统的完整性和数据一致性。
Showing
8 changed files
with
106 additions
and
3 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/line/MesWmProductSalesLineRespVO.java
| ... | ... | @@ -16,6 +16,9 @@ public class MesWmProductSalesLineRespVO { |
| 16 | 16 | @Schema(description = "出库单ID", example = "1") |
| 17 | 17 | private Long salesId; |
| 18 | 18 | |
| 19 | + @Schema(description = "发货通知单行ID", example = "1") | |
| 20 | + private Long noticeLineId; | |
| 21 | + | |
| 19 | 22 | @Schema(description = "物料ID", example = "1") |
| 20 | 23 | private Long itemId; |
| 21 | 24 | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/line/MesWmProductSalesLineSaveReqVO.java
| ... | ... | @@ -17,6 +17,9 @@ public class MesWmProductSalesLineSaveReqVO { |
| 17 | 17 | @NotNull(message = "出库单ID不能为空") |
| 18 | 18 | private Long salesId; |
| 19 | 19 | |
| 20 | + @Schema(description = "发货通知单行ID", example = "1") | |
| 21 | + private Long noticeLineId; | |
| 22 | + | |
| 20 | 23 | @Schema(description = "物料ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 21 | 24 | @NotNull(message = "物料不能为空") |
| 22 | 25 | private Long itemId; | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesLineDO.java
| ... | ... | @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| 5 | 5 | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO; |
| 6 | 6 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| 7 | 7 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO; |
| 8 | +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeLineDO; | |
| 8 | 9 | import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; |
| 9 | 10 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 10 | 11 | import com.baomidou.mybatisplus.annotation.TableId; |
| ... | ... | @@ -40,6 +41,12 @@ public class MesWmProductSalesLineDO extends BaseDO { |
| 40 | 41 | */ |
| 41 | 42 | private Long salesId; |
| 42 | 43 | /** |
| 44 | + * 发货通知单行ID | |
| 45 | + * | |
| 46 | + * 关联 {@link MesWmSalesNoticeLineDO#getId()} | |
| 47 | + */ | |
| 48 | + private Long noticeLineId; | |
| 49 | + /** | |
| 43 | 50 | * 物料ID |
| 44 | 51 | * |
| 45 | 52 | * 关联 {@link MesMdItemDO#getId()} | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
| ... | ... | @@ -619,6 +619,18 @@ public interface ErrorCodeConstants { |
| 619 | 619 | ErrorCode WM_PRODUCT_SALES_LINE_QUANTITY_INVALID = new ErrorCode(1_040_714_014, "出库数量必须大于 0"); |
| 620 | 620 | ErrorCode WM_PRODUCT_SALES_DETAIL_LINE_NOT_MATCH = new ErrorCode(1_040_714_015, "拣货明细不属于指定的销售出库单"); |
| 621 | 621 | ErrorCode WM_PRODUCT_SALES_DETAIL_ITEM_MISMATCH = new ErrorCode(1_040_714_016, "拣货明细的物料与销售出库单行的物料不一致"); |
| 622 | + ErrorCode WM_PRODUCT_SALES_LINE_SALES_NOTICE_LINE_REQUIRED = new ErrorCode(1_040_714_017, | |
| 623 | + "出库单关联了发货通知单,必须选择发货通知单行"); | |
| 624 | + ErrorCode WM_PRODUCT_SALES_LINE_SALES_NOTICE_LINE_NOT_ALLOWED = new ErrorCode(1_040_714_018, | |
| 625 | + "出库单未关联发货通知单,不能选择发货通知单行"); | |
| 626 | + ErrorCode WM_PRODUCT_SALES_LINE_NOTICE_LINE_ITEM_MISMATCH = new ErrorCode(1_040_714_030, | |
| 627 | + "出库行物料与发货通知单行物料不一致"); | |
| 628 | + ErrorCode WM_PRODUCT_SALES_LINE_NOTICE_LINE_QUANTITY_MISMATCH = new ErrorCode(1_040_714_031, | |
| 629 | + "出库行数量与发货通知单行数量不一致"); | |
| 630 | + ErrorCode WM_PRODUCT_SALES_LINE_NOTICE_LINE_BATCH_MISMATCH = new ErrorCode(1_040_714_032, | |
| 631 | + "出库行批次号与发货通知单行批次号不一致"); | |
| 632 | + ErrorCode WM_PRODUCT_SALES_LINE_NOTICE_LINE_OQC_MISMATCH = new ErrorCode(1_040_714_033, | |
| 633 | + "出库行 OQC 检验标识与发货通知单行不一致"); | |
| 622 | 634 | |
| 623 | 635 | // ========== MES 仓库管理-杂项出库单(1-040-715-000) ========== |
| 624 | 636 | ErrorCode WM_MISC_ISSUE_NOT_EXISTS = new ErrorCode(1_040_715_000, "杂项出库单不存在"); |
| ... | ... | @@ -651,6 +663,7 @@ public interface ErrorCodeConstants { |
| 651 | 663 | ErrorCode WM_SALES_NOTICE_CLIENT_MISMATCH = new ErrorCode(1_040_720_005, "发货通知单的客户与当前单据不一致"); |
| 652 | 664 | ErrorCode WM_SALES_NOTICE_LINE_NOT_EXISTS = new ErrorCode(1_040_720_010, "发货通知单行不存在"); |
| 653 | 665 | ErrorCode WM_SALES_NOTICE_LINE_EMPTY = new ErrorCode(1_040_720_011, "发货通知单行为空,不能提交"); |
| 666 | + ErrorCode WM_SALES_NOTICE_LINE_NOT_MATCH = new ErrorCode(1_040_720_012, "发货通知单行不属于指定的发货通知单"); | |
| 654 | 667 | |
| 655 | 668 | // ========== MES 仓库管理-条码配置(1-040-730-000) ========== |
| 656 | 669 | ErrorCode WM_BARCODE_CONFIG_NOT_EXISTS = new ErrorCode(1_040_730_000, "条码配置不存在"); | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineServiceImpl.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mes.service.wm.productsales; |
| 3 | 3 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 4 | 4 | import cn.hutool.core.collection.CollUtil; |
| 5 | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | +import cn.hutool.core.util.ObjUtil; | |
| 6 | 7 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 7 | 8 | import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.line.MesWmProductSalesLinePageReqVO; |
| 8 | 9 | import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.line.MesWmProductSalesLineSaveReqVO; |
| ... | ... | @@ -14,6 +15,8 @@ import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; |
| 14 | 15 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| 15 | 16 | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| 16 | 17 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| 18 | +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeLineDO; | |
| 19 | +import cn.iocoder.yudao.module.mes.service.wm.salesnotice.MesWmSalesNoticeLineService; | |
| 17 | 20 | import jakarta.annotation.Resource; |
| 18 | 21 | import lombok.extern.slf4j.Slf4j; |
| 19 | 22 | import org.springframework.context.annotation.Lazy; |
| ... | ... | @@ -26,7 +29,7 @@ import java.util.Objects; |
| 26 | 29 | |
| 27 | 30 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 28 | 31 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 29 | -import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_PRODUCT_SALES_LINE_NOT_EXISTS; | |
| 32 | +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; | |
| 30 | 33 | |
| 31 | 34 | /** |
| 32 | 35 | * MES 销售出库单行 Service 实现类 |
| ... | ... | @@ -49,6 +52,9 @@ public class MesWmProductSalesLineServiceImpl implements MesWmProductSalesLineSe |
| 49 | 52 | @Resource |
| 50 | 53 | @Lazy |
| 51 | 54 | private MesWmProductSalesService productSalesService; |
| 55 | + @Resource | |
| 56 | + @Lazy | |
| 57 | + private MesWmSalesNoticeLineService salesNoticeLineService; | |
| 52 | 58 | |
| 53 | 59 | @Override |
| 54 | 60 | public Long createProductSalesLine(MesWmProductSalesLineSaveReqVO createReqVO) { |
| ... | ... | @@ -143,13 +149,55 @@ public class MesWmProductSalesLineServiceImpl implements MesWmProductSalesLineSe |
| 143 | 149 | */ |
| 144 | 150 | private void validateLineSaveData(MesWmProductSalesLineSaveReqVO reqVO) { |
| 145 | 151 | // 校验主单存在且为草稿状态 |
| 146 | - productSalesService.validateProductSalesExistsAndDraft(reqVO.getSalesId()); | |
| 152 | + MesWmProductSalesDO sales = productSalesService.validateProductSalesExistsAndDraft(reqVO.getSalesId()); | |
| 147 | 153 | // 校验物料存在 |
| 148 | 154 | itemService.validateItemExistsAndEnable(reqVO.getItemId()); |
| 155 | + // 校验关联发货通知单行(存在性 + 归属 + 字段一致性) | |
| 156 | + validateSalesNoticeLine(sales, reqVO); | |
| 149 | 157 | // 根据 batchCode 解析 batchId |
| 150 | 158 | fillBatchId(reqVO); |
| 151 | 159 | } |
| 152 | 160 | |
| 161 | + /** | |
| 162 | + * 校验发货通知单行 | |
| 163 | + * | |
| 164 | + * @param sales 出库单 | |
| 165 | + * @param reqVO 出库行请求 | |
| 166 | + */ | |
| 167 | + private void validateSalesNoticeLine(MesWmProductSalesDO sales, MesWmProductSalesLineSaveReqVO reqVO) { | |
| 168 | + Long noticeLineId = reqVO.getNoticeLineId(); | |
| 169 | + // 情况一:如果出库单关联了发货通知单,则必须关联发货通知单行 | |
| 170 | + if (sales.getNoticeId() != null) { | |
| 171 | + if (noticeLineId == null) { | |
| 172 | + throw exception(WM_PRODUCT_SALES_LINE_SALES_NOTICE_LINE_REQUIRED); | |
| 173 | + } | |
| 174 | + MesWmSalesNoticeLineDO noticeLine = salesNoticeLineService.validateSalesNoticeLineExists( | |
| 175 | + noticeLineId, sales.getNoticeId()); | |
| 176 | + // 校验关键字段一致性:物料、数量、批次号、OQC 检验标识 | |
| 177 | + if (ObjUtil.notEqual(reqVO.getItemId(), noticeLine.getItemId())) { | |
| 178 | + throw exception(WM_PRODUCT_SALES_LINE_NOTICE_LINE_ITEM_MISMATCH); | |
| 179 | + } | |
| 180 | + if (noticeLine.getQuantity() != null && reqVO.getQuantity() != null | |
| 181 | + && reqVO.getQuantity().compareTo(noticeLine.getQuantity()) != 0) { | |
| 182 | + throw exception(WM_PRODUCT_SALES_LINE_NOTICE_LINE_QUANTITY_MISMATCH); | |
| 183 | + } | |
| 184 | + if (StrUtil.isNotBlank(noticeLine.getBatchCode()) | |
| 185 | + && ObjUtil.notEqual(reqVO.getBatchCode(), noticeLine.getBatchCode())) { | |
| 186 | + throw exception(WM_PRODUCT_SALES_LINE_NOTICE_LINE_BATCH_MISMATCH); | |
| 187 | + } | |
| 188 | + if (noticeLine.getOqcCheckFlag() != null | |
| 189 | + && ObjUtil.notEqual(reqVO.getOqcCheckFlag(), noticeLine.getOqcCheckFlag())) { | |
| 190 | + throw exception(WM_PRODUCT_SALES_LINE_NOTICE_LINE_OQC_MISMATCH); | |
| 191 | + } | |
| 192 | + return; | |
| 193 | + } | |
| 194 | + | |
| 195 | + // 情况二:如果出库单没有关联发货通知单,则不允许关联发货通知单行 | |
| 196 | + if (noticeLineId != null) { | |
| 197 | + throw exception(WM_PRODUCT_SALES_LINE_SALES_NOTICE_LINE_NOT_ALLOWED); | |
| 198 | + } | |
| 199 | + } | |
| 200 | + | |
| 153 | 201 | @Override |
| 154 | 202 | @Transactional(rollbackFor = Exception.class) |
| 155 | 203 | public void updateProductSalesLineWhenOqcFinish(Long id, Long oqcId, Integer checkResult) { | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeLineService.java
| ... | ... | @@ -66,4 +66,21 @@ public interface MesWmSalesNoticeLineService { |
| 66 | 66 | */ |
| 67 | 67 | void deleteSalesNoticeLineByNoticeId(Long noticeId); |
| 68 | 68 | |
| 69 | + /** | |
| 70 | + * 校验发货通知单行存在 | |
| 71 | + * | |
| 72 | + * @param id 行编号 | |
| 73 | + * @return 发货通知单行 | |
| 74 | + */ | |
| 75 | + MesWmSalesNoticeLineDO validateSalesNoticeLineExists(Long id); | |
| 76 | + | |
| 77 | + /** | |
| 78 | + * 校验发货通知单行存在,并且属于指定的通知单 | |
| 79 | + * | |
| 80 | + * @param lineId 行编号 | |
| 81 | + * @param noticeId 通知单编号 | |
| 82 | + * @return 发货通知单行 | |
| 83 | + */ | |
| 84 | + MesWmSalesNoticeLineDO validateSalesNoticeLineExists(Long lineId, Long noticeId); | |
| 85 | + | |
| 69 | 86 | } | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeLineServiceImpl.java
| ... | ... | @@ -100,7 +100,8 @@ public class MesWmSalesNoticeLineServiceImpl implements MesWmSalesNoticeLineServ |
| 100 | 100 | salesNoticeLineMapper.deleteByNoticeId(noticeId); |
| 101 | 101 | } |
| 102 | 102 | |
| 103 | - private MesWmSalesNoticeLineDO validateSalesNoticeLineExists(Long id) { | |
| 103 | + @Override | |
| 104 | + public MesWmSalesNoticeLineDO validateSalesNoticeLineExists(Long id) { | |
| 104 | 105 | MesWmSalesNoticeLineDO line = salesNoticeLineMapper.selectById(id); |
| 105 | 106 | if (line == null) { |
| 106 | 107 | throw exception(WM_SALES_NOTICE_LINE_NOT_EXISTS); |
| ... | ... | @@ -108,6 +109,16 @@ public class MesWmSalesNoticeLineServiceImpl implements MesWmSalesNoticeLineServ |
| 108 | 109 | return line; |
| 109 | 110 | } |
| 110 | 111 | |
| 112 | + @Override | |
| 113 | + public MesWmSalesNoticeLineDO validateSalesNoticeLineExists(Long lineId, Long noticeId) { | |
| 114 | + MesWmSalesNoticeLineDO line = validateSalesNoticeLineExists(lineId); | |
| 115 | + // 进一步校验行的 noticeId 与传入的 noticeId 是否匹配 | |
| 116 | + if (noticeId != null && ObjUtil.notEqual(line.getNoticeId(), noticeId)) { | |
| 117 | + throw exception(WM_SALES_NOTICE_LINE_NOT_MATCH); | |
| 118 | + } | |
| 119 | + return line; | |
| 120 | + } | |
| 121 | + | |
| 111 | 122 | private void validateLineSaveData(MesWmSalesNoticeLineSaveReqVO reqVO) { |
| 112 | 123 | // 校验父单据存在且为草稿状态 |
| 113 | 124 | validateNoticeStatusDraft(reqVO.getNoticeId()); | ... | ... |
yudao-module-mes/src/test/resources/sql/create_tables.sql
| ... | ... | @@ -387,6 +387,7 @@ CREATE TABLE IF NOT EXISTS "mes_qc_rqc" ( |
| 387 | 387 | CREATE TABLE IF NOT EXISTS "mes_wm_product_sales_line" ( |
| 388 | 388 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 389 | 389 | "sales_id" bigint NOT NULL, |
| 390 | + "notice_line_id" bigint DEFAULT NULL, | |
| 390 | 391 | "item_id" bigint NOT NULL, |
| 391 | 392 | "quantity" decimal(20,6) NOT NULL, |
| 392 | 393 | "batch_id" bigint DEFAULT NULL, | ... | ... |