gongchunyi
5 天以前 d5e8cbc39a363ec8a6cdc80360e2112fb3b2a9c0
fix: 生产订单领料台账和详情调整
已添加1个文件
已修改6个文件
170 ■■■■■ 文件已修改
doc/20261108_quality_inspect_first_inspect.sql 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderPickVo.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionOrderPickController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductionOrderPickService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/pojo/QualityInspect.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionOrderPickRecordMapper.xml 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20261108_quality_inspect_first_inspect.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
-- è¿‡ç¨‹æ£€éªŒæ–°å¢žâ€œé¦–检”标记
-- æ—¥æœŸï¼š2026-11-08
-- è¯´æ˜Žï¼šé¦–检仅用于过程检验(inspect_type = 1),0=否,1=是
ALTER TABLE `quality_inspect`
    ADD COLUMN `is_first_inspect` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否首检(0:否;1:是)' AFTER `process`;
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderPickVo.java
@@ -29,4 +29,10 @@
    @Schema(description = "批号")
    private List<String> batchNoList;
    @Schema(description = "领料流水id")
    private Long recordId;
    @Schema(description = "领料主单id")
    private Long pickId;
}
src/main/java/com/ruoyi/production/controller/ProductionOrderPickController.java
@@ -45,8 +45,12 @@
    @GetMapping("/detail/{productionOrderId}")
    @Operation(summary = "查询已领料详情")
    public R<List<ProductionOrderPickVo>> listPickedDetail(@PathVariable Long productionOrderId) {
        return R.ok(productionOrderPickService.listPickedDetail(productionOrderId));
    public R<List<ProductionOrderPickVo>> listPickedDetail(
            @PathVariable Long productionOrderId,
            @RequestParam(defaultValue = "false") boolean records) {
        return R.ok(records
                ? productionOrderPickService.listPickedRecordDetail(productionOrderId)
                : productionOrderPickService.listPickedDetail(productionOrderId));
    }
}
src/main/java/com/ruoyi/production/service/ProductionOrderPickService.java
@@ -22,4 +22,7 @@
    Boolean updatePick(ProductionOrderPickDto dto);
    List<ProductionOrderPickVo> listPickedDetail(Long productionOrderId);
    /** é¢†æ–™è¯¦æƒ…:一条流水一行,用于“领料详情”弹窗 */
    List<ProductionOrderPickVo> listPickedRecordDetail(Long productionOrderId);
}
src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
@@ -31,6 +31,7 @@
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -189,11 +190,110 @@
            return Collections.emptyList();
        }
        List<ProductionOrderPickVo> detailList = baseMapper.listPickedDetailByOrderId(productionOrderId);
        fillStockQuantity(detailList);
        fillBatchNoList(detailList);
        fillSelectableBatchNoList(detailList);
        return detailList;
    }
    @Override
    public List<ProductionOrderPickVo> listPickedRecordDetail(Long productionOrderId) {
        // é¢†æ–™è¯¦æƒ…:按 production_order_pick_record ä¸€æ¡ä¸€è¡Œï¼Œå±•示每次领取量,而不是主单汇总。
        if (productionOrderId == null) {
            return Collections.emptyList();
        }
        List<ProductionOrderPickVo> detailList =
                productionOrderPickRecordMapper.listPickedDetailByOrderId(productionOrderId);
        allocateRecordReturnQuantity(detailList);
        fillStockQuantity(detailList);
        return detailList;
    }
    private void allocateRecordReturnQuantity(List<ProductionOrderPickVo> detailList) {
        // é€€æ–™æ•°é‡æŒ‰é¢†æ–™ä¸»å•汇总存储,详情按流水拆分展示时按领取量分摊到每条流水,
        // ä¿è¯â€œå®žé™…数量 = é¢†å–数量 - åˆ†æ‘Šé€€æ–™â€ä¸”同一主单各流水合计与主单一致。
        if (detailList == null || detailList.isEmpty()) {
            return;
        }
        Map<Long, List<ProductionOrderPickVo>> pickGroupMap = new LinkedHashMap<>();
        for (ProductionOrderPickVo detail : detailList) {
            if (detail == null || detail.getId() == null) {
                continue;
            }
            pickGroupMap.computeIfAbsent(detail.getId(), key -> new ArrayList<>()).add(detail);
        }
        for (List<ProductionOrderPickVo> pickGroup : pickGroupMap.values()) {
            BigDecimal totalReturnQty = BigDecimal.ZERO;
            BigDecimal totalRecordQuantity = BigDecimal.ZERO;
            for (ProductionOrderPickVo detail : pickGroup) {
                totalReturnQty = defaultDecimal(detail.getReturnQty());
                totalRecordQuantity = totalRecordQuantity.add(recordQuantity(detail));
            }
            if (totalRecordQuantity.compareTo(BigDecimal.ZERO) <= 0) {
                for (ProductionOrderPickVo detail : pickGroup) {
                    detail.setReturnQty(BigDecimal.ZERO);
                    detail.setActualQty(BigDecimal.ZERO);
                }
                continue;
            }
            BigDecimal effectiveReturnQty = totalReturnQty.min(totalRecordQuantity);
            BigDecimal allocatedReturnQty = BigDecimal.ZERO;
            for (int i = 0; i < pickGroup.size(); i++) {
                ProductionOrderPickVo detail = pickGroup.get(i);
                BigDecimal rowQuantity = recordQuantity(detail);
                BigDecimal returnQty;
                if (i == pickGroup.size() - 1) {
                    returnQty = effectiveReturnQty.subtract(allocatedReturnQty);
                } else {
                    returnQty = effectiveReturnQty.multiply(rowQuantity)
                            .divide(totalRecordQuantity, 4, RoundingMode.HALF_UP)
                            .setScale(2, RoundingMode.HALF_UP);
                }
                if (returnQty.compareTo(rowQuantity) > 0) {
                    returnQty = rowQuantity;
                }
                if (returnQty.compareTo(BigDecimal.ZERO) < 0) {
                    returnQty = BigDecimal.ZERO;
                }
                detail.setReturnQty(returnQty);
                detail.setActualQty(rowQuantity.subtract(returnQty));
                allocatedReturnQty = allocatedReturnQty.add(returnQty);
            }
        }
    }
    private BigDecimal recordQuantity(ProductionOrderPickVo detail) {
        return defaultDecimal(detail.getPickQuantity()).add(defaultDecimal(detail.getFeedingQty()));
    }
    private void fillStockQuantity(List<ProductionOrderPickVo> detailList) {
        // æ‰¹é‡æŸ¥è¯¢æ˜Žç»†ä¸­æ¶‰åŠè§„格的当前库存数量,供前端领料台账展示。
        if (detailList == null || detailList.isEmpty()) {
            return;
        }
        Set<Long> productModelIdSet = detailList.stream()
                .map(ProductionOrderPickVo::getProductModelId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        if (productModelIdSet.isEmpty()) {
            return;
        }
        List<StockInventory> stockList = stockInventoryMapper.selectList(
                Wrappers.<StockInventory>lambdaQuery()
                        .in(StockInventory::getProductModelId, productModelIdSet));
        Map<Long, BigDecimal> stockQuantityMap = new HashMap<>();
        for (StockInventory stockItem : stockList) {
            if (stockItem == null || stockItem.getProductModelId() == null) {
                continue;
            }
            stockQuantityMap.merge(stockItem.getProductModelId(),
                    defaultDecimal(stockItem.getQualitity()), BigDecimal::add);
        }
        for (ProductionOrderPickVo detail : detailList) {
            detail.setStockQuantity(stockQuantityMap.getOrDefault(detail.getProductModelId(), BigDecimal.ZERO));
        }
    }
    private void processDeletePickIds(ProductionOrderPickDto rootDto,
                                      Map<Long, ProductionOrderPick> existingPickMap,
                                      Long productionOrderId) {
src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
@@ -64,6 +64,13 @@
    private String process;
    /**
     * æ˜¯å¦é¦–检(过程检验,0:否;1:是)
     */
    @Excel(name = "首检", readConverterExp = "0=否,1=是")
    @TableField("is_first_inspect")
    private Integer isFirstInspect;
    /**
     * æ£€éªŒå‘˜
     */
    @Excel(name = "检验员")
src/main/resources/mapper/production/ProductionOrderPickRecordMapper.xml
@@ -22,19 +22,49 @@
        <result column="dept_id" property="deptId" />
    </resultMap>
    <!-- é¢†æ–™è¯¦æƒ…:一条领料流水一行,展示每次实际领取(含补料)的数量,而不是主单汇总 -->
    <select id="listPickedDetailByOrderId" resultType="com.ruoyi.production.bean.vo.ProductionOrderPickVo">
        select popr.*,
               poro.operation_name as operationName,
        select popr.pick_id as id,
               popr.id as recordId,
               popr.pick_id as pickId,
               popr.production_order_id as productionOrderId,
               popr.production_operation_task_id as productionOperationTaskId,
               pop.technology_operation_id as technologyOperationId,
               pop.operation_name as operationName,
               popr.product_model_id as productModelId,
               popr.batch_no as batchNo,
               popr.before_quantity as beforeQuantity,
               popr.after_quantity as afterQuantity,
               popr.pick_type as pickType,
               popr.remark,
               popr.create_time as createTime,
               popr.update_time as updateTime,
               popr.create_user as createUser,
               popr.dept_id as deptId,
               case
                   when popr.pick_type = 2 then 0
                   else round(pop.demanded_quantity * popr.pick_quantity / nullif(pop.quantity, 0), 2)
               end as demandedQuantity,
               case
                   when popr.pick_type = 2 then popr.pick_quantity
                   else 0
               end as feedingQty,
               case
                   when popr.pick_type = 2 then 0
                   else popr.pick_quantity
               end as pickQuantity,
               pop.return_qty as returnQty,
               pop.is_returned as returned,
               pop.is_bom as bom,
               p.product_name as productName,
               pm.model as model,
               pm.unit as unit
        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.production_order_routing_operation_id = poro.id
                 join production_order_pick pop on pop.id = popr.pick_id
                 left join product_model pm on popr.product_model_id = pm.id
                 left join product p on pm.product_id = p.id
        where popr.production_order_id = #{productionOrderId}
        order by popr.create_time desc, popr.id desc
        order by popr.create_time, popr.id
    </select>
    <select id="listFeedingRecord" resultType="com.ruoyi.production.bean.vo.ProductionOrderPickRecordVo">