From 55426245a1ff84a8ce67ce33e6b53f4f38f720f7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 12 May 2026 21:08:36 +0800 Subject: [PATCH] feat(wms):优化 receipt order 实体的实现(增加 code review) --- yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderController.java | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderDetailController.java | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailRespVO.java | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailSaveReqVO.java | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderPageReqVO.java | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderRespVO.java | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderSaveReqVO.java | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/receipt/WmsReceiptOrderDO.java | 2 +- yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderDetailMapper.java | 34 ++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderMapper.java | 42 ++++++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/ErrorCodeConstants.java | 2 ++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsReceiptOrderStatusEnum.java | 38 ++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsReceiptOrderTypeEnum.java | 38 ++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailService.java | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImpl.java | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderService.java | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImpl.java | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImplTest.java | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImplTest.java | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 19 files changed, 1535 insertions(+), 1 deletion(-) create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderController.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderDetailController.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailRespVO.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailSaveReqVO.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderPageReqVO.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderRespVO.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderSaveReqVO.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderDetailMapper.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderMapper.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsReceiptOrderStatusEnum.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsReceiptOrderTypeEnum.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailService.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImpl.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderService.java create mode 100644 yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImpl.java create mode 100644 yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImplTest.java create mode 100644 yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImplTest.java diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderController.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderController.java new file mode 100644 index 0000000..ade0757 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderController.java @@ -0,0 +1,220 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.NumberUtil; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail.WmsReceiptOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.merchant.WmsMerchantDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.receipt.WmsReceiptOrderDetailService; +import cn.iocoder.yudao.module.wms.service.order.receipt.WmsReceiptOrderService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; + +@Tag(name = "管理后台 - WMS 入库单") +@RestController +@RequestMapping("/wms/receipt-order") +@Validated +public class WmsReceiptOrderController { + + @Resource + private WmsReceiptOrderService receiptOrderService; + @Resource + private WmsReceiptOrderDetailService receiptOrderDetailService; + @Resource + private WmsMerchantService merchantService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsWarehouseAreaService warehouseAreaService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + + @Resource + private WmsProperties wmsProperties; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建入库单") + @PreAuthorize("@ss.hasPermission('wms:receipt-order:create')") + public CommonResult createReceiptOrder(@Valid @RequestBody WmsReceiptOrderSaveReqVO createReqVO) { + return success(receiptOrderService.createReceiptOrder(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新入库单") + @PreAuthorize("@ss.hasPermission('wms:receipt-order:update')") + public CommonResult updateReceiptOrder(@Valid @RequestBody WmsReceiptOrderSaveReqVO updateReqVO) { + receiptOrderService.updateReceiptOrder(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除入库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:receipt-order:delete')") + public CommonResult deleteReceiptOrder(@RequestParam("id") Long id) { + receiptOrderService.deleteReceiptOrder(id); + return success(true); + } + + @PutMapping("/complete") + @Operation(summary = "完成入库") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:receipt-order:complete')") + public CommonResult completeReceiptOrder(@RequestParam("id") Long id) { + receiptOrderService.completeReceiptOrder(id); + return success(true); + } + + @PutMapping("/cancel") + @Operation(summary = "作废入库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:receipt-order:cancel')") + public CommonResult cancelReceiptOrder(@RequestParam("id") Long id) { + receiptOrderService.cancelReceiptOrder(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得入库单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:receipt-order:query')") + public CommonResult getReceiptOrder(@RequestParam("id") Long id) { + WmsReceiptOrderDO order = receiptOrderService.getReceiptOrder(id); + if (order == null) { + return success(null); + } + // 获得入库单的明细列表 + List detailList = receiptOrderDetailService.getReceiptOrderDetailList(id); + // 拼接结果返回 + WmsReceiptOrderRespVO respVO = buildReceiptOrderRespVO(order) + .setDetails(buildReceiptOrderDetailRespVOList(detailList)); + return success(respVO); + } + + @GetMapping("/page") + @Operation(summary = "获得入库单分页") + @PreAuthorize("@ss.hasPermission('wms:receipt-order:query')") + public CommonResult> getReceiptOrderPage(@Valid WmsReceiptOrderPageReqVO pageReqVO) { + PageResult pageResult = receiptOrderService.getReceiptOrderPage(pageReqVO); + return success(new PageResult<>(buildReceiptOrderRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出入库单 Excel") + @PreAuthorize("@ss.hasPermission('wms:receipt-order:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportReceiptOrderExcel(@Valid WmsReceiptOrderPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = receiptOrderService.getReceiptOrderPage(pageReqVO).getList(); + ExcelUtils.write(response, "入库单.xls", "数据", WmsReceiptOrderRespVO.class, + buildReceiptOrderRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private WmsReceiptOrderRespVO buildReceiptOrderRespVO(WmsReceiptOrderDO order) { + if (order == null) { + return null; + } + List list = buildReceiptOrderRespVOList(List.of(order)); + return CollUtil.getFirst(list); + } + + private List buildReceiptOrderRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的商户、仓库、库区、用户等数据 + Map merchantMap = merchantService.getMerchantMap(convertSet(list, WmsReceiptOrderDO::getMerchantId)); + Map warehouseMap = warehouseService.getWarehouseMap(convertSet(list, WmsReceiptOrderDO::getWarehouseId)); + Map areaMap = wmsProperties.isAreaEnabled() + ? warehouseAreaService.getWarehouseAreaMap(convertSet(list, WmsReceiptOrderDO::getAreaId)) + : Collections.emptyMap(); + Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(list, + order -> Stream.of(parseUserId(order.getCreator()), parseUserId(order.getUpdater())))); + // 拼接数据 + return BeanUtils.toBean(list, WmsReceiptOrderRespVO.class, vo -> { + MapUtils.findAndThen(merchantMap, vo.getMerchantId(), merchant -> vo.setMerchantName(merchant.getName())); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + MapUtils.findAndThen(areaMap, vo.getAreaId(), area -> vo.setAreaName(area.getName())); + MapUtils.findAndThen(userMap, parseUserId(vo.getCreator()), user -> vo.setCreatorName(user.getNickname())); + MapUtils.findAndThen(userMap, parseUserId(vo.getUpdater()), user -> vo.setUpdaterName(user.getNickname())); + }); + } + + private Long parseUserId(String userId) { + return NumberUtil.parseLong(userId, null); + } + + private List buildReceiptOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的商品、SKU、仓库、库区等数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsReceiptOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(list, WmsReceiptOrderDetailDO::getWarehouseId)); + Map areaMap = wmsProperties.isAreaEnabled() + ? warehouseAreaService.getWarehouseAreaMap(convertSet(list, WmsReceiptOrderDetailDO::getAreaId)) + : Collections.emptyMap(); + // 拼接数据 + return BeanUtils.toBean(list, WmsReceiptOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + MapUtils.findAndThen(areaMap, vo.getAreaId(), area -> vo.setAreaName(area.getName())); + }); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderDetailController.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderDetailController.java new file mode 100644 index 0000000..09e8345 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/WmsReceiptOrderDetailController.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail.WmsReceiptOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.receipt.WmsReceiptOrderDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - WMS 入库单明细") +@RestController +@RequestMapping("/wms/receipt-order-detail") +@Validated +public class WmsReceiptOrderDetailController { + + @Resource + private WmsReceiptOrderDetailService receiptOrderDetailService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsWarehouseAreaService warehouseAreaService; + + @Resource + private WmsProperties wmsProperties; + + @GetMapping("/list-by-order-id") + @Operation(summary = "获得入库单明细列表") + @Parameter(name = "orderId", description = "入库单编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:receipt-order:query')") + public CommonResult> getReceiptOrderDetailListByOrderId( + @RequestParam("orderId") Long orderId) { + List list = receiptOrderDetailService.getReceiptOrderDetailList(orderId); + return success(buildReceiptOrderDetailRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private List buildReceiptOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 查询关联数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsReceiptOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(list, WmsReceiptOrderDetailDO::getWarehouseId)); + Map areaMap = wmsProperties.isAreaEnabled() + ? warehouseAreaService.getWarehouseAreaMap(convertSet(list, WmsReceiptOrderDetailDO::getAreaId)) + : Collections.emptyMap(); + // 拼接数据 + return BeanUtils.toBean(list, WmsReceiptOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + MapUtils.findAndThen(areaMap, vo.getAreaId(), area -> vo.setAreaName(area.getName())); + }); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailRespVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailRespVO.java new file mode 100644 index 0000000..ca9f4a8 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailRespVO.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 入库单明细 Response VO") +@Data +public class WmsReceiptOrderDetailRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "入库单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long orderId; + + @Schema(description = "商品编号", example = "2048") + private Long itemId; + + @Schema(description = "商品编号", example = "SPU-APPLE") + private String itemCode; + + @Schema(description = "商品名称", example = "红富士苹果") + private String itemName; + + @Schema(description = "商品单位", example = "箱") + private String unit; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long skuId; + + @Schema(description = "规格编号", example = "SKU-APPLE-10KG") + private String skuCode; + + @Schema(description = "规格名称", example = "10kg 箱装") + private String skuName; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long warehouseId; + + @Schema(description = "仓库名称", example = "北京仓") + private String warehouseName; + + @Schema(description = "库区编号", example = "1024") + private Long areaId; + + @Schema(description = "库区名称", example = "北京库区01") + private String areaName; + + @Schema(description = "批号", example = "B202605110001") + private String batchNo; + + @Schema(description = "生产日期") + private LocalDateTime productionDate; + + @Schema(description = "过期日期") + private LocalDateTime expirationDate; + + @Schema(description = "入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal quantity; + + @Schema(description = "金额", example = "1000.00") + private BigDecimal amount; + + @Schema(description = "备注", example = "备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailSaveReqVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailSaveReqVO.java new file mode 100644 index 0000000..7007398 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/detail/WmsReceiptOrderDetailSaveReqVO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 入库单明细保存 Request VO") +@Data +public class WmsReceiptOrderDetailSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "SKU 不能为空") + private Long skuId; + + @Schema(description = "库区编号", example = "1024") + private Long areaId; + + @Schema(description = "批号", example = "B202605110001") + @Size(max = 64, message = "批号长度不能超过 64 个字符") + private String batchNo; + + @Schema(description = "生产日期") + private LocalDateTime productionDate; + + @Schema(description = "过期日期") + private LocalDateTime expirationDate; + + @Schema(description = "入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "入库数量不能为空") + @DecimalMin(value = "0", inclusive = false, message = "入库数量必须大于 0") + private BigDecimal quantity; + + @Schema(description = "金额", example = "1000.00") + @DecimalMin(value = "0", message = "金额不能小于 0") + private BigDecimal amount; + + @Schema(description = "备注", example = "备注") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderPageReqVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderPageReqVO.java new file mode 100644 index 0000000..e6db2a8 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderPageReqVO.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsReceiptOrderStatusEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsReceiptOrderTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - WMS 入库单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsReceiptOrderPageReqVO extends PageParam { + + @Schema(description = "入库单号", example = "RK202605110001") + private String no; + + @Schema(description = "单据状态", example = "0") + @InEnum(WmsReceiptOrderStatusEnum.class) + private Integer status; + + @Schema(description = "仓库编号", example = "1024") + private Long warehouseId; + + @Schema(description = "供应商编号", example = "1024") + private Long merchantId; + + @Schema(description = "单据日期") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] orderDate; + + @Schema(description = "最小数量", example = "1.00") + private BigDecimal totalQuantityMin; + + @Schema(description = "最大数量", example = "100.00") + private BigDecimal totalQuantityMax; + + @Schema(description = "最小总金额", example = "1.00") + private BigDecimal totalAmountMin; + + @Schema(description = "最大总金额", example = "1000.00") + private BigDecimal totalAmountMax; + + @Schema(description = "入库类型", example = "1") + @InEnum(WmsReceiptOrderTypeEnum.class) + private Integer type; + + @Schema(description = "业务单号", example = "PO202605110001") + private String bizOrderNo; + + @Schema(description = "创建用户", example = "1") + private String creator; + + @Schema(description = "更新用户", example = "1") + private String updater; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "更新时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] updateTime; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderRespVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderRespVO.java new file mode 100644 index 0000000..29412dd --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderRespVO.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail.WmsReceiptOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - WMS 入库单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class WmsReceiptOrderRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "入库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "RK202605110001") + @ExcelProperty("入库单号") + private String no; + + @Schema(description = "入库类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "入库类型", converter = DictConvert.class) + @DictFormat(DictTypeConstants.RECEIPT_ORDER_TYPE) + private Integer type; + + @Schema(description = "入库状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @ExcelProperty(value = "入库状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.RECEIPT_ORDER_STATUS) + private Integer status; + + @Schema(description = "业务单号", example = "PO202605110001") + @ExcelProperty("业务单号") + private String bizOrderNo; + + @Schema(description = "供应商编号", example = "1024") + private Long merchantId; + + @Schema(description = "供应商名称", example = "某某公司") + @ExcelProperty("供应商") + private String merchantName; + + @Schema(description = "备注", example = "备注") + private String remark; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long warehouseId; + + @Schema(description = "仓库名称", example = "北京仓") + @ExcelProperty("仓库") + private String warehouseName; + + @Schema(description = "库区编号", example = "1024") + private Long areaId; + + @Schema(description = "库区名称", example = "北京库区01") + private String areaName; + + @Schema(description = "入库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @ExcelProperty("入库数量") + private BigDecimal totalQuantity; + + @Schema(description = "总金额", example = "1000.00") + @ExcelProperty("总金额") + private BigDecimal totalAmount; + + @Schema(description = "入库明细") + private List details; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "创建者", example = "1") + private String creator; + @Schema(description = "创建者名称", example = "芋道") + private String creatorName; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + + @Schema(description = "更新者", example = "1") + private String updater; + @Schema(description = "更新者名称", example = "芋道") + private String updaterName; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderSaveReqVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderSaveReqVO.java new file mode 100644 index 0000000..c9e854e --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/receipt/vo/order/WmsReceiptOrderSaveReqVO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.detail.WmsReceiptOrderDetailSaveReqVO; +import cn.iocoder.yudao.module.wms.enums.order.WmsReceiptOrderTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Schema(description = "管理后台 - WMS 入库单保存 Request VO") +@Data +public class WmsReceiptOrderSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "入库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "RK202605110001") + @NotBlank(message = "入库单号不能为空") + @Size(max = 64, message = "入库单号长度不能超过 64 个字符") + private String no; + + @Schema(description = "入库类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "入库类型不能为空") + @InEnum(WmsReceiptOrderTypeEnum.class) + private Integer type; + + @Schema(description = "业务单号", example = "PO202605110001") + @Size(max = 64, message = "业务单号长度不能超过 64 个字符") + private String bizOrderNo; + + @Schema(description = "供应商编号", example = "1024") + private Long merchantId; + + @Schema(description = "备注", example = "备注") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "仓库不能为空") + private Long warehouseId; + + @Schema(description = "库区编号", example = "1024") + private Long areaId; + + @Schema(description = "入库数量", example = "100.00") + private BigDecimal totalQuantity; + + @Schema(description = "总金额", example = "1000.00") + private BigDecimal totalAmount; + + @Schema(description = "入库明细") + @Valid + private List details; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/receipt/WmsReceiptOrderDO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/receipt/WmsReceiptOrderDO.java index 328c8e7..3a66f14 100644 --- a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/receipt/WmsReceiptOrderDO.java +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/receipt/WmsReceiptOrderDO.java @@ -53,7 +53,7 @@ public class WmsReceiptOrderDO extends BaseDO { */ private String bizOrderNo; /** - * 往来企业编号 + * 供应商编号 * * 关联 {@link WmsMerchantDO#getId()} */ diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderDetailMapper.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderDetailMapper.java new file mode 100644 index 0000000..aac5901 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderDetailMapper.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.receipt; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 入库单明细 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsReceiptOrderDetailMapper extends BaseMapperX { + + default List selectListByOrderId(Long orderId) { + return selectList(WmsReceiptOrderDetailDO::getOrderId, orderId); + } + + default List selectListByOrderIds(Collection orderIds) { + return selectList(new LambdaQueryWrapperX() + .inIfPresent(WmsReceiptOrderDetailDO::getOrderId, orderIds) + .orderByAsc(WmsReceiptOrderDetailDO::getOrderId) + .orderByAsc(WmsReceiptOrderDetailDO::getId)); + } + + default void deleteByOrderId(Long orderId) { + delete(WmsReceiptOrderDetailDO::getOrderId, orderId); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderMapper.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderMapper.java new file mode 100644 index 0000000..ce46fba --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/receipt/WmsReceiptOrderMapper.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.receipt; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * WMS 入库单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsReceiptOrderMapper extends BaseMapperX { + + default PageResult selectPage(WmsReceiptOrderPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(WmsReceiptOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(WmsReceiptOrderDO::getStatus, reqVO.getStatus()) + .eqIfPresent(WmsReceiptOrderDO::getWarehouseId, reqVO.getWarehouseId()) + .eqIfPresent(WmsReceiptOrderDO::getMerchantId, reqVO.getMerchantId()) + .betweenIfPresent(WmsReceiptOrderDO::getCreateTime, reqVO.getOrderDate()) + .geIfPresent(WmsReceiptOrderDO::getTotalQuantity, reqVO.getTotalQuantityMin()) + .leIfPresent(WmsReceiptOrderDO::getTotalQuantity, reqVO.getTotalQuantityMax()) + .geIfPresent(WmsReceiptOrderDO::getTotalAmount, reqVO.getTotalAmountMin()) + .leIfPresent(WmsReceiptOrderDO::getTotalAmount, reqVO.getTotalAmountMax()) + .eqIfPresent(WmsReceiptOrderDO::getType, reqVO.getType()) + .likeIfPresent(WmsReceiptOrderDO::getBizOrderNo, reqVO.getBizOrderNo()) + .eqIfPresent(WmsReceiptOrderDO::getCreator, reqVO.getCreator()) + .eqIfPresent(WmsReceiptOrderDO::getUpdater, reqVO.getUpdater()) + .betweenIfPresent(WmsReceiptOrderDO::getCreateTime, reqVO.getCreateTime()) + .betweenIfPresent(WmsReceiptOrderDO::getUpdateTime, reqVO.getUpdateTime()) + .orderByDesc(WmsReceiptOrderDO::getId)); + } + + default WmsReceiptOrderDO selectByNo(String no) { + return selectOne(WmsReceiptOrderDO::getNo, no); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/ErrorCodeConstants.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/ErrorCodeConstants.java index 50a9a17..af999bd 100644 --- a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/ErrorCodeConstants.java +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/ErrorCodeConstants.java @@ -55,5 +55,7 @@ public interface ErrorCodeConstants { ErrorCode RECEIPT_ORDER_AREA_REQUIRED = new ErrorCode(1_060_200_004, "库区模式下,入库单库区不能为空"); ErrorCode RECEIPT_ORDER_AREA_NOT_MATCH_WAREHOUSE = new ErrorCode(1_060_200_005, "库区不属于入库仓库"); ErrorCode RECEIPT_ORDER_COMPLETED_CANNOT_DELETE = new ErrorCode(1_060_200_006, "入库单已完成,不能删除"); + ErrorCode RECEIPT_ORDER_DETAIL_NOT_EXISTS = new ErrorCode(1_060_200_007, "入库单明细不存在"); + ErrorCode RECEIPT_ORDER_MERCHANT_NOT_SUPPLIER = new ErrorCode(1_060_200_008, "入库单供应商必须选择供应商或客户/供应商类型的往来企业"); } diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsReceiptOrderStatusEnum.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsReceiptOrderStatusEnum.java new file mode 100644 index 0000000..4548b84 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsReceiptOrderStatusEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.wms.enums.order; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * WMS 入库单状态枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum WmsReceiptOrderStatusEnum implements ArrayValuable { + + CANCELED(-1, "作废"), + PENDING(0, "暂存"), + COMPLETED(1, "完成入库"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(WmsReceiptOrderStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * 状态 + */ + private final Integer status; + /** + * 名称 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsReceiptOrderTypeEnum.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsReceiptOrderTypeEnum.java new file mode 100644 index 0000000..cd6e724 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsReceiptOrderTypeEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.wms.enums.order; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * WMS 入库单类型枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum WmsReceiptOrderTypeEnum implements ArrayValuable { + + PURCHASE(1, "采购入库"), + OUTSOURCING(2, "外协入库"), + RETURN(3, "退货入库"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(WmsReceiptOrderTypeEnum::getType).toArray(Integer[]::new); + + /** + * 类型 + */ + private final Integer type; + /** + * 名称 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailService.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailService.java new file mode 100644 index 0000000..fc1c2db --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailService.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.wms.service.order.receipt; + +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 入库单明细 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsReceiptOrderDetailService { + + /** + * 创建入库单明细列表 + * + * @param orderId 入库单编号 + * @param list 明细列表 + */ + void createReceiptOrderDetailList(Long orderId, List list); + + /** + * 更新入库单明细列表 + * + * @param orderId 入库单编号 + * @param list 明细列表 + */ + void updateReceiptOrderDetailList(Long orderId, List list); + + /** + * 按入库单编号删除明细列表 + * + * @param orderId 入库单编号 + */ + void deleteReceiptOrderDetailListByOrderId(Long orderId); + + /** + * 按入库单编号获得明细列表 + * + * @param orderId 入库单编号 + * @return 明细列表 + */ + List getReceiptOrderDetailList(Long orderId); + + /** + * 按入库单编号集合获得明细列表 + * + * @param orderIds 入库单编号集合 + * @return 明细列表 + */ + List getReceiptOrderDetailList(Collection orderIds); + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImpl.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImpl.java new file mode 100644 index 0000000..1be7429 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImpl.java @@ -0,0 +1,89 @@ +package cn.iocoder.yudao.module.wms.service.order.receipt; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.receipt.WmsReceiptOrderDetailMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.RECEIPT_ORDER_DETAIL_NOT_EXISTS; + +/** + * WMS 入库单明细 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsReceiptOrderDetailServiceImpl implements WmsReceiptOrderDetailService { + + @Resource + private WmsReceiptOrderDetailMapper receiptOrderDetailMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public void createReceiptOrderDetailList(Long orderId, List list) { + if (CollUtil.isEmpty(list)) { + return; + } + if (CollUtil.isNotEmpty(convertList(list, WmsReceiptOrderDetailDO::getId))) { + throw exception(RECEIPT_ORDER_DETAIL_NOT_EXISTS); + } + list.forEach(detail -> detail.setOrderId(orderId)); + receiptOrderDetailMapper.insertBatch(list); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateReceiptOrderDetailList(Long orderId, List list) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = receiptOrderDetailMapper.selectListByOrderId(orderId); + List newList = CollUtil.isEmpty(list) ? ListUtil.of() : list; + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + if (CollUtil.isNotEmpty(convertList(diffList.get(0), WmsReceiptOrderDetailDO::getId))) { + throw exception(RECEIPT_ORDER_DETAIL_NOT_EXISTS); + } + diffList.get(0).forEach(detail -> detail.setOrderId(orderId)); + receiptOrderDetailMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + diffList.get(1).forEach(detail -> detail.setOrderId(orderId)); + receiptOrderDetailMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + receiptOrderDetailMapper.deleteByIds(convertList(diffList.get(2), WmsReceiptOrderDetailDO::getId)); + } + } + + @Override + public void deleteReceiptOrderDetailListByOrderId(Long orderId) { + receiptOrderDetailMapper.deleteByOrderId(orderId); + } + + @Override + public List getReceiptOrderDetailList(Long orderId) { + return receiptOrderDetailMapper.selectListByOrderId(orderId); + } + + @Override + public List getReceiptOrderDetailList(Collection orderIds) { + if (CollUtil.isEmpty(orderIds)) { + return ListUtil.of(); + } + return receiptOrderDetailMapper.selectListByOrderIds(orderIds); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderService.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderService.java new file mode 100644 index 0000000..f17a441 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderService.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.wms.service.order.receipt; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDO; +import jakarta.validation.Valid; + +/** + * WMS 入库单 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsReceiptOrderService { + + /** + * 创建入库单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createReceiptOrder(@Valid WmsReceiptOrderSaveReqVO createReqVO); + + /** + * 更新入库单 + * + * @param updateReqVO 更新信息 + */ + void updateReceiptOrder(@Valid WmsReceiptOrderSaveReqVO updateReqVO); + + /** + * 删除入库单 + * + * @param id 编号 + */ + void deleteReceiptOrder(Long id); + + /** + * 完成入库 + * + * @param id 编号 + */ + void completeReceiptOrder(Long id); + + /** + * 作废入库单 + * + * @param id 编号 + */ + void cancelReceiptOrder(Long id); + + /** + * 获得入库单 + * + * @param id 编号 + * @return 入库单 + */ + WmsReceiptOrderDO getReceiptOrder(Long id); + + /** + * 获得入库单分页 + * + * @param pageReqVO 分页查询 + * @return 入库单分页 + */ + PageResult getReceiptOrderPage(WmsReceiptOrderPageReqVO pageReqVO); + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImpl.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImpl.java new file mode 100644 index 0000000..e04b32f --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImpl.java @@ -0,0 +1,273 @@ +package cn.iocoder.yudao.module.wms.service.order.receipt; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.receipt.vo.order.WmsReceiptOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.merchant.WmsMerchantDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.receipt.WmsReceiptOrderMapper; +import cn.iocoder.yudao.module.wms.enums.inventory.WmsInventoryOrderTypeEnum; +import cn.iocoder.yudao.module.wms.enums.md.WmsMerchantTypeEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsReceiptOrderStatusEnum; +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 入库单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsReceiptOrderServiceImpl implements WmsReceiptOrderService { + + private static final Long EMPTY_AREA_ID = 0L; + + @Resource + private WmsReceiptOrderMapper receiptOrderMapper; + @Resource + private WmsReceiptOrderDetailService receiptOrderDetailService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsWarehouseAreaService warehouseAreaService; + @Resource + private WmsMerchantService merchantService; + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsInventoryService inventoryService; + @Resource + private WmsProperties wmsProperties; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createReceiptOrder(WmsReceiptOrderSaveReqVO createReqVO) { + return createReceiptOrder(createReqVO, false).getId(); + } + + // TODO @AI:不需要 detailRequired 参数呀。。。(对齐 mes 的做法) + private WmsReceiptOrderDO createReceiptOrder(WmsReceiptOrderSaveReqVO createReqVO, boolean detailRequired) { + // 1. 校验入库单保存数据 + validateReceiptOrderSaveData(createReqVO); + // TODO @AI:是不是(对齐 mes 的做法) + List details = buildReceiptOrderDetailList(createReqVO, detailRequired); + + // 2.1 插入入库单 + WmsReceiptOrderDO order = BeanUtils.toBean(createReqVO, WmsReceiptOrderDO.class); + order.setStatus(WmsReceiptOrderStatusEnum.PENDING.getStatus()); + fillReceiptOrderTotal(order, details); + receiptOrderMapper.insert(order); + // 2.2 插入入库单明细 + receiptOrderDetailService.createReceiptOrderDetailList(order.getId(), details); + return order; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateReceiptOrder(WmsReceiptOrderSaveReqVO updateReqVO) { + updateReceiptOrder(updateReqVO, false); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteReceiptOrder(Long id) { + // 1. 校验存在,且暂存 + WmsReceiptOrderDO order = validateReceiptOrderExists(id); + // TODO @AI:只有未“COMPLETED”不允许删除。 + validateReceiptOrderPending(order); + + // 2.1 删除入库单 + receiptOrderMapper.deleteById(id); + // 2.1 删除入库单明细 + receiptOrderDetailService.deleteReceiptOrderDetailListByOrderId(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void completeReceiptOrder(Long id) { + // 1.1 校验存在,且未完成 + WmsReceiptOrderDO order = validateReceiptOrderExists(id); + validateReceiptOrderPending(order); + // 1.2 校验入库单明细存在 + List details = validateReceiptOrderDetailExists(id); + + // 2.1 完成入库单 + WmsReceiptOrderDO updateObj = new WmsReceiptOrderDO().setId(id).setStatus(WmsReceiptOrderStatusEnum.COMPLETED.getStatus()); + receiptOrderMapper.updateById(updateObj); + // 2.2 写入库存 + createInventory(order, details); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelReceiptOrder(Long id) { + // 1. 校验存在,且未完成 + WmsReceiptOrderDO order = validateReceiptOrderExists(id); + validateReceiptOrderPending(order); + + // 2. 作废入库单 + receiptOrderMapper.updateById(new WmsReceiptOrderDO().setId(id) + .setStatus(WmsReceiptOrderStatusEnum.CANCELED.getStatus())); + } + + @Override + public WmsReceiptOrderDO getReceiptOrder(Long id) { + return receiptOrderMapper.selectById(id); + } + + @Override + public PageResult getReceiptOrderPage(WmsReceiptOrderPageReqVO pageReqVO) { + return receiptOrderMapper.selectPage(pageReqVO); + } + + private WmsReceiptOrderDO updateReceiptOrder(WmsReceiptOrderSaveReqVO updateReqVO, boolean detailRequired) { + // 1. 校验入库单保存数据 + WmsReceiptOrderDO oldOrder = validateReceiptOrderExists(updateReqVO.getId()); + validateReceiptOrderPending(oldOrder); + validateReceiptOrderSaveData(updateReqVO); + List details = buildReceiptOrderDetailList(updateReqVO, detailRequired); + + // 2.1 更新入库单 + WmsReceiptOrderDO updateObj = BeanUtils.toBean(updateReqVO, WmsReceiptOrderDO.class) + .setStatus(WmsReceiptOrderStatusEnum.PENDING.getStatus()); + fillReceiptOrderTotal(updateObj, details); + receiptOrderMapper.updateById(updateObj); + // 2.2 更新入库单明细 + receiptOrderDetailService.updateReceiptOrderDetailList(updateReqVO.getId(), details); + return updateObj; + } + + private void validateReceiptOrderSaveData(WmsReceiptOrderSaveReqVO reqVO) { + validateReceiptOrderNoUnique(reqVO.getId(), reqVO.getNo()); + warehouseService.validateWarehouseExists(reqVO.getWarehouseId()); + validateReceiptOrderMerchant(reqVO.getMerchantId()); + reqVO.setAreaId(normalizeAreaId(reqVO.getAreaId(), reqVO.getWarehouseId(), false)); + } + + private void validateReceiptOrderNoUnique(Long id, String no) { + WmsReceiptOrderDO order = receiptOrderMapper.selectByNo(no); + if (order == null) { + return; + } + if (id == null || ObjectUtil.notEqual(order.getId(), id)) { + throw exception(RECEIPT_ORDER_NO_DUPLICATE); + } + } + + private WmsReceiptOrderDO validateReceiptOrderExists(Long id) { + WmsReceiptOrderDO order = id == null ? null : receiptOrderMapper.selectById(id); + if (order == null) { + throw exception(RECEIPT_ORDER_NOT_EXISTS); + } + return order; + } + + private void validateReceiptOrderPending(WmsReceiptOrderDO order) { + if (ObjectUtil.notEqual(order.getStatus(), WmsReceiptOrderStatusEnum.PENDING.getStatus())) { + throw exception(RECEIPT_ORDER_STATUS_NOT_PENDING); + } + } + + private List validateReceiptOrderDetailExists(Long id) { + List details = receiptOrderDetailService.getReceiptOrderDetailList(id); + if (CollUtil.isEmpty(details)) { + throw exception(RECEIPT_ORDER_DETAIL_REQUIRED); + } + return details; + } + + private void validateReceiptOrderMerchant(Long merchantId) { + if (merchantId == null) { + return; + } + WmsMerchantDO merchant = merchantService.validateMerchantExists(merchantId); + if (ObjectUtil.notEqual(merchant.getType(), WmsMerchantTypeEnum.SUPPLIER.getType()) + && ObjectUtil.notEqual(merchant.getType(), WmsMerchantTypeEnum.CUSTOMER_SUPPLIER.getType())) { + throw exception(RECEIPT_ORDER_MERCHANT_NOT_SUPPLIER); + } + } + + private List buildReceiptOrderDetailList(WmsReceiptOrderSaveReqVO reqVO, boolean required) { + if (CollUtil.isEmpty(reqVO.getDetails())) { + if (required) { + throw exception(RECEIPT_ORDER_DETAIL_REQUIRED); + } + return ListUtil.of(); + } + return convertList(reqVO.getDetails(), detail -> { + itemSkuService.validateItemSkuExists(detail.getSkuId()); + WmsReceiptOrderDetailDO detailDO = BeanUtils.toBean(detail, WmsReceiptOrderDetailDO.class); + detailDO.setWarehouseId(reqVO.getWarehouseId()); + detailDO.setAreaId(normalizeAreaId(detail.getAreaId() == null ? reqVO.getAreaId() : detail.getAreaId(), + reqVO.getWarehouseId(), required)); + return detailDO; + }); + } + + private Long normalizeAreaId(Long areaId, Long warehouseId, boolean required) { + if (!wmsProperties.isAreaEnabled()) { + return EMPTY_AREA_ID; + } + if (areaId == null || ObjectUtil.equal(areaId, EMPTY_AREA_ID)) { + if (required) { + throw exception(RECEIPT_ORDER_AREA_REQUIRED); + } + return EMPTY_AREA_ID; + } + WmsWarehouseAreaDO area = warehouseAreaService.validateWarehouseAreaExists(areaId); + if (ObjectUtil.notEqual(area.getWarehouseId(), warehouseId)) { + throw exception(RECEIPT_ORDER_AREA_NOT_MATCH_WAREHOUSE); + } + return areaId; + } + + // DONE @AI:保留手工总金额优先;未填写时按明细金额汇总回填。 + // TODO @AI:前端自动计算。 + private void fillReceiptOrderTotal(WmsReceiptOrderDO order, List details) { + BigDecimal totalQuantity = BigDecimal.ZERO; + BigDecimal totalAmount = BigDecimal.ZERO; + for (WmsReceiptOrderDetailDO detail : details) { + totalQuantity = totalQuantity.add(detail.getQuantity()); + totalAmount = totalAmount.add(detail.getAmount() == null ? BigDecimal.ZERO : detail.getAmount()); + } + order.setTotalQuantity(totalQuantity); + if (order.getTotalAmount() == null) { + order.setTotalAmount(totalAmount); + } + } + + private void createInventory(WmsReceiptOrderDO order, List details) { + List items = convertList(details, detail -> new WmsInventoryChangeReqDTO.Item() + .setSkuId(detail.getSkuId()).setWarehouseId(detail.getWarehouseId()).setAreaId(detail.getAreaId()) + .setQuantity(detail.getQuantity()).setBatchNo(detail.getBatchNo()) + .setProductionDate(detail.getProductionDate()).setExpirationDate(detail.getExpirationDate()) + .setAmount(detail.getAmount()).setRemark(detail.getRemark())); + inventoryService.changeInventory(new WmsInventoryChangeReqDTO() + .setOrderId(order.getId()).setOrderNo(order.getNo()) + .setOrderType(WmsInventoryOrderTypeEnum.RECEIPT.getType()).setItems(items)); + } + +} diff --git a/yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImplTest.java b/yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImplTest.java new file mode 100644 index 0000000..fc4b2ed --- /dev/null +++ b/yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderDetailServiceImplTest.java @@ -0,0 +1,112 @@ +package cn.iocoder.yudao.module.wms.service.order.receipt; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.receipt.WmsReceiptOrderDetailMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import java.math.BigDecimal; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.RECEIPT_ORDER_DETAIL_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +@Import(WmsReceiptOrderDetailServiceImpl.class) +public class WmsReceiptOrderDetailServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsReceiptOrderDetailServiceImpl receiptOrderDetailService; + + @Resource + private WmsReceiptOrderDetailMapper receiptOrderDetailMapper; + + @Test + public void testCreateReceiptOrderDetailList_success() { + // mock 数据 + Long orderId = 10L; + WmsReceiptOrderDetailDO detail01 = createReceiptOrderDetail(null, 1001L, "1.00"); + WmsReceiptOrderDetailDO detail02 = createReceiptOrderDetail(null, 1002L, "2.00"); + + // 调用 + receiptOrderDetailService.createReceiptOrderDetailList(orderId, List.of(detail01, detail02)); + + // 断言 + List details = receiptOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + assertEquals(orderId, details.get(0).getOrderId()); + assertEquals(orderId, details.get(1).getOrderId()); + } + + @Test + public void testCreateReceiptOrderDetailList_detailNotExists() { + // mock 数据 + WmsReceiptOrderDetailDO detail = createReceiptOrderDetail(null, 1001L, "1.00"); + detail.setId(999L); + + // 调用,并断言 + assertServiceException(() -> receiptOrderDetailService.createReceiptOrderDetailList(10L, List.of(detail)), + RECEIPT_ORDER_DETAIL_NOT_EXISTS); + } + + @Test + public void testUpdateReceiptOrderDetailList_diff() { + // mock 数据 + Long orderId = 10L; + WmsReceiptOrderDetailDO detail01 = createReceiptOrderDetail(orderId, 1001L, "1.00"); + WmsReceiptOrderDetailDO detail02 = createReceiptOrderDetail(orderId, 1002L, "2.00"); + receiptOrderDetailMapper.insert(detail01); + receiptOrderDetailMapper.insert(detail02); + WmsReceiptOrderDetailDO updateDetail = createReceiptOrderDetail(999L, 2001L, "11.00"); + updateDetail.setId(detail01.getId()); + WmsReceiptOrderDetailDO createDetail = createReceiptOrderDetail(null, 2002L, "22.00"); + + // 调用 + receiptOrderDetailService.updateReceiptOrderDetailList(orderId, List.of(updateDetail, createDetail)); + + // 断言:修改 + WmsReceiptOrderDetailDO dbUpdateDetail = receiptOrderDetailMapper.selectById(detail01.getId()); + assertNotNull(dbUpdateDetail); + assertEquals(orderId, dbUpdateDetail.getOrderId()); + assertEquals(2001L, dbUpdateDetail.getSkuId()); + assertEquals(0, new BigDecimal("11.00").compareTo(dbUpdateDetail.getQuantity())); + // 断言:新增 + List details = receiptOrderDetailMapper.selectListByOrderId(orderId); + assertEquals(2, details.size()); + WmsReceiptOrderDetailDO dbCreateDetail = details.stream() + .filter(detail -> Long.valueOf(2002L).equals(detail.getSkuId())) + .findFirst().orElse(null); + assertNotNull(dbCreateDetail); + assertEquals(orderId, dbCreateDetail.getOrderId()); + assertEquals(0, new BigDecimal("22.00").compareTo(dbCreateDetail.getQuantity())); + // 断言:删除 + assertNull(receiptOrderDetailMapper.selectById(detail02.getId())); + } + + @Test + public void testUpdateReceiptOrderDetailList_detailNotExists() { + // mock 数据 + WmsReceiptOrderDetailDO detail = createReceiptOrderDetail(10L, 1001L, "1.00"); + detail.setId(999L); + + // 调用,并断言 + assertServiceException(() -> receiptOrderDetailService.updateReceiptOrderDetailList(10L, List.of(detail)), + RECEIPT_ORDER_DETAIL_NOT_EXISTS); + } + + private static WmsReceiptOrderDetailDO createReceiptOrderDetail(Long orderId, Long skuId, String quantity) { + return WmsReceiptOrderDetailDO.builder() + .orderId(orderId) + .skuId(skuId) + .warehouseId(100L) + .areaId(0L) + .quantity(new BigDecimal(quantity)) + .amount(new BigDecimal("100.00")) + .build(); + } + +} diff --git a/yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImplTest.java b/yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImplTest.java new file mode 100644 index 0000000..f80e624 --- /dev/null +++ b/yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImplTest.java @@ -0,0 +1,121 @@ +package cn.iocoder.yudao.module.wms.service.order.receipt; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.receipt.WmsReceiptOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.receipt.WmsReceiptOrderDetailMapper; +import cn.iocoder.yudao.module.wms.dal.mysql.order.receipt.WmsReceiptOrderMapper; +import cn.iocoder.yudao.module.wms.enums.inventory.WmsInventoryOrderTypeEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsReceiptOrderStatusEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsReceiptOrderTypeEnum; +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import java.math.BigDecimal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +@Import({WmsReceiptOrderServiceImpl.class, WmsReceiptOrderDetailServiceImpl.class, WmsProperties.class}) +public class WmsReceiptOrderServiceImplTest extends BaseDbUnitTest { + + @Resource + private WmsReceiptOrderServiceImpl receiptOrderService; + + @Resource + private WmsReceiptOrderMapper receiptOrderMapper; + @Resource + private WmsReceiptOrderDetailMapper receiptOrderDetailMapper; + + @MockitoBean + private WmsWarehouseService warehouseService; + @MockitoBean + private WmsWarehouseAreaService warehouseAreaService; + @MockitoBean + private WmsMerchantService merchantService; + @MockitoBean + private WmsItemSkuService itemSkuService; + @MockitoBean + private WmsInventoryService inventoryService; + + @Test + public void testCompleteReceiptOrder_success() { + // mock 数据 + Long warehouseId = 100L; + Long skuId = 200L; + WmsReceiptOrderDO order = createReceiptOrder(warehouseId); + receiptOrderMapper.insert(order); + receiptOrderDetailMapper.insert(createReceiptOrderDetail(order.getId(), skuId, warehouseId)); + + // 调用 + receiptOrderService.completeReceiptOrder(order.getId()); + + // 断言:入库单 + WmsReceiptOrderDO dbOrder = receiptOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(WmsReceiptOrderStatusEnum.COMPLETED.getStatus(), dbOrder.getStatus()); + // 断言:库存变更 + ArgumentCaptor captor = ArgumentCaptor.forClass(WmsInventoryChangeReqDTO.class); + verify(inventoryService).changeInventory(captor.capture()); + WmsInventoryChangeReqDTO inventoryReqDTO = captor.getValue(); + assertEquals(order.getId(), inventoryReqDTO.getOrderId()); + assertEquals(order.getNo(), inventoryReqDTO.getOrderNo()); + assertEquals(WmsInventoryOrderTypeEnum.RECEIPT.getType(), inventoryReqDTO.getOrderType()); + assertEquals(1, inventoryReqDTO.getItems().size()); + assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId()); + assertEquals(warehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId()); + assertEquals(0, new BigDecimal("2.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity())); + } + + @Test + public void testCancelReceiptOrder_success() { + // mock 数据 + WmsReceiptOrderDO order = createReceiptOrder(100L); + receiptOrderMapper.insert(order); + + // 调用 + receiptOrderService.cancelReceiptOrder(order.getId()); + + // 断言 + WmsReceiptOrderDO dbOrder = receiptOrderMapper.selectById(order.getId()); + assertNotNull(dbOrder); + assertEquals(WmsReceiptOrderStatusEnum.CANCELED.getStatus(), dbOrder.getStatus()); + verify(inventoryService, never()).changeInventory(any()); + } + + private static WmsReceiptOrderDO createReceiptOrder(Long warehouseId) { + return new WmsReceiptOrderDO() + .setNo("RK202605120001") + .setType(WmsReceiptOrderTypeEnum.PURCHASE.getType()) + .setStatus(WmsReceiptOrderStatusEnum.PENDING.getStatus()) + .setWarehouseId(warehouseId) + .setAreaId(0L) + .setTotalQuantity(new BigDecimal("2.00")) + .setTotalAmount(new BigDecimal("20.00")); + } + + private static WmsReceiptOrderDetailDO createReceiptOrderDetail(Long orderId, Long skuId, Long warehouseId) { + return WmsReceiptOrderDetailDO.builder() + .orderId(orderId) + .skuId(skuId) + .warehouseId(warehouseId) + .areaId(0L) + .quantity(new BigDecimal("2.00")) + .amount(new BigDecimal("20.00")) + .build(); + } + +} -- libgit2 0.21.4