Commit dbc6134f805d82d4a3d6d214d71e8eda4064df7f

Authored by YunaiV
1 parent 3479ef81

✨ ERP:增加库存变更记录

yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
... ... @@ -27,6 +27,12 @@ public interface ErrorCodeConstants {
27 27 ErrorCode STOCK_IN_APPROVE_FAIL = new ErrorCode(1_030_401_003, "审核失败,只有未审核的入库单才能审核");
28 28 ErrorCode STOCK_IN_NO_EXISTS = new ErrorCode(1_030_401_004, "生成入库单失败,请重新提交");
29 29  
  30 + // ========== ERP 其它出库单 1-030-402-000 ==========
  31 +
  32 + // ========== ERP 产品库存 1-030-403-000 ==========
  33 + ErrorCode STOCK_COUNT_NEGATIVE = new ErrorCode(1_030_403_000, "操作失败,产品当前库存:{},小于变更数量:{}");
  34 + ErrorCode STOCK_COUNT_NEGATIVE2 = new ErrorCode(1_030_403_000, "操作失败,库存不足");
  35 +
30 36 // ========== ERP 产品 1-030-500-000 ==========
31 37 ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_030_500_000, "产品不存在");
32 38 ErrorCode PRODUCT_NOT_ENABLE = new ErrorCode(1_030_500_001, "产品({})未启用");
... ...
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockRecordDO.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.erp.dal.dataobject.stock;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
  5 +import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;
5 6 import com.baomidou.mybatisplus.annotation.KeySequence;
6 7 import com.baomidou.mybatisplus.annotation.TableId;
7 8 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -56,25 +57,25 @@ public class ErpStockRecordDO extends BaseDO {
56 57 /**
57 58 * 业务类型
58 59 *
59   - * 枚举 {@link cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum}
  60 + * 枚举 {@link ErpStockRecordBizTypeEnum}
60 61 */
61 62 private Integer bizType;
62 63 /**
63 64 * 业务编号
64 65 *
65   - * 例如说:TODO
  66 + * 例如说:{@link ErpStockInDO#getId()}
66 67 */
67 68 private Long bizId;
68 69 /**
69 70 * 业务项编号
70 71 *
71   - * 例如说:TODO
  72 + * 例如说:{@link ErpStockInItemDO#getId()}
72 73 */
73 74 private Long bizItemId;
74 75 /**
75 76 * 业务单号
76 77 *
77   - * 例如说:TODO
  78 + * 例如说:{@link ErpStockInDO#getNo()}
78 79 */
79 80 private String bizNo;
80 81  
... ...
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMapper.java
... ... @@ -5,8 +5,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 6 import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;
7 7 import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
  8 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
8 9 import org.apache.ibatis.annotations.Mapper;
9 10  
  11 +import java.math.BigDecimal;
  12 +
10 13 /**
11 14 * ERP 产品库存 Mapper
12 15 *
... ... @@ -27,4 +30,18 @@ public interface ErpStockMapper extends BaseMapperX<ErpStockDO> {
27 30 ErpStockDO::getWarehouseId, warehouseId);
28 31 }
29 32  
  33 + default int updateCountIncrement(Long id, BigDecimal count, boolean negativeEnable) {
  34 + LambdaUpdateWrapper<ErpStockDO> updateWrapper = new LambdaUpdateWrapper<ErpStockDO>()
  35 + .eq(ErpStockDO::getId, id);
  36 + if (count.compareTo(BigDecimal.ZERO) > 0) {
  37 + updateWrapper.setSql("count = count + " + count);
  38 + } else if (count.compareTo(BigDecimal.ZERO) < 0) {
  39 + if (!negativeEnable) {
  40 + updateWrapper.gt(ErpStockDO::getCount, count.abs());
  41 + }
  42 + updateWrapper.setSql("count = count - " + count.abs());
  43 + }
  44 + return update(null, updateWrapper);
  45 + }
  46 +
30 47 }
31 48 \ No newline at end of file
... ...
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInServiceImpl.java
... ... @@ -13,8 +13,10 @@ import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInItemMapper;
13 13 import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInMapper;
14 14 import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;
15 15 import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
  16 +import cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum;
16 17 import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
17 18 import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;
  19 +import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockInCreateReqBO;
18 20 import jakarta.annotation.Resource;
19 21 import org.springframework.stereotype.Service;
20 22 import org.springframework.transaction.annotation.Transactional;
... ... @@ -54,6 +56,8 @@ public class ErpStockInServiceImpl implements ErpStockInService {
54 56 private ErpWarehouseService warehouseService;
55 57 @Resource
56 58 private ErpSupplierService supplierService;
  59 + @Resource
  60 + private ErpStockRecordService stockRecordService;
57 61  
58 62 @Override
59 63 @Transactional(rollbackFor = Exception.class)
... ... @@ -102,23 +106,31 @@ public class ErpStockInServiceImpl implements ErpStockInService {
102 106 @Override
103 107 @Transactional(rollbackFor = Exception.class)
104 108 public void updateStockInStatus(Long id, Integer status) {
  109 + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
105 110 // 1.1 校验存在
106 111 ErpStockInDO stockIn = validateStockInExists(id);
107 112 // 1.2 校验状态
108 113 if (stockIn.getStatus().equals(status)) {
109   - throw exception(ErpAuditStatus.PROCESS.getStatus().equals(status) ?
110   - STOCK_IN_PROCESS_FAIL : STOCK_IN_APPROVE_FAIL);
  114 + throw exception(approve ? STOCK_IN_APPROVE_FAIL : STOCK_IN_PROCESS_FAIL);
111 115 }
112 116  
113 117 // 2. 更新状态
114 118 int updateCount = stockInMapper.updateByIdAndStatus(id, stockIn.getStatus(),
115 119 new ErpStockInDO().setStatus(status));
116 120 if (updateCount == 0) {
117   - throw exception(ErpAuditStatus.PROCESS.getStatus().equals(status) ?
118   - STOCK_IN_PROCESS_FAIL : STOCK_IN_APPROVE_FAIL);
  121 + throw exception(approve ? STOCK_IN_APPROVE_FAIL : STOCK_IN_PROCESS_FAIL);
119 122 }
120 123  
121   - // 3. TODO 芋艿:调整库存记录
  124 + // 3. 变更库存
  125 + List<ErpStockInItemDO> stockInItems = stockInItemMapper.selectListByInId(id);
  126 + Integer bizType = approve ? ErpStockRecordBizTypeEnum.OTHER_IN.getType()
  127 + : ErpStockRecordBizTypeEnum.OTHER_IN_CANCEL.getType();
  128 + stockInItems.forEach(stockInItem -> {
  129 + BigDecimal count = approve ? stockInItem.getCount() : stockInItem.getCount().negate();
  130 + stockRecordService.createStockRecord(new ErpStockInCreateReqBO(
  131 + stockInItem.getProductId(), stockInItem.getWarehouseId(), count,
  132 + bizType, stockInItem.getInId(), stockInItem.getId(), stockIn.getNo()));
  133 + });
122 134 }
123 135  
124 136 private List<ErpStockInItemDO> validateStockInItems(List<ErpStockInSaveReqVO.Item> list) {
... ...
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordService.java
... ... @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.erp.service.stock;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO;
5 5 import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockRecordDO;
  6 +import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockInCreateReqBO;
  7 +import jakarta.validation.Valid;
6 8  
7 9 /**
8 10 * ERP 产品库存明细 Service 接口
... ... @@ -27,4 +29,11 @@ public interface ErpStockRecordService {
27 29 */
28 30 PageResult<ErpStockRecordDO> getStockRecordPage(ErpStockRecordPageReqVO pageReqVO);
29 31  
  32 + /**
  33 + * 创建库存明细
  34 + *
  35 + * @param createReqBO 创建库存明细 BO
  36 + */
  37 + void createStockRecord(@Valid ErpStockInCreateReqBO createReqBO);
  38 +
30 39 }
31 40 \ No newline at end of file
... ...
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordServiceImpl.java
1 1 package cn.iocoder.yudao.module.erp.service.stock;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 5 import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO;
5 6 import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockRecordDO;
6 7 import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockRecordMapper;
  8 +import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockInCreateReqBO;
7 9 import jakarta.annotation.Resource;
8 10 import org.springframework.stereotype.Service;
  11 +import org.springframework.transaction.annotation.Transactional;
9 12 import org.springframework.validation.annotation.Validated;
10 13  
  14 +import java.math.BigDecimal;
  15 +
11 16 /**
12 17 * ERP 产品库存明细 Service 实现类
13 18 *
... ... @@ -20,6 +25,9 @@ public class ErpStockRecordServiceImpl implements ErpStockRecordService {
20 25 @Resource
21 26 private ErpStockRecordMapper stockRecordMapper;
22 27  
  28 + @Resource
  29 + private ErpStockService stockService;
  30 +
23 31 @Override
24 32 public ErpStockRecordDO getStockRecord(Long id) {
25 33 return stockRecordMapper.selectById(id);
... ... @@ -30,4 +38,16 @@ public class ErpStockRecordServiceImpl implements ErpStockRecordService {
30 38 return stockRecordMapper.selectPage(pageReqVO);
31 39 }
32 40  
  41 + @Override
  42 + @Transactional(rollbackFor = Exception.class)
  43 + public void createStockRecord(ErpStockInCreateReqBO createReqBO) {
  44 + // 1. 更新库存
  45 + BigDecimal totalCount = stockService.updateStockCountIncrement(
  46 + createReqBO.getProductId(), createReqBO.getWarehouseId(), createReqBO.getCount());
  47 + // 2. 创建库存明细
  48 + ErpStockRecordDO stockRecord = BeanUtils.toBean(createReqBO, ErpStockRecordDO.class)
  49 + .setTotalCount(totalCount);
  50 + stockRecordMapper.insert(stockRecord);
  51 + }
  52 +
33 53 }
34 54 \ No newline at end of file
... ...
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockService.java
... ... @@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;
5 5 import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
6 6  
  7 +import java.math.BigDecimal;
  8 +
7 9 /**
8 10 * ERP 产品库存 Service 接口
9 11 *
... ... @@ -36,4 +38,14 @@ public interface ErpStockService {
36 38 */
37 39 PageResult<ErpStockDO> getStockPage(ErpStockPageReqVO pageReqVO);
38 40  
  41 + /**
  42 + * 增量更新产品库存数量
  43 + *
  44 + * @param productId 产品编号
  45 + * @param warehouseId 仓库编号
  46 + * @param count 增量数量:正数,表示增加;负数,表示减少
  47 + * @return 更新后的库存
  48 + */
  49 + BigDecimal updateStockCountIncrement(Long productId, Long warehouseId, BigDecimal count);
  50 +
39 51 }
40 52 \ No newline at end of file
... ...
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockServiceImpl.java
... ... @@ -8,6 +8,12 @@ import jakarta.annotation.Resource;
8 8 import org.springframework.stereotype.Service;
9 9 import org.springframework.validation.annotation.Validated;
10 10  
  11 +import java.math.BigDecimal;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  14 +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_COUNT_NEGATIVE;
  15 +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_COUNT_NEGATIVE2;
  16 +
11 17 /**
12 18 * ERP 产品库存 Service 实现类
13 19 *
... ... @@ -17,6 +23,13 @@ import org.springframework.validation.annotation.Validated;
17 23 @Validated
18 24 public class ErpStockServiceImpl implements ErpStockService {
19 25  
  26 + /**
  27 + * 允许库存为负数
  28 + *
  29 + * TODO 芋艿:后续做成 db 配置
  30 + */
  31 + private static final Boolean NEGATIVE_STOCK_COUNT_ENABLE = false;
  32 +
20 33 @Resource
21 34 private ErpStockMapper stockMapper;
22 35  
... ... @@ -35,4 +48,27 @@ public class ErpStockServiceImpl implements ErpStockService {
35 48 return stockMapper.selectPage(pageReqVO);
36 49 }
37 50  
  51 + @Override
  52 + public BigDecimal updateStockCountIncrement(Long productId, Long warehouseId, BigDecimal count) {
  53 + // 1.1 查询当前库存
  54 + ErpStockDO stock = stockMapper.selectByProductIdAndWarehouseId(productId, warehouseId);
  55 + if (stock == null) {
  56 + stock = new ErpStockDO().setProductId(productId).setWarehouseId(warehouseId).setCount(BigDecimal.ZERO);
  57 + stockMapper.insert(stock);
  58 + }
  59 + // 1.2 校验库存是否充足
  60 + if (!NEGATIVE_STOCK_COUNT_ENABLE && stock.getCount().add(count).compareTo(BigDecimal.ZERO) < 0) {
  61 + throw exception(STOCK_COUNT_NEGATIVE, stock.getCount(), count);
  62 + }
  63 +
  64 + // 2. 库存变更
  65 + int updateCount = stockMapper.updateCountIncrement(stock.getId(), count, NEGATIVE_STOCK_COUNT_ENABLE);
  66 + if (updateCount == 0) {
  67 + throw exception(STOCK_COUNT_NEGATIVE2); // 此时不好去查询最新库存,所以直接抛出该提示,不提供具体库存数字
  68 + }
  69 +
  70 + // 3. 返回最新库存
  71 + return stock.getCount().add(count);
  72 + }
  73 +
38 74 }
39 75 \ No newline at end of file
... ...
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/bo/ErpStockInCreateReqBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.erp.service.stock.bo;
  2 +
  3 +import jakarta.validation.constraints.NotNull;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Data;
  6 +import lombok.NoArgsConstructor;
  7 +
  8 +import java.math.BigDecimal;
  9 +
  10 +/**
  11 + * 库存明细的创建 Request BO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Data
  16 +@NoArgsConstructor
  17 +@AllArgsConstructor
  18 +public class ErpStockInCreateReqBO {
  19 +
  20 + /**
  21 + * 产品编号
  22 + */
  23 + @NotNull(message = "产品编号不能为空")
  24 + private Long productId;
  25 + /**
  26 + * 仓库编号
  27 + */
  28 + @NotNull(message = "仓库编号不能为空")
  29 + private Long warehouseId;
  30 + /**
  31 + * 出入库数量
  32 + *
  33 + * 正数,表示入库;负数,表示出库
  34 + */
  35 + @NotNull(message = "出入库数量不能为空")
  36 + private BigDecimal count;
  37 +
  38 + /**
  39 + * 业务类型
  40 + */
  41 + @NotNull(message = "业务类型不能为空")
  42 + private Integer bizType;
  43 + /**
  44 + * 业务编号
  45 + */
  46 + @NotNull(message = "业务编号不能为空")
  47 + private Long bizId;
  48 + /**
  49 + * 业务项编号
  50 + */
  51 + @NotNull(message = "业务项编号不能为空")
  52 + private Long bizItemId;
  53 + /**
  54 + * 业务单号
  55 + */
  56 + @NotNull(message = "业务单号不能为空")
  57 + private String bizNo;
  58 +
  59 +}
... ...