Commit 2e113305a4cdf3adba124b1ddf5a4eced1b50f79

Authored by YunaiV
1 parent b66e963a

✨ feat(mes): 添加外协入库单审批逻辑,支持 IQC 完成后状态更新

新增外协入库单的审批功能,允许在 IQC 检验完成后更新入库单状态,并处理合格与不合格品的数量。此功能提升了外协入库单的管理效率,确保了数据的准确性和一致性。
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java
... ... @@ -20,6 +20,7 @@ import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
20 20 import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
21 21 import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateDetailService;
22 22 import cn.iocoder.yudao.module.mes.service.wm.arrivalnotice.MesWmArrivalNoticeService;
  23 +import cn.iocoder.yudao.module.mes.service.wm.outsourcereceipt.MesWmOutsourceReceiptService;
23 24 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
24 25 import jakarta.annotation.Resource;
25 26 import org.springframework.stereotype.Service;
... ... @@ -56,6 +57,8 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
56 57 @Resource
57 58 private MesWmArrivalNoticeService arrivalNoticeService;
58 59 @Resource
  60 + private MesWmOutsourceReceiptService outsourceReceiptService;
  61 + @Resource
59 62 private MesMdVendorService vendorService;
60 63 @Resource
61 64 private MesMdItemService itemService;
... ... @@ -147,8 +150,10 @@ public class MesQcIqcServiceImpl implements MesQcIqcService {
147 150 if (Objects.equals(iqc.getSourceDocType(), MesBizTypeConstants.WM_ARRIVAL_NOTICE)) {
148 151 arrivalNoticeService.approveArrivalNoticeWhenIqcComplete(iqc.getSourceDocId(), iqc.getSourceLineId(),
149 152 iqc.getId(), iqc.getQualifiedQuantity());
  153 + } else if (Objects.equals(iqc.getSourceDocType(), MesBizTypeConstants.WM_OUTSOURCE_RECPT)) {
  154 + outsourceReceiptService.approveOutsourceReceiptWhenIqcComplete(iqc.getSourceDocId(), iqc.getSourceLineId(),
  155 + iqc.getId(), iqc.getQualifiedQuantity(), iqc.getUnqualifiedQuantity());
150 156 }
151   - // TODO 外协入库单:未实现,占位
152 157 }
153 158  
154 159 @Override
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptLineService.java
... ... @@ -51,10 +51,32 @@ public interface MesWmOutsourceReceiptLineService {
51 51 PageResult<MesWmOutsourceReceiptLineDO> getOutsourceReceiptLinePage(MesWmOutsourceReceiptLinePageReqVO pageReqVO);
52 52  
53 53 /**
54   - * 删除委外收货单行(根据收货单编号)
  54 + * 获得外协入库单行列表(根据入库单编号)
55 55 *
56   - * @param receiptId 收货单编号
  56 + * @param receiptId 入库单编号
  57 + * @return 外协入库单行列表
  58 + */
  59 + List<MesWmOutsourceReceiptLineDO> getOutsourceReceiptLineListByReceiptId(Long receiptId);
  60 +
  61 + /**
  62 + * 删除外协入库单行(根据入库单编号)
  63 + *
  64 + * @param receiptId 入库单编号
57 65 */
58 66 void deleteOutsourceReceiptLineByReceiptId(Long receiptId);
59 67  
  68 + /**
  69 + * 直接更新外协入库单行 DO(用于 IQC 回写)
  70 + *
  71 + * @param line 行 DO
  72 + */
  73 + void updateOutsourceReceiptLineDO(MesWmOutsourceReceiptLineDO line);
  74 +
  75 + /**
  76 + * 直接插入外协入库单行 DO(用于 QC 按质量拆分行)
  77 + *
  78 + * @param line 行 DO
  79 + */
  80 + void createOutsourceReceiptLineDO(MesWmOutsourceReceiptLineDO line);
  81 +
60 82 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptLineServiceImpl.java
... ... @@ -12,6 +12,8 @@ import jakarta.annotation.Resource;
12 12 import org.springframework.stereotype.Service;
13 13 import org.springframework.validation.annotation.Validated;
14 14  
  15 +import java.util.List;
  16 +
15 17 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
16 18 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_OUTSOURCE_RECEIPT_LINE_NOT_EXISTS;
17 19  
... ... @@ -82,6 +84,11 @@ public class MesWmOutsourceReceiptLineServiceImpl implements MesWmOutsourceRecei
82 84 }
83 85  
84 86 @Override
  87 + public List<MesWmOutsourceReceiptLineDO> getOutsourceReceiptLineListByReceiptId(Long receiptId) {
  88 + return lineMapper.selectListByReceiptId(receiptId);
  89 + }
  90 +
  91 + @Override
85 92 public void deleteOutsourceReceiptLineByReceiptId(Long receiptId) {
86 93 lineMapper.deleteByReceiptId(receiptId);
87 94 }
... ... @@ -92,4 +99,14 @@ public class MesWmOutsourceReceiptLineServiceImpl implements MesWmOutsourceRecei
92 99 }
93 100 }
94 101  
  102 + @Override
  103 + public void updateOutsourceReceiptLineDO(MesWmOutsourceReceiptLineDO line) {
  104 + lineMapper.updateById(line);
  105 + }
  106 +
  107 + @Override
  108 + public void createOutsourceReceiptLineDO(MesWmOutsourceReceiptLineDO line) {
  109 + lineMapper.insert(line);
  110 + }
  111 +
95 112 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptService.java
... ... @@ -6,6 +6,8 @@ import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.MesWm
6 6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptDO;
7 7 import jakarta.validation.Valid;
8 8  
  9 +import java.math.BigDecimal;
  10 +
9 11 /**
10 12 * MES 外协入库单 Service 接口
11 13 */
... ... @@ -77,4 +79,16 @@ public interface MesWmOutsourceReceiptService {
77 79 */
78 80 void cancelOutsourceReceipt(Long id);
79 81  
  82 + /**
  83 + * IQC 检验完成后回写外协入库单(拆分行 + 推进主表状态)
  84 + *
  85 + * @param receiptId 外协入库单编号
  86 + * @param lineId 入库单行编号
  87 + * @param iqcId IQC 检验单编号
  88 + * @param qualifiedQuantity 合格品数量
  89 + * @param unqualifiedQuantity 不合格品数量
  90 + */
  91 + void approveOutsourceReceiptWhenIqcComplete(Long receiptId, Long lineId, Long iqcId,
  92 + BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity);
  93 +
80 94 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImpl.java
... ... @@ -6,14 +6,13 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
7 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9   -import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
10 9 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.MesWmOutsourceReceiptPageReqVO;
11 10 import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.MesWmOutsourceReceiptSaveReqVO;
  11 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
12 12 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptDO;
13 13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptDetailDO;
14 14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptLineDO;
15   -import cn.iocoder.yudao.module.mes.dal.mysql.wm.outsourcereceipt.MesWmOutsourceReceiptDetailMapper;
16   -import cn.iocoder.yudao.module.mes.dal.mysql.wm.outsourcereceipt.MesWmOutsourceReceiptLineMapper;
  15 +
17 16 import cn.iocoder.yudao.module.mes.dal.mysql.wm.outsourcereceipt.MesWmOutsourceReceiptMapper;
18 17 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
19 18 import cn.iocoder.yudao.module.mes.enums.wm.MesWmOutsourceReceiptStatusEnum;
... ... @@ -23,7 +22,6 @@ import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
23 22 import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
24 23 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
25 24 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
26   -import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
27 25 import jakarta.annotation.Resource;
28 26 import org.springframework.stereotype.Service;
29 27 import org.springframework.transaction.annotation.Transactional;
... ... @@ -48,23 +46,16 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe
48 46 private MesWmOutsourceReceiptMapper outsourceReceiptMapper;
49 47  
50 48 @Resource
51   - private MesWmOutsourceReceiptLineMapper outsourceReceiptLineMapper;
52   -
  49 + private MesWmOutsourceReceiptLineService outsourceReceiptLineService;
53 50 @Resource
54   - private MesWmOutsourceReceiptDetailMapper outsourceReceiptDetailMapper;
55   -
  51 + private MesWmOutsourceReceiptDetailService outsourceReceiptDetailService;
56 52 @Resource
57 53 private MesWmTransactionService wmTransactionService;
58   -
59 54 @Resource
60 55 private MesMdVendorService vendorService;
61   -
62 56 @Resource
63 57 private MesMdItemService itemService;
64 58  
65   - @Resource
66   - private MesWmWarehouseAreaService warehouseAreaService;
67   -
68 59 @Override
69 60 public Long createOutsourceReceipt(MesWmOutsourceReceiptSaveReqVO createReqVO) {
70 61 // 校验编码唯一
... ... @@ -100,8 +91,8 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe
100 91 validateOutsourceReceiptExistsAndDraft(id);
101 92  
102 93 // 级联删除明细和行
103   - outsourceReceiptDetailMapper.deleteByReceiptId(id);
104   - outsourceReceiptLineMapper.deleteByReceiptId(id);
  94 + outsourceReceiptDetailService.deleteOutsourceReceiptDetailByReceiptId(id);
  95 + outsourceReceiptLineService.deleteOutsourceReceiptLineByReceiptId(id);
105 96 // 删除
106 97 outsourceReceiptMapper.deleteById(id);
107 98 }
... ... @@ -122,7 +113,7 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe
122 113 // 校验存在 + 草稿状态
123 114 validateOutsourceReceiptExistsAndDraft(id);
124 115 // 校验至少有一条行
125   - List<MesWmOutsourceReceiptLineDO> lines = outsourceReceiptLineMapper.selectListByReceiptId(id);
  116 + List<MesWmOutsourceReceiptLineDO> lines = outsourceReceiptLineService.getOutsourceReceiptLineListByReceiptId(id);
126 117 if (CollUtil.isEmpty(lines)) {
127 118 throw exception(WM_OUTSOURCE_RECEIPT_NO_LINE);
128 119 }
... ... @@ -146,10 +137,10 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe
146 137 throw exception(WM_OUTSOURCE_RECEIPT_STATUS_ERROR);
147 138 }
148 139 // 1.2 检查每个行的明细数量是否完成上架
149   - List<MesWmOutsourceReceiptLineDO> lines = outsourceReceiptLineMapper.selectListByReceiptId(id);
  140 + List<MesWmOutsourceReceiptLineDO> lines = outsourceReceiptLineService.getOutsourceReceiptLineListByReceiptId(id);
150 141 if (CollUtil.isNotEmpty(lines)) {
151 142 // 批量查询所有明细
152   - List<MesWmOutsourceReceiptDetailDO> allDetails = outsourceReceiptDetailMapper.selectListByReceiptId(id);
  143 + List<MesWmOutsourceReceiptDetailDO> allDetails = outsourceReceiptDetailService.getOutsourceReceiptDetailListByReceiptId(id);
153 144 Map<Long, List<MesWmOutsourceReceiptDetailDO>> detailMap = CollectionUtils.convertMultiMap(
154 145 allDetails, MesWmOutsourceReceiptDetailDO::getLineId);
155 146 // 检查每行的明细数量
... ... @@ -189,7 +180,7 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe
189 180 }
190 181  
191 182 private void createTransactionList(MesWmOutsourceReceiptDO receipt) {
192   - List<MesWmOutsourceReceiptDetailDO> details = outsourceReceiptDetailMapper.selectListByReceiptId(receipt.getId());
  183 + List<MesWmOutsourceReceiptDetailDO> details = outsourceReceiptDetailService.getOutsourceReceiptDetailListByReceiptId(receipt.getId());
193 184 wmTransactionService.createTransactionList(convertList(details, detail -> new MesWmTransactionSaveReqDTO()
194 185 .setType(MesWmTransactionTypeEnum.IN.getType()).setItemId(detail.getItemId())
195 186 .setQuantity(detail.getQuantity()) // 入库数量为正数
... ... @@ -216,6 +207,41 @@ public class MesWmOutsourceReceiptServiceImpl implements MesWmOutsourceReceiptSe
216 207 .setId(id).setStatus(MesWmOutsourceReceiptStatusEnum.CANCELED.getStatus()));
217 208 }
218 209  
  210 + @Override
  211 + @Transactional(rollbackFor = Exception.class)
  212 + public void approveOutsourceReceiptWhenIqcComplete(Long receiptId, Long lineId, Long iqcId,
  213 + BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity) {
  214 + // 1.1 校验入库单存在
  215 + MesWmOutsourceReceiptDO receipt = validateOutsourceReceiptExists(receiptId);
  216 + // 1.2 校验状态为待检验
  217 + if (ObjUtil.notEqual(MesWmOutsourceReceiptStatusEnum.CONFIRMED.getStatus(), receipt.getStatus())) {
  218 + throw exception(WM_OUTSOURCE_RECEIPT_STATUS_ERROR);
  219 + }
  220 + // 1.3 校验行存在
  221 + MesWmOutsourceReceiptLineDO line = outsourceReceiptLineService.getOutsourceReceiptLine(lineId);
  222 + if (line == null) {
  223 + throw exception(WM_OUTSOURCE_RECEIPT_LINE_NOT_EXISTS);
  224 + }
  225 +
  226 + // 2.1 合格品处理:更新原行(new 一个 DO,避免覆盖更新)
  227 + if (qualifiedQuantity != null && qualifiedQuantity.compareTo(BigDecimal.ZERO) > 0) {
  228 + outsourceReceiptLineService.updateOutsourceReceiptLineDO(new MesWmOutsourceReceiptLineDO()
  229 + .setId(lineId).setQualityStatus(MesWmQualityStatusEnum.PASS.getStatus())
  230 + .setIqcId(iqcId).setQuantity(qualifiedQuantity));
  231 + }
  232 + // 2.2 不合格品处理:拷贝原行全部属性,新增一行
  233 + if (unqualifiedQuantity != null && unqualifiedQuantity.compareTo(BigDecimal.ZERO) > 0) {
  234 + MesWmOutsourceReceiptLineDO unqualifiedLine = BeanUtils.toBean(line, MesWmOutsourceReceiptLineDO.class)
  235 + .setId(null).setQualityStatus(MesWmQualityStatusEnum.FAIL.getStatus())
  236 + .setIqcId(iqcId).setQuantity(unqualifiedQuantity);
  237 + outsourceReceiptLineService.createOutsourceReceiptLineDO(unqualifiedLine);
  238 + }
  239 +
  240 + // 3. 直接更新主表状态为"待上架"(对齐 KTG:IQC 完成后直接推进状态)
  241 + outsourceReceiptMapper.updateById(new MesWmOutsourceReceiptDO()
  242 + .setId(receiptId).setStatus(MesWmOutsourceReceiptStatusEnum.APPROVING.getStatus()));
  243 + }
  244 +
219 245 private MesWmOutsourceReceiptDO validateOutsourceReceiptExists(Long id) {
220 246 MesWmOutsourceReceiptDO receipt = outsourceReceiptMapper.selectById(id);
221 247 if (receipt == null) {
... ...