From 3066a8f5c1f475997bfe23857ba4bae9395900c4 Mon Sep 17 00:00:00 2001
From: yuan <123@>
Date: 星期四, 13 八月 2026 10:19:24 +0800
Subject: [PATCH] fix: 领料功能修改,移除订单领料,添加工序领料功能,支持按工序查询物料与已领料记录
---
src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 270 insertions(+), 1 deletions(-)
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
index 598ae6b..e4b50f5 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
@@ -10,14 +10,20 @@
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.production.bean.dto.ProductionOrderPickDto;
import com.ruoyi.production.bean.vo.ProductionOrderPickVo;
+import com.ruoyi.production.mapper.ProductionBomStructureMapper;
import com.ruoyi.production.mapper.ProductionOperationTaskMapper;
import com.ruoyi.production.mapper.ProductionOrderMapper;
import com.ruoyi.production.mapper.ProductionOrderPickMapper;
import com.ruoyi.production.mapper.ProductionOrderPickRecordMapper;
+import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper;
+import com.ruoyi.production.pojo.ProductionBomStructure;
+import com.ruoyi.production.pojo.ProductionOperationTask;
import com.ruoyi.production.pojo.ProductionOrder;
import com.ruoyi.production.pojo.ProductionOrderPick;
import com.ruoyi.production.pojo.ProductionOrderPickRecord;
+import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
import com.ruoyi.production.service.ProductionOrderPickService;
+import com.ruoyi.production.service.ProductionOrderService;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.mapper.StockInventoryMapper;
import com.ruoyi.stock.pojo.StockInRecord;
@@ -26,6 +32,8 @@
import com.ruoyi.stock.service.StockInventoryService;
import com.ruoyi.stock.service.StockInRecordService;
import com.ruoyi.stock.service.StockOutRecordService;
+import com.ruoyi.technology.mapper.TechnologyOperationMapper;
+import com.ruoyi.technology.pojo.TechnologyOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -53,6 +61,10 @@
private final ProductionOrderMapper productionOrderMapper;
private final ProductionOperationTaskMapper productionOperationTaskMapper;
private final ProductionOrderPickRecordMapper productionOrderPickRecordMapper;
+ private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
+ private final ProductionBomStructureMapper productionBomStructureMapper;
+ private final ProductionOrderService productionOrderService;
+ private final TechnologyOperationMapper technologyOperationMapper;
private final StockInventoryMapper stockInventoryMapper;
private final StockInventoryService stockInventoryService;
private final StockInRecordService stockInRecordService;
@@ -83,6 +95,7 @@
// 淇濆瓨棰嗘枡涓昏褰曞揩鐓с��
ProductionOrderPick orderPick = new ProductionOrderPick();
orderPick.setProductionOrderId(resolvedDto.getProductionOrderId());
+ orderPick.setProductionOperationTaskId(resolvedDto.getProductionOperationTaskId());
orderPick.setProductModelId(resolvedDto.getProductModelId());
orderPick.setBatchNo(storedBatchNo);
orderPick.setQuantity(resolvedDto.getPickQuantity());
@@ -191,7 +204,237 @@
List<ProductionOrderPickVo> detailList = baseMapper.listPickedDetailByOrderId(productionOrderId);
fillBatchNoList(detailList);
fillSelectableBatchNoList(detailList);
+ fillPickedFlagAndEffectiveQuantity(detailList);
return detailList;
+ }
+
+ /**
+ * 宸ュ簭鐗╂枡娓呭崟 = 璁㈠崟BOM蹇収涓睘浜庤宸ュ簭鐨勫彲棰嗙敤鐗╂枡寤鸿 + 璇ュ伐鍗曞凡棰嗘枡璁板綍銆�
+ * 寤鸿琛� picked=false锛坕d 涓虹┖锛夛紝鍘嗗彶棰嗘枡琛� picked=true銆�
+ */
+ @Override
+ public List<ProductionOrderPickVo> listTaskMaterials(Long productionOperationTaskId) {
+ if (productionOperationTaskId == null) {
+ return Collections.emptyList();
+ }
+ TaskOperationContext context = resolveTaskOperationContext(productionOperationTaskId);
+
+ List<ProductionOrderPickVo> result = new ArrayList<>();
+ // BOM 寤鸿鐗╂枡锛氭寜褰撳墠宸ュ簭杩囨护锛屼笉甯︿换浣曢渶姹傛暟閲�
+ List<ProductionOrderPickVo> suggestionList =
+ productionOrderService.pick(context.productionOrderId(), context.technologyOperationId());
+ for (ProductionOrderPickVo suggestion : suggestionList) {
+ suggestion.setId(null);
+ suggestion.setPicked(false);
+ suggestion.setProductionOrderId(context.productionOrderId());
+ suggestion.setProductionOperationTaskId(productionOperationTaskId);
+ suggestion.setTechnologyOperationId(context.technologyOperationId());
+ if (StringUtils.isEmpty(suggestion.getOperationName())) {
+ suggestion.setOperationName(context.operationName());
+ }
+ suggestion.setWorkOrderNo(context.workOrderNo());
+ result.add(suggestion);
+ }
+
+ // 璇ュ伐鍗曞凡浜х敓鐨勯鏂欒褰�
+ List<ProductionOrderPickVo> pickedList = baseMapper.listPickedDetailByTaskId(productionOperationTaskId);
+ fillBatchNoList(pickedList);
+ fillSelectableBatchNoList(pickedList);
+ fillPickedFlagAndEffectiveQuantity(pickedList);
+ result.addAll(pickedList);
+ return result;
+ }
+
+ /**
+ * 宸ュ簭缁熶竴棰嗘枡锛�
+ * 1) 鐢卞伐鍗曞弽鏌ヨ鍗曚笌宸ュ簭锛屼笉淇′换鍓嶇浼犲叆鐨勫綊灞炲瓧娈碉紱
+ * 2) 閫愯鏍¢獙鐗╂枡銆佹暟閲忎笌鎵规锛�
+ * 3) 澶嶇敤鍑哄簱鐢宠 + 绔嬪嵆瀹℃壒鐨勫簱瀛樻墸鍑忔祦绋嬶紱
+ * 4) 鍐欏叆棰嗘枡涓昏褰曚笌娴佹按锛岄噸澶嶉鏂欏嵆鏂板璁板綍锛屼笉鍖哄垎鍒濋涓庤ˉ鏂欍��
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean savePickByTask(ProductionOrderPickDto dto) {
+ if (dto == null) {
+ throw new ServiceException("鍙傛暟涓嶈兘涓虹┖");
+ }
+ Long productionOperationTaskId = dto.getProductionOperationTaskId();
+ if (productionOperationTaskId == null) {
+ throw new ServiceException("宸ュ崟ID涓嶈兘涓虹┖");
+ }
+ TaskOperationContext context = resolveTaskOperationContext(productionOperationTaskId);
+
+ List<ProductionOrderPickDto> pickItems = resolvePickItems(dto).stream()
+ .filter(Objects::nonNull)
+ .filter(item -> item.getProductModelId() != null || item.getPickQuantity() != null)
+ .collect(Collectors.toList());
+ if (pickItems.isEmpty()) {
+ throw new ServiceException("璇疯嚦灏戝~鍐欎竴鏉¢鏂欑墿鏂�");
+ }
+
+ for (int i = 0; i < pickItems.size(); i++) {
+ int rowNo = i + 1;
+ ProductionOrderPickDto resolvedDto = mergeDto(dto, pickItems.get(i));
+ if (resolvedDto.getProductModelId() == null) {
+ throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細浜у搧瑙勬牸涓嶈兘涓虹┖");
+ }
+ if (resolvedDto.getPickQuantity() == null
+ || resolvedDto.getPickQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+ throw new ServiceException("绗�" + rowNo + "琛屽弬鏁伴敊璇細棰嗘枡鏁伴噺蹇呴』澶т簬0");
+ }
+
+ List<String> batchNoList = resolveBatchNoList(resolvedDto);
+ String inventoryBatchNo = pickInventoryBatchNo(batchNoList);
+ String storedBatchNo = formatBatchNoStorage(batchNoList);
+
+ ProductionOrderPick orderPick = new ProductionOrderPick();
+ orderPick.setProductionOrderId(context.productionOrderId());
+ orderPick.setProductionOperationTaskId(productionOperationTaskId);
+ orderPick.setProductModelId(resolvedDto.getProductModelId());
+ orderPick.setBatchNo(storedBatchNo);
+ orderPick.setQuantity(resolvedDto.getPickQuantity());
+ orderPick.setRemark(resolvedDto.getRemark());
+ orderPick.setOperationName(context.operationName());
+ orderPick.setTechnologyOperationId(context.technologyOperationId());
+ // 鎵佸钩BOM涓嶉厤缃暟閲忥紝棰嗘枡涓嶅啀鏈夐渶姹傛暟閲忕害鏉�
+ orderPick.setDemandedQuantity(null);
+ orderPick.setBom(isBomMaterial(context, resolvedDto.getProductModelId()));
+ orderPick.setReturned(false);
+ baseMapper.insert(orderPick);
+
+ // 绔嬪嵆鎵e噺搴撳瓨锛屽け璐ュ垯鏁翠釜浜嬪姟鍥炴粴
+ subtractInventory(orderPick.getId(),
+ resolvedDto.getProductModelId(),
+ storedBatchNo,
+ resolvedDto.getPickQuantity(),
+ rowNo,
+ PICK_STOCK_OUT_RECORD_TYPE);
+
+ insertPickRecord(orderPick.getId(),
+ context.productionOrderId(),
+ productionOperationTaskId,
+ resolvedDto.getProductModelId(),
+ inventoryBatchNo,
+ resolvedDto.getPickQuantity(),
+ BigDecimal.ZERO,
+ resolvedDto.getPickQuantity(),
+ PICK_TYPE_NORMAL,
+ resolvedDto.getRemark(),
+ null);
+ }
+ return true;
+ }
+
+ /**
+ * 褰撳墠宸ュ簭鏄惁宸叉湁鏈夋晥棰嗘枡锛氫换涓�鐗╂枡鐨勫噣棰嗘枡鏁伴噺锛堥鏂�+琛ユ枡-閫�鏂欙級澶т簬0銆�
+ */
+ @Override
+ public boolean hasEffectivePick(Long productionOperationTaskId) {
+ if (productionOperationTaskId == null) {
+ return false;
+ }
+ List<ProductionOrderPick> pickList = baseMapper.selectList(
+ Wrappers.<ProductionOrderPick>lambdaQuery()
+ .eq(ProductionOrderPick::getProductionOperationTaskId, productionOperationTaskId));
+ for (ProductionOrderPick pick : pickList) {
+ if (pick == null) {
+ continue;
+ }
+ if (resolveEffectiveQuantity(pick).compareTo(BigDecimal.ZERO) > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 鐢卞伐鍗曡В鏋愬嚭璁㈠崟銆佸伐搴忓揩鐓т笌宸ュ簭鍚嶇О锛屽苟鏍¢獙褰掑睘鍏崇郴銆�
+ */
+ private TaskOperationContext resolveTaskOperationContext(Long productionOperationTaskId) {
+ ProductionOperationTask task = productionOperationTaskMapper.selectById(productionOperationTaskId);
+ if (task == null) {
+ throw new ServiceException("宸ュ崟涓嶅瓨鍦�");
+ }
+ if (task.getProductionOrderId() == null) {
+ throw new ServiceException("宸ュ崟鏈叧鑱旂敓浜ц鍗�");
+ }
+ ProductionOrder productionOrder = productionOrderMapper.selectById(task.getProductionOrderId());
+ if (productionOrder == null) {
+ throw new ServiceException("鐢熶骇璁㈠崟涓嶅瓨鍦�");
+ }
+ ProductionOrderRoutingOperation routingOperation =
+ productionOrderRoutingOperationMapper.selectById(task.getProductionOrderRoutingOperationId());
+ if (routingOperation == null) {
+ throw new ServiceException("宸ュ崟瀵瑰簲鐨勮鍗曞伐搴忎笉瀛樺湪");
+ }
+ if (routingOperation.getProductionOrderId() != null
+ && !Objects.equals(routingOperation.getProductionOrderId(), task.getProductionOrderId())) {
+ throw new ServiceException("宸ュ崟涓庤鍗曞伐搴忓綊灞炰笉涓�鑷�");
+ }
+ if (routingOperation.getTechnologyOperationId() == null) {
+ throw new ServiceException("璁㈠崟宸ュ簭鏈叧鑱斿伐鑹哄伐搴�");
+ }
+ String operationName = routingOperation.getOperationName();
+ if (StringUtils.isEmpty(operationName)) {
+ TechnologyOperation technologyOperation =
+ technologyOperationMapper.selectById(routingOperation.getTechnologyOperationId());
+ operationName = technologyOperation == null ? null : technologyOperation.getName();
+ }
+ return new TaskOperationContext(
+ task.getId(),
+ task.getProductionOrderId(),
+ routingOperation.getTechnologyOperationId(),
+ operationName,
+ task.getWorkOrderNo());
+ }
+
+ /**
+ * 鍒ゆ柇鐗╂枡鏄惁鏉ヨ嚜璁㈠崟BOM蹇収涓殑褰撳墠宸ュ簭銆�
+ */
+ private boolean isBomMaterial(TaskOperationContext context, Long productModelId) {
+ if (productModelId == null) {
+ return false;
+ }
+ return productionBomStructureMapper.selectCount(
+ Wrappers.<ProductionBomStructure>lambdaQuery()
+ .eq(ProductionBomStructure::getProductionOrderId, context.productionOrderId())
+ .eq(ProductionBomStructure::getTechnologyOperationId, context.technologyOperationId())
+ .eq(ProductionBomStructure::getProductModelId, productModelId)) > 0;
+ }
+
+ private void fillPickedFlagAndEffectiveQuantity(List<ProductionOrderPickVo> detailList) {
+ if (detailList == null || detailList.isEmpty()) {
+ return;
+ }
+ for (ProductionOrderPickVo detail : detailList) {
+ if (detail == null) {
+ continue;
+ }
+ detail.setPicked(detail.getId() != null);
+ detail.setEffectiveQuantity(resolveEffectiveQuantity(detail));
+ }
+ }
+
+ /**
+ * 鍑�棰嗘枡鏁伴噺 = 棰嗘枡鏁伴噺 + 琛ユ枡鏁伴噺 - 閫�鏂欐暟閲忋��
+ */
+ private BigDecimal resolveEffectiveQuantity(ProductionOrderPick pick) {
+ if (pick == null) {
+ return BigDecimal.ZERO;
+ }
+ return defaultDecimal(pick.getQuantity())
+ .add(defaultDecimal(pick.getFeedingQty()))
+ .subtract(defaultDecimal(pick.getReturnQty()));
+ }
+
+ /**
+ * 宸ュ崟瀵瑰簲鐨勯鏂欎笂涓嬫枃锛岄伩鍏嶄緷璧栧墠绔紶鍏ョ殑璁㈠崟/宸ュ簭瀛楁銆�
+ */
+ private record TaskOperationContext(Long productionOperationTaskId,
+ Long productionOrderId,
+ Long technologyOperationId,
+ String operationName,
+ String workOrderNo) {
}
private void processDeletePickIds(ProductionOrderPickDto rootDto,
@@ -294,6 +537,7 @@
ProductionOrderPick orderPick = new ProductionOrderPick();
orderPick.setProductionOrderId(dto.getProductionOrderId());
+ orderPick.setProductionOperationTaskId(dto.getProductionOperationTaskId());
orderPick.setProductModelId(dto.getProductModelId());
orderPick.setBatchNo(storedBatchNo);
orderPick.setQuantity(dto.getPickQuantity());
@@ -430,7 +674,7 @@
BigDecimal totalReturnQty = oldReturnQty.add(currentReturnQty);
if (currentReturnQty.compareTo(BigDecimal.ZERO) > 0) {
String returnBatchNo = resolveInventoryBatchNoFromStored(oldPick.getBatchNo());
- addInventory(oldPick.getId(), oldPick.getProductModelId(), returnBatchNo, currentReturnQty, FEED_RETURN_IN_RECORD_TYPE);
+ addInventoryRecordOnly(oldPick.getId(), oldPick.getProductModelId(), returnBatchNo, currentReturnQty, FEED_RETURN_IN_RECORD_TYPE);
}
BigDecimal actualQty = defaultDecimal(oldPick.getQuantity())
@@ -737,6 +981,31 @@
}
}
+ private void addInventoryRecordOnly(Long recordId,
+ Long productModelId,
+ String batchNo,
+ BigDecimal quantity,
+ String stockInRecordType) {
+ // 浠呰褰曞叆搴撶敵璇凤紝涓嶅仛瀹℃牳閫氳繃銆�
+ BigDecimal addQuantity = defaultDecimal(quantity);
+ if (addQuantity.compareTo(BigDecimal.ZERO) <= 0) {
+ return;
+ }
+ try {
+ StockInventoryDto stockInventoryDto = new StockInventoryDto();
+ stockInventoryDto.setProductModelId(productModelId);
+ stockInventoryDto.setBatchNo(batchNo);
+ stockInventoryDto.setQualitity(addQuantity);
+ stockInventoryDto.setRecordType(stockInRecordType);
+ stockInventoryDto.setRecordId(recordId == null ? 0L : recordId);
+ stockInventoryService.addStockInRecordOnly(stockInventoryDto);
+ } catch (ServiceException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceException("閫�鏂欏叆搴撹褰曚繚瀛樺け璐ワ細" + ex.getMessage());
+ }
+ }
+
private List<ProductionOrderPickDto> resolvePickItems(ProductionOrderPickDto dto) {
// 瑙f瀽鏂板鍦烘櫙鐨勯鏂欐槑缁嗛泦鍚堛��
if (dto == null) {
--
Gitblit v1.9.3