Commit 93607eb9238f210b5cd3cac5da679f3c621b55b2

Authored by YunaiV
1 parent eba504d0

feat(wms):新增出库管理

Showing 28 changed files with 1724 additions and 4 deletions
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/detail/WmsInventoryDetailPageReqVO.java
@@ -7,6 +7,7 @@ import lombok.EqualsAndHashCode; @@ -7,6 +7,7 @@ import lombok.EqualsAndHashCode;
7 import lombok.ToString; 7 import lombok.ToString;
8 import org.springframework.format.annotation.DateTimeFormat; 8 import org.springframework.format.annotation.DateTimeFormat;
9 9
  10 +import java.math.BigDecimal;
10 import java.time.LocalDateTime; 11 import java.time.LocalDateTime;
11 12
12 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; 13 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@@ -53,6 +54,9 @@ public class WmsInventoryDetailPageReqVO extends PageParam { @@ -53,6 +54,9 @@ public class WmsInventoryDetailPageReqVO extends PageParam {
53 @Schema(description = "批号", example = "BATCH001") 54 @Schema(description = "批号", example = "BATCH001")
54 private String batchNo; 55 private String batchNo;
55 56
  57 + @Schema(description = "最小剩余库存数量", example = "0.01")
  58 + private BigDecimal minRemainQuantity;
  59 +
56 @Schema(description = "距离过期天数", example = "30") 60 @Schema(description = "距离过期天数", example = "30")
57 private Integer daysToExpires; 61 private Integer daysToExpires;
58 62
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/WmsShipmentOrderController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.NumberUtil;
  5 +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
  6 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  8 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  9 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  10 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  11 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  12 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  13 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  14 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailRespVO;
  15 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderPageReqVO;
  16 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderRespVO;
  17 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO;
  18 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO;
  19 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
  20 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.merchant.WmsMerchantDO;
  21 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO;
  22 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO;
  23 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDO;
  24 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO;
  25 +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties;
  26 +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService;
  27 +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
  28 +import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService;
  29 +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService;
  30 +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService;
  31 +import cn.iocoder.yudao.module.wms.service.order.shipment.WmsShipmentOrderDetailService;
  32 +import cn.iocoder.yudao.module.wms.service.order.shipment.WmsShipmentOrderService;
  33 +import io.swagger.v3.oas.annotations.Operation;
  34 +import io.swagger.v3.oas.annotations.Parameter;
  35 +import io.swagger.v3.oas.annotations.tags.Tag;
  36 +import jakarta.annotation.Resource;
  37 +import jakarta.servlet.http.HttpServletResponse;
  38 +import jakarta.validation.Valid;
  39 +import org.springframework.security.access.prepost.PreAuthorize;
  40 +import org.springframework.validation.annotation.Validated;
  41 +import org.springframework.web.bind.annotation.*;
  42 +
  43 +import java.io.IOException;
  44 +import java.util.Collections;
  45 +import java.util.List;
  46 +import java.util.Map;
  47 +import java.util.stream.Stream;
  48 +
  49 +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
  50 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  51 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  52 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
  53 +
  54 +@Tag(name = "管理后台 - WMS 出库单")
  55 +@RestController
  56 +@RequestMapping("/wms/shipment-order")
  57 +@Validated
  58 +public class WmsShipmentOrderController {
  59 +
  60 + @Resource
  61 + private WmsShipmentOrderService shipmentOrderService;
  62 + @Resource
  63 + private WmsShipmentOrderDetailService shipmentOrderDetailService;
  64 + @Resource
  65 + private WmsMerchantService merchantService;
  66 + @Resource
  67 + private WmsWarehouseService warehouseService;
  68 + @Resource
  69 + private WmsWarehouseAreaService warehouseAreaService;
  70 + @Resource
  71 + private WmsItemService itemService;
  72 + @Resource
  73 + private WmsItemSkuService itemSkuService;
  74 +
  75 + @Resource
  76 + private WmsProperties wmsProperties;
  77 +
  78 + @Resource
  79 + private AdminUserApi adminUserApi;
  80 +
  81 + @PostMapping("/create")
  82 + @Operation(summary = "创建出库单")
  83 + @PreAuthorize("@ss.hasPermission('wms:shipment-order:create')")
  84 + public CommonResult<Long> createShipmentOrder(@Valid @RequestBody WmsShipmentOrderSaveReqVO createReqVO) {
  85 + return success(shipmentOrderService.createShipmentOrder(createReqVO));
  86 + }
  87 +
  88 + @PutMapping("/update")
  89 + @Operation(summary = "更新出库单")
  90 + @PreAuthorize("@ss.hasPermission('wms:shipment-order:update')")
  91 + public CommonResult<Boolean> updateShipmentOrder(@Valid @RequestBody WmsShipmentOrderSaveReqVO updateReqVO) {
  92 + shipmentOrderService.updateShipmentOrder(updateReqVO);
  93 + return success(true);
  94 + }
  95 +
  96 + @DeleteMapping("/delete")
  97 + @Operation(summary = "删除出库单")
  98 + @Parameter(name = "id", description = "编号", required = true)
  99 + @PreAuthorize("@ss.hasPermission('wms:shipment-order:delete')")
  100 + public CommonResult<Boolean> deleteShipmentOrder(@RequestParam("id") Long id) {
  101 + shipmentOrderService.deleteShipmentOrder(id);
  102 + return success(true);
  103 + }
  104 +
  105 + @PutMapping("/complete")
  106 + @Operation(summary = "完成出库")
  107 + @Parameter(name = "id", description = "编号", required = true)
  108 + @PreAuthorize("@ss.hasPermission('wms:shipment-order:complete')")
  109 + public CommonResult<Boolean> completeShipmentOrder(@RequestParam("id") Long id) {
  110 + shipmentOrderService.completeShipmentOrder(id);
  111 + return success(true);
  112 + }
  113 +
  114 + @PutMapping("/cancel")
  115 + @Operation(summary = "作废出库单")
  116 + @Parameter(name = "id", description = "编号", required = true)
  117 + @PreAuthorize("@ss.hasPermission('wms:shipment-order:cancel')")
  118 + public CommonResult<Boolean> cancelShipmentOrder(@RequestParam("id") Long id) {
  119 + shipmentOrderService.cancelShipmentOrder(id);
  120 + return success(true);
  121 + }
  122 +
  123 + @GetMapping("/get")
  124 + @Operation(summary = "获得出库单")
  125 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  126 + @PreAuthorize("@ss.hasPermission('wms:shipment-order:query')")
  127 + public CommonResult<WmsShipmentOrderRespVO> getShipmentOrder(@RequestParam("id") Long id) {
  128 + WmsShipmentOrderDO order = shipmentOrderService.getShipmentOrder(id);
  129 + if (order == null) {
  130 + return success(null);
  131 + }
  132 + // 获得出库单的明细列表
  133 + List<WmsShipmentOrderDetailDO> detailList = shipmentOrderDetailService.getShipmentOrderDetailList(id);
  134 + // 拼接结果返回
  135 + WmsShipmentOrderRespVO respVO = buildShipmentOrderRespVO(order)
  136 + .setDetails(buildShipmentOrderDetailRespVOList(detailList));
  137 + return success(respVO);
  138 + }
  139 +
  140 + @GetMapping("/page")
  141 + @Operation(summary = "获得出库单分页")
  142 + @PreAuthorize("@ss.hasPermission('wms:shipment-order:query')")
  143 + public CommonResult<PageResult<WmsShipmentOrderRespVO>> getShipmentOrderPage(@Valid WmsShipmentOrderPageReqVO pageReqVO) {
  144 + PageResult<WmsShipmentOrderDO> pageResult = shipmentOrderService.getShipmentOrderPage(pageReqVO);
  145 + return success(new PageResult<>(buildShipmentOrderRespVOList(pageResult.getList()), pageResult.getTotal()));
  146 + }
  147 +
  148 + @GetMapping("/export-excel")
  149 + @Operation(summary = "导出出库单 Excel")
  150 + @PreAuthorize("@ss.hasPermission('wms:shipment-order:export')")
  151 + @ApiAccessLog(operateType = EXPORT)
  152 + public void exportShipmentOrderExcel(@Valid WmsShipmentOrderPageReqVO pageReqVO,
  153 + HttpServletResponse response) throws IOException {
  154 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  155 + List<WmsShipmentOrderDO> list = shipmentOrderService.getShipmentOrderPage(pageReqVO).getList();
  156 + ExcelUtils.write(response, "出库单.xls", "数据", WmsShipmentOrderRespVO.class,
  157 + buildShipmentOrderRespVOList(list));
  158 + }
  159 +
  160 + // ==================== 拼接 VO ====================
  161 +
  162 + private WmsShipmentOrderRespVO buildShipmentOrderRespVO(WmsShipmentOrderDO order) {
  163 + if (order == null) {
  164 + return null;
  165 + }
  166 + List<WmsShipmentOrderRespVO> list = buildShipmentOrderRespVOList(List.of(order));
  167 + return CollUtil.getFirst(list);
  168 + }
  169 +
  170 + private List<WmsShipmentOrderRespVO> buildShipmentOrderRespVOList(List<WmsShipmentOrderDO> list) {
  171 + if (CollUtil.isEmpty(list)) {
  172 + return Collections.emptyList();
  173 + }
  174 + // 获取相关的商户、仓库、库区、用户等数据
  175 + Map<Long, WmsMerchantDO> merchantMap = merchantService.getMerchantMap(convertSet(list, WmsShipmentOrderDO::getMerchantId));
  176 + Map<Long, WmsWarehouseDO> warehouseMap = warehouseService.getWarehouseMap(convertSet(list, WmsShipmentOrderDO::getWarehouseId));
  177 + Map<Long, WmsWarehouseAreaDO> areaMap = wmsProperties.isAreaEnabled()
  178 + ? warehouseAreaService.getWarehouseAreaMap(convertSet(list, WmsShipmentOrderDO::getAreaId))
  179 + : Collections.emptyMap();
  180 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(list,
  181 + order -> Stream.of(parseUserId(order.getCreator()), parseUserId(order.getUpdater()))));
  182 + // 拼接数据
  183 + return BeanUtils.toBean(list, WmsShipmentOrderRespVO.class, vo -> {
  184 + MapUtils.findAndThen(merchantMap, vo.getMerchantId(), merchant -> vo.setMerchantName(merchant.getName()));
  185 + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName()));
  186 + MapUtils.findAndThen(areaMap, vo.getAreaId(), area -> vo.setAreaName(area.getName()));
  187 + MapUtils.findAndThen(userMap, parseUserId(vo.getCreator()), user -> vo.setCreatorName(user.getNickname()));
  188 + MapUtils.findAndThen(userMap, parseUserId(vo.getUpdater()), user -> vo.setUpdaterName(user.getNickname()));
  189 + });
  190 + }
  191 +
  192 + private Long parseUserId(String userId) {
  193 + return NumberUtil.parseLong(userId, null);
  194 + }
  195 +
  196 + private List<WmsShipmentOrderDetailRespVO> buildShipmentOrderDetailRespVOList(List<WmsShipmentOrderDetailDO> list) {
  197 + if (CollUtil.isEmpty(list)) {
  198 + return Collections.emptyList();
  199 + }
  200 + // 获取相关的商品、SKU、仓库、库区等数据
  201 + Map<Long, WmsItemSkuDO> skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsShipmentOrderDetailDO::getSkuId));
  202 + Map<Long, WmsItemDO> itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId));
  203 + Map<Long, WmsWarehouseDO> warehouseMap = warehouseService.getWarehouseMap(
  204 + convertSet(list, WmsShipmentOrderDetailDO::getWarehouseId));
  205 + Map<Long, WmsWarehouseAreaDO> areaMap = wmsProperties.isAreaEnabled()
  206 + ? warehouseAreaService.getWarehouseAreaMap(convertSet(list, WmsShipmentOrderDetailDO::getAreaId))
  207 + : Collections.emptyMap();
  208 + // 拼接数据
  209 + return BeanUtils.toBean(list, WmsShipmentOrderDetailRespVO.class, vo -> {
  210 + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> {
  211 + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId());
  212 + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode())
  213 + .setItemName(item.getName()).setUnit(item.getUnit()));
  214 + });
  215 + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName()));
  216 + MapUtils.findAndThen(areaMap, vo.getAreaId(), area -> vo.setAreaName(area.getName()));
  217 + });
  218 + }
  219 +
  220 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/WmsShipmentOrderDetailController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailRespVO;
  8 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO;
  9 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
  10 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO;
  11 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO;
  12 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO;
  13 +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties;
  14 +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService;
  15 +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
  16 +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService;
  17 +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService;
  18 +import cn.iocoder.yudao.module.wms.service.order.shipment.WmsShipmentOrderDetailService;
  19 +import io.swagger.v3.oas.annotations.Operation;
  20 +import io.swagger.v3.oas.annotations.Parameter;
  21 +import io.swagger.v3.oas.annotations.tags.Tag;
  22 +import jakarta.annotation.Resource;
  23 +import org.springframework.security.access.prepost.PreAuthorize;
  24 +import org.springframework.validation.annotation.Validated;
  25 +import org.springframework.web.bind.annotation.GetMapping;
  26 +import org.springframework.web.bind.annotation.RequestMapping;
  27 +import org.springframework.web.bind.annotation.RequestParam;
  28 +import org.springframework.web.bind.annotation.RestController;
  29 +
  30 +import java.util.Collections;
  31 +import java.util.List;
  32 +import java.util.Map;
  33 +
  34 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  35 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  36 +
  37 +@Tag(name = "管理后台 - WMS 出库单明细")
  38 +@RestController
  39 +@RequestMapping("/wms/shipment-order-detail")
  40 +@Validated
  41 +public class WmsShipmentOrderDetailController {
  42 +
  43 + @Resource
  44 + private WmsShipmentOrderDetailService shipmentOrderDetailService;
  45 + @Resource
  46 + private WmsItemService itemService;
  47 + @Resource
  48 + private WmsItemSkuService itemSkuService;
  49 + @Resource
  50 + private WmsWarehouseService warehouseService;
  51 + @Resource
  52 + private WmsWarehouseAreaService warehouseAreaService;
  53 +
  54 + @Resource
  55 + private WmsProperties wmsProperties;
  56 +
  57 + @GetMapping("/list-by-order-id")
  58 + @Operation(summary = "获得出库单明细列表")
  59 + @Parameter(name = "orderId", description = "出库单编号", required = true, example = "1024")
  60 + @PreAuthorize("@ss.hasPermission('wms:shipment-order:query')")
  61 + public CommonResult<List<WmsShipmentOrderDetailRespVO>> getShipmentOrderDetailListByOrderId(
  62 + @RequestParam("orderId") Long orderId) {
  63 + List<WmsShipmentOrderDetailDO> list = shipmentOrderDetailService.getShipmentOrderDetailList(orderId);
  64 + return success(buildShipmentOrderDetailRespVOList(list));
  65 + }
  66 +
  67 + // ==================== 拼接 VO ====================
  68 +
  69 + private List<WmsShipmentOrderDetailRespVO> buildShipmentOrderDetailRespVOList(List<WmsShipmentOrderDetailDO> list) {
  70 + if (CollUtil.isEmpty(list)) {
  71 + return Collections.emptyList();
  72 + }
  73 + // 查询关联数据
  74 + Map<Long, WmsItemSkuDO> skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsShipmentOrderDetailDO::getSkuId));
  75 + Map<Long, WmsItemDO> itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId));
  76 + Map<Long, WmsWarehouseDO> warehouseMap = warehouseService.getWarehouseMap(
  77 + convertSet(list, WmsShipmentOrderDetailDO::getWarehouseId));
  78 + Map<Long, WmsWarehouseAreaDO> areaMap = wmsProperties.isAreaEnabled()
  79 + ? warehouseAreaService.getWarehouseAreaMap(convertSet(list, WmsShipmentOrderDetailDO::getAreaId))
  80 + : Collections.emptyMap();
  81 + // 拼接数据
  82 + return BeanUtils.toBean(list, WmsShipmentOrderDetailRespVO.class, vo -> {
  83 + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> {
  84 + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId());
  85 + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode())
  86 + .setItemName(item.getName()).setUnit(item.getUnit()));
  87 + });
  88 + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName()));
  89 + MapUtils.findAndThen(areaMap, vo.getAreaId(), area -> vo.setAreaName(area.getName()));
  90 + });
  91 + }
  92 +
  93 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/detail/WmsShipmentOrderDetailRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.math.BigDecimal;
  7 +import java.time.LocalDateTime;
  8 +
  9 +@Schema(description = "管理后台 - WMS 出库单明细 Response VO")
  10 +@Data
  11 +public class WmsShipmentOrderDetailRespVO {
  12 +
  13 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "出库单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  17 + private Long orderId;
  18 +
  19 + @Schema(description = "商品编号", example = "2048")
  20 + private Long itemId;
  21 +
  22 + @Schema(description = "商品编号", example = "SPU-APPLE")
  23 + private String itemCode;
  24 +
  25 + @Schema(description = "商品名称", example = "红富士苹果")
  26 + private String itemName;
  27 +
  28 + @Schema(description = "商品单位", example = "箱")
  29 + private String unit;
  30 +
  31 + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  32 + private Long skuId;
  33 +
  34 + @Schema(description = "规格编号", example = "SKU-APPLE-10KG")
  35 + private String skuCode;
  36 +
  37 + @Schema(description = "规格名称", example = "10kg 箱装")
  38 + private String skuName;
  39 +
  40 + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  41 + private Long warehouseId;
  42 +
  43 + @Schema(description = "仓库名称", example = "北京仓")
  44 + private String warehouseName;
  45 +
  46 + @Schema(description = "库区编号", example = "1024")
  47 + private Long areaId;
  48 +
  49 + @Schema(description = "库区名称", example = "北京库区01")
  50 + private String areaName;
  51 +
  52 + @Schema(description = "库存明细编号", example = "1024")
  53 + private Long inventoryDetailId;
  54 +
  55 + @Schema(description = "批号", example = "B202605110001")
  56 + private String batchNo;
  57 +
  58 + @Schema(description = "生产日期")
  59 + private LocalDateTime productionDate;
  60 +
  61 + @Schema(description = "过期日期")
  62 + private LocalDateTime expirationDate;
  63 +
  64 + @Schema(description = "出库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
  65 + private BigDecimal quantity;
  66 +
  67 + @Schema(description = "金额", example = "1000.00")
  68 + private BigDecimal amount;
  69 +
  70 + @Schema(description = "备注", example = "备注")
  71 + private String remark;
  72 +
  73 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  74 + private LocalDateTime createTime;
  75 +
  76 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/detail/WmsShipmentOrderDetailSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.DecimalMin;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import jakarta.validation.constraints.Size;
  7 +import lombok.Data;
  8 +
  9 +import java.math.BigDecimal;
  10 +import java.time.LocalDateTime;
  11 +
  12 +@Schema(description = "管理后台 - WMS 出库单明细保存 Request VO")
  13 +@Data
  14 +public class WmsShipmentOrderDetailSaveReqVO {
  15 +
  16 + @Schema(description = "编号", example = "1024")
  17 + private Long id;
  18 +
  19 + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  20 + @NotNull(message = "SKU 不能为空")
  21 + private Long skuId;
  22 +
  23 + @Schema(description = "库区编号", example = "1024")
  24 + private Long areaId;
  25 +
  26 + @Schema(description = "库存明细编号", example = "1024")
  27 + private Long inventoryDetailId;
  28 +
  29 + @Schema(description = "批号", example = "B202605110001")
  30 + @Size(max = 64, message = "批号长度不能超过 64 个字符")
  31 + private String batchNo;
  32 +
  33 + @Schema(description = "生产日期")
  34 + private LocalDateTime productionDate;
  35 +
  36 + @Schema(description = "过期日期")
  37 + private LocalDateTime expirationDate;
  38 +
  39 + @Schema(description = "出库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
  40 + @NotNull(message = "出库数量不能为空")
  41 + @DecimalMin(value = "0", inclusive = false, message = "出库数量必须大于 0")
  42 + private BigDecimal quantity;
  43 +
  44 + @Schema(description = "金额", example = "1000.00")
  45 + @DecimalMin(value = "0", message = "金额不能小于 0")
  46 + private BigDecimal amount;
  47 +
  48 + @Schema(description = "备注", example = "备注")
  49 + @Size(max = 255, message = "备注长度不能超过 255 个字符")
  50 + private String remark;
  51 +
  52 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/order/WmsShipmentOrderPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum;
  6 +import cn.iocoder.yudao.module.wms.enums.order.WmsShipmentOrderTypeEnum;
  7 +import io.swagger.v3.oas.annotations.media.Schema;
  8 +import lombok.Data;
  9 +import lombok.EqualsAndHashCode;
  10 +import lombok.ToString;
  11 +import org.springframework.format.annotation.DateTimeFormat;
  12 +
  13 +import java.math.BigDecimal;
  14 +import java.time.LocalDateTime;
  15 +
  16 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  17 +
  18 +@Schema(description = "管理后台 - WMS 出库单分页 Request VO")
  19 +@Data
  20 +@EqualsAndHashCode(callSuper = true)
  21 +@ToString(callSuper = true)
  22 +public class WmsShipmentOrderPageReqVO extends PageParam {
  23 +
  24 + @Schema(description = "出库单号", example = "CK202605110001")
  25 + private String no;
  26 +
  27 + @Schema(description = "单据状态", example = "0")
  28 + @InEnum(WmsOrderStatusEnum.class)
  29 + private Integer status;
  30 +
  31 + @Schema(description = "仓库编号", example = "1024")
  32 + private Long warehouseId;
  33 +
  34 + @Schema(description = "客户编号", example = "1024")
  35 + private Long merchantId;
  36 +
  37 + @Schema(description = "单据日期")
  38 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  39 + private LocalDateTime[] orderDate;
  40 +
  41 + @Schema(description = "最小数量", example = "1.00")
  42 + private BigDecimal totalQuantityMin;
  43 +
  44 + @Schema(description = "最大数量", example = "100.00")
  45 + private BigDecimal totalQuantityMax;
  46 +
  47 + @Schema(description = "最小总金额", example = "1.00")
  48 + private BigDecimal totalAmountMin;
  49 +
  50 + @Schema(description = "最大总金额", example = "1000.00")
  51 + private BigDecimal totalAmountMax;
  52 +
  53 + @Schema(description = "出库类型", example = "201")
  54 + @InEnum(WmsShipmentOrderTypeEnum.class)
  55 + private Integer type;
  56 +
  57 + @Schema(description = "业务单号", example = "SO202605110001")
  58 + private String bizOrderNo;
  59 +
  60 + @Schema(description = "创建用户", example = "1")
  61 + private String creator;
  62 +
  63 + @Schema(description = "更新用户", example = "1")
  64 + private String updater;
  65 +
  66 + @Schema(description = "创建时间")
  67 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  68 + private LocalDateTime[] createTime;
  69 +
  70 + @Schema(description = "更新时间")
  71 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  72 + private LocalDateTime[] updateTime;
  73 +
  74 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/order/WmsShipmentOrderRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order;
  2 +
  3 +import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
  4 +import cn.idev.excel.annotation.ExcelProperty;
  5 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  6 +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  7 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailRespVO;
  8 +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants;
  9 +import io.swagger.v3.oas.annotations.media.Schema;
  10 +import lombok.Data;
  11 +
  12 +import java.math.BigDecimal;
  13 +import java.time.LocalDateTime;
  14 +import java.util.List;
  15 +
  16 +@Schema(description = "管理后台 - WMS 出库单 Response VO")
  17 +@Data
  18 +@ExcelIgnoreUnannotated
  19 +public class WmsShipmentOrderRespVO {
  20 +
  21 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  22 + private Long id;
  23 +
  24 + @Schema(description = "出库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "CK202605110001")
  25 + @ExcelProperty("出库单号")
  26 + private String no;
  27 +
  28 + @Schema(description = "出库类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "201")
  29 + @ExcelProperty(value = "出库类型", converter = DictConvert.class)
  30 + @DictFormat(DictTypeConstants.SHIPMENT_ORDER_TYPE)
  31 + private Integer type;
  32 +
  33 + @Schema(description = "出库状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  34 + @ExcelProperty(value = "出库状态", converter = DictConvert.class)
  35 + @DictFormat(DictTypeConstants.ORDER_STATUS)
  36 + private Integer status;
  37 +
  38 + @Schema(description = "业务单号", example = "SO202605110001")
  39 + @ExcelProperty("业务单号")
  40 + private String bizOrderNo;
  41 +
  42 + @Schema(description = "客户编号", example = "1024")
  43 + private Long merchantId;
  44 +
  45 + @Schema(description = "客户名称", example = "某某公司")
  46 + @ExcelProperty("客户")
  47 + private String merchantName;
  48 +
  49 + @Schema(description = "备注", example = "备注")
  50 + private String remark;
  51 +
  52 + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  53 + private Long warehouseId;
  54 +
  55 + @Schema(description = "仓库名称", example = "北京仓")
  56 + @ExcelProperty("仓库")
  57 + private String warehouseName;
  58 +
  59 + @Schema(description = "库区编号", example = "1024")
  60 + private Long areaId;
  61 +
  62 + @Schema(description = "库区名称", example = "北京库区01")
  63 + private String areaName;
  64 +
  65 + @Schema(description = "出库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
  66 + @ExcelProperty("出库数量")
  67 + private BigDecimal totalQuantity;
  68 +
  69 + @Schema(description = "总金额", example = "1000.00")
  70 + @ExcelProperty("总金额")
  71 + private BigDecimal totalAmount;
  72 +
  73 + @Schema(description = "出库明细")
  74 + private List<WmsShipmentOrderDetailRespVO> details;
  75 +
  76 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  77 + @ExcelProperty("创建时间")
  78 + private LocalDateTime createTime;
  79 +
  80 + @Schema(description = "创建者", example = "1")
  81 + private String creator;
  82 + @Schema(description = "创建者名称", example = "芋道")
  83 + private String creatorName;
  84 +
  85 + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
  86 + private LocalDateTime updateTime;
  87 +
  88 + @Schema(description = "更新者", example = "1")
  89 + private String updater;
  90 + @Schema(description = "更新者名称", example = "芋道")
  91 + private String updaterName;
  92 +
  93 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/shipment/vo/order/WmsShipmentOrderSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.detail.WmsShipmentOrderDetailSaveReqVO;
  5 +import cn.iocoder.yudao.module.wms.enums.order.WmsShipmentOrderTypeEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import jakarta.validation.Valid;
  8 +import jakarta.validation.constraints.NotBlank;
  9 +import jakarta.validation.constraints.NotNull;
  10 +import jakarta.validation.constraints.Size;
  11 +import lombok.Data;
  12 +
  13 +import java.math.BigDecimal;
  14 +import java.util.List;
  15 +
  16 +@Schema(description = "管理后台 - WMS 出库单保存 Request VO")
  17 +@Data
  18 +public class WmsShipmentOrderSaveReqVO {
  19 +
  20 + @Schema(description = "编号", example = "1024")
  21 + private Long id;
  22 +
  23 + @Schema(description = "出库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "CK202605110001")
  24 + @NotBlank(message = "出库单号不能为空")
  25 + @Size(max = 64, message = "出库单号长度不能超过 64 个字符")
  26 + private String no;
  27 +
  28 + @Schema(description = "出库类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "201")
  29 + @NotNull(message = "出库类型不能为空")
  30 + @InEnum(WmsShipmentOrderTypeEnum.class)
  31 + private Integer type;
  32 +
  33 + @Schema(description = "业务单号", example = "SO202605110001")
  34 + @Size(max = 64, message = "业务单号长度不能超过 64 个字符")
  35 + private String bizOrderNo;
  36 +
  37 + @Schema(description = "客户编号", example = "1024")
  38 + private Long merchantId;
  39 +
  40 + @Schema(description = "备注", example = "备注")
  41 + @Size(max = 255, message = "备注长度不能超过 255 个字符")
  42 + private String remark;
  43 +
  44 + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  45 + @NotNull(message = "仓库不能为空")
  46 + private Long warehouseId;
  47 +
  48 + @Schema(description = "库区编号", example = "1024")
  49 + private Long areaId;
  50 +
  51 + @Schema(description = "出库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
  52 + @NotNull(message = "出库数量不能为空")
  53 + private BigDecimal totalQuantity;
  54 +
  55 + @Schema(description = "总金额", example = "1000.00")
  56 + private BigDecimal totalAmount;
  57 +
  58 + @Schema(description = "出库明细")
  59 + @Valid
  60 + private List<WmsShipmentOrderDetailSaveReqVO> details;
  61 +
  62 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/shipment/WmsShipmentOrderDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.merchant.WmsMerchantDO;
  5 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO;
  6 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO;
  7 +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants;
  8 +import cn.iocoder.yudao.module.wms.enums.order.WmsShipmentOrderTypeEnum;
  9 +import com.baomidou.mybatisplus.annotation.KeySequence;
  10 +import com.baomidou.mybatisplus.annotation.TableId;
  11 +import com.baomidou.mybatisplus.annotation.TableName;
  12 +import lombok.*;
  13 +
  14 +import java.math.BigDecimal;
  15 +
  16 +/**
  17 + * WMS 出库单 DO
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@TableName("wms_shipment_order")
  22 +@KeySequence("wms_shipment_order_seq")
  23 +@Data
  24 +@EqualsAndHashCode(callSuper = true)
  25 +@ToString(callSuper = true)
  26 +@Builder
  27 +@NoArgsConstructor
  28 +@AllArgsConstructor
  29 +public class WmsShipmentOrderDO extends BaseDO {
  30 +
  31 + /**
  32 + * 主键编号
  33 + */
  34 + @TableId
  35 + private Long id;
  36 + /**
  37 + * 出库单号
  38 + */
  39 + private String no;
  40 + /**
  41 + * 出库类型
  42 + *
  43 + * 枚举 {@link WmsShipmentOrderTypeEnum}
  44 + * 字典 {@link DictTypeConstants#SHIPMENT_ORDER_TYPE}
  45 + */
  46 + private Integer type;
  47 + /**
  48 + * 出库状态
  49 + *
  50 + * 字典 {@link DictTypeConstants#ORDER_STATUS}
  51 + */
  52 + private Integer status;
  53 + /**
  54 + * 业务订单号
  55 + */
  56 + private String bizOrderNo;
  57 + /**
  58 + * 客户编号
  59 + *
  60 + * 关联 {@link WmsMerchantDO#getId()}
  61 + */
  62 + private Long merchantId;
  63 + /**
  64 + * 备注
  65 + */
  66 + private String remark;
  67 +
  68 + // ========= 仓库库区字段 =========
  69 +
  70 + /**
  71 + * 仓库编号
  72 + *
  73 + * 关联 {@link WmsWarehouseDO#getId()}
  74 + */
  75 + private Long warehouseId;
  76 + /**
  77 + * 库区编号
  78 + *
  79 + * 关联 {@link WmsWarehouseAreaDO#getId()}
  80 + */
  81 + private Long areaId;
  82 +
  83 + // ========= 汇总金额字段 =========
  84 +
  85 + /**
  86 + * 总数量
  87 + */
  88 + private BigDecimal totalQuantity;
  89 + /**
  90 + * 总金额
  91 + */
  92 + private BigDecimal totalAmount;
  93 +
  94 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/shipment/WmsShipmentOrderDetailDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDetailDO;
  5 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
  6 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO;
  7 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO;
  8 +import com.baomidou.mybatisplus.annotation.KeySequence;
  9 +import com.baomidou.mybatisplus.annotation.TableId;
  10 +import com.baomidou.mybatisplus.annotation.TableName;
  11 +import lombok.*;
  12 +
  13 +import java.math.BigDecimal;
  14 +import java.time.LocalDateTime;
  15 +
  16 +/**
  17 + * WMS 出库单明细 DO
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@TableName("wms_shipment_order_detail")
  22 +@KeySequence("wms_shipment_order_detail_seq")
  23 +@Data
  24 +@EqualsAndHashCode(callSuper = true)
  25 +@ToString(callSuper = true)
  26 +@Builder
  27 +@NoArgsConstructor
  28 +@AllArgsConstructor
  29 +public class WmsShipmentOrderDetailDO extends BaseDO {
  30 +
  31 + /**
  32 + * 主键编号
  33 + */
  34 + @TableId
  35 + private Long id;
  36 + /**
  37 + * 备注
  38 + */
  39 + private String remark;
  40 +
  41 + // ========= 单据商品字段 =========
  42 +
  43 + /**
  44 + * 出库单编号
  45 + *
  46 + * 关联 {@link WmsShipmentOrderDO#getId()}
  47 + */
  48 + private Long orderId;
  49 + /**
  50 + * 商品 SKU 编号
  51 + *
  52 + * 关联 {@link WmsItemSkuDO#getId()}
  53 + */
  54 + private Long skuId;
  55 +
  56 + // ========= 仓库库区字段 =========
  57 +
  58 + /**
  59 + * 仓库编号
  60 + *
  61 + * 关联 {@link WmsWarehouseDO#getId()}
  62 + */
  63 + private Long warehouseId;
  64 + /**
  65 + * 库区编号
  66 + *
  67 + * 关联 {@link WmsWarehouseAreaDO#getId()}
  68 + */
  69 + private Long areaId;
  70 + /**
  71 + * 库存明细编号
  72 + *
  73 + * 关联 {@link WmsInventoryDetailDO#getId()}
  74 + */
  75 + private Long inventoryDetailId;
  76 +
  77 + // ========= 批次效期字段 =========
  78 +
  79 + /**
  80 + * 批号
  81 + */
  82 + private String batchNo;
  83 + /**
  84 + * 生产日期
  85 + */
  86 + private LocalDateTime productionDate;
  87 + /**
  88 + * 过期日期
  89 + */
  90 + private LocalDateTime expirationDate;
  91 +
  92 + // ========= 汇总金额字段 =========
  93 +
  94 + /**
  95 + * 出库数量
  96 + */
  97 + private BigDecimal quantity;
  98 + /**
  99 + * 金额(小计、非单价)
  100 + */
  101 + private BigDecimal amount;
  102 +
  103 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryDetailMapper.java
@@ -8,8 +8,10 @@ import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.detail.WmsInven @@ -8,8 +8,10 @@ import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.detail.WmsInven
8 import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDetailDO; 8 import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDetailDO;
9 import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; 9 import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO;
10 import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; 10 import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
  11 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
11 import org.apache.ibatis.annotations.Mapper; 12 import org.apache.ibatis.annotations.Mapper;
12 13
  14 +import java.math.BigDecimal;
13 import java.time.LocalDate; 15 import java.time.LocalDate;
14 import java.time.LocalDateTime; 16 import java.time.LocalDateTime;
15 import java.time.LocalTime; 17 import java.time.LocalTime;
@@ -35,6 +37,7 @@ public interface WmsInventoryDetailMapper extends BaseMapperX&lt;WmsInventoryDetail @@ -35,6 +37,7 @@ public interface WmsInventoryDetailMapper extends BaseMapperX&lt;WmsInventoryDetail
35 .eqIfPresent(WmsInventoryDetailDO::getWarehouseId, reqVO.getWarehouseId()) 37 .eqIfPresent(WmsInventoryDetailDO::getWarehouseId, reqVO.getWarehouseId())
36 .eqIfPresent(WmsInventoryDetailDO::getAreaId, reqVO.getAreaId()) 38 .eqIfPresent(WmsInventoryDetailDO::getAreaId, reqVO.getAreaId())
37 .likeIfPresent(WmsInventoryDetailDO::getBatchNo, reqVO.getBatchNo()) 39 .likeIfPresent(WmsInventoryDetailDO::getBatchNo, reqVO.getBatchNo())
  40 + .geIfPresent(WmsInventoryDetailDO::getRemainQuantity, reqVO.getMinRemainQuantity())
38 .betweenIfPresent(WmsInventoryDetailDO::getCreateTime, reqVO.getCreateTime()); 41 .betweenIfPresent(WmsInventoryDetailDO::getCreateTime, reqVO.getCreateTime());
39 appendDaysToExpiresQuery(query, reqVO.getDaysToExpires()); 42 appendDaysToExpiresQuery(query, reqVO.getDaysToExpires());
40 appendDimensionOrder(query, reqVO.getType()); 43 appendDimensionOrder(query, reqVO.getType());
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/shipment/WmsShipmentOrderDetailMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.dal.mysql.order.shipment;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  5 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO;
  6 +import org.apache.ibatis.annotations.Mapper;
  7 +
  8 +import java.util.Collection;
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * WMS 出库单明细 Mapper
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Mapper
  17 +public interface WmsShipmentOrderDetailMapper extends BaseMapperX<WmsShipmentOrderDetailDO> {
  18 +
  19 + default List<WmsShipmentOrderDetailDO> selectListByOrderId(Long orderId) {
  20 + return selectList(WmsShipmentOrderDetailDO::getOrderId, orderId);
  21 + }
  22 +
  23 + default List<WmsShipmentOrderDetailDO> selectListByOrderIds(Collection<Long> orderIds) {
  24 + return selectList(new LambdaQueryWrapperX<WmsShipmentOrderDetailDO>()
  25 + .inIfPresent(WmsShipmentOrderDetailDO::getOrderId, orderIds)
  26 + .orderByAsc(WmsShipmentOrderDetailDO::getOrderId)
  27 + .orderByAsc(WmsShipmentOrderDetailDO::getId));
  28 + }
  29 +
  30 + default void deleteByOrderId(Long orderId) {
  31 + delete(WmsShipmentOrderDetailDO::getOrderId, orderId);
  32 + }
  33 +
  34 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/shipment/WmsShipmentOrderMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.dal.mysql.order.shipment;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderPageReqVO;
  7 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +/**
  11 + * WMS 出库单 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface WmsShipmentOrderMapper extends BaseMapperX<WmsShipmentOrderDO> {
  17 +
  18 + default PageResult<WmsShipmentOrderDO> selectPage(WmsShipmentOrderPageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<WmsShipmentOrderDO>()
  20 + .likeIfPresent(WmsShipmentOrderDO::getNo, reqVO.getNo())
  21 + .eqIfPresent(WmsShipmentOrderDO::getStatus, reqVO.getStatus())
  22 + .eqIfPresent(WmsShipmentOrderDO::getWarehouseId, reqVO.getWarehouseId())
  23 + .eqIfPresent(WmsShipmentOrderDO::getMerchantId, reqVO.getMerchantId())
  24 + .betweenIfPresent(WmsShipmentOrderDO::getCreateTime, reqVO.getOrderDate())
  25 + .geIfPresent(WmsShipmentOrderDO::getTotalQuantity, reqVO.getTotalQuantityMin())
  26 + .leIfPresent(WmsShipmentOrderDO::getTotalQuantity, reqVO.getTotalQuantityMax())
  27 + .geIfPresent(WmsShipmentOrderDO::getTotalAmount, reqVO.getTotalAmountMin())
  28 + .leIfPresent(WmsShipmentOrderDO::getTotalAmount, reqVO.getTotalAmountMax())
  29 + .eqIfPresent(WmsShipmentOrderDO::getType, reqVO.getType())
  30 + .likeIfPresent(WmsShipmentOrderDO::getBizOrderNo, reqVO.getBizOrderNo())
  31 + .eqIfPresent(WmsShipmentOrderDO::getCreator, reqVO.getCreator())
  32 + .eqIfPresent(WmsShipmentOrderDO::getUpdater, reqVO.getUpdater())
  33 + .betweenIfPresent(WmsShipmentOrderDO::getCreateTime, reqVO.getCreateTime())
  34 + .betweenIfPresent(WmsShipmentOrderDO::getUpdateTime, reqVO.getUpdateTime())
  35 + .orderByDesc(WmsShipmentOrderDO::getId));
  36 + }
  37 +
  38 + default WmsShipmentOrderDO selectByNo(String no) {
  39 + return selectOne(WmsShipmentOrderDO::getNo, no);
  40 + }
  41 +
  42 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/DictTypeConstants.java
@@ -10,8 +10,7 @@ public interface DictTypeConstants { @@ -10,8 +10,7 @@ public interface DictTypeConstants {
10 String MERCHANT_TYPE = "merchant_type"; 10 String MERCHANT_TYPE = "merchant_type";
11 String ORDER_STATUS = "wms_order_status"; 11 String ORDER_STATUS = "wms_order_status";
12 String RECEIPT_ORDER_TYPE = "wms_receipt_order_type"; 12 String RECEIPT_ORDER_TYPE = "wms_receipt_order_type";
13 - String SHIPMENT_STATUS = "wms_shipment_status";  
14 - String SHIPMENT_TYPE = "wms_shipment_type"; 13 + String SHIPMENT_ORDER_TYPE = "wms_shipment_order_type";
15 String INVENTORY_HISTORY_TYPE = "wms_inventory_history_type"; 14 String INVENTORY_HISTORY_TYPE = "wms_inventory_history_type";
16 String MOVEMENT_STATUS = "wms_movement_status"; 15 String MOVEMENT_STATUS = "wms_movement_status";
17 String CHECK_STATUS = "wms_check_status"; 16 String CHECK_STATUS = "wms_check_status";
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/ErrorCodeConstants.java
@@ -48,6 +48,7 @@ public interface ErrorCodeConstants { @@ -48,6 +48,7 @@ public interface ErrorCodeConstants {
48 // ========== WMS 基础数据-往来企业 1-060-105-000 ========== 48 // ========== WMS 基础数据-往来企业 1-060-105-000 ==========
49 ErrorCode MERCHANT_NOT_EXISTS = new ErrorCode(1_060_105_000, "往来企业不存在"); 49 ErrorCode MERCHANT_NOT_EXISTS = new ErrorCode(1_060_105_000, "往来企业不存在");
50 ErrorCode MERCHANT_NOT_SUPPLIER = new ErrorCode(1_060_105_001, "往来企业必须是供应商或客户/供应商类型"); 50 ErrorCode MERCHANT_NOT_SUPPLIER = new ErrorCode(1_060_105_001, "往来企业必须是供应商或客户/供应商类型");
  51 + ErrorCode MERCHANT_NOT_CUSTOMER = new ErrorCode(1_060_105_002, "往来企业必须是客户或客户/供应商类型");
51 52
52 // ========== WMS 入库单 1-060-200-000 ========== 53 // ========== WMS 入库单 1-060-200-000 ==========
53 ErrorCode RECEIPT_ORDER_NOT_EXISTS = new ErrorCode(1_060_200_000, "入库单不存在"); 54 ErrorCode RECEIPT_ORDER_NOT_EXISTS = new ErrorCode(1_060_200_000, "入库单不存在");
@@ -58,4 +59,18 @@ public interface ErrorCodeConstants { @@ -58,4 +59,18 @@ public interface ErrorCodeConstants {
58 ErrorCode RECEIPT_ORDER_STATUS_NOT_DELETABLE = new ErrorCode(1_060_200_005, "入库单状态不是草稿或已作废,不能删除"); 59 ErrorCode RECEIPT_ORDER_STATUS_NOT_DELETABLE = new ErrorCode(1_060_200_005, "入库单状态不是草稿或已作废,不能删除");
59 ErrorCode RECEIPT_ORDER_DETAIL_NOT_EXISTS = new ErrorCode(1_060_200_007, "入库单明细不存在"); 60 ErrorCode RECEIPT_ORDER_DETAIL_NOT_EXISTS = new ErrorCode(1_060_200_007, "入库单明细不存在");
60 61
  62 + // ========== WMS 出库单 1-060-201-000 ==========
  63 + ErrorCode SHIPMENT_ORDER_NOT_EXISTS = new ErrorCode(1_060_201_000, "出库单不存在");
  64 + ErrorCode SHIPMENT_ORDER_NO_DUPLICATE = new ErrorCode(1_060_201_001, "出库单号重复");
  65 + ErrorCode SHIPMENT_ORDER_STATUS_NOT_PREPARE = new ErrorCode(1_060_201_002, "出库单状态不是草稿,不能操作");
  66 + ErrorCode SHIPMENT_ORDER_DETAIL_REQUIRED = new ErrorCode(1_060_201_003, "出库单至少包含一条明细");
  67 + ErrorCode SHIPMENT_ORDER_AREA_REQUIRED = new ErrorCode(1_060_201_004, "库区模式下,出库单库区不能为空");
  68 + ErrorCode SHIPMENT_ORDER_STATUS_NOT_DELETABLE = new ErrorCode(1_060_201_005, "出库单状态不是草稿或已作废,不能删除");
  69 + ErrorCode SHIPMENT_ORDER_DETAIL_NOT_EXISTS = new ErrorCode(1_060_201_007, "出库单明细不存在");
  70 +
  71 + // ========== WMS 库存 1-060-300-000 ==========
  72 + ErrorCode INVENTORY_QUANTITY_NOT_ENOUGH = new ErrorCode(1_060_300_000, "库存不足,当前库存:{},变更数量:{}");
  73 + ErrorCode INVENTORY_DETAIL_NOT_EXISTS = new ErrorCode(1_060_300_001, "库存明细不存在");
  74 + ErrorCode INVENTORY_DETAIL_REMAIN_QUANTITY_NOT_ENOUGH = new ErrorCode(1_060_300_002, "库存明细剩余数量不足,当前剩余:{},出库数量:{}");
  75 +
61 } 76 }
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsOrderTypeConstants.java
@@ -19,4 +19,10 @@ public final class WmsOrderTypeConstants { @@ -19,4 +19,10 @@ public final class WmsOrderTypeConstants {
19 public static final int RECEIPT_RETURN = 102; // 退货入库:WmsReceiptOrderDO 19 public static final int RECEIPT_RETURN = 102; // 退货入库:WmsReceiptOrderDO
20 public static final int RECEIPT_GIVE_BACK = 103; // 归还入库:WmsReceiptOrderDO 20 public static final int RECEIPT_GIVE_BACK = 103; // 归还入库:WmsReceiptOrderDO
21 21
  22 + // ========== 出库单类型 [200, 300) ==========
  23 +
  24 + public static final int SHIPMENT_RETURN = 200; // 退货出库:WmsShipmentOrderDO
  25 + public static final int SHIPMENT_SALE = 201; // 销售出库:WmsShipmentOrderDO
  26 + public static final int SHIPMENT_PRODUCTION = 202; // 生产出库:WmsShipmentOrderDO
  27 +
22 } 28 }
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/order/WmsShipmentOrderTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.enums.order;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * WMS 出库单类型枚举
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Getter
  15 +@AllArgsConstructor
  16 +public enum WmsShipmentOrderTypeEnum implements ArrayValuable<Integer> {
  17 +
  18 + RETURN(WmsOrderTypeConstants.SHIPMENT_RETURN, "退货出库"),
  19 + SALE(WmsOrderTypeConstants.SHIPMENT_SALE, "销售出库"),
  20 + PRODUCTION(WmsOrderTypeConstants.SHIPMENT_PRODUCTION, "生产出库");
  21 +
  22 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(WmsShipmentOrderTypeEnum::getType).toArray(Integer[]::new);
  23 +
  24 + /**
  25 + * 类型
  26 + */
  27 + private final Integer type;
  28 + /**
  29 + * 名称
  30 + */
  31 + private final String name;
  32 +
  33 + @Override
  34 + public Integer[] array() {
  35 + return ARRAYS;
  36 + }
  37 +
  38 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/dto/WmsInventoryChangeReqDTO.java
@@ -54,6 +54,10 @@ public class WmsInventoryChangeReqDTO { @@ -54,6 +54,10 @@ public class WmsInventoryChangeReqDTO {
54 */ 54 */
55 private Long areaId; 55 private Long areaId;
56 /** 56 /**
  57 + * 库存明细编号
  58 + */
  59 + private Long inventoryDetailId;
  60 + /**
57 * 变更数量 61 * 变更数量
58 */ 62 */
59 private BigDecimal quantity; 63 private BigDecimal quantity;
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/merchant/WmsMerchantService.java
@@ -55,9 +55,19 @@ public interface WmsMerchantService { @@ -55,9 +55,19 @@ public interface WmsMerchantService {
55 * @param id 编号 55 * @param id 编号
56 * @return 往来企业 56 * @return 往来企业
57 */ 57 */
  58 + @SuppressWarnings("UnusedReturnValue")
58 WmsMerchantDO validateSupplierMerchantExists(Long id); 59 WmsMerchantDO validateSupplierMerchantExists(Long id);
59 60
60 /** 61 /**
  62 + * 校验客户存在
  63 + *
  64 + * @param id 编号
  65 + * @return 往来企业
  66 + */
  67 + @SuppressWarnings("UnusedReturnValue")
  68 + WmsMerchantDO validateCustomerMerchantExists(Long id);
  69 +
  70 + /**
61 * 获得往来企业 71 * 获得往来企业
62 * 72 *
63 * @param id 编号 73 * @param id 编号
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/merchant/WmsMerchantServiceImpl.java
@@ -79,6 +79,16 @@ public class WmsMerchantServiceImpl implements WmsMerchantService { @@ -79,6 +79,16 @@ public class WmsMerchantServiceImpl implements WmsMerchantService {
79 } 79 }
80 80
81 @Override 81 @Override
  82 + public WmsMerchantDO validateCustomerMerchantExists(Long id) {
  83 + WmsMerchantDO merchant = validateMerchantExists(id);
  84 + if (ObjectUtil.notEqual(merchant.getType(), WmsMerchantTypeEnum.CUSTOMER.getType())
  85 + && ObjectUtil.notEqual(merchant.getType(), WmsMerchantTypeEnum.CUSTOMER_SUPPLIER.getType())) {
  86 + throw exception(MERCHANT_NOT_CUSTOMER);
  87 + }
  88 + return merchant;
  89 + }
  90 +
  91 + @Override
82 public WmsMerchantDO getMerchant(Long id) { 92 public WmsMerchantDO getMerchant(Long id) {
83 return merchantMapper.selectById(id); 93 return merchantMapper.selectById(id);
84 } 94 }
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/receipt/WmsReceiptOrderServiceImpl.java
@@ -171,6 +171,14 @@ public class WmsReceiptOrderServiceImpl implements WmsReceiptOrderService { @@ -171,6 +171,14 @@ public class WmsReceiptOrderServiceImpl implements WmsReceiptOrderService {
171 return order; 171 return order;
172 } 172 }
173 173
  174 + /**
  175 + * 增加入库单对应库存。
  176 + *
  177 + * 入库在库存变更模型中使用正数数量;批次模式下,会根据入库明细生成新的库存明细。
  178 + *
  179 + * @param order 入库单
  180 + * @param details 入库单明细列表
  181 + */
174 private void createInventory(WmsReceiptOrderDO order, List<WmsReceiptOrderDetailDO> details) { 182 private void createInventory(WmsReceiptOrderDO order, List<WmsReceiptOrderDetailDO> details) {
175 List<WmsInventoryChangeReqDTO.Item> items = convertList(details, 183 List<WmsInventoryChangeReqDTO.Item> items = convertList(details,
176 detail -> BeanUtils.toBean(detail, WmsInventoryChangeReqDTO.Item.class)); 184 detail -> BeanUtils.toBean(detail, WmsInventoryChangeReqDTO.Item.class));
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderDetailService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.service.order.shipment;
  2 +
  3 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO;
  4 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO;
  5 +
  6 +import java.util.Collection;
  7 +import java.util.List;
  8 +
  9 +/**
  10 + * WMS 出库单明细 Service 接口
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface WmsShipmentOrderDetailService {
  15 +
  16 + /**
  17 + * 创建出库单明细列表
  18 + *
  19 + * @param orderId 出库单编号
  20 + * @param reqVO 出库单保存信息
  21 + */
  22 + void createShipmentOrderDetailList(Long orderId, WmsShipmentOrderSaveReqVO reqVO);
  23 +
  24 + /**
  25 + * 更新出库单明细列表
  26 + *
  27 + * @param orderId 出库单编号
  28 + * @param reqVO 出库单保存信息
  29 + */
  30 + void updateShipmentOrderDetailList(Long orderId, WmsShipmentOrderSaveReqVO reqVO);
  31 +
  32 + /**
  33 + * 按出库单编号删除明细列表
  34 + *
  35 + * @param orderId 出库单编号
  36 + */
  37 + void deleteShipmentOrderDetailListByOrderId(Long orderId);
  38 +
  39 + /**
  40 + * 按出库单编号获得明细列表
  41 + *
  42 + * @param orderId 出库单编号
  43 + * @return 明细列表
  44 + */
  45 + List<WmsShipmentOrderDetailDO> getShipmentOrderDetailList(Long orderId);
  46 +
  47 + /**
  48 + * 按出库单编号集合获得明细列表
  49 + *
  50 + * @param orderIds 出库单编号集合
  51 + * @return 明细列表
  52 + */
  53 + List<WmsShipmentOrderDetailDO> getShipmentOrderDetailList(Collection<Long> orderIds);
  54 +
  55 + /**
  56 + * 校验出库单明细列表存在
  57 + *
  58 + * @param orderId 出库单编号
  59 + * @return 明细列表
  60 + */
  61 + List<WmsShipmentOrderDetailDO> validateShipmentOrderDetailListExists(Long orderId);
  62 +
  63 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderDetailServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.service.order.shipment;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.collection.ListUtil;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
  7 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO;
  8 +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO;
  9 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO;
  10 +import cn.iocoder.yudao.module.wms.dal.mysql.order.shipment.WmsShipmentOrderDetailMapper;
  11 +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties;
  12 +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
  13 +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService;
  14 +import jakarta.annotation.Resource;
  15 +import org.springframework.stereotype.Service;
  16 +import org.springframework.transaction.annotation.Transactional;
  17 +import org.springframework.validation.annotation.Validated;
  18 +
  19 +import java.util.Collection;
  20 +import java.util.List;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  23 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList;
  25 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*;
  26 +
  27 +/**
  28 + * WMS 出库单明细 Service 实现类
  29 + *
  30 + * @author 芋道源码
  31 + */
  32 +@Service
  33 +@Validated
  34 +public class WmsShipmentOrderDetailServiceImpl implements WmsShipmentOrderDetailService {
  35 +
  36 + @Resource
  37 + private WmsShipmentOrderDetailMapper shipmentOrderDetailMapper;
  38 + @Resource
  39 + private WmsItemSkuService itemSkuService;
  40 + @Resource
  41 + private WmsWarehouseAreaService warehouseAreaService;
  42 + @Resource
  43 + private WmsProperties wmsProperties;
  44 +
  45 + @Override
  46 + @Transactional(rollbackFor = Exception.class)
  47 + public void createShipmentOrderDetailList(Long orderId, WmsShipmentOrderSaveReqVO reqVO) {
  48 + List<WmsShipmentOrderDetailDO> list = buildShipmentOrderDetailList(reqVO);
  49 + if (CollUtil.isEmpty(list)) {
  50 + return;
  51 + }
  52 + list.forEach(detail -> detail.setId(null).setOrderId(orderId));
  53 + shipmentOrderDetailMapper.insertBatch(list);
  54 + }
  55 +
  56 + @Override
  57 + @Transactional(rollbackFor = Exception.class)
  58 + public void updateShipmentOrderDetailList(Long orderId, WmsShipmentOrderSaveReqVO reqVO) {
  59 + // 第一步,对比新老数据,获得添加、修改、删除的列表
  60 + List<WmsShipmentOrderDetailDO> oldList = shipmentOrderDetailMapper.selectListByOrderId(orderId);
  61 + List<WmsShipmentOrderDetailDO> list = buildShipmentOrderDetailList(reqVO);
  62 + List<WmsShipmentOrderDetailDO> newList = CollUtil.isEmpty(list) ? ListUtil.of() : list;
  63 + List<List<WmsShipmentOrderDetailDO>> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录
  64 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));
  65 +
  66 + // 第二步,批量添加、修改、删除
  67 + if (CollUtil.isNotEmpty(diffList.get(0))) {
  68 + if (CollUtil.isNotEmpty(convertList(diffList.get(0), WmsShipmentOrderDetailDO::getId))) {
  69 + throw exception(SHIPMENT_ORDER_DETAIL_NOT_EXISTS);
  70 + }
  71 + diffList.get(0).forEach(detail -> detail.setOrderId(orderId));
  72 + shipmentOrderDetailMapper.insertBatch(diffList.get(0));
  73 + }
  74 + if (CollUtil.isNotEmpty(diffList.get(1))) {
  75 + diffList.get(1).forEach(detail -> detail.setOrderId(orderId));
  76 + shipmentOrderDetailMapper.updateBatch(diffList.get(1));
  77 + }
  78 + if (CollUtil.isNotEmpty(diffList.get(2))) {
  79 + shipmentOrderDetailMapper.deleteByIds(convertList(diffList.get(2), WmsShipmentOrderDetailDO::getId));
  80 + }
  81 + }
  82 +
  83 + @Override
  84 + public void deleteShipmentOrderDetailListByOrderId(Long orderId) {
  85 + shipmentOrderDetailMapper.deleteByOrderId(orderId);
  86 + }
  87 +
  88 + @Override
  89 + public List<WmsShipmentOrderDetailDO> getShipmentOrderDetailList(Long orderId) {
  90 + return shipmentOrderDetailMapper.selectListByOrderId(orderId);
  91 + }
  92 +
  93 + @Override
  94 + public List<WmsShipmentOrderDetailDO> getShipmentOrderDetailList(Collection<Long> orderIds) {
  95 + if (CollUtil.isEmpty(orderIds)) {
  96 + return ListUtil.of();
  97 + }
  98 + return shipmentOrderDetailMapper.selectListByOrderIds(orderIds);
  99 + }
  100 +
  101 + @Override
  102 + public List<WmsShipmentOrderDetailDO> validateShipmentOrderDetailListExists(Long orderId) {
  103 + List<WmsShipmentOrderDetailDO> details = shipmentOrderDetailMapper.selectListByOrderId(orderId);
  104 + if (CollUtil.isEmpty(details)) {
  105 + throw exception(SHIPMENT_ORDER_DETAIL_REQUIRED);
  106 + }
  107 + if (wmsProperties.isAreaEnabled() && details.stream()
  108 + .anyMatch(detail -> ObjectUtils.equalsAny(detail.getAreaId(), null, WmsWarehouseAreaDO.ID_EMPTY))) {
  109 + throw exception(SHIPMENT_ORDER_AREA_REQUIRED);
  110 + }
  111 + return details;
  112 + }
  113 +
  114 + private List<WmsShipmentOrderDetailDO> buildShipmentOrderDetailList(WmsShipmentOrderSaveReqVO reqVO) {
  115 + if (CollUtil.isEmpty(reqVO.getDetails())) {
  116 + return ListUtil.of();
  117 + }
  118 + return convertList(reqVO.getDetails(), detail -> {
  119 + // 校验 SKU 存在
  120 + itemSkuService.validateItemSkuExists(detail.getSkuId());
  121 + // 校验仓库区域存在,并且属于当前仓库
  122 + Long areaId = warehouseAreaService.validateAndNormalizeWarehouseAreaId(
  123 + detail.getAreaId() == null ? reqVO.getAreaId() : detail.getAreaId(), reqVO.getWarehouseId());
  124 + // 构建对象
  125 + return BeanUtils.toBean(detail, WmsShipmentOrderDetailDO.class)
  126 + .setWarehouseId(reqVO.getWarehouseId()).setAreaId(areaId);
  127 + });
  128 + }
  129 +
  130 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.service.order.shipment;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderPageReqVO;
  5 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO;
  6 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +/**
  10 + * WMS 出库单 Service 接口
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface WmsShipmentOrderService {
  15 +
  16 + /**
  17 + * 创建出库单
  18 + *
  19 + * @param createReqVO 创建信息
  20 + * @return 编号
  21 + */
  22 + Long createShipmentOrder(@Valid WmsShipmentOrderSaveReqVO createReqVO);
  23 +
  24 + /**
  25 + * 更新出库单
  26 + *
  27 + * @param updateReqVO 更新信息
  28 + */
  29 + void updateShipmentOrder(@Valid WmsShipmentOrderSaveReqVO updateReqVO);
  30 +
  31 + /**
  32 + * 删除出库单
  33 + *
  34 + * @param id 编号
  35 + */
  36 + void deleteShipmentOrder(Long id);
  37 +
  38 + /**
  39 + * 完成出库
  40 + *
  41 + * @param id 编号
  42 + */
  43 + void completeShipmentOrder(Long id);
  44 +
  45 + /**
  46 + * 作废出库单
  47 + *
  48 + * @param id 编号
  49 + */
  50 + void cancelShipmentOrder(Long id);
  51 +
  52 + /**
  53 + * 获得出库单
  54 + *
  55 + * @param id 编号
  56 + * @return 出库单
  57 + */
  58 + WmsShipmentOrderDO getShipmentOrder(Long id);
  59 +
  60 + /**
  61 + * 获得出库单分页
  62 + *
  63 + * @param pageReqVO 分页查询
  64 + * @return 出库单分页
  65 + */
  66 + PageResult<WmsShipmentOrderDO> getShipmentOrderPage(WmsShipmentOrderPageReqVO pageReqVO);
  67 +
  68 +}
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.service.order.shipment;
  2 +
  3 +import cn.hutool.core.util.ObjectUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderPageReqVO;
  7 +import cn.iocoder.yudao.module.wms.controller.admin.order.shipment.vo.order.WmsShipmentOrderSaveReqVO;
  8 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDO;
  9 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO;
  10 +import cn.iocoder.yudao.module.wms.dal.mysql.order.shipment.WmsShipmentOrderMapper;
  11 +import cn.iocoder.yudao.module.wms.enums.inventory.WmsInventoryOrderTypeEnum;
  12 +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum;
  13 +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService;
  14 +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO;
  15 +import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService;
  16 +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService;
  17 +import jakarta.annotation.Resource;
  18 +import org.springframework.stereotype.Service;
  19 +import org.springframework.transaction.annotation.Transactional;
  20 +import org.springframework.validation.annotation.Validated;
  21 +
  22 +import java.math.BigDecimal;
  23 +import java.util.List;
  24 +
  25 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  27 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*;
  28 +
  29 +/**
  30 + * WMS 出库单 Service 实现类
  31 + *
  32 + * @author 芋道源码
  33 + */
  34 +@Service
  35 +@Validated
  36 +public class WmsShipmentOrderServiceImpl implements WmsShipmentOrderService {
  37 +
  38 + @Resource
  39 + private WmsShipmentOrderMapper shipmentOrderMapper;
  40 + @Resource
  41 + private WmsShipmentOrderDetailService shipmentOrderDetailService;
  42 + @Resource
  43 + private WmsWarehouseService warehouseService;
  44 + @Resource
  45 + private WmsMerchantService merchantService;
  46 + @Resource
  47 + private WmsInventoryService inventoryService;
  48 +
  49 + @Override
  50 + @Transactional(rollbackFor = Exception.class)
  51 + public Long createShipmentOrder(WmsShipmentOrderSaveReqVO createReqVO) {
  52 + // 1. 校验出库单保存数据
  53 + validateShipmentOrderSaveData(createReqVO);
  54 +
  55 + // 2.1 插入出库单
  56 + WmsShipmentOrderDO order = BeanUtils.toBean(createReqVO, WmsShipmentOrderDO.class);
  57 + order.setStatus(WmsOrderStatusEnum.PREPARE.getStatus());
  58 + shipmentOrderMapper.insert(order);
  59 + // 2.2 插入出库单明细
  60 + shipmentOrderDetailService.createShipmentOrderDetailList(order.getId(), createReqVO);
  61 + return order.getId();
  62 + }
  63 +
  64 + @Override
  65 + @Transactional(rollbackFor = Exception.class)
  66 + public void updateShipmentOrder(WmsShipmentOrderSaveReqVO updateReqVO) {
  67 + // 1. 校验出库单保存数据
  68 + validateShipmentOrderPrepare(updateReqVO.getId());
  69 + validateShipmentOrderSaveData(updateReqVO);
  70 +
  71 + // 2.1 更新出库单
  72 + WmsShipmentOrderDO updateObj = BeanUtils.toBean(updateReqVO, WmsShipmentOrderDO.class)
  73 + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus());
  74 + shipmentOrderMapper.updateById(updateObj);
  75 + // 2.2 更新出库单明细
  76 + shipmentOrderDetailService.updateShipmentOrderDetailList(updateReqVO.getId(), updateReqVO);
  77 + }
  78 +
  79 + @Override
  80 + @Transactional(rollbackFor = Exception.class)
  81 + public void deleteShipmentOrder(Long id) {
  82 + // 1. 校验存在,且可删除
  83 + WmsShipmentOrderDO order = validateShipmentOrderExists(id);
  84 + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus())
  85 + && ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.CANCELED.getStatus())) {
  86 + throw exception(SHIPMENT_ORDER_STATUS_NOT_DELETABLE);
  87 + }
  88 +
  89 + // 2.1 删除出库单
  90 + shipmentOrderMapper.deleteById(id);
  91 + // 2.2 删除出库单明细
  92 + shipmentOrderDetailService.deleteShipmentOrderDetailListByOrderId(id);
  93 + }
  94 +
  95 + @Override
  96 + @Transactional(rollbackFor = Exception.class)
  97 + public void completeShipmentOrder(Long id) {
  98 + // 1.1 校验存在,且草稿
  99 + WmsShipmentOrderDO order = validateShipmentOrderPrepare(id);
  100 + // 1.2 校验出库单明细存在
  101 + List<WmsShipmentOrderDetailDO> details = shipmentOrderDetailService.validateShipmentOrderDetailListExists(id);
  102 +
  103 + // 2. 完成出库单
  104 + shipmentOrderMapper.updateById(new WmsShipmentOrderDO().setId(id)
  105 + .setStatus(WmsOrderStatusEnum.FINISHED.getStatus()));
  106 +
  107 + // 3. 扣减库存
  108 + changeInventory(order, details);
  109 + }
  110 +
  111 + @Override
  112 + @Transactional(rollbackFor = Exception.class)
  113 + public void cancelShipmentOrder(Long id) {
  114 + // 1. 校验存在,且草稿
  115 + validateShipmentOrderPrepare(id);
  116 +
  117 + // 2. 作废出库单
  118 + shipmentOrderMapper.updateById(new WmsShipmentOrderDO().setId(id)
  119 + .setStatus(WmsOrderStatusEnum.CANCELED.getStatus()));
  120 + }
  121 +
  122 + @Override
  123 + public WmsShipmentOrderDO getShipmentOrder(Long id) {
  124 + return shipmentOrderMapper.selectById(id);
  125 + }
  126 +
  127 + @Override
  128 + public PageResult<WmsShipmentOrderDO> getShipmentOrderPage(WmsShipmentOrderPageReqVO pageReqVO) {
  129 + return shipmentOrderMapper.selectPage(pageReqVO);
  130 + }
  131 +
  132 + private void validateShipmentOrderSaveData(WmsShipmentOrderSaveReqVO reqVO) {
  133 + // 校验出库单号唯一
  134 + validateShipmentOrderNoUnique(reqVO.getId(), reqVO.getNo());
  135 + // 校验仓库存在
  136 + warehouseService.validateWarehouseExists(reqVO.getWarehouseId());
  137 + // 校验客户类型
  138 + if (reqVO.getMerchantId() != null) {
  139 + merchantService.validateCustomerMerchantExists(reqVO.getMerchantId());
  140 + }
  141 + }
  142 +
  143 + private void validateShipmentOrderNoUnique(Long id, String no) {
  144 + WmsShipmentOrderDO order = shipmentOrderMapper.selectByNo(no);
  145 + if (order == null) {
  146 + return;
  147 + }
  148 + if (id == null || ObjectUtil.notEqual(order.getId(), id)) {
  149 + throw exception(SHIPMENT_ORDER_NO_DUPLICATE);
  150 + }
  151 + }
  152 +
  153 + private WmsShipmentOrderDO validateShipmentOrderExists(Long id) {
  154 + WmsShipmentOrderDO order = id == null ? null : shipmentOrderMapper.selectById(id);
  155 + if (order == null) {
  156 + throw exception(SHIPMENT_ORDER_NOT_EXISTS);
  157 + }
  158 + return order;
  159 + }
  160 +
  161 + /**
  162 + * 校验出库单存在且为草稿状态
  163 + *
  164 + * @param id 出库单编号
  165 + * @return 出库单
  166 + */
  167 + private WmsShipmentOrderDO validateShipmentOrderPrepare(Long id) {
  168 + WmsShipmentOrderDO order = validateShipmentOrderExists(id);
  169 + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus())) {
  170 + throw exception(SHIPMENT_ORDER_STATUS_NOT_PREPARE);
  171 + }
  172 + return order;
  173 + }
  174 +
  175 + /**
  176 + * 扣减出库单对应库存。
  177 + *
  178 + * 出库在库存变更模型中使用负数数量;批次模式下,明细里的 inventoryDetailId 会用于扣减对应库存明细的剩余数量。
  179 + *
  180 + * @param order 出库单
  181 + * @param details 出库单明细列表
  182 + */
  183 + private void changeInventory(WmsShipmentOrderDO order, List<WmsShipmentOrderDetailDO> details) {
  184 + List<WmsInventoryChangeReqDTO.Item> items = convertList(details,
  185 + detail -> BeanUtils.toBean(detail, WmsInventoryChangeReqDTO.Item.class)
  186 + .setQuantity(detail.getQuantity().multiply(BigDecimal.valueOf(-1L))));
  187 + inventoryService.changeInventory(new WmsInventoryChangeReqDTO()
  188 + .setOrderId(order.getId()).setOrderNo(order.getNo())
  189 + .setOrderType(WmsInventoryOrderTypeEnum.SHIPMENT.getType()).setItems(items));
  190 + }
  191 +
  192 +}
yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/order/shipment/WmsShipmentOrderServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.wms.service.order.shipment;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDO;
  5 +import cn.iocoder.yudao.module.wms.dal.dataobject.order.shipment.WmsShipmentOrderDetailDO;
  6 +import cn.iocoder.yudao.module.wms.dal.mysql.order.shipment.WmsShipmentOrderDetailMapper;
  7 +import cn.iocoder.yudao.module.wms.dal.mysql.order.shipment.WmsShipmentOrderMapper;
  8 +import cn.iocoder.yudao.module.wms.enums.inventory.WmsInventoryOrderTypeEnum;
  9 +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum;
  10 +import cn.iocoder.yudao.module.wms.enums.order.WmsShipmentOrderTypeEnum;
  11 +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties;
  12 +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService;
  13 +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO;
  14 +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService;
  15 +import cn.iocoder.yudao.module.wms.service.md.merchant.WmsMerchantService;
  16 +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService;
  17 +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService;
  18 +import jakarta.annotation.Resource;
  19 +import org.junit.jupiter.api.Test;
  20 +import org.mockito.ArgumentCaptor;
  21 +import org.springframework.context.annotation.Import;
  22 +import org.springframework.test.context.bean.override.mockito.MockitoBean;
  23 +
  24 +import java.math.BigDecimal;
  25 +
  26 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  27 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.SHIPMENT_ORDER_AREA_REQUIRED;
  28 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.SHIPMENT_ORDER_DETAIL_REQUIRED;
  29 +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.SHIPMENT_ORDER_STATUS_NOT_DELETABLE;
  30 +import static org.junit.jupiter.api.Assertions.assertEquals;
  31 +import static org.junit.jupiter.api.Assertions.assertNotNull;
  32 +import static org.junit.jupiter.api.Assertions.assertNull;
  33 +import static org.mockito.ArgumentMatchers.any;
  34 +import static org.mockito.Mockito.never;
  35 +import static org.mockito.Mockito.verify;
  36 +
  37 +@Import({WmsShipmentOrderServiceImpl.class, WmsShipmentOrderDetailServiceImpl.class, WmsProperties.class})
  38 +public class WmsShipmentOrderServiceImplTest extends BaseDbUnitTest {
  39 +
  40 + @Resource
  41 + private WmsShipmentOrderServiceImpl shipmentOrderService;
  42 +
  43 + @Resource
  44 + private WmsShipmentOrderMapper shipmentOrderMapper;
  45 + @Resource
  46 + private WmsShipmentOrderDetailMapper shipmentOrderDetailMapper;
  47 +
  48 + @MockitoBean
  49 + private WmsWarehouseService warehouseService;
  50 + @MockitoBean
  51 + private WmsWarehouseAreaService warehouseAreaService;
  52 + @MockitoBean
  53 + private WmsMerchantService merchantService;
  54 + @MockitoBean
  55 + private WmsItemSkuService itemSkuService;
  56 + @MockitoBean
  57 + private WmsInventoryService inventoryService;
  58 +
  59 + @Test
  60 + public void testCompleteShipmentOrder_success() {
  61 + // mock 数据
  62 + Long warehouseId = 100L;
  63 + Long skuId = 200L;
  64 + Long inventoryDetailId = 300L;
  65 + WmsShipmentOrderDO order = createShipmentOrder(warehouseId);
  66 + shipmentOrderMapper.insert(order);
  67 + shipmentOrderDetailMapper.insert(createShipmentOrderDetail(order.getId(), skuId, warehouseId, 400L,
  68 + inventoryDetailId));
  69 +
  70 + // 调用
  71 + shipmentOrderService.completeShipmentOrder(order.getId());
  72 +
  73 + // 断言:出库单
  74 + WmsShipmentOrderDO dbOrder = shipmentOrderMapper.selectById(order.getId());
  75 + assertNotNull(dbOrder);
  76 + assertEquals(WmsOrderStatusEnum.FINISHED.getStatus(), dbOrder.getStatus());
  77 + // 断言:库存变更
  78 + ArgumentCaptor<WmsInventoryChangeReqDTO> captor = ArgumentCaptor.forClass(WmsInventoryChangeReqDTO.class);
  79 + verify(inventoryService).changeInventory(captor.capture());
  80 + WmsInventoryChangeReqDTO inventoryReqDTO = captor.getValue();
  81 + assertEquals(order.getId(), inventoryReqDTO.getOrderId());
  82 + assertEquals(order.getNo(), inventoryReqDTO.getOrderNo());
  83 + assertEquals(WmsInventoryOrderTypeEnum.SHIPMENT.getType(), inventoryReqDTO.getOrderType());
  84 + assertEquals(1, inventoryReqDTO.getItems().size());
  85 + assertEquals(skuId, inventoryReqDTO.getItems().get(0).getSkuId());
  86 + assertEquals(warehouseId, inventoryReqDTO.getItems().get(0).getWarehouseId());
  87 + assertEquals(inventoryDetailId, inventoryReqDTO.getItems().get(0).getInventoryDetailId());
  88 + assertEquals(0, new BigDecimal("-2.00").compareTo(inventoryReqDTO.getItems().get(0).getQuantity()));
  89 + }
  90 +
  91 + @Test
  92 + public void testCompleteShipmentOrder_detailRequired() {
  93 + // mock 数据
  94 + WmsShipmentOrderDO order = createShipmentOrder(100L);
  95 + shipmentOrderMapper.insert(order);
  96 +
  97 + // 调用,并断言
  98 + assertServiceException(() -> shipmentOrderService.completeShipmentOrder(order.getId()),
  99 + SHIPMENT_ORDER_DETAIL_REQUIRED);
  100 + verify(inventoryService, never()).changeInventory(any());
  101 + }
  102 +
  103 + @Test
  104 + public void testCompleteShipmentOrder_areaRequired() {
  105 + // mock 数据
  106 + WmsShipmentOrderDO order = createShipmentOrder(100L);
  107 + shipmentOrderMapper.insert(order);
  108 + shipmentOrderDetailMapper.insert(createShipmentOrderDetail(order.getId(), 200L, 100L, 0L, null));
  109 +
  110 + // 调用,并断言
  111 + assertServiceException(() -> shipmentOrderService.completeShipmentOrder(order.getId()),
  112 + SHIPMENT_ORDER_AREA_REQUIRED);
  113 + verify(inventoryService, never()).changeInventory(any());
  114 + }
  115 +
  116 + @Test
  117 + public void testCancelShipmentOrder_success() {
  118 + // mock 数据
  119 + WmsShipmentOrderDO order = createShipmentOrder(100L);
  120 + shipmentOrderMapper.insert(order);
  121 +
  122 + // 调用
  123 + shipmentOrderService.cancelShipmentOrder(order.getId());
  124 +
  125 + // 断言
  126 + WmsShipmentOrderDO dbOrder = shipmentOrderMapper.selectById(order.getId());
  127 + assertNotNull(dbOrder);
  128 + assertEquals(WmsOrderStatusEnum.CANCELED.getStatus(), dbOrder.getStatus());
  129 + verify(inventoryService, never()).changeInventory(any());
  130 + }
  131 +
  132 + @Test
  133 + public void testDeleteShipmentOrder_canceled() {
  134 + // mock 数据
  135 + WmsShipmentOrderDO order = createShipmentOrder(100L).setStatus(WmsOrderStatusEnum.CANCELED.getStatus());
  136 + shipmentOrderMapper.insert(order);
  137 + shipmentOrderDetailMapper.insert(createShipmentOrderDetail(order.getId(), 200L, 100L, 300L, null));
  138 +
  139 + // 调用
  140 + shipmentOrderService.deleteShipmentOrder(order.getId());
  141 +
  142 + // 断言
  143 + assertNull(shipmentOrderMapper.selectById(order.getId()));
  144 + assertEquals(0, shipmentOrderDetailMapper.selectListByOrderId(order.getId()).size());
  145 + }
  146 +
  147 + @Test
  148 + public void testDeleteShipmentOrder_finished() {
  149 + // mock 数据
  150 + WmsShipmentOrderDO order = createShipmentOrder(100L).setStatus(WmsOrderStatusEnum.FINISHED.getStatus());
  151 + shipmentOrderMapper.insert(order);
  152 +
  153 + // 调用,并断言
  154 + assertServiceException(() -> shipmentOrderService.deleteShipmentOrder(order.getId()),
  155 + SHIPMENT_ORDER_STATUS_NOT_DELETABLE);
  156 + }
  157 +
  158 + private static WmsShipmentOrderDO createShipmentOrder(Long warehouseId) {
  159 + return new WmsShipmentOrderDO()
  160 + .setNo("CK202605120001")
  161 + .setType(WmsShipmentOrderTypeEnum.SALE.getType())
  162 + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus())
  163 + .setWarehouseId(warehouseId)
  164 + .setAreaId(0L)
  165 + .setTotalQuantity(new BigDecimal("2.00"))
  166 + .setTotalAmount(new BigDecimal("20.00"));
  167 + }
  168 +
  169 + private static WmsShipmentOrderDetailDO createShipmentOrderDetail(Long orderId, Long skuId, Long warehouseId,
  170 + Long areaId, Long inventoryDetailId) {
  171 + return WmsShipmentOrderDetailDO.builder()
  172 + .orderId(orderId)
  173 + .skuId(skuId)
  174 + .warehouseId(warehouseId)
  175 + .areaId(areaId)
  176 + .inventoryDetailId(inventoryDetailId)
  177 + .quantity(new BigDecimal("2.00"))
  178 + .amount(new BigDecimal("20.00"))
  179 + .build();
  180 + }
  181 +
  182 +}
yudao-module-wms/src/test/resources/sql/clean.sql
  1 +DELETE FROM "wms_shipment_order_detail";
  2 +DELETE FROM "wms_shipment_order";
1 DELETE FROM "wms_receipt_order_detail"; 3 DELETE FROM "wms_receipt_order_detail";
2 DELETE FROM "wms_receipt_order"; 4 DELETE FROM "wms_receipt_order";
3 DELETE FROM "wms_inventory_history"; 5 DELETE FROM "wms_inventory_history";
yudao-module-wms/src/test/resources/sql/create_tables.sql
@@ -106,8 +106,8 @@ CREATE TABLE IF NOT EXISTS &quot;wms_inventory_history&quot; ( @@ -106,8 +106,8 @@ CREATE TABLE IF NOT EXISTS &quot;wms_inventory_history&quot; (
106 CREATE TABLE IF NOT EXISTS "wms_receipt_order" ( 106 CREATE TABLE IF NOT EXISTS "wms_receipt_order" (
107 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, 107 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
108 "no" varchar(64) NOT NULL, 108 "no" varchar(64) NOT NULL,
109 - "type" tinyint NOT NULL,  
110 - "status" tinyint NOT NULL DEFAULT '0', 109 + "type" int NOT NULL,
  110 + "status" int NOT NULL DEFAULT '0',
111 "biz_order_no" varchar(64) DEFAULT NULL, 111 "biz_order_no" varchar(64) DEFAULT NULL,
112 "merchant_id" bigint DEFAULT NULL, 112 "merchant_id" bigint DEFAULT NULL,
113 "remark" varchar(255) DEFAULT NULL, 113 "remark" varchar(255) DEFAULT NULL,
@@ -144,3 +144,46 @@ CREATE TABLE IF NOT EXISTS &quot;wms_receipt_order_detail&quot; ( @@ -144,3 +144,46 @@ CREATE TABLE IF NOT EXISTS &quot;wms_receipt_order_detail&quot; (
144 "tenant_id" bigint NOT NULL DEFAULT '0', 144 "tenant_id" bigint NOT NULL DEFAULT '0',
145 PRIMARY KEY ("id") 145 PRIMARY KEY ("id")
146 ) COMMENT 'WMS 入库单明细表'; 146 ) COMMENT 'WMS 入库单明细表';
  147 +
  148 +CREATE TABLE IF NOT EXISTS "wms_shipment_order" (
  149 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  150 + "no" varchar(64) NOT NULL,
  151 + "type" int NOT NULL,
  152 + "status" int NOT NULL DEFAULT '0',
  153 + "biz_order_no" varchar(64) DEFAULT NULL,
  154 + "merchant_id" bigint DEFAULT NULL,
  155 + "remark" varchar(255) DEFAULT NULL,
  156 + "warehouse_id" bigint NOT NULL,
  157 + "area_id" bigint NOT NULL DEFAULT '0',
  158 + "total_quantity" decimal(20, 2) NOT NULL DEFAULT '0',
  159 + "total_amount" decimal(10, 2) DEFAULT NULL,
  160 + "creator" varchar(64) DEFAULT '',
  161 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  162 + "updater" varchar(64) DEFAULT '',
  163 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  164 + "deleted" bit NOT NULL DEFAULT FALSE,
  165 + "tenant_id" bigint NOT NULL DEFAULT '0',
  166 + PRIMARY KEY ("id")
  167 +) COMMENT 'WMS 出库单表';
  168 +
  169 +CREATE TABLE IF NOT EXISTS "wms_shipment_order_detail" (
  170 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  171 + "remark" varchar(255) DEFAULT NULL,
  172 + "order_id" bigint NOT NULL,
  173 + "sku_id" bigint NOT NULL,
  174 + "warehouse_id" bigint NOT NULL,
  175 + "area_id" bigint NOT NULL DEFAULT '0',
  176 + "inventory_detail_id" bigint DEFAULT NULL,
  177 + "batch_no" varchar(64) DEFAULT NULL,
  178 + "production_date" timestamp DEFAULT NULL,
  179 + "expiration_date" timestamp DEFAULT NULL,
  180 + "quantity" decimal(20, 2) NOT NULL DEFAULT '0',
  181 + "amount" decimal(10, 2) DEFAULT NULL,
  182 + "creator" varchar(64) DEFAULT '',
  183 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  184 + "updater" varchar(64) DEFAULT '',
  185 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  186 + "deleted" bit NOT NULL DEFAULT FALSE,
  187 + "tenant_id" bigint NOT NULL DEFAULT '0',
  188 + PRIMARY KEY ("id")
  189 +) COMMENT 'WMS 出库单明细表';