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 7 import lombok.ToString;
8 8 import org.springframework.format.annotation.DateTimeFormat;
9 9  
  10 +import java.math.BigDecimal;
10 11 import java.time.LocalDateTime;
11 12  
12 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 54 @Schema(description = "批号", example = "BATCH001")
54 55 private String batchNo;
55 56  
  57 + @Schema(description = "最小剩余库存数量", example = "0.01")
  58 + private BigDecimal minRemainQuantity;
  59 +
56 60 @Schema(description = "距离过期天数", example = "30")
57 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 import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDetailDO;
9 9 import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO;
10 10 import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
  11 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
11 12 import org.apache.ibatis.annotations.Mapper;
12 13  
  14 +import java.math.BigDecimal;
13 15 import java.time.LocalDate;
14 16 import java.time.LocalDateTime;
15 17 import java.time.LocalTime;
... ... @@ -35,6 +37,7 @@ public interface WmsInventoryDetailMapper extends BaseMapperX&lt;WmsInventoryDetail
35 37 .eqIfPresent(WmsInventoryDetailDO::getWarehouseId, reqVO.getWarehouseId())
36 38 .eqIfPresent(WmsInventoryDetailDO::getAreaId, reqVO.getAreaId())
37 39 .likeIfPresent(WmsInventoryDetailDO::getBatchNo, reqVO.getBatchNo())
  40 + .geIfPresent(WmsInventoryDetailDO::getRemainQuantity, reqVO.getMinRemainQuantity())
38 41 .betweenIfPresent(WmsInventoryDetailDO::getCreateTime, reqVO.getCreateTime());
39 42 appendDaysToExpiresQuery(query, reqVO.getDaysToExpires());
40 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 10 String MERCHANT_TYPE = "merchant_type";
11 11 String ORDER_STATUS = "wms_order_status";
12 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 14 String INVENTORY_HISTORY_TYPE = "wms_inventory_history_type";
16 15 String MOVEMENT_STATUS = "wms_movement_status";
17 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 48 // ========== WMS 基础数据-往来企业 1-060-105-000 ==========
49 49 ErrorCode MERCHANT_NOT_EXISTS = new ErrorCode(1_060_105_000, "往来企业不存在");
50 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 53 // ========== WMS 入库单 1-060-200-000 ==========
53 54 ErrorCode RECEIPT_ORDER_NOT_EXISTS = new ErrorCode(1_060_200_000, "入库单不存在");
... ... @@ -58,4 +59,18 @@ public interface ErrorCodeConstants {
58 59 ErrorCode RECEIPT_ORDER_STATUS_NOT_DELETABLE = new ErrorCode(1_060_200_005, "入库单状态不是草稿或已作废,不能删除");
59 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 19 public static final int RECEIPT_RETURN = 102; // 退货入库:WmsReceiptOrderDO
20 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 54 */
55 55 private Long areaId;
56 56 /**
  57 + * 库存明细编号
  58 + */
  59 + private Long inventoryDetailId;
  60 + /**
57 61 * 变更数量
58 62 */
59 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 55 * @param id 编号
56 56 * @return 往来企业
57 57 */
  58 + @SuppressWarnings("UnusedReturnValue")
58 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 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 79 }
80 80  
81 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 92 public WmsMerchantDO getMerchant(Long id) {
83 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 171 return order;
172 172 }
173 173  
  174 + /**
  175 + * 增加入库单对应库存。
  176 + *
  177 + * 入库在库存变更模型中使用正数数量;批次模式下,会根据入库明细生成新的库存明细。
  178 + *
  179 + * @param order 入库单
  180 + * @param details 入库单明细列表
  181 + */
174 182 private void createInventory(WmsReceiptOrderDO order, List<WmsReceiptOrderDetailDO> details) {
175 183 List<WmsInventoryChangeReqDTO.Item> items = convertList(details,
176 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 3 DELETE FROM "wms_receipt_order_detail";
2 4 DELETE FROM "wms_receipt_order";
3 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 106 CREATE TABLE IF NOT EXISTS "wms_receipt_order" (
107 107 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
108 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 111 "biz_order_no" varchar(64) DEFAULT NULL,
112 112 "merchant_id" bigint DEFAULT NULL,
113 113 "remark" varchar(255) DEFAULT NULL,
... ... @@ -144,3 +144,46 @@ CREATE TABLE IF NOT EXISTS &quot;wms_receipt_order_detail&quot; (
144 144 "tenant_id" bigint NOT NULL DEFAULT '0',
145 145 PRIMARY KEY ("id")
146 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 出库单明细表';
... ...