| src/main/java/com/zy/asrs/entity/result/MaterialInOutRawDTO.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/entity/result/PreviousSettleEndingQtyDTO.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/mapper/MonthlySettleMapper.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/service/impl/MonthlySettleServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/resources/mapper/MonthlySettleMapper.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/webapp/static/js/monthlySettle/monthlySettle.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/webapp/views/monthlySettle/monthlySettle.html | ●●●●● 补丁 | 查看 | 原始文档 | 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 <= #{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 <= #{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 <= #{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 <= #{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>