加入change变化金额问题

This commit is contained in:
2026-01-20 15:48:08 +08:00
parent 612d9cceca
commit fbf6149e30
12 changed files with 231 additions and 72 deletions

View File

@@ -48,6 +48,17 @@ public class EqpAuxiliaryMaterial extends BaseEntity {
* 当前库存数量
*/
private Long quantity;
/**
* 当前移动加权平均单价
*/
private java.math.BigDecimal unitPrice;
/**
* 当前库存总金额(可冗余,= quantity * unit_price
*/
private java.math.BigDecimal totalAmount;
/**
* 删除标志0=存在 2=删除)
*/

View File

@@ -6,7 +6,6 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 辅料变动记录对象 eqp_auxiliary_material_change
@@ -38,6 +37,22 @@ public class EqpAuxiliaryMaterialChange extends BaseEntity {
* 变动数量
*/
private Long changeQuantity;
/**
* 入库单价(仅增加时有意义)
*/
private java.math.BigDecimal inUnitPrice;
/**
* 单价快照(减少时必填)
*/
private java.math.BigDecimal unitPriceSnapshot;
/**
* 本次变动金额(有符号;减少为负)
*/
private java.math.BigDecimal amount;
/**
* 变动原因
*/

View File

@@ -48,6 +48,17 @@ public class EqpSparePart extends BaseEntity {
* 当前库存数量
*/
private Long quantity;
/**
* 当前移动加权平均单价
*/
private java.math.BigDecimal unitPrice;
/**
* 当前库存总金额(可冗余,= quantity * unit_price
*/
private java.math.BigDecimal totalAmount;
/**
* 删除标志0=存在 2=删除)
*/

View File

@@ -6,7 +6,6 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 备品备件变动记录对象 eqp_spare_parts_change
@@ -38,6 +37,22 @@ public class EqpSparePartsChange extends BaseEntity {
* 变动数量
*/
private Long changeQuantity;
/**
* 入库单价(仅增加时有意义)
*/
private java.math.BigDecimal inUnitPrice;
/**
* 单价快照(减少时必填)
*/
private java.math.BigDecimal unitPriceSnapshot;
/**
* 本次变动金额(有符号;减少为负)
*/
private java.math.BigDecimal amount;
/**
* 变动原因
*/

View File

@@ -39,6 +39,11 @@ public class EqpAuxiliaryMaterialChangeBo extends BaseEntity {
*/
private Long changeQuantity;
/**
* 入库单价(前端传入,仅 changeType=增加 时必填)
*/
private java.math.BigDecimal inUnitPrice;
/**
* 变动原因
*/

View File

@@ -39,6 +39,11 @@ public class EqpSparePartsChangeBo extends BaseEntity {
*/
private Long changeQuantity;
/**
* 入库单价(前端传入,仅 changeType=增加 时必填)
*/
private java.math.BigDecimal inUnitPrice;
/**
* 变动原因
*/

View File

@@ -22,7 +22,6 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
@@ -67,7 +66,6 @@ public class EqpAuxiliaryMaterialChangeServiceImpl implements IEqpAuxiliaryMater
}
private LambdaQueryWrapper<EqpAuxiliaryMaterialChange> buildQueryWrapper(EqpAuxiliaryMaterialChangeBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<EqpAuxiliaryMaterialChange> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getAuxiliaryId() != null, EqpAuxiliaryMaterialChange::getAuxiliaryId, bo.getAuxiliaryId());
lqw.eq(StringUtils.isNotBlank(bo.getChangeType()), EqpAuxiliaryMaterialChange::getChangeType, bo.getChangeType());
@@ -146,23 +144,80 @@ public class EqpAuxiliaryMaterialChangeServiceImpl implements IEqpAuxiliaryMater
return R.fail("辅料已删除,无法操作");
}
// 3. 库存变动逻辑
Long newQuantity; // 替换为Integer原表quantity是int
if ("减少".equals(bo.getChangeType())) {
// 减少库存时校验库存充足性
if (auxiliaryMaterial.getQuantity() < bo.getChangeQuantity()) {
return R.fail("库存不足,当前库存:" + auxiliaryMaterial.getQuantity() + ",需减少:" + bo.getChangeQuantity());
// 3. 库存与金额变动逻辑(移动加权平均 + 消耗单价快照)
java.math.BigDecimal oldUnitPrice = auxiliaryMaterial.getUnitPrice() == null ? java.math.BigDecimal.ZERO : auxiliaryMaterial.getUnitPrice();
java.math.BigDecimal oldTotalAmount = auxiliaryMaterial.getTotalAmount() == null ? java.math.BigDecimal.ZERO : auxiliaryMaterial.getTotalAmount();
Long oldQty = auxiliaryMaterial.getQuantity() == null ? 0L : auxiliaryMaterial.getQuantity();
Long changeQty = bo.getChangeQuantity();
Long newQuantity;
java.math.BigDecimal newUnitPrice = oldUnitPrice;
java.math.BigDecimal newTotalAmount = oldTotalAmount;
// 从上下文获取操作人
String operator = String.valueOf(LoginHelper.getUsername());
// 4. 插入变动记录(先组装,后续补齐单价快照与金额)
EqpAuxiliaryMaterialChange changeLog = new EqpAuxiliaryMaterialChange();
changeLog.setAuxiliaryId(bo.getAuxiliaryId());
changeLog.setChangeType(bo.getChangeType());
changeLog.setChangeQuantity(changeQty);
changeLog.setReason(bo.getReason());
changeLog.setRemark(bo.getRemark());
changeLog.setChangeTime(new Date());
changeLog.setCreateBy(operator);
changeLog.setUpdateBy(operator);
changeLog.setDelFlag("0");
if ("增加".equals(bo.getChangeType())) {
// 入库单价前端必填
if (bo.getInUnitPrice() == null) {
return R.fail("入库单价不能为空。建议按当前台账单价补齐后再提交。");
}
newQuantity = auxiliaryMaterial.getQuantity() - bo.getChangeQuantity();
java.math.BigDecimal inUnitPrice = bo.getInUnitPrice();
newQuantity = oldQty + changeQty;
// 移动加权平均价: (old_price*old_qty + in_price*in_qty) / (old_qty + in_qty)
java.math.BigDecimal numerator = oldUnitPrice.multiply(java.math.BigDecimal.valueOf(oldQty))
.add(inUnitPrice.multiply(java.math.BigDecimal.valueOf(changeQty)));
java.math.BigDecimal denominator = java.math.BigDecimal.valueOf(newQuantity);
newUnitPrice = denominator.compareTo(java.math.BigDecimal.ZERO) > 0
? numerator.divide(denominator, 6, java.math.RoundingMode.HALF_UP)
: java.math.BigDecimal.ZERO;
newTotalAmount = newUnitPrice.multiply(java.math.BigDecimal.valueOf(newQuantity)).setScale(2, java.math.RoundingMode.HALF_UP);
changeLog.setInUnitPrice(inUnitPrice);
// 增加时也记录快照(记录“入库后单价”更利于追溯)
changeLog.setUnitPriceSnapshot(newUnitPrice);
// amount 采用有符号:增加为正
changeLog.setAmount(inUnitPrice.multiply(java.math.BigDecimal.valueOf(changeQty)).setScale(2, java.math.RoundingMode.HALF_UP));
} else {
// 增加库存直接累加
newQuantity = auxiliaryMaterial.getQuantity() + bo.getChangeQuantity();
// 减少库存时校验库存充足性
if (oldQty < changeQty) {
return R.fail("库存不足,当前库存:" + oldQty + ",需减少:" + changeQty);
}
newQuantity = oldQty - changeQty;
// 减少时捕捉当前台账单价快照amount 为负
java.math.BigDecimal unitPriceSnapshot = oldUnitPrice;
java.math.BigDecimal costAbs = unitPriceSnapshot.multiply(java.math.BigDecimal.valueOf(changeQty)).setScale(2, java.math.RoundingMode.HALF_UP);
changeLog.setUnitPriceSnapshot(unitPriceSnapshot);
changeLog.setAmount(costAbs.negate());
// 更新库存总金额(按剩余库存重新计算)
newUnitPrice = oldUnitPrice;
newTotalAmount = newUnitPrice.multiply(java.math.BigDecimal.valueOf(newQuantity)).setScale(2, java.math.RoundingMode.HALF_UP);
}
// 4. 更新辅料库存(填充审计字段
// 5. 更新辅料库存(数量 + 单价 + 总金额
auxiliaryMaterial.setQuantity(newQuantity);
// 从上下文获取操作人(替换为你的登录助手类)
String operator = String.valueOf(LoginHelper.getUsername());
auxiliaryMaterial.setUnitPrice(newUnitPrice);
auxiliaryMaterial.setTotalAmount(newTotalAmount);
auxiliaryMaterial.setUpdateBy(operator);
auxiliaryMaterial.setUpdateTime(new Date());
boolean updateSuccess = eqpAuxiliaryMaterialMapper.updateById(auxiliaryMaterial) > 0;
@@ -170,21 +225,7 @@ public class EqpAuxiliaryMaterialChangeServiceImpl implements IEqpAuxiliaryMater
return R.fail("库存更新失败");
}
// 5. 插入变动记录基于EqpAuxiliaryMaterialChangeBo转换
EqpAuxiliaryMaterialChange changeLog = new EqpAuxiliaryMaterialChange();
// 从Bo复制核心字段
changeLog.setAuxiliaryId(bo.getAuxiliaryId());
changeLog.setChangeType(bo.getChangeType());
changeLog.setChangeQuantity(bo.getChangeQuantity());
changeLog.setReason(bo.getReason()); // 变动原因
changeLog.setRemark(bo.getRemark()); // 备注
// 补充审计字段
changeLog.setChangeTime(new Date());
changeLog.setCreateBy(operator);
changeLog.setUpdateBy(operator);
changeLog.setDelFlag("0"); // 未删除
// 插入变动记录
// 6. 插入变动记录
int insertCount = baseMapper.insert(changeLog);
if (insertCount <= 0) {
return R.fail("变动记录插入失败");

View File

@@ -3,7 +3,6 @@ package com.klp.mes.eqp.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.klp.common.core.domain.R;
import com.klp.common.core.domain.model.LoginUser;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -12,7 +11,6 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.klp.common.helper.LoginHelper;
import com.klp.common.utils.StringUtils;
import com.klp.mes.eqp.domain.EqpSparePart;
import com.klp.mes.eqp.domain.bo.EqpSparePartBo;
import com.klp.mes.eqp.mapper.EqpSparePartMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
@@ -26,7 +24,6 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
@@ -85,7 +82,6 @@ public class EqpSparePartsChangeServiceImpl implements IEqpSparePartsChangeServi
}
private LambdaQueryWrapper<EqpSparePartsChange> buildQueryWrapper(EqpSparePartsChangeBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<EqpSparePartsChange> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getPartId() != null, EqpSparePartsChange::getPartId, bo.getPartId());
lqw.eq(StringUtils.isNotBlank(bo.getChangeType()), EqpSparePartsChange::getChangeType, bo.getChangeType());
@@ -164,23 +160,71 @@ public class EqpSparePartsChangeServiceImpl implements IEqpSparePartsChangeServi
return R.fail("备品备件已删除,无法操作");
}
// 3. 库存变动逻辑
// 3. 库存与金额变动逻辑(移动加权平均 + 消耗单价快照)
java.math.BigDecimal oldUnitPrice = sparePart.getUnitPrice() == null ? java.math.BigDecimal.ZERO : sparePart.getUnitPrice();
java.math.BigDecimal oldTotalAmount = sparePart.getTotalAmount() == null ? java.math.BigDecimal.ZERO : sparePart.getTotalAmount();
Long oldQty = sparePart.getQuantity() == null ? 0L : sparePart.getQuantity();
Long changeQty = bo.getChangeQuantity();
Long newQuantity;
if ("减少".equals(bo.getChangeType())) {
// 减少库存时校验库存充足性
if (sparePart.getQuantity() < bo.getChangeQuantity()) {
return R.fail("库存不足,当前库存:" + sparePart.getQuantity() + ",需减少:" + bo.getChangeQuantity());
java.math.BigDecimal newUnitPrice = oldUnitPrice;
java.math.BigDecimal newTotalAmount = oldTotalAmount;
String operator = String.valueOf(LoginHelper.getUsername());
EqpSparePartsChange changeLog = new EqpSparePartsChange();
changeLog.setPartId(bo.getPartId());
changeLog.setChangeType(bo.getChangeType());
changeLog.setChangeQuantity(changeQty);
changeLog.setReason(bo.getReason());
changeLog.setRemark(bo.getRemark());
changeLog.setChangeTime(new Date());
changeLog.setCreateBy(operator);
changeLog.setUpdateBy(operator);
changeLog.setDelFlag("0");
if ("增加".equals(bo.getChangeType())) {
if (bo.getInUnitPrice() == null) {
return R.fail("入库单价不能为空。建议按当前台账单价补齐后再提交。");
}
newQuantity = sparePart.getQuantity() - bo.getChangeQuantity();
java.math.BigDecimal inUnitPrice = bo.getInUnitPrice();
newQuantity = oldQty + changeQty;
java.math.BigDecimal numerator = oldUnitPrice.multiply(java.math.BigDecimal.valueOf(oldQty))
.add(inUnitPrice.multiply(java.math.BigDecimal.valueOf(changeQty)));
java.math.BigDecimal denominator = java.math.BigDecimal.valueOf(newQuantity);
newUnitPrice = denominator.compareTo(java.math.BigDecimal.ZERO) > 0
? numerator.divide(denominator, 6, java.math.RoundingMode.HALF_UP)
: java.math.BigDecimal.ZERO;
newTotalAmount = newUnitPrice.multiply(java.math.BigDecimal.valueOf(newQuantity)).setScale(2, java.math.RoundingMode.HALF_UP);
changeLog.setInUnitPrice(inUnitPrice);
changeLog.setUnitPriceSnapshot(newUnitPrice);
changeLog.setAmount(inUnitPrice.multiply(java.math.BigDecimal.valueOf(changeQty)).setScale(2, java.math.RoundingMode.HALF_UP));
} else {
// 增加库存直接累加
newQuantity = sparePart.getQuantity() + bo.getChangeQuantity();
if (oldQty < changeQty) {
return R.fail("库存不足,当前库存:" + oldQty + ",需减少:" + changeQty);
}
newQuantity = oldQty - changeQty;
java.math.BigDecimal unitPriceSnapshot = oldUnitPrice;
java.math.BigDecimal costAbs = unitPriceSnapshot.multiply(java.math.BigDecimal.valueOf(changeQty)).setScale(2, java.math.RoundingMode.HALF_UP);
changeLog.setUnitPriceSnapshot(unitPriceSnapshot);
changeLog.setAmount(costAbs.negate());
newUnitPrice = oldUnitPrice;
newTotalAmount = newUnitPrice.multiply(java.math.BigDecimal.valueOf(newQuantity)).setScale(2, java.math.RoundingMode.HALF_UP);
}
// 4. 更新备件库存(填充审计字段
// 4. 更新备件库存(数量 + 单价 + 总金额
sparePart.setQuantity(newQuantity);
// 从Bo获取操作人优先或默认值
String operator = String.valueOf(LoginHelper.getUsername());
sparePart.setUnitPrice(newUnitPrice);
sparePart.setTotalAmount(newTotalAmount);
sparePart.setUpdateBy(operator);
sparePart.setUpdateTime(new Date());
boolean updateSuccess = eqpSparePartMapper.updateById(sparePart) > 0;
@@ -188,21 +232,7 @@ public class EqpSparePartsChangeServiceImpl implements IEqpSparePartsChangeServi
return R.fail("库存更新失败");
}
// 5. 插入变动记录基于EqpSparePartsChangeBo转换
EqpSparePartsChange changeLog = new EqpSparePartsChange();
// 从Bo复制核心字段
changeLog.setPartId(bo.getPartId());
changeLog.setChangeType(bo.getChangeType());
changeLog.setChangeQuantity(bo.getChangeQuantity());
changeLog.setReason(bo.getReason()); // 变动原因Bo中应包含该字段
changeLog.setRemark(bo.getRemark()); // 备注Bo中应包含该字段
// 补充审计字段
changeLog.setChangeTime(new Date());
changeLog.setCreateBy(operator);
changeLog.setUpdateBy(operator);
changeLog.setDelFlag("0"); // 未删除
// 插入变动记录需注入对应Mapper
// 5. 插入变动记录
int insertCount = baseMapper.insert(changeLog);
if (insertCount <= 0) {
return R.fail("变动记录插入失败");