feat(mat): 添加入库记录时同步更新库存和价格历史功能

- 在 MatPurchaseInDetailService 中新增 insertByBoWithInventoryAndPriceHistory 方法
- 在 MatPurchaseInDetailService 中新增 deleteWithValidByIdsWithInventoryAndPriceHistory 方法
- 扩展 MatPriceHistory 实体类添加 purchaseInDetailId 关联字段
- 更新 MatPriceHistoryMapper.xml 映射文件包含新的关联字段
- 修改控制器方法调用新的带库存和价格历史更新的入库接口
- 实现库存数量更新逻辑和价格历史记录管理功能
- 添加入库记录删除时还原库存和价格历史的功能
This commit is contained in:
2026-01-30 16:13:14 +08:00
parent 9f2cca7ddd
commit d9e0205da1
7 changed files with 210 additions and 5 deletions

View File

@@ -1,9 +1,14 @@
package com.gear.mat.controller;
import java.util.Date;
import java.util.List;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import com.gear.mat.domain.bo.MatMaterialBo;
import com.gear.mat.domain.bo.MatPriceHistoryBo;
import com.gear.mat.domain.vo.MatMaterialVo;
import com.gear.mat.domain.vo.MatPriceHistoryVo;
import lombok.RequiredArgsConstructor;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.*;
@@ -25,6 +30,8 @@ import com.gear.mat.domain.bo.MatPurchaseInDetailBo;
import com.gear.mat.service.IMatPurchaseInDetailService;
import com.gear.common.core.page.TableDataInfo;
import java.math.BigDecimal;
/**
* 入库记录
*
@@ -75,7 +82,7 @@ public class MatPurchaseInDetailController extends BaseController {
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody MatPurchaseInDetailBo bo) {
return toAjax(iMatPurchaseInDetailService.insertByBo(bo));
return toAjax(iMatPurchaseInDetailService.insertByBoWithInventoryAndPriceHistory(bo));
}
/**
@@ -97,6 +104,6 @@ public class MatPurchaseInDetailController extends BaseController {
@DeleteMapping("/{detailIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] detailIds) {
return toAjax(iMatPurchaseInDetailService.deleteWithValidByIds(Arrays.asList(detailIds), true));
return toAjax(iMatPurchaseInDetailService.deleteWithValidByIdsWithInventoryAndPriceHistory(Arrays.asList(detailIds), true));
}
}

View File

@@ -52,4 +52,6 @@ public class MatPriceHistory extends BaseEntity {
*/
private String remark;
// 关联入库记录id purchase_in_detail_id
private Long purchaseInDetailId;
}

View File

@@ -48,4 +48,7 @@ public class MatPriceHistoryBo extends BaseEntity {
private String remark;
// 关联入库记录id purchase_in_detail_id
private Long purchaseInDetailId;
}

View File

@@ -91,4 +91,6 @@ public class MatPriceHistoryVo {
private BigDecimal currentStock;
// 关联入库记录id purchase_in_detail_id
private Long purchaseInDetailId;
}

View File

@@ -37,6 +37,11 @@ public interface IMatPurchaseInDetailService {
*/
Boolean insertByBo(MatPurchaseInDetailBo bo);
/**
* 新增入库记录并更新库存和价格历史
*/
Boolean insertByBoWithInventoryAndPriceHistory(MatPurchaseInDetailBo bo);
/**
* 修改入库记录
*/
@@ -46,4 +51,9 @@ public interface IMatPurchaseInDetailService {
* 校验并批量删除入库记录信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 校验并批量删除入库记录信息,并还原库存和价格历史
*/
Boolean deleteWithValidByIdsWithInventoryAndPriceHistory(Collection<Long> ids, Boolean isValid);
}

View File

@@ -15,10 +15,17 @@ import com.gear.mat.domain.vo.MatPurchaseInDetailVo;
import com.gear.mat.domain.MatPurchaseInDetail;
import com.gear.mat.mapper.MatPurchaseInDetailMapper;
import com.gear.mat.service.IMatPurchaseInDetailService;
import com.gear.mat.service.IMatMaterialService;
import com.gear.mat.service.IMatPriceHistoryService;
import com.gear.mat.domain.vo.MatMaterialVo;
import com.gear.mat.domain.vo.MatPriceHistoryVo;
import com.gear.mat.domain.bo.MatMaterialBo;
import com.gear.mat.domain.bo.MatPriceHistoryBo;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.*;
import java.math.BigDecimal;
import java.util.stream.Collectors;
/**
* 入库记录Service业务层处理
@@ -31,6 +38,8 @@ import java.util.Collection;
public class MatPurchaseInDetailServiceImpl implements IMatPurchaseInDetailService {
private final MatPurchaseInDetailMapper baseMapper;
private final IMatMaterialService matMaterialService;
private final IMatPriceHistoryService matPriceHistoryService;
/**
* 查询入库记录
@@ -131,4 +140,174 @@ public class MatPurchaseInDetailServiceImpl implements IMatPurchaseInDetailServi
}
return baseMapper.deleteBatchIds(ids) > 0;
}
@Override
public Boolean insertByBoWithInventoryAndPriceHistory(MatPurchaseInDetailBo bo) {
boolean success = insertByBo(bo);
if (success && bo.getMaterialId() != null && bo.getInNum() != null && bo.getInPrice() != null) {
// 更新库存数量
updateInventory(bo.getMaterialId(), bo.getInNum());
// 插入价格历史记录
insertPriceHistory(bo.getMaterialId(), bo.getInPrice(), bo.getInNum(), bo.getDetailId());
}
return success;
}
@Override
public Boolean deleteWithValidByIdsWithInventoryAndPriceHistory(Collection<Long> ids, Boolean isValid) {
// 获取待删除的入库记录信息,以便后续还原库存和价格历史
List<MatPurchaseInDetailVo> recordsToDelete = new ArrayList<>();
for (Long detailId : ids) {
MatPurchaseInDetailVo record = queryById(detailId);
if (record != null) {
recordsToDelete.add(record);
}
}
boolean success = deleteWithValidByIds(ids, isValid);
if (success) {
// 对于每个被删除的入库记录,还原库存并删除对应的价格历史记录
for (MatPurchaseInDetailVo record : recordsToDelete) {
// 还原库存数量
revertInventory(record.getMaterialId(), record.getInNum());
// 删除对应的价格历史记录
deletePriceHistory(record.getMaterialId(), record.getDetailId());
}
}
return success;
}
/**
* 更新库存数量
*/
private void updateInventory(Long materialId, BigDecimal quantity) {
MatMaterialVo material = matMaterialService.queryById(materialId);
if (material != null) {
MatMaterialBo materialBo = new MatMaterialBo();
materialBo.setMaterialId(materialId);
// 计算新的库存数量
BigDecimal newStock = material.getCurrentStock().add(quantity);
materialBo.setCurrentStock(newStock);
matMaterialService.updateByBo(materialBo);
}
}
/**
* 插入价格历史记录
*/
private void insertPriceHistory(Long materialId, BigDecimal price, BigDecimal quantity, Long detailId) {
// 获取当前库存和平均价格信息
MatMaterialVo material = matMaterialService.queryById(materialId);
if (material != null) {
// 计算新的平均价格
BigDecimal avgPrice = calculateAveragePrice(materialId, price, quantity);
// 创建价格历史记录
MatPriceHistoryBo priceHistoryBo = new MatPriceHistoryBo();
priceHistoryBo.setMaterialId(materialId);
priceHistoryBo.setPrice(price);
priceHistoryBo.setAvgPrice(avgPrice);
priceHistoryBo.setQuantity(quantity);
priceHistoryBo.setPurchaseInDetailId(detailId); // 关联入库明细ID
matPriceHistoryService.insertByBo(priceHistoryBo);
}
}
/**
* 计算平均价格
*/
private BigDecimal calculateAveragePrice(Long materialId, BigDecimal newPrice, BigDecimal newQuantity) {
// 获取最新的价格历史记录来计算平均价格
MatPriceHistoryBo condition = new MatPriceHistoryBo();
condition.setMaterialId(materialId);
List<MatPriceHistoryVo> historyList = matPriceHistoryService.queryList(condition);
// 如果没有历史记录,则平均价格就是当前价格
if (historyList == null || historyList.isEmpty()) {
return newPrice;
}
// 获取最近一次的平均价格和库存信息
MatPriceHistoryVo lastHistory = historyList.get(0); // 最近的一条记录
// 获取当前物料的现有库存
MatMaterialVo material = matMaterialService.queryById(materialId);
if (material == null) {
return newPrice;
}
BigDecimal existingStock = material.getCurrentStock().subtract(newQuantity); // 扣除本次入库的数量
BigDecimal existingAvgPrice = lastHistory.getAvgPrice();
// 计算加权平均价格
if (existingStock.compareTo(BigDecimal.ZERO) <= 0) {
// 如果之前没有库存,则直接使用新价格
return newPrice;
} else {
// 加权平均 = (原库存数量 * 原平均价 + 新入库数量 * 新价格) / (原库存数量 + 新入库数量)
BigDecimal totalValue = existingStock.multiply(existingAvgPrice).add(newQuantity.multiply(newPrice));
BigDecimal totalQuantity = existingStock.add(newQuantity);
if (totalQuantity.compareTo(BigDecimal.ZERO) == 0) {
return newPrice;
}
return totalValue.divide(totalQuantity, 6, BigDecimal.ROUND_HALF_UP); // 保留6位小数
}
}
/**
* 还原库存数量(减少库存)
*/
private void revertInventory(Long materialId, BigDecimal quantity) {
MatMaterialVo material = matMaterialService.queryById(materialId);
if (material != null) {
MatMaterialBo materialBo = new MatMaterialBo();
materialBo.setMaterialId(materialId);
materialBo.setMaterialName(material.getMaterialName());
materialBo.setSpec(material.getSpec());
materialBo.setModel(material.getModel());
materialBo.setFactory(material.getFactory());
materialBo.setUnit(material.getUnit());
// 计算还原后的库存数量(减去本次入库的数量)
BigDecimal newStock = material.getCurrentStock().subtract(quantity);
if (newStock.compareTo(BigDecimal.ZERO) < 0) {
newStock = BigDecimal.ZERO; // 库存不能为负数
}
materialBo.setCurrentStock(newStock);
materialBo.setRemark(material.getRemark());
matMaterialService.updateByBo(materialBo);
}
}
/**
* 删除对应的价格历史记录
*/
private void deletePriceHistory(Long materialId, Long purchaseInDetailId) {
MatPriceHistoryBo condition = new MatPriceHistoryBo();
condition.setMaterialId(materialId);
condition.setPurchaseInDetailId(purchaseInDetailId); // 使用精确的入库明细ID进行匹配
// 精确删除对应的记录
List<MatPriceHistoryVo> historyList = matPriceHistoryService.queryList(condition);
if (historyList != null && !historyList.isEmpty()) {
List<Long> historyIdsToDelete = historyList.stream()
.map(MatPriceHistoryVo::getHistoryId)
.collect(Collectors.toList());
matPriceHistoryService.deleteWithValidByIds(historyIdsToDelete, true);
}
}
}

View File

@@ -16,6 +16,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="updateTime" column="update_time"/>
<result property="updateBy" column="update_by"/>
<result property="remark" column="remark"/>
<result property="purchaseInDetailId" column="purchase_in_detail_id"/>
</resultMap>
<select id="selectVoPagePlus" resultType="com.gear.mat.domain.vo.MatPriceHistoryVo">
SELECT
@@ -29,6 +30,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
t.update_time AS updateTime,
t.update_by AS updateBy,
t.remark AS remark,
t.purchase_in_detail_id AS purchaseInDetailId,
mm.material_name AS materialName,
mm.spec AS spec,
mm.model AS model,