From 02a98973275c0c792e5fefdacdbea272b053f886 Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期二, 28 四月 2026 11:06:00 +0800
Subject: [PATCH] feat:1.补料和补料记录 2.退料

---
 src/main/java/com/ruoyi/production/bean/dto/ProductionOrderPickDto.java                   |    6 +
 src/main/java/com/ruoyi/production/bean/vo/ProductionOrderPickRecordVo.java               |   11 +
 src/main/resources/mapper/production/ProductionOrderPickMapper.xml                        |    5 
 src/main/resources/mapper/production/ProductionOrderPickRecordMapper.xml                  |   21 +++
 src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickRecordServiceImpl.java |   12 ++
 src/main/java/com/ruoyi/production/service/ProductionOrderPickRecordService.java          |    5 
 src/main/java/com/ruoyi/production/mapper/ProductionOrderPickRecordMapper.java            |    5 
 src/main/java/com/ruoyi/production/pojo/ProductionOrderPick.java                          |   13 ++
 src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java       |  248 +++++++++++++++++++++++++++++++++++++++-
 src/main/java/com/ruoyi/production/pojo/ProductionOrderPickRecord.java                    |    3 
 src/main/java/com/ruoyi/production/controller/ProductionOrderPickRecordController.java    |   19 +++
 src/main/java/com/ruoyi/production/bean/dto/ProductionOrderPickRecordDto.java             |   12 ++
 12 files changed, 348 insertions(+), 12 deletions(-)

diff --git a/src/main/java/com/ruoyi/production/bean/dto/ProductionOrderPickDto.java b/src/main/java/com/ruoyi/production/bean/dto/ProductionOrderPickDto.java
index 0c7f5cc..1b226ad 100644
--- a/src/main/java/com/ruoyi/production/bean/dto/ProductionOrderPickDto.java
+++ b/src/main/java/com/ruoyi/production/bean/dto/ProductionOrderPickDto.java
@@ -33,6 +33,12 @@
     @Schema(description = "澶囨敞")
     private String remark;
 
+    @Schema(description = "琛ユ枡鍘熷洜")
+    private String feedingReason;
+
+    @Schema(description = "鏈琛ユ枡鏁伴噺")
+    private BigDecimal feedingQuantity;
+
     @Schema(description = "棰嗘枡鏄庣粏鍒楄〃")
     @JsonAlias({"dto", "productionOrderPickDto"})
     private List<ProductionOrderPickDto> pickList;
diff --git a/src/main/java/com/ruoyi/production/bean/dto/ProductionOrderPickRecordDto.java b/src/main/java/com/ruoyi/production/bean/dto/ProductionOrderPickRecordDto.java
new file mode 100644
index 0000000..6954c4a
--- /dev/null
+++ b/src/main/java/com/ruoyi/production/bean/dto/ProductionOrderPickRecordDto.java
@@ -0,0 +1,12 @@
+package com.ruoyi.production.bean.dto;
+
+import com.ruoyi.production.pojo.ProductionOrderPickRecord;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(name = "ProductionOrderPickRecordDto", description = "棰嗘枡璁板綍鏌ヨ鍙傛暟")
+public class ProductionOrderPickRecordDto extends ProductionOrderPickRecord {
+}
diff --git a/src/main/java/com/ruoyi/production/bean/vo/ProductionOrderPickRecordVo.java b/src/main/java/com/ruoyi/production/bean/vo/ProductionOrderPickRecordVo.java
index b88aa8a..aea1c85 100644
--- a/src/main/java/com/ruoyi/production/bean/vo/ProductionOrderPickRecordVo.java
+++ b/src/main/java/com/ruoyi/production/bean/vo/ProductionOrderPickRecordVo.java
@@ -1,9 +1,12 @@
 package com.ruoyi.production.bean.vo;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.ruoyi.production.pojo.ProductionOrderPickRecord;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
 
 @Data
 @EqualsAndHashCode(callSuper = true)
@@ -21,5 +24,11 @@
 
     @Schema(description = "鍗曚綅")
     private String unit;
-}
 
+    @Schema(description = "琛ユ枡浜�")
+    private String supplementUserName;
+
+    @Schema(description = "琛ユ枡鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime supplementTime;
+}
diff --git a/src/main/java/com/ruoyi/production/controller/ProductionOrderPickRecordController.java b/src/main/java/com/ruoyi/production/controller/ProductionOrderPickRecordController.java
index a266c8e..05e1a50 100644
--- a/src/main/java/com/ruoyi/production/controller/ProductionOrderPickRecordController.java
+++ b/src/main/java/com/ruoyi/production/controller/ProductionOrderPickRecordController.java
@@ -1,7 +1,17 @@
 package com.ruoyi.production.controller;
 
+import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.production.bean.dto.ProductionOrderPickRecordDto;
+import com.ruoyi.production.bean.vo.ProductionOrderPickRecordVo;
+import com.ruoyi.production.service.ProductionOrderPickRecordService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
 
 /**
  * <p>
@@ -13,6 +23,15 @@
  */
 @RestController
 @RequestMapping("/productionOrderPickRecord")
+@Tag(name = "鐢熶骇璁㈠崟棰嗘枡璁板綍")
+@RequiredArgsConstructor
 public class ProductionOrderPickRecordController {
 
+    private final ProductionOrderPickRecordService productionOrderPickRecordService;
+
+    @GetMapping("/feeding")
+    @Operation(summary = "鏌ヨ琛ユ枡璁板綍")
+    public R<List<ProductionOrderPickRecordVo>> listFeedingRecord(ProductionOrderPickRecordDto dto) {
+        return R.ok(productionOrderPickRecordService.listFeedingRecord(dto));
+    }
 }
diff --git a/src/main/java/com/ruoyi/production/mapper/ProductionOrderPickRecordMapper.java b/src/main/java/com/ruoyi/production/mapper/ProductionOrderPickRecordMapper.java
index dc909cd..6b18e1c 100644
--- a/src/main/java/com/ruoyi/production/mapper/ProductionOrderPickRecordMapper.java
+++ b/src/main/java/com/ruoyi/production/mapper/ProductionOrderPickRecordMapper.java
@@ -1,6 +1,8 @@
 package com.ruoyi.production.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.production.bean.dto.ProductionOrderPickRecordDto;
+import com.ruoyi.production.bean.vo.ProductionOrderPickRecordVo;
 import com.ruoyi.production.bean.vo.ProductionOrderPickVo;
 import com.ruoyi.production.pojo.ProductionOrderPickRecord;
 import org.apache.ibatis.annotations.Mapper;
@@ -20,5 +22,6 @@
 public interface ProductionOrderPickRecordMapper extends BaseMapper<ProductionOrderPickRecord> {
 
     List<ProductionOrderPickVo> listPickedDetailByOrderId(@Param("productionOrderId") Long productionOrderId);
-}
 
+    List<ProductionOrderPickRecordVo> listFeedingRecord(ProductionOrderPickRecordDto dto);
+}
diff --git a/src/main/java/com/ruoyi/production/pojo/ProductionOrderPick.java b/src/main/java/com/ruoyi/production/pojo/ProductionOrderPick.java
index a15270c..61c5d19 100644
--- a/src/main/java/com/ruoyi/production/pojo/ProductionOrderPick.java
+++ b/src/main/java/com/ruoyi/production/pojo/ProductionOrderPick.java
@@ -66,6 +66,19 @@
     @Schema(description = "闇�姹傛暟閲�")
     private BigDecimal demandedQuantity;
 
+    @Schema(description = "琛ユ枡鎬婚噺")
+    private BigDecimal  feedingQty;
+
+    @Schema(description = "閫�鏂欐暟閲�")
+    private BigDecimal  returnQty;
+
+    @Schema(description = "瀹為檯鏁伴噺")
+    private BigDecimal  actualQty;
+
+    @Schema(description = "鏄惁宸查��鏂�")
+    @TableField("is_returned")
+    private Boolean returned;
+
     @Schema(description = "鏄惁bom棰嗘枡")
     @TableField("is_bom")
     private Boolean bom;
diff --git a/src/main/java/com/ruoyi/production/pojo/ProductionOrderPickRecord.java b/src/main/java/com/ruoyi/production/pojo/ProductionOrderPickRecord.java
index 75bf484..4f0dc49 100644
--- a/src/main/java/com/ruoyi/production/pojo/ProductionOrderPickRecord.java
+++ b/src/main/java/com/ruoyi/production/pojo/ProductionOrderPickRecord.java
@@ -57,6 +57,9 @@
     @Schema(description = "澶囨敞")
     private String remark;
 
+    @Schema(description = "琛ユ枡鍘熷洜")
+    private String feedingReason;
+
     @Schema(description = "鍒涘缓鏃堕棿")
     @TableField(fill = FieldFill.INSERT)
     private LocalDateTime createTime;
diff --git a/src/main/java/com/ruoyi/production/service/ProductionOrderPickRecordService.java b/src/main/java/com/ruoyi/production/service/ProductionOrderPickRecordService.java
index 4787929..ebd6e27 100644
--- a/src/main/java/com/ruoyi/production/service/ProductionOrderPickRecordService.java
+++ b/src/main/java/com/ruoyi/production/service/ProductionOrderPickRecordService.java
@@ -1,7 +1,11 @@
 package com.ruoyi.production.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.production.bean.dto.ProductionOrderPickRecordDto;
+import com.ruoyi.production.bean.vo.ProductionOrderPickRecordVo;
 import com.ruoyi.production.pojo.ProductionOrderPickRecord;
+
+import java.util.List;
 
 /**
  * <p>
@@ -13,4 +17,5 @@
  */
 public interface ProductionOrderPickRecordService extends IService<ProductionOrderPickRecord> {
 
+    List<ProductionOrderPickRecordVo> listFeedingRecord(ProductionOrderPickRecordDto dto);
 }
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickRecordServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickRecordServiceImpl.java
index e852999..1cf7c1e 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickRecordServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickRecordServiceImpl.java
@@ -1,10 +1,15 @@
 package com.ruoyi.production.service.impl;
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.production.bean.dto.ProductionOrderPickRecordDto;
+import com.ruoyi.production.bean.vo.ProductionOrderPickRecordVo;
 import com.ruoyi.production.mapper.ProductionOrderPickRecordMapper;
 import com.ruoyi.production.pojo.ProductionOrderPickRecord;
 import com.ruoyi.production.service.ProductionOrderPickRecordService;
 import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
 
 /**
  * <p>
@@ -17,4 +22,11 @@
 @Service
 public class ProductionOrderPickRecordServiceImpl extends ServiceImpl<ProductionOrderPickRecordMapper, ProductionOrderPickRecord> implements ProductionOrderPickRecordService {
 
+    @Override
+    public List<ProductionOrderPickRecordVo> listFeedingRecord(ProductionOrderPickRecordDto dto) {
+        if (dto == null || dto.getProductionOrderId() == null || dto.getPickId() == null) {
+            return Collections.emptyList();
+        }
+        return baseMapper.listFeedingRecord(dto);
+    }
 }
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 8f2f728..beedbb5 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
@@ -39,6 +39,9 @@
 @RequiredArgsConstructor
 public class ProductionOrderPickServiceImpl extends ServiceImpl<ProductionOrderPickMapper, ProductionOrderPick> implements ProductionOrderPickService {
 
+    private static final byte PICK_TYPE_NORMAL = 1;
+    private static final byte PICK_TYPE_FEEDING = 2;
+
     private final ProductionOrderMapper productionOrderMapper;
     private final ProductionOperationTaskMapper productionOperationTaskMapper;
     private final ProductionOrderPickRecordMapper productionOrderPickRecordMapper;
@@ -68,6 +71,7 @@
             orderPick.setTechnologyOperationId(resolvedDto.getTechnologyOperationId());
             orderPick.setDemandedQuantity(resolvedDto.getDemandedQuantity());
             orderPick.setBom(resolvedDto.getBom());
+            orderPick.setReturned(false);
             baseMapper.insert(orderPick);
 
             insertPickRecord(orderPick.getId(),
@@ -79,7 +83,8 @@
                     BigDecimal.ZERO,
                     resolvedDto.getPickQuantity(),
                     resolvedDto.getPickType(),
-                    resolvedDto.getRemark());
+                    resolvedDto.getRemark(),
+                    resolvedDto.getFeedingReason());
         }
         return true;
     }
@@ -105,6 +110,15 @@
         Map<Long, ProductionOrderPick> existingPickMap = existingPickList.stream()
                 .filter(item -> item.getId() != null)
                 .collect(Collectors.toMap(ProductionOrderPick::getId, Function.identity(), (a, b) -> a));
+
+        if (isFeedingRequest(dto)) {
+            processFeedingPickItems(dto, existingPickMap, productionOrderId);
+            return true;
+        }
+        if (isReturnRequest(dto)) {
+            processReturnPickItems(dto, existingPickMap, productionOrderId);
+            return true;
+        }
 
         processDeletePickIds(dto, existingPickMap, productionOrderId);
 
@@ -174,7 +188,8 @@
                     oldQuantity,
                     BigDecimal.ZERO,
                     rootDto.getPickType(),
-                    rootDto.getRemark());
+                    rootDto.getRemark(),
+                    rootDto.getFeedingReason());
             existingPickMap.remove(deleteId);
         }
     }
@@ -191,7 +206,7 @@
                 .filter(item -> item.getId() != null)
                 .filter(item -> Objects.equals(item.getProductionOrderId(), productionOrderId))
                 .filter(item -> !keepPickIdSet.contains(item.getId()))
-                .collect(Collectors.toList());
+                .toList();
         for (ProductionOrderPick missingPick : missingPickList) {
             String oldBatchNo = resolveInventoryBatchNoFromStored(missingPick.getBatchNo());
             BigDecimal oldQuantity = defaultDecimal(missingPick.getQuantity());
@@ -209,7 +224,8 @@
                     oldQuantity,
                     BigDecimal.ZERO,
                     rootDto.getPickType(),
-                    rootDto.getRemark());
+                    rootDto.getRemark(),
+                    rootDto.getFeedingReason());
             existingPickMap.remove(missingPick.getId());
         }
     }
@@ -230,6 +246,7 @@
         orderPick.setTechnologyOperationId(dto.getTechnologyOperationId());
         orderPick.setDemandedQuantity(dto.getDemandedQuantity());
         orderPick.setBom(dto.getBom());
+        orderPick.setReturned(false);
         baseMapper.insert(orderPick);
 
         insertPickRecord(orderPick.getId(),
@@ -241,7 +258,111 @@
                 BigDecimal.ZERO,
                 dto.getPickQuantity(),
                 dto.getPickType(),
-                dto.getRemark());
+                dto.getRemark(),
+                dto.getFeedingReason());
+    }
+
+    private void processFeedingPickItems(ProductionOrderPickDto rootDto,
+                                         Map<Long, ProductionOrderPick> existingPickMap,
+                                         Long productionOrderId) {
+        List<ProductionOrderPickDto> pickItems = resolveUpdateItems(rootDto);
+        for (int i = 0; i < pickItems.size(); i++) {
+            int rowNo = i + 1;
+            ProductionOrderPickDto resolvedDto = mergeDto(rootDto, pickItems.get(i));
+            if (isEmptyUpdateItem(resolvedDto)) {
+                continue;
+            }
+            if (!isFeedingPick(resolvedDto)) {
+                throw new ServiceException("琛ユ枡璇锋眰涓殑棰嗘枡绫诲瀷蹇呴』鍏ㄩ儴涓�2");
+            }
+            if (resolvedDto.getProductionOrderId() == null) {
+                resolvedDto.setProductionOrderId(productionOrderId);
+            }
+            validateFeedingParam(resolvedDto, rowNo);
+
+            ProductionOrderPick oldPick = existingPickMap.get(resolvedDto.getId());
+            if (oldPick == null || !Objects.equals(oldPick.getProductionOrderId(), productionOrderId)) {
+                throw new ServiceException("绗�" + rowNo + "鏉¢鏂欒褰曚笉瀛樺湪鎴栦笉灞炰簬褰撳墠璁㈠崟");
+            }
+            addFeedingPick(resolvedDto, oldPick, rowNo);
+        }
+    }
+
+    private void addFeedingPick(ProductionOrderPickDto dto, ProductionOrderPick oldPick, int rowNo) {
+        if (dto.getProductModelId() != null && !Objects.equals(dto.getProductModelId(), oldPick.getProductModelId())) {
+            throw new ServiceException("绗�" + rowNo + "鏉¤ˉ鏂欎骇鍝佽鏍间笌棰嗘枡璁板綍涓嶄竴鑷�");
+        }
+        Long productModelId = oldPick.getProductModelId();
+        List<String> batchNoList = resolveBatchNoList(dto);
+        String inventoryBatchNo = batchNoList.isEmpty()
+                ? resolveInventoryBatchNoFromStored(oldPick.getBatchNo())
+                : pickInventoryBatchNo(batchNoList);
+        BigDecimal feedingQuantity = dto.getFeedingQuantity();
+
+        subtractInventory(productModelId, inventoryBatchNo, feedingQuantity, rowNo);
+
+        BigDecimal beforeFeedingQty = sumFeedingQuantity(dto.getProductionOrderId(), oldPick.getId());
+        BigDecimal afterFeedingQty = beforeFeedingQty.add(feedingQuantity);
+        insertPickRecord(oldPick.getId(),
+                dto.getProductionOrderId(),
+                dto.getProductionOperationTaskId(),
+                productModelId,
+                inventoryBatchNo,
+                feedingQuantity,
+                beforeFeedingQty,
+                afterFeedingQty,
+                PICK_TYPE_FEEDING,
+                dto.getRemark(),
+                dto.getFeedingReason());
+
+        ProductionOrderPick updatePick = new ProductionOrderPick();
+        updatePick.setId(oldPick.getId());
+        updatePick.setFeedingQty(afterFeedingQty);
+        updatePick.setActualQty(calculateActualQty(oldPick, afterFeedingQty));
+        int affected = baseMapper.updateById(updatePick);
+        if (affected <= 0) {
+            throw new ServiceException("绗�" + rowNo + "鏉¤ˉ鏂欐�婚噺鏇存柊澶辫触");
+        }
+        oldPick.setFeedingQty(afterFeedingQty);
+        oldPick.setActualQty(updatePick.getActualQty());
+    }
+
+    private void processReturnPickItems(ProductionOrderPickDto rootDto,
+                                        Map<Long, ProductionOrderPick> existingPickMap,
+                                        Long productionOrderId) {
+        List<ProductionOrderPickDto> pickItems = resolveUpdateItems(rootDto);
+        for (int i = 0; i < pickItems.size(); i++) {
+            int rowNo = i + 1;
+            ProductionOrderPickDto resolvedDto = mergeDto(rootDto, pickItems.get(i));
+            if (isEmptyUpdateItem(resolvedDto)) {
+                continue;
+            }
+            if (resolvedDto.getProductionOrderId() == null) {
+                resolvedDto.setProductionOrderId(productionOrderId);
+            }
+            validateReturnParam(resolvedDto, rowNo);
+
+            ProductionOrderPick oldPick = existingPickMap.get(resolvedDto.getId());
+            if (oldPick == null || !Objects.equals(oldPick.getProductionOrderId(), productionOrderId)) {
+                throw new ServiceException("绗�" + rowNo + "鏉¢鏂欒褰曚笉瀛樺湪鎴栦笉灞炰簬褰撳墠璁㈠崟");
+            }
+            updateReturnPick(resolvedDto, oldPick, rowNo);
+        }
+    }
+
+    private void updateReturnPick(ProductionOrderPickDto dto, ProductionOrderPick oldPick, int rowNo) {
+        ProductionOrderPick updatePick = new ProductionOrderPick();
+        updatePick.setId(oldPick.getId());
+        updatePick.setReturnQty(dto.getReturnQty());
+        updatePick.setActualQty(dto.getActualQty());
+        updatePick.setReturned(true);
+        int affected = baseMapper.updateById(updatePick);
+        if (affected <= 0) {
+            throw new ServiceException("绗�" + rowNo + "鏉¢��鏂欎俊鎭洿鏂板け璐�");
+        }
+        oldPick.setReturnQty(updatePick.getReturnQty());
+        oldPick.setActualQty(updatePick.getActualQty());
+        oldPick.setReturned(true);
     }
 
     private void updateExistingPick(ProductionOrderPickDto dto,
@@ -304,7 +425,8 @@
                     oldQuantity,
                     newQuantity,
                     dto.getPickType(),
-                    dto.getRemark());
+                    dto.getRemark(),
+                    dto.getFeedingReason());
         }
     }
 
@@ -317,7 +439,8 @@
                                   BigDecimal beforeQuantity,
                                   BigDecimal afterQuantity,
                                   Byte pickType,
-                                  String remark) {
+                                  String remark,
+                                  String feedingReason) {
         ProductionOrderPickRecord pickRecord = new ProductionOrderPickRecord();
         pickRecord.setPickId(pickId);
         pickRecord.setProductionOrderId(productionOrderId);
@@ -327,8 +450,9 @@
         pickRecord.setPickQuantity(defaultDecimal(pickQuantity));
         pickRecord.setBeforeQuantity(defaultDecimal(beforeQuantity));
         pickRecord.setAfterQuantity(defaultDecimal(afterQuantity));
-        pickRecord.setPickType(pickType == null ? (byte) 1 : pickType);
+        pickRecord.setPickType(pickType == null ? PICK_TYPE_NORMAL : pickType);
         pickRecord.setRemark(remark);
+        pickRecord.setFeedingReason(feedingReason);
         productionOrderPickRecordMapper.insert(pickRecord);
     }
 
@@ -405,6 +529,11 @@
                 && StringUtils.isEmpty(dto.getBatchNo())
                 && (dto.getBatchNoList() == null || dto.getBatchNoList().isEmpty())
                 && dto.getPickType() == null
+                && dto.getFeedingQuantity() == null
+                && StringUtils.isEmpty(dto.getFeedingReason())
+                && dto.getReturnQty() == null
+                && dto.getActualQty() == null
+                && dto.getReturned() == null
                 && dto.getProductionOperationTaskId() == null
                 && dto.getTechnologyOperationId() == null
                 && StringUtils.isEmpty(dto.getOperationName())
@@ -440,10 +569,15 @@
             merged.setPickQuantity(itemDto.getPickQuantity());
             merged.setPickType(itemDto.getPickType());
             merged.setRemark(itemDto.getRemark());
+            merged.setFeedingReason(itemDto.getFeedingReason());
+            merged.setFeedingQuantity(itemDto.getFeedingQuantity());
             merged.setTechnologyOperationId(itemDto.getTechnologyOperationId());
             merged.setOperationName(itemDto.getOperationName());
             merged.setDemandedQuantity(itemDto.getDemandedQuantity());
             merged.setBom(itemDto.getBom());
+            merged.setReturnQty(itemDto.getReturnQty());
+            merged.setActualQty(itemDto.getActualQty());
+            merged.setReturned(itemDto.getReturned());
         }
         if (merged.getId() == null) {
             merged.setId(rootDto.getId());
@@ -472,6 +606,12 @@
         if (merged.getRemark() == null) {
             merged.setRemark(rootDto.getRemark());
         }
+        if (merged.getFeedingReason() == null) {
+            merged.setFeedingReason(rootDto.getFeedingReason());
+        }
+        if (merged.getFeedingQuantity() == null) {
+            merged.setFeedingQuantity(rootDto.getFeedingQuantity());
+        }
         if (merged.getTechnologyOperationId() == null) {
             merged.setTechnologyOperationId(rootDto.getTechnologyOperationId());
         }
@@ -483,6 +623,15 @@
         }
         if (merged.getBom() == null) {
             merged.setBom(rootDto.getBom());
+        }
+        if (merged.getReturnQty() == null) {
+            merged.setReturnQty(rootDto.getReturnQty());
+        }
+        if (merged.getActualQty() == null) {
+            merged.setActualQty(rootDto.getActualQty());
+        }
+        if (merged.getReturned() == null) {
+            merged.setReturned(rootDto.getReturned());
         }
         return merged;
     }
@@ -497,9 +646,89 @@
         if (dto.getPickQuantity() == null || dto.getPickQuantity().compareTo(BigDecimal.ZERO) <= 0) {
             throw new ServiceException("绗�" + rowNo + "鏉¢鏂欐暟閲忓繀椤诲ぇ浜�0");
         }
-        if (dto.getPickType() != null && dto.getPickType() != 1 && dto.getPickType() != 2) {
+        if (dto.getPickType() != null && dto.getPickType() != PICK_TYPE_NORMAL && dto.getPickType() != PICK_TYPE_FEEDING) {
             throw new ServiceException("绗�" + rowNo + "鏉¢鏂欑被鍨嬪彧鑳芥槸1鎴�2");
         }
+    }
+
+    private void validateFeedingParam(ProductionOrderPickDto dto, int rowNo) {
+        if (dto.getProductionOrderId() == null) {
+            throw new ServiceException("绗�" + rowNo + "鏉$敓浜ц鍗旾D涓嶈兘涓虹┖");
+        }
+        if (dto.getId() == null) {
+            throw new ServiceException("绗�" + rowNo + "鏉¢鏂橧D涓嶈兘涓虹┖");
+        }
+        if (dto.getFeedingQuantity() == null || dto.getFeedingQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+            throw new ServiceException("绗�" + rowNo + "鏉℃湰娆¤ˉ鏂欐暟閲忓繀椤诲ぇ浜�0");
+        }
+        if (!isFeedingPick(dto)) {
+            throw new ServiceException("绗�" + rowNo + "鏉¤ˉ鏂欑被鍨嬪繀椤讳负2");
+        }
+    }
+
+    private void validateReturnParam(ProductionOrderPickDto dto, int rowNo) {
+        if (dto.getProductionOrderId() == null) {
+            throw new ServiceException("绗�" + rowNo + "鏉$敓浜ц鍗旾D涓嶈兘涓虹┖");
+        }
+        if (dto.getId() == null) {
+            throw new ServiceException("绗�" + rowNo + "鏉¢鏂橧D涓嶈兘涓虹┖");
+        }
+        if (dto.getReturnQty() == null || dto.getReturnQty().compareTo(BigDecimal.ZERO) < 0) {
+            throw new ServiceException("绗�" + rowNo + "鏉¢��鏂欐暟閲忎笉鑳戒负绌轰笖涓嶈兘灏忎簬0");
+        }
+        if (dto.getActualQty() == null || dto.getActualQty().compareTo(BigDecimal.ZERO) < 0) {
+            throw new ServiceException("绗�" + rowNo + "鏉″疄闄呮暟閲忎笉鑳戒负绌轰笖涓嶈兘灏忎簬0");
+        }
+    }
+
+    private boolean isFeedingRequest(ProductionOrderPickDto dto) {
+        if (isFeedingPick(dto)) {
+            return true;
+        }
+        if (dto.getPickList() == null || dto.getPickList().isEmpty()) {
+            return false;
+        }
+        return dto.getPickList().stream()
+                .filter(Objects::nonNull)
+                .anyMatch(this::isFeedingPick);
+    }
+
+    private boolean isFeedingPick(ProductionOrderPickDto dto) {
+        return dto != null && Objects.equals(dto.getPickType(), PICK_TYPE_FEEDING);
+    }
+
+    private boolean isReturnRequest(ProductionOrderPickDto dto) {
+        if (isReturnPick(dto)) {
+            return true;
+        }
+        if (dto.getPickList() == null || dto.getPickList().isEmpty()) {
+            return false;
+        }
+        return dto.getPickList().stream()
+                .filter(Objects::nonNull)
+                .anyMatch(this::isReturnPick);
+    }
+
+    private boolean isReturnPick(ProductionOrderPickDto dto) {
+        return dto != null && Boolean.TRUE.equals(dto.getReturned());
+    }
+
+    private BigDecimal sumFeedingQuantity(Long productionOrderId, Long pickId) {
+        List<ProductionOrderPickRecord> feedingRecords = productionOrderPickRecordMapper.selectList(
+                Wrappers.<ProductionOrderPickRecord>lambdaQuery()
+                        .eq(ProductionOrderPickRecord::getProductionOrderId, productionOrderId)
+                        .eq(ProductionOrderPickRecord::getPickId, pickId)
+                        .eq(ProductionOrderPickRecord::getPickType, PICK_TYPE_FEEDING));
+        return feedingRecords.stream()
+                .map(ProductionOrderPickRecord::getPickQuantity)
+                .map(this::defaultDecimal)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+    }
+
+    private BigDecimal calculateActualQty(ProductionOrderPick pick, BigDecimal feedingQty) {
+        return defaultDecimal(pick.getQuantity())
+                .add(defaultDecimal(feedingQty))
+                .subtract(defaultDecimal(pick.getReturnQty()));
     }
 
     private String normalizeBatchNo(String batchNo) {
@@ -654,4 +883,3 @@
         return value == null ? BigDecimal.ZERO : value;
     }
 }
-
diff --git a/src/main/resources/mapper/production/ProductionOrderPickMapper.xml b/src/main/resources/mapper/production/ProductionOrderPickMapper.xml
index 7dd8a1f..8b0a0d7 100644
--- a/src/main/resources/mapper/production/ProductionOrderPickMapper.xml
+++ b/src/main/resources/mapper/production/ProductionOrderPickMapper.xml
@@ -17,12 +17,17 @@
         <result column="operation_name" property="operationName" />
         <result column="technology_operation_id" property="technologyOperationId" />
         <result column="demanded_quantity" property="demandedQuantity" />
+        <result column="feeding_qty" property="feedingQty" />
+        <result column="return_qty" property="returnQty" />
+        <result column="actual_qty" property="actualQty" />
+        <result column="is_returned" property="returned" />
         <result column="is_bom" property="bom" />
     </resultMap>
 
     <select id="listPickedDetailByOrderId" resultType="com.ruoyi.production.bean.vo.ProductionOrderPickVo">
         select pop.*,
                pop.is_bom as bom,
+               pop.is_returned as returned,
                pop.quantity as pickQuantity,
                p.product_name as productName,
                pm.model as model,
diff --git a/src/main/resources/mapper/production/ProductionOrderPickRecordMapper.xml b/src/main/resources/mapper/production/ProductionOrderPickRecordMapper.xml
index 818f4d9..4614ef7 100644
--- a/src/main/resources/mapper/production/ProductionOrderPickRecordMapper.xml
+++ b/src/main/resources/mapper/production/ProductionOrderPickRecordMapper.xml
@@ -15,6 +15,7 @@
         <result column="after_quantity" property="afterQuantity" />
         <result column="pick_type" property="pickType" />
         <result column="remark" property="remark" />
+        <result column="feeding_reason" property="feedingReason" />
         <result column="create_time" property="createTime" />
         <result column="update_time" property="updateTime" />
         <result column="create_user" property="createUser" />
@@ -36,4 +37,24 @@
         order by popr.create_time desc, popr.id desc
     </select>
 
+    <select id="listFeedingRecord" resultType="com.ruoyi.production.bean.vo.ProductionOrderPickRecordVo">
+        select popr.*,
+               poro.operation_name as operationName,
+               p.product_name as productName,
+               pm.model as model,
+               pm.unit as unit,
+               coalesce(su.nick_name, su.user_name) as supplementUserName,
+               popr.create_time as supplementTime
+        from production_order_pick_record popr
+                 left join production_operation_task pot on popr.production_operation_task_id = pot.id
+                 left join production_order_routing_operation poro on pot.technology_routing_operation_id = poro.id
+                 left join product_model pm on popr.product_model_id = pm.id
+                 left join product p on pm.product_id = p.id
+                 left join sys_user su on popr.create_user = su.user_id
+        where popr.production_order_id = #{productionOrderId}
+          and popr.pick_id = #{pickId}
+          and popr.pick_type = 2
+        order by popr.create_time desc, popr.id desc
+    </select>
+
 </mapper>

--
Gitblit v1.9.3