From e3d1dac817306d44450cdfe35e7cacf6abd2da57 Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Wed, 17 Jun 2026 15:33:48 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E4=BF=AE=E6=94=B9=E7=9B=98=E7=82=B9?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3=E5=8F=8A?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=BA=93=E5=AD=98=E6=80=BB=E8=A7=88=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/erp/enums/ErrorCodeConstants.java | 1 + .../stock/ErpStockCheckSourceTypeEnum.java | 26 ++ .../admin/stock/ErpWarehouseController.java | 20 +- .../ErpStockCheckGenerateByLocationReqVO.java | 5 +- .../ErpStockCheckGenerateByProductReqVO.java | 3 + .../stock/vo/check/ErpStockCheckRespVO.java | 6 + .../vo/check/ErpStockCheckSaveReqVO.java | 9 +- .../warehouse/ErpWarehouseSummaryRespVO.java | 66 +++++ .../dal/dataobject/stock/ErpStockCheckDO.java | 4 + .../dal/mysql/stock/ErpWarehouseMapper.java | 7 +- .../stock/ErpStockCheckServiceImpl.java | 19 +- .../service/stock/ErpWarehouseService.java | 2 +- .../stock/ErpWarehouseServiceImpl.java | 4 +- .../stock/ErpWarehouseSummaryService.java | 9 + .../stock/ErpWarehouseSummaryServiceImpl.java | 261 ++++++++++++++++++ 15 files changed, 429 insertions(+), 13 deletions(-) create mode 100644 yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockCheckSourceTypeEnum.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseSummaryRespVO.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseSummaryService.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseSummaryServiceImpl.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 33af6e76a..d80d25dc4 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 @@ -160,6 +160,7 @@ public interface ErrorCodeConstants { 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, "审核失败,审核结果只支持通过或驳回"); + ErrorCode STOCK_CHECK_SOURCE_TYPE_NOT_EXISTS = new ErrorCode(1_030_403_014, "盘点单生成来源类型不存在"); // ========== 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/ErpStockCheckSourceTypeEnum.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockCheckSourceTypeEnum.java new file mode 100644 index 000000000..06dcd5982 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockCheckSourceTypeEnum.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.erp.enums.stock; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +@RequiredArgsConstructor +@Getter +public enum ErpStockCheckSourceTypeEnum implements IntArrayValuable { + + BY_LOCATION(1, "按库存"), + BY_PRODUCT(2, "按产品"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ErpStockCheckSourceTypeEnum::getType).toArray(); + + private final Integer type; + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpWarehouseController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpWarehouseController.java index 5d80c2cf9..d87830a28 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpWarehouseController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpWarehouseController.java @@ -5,13 +5,17 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 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.validation.InEnum; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.erp.controller.admin.stock.enums.ErpWarehouseCategoryEnum; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehousePageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseSummaryRespVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseRespVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseSaveReqVO; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; import cn.iocoder.yudao.module.erp.service.stock.ErpWarehouseService; +import cn.iocoder.yudao.module.erp.service.stock.ErpWarehouseSummaryService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; @@ -38,6 +42,8 @@ public class ErpWarehouseController { @Resource private ErpWarehouseService warehouseService; + @Resource + private ErpWarehouseSummaryService warehouseSummaryService; @PostMapping("/create") @Operation(summary = "创建仓库") @@ -93,13 +99,23 @@ public class ErpWarehouseController { @GetMapping("/simple-list") @Operation(summary = "获得仓库精简列表", description = "只包含被开启的仓库,主要用于前端的下拉选项") - public CommonResult> getWarehouseSimpleList() { - List list = warehouseService.getWarehouseListByStatus(CommonStatusEnum.ENABLE.getStatus()); + @Parameter(name = "categoryType", description = "仓库分类", example = "1") + public CommonResult> getWarehouseSimpleList( + @RequestParam(value = "categoryType", required = false) Integer categoryType) { + List list = warehouseService.getWarehouseListByStatus(CommonStatusEnum.ENABLE.getStatus(), categoryType); return success(convertList(list, warehouse -> new ErpWarehouseRespVO().setId(warehouse.getId()) .setName(warehouse.getName()).setCategoryType(warehouse.getCategoryType()) .setDefaultStatus(warehouse.getDefaultStatus()))); } + @GetMapping("/summary") + @Operation(summary = "获得仓库总览") + @Parameter(name = "warehouseId", description = "仓库编号", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('erp:warehouse:query')") + public CommonResult getWarehouseSummary(@RequestParam("warehouseId") Long warehouseId) { + return success(warehouseSummaryService.getWarehouseSummary(warehouseId)); + } + @GetMapping("/export-excel") @Operation(summary = "导出仓库 Excel") @PreAuthorize("@ss.hasPermission('erp:warehouse:export')") 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 index f7ff71bd0..a01758ad0 100644 --- 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 @@ -3,7 +3,6 @@ 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") @@ -16,8 +15,8 @@ public class ErpStockCheckGenerateByLocationReqVO { @Schema(description = "库区编号列表", example = "[11,12]") private List areaIds; - @Schema(description = "是否允许为空,默认 false", example = "false") - private Boolean allowEmpty; + @Schema(description = "是否允许为空库存,默认 true", example = "true") + private Boolean allowEmpty = Boolean.TRUE; 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 index 76564c842..5ead9c285 100644 --- 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 @@ -14,4 +14,7 @@ public class ErpStockCheckGenerateByProductReqVO { @NotEmpty(message = "产品编号列表不能为空") private List productIds; + @Schema(description = "是否允许为空库存,默认 true", example = "true") + private Boolean allowEmpty = Boolean.TRUE; + } 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 ee93dfa40..633b73157 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 @@ -2,6 +2,7 @@ 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 static cn.iocoder.yudao.module.erp.enums.DictTypeConstants.PRODUCT_CATEGORY_TYPE; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -31,6 +32,11 @@ public class ErpStockCheckRespVO { @ExcelProperty("盘点时间") private LocalDateTime checkTime; + @Schema(description = "生成来源类型:1-按库存,2-按产品", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "生成来源类型", converter = DictConvert.class) + @DictFormat("erp_stock_check_source_type") + private Integer sourceType; + @Schema(description = "合计数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15663") @ExcelProperty("合计数量") private BigDecimal totalCount; 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 cf04eca64..fc6e77997 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 @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check; import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.erp.enums.stock.ErpStockCheckSourceTypeEnum; import lombok.Data; import javax.validation.Valid; @@ -21,13 +23,18 @@ public class ErpStockCheckSaveReqVO { @NotNull(message = "盘点时间不能为空") private LocalDateTime checkTime; + @Schema(description = "生成来源类型:1-按库存,2-按产品", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "生成来源类型不能为空") + @InEnum(ErpStockCheckSourceTypeEnum.class) + private Integer sourceType; + @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) + @Schema(description = "盘点项列表", implementation = Item.class) @NotEmpty(message = "盘点项列表不能为空") @Valid private List items; diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseSummaryRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseSummaryRespVO.java new file mode 100644 index 000000000..b78704822 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseSummaryRespVO.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - ERP 仓库总览 Response VO") +@Data +public class ErpWarehouseSummaryRespVO { + + @Schema(description = "仓库编号", example = "1") + private Long warehouseId; + + @Schema(description = "仓库名称", example = "A仓") + private String warehouseName; + + @Schema(description = "产品库存量", example = "1000") + private BigDecimal productStockCount; + + @Schema(description = "产品库存展示", example = "2托3包4个") + private String productStockDisplay; + + @Schema(description = "物料库存量", example = "1000") + private BigDecimal materialStockCount; + + @Schema(description = "物料库存展示", example = "10采购单位20库存单位") + private String materialStockDisplay; + + @Schema(description = "备件库存量", example = "1000") + private BigDecimal sparePartStockCount; + + @Schema(description = "备件库存展示", example = "10采购单位20库存单位") + private String sparePartStockDisplay; + + @Schema(description = "今日入库单数量", example = "5") + private Long todayStockInOrderCount; + + @Schema(description = "今日入库明细行数量", example = "18") + private Long todayStockInItemCount; + + @Schema(description = "今日入库产成品展示", example = "2托3包4个") + private String todayProductStockInDisplay; + + @Schema(description = "今日入库物料展示", example = "100个") + private String todayMaterialStockInDisplay; + + @Schema(description = "今日入库备件展示", example = "100个") + private String todaySparePartStockInDisplay; + + @Schema(description = "今日出库单数量", example = "3") + private Long todayStockOutOrderCount; + + @Schema(description = "今日出库明细行数量", example = "10") + private Long todayStockOutItemCount; + + @Schema(description = "今日出库产成品展示", example = "2托3包4个") + private String todayProductStockOutDisplay; + + @Schema(description = "今日出库物料展示", example = "100个") + private String todayMaterialStockOutDisplay; + + @Schema(description = "今日出库备件展示", example = "100个") + private String todaySparePartStockOutDisplay; + +} 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 54130f443..2986a7b76 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 @@ -37,6 +37,10 @@ public class ErpStockCheckDO extends BaseDO { * 盘点时间 */ private LocalDateTime checkTime; + /** + * 生成来源类型:1-按库存,2-按产品 + */ + private Integer sourceType; /** * 合计数量 */ diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpWarehouseMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpWarehouseMapper.java index f9d36a735..d12c0c32f 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpWarehouseMapper.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpWarehouseMapper.java @@ -44,8 +44,11 @@ public interface ErpWarehouseMapper extends BaseMapperX { return selectOne(ErpWarehouseDO::getDefaultStatus, true); } - default List selectListByStatus(Integer status) { - return selectList(ErpWarehouseDO::getStatus, status); + default List selectListByStatus(Integer status, Integer categoryType) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(ErpWarehouseDO::getStatus, status) + .eqIfPresent(ErpWarehouseDO::getCategoryType, categoryType) + .orderByDesc(ErpWarehouseDO::getId)); } } 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 53badaa8d..06c6130c5 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 @@ -29,6 +29,7 @@ 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.ErpStockCheckSourceTypeEnum; 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; @@ -63,6 +64,7 @@ import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_CHECK_D 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_SOURCE_TYPE_NOT_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; @@ -102,6 +104,7 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService { @Override @Transactional(rollbackFor = Exception.class) public Long createStockCheck(ErpStockCheckSaveReqVO createReqVO) { + validateSourceType(createReqVO.getSourceType()); List stockCheckItems = validateStockCheckItems(createReqVO.getItems()); String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_CHECK_NO_PREFIX); if (stockCheckMapper.selectByNo(no) != null) { @@ -130,6 +133,7 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService { throw exception(STOCK_CHECK_UPDATE_FAIL_PROCESS, stockCheck.getNo()); } + validateSourceType(updateReqVO.getSourceType()); List stockCheckItems = validateStockCheckItems(updateReqVO.getItems()); ErpStockCheckDO updateObj = BeanUtils.toBean(updateReqVO, ErpStockCheckDO.class, in -> in .setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add, BigDecimal.ZERO)) @@ -289,7 +293,7 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService { return Collections.emptyList(); } List stockList = stockMapper.selectListByWarehouseIdsAndAreaIds(reqVO.getWarehouseIds(), reqVO.getAreaIds()); - if (!Boolean.TRUE.equals(reqVO.getAllowEmpty())) { + if (Boolean.FALSE.equals(reqVO.getAllowEmpty())) { stockList = filterNonZeroStocks(stockList); } return buildPreviewItems(stockList); @@ -300,7 +304,11 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService { if (reqVO == null || CollUtil.isEmpty(reqVO.getProductIds())) { return Collections.emptyList(); } - return buildPreviewItems(filterNonZeroStocks(stockMapper.selectListByProductIds(reqVO.getProductIds()))); + List stockList = stockMapper.selectListByProductIds(reqVO.getProductIds()); + if (Boolean.FALSE.equals(reqVO.getAllowEmpty())) { + stockList = filterNonZeroStocks(stockList); + } + return buildPreviewItems(stockList); } private List filterNonZeroStocks(List stockList) { @@ -484,4 +492,11 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService { return stockCheck; } + private void validateSourceType(Integer sourceType) { + if (!ErpStockCheckSourceTypeEnum.BY_LOCATION.getType().equals(sourceType) + && !ErpStockCheckSourceTypeEnum.BY_PRODUCT.getType().equals(sourceType)) { + throw exception(STOCK_CHECK_SOURCE_TYPE_NOT_EXISTS); + } + } + } diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseService.java index 0d514cabb..5271a804b 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseService.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseService.java @@ -75,7 +75,7 @@ public interface ErpWarehouseService { * @param status 状态 * @return 仓库列表 */ - List getWarehouseListByStatus(Integer status); + List getWarehouseListByStatus(Integer status, Integer categoryType); /** * 获得仓库列表 diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseServiceImpl.java index 10eade2f5..38e377f63 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseServiceImpl.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseServiceImpl.java @@ -128,8 +128,8 @@ public class ErpWarehouseServiceImpl implements ErpWarehouseService { } @Override - public List getWarehouseListByStatus(Integer status) { - return warehouseMapper.selectListByStatus(status); + public List getWarehouseListByStatus(Integer status, Integer categoryType) { + return warehouseMapper.selectListByStatus(status, categoryType); } @Override diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseSummaryService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseSummaryService.java new file mode 100644 index 000000000..fc0bde160 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseSummaryService.java @@ -0,0 +1,9 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseSummaryRespVO; + +public interface ErpWarehouseSummaryService { + + ErpWarehouseSummaryRespVO getWarehouseSummary(Long warehouseId); + +} diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseSummaryServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseSummaryServiceImpl.java new file mode 100644 index 000000000..c9ac20c6d --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseSummaryServiceImpl.java @@ -0,0 +1,261 @@ +package cn.iocoder.yudao.module.erp.service.stock; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseSummaryRespVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO; +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockInMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockOutItemMapper; +import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockOutMapper; +import cn.iocoder.yudao.module.erp.service.product.ErpProductService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +@Service +@Validated +public class ErpWarehouseSummaryServiceImpl implements ErpWarehouseSummaryService { + + @Resource + private ErpWarehouseService warehouseService; + @Resource + private ErpStockMapper stockMapper; + @Resource + private ErpStockInMapper stockInMapper; + @Resource + private ErpStockInItemMapper stockInItemMapper; + @Resource + private ErpStockOutMapper stockOutMapper; + @Resource + private ErpStockOutItemMapper stockOutItemMapper; + @Resource + private ErpProductService productService; + + @Override + public ErpWarehouseSummaryRespVO getWarehouseSummary(Long warehouseId) { + ErpWarehouseDO warehouse = warehouseService.getWarehouse(warehouseId); + ErpWarehouseSummaryRespVO respVO = new ErpWarehouseSummaryRespVO(); + respVO.setWarehouseId(warehouseId); + respVO.setWarehouseName(warehouse != null ? warehouse.getName() : null); + + List stockList = stockMapper.selectListByWarehouseIdsAndAreaIds(Collections.singletonList(warehouseId), null); + fillStockSummary(respVO, stockList); + + LocalDateTime beginTime = LocalDate.now().atStartOfDay(); + LocalDateTime endTime = beginTime.plusDays(1); + fillTodayInSummary(respVO, warehouseId, beginTime, endTime); + fillTodayOutSummary(respVO, warehouseId, beginTime, endTime); + return respVO; + } + + private void fillStockSummary(ErpWarehouseSummaryRespVO respVO, List stockList) { + if (CollUtil.isEmpty(stockList)) { + respVO.setProductStockCount(BigDecimal.ZERO); + respVO.setMaterialStockCount(BigDecimal.ZERO); + respVO.setSparePartStockCount(BigDecimal.ZERO); + respVO.setProductStockDisplay(formatProductDisplay(BigDecimal.ZERO, null, null, null)); + respVO.setMaterialStockDisplay(formatUnitDisplay(BigDecimal.ZERO, null, null, null)); + respVO.setSparePartStockDisplay(formatUnitDisplay(BigDecimal.ZERO, null, null, null)); + return; + } + + Map> categoryMap = stockList.stream().collect(Collectors.groupingBy(this::resolveCategoryType)); + BigDecimal productCount = sumCount(categoryMap.get(1)); + BigDecimal materialCount = sumCount(categoryMap.get(2)); + BigDecimal sparePartCount = sumCount(categoryMap.get(3)); + + respVO.setProductStockCount(productCount); + respVO.setMaterialStockCount(materialCount); + respVO.setSparePartStockCount(sparePartCount); + + ErpStockDO productStock = firstStock(categoryMap.get(1)); + ErpStockDO materialStock = firstStock(categoryMap.get(2)); + ErpStockDO sparePartStock = firstStock(categoryMap.get(3)); + respVO.setProductStockDisplay(formatProductDisplay(productCount, + productStock != null ? productStock.getPalletTotalQuantity() : null, + productStock != null ? productStock.getPalletPackageQuantity() : null, + productStock != null ? productStock.getPackageQuantity() : null)); + respVO.setMaterialStockDisplay(formatUnitDisplay(materialCount, + materialStock != null ? materialStock.getPurchaseUnitConvertQuantity() : null, + materialStock != null ? materialStock.getPurchaseUnitName() : null, + materialStock != null ? materialStock.getUnitName() : null)); + respVO.setSparePartStockDisplay(formatUnitDisplay(sparePartCount, + sparePartStock != null ? sparePartStock.getPurchaseUnitConvertQuantity() : null, + sparePartStock != null ? sparePartStock.getPurchaseUnitName() : null, + sparePartStock != null ? sparePartStock.getUnitName() : null)); + } + + private void fillTodayInSummary(ErpWarehouseSummaryRespVO respVO, Long warehouseId, LocalDateTime beginTime, LocalDateTime endTime) { + List stockInList = stockInMapper.selectList(new LambdaQueryWrapper() + .eq(ErpStockInDO::getStatus, 20) + .between(ErpStockInDO::getInTime, beginTime, endTime)); + List inIds = stockInList.stream() + .filter(stockIn -> hasWarehouseIn(stockIn.getId(), warehouseId)) + .map(ErpStockInDO::getId) + .collect(Collectors.toList()); + respVO.setTodayStockInOrderCount((long) inIds.size()); + + List itemList = stockInItemMapper.selectListByInIds(inIds); + respVO.setTodayStockInItemCount((long) itemList.size()); + fillInOutDisplay(respVO, true, itemList); + } + + private void fillTodayOutSummary(ErpWarehouseSummaryRespVO respVO, Long warehouseId, LocalDateTime beginTime, LocalDateTime endTime) { + List stockOutList = stockOutMapper.selectList(new LambdaQueryWrapper() + .eq(ErpStockOutDO::getStatus, 20) + .between(ErpStockOutDO::getOutTime, beginTime, endTime)); + List outIds = stockOutList.stream() + .filter(stockOut -> hasWarehouseOut(stockOut.getId(), warehouseId)) + .map(ErpStockOutDO::getId) + .collect(Collectors.toList()); + respVO.setTodayStockOutOrderCount((long) outIds.size()); + + List itemList = stockOutItemMapper.selectListByOutIds(outIds); + respVO.setTodayStockOutItemCount((long) itemList.size()); + fillInOutDisplay(respVO, false, itemList); + } + + private void fillInOutDisplay(ErpWarehouseSummaryRespVO respVO, boolean inbound, List itemList) { + if (CollUtil.isEmpty(itemList)) { + if (inbound) { + respVO.setTodayProductStockInDisplay(formatProductDisplay(BigDecimal.ZERO, null, null, null)); + respVO.setTodayMaterialStockInDisplay(formatUnitDisplay(BigDecimal.ZERO, null, null, null)); + respVO.setTodaySparePartStockInDisplay(formatUnitDisplay(BigDecimal.ZERO, null, null, null)); + } else { + respVO.setTodayProductStockOutDisplay(formatProductDisplay(BigDecimal.ZERO, null, null, null)); + respVO.setTodayMaterialStockOutDisplay(formatUnitDisplay(BigDecimal.ZERO, null, null, null)); + respVO.setTodaySparePartStockOutDisplay(formatUnitDisplay(BigDecimal.ZERO, null, null, null)); + } + return; + } + + Set productIds = itemList.stream() + .map(item -> item instanceof ErpStockInItemDO ? ((ErpStockInItemDO) item).getProductId() : ((ErpStockOutItemDO) item).getProductId()) + .collect(Collectors.toSet()); + Map productMap = productService.getProductVOMap(productIds); + + BigDecimal productCount = BigDecimal.ZERO; + BigDecimal materialCount = BigDecimal.ZERO; + BigDecimal sparePartCount = BigDecimal.ZERO; + + for (Object item : itemList) { + Long productId = item instanceof ErpStockInItemDO ? ((ErpStockInItemDO) item).getProductId() : ((ErpStockOutItemDO) item).getProductId(); + BigDecimal count = item instanceof ErpStockInItemDO ? ((ErpStockInItemDO) item).getCount() : ((ErpStockOutItemDO) item).getCount(); + ErpProductRespVO product = productMap.get(productId); + Integer categoryType = product != null ? product.getCategoryType() : null; + if (Integer.valueOf(1).equals(categoryType)) { + productCount = productCount.add(safeCount(count)); + } else if (Integer.valueOf(2).equals(categoryType)) { + materialCount = materialCount.add(safeCount(count)); + } else if (Integer.valueOf(3).equals(categoryType)) { + sparePartCount = sparePartCount.add(safeCount(count)); + } + } + + if (inbound) { + respVO.setTodayProductStockInDisplay(formatProductDisplay(productCount, null, null, null)); + respVO.setTodayMaterialStockInDisplay(formatUnitDisplay(materialCount, null, null, null)); + respVO.setTodaySparePartStockInDisplay(formatUnitDisplay(sparePartCount, null, null, null)); + } else { + respVO.setTodayProductStockOutDisplay(formatProductDisplay(productCount, null, null, null)); + respVO.setTodayMaterialStockOutDisplay(formatUnitDisplay(materialCount, null, null, null)); + respVO.setTodaySparePartStockOutDisplay(formatUnitDisplay(sparePartCount, null, null, null)); + } + } + + private boolean hasWarehouseIn(Long inId, Long warehouseId) { + return stockInItemMapper.selectListByInId(inId).stream().anyMatch(item -> warehouseId.equals(item.getWarehouseId())); + } + + private boolean hasWarehouseOut(Long outId, Long warehouseId) { + return stockOutItemMapper.selectListByOutId(outId).stream().anyMatch(item -> warehouseId.equals(item.getWarehouseId())); + } + + private Integer resolveCategoryType(ErpStockDO stock) { + if (stock.getCategoryType() != null) { + return stock.getCategoryType(); + } + ErpProductRespVO product = productService.getProductVOMap(Collections.singleton(stock.getProductId())).get(stock.getProductId()); + return product != null ? product.getCategoryType() : null; + } + + private ErpStockDO firstStock(List list) { + return CollUtil.isEmpty(list) ? null : list.get(0); + } + + private BigDecimal sumCount(List list) { + if (CollUtil.isEmpty(list)) { + return BigDecimal.ZERO; + } + BigDecimal total = BigDecimal.ZERO; + for (ErpStockDO stock : list) { + total = total.add(safeCount(stock.getCount())); + } + return total; + } + + private BigDecimal safeCount(BigDecimal count) { + return count == null ? BigDecimal.ZERO : count; + } + + private String formatProductDisplay(BigDecimal count, BigDecimal palletTotalQuantity, BigDecimal palletPackageQuantity, + BigDecimal packageQuantity) { + BigDecimal safeCount = safeCount(count); + if (palletTotalQuantity == null || palletTotalQuantity.compareTo(BigDecimal.ZERO) <= 0) { + return formatNumber(safeCount) + "个"; + } + BigDecimal palletCount = safeCount.divideToIntegralValue(palletTotalQuantity); + BigDecimal remainAfterPallet = safeCount.remainder(palletTotalQuantity); + if (palletPackageQuantity == null || palletPackageQuantity.compareTo(BigDecimal.ZERO) <= 0) { + return formatNumber(palletCount) + "托" + formatNumber(remainAfterPallet) + "个"; + } + BigDecimal packageCount = remainAfterPallet.divideToIntegralValue(palletPackageQuantity); + BigDecimal remainAfterPackage = remainAfterPallet.remainder(palletPackageQuantity); + if (packageQuantity == null || packageQuantity.compareTo(BigDecimal.ZERO) <= 0) { + return formatNumber(palletCount) + "托" + formatNumber(packageCount) + "包" + formatNumber(remainAfterPackage) + "个"; + } + BigDecimal unitCount = remainAfterPackage.divideToIntegralValue(packageQuantity); + return formatNumber(palletCount) + "托" + formatNumber(packageCount) + "包" + formatNumber(unitCount) + "个"; + } + + private String formatUnitDisplay(BigDecimal count, BigDecimal purchaseUnitConvertQuantity, String purchaseUnitName, String unitName) { + BigDecimal safeCount = safeCount(count); + if (purchaseUnitConvertQuantity == null || purchaseUnitConvertQuantity.compareTo(BigDecimal.ZERO) <= 0 + || purchaseUnitName == null || unitName == null) { + return formatNumber(safeCount) + (unitName != null ? unitName : "个"); + } + BigDecimal purchaseCount = safeCount.divideToIntegralValue(purchaseUnitConvertQuantity); + BigDecimal unitCount = safeCount.remainder(purchaseUnitConvertQuantity); + StringBuilder display = new StringBuilder(); + if (purchaseCount.compareTo(BigDecimal.ZERO) > 0) { + display.append(formatNumber(purchaseCount)).append(purchaseUnitName); + } + if (unitCount.compareTo(BigDecimal.ZERO) > 0 || display.length() == 0) { + display.append(formatNumber(unitCount)).append(unitName); + } + return display.toString(); + } + + private String formatNumber(BigDecimal value) { + return value == null ? "0" : value.stripTrailingZeros().toPlainString(); + } + +}