diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java index 8f3cadd8..919f9a4d 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java @@ -38,6 +38,22 @@ public class IostorInvController { @Resource private IostorInvService iostorInvService; + @GetMapping("/availableInventoryPage") + @Operation(summary = "获得可用库存分页") + @PreAuthorize("@ss.hasPermission('wms:iostor-inv:query')") + public CommonResult> getAvailableInventoryPage( + @Valid AvailableInventoryPageReqVO reqVO) { + return success(iostorInvService.getAvailableInventoryPage(reqVO)); + } + + @PostMapping("/expandAvailableInventory") + @Operation(summary = "按箱号展开全部可用子卷") + @PreAuthorize("@ss.hasPermission('wms:iostor-inv:query')") + public CommonResult> expandAvailableInventory( + @Valid @RequestBody ExpandAvailableInventoryReqVO reqVO) { + return success(iostorInvService.expandAvailableInventory(reqVO)); + } + @PostMapping("/create") @Operation(summary = "创建出入库单主表") @PreAuthorize("@ss.hasPermission('wms:iostor-inv:create')") diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryPageReqVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryPageReqVO.java new file mode 100644 index 00000000..afc4f5fe --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryPageReqVO.java @@ -0,0 +1,27 @@ +package cn.code.nl.module.wms.controller.admin.iostorinv.vo; + +import cn.code.nl.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - 可用库存分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class AvailableInventoryPageReqVO extends PageParam { + + @Schema(description = "仓库标识", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "仓库标识不能为空") + private String storId; + + @Schema(description = "物料编码") + private String materialCode; + + @Schema(description = "箱号") + private String vehicleCode; + + @Schema(description = "批次") + private String pcsn; + +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryRespVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryRespVO.java new file mode 100644 index 00000000..b6ba82e4 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryRespVO.java @@ -0,0 +1,41 @@ +package cn.code.nl.module.wms.controller.admin.iostorinv.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - 可用库存 Response VO") +@Data +public class AvailableInventoryRespVO { + + @Schema(description = "箱号") + private String vehicleCode; + @Schema(description = "批次") + private String pcsn; + @Schema(description = "物料标识") + private String materialId; + @Schema(description = "物料编码") + private String materialCode; + @Schema(description = "物料名称") + private String materialName; + @Schema(description = "可用数量") + private BigDecimal availableQty; + @Schema(description = "计量单位标识") + private String qtyUnitId; + @Schema(description = "计量单位名称") + private String qtyUnitName; + @Schema(description = "来源单据号") + private String extCode; + @Schema(description = "来源单据类型") + private String extType; + @Schema(description = "来源单据明细号") + private String extDtlCode; + @Schema(description = "仓库标识") + private String storId; + @Schema(description = "仓库编码") + private String storCode; + @Schema(description = "仓库名称") + private String storName; + +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/ExpandAvailableInventoryReqVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/ExpandAvailableInventoryReqVO.java new file mode 100644 index 00000000..7becb7cf --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/ExpandAvailableInventoryReqVO.java @@ -0,0 +1,21 @@ +package cn.code.nl.module.wms.controller.admin.iostorinv.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 按箱号展开可用库存 Request VO") +@Data +public class ExpandAvailableInventoryReqVO { + + @Schema(description = "仓库标识", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "仓库标识不能为空") + private String storId; + + @Schema(description = "箱号列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "箱号列表不能为空") + private List vehicleCodes; + +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/iostorinvdtl/IostorinvDtlMapper.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/iostorinvdtl/IostorinvDtlMapper.java index df00b8ea..e4bdde7d 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/iostorinvdtl/IostorinvDtlMapper.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/iostorinvdtl/IostorinvDtlMapper.java @@ -3,10 +3,15 @@ package cn.code.nl.module.wms.dal.mysql.iostorinvdtl; import java.util.*; import cn.code.nl.framework.common.pojo.PageResult; +import cn.code.nl.framework.mybatis.core.util.MyBatisUtils; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import cn.code.nl.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.code.nl.framework.mybatis.core.mapper.BaseMapperX; import cn.code.nl.module.wms.dal.dataobject.iostorinvdtl.IostorinvDtlDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import cn.code.nl.module.wms.controller.admin.iostorinv.vo.AvailableInventoryPageReqVO; +import cn.code.nl.module.wms.controller.admin.iostorinv.vo.AvailableInventoryRespVO; import cn.code.nl.module.wms.controller.admin.iostorinvdtl.vo.*; /** @@ -17,6 +22,27 @@ import cn.code.nl.module.wms.controller.admin.iostorinvdtl.vo.*; @Mapper public interface IostorinvDtlMapper extends BaseMapperX { + /** + * 分页查询指定仓库的可用库存。 + */ + default PageResult selectAvailableInventoryPage(AvailableInventoryPageReqVO reqVO) { + Page page = selectAvailableInventoryPage( + MyBatisUtils.buildPage(reqVO), reqVO); + return new PageResult<>(page.getRecords(), page.getTotal()); + } + + /** + * 执行可用库存分页 SQL。 + */ + Page selectAvailableInventoryPage( + Page page, @Param("reqVO") AvailableInventoryPageReqVO reqVO); + + /** + * 查询指定仓库、指定箱号下的全部可用子卷。 + */ + List selectAvailableInventoryByVehicleCodes( + @Param("storId") String storId, @Param("vehicleCodes") Collection vehicleCodes); + default PageResult selectPage(IostorinvDtlPageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() .eqIfPresent(IostorinvDtlDO::getIostorinvId, reqVO.getIostorinvId()) @@ -41,4 +67,4 @@ public interface IostorinvDtlMapper extends BaseMapperX { .orderByDesc(IostorinvDtlDO::getIostorinvdtlId)); } -} \ No newline at end of file +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java index 68b722bf..1b28f1e6 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java @@ -22,6 +22,22 @@ public interface IostorInvService { */ String createOutbound(@Valid IostorInvCreateReqVO reqVO); + /** + * 获得指定仓库的可用库存分页。 + * + * @param reqVO 分页查询条件 + * @return 可用库存分页 + */ + PageResult getAvailableInventoryPage(@Valid AvailableInventoryPageReqVO reqVO); + + /** + * 按箱号展开指定仓库中的全部可用子卷。 + * + * @param reqVO 展开条件 + * @return 可用子卷列表 + */ + List expandAvailableInventory(@Valid ExpandAvailableInventoryReqVO reqVO); + /** * 创建出入库单主表 * diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java index bcb984be..17623860 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java @@ -14,6 +14,7 @@ import cn.code.nl.framework.common.pojo.PageParam; import cn.code.nl.framework.common.util.object.BeanUtils; import cn.code.nl.module.wms.dal.mysql.iostorinv.IostorInvMapper; +import cn.code.nl.module.wms.dal.mysql.iostorinvdtl.IostorinvDtlMapper; import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.code.nl.framework.common.util.collection.CollectionUtils.convertList; @@ -32,11 +33,26 @@ public class IostorInvServiceImpl implements IostorInvService { @Resource private IostorInvMapper iostorInvMapper; + @Resource + private IostorinvDtlMapper iostorinvDtlMapper; + @Override public String createOutbound(IostorInvCreateReqVO reqVO) { throw new UnsupportedOperationException("出库单聚合保存功能尚未实现"); } + @Override + public PageResult getAvailableInventoryPage(AvailableInventoryPageReqVO reqVO) { + return iostorinvDtlMapper.selectAvailableInventoryPage(reqVO); + } + + @Override + public List expandAvailableInventory(ExpandAvailableInventoryReqVO reqVO) { + // 用户可以独立勾选子卷;按涉及箱号去重后,由数据库展开箱内全部可用子卷。 + Set vehicleCodes = new LinkedHashSet<>(reqVO.getVehicleCodes()); + return iostorinvDtlMapper.selectAvailableInventoryByVehicleCodes(reqVO.getStorId(), vehicleCodes); + } + @Override public String createIostorInv(IostorInvSaveReqVO createReqVO) { // 插入 diff --git a/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/iostorinvdtl/IostorinvDtlMapper.xml b/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/iostorinvdtl/IostorinvDtlMapper.xml index 9e1b610f..0711072a 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/iostorinvdtl/IostorinvDtlMapper.xml +++ b/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/iostorinvdtl/IostorinvDtlMapper.xml @@ -2,11 +2,62 @@ - + + gp.vehicle_code AS vehicle_code, + gp.pcsn AS pcsn, + gp.material_id AS material_id, + gp.material_code AS material_code, + mb.material_name AS material_name, + gp.qty - gp.frozen_qty AS available_qty, + gp.qty_unit_id AS qty_unit_id, + gp.qty_unit_name AS qty_unit_name, + gp.ext_code AS ext_code, + gp.ext_type AS ext_type, + gp.ext_dtl_code AS ext_dtl_code, + sa.stor_id AS stor_id, + sa.stor_code AS stor_code, + sa.stor_name AS stor_name + - \ No newline at end of file + + + + + diff --git a/nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceLocalSpringTest.java b/nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceLocalSpringTest.java index f361757f..438a6295 100644 --- a/nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceLocalSpringTest.java +++ b/nl-module-wms/nl-module-wms-server/src/test/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceLocalSpringTest.java @@ -1,17 +1,22 @@ package cn.code.nl.module.wms.service.iostorinv; import cn.code.nl.module.wms.controller.admin.iostorinv.vo.IostorInvCreateReqVO; +import cn.code.nl.module.wms.controller.admin.iostorinv.vo.AvailableInventoryRespVO; +import cn.code.nl.module.wms.controller.admin.iostorinv.vo.ExpandAvailableInventoryReqVO; import jakarta.annotation.Resource; import jakarta.validation.ConstraintViolationException; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Disabled; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import java.time.LocalDateTime; import java.util.Collections; +import java.util.List; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; @SpringBootTest @ActiveProfiles("local") @@ -34,4 +39,23 @@ class IostorInvServiceLocalSpringTest { assertTrue(exception.getConstraintViolations().stream() .anyMatch(violation -> violation.getPropertyPath().toString().contains("details"))); } + + /** + * 本地库数据前置:仓库 stor-1 的 BOX-001 有两个可用子卷,另一仓库也存在 BOX-001。 + * 当前仓库没有可移植的 WMS 本地测试数据集,避免向开发库写入固定业务数据,准备好上述数据后可手动启用。 + */ + @Test + @Disabled("需要 local 数据库预置跨仓库 BOX-001 测试数据") + void expandAvailableInventoryShouldReturnAllChildrenOnceAndOnlyFromRequestedWarehouse() { + ExpandAvailableInventoryReqVO reqVO = new ExpandAvailableInventoryReqVO(); + reqVO.setStorId("stor-1"); + reqVO.setVehicleCodes(List.of("BOX-001", "BOX-001")); + + List result = iostorInvService.expandAvailableInventory(reqVO); + + assertEquals(2, result.size()); + assertTrue(result.stream().allMatch(item -> "BOX-001".equals(item.getVehicleCode()))); + assertTrue(result.stream().allMatch(item -> "stor-1".equals(item.getStorId()))); + assertEquals(2, result.stream().map(AvailableInventoryRespVO::getPcsn).distinct().count()); + } }