diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/WmsCheckOrderController.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/WmsCheckOrderController.java new file mode 100644 index 0000000..d0044c6 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/WmsCheckOrderController.java @@ -0,0 +1,220 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.NumberUtil; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.check.WmsCheckOrderDetailService; +import cn.iocoder.yudao.module.wms.service.order.check.WmsCheckOrderService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; + +@Tag(name = "管理后台 - WMS 盘库单") +@RestController +@RequestMapping("/wms/check-order") +@Validated +public class WmsCheckOrderController { + + @Resource + private WmsCheckOrderService checkOrderService; + @Resource + private WmsCheckOrderDetailService checkOrderDetailService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsWarehouseAreaService warehouseAreaService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + + @Resource + private WmsProperties wmsProperties; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建盘库单") + @PreAuthorize("@ss.hasPermission('wms:check-order:create')") + public CommonResult createCheckOrder(@Valid @RequestBody WmsCheckOrderSaveReqVO createReqVO) { + return success(checkOrderService.createCheckOrder(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新盘库单") + @PreAuthorize("@ss.hasPermission('wms:check-order:update')") + public CommonResult updateCheckOrder(@Valid @RequestBody WmsCheckOrderSaveReqVO updateReqVO) { + checkOrderService.updateCheckOrder(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除盘库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:check-order:delete')") + public CommonResult deleteCheckOrder(@RequestParam("id") Long id) { + checkOrderService.deleteCheckOrder(id); + return success(true); + } + + @PutMapping("/complete") + @Operation(summary = "完成盘库") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:check-order:complete')") + public CommonResult completeCheckOrder(@RequestParam("id") Long id) { + checkOrderService.completeCheckOrder(id); + return success(true); + } + + @PutMapping("/cancel") + @Operation(summary = "作废盘库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:check-order:cancel')") + public CommonResult cancelCheckOrder(@RequestParam("id") Long id) { + checkOrderService.cancelCheckOrder(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得盘库单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:check-order:query')") + public CommonResult getCheckOrder(@RequestParam("id") Long id) { + WmsCheckOrderDO order = checkOrderService.getCheckOrder(id); + if (order == null) { + return success(null); + } + // 获得盘库单的明细列表 + List detailList = checkOrderDetailService.getCheckOrderDetailList(id); + // 拼接结果返回 + WmsCheckOrderRespVO respVO = buildCheckOrderRespVO(order) + .setDetails(buildCheckOrderDetailRespVOList(detailList)); + return success(respVO); + } + + @GetMapping("/page") + @Operation(summary = "获得盘库单分页") + @PreAuthorize("@ss.hasPermission('wms:check-order:query')") + public CommonResult> getCheckOrderPage(@Valid WmsCheckOrderPageReqVO pageReqVO) { + PageResult pageResult = checkOrderService.getCheckOrderPage(pageReqVO); + return success(new PageResult<>(buildCheckOrderRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出盘库单 Excel") + @PreAuthorize("@ss.hasPermission('wms:check-order:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportCheckOrderExcel(@Valid WmsCheckOrderPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = checkOrderService.getCheckOrderPage(pageReqVO).getList(); + ExcelUtils.write(response, "盘库单.xls", "数据", WmsCheckOrderRespVO.class, + buildCheckOrderRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private WmsCheckOrderRespVO buildCheckOrderRespVO(WmsCheckOrderDO order) { + if (order == null) { + return null; + } + List list = buildCheckOrderRespVOList(List.of(order)); + return CollUtil.getFirst(list); + } + + private List buildCheckOrderRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的仓库、库区、用户等数据 + Map warehouseMap = warehouseService.getWarehouseMap(convertSet(list, WmsCheckOrderDO::getWarehouseId)); + Map areaMap = wmsProperties.isAreaEnabled() + ? warehouseAreaService.getWarehouseAreaMap(convertSet(list, WmsCheckOrderDO::getAreaId)) + : Collections.emptyMap(); + Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(list, + order -> Stream.of(parseUserId(order.getCreator()), parseUserId(order.getUpdater())))); + // 拼接数据 + return BeanUtils.toBean(list, WmsCheckOrderRespVO.class, vo -> { + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + MapUtils.findAndThen(areaMap, vo.getAreaId(), area -> vo.setAreaName(area.getName())); + MapUtils.findAndThen(userMap, parseUserId(vo.getCreator()), user -> vo.setCreatorName(user.getNickname())); + MapUtils.findAndThen(userMap, parseUserId(vo.getUpdater()), user -> vo.setUpdaterName(user.getNickname())); + }); + } + + private Long parseUserId(String userId) { + return NumberUtil.parseLong(userId, null); + } + + private List buildCheckOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的商品、SKU、仓库、库区等数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsCheckOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(list, WmsCheckOrderDetailDO::getWarehouseId)); + Map areaMap = wmsProperties.isAreaEnabled() + ? warehouseAreaService.getWarehouseAreaMap(convertSet(list, WmsCheckOrderDetailDO::getAreaId)) + : Collections.emptyMap(); + // 拼接数据 + return BeanUtils.toBean(list, WmsCheckOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + MapUtils.findAndThen(areaMap, vo.getAreaId(), area -> vo.setAreaName(area.getName())); + if (vo.getQuantity() != null && vo.getCheckQuantity() != null) { + vo.setDifferenceQuantity(vo.getCheckQuantity().subtract(vo.getQuantity())); + } else { + vo.setDifferenceQuantity(BigDecimal.ZERO); + } + }); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/WmsCheckOrderDetailController.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/WmsCheckOrderDetailController.java new file mode 100644 index 0000000..b818d2c --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/WmsCheckOrderDetailController.java @@ -0,0 +1,99 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.check.WmsCheckOrderDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - WMS 盘库单明细") +@RestController +@RequestMapping("/wms/check-order-detail") +@Validated +public class WmsCheckOrderDetailController { + + @Resource + private WmsCheckOrderDetailService checkOrderDetailService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsWarehouseAreaService warehouseAreaService; + + @Resource + private WmsProperties wmsProperties; + + @GetMapping("/list-by-order-id") + @Operation(summary = "获得盘库单明细列表") + @Parameter(name = "orderId", description = "盘库单编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:check-order:query')") + public CommonResult> getCheckOrderDetailListByOrderId( + @RequestParam("orderId") Long orderId) { + List list = checkOrderDetailService.getCheckOrderDetailList(orderId); + return success(buildCheckOrderDetailRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private List buildCheckOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 查询关联数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsCheckOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap( + convertSet(list, WmsCheckOrderDetailDO::getWarehouseId)); + Map areaMap = wmsProperties.isAreaEnabled() + ? warehouseAreaService.getWarehouseAreaMap(convertSet(list, WmsCheckOrderDetailDO::getAreaId)) + : Collections.emptyMap(); + // 拼接数据 + return BeanUtils.toBean(list, WmsCheckOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName())); + MapUtils.findAndThen(areaMap, vo.getAreaId(), area -> vo.setAreaName(area.getName())); + if (vo.getQuantity() != null && vo.getCheckQuantity() != null) { + vo.setDifferenceQuantity(vo.getCheckQuantity().subtract(vo.getQuantity())); + } else { + vo.setDifferenceQuantity(BigDecimal.ZERO); + } + }); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailRespVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailRespVO.java new file mode 100644 index 0000000..07eac6f --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailRespVO.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 盘库单明细 Response VO") +@Data +public class WmsCheckOrderDetailRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "盘库单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long orderId; + + @Schema(description = "商品编号", example = "2048") + private Long itemId; + + @Schema(description = "商品编号", example = "SPU-APPLE") + private String itemCode; + + @Schema(description = "商品名称", example = "红富士苹果") + private String itemName; + + @Schema(description = "商品单位", example = "箱") + private String unit; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long skuId; + + @Schema(description = "规格编号", example = "SKU-APPLE-10KG") + private String skuCode; + + @Schema(description = "规格名称", example = "10kg 箱装") + private String skuName; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long warehouseId; + + @Schema(description = "仓库名称", example = "北京仓") + private String warehouseName; + + @Schema(description = "库区编号", example = "1024") + private Long areaId; + + @Schema(description = "库区名称", example = "北京库区01") + private String areaName; + + @Schema(description = "库存编号", example = "1024") + private Long inventoryId; + + @Schema(description = "库存明细编号", example = "1024") + private Long inventoryDetailId; + + @Schema(description = "批号", example = "B202605110001") + private String batchNo; + + @Schema(description = "生产日期") + private LocalDateTime productionDate; + + @Schema(description = "过期日期") + private LocalDateTime expirationDate; + + @Schema(description = "入库时间") + private LocalDateTime receiptTime; + + @Schema(description = "账面数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal quantity; + + @Schema(description = "实盘数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "98.00") + private BigDecimal checkQuantity; + + @Schema(description = "盈亏数量", example = "-2.00") + private BigDecimal differenceQuantity; + + @Schema(description = "金额", example = "1000.00") + private BigDecimal amount; + + @Schema(description = "备注", example = "备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailSaveReqVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailSaveReqVO.java new file mode 100644 index 0000000..48c9f32 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/detail/WmsCheckOrderDetailSaveReqVO.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 盘库单明细保存 Request VO") +@Data +public class WmsCheckOrderDetailSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "SKU 不能为空") + private Long skuId; + + @Schema(description = "库区编号", example = "1024") + private Long areaId; + + @Schema(description = "库存编号", example = "1024") + private Long inventoryId; + + @Schema(description = "库存明细编号", example = "1024") + private Long inventoryDetailId; + + @Schema(description = "批号", example = "B202605110001") + @Size(max = 64, message = "批号长度不能超过 64 个字符") + private String batchNo; + + @Schema(description = "生产日期") + private LocalDateTime productionDate; + + @Schema(description = "过期日期") + private LocalDateTime expirationDate; + + @Schema(description = "入库时间") + private LocalDateTime receiptTime; + + @Schema(description = "账面数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "账面数量不能为空") + @DecimalMin(value = "0", message = "账面数量不能小于 0") + private BigDecimal quantity; + + @Schema(description = "实盘数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "98.00") + @NotNull(message = "实盘数量不能为空") + @DecimalMin(value = "0", message = "实盘数量不能小于 0") + private BigDecimal checkQuantity; + + @Schema(description = "金额", example = "1000.00") + @DecimalMin(value = "0", message = "金额不能小于 0") + private BigDecimal amount; + + @Schema(description = "备注", example = "备注") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderPageReqVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderPageReqVO.java new file mode 100644 index 0000000..b552997 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderPageReqVO.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - WMS 盘库单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsCheckOrderPageReqVO extends PageParam { + + @Schema(description = "盘库单号", example = "PK202605110001") + private String no; + + @Schema(description = "单据状态", example = "0") + @InEnum(WmsOrderStatusEnum.class) + private Integer status; + + @Schema(description = "仓库编号", example = "1024") + private Long warehouseId; + + @Schema(description = "库区编号", example = "1024") + private Long areaId; + + @Schema(description = "单据日期") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] orderDate; + + @Schema(description = "最小盈亏数量", example = "-10.00") + private BigDecimal totalQuantityMin; + + @Schema(description = "最大盈亏数量", example = "100.00") + private BigDecimal totalQuantityMax; + + @Schema(description = "最小总金额", example = "1.00") + private BigDecimal totalAmountMin; + + @Schema(description = "最大总金额", example = "1000.00") + private BigDecimal totalAmountMax; + + @Schema(description = "创建用户", example = "1") + private String creator; + + @Schema(description = "更新用户", example = "1") + private String updater; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "更新时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] updateTime; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderRespVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderRespVO.java new file mode 100644 index 0000000..e266a2a --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderRespVO.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - WMS 盘库单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class WmsCheckOrderRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "盘库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "PK202605110001") + @ExcelProperty("盘库单号") + private String no; + + @Schema(description = "盘库状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @ExcelProperty(value = "盘库状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.ORDER_STATUS) + private Integer status; + + @Schema(description = "备注", example = "备注") + private String remark; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long warehouseId; + + @Schema(description = "仓库名称", example = "北京仓") + @ExcelProperty("仓库") + private String warehouseName; + + @Schema(description = "库区编号", example = "1024") + private Long areaId; + + @Schema(description = "库区名称", example = "北京库区01") + private String areaName; + + @Schema(description = "盈亏数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @ExcelProperty("盈亏数量") + private BigDecimal totalQuantity; + + @Schema(description = "总金额", example = "1000.00") + @ExcelProperty("总金额") + private BigDecimal totalAmount; + + @Schema(description = "盘库明细") + private List details; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "创建者", example = "1") + private String creator; + @Schema(description = "创建者名称", example = "芋道") + private String creatorName; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + + @Schema(description = "更新者", example = "1") + private String updater; + @Schema(description = "更新者名称", example = "芋道") + private String updaterName; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderSaveReqVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderSaveReqVO.java new file mode 100644 index 0000000..cf91e01 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/check/vo/order/WmsCheckOrderSaveReqVO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order; + +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.detail.WmsCheckOrderDetailSaveReqVO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Schema(description = "管理后台 - WMS 盘库单保存 Request VO") +@Data +public class WmsCheckOrderSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "盘库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "PK202605110001") + @NotBlank(message = "盘库单号不能为空") + @Size(max = 64, message = "盘库单号长度不能超过 64 个字符") + private String no; + + @Schema(description = "备注", example = "备注") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + + @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "仓库不能为空") + private Long warehouseId; + + @Schema(description = "库区编号", example = "1024") + private Long areaId; + + @Schema(description = "盈亏数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "盈亏数量不能为空") + private BigDecimal totalQuantity; + + @Schema(description = "总金额", example = "1000.00") + private BigDecimal totalAmount; + + @Schema(description = "盘库明细") + @Valid + private List details; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/WmsMovementOrderController.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/WmsMovementOrderController.java new file mode 100644 index 0000000..6d2a17a --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/WmsMovementOrderController.java @@ -0,0 +1,226 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.NumberUtil; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderRespVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.movement.WmsMovementOrderDetailService; +import cn.iocoder.yudao.module.wms.service.order.movement.WmsMovementOrderService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; + +@Tag(name = "管理后台 - WMS 移库单") +@RestController +@RequestMapping("/wms/movement-order") +@Validated +public class WmsMovementOrderController { + + @Resource + private WmsMovementOrderService movementOrderService; + @Resource + private WmsMovementOrderDetailService movementOrderDetailService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsWarehouseAreaService warehouseAreaService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + + @Resource + private WmsProperties wmsProperties; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建移库单") + @PreAuthorize("@ss.hasPermission('wms:movement-order:create')") + public CommonResult createMovementOrder(@Valid @RequestBody WmsMovementOrderSaveReqVO createReqVO) { + return success(movementOrderService.createMovementOrder(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新移库单") + @PreAuthorize("@ss.hasPermission('wms:movement-order:update')") + public CommonResult updateMovementOrder(@Valid @RequestBody WmsMovementOrderSaveReqVO updateReqVO) { + movementOrderService.updateMovementOrder(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除移库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:movement-order:delete')") + public CommonResult deleteMovementOrder(@RequestParam("id") Long id) { + movementOrderService.deleteMovementOrder(id); + return success(true); + } + + @PutMapping("/complete") + @Operation(summary = "完成移库") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:movement-order:complete')") + public CommonResult completeMovementOrder(@RequestParam("id") Long id) { + movementOrderService.completeMovementOrder(id); + return success(true); + } + + @PutMapping("/cancel") + @Operation(summary = "作废移库单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:movement-order:cancel')") + public CommonResult cancelMovementOrder(@RequestParam("id") Long id) { + movementOrderService.cancelMovementOrder(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得移库单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:movement-order:query')") + public CommonResult getMovementOrder(@RequestParam("id") Long id) { + WmsMovementOrderDO order = movementOrderService.getMovementOrder(id); + if (order == null) { + return success(null); + } + // 获得移库单的明细列表 + List detailList = movementOrderDetailService.getMovementOrderDetailList(id); + // 拼接结果返回 + WmsMovementOrderRespVO respVO = buildMovementOrderRespVO(order) + .setDetails(buildMovementOrderDetailRespVOList(detailList)); + return success(respVO); + } + + @GetMapping("/page") + @Operation(summary = "获得移库单分页") + @PreAuthorize("@ss.hasPermission('wms:movement-order:query')") + public CommonResult> getMovementOrderPage( + @Valid WmsMovementOrderPageReqVO pageReqVO) { + PageResult pageResult = movementOrderService.getMovementOrderPage(pageReqVO); + return success(new PageResult<>(buildMovementOrderRespVOList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出移库单 Excel") + @PreAuthorize("@ss.hasPermission('wms:movement-order:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportMovementOrderExcel(@Valid WmsMovementOrderPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = movementOrderService.getMovementOrderPage(pageReqVO).getList(); + ExcelUtils.write(response, "移库单.xls", "数据", WmsMovementOrderRespVO.class, + buildMovementOrderRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private WmsMovementOrderRespVO buildMovementOrderRespVO(WmsMovementOrderDO order) { + if (order == null) { + return null; + } + List list = buildMovementOrderRespVOList(List.of(order)); + return CollUtil.getFirst(list); + } + + private List buildMovementOrderRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的仓库、库区、用户等数据 + Map warehouseMap = warehouseService.getWarehouseMap(convertSetByFlatMap(list, + order -> Stream.of(order.getSourceWarehouseId(), order.getTargetWarehouseId()))); + Map areaMap = wmsProperties.isAreaEnabled() + ? warehouseAreaService.getWarehouseAreaMap(convertSetByFlatMap(list, + order -> Stream.of(order.getSourceAreaId(), order.getTargetAreaId()))) + : Collections.emptyMap(); + Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(list, + order -> Stream.of(parseUserId(order.getCreator()), parseUserId(order.getUpdater())))); + // 拼接数据 + return BeanUtils.toBean(list, WmsMovementOrderRespVO.class, vo -> { + MapUtils.findAndThen(warehouseMap, vo.getSourceWarehouseId(), + warehouse -> vo.setSourceWarehouseName(warehouse.getName())); + MapUtils.findAndThen(warehouseMap, vo.getTargetWarehouseId(), + warehouse -> vo.setTargetWarehouseName(warehouse.getName())); + MapUtils.findAndThen(areaMap, vo.getSourceAreaId(), area -> vo.setSourceAreaName(area.getName())); + MapUtils.findAndThen(areaMap, vo.getTargetAreaId(), area -> vo.setTargetAreaName(area.getName())); + MapUtils.findAndThen(userMap, parseUserId(vo.getCreator()), user -> vo.setCreatorName(user.getNickname())); + MapUtils.findAndThen(userMap, parseUserId(vo.getUpdater()), user -> vo.setUpdaterName(user.getNickname())); + }); + } + + private Long parseUserId(String userId) { + return NumberUtil.parseLong(userId, null); + } + + private List buildMovementOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 获取相关的商品、SKU、仓库、库区等数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsMovementOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap(convertSetByFlatMap(list, + detail -> Stream.of(detail.getSourceWarehouseId(), detail.getTargetWarehouseId()))); + Map areaMap = wmsProperties.isAreaEnabled() + ? warehouseAreaService.getWarehouseAreaMap(convertSetByFlatMap(list, + detail -> Stream.of(detail.getSourceAreaId(), detail.getTargetAreaId()))) + : Collections.emptyMap(); + // 拼接数据 + return BeanUtils.toBean(list, WmsMovementOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getSourceWarehouseId(), + warehouse -> vo.setSourceWarehouseName(warehouse.getName())); + MapUtils.findAndThen(warehouseMap, vo.getTargetWarehouseId(), + warehouse -> vo.setTargetWarehouseName(warehouse.getName())); + MapUtils.findAndThen(areaMap, vo.getSourceAreaId(), area -> vo.setSourceAreaName(area.getName())); + MapUtils.findAndThen(areaMap, vo.getTargetAreaId(), area -> vo.setTargetAreaName(area.getName())); + }); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/WmsMovementOrderDetailController.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/WmsMovementOrderDetailController.java new file mode 100644 index 0000000..0c3755e --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/WmsMovementOrderDetailController.java @@ -0,0 +1,100 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemService; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import cn.iocoder.yudao.module.wms.service.order.movement.WmsMovementOrderDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; + +@Tag(name = "管理后台 - WMS 移库单明细") +@RestController +@RequestMapping("/wms/movement-order-detail") +@Validated +public class WmsMovementOrderDetailController { + + @Resource + private WmsMovementOrderDetailService movementOrderDetailService; + @Resource + private WmsItemService itemService; + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsWarehouseAreaService warehouseAreaService; + + @Resource + private WmsProperties wmsProperties; + + @GetMapping("/list-by-order-id") + @Operation(summary = "获得移库单明细列表") + @Parameter(name = "orderId", description = "移库单编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:movement-order:query')") + public CommonResult> getMovementOrderDetailListByOrderId( + @RequestParam("orderId") Long orderId) { + List list = movementOrderDetailService.getMovementOrderDetailList(orderId); + return success(buildMovementOrderDetailRespVOList(list)); + } + + // ==================== 拼接 VO ==================== + + private List buildMovementOrderDetailRespVOList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + // 查询关联数据 + Map skuMap = itemSkuService.getItemSkuMap(convertSet(list, WmsMovementOrderDetailDO::getSkuId)); + Map itemMap = itemService.getItemMap(convertSet(skuMap.values(), WmsItemSkuDO::getItemId)); + Map warehouseMap = warehouseService.getWarehouseMap(convertSetByFlatMap(list, + detail -> Stream.of(detail.getSourceWarehouseId(), detail.getTargetWarehouseId()))); + Map areaMap = wmsProperties.isAreaEnabled() + ? warehouseAreaService.getWarehouseAreaMap(convertSetByFlatMap(list, + detail -> Stream.of(detail.getSourceAreaId(), detail.getTargetAreaId()))) + : Collections.emptyMap(); + // 拼接数据 + return BeanUtils.toBean(list, WmsMovementOrderDetailRespVO.class, vo -> { + MapUtils.findAndThen(skuMap, vo.getSkuId(), sku -> { + vo.setSkuCode(sku.getCode()).setSkuName(sku.getName()).setItemId(sku.getItemId()); + MapUtils.findAndThen(itemMap, sku.getItemId(), item -> vo.setItemCode(item.getCode()) + .setItemName(item.getName()).setUnit(item.getUnit())); + }); + MapUtils.findAndThen(warehouseMap, vo.getSourceWarehouseId(), + warehouse -> vo.setSourceWarehouseName(warehouse.getName())); + MapUtils.findAndThen(warehouseMap, vo.getTargetWarehouseId(), + warehouse -> vo.setTargetWarehouseName(warehouse.getName())); + MapUtils.findAndThen(areaMap, vo.getSourceAreaId(), area -> vo.setSourceAreaName(area.getName())); + MapUtils.findAndThen(areaMap, vo.getTargetAreaId(), area -> vo.setTargetAreaName(area.getName())); + }); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailRespVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailRespVO.java new file mode 100644 index 0000000..51a3344 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailRespVO.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 移库单明细 Response VO") +@Data +public class WmsMovementOrderDetailRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "移库单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long orderId; + + @Schema(description = "商品编号", example = "2048") + private Long itemId; + + @Schema(description = "商品编号", example = "SPU-APPLE") + private String itemCode; + + @Schema(description = "商品名称", example = "红富士苹果") + private String itemName; + + @Schema(description = "商品单位", example = "箱") + private String unit; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long skuId; + + @Schema(description = "规格编号", example = "SKU-APPLE-10KG") + private String skuCode; + + @Schema(description = "规格名称", example = "10kg 箱装") + private String skuName; + + @Schema(description = "来源仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long sourceWarehouseId; + + @Schema(description = "来源仓库名称", example = "北京仓") + private String sourceWarehouseName; + + @Schema(description = "来源库区编号", example = "1024") + private Long sourceAreaId; + + @Schema(description = "来源库区名称", example = "北京库区01") + private String sourceAreaName; + + @Schema(description = "目标仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long targetWarehouseId; + + @Schema(description = "目标仓库名称", example = "上海仓") + private String targetWarehouseName; + + @Schema(description = "目标库区编号", example = "2048") + private Long targetAreaId; + + @Schema(description = "目标库区名称", example = "上海库区01") + private String targetAreaName; + + @Schema(description = "库存明细编号", example = "1024") + private Long inventoryDetailId; + + @Schema(description = "批号", example = "B202605110001") + private String batchNo; + + @Schema(description = "生产日期") + private LocalDateTime productionDate; + + @Schema(description = "过期日期") + private LocalDateTime expirationDate; + + @Schema(description = "移库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal quantity; + + @Schema(description = "金额", example = "1000.00") + private BigDecimal amount; + + @Schema(description = "备注", example = "备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailSaveReqVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailSaveReqVO.java new file mode 100644 index 0000000..a9f1c40 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/detail/WmsMovementOrderDetailSaveReqVO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - WMS 移库单明细保存 Request VO") +@Data +public class WmsMovementOrderDetailSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "SKU 不能为空") + private Long skuId; + + @Schema(description = "来源库区编号", example = "1024") + private Long sourceAreaId; + + @Schema(description = "目标库区编号", example = "2048") + private Long targetAreaId; + + @Schema(description = "库存明细编号", example = "1024") + private Long inventoryDetailId; + + @Schema(description = "批号", example = "B202605110001") + @Size(max = 64, message = "批号长度不能超过 64 个字符") + private String batchNo; + + @Schema(description = "生产日期") + private LocalDateTime productionDate; + + @Schema(description = "过期日期") + private LocalDateTime expirationDate; + + @Schema(description = "移库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "移库数量不能为空") + @DecimalMin(value = "0", inclusive = false, message = "移库数量必须大于 0") + private BigDecimal quantity; + + @Schema(description = "金额", example = "1000.00") + @DecimalMin(value = "0", message = "金额不能小于 0") + private BigDecimal amount; + + @Schema(description = "备注", example = "备注") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderPageReqVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderPageReqVO.java new file mode 100644 index 0000000..38487b8 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderPageReqVO.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - WMS 移库单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WmsMovementOrderPageReqVO extends PageParam { + + @Schema(description = "移库单号", example = "YK202605110001") + private String no; + + @Schema(description = "单据状态", example = "0") + @InEnum(WmsOrderStatusEnum.class) + private Integer status; + + @Schema(description = "来源仓库编号", example = "1024") + private Long sourceWarehouseId; + + @Schema(description = "目标仓库编号", example = "2048") + private Long targetWarehouseId; + + @Schema(description = "单据日期") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] orderDate; + + @Schema(description = "最小数量", example = "1.00") + private BigDecimal totalQuantityMin; + + @Schema(description = "最大数量", example = "100.00") + private BigDecimal totalQuantityMax; + + @Schema(description = "最小总金额", example = "1.00") + private BigDecimal totalAmountMin; + + @Schema(description = "最大总金额", example = "1000.00") + private BigDecimal totalAmountMax; + + @Schema(description = "创建用户", example = "1") + private String creator; + + @Schema(description = "更新用户", example = "1") + private String updater; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "更新时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] updateTime; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderRespVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderRespVO.java new file mode 100644 index 0000000..9923d2b --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderRespVO.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailRespVO; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - WMS 移库单 Response VO") +@Data +@ExcelIgnoreUnannotated +public class WmsMovementOrderRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "移库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "YK202605110001") + @ExcelProperty("移库单号") + private String no; + + @Schema(description = "移库状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @ExcelProperty(value = "移库状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.ORDER_STATUS) + private Integer status; + + @Schema(description = "备注", example = "备注") + private String remark; + + @Schema(description = "来源仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long sourceWarehouseId; + + @Schema(description = "来源仓库名称", example = "北京仓") + @ExcelProperty("来源仓库") + private String sourceWarehouseName; + + @Schema(description = "来源库区编号", example = "1024") + private Long sourceAreaId; + + @Schema(description = "来源库区名称", example = "北京库区01") + private String sourceAreaName; + + @Schema(description = "目标仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long targetWarehouseId; + + @Schema(description = "目标仓库名称", example = "上海仓") + @ExcelProperty("目标仓库") + private String targetWarehouseName; + + @Schema(description = "目标库区编号", example = "2048") + private Long targetAreaId; + + @Schema(description = "目标库区名称", example = "上海库区01") + private String targetAreaName; + + @Schema(description = "移库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @ExcelProperty("移库数量") + private BigDecimal totalQuantity; + + @Schema(description = "总金额", example = "1000.00") + @ExcelProperty("总金额") + private BigDecimal totalAmount; + + @Schema(description = "移库明细") + private List details; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "创建者", example = "1") + private String creator; + @Schema(description = "创建者名称", example = "芋道") + private String creatorName; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + + @Schema(description = "更新者", example = "1") + private String updater; + @Schema(description = "更新者名称", example = "芋道") + private String updaterName; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderSaveReqVO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderSaveReqVO.java new file mode 100644 index 0000000..d272df5 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/order/movement/vo/order/WmsMovementOrderSaveReqVO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order; + +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.detail.WmsMovementOrderDetailSaveReqVO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Schema(description = "管理后台 - WMS 移库单保存 Request VO") +@Data +public class WmsMovementOrderSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "移库单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "YK202605110001") + @NotBlank(message = "移库单号不能为空") + @Size(max = 64, message = "移库单号长度不能超过 64 个字符") + private String no; + + @Schema(description = "备注", example = "备注") + @Size(max = 255, message = "备注长度不能超过 255 个字符") + private String remark; + + @Schema(description = "来源仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "来源仓库不能为空") + private Long sourceWarehouseId; + + @Schema(description = "来源库区编号", example = "1024") + private Long sourceAreaId; + + @Schema(description = "目标仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "目标仓库不能为空") + private Long targetWarehouseId; + + @Schema(description = "目标库区编号", example = "2048") + private Long targetAreaId; + + @Schema(description = "移库数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + @NotNull(message = "移库数量不能为空") + private BigDecimal totalQuantity; + + @Schema(description = "总金额", example = "1000.00") + private BigDecimal totalAmount; + + @Schema(description = "移库明细") + @Valid + private List details; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/check/WmsCheckOrderDO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/check/WmsCheckOrderDO.java new file mode 100644 index 0000000..93e9851 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/check/WmsCheckOrderDO.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.order.check; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * WMS 盘库单 DO + * + * @author 芋道源码 + */ +@TableName("wms_check_order") +@KeySequence("wms_check_order_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsCheckOrderDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 盘库单号 + */ + private String no; + /** + * 盘库状态 + * + * 字典 {@link DictTypeConstants#ORDER_STATUS} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + + // ========= 仓库库区字段 ========= + + /** + * 仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 库区编号 + * + * 关联 {@link WmsWarehouseAreaDO#getId()} + */ + private Long areaId; + + // ========= 汇总金额字段 ========= + + /** + * 盈亏数量 + */ + private BigDecimal totalQuantity; + /** + * 总金额 + */ + private BigDecimal totalAmount; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/check/WmsCheckOrderDetailDO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/check/WmsCheckOrderDetailDO.java new file mode 100644 index 0000000..d673e5a --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/check/WmsCheckOrderDetailDO.java @@ -0,0 +1,118 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.order.check; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDetailDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * WMS 盘库单明细 DO + * + * @author 芋道源码 + */ +@TableName("wms_check_order_detail") +@KeySequence("wms_check_order_detail_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsCheckOrderDetailDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 备注 + */ + private String remark; + + // ========= 单据商品字段 ========= + + /** + * 盘库单编号 + * + * 关联 {@link WmsCheckOrderDO#getId()} + */ + private Long orderId; + /** + * 商品 SKU 编号 + * + * 关联 {@link WmsItemSkuDO#getId()} + */ + private Long skuId; + + // ========= 仓库库区字段 ========= + + /** + * 仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long warehouseId; + /** + * 库区编号 + * + * 关联 {@link WmsWarehouseAreaDO#getId()} + */ + private Long areaId; + /** + * 库存编号 + * + * 关联 {@link WmsInventoryDO#getId()} + */ + private Long inventoryId; + /** + * 库存明细编号 + * + * 关联 {@link WmsInventoryDetailDO#getId()} + */ + private Long inventoryDetailId; + + // ========= 批次效期字段 ========= + + /** + * 批号 + */ + private String batchNo; + /** + * 生产日期 + */ + private LocalDateTime productionDate; + /** + * 过期日期 + */ + private LocalDateTime expirationDate; + /** + * 入库时间 + */ + private LocalDateTime receiptTime; + + // ========= 汇总金额字段 ========= + + /** + * 账面数量 + */ + private BigDecimal quantity; + /** + * 实盘数量 + */ + private BigDecimal checkQuantity; + /** + * 金额(小计、非单价) + */ + private BigDecimal amount; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/movement/WmsMovementOrderDO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/movement/WmsMovementOrderDO.java new file mode 100644 index 0000000..b219db2 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/movement/WmsMovementOrderDO.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.order.movement; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import cn.iocoder.yudao.module.wms.enums.DictTypeConstants; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * WMS 移库单 DO + * + * @author 芋道源码 + */ +@TableName("wms_movement_order") +@KeySequence("wms_movement_order_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsMovementOrderDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 移库单号 + */ + private String no; + /** + * 移库状态 + * + * 字典 {@link DictTypeConstants#ORDER_STATUS} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + + // ========= 来源仓库库区字段 ========= + + /** + * 来源仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long sourceWarehouseId; + /** + * 来源库区编号 + * + * 关联 {@link WmsWarehouseAreaDO#getId()} + */ + private Long sourceAreaId; + + // ========= 目标仓库库区字段 ========= + + /** + * 目标仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long targetWarehouseId; + /** + * 目标库区编号 + * + * 关联 {@link WmsWarehouseAreaDO#getId()} + */ + private Long targetAreaId; + + // ========= 汇总金额字段 ========= + + /** + * 总数量 + */ + private BigDecimal totalQuantity; + /** + * 总金额 + */ + private BigDecimal totalAmount; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/movement/WmsMovementOrderDetailDO.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/movement/WmsMovementOrderDetailDO.java new file mode 100644 index 0000000..4fc5a7f --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/order/movement/WmsMovementOrderDetailDO.java @@ -0,0 +1,118 @@ +package cn.iocoder.yudao.module.wms.dal.dataobject.order.movement; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryDetailDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * WMS 移库单明细 DO + * + * @author 芋道源码 + */ +@TableName("wms_movement_order_detail") +@KeySequence("wms_movement_order_detail_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WmsMovementOrderDetailDO extends BaseDO { + + /** + * 主键编号 + */ + @TableId + private Long id; + /** + * 备注 + */ + private String remark; + + // ========= 单据商品字段 ========= + + /** + * 移库单编号 + * + * 关联 {@link WmsMovementOrderDO#getId()} + */ + private Long orderId; + /** + * 商品 SKU 编号 + * + * 关联 {@link WmsItemSkuDO#getId()} + */ + private Long skuId; + + // ========= 来源仓库库区字段 ========= + + /** + * 来源仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long sourceWarehouseId; + /** + * 来源库区编号 + * + * 关联 {@link WmsWarehouseAreaDO#getId()} + */ + private Long sourceAreaId; + /** + * 库存明细编号 + * + * 关联 {@link WmsInventoryDetailDO#getId()} + */ + private Long inventoryDetailId; + + // ========= 目标仓库库区字段 ========= + + /** + * 目标仓库编号 + * + * 关联 {@link WmsWarehouseDO#getId()} + */ + private Long targetWarehouseId; + /** + * 目标库区编号 + * + * 关联 {@link WmsWarehouseAreaDO#getId()} + */ + private Long targetAreaId; + + // ========= 批次效期字段 ========= + + /** + * 批号 + */ + private String batchNo; + /** + * 生产日期 + */ + private LocalDateTime productionDate; + /** + * 过期日期 + */ + private LocalDateTime expirationDate; + + // ========= 汇总金额字段 ========= + + /** + * 移库数量 + */ + private BigDecimal quantity; + /** + * 金额(小计、非单价) + */ + private BigDecimal amount; + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/check/WmsCheckOrderDetailMapper.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/check/WmsCheckOrderDetailMapper.java new file mode 100644 index 0000000..25a794d --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/check/WmsCheckOrderDetailMapper.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.check; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 盘库单明细 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsCheckOrderDetailMapper extends BaseMapperX { + + default List selectListByOrderId(Long orderId) { + return selectList(WmsCheckOrderDetailDO::getOrderId, orderId); + } + + default List selectListByOrderIds(Collection orderIds) { + return selectList(new LambdaQueryWrapperX() + .inIfPresent(WmsCheckOrderDetailDO::getOrderId, orderIds) + .orderByAsc(WmsCheckOrderDetailDO::getOrderId) + .orderByAsc(WmsCheckOrderDetailDO::getId)); + } + + default void deleteByOrderId(Long orderId) { + delete(WmsCheckOrderDetailDO::getOrderId, orderId); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/check/WmsCheckOrderMapper.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/check/WmsCheckOrderMapper.java new file mode 100644 index 0000000..4d15ce8 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/check/WmsCheckOrderMapper.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.check; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * WMS 盘库单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsCheckOrderMapper extends BaseMapperX { + + default PageResult selectPage(WmsCheckOrderPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(WmsCheckOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(WmsCheckOrderDO::getStatus, reqVO.getStatus()) + .eqIfPresent(WmsCheckOrderDO::getWarehouseId, reqVO.getWarehouseId()) + .eqIfPresent(WmsCheckOrderDO::getAreaId, reqVO.getAreaId()) + .betweenIfPresent(WmsCheckOrderDO::getCreateTime, reqVO.getOrderDate()) + .geIfPresent(WmsCheckOrderDO::getTotalQuantity, reqVO.getTotalQuantityMin()) + .leIfPresent(WmsCheckOrderDO::getTotalQuantity, reqVO.getTotalQuantityMax()) + .geIfPresent(WmsCheckOrderDO::getTotalAmount, reqVO.getTotalAmountMin()) + .leIfPresent(WmsCheckOrderDO::getTotalAmount, reqVO.getTotalAmountMax()) + .eqIfPresent(WmsCheckOrderDO::getCreator, reqVO.getCreator()) + .eqIfPresent(WmsCheckOrderDO::getUpdater, reqVO.getUpdater()) + .betweenIfPresent(WmsCheckOrderDO::getCreateTime, reqVO.getCreateTime()) + .betweenIfPresent(WmsCheckOrderDO::getUpdateTime, reqVO.getUpdateTime()) + .orderByDesc(WmsCheckOrderDO::getId)); + } + + default WmsCheckOrderDO selectByNo(String no) { + return selectOne(WmsCheckOrderDO::getNo, no); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/movement/WmsMovementOrderDetailMapper.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/movement/WmsMovementOrderDetailMapper.java new file mode 100644 index 0000000..7d1407b --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/movement/WmsMovementOrderDetailMapper.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.movement; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 移库单明细 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsMovementOrderDetailMapper extends BaseMapperX { + + default List selectListByOrderId(Long orderId) { + return selectList(WmsMovementOrderDetailDO::getOrderId, orderId); + } + + default List selectListByOrderIds(Collection orderIds) { + return selectList(new LambdaQueryWrapperX() + .inIfPresent(WmsMovementOrderDetailDO::getOrderId, orderIds) + .orderByAsc(WmsMovementOrderDetailDO::getOrderId) + .orderByAsc(WmsMovementOrderDetailDO::getId)); + } + + default void deleteByOrderId(Long orderId) { + delete(WmsMovementOrderDetailDO::getOrderId, orderId); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/movement/WmsMovementOrderMapper.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/movement/WmsMovementOrderMapper.java new file mode 100644 index 0000000..ebdc3a5 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/order/movement/WmsMovementOrderMapper.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.wms.dal.mysql.order.movement; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderPageReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * WMS 移库单 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface WmsMovementOrderMapper extends BaseMapperX { + + default PageResult selectPage(WmsMovementOrderPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(WmsMovementOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(WmsMovementOrderDO::getStatus, reqVO.getStatus()) + .eqIfPresent(WmsMovementOrderDO::getSourceWarehouseId, reqVO.getSourceWarehouseId()) + .eqIfPresent(WmsMovementOrderDO::getTargetWarehouseId, reqVO.getTargetWarehouseId()) + .betweenIfPresent(WmsMovementOrderDO::getCreateTime, reqVO.getOrderDate()) + .geIfPresent(WmsMovementOrderDO::getTotalQuantity, reqVO.getTotalQuantityMin()) + .leIfPresent(WmsMovementOrderDO::getTotalQuantity, reqVO.getTotalQuantityMax()) + .geIfPresent(WmsMovementOrderDO::getTotalAmount, reqVO.getTotalAmountMin()) + .leIfPresent(WmsMovementOrderDO::getTotalAmount, reqVO.getTotalAmountMax()) + .eqIfPresent(WmsMovementOrderDO::getCreator, reqVO.getCreator()) + .eqIfPresent(WmsMovementOrderDO::getUpdater, reqVO.getUpdater()) + .betweenIfPresent(WmsMovementOrderDO::getCreateTime, reqVO.getCreateTime()) + .betweenIfPresent(WmsMovementOrderDO::getUpdateTime, reqVO.getUpdateTime()) + .orderByDesc(WmsMovementOrderDO::getId)); + } + + default WmsMovementOrderDO selectByNo(String no) { + return selectOne(WmsMovementOrderDO::getNo, no); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailService.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailService.java new file mode 100644 index 0000000..8f9b23b --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailService.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.wms.service.order.check; + +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 盘库单明细 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsCheckOrderDetailService { + + /** + * 创建盘库单明细列表 + * + * @param orderId 盘库单编号 + * @param reqVO 盘库单保存信息 + */ + void createCheckOrderDetailList(Long orderId, WmsCheckOrderSaveReqVO reqVO); + + /** + * 更新盘库单明细列表 + * + * @param orderId 盘库单编号 + * @param reqVO 盘库单保存信息 + */ + void updateCheckOrderDetailList(Long orderId, WmsCheckOrderSaveReqVO reqVO); + + /** + * 按盘库单编号删除明细列表 + * + * @param orderId 盘库单编号 + */ + void deleteCheckOrderDetailListByOrderId(Long orderId); + + /** + * 按盘库单编号获得明细列表 + * + * @param orderId 盘库单编号 + * @return 明细列表 + */ + List getCheckOrderDetailList(Long orderId); + + /** + * 按盘库单编号集合获得明细列表 + * + * @param orderIds 盘库单编号集合 + * @return 明细列表 + */ + List getCheckOrderDetailList(Collection orderIds); + + /** + * 校验盘库单明细列表存在 + * + * @param orderId 盘库单编号 + * @return 明细列表 + */ + List validateCheckOrderDetailListExists(Long orderId); + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailServiceImpl.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailServiceImpl.java new file mode 100644 index 0000000..fd41711 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderDetailServiceImpl.java @@ -0,0 +1,130 @@ +package cn.iocoder.yudao.module.wms.service.order.check; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.check.WmsCheckOrderDetailMapper; +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 盘库单明细 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsCheckOrderDetailServiceImpl implements WmsCheckOrderDetailService { + + @Resource + private WmsCheckOrderDetailMapper checkOrderDetailMapper; + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsWarehouseAreaService warehouseAreaService; + @Resource + private WmsProperties wmsProperties; + + @Override + @Transactional(rollbackFor = Exception.class) + public void createCheckOrderDetailList(Long orderId, WmsCheckOrderSaveReqVO reqVO) { + List list = buildCheckOrderDetailList(reqVO); + if (CollUtil.isEmpty(list)) { + return; + } + list.forEach(detail -> detail.setId(null).setOrderId(orderId)); + checkOrderDetailMapper.insertBatch(list); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateCheckOrderDetailList(Long orderId, WmsCheckOrderSaveReqVO reqVO) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = checkOrderDetailMapper.selectListByOrderId(orderId); + List list = buildCheckOrderDetailList(reqVO); + List newList = CollUtil.isEmpty(list) ? ListUtil.of() : list; + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + if (CollUtil.isNotEmpty(convertList(diffList.get(0), WmsCheckOrderDetailDO::getId))) { + throw exception(CHECK_ORDER_DETAIL_NOT_EXISTS); + } + diffList.get(0).forEach(detail -> detail.setOrderId(orderId)); + checkOrderDetailMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + diffList.get(1).forEach(detail -> detail.setOrderId(orderId)); + checkOrderDetailMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + checkOrderDetailMapper.deleteByIds(convertList(diffList.get(2), WmsCheckOrderDetailDO::getId)); + } + } + + @Override + public void deleteCheckOrderDetailListByOrderId(Long orderId) { + checkOrderDetailMapper.deleteByOrderId(orderId); + } + + @Override + public List getCheckOrderDetailList(Long orderId) { + return checkOrderDetailMapper.selectListByOrderId(orderId); + } + + @Override + public List getCheckOrderDetailList(Collection orderIds) { + if (CollUtil.isEmpty(orderIds)) { + return ListUtil.of(); + } + return checkOrderDetailMapper.selectListByOrderIds(orderIds); + } + + @Override + public List validateCheckOrderDetailListExists(Long orderId) { + List details = checkOrderDetailMapper.selectListByOrderId(orderId); + if (CollUtil.isEmpty(details)) { + throw exception(CHECK_ORDER_DETAIL_REQUIRED); + } + if (wmsProperties.isAreaEnabled() && details.stream() + .anyMatch(detail -> ObjectUtils.equalsAny(detail.getAreaId(), null, WmsWarehouseAreaDO.ID_EMPTY))) { + throw exception(CHECK_ORDER_AREA_REQUIRED); + } + return details; + } + + private List buildCheckOrderDetailList(WmsCheckOrderSaveReqVO reqVO) { + if (CollUtil.isEmpty(reqVO.getDetails())) { + return ListUtil.of(); + } + return convertList(reqVO.getDetails(), detail -> { + // 校验 SKU 存在 + itemSkuService.validateItemSkuExists(detail.getSkuId()); + // 校验库区存在,并且属于当前仓库 + Long areaId = warehouseAreaService.validateAndNormalizeWarehouseAreaId( + detail.getAreaId() == null ? reqVO.getAreaId() : detail.getAreaId(), reqVO.getWarehouseId()); + // 构建对象 + return BeanUtils.toBean(detail, WmsCheckOrderDetailDO.class) + .setWarehouseId(reqVO.getWarehouseId()).setAreaId(areaId); + }); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderService.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderService.java new file mode 100644 index 0000000..766c47b --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderService.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.wms.service.order.check; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDO; +import jakarta.validation.Valid; + +/** + * WMS 盘库单 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsCheckOrderService { + + /** + * 创建盘库单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createCheckOrder(@Valid WmsCheckOrderSaveReqVO createReqVO); + + /** + * 更新盘库单 + * + * @param updateReqVO 更新信息 + */ + void updateCheckOrder(@Valid WmsCheckOrderSaveReqVO updateReqVO); + + /** + * 删除盘库单 + * + * @param id 编号 + */ + void deleteCheckOrder(Long id); + + /** + * 完成盘库 + * + * @param id 编号 + */ + void completeCheckOrder(Long id); + + /** + * 作废盘库单 + * + * @param id 编号 + */ + void cancelCheckOrder(Long id); + + /** + * 获得盘库单 + * + * @param id 编号 + * @return 盘库单 + */ + WmsCheckOrderDO getCheckOrder(Long id); + + /** + * 获得盘库单分页 + * + * @param pageReqVO 分页查询 + * @return 盘库单分页 + */ + PageResult getCheckOrderPage(WmsCheckOrderPageReqVO pageReqVO); + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderServiceImpl.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderServiceImpl.java new file mode 100644 index 0000000..7b66992 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/check/WmsCheckOrderServiceImpl.java @@ -0,0 +1,201 @@ +package cn.iocoder.yudao.module.wms.service.order.check; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.check.vo.order.WmsCheckOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.check.WmsCheckOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.check.WmsCheckOrderMapper; +import cn.iocoder.yudao.module.wms.enums.inventory.WmsInventoryOrderTypeEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 盘库单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsCheckOrderServiceImpl implements WmsCheckOrderService { + + @Resource + private WmsCheckOrderMapper checkOrderMapper; + @Resource + private WmsCheckOrderDetailService checkOrderDetailService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsWarehouseAreaService warehouseAreaService; + @Resource + private WmsInventoryService inventoryService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createCheckOrder(WmsCheckOrderSaveReqVO createReqVO) { + // 1. 校验盘库单保存数据 + validateCheckOrderSaveData(createReqVO); + + // 2.1 插入盘库单 + WmsCheckOrderDO order = BeanUtils.toBean(createReqVO, WmsCheckOrderDO.class); + order.setStatus(WmsOrderStatusEnum.PREPARE.getStatus()); + checkOrderMapper.insert(order); + // 2.2 插入盘库单明细 + checkOrderDetailService.createCheckOrderDetailList(order.getId(), createReqVO); + return order.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateCheckOrder(WmsCheckOrderSaveReqVO updateReqVO) { + // 1. 校验盘库单保存数据 + validateCheckOrderPrepare(updateReqVO.getId()); + validateCheckOrderSaveData(updateReqVO); + + // 2.1 更新盘库单 + WmsCheckOrderDO updateObj = BeanUtils.toBean(updateReqVO, WmsCheckOrderDO.class) + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus()); + checkOrderMapper.updateById(updateObj); + // 2.2 更新盘库单明细 + checkOrderDetailService.updateCheckOrderDetailList(updateReqVO.getId(), updateReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteCheckOrder(Long id) { + // 1. 校验存在,且可删除 + WmsCheckOrderDO order = validateCheckOrderExists(id); + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus()) + && ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.CANCELED.getStatus())) { + throw exception(CHECK_ORDER_STATUS_NOT_DELETABLE); + } + + // 2.1 删除盘库单 + checkOrderMapper.deleteById(id); + // 2.2 删除盘库单明细 + checkOrderDetailService.deleteCheckOrderDetailListByOrderId(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void completeCheckOrder(Long id) { + // 1.1 校验存在,且草稿 + WmsCheckOrderDO order = validateCheckOrderPrepare(id); + // 1.2 校验盘库单明细存在 + List details = checkOrderDetailService.validateCheckOrderDetailListExists(id); + + // 2. 完成盘库单 + checkOrderMapper.updateById(new WmsCheckOrderDO().setId(id) + .setStatus(WmsOrderStatusEnum.FINISHED.getStatus())); + + // 3. 盘点库存 + changeInventory(order, details); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelCheckOrder(Long id) { + // 1. 校验存在,且草稿 + validateCheckOrderPrepare(id); + + // 2. 作废盘库单 + checkOrderMapper.updateById(new WmsCheckOrderDO().setId(id) + .setStatus(WmsOrderStatusEnum.CANCELED.getStatus())); + } + + @Override + public WmsCheckOrderDO getCheckOrder(Long id) { + return checkOrderMapper.selectById(id); + } + + @Override + public PageResult getCheckOrderPage(WmsCheckOrderPageReqVO pageReqVO) { + return checkOrderMapper.selectPage(pageReqVO); + } + + private void validateCheckOrderSaveData(WmsCheckOrderSaveReqVO reqVO) { + // 校验盘库单号唯一 + validateCheckOrderNoUnique(reqVO.getId(), reqVO.getNo()); + // 校验仓库、库区存在 + warehouseService.validateWarehouseExists(reqVO.getWarehouseId()); + reqVO.setAreaId(warehouseAreaService.validateAndNormalizeWarehouseAreaId(reqVO.getAreaId(), + reqVO.getWarehouseId())); + } + + private void validateCheckOrderNoUnique(Long id, String no) { + WmsCheckOrderDO order = checkOrderMapper.selectByNo(no); + if (order == null) { + return; + } + if (id == null || ObjectUtil.notEqual(order.getId(), id)) { + throw exception(CHECK_ORDER_NO_DUPLICATE); + } + } + + private WmsCheckOrderDO validateCheckOrderExists(Long id) { + WmsCheckOrderDO order = id == null ? null : checkOrderMapper.selectById(id); + if (order == null) { + throw exception(CHECK_ORDER_NOT_EXISTS); + } + return order; + } + + /** + * 校验盘库单存在且为草稿状态 + * + * @param id 盘库单编号 + * @return 盘库单 + */ + private WmsCheckOrderDO validateCheckOrderPrepare(Long id) { + WmsCheckOrderDO order = validateCheckOrderExists(id); + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus())) { + throw exception(CHECK_ORDER_STATUS_NOT_PREPARE); + } + return order; + } + + /** + * 盘点盘库单对应库存。 + * + * 盘库只对盈亏数量生成库存变更:实盘数量大于账面数量时增加库存,反之扣减库存;批次模式下,扣减库存会使用 + * inventoryDetailId 扣减对应库存明细的剩余数量。 + * + * @param order 盘库单 + * @param details 盘库单明细列表 + */ + private void changeInventory(WmsCheckOrderDO order, List details) { + List items = new ArrayList<>(details.size()); + for (WmsCheckOrderDetailDO detail : details) { + BigDecimal differenceQuantity = detail.getCheckQuantity().subtract(detail.getQuantity()); + if (differenceQuantity.compareTo(BigDecimal.ZERO) == 0) { + continue; + } + WmsInventoryChangeReqDTO.Item item = BeanUtils.toBean(detail, WmsInventoryChangeReqDTO.Item.class) + .setQuantity(differenceQuantity); + if (differenceQuantity.compareTo(BigDecimal.ZERO) > 0) { + item.setInventoryDetailId(null); + } + items.add(item); + } + inventoryService.changeInventory(new WmsInventoryChangeReqDTO() + .setOrderId(order.getId()).setOrderNo(order.getNo()) + .setOrderType(WmsInventoryOrderTypeEnum.CHECK.getType()).setItems(items)); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailService.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailService.java new file mode 100644 index 0000000..e59f8ed --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailService.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.wms.service.order.movement; + +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; + +import java.util.Collection; +import java.util.List; + +/** + * WMS 移库单明细 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsMovementOrderDetailService { + + /** + * 创建移库单明细列表 + * + * @param orderId 移库单编号 + * @param reqVO 移库单保存信息 + */ + void createMovementOrderDetailList(Long orderId, WmsMovementOrderSaveReqVO reqVO); + + /** + * 更新移库单明细列表 + * + * @param orderId 移库单编号 + * @param reqVO 移库单保存信息 + */ + void updateMovementOrderDetailList(Long orderId, WmsMovementOrderSaveReqVO reqVO); + + /** + * 按移库单编号删除明细列表 + * + * @param orderId 移库单编号 + */ + void deleteMovementOrderDetailListByOrderId(Long orderId); + + /** + * 按移库单编号获得明细列表 + * + * @param orderId 移库单编号 + * @return 明细列表 + */ + List getMovementOrderDetailList(Long orderId); + + /** + * 按移库单编号集合获得明细列表 + * + * @param orderIds 移库单编号集合 + * @return 明细列表 + */ + List getMovementOrderDetailList(Collection orderIds); + + /** + * 校验移库单明细列表存在 + * + * @param orderId 移库单编号 + * @return 明细列表 + */ + List validateMovementOrderDetailListExists(Long orderId); + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailServiceImpl.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailServiceImpl.java new file mode 100644 index 0000000..5b44408 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderDetailServiceImpl.java @@ -0,0 +1,136 @@ +package cn.iocoder.yudao.module.wms.service.order.movement; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.md.warehouse.WmsWarehouseAreaDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.movement.WmsMovementOrderDetailMapper; +import cn.iocoder.yudao.module.wms.framework.config.WmsProperties; +import cn.iocoder.yudao.module.wms.service.md.item.WmsItemSkuService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 移库单明细 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsMovementOrderDetailServiceImpl implements WmsMovementOrderDetailService { + + @Resource + private WmsMovementOrderDetailMapper movementOrderDetailMapper; + @Resource + private WmsItemSkuService itemSkuService; + @Resource + private WmsWarehouseAreaService warehouseAreaService; + @Resource + private WmsProperties wmsProperties; + + @Override + @Transactional(rollbackFor = Exception.class) + public void createMovementOrderDetailList(Long orderId, WmsMovementOrderSaveReqVO reqVO) { + List list = buildMovementOrderDetailList(reqVO); + if (CollUtil.isEmpty(list)) { + return; + } + list.forEach(detail -> detail.setId(null).setOrderId(orderId)); + movementOrderDetailMapper.insertBatch(list); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateMovementOrderDetailList(Long orderId, WmsMovementOrderSaveReqVO reqVO) { + // 第一步,对比新老数据,获得添加、修改、删除的列表 + List oldList = movementOrderDetailMapper.selectListByOrderId(orderId); + List list = buildMovementOrderDetailList(reqVO); + List newList = CollUtil.isEmpty(list) ? ListUtil.of() : list; + List> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); + + // 第二步,批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffList.get(0))) { + if (CollUtil.isNotEmpty(convertList(diffList.get(0), WmsMovementOrderDetailDO::getId))) { + throw exception(MOVEMENT_ORDER_DETAIL_NOT_EXISTS); + } + diffList.get(0).forEach(detail -> detail.setOrderId(orderId)); + movementOrderDetailMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + diffList.get(1).forEach(detail -> detail.setOrderId(orderId)); + movementOrderDetailMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + movementOrderDetailMapper.deleteByIds(convertList(diffList.get(2), WmsMovementOrderDetailDO::getId)); + } + } + + @Override + public void deleteMovementOrderDetailListByOrderId(Long orderId) { + movementOrderDetailMapper.deleteByOrderId(orderId); + } + + @Override + public List getMovementOrderDetailList(Long orderId) { + return movementOrderDetailMapper.selectListByOrderId(orderId); + } + + @Override + public List getMovementOrderDetailList(Collection orderIds) { + if (CollUtil.isEmpty(orderIds)) { + return ListUtil.of(); + } + return movementOrderDetailMapper.selectListByOrderIds(orderIds); + } + + @Override + public List validateMovementOrderDetailListExists(Long orderId) { + List details = movementOrderDetailMapper.selectListByOrderId(orderId); + if (CollUtil.isEmpty(details)) { + throw exception(MOVEMENT_ORDER_DETAIL_REQUIRED); + } + if (wmsProperties.isAreaEnabled() && details.stream().anyMatch(detail -> + ObjectUtils.equalsAny(detail.getSourceAreaId(), null, WmsWarehouseAreaDO.ID_EMPTY) + || ObjectUtils.equalsAny(detail.getTargetAreaId(), null, WmsWarehouseAreaDO.ID_EMPTY))) { + throw exception(MOVEMENT_ORDER_AREA_REQUIRED); + } + return details; + } + + private List buildMovementOrderDetailList(WmsMovementOrderSaveReqVO reqVO) { + if (CollUtil.isEmpty(reqVO.getDetails())) { + return ListUtil.of(); + } + return convertList(reqVO.getDetails(), detail -> { + // 校验 SKU 存在 + itemSkuService.validateItemSkuExists(detail.getSkuId()); + // 校验来源、目标库区存在,并且属于对应仓库 + Long sourceAreaId = warehouseAreaService.validateAndNormalizeWarehouseAreaId( + detail.getSourceAreaId() == null ? reqVO.getSourceAreaId() : detail.getSourceAreaId(), + reqVO.getSourceWarehouseId()); + Long targetAreaId = warehouseAreaService.validateAndNormalizeWarehouseAreaId( + detail.getTargetAreaId() == null ? reqVO.getTargetAreaId() : detail.getTargetAreaId(), + reqVO.getTargetWarehouseId()); + // 构建对象 + return BeanUtils.toBean(detail, WmsMovementOrderDetailDO.class) + .setSourceWarehouseId(reqVO.getSourceWarehouseId()).setSourceAreaId(sourceAreaId) + .setTargetWarehouseId(reqVO.getTargetWarehouseId()).setTargetAreaId(targetAreaId); + }); + } + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderService.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderService.java new file mode 100644 index 0000000..c3bba06 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderService.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.wms.service.order.movement; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDO; +import jakarta.validation.Valid; + +/** + * WMS 移库单 Service 接口 + * + * @author 芋道源码 + */ +public interface WmsMovementOrderService { + + /** + * 创建移库单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createMovementOrder(@Valid WmsMovementOrderSaveReqVO createReqVO); + + /** + * 更新移库单 + * + * @param updateReqVO 更新信息 + */ + void updateMovementOrder(@Valid WmsMovementOrderSaveReqVO updateReqVO); + + /** + * 删除移库单 + * + * @param id 编号 + */ + void deleteMovementOrder(Long id); + + /** + * 完成移库 + * + * @param id 编号 + */ + void completeMovementOrder(Long id); + + /** + * 作废移库单 + * + * @param id 编号 + */ + void cancelMovementOrder(Long id); + + /** + * 获得移库单 + * + * @param id 编号 + * @return 移库单 + */ + WmsMovementOrderDO getMovementOrder(Long id); + + /** + * 获得移库单分页 + * + * @param pageReqVO 分页查询 + * @return 移库单分页 + */ + PageResult getMovementOrderPage(WmsMovementOrderPageReqVO pageReqVO); + +} diff --git a/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderServiceImpl.java b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderServiceImpl.java new file mode 100644 index 0000000..39367f0 --- /dev/null +++ b/yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/order/movement/WmsMovementOrderServiceImpl.java @@ -0,0 +1,204 @@ +package cn.iocoder.yudao.module.wms.service.order.movement; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderPageReqVO; +import cn.iocoder.yudao.module.wms.controller.admin.order.movement.vo.order.WmsMovementOrderSaveReqVO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDO; +import cn.iocoder.yudao.module.wms.dal.dataobject.order.movement.WmsMovementOrderDetailDO; +import cn.iocoder.yudao.module.wms.dal.mysql.order.movement.WmsMovementOrderMapper; +import cn.iocoder.yudao.module.wms.enums.inventory.WmsInventoryOrderTypeEnum; +import cn.iocoder.yudao.module.wms.enums.order.WmsOrderStatusEnum; +import cn.iocoder.yudao.module.wms.service.inventory.WmsInventoryService; +import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseAreaService; +import cn.iocoder.yudao.module.wms.service.md.warehouse.WmsWarehouseService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.ArrayList; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; + +/** + * WMS 移库单 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class WmsMovementOrderServiceImpl implements WmsMovementOrderService { + + @Resource + private WmsMovementOrderMapper movementOrderMapper; + @Resource + private WmsMovementOrderDetailService movementOrderDetailService; + @Resource + private WmsWarehouseService warehouseService; + @Resource + private WmsWarehouseAreaService warehouseAreaService; + @Resource + private WmsInventoryService inventoryService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createMovementOrder(WmsMovementOrderSaveReqVO createReqVO) { + // 1. 校验移库单保存数据 + validateMovementOrderSaveData(createReqVO); + + // 2.1 插入移库单 + WmsMovementOrderDO order = BeanUtils.toBean(createReqVO, WmsMovementOrderDO.class); + order.setStatus(WmsOrderStatusEnum.PREPARE.getStatus()); + movementOrderMapper.insert(order); + // 2.2 插入移库单明细 + movementOrderDetailService.createMovementOrderDetailList(order.getId(), createReqVO); + return order.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateMovementOrder(WmsMovementOrderSaveReqVO updateReqVO) { + // 1. 校验移库单保存数据 + validateMovementOrderPrepare(updateReqVO.getId()); + validateMovementOrderSaveData(updateReqVO); + + // 2.1 更新移库单 + WmsMovementOrderDO updateObj = BeanUtils.toBean(updateReqVO, WmsMovementOrderDO.class) + .setStatus(WmsOrderStatusEnum.PREPARE.getStatus()); + movementOrderMapper.updateById(updateObj); + // 2.2 更新移库单明细 + movementOrderDetailService.updateMovementOrderDetailList(updateReqVO.getId(), updateReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteMovementOrder(Long id) { + // 1. 校验存在,且可删除 + WmsMovementOrderDO order = validateMovementOrderExists(id); + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus()) + && ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.CANCELED.getStatus())) { + throw exception(MOVEMENT_ORDER_STATUS_NOT_DELETABLE); + } + + // 2.1 删除移库单 + movementOrderMapper.deleteById(id); + // 2.2 删除移库单明细 + movementOrderDetailService.deleteMovementOrderDetailListByOrderId(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void completeMovementOrder(Long id) { + // 1.1 校验存在,且草稿 + WmsMovementOrderDO order = validateMovementOrderPrepare(id); + // 1.2 校验移库单明细存在 + List details = movementOrderDetailService.validateMovementOrderDetailListExists(id); + + // 2. 完成移库单 + movementOrderMapper.updateById(new WmsMovementOrderDO().setId(id) + .setStatus(WmsOrderStatusEnum.FINISHED.getStatus())); + + // 3. 移动库存 + changeInventory(order, details); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelMovementOrder(Long id) { + // 1. 校验存在,且草稿 + validateMovementOrderPrepare(id); + + // 2. 作废移库单 + movementOrderMapper.updateById(new WmsMovementOrderDO().setId(id) + .setStatus(WmsOrderStatusEnum.CANCELED.getStatus())); + } + + @Override + public WmsMovementOrderDO getMovementOrder(Long id) { + return movementOrderMapper.selectById(id); + } + + @Override + public PageResult getMovementOrderPage(WmsMovementOrderPageReqVO pageReqVO) { + return movementOrderMapper.selectPage(pageReqVO); + } + + private void validateMovementOrderSaveData(WmsMovementOrderSaveReqVO reqVO) { + // 校验移库单号唯一 + validateMovementOrderNoUnique(reqVO.getId(), reqVO.getNo()); + // 校验仓库、库区存在 + warehouseService.validateWarehouseExists(reqVO.getSourceWarehouseId()); + warehouseService.validateWarehouseExists(reqVO.getTargetWarehouseId()); + reqVO.setSourceAreaId(warehouseAreaService.validateAndNormalizeWarehouseAreaId(reqVO.getSourceAreaId(), + reqVO.getSourceWarehouseId())); + reqVO.setTargetAreaId(warehouseAreaService.validateAndNormalizeWarehouseAreaId(reqVO.getTargetAreaId(), + reqVO.getTargetWarehouseId())); + // 校验来源和目标不能相同 + if (ObjectUtil.equal(reqVO.getSourceWarehouseId(), reqVO.getTargetWarehouseId()) + && ObjectUtil.equal(reqVO.getSourceAreaId(), reqVO.getTargetAreaId())) { + throw exception(MOVEMENT_ORDER_WAREHOUSE_AREA_SAME); + } + } + + private void validateMovementOrderNoUnique(Long id, String no) { + WmsMovementOrderDO order = movementOrderMapper.selectByNo(no); + if (order == null) { + return; + } + if (id == null || ObjectUtil.notEqual(order.getId(), id)) { + throw exception(MOVEMENT_ORDER_NO_DUPLICATE); + } + } + + private WmsMovementOrderDO validateMovementOrderExists(Long id) { + WmsMovementOrderDO order = id == null ? null : movementOrderMapper.selectById(id); + if (order == null) { + throw exception(MOVEMENT_ORDER_NOT_EXISTS); + } + return order; + } + + /** + * 校验移库单存在且为草稿状态 + * + * @param id 移库单编号 + * @return 移库单 + */ + private WmsMovementOrderDO validateMovementOrderPrepare(Long id) { + WmsMovementOrderDO order = validateMovementOrderExists(id); + if (ObjectUtil.notEqual(order.getStatus(), WmsOrderStatusEnum.PREPARE.getStatus())) { + throw exception(MOVEMENT_ORDER_STATUS_NOT_PREPARE); + } + return order; + } + + /** + * 移动移库单对应库存。 + * + * 移库会生成两条库存变更:来源仓库扣减库存,目标仓库增加库存;批次模式下,来源库存明细用 + * inventoryDetailId 扣减剩余数量,目标仓库生成新的库存明细。 + * + * @param order 移库单 + * @param details 移库单明细列表 + */ + private void changeInventory(WmsMovementOrderDO order, List details) { + List items = new ArrayList<>(details.size() * 2); + for (WmsMovementOrderDetailDO detail : details) { + items.add(BeanUtils.toBean(detail, WmsInventoryChangeReqDTO.Item.class) + .setWarehouseId(detail.getSourceWarehouseId()).setAreaId(detail.getSourceAreaId()) + .setQuantity(detail.getQuantity().negate())); + items.add(BeanUtils.toBean(detail, WmsInventoryChangeReqDTO.Item.class) + .setWarehouseId(detail.getTargetWarehouseId()).setAreaId(detail.getTargetAreaId()) + .setInventoryDetailId(null).setQuantity(detail.getQuantity())); + } + inventoryService.changeInventory(new WmsInventoryChangeReqDTO() + .setOrderId(order.getId()).setOrderNo(order.getNo()) + .setOrderType(WmsInventoryOrderTypeEnum.MOVEMENT.getType()).setItems(items)); + } + +}