From ac2e2b5af3726ee64dc37c3ebf26989a0346f22a Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期六, 07 十一月 2026 19:16:05 +0800
Subject: [PATCH] feat(feedback): 优化报工审批流程并增加报工人校验
---
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java | 329 ++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 264 insertions(+), 65 deletions(-)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java
index 42d30cb..55c034a 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java
@@ -26,6 +26,8 @@
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
+import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
import cn.iocoder.yudao.module.mes.service.wm.productreceipt.MesWmProductReceiptService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
@@ -39,9 +41,15 @@
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -61,6 +69,8 @@
@Resource
private MesWmProductProduceLineService productProduceLineService;
@Resource
+ private cn.iocoder.yudao.module.mes.dal.mysql.wm.productproduce.MesWmProductProduceLineMapper produceLineMapper;
+ @Resource
private MesWmProductProduceDetailService productProduceDetailService;
@Resource
private MesProWorkOrderService workOrderService;
@@ -68,6 +78,8 @@
private MesProWorkOrderProcessService workOrderProcessService;
@Resource
private MesProRouteProductService routeProductService;
+ @Resource
+ private MesProRouteProcessService routeProcessService;
@Resource
private MesWmProductReceiptService productReceiptService;
@Resource
@@ -80,6 +92,8 @@
private MesWmWarehouseLocationService locationService;
@Resource
private MesWmWarehouseAreaService areaService;
+ @Resource
+ private MesMdItemService itemService;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -99,12 +113,34 @@
productProduceMapper.updateById(new MesWmProductProduceDO()
.setId(id).setStatus(MesWmProductProduceStatusEnum.FINISHED.getStatus()));
- // 4. 鍙湁鏈亾宸ュ簭鐨勫悎鏍间骇鍑烘墠鐢熸垚浜у搧鍏ュ簱鍗曪紝閬垮厤涓棿宸ュ簭閲嶅鐢熸垚鎴愬搧鍏ュ簱
- MesProWorkOrderProcessDO lastProcess = workOrderProcessService
- .getLastWorkOrderProcessByWorkOrderId(produce.getWorkOrderId());
- if (lastProcess != null && ObjUtil.equal(lastProcess.getProcessId(), produce.getProcessId())) {
- productReceiptService.generateProductReceipt(produce, lines);
+ // 4. 鏈亾宸ュ簭鐢熸垚鎴愬搧鍏ュ簱鍗曪紱涓棿宸ュ簭浜у嚭鍒板疄闄呬粨鏃剁敓鎴愬崐鎴愬搧鍏ュ簱鍗曪紝閬垮厤銆屾湁搴撳瓨鏃犲崟鎹��
+ // 浜у嚭鍒拌櫄鎷熺嚎杈瑰簱灞炰簬鍐呴儴鍦ㄥ埗锛屼笉鐢熸垚鍏ュ簱鍗�
+ boolean lastProcess = isLastProcess(produce.getWorkOrderId(), produce.getProcessId(), produce.getFeedbackId());
+ if (lastProcess || hasRealWarehouseOutput(lines)) {
+ productReceiptService.generateProductReceipt(produce, lines, lastProcess);
}
+ }
+
+ /**
+ * 鍒ゆ柇鍚堟牸浜у嚭鏄惁钀藉湪瀹為檯浠擄紙闈炶櫄鎷熺嚎杈瑰簱锛�
+ */
+ private boolean hasRealWarehouseOutput(List<MesWmProductProduceLineDO> lines) {
+ MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
+ Long virtualWarehouseId = virtualWarehouse != null ? virtualWarehouse.getId() : null;
+ for (MesWmProductProduceLineDO line : lines) {
+ if (!ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.PASS.getStatus())) {
+ continue;
+ }
+ List<MesWmProductProduceDetailDO> details = productProduceDetailService
+ .getProductProduceDetailListByLineId(line.getId());
+ if (CollUtil.isEmpty(details)) {
+ continue;
+ }
+ if (!ObjUtil.equal(details.get(0).getWarehouseId(), virtualWarehouseId)) {
+ return true;
+ }
+ }
+ return false;
}
private void createTransactionList(MesWmProductProduceDO produce, List<MesWmProductProduceLineDO> lines) {
@@ -112,7 +148,16 @@
? feedbackMapper.selectById(produce.getFeedbackId()) : null;
String bizCode = feedback != null && cn.hutool.core.util.StrUtil.isNotBlank(feedback.getCode())
? feedback.getCode() : "PRODUCE-" + produce.getId();
+ MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
+ Long virtualWarehouseId = virtualWarehouse != null ? virtualWarehouse.getId() : null;
for (MesWmProductProduceLineDO line : lines) {
+ // 涓嶅悎鏍煎搧淇濈暀浜у嚭璁板綍渚涜川妫�澶勭疆锛屼笉璁″叆鍙敤搴撳瓨銆�
+ if (ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.FAIL.getStatus())) {
+ continue;
+ }
+ if (!ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.PASS.getStatus())) {
+ throw exception(WM_PRODUCT_PRODUCE_STATUS_INVALID);
+ }
// 1. 鏍¢獙姣忚鏄庣粏鏁伴噺涔嬪拰绛変簬琛屾暟閲�
List<MesWmProductProduceDetailDO> details = productProduceDetailService.getProductProduceDetailListByLineId(
line.getId());
@@ -121,10 +166,17 @@
if (line.getQuantity() != null && totalDetailQty.compareTo(line.getQuantity()) != 0) {
throw exception(WM_PRODUCT_PRODUCE_DETAIL_QUANTITY_MISMATCH);
}
- // 2. 鎸夋槑缁嗗垱寤哄簱瀛樹簨鍔★紙浜у搧浜у嚭鍒扮嚎杈瑰簱锛�
+ // 2. 鎸夋槑缁嗗垱寤哄簱瀛樹簨鍔�
for (MesWmProductProduceDetailDO detail : details) {
+ // 铏氭嫙绾胯竟搴撲骇鍑哄睘浜庤溅闂村湪鍒讹紝闇�绔嬪嵆璁拌处渚涗笅閬撳伐搴忓�掑啿棰嗙敤锛�
+ // 瀹為檯浠撳簱鐨勬垚鍝�/鍗婃垚鍝佺暀鍒颁骇鍝佸叆搴撳崟銆屾墽琛屽叆搴撱�嶆椂璁拌处锛岄伩鍏嶆湭涓婃灦灏卞厛鏈夊簱瀛樻祦姘�
+ if (!ObjUtil.equal(detail.getWarehouseId(), virtualWarehouseId)) {
+ continue;
+ }
+ var item = itemService.validateItemExists(detail.getItemId());
+ Long stockItemId = item.getMdmItemId() != null ? item.getMdmItemId() : item.getId();
wmTransactionService.createTransaction(new MesWmTransactionSaveReqDTO()
- .setType(MesWmTransactionTypeEnum.IN.getType()).setItemId(detail.getItemId())
+ .setType(MesWmTransactionTypeEnum.IN.getType()).setItemId(stockItemId)
.setQuantity(detail.getQuantity())
.setBatchId(detail.getBatchId()).setBatchCode(detail.getBatchCode())
.setWarehouseId(detail.getWarehouseId()).setLocationId(detail.getLocationId()).setAreaId(detail.getAreaId())
@@ -154,9 +206,118 @@
.build();
productProduceMapper.insert(produce);
- // 2. 鑾峰彇鎴栫敓鎴愭壒娆★紙瀹℃壒璇锋眰涓殑鎵规灞炴�т紭鍏堬級
+ // 2. 瑙f瀽鏈鎶ュ伐鐨勪骇鍑哄垪琛細涓讳骇鍑轰负鎶ュ伐鐗╂枡锛岃仈浜у嚭鎸夊伐鍗曞伐搴忓揩鐓ч『搴忓苟鍥炴煡璺嚎閰嶆瘮
+ List<OutputPlan> outputs = resolveOutputPlans(feedback, workOrder);
+
+ // 3. 鏍规嵁鏄惁闇�瑕佹楠屽垎鏀鐞�
+ if (checkFlag) {
+ // 3.1 闇�瑕佹楠岋細姣忎釜浜у嚭鍒涘缓涓�鏉°�屽緟妫�楠屻�嶈锛屼笉鐢熸垚鏄庣粏
+ for (OutputPlan output : outputs) {
+ BatchInfo batch = resolveBatch(feedback, workOrder, output.itemId(), approveReqVO);
+ MesWmProductProduceLineDO line = buildProduceLine(produce, feedback, output.itemId(),
+ batch.id(), batch.code(), scaleQuantity(feedback.getFeedbackQuantity(), output.ratio()),
+ MesWmQualityStatusEnum.PENDING.getStatus());
+ productProduceLineService.createProductProduceLine(line);
+ }
+ // 娉ㄦ剰锛歝heckFlag=true 鏃朵笉鐢熸垚鏄庣粏琛岋紝绛� IPQC 妫�楠屽畬鎴愬悗鐢� splitPendingAndFinishProduce 鎸夎川閲忕粨鏋滄媶琛�+鐢熸垚鏄庣粏
+ } else {
+ // 3.2 鏃犻渶妫�楠岋細姣忎釜浜у嚭鎸夊悎鏍煎搧/涓嶅悎鏍煎搧鍚勭敓鎴愪竴琛� + 鏄庣粏
+ BigDecimal qualifiedQty = ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO);
+ BigDecimal unqualifiedQty = ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO);
+ for (OutputPlan output : outputs) {
+ BigDecimal outputQualifiedQty = scaleQuantity(qualifiedQty, output.ratio());
+ BigDecimal outputUnqualifiedQty = scaleQuantity(unqualifiedQty, output.ratio());
+ BatchInfo batch = resolveBatch(feedback, workOrder, output.itemId(), approveReqVO);
+ // 浜у嚭浠撳簱锛氬伐鍗曞伐搴忛厤缃紭鍏堬紝鍏舵璇ヤ骇鍑虹墿鏂欑殑璺嚎浜у搧浠撳簱锛屼腑闂村伐搴忓彲鍥為��铏氭嫙绾胯竟搴�
+ WarehouseTarget outputTarget = resolveOutputTarget(feedback.getWorkOrderId(),
+ feedback.getProcessId(), output.itemId(), feedback.getId());
+ // 3.2.1 涓嶅悎鏍煎搧琛� + 鏄庣粏
+ if (outputUnqualifiedQty.compareTo(BigDecimal.ZERO) > 0) {
+ MesWmProductProduceLineDO unqualifiedLine = buildProduceLine(produce, feedback, output.itemId(),
+ batch.id(), batch.code(), outputUnqualifiedQty, MesWmQualityStatusEnum.FAIL.getStatus());
+ productProduceLineService.createProductProduceLine(unqualifiedLine);
+ MesWmProductProduceDetailDO unqualifiedDetail = buildProduceDetail(produce, output.itemId(),
+ batch.id(), batch.code(), unqualifiedLine.getId(), outputUnqualifiedQty,
+ outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId());
+ productProduceDetailService.createProductProduceDetail(unqualifiedDetail);
+ }
+ // 3.2.2 鍚堟牸鍝佽 + 鏄庣粏
+ if (outputQualifiedQty.compareTo(BigDecimal.ZERO) > 0) {
+ MesWmProductProduceLineDO qualifiedLine = buildProduceLine(produce, feedback, output.itemId(),
+ batch.id(), batch.code(), outputQualifiedQty, MesWmQualityStatusEnum.PASS.getStatus());
+ productProduceLineService.createProductProduceLine(qualifiedLine);
+ MesWmProductProduceDetailDO qualifiedDetail = buildProduceDetail(produce, output.itemId(),
+ batch.id(), batch.code(), qualifiedLine.getId(), outputQualifiedQty,
+ outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId());
+ productProduceDetailService.createProductProduceDetail(qualifiedDetail);
+ }
+ }
+ }
+ return produce;
+ }
+
+ /**
+ * 瑙f瀽鎶ュ伐浜у嚭鍒楄〃锛氫富浜у嚭鍥哄畾涓烘姤宸ョ墿鏂欙紙閰嶆瘮 1锛夛紝鑱斾骇鍑烘寜宸ュ崟宸ュ簭蹇収椤哄簭锛岄厤姣斿洖鏌ュ伐鑹鸿矾绾块厤缃��
+ */
+ private List<OutputPlan> resolveOutputPlans(MesProFeedbackDO feedback, MesProWorkOrderDO workOrder) {
+ List<OutputPlan> single = List.of(new OutputPlan(feedback.getItemId(), BigDecimal.ONE));
+ if (feedback.getWorkOrderId() == null || feedback.getProcessId() == null) {
+ return single;
+ }
+ MesProWorkOrderProcessDO workOrderProcess = workOrderProcessService
+ .getWorkOrderProcessByWorkOrderIdAndProcessId(feedback.getWorkOrderId(), feedback.getProcessId());
+ List<Long> snapshotItemIds = parseItemIds(workOrderProcess != null ? workOrderProcess.getOutputItemId() : null);
+ if (snapshotItemIds.size() <= 1) {
+ return single;
+ }
+ // 鍥炴煡璺嚎浜у嚭閰嶆瘮锛氭壘涓嶅埌閰嶇疆鏃舵寜 1 澶勭悊
+ Map<Long, BigDecimal> ratioMap = new HashMap<>();
+ Long routeId = feedback.getRouteId() != null ? feedback.getRouteId()
+ : (workOrder != null ? workOrder.getRouteId() : null);
+ if (routeId != null) {
+ var routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, feedback.getProcessId());
+ if (routeProcess != null) {
+ for (var config : routeProcessService.getProcessOutputs(routeProcess.getId(),
+ workOrder != null ? workOrder.getProductId() : null)) {
+ if (config.getItemId() != null && config.getQuantity() != null) {
+ ratioMap.putIfAbsent(config.getItemId(), config.getQuantity());
+ }
+ }
+ }
+ }
+ List<OutputPlan> plans = new ArrayList<>();
+ plans.add(new OutputPlan(feedback.getItemId(), BigDecimal.ONE));
+ for (Long itemId : snapshotItemIds) {
+ if (itemId == null || itemId.equals(feedback.getItemId())) {
+ continue;
+ }
+ plans.add(new OutputPlan(itemId, ratioMap.getOrDefault(itemId, BigDecimal.ONE)));
+ }
+ return plans;
+ }
+
+ private List<Long> parseItemIds(String csv) {
+ if (csv == null || csv.isBlank()) {
+ return Collections.emptyList();
+ }
+ return Arrays.stream(csv.split(","))
+ .map(String::trim).filter(value -> !value.isEmpty())
+ .map(Long::valueOf).distinct().toList();
+ }
+
+ /**
+ * 浜у嚭鏁伴噺 = 鍩哄噯鏁伴噺 脳 閰嶆瘮锛屼繚鐣� 6 浣嶅皬鏁帮紝涓庝骇鍑哄崟鏁伴噺瀛楁绮惧害涓�鑷淬��
+ */
+ private BigDecimal scaleQuantity(BigDecimal base, BigDecimal ratio) {
+ BigDecimal baseValue = base != null ? base : BigDecimal.ZERO;
+ BigDecimal ratioValue = ratio != null ? ratio : BigDecimal.ONE;
+ return baseValue.multiply(ratioValue).setScale(6, RoundingMode.HALF_UP);
+ }
+
+ private BatchInfo resolveBatch(MesProFeedbackDO feedback, MesProWorkOrderDO workOrder,
+ Long itemId, MesProFeedbackApproveReqVO approveReqVO) {
MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO()
- .setItemId(feedback.getItemId())
+ .setItemId(itemId)
.setProduceDate(LocalDate.now().atStartOfDay()) // 鎴柇鍒板綋澶╅浂鐐癸紝纭繚鍚屼竴澶╃殑鎶ュ伐鐢熸垚鐩稿悓鎵规鍙�
.setExpireDate(feedback.getExpireDate())
.setWorkOrderId(feedback.getWorkOrderId())
@@ -179,70 +340,37 @@
if (approveReqVO.getQualityStatus() != null) batchReqVO.setQualityStatus(approveReqVO.getQualityStatus());
}
MesWmBatchDO batch = batchService.getOrGenerateBatchCode(batchReqVO);
- Long batchId = batch != null ? batch.getId() : null;
- String batchCode = batch != null ? batch.getCode() : null;
-
- // 1.5 浼樺厛浣跨敤宸ュ崟宸ュ簭閰嶇疆鐨勬姤宸ヤ粨搴擄紙涓嬪崟鏃跺彲缂栬緫锛夛紝鍏舵宸ヨ壓璺嚎浜у搧锛屾渶鍚庡洖閫�铏氭嫙绾胯竟搴�
- WarehouseTarget outputTarget = resolveOutputTarget(feedback.getWorkOrderId(),
- feedback.getProcessId(), feedback.getItemId());
-
- // 3. 鏍规嵁鏄惁闇�瑕佹楠屽垎鏀鐞�
- if (checkFlag) {
- // 3.1 闇�瑕佹楠岋細鍒涘缓涓�鏉¤锛堣川閲忕姸鎬�=寰呮楠岋級锛屼笉鐢熸垚鏄庣粏
- MesWmProductProduceLineDO line = buildProduceLine(produce, feedback, batchId, batchCode,
- feedback.getFeedbackQuantity(), MesWmQualityStatusEnum.PENDING.getStatus());
- productProduceLineService.createProductProduceLine(line);
- // 娉ㄦ剰锛歝heckFlag=true 鏃朵笉鐢熸垚鏄庣粏琛岋紝绛� IPQC 妫�楠屽畬鎴愬悗鐢� splitPendingAndFinishProduce 鎸夎川閲忕粨鏋滄媶琛�+鐢熸垚鏄庣粏
- } else {
- // 3.2 鏃犻渶妫�楠岋細鎸夊悎鏍煎搧/涓嶅悎鏍煎搧鍚勭敓鎴愪竴琛� + 鏄庣粏
- BigDecimal qualifiedQty = ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO);
- BigDecimal unqualifiedQty = ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO);
- // 3.2.1 涓嶅悎鏍煎搧琛� + 鏄庣粏
- if (unqualifiedQty.compareTo(BigDecimal.ZERO) > 0) {
- MesWmProductProduceLineDO unqualifiedLine = buildProduceLine(produce, feedback, batchId, batchCode,
- unqualifiedQty, MesWmQualityStatusEnum.FAIL.getStatus());
- productProduceLineService.createProductProduceLine(unqualifiedLine);
- MesWmProductProduceDetailDO unqualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode,
- unqualifiedLine.getId(), unqualifiedQty,
- outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId());
- productProduceDetailService.createProductProduceDetail(unqualifiedDetail);
- }
- // 3.2.2 鍚堟牸鍝佽 + 鏄庣粏
- if (qualifiedQty.compareTo(BigDecimal.ZERO) > 0) {
- MesWmProductProduceLineDO qualifiedLine = buildProduceLine(produce, feedback, batchId, batchCode,
- qualifiedQty, MesWmQualityStatusEnum.PASS.getStatus());
- productProduceLineService.createProductProduceLine(qualifiedLine);
- MesWmProductProduceDetailDO qualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode,
- qualifiedLine.getId(), qualifiedQty,
- outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId());
- productProduceDetailService.createProductProduceDetail(qualifiedDetail);
- }
- }
- return produce;
+ return new BatchInfo(batch != null ? batch.getId() : null, batch != null ? batch.getCode() : null);
}
private MesWmProductProduceLineDO buildProduceLine(MesWmProductProduceDO produce, MesProFeedbackDO feedback,
- Long batchId, String batchCode,
+ Long itemId, Long batchId, String batchCode,
BigDecimal quantity, Integer qualityStatus) {
return MesWmProductProduceLineDO.builder()
.produceId(produce.getId()).feedbackId(feedback.getId())
- .itemId(feedback.getItemId()).quantity(quantity)
+ .itemId(itemId).quantity(quantity)
.batchId(batchId).batchCode(batchCode)
.expireDate(feedback.getExpireDate()).lotNumber(feedback.getLotNumber())
.qualityStatus(qualityStatus)
.build();
}
- private MesWmProductProduceDetailDO buildProduceDetail(MesWmProductProduceDO produce, MesProFeedbackDO feedback,
+ private MesWmProductProduceDetailDO buildProduceDetail(MesWmProductProduceDO produce, Long itemId,
Long batchId, String batchCode,
Long lineId, BigDecimal quantity,
Long warehouseId, Long locationId, Long areaId) {
return MesWmProductProduceDetailDO.builder()
.produceId(produce.getId()).lineId(lineId)
- .itemId(feedback.getItemId()).quantity(quantity)
+ .itemId(itemId).quantity(quantity)
.batchId(batchId).batchCode(batchCode)
.warehouseId(warehouseId).locationId(locationId).areaId(areaId)
.build();
+ }
+
+ private record OutputPlan(Long itemId, BigDecimal ratio) {
+ }
+
+ private record BatchInfo(Long id, String code) {
}
@Override
@@ -275,15 +403,56 @@
throw exception(WM_PRODUCT_PRODUCE_NOT_EXISTS);
}
// 2. 閫氳繃 sourceLineId 鐩存帴瀹氫綅寰呮楠岃
- MesWmProductProduceLineDO pendingLine = productProduceLineService.validateProductProduceLineExists(sourceLineId);
- WarehouseTarget outputTarget = resolveOutputTarget(produce.getWorkOrderId(),
- produce.getProcessId(), pendingLine.getItemId());
+ MesWmProductProduceLineDO pendingLine = produceLineMapper.selectByIdForUpdate(sourceLineId);
+ if (pendingLine == null) throw exception(WM_PRODUCT_PRODUCE_LINE_NOT_EXISTS);
+ if (!java.util.Objects.equals(pendingLine.getProduceId(), produce.getId())
+ || !java.util.Objects.equals(pendingLine.getFeedbackId(), feedbackId)) {
+ throw exception(QC_IPQC_SOURCE_LINE_NOT_BELONG);
+ }
+ if (!java.util.Objects.equals(pendingLine.getQualityStatus(), MesWmQualityStatusEnum.PENDING.getStatus())) {
+ throw exception(QC_IPQC_SOURCE_LINE_NOT_PENDING);
+ }
+ if (qualifiedQty == null || unqualifiedQty == null || qualifiedQty.signum() < 0 || unqualifiedQty.signum() < 0
+ || qualifiedQty.add(unqualifiedQty).signum() <= 0 || pendingLine.getQuantity() == null
+ || qualifiedQty.add(unqualifiedQty).compareTo(pendingLine.getQuantity()) != 0) {
+ throw exception(QC_IPQC_SOURCE_QUANTITY_INVALID);
+ }
+ // 3. 鍙媶鍒嗗綋鍓嶆楠岀殑浜у嚭琛岋紱鍚屼竴鎶ュ伐鐨勫叾瀹冧骇鍑虹墿鏂欏悇鑷缓妫�楠屽崟銆佺嫭绔嬫楠�
+ applyQualitySplit(produce, pendingLine, qualifiedQty, unqualifiedQty);
- // 3A. 鎯呭喌涓�锛氬瓨鍦ㄤ笉鍚堟牸鍝佹暟閲忥紝闇�瑕佹媶鍒嗚
+ // 4. 鍏ㄩ儴浜у嚭鐗╂枡妫�楠屽畬鎴愬悗鎵嶅畬鎴愪骇鍑哄崟锛堝垱寤哄簱瀛樹簨鍔� + 鏇存柊鐘舵�佷负宸插畬鎴愶級
+ boolean allInspected = productProduceLineService.getProductProduceLineListByProduceId(produce.getId())
+ .stream().noneMatch(line -> ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.PENDING.getStatus()));
+ if (allInspected) {
+ finishProductProduce(produce.getId());
+ }
+ }
+
+ /**
+ * 鎸夊悎鏍�/涓嶅悎鏍兼暟閲忔媶鍒嗕竴鏉″緟妫�琛岋細鐢熸垚鏄庣粏锛屽繀瑕佹椂鏂板涓嶅悎鏍艰銆�
+ */
+ private void applyQualitySplit(MesWmProductProduceDO produce, MesWmProductProduceLineDO pendingLine,
+ BigDecimal qualifiedQty, BigDecimal unqualifiedQty) {
+ WarehouseTarget outputTarget = resolveOutputTarget(produce.getWorkOrderId(),
+ produce.getProcessId(), pendingLine.getItemId(), produce.getFeedbackId());
+
+ if (qualifiedQty.signum() == 0) {
+ pendingLine.setQualityStatus(MesWmQualityStatusEnum.FAIL.getStatus());
+ productProduceLineService.updateProductProduceLine(pendingLine);
+ productProduceDetailService.createProductProduceDetail(MesWmProductProduceDetailDO.builder()
+ .produceId(produce.getId()).lineId(pendingLine.getId())
+ .itemId(pendingLine.getItemId()).quantity(unqualifiedQty)
+ .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode())
+ .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId())
+ .build());
+ return;
+ }
+
+ // 鎯呭喌涓�锛氬瓨鍦ㄤ笉鍚堟牸鍝佹暟閲忥紝闇�瑕佹媶鍒嗚
if (unqualifiedQty != null && unqualifiedQty.compareTo(BigDecimal.ZERO) > 0) {
- // 3A.1 涓嶅悎鏍煎搧锛氭柊寤轰竴琛� + 鏄庣粏
+ // 涓嶅悎鏍煎搧锛氭柊寤轰竴琛� + 鏄庣粏
MesWmProductProduceLineDO unqualifiedLine = MesWmProductProduceLineDO.builder()
- .produceId(produce.getId()).feedbackId(feedbackId)
+ .produceId(produce.getId()).feedbackId(pendingLine.getFeedbackId())
.itemId(pendingLine.getItemId()).quantity(unqualifiedQty)
.batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode())
.expireDate(pendingLine.getExpireDate()).lotNumber(pendingLine.getLotNumber())
@@ -296,7 +465,7 @@
.batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode())
.warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId())
.build());
- // 3A.2 鍚堟牸鍝侊細澶嶇敤鍘熷緟妫�琛岋紝鏇存柊鏁伴噺鍜岀姸鎬� + 鏄庣粏
+ // 鍚堟牸鍝侊細澶嶇敤鍘熷緟妫�琛岋紝鏇存柊鏁伴噺鍜岀姸鎬� + 鏄庣粏
pendingLine.setQuantity(ObjectUtil.defaultIfNull(qualifiedQty, BigDecimal.ZERO));
pendingLine.setQualityStatus(MesWmQualityStatusEnum.PASS.getStatus());
productProduceLineService.updateProductProduceLine(pendingLine);
@@ -309,7 +478,7 @@
.build());
}
} else {
- // 3B. 鎯呭喌浜岋細鍏ㄩ儴鍚堟牸锛岀洿鎺ユ洿鏂拌鐘舵�� + 鏄庣粏
+ // 鎯呭喌浜岋細鍏ㄩ儴鍚堟牸锛岀洿鎺ユ洿鏂拌鐘舵�� + 鏄庣粏
pendingLine.setQualityStatus(MesWmQualityStatusEnum.PASS.getStatus());
productProduceLineService.updateProductProduceLine(pendingLine);
productProduceDetailService.createProductProduceDetail(MesWmProductProduceDetailDO.builder()
@@ -319,12 +488,10 @@
.warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId())
.build());
}
-
- // 4. 瀹屾垚浜у嚭鍗曪紙鍒涘缓搴撳瓨浜嬪姟 + 鏇存柊鐘舵�佷负宸插畬鎴愶級
- finishProductProduce(produce.getId());
}
- private WarehouseTarget resolveOutputTarget(Long workOrderId, Long processId, Long itemId) {
+ private WarehouseTarget resolveOutputTarget(Long workOrderId, Long processId, Long itemId, Long feedbackId) {
+ boolean lastProcess = isLastProcess(workOrderId, processId, feedbackId);
// 1. 宸ュ崟宸ュ簭閰嶇疆锛堜笅鍗曟椂鍙紪杈戯級
if (workOrderId != null && processId != null) {
MesProWorkOrderProcessDO workOrderProcess = workOrderProcessService
@@ -333,6 +500,7 @@
&& workOrderProcess.getLocationId() != null && workOrderProcess.getAreaId() != null) {
areaService.validateWarehouseAreaExists(workOrderProcess.getWarehouseId(),
workOrderProcess.getLocationId(), workOrderProcess.getAreaId());
+ validateFinalWarehouse(lastProcess, workOrderProcess.getWarehouseId());
return new WarehouseTarget(workOrderProcess.getWarehouseId(),
workOrderProcess.getLocationId(), workOrderProcess.getAreaId());
}
@@ -344,7 +512,11 @@
&& routeProduct.getLocationId() != null && routeProduct.getAreaId() != null) {
areaService.validateWarehouseAreaExists(routeProduct.getWarehouseId(),
routeProduct.getLocationId(), routeProduct.getAreaId());
+ validateFinalWarehouse(lastProcess, routeProduct.getWarehouseId());
return new WarehouseTarget(routeProduct.getWarehouseId(), routeProduct.getLocationId(), routeProduct.getAreaId());
+ }
+ if (lastProcess) {
+ throw exception(WM_PRODUCT_PRODUCE_OUTPUT_WAREHOUSE_REQUIRED);
}
MesWmWarehouseDO warehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
MesWmWarehouseLocationDO location = locationService.getWarehouseLocationByCode(
@@ -353,6 +525,33 @@
return new WarehouseTarget(warehouse.getId(), location.getId(), area.getId());
}
+ private boolean isLastProcess(Long workOrderId, Long processId, Long feedbackId) {
+ MesProWorkOrderProcessDO last = workOrderProcessService.getLastWorkOrderProcessByWorkOrderId(workOrderId);
+ if (last != null) return ObjUtil.equal(last.getProcessId(), processId);
+ MesProFeedbackDO feedback = feedbackId == null ? null : feedbackMapper.selectById(feedbackId);
+ if (feedback == null || feedback.getRouteId() == null) {
+ throw exception(PRO_FEEDBACK_PARAM_INVALID, "鏃犳硶纭畾鏈亾宸ュ簭锛岃妫�鏌ュ伐鍗曞拰宸ヨ壓璺嚎鐨勫伐搴忛『搴�");
+ }
+ var processes = routeProcessService.getRouteProcessListByRouteId(feedback.getRouteId());
+ if (CollUtil.isEmpty(processes) || processes.stream().anyMatch(p -> p.getSort() == null)) {
+ throw exception(PRO_FEEDBACK_PARAM_INVALID, "鏃犳硶纭畾鏈亾宸ュ簭锛岃妫�鏌ュ伐鍗曞拰宸ヨ壓璺嚎鐨勫伐搴忛『搴�");
+ }
+ int maxSort = processes.stream().mapToInt(p -> p.getSort()).max().orElseThrow();
+ var lastProcesses = processes.stream().filter(p -> p.getSort() == maxSort).toList();
+ if (lastProcesses.size() != 1 || lastProcesses.get(0).getProcessId() == null) {
+ throw exception(PRO_FEEDBACK_PARAM_INVALID, "鏃犳硶鍞竴纭畾鏈亾宸ュ簭锛岃妫�鏌ュ伐鑹鸿矾绾跨殑宸ュ簭椤哄簭");
+ }
+ return ObjUtil.equal(lastProcesses.get(0).getProcessId(), processId);
+ }
+
+ private void validateFinalWarehouse(boolean lastProcess, Long warehouseId) {
+ if (!lastProcess) return;
+ MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
+ if (virtualWarehouse != null && ObjUtil.equal(warehouseId, virtualWarehouse.getId())) {
+ throw exception(WM_PRODUCT_PRODUCE_OUTPUT_WAREHOUSE_REQUIRED);
+ }
+ }
+
private record WarehouseTarget(Long warehouseId, Long locationId, Long areaId) {
}
--
Gitblit v1.9.3