ERP:增加库存变更记录

This commit is contained in:
YunaiV 2024-02-07 12:24:56 +08:00
parent 3479ef8123
commit dbc6134f80
9 changed files with 181 additions and 9 deletions

View File

@ -27,6 +27,12 @@ public interface ErrorCodeConstants {
ErrorCode STOCK_IN_APPROVE_FAIL = new ErrorCode(1_030_401_003, "审核失败,只有未审核的入库单才能审核");
ErrorCode STOCK_IN_NO_EXISTS = new ErrorCode(1_030_401_004, "生成入库单失败,请重新提交");
// ========== ERP 其它出库单 1-030-402-000 ==========
// ========== ERP 产品库存 1-030-403-000 ==========
ErrorCode STOCK_COUNT_NEGATIVE = new ErrorCode(1_030_403_000, "操作失败,产品当前库存:{},小于变更数量:{}");
ErrorCode STOCK_COUNT_NEGATIVE2 = new ErrorCode(1_030_403_000, "操作失败,库存不足");
// ========== ERP 产品 1-030-500-000 ==========
ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_030_500_000, "产品不存在");
ErrorCode PRODUCT_NOT_ENABLE = new ErrorCode(1_030_500_001, "产品({})未启用");

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.erp.dal.dataobject.stock;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -56,25 +57,25 @@ public class ErpStockRecordDO extends BaseDO {
/**
* 业务类型
*
* 枚举 {@link cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum}
* 枚举 {@link ErpStockRecordBizTypeEnum}
*/
private Integer bizType;
/**
* 业务编号
*
* 例如说TODO
* 例如说{@link ErpStockInDO#getId()}
*/
private Long bizId;
/**
* 业务项编号
*
* 例如说TODO
* 例如说{@link ErpStockInItemDO#getId()}
*/
private Long bizItemId;
/**
* 业务单号
*
* 例如说TODO
* 例如说{@link ErpStockInDO#getNo()}
*/
private String bizNo;

View File

@ -5,8 +5,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.math.BigDecimal;
/**
* ERP 产品库存 Mapper
*
@ -27,4 +30,18 @@ public interface ErpStockMapper extends BaseMapperX<ErpStockDO> {
ErpStockDO::getWarehouseId, warehouseId);
}
default int updateCountIncrement(Long id, BigDecimal count, boolean negativeEnable) {
LambdaUpdateWrapper<ErpStockDO> updateWrapper = new LambdaUpdateWrapper<ErpStockDO>()
.eq(ErpStockDO::getId, id);
if (count.compareTo(BigDecimal.ZERO) > 0) {
updateWrapper.setSql("count = count + " + count);
} else if (count.compareTo(BigDecimal.ZERO) < 0) {
if (!negativeEnable) {
updateWrapper.gt(ErpStockDO::getCount, count.abs());
}
updateWrapper.setSql("count = count - " + count.abs());
}
return update(null, updateWrapper);
}
}

View File

@ -13,8 +13,10 @@ import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInItemMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInMapper;
import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;
import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;
import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockInCreateReqBO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -54,6 +56,8 @@ public class ErpStockInServiceImpl implements ErpStockInService {
private ErpWarehouseService warehouseService;
@Resource
private ErpSupplierService supplierService;
@Resource
private ErpStockRecordService stockRecordService;
@Override
@Transactional(rollbackFor = Exception.class)
@ -102,23 +106,31 @@ public class ErpStockInServiceImpl implements ErpStockInService {
@Override
@Transactional(rollbackFor = Exception.class)
public void updateStockInStatus(Long id, Integer status) {
boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
// 1.1 校验存在
ErpStockInDO stockIn = validateStockInExists(id);
// 1.2 校验状态
if (stockIn.getStatus().equals(status)) {
throw exception(ErpAuditStatus.PROCESS.getStatus().equals(status) ?
STOCK_IN_PROCESS_FAIL : STOCK_IN_APPROVE_FAIL);
throw exception(approve ? STOCK_IN_APPROVE_FAIL : STOCK_IN_PROCESS_FAIL);
}
// 2. 更新状态
int updateCount = stockInMapper.updateByIdAndStatus(id, stockIn.getStatus(),
new ErpStockInDO().setStatus(status));
if (updateCount == 0) {
throw exception(ErpAuditStatus.PROCESS.getStatus().equals(status) ?
STOCK_IN_PROCESS_FAIL : STOCK_IN_APPROVE_FAIL);
throw exception(approve ? STOCK_IN_APPROVE_FAIL : STOCK_IN_PROCESS_FAIL);
}
// 3. TODO 芋艿调整库存记录
// 3. 变更库存
List<ErpStockInItemDO> stockInItems = stockInItemMapper.selectListByInId(id);
Integer bizType = approve ? ErpStockRecordBizTypeEnum.OTHER_IN.getType()
: ErpStockRecordBizTypeEnum.OTHER_IN_CANCEL.getType();
stockInItems.forEach(stockInItem -> {
BigDecimal count = approve ? stockInItem.getCount() : stockInItem.getCount().negate();
stockRecordService.createStockRecord(new ErpStockInCreateReqBO(
stockInItem.getProductId(), stockInItem.getWarehouseId(), count,
bizType, stockInItem.getInId(), stockInItem.getId(), stockIn.getNo()));
});
}
private List<ErpStockInItemDO> validateStockInItems(List<ErpStockInSaveReqVO.Item> list) {

View File

@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.erp.service.stock;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockRecordDO;
import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockInCreateReqBO;
import jakarta.validation.Valid;
/**
* ERP 产品库存明细 Service 接口
@ -27,4 +29,11 @@ public interface ErpStockRecordService {
*/
PageResult<ErpStockRecordDO> getStockRecordPage(ErpStockRecordPageReqVO pageReqVO);
/**
* 创建库存明细
*
* @param createReqBO 创建库存明细 BO
*/
void createStockRecord(@Valid ErpStockInCreateReqBO createReqBO);
}

View File

@ -1,13 +1,18 @@
package cn.iocoder.yudao.module.erp.service.stock;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockRecordDO;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockRecordMapper;
import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockInCreateReqBO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
/**
* ERP 产品库存明细 Service 实现类
*
@ -20,6 +25,9 @@ public class ErpStockRecordServiceImpl implements ErpStockRecordService {
@Resource
private ErpStockRecordMapper stockRecordMapper;
@Resource
private ErpStockService stockService;
@Override
public ErpStockRecordDO getStockRecord(Long id) {
return stockRecordMapper.selectById(id);
@ -30,4 +38,16 @@ public class ErpStockRecordServiceImpl implements ErpStockRecordService {
return stockRecordMapper.selectPage(pageReqVO);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void createStockRecord(ErpStockInCreateReqBO createReqBO) {
// 1. 更新库存
BigDecimal totalCount = stockService.updateStockCountIncrement(
createReqBO.getProductId(), createReqBO.getWarehouseId(), createReqBO.getCount());
// 2. 创建库存明细
ErpStockRecordDO stockRecord = BeanUtils.toBean(createReqBO, ErpStockRecordDO.class)
.setTotalCount(totalCount);
stockRecordMapper.insert(stockRecord);
}
}

View File

@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
import java.math.BigDecimal;
/**
* ERP 产品库存 Service 接口
*
@ -36,4 +38,14 @@ public interface ErpStockService {
*/
PageResult<ErpStockDO> getStockPage(ErpStockPageReqVO pageReqVO);
/**
* 增量更新产品库存数量
*
* @param productId 产品编号
* @param warehouseId 仓库编号
* @param count 增量数量正数表示增加负数表示减少
* @return 更新后的库存
*/
BigDecimal updateStockCountIncrement(Long productId, Long warehouseId, BigDecimal count);
}

View File

@ -8,6 +8,12 @@ import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_COUNT_NEGATIVE;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_COUNT_NEGATIVE2;
/**
* ERP 产品库存 Service 实现类
*
@ -17,6 +23,13 @@ import org.springframework.validation.annotation.Validated;
@Validated
public class ErpStockServiceImpl implements ErpStockService {
/**
* 允许库存为负数
*
* TODO 芋艿后续做成 db 配置
*/
private static final Boolean NEGATIVE_STOCK_COUNT_ENABLE = false;
@Resource
private ErpStockMapper stockMapper;
@ -35,4 +48,27 @@ public class ErpStockServiceImpl implements ErpStockService {
return stockMapper.selectPage(pageReqVO);
}
@Override
public BigDecimal updateStockCountIncrement(Long productId, Long warehouseId, BigDecimal count) {
// 1.1 查询当前库存
ErpStockDO stock = stockMapper.selectByProductIdAndWarehouseId(productId, warehouseId);
if (stock == null) {
stock = new ErpStockDO().setProductId(productId).setWarehouseId(warehouseId).setCount(BigDecimal.ZERO);
stockMapper.insert(stock);
}
// 1.2 校验库存是否充足
if (!NEGATIVE_STOCK_COUNT_ENABLE && stock.getCount().add(count).compareTo(BigDecimal.ZERO) < 0) {
throw exception(STOCK_COUNT_NEGATIVE, stock.getCount(), count);
}
// 2. 库存变更
int updateCount = stockMapper.updateCountIncrement(stock.getId(), count, NEGATIVE_STOCK_COUNT_ENABLE);
if (updateCount == 0) {
throw exception(STOCK_COUNT_NEGATIVE2); // 此时不好去查询最新库存所以直接抛出该提示不提供具体库存数字
}
// 3. 返回最新库存
return stock.getCount().add(count);
}
}

View File

@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.erp.service.stock.bo;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* 库存明细的创建 Request BO
*
* @author 芋道源码
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ErpStockInCreateReqBO {
/**
* 产品编号
*/
@NotNull(message = "产品编号不能为空")
private Long productId;
/**
* 仓库编号
*/
@NotNull(message = "仓库编号不能为空")
private Long warehouseId;
/**
* 出入库数量
*
* 正数表示入库负数表示出库
*/
@NotNull(message = "出入库数量不能为空")
private BigDecimal count;
/**
* 业务类型
*/
@NotNull(message = "业务类型不能为空")
private Integer bizType;
/**
* 业务编号
*/
@NotNull(message = "业务编号不能为空")
private Long bizId;
/**
* 业务项编号
*/
@NotNull(message = "业务项编号不能为空")
private Long bizItemId;
/**
* 业务单号
*/
@NotNull(message = "业务单号不能为空")
private String bizNo;
}