diff --git a/src/main/java/com/gaotao/common/utils/CountModeChecker.java b/src/main/java/com/gaotao/common/utils/CountModeChecker.java new file mode 100644 index 0000000..a0db824 --- /dev/null +++ b/src/main/java/com/gaotao/common/utils/CountModeChecker.java @@ -0,0 +1,131 @@ +package com.gaotao.common.utils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +/** + * 盘点模式检查工具类 - rqrq + * + *

用于快速判断当前是否处于盘点模式,如果是盘点模式则抛出异常阻止其他业务操作

+ * + *

使用方式:

+ *
+ * // 1. 注入工具类
+ * @Autowired
+ * private CountModeChecker countModeChecker;
+ * 
+ * // 2. 在业务方法开头调用检查
+ * public void yourBusinessMethod(String site) {
+ *     countModeChecker.checkNotInCountMode(site);  // 如果是盘点模式会抛出异常
+ *     // ... 业务逻辑
+ * }
+ * 
+ * // 3. 也可以只获取状态不抛异常
+ * if (countModeChecker.isInCountMode(site)) {
+ *     // 处理盘点模式的情况
+ * }
+ * 
+ * + * @author rqrq + * @date 2025/12/23 + */ +@Component +public class CountModeChecker { + + @Autowired + private JdbcTemplate jdbcTemplate; + + /** + * 检查当前是否在盘点模式,如果是则抛出异常 - rqrq + * + *

业务方法开头调用此方法,可以快速阻止盘点模式下的其他操作

+ * + * @param site 工厂编码 + * @throws RuntimeException 如果当前处于盘点模式 + * @author rqrq + */ + public void checkNotInCountMode(String site) { + if (isInCountMode(site)) { + throw new RuntimeException("当前处于盘点模式,无法进行此操作,请先关闭盘点模式"); + } + } + + /** + * 检查当前是否在盘点模式,如果是则抛出自定义异常信息 - rqrq + * + * @param site 工厂编码 + * @param customMessage 自定义错误信息 + * @throws RuntimeException 如果当前处于盘点模式 + * @author rqrq + */ + public void checkNotInCountMode(String site, String customMessage) { + if (isInCountMode(site)) { + throw new RuntimeException(customMessage); + } + } + + /** + * 判断当前是否处于盘点模式 - rqrq + * + * @param site 工厂编码 + * @return true=盘点模式 false=非盘点模式 + * @author rqrq + */ + public boolean isInCountMode(String site) { + if (!StringUtils.hasText(site)) { + return false; + } + + String countModeValue = getCountModeValue(site); + return "Y".equals(countModeValue); + } + + /** + * 获取盘点模式参数值 - rqrq + * + * @param site 工厂编码 + * @return Y=盘点模式 N=非盘点模式 null=未配置 + * @author rqrq + */ + public String getCountModeValue(String site) { + try { + String sql = "SELECT parameter_value FROM wms_sys_parameter WHERE site = ? AND parameter_key = 'IF_COUNT'"; + return jdbcTemplate.queryForObject(sql, String.class, site); + } catch (Exception e) { + // 查询失败或无数据时返回N(默认非盘点模式) + return "N"; + } + } + + /** + * 检查当前必须在盘点模式,如果不是则抛出异常 - rqrq + * + *

盘点相关业务方法开头调用此方法,确保只有盘点模式下才能执行

+ * + * @param site 工厂编码 + * @throws RuntimeException 如果当前不在盘点模式 + * @author rqrq + */ + public void checkMustInCountMode(String site) { + if (!isInCountMode(site)) { + throw new RuntimeException("当前不在盘点模式,无法进行此操作,请先开启盘点模式"); + } + } + + /** + * 检查当前必须在盘点模式,如果不是则抛出自定义异常信息 - rqrq + * + * @param site 工厂编码 + * @param customMessage 自定义错误信息 + * @throws RuntimeException 如果当前不在盘点模式 + * @author rqrq + */ + public void checkMustInCountMode(String site, String customMessage) { + if (!isInCountMode(site)) { + throw new RuntimeException(customMessage); + } + } +} + diff --git a/src/main/java/com/gaotao/modules/check/controller/PhysicalInventoryController.java b/src/main/java/com/gaotao/modules/check/controller/PhysicalInventoryController.java index dbd9620..d89fa9e 100644 --- a/src/main/java/com/gaotao/modules/check/controller/PhysicalInventoryController.java +++ b/src/main/java/com/gaotao/modules/check/controller/PhysicalInventoryController.java @@ -428,4 +428,88 @@ public class PhysicalInventoryController { physicalInventoryService.manualHandleCount(query); return R.ok(); } + + // ==================== 盘点模式管理 ==================== - rqrq + + /** + * @Description 获取当前盘点模式状态 - rqrq + * @param query 查询条件(包含site) + * @return R 返回结果 Y=盘点模式 N=非盘点模式 + * @author rqrq + */ + @PostMapping("/getSysIfCount") + public R getSysIfCount(@RequestBody java.util.Map query) { + String site = query.get("site"); + String result = physicalInventoryService.getSysIfCount(site); + return R.ok().put("value", result); + } + + /** + * @Description 开启或关闭盘点模式 - rqrq + * @param query 参数(包含site、value) + * @return R + * @author rqrq + */ + @PostMapping("/updateSysIfCount") + public R updateSysIfCount(@RequestBody java.util.Map query) { + String site = query.get("site"); + String value = query.get("value"); + physicalInventoryService.updateSysIfCount(site, value); + return R.ok(); + } + + // ==================== 自动盘盈盘亏处理 ==================== - rqrq + + @Autowired + private com.gaotao.modules.check.service.CountAdjustmentService countAdjustmentService; + + /** + * @Description 检查是否有需要系统处理的异常结果 - rqrq + * @param request 请求参数(包含site、countNo) + * @return R 返回需要处理的记录数 + * @author rqrq + */ + @PostMapping("/checkSystemHandleCount") + public R checkSystemHandleCount(@RequestBody CountAdjustmentRequest request) { + int count = countAdjustmentService.checkSystemHandleCount(request); + return R.ok().put("count", count); + } + + /** + * @Description 执行自动盘盈盘亏处理 - rqrq + * @param request 请求参数(包含site、countNo、username) + * @return R 返回处理结果 + * @author rqrq + */ + @PostMapping("/executeSystemAdjustment") + public R executeSystemAdjustment(@RequestBody CountAdjustmentRequest request) { + CountAdjustmentResult result = countAdjustmentService.executeSystemAdjustment(request); + return R.ok().put("row", result); + } + + /** + * @Description 查询盘盈盘亏事务记录 - rqrq + * @param request 请求参数(包含site、countNo) + * @return R 返回事务列表 + * @author rqrq + */ + @PostMapping("/queryAdjustmentTransList") + public R queryAdjustmentTransList(@RequestBody CountAdjustmentRequest request) { + java.util.List rows = countAdjustmentService.queryAdjustmentTransList(request); + return R.ok().put("rows", rows); + } + + /** + * @Description 查询盘盈盘亏事务子明细 - rqrq + * @param query 请求参数(包含site、transNo) + * @return R 返回子明细列表 + * @author rqrq + */ + @PostMapping("/queryAdjustmentTransSubList") + public R queryAdjustmentTransSubList(@RequestBody java.util.Map query) { + String site = query.get("site"); + String transNo = query.get("transNo"); + java.util.List rows = countAdjustmentService.queryAdjustmentTransSubList(site, transNo); + return R.ok().put("rows", rows); + } } diff --git a/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentItem.java b/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentItem.java new file mode 100644 index 0000000..01c1d31 --- /dev/null +++ b/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentItem.java @@ -0,0 +1,102 @@ +package com.gaotao.modules.check.entity; + +import lombok.Data; +import org.apache.ibatis.type.Alias; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * 盘点调整明细项实体类 - 按ERP维度汇总后的盘盈盘亏数据 - rqrq + * + *

用途:用于盘盈盘亏处理时按维度汇总的库存数据

+ * + *

汇总维度:

+ *
    + *
  • site - 工厂编码
  • + *
  • warehouseId - 仓库ID
  • + *
  • locationId - 库位ID
  • + *
  • partNo - 物料号
  • + *
  • batchNo - 批号
  • + *
  • wdr - WDR
  • + *
  • expiredDate - 过期日期
  • + *
  • engChgLevel - 工程变更级别
  • + *
+ * + *

业务逻辑:

+ *
+ * // 根据diffQty判断盘盈盘亏
+ * if (diffQty > 0) {
+ *     // 盘盈:库存增加
+ * } else if (diffQty < 0) {
+ *     // 盘亏:库存减少
+ * }
+ * 
+ * + * @author rqrq + * @date 2025/12/23 + */ +@Data +@Alias("CountAdjustmentItem") +public class CountAdjustmentItem { + + // ==================== 维度字段 ==================== + + /** + * 工厂编码 + */ + private String site; + + /** + * 仓库ID + */ + private String warehouseId; + + /** + * 库位ID + */ + private String locationId; + + /** + * 物料号 + */ + private String partNo; + + /** + * 批号 + */ + private String batchNo; + + /** + * WDR(物料追溯号) + */ + private String wdr; + + /** + * 过期日期 + */ + private Date expiredDate; + + /** + * 工程变更级别 + */ + private String engChgLevel; + + // ==================== 数量字段 ==================== + + /** + * 汇总后的差异数量 + *

正数=盘盈,负数=盘亏

+ */ + private BigDecimal diffQty; + + // ==================== 关联明细 ==================== + + /** + * 该维度下的所有标签明细列表 + *

用于生成trans_detail_sub

+ */ + private List labelItems; +} + diff --git a/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentLabelItem.java b/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentLabelItem.java new file mode 100644 index 0000000..3b947b6 --- /dev/null +++ b/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentLabelItem.java @@ -0,0 +1,119 @@ +package com.gaotao.modules.check.entity; + +import lombok.Data; +import org.apache.ibatis.type.Alias; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 盘点调整标签明细实体类 - 标签级别的盘盈盘亏数据 - rqrq + * + *

用途:用于盘盈盘亏处理时记录每个标签的调整信息

+ * + *

数据来源:

+ *
    + *
  • count_result表:盘点结果信息
  • + *
  • handling_unit表:标签详细信息
  • + *
+ * + *

业务逻辑:

+ *
+ * // 1. 用于生成trans_detail_sub记录
+ * // 2. 用于更新handling_unit.qty
+ * if (newQty <= 0) {
+ *     // 设置in_stock_flag = 'N'
+ * }
+ * 
+ * + * @author rqrq + * @date 2025/12/23 + */ +@Data +@Alias("CountAdjustmentLabelItem") +public class CountAdjustmentLabelItem { + + // ==================== 盘点结果字段 ==================== + + /** + * 盘点结果ID + */ + private Long countResultId; + + /** + * 盘点单号 + */ + private String countNo; + + /** + * 盘点结果类型 + *

QTY_DIFF=数量差异,MISSING=缺失

+ */ + private String countResult; + + /** + * 差异数量 + *

正数=盘盈,负数=盘亏

+ */ + private BigDecimal diffQty; + + // ==================== 标签基本信息 ==================== + + /** + * 工厂编码 + */ + private String site; + + /** + * 标签号(handling_unit.unit_id) + */ + private String unitId; + + /** + * 栈板号 + */ + private String palletId; + + // ==================== 库存维度字段 ==================== + + /** + * 物料号 + */ + private String partNo; + + /** + * 标签当前数量 + */ + private BigDecimal qty; + + /** + * 批号 + */ + private String batchNo; + + /** + * 库位ID + */ + private String locationId; + + /** + * 仓库ID + */ + private String warehouseId; + + /** + * WDR(物料追溯号) + */ + private String wdr; + + /** + * 过期日期 + */ + private Date expiredDate; + + /** + * 工程变更级别 + */ + private String engChgLevel; +} + diff --git a/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentRequest.java b/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentRequest.java new file mode 100644 index 0000000..6d265d2 --- /dev/null +++ b/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentRequest.java @@ -0,0 +1,39 @@ +package com.gaotao.modules.check.entity; + +import lombok.Data; +import org.apache.ibatis.type.Alias; + +/** + * 盘点调整请求参数实体类 - rqrq + * + *

用途:用于自动处理盈亏功能的请求参数传递

+ * + *

使用场景:

+ *
    + *
  • Controller接收前端请求参数
  • + *
  • Service层方法入参
  • + *
+ * + * @author rqrq + * @date 2025/12/23 + */ +@Data +@Alias("CountAdjustmentRequest") +public class CountAdjustmentRequest { + + /** + * 工厂编码(必填) + */ + private String site; + + /** + * 盘点单号(必填) + */ + private String countNo; + + /** + * 操作用户名(必填) + */ + private String username; +} + diff --git a/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentResult.java b/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentResult.java new file mode 100644 index 0000000..89c578e --- /dev/null +++ b/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentResult.java @@ -0,0 +1,75 @@ +package com.gaotao.modules.check.entity; + +import lombok.Data; +import org.apache.ibatis.type.Alias; + +import java.util.List; + +/** + * 盘点调整处理结果实体类 - rqrq + * + *

用途:用于返回自动处理盈亏的执行结果

+ * + * @author rqrq + * @date 2025/12/23 + */ +@Data +@Alias("CountAdjustmentResult") +public class CountAdjustmentResult { + + /** + * 处理是否成功 + */ + private boolean success; + + /** + * 结果消息 + */ + private String message; + + /** + * 生成的盘亏单据号列表 + */ + private List lossTransNoList; + + /** + * 生成的盘盈单据号列表 + */ + private List profitTransNoList; + + /** + * 处理的盘亏记录数 + */ + private int lossCount; + + /** + * 处理的盘盈记录数 + */ + private int profitCount; + + /** + * 处理的标签数 + */ + private int labelCount; + + /** + * 创建成功结果 - rqrq + */ + public static CountAdjustmentResult success(String message) { + CountAdjustmentResult result = new CountAdjustmentResult(); + result.setSuccess(true); + result.setMessage(message); + return result; + } + + /** + * 创建失败结果 - rqrq + */ + public static CountAdjustmentResult fail(String message) { + CountAdjustmentResult result = new CountAdjustmentResult(); + result.setSuccess(false); + result.setMessage(message); + return result; + } +} + diff --git a/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentTransData.java b/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentTransData.java new file mode 100644 index 0000000..5a220db --- /dev/null +++ b/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentTransData.java @@ -0,0 +1,116 @@ +package com.gaotao.modules.check.entity; + +import lombok.Data; +import org.apache.ibatis.type.Alias; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 盘盈盘亏事务明细实体类 - 用于查询展示 - rqrq + * + *

用途:用于前端盘盈盘亏记录页签左侧列表展示

+ * + * @author rqrq + * @date 2025/12/23 + */ +@Data +@Alias("CountAdjustmentTransData") +public class CountAdjustmentTransData { + + // ==================== trans_header字段 ==================== + + /** + * 工厂编码 + */ + private String site; + + /** + * 事务号 + */ + private String transNo; + + /** + * 事务日期 + */ + private Date transDate; + + /** + * 事务类型 + *

PK=盘亏,PY=盘盈

+ */ + private String transTypeDb; + + /** + * 事务类型描述 + */ + private String transTypeDesc; + + /** + * 仓库ID + */ + private String warehouseId; + + /** + * 关联盘点单号(存储在order_ref1) + */ + private String orderRef1; + + /** + * 备注 + */ + private String remark; + + /** + * 操作用户 + */ + private String userName; + + // ==================== trans_detail字段 ==================== + + /** + * 行号 + */ + private Double itemNo; + + /** + * 物料号 + */ + private String partNo; + + /** + * 事务数量 + */ + private BigDecimal transQty; + + /** + * 批号 + */ + private String batchNo; + + /** + * 库位ID + */ + private String locationId; + + /** + * 方向(IN=入库,OUT=出库) + */ + private String direction; + + /** + * 方向描述 + */ + private String directionDesc; + + /** + * WDR号 + */ + private String wdrNo; + + /** + * 工程变更级别 + */ + private String engChgLevel; +} + diff --git a/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentTransSubData.java b/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentTransSubData.java new file mode 100644 index 0000000..276a576 --- /dev/null +++ b/src/main/java/com/gaotao/modules/check/entity/CountAdjustmentTransSubData.java @@ -0,0 +1,85 @@ +package com.gaotao.modules.check.entity; + +import lombok.Data; +import org.apache.ibatis.type.Alias; + +import java.math.BigDecimal; + +/** + * 盘盈盘亏事务子明细实体类 - 用于查询展示 - rqrq + * + *

用途:用于前端盘盈盘亏记录页签右侧列表展示

+ * + * @author rqrq + * @date 2025/12/23 + */ +@Data +@Alias("CountAdjustmentTransSubData") +public class CountAdjustmentTransSubData { + + /** + * 工厂编码 + */ + private String site; + + /** + * 事务号 + */ + private String transNo; + + /** + * 行号 + */ + private Double itemNo; + + /** + * 子行号 + */ + private String subNo; + + /** + * 子数量 + */ + private Double subQty; + + /** + * 方向(IN=入库,OUT=出库) + */ + private String direction; + + /** + * 方向描述 + */ + private String directionDesc; + + /** + * 标签号 + */ + private String handlingUnitId; + + /** + * 物料号 + */ + private String partNo; + + /** + * 批号 + */ + private String batchNo; + + /** + * 库位ID + */ + private String locationId; + + /** + * 工程变更级别 + */ + private String engChgLevel; + + /** + * 栈板号(存储在order_ref1) + */ + private String palletId; +} + diff --git a/src/main/java/com/gaotao/modules/check/mapper/CountAdjustmentMapper.java b/src/main/java/com/gaotao/modules/check/mapper/CountAdjustmentMapper.java new file mode 100644 index 0000000..3630309 --- /dev/null +++ b/src/main/java/com/gaotao/modules/check/mapper/CountAdjustmentMapper.java @@ -0,0 +1,116 @@ +package com.gaotao.modules.check.mapper; + +import com.gaotao.modules.check.entity.CountAdjustmentLabelItem; +import com.gaotao.modules.trans.entity.TransDetail; +import com.gaotao.modules.trans.entity.TransDetailSub; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + + +import com.gaotao.modules.check.entity.CountAdjustmentTransData; +import com.gaotao.modules.check.entity.CountAdjustmentTransSubData; +import java.math.BigDecimal; +import java.util.List; + +/** + * 盘点调整Mapper接口 - rqrq + * + *

功能:提供盘盈盘亏处理相关的数据库操作

+ * + * @author rqrq + * @date 2025/12/23 + */ +@Mapper +public interface CountAdjustmentMapper { + + /** + * 统计需要系统处理的异常结果数量 - rqrq + * + * @param site 工厂编码 + * @param countNo 盘点单号 + * @return int 记录数 + * @author rqrq + */ + int countSystemHandleRecords(@Param("site") String site, @Param("countNo") String countNo); + + /** + * 查询待系统处理的标签明细(排除SURPLUS类型)- rqrq + * + *

关联handling_unit获取标签详细信息

+ * + * @param site 工厂编码 + * @param countNo 盘点单号 + * @return List 标签明细列表 + * @author rqrq + */ + List querySystemHandleLabels(@Param("site") String site, @Param("countNo") String countNo); + + /** + * 插入事务明细 - rqrq + * + * @param detail 事务明细 + * @return int 影响行数 + * @author rqrq + */ + int insertTransDetail(TransDetail detail); + + /** + * 插入事务子明细 - rqrq + * + * @param sub 事务子明细 + * @return int 影响行数 + * @author rqrq + */ + int insertTransDetailSub(TransDetailSub sub); + + /** + * 更新标签数量 - rqrq + * + * @param site 工厂编码 + * @param unitId 标签号 + * @param qty 新数量 + * @param inStockFlag 在库标记 + * @param username 操作用户 + * @return int 影响行数 + * @author rqrq + */ + int updateHandlingUnitQty(@Param("site") String site, + @Param("unitId") String unitId, + @Param("qty") BigDecimal qty, + @Param("inStockFlag") String inStockFlag, + @Param("username") String username); + + /** + * 更新盘点结果处理标记 - rqrq + * + * @param countResultId 盘点结果ID + * @param username 操作用户 + * @return int 影响行数 + * @author rqrq + */ + int updateCountResultHandleFlag(@Param("countResultId") Long countResultId, + @Param("username") String username); + + // ==================== 盘盈盘亏记录查询 ==================== - rqrq + + /** + * 查询盘盈盘亏事务头和明细 - rqrq + * + * @param site 工厂编码 + * @param countNo 盘点单号(存储在order_ref1中) + * @return List 事务列表 + * @author rqrq + */ + List queryAdjustmentTransList(@Param("site") String site, @Param("countNo") String countNo); + + /** + * 查询事务子明细 - rqrq + * + * @param site 工厂编码 + * @param transNo 事务号 + * @return List 子明细列表 + * @author rqrq + */ + List queryAdjustmentTransSubList(@Param("site") String site, @Param("transNo") String transNo); +} + diff --git a/src/main/java/com/gaotao/modules/check/service/CountAdjustmentService.java b/src/main/java/com/gaotao/modules/check/service/CountAdjustmentService.java new file mode 100644 index 0000000..5da8363 --- /dev/null +++ b/src/main/java/com/gaotao/modules/check/service/CountAdjustmentService.java @@ -0,0 +1,84 @@ +package com.gaotao.modules.check.service; + +import com.gaotao.modules.check.entity.CountAdjustmentRequest; +import com.gaotao.modules.check.entity.CountAdjustmentResult; +import com.gaotao.modules.check.entity.CountAdjustmentTransData; +import com.gaotao.modules.check.entity.CountAdjustmentTransSubData; + +import java.util.List; + +/** + * 盘点调整服务接口 - 用于自动处理盘盈盘亏 - rqrq + * + *

功能说明:

+ *
    + *
  • 自动处理系统处理方式的盘点异常结果
  • + *
  • 生成盘亏(PK)和盘盈(PY)事务单据
  • + *
  • 更新库存和标签数量
  • + *
+ * + *

处理范围:

+ *
    + *
  • QTY_DIFF(数量差异):差异数量正数=盘盈,负数=盘亏
  • + *
  • MISSING(标签缺失):差异数量为负数=盘亏
  • + *
  • SURPLUS(多出标签):不处理,需手工处理
  • + *
+ * + * @author rqrq + * @date 2025/12/23 + */ +public interface CountAdjustmentService { + + /** + * 检查是否有需要系统处理的异常结果 - rqrq + * + * @param request 请求参数(包含site和countNo) + * @return int 需要处理的记录数,0表示无需处理 + * @author rqrq + */ + int checkSystemHandleCount(CountAdjustmentRequest request); + + /** + * 执行自动盘盈盘亏处理 - rqrq + * + *

处理流程:

+ *
    + *
  1. 查询所有待系统处理的盘点结果(handle_type='SYSTEM' AND handle_flag='N')
  2. + *
  3. 排除SURPLUS类型(多出标签不处理)
  4. + *
  5. 关联handling_unit获取标签详细信息
  6. + *
  7. 按ERP维度汇总差异数量
  8. + *
  9. 分离盘盈和盘亏数据
  10. + *
  11. 按仓库分组处理盘亏:生成PK单据、更新库存
  12. + *
  13. 按仓库分组处理盘盈:生成PY单据、更新库存
  14. + *
  15. 逐个标签更新handling_unit.qty
  16. + *
  17. 更新盘点结果handle_flag='Y'
  18. + *
+ * + * @param request 请求参数(包含site、countNo、username) + * @return CountAdjustmentResult 处理结果 + * @author rqrq + */ + CountAdjustmentResult executeSystemAdjustment(CountAdjustmentRequest request); + + // ==================== 盘盈盘亏记录查询 ==================== - rqrq + + /** + * 查询盘盈盘亏事务记录 - rqrq + * + * @param request 请求参数(包含site和countNo) + * @return List 事务列表 + * @author rqrq + */ + List queryAdjustmentTransList(CountAdjustmentRequest request); + + /** + * 查询盘盈盘亏事务子明细 - rqrq + * + * @param site 工厂编码 + * @param transNo 事务号 + * @return List 子明细列表 + * @author rqrq + */ + List queryAdjustmentTransSubList(String site, String transNo); +} + diff --git a/src/main/java/com/gaotao/modules/check/service/impl/CountAdjustmentServiceImpl.java b/src/main/java/com/gaotao/modules/check/service/impl/CountAdjustmentServiceImpl.java new file mode 100644 index 0000000..398989f --- /dev/null +++ b/src/main/java/com/gaotao/modules/check/service/impl/CountAdjustmentServiceImpl.java @@ -0,0 +1,502 @@ +package com.gaotao.modules.check.service.impl; + +import com.gaotao.modules.check.entity.*; +import com.gaotao.modules.check.mapper.CountAdjustmentMapper; +import com.gaotao.modules.check.service.CountAdjustmentService; +import com.gaotao.modules.trans.entity.TransHeader; +import com.gaotao.modules.trans.entity.TransDetail; +import com.gaotao.modules.trans.entity.TransDetailSub; +import com.gaotao.modules.trans.entity.TransNoControl; +import com.gaotao.modules.trans.service.TransNoControlService; +import com.gaotao.modules.trans.service.TransHeaderService; +import com.gaotao.modules.warehouse.service.InventoryStockService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 盘点调整服务实现类 - 自动处理盘盈盘亏 - rqrq + * + *

处理流程:

+ *
    + *
  1. 查询待系统处理的盘点结果(handle_type='SYSTEM' AND handle_flag='N')
  2. + *
  3. 排除SURPLUS类型(多出标签需手工处理)
  4. + *
  5. 关联handling_unit获取标签详细信息
  6. + *
  7. 按ERP维度汇总差异数量
  8. + *
  9. 分离盘盈(diffQty>0)和盘亏(diffQty<0)数据
  10. + *
  11. 处理盘亏:生成PK单据、减少库存
  12. + *
  13. 处理盘盈:生成PY单据、增加库存
  14. + *
  15. 逐个标签更新handling_unit.qty
  16. + *
  17. 更新盘点结果handle_flag='Y'
  18. + *
+ * + * @author rqrq + * @date 2025/12/23 + */ +@Slf4j +@Service +public class CountAdjustmentServiceImpl implements CountAdjustmentService { + + @Autowired + private CountAdjustmentMapper countAdjustmentMapper; + + @Autowired + private TransNoControlService transNoService; + + @Autowired + private TransHeaderService transHeaderService; + + @Autowired + private InventoryStockService inventoryStockService; + + /** + * 检查是否有需要系统处理的异常结果 - rqrq + */ + @Override + public int checkSystemHandleCount(CountAdjustmentRequest request) { + log.info("开始检查系统处理异常数量 - rqrq,site={}, countNo={}", request.getSite(), request.getCountNo()); + + // 参数校验 - rqrq + if (!StringUtils.hasText(request.getSite())) { + throw new RuntimeException("站点不能为空"); + } + if (!StringUtils.hasText(request.getCountNo())) { + throw new RuntimeException("盘点单号不能为空"); + } + + int count = countAdjustmentMapper.countSystemHandleRecords(request.getSite(), request.getCountNo()); + log.info("检查系统处理异常数量完成,共{}条记录 - rqrq", count); + return count; + } + + /** + * 执行自动盘盈盘亏处理 - rqrq + */ + @Override + @Transactional(rollbackFor = Exception.class) + public CountAdjustmentResult executeSystemAdjustment(CountAdjustmentRequest request) { + log.info("开始执行自动盘盈盘亏处理 - rqrq,site={}, countNo={}, username={}", + request.getSite(), request.getCountNo(), request.getUsername()); + + // 1. 参数校验 - rqrq + validateRequest(request); + + // 2. 查询待系统处理的盘点结果(排除SURPLUS类型)- rqrq + List labelItems = countAdjustmentMapper.querySystemHandleLabels( + request.getSite(), request.getCountNo()); + + if (labelItems == null || labelItems.isEmpty()) { + log.info("没有需要系统处理的异常结果 - rqrq"); + return CountAdjustmentResult.fail("无异常结果需要系统处理"); + } + log.info("查询到待处理标签明细:{}条 - rqrq", labelItems.size()); + + // 3. 按ERP维度汇总差异数量 - rqrq + List summaryItems = summarizeByErpDimension(labelItems); + log.info("按ERP维度汇总后:{}条记录 - rqrq", summaryItems.size()); + + // 4. 分离盘盈和盘亏数据 - rqrq + List lossItems = summaryItems.stream() + .filter(item -> item.getDiffQty().compareTo(BigDecimal.ZERO) < 0) + .collect(Collectors.toList()); + List profitItems = summaryItems.stream() + .filter(item -> item.getDiffQty().compareTo(BigDecimal.ZERO) > 0) + .collect(Collectors.toList()); + + log.info("盘亏记录:{}条,盘盈记录:{}条 - rqrq", lossItems.size(), profitItems.size()); + + // 5. 创建结果对象 - rqrq + CountAdjustmentResult result = new CountAdjustmentResult(); + result.setSuccess(true); + result.setLossTransNoList(new ArrayList<>()); + result.setProfitTransNoList(new ArrayList<>()); + result.setLossCount(lossItems.size()); + result.setProfitCount(profitItems.size()); + result.setLabelCount(labelItems.size()); + + // 6. 处理盘亏(按仓库分组)- rqrq + if (!lossItems.isEmpty()) { + processLossItems(lossItems, request, result); + } + + // 7. 处理盘盈(按仓库分组)- rqrq + if (!profitItems.isEmpty()) { + processProfitItems(profitItems, request, result); + } + + // 8. 逐个标签更新handling_unit.qty - rqrq + updateHandlingUnitQty(labelItems, request.getUsername()); + + // 9. 更新盘点结果handle_flag='Y' - rqrq + updateCountResultHandleFlag(labelItems, request.getUsername()); + + result.setMessage("处理完成,盘亏" + result.getLossCount() + "条,盘盈" + result.getProfitCount() + "条"); + log.info("自动盘盈盘亏处理完成 - rqrq,{}", result.getMessage()); + return result; + } + + /** + * 参数校验 - rqrq + */ + private void validateRequest(CountAdjustmentRequest request) { + if (!StringUtils.hasText(request.getSite())) { + throw new RuntimeException("站点不能为空"); + } + if (!StringUtils.hasText(request.getCountNo())) { + throw new RuntimeException("盘点单号不能为空"); + } + if (!StringUtils.hasText(request.getUsername())) { + throw new RuntimeException("操作用户不能为空"); + } + } + + /** + * 按ERP维度汇总差异数量 - rqrq + * + *

汇总维度:site + warehouseId + locationId + partNo + batchNo + wdr + expiredDate + engChgLevel

+ */ + private List summarizeByErpDimension(List labelItems) { + // 使用Map按维度汇总 - rqrq + Map summaryMap = new HashMap<>(); + + for (CountAdjustmentLabelItem label : labelItems) { + // 构建维度Key - rqrq + String key = buildDimensionKey(label); + + CountAdjustmentItem item = summaryMap.get(key); + if (item == null) { + item = new CountAdjustmentItem(); + item.setSite(label.getSite()); + item.setWarehouseId(label.getWarehouseId()); + item.setLocationId(label.getLocationId()); + item.setPartNo(label.getPartNo()); + item.setBatchNo(label.getBatchNo()); + item.setWdr(label.getWdr()); + item.setExpiredDate(label.getExpiredDate()); + item.setEngChgLevel(label.getEngChgLevel()); + item.setDiffQty(BigDecimal.ZERO); + item.setLabelItems(new ArrayList<>()); + summaryMap.put(key, item); + } + + // 累加差异数量 - rqrq + item.setDiffQty(item.getDiffQty().add(label.getDiffQty())); + item.getLabelItems().add(label); + } + + return new ArrayList<>(summaryMap.values()); + } + + /** + * 构建维度Key - rqrq + */ + private String buildDimensionKey(CountAdjustmentLabelItem label) { + return String.join("||", + nullToEmpty(label.getSite()), + nullToEmpty(label.getWarehouseId()), + nullToEmpty(label.getLocationId()), + nullToEmpty(label.getPartNo()), + nullToEmpty(label.getBatchNo()), + nullToEmpty(label.getWdr()), + label.getExpiredDate() != null ? label.getExpiredDate().toString() : "", + nullToEmpty(label.getEngChgLevel()) + ); + } + + private String nullToEmpty(String str) { + return str != null ? str : ""; + } + + /** + * 处理盘亏(按仓库分组)- rqrq + * + *

处理流程:

+ *
    + *
  1. 按site+warehouseId分组
  2. + *
  3. 每组生成一个盘亏单据(PK)
  4. + *
  5. 创建trans_header(order_ref1=盘点单号)
  6. + *
  7. 创建trans_detail(按维度)
  8. + *
  9. 创建trans_detail_sub(按标签)
  10. + *
  11. 减少库存
  12. + *
+ */ + private void processLossItems(List lossItems, + CountAdjustmentRequest request, + CountAdjustmentResult result) { + log.info("开始处理盘亏,共{}条记录 - rqrq", lossItems.size()); + + // 按site+warehouseId分组 - rqrq + Map> groupedItems = lossItems.stream() + .collect(Collectors.groupingBy(item -> item.getSite() + "||" + item.getWarehouseId())); + + for (Map.Entry> entry : groupedItems.entrySet()) { + String[] keys = entry.getKey().split("\\|\\|"); + String site = keys[0]; + String warehouseId = keys.length > 1 ? keys[1] : ""; + List items = entry.getValue(); + + // 生成盘亏单据号 - rqrq + TransNoControl transData = transNoService.getTransNo(site, "PK", 10); + String transNo = transData.getNewTransNo(); + log.info("生成盘亏单据号:{} - rqrq", transNo); + + // 创建trans_header - rqrq + TransHeader header = createTransHeader(site, transNo, warehouseId, "PK", + request.getCountNo(), request.getUsername()); + transHeaderService.save(header); + + // 创建trans_detail和trans_detail_sub - rqrq + int itemNo = 1; + for (CountAdjustmentItem item : items) { + // 创建trans_detail - rqrq + TransDetail detail = createTransDetail(transNo, itemNo, item, "OUT"); + countAdjustmentMapper.insertTransDetail(detail); + + // 创建trans_detail_sub(按标签)- rqrq + int subNo = 1; + for (CountAdjustmentLabelItem label : item.getLabelItems()) { + TransDetailSub sub = createTransDetailSub(transNo, itemNo, subNo, label, "OUT"); + countAdjustmentMapper.insertTransDetailSub(sub); + subNo++; + } + + // 减少库存 - rqrq + inventoryStockService.reduceStockWithLock( + item.getSite(), + item.getWarehouseId(), + item.getPartNo(), + item.getBatchNo(), + item.getLocationId(), + item.getDiffQty().abs(), + item.getWdr(), + item.getEngChgLevel() + ); + + itemNo++; + } + + result.getLossTransNoList().add(transNo); + } + + log.info("盘亏处理完成,生成{}个单据 - rqrq", result.getLossTransNoList().size()); + } + + /** + * 处理盘盈(按仓库分组)- rqrq + */ + private void processProfitItems(List profitItems, + CountAdjustmentRequest request, + CountAdjustmentResult result) { + log.info("开始处理盘盈,共{}条记录 - rqrq", profitItems.size()); + + // 按site+warehouseId分组 - rqrq + Map> groupedItems = profitItems.stream() + .collect(Collectors.groupingBy(item -> item.getSite() + "||" + item.getWarehouseId())); + + for (Map.Entry> entry : groupedItems.entrySet()) { + String[] keys = entry.getKey().split("\\|\\|"); + String site = keys[0]; + String warehouseId = keys.length > 1 ? keys[1] : ""; + List items = entry.getValue(); + + // 生成盘盈单据号 - rqrq + TransNoControl transData = transNoService.getTransNo(site, "PY", 10); + String transNo = transData.getNewTransNo(); + log.info("生成盘盈单据号:{} - rqrq", transNo); + + // 创建trans_header - rqrq + TransHeader header = createTransHeader(site, transNo, warehouseId, "PY", + request.getCountNo(), request.getUsername()); + transHeaderService.save(header); + + // 创建trans_detail和trans_detail_sub - rqrq + int itemNo = 1; + for (CountAdjustmentItem item : items) { + // 创建trans_detail - rqrq + TransDetail detail = createTransDetail(transNo, itemNo, item, "IN"); + countAdjustmentMapper.insertTransDetail(detail); + + // 创建trans_detail_sub(按标签)- rqrq + int subNo = 1; + for (CountAdjustmentLabelItem label : item.getLabelItems()) { + TransDetailSub sub = createTransDetailSub(transNo, itemNo, subNo, label, "IN"); + countAdjustmentMapper.insertTransDetailSub(sub); + subNo++; + } + + // 增加库存 - rqrq + inventoryStockService.updateStockWithLock( + item.getSite(), + item.getWarehouseId(), + item.getPartNo(), + item.getBatchNo(), + item.getLocationId(), + item.getWdr(), + item.getDiffQty(), + "N", + item.getEngChgLevel() + ); + + itemNo++; + } + + result.getProfitTransNoList().add(transNo); + } + + log.info("盘盈处理完成,生成{}个单据 - rqrq", result.getProfitTransNoList().size()); + } + + /** + * 创建事务头 - rqrq + */ + private TransHeader createTransHeader(String site, String transNo, String warehouseId, + String transType, String countNo, String username) { + TransHeader header = new TransHeader(); + header.setSite(site); + header.setTransNo(transNo); + header.setTransDate(new Date()); + header.setTransTypeDb(transType); + header.setWarehouseId(warehouseId); + header.setUserId(username); + header.setUserName(username); + header.setOrderRef1(countNo); // 关联盘点单号 - rqrq + header.setRemark(transType.equals("PK") ? "盘亏处理" : "盘盈处理"); + header.setStatus("COMPLETED"); + header.setStatusDb("COMPLETED"); + header.setEnterDate(new Date()); + header.setIfsFlag("N"); + return header; + } + + /** + * 创建事务明细 - rqrq + */ + private TransDetail createTransDetail(String transNo, int itemNo, + CountAdjustmentItem item, String direction) { + TransDetail detail = new TransDetail(); + detail.setSite(item.getSite()); + detail.setTransNo(transNo); + detail.setItemNo((double) itemNo); + detail.setPartNo(item.getPartNo()); + detail.setTransQty(item.getDiffQty().abs()); + detail.setBatchNo(item.getBatchNo()); + detail.setLocationId(item.getLocationId()); + detail.setDirection(direction); + detail.setWdrNo(item.getWdr()); + detail.setEngChgLevel(item.getEngChgLevel()); + return detail; + } + + /** + * 创建事务子明细 - rqrq + */ + private TransDetailSub createTransDetailSub(String transNo, int itemNo, int subNo, + CountAdjustmentLabelItem label, String direction) { + TransDetailSub sub = new TransDetailSub(); + sub.setSite(label.getSite()); + sub.setTransNo(transNo); + sub.setItemNo((double) itemNo); + sub.setSubNo(String.valueOf(subNo)); + sub.setSubQty(label.getDiffQty().abs().doubleValue()); + sub.setDirection(direction); + sub.setHandlingUnitId(label.getUnitId()); + sub.setPartNo(label.getPartNo()); + sub.setBatchNo(label.getBatchNo()); + sub.setLocationId(label.getLocationId()); + sub.setEngChgLevel(label.getEngChgLevel()); + sub.setOrderRef1(label.getPalletId()); // 记录栈板号 - rqrq + return sub; + } + + /** + * 更新标签数量 - rqrq + * + *

如果更新后qty<=0,设置in_stock_flag='N'

+ */ + private void updateHandlingUnitQty(List labelItems, String username) { + log.info("开始更新标签数量,共{}个标签 - rqrq", labelItems.size()); + + for (CountAdjustmentLabelItem label : labelItems) { + // 计算新数量 - rqrq + BigDecimal newQty = label.getQty().add(label.getDiffQty()); + String inStockFlag = newQty.compareTo(BigDecimal.ZERO) > 0 ? "Y" : "N"; + + // 如果新数量小于0,设为0 - rqrq + if (newQty.compareTo(BigDecimal.ZERO) < 0) { + newQty = BigDecimal.ZERO; + } + + // 更新handling_unit - rqrq + countAdjustmentMapper.updateHandlingUnitQty( + label.getSite(), + label.getUnitId(), + newQty, + inStockFlag, + username + ); + } + + log.info("标签数量更新完成 - rqrq"); + } + + /** + * 更新盘点结果处理标记 - rqrq + */ + private void updateCountResultHandleFlag(List labelItems, String username) { + log.info("开始更新盘点结果处理标记 - rqrq"); + + for (CountAdjustmentLabelItem label : labelItems) { + countAdjustmentMapper.updateCountResultHandleFlag(label.getCountResultId(), username); + } + + log.info("盘点结果处理标记更新完成 - rqrq"); + } + + // ==================== 盘盈盘亏记录查询 ==================== - rqrq + + /** + * 查询盘盈盘亏事务记录 - rqrq + */ + @Override + public List queryAdjustmentTransList(CountAdjustmentRequest request) { + log.info("开始查询盘盈盘亏事务记录 - rqrq,site={}, countNo={}", request.getSite(), request.getCountNo()); + + if (!StringUtils.hasText(request.getSite())) { + throw new RuntimeException("站点不能为空"); + } + if (!StringUtils.hasText(request.getCountNo())) { + throw new RuntimeException("盘点单号不能为空"); + } + + List list = countAdjustmentMapper.queryAdjustmentTransList( + request.getSite(), request.getCountNo()); + log.info("查询盘盈盘亏事务记录完成,共{}条记录 - rqrq", list != null ? list.size() : 0); + return list; + } + + /** + * 查询盘盈盘亏事务子明细 - rqrq + */ + @Override + public List queryAdjustmentTransSubList(String site, String transNo) { + log.info("开始查询盘盈盘亏事务子明细 - rqrq,site={}, transNo={}", site, transNo); + + if (!StringUtils.hasText(site)) { + throw new RuntimeException("站点不能为空"); + } + if (!StringUtils.hasText(transNo)) { + throw new RuntimeException("事务号不能为空"); + } + + List list = countAdjustmentMapper.queryAdjustmentTransSubList(site, transNo); + log.info("查询盘盈盘亏事务子明细完成,共{}条记录 - rqrq", list != null ? list.size() : 0); + return list; + } +} + diff --git a/src/main/resources/mapper/check/CountAdjustmentMapper.xml b/src/main/resources/mapper/check/CountAdjustmentMapper.xml new file mode 100644 index 0000000..20c1f31 --- /dev/null +++ b/src/main/resources/mapper/check/CountAdjustmentMapper.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + INSERT INTO trans_detail ( + site, trans_no, item_no, part_no, trans_qty, + batch_no, location_id, direction, wdr_no, eng_chg_level + ) VALUES ( + #{site}, #{transNo}, #{itemNo}, #{partNo}, #{transQty}, + #{batchNo}, #{locationId}, #{direction}, #{wdrNo}, #{engChgLevel} + ) + + + + + INSERT INTO trans_detail_sub ( + site, trans_no, item_no, sub_no, sub_qty, direction, + handling_unit_id, part_no, batch_no, location_id, eng_chg_level, order_ref1 + ) VALUES ( + #{site}, #{transNo}, #{itemNo}, #{subNo}, #{subQty}, #{direction}, + #{handlingUnitId}, #{partNo}, #{batchNo}, #{locationId}, #{engChgLevel}, #{orderRef1} + ) + + + + + UPDATE handling_unit + SET qty = #{qty}, + in_stock_flag = #{inStockFlag}, + modified_by = #{username}, + modified_date = GETDATE() + WHERE site = #{site} + AND unit_id = #{unitId} + + + + + UPDATE count_result + SET handle_flag = 'Y', + handle_time = GETDATE(), + handle_by = #{username} + WHERE id = #{countResultId} + + + + + + + + + +