From ab03c51450def522e41bbdf4d547834b45f8ad3c Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Fri, 12 Jun 2026 16:54:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9E=E7=9B=98=E7=82=B9?= =?UTF-8?q?=E5=8D=95=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/erp/enums/ErrorCodeConstants.java | 8 + .../stock/ErpStockCheckApproveActionEnum.java | 16 + .../admin/stock/ErpStockCheckController.java | 172 +++++-- .../ErpStockCheckApproveRecordRespVO.java | 45 ++ .../vo/check/ErpStockCheckAuditReqVO.java | 23 + .../ErpStockCheckGenerateByLocationReqVO.java | 26 ++ .../ErpStockCheckGenerateByProductReqVO.java | 17 + .../stock/vo/check/ErpStockCheckRespVO.java | 21 +- .../vo/check/ErpStockCheckSaveReqVO.java | 22 +- .../vo/check/ErpStockCheckSubmitReqVO.java | 22 + .../stock/ErpStockCheckApproveRecordDO.java | 39 ++ .../dal/dataobject/stock/ErpStockCheckDO.java | 6 +- .../dataobject/stock/ErpStockCheckItemDO.java | 12 +- .../ErpStockCheckApproveRecordMapper.java | 23 + .../erp/dal/mysql/stock/ErpStockMapper.java | 12 + .../service/stock/ErpStockCheckService.java | 18 +- .../stock/ErpStockCheckServiceImpl.java | 440 ++++++++++++++---- 17 files changed, 776 insertions(+), 146 deletions(-) create mode 100644 yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockCheckApproveActionEnum.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckApproveRecordRespVO.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckAuditReqVO.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckGenerateByLocationReqVO.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckGenerateByProductReqVO.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSubmitReqVO.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckApproveRecordDO.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckApproveRecordMapper.java 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 30268ff4f..33af6e76a 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 @@ -152,6 +152,14 @@ public interface ErrorCodeConstants { ErrorCode STOCK_CHECK_APPROVE_FAIL = new ErrorCode(1_030_403_003, "审核失败,只有未审核的盘点单才能审核"); ErrorCode STOCK_CHECK_NO_EXISTS = new ErrorCode(1_030_403_004, "生成盘点号失败,请重新提交"); ErrorCode STOCK_CHECK_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_403_005, "库存盘点单({})已审核,无法修改"); + ErrorCode STOCK_CHECK_DELETE_FAIL_PROCESS = new ErrorCode(1_030_403_006, "库存盘点单({})审核中,无法删除"); + ErrorCode STOCK_CHECK_UPDATE_FAIL_PROCESS = new ErrorCode(1_030_403_007, "库存盘点单({})审核中,无法修改"); + ErrorCode STOCK_CHECK_SUBMIT_FAIL_STATUS = new ErrorCode(1_030_403_008, "提交审核失败,只有待提交或已驳回的盘点单才能提交审核"); + ErrorCode STOCK_CHECK_SUBMIT_FAIL_USER = new ErrorCode(1_030_403_009, "提交审核失败,只有创建人才能提交审核"); + ErrorCode STOCK_CHECK_SUBMIT_FAIL_AUDIT_USER_EMPTY = new ErrorCode(1_030_403_010, "提交审核失败,请选择审核人"); + ErrorCode STOCK_CHECK_AUDIT_FAIL_STATUS = new ErrorCode(1_030_403_011, "审核失败,只有审核中的盘点单才能审核"); + ErrorCode STOCK_CHECK_AUDIT_FAIL_USER = new ErrorCode(1_030_403_012, "审核失败,当前用户不是指定审核人"); + ErrorCode STOCK_CHECK_AUDIT_FAIL_RESULT = new ErrorCode(1_030_403_013, "审核失败,审核结果只支持通过或驳回"); // ========== ERP 产品库存 1-030-404-000 ========== ErrorCode STOCK_COUNT_NEGATIVE = new ErrorCode(1_030_404_000, "操作失败,产品({})所在仓库({})的库存:{},小于变更数量:{}"); diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockCheckApproveActionEnum.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockCheckApproveActionEnum.java new file mode 100644 index 000000000..b9b3392b4 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockCheckApproveActionEnum.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.erp.enums.stock; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public enum ErpStockCheckApproveActionEnum { + + SUBMIT("SUBMIT"), + APPROVE("APPROVE"), + REJECT("REJECT"); + + private final String actionType; + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java index e2a45aec0..72370a12f 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java @@ -6,16 +6,28 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckApproveRecordRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckAuditReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckGenerateByLocationReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckGenerateByProductReqVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckRespVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSaveReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSubmitReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckApproveRecordDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.warehousearea.WarehouseAreaDO; import cn.iocoder.yudao.module.erp.service.product.ErpProductService; import cn.iocoder.yudao.module.erp.service.stock.ErpStockCheckService; +import cn.iocoder.yudao.module.erp.service.stock.ErpWarehouseService; +import cn.iocoder.yudao.module.erp.service.warehousearea.WarehouseAreaService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import io.swagger.v3.oas.annotations.Operation; @@ -23,21 +35,32 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -@Tag(name = "管理后台 - ERP 库存调拨单") +@Tag(name = "管理后台 - ERP 库存盘点单") @RestController @RequestMapping("/erp/stock-check") @Validated @@ -47,19 +70,22 @@ public class ErpStockCheckController { private ErpStockCheckService stockCheckService; @Resource private ErpProductService productService; - + @Resource + private ErpWarehouseService warehouseService; + @Resource + private WarehouseAreaService warehouseAreaService; @Resource private AdminUserApi adminUserApi; @PostMapping("/create") - @Operation(summary = "创建库存调拨单") + @Operation(summary = "创建库存盘点单") @PreAuthorize("@ss.hasPermission('erp:stock-check:create')") public CommonResult createStockCheck(@Valid @RequestBody ErpStockCheckSaveReqVO createReqVO) { return success(stockCheckService.createStockCheck(createReqVO)); } @PutMapping("/update") - @Operation(summary = "更新库存调拨单") + @Operation(summary = "更新库存盘点单") @PreAuthorize("@ss.hasPermission('erp:stock-check:update')") public CommonResult updateStockCheck(@Valid @RequestBody ErpStockCheckSaveReqVO updateReqVO) { stockCheckService.updateStockCheck(updateReqVO); @@ -67,16 +93,56 @@ public class ErpStockCheckController { } @PutMapping("/update-status") - @Operation(summary = "更新库存调拨单的状态") + @Operation(summary = "更新库存盘点单状态") @PreAuthorize("@ss.hasPermission('erp:stock-check:update-status')") + @RateLimiter(count = 1, timeUnit = TimeUnit.SECONDS) public CommonResult updateStockCheckStatus(@RequestParam("id") Long id, - @RequestParam("status") Integer status) { + @RequestParam("status") Integer status) { stockCheckService.updateStockCheckStatus(id, status); return success(true); } + @PutMapping("/submit") + @Operation(summary = "提交库存盘点单审核") + @PreAuthorize("@ss.hasPermission('erp:stock-check:update')") + @RateLimiter(count = 1, timeUnit = TimeUnit.SECONDS) + public CommonResult submitStockCheckAudit(@Valid @RequestBody ErpStockCheckSubmitReqVO submitReqVO) { + stockCheckService.submitStockCheckAudit(submitReqVO); + return success(true); + } + + @PutMapping("/audit") + @Operation(summary = "审核库存盘点单") + @PreAuthorize("@ss.hasPermission('erp:stock-check:update-status')") + @RateLimiter(count = 1, timeUnit = TimeUnit.SECONDS) + public CommonResult auditStockCheck(@Valid @RequestBody ErpStockCheckAuditReqVO auditReqVO) { + stockCheckService.auditStockCheck(auditReqVO); + return success(true); + } + + @GetMapping("/approve-record-list") + @Operation(summary = "获得库存盘点单审核记录") + @PreAuthorize("@ss.hasPermission('erp:stock-check:query')") + public CommonResult> getApproveRecordList(@RequestParam("id") Long id) { + return success(buildApproveRecordRespList(stockCheckService.getStockCheckApproveRecordList(id))); + } + + @PostMapping("/generate-items/by-location") + @Operation(summary = "按仓库/库区生成盘点项") + @PreAuthorize("@ss.hasPermission('erp:stock-check:query')") + public CommonResult> generateItemsByLocation(@Valid @RequestBody ErpStockCheckGenerateByLocationReqVO reqVO) { + return success(stockCheckService.generateStockCheckItemsByLocation(reqVO)); + } + + @PostMapping("/generate-items/by-product") + @Operation(summary = "按产品生成盘点项") + @PreAuthorize("@ss.hasPermission('erp:stock-check:query')") + public CommonResult> generateItemsByProduct(@Valid @RequestBody ErpStockCheckGenerateByProductReqVO reqVO) { + return success(stockCheckService.generateStockCheckItemsByProduct(reqVO)); + } + @DeleteMapping("/delete") - @Operation(summary = "删除库存调拨单") + @Operation(summary = "删除库存盘点单") @Parameter(name = "ids", description = "编号数组", required = true) @PreAuthorize("@ss.hasPermission('erp:stock-check:delete')") public CommonResult deleteStockCheck(@RequestParam("ids") List ids) { @@ -85,7 +151,7 @@ public class ErpStockCheckController { } @GetMapping("/get") - @Operation(summary = "获得库存调拨单") + @Operation(summary = "获得库存盘点单") @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('erp:stock-check:query')") public CommonResult getStockCheck(@RequestParam("id") Long id) { @@ -93,57 +159,89 @@ public class ErpStockCheckController { if (stockCheck == null) { return success(null); } - List stockCheckItemList = stockCheckService.getStockCheckItemListByCheckId(id); - Map productMap = productService.getProductVOMap( - convertSet(stockCheckItemList, ErpStockCheckItemDO::getProductId)); - return success(BeanUtils.toBean(stockCheck, ErpStockCheckRespVO.class, stockCheckVO -> - stockCheckVO.setItems(BeanUtils.toBean(stockCheckItemList, ErpStockCheckRespVO.Item.class, item -> - MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) - .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))))); + return success(buildStockCheckRespVO(stockCheck, + stockCheckService.getStockCheckItemListByCheckId(id), + stockCheckService.getStockCheckApproveRecordList(id))); } @GetMapping("/page") - @Operation(summary = "获得库存调拨单分页") + @Operation(summary = "获得库存盘点单分页") @PreAuthorize("@ss.hasPermission('erp:stock-check:query')") public CommonResult> getStockCheckPage(@Valid ErpStockCheckPageReqVO pageReqVO) { - PageResult pageResult = stockCheckService.getStockCheckPage(pageReqVO); - return success(buildStockCheckVOPageResult(pageResult)); + return success(buildStockCheckVOPageResult(stockCheckService.getStockCheckPage(pageReqVO))); } @GetMapping("/export-excel") - @Operation(summary = "导出库存调拨单 Excel") + @Operation(summary = "导出库存盘点单 Excel") @PreAuthorize("@ss.hasPermission('erp:stock-check:export')") @ApiAccessLog(operateType = EXPORT) public void exportStockCheckExcel(@Valid ErpStockCheckPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { + HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); List list = buildStockCheckVOPageResult(stockCheckService.getStockCheckPage(pageReqVO)).getList(); - // 导出 Excel - ExcelUtils.write(response, "库存调拨单.xls", "数据", ErpStockCheckRespVO.class, list); + ExcelUtils.write(response, "库存盘点单.xls", "数据", ErpStockCheckRespVO.class, list); } private PageResult buildStockCheckVOPageResult(PageResult pageResult) { if (CollUtil.isEmpty(pageResult.getList())) { return PageResult.empty(pageResult.getTotal()); } - // 1.1 盘点项 List stockCheckItemList = stockCheckService.getStockCheckItemListByCheckIds( convertSet(pageResult.getList(), ErpStockCheckDO::getId)); Map> stockCheckItemMap = convertMultiMap(stockCheckItemList, ErpStockCheckItemDO::getCheckId); - // 1.2 产品信息 - Map productMap = productService.getProductVOMap( - convertSet(stockCheckItemList, ErpStockCheckItemDO::getProductId)); - // 1.3 管理员信息 - Map userMap = adminUserApi.getUserMap( - convertSet(pageResult.getList(), stockCheck -> Long.parseLong(stockCheck.getCreator()))); - // 2. 开始拼接 return BeanUtils.toBean(pageResult, ErpStockCheckRespVO.class, stockCheck -> { - stockCheck.setItems(BeanUtils.toBean(stockCheckItemMap.get(stockCheck.getId()), ErpStockCheckRespVO.Item.class, - item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) - .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))); - stockCheck.setProductNames(CollUtil.join(stockCheck.getItems(), ",", ErpStockCheckRespVO.Item::getProductName)); - MapUtils.findAndThen(userMap, Long.parseLong(stockCheck.getCreator()), user -> stockCheck.setCreatorName(user.getNickname())); + stockCheck.setItems(buildItemRespList(stockCheckItemMap.get(stockCheck.getId()))); + stockCheck.setProductNames(CollUtil.join(stockCheck.getItems(), ",", ErpStockCheckRespVO.Item::getProductName)); + fillUserInfo(stockCheck); + }); + } + + private ErpStockCheckRespVO buildStockCheckRespVO(ErpStockCheckDO stockCheck, List stockCheckItemList, + List approveRecords) { + ErpStockCheckRespVO stockCheckVO = BeanUtils.toBean(stockCheck, ErpStockCheckRespVO.class); + stockCheckVO.setItems(buildItemRespList(stockCheckItemList)); + stockCheckVO.setProductNames(CollUtil.join(stockCheckVO.getItems(), ",", ErpStockCheckRespVO.Item::getProductName)); + stockCheckVO.setApproveRecords(buildApproveRecordRespList(approveRecords)); + fillUserInfo(stockCheckVO); + return stockCheckVO; + } + + private List buildItemRespList(List itemList) { + if (CollUtil.isEmpty(itemList)) { + return new ArrayList<>(); + } + Map productMap = productService.getProductVOMap(convertSet(itemList, ErpStockCheckItemDO::getProductId)); + Map warehouseMap = warehouseService.getWarehouseMap(convertSet(itemList, ErpStockCheckItemDO::getWarehouseId)); + Map areaMap = warehouseAreaService.getWarehouseAreaMap(convertSet(itemList, ErpStockCheckItemDO::getAreaId)); + return BeanUtils.toBean(itemList, ErpStockCheckRespVO.Item.class, item -> { + MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())); + MapUtils.findAndThen(warehouseMap, item.getWarehouseId(), warehouse -> item.setWarehouseName(warehouse.getName())); + if (item.getAreaName() == null) { + MapUtils.findAndThen(areaMap, item.getAreaId(), area -> item.setAreaName(area.getAreaName())); + } + }); + } + + private void fillUserInfo(ErpStockCheckRespVO stockCheck) { + List userIds = new ArrayList<>(); + userIds.add(NumberUtils.parseLong(stockCheck.getCreator())); + userIds.add(stockCheck.getAuditUserId()); + Map userMap = adminUserApi.getUserMap(userIds); + MapUtils.findAndThen(userMap, NumberUtils.parseLong(stockCheck.getCreator()), user -> stockCheck.setCreatorName(user.getNickname())); + MapUtils.findAndThen(userMap, stockCheck.getAuditUserId(), user -> stockCheck.setAuditUserName(user.getNickname())); + } + + private List buildApproveRecordRespList(List records) { + if (CollUtil.isEmpty(records)) { + return new ArrayList<>(); + } + Map userMap = adminUserApi.getUserMap(convertListByFlatMap(records, + record -> Stream.of(NumberUtils.parseLong(record.getCreator()), record.getTargetUserId()))); + return BeanUtils.toBean(records, ErpStockCheckApproveRecordRespVO.class, record -> { + MapUtils.findAndThen(userMap, NumberUtils.parseLong(record.getCreator()), user -> record.setCreatorName(user.getNickname())); + MapUtils.findAndThen(userMap, record.getTargetUserId(), user -> record.setTargetUserName(user.getNickname())); }); } -} \ No newline at end of file +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckApproveRecordRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckApproveRecordRespVO.java new file mode 100644 index 000000000..8ccd8379e --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckApproveRecordRespVO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - ERP 库存盘点单审核记录 Response VO") +@Data +public class ErpStockCheckApproveRecordRespVO { + + @Schema(description = "编号", example = "1") + private Long id; + + @Schema(description = "盘点单编号", example = "1") + private Long stockCheckId; + + @Schema(description = "操作类型", example = "SUBMIT") + private String actionType; + + @Schema(description = "变更前状态", example = "0") + private Integer fromStatus; + + @Schema(description = "变更后状态", example = "10") + private Integer toStatus; + + @Schema(description = "目标审核人编号", example = "1") + private Long targetUserId; + + @Schema(description = "目标审核人名称", example = "李四") + private String targetUserName; + + @Schema(description = "备注", example = "请审核") + private String remark; + + @Schema(description = "操作人", example = "1") + private String creator; + + @Schema(description = "操作人名称", example = "张三") + private String creatorName; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckAuditReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckAuditReqVO.java new file mode 100644 index 000000000..3c225f94d --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckAuditReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - ERP 库存盘点单审核 Request VO") +@Data +public class ErpStockCheckAuditReqVO { + + @Schema(description = "盘点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @NotNull(message = "盘点编号不能为空") + private Long id; + + @Schema(description = "审核结果 20-通过 1-驳回", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") + @NotNull(message = "审核结果不能为空") + private Integer status; + + @Schema(description = "审核备注", example = "审核通过") + private String remark; + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckGenerateByLocationReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckGenerateByLocationReqVO.java new file mode 100644 index 000000000..f7ff71bd0 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckGenerateByLocationReqVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import java.util.List; + +@Schema(description = "管理后台 - ERP 库存盘点按仓库/库区生成盘点项 Request VO") +@Data +public class ErpStockCheckGenerateByLocationReqVO { + + @Schema(description = "仓库编号列表", example = "[1,2]") + private List warehouseIds; + + @Schema(description = "库区编号列表", example = "[11,12]") + private List areaIds; + + @Schema(description = "是否允许为空,默认 false", example = "false") + private Boolean allowEmpty; + + public boolean validSelection() { + return (warehouseIds != null && !warehouseIds.isEmpty()) || (areaIds != null && !areaIds.isEmpty()); + } + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckGenerateByProductReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckGenerateByProductReqVO.java new file mode 100644 index 000000000..76564c842 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckGenerateByProductReqVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import java.util.List; + +@Schema(description = "管理后台 - ERP 库存盘点按产品生成盘点项 Request VO") +@Data +public class ErpStockCheckGenerateByProductReqVO { + + @Schema(description = "产品编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]") + @NotEmpty(message = "产品编号列表不能为空") + private List productIds; + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java index 88b75bb9c..ee93dfa40 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java @@ -44,6 +44,12 @@ public class ErpStockCheckRespVO { @DictFormat(AUDIT_STATUS) private Integer status; + @Schema(description = "审核人编号", example = "1888") + private Long auditUserId; + + @Schema(description = "审核人名称", example = "李四") + private String auditUserName; + @Schema(description = "备注", example = "随便") @ExcelProperty("备注") private String remark; @@ -67,6 +73,9 @@ public class ErpStockCheckRespVO { @ExcelProperty("产品信息") private String productNames; + @Schema(description = "审核记录") + private List approveRecords; + @Data public static class Item { @@ -76,6 +85,15 @@ public class ErpStockCheckRespVO { @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") private Long warehouseId; + @Schema(description = "仓库名称", example = "成品仓") + private String warehouseName; + + @Schema(description = "库区编号", example = "1") + private Long areaId; + + @Schema(description = "库区名称", example = "A区") + private String areaName; + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") private Long productId; @@ -91,7 +109,6 @@ public class ErpStockCheckRespVO { private BigDecimal actualCount; @Schema(description = "盈亏数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") - @NotNull(message = "盈亏数量不能为空") private BigDecimal count; @Schema(description = "备注", example = "随便") @@ -108,4 +125,4 @@ public class ErpStockCheckRespVO { } -} \ No newline at end of file +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSaveReqVO.java index 80e126a28..cf04eca64 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSaveReqVO.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSaveReqVO.java @@ -10,15 +10,15 @@ import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; -@Schema(description = "管理后台 - ERP 其它出库单新增/修改 Request VO") +@Schema(description = "管理后台 - ERP 库存盘点单新增/修改 Request VO") @Data public class ErpStockCheckSaveReqVO { - @Schema(description = "出库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @Schema(description = "盘点编号", example = "11756") private Long id; - @Schema(description = "出库时间", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "出库时间不能为空") + @Schema(description = "盘点时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "盘点时间不能为空") private LocalDateTime checkTime; @Schema(description = "备注", example = "随便") @@ -27,21 +27,24 @@ public class ErpStockCheckSaveReqVO { @Schema(description = "附件 URL", example = "https://www.iocoder.cn/1.doc") private String fileUrl; - @Schema(description = "出库项列表", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "出库项列表不能为空") + @Schema(description = "盘点项列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "盘点项列表不能为空") @Valid private List items; @Data public static class Item { - @Schema(description = "出库项编号", example = "11756") + @Schema(description = "盘点项编号", example = "11756") private Long id; @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") @NotNull(message = "仓库编号不能为空") private Long warehouseId; + @Schema(description = "库区编号", example = "1") + private Long areaId; + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") @NotNull(message = "产品编号不能为空") private Long productId; @@ -57,8 +60,7 @@ public class ErpStockCheckSaveReqVO { @NotNull(message = "实际数量不能为空") private BigDecimal actualCount; - @Schema(description = "盈亏数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") - @NotNull(message = "盈亏数量不能为空") + @Schema(description = "盈亏数量,服务端自动按 实际数量 - 账面数量 计算", example = "100.00") private BigDecimal count; @Schema(description = "备注", example = "随便") @@ -66,4 +68,4 @@ public class ErpStockCheckSaveReqVO { } -} \ No newline at end of file +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSubmitReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSubmitReqVO.java new file mode 100644 index 000000000..d60c9529c --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSubmitReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - ERP 库存盘点单提交审核 Request VO") +@Data +public class ErpStockCheckSubmitReqVO { + + @Schema(description = "盘点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @NotNull(message = "盘点编号不能为空") + private Long id; + + @Schema(description = "审核人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1888") + private Long auditUserId; + + @Schema(description = "提交备注", example = "请审核") + private String remark; + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckApproveRecordDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckApproveRecordDO.java new file mode 100644 index 000000000..0eaca6e4d --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckApproveRecordDO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.stock; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@TableName("erp_stock_check_approve_record") +@KeySequence("erp_stock_check_approve_record_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockCheckApproveRecordDO extends BaseDO { + + @TableId + private Long id; + + private Long stockCheckId; + + private String actionType; + + private Integer fromStatus; + + private Integer toStatus; + + private Long targetUserId; + + private String remark; + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckDO.java index e9168275f..54130f443 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckDO.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckDO.java @@ -51,6 +51,10 @@ public class ErpStockCheckDO extends BaseDO { * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} */ private Integer status; + /** + * 审核人编号 + */ + private Long auditUserId; /** * 备注 */ @@ -60,4 +64,4 @@ public class ErpStockCheckDO extends BaseDO { */ private String fileUrl; -} \ No newline at end of file +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckItemDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckItemDO.java index c3c4dbf99..ea31c20f2 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckItemDO.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckItemDO.java @@ -41,6 +41,14 @@ public class ErpStockCheckItemDO extends BaseDO { * 关联 {@link ErpWarehouseDO#getId()} */ private Long warehouseId; + /** + * 库区编号 + */ + private Long areaId; + /** + * 库区名称 + */ + private String areaName; /** * 产品编号 * @@ -68,7 +76,7 @@ public class ErpStockCheckItemDO extends BaseDO { /** * 盈亏数量 * - * count = stockCount - actualCount + * count = actualCount - stockCount */ private BigDecimal count; /** @@ -80,4 +88,4 @@ public class ErpStockCheckItemDO extends BaseDO { */ private String remark; -} \ No newline at end of file +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckApproveRecordMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckApproveRecordMapper.java new file mode 100644 index 000000000..778c5af5b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckApproveRecordMapper.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckApproveRecordDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface ErpStockCheckApproveRecordMapper extends BaseMapperX { + + default List selectListByStockCheckId(Long stockCheckId) { + return selectList(new LambdaQueryWrapperX() + .eq(ErpStockCheckApproveRecordDO::getStockCheckId, stockCheckId) + .orderByAsc(ErpStockCheckApproveRecordDO::getId)); + } + + default int deleteByStockCheckId(Long stockCheckId) { + return delete(ErpStockCheckApproveRecordDO::getStockCheckId, stockCheckId); + } + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMapper.java index 9305640ac..9f99c25f1 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMapper.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMapper.java @@ -75,6 +75,18 @@ public interface ErpStockMapper extends BaseMapperX { .eq("product_id", productId)); } + default List selectListByWarehouseIdsAndAreaIds(List warehouseIds, List areaIds) { + LambdaQueryWrapperX query = new LambdaQueryWrapperX() + .in(CollUtil.isNotEmpty(warehouseIds), ErpStockDO::getWarehouseId, warehouseIds) + .in(CollUtil.isNotEmpty(areaIds), ErpStockDO::getAreaId, areaIds); + return selectList(query); + } + + default List selectListByProductIds(List productIds) { + return selectList(new LambdaQueryWrapperX() + .in(CollUtil.isNotEmpty(productIds), ErpStockDO::getProductId, productIds)); + } + default int updateCountIncrement(Long id, BigDecimal count, boolean negativeEnable) { LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper() .eq(ErpStockDO::getId, id); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckService.java index 02a192165..952ea5280 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckService.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckService.java @@ -1,8 +1,14 @@ 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.check.ErpStockCheckAuditReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckGenerateByLocationReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckGenerateByProductReqVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckRespVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSaveReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSubmitReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckApproveRecordDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; @@ -40,6 +46,10 @@ public interface ErpStockCheckService { */ void updateStockCheckStatus(Long id, Integer status); + void submitStockCheckAudit(@Valid ErpStockCheckSubmitReqVO submitReqVO); + + void auditStockCheck(@Valid ErpStockCheckAuditReqVO auditReqVO); + /** * 删除库存盘点单 * @@ -81,4 +91,10 @@ public interface ErpStockCheckService { */ List getStockCheckItemListByCheckIds(Collection checkIds); -} \ No newline at end of file + List getStockCheckApproveRecordList(Long stockCheckId); + + List generateStockCheckItemsByLocation(@Valid ErpStockCheckGenerateByLocationReqVO reqVO); + + List generateStockCheckItemsByProduct(@Valid ErpStockCheckGenerateByProductReqVO reqVO); + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.java index 3739bf5cb..53badaa8d 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.java @@ -2,42 +2,76 @@ 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.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckAuditReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckGenerateByLocationReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckGenerateByProductReqVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckRespVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSaveReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSubmitReqVO; import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckApproveRecordDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.warehousearea.WarehouseAreaDO; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockCheckApproveRecordMapper; import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockCheckItemMapper; import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockCheckMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMapper; 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.ErpStockCheckApproveActionEnum; 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.stock.bo.ErpStockRecordCreateReqBO; +import cn.iocoder.yudao.module.erp.service.warehousearea.WarehouseAreaService; +import cn.iocoder.yudao.module.system.api.permission.PermissionApi; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; 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.*; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_APPROVE_FAIL; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_AUDIT_FAIL_RESULT; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_AUDIT_FAIL_STATUS; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_AUDIT_FAIL_USER; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_DELETE_FAIL_APPROVE; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_DELETE_FAIL_PROCESS; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_NOT_EXISTS; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_NO_EXISTS; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_PROCESS_FAIL; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_SUBMIT_FAIL_AUDIT_USER_EMPTY; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_SUBMIT_FAIL_STATUS; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_SUBMIT_FAIL_USER; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_UPDATE_FAIL_APPROVE; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_UPDATE_FAIL_PROCESS; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.WAREHOUSE_AREA_NOT_EXISTS; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.WAREHOUSE_LOCATION_WAREHOUSE_AREA_NOT_MATCH; -// TODO 芋艿:记录操作日志 - -/** - * ERP 库存盘点单 Service 实现类 - * - * @author 芋道源码 - */ @Service @Validated public class ErpStockCheckServiceImpl implements ErpStockCheckService { @@ -46,36 +80,41 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService { private ErpStockCheckMapper stockCheckMapper; @Resource private ErpStockCheckItemMapper stockCheckItemMapper; - + @Resource + private ErpStockCheckApproveRecordMapper stockCheckApproveRecordMapper; + @Resource + private ErpStockMapper stockMapper; @Resource private ErpNoRedisDAO noRedisDAO; - @Resource private ErpProductService productService; @Resource private ErpWarehouseService warehouseService; @Resource + private WarehouseAreaService warehouseAreaService; + @Resource private ErpStockRecordService stockRecordService; + @Resource + private AdminUserApi adminUserApi; + @Resource + private PermissionApi permissionApi; @Override @Transactional(rollbackFor = Exception.class) public Long createStockCheck(ErpStockCheckSaveReqVO createReqVO) { - // 1.1 校验盘点项的有效性 List stockCheckItems = validateStockCheckItems(createReqVO.getItems()); - // 1.2 生成盘点单号,并校验唯一性 String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_CHECK_NO_PREFIX); if (stockCheckMapper.selectByNo(no) != null) { throw exception(STOCK_CHECK_NO_EXISTS); } - // 2.1 插入盘点单 ErpStockCheckDO stockCheck = BeanUtils.toBean(createReqVO, ErpStockCheckDO.class, in -> in - .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()) - .setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add)) + .setNo(no) + .setStatus(ErpAuditStatus.DRAFT.getStatus()) + .setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add, BigDecimal.ZERO)) .setTotalPrice(getSumValue(stockCheckItems, ErpStockCheckItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO))); stockCheckMapper.insert(stockCheck); - // 2.2 插入盘点单项 - stockCheckItems.forEach(o -> o.setCheckId(stockCheck.getId())); + stockCheckItems.forEach(item -> item.setCheckId(stockCheck.getId())); stockCheckItemMapper.insertBatch(stockCheckItems); return stockCheck.getId(); } @@ -83,101 +122,119 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService { @Override @Transactional(rollbackFor = Exception.class) public void updateStockCheck(ErpStockCheckSaveReqVO updateReqVO) { - // 1.1 校验存在 ErpStockCheckDO stockCheck = validateStockCheckExists(updateReqVO.getId()); if (ErpAuditStatus.APPROVE.getStatus().equals(stockCheck.getStatus())) { throw exception(STOCK_CHECK_UPDATE_FAIL_APPROVE, stockCheck.getNo()); } - // 1.2 校验盘点项的有效性 - List stockCheckItems = validateStockCheckItems(updateReqVO.getItems()); + if (ErpAuditStatus.PROCESS.getStatus().equals(stockCheck.getStatus())) { + throw exception(STOCK_CHECK_UPDATE_FAIL_PROCESS, stockCheck.getNo()); + } - // 2.1 更新盘点单 + List stockCheckItems = validateStockCheckItems(updateReqVO.getItems()); ErpStockCheckDO updateObj = BeanUtils.toBean(updateReqVO, ErpStockCheckDO.class, in -> in - .setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add)) - .setTotalPrice(getSumValue(stockCheckItems, ErpStockCheckItemDO::getTotalPrice, BigDecimal::add))); + .setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add, BigDecimal.ZERO)) + .setTotalPrice(getSumValue(stockCheckItems, ErpStockCheckItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO))); stockCheckMapper.updateById(updateObj); - // 2.2 更新盘点单项 updateStockCheckItemList(updateReqVO.getId(), stockCheckItems); } @Override @Transactional(rollbackFor = Exception.class) public void updateStockCheckStatus(Long id, Integer status) { - boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); - // 1.1 校验存在 ErpStockCheckDO stockCheck = validateStockCheckExists(id); - // 1.2 校验状态 - if (stockCheck.getStatus().equals(status)) { - throw exception(approve ? STOCK_CHECK_APPROVE_FAIL : STOCK_CHECK_PROCESS_FAIL); + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); + if (approve) { + if (!ErpAuditStatus.PROCESS.getStatus().equals(stockCheck.getStatus())) { + throw exception(STOCK_CHECK_APPROVE_FAIL); + } + } else { + if (!ErpAuditStatus.APPROVE.getStatus().equals(stockCheck.getStatus())) { + throw exception(STOCK_CHECK_PROCESS_FAIL); + } } - // 2. 更新状态 - int updateCount = stockCheckMapper.updateByIdAndStatus(id, stockCheck.getStatus(), - new ErpStockCheckDO().setStatus(status)); + int updateCount = stockCheckMapper.updateByIdAndStatus(id, stockCheck.getStatus(), new ErpStockCheckDO().setStatus(status)); if (updateCount == 0) { throw exception(approve ? STOCK_CHECK_APPROVE_FAIL : STOCK_CHECK_PROCESS_FAIL); } - // 3. 变更库存 List stockCheckItems = stockCheckItemMapper.selectListByCheckId(id); - stockCheckItems.forEach(stockCheckItem -> { - // 没有盈亏,不用出入库 - if (stockCheckItem.getCount().compareTo(BigDecimal.ZERO) == 0) { - return; - } - // 10;12;-2() - BigDecimal count = approve ? stockCheckItem.getCount(): stockCheckItem.getCount().negate(); - Integer bizType; - if (approve) { - bizType = count.compareTo(BigDecimal.ZERO) > 0 ? ErpStockRecordBizTypeEnum.CHECK_MORE_IN.getType() - : ErpStockRecordBizTypeEnum.CHECK_LESS_OUT.getType(); - } else { - bizType = count.compareTo(BigDecimal.ZERO) > 0 ? ErpStockRecordBizTypeEnum.CHECK_MORE_IN_CANCEL.getType() - : ErpStockRecordBizTypeEnum.CHECK_LESS_OUT_CANCEL.getType(); - } - ErpProductDO productDO = productService.getProduct(stockCheckItem.getProductId()); - stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO( - stockCheckItem.getProductId(),productDO.getCategoryId(), stockCheckItem.getWarehouseId(), count, - bizType, stockCheckItem.getCheckId(), stockCheckItem.getId(), stockCheck.getNo(),stockCheck.getCheckTime())); - }); + if (approve) { + applyStockCheckEffect(stockCheck, stockCheckItems); + } else { + cancelStockCheckEffect(stockCheck, stockCheckItems); + } } - private List validateStockCheckItems(List list) { - // 1.1 校验产品存在 - List productList = productService.validProductList( - convertSet(list, ErpStockCheckSaveReqVO.Item::getProductId)); - Map productMap = convertMap(productList, ErpProductDO::getId); - // 1.2 校验仓库存在 - warehouseService.validWarehouseList(convertSet(list, ErpStockCheckSaveReqVO.Item::getWarehouseId)); - // 2. 转化为 ErpStockCheckItemDO 列表 - return convertList(list, o -> BeanUtils.toBean(o, ErpStockCheckItemDO.class, item -> item - .setProductUnitId(productMap.get(item.getProductId()).getUnitId()) - .setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())))); + @Override + @Transactional(rollbackFor = Exception.class) + public void submitStockCheckAudit(ErpStockCheckSubmitReqVO submitReqVO) { + ErpStockCheckDO stockCheck = validateStockCheckExists(submitReqVO.getId()); + Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); + if (!Objects.equals(NumberUtils.parseLong(stockCheck.getCreator()), loginUserId)) { + throw exception(STOCK_CHECK_SUBMIT_FAIL_USER); + } + if (!ErpAuditStatus.DRAFT.getStatus().equals(stockCheck.getStatus()) + && !ErpAuditStatus.UN_APPROVE.getStatus().equals(stockCheck.getStatus())) { + throw exception(STOCK_CHECK_SUBMIT_FAIL_STATUS); + } + + Long auditUserId = submitReqVO.getAuditUserId() != null ? submitReqVO.getAuditUserId() : stockCheck.getAuditUserId(); + if (auditUserId == null) { + throw exception(STOCK_CHECK_SUBMIT_FAIL_AUDIT_USER_EMPTY); + } + adminUserApi.validateUser(auditUserId); + + Integer fromStatus = stockCheck.getStatus(); + int updateCount = stockCheckMapper.updateByIdAndStatus(stockCheck.getId(), fromStatus, + new ErpStockCheckDO().setAuditUserId(auditUserId).setStatus(ErpAuditStatus.PROCESS.getStatus())); + if (updateCount == 0) { + throw exception(STOCK_CHECK_SUBMIT_FAIL_STATUS); + } + + createApproveRecord(stockCheck.getId(), ErpStockCheckApproveActionEnum.SUBMIT, + fromStatus, ErpAuditStatus.PROCESS.getStatus(), auditUserId, submitReqVO.getRemark()); } - private void updateStockCheckItemList(Long id, List newList) { - // 第一步,对比新老数据,获得添加、修改、删除的列表 - List oldList = stockCheckItemMapper.selectListByCheckId(id); - List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 - (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + @Override + @Transactional(rollbackFor = Exception.class) + public void auditStockCheck(ErpStockCheckAuditReqVO auditReqVO) { + ErpStockCheckDO stockCheck = validateStockCheckExists(auditReqVO.getId()); + if (!ErpAuditStatus.PROCESS.getStatus().equals(stockCheck.getStatus())) { + throw exception(STOCK_CHECK_AUDIT_FAIL_STATUS); + } - // 第二步,批量添加、修改、删除 - if (CollUtil.isNotEmpty(diffList.get(0))) { - diffList.get(0).forEach(o -> o.setCheckId(id)); - stockCheckItemMapper.insertBatch(diffList.get(0)); + Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); + boolean adminCanAudit = permissionApi.hasAnyRoles(loginUserId, RoleCodeEnum.SUPER_ADMIN.getCode()); + if (!Objects.equals(stockCheck.getAuditUserId(), loginUserId) && !adminCanAudit) { + throw exception(STOCK_CHECK_AUDIT_FAIL_USER); } - if (CollUtil.isNotEmpty(diffList.get(1))) { - stockCheckItemMapper.updateBatch(diffList.get(1)); + if (!ErpAuditStatus.APPROVE.getStatus().equals(auditReqVO.getStatus()) + && !ErpAuditStatus.UN_APPROVE.getStatus().equals(auditReqVO.getStatus())) { + throw exception(STOCK_CHECK_AUDIT_FAIL_RESULT); } - if (CollUtil.isNotEmpty(diffList.get(2))) { - stockCheckItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpStockCheckItemDO::getId)); + + Integer fromStatus = stockCheck.getStatus(); + int updateCount = stockCheckMapper.updateByIdAndStatus(stockCheck.getId(), fromStatus, + new ErpStockCheckDO().setStatus(auditReqVO.getStatus())); + if (updateCount == 0) { + throw exception(STOCK_CHECK_AUDIT_FAIL_STATUS); + } + + List stockCheckItems = stockCheckItemMapper.selectListByCheckId(stockCheck.getId()); + if (ErpAuditStatus.APPROVE.getStatus().equals(auditReqVO.getStatus())) { + applyStockCheckEffect(stockCheck, stockCheckItems); } + + Long targetUserId = NumberUtils.parseLong(stockCheck.getCreator()); + createApproveRecord(stockCheck.getId(), ErpAuditStatus.APPROVE.getStatus().equals(auditReqVO.getStatus()) + ? ErpStockCheckApproveActionEnum.APPROVE : ErpStockCheckApproveActionEnum.REJECT, + fromStatus, auditReqVO.getStatus(), targetUserId, auditReqVO.getRemark()); } @Override @Transactional(rollbackFor = Exception.class) public void deleteStockCheck(List ids) { - // 1. 校验不处于已审批 List stockChecks = stockCheckMapper.selectBatchIds(ids); if (CollUtil.isEmpty(stockChecks)) { return; @@ -186,25 +243,18 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService { if (ErpAuditStatus.APPROVE.getStatus().equals(stockCheck.getStatus())) { throw exception(STOCK_CHECK_DELETE_FAIL_APPROVE, stockCheck.getNo()); } + if (ErpAuditStatus.PROCESS.getStatus().equals(stockCheck.getStatus())) { + throw exception(STOCK_CHECK_DELETE_FAIL_PROCESS, stockCheck.getNo()); + } }); - // 2. 遍历删除,并记录操作日志 stockChecks.forEach(stockCheck -> { - // 2.1 删除盘点单 stockCheckMapper.deleteById(stockCheck.getId()); - // 2.2 删除盘点单项 stockCheckItemMapper.deleteByCheckId(stockCheck.getId()); + stockCheckApproveRecordMapper.deleteByStockCheckId(stockCheck.getId()); }); } - private ErpStockCheckDO validateStockCheckExists(Long id) { - ErpStockCheckDO stockCheck = stockCheckMapper.selectById(id); - if (stockCheck == null) { - throw exception(STOCK_CHECK_NOT_EXISTS); - } - return stockCheck; - } - @Override public ErpStockCheckDO getStockCheck(Long id) { return stockCheckMapper.selectById(id); @@ -215,8 +265,6 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService { return stockCheckMapper.selectPage(pageReqVO); } - // ==================== 盘点项 ==================== - @Override public List getStockCheckItemListByCheckId(Long checkId) { return stockCheckItemMapper.selectListByCheckId(checkId); @@ -230,4 +278,210 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService { return stockCheckItemMapper.selectListByCheckIds(checkIds); } -} \ No newline at end of file + @Override + public List getStockCheckApproveRecordList(Long stockCheckId) { + return stockCheckApproveRecordMapper.selectListByStockCheckId(stockCheckId); + } + + @Override + public List generateStockCheckItemsByLocation(ErpStockCheckGenerateByLocationReqVO reqVO) { + if (reqVO == null || !reqVO.validSelection()) { + return Collections.emptyList(); + } + List stockList = stockMapper.selectListByWarehouseIdsAndAreaIds(reqVO.getWarehouseIds(), reqVO.getAreaIds()); + if (!Boolean.TRUE.equals(reqVO.getAllowEmpty())) { + stockList = filterNonZeroStocks(stockList); + } + return buildPreviewItems(stockList); + } + + @Override + public List generateStockCheckItemsByProduct(ErpStockCheckGenerateByProductReqVO reqVO) { + if (reqVO == null || CollUtil.isEmpty(reqVO.getProductIds())) { + return Collections.emptyList(); + } + return buildPreviewItems(filterNonZeroStocks(stockMapper.selectListByProductIds(reqVO.getProductIds()))); + } + + private List filterNonZeroStocks(List stockList) { + if (CollUtil.isEmpty(stockList)) { + return Collections.emptyList(); + } + List result = new ArrayList<>(); + for (ErpStockDO stock : stockList) { + if (stock != null && stock.getCount() != null && stock.getCount().compareTo(BigDecimal.ZERO) != 0) { + result.add(stock); + } + } + return result; + } + + private List buildPreviewItems(List stockList) { + if (CollUtil.isEmpty(stockList)) { + return Collections.emptyList(); + } + Map productMap = productService.getProductMap(convertSet(stockList, ErpStockDO::getProductId)); + Map warehouseMap = warehouseService.getWarehouseMap(convertSet(stockList, ErpStockDO::getWarehouseId)); + Map areaMap = warehouseAreaService.getWarehouseAreaMap(convertSet(stockList, ErpStockDO::getAreaId)); + List result = new ArrayList<>(); + for (ErpStockDO stock : stockList) { + ErpStockCheckRespVO.Item item = new ErpStockCheckRespVO.Item(); + item.setWarehouseId(stock.getWarehouseId()); + item.setAreaId(stock.getAreaId()); + item.setAreaName(stock.getAreaName()); + item.setProductId(stock.getProductId()); + item.setStockCount(stock.getCount() != null ? stock.getCount() : BigDecimal.ZERO); + item.setActualCount(stock.getCount() != null ? stock.getCount() : BigDecimal.ZERO); + item.setCount(BigDecimal.ZERO); + MapUtils.findAndThen(warehouseMap, stock.getWarehouseId(), warehouse -> item.setWarehouseName(warehouse.getName())); + if (item.getAreaName() == null) { + MapUtils.findAndThen(areaMap, stock.getAreaId(), area -> item.setAreaName(area.getAreaName())); + } + ErpProductDO productDO = productMap.get(stock.getProductId()); + item.setProductPrice(resolveProductPrice(productDO)); + if (productDO != null) { + item.setProductName(productDO.getName()); + item.setProductBarCode(productDO.getBarCode()); + ErpProductRespVO productVO = productService.getProduct(productDO.getId()); + if (productVO != null) { + item.setProductUnitName(productVO.getUnitName()); + } + } + result.add(item); + } + return result; + } + + private BigDecimal resolveProductPrice(ErpProductDO productDO) { + if (productDO == null) { + return BigDecimal.ZERO; + } + if (productDO.getPurchasePrice() != null) { + return productDO.getPurchasePrice(); + } + if (productDO.getSalePrice() != null) { + return productDO.getSalePrice(); + } + return BigDecimal.ZERO; + } + + private void applyStockCheckEffect(ErpStockCheckDO stockCheck, List stockCheckItems) { + operateStockCheckEffect(stockCheck, stockCheckItems, true); + } + + private void cancelStockCheckEffect(ErpStockCheckDO stockCheck, List stockCheckItems) { + operateStockCheckEffect(stockCheck, stockCheckItems, false); + } + + private void operateStockCheckEffect(ErpStockCheckDO stockCheck, List stockCheckItems, boolean approve) { + stockCheckItems.forEach(stockCheckItem -> { + if (stockCheckItem.getCount() == null || stockCheckItem.getCount().compareTo(BigDecimal.ZERO) == 0) { + return; + } + BigDecimal count = approve ? stockCheckItem.getCount() : stockCheckItem.getCount().negate(); + Integer bizType; + if (approve) { + bizType = stockCheckItem.getCount().compareTo(BigDecimal.ZERO) > 0 + ? ErpStockRecordBizTypeEnum.CHECK_MORE_IN.getType() + : ErpStockRecordBizTypeEnum.CHECK_LESS_OUT.getType(); + } else { + bizType = stockCheckItem.getCount().compareTo(BigDecimal.ZERO) > 0 + ? ErpStockRecordBizTypeEnum.CHECK_MORE_IN_CANCEL.getType() + : ErpStockRecordBizTypeEnum.CHECK_LESS_OUT_CANCEL.getType(); + } + ErpProductDO productDO = productService.validProductList(Collections.singleton(stockCheckItem.getProductId())) + .stream().findFirst().orElse(null); + if (productDO == null) { + return; + } + stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO( + stockCheckItem.getProductId(), productDO.getCategoryId(), null, + stockCheckItem.getWarehouseId(), stockCheckItem.getAreaId(), stockCheckItem.getAreaName(), + count, bizType, stockCheckItem.getCheckId(), stockCheckItem.getId(), stockCheck.getNo(), stockCheck.getCheckTime())); + }); + } + + private List validateStockCheckItems(List list) { + List productList = productService.validProductList(convertSet(list, ErpStockCheckSaveReqVO.Item::getProductId)); + Map productMap = convertMap(productList, ErpProductDO::getId); + Map warehouseMap = convertMap( + warehouseService.validWarehouseList(convertSet(list, ErpStockCheckSaveReqVO.Item::getWarehouseId)), + ErpWarehouseDO::getId); + Map areaMap = warehouseAreaService.getWarehouseAreaMap(convertSet(list, ErpStockCheckSaveReqVO.Item::getAreaId)); + + list.forEach(item -> { + if (item.getAreaId() == null) { + return; + } + WarehouseAreaDO area = areaMap.get(item.getAreaId()); + if (area == null) { + throw exception(WAREHOUSE_AREA_NOT_EXISTS); + } + if (!warehouseMap.containsKey(item.getWarehouseId()) || !Objects.equals(item.getWarehouseId(), area.getWarehouseId())) { + throw exception(WAREHOUSE_LOCATION_WAREHOUSE_AREA_NOT_MATCH); + } + }); + + return convertList(list, item -> { + ErpProductDO productDO = productMap.get(item.getProductId()); + WarehouseAreaDO area = item.getAreaId() != null ? areaMap.get(item.getAreaId()) : null; + BigDecimal stockCount = item.getStockCount() != null ? item.getStockCount() : BigDecimal.ZERO; + BigDecimal actualCount = item.getActualCount() != null ? item.getActualCount() : BigDecimal.ZERO; + BigDecimal count = actualCount.subtract(stockCount); + BigDecimal productPrice = item.getProductPrice() != null ? item.getProductPrice() : resolveProductPrice(productDO); + return ErpStockCheckItemDO.builder() + .id(item.getId()) + .warehouseId(item.getWarehouseId()) + .areaId(item.getAreaId()) + .areaName(area != null ? area.getAreaName() : null) + .productId(item.getProductId()) + .productUnitId(productDO != null ? productDO.getUnitId() : null) + .productPrice(productPrice) + .stockCount(stockCount) + .actualCount(actualCount) + .count(count) + .totalPrice(MoneyUtils.priceMultiply(productPrice, count)) + .remark(item.getRemark()) + .build(); + }); + } + + private void updateStockCheckItemList(Long id, List newList) { + List oldList = stockCheckItemMapper.selectListByCheckId(id); + List> diffList = diffList(oldList, newList, + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(item -> item.setCheckId(id)); + stockCheckItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + diffList.get(1).forEach(item -> item.setCheckId(id)); + stockCheckItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + stockCheckItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpStockCheckItemDO::getId)); + } + } + + private void createApproveRecord(Long stockCheckId, ErpStockCheckApproveActionEnum action, Integer fromStatus, + Integer toStatus, Long targetUserId, String remark) { + stockCheckApproveRecordMapper.insert(ErpStockCheckApproveRecordDO.builder() + .stockCheckId(stockCheckId) + .actionType(action.getActionType()) + .fromStatus(fromStatus) + .toStatus(toStatus) + .targetUserId(targetUserId) + .remark(remark) + .build()); + } + + private ErpStockCheckDO validateStockCheckExists(Long id) { + ErpStockCheckDO stockCheck = stockCheckMapper.selectById(id); + if (stockCheck == null) { + throw exception(STOCK_CHECK_NOT_EXISTS); + } + return stockCheck; + } + +}