Browse Source

✨ ERP:增加库存变更记录

YunaiV 1 year ago
parent
commit
dbc6134f80

+ 6 - 0
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 {
     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, "产品({})未启用");

+ 5 - 4
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;
 
 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;
 

+ 17 - 0
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;
 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);
+    }
+
 }

+ 17 - 5
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;
 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) {

+ 9 - 0
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;
 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);
+
 }

+ 20 - 0
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordServiceImpl.java

@@ -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);
+    }
+
 }

+ 12 - 0
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;
 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);
+
 }

+ 36 - 0
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;
 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);
+    }
+
 }

+ 59 - 0
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/bo/ErpStockInCreateReqBO.java

@@ -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;
+
+}