Commit a693d31e23ab9047434414748b4d1a46be8644dd
1 parent
8bcd65d2
✨ feat(stock): 添加虚拟仓过滤功能
新增虚拟仓过滤模式,支持排除虚拟仓和仅查看虚拟仓的选项。更新相关请求对象和数据查询逻辑,以便后端处理虚拟仓的过滤需求。
Showing
6 changed files
with
291 additions
and
6 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockPageReqVO.java
| @@ -12,6 +12,18 @@ import lombok.ToString; | @@ -12,6 +12,18 @@ import lombok.ToString; | ||
| 12 | @ToString(callSuper = true) | 12 | @ToString(callSuper = true) |
| 13 | public class MesWmMaterialStockPageReqVO extends PageParam { | 13 | public class MesWmMaterialStockPageReqVO extends PageParam { |
| 14 | 14 | ||
| 15 | + /** | ||
| 16 | + * 虚拟仓过滤模式 - 排除虚拟仓 | ||
| 17 | + */ | ||
| 18 | + public static final String VIRTUAL_FILTER_EXCLUDE = "exclude"; | ||
| 19 | + /** | ||
| 20 | + * 虚拟仓过滤模式 - 只看虚拟仓 | ||
| 21 | + */ | ||
| 22 | + public static final String VIRTUAL_FILTER_ONLY = "only"; | ||
| 23 | + | ||
| 24 | + @Schema(description = "虚拟仓过滤模式", example = "exclude") | ||
| 25 | + private String virtualFilter; | ||
| 26 | + | ||
| 15 | @Schema(description = "物料分类编号", example = "1") | 27 | @Schema(description = "物料分类编号", example = "1") |
| 16 | private Long itemTypeId; | 28 | private Long itemTypeId; |
| 17 | 29 |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java
| @@ -21,8 +21,9 @@ public interface MesWmMaterialStockMapper extends BaseMapperX<MesWmMaterialStock | @@ -21,8 +21,9 @@ public interface MesWmMaterialStockMapper extends BaseMapperX<MesWmMaterialStock | ||
| 21 | 21 | ||
| 22 | default PageResult<MesWmMaterialStockDO> selectPage(MesWmMaterialStockPageReqVO reqVO, | 22 | default PageResult<MesWmMaterialStockDO> selectPage(MesWmMaterialStockPageReqVO reqVO, |
| 23 | Collection<Long> itemTypeIds, | 23 | Collection<Long> itemTypeIds, |
| 24 | - Collection<Long> itemIds) { | ||
| 25 | - return selectPage(reqVO, new LambdaQueryWrapperX<MesWmMaterialStockDO>() | 24 | + Collection<Long> itemIds, |
| 25 | + Long virtualWarehouseId) { | ||
| 26 | + LambdaQueryWrapperX<MesWmMaterialStockDO> wrapper = new LambdaQueryWrapperX<MesWmMaterialStockDO>() | ||
| 26 | .inIfPresent(MesWmMaterialStockDO::getItemTypeId, itemTypeIds) | 27 | .inIfPresent(MesWmMaterialStockDO::getItemTypeId, itemTypeIds) |
| 27 | .inIfPresent(MesWmMaterialStockDO::getItemId, itemIds) | 28 | .inIfPresent(MesWmMaterialStockDO::getItemId, itemIds) |
| 28 | .likeIfPresent(MesWmMaterialStockDO::getBatchCode, reqVO.getBatchCode()) | 29 | .likeIfPresent(MesWmMaterialStockDO::getBatchCode, reqVO.getBatchCode()) |
| @@ -31,9 +32,18 @@ public interface MesWmMaterialStockMapper extends BaseMapperX<MesWmMaterialStock | @@ -31,9 +32,18 @@ public interface MesWmMaterialStockMapper extends BaseMapperX<MesWmMaterialStock | ||
| 31 | .eqIfPresent(MesWmMaterialStockDO::getLocationId, reqVO.getLocationId()) | 32 | .eqIfPresent(MesWmMaterialStockDO::getLocationId, reqVO.getLocationId()) |
| 32 | .eqIfPresent(MesWmMaterialStockDO::getAreaId, reqVO.getAreaId()) | 33 | .eqIfPresent(MesWmMaterialStockDO::getAreaId, reqVO.getAreaId()) |
| 33 | .eqIfPresent(MesWmMaterialStockDO::getVendorId, reqVO.getVendorId()) | 34 | .eqIfPresent(MesWmMaterialStockDO::getVendorId, reqVO.getVendorId()) |
| 34 | - .eqIfPresent(MesWmMaterialStockDO::getFrozen, reqVO.getFrozen()) | ||
| 35 | - .ne(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO) | ||
| 36 | - .orderByAsc(MesWmMaterialStockDO::getReceiptTime)); | 35 | + .eqIfPresent(MesWmMaterialStockDO::getFrozen, reqVO.getFrozen()); |
| 36 | + wrapper.ne(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO) | ||
| 37 | + .orderByAsc(MesWmMaterialStockDO::getReceiptTime); | ||
| 38 | + // 虚拟仓过滤(Service 层已将 virtualFilter 解析为 virtualWarehouseId) | ||
| 39 | + if (virtualWarehouseId != null) { | ||
| 40 | + if (MesWmMaterialStockPageReqVO.VIRTUAL_FILTER_ONLY.equals(reqVO.getVirtualFilter())) { | ||
| 41 | + wrapper.eq(MesWmMaterialStockDO::getWarehouseId, virtualWarehouseId); | ||
| 42 | + } else if (MesWmMaterialStockPageReqVO.VIRTUAL_FILTER_EXCLUDE.equals(reqVO.getVirtualFilter())) { | ||
| 43 | + wrapper.ne(MesWmMaterialStockDO::getWarehouseId, virtualWarehouseId); | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | + return selectPage(reqVO, wrapper); | ||
| 37 | } | 47 | } |
| 38 | 48 | ||
| 39 | default Long selectCountByWarehouseId(Long warehouseId) { | 49 | default Long selectCountByWarehouseId(Long warehouseId) { |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java
| 1 | package cn.iocoder.yudao.module.mes.service.wm.materialstock; | 1 | package cn.iocoder.yudao.module.mes.service.wm.materialstock; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.lang.Assert; | ||
| 4 | import cn.hutool.core.util.ObjUtil; | 5 | import cn.hutool.core.util.ObjUtil; |
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | import cn.iocoder.yudao.framework.common.util.collection.SetUtils; | 7 | import cn.iocoder.yudao.framework.common.util.collection.SetUtils; |
| @@ -11,11 +12,14 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; | @@ -11,11 +12,14 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; | ||
| 11 | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; | 12 | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; |
| 12 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO; | 13 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO; |
| 13 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; | 14 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; |
| 15 | +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; | ||
| 14 | import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper; | 16 | import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper; |
| 15 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; | 17 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| 16 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService; | 18 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService; |
| 17 | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; | 19 | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| 20 | +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; | ||
| 18 | import jakarta.annotation.Resource; | 21 | import jakarta.annotation.Resource; |
| 22 | +import org.springframework.context.annotation.Lazy; | ||
| 19 | import org.springframework.stereotype.Service; | 23 | import org.springframework.stereotype.Service; |
| 20 | import org.springframework.validation.annotation.Validated; | 24 | import org.springframework.validation.annotation.Validated; |
| 21 | 25 | ||
| @@ -43,6 +47,9 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService | @@ -43,6 +47,9 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService | ||
| 43 | private MesMdItemTypeService itemTypeService; | 47 | private MesMdItemTypeService itemTypeService; |
| 44 | @Resource | 48 | @Resource |
| 45 | private MesWmWarehouseAreaService areaService; | 49 | private MesWmWarehouseAreaService areaService; |
| 50 | + @Resource | ||
| 51 | + @Lazy | ||
| 52 | + private MesWmWarehouseService warehouseService; | ||
| 46 | 53 | ||
| 47 | @Override | 54 | @Override |
| 48 | public MesWmMaterialStockDO getMaterialStock(Long id) { | 55 | public MesWmMaterialStockDO getMaterialStock(Long id) { |
| @@ -77,9 +84,18 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService | @@ -77,9 +84,18 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService | ||
| 77 | if (pageReqVO.getItemId() != null) { | 84 | if (pageReqVO.getItemId() != null) { |
| 78 | itemIds = SetUtils.asSet(pageReqVO.getItemId()); | 85 | itemIds = SetUtils.asSet(pageReqVO.getItemId()); |
| 79 | } | 86 | } |
| 87 | + // 1.3 解析 virtualFilter:转换为虚拟仓 warehouseId | ||
| 88 | + Long virtualWarehouseId = null; | ||
| 89 | + String virtualFilter = pageReqVO.getVirtualFilter(); | ||
| 90 | + if (MesWmMaterialStockPageReqVO.VIRTUAL_FILTER_EXCLUDE.equals(virtualFilter) | ||
| 91 | + || MesWmMaterialStockPageReqVO.VIRTUAL_FILTER_ONLY.equals(virtualFilter)) { | ||
| 92 | + MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode( | ||
| 93 | + MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE); | ||
| 94 | + Assert.notNull(virtualWarehouse, "虚拟仓库(WIP_VIRTUAL_WAREHOUSE)不存在"); | ||
| 95 | + } | ||
| 80 | 96 | ||
| 81 | // 2. 分页查询 | 97 | // 2. 分页查询 |
| 82 | - return materialStockMapper.selectPage(pageReqVO, itemTypeIds, itemIds); | 98 | + return materialStockMapper.selectPage(pageReqVO, itemTypeIds, itemIds, virtualWarehouseId); |
| 83 | } | 99 | } |
| 84 | 100 | ||
| 85 | @Override | 101 | @Override |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesDetailServiceImpl.java
| @@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; | @@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; | ||
| 10 | import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; | 10 | import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; |
| 11 | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; | 11 | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| 12 | import jakarta.annotation.Resource; | 12 | import jakarta.annotation.Resource; |
| 13 | +import org.springframework.context.annotation.Lazy; | ||
| 13 | import org.springframework.stereotype.Service; | 14 | import org.springframework.stereotype.Service; |
| 14 | import org.springframework.validation.annotation.Validated; | 15 | import org.springframework.validation.annotation.Validated; |
| 15 | 16 | ||
| @@ -33,6 +34,7 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta | @@ -33,6 +34,7 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta | ||
| 33 | @Resource | 34 | @Resource |
| 34 | private MesMdItemService itemService; | 35 | private MesMdItemService itemService; |
| 35 | @Resource | 36 | @Resource |
| 37 | + @Lazy | ||
| 36 | private MesWmProductSalesLineService productSalesLineService; | 38 | private MesWmProductSalesLineService productSalesLineService; |
| 37 | @Resource | 39 | @Resource |
| 38 | private MesWmMaterialStockService materialStockService; | 40 | private MesWmMaterialStockService materialStockService; |
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productsales/MesWmProductSalesDetailMapperTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||
| 4 | +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO; | ||
| 5 | +import jakarta.annotation.Resource; | ||
| 6 | +import org.junit.jupiter.api.Test; | ||
| 7 | + | ||
| 8 | +import java.math.BigDecimal; | ||
| 9 | +import java.util.List; | ||
| 10 | +import java.util.function.Consumer; | ||
| 11 | + | ||
| 12 | +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||
| 13 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||
| 14 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||
| 15 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 16 | + | ||
| 17 | +/** | ||
| 18 | + * {@link MesWmProductSalesDetailMapper} 的单元测试 | ||
| 19 | + * <p> | ||
| 20 | + * 覆盖:selectListByLineId、selectListBySalesId、deleteByLineId、deleteBySalesId | ||
| 21 | + */ | ||
| 22 | +public class MesWmProductSalesDetailMapperTest extends BaseDbUnitTest { | ||
| 23 | + | ||
| 24 | + @Resource | ||
| 25 | + private MesWmProductSalesDetailMapper detailMapper; | ||
| 26 | + | ||
| 27 | + // ==================== 辅助方法 ==================== | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * 创建明细 DO,固定 BigDecimal 精度为 2 位,避免 H2 decimal(14,2) 精度不匹配 | ||
| 31 | + */ | ||
| 32 | + private MesWmProductSalesDetailDO createDetailPojo(Consumer<MesWmProductSalesDetailDO> consumer) { | ||
| 33 | + return randomPojo(MesWmProductSalesDetailDO.class, o -> { | ||
| 34 | + o.setQuantity(new BigDecimal("10.00")); | ||
| 35 | + consumer.accept(o); | ||
| 36 | + }); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + // ==================== selectListByLineId ==================== | ||
| 40 | + | ||
| 41 | + @Test | ||
| 42 | + public void testSelectListByLineId_match() { | ||
| 43 | + Long lineId = 100L; | ||
| 44 | + MesWmProductSalesDetailDO match = createDetailPojo(o -> { | ||
| 45 | + o.setLineId(lineId); | ||
| 46 | + o.setSalesId(1L); | ||
| 47 | + }); | ||
| 48 | + detailMapper.insert(match); | ||
| 49 | + // 插入另一条不同 lineId 的记录,不应被返回 | ||
| 50 | + detailMapper.insert(cloneIgnoreId(match, o -> o.setLineId(999L))); | ||
| 51 | + | ||
| 52 | + List<MesWmProductSalesDetailDO> result = detailMapper.selectListByLineId(lineId); | ||
| 53 | + | ||
| 54 | + assertEquals(1, result.size()); | ||
| 55 | + assertPojoEquals(match, result.get(0)); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + @Test | ||
| 59 | + public void testSelectListByLineId_multipleRows() { | ||
| 60 | + Long lineId = 200L; | ||
| 61 | + MesWmProductSalesDetailDO detail1 = createDetailPojo(o -> { | ||
| 62 | + o.setLineId(lineId); | ||
| 63 | + o.setSalesId(2L); | ||
| 64 | + }); | ||
| 65 | + MesWmProductSalesDetailDO detail2 = createDetailPojo(o -> { | ||
| 66 | + o.setLineId(lineId); | ||
| 67 | + o.setSalesId(2L); | ||
| 68 | + }); | ||
| 69 | + detailMapper.insert(detail1); | ||
| 70 | + detailMapper.insert(detail2); | ||
| 71 | + | ||
| 72 | + List<MesWmProductSalesDetailDO> result = detailMapper.selectListByLineId(lineId); | ||
| 73 | + | ||
| 74 | + assertEquals(2, result.size()); | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + @Test | ||
| 78 | + public void testSelectListByLineId_noMatch() { | ||
| 79 | + MesWmProductSalesDetailDO detail = createDetailPojo(o -> { | ||
| 80 | + o.setLineId(300L); | ||
| 81 | + o.setSalesId(3L); | ||
| 82 | + }); | ||
| 83 | + detailMapper.insert(detail); | ||
| 84 | + | ||
| 85 | + List<MesWmProductSalesDetailDO> result = detailMapper.selectListByLineId(999L); | ||
| 86 | + | ||
| 87 | + assertTrue(result.isEmpty()); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + // ==================== selectListBySalesId ==================== | ||
| 91 | + | ||
| 92 | + @Test | ||
| 93 | + public void testSelectListBySalesId_match() { | ||
| 94 | + Long salesId = 10L; | ||
| 95 | + MesWmProductSalesDetailDO match = createDetailPojo(o -> { | ||
| 96 | + o.setSalesId(salesId); | ||
| 97 | + o.setLineId(1L); | ||
| 98 | + }); | ||
| 99 | + detailMapper.insert(match); | ||
| 100 | + // 插入另一条不同 salesId 的记录 | ||
| 101 | + detailMapper.insert(cloneIgnoreId(match, o -> o.setSalesId(888L))); | ||
| 102 | + | ||
| 103 | + List<MesWmProductSalesDetailDO> result = detailMapper.selectListBySalesId(salesId); | ||
| 104 | + | ||
| 105 | + assertEquals(1, result.size()); | ||
| 106 | + assertPojoEquals(match, result.get(0)); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + @Test | ||
| 110 | + public void testSelectListBySalesId_multipleRows() { | ||
| 111 | + Long salesId = 20L; | ||
| 112 | + MesWmProductSalesDetailDO detail1 = createDetailPojo(o -> { | ||
| 113 | + o.setSalesId(salesId); | ||
| 114 | + o.setLineId(11L); | ||
| 115 | + }); | ||
| 116 | + MesWmProductSalesDetailDO detail2 = createDetailPojo(o -> { | ||
| 117 | + o.setSalesId(salesId); | ||
| 118 | + o.setLineId(12L); | ||
| 119 | + }); | ||
| 120 | + detailMapper.insert(detail1); | ||
| 121 | + detailMapper.insert(detail2); | ||
| 122 | + | ||
| 123 | + List<MesWmProductSalesDetailDO> result = detailMapper.selectListBySalesId(salesId); | ||
| 124 | + | ||
| 125 | + assertEquals(2, result.size()); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + @Test | ||
| 129 | + public void testSelectListBySalesId_noMatch() { | ||
| 130 | + MesWmProductSalesDetailDO detail = createDetailPojo(o -> { | ||
| 131 | + o.setSalesId(30L); | ||
| 132 | + o.setLineId(2L); | ||
| 133 | + }); | ||
| 134 | + detailMapper.insert(detail); | ||
| 135 | + | ||
| 136 | + List<MesWmProductSalesDetailDO> result = detailMapper.selectListBySalesId(9999L); | ||
| 137 | + | ||
| 138 | + assertTrue(result.isEmpty()); | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + // ==================== deleteByLineId ==================== | ||
| 142 | + | ||
| 143 | + @Test | ||
| 144 | + public void testDeleteByLineId_deletesOnlyTargetLine() { | ||
| 145 | + Long lineId = 400L; | ||
| 146 | + MesWmProductSalesDetailDO toDelete = createDetailPojo(o -> { | ||
| 147 | + o.setLineId(lineId); | ||
| 148 | + o.setSalesId(4L); | ||
| 149 | + }); | ||
| 150 | + MesWmProductSalesDetailDO toKeep = createDetailPojo(o -> { | ||
| 151 | + o.setLineId(500L); | ||
| 152 | + o.setSalesId(4L); | ||
| 153 | + }); | ||
| 154 | + detailMapper.insert(toDelete); | ||
| 155 | + detailMapper.insert(toKeep); | ||
| 156 | + | ||
| 157 | + detailMapper.deleteByLineId(lineId); | ||
| 158 | + | ||
| 159 | + // lineId=400 的记录已被逻辑删除 | ||
| 160 | + List<MesWmProductSalesDetailDO> deletedResult = detailMapper.selectListByLineId(lineId); | ||
| 161 | + assertTrue(deletedResult.isEmpty(), "lineId=400 的记录应被删除"); | ||
| 162 | + | ||
| 163 | + // lineId=500 的记录保留 | ||
| 164 | + List<MesWmProductSalesDetailDO> keptResult = detailMapper.selectListByLineId(500L); | ||
| 165 | + assertEquals(1, keptResult.size()); | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + @Test | ||
| 169 | + public void testDeleteByLineId_multipleRows() { | ||
| 170 | + Long lineId = 600L; | ||
| 171 | + detailMapper.insert(createDetailPojo(o -> { o.setLineId(lineId); o.setSalesId(5L); })); | ||
| 172 | + detailMapper.insert(createDetailPojo(o -> { o.setLineId(lineId); o.setSalesId(5L); })); | ||
| 173 | + | ||
| 174 | + detailMapper.deleteByLineId(lineId); | ||
| 175 | + | ||
| 176 | + List<MesWmProductSalesDetailDO> result = detailMapper.selectListByLineId(lineId); | ||
| 177 | + assertTrue(result.isEmpty(), "同 lineId 下的所有记录均应被删除"); | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + // ==================== deleteBySalesId ==================== | ||
| 181 | + | ||
| 182 | + @Test | ||
| 183 | + public void testDeleteBySalesId_deletesOnlyTargetSales() { | ||
| 184 | + Long salesId = 700L; | ||
| 185 | + MesWmProductSalesDetailDO toDelete = createDetailPojo(o -> { | ||
| 186 | + o.setSalesId(salesId); | ||
| 187 | + o.setLineId(6L); | ||
| 188 | + }); | ||
| 189 | + MesWmProductSalesDetailDO toKeep = createDetailPojo(o -> { | ||
| 190 | + o.setSalesId(800L); | ||
| 191 | + o.setLineId(7L); | ||
| 192 | + }); | ||
| 193 | + detailMapper.insert(toDelete); | ||
| 194 | + detailMapper.insert(toKeep); | ||
| 195 | + | ||
| 196 | + detailMapper.deleteBySalesId(salesId); | ||
| 197 | + | ||
| 198 | + // salesId=700 的记录已被逻辑删除 | ||
| 199 | + List<MesWmProductSalesDetailDO> deletedResult = detailMapper.selectListBySalesId(salesId); | ||
| 200 | + assertTrue(deletedResult.isEmpty(), "salesId=700 的记录应被删除"); | ||
| 201 | + | ||
| 202 | + // salesId=800 的记录保留 | ||
| 203 | + List<MesWmProductSalesDetailDO> keptResult = detailMapper.selectListBySalesId(800L); | ||
| 204 | + assertEquals(1, keptResult.size()); | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + @Test | ||
| 208 | + public void testDeleteBySalesId_multipleRows() { | ||
| 209 | + Long salesId = 900L; | ||
| 210 | + detailMapper.insert(createDetailPojo(o -> { o.setSalesId(salesId); o.setLineId(8L); })); | ||
| 211 | + detailMapper.insert(createDetailPojo(o -> { o.setSalesId(salesId); o.setLineId(9L); })); | ||
| 212 | + detailMapper.insert(createDetailPojo(o -> { o.setSalesId(salesId); o.setLineId(10L); })); | ||
| 213 | + | ||
| 214 | + detailMapper.deleteBySalesId(salesId); | ||
| 215 | + | ||
| 216 | + List<MesWmProductSalesDetailDO> result = detailMapper.selectListBySalesId(salesId); | ||
| 217 | + assertTrue(result.isEmpty(), "同 salesId 下的所有记录均应被删除"); | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | +} |
yudao-module-mes/src/test/resources/sql/create_tables.sql
| @@ -678,3 +678,28 @@ CREATE TABLE IF NOT EXISTS "mes_pro_route_process" ( | @@ -678,3 +678,28 @@ CREATE TABLE IF NOT EXISTS "mes_pro_route_process" ( | ||
| 678 | "tenant_id" bigint NOT NULL DEFAULT 0, | 678 | "tenant_id" bigint NOT NULL DEFAULT 0, |
| 679 | PRIMARY KEY ("id") | 679 | PRIMARY KEY ("id") |
| 680 | ); | 680 | ); |
| 681 | + | ||
| 682 | +-- ---------------------------- | ||
| 683 | +-- MES 销售出库明细 | ||
| 684 | +-- ---------------------------- | ||
| 685 | +CREATE TABLE IF NOT EXISTS "mes_wm_product_sales_detail" ( | ||
| 686 | + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||
| 687 | + "line_id" bigint NOT NULL, | ||
| 688 | + "sales_id" bigint NOT NULL, | ||
| 689 | + "item_id" bigint NOT NULL, | ||
| 690 | + "quantity" decimal(14,2) DEFAULT NULL, | ||
| 691 | + "material_stock_id" bigint DEFAULT NULL, | ||
| 692 | + "batch_id" bigint DEFAULT NULL, | ||
| 693 | + "batch_code" varchar(255) DEFAULT NULL, | ||
| 694 | + "warehouse_id" bigint DEFAULT NULL, | ||
| 695 | + "location_id" bigint DEFAULT NULL, | ||
| 696 | + "area_id" bigint DEFAULT NULL, | ||
| 697 | + "remark" varchar(500) DEFAULT NULL, | ||
| 698 | + "creator" varchar(64) DEFAULT '', | ||
| 699 | + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| 700 | + "updater" varchar(64) DEFAULT '', | ||
| 701 | + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| 702 | + "deleted" bit NOT NULL DEFAULT FALSE, | ||
| 703 | + "tenant_id" bigint NOT NULL DEFAULT 0, | ||
| 704 | + PRIMARY KEY ("id") | ||
| 705 | +); |