feat:新增盘点单相关接口

main
HuangHuiKang 5 days ago
parent 950df74b83
commit ab03c51450

@ -152,6 +152,14 @@ public interface ErrorCodeConstants {
ErrorCode STOCK_CHECK_APPROVE_FAIL = new ErrorCode(1_030_403_003, "审核失败,只有未审核的盘点单才能审核"); 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_NO_EXISTS = new ErrorCode(1_030_403_004, "生成盘点号失败,请重新提交");
ErrorCode STOCK_CHECK_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_403_005, "库存盘点单({})已审核,无法修改"); 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 ========== // ========== ERP 产品库存 1-030-404-000 ==========
ErrorCode STOCK_COUNT_NEGATIVE = new ErrorCode(1_030_404_000, "操作失败,产品({})所在仓库({})的库存:{},小于变更数量:{}"); ErrorCode STOCK_COUNT_NEGATIVE = new ErrorCode(1_030_404_000, "操作失败,产品({})所在仓库({})的库存:{},小于变更数量:{}");

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

@ -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.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 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.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 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.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.ErpStockCheckPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckRespVO; 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.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.ErpStockCheckDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; 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.product.ErpProductService;
import cn.iocoder.yudao.module.erp.service.stock.ErpStockCheckService; 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.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation; 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 io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; 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.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; import javax.validation.Valid;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; 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.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 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.convertMultiMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - ERP 库存调拨单") @Tag(name = "管理后台 - ERP 库存盘点单")
@RestController @RestController
@RequestMapping("/erp/stock-check") @RequestMapping("/erp/stock-check")
@Validated @Validated
@ -47,19 +70,22 @@ public class ErpStockCheckController {
private ErpStockCheckService stockCheckService; private ErpStockCheckService stockCheckService;
@Resource @Resource
private ErpProductService productService; private ErpProductService productService;
@Resource
private ErpWarehouseService warehouseService;
@Resource
private WarehouseAreaService warehouseAreaService;
@Resource @Resource
private AdminUserApi adminUserApi; private AdminUserApi adminUserApi;
@PostMapping("/create") @PostMapping("/create")
@Operation(summary = "创建库存调拨单") @Operation(summary = "创建库存盘点单")
@PreAuthorize("@ss.hasPermission('erp:stock-check:create')") @PreAuthorize("@ss.hasPermission('erp:stock-check:create')")
public CommonResult<Long> createStockCheck(@Valid @RequestBody ErpStockCheckSaveReqVO createReqVO) { public CommonResult<Long> createStockCheck(@Valid @RequestBody ErpStockCheckSaveReqVO createReqVO) {
return success(stockCheckService.createStockCheck(createReqVO)); return success(stockCheckService.createStockCheck(createReqVO));
} }
@PutMapping("/update") @PutMapping("/update")
@Operation(summary = "更新库存调拨单") @Operation(summary = "更新库存盘点单")
@PreAuthorize("@ss.hasPermission('erp:stock-check:update')") @PreAuthorize("@ss.hasPermission('erp:stock-check:update')")
public CommonResult<Boolean> updateStockCheck(@Valid @RequestBody ErpStockCheckSaveReqVO updateReqVO) { public CommonResult<Boolean> updateStockCheck(@Valid @RequestBody ErpStockCheckSaveReqVO updateReqVO) {
stockCheckService.updateStockCheck(updateReqVO); stockCheckService.updateStockCheck(updateReqVO);
@ -67,16 +93,56 @@ public class ErpStockCheckController {
} }
@PutMapping("/update-status") @PutMapping("/update-status")
@Operation(summary = "更新库存调拨单的状态") @Operation(summary = "更新库存盘点单状态")
@PreAuthorize("@ss.hasPermission('erp:stock-check:update-status')") @PreAuthorize("@ss.hasPermission('erp:stock-check:update-status')")
@RateLimiter(count = 1, timeUnit = TimeUnit.SECONDS)
public CommonResult<Boolean> updateStockCheckStatus(@RequestParam("id") Long id, public CommonResult<Boolean> updateStockCheckStatus(@RequestParam("id") Long id,
@RequestParam("status") Integer status) { @RequestParam("status") Integer status) {
stockCheckService.updateStockCheckStatus(id, status); stockCheckService.updateStockCheckStatus(id, status);
return success(true); return success(true);
} }
@PutMapping("/submit")
@Operation(summary = "提交库存盘点单审核")
@PreAuthorize("@ss.hasPermission('erp:stock-check:update')")
@RateLimiter(count = 1, timeUnit = TimeUnit.SECONDS)
public CommonResult<Boolean> 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<Boolean> 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<List<ErpStockCheckApproveRecordRespVO>> 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<List<ErpStockCheckRespVO.Item>> 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<List<ErpStockCheckRespVO.Item>> generateItemsByProduct(@Valid @RequestBody ErpStockCheckGenerateByProductReqVO reqVO) {
return success(stockCheckService.generateStockCheckItemsByProduct(reqVO));
}
@DeleteMapping("/delete") @DeleteMapping("/delete")
@Operation(summary = "删除库存调拨单") @Operation(summary = "删除库存盘点单")
@Parameter(name = "ids", description = "编号数组", required = true) @Parameter(name = "ids", description = "编号数组", required = true)
@PreAuthorize("@ss.hasPermission('erp:stock-check:delete')") @PreAuthorize("@ss.hasPermission('erp:stock-check:delete')")
public CommonResult<Boolean> deleteStockCheck(@RequestParam("ids") List<Long> ids) { public CommonResult<Boolean> deleteStockCheck(@RequestParam("ids") List<Long> ids) {
@ -85,7 +151,7 @@ public class ErpStockCheckController {
} }
@GetMapping("/get") @GetMapping("/get")
@Operation(summary = "获得库存调拨单") @Operation(summary = "获得库存盘点单")
@Parameter(name = "id", description = "编号", required = true, example = "1024") @Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('erp:stock-check:query')") @PreAuthorize("@ss.hasPermission('erp:stock-check:query')")
public CommonResult<ErpStockCheckRespVO> getStockCheck(@RequestParam("id") Long id) { public CommonResult<ErpStockCheckRespVO> getStockCheck(@RequestParam("id") Long id) {
@ -93,57 +159,89 @@ public class ErpStockCheckController {
if (stockCheck == null) { if (stockCheck == null) {
return success(null); return success(null);
} }
List<ErpStockCheckItemDO> stockCheckItemList = stockCheckService.getStockCheckItemListByCheckId(id); return success(buildStockCheckRespVO(stockCheck,
Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap( stockCheckService.getStockCheckItemListByCheckId(id),
convertSet(stockCheckItemList, ErpStockCheckItemDO::getProductId)); stockCheckService.getStockCheckApproveRecordList(id)));
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") @GetMapping("/page")
@Operation(summary = "获得库存调拨单分页") @Operation(summary = "获得库存盘点单分页")
@PreAuthorize("@ss.hasPermission('erp:stock-check:query')") @PreAuthorize("@ss.hasPermission('erp:stock-check:query')")
public CommonResult<PageResult<ErpStockCheckRespVO>> getStockCheckPage(@Valid ErpStockCheckPageReqVO pageReqVO) { public CommonResult<PageResult<ErpStockCheckRespVO>> getStockCheckPage(@Valid ErpStockCheckPageReqVO pageReqVO) {
PageResult<ErpStockCheckDO> pageResult = stockCheckService.getStockCheckPage(pageReqVO); return success(buildStockCheckVOPageResult(stockCheckService.getStockCheckPage(pageReqVO)));
return success(buildStockCheckVOPageResult(pageResult));
} }
@GetMapping("/export-excel") @GetMapping("/export-excel")
@Operation(summary = "导出库存调拨单 Excel") @Operation(summary = "导出库存盘点单 Excel")
@PreAuthorize("@ss.hasPermission('erp:stock-check:export')") @PreAuthorize("@ss.hasPermission('erp:stock-check:export')")
@ApiAccessLog(operateType = EXPORT) @ApiAccessLog(operateType = EXPORT)
public void exportStockCheckExcel(@Valid ErpStockCheckPageReqVO pageReqVO, public void exportStockCheckExcel(@Valid ErpStockCheckPageReqVO pageReqVO,
HttpServletResponse response) throws IOException { HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<ErpStockCheckRespVO> list = buildStockCheckVOPageResult(stockCheckService.getStockCheckPage(pageReqVO)).getList(); List<ErpStockCheckRespVO> list = buildStockCheckVOPageResult(stockCheckService.getStockCheckPage(pageReqVO)).getList();
// 导出 Excel ExcelUtils.write(response, "库存盘点单.xls", "数据", ErpStockCheckRespVO.class, list);
ExcelUtils.write(response, "库存调拨单.xls", "数据", ErpStockCheckRespVO.class, list);
} }
private PageResult<ErpStockCheckRespVO> buildStockCheckVOPageResult(PageResult<ErpStockCheckDO> pageResult) { private PageResult<ErpStockCheckRespVO> buildStockCheckVOPageResult(PageResult<ErpStockCheckDO> pageResult) {
if (CollUtil.isEmpty(pageResult.getList())) { if (CollUtil.isEmpty(pageResult.getList())) {
return PageResult.empty(pageResult.getTotal()); return PageResult.empty(pageResult.getTotal());
} }
// 1.1 盘点项
List<ErpStockCheckItemDO> stockCheckItemList = stockCheckService.getStockCheckItemListByCheckIds( List<ErpStockCheckItemDO> stockCheckItemList = stockCheckService.getStockCheckItemListByCheckIds(
convertSet(pageResult.getList(), ErpStockCheckDO::getId)); convertSet(pageResult.getList(), ErpStockCheckDO::getId));
Map<Long, List<ErpStockCheckItemDO>> stockCheckItemMap = convertMultiMap(stockCheckItemList, ErpStockCheckItemDO::getCheckId); Map<Long, List<ErpStockCheckItemDO>> stockCheckItemMap = convertMultiMap(stockCheckItemList, ErpStockCheckItemDO::getCheckId);
// 1.2 产品信息
Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(
convertSet(stockCheckItemList, ErpStockCheckItemDO::getProductId));
// 1.3 管理员信息
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(pageResult.getList(), stockCheck -> Long.parseLong(stockCheck.getCreator())));
// 2. 开始拼接
return BeanUtils.toBean(pageResult, ErpStockCheckRespVO.class, stockCheck -> { return BeanUtils.toBean(pageResult, ErpStockCheckRespVO.class, stockCheck -> {
stockCheck.setItems(BeanUtils.toBean(stockCheckItemMap.get(stockCheck.getId()), ErpStockCheckRespVO.Item.class, stockCheck.setItems(buildItemRespList(stockCheckItemMap.get(stockCheck.getId())));
item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName()) stockCheck.setProductNames(CollUtil.join(stockCheck.getItems(), ",", ErpStockCheckRespVO.Item::getProductName));
.setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName())))); fillUserInfo(stockCheck);
stockCheck.setProductNames(CollUtil.join(stockCheck.getItems(), "", ErpStockCheckRespVO.Item::getProductName)); });
MapUtils.findAndThen(userMap, Long.parseLong(stockCheck.getCreator()), user -> stockCheck.setCreatorName(user.getNickname())); }
private ErpStockCheckRespVO buildStockCheckRespVO(ErpStockCheckDO stockCheck, List<ErpStockCheckItemDO> stockCheckItemList,
List<ErpStockCheckApproveRecordDO> 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<ErpStockCheckRespVO.Item> buildItemRespList(List<ErpStockCheckItemDO> itemList) {
if (CollUtil.isEmpty(itemList)) {
return new ArrayList<>();
}
Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(convertSet(itemList, ErpStockCheckItemDO::getProductId));
Map<Long, ErpWarehouseDO> warehouseMap = warehouseService.getWarehouseMap(convertSet(itemList, ErpStockCheckItemDO::getWarehouseId));
Map<Long, WarehouseAreaDO> 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<Long> userIds = new ArrayList<>();
userIds.add(NumberUtils.parseLong(stockCheck.getCreator()));
userIds.add(stockCheck.getAuditUserId());
Map<Long, AdminUserRespDTO> 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<ErpStockCheckApproveRecordRespVO> buildApproveRecordRespList(List<ErpStockCheckApproveRecordDO> records) {
if (CollUtil.isEmpty(records)) {
return new ArrayList<>();
}
Map<Long, AdminUserRespDTO> 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()));
}); });
} }
} }

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

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

@ -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<Long> warehouseIds;
@Schema(description = "库区编号列表", example = "[11,12]")
private List<Long> areaIds;
@Schema(description = "是否允许为空,默认 false", example = "false")
private Boolean allowEmpty;
public boolean validSelection() {
return (warehouseIds != null && !warehouseIds.isEmpty()) || (areaIds != null && !areaIds.isEmpty());
}
}

@ -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<Long> productIds;
}

@ -44,6 +44,12 @@ public class ErpStockCheckRespVO {
@DictFormat(AUDIT_STATUS) @DictFormat(AUDIT_STATUS)
private Integer status; private Integer status;
@Schema(description = "审核人编号", example = "1888")
private Long auditUserId;
@Schema(description = "审核人名称", example = "李四")
private String auditUserName;
@Schema(description = "备注", example = "随便") @Schema(description = "备注", example = "随便")
@ExcelProperty("备注") @ExcelProperty("备注")
private String remark; private String remark;
@ -67,6 +73,9 @@ public class ErpStockCheckRespVO {
@ExcelProperty("产品信息") @ExcelProperty("产品信息")
private String productNames; private String productNames;
@Schema(description = "审核记录")
private List<ErpStockCheckApproveRecordRespVO> approveRecords;
@Data @Data
public static class Item { public static class Item {
@ -76,6 +85,15 @@ public class ErpStockCheckRespVO {
@Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113")
private Long warehouseId; 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") @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113")
private Long productId; private Long productId;
@ -91,7 +109,6 @@ public class ErpStockCheckRespVO {
private BigDecimal actualCount; private BigDecimal actualCount;
@Schema(description = "盈亏数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") @Schema(description = "盈亏数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
@NotNull(message = "盈亏数量不能为空")
private BigDecimal count; private BigDecimal count;
@Schema(description = "备注", example = "随便") @Schema(description = "备注", example = "随便")
@ -108,4 +125,4 @@ public class ErpStockCheckRespVO {
} }
} }

@ -10,15 +10,15 @@ import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
@Schema(description = "管理后台 - ERP 其它出库单新增/修改 Request VO") @Schema(description = "管理后台 - ERP 存盘点单新增/修改 Request VO")
@Data @Data
public class ErpStockCheckSaveReqVO { public class ErpStockCheckSaveReqVO {
@Schema(description = "出库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756") @Schema(description = "盘点编号", example = "11756")
private Long id; private Long id;
@Schema(description = "出库时间", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "盘点时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "出库时间不能为空") @NotNull(message = "盘点时间不能为空")
private LocalDateTime checkTime; private LocalDateTime checkTime;
@Schema(description = "备注", example = "随便") @Schema(description = "备注", example = "随便")
@ -27,21 +27,24 @@ public class ErpStockCheckSaveReqVO {
@Schema(description = "附件 URL", example = "https://www.iocoder.cn/1.doc") @Schema(description = "附件 URL", example = "https://www.iocoder.cn/1.doc")
private String fileUrl; private String fileUrl;
@Schema(description = "出库项列表", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "盘点项列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "出库项列表不能为空") @NotEmpty(message = "盘点项列表不能为空")
@Valid @Valid
private List<Item> items; private List<Item> items;
@Data @Data
public static class Item { public static class Item {
@Schema(description = "出库项编号", example = "11756") @Schema(description = "盘点项编号", example = "11756")
private Long id; private Long id;
@Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113")
@NotNull(message = "仓库编号不能为空") @NotNull(message = "仓库编号不能为空")
private Long warehouseId; private Long warehouseId;
@Schema(description = "库区编号", example = "1")
private Long areaId;
@Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113")
@NotNull(message = "产品编号不能为空") @NotNull(message = "产品编号不能为空")
private Long productId; private Long productId;
@ -57,8 +60,7 @@ public class ErpStockCheckSaveReqVO {
@NotNull(message = "实际数量不能为空") @NotNull(message = "实际数量不能为空")
private BigDecimal actualCount; private BigDecimal actualCount;
@Schema(description = "盈亏数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") @Schema(description = "盈亏数量,服务端自动按 实际数量 - 账面数量 计算", example = "100.00")
@NotNull(message = "盈亏数量不能为空")
private BigDecimal count; private BigDecimal count;
@Schema(description = "备注", example = "随便") @Schema(description = "备注", example = "随便")
@ -66,4 +68,4 @@ public class ErpStockCheckSaveReqVO {
} }
} }

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

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

@ -51,6 +51,10 @@ public class ErpStockCheckDO extends BaseDO {
* {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus} * {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}
*/ */
private Integer status; private Integer status;
/**
*
*/
private Long auditUserId;
/** /**
* *
*/ */
@ -60,4 +64,4 @@ public class ErpStockCheckDO extends BaseDO {
*/ */
private String fileUrl; private String fileUrl;
} }

@ -41,6 +41,14 @@ public class ErpStockCheckItemDO extends BaseDO {
* {@link ErpWarehouseDO#getId()} * {@link ErpWarehouseDO#getId()}
*/ */
private Long warehouseId; 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; private BigDecimal count;
/** /**
@ -80,4 +88,4 @@ public class ErpStockCheckItemDO extends BaseDO {
*/ */
private String remark; private String remark;
} }

@ -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<ErpStockCheckApproveRecordDO> {
default List<ErpStockCheckApproveRecordDO> selectListByStockCheckId(Long stockCheckId) {
return selectList(new LambdaQueryWrapperX<ErpStockCheckApproveRecordDO>()
.eq(ErpStockCheckApproveRecordDO::getStockCheckId, stockCheckId)
.orderByAsc(ErpStockCheckApproveRecordDO::getId));
}
default int deleteByStockCheckId(Long stockCheckId) {
return delete(ErpStockCheckApproveRecordDO::getStockCheckId, stockCheckId);
}
}

@ -75,6 +75,18 @@ public interface ErpStockMapper extends BaseMapperX<ErpStockDO> {
.eq("product_id", productId)); .eq("product_id", productId));
} }
default List<ErpStockDO> selectListByWarehouseIdsAndAreaIds(List<Long> warehouseIds, List<Long> areaIds) {
LambdaQueryWrapperX<ErpStockDO> query = new LambdaQueryWrapperX<ErpStockDO>()
.in(CollUtil.isNotEmpty(warehouseIds), ErpStockDO::getWarehouseId, warehouseIds)
.in(CollUtil.isNotEmpty(areaIds), ErpStockDO::getAreaId, areaIds);
return selectList(query);
}
default List<ErpStockDO> selectListByProductIds(List<Long> productIds) {
return selectList(new LambdaQueryWrapperX<ErpStockDO>()
.in(CollUtil.isNotEmpty(productIds), ErpStockDO::getProductId, productIds));
}
default int updateCountIncrement(Long id, BigDecimal count, boolean negativeEnable) { default int updateCountIncrement(Long id, BigDecimal count, boolean negativeEnable) {
LambdaUpdateWrapper<ErpStockDO> updateWrapper = new LambdaUpdateWrapper<ErpStockDO>() LambdaUpdateWrapper<ErpStockDO> updateWrapper = new LambdaUpdateWrapper<ErpStockDO>()
.eq(ErpStockDO::getId, id); .eq(ErpStockDO::getId, id);

@ -1,8 +1,14 @@
package cn.iocoder.yudao.module.erp.service.stock; package cn.iocoder.yudao.module.erp.service.stock;
import cn.iocoder.yudao.framework.common.pojo.PageResult; 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.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.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.ErpStockCheckDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO;
@ -40,6 +46,10 @@ public interface ErpStockCheckService {
*/ */
void updateStockCheckStatus(Long id, Integer status); void updateStockCheckStatus(Long id, Integer status);
void submitStockCheckAudit(@Valid ErpStockCheckSubmitReqVO submitReqVO);
void auditStockCheck(@Valid ErpStockCheckAuditReqVO auditReqVO);
/** /**
* *
* *
@ -81,4 +91,10 @@ public interface ErpStockCheckService {
*/ */
List<ErpStockCheckItemDO> getStockCheckItemListByCheckIds(Collection<Long> checkIds); List<ErpStockCheckItemDO> getStockCheckItemListByCheckIds(Collection<Long> checkIds);
} List<ErpStockCheckApproveRecordDO> getStockCheckApproveRecordList(Long stockCheckId);
List<ErpStockCheckRespVO.Item> generateStockCheckItemsByLocation(@Valid ErpStockCheckGenerateByLocationReqVO reqVO);
List<ErpStockCheckRespVO.Item> generateStockCheckItemsByProduct(@Valid ErpStockCheckGenerateByProductReqVO reqVO);
}

@ -2,42 +2,76 @@ package cn.iocoder.yudao.module.erp.service.stock;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; 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.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 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.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.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.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.ErpStockCheckDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockCheckItemDO; 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.ErpStockCheckItemMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockCheckMapper; 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.dal.redis.no.ErpNoRedisDAO;
import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; 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.enums.stock.ErpStockRecordBizTypeEnum;
import cn.iocoder.yudao.module.erp.service.product.ErpProductService; 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.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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; 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.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; 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 @Service
@Validated @Validated
public class ErpStockCheckServiceImpl implements ErpStockCheckService { public class ErpStockCheckServiceImpl implements ErpStockCheckService {
@ -46,36 +80,41 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService {
private ErpStockCheckMapper stockCheckMapper; private ErpStockCheckMapper stockCheckMapper;
@Resource @Resource
private ErpStockCheckItemMapper stockCheckItemMapper; private ErpStockCheckItemMapper stockCheckItemMapper;
@Resource
private ErpStockCheckApproveRecordMapper stockCheckApproveRecordMapper;
@Resource
private ErpStockMapper stockMapper;
@Resource @Resource
private ErpNoRedisDAO noRedisDAO; private ErpNoRedisDAO noRedisDAO;
@Resource @Resource
private ErpProductService productService; private ErpProductService productService;
@Resource @Resource
private ErpWarehouseService warehouseService; private ErpWarehouseService warehouseService;
@Resource @Resource
private WarehouseAreaService warehouseAreaService;
@Resource
private ErpStockRecordService stockRecordService; private ErpStockRecordService stockRecordService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private PermissionApi permissionApi;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Long createStockCheck(ErpStockCheckSaveReqVO createReqVO) { public Long createStockCheck(ErpStockCheckSaveReqVO createReqVO) {
// 1.1 校验盘点项的有效性
List<ErpStockCheckItemDO> stockCheckItems = validateStockCheckItems(createReqVO.getItems()); List<ErpStockCheckItemDO> stockCheckItems = validateStockCheckItems(createReqVO.getItems());
// 1.2 生成盘点单号,并校验唯一性
String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_CHECK_NO_PREFIX); String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_CHECK_NO_PREFIX);
if (stockCheckMapper.selectByNo(no) != null) { if (stockCheckMapper.selectByNo(no) != null) {
throw exception(STOCK_CHECK_NO_EXISTS); throw exception(STOCK_CHECK_NO_EXISTS);
} }
// 2.1 插入盘点单
ErpStockCheckDO stockCheck = BeanUtils.toBean(createReqVO, ErpStockCheckDO.class, in -> in ErpStockCheckDO stockCheck = BeanUtils.toBean(createReqVO, ErpStockCheckDO.class, in -> in
.setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()) .setNo(no)
.setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add)) .setStatus(ErpAuditStatus.DRAFT.getStatus())
.setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add, BigDecimal.ZERO))
.setTotalPrice(getSumValue(stockCheckItems, ErpStockCheckItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO))); .setTotalPrice(getSumValue(stockCheckItems, ErpStockCheckItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)));
stockCheckMapper.insert(stockCheck); stockCheckMapper.insert(stockCheck);
// 2.2 插入盘点单项 stockCheckItems.forEach(item -> item.setCheckId(stockCheck.getId()));
stockCheckItems.forEach(o -> o.setCheckId(stockCheck.getId()));
stockCheckItemMapper.insertBatch(stockCheckItems); stockCheckItemMapper.insertBatch(stockCheckItems);
return stockCheck.getId(); return stockCheck.getId();
} }
@ -83,101 +122,119 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateStockCheck(ErpStockCheckSaveReqVO updateReqVO) { public void updateStockCheck(ErpStockCheckSaveReqVO updateReqVO) {
// 1.1 校验存在
ErpStockCheckDO stockCheck = validateStockCheckExists(updateReqVO.getId()); ErpStockCheckDO stockCheck = validateStockCheckExists(updateReqVO.getId());
if (ErpAuditStatus.APPROVE.getStatus().equals(stockCheck.getStatus())) { if (ErpAuditStatus.APPROVE.getStatus().equals(stockCheck.getStatus())) {
throw exception(STOCK_CHECK_UPDATE_FAIL_APPROVE, stockCheck.getNo()); throw exception(STOCK_CHECK_UPDATE_FAIL_APPROVE, stockCheck.getNo());
} }
// 1.2 校验盘点项的有效性 if (ErpAuditStatus.PROCESS.getStatus().equals(stockCheck.getStatus())) {
List<ErpStockCheckItemDO> stockCheckItems = validateStockCheckItems(updateReqVO.getItems()); throw exception(STOCK_CHECK_UPDATE_FAIL_PROCESS, stockCheck.getNo());
}
// 2.1 更新盘点单 List<ErpStockCheckItemDO> stockCheckItems = validateStockCheckItems(updateReqVO.getItems());
ErpStockCheckDO updateObj = BeanUtils.toBean(updateReqVO, ErpStockCheckDO.class, in -> in ErpStockCheckDO updateObj = BeanUtils.toBean(updateReqVO, ErpStockCheckDO.class, in -> in
.setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add)) .setTotalCount(getSumValue(stockCheckItems, ErpStockCheckItemDO::getCount, BigDecimal::add, BigDecimal.ZERO))
.setTotalPrice(getSumValue(stockCheckItems, ErpStockCheckItemDO::getTotalPrice, BigDecimal::add))); .setTotalPrice(getSumValue(stockCheckItems, ErpStockCheckItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)));
stockCheckMapper.updateById(updateObj); stockCheckMapper.updateById(updateObj);
// 2.2 更新盘点单项
updateStockCheckItemList(updateReqVO.getId(), stockCheckItems); updateStockCheckItemList(updateReqVO.getId(), stockCheckItems);
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateStockCheckStatus(Long id, Integer status) { public void updateStockCheckStatus(Long id, Integer status) {
boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
// 1.1 校验存在
ErpStockCheckDO stockCheck = validateStockCheckExists(id); ErpStockCheckDO stockCheck = validateStockCheckExists(id);
// 1.2 校验状态 boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
if (stockCheck.getStatus().equals(status)) { if (approve) {
throw exception(approve ? STOCK_CHECK_APPROVE_FAIL : STOCK_CHECK_PROCESS_FAIL); 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) { if (updateCount == 0) {
throw exception(approve ? STOCK_CHECK_APPROVE_FAIL : STOCK_CHECK_PROCESS_FAIL); throw exception(approve ? STOCK_CHECK_APPROVE_FAIL : STOCK_CHECK_PROCESS_FAIL);
} }
// 3. 变更库存
List<ErpStockCheckItemDO> stockCheckItems = stockCheckItemMapper.selectListByCheckId(id); List<ErpStockCheckItemDO> stockCheckItems = stockCheckItemMapper.selectListByCheckId(id);
stockCheckItems.forEach(stockCheckItem -> { if (approve) {
// 没有盈亏,不用出入库 applyStockCheckEffect(stockCheck, stockCheckItems);
if (stockCheckItem.getCount().compareTo(BigDecimal.ZERO) == 0) { } else {
return; cancelStockCheckEffect(stockCheck, stockCheckItems);
} }
// 1012-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()));
});
} }
private List<ErpStockCheckItemDO> validateStockCheckItems(List<ErpStockCheckSaveReqVO.Item> list) { @Override
// 1.1 校验产品存在 @Transactional(rollbackFor = Exception.class)
List<ErpProductDO> productList = productService.validProductList( public void submitStockCheckAudit(ErpStockCheckSubmitReqVO submitReqVO) {
convertSet(list, ErpStockCheckSaveReqVO.Item::getProductId)); ErpStockCheckDO stockCheck = validateStockCheckExists(submitReqVO.getId());
Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId); Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
// 1.2 校验仓库存在 if (!Objects.equals(NumberUtils.parseLong(stockCheck.getCreator()), loginUserId)) {
warehouseService.validWarehouseList(convertSet(list, ErpStockCheckSaveReqVO.Item::getWarehouseId)); throw exception(STOCK_CHECK_SUBMIT_FAIL_USER);
// 2. 转化为 ErpStockCheckItemDO 列表 }
return convertList(list, o -> BeanUtils.toBean(o, ErpStockCheckItemDO.class, item -> item if (!ErpAuditStatus.DRAFT.getStatus().equals(stockCheck.getStatus())
.setProductUnitId(productMap.get(item.getProductId()).getUnitId()) && !ErpAuditStatus.UN_APPROVE.getStatus().equals(stockCheck.getStatus())) {
.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())))); 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<ErpStockCheckItemDO> newList) { @Override
// 第一步,对比新老数据,获得添加、修改、删除的列表 @Transactional(rollbackFor = Exception.class)
List<ErpStockCheckItemDO> oldList = stockCheckItemMapper.selectListByCheckId(id); public void auditStockCheck(ErpStockCheckAuditReqVO auditReqVO) {
List<List<ErpStockCheckItemDO>> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 ErpStockCheckDO stockCheck = validateStockCheckExists(auditReqVO.getId());
(oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); if (!ErpAuditStatus.PROCESS.getStatus().equals(stockCheck.getStatus())) {
throw exception(STOCK_CHECK_AUDIT_FAIL_STATUS);
}
// 第二步,批量添加、修改、删除 Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
if (CollUtil.isNotEmpty(diffList.get(0))) { boolean adminCanAudit = permissionApi.hasAnyRoles(loginUserId, RoleCodeEnum.SUPER_ADMIN.getCode());
diffList.get(0).forEach(o -> o.setCheckId(id)); if (!Objects.equals(stockCheck.getAuditUserId(), loginUserId) && !adminCanAudit) {
stockCheckItemMapper.insertBatch(diffList.get(0)); throw exception(STOCK_CHECK_AUDIT_FAIL_USER);
} }
if (CollUtil.isNotEmpty(diffList.get(1))) { if (!ErpAuditStatus.APPROVE.getStatus().equals(auditReqVO.getStatus())
stockCheckItemMapper.updateBatch(diffList.get(1)); && !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<ErpStockCheckItemDO> 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 @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void deleteStockCheck(List<Long> ids) { public void deleteStockCheck(List<Long> ids) {
// 1. 校验不处于已审批
List<ErpStockCheckDO> stockChecks = stockCheckMapper.selectBatchIds(ids); List<ErpStockCheckDO> stockChecks = stockCheckMapper.selectBatchIds(ids);
if (CollUtil.isEmpty(stockChecks)) { if (CollUtil.isEmpty(stockChecks)) {
return; return;
@ -186,25 +243,18 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService {
if (ErpAuditStatus.APPROVE.getStatus().equals(stockCheck.getStatus())) { if (ErpAuditStatus.APPROVE.getStatus().equals(stockCheck.getStatus())) {
throw exception(STOCK_CHECK_DELETE_FAIL_APPROVE, stockCheck.getNo()); 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 -> { stockChecks.forEach(stockCheck -> {
// 2.1 删除盘点单
stockCheckMapper.deleteById(stockCheck.getId()); stockCheckMapper.deleteById(stockCheck.getId());
// 2.2 删除盘点单项
stockCheckItemMapper.deleteByCheckId(stockCheck.getId()); 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 @Override
public ErpStockCheckDO getStockCheck(Long id) { public ErpStockCheckDO getStockCheck(Long id) {
return stockCheckMapper.selectById(id); return stockCheckMapper.selectById(id);
@ -215,8 +265,6 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService {
return stockCheckMapper.selectPage(pageReqVO); return stockCheckMapper.selectPage(pageReqVO);
} }
// ==================== 盘点项 ====================
@Override @Override
public List<ErpStockCheckItemDO> getStockCheckItemListByCheckId(Long checkId) { public List<ErpStockCheckItemDO> getStockCheckItemListByCheckId(Long checkId) {
return stockCheckItemMapper.selectListByCheckId(checkId); return stockCheckItemMapper.selectListByCheckId(checkId);
@ -230,4 +278,210 @@ public class ErpStockCheckServiceImpl implements ErpStockCheckService {
return stockCheckItemMapper.selectListByCheckIds(checkIds); return stockCheckItemMapper.selectListByCheckIds(checkIds);
} }
} @Override
public List<ErpStockCheckApproveRecordDO> getStockCheckApproveRecordList(Long stockCheckId) {
return stockCheckApproveRecordMapper.selectListByStockCheckId(stockCheckId);
}
@Override
public List<ErpStockCheckRespVO.Item> generateStockCheckItemsByLocation(ErpStockCheckGenerateByLocationReqVO reqVO) {
if (reqVO == null || !reqVO.validSelection()) {
return Collections.emptyList();
}
List<ErpStockDO> stockList = stockMapper.selectListByWarehouseIdsAndAreaIds(reqVO.getWarehouseIds(), reqVO.getAreaIds());
if (!Boolean.TRUE.equals(reqVO.getAllowEmpty())) {
stockList = filterNonZeroStocks(stockList);
}
return buildPreviewItems(stockList);
}
@Override
public List<ErpStockCheckRespVO.Item> generateStockCheckItemsByProduct(ErpStockCheckGenerateByProductReqVO reqVO) {
if (reqVO == null || CollUtil.isEmpty(reqVO.getProductIds())) {
return Collections.emptyList();
}
return buildPreviewItems(filterNonZeroStocks(stockMapper.selectListByProductIds(reqVO.getProductIds())));
}
private List<ErpStockDO> filterNonZeroStocks(List<ErpStockDO> stockList) {
if (CollUtil.isEmpty(stockList)) {
return Collections.emptyList();
}
List<ErpStockDO> 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<ErpStockCheckRespVO.Item> buildPreviewItems(List<ErpStockDO> stockList) {
if (CollUtil.isEmpty(stockList)) {
return Collections.emptyList();
}
Map<Long, ErpProductDO> productMap = productService.getProductMap(convertSet(stockList, ErpStockDO::getProductId));
Map<Long, ErpWarehouseDO> warehouseMap = warehouseService.getWarehouseMap(convertSet(stockList, ErpStockDO::getWarehouseId));
Map<Long, WarehouseAreaDO> areaMap = warehouseAreaService.getWarehouseAreaMap(convertSet(stockList, ErpStockDO::getAreaId));
List<ErpStockCheckRespVO.Item> 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<ErpStockCheckItemDO> stockCheckItems) {
operateStockCheckEffect(stockCheck, stockCheckItems, true);
}
private void cancelStockCheckEffect(ErpStockCheckDO stockCheck, List<ErpStockCheckItemDO> stockCheckItems) {
operateStockCheckEffect(stockCheck, stockCheckItems, false);
}
private void operateStockCheckEffect(ErpStockCheckDO stockCheck, List<ErpStockCheckItemDO> 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<ErpStockCheckItemDO> validateStockCheckItems(List<ErpStockCheckSaveReqVO.Item> list) {
List<ErpProductDO> productList = productService.validProductList(convertSet(list, ErpStockCheckSaveReqVO.Item::getProductId));
Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId);
Map<Long, ErpWarehouseDO> warehouseMap = convertMap(
warehouseService.validWarehouseList(convertSet(list, ErpStockCheckSaveReqVO.Item::getWarehouseId)),
ErpWarehouseDO::getId);
Map<Long, WarehouseAreaDO> 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<ErpStockCheckItemDO> newList) {
List<ErpStockCheckItemDO> oldList = stockCheckItemMapper.selectListByCheckId(id);
List<List<ErpStockCheckItemDO>> 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;
}
}

Loading…
Cancel
Save