From 94247addc6a572698ab5e448086143a327dd5a4d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 8 Feb 2024 06:59:19 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20ERP=EF=BC=9A=E5=A2=9E=E5=8A=A0=20ER?= =?UTF-8?q?P=20=E7=9B=98=E7=82=B9=E5=8D=95=E7=9A=84=E5=AE=9E=E7=8E=B0=2010?= =?UTF-8?q?0%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/erp/enums/ErrorCodeConstants.java | 20 +- .../stock/ErpStockRecordBizTypeEnum.java | 5 + .../admin/stock/ErpStockCheckController.java | 149 +++++++++++ .../admin/stock/ErpStockMoveController.java | 3 - .../vo/check/ErpStockCheckPageReqVO.java | 45 ++++ .../stock/vo/check/ErpStockCheckRespVO.java | 111 +++++++++ .../vo/check/ErpStockCheckSaveReqVO.java | 69 ++++++ .../dal/dataobject/stock/ErpStockCheckDO.java | 63 +++++ .../dataobject/stock/ErpStockCheckItemDO.java | 83 +++++++ .../mysql/stock/ErpStockCheckItemMapper.java | 30 +++ .../dal/mysql/stock/ErpStockCheckMapper.java | 46 ++++ .../erp/dal/redis/no/ErpNoRedisDAO.java | 5 + .../service/stock/ErpStockCheckService.java | 84 +++++++ .../stock/ErpStockCheckServiceImpl.java | 232 ++++++++++++++++++ .../service/stock/ErpStockInServiceImpl.java | 6 +- .../stock/ErpStockMoveServiceImpl.java | 10 +- .../service/stock/ErpStockOutServiceImpl.java | 13 +- 17 files changed, 952 insertions(+), 22 deletions(-) create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckPageReqVO.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSaveReqVO.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckDO.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckItemDO.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckItemMapper.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckMapper.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckService.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.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 ace58df2a..7c04c19f1 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 @@ -40,17 +40,25 @@ public interface ErrorCodeConstants { ErrorCode STOCK_OUT_NO_EXISTS = new ErrorCode(1_030_402_004, "生成出库单失败,请重新提交"); ErrorCode STOCK_OUT_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_402_005, "其它出库单({})已审核,无法修改"); - // ========== ERP 其它出库单 1-030-403-000 ========== + // ========== ERP 库存调拨单 1-030-403-000 ========== ErrorCode STOCK_MOVE_NOT_EXISTS = new ErrorCode(1_030_402_000, "库存调拨单不存在"); ErrorCode STOCK_MOVE_DELETE_FAIL_APPROVE = new ErrorCode(1_030_402_001, "库存调拨单({})已审核,无法删除"); - ErrorCode STOCK_MOVE_PROCESS_FAIL = new ErrorCode(1_030_402_002, "反审核失败,只有已审核的出库单才能反审核"); - ErrorCode STOCK_MOVE_APPROVE_FAIL = new ErrorCode(1_030_402_003, "审核失败,只有未审核的出库单才能审核"); + ErrorCode STOCK_MOVE_PROCESS_FAIL = new ErrorCode(1_030_402_002, "反审核失败,只有已审核的调拨单才能反审核"); + ErrorCode STOCK_MOVE_APPROVE_FAIL = new ErrorCode(1_030_402_003, "审核失败,只有未审核的调拨单才能审核"); ErrorCode STOCK_MOVE_NO_EXISTS = new ErrorCode(1_030_402_004, "生成调拨号失败,请重新提交"); ErrorCode STOCK_MOVE_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_402_005, "库存调拨单({})已审核,无法修改"); - // ========== 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-403-000 ========== + ErrorCode STOCK_CHECK_NOT_EXISTS = new ErrorCode(1_030_403_000, "库存盘点单不存在"); + ErrorCode STOCK_CHECK_DELETE_FAIL_APPROVE = new ErrorCode(1_030_403_001, "库存盘点单({})已审核,无法删除"); + ErrorCode STOCK_CHECK_PROCESS_FAIL = new ErrorCode(1_030_403_002, "反审核失败,只有已审核的盘点单才能反审核"); + 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, "库存盘点单({})已审核,无法修改"); + + // ========== ERP 产品库存 1-030-404-000 ========== + ErrorCode STOCK_COUNT_NEGATIVE = new ErrorCode(1_030_404_000, "操作失败,产品({})所在仓库({})的库存:{},小于变更数量:{}"); + ErrorCode STOCK_COUNT_NEGATIVE2 = new ErrorCode(1_030_404_001, "操作失败,产品({})所在仓库({})的库存不足"); // ========== ERP 产品 1-030-500-000 ========== ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_030_500_000, "产品不存在"); diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java index d56bfa928..31129a2e4 100644 --- a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java +++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java @@ -25,6 +25,11 @@ public enum ErpStockRecordBizTypeEnum implements IntArrayValuable { MOVE_IN_CANCEL(31, "调拨入库(作废)"), MOVE_OUT(32, "调拨出库"), MOVE_OUT_CANCEL(33, "调拨出库(作废)"), + + CHECK_MORE_IN(40, "盘盈入库"), + CHECK_MORE_IN_CANCEL(41, "盘盈入库(作废)"), + CHECK_LESS_OUT(42, "盘亏出库"), + CHECK_LESS_OUT_CANCEL(43, "盘亏出库(作废)"), ; public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ErpStockRecordBizTypeEnum::getType).toArray(); 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 new file mode 100644 index 000000000..c2abc6ec9 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java @@ -0,0 +1,149 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock; + +import cn.hutool.core.collection.CollUtil; +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.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +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.dal.dataobject.stock.ErpStockCheckDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import cn.iocoder.yudao.module.erp.service.stock.ErpStockCheckService; +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; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 库存调拨单") +@RestController +@RequestMapping("/erp/stock-check") +@Validated +public class ErpStockCheckController { + + @Resource + private ErpStockCheckService stockCheckService; + @Resource + private ErpProductService productService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @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 = "更新库存调拨单") + @PreAuthorize("@ss.hasPermission('erp:stock-check:update')") + public CommonResult updateStockCheck(@Valid @RequestBody ErpStockCheckSaveReqVO updateReqVO) { + stockCheckService.updateStockCheck(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新库存调拨单的状态") + @PreAuthorize("@ss.hasPermission('erp:stock-check:update-status')") + public CommonResult updateStockCheckStatus(@RequestParam("id") Long id, + @RequestParam("status") Integer status) { + stockCheckService.updateStockCheckStatus(id, status); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除库存调拨单") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('erp:stock-check:delete')") + public CommonResult deleteStockCheck(@RequestParam("ids") List ids) { + stockCheckService.deleteStockCheck(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得库存调拨单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:stock-check:query')") + public CommonResult getStockCheck(@RequestParam("id") Long id) { + ErpStockCheckDO stockCheck = stockCheckService.getStockCheck(id); + 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())))))); + } + + @GetMapping("/page") + @Operation(summary = "获得库存调拨单分页") + @PreAuthorize("@ss.hasPermission('erp:stock-check:query')") + public CommonResult> getStockCheckPage(@Valid ErpStockCheckPageReqVO pageReqVO) { + PageResult pageResult = stockCheckService.getStockCheckPage(pageReqVO); + return success(buildStockCheckVOPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出库存调拨单 Excel") + @PreAuthorize("@ss.hasPermission('erp:stock-check:export')") + @OperateLog(type = EXPORT) + public void exportStockCheckExcel(@Valid ErpStockCheckPageReqVO pageReqVO, + 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); + } + + 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(), erpStockRecordDO -> Long.parseLong(erpStockRecordDO.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())); + }); + } + +} \ 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/ErpStockMoveController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockMoveController.java index e39b7bba7..d8c838b61 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockMoveController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockMoveController.java @@ -16,7 +16,6 @@ import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockMoveItemDO; import cn.iocoder.yudao.module.erp.service.product.ErpProductService; -import cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService; import cn.iocoder.yudao.module.erp.service.stock.ErpStockMoveService; import cn.iocoder.yudao.module.erp.service.stock.ErpStockService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; @@ -53,8 +52,6 @@ public class ErpStockMoveController { private ErpStockService stockService; @Resource private ErpProductService productService; - @Resource - private ErpCustomerService customerService; @Resource private AdminUserApi adminUserApi; diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckPageReqVO.java new file mode 100644 index 000000000..2bae14c1e --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckPageReqVO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - ERP 库存盘点单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpStockCheckPageReqVO extends PageParam { + + @Schema(description = "盘点单号", example = "S123") + private String no; + + @Schema(description = "仓库编号", example = "3113") + private Long warehouseId; + + @Schema(description = "盘点时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] checkTime; + + @Schema(description = "状态", example = "10") + @InEnum(ErpAuditStatus.class) + private Integer status; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "创建者") + private String creator; + + @Schema(description = "产品编号", example = "1") + private Long productId; + +} \ 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/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 new file mode 100644 index 000000000..d761516d9 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java @@ -0,0 +1,111 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.module.erp.enums.DictTypeConstants.AUDIT_STATUS; + +@Schema(description = "管理后台 - ERP 库存盘点单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpStockCheckRespVO { + + @Schema(description = "盘点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + @ExcelProperty("盘点编号") + private Long id; + + @Schema(description = "盘点单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "S123") + @ExcelProperty("盘点单号") + private String no; + + @Schema(description = "盘点时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("盘点时间") + private LocalDateTime checkTime; + + @Schema(description = "合计数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15663") + @ExcelProperty("合计数量") + private BigDecimal totalCount; + + @Schema(description = "合计金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "24906") + @ExcelProperty("合计金额") + private BigDecimal totalPrice; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @ExcelProperty(value = "状态", converter = DictConvert.class) + @DictFormat(AUDIT_STATUS) + private Integer status; + + @Schema(description = "备注", example = "随便") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "附件 URL", example = "https://www.iocoder.cn/1.doc") + private String fileUrl; + + @Schema(description = "审核人", example = "芋道") + private String creator; + @Schema(description = "审核人名称", example = "芋道") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "盘点项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Schema(description = "产品信息", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("产品信息") + private String productNames; + + @Data + public static class Item { + + @Schema(description = "盘点项编号", example = "11756") + private Long id; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + private Long productId; + + @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "账面数量(当前库存)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "账面数量不能为空") + private BigDecimal stockCount; + + @Schema(description = "实际数量(实际库存)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "实际数量不能为空") + private BigDecimal actualCount; + + @Schema(description = "盈亏数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "盈亏数量不能为空") + private BigDecimal count; + + @Schema(description = "备注", example = "随便") + private String remark; + + // ========== 关联字段 ========== + + @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; + + } + +} \ 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 new file mode 100644 index 000000000..0af223fb5 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSaveReqVO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - ERP 其它出库单新增/修改 Request VO") +@Data +public class ErpStockCheckSaveReqVO { + + @Schema(description = "出库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") + private Long id; + + @Schema(description = "出库时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出库时间不能为空") + private LocalDateTime checkTime; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "附件 URL", example = "https://www.iocoder.cn/1.doc") + private String fileUrl; + + @Schema(description = "出库项列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "出库项列表不能为空") + @Valid + private List items; + + @Data + public static class Item { + + @Schema(description = "出库项编号", example = "11756") + private Long id; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "仓库编号不能为空") + private Long warehouseId; + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") + @NotNull(message = "产品编号不能为空") + private Long productId; + + @Schema(description = "产品单价", example = "100.00") + private BigDecimal productPrice; + + @Schema(description = "账面数量(当前库存)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "账面数量不能为空") + private BigDecimal stockCount; + + @Schema(description = "实际数量(实际库存)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "实际数量不能为空") + private BigDecimal actualCount; + + @Schema(description = "盈亏数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "盈亏数量不能为空") + private BigDecimal count; + + @Schema(description = "备注", example = "随便") + 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/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 new file mode 100644 index 000000000..e9168275f --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckDO.java @@ -0,0 +1,63 @@ +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.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * ERP 库存盘点单 DO + * + * @author 芋道源码 + */ +@TableName("erp_stock_check") +@KeySequence("erp_stock_check_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockCheckDO extends BaseDO { + + /** + * 盘点编号 + */ + @TableId + private Long id; + /** + * 盘点单号 + */ + private String no; + /** + * 盘点时间 + */ + private LocalDateTime checkTime; + /** + * 合计数量 + */ + private BigDecimal totalCount; + /** + * 合计金额,单位:元 + */ + private BigDecimal totalPrice; + /** + * 状态 + * + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * 附件 URL + */ + 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 new file mode 100644 index 000000000..c3c4dbf99 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckItemDO.java @@ -0,0 +1,83 @@ +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 com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * ERP 库存盘点单项 DO + * + * @author 芋道源码 + */ +@TableName("erp_stock_check_item") +@KeySequence("erp_stock_check_item_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpStockCheckItemDO extends BaseDO { + + /** + * 盘点项编号 + */ + @TableId + private Long id; + /** + * 盘点编号 + * + * 关联 {@link ErpStockCheckDO#getId()} + */ + private Long checkId; + /** + * 仓库编号 + * + * 关联 {@link ErpWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 产品编号 + * + * 关联 {@link ErpProductDO#getId()} + */ + private Long productId; + /** + * 产品单位编号 + * + * 冗余 {@link ErpProductDO#getUnitId()} + */ + private Long productUnitId; + /** + * 产品单价 + */ + private BigDecimal productPrice; + /** + * 账面数量(当前库存) + */ + private BigDecimal stockCount; + /** + * 实际数量(实际库存) + */ + private BigDecimal actualCount; + /** + * 盈亏数量 + * + * count = stockCount - actualCount + */ + private BigDecimal count; + /** + * 合计金额,单位:元 + */ + private BigDecimal totalPrice; + /** + * 备注 + */ + 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/ErpStockCheckItemMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckItemMapper.java new file mode 100644 index 000000000..ae13f9f96 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckItemMapper.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 库存盘点单项 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpStockCheckItemMapper extends BaseMapperX { + + default List selectListByCheckId(Long checkId) { + return selectList(ErpStockCheckItemDO::getCheckId, checkId); + } + + default List selectListByCheckIds(Collection checkIds) { + return selectList(ErpStockCheckItemDO::getCheckId, checkIds); + } + + default int deleteByCheckId(Long checkId) { + return delete(ErpStockCheckItemDO::getCheckId, checkId); + } + +} \ 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/ErpStockCheckMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckMapper.java new file mode 100644 index 000000000..3328f39b2 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckMapper.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.stock; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * ERP 库存调拨单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpStockCheckMapper extends BaseMapperX { + + default PageResult selectPage(ErpStockCheckPageReqVO reqVO) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX() + .eqIfPresent(ErpStockCheckDO::getNo, reqVO.getNo()) + .betweenIfPresent(ErpStockCheckDO::getCheckTime, reqVO.getCheckTime()) + .eqIfPresent(ErpStockCheckDO::getStatus, reqVO.getStatus()) + .likeIfPresent(ErpStockCheckDO::getRemark, reqVO.getRemark()) + .eqIfPresent(ErpStockCheckDO::getCreator, reqVO.getCreator()) + .orderByDesc(ErpStockCheckDO::getId); + if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) { + query.leftJoin(ErpStockCheckItemDO.class, ErpStockCheckItemDO::getCheckId, ErpStockCheckDO::getId) + .eq(reqVO.getWarehouseId() != null, ErpStockCheckItemDO::getWarehouseId, reqVO.getWarehouseId()) + .eq(reqVO.getProductId() != null, ErpStockCheckItemDO::getProductId, reqVO.getProductId()) + .groupBy(ErpStockCheckDO::getId); // 避免 1 对多查询,产生相同的 1 + } + return selectJoinPage(reqVO, ErpStockCheckDO.class, query); + } + + default int updateByIdAndStatus(Long id, Integer status, ErpStockCheckDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(ErpStockCheckDO::getId, id).eq(ErpStockCheckDO::getStatus, status)); + } + + default ErpStockCheckDO selectByNo(String no) { + return selectOne(ErpStockCheckDO::getNo, no); + } + +} \ 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/redis/no/ErpNoRedisDAO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java index 4be18012b..bd2612db1 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java @@ -33,6 +33,11 @@ public class ErpNoRedisDAO { */ public static final String STOCK_MOVE_NO_PREFIX = "QCDB"; + /** + * 库存盘点 {@link cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO} + */ + public static final String STOCK_CHECK_NO_PREFIX = "QCPD"; + @Resource private StringRedisTemplate stringRedisTemplate; 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 new file mode 100644 index 000000000..2f8a0f9ee --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckService.java @@ -0,0 +1,84 @@ +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.ErpStockCheckPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * ERP 库存盘点单 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpStockCheckService { + + /** + * 创建库存盘点单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStockCheck(@Valid ErpStockCheckSaveReqVO createReqVO); + + /** + * 更新库存盘点单 + * + * @param updateReqVO 更新信息 + */ + void updateStockCheck(@Valid ErpStockCheckSaveReqVO updateReqVO); + + /** + * 更新库存盘点单的状态 + * + * @param id 编号 + * @param status 状态 + */ + void updateStockCheckStatus(Long id, Integer status); + + /** + * 删除库存盘点单 + * + * @param ids 编号数组 + */ + void deleteStockCheck(List ids); + + /** + * 获得库存盘点单 + * + * @param id 编号 + * @return 库存盘点单 + */ + ErpStockCheckDO getStockCheck(Long id); + + /** + * 获得库存盘点单分页 + * + * @param pageReqVO 分页查询 + * @return 库存盘点单分页 + */ + PageResult getStockCheckPage(ErpStockCheckPageReqVO pageReqVO); + + // ==================== 盘点项 ==================== + + /** + * 获得库存盘点单项列表 + * + * @param checkId 盘点编号 + * @return 库存盘点单项列表 + */ + List getStockCheckItemListByCheckId(Long checkId); + + /** + * 获得库存盘点单项 List + * + * @param checkIds 盘点编号数组 + * @return 库存盘点单项 List + */ + List getStockCheckItemListByCheckIds(Collection checkIds); + +} \ 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/ErpStockCheckServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.java new file mode 100644 index 000000000..49aca94d3 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.java @@ -0,0 +1,232 @@ +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.MoneyUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +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.mysql.stock.ErpStockCheckItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockCheckMapper; +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.stock.bo.ErpStockRecordCreateReqBO; +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; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +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.*; + +// TODO 芋艿:记录操作日志 + +/** + * ERP 库存盘点单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpStockCheckServiceImpl implements ErpStockCheckService { + + @Resource + private ErpStockCheckMapper stockCheckMapper; + @Resource + private ErpStockCheckItemMapper stockCheckItemMapper; + + @Resource + private ErpNoRedisDAO noRedisDAO; + + @Resource + private ErpProductService productService; + @Resource + private ErpWarehouseService warehouseService; + @Resource + private ErpStockRecordService stockRecordService; + + @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)) + .setTotalPrice(getSumValue(stockCheckItems, ErpStockCheckItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO))); + stockCheckMapper.insert(stockCheck); + // 2.2 插入盘点单项 + stockCheckItems.forEach(o -> o.setCheckId(stockCheck.getId())); + stockCheckItemMapper.insertBatch(stockCheckItems); + return stockCheck.getId(); + } + + @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()); + + // 2.1 更新盘点单 + ErpStockCheckDO updateObj = BeanUtils.toBean(updateReqVO, ErpStockCheckDO.class, in -> in + .setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add)) + .setTotalPrice(getSumValue(stockCheckItems, ErpStockCheckItemDO::getTotalPrice, BigDecimal::add))); + 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); + } + + // 2. 更新状态 + 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(); + } + stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO( + stockCheckItem.getProductId(), stockCheckItem.getWarehouseId(), count, + bizType, stockCheckItem.getCheckId(), stockCheckItem.getId(), stockCheck.getNo())); + }); + } + + 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())))); + } + + 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())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + diffList.get(0).forEach(o -> o.setCheckId(id)); + stockCheckItemMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + stockCheckItemMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + stockCheckItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpStockCheckItemDO::getId)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteStockCheck(List ids) { + // 1. 校验不处于已审批 + List stockChecks = stockCheckMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(stockChecks)) { + return; + } + stockChecks.forEach(stockCheck -> { + if (ErpAuditStatus.APPROVE.getStatus().equals(stockCheck.getStatus())) { + throw exception(STOCK_CHECK_DELETE_FAIL_APPROVE, stockCheck.getNo()); + } + }); + + // 2. 遍历删除,并记录操作日志 + stockChecks.forEach(stockCheck -> { + // 2.1 删除盘点单 + stockCheckMapper.deleteById(stockCheck.getId()); + // 2.2 删除盘点单项 + stockCheckItemMapper.deleteByCheckId(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); + } + + @Override + public PageResult getStockCheckPage(ErpStockCheckPageReqVO pageReqVO) { + return stockCheckMapper.selectPage(pageReqVO); + } + + // ==================== 盘点项 ==================== + + @Override + public List getStockCheckItemListByCheckId(Long checkId) { + return stockCheckItemMapper.selectListByCheckId(checkId); + } + + @Override + public List getStockCheckItemListByCheckIds(Collection checkIds) { + if (CollUtil.isEmpty(checkIds)) { + return Collections.emptyList(); + } + return stockCheckItemMapper.selectListByCheckIds(checkIds); + } + +} \ 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/ErpStockInServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInServiceImpl.java index 22b08b266..213fca8b8 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 @@ -138,10 +138,12 @@ public class ErpStockInServiceImpl implements ErpStockInService { private List validateStockInItems(List list) { // 1.1 校验产品存在 - List productList = productService.validProductList(convertSet(list, ErpStockInSaveReqVO.Item::getProductId)); + List productList = productService.validProductList( + convertSet(list, ErpStockInSaveReqVO.Item::getProductId)); Map productMap = convertMap(productList, ErpProductDO::getId); // 1.2 校验仓库存在 - warehouseService.validWarehouseList(convertSet(list, ErpStockInSaveReqVO.Item::getWarehouseId)); + warehouseService.validWarehouseList(convertSet( + list, ErpStockInSaveReqVO.Item::getWarehouseId)); // 2. 转化为 ErpStockInItemDO 列表 return convertList(list, o -> BeanUtils.toBean(o, ErpStockInItemDO.class, item -> item .setProductUnitId(productMap.get(item.getProductId()).getUnitId()) diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveServiceImpl.java index 089c98e72..a0d8f79d4 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveServiceImpl.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveServiceImpl.java @@ -214,16 +214,16 @@ public class ErpStockMoveServiceImpl implements ErpStockMoveService { // ==================== 出库项 ==================== @Override - public List getStockMoveItemListByMoveId(Long inId) { - return stockMoveItemMapper.selectListByMoveId(inId); + public List getStockMoveItemListByMoveId(Long moveId) { + return stockMoveItemMapper.selectListByMoveId(moveId); } @Override - public List getStockMoveItemListByMoveIds(Collection inIds) { - if (CollUtil.isEmpty(inIds)) { + public List getStockMoveItemListByMoveIds(Collection moveIds) { + if (CollUtil.isEmpty(moveIds)) { return Collections.emptyList(); } - return stockMoveItemMapper.selectListByMoveIds(inIds); + return stockMoveItemMapper.selectListByMoveIds(moveIds); } } \ 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/ErpStockOutServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutServiceImpl.java index 8117ef36a..9fa192465 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutServiceImpl.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutServiceImpl.java @@ -139,7 +139,8 @@ public class ErpStockOutServiceImpl implements ErpStockOutService { private List validateStockOutItems(List list) { // 1.1 校验产品存在 - List productList = productService.validProductList(convertSet(list, ErpStockOutSaveReqVO.Item::getProductId)); + List productList = productService.validProductList( + convertSet(list, ErpStockOutSaveReqVO.Item::getProductId)); Map productMap = convertMap(productList, ErpProductDO::getId); // 1.2 校验仓库存在 warehouseService.validWarehouseList(convertSet(list, ErpStockOutSaveReqVO.Item::getWarehouseId)); @@ -212,16 +213,16 @@ public class ErpStockOutServiceImpl implements ErpStockOutService { // ==================== 出库项 ==================== @Override - public List getStockOutItemListByOutId(Long inId) { - return stockOutItemMapper.selectListByOutId(inId); + public List getStockOutItemListByOutId(Long outId) { + return stockOutItemMapper.selectListByOutId(outId); } @Override - public List getStockOutItemListByOutIds(Collection inIds) { - if (CollUtil.isEmpty(inIds)) { + public List getStockOutItemListByOutIds(Collection outIds) { + if (CollUtil.isEmpty(outIds)) { return Collections.emptyList(); } - return stockOutItemMapper.selectListByOutIds(inIds); + return stockOutItemMapper.selectListByOutIds(outIds); } } \ No newline at end of file