自动化立体仓库 - WMS系统
chen.llin
2025-12-24 43525a12afaf7368decbed730f0240a6709d8355
月结
1个文件已添加
6个文件已修改
282 ■■■■ 已修改文件
src/main/java/com/zy/asrs/entity/result/MaterialInOutRawDTO.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/result/PreviousSettleEndingQtyDTO.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/MonthlySettleMapper.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/MonthlySettleServiceImpl.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/MonthlySettleMapper.xml 85 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/monthlySettle/monthlySettle.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/monthlySettle/monthlySettle.html 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/result/MaterialInOutRawDTO.java
New file
@@ -0,0 +1,43 @@
package com.zy.asrs.entity.result;
import lombok.Data;
import java.math.BigDecimal;
/**
 * 物料出入库原始数据(未分类)
 */
@Data
public class MaterialInOutRawDTO {
    /**
     * 物料编码
     */
    private String matnr;
    /**
     * 物料名称
     */
    private String maktx;
    /**
     * 批次
     */
    private String batch;
    /**
     * 品牌
     */
    private String brand;
    /**
     * 数量(都是正数)
     */
    private BigDecimal qty;
    /**
     * 入出库类型(0:未知,1:入库,2:出库)
     */
    private Integer pakinPakoutStatus;
}
src/main/java/com/zy/asrs/entity/result/PreviousSettleEndingQtyDTO.java
@@ -26,6 +26,11 @@
    private String brand;
    
    /**
     * 物料名称
     */
    private String maktx;
    /**
     * 期末库存
     */
    private BigDecimal endingQty;
src/main/java/com/zy/asrs/mapper/MonthlySettleMapper.java
@@ -2,7 +2,7 @@
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.zy.asrs.entity.MonthlySettle;
import com.zy.asrs.entity.result.MaterialInOutStatDTO;
import com.zy.asrs.entity.result.MaterialInOutRawDTO;
import com.zy.asrs.entity.result.PreviousSettleEndingQtyDTO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -30,9 +30,14 @@
    int countUnfinishedOrdersInRange(@Param("startDate") String startDate, @Param("endDate") String endDate);
    /**
     * 统计月结时间范围内的物料出入库数量(合并入库和出库)
     * 统计月结时间范围内的物料出入库数量(从入库表查询)
     */
    List<MaterialInOutStatDTO> statisticsMaterialInOut(@Param("startDate") String startDate, @Param("endDate") String endDate);
    List<MaterialInOutRawDTO> statisticsMaterialInOutFromPakin(@Param("startDate") String startDate, @Param("endDate") String endDate);
    /**
     * 统计月结时间范围内的物料出入库数量(从出库表查询)
     */
    List<MaterialInOutRawDTO> statisticsMaterialInOutFromPakout(@Param("startDate") String startDate, @Param("endDate") String endDate);
    /**
     * 获取上一个月结的物料期末库存
src/main/java/com/zy/asrs/service/impl/MonthlySettleServiceImpl.java
@@ -7,6 +7,7 @@
import com.core.exception.CoolException;
import com.zy.asrs.entity.MonthlySettle;
import com.zy.asrs.entity.MonthlySettleDetail;
import com.zy.asrs.entity.result.MaterialInOutRawDTO;
import com.zy.asrs.entity.result.MaterialInOutStatDTO;
import com.zy.asrs.entity.result.MonthlySettleResultVO;
import com.zy.asrs.entity.result.MonthlySettleStatisticsVO;
@@ -139,21 +140,84 @@
            throw new CoolException("月结时间范围内存在未完成的订单,无法进行月结");
        }
        // 统计物料出入库数量(合并入库和出库)
        // 统计物料出入库数量(分别查询两个表,在Java代码中合并)
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String startDateStr = dateFormat.format(startDate);
        String endDateStr = dateTimeFormat.format(endDate);
        List<MaterialInOutStatDTO> materialStats = this.baseMapper.statisticsMaterialInOut(startDateStr, endDateStr);
        // 分别查询入库表和出库表
        List<MaterialInOutRawDTO> pakinDataList = this.baseMapper.statisticsMaterialInOutFromPakin(startDateStr, endDateStr);
        List<MaterialInOutRawDTO> pakoutDataList = this.baseMapper.statisticsMaterialInOutFromPakout(startDateStr, endDateStr);
        // 合并两个表的数据
        List<MaterialInOutRawDTO> rawDataList = new java.util.ArrayList<>();
        if (pakinDataList != null && !pakinDataList.isEmpty()) {
            rawDataList.addAll(pakinDataList);
        }
        if (pakoutDataList != null && !pakoutDataList.isEmpty()) {
            rawDataList.addAll(pakoutDataList);
        }
        
        // 检查是否有出入库历史订单
        if (materialStats == null || materialStats.isEmpty()) {
        if (rawDataList == null || rawDataList.isEmpty()) {
            throw new CoolException("月结时间范围内没有出入库历史订单,无法进行月结");
        }
        // 在Java代码中处理分类统计逻辑
        // 1. 处理NULL值(batch、brand、maktx)
        // 2. 根据物料编码、批次、品牌分组
        // 3. 根据 pakin_pakout_status 区分入库和出库
        Map<String, MaterialInOutStatDTO> materialStatsMap = new HashMap<>();
        Map<String, String> maktxMap = new HashMap<>(); // 存储物料名称(取第一个非空值)
        for (MaterialInOutRawDTO raw : rawDataList) {
            // 处理NULL值
            String matnr = raw.getMatnr();
            String batch = raw.getBatch() != null ? raw.getBatch() : "";
            String brand = raw.getBrand() != null ? raw.getBrand() : "";
            String maktx = raw.getMaktx() != null ? raw.getMaktx() : "";
            String key = matnr + "_" + batch + "_" + brand;
            // 保存物料名称(取第一个非空值)
            if (maktx != null && !maktx.isEmpty() && !maktxMap.containsKey(key)) {
                maktxMap.put(key, maktx);
            }
            MaterialInOutStatDTO stat = materialStatsMap.get(key);
            if (stat == null) {
                stat = new MaterialInOutStatDTO();
                stat.setMatnr(matnr);
                stat.setMaktx(maktxMap.getOrDefault(key, ""));
                stat.setBatch(batch);
                stat.setBrand(brand);
                stat.setInQty(BigDecimal.ZERO);
                stat.setOutQty(BigDecimal.ZERO);
                materialStatsMap.put(key, stat);
            }
            // 根据 pakin_pakout_status 分类统计
            // 1 = 入库,2 = 出库,0 = 未知(已在SQL中过滤)
            BigDecimal qty = raw.getQty() != null ? raw.getQty() : BigDecimal.ZERO;
            Integer status = raw.getPakinPakoutStatus();
            if (status != null && qty.compareTo(BigDecimal.ZERO) > 0) {
                if (status == 1) {
                    // 入库
                    stat.setInQty(stat.getInQty().add(qty));
                } else if (status == 2) {
                    // 出库
                    stat.setOutQty(stat.getOutQty().add(qty));
                }
            }
        }
        // 转换为List
        List<MaterialInOutStatDTO> materialStats = new java.util.ArrayList<>(materialStatsMap.values());
        // 获取上一个月结记录(用于计算期初库存)
        MonthlySettle previousSettle = getLatestSettle();
        // 获取上一个月结记录(用于计算期初库存和承接上一期的物料)
        MonthlySettle   previousSettle = getLatestSettle();
        Map<String, BigDecimal> previousEndingQtyMap = new HashMap<>();
        Map<String, PreviousSettleEndingQtyDTO> previousDetailMap = new HashMap<>(); // 存储上一期的完整明细信息
        if (previousSettle != null) {
            List<PreviousSettleEndingQtyDTO> previousDetails = this.baseMapper.getPreviousSettleEndingQty(previousSettle.getId());
            for (PreviousSettleEndingQtyDTO detail : previousDetails) {
@@ -162,6 +226,7 @@
                            (detail.getBrand() != null ? detail.getBrand() : "");
                BigDecimal endingQty = detail.getEndingQty() != null ? detail.getEndingQty() : BigDecimal.ZERO;
                previousEndingQtyMap.put(key, endingQty);
                previousDetailMap.put(key, detail); // 保存完整信息,用于后续创建明细
            }
        }
@@ -184,7 +249,17 @@
        BigDecimal totalOutQty = BigDecimal.ZERO;
        int materialCount = 0;
        // 创建月结明细
        // 创建 Map 存储本期有出入库的物料(用于判断是否需要从上一期继承)
        Map<String, MaterialInOutStatDTO> currentMaterialMap = new HashMap<>();
        for (MaterialInOutStatDTO stat : materialStats) {
            String matnr = stat.getMatnr();
            String batch = stat.getBatch() != null ? stat.getBatch() : "";
            String brand = stat.getBrand() != null ? stat.getBrand() : "";
            String key = matnr + "_" + batch + "_" + brand;
            currentMaterialMap.put(key, stat);
        }
        // 1. 处理本期有出入库的物料
        for (MaterialInOutStatDTO stat : materialStats) {
            // 1. 提取基础信息
            String matnr = stat.getMatnr();
@@ -216,6 +291,49 @@
            materialCount++;
        }
        // 2. 处理上一期存在但本期没有出入库的物料(需要承接显示)
        if (previousSettle != null && !previousDetailMap.isEmpty()) {
            for (Map.Entry<String, PreviousSettleEndingQtyDTO> entry : previousDetailMap.entrySet()) {
                String key = entry.getKey();
                // 如果本期没有出入库,但上一期有期末库存(且不为0),则需要创建一条记录
                if (!currentMaterialMap.containsKey(key)) {
                    PreviousSettleEndingQtyDTO previousDetail = entry.getValue();
                    BigDecimal previousEndingQty = previousDetail.getEndingQty() != null ? previousDetail.getEndingQty() : BigDecimal.ZERO;
                    // 如果上一期的期末库存不为0,或者即使为0也要显示(用于对比)
                    // 这里我们总是创建记录,即使上一期期末库存为0,因为用户要求"如果上一期存在过,本期库存归零也要显示一条为0的记录"
                    String matnr = previousDetail.getMatnr();
                    String batch = previousDetail.getBatch() != null ? previousDetail.getBatch() : "";
                    String brand = previousDetail.getBrand() != null ? previousDetail.getBrand() : "";
                    String maktx = previousDetail.getMaktx() != null ? previousDetail.getMaktx() : "";
                    // 本期没有出入库,所以入库和出库数量都为0
                    BigDecimal inQty = BigDecimal.ZERO;
                    BigDecimal outQty = BigDecimal.ZERO;
                    // 期初库存 = 上一期的期末库存
                    BigDecimal beginningQty = previousEndingQty;
                    // 期末库存 = 期初 + 入库 - 出库 = 期初(因为本期没有出入库)
                    BigDecimal endingQty = beginningQty;
                    // 获取当前实际库存
                    BigDecimal stockQtyDecimal = getCurrentStockQty(matnr, batch);
                    // 计算差异
                    BigDecimal diffQty = calculateDiffQty(stockQtyDecimal, endingQty);
                    // 创建并保存明细记录
                    MonthlySettleDetail detail = buildMonthlySettleDetail(
                        monthlySettle.getId(), settleNo, matnr, batch, maktx, brand,
                        beginningQty, inQty, outQty, endingQty, stockQtyDecimal, diffQty
                    );
                    detail.setIsDeleted(0); // 未删除
                    monthlySettleDetailMapper.insert(detail);
                    // 累计统计(虽然入库和出库为0,但也要计入物料种类数)
                    materialCount++;
                }
            }
        }
        // 更新月结主记录
        monthlySettle.setTotalInQty(totalInQty);
        monthlySettle.setTotalOutQty(totalOutQty);
src/main/resources/mapper/MonthlySettleMapper.xml
@@ -58,60 +58,57 @@
        ) t
    </select>
    <!-- 统计月结时间范围内的物料出入库数量(合并入库出库) -->
    <select id="statisticsMaterialInOut" resultType="com.zy.asrs.entity.result.MaterialInOutStatDTO">
    <!-- 统计月结时间范围内的物料出入库数量(从入库表查询) -->
    <select id="statisticsMaterialInOutFromPakin" resultType="com.zy.asrs.entity.result.MaterialInOutRawDTO">
        SELECT 
            matnr,
            MAX(maktx) as maktx,
            batch,
            brand,
            SUM(in_qty) as inQty,
            SUM(out_qty) as outQty
        FROM (
            SELECT
                od.matnr,
                MAX(od.maktx) as maktx,
                ISNULL(od.batch, '') as batch,
                ISNULL(od.brand, '') as brand,
                SUM(od.qty) as in_qty,
                0 as out_qty
            FROM man_order_log_pakin o
            INNER JOIN man_order_detl_log_pakin od ON o.id = od.order_id
            WHERE o.status = 1
            AND CONVERT(date, o.order_time) >= #{startDate}
            AND o.order_time &lt;= #{endDate}
            AND o.move_status = 2
            AND (o.monthly_settle_id IS NULL OR o.monthly_settle_id = 0)
            GROUP BY od.matnr, od.batch, od.brand
            UNION ALL
            SELECT
                od.matnr,
                MAX(od.maktx) as maktx,
                ISNULL(od.batch, '') as batch,
                ISNULL(od.brand, '') as brand,
                0 as in_qty,
                SUM(od.qty) as out_qty
            FROM man_order_log_pakout o
            INNER JOIN man_order_detl_log_pakout od ON o.id = od.order_id
            WHERE o.status = 1
            AND CONVERT(date, o.order_time) >= #{startDate}
            AND o.order_time &lt;= #{endDate}
            AND o.move_status = 2
            AND (o.monthly_settle_id IS NULL OR o.monthly_settle_id = 0)
            GROUP BY od.matnr, od.batch, od.brand
        ) t
        GROUP BY matnr, batch, brand
            od.matnr,
            od.maktx,
            od.batch,
            od.brand,
            SUM(od.qty) as qty,
            COALESCE(od.pakin_pakout_status, o.pakin_pakout_status) as pakinPakoutStatus
        FROM man_order_log_pakin o
        INNER JOIN man_order_detl_log_pakin od ON o.id = od.order_id
        WHERE o.status = 1
        AND CONVERT(date, o.order_time) >= #{startDate}
        AND o.order_time &lt;= #{endDate}
        AND o.move_status = 2
        AND (o.monthly_settle_id IS NULL OR o.monthly_settle_id = 0)
        AND COALESCE(od.pakin_pakout_status, o.pakin_pakout_status) IN (1, 2)
        GROUP BY od.matnr, od.maktx, od.batch, od.brand, COALESCE(od.pakin_pakout_status, o.pakin_pakout_status)
    </select>
    <!-- 统计月结时间范围内的物料出入库数量(从出库表查询) -->
    <select id="statisticsMaterialInOutFromPakout" resultType="com.zy.asrs.entity.result.MaterialInOutRawDTO">
        SELECT
            od.matnr,
            od.maktx,
            od.batch,
            od.brand,
            SUM(od.qty) as qty,
            COALESCE(od.pakin_pakout_status, o.pakin_pakout_status) as pakinPakoutStatus
        FROM man_order_log_pakout o
        INNER JOIN man_order_detl_log_pakout od ON o.id = od.order_id
        WHERE o.status = 1
        AND CONVERT(date, o.order_time) >= #{startDate}
        AND o.order_time &lt;= #{endDate}
        AND o.move_status = 2
        AND (o.monthly_settle_id IS NULL OR o.monthly_settle_id = 0)
        AND COALESCE(od.pakin_pakout_status, o.pakin_pakout_status) IN (1, 2)
        GROUP BY od.matnr, od.maktx, od.batch, od.brand, COALESCE(od.pakin_pakout_status, o.pakin_pakout_status)
    </select>
    <!-- 获取上一个月结的物料期末库存 -->
    <select id="getPreviousSettleEndingQty" resultType="com.zy.asrs.entity.result.PreviousSettleEndingQtyDTO">
        SELECT 
            matnr,
            batch,
            brand,
            ISNULL(batch, '') as batch,
            ISNULL(brand, '') as brand,
            ISNULL(maktx, '') as maktx,
            ending_qty as endingQty
        FROM man_monthly_settle_detail
        WHERE settle_id = #{previousSettleId}
        AND is_deleted = 0
    </select>
    <!-- 更新入库订单的月结信息 -->
src/main/webapp/static/js/monthlySettle/monthlySettle.js
@@ -402,6 +402,7 @@
                                data: details,
                                page: true,
                                cellMinWidth: 100,
                                width: '100%',
                                cols: [[
                                    {type: 'numbers', title: '序号', width: 60, align: 'center'},
                                    {field: 'matnr', title: '物料编码', width: 150},
@@ -545,8 +546,7 @@
            }
        }
    });
    debugger; // 调试用:检查 searchData 的值
    // 获取 table 实例
    var tableInstance = child ? parent.tableIns : tableIns;
    
src/main/webapp/views/monthlySettle/monthlySettle.html
@@ -107,7 +107,9 @@
            </div>
        </div>
        <hr class="layui-bg-gray">
        <table id="detailTable" lay-filter="detailTable"></table>
        <div style="overflow-x: auto; width: 100%;">
            <table id="detailTable" lay-filter="detailTable"></table>
        </div>
    </div>
</script>