From 593e1fd59c68422b582c21a27ab8b9e43fcb2bc6 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 6 Feb 2024 23:28:57 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20ERP=EF=BC=9A=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E5=BA=93=E5=8D=95=E7=9A=84=E5=AE=A1=E6=89=B9=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/collection/CollectionUtils.java | 15 +++-- .../common/util/number/NumberUtils.java | 20 ++++++ .../module/erp/enums/ErrorCodeConstants.java | 6 +- .../admin/stock/ErpStockInController.java | 28 +++++--- .../admin/stock/vo/in/ErpStockInRespVO.java | 2 + .../stock/vo/in/ErpStockInSaveReqVO.java | 3 +- .../erp/dal/mysql/stock/ErpStockInMapper.java | 6 ++ .../erp/service/stock/ErpStockInService.java | 12 +++- .../service/stock/ErpStockInServiceImpl.java | 67 ++++++++++++++----- 9 files changed, 125 insertions(+), 34 deletions(-) diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java index 35ebc8ff3..2d9e4dd63 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java @@ -288,11 +288,16 @@ public class CollectionUtils { public static > V getSumValue(List from, Function valueFunc, BinaryOperator accumulator) { + return getSumValue(from, valueFunc, accumulator, null); + } + + public static > V getSumValue(List from, Function valueFunc, + BinaryOperator accumulator, V defaultValue) { if (CollUtil.isEmpty(from)) { - return null; + return defaultValue; } - assert from.size() > 0; // 断言,避免告警 - return from.stream().map(valueFunc).reduce(accumulator).get(); + assert !from.isEmpty(); // 断言,避免告警 + return from.stream().map(valueFunc).filter(Objects::nonNull).reduce(accumulator).orElse(defaultValue); } public static void addIfNotNull(Collection coll, T item) { @@ -302,8 +307,8 @@ public class CollectionUtils { coll.add(item); } - public static Collection singleton(T deptId) { - return deptId == null ? Collections.emptyList() : Collections.singleton(deptId); + public static Collection singleton(T obj) { + return obj == null ? Collections.emptyList() : Collections.singleton(obj); } } diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java index 55ab367a3..ea131e86e 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java @@ -1,7 +1,10 @@ package cn.iocoder.yudao.framework.common.util.number; +import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; +import java.math.BigDecimal; + /** * 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能 * @@ -37,4 +40,21 @@ public class NumberUtils { return distance; } + /** + * 提供精确的乘法运算 + * + * 和 hutool {@link NumberUtil#mul(BigDecimal...)} 的差别是,如果存在 null,则返回 null + * + * @param values 多个被乘值 + * @return 积 + */ + public static BigDecimal mul(BigDecimal... values) { + for (BigDecimal value : values) { + if (value == null) { + return null; + } + } + return NumberUtil.mul(values); + } + } diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java index 1cc2807ad..5cfec682c 100644 --- a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java +++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java @@ -13,7 +13,7 @@ public interface ErrorCodeConstants { ErrorCode SUPPLIER_NOT_EXISTS = new ErrorCode(1_030_100_000, "供应商不存在"); ErrorCode SUPPLIER_NOT_ENABLE = new ErrorCode(1_030_100_000, "供应商({})未启用"); - // ========== 销售订单(1-030-200-000) ========== + // ========== ERP 销售订单(1-030-200-000) ========== ErrorCode SALE_ORDER_NOT_EXISTS = new ErrorCode(1_020_200_000, "销售订单不存在"); // ========== ERP 仓库 1-030-400-000 ========== @@ -22,6 +22,10 @@ public interface ErrorCodeConstants { // ========== ERP 其它入库单 1-030-401-000 ========== ErrorCode STOCK_IN_NOT_EXISTS = new ErrorCode(1_030_401_000, "其它入库单不存在"); + ErrorCode STOCK_IN_DELETE_FAIL_APPROVE = new ErrorCode(1_030_401_001, "其它入库单({})已审核,无法删除"); + ErrorCode STOCK_IN_PROCESS_FAIL = new ErrorCode(1_030_401_002, "反审核失败,只有已审核的入库单才能反审核"); + ErrorCode STOCK_IN_APPROVE_FAIL = new ErrorCode(1_030_401_003, "审核失败,只有未审核的入库单才能审核"); + // ========== ERP 产品 1-030-500-000 ========== ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_030_500_000, "产品不存在"); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockInController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockInController.java index 1351516ed..530d04350 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockInController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockInController.java @@ -75,12 +75,21 @@ public class ErpStockInController { return success(true); } + @PutMapping("/update-status") + @Operation(summary = "更新其它入库单的状态") + @PreAuthorize("@ss.hasPermission('erp:stock-in:update')") + public CommonResult updateStockInStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + stockInService.updateStockInStatus(id, status); + return success(true); + } + @DeleteMapping("/delete") @Operation(summary = "删除其它入库单") - @Parameter(name = "id", description = "编号", required = true) + @Parameter(name = "ids", description = "编号数组", required = true) @PreAuthorize("@ss.hasPermission('erp:stock-in:delete')") - public CommonResult deleteStockIn(@RequestParam("id") Long id) { - stockInService.deleteStockIn(id); + public CommonResult deleteStockIn(@RequestParam("ids") List ids) { + stockInService.deleteStockIn(ids); return success(true); } @@ -93,12 +102,15 @@ public class ErpStockInController { if (stockIn == null) { return success(null); } - List stockInItems = stockInService.getStockInItemListByInId(id); - // TODO 芋艿:有个锤子; + List stockInItemList = stockInService.getStockInItemListByInId(id); + Map productMap = productService.getProductVOMap( + convertSet(stockInItemList, ErpStockInItemDO::getProductId)); return success(BeanUtils.toBean(stockIn, ErpStockInRespVO.class, stockInVO -> - stockInVO.setItems(BeanUtils.toBean(stockInItems, ErpStockInRespVO.Item.class, item -> { + stockInVO.setItems(BeanUtils.toBean(stockInItemList, ErpStockInRespVO.Item.class, item -> { ErpStockDO stock = stockService.getStock(item.getProductId(), item.getWarehouseId()); item.setStockCount(stock != null ? stock.getCount() : BigDecimal.ZERO); + MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())); })))); } @@ -142,8 +154,8 @@ public class ErpStockInController { // 2. 开始拼接 return BeanUtils.toBean(pageResult, ErpStockInRespVO.class, stockIn -> { stockIn.setItems(BeanUtils.toBean(stockInItemMap.get(stockIn.getId()), ErpStockInRespVO.Item.class, - item -> MapUtils.findAndThen(productMap, item.getProductId(), - product -> item.setProductName(product.getName()).setProductUnitName(product.getUnitName())))); + item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))); stockIn.setProductNames(CollUtil.join(stockIn.getItems(), ",", ErpStockInRespVO.Item::getProductName)); MapUtils.findAndThen(supplierMap, stockIn.getSupplierId(), supplier -> stockIn.setSupplierName(supplier.getName())); MapUtils.findAndThen(userMap, Long.parseLong(stockIn.getCreator()), user -> stockIn.setCreatorName(user.getNickname())); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInRespVO.java index 07897ad30..e6263db61 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInRespVO.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInRespVO.java @@ -97,6 +97,8 @@ public class ErpStockInRespVO { @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "巧克力") private String productName; + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "A9985") + private String productBarCode; @Schema(description = "产品单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒") private String productUnitName; diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInSaveReqVO.java index c3cc6be95..e3a87da8a 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInSaveReqVO.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInSaveReqVO.java @@ -53,8 +53,7 @@ public class ErpStockInSaveReqVO { @NotNull(message = "产品编号不能为空") private Long productId; - @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") - @NotNull(message = "产品单价不能为空") + @Schema(description = "产品单价", example = "100.00") private BigDecimal productPrice; @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInMapper.java index 0b3e71a5f..eadf02c75 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInMapper.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInMapper.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInPageReqVO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; /** @@ -34,4 +35,9 @@ public interface ErpStockInMapper extends BaseMapperX { return selectJoinPage(reqVO, ErpStockInDO.class, query); } + default int updateByIdAndStatus(Long id, Integer status, ErpStockInDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpStockInDO::getId, id).eq(ErpStockInDO::getStatus, status)); + } + } \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInService.java index 92555135f..4e51545e4 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInService.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInService.java @@ -33,11 +33,19 @@ public interface ErpStockInService { void updateStockIn(@Valid ErpStockInSaveReqVO updateReqVO); /** - * 删除其它入库单 + * 更新其它入库单的状态 * * @param id 编号 + * @param status 状态 */ - void deleteStockIn(Long id); + void updateStockInStatus(Long id, Integer status); + + /** + * 删除其它入库单 + * + * @param ids 编号数组 + */ + void deleteStockIn(List ids); /** * 获得其它入库单 diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInServiceImpl.java index 0cf716018..305676445 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInServiceImpl.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInServiceImpl.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.erp.service.stock; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInSaveReqVO; @@ -26,9 +27,9 @@ import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; -import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_IN_NOT_EXISTS; - +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; +// TODO 芋艿:记录操作日志 /** * ERP 其它入库单 Service 实现类 * @@ -62,9 +63,9 @@ public class ErpStockInServiceImpl implements ErpStockInService { ErpStockInDO stockIn = BeanUtils.toBean(createReqVO, ErpStockInDO.class, in -> in .setStatus(ErpAuditStatus.PROCESS.getStatus()) .setTotalCount(getSumValue(stockInItems, ErpStockInItemDO::getCount, BigDecimal::add)) - .setTotalPrice(getSumValue(stockInItems, ErpStockInItemDO::getTotalPrice, BigDecimal::add))); + .setTotalPrice(getSumValue(stockInItems, ErpStockInItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO))); stockInMapper.insert(stockIn); - // 2. 插入子表 + // 2.2 插入入库单项 stockInItems.forEach(o -> o.setInId(stockIn.getId())); stockInItemMapper.insertBatch(stockInItems); return stockIn.getId(); @@ -89,6 +90,28 @@ public class ErpStockInServiceImpl implements ErpStockInService { updateStockInItemList(updateReqVO.getId(), stockInItems); } + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStockInStatus(Long id, Integer 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); + } + + // 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); + } + + // 3. TODO 芋艿:调整库存记录 + } + private List validateStockInItems(List list) { // 1.1 校验产品存在 List productList = productService.validProductList(convertSet(list, ErpStockInSaveReqVO.Item::getProductId)); @@ -98,7 +121,7 @@ public class ErpStockInServiceImpl implements ErpStockInService { // 2. 转化为 ErpStockInItemDO 列表 return convertList(list, o -> BeanUtils.toBean(o, ErpStockInItemDO.class, item -> item .setProductUnitId(productMap.get(item.getProductId()).getUnitId()) - .setTotalPrice(item.getProductPrice().multiply(item.getCount())))); + .setTotalPrice(NumberUtils.mul(item.getProductPrice(), item.getCount())))); } private void updateStockInItemList(Long id, List newList) { @@ -122,21 +145,33 @@ public class ErpStockInServiceImpl implements ErpStockInService { @Override @Transactional(rollbackFor = Exception.class) - public void deleteStockIn(Long id) { - // 1. 校验存在 - validateStockInExists(id); - // TODO 芋艿:校验一下; + public void deleteStockIn(List ids) { + // 1. 校验不处于已审批 + List stockIns = stockInMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(stockIns)) { + return; + } + stockIns.forEach(stockIn -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(stockIn.getStatus())) { + throw exception(STOCK_IN_DELETE_FAIL_APPROVE, stockIn.getNo()); + } + }); - // 2.1 删除 - stockInMapper.deleteById(id); - // 2.2 删除子表 - stockInItemMapper.deleteByInId(id); + // 2. 遍历删除,并记录操作日志 + stockIns.forEach(stockIn -> { + // 2.1 删除入库单 + stockInMapper.deleteById(stockIn.getId()); + // 2.2 删除入库单项 + stockInItemMapper.deleteByInId(stockIn.getId()); + }); } - private void validateStockInExists(Long id) { - if (stockInMapper.selectById(id) == null) { + private ErpStockInDO validateStockInExists(Long id) { + ErpStockInDO stockIn = stockInMapper.selectById(id); + if (stockIn == null) { throw exception(STOCK_IN_NOT_EXISTS); } + return stockIn; } @Override @@ -149,7 +184,7 @@ public class ErpStockInServiceImpl implements ErpStockInService { return stockInMapper.selectPage(pageReqVO); } - // ==================== 子表(ERP 其它入库单项) ==================== + // ==================== 入库项 ==================== @Override public List getStockInItemListByInId(Long inId) {