[fix] 出入库工具复查修复:调拨待办漏本人修改/待办行串位/调拨派生入库单凭证回查 (REQ-20260919-0003) #22
@@ -540,7 +540,9 @@
|
||||
- 返回 `record/sceneType/warehouses/items/processes/files/relatedOutOrder`。
|
||||
- `items` 按物料定义、物料索引和仓库汇总;`includeSerials` 默认 false,`serialLimit` 默认 100、上限 500,
|
||||
同时返回 `serialTotal/serialTruncated`。
|
||||
- `files.application/files.confirmation` 仅返回 `id/fileName/bucket/objectKey`。
|
||||
- `files.application/files.confirmation/files.allocationAudit` 仅返回 `id/fileName/bucket/objectKey`。
|
||||
调拨派生的入库单其申请附件与审批流图片挂在调拨单维度(`aggregate_id=调拨单id, in_out_allocation_type=2`),
|
||||
已按 `allocationOrderNo` 自动回查合并:申请附件并入 `application`,审批流图片(fileType=3)在 `allocationAudit`。
|
||||
- `relatedOutOrder` 仅给出关联出库单号和查询工具名,不内嵌出库单内容。
|
||||
|
||||
详细设计见 `warehouse-in-mcp-tools-design.md`。
|
||||
@@ -555,8 +557,10 @@
|
||||
- 分类:warehouse | 类型:读
|
||||
- 数据来源:m2 流程视图 `warehouse_out_process` / `warehouse_in_process` / `warehouse_allocation_process`
|
||||
- 权限:待办是"人"的语义,只按流程表 `user_id = 当前登录人` 过滤,不叠加代理商行级权限。
|
||||
- 待办判定:`user_id = 当前登录人 AND soft_delete = 0 AND operation_type IN (0,1,2)`
|
||||
(3=纯日志排除)。`todoOnly` 默认 true 再限定 `schedule_state=1` 真待办。
|
||||
- 待办判定:`user_id = 当前登录人 AND soft_delete = 0`;出入库视图排除
|
||||
`operation_type=3`(纯日志),调拨视图不过滤 `operation_type`(其 3=本人修改是真实待办)。
|
||||
`todoOnly` 默认 true 再限定 `schedule_state=1` 真待办,且该条件下推到第二阶段组装查询,
|
||||
代表行不串到已办审批行。
|
||||
- 同一笔调拨最多以调拨审批/确认、出库执行、入库执行 3 个视角出现,不去重;
|
||||
派生自调拨的真实出入库单带 `derivedFrom="allocation"`,可按 `allocationOrderNo` 归并。
|
||||
- 分页实现:先全量查符合条件流程行按单据 id 内存去重 → 内存分页 → 按页 id 批量查视图行组装,防 N+1。
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
- 可选 `serials` 以及完整 `serialTotal`、`serialTruncated`;
|
||||
- `schedule_state=2` 的已处理流程;
|
||||
- `file_type=0/1` 的申请、确认入库附件,仅返回 OSS 对象定位,不生成签名 URL;
|
||||
调拨派生入库单的申请附件(fileType=0)与审批流图片(fileType=3)挂在调拨单维度
|
||||
(`aggregate_id=调拨单id, in_out_allocation_type=2`),详情按 `allocationOrderNo` 回查合并:
|
||||
前者并入 `files.application`,后者在 `files.allocationAudit`;
|
||||
- `relatedOutOrder`:仅出库单号和建议调用的 `warehouse.out.detail.query` 工具名。
|
||||
|
||||
## 4. 权限与安全
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
流程视图一行 = 单据 × 处理人 × 待办环节。待办判定:
|
||||
|
||||
```
|
||||
user_id = 当前登录人 AND soft_delete = 0 AND operation_type IN (0, 1, 2)
|
||||
user_id = 当前登录人 AND soft_delete = 0
|
||||
```
|
||||
|
||||
- `operation_type=3` 为"驳回或修改记录",纯日志不需要待办,排除(出入库视图);
|
||||
调拨视图 `operation_type=4`(撤销)同为纯日志,也被 IN (0,1,2) 排除。
|
||||
- 出入库视图再排除 `operation_type=3`("驳回或修改记录",纯日志不需要待办);
|
||||
调拨视图**不过滤** `operation_type`——其 `3=本人修改` 是真实待办环节(调拨被驳回后
|
||||
申请人需修改重新提交),老系统调拨待办接口同样不过滤(`warehouseAllocationProcessListByPage`
|
||||
仅按 user_id + soft_delete 过滤)。`4=撤销` 在调拨视图也视为可操作环节一并返回。
|
||||
- 不按代理商过滤:待办是"人"的语义,不是代理商数据权限语义。
|
||||
老系统出入库待办同样只按 `user_id` 过滤(`input.setUserId(user.getId())`),
|
||||
只有调拨待办显式 `eq("user_id", user.getId())` 且代理行级过滤被注释掉,
|
||||
@@ -34,8 +36,8 @@ user_id = 当前登录人 AND soft_delete = 0 AND operation_type IN (0, 1, 2)
|
||||
| 0 | 审批流操作 | 审批流操作 |
|
||||
| 1 | 确认出库 / 确认入库 | 确认入库操作 |
|
||||
| 2 | 本人修改(需申请人待办) | 确认出库操作 |
|
||||
| 3 | 驳回或修改操作(纯日志,排除) | 本人修改 |
|
||||
| 4 | — | 撤销(纯日志,排除) |
|
||||
| 3 | 驳回或修改操作(纯日志,排除) | 本人修改(真实待办,保留) |
|
||||
| 4 | — | 撤销(保留) |
|
||||
|
||||
`schedule_state`:0=无需待办,1=待办,2=已办。
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.iiv.agent.entity.m2;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/** 调拨/采购/划转单 warehouse_allocation(m2,只读):op_type 0调拨 1采购 2划转。 */
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@TableName("warehouse_allocation")
|
||||
public class WarehouseAllocation implements Serializable {
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Integer softDelete;
|
||||
@TableField("allocation_order_no")
|
||||
private String allocationOrderNo;
|
||||
private Integer opType;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package me.iiv.agent.mapper.m2;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import me.iiv.agent.entity.m2.WarehouseAllocation;
|
||||
|
||||
public interface WarehouseAllocationMapper extends BaseMapper<WarehouseAllocation> {
|
||||
}
|
||||
@@ -3,11 +3,13 @@ package me.iiv.agent.mcp.tool;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import me.iiv.agent.entity.m0.User;
|
||||
import me.iiv.agent.entity.m2.WarehouseAllocation;
|
||||
import me.iiv.agent.entity.m2.WarehouseIn;
|
||||
import me.iiv.agent.entity.m2.WarehouseInAuditProcess;
|
||||
import me.iiv.agent.entity.m2.WarehouseInDetail;
|
||||
import me.iiv.agent.entity.m2.WarehouseInOutPic;
|
||||
import me.iiv.agent.entity.m2.WarehouseType;
|
||||
import me.iiv.agent.mapper.m2.WarehouseAllocationMapper;
|
||||
import me.iiv.agent.mapper.m2.WarehouseInAuditProcessMapper;
|
||||
import me.iiv.agent.mapper.m2.WarehouseInDetailMapper;
|
||||
import me.iiv.agent.mapper.m2.WarehouseInMapper;
|
||||
@@ -49,6 +51,7 @@ public class WarehouseInTools implements ToolObject {
|
||||
private static final int DEFAULT_SERIAL_LIMIT = 100;
|
||||
private static final int MAX_SERIAL_LIMIT = 500;
|
||||
private static final int IN_TYPE = 0;
|
||||
private static final int ALLOCATION_TYPE = 2;
|
||||
private static final ZoneId BUSINESS_ZONE = ZoneId.of("Asia/Shanghai");
|
||||
private static final Pattern DATE_PATTERN = Pattern.compile("^\\d{4}-\\d{2}-\\d{2}$");
|
||||
private static final DateTimeFormatter DATE_FORMATTER =
|
||||
@@ -71,6 +74,7 @@ public class WarehouseInTools implements ToolObject {
|
||||
final WarehouseInDetailMapper detailMapper;
|
||||
final WarehouseInAuditProcessMapper processMapper;
|
||||
final WarehouseInOutPicMapper picMapper;
|
||||
final WarehouseAllocationMapper allocationMapper;
|
||||
final WarehouseTypeMapper warehouseTypeMapper;
|
||||
final PermissionService permissionService;
|
||||
|
||||
@@ -78,12 +82,14 @@ public class WarehouseInTools implements ToolObject {
|
||||
WarehouseInDetailMapper detailMapper,
|
||||
WarehouseInAuditProcessMapper processMapper,
|
||||
WarehouseInOutPicMapper picMapper,
|
||||
WarehouseAllocationMapper allocationMapper,
|
||||
WarehouseTypeMapper warehouseTypeMapper,
|
||||
PermissionService permissionService) {
|
||||
this.warehouseInMapper = warehouseInMapper;
|
||||
this.detailMapper = detailMapper;
|
||||
this.processMapper = processMapper;
|
||||
this.picMapper = picMapper;
|
||||
this.allocationMapper = allocationMapper;
|
||||
this.warehouseTypeMapper = warehouseTypeMapper;
|
||||
this.permissionService = permissionService;
|
||||
}
|
||||
@@ -180,6 +186,8 @@ public class WarehouseInTools implements ToolObject {
|
||||
@Tool(name = "warehouse.in.detail.query", description = "按 id 或入库单号查询入库单详情。"
|
||||
+ "行级权限与 warehouse.in.list.query 完全一致,不存在与无权访问统一返回 NOT_FOUND_OR_FORBIDDEN。"
|
||||
+ "返回主单、场景类型、仓库、按物料和仓库汇总的明细、可选 SN、已处理流程和申请/确认附件;调拨入库单可直接查询。"
|
||||
+ "files 含 application(申请附件)/confirmation(入库确认)/allocationAudit(调拨审批流图片);"
|
||||
+ "调拨派生的入库单其申请附件与审批图片挂在调拨单维度,已按调拨单号自动回查合并。"
|
||||
+ "关联出库仅返回索引,不内嵌内容。includeSerials 默认 false;serialLimit 默认100、上限500。")
|
||||
public Map<String, Object> warehouseInDetailQuery(
|
||||
@ToolParam(required = false, description = "入库单主键 id,与 inOrderNo 至少传一个") Long id,
|
||||
@@ -222,11 +230,19 @@ public class WarehouseInTools implements ToolObject {
|
||||
.eq(WarehouseInAuditProcess::getScheduleState, 2)
|
||||
.orderByAsc(WarehouseInAuditProcess::getOperationTime)
|
||||
.orderByAsc(WarehouseInAuditProcess::getId));
|
||||
List<WarehouseInOutPic> pics = picMapper.selectList(new LambdaQueryWrapper<WarehouseInOutPic>()
|
||||
List<WarehouseInOutPic> pics = new ArrayList<>(picMapper.selectList(new LambdaQueryWrapper<WarehouseInOutPic>()
|
||||
.eq(WarehouseInOutPic::getSoftDelete, 0)
|
||||
.eq(WarehouseInOutPic::getAggregateId, order.getId())
|
||||
.eq(WarehouseInOutPic::getInOutAllocationType, IN_TYPE)
|
||||
.orderByAsc(WarehouseInOutPic::getId));
|
||||
.orderByAsc(WarehouseInOutPic::getId)));
|
||||
// 调拨派生的入库单:申请附件(fileType=0)和审批流图片(fileType=3)挂在调拨单维度
|
||||
// (aggregate_id=调拨单id, in_out_allocation_type=2),按调拨单号回查补齐,否则这两类凭证恒为空。
|
||||
List<WarehouseInOutPic> allocationPics = allocationPics(order);
|
||||
if (!allocationPics.isEmpty()) {
|
||||
Set<Long> seen = pics.stream().map(WarehouseInOutPic::getId).collect(Collectors.toSet());
|
||||
allocationPics.stream().filter(pic -> pic.getId() == null || seen.add(pic.getId()))
|
||||
.forEach(pics::add);
|
||||
}
|
||||
WarehouseType type = order.getWarehouseTypeId() == null
|
||||
? null : warehouseTypeMapper.selectById(order.getWarehouseTypeId());
|
||||
|
||||
@@ -242,6 +258,7 @@ public class WarehouseInTools implements ToolObject {
|
||||
Map<String, Object> files = new LinkedHashMap<>();
|
||||
files.put("application", fileRecords(pics, 0));
|
||||
files.put("confirmation", fileRecords(pics, 1));
|
||||
files.put("allocationAudit", fileRecords(pics, 3));
|
||||
result.put("files", files);
|
||||
result.put("relatedOutOrder", relatedOutOrder(order));
|
||||
return result;
|
||||
@@ -413,8 +430,26 @@ public class WarehouseInTools implements ToolObject {
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Map<String, Object> relatedOutOrder(WarehouseIn order) {
|
||||
if (!StringUtils.hasText(order.getOutOrderNo())) return null;
|
||||
/** 调拨派生入库单的申请/审批凭证挂在调拨单维度,按调拨单号回查;调拨单不存在或无调拨单号时返回空。 */
|
||||
private List<WarehouseInOutPic> allocationPics(WarehouseIn order) {
|
||||
if (!StringUtils.hasText(order.getAllocationOrderNo())) {
|
||||
return List.of();
|
||||
}
|
||||
WarehouseAllocation allocation = allocationMapper.selectOne(new LambdaQueryWrapper<WarehouseAllocation>()
|
||||
.eq(WarehouseAllocation::getSoftDelete, 0)
|
||||
.eq(WarehouseAllocation::getAllocationOrderNo, order.getAllocationOrderNo())
|
||||
.last("LIMIT 1"));
|
||||
if (allocation == null) {
|
||||
return List.of();
|
||||
}
|
||||
return picMapper.selectList(new LambdaQueryWrapper<WarehouseInOutPic>()
|
||||
.eq(WarehouseInOutPic::getSoftDelete, 0)
|
||||
.eq(WarehouseInOutPic::getAggregateId, allocation.getId())
|
||||
.eq(WarehouseInOutPic::getInOutAllocationType, ALLOCATION_TYPE)
|
||||
.orderByAsc(WarehouseInOutPic::getId));
|
||||
}
|
||||
|
||||
private Map<String, Object> relatedOutOrder(WarehouseIn order) { if (!StringUtils.hasText(order.getOutOrderNo())) return null;
|
||||
Map<String, Object> related = new LinkedHashMap<>();
|
||||
related.put("outOrderNo", order.getOutOrderNo());
|
||||
related.put("queryTool", "warehouse.out.detail.query");
|
||||
|
||||
@@ -40,7 +40,7 @@ public class WarehouseTodoTools implements ToolObject {
|
||||
private static final DateTimeFormatter TIME_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(BUSINESS_ZONE);
|
||||
|
||||
/** 待办环节只保留可办理的操作类型,3(驳回或修改记录)为纯日志,排除。 */
|
||||
/** 待办环节只保留可办理的操作类型,3(驳回或修改记录)为纯日志,排除。仅适用于出入库视图,调拨视图不过滤。 */
|
||||
private static final List<Integer> TODO_OPERATION_TYPES = List.of(0, 1, 2);
|
||||
|
||||
private static final Map<Integer, String> SCHEDULE_STATE_TEXT = Map.of(
|
||||
@@ -76,8 +76,9 @@ public class WarehouseTodoTools implements ToolObject {
|
||||
@Tool(name = "warehouse.todo.list.query", description = "分页查询当前用户的仓库待办,统一老系统 /myWarehouseOut/needToBeDealtWith、"
|
||||
+ "/myWarehouseIn/needToBeDealtWith、/warehouseAllocation/warehouseAllocationProcessListByPage 三个待办接口。"
|
||||
+ "数据来自 m2 流程视图 warehouse_out_process / warehouse_in_process / warehouse_allocation_process:"
|
||||
+ "待办语义 = 视图里存在当前用户的任务行(user_id = 当前登录人 AND soft_delete = 0 AND operation_type IN (0,1,2),"
|
||||
+ "3=纯日志排除),不按代理商过滤。同一笔调拨最多以调拨审批/确认、出库执行、入库执行 3 个视角出现,不去重;"
|
||||
+ "待办语义 = 视图里存在当前用户的任务行(user_id = 当前登录人 AND soft_delete = 0,"
|
||||
+ "出库/入库视图排除 operation_type=3 纯日志;调拨视图不过滤 operation_type,其 3=本人修改 是真实待办),不按代理商过滤。"
|
||||
+ "同一笔调拨最多以调拨审批/确认、出库执行、入库执行 3 个视角出现,不去重;"
|
||||
+ "派生自调拨的真实出入库单带 derivedFrom=\"allocation\" 标记。"
|
||||
+ "docType:out=出库单 in=入库单 allocation=调拨/采购/划转单(按 op_type 区分) all=全部,默认 all。"
|
||||
+ "todoOnly 默认 true(只返回 schedule_state=1 真待办),false 返回当前用户参与过的全部单据。"
|
||||
@@ -174,11 +175,11 @@ public class WarehouseTodoTools implements ToolObject {
|
||||
}
|
||||
|
||||
private List<TodoStub> allocationStubs(Long userId, boolean onlyTodo, Integer orderState, String orderNo) {
|
||||
// 老系统调拨待办接口不过滤 operation_type(3=本人修改 是真实待办环节),与出入库视图口径不同
|
||||
LambdaQueryWrapper<WarehouseAllocationProcess> wrapper = new LambdaQueryWrapper<WarehouseAllocationProcess>()
|
||||
.select(WarehouseAllocationProcess::getWarehouseAllocationId, WarehouseAllocationProcess::getApplyDate)
|
||||
.eq(WarehouseAllocationProcess::getSoftDelete, 0)
|
||||
.eq(WarehouseAllocationProcess::getUserId, userId)
|
||||
.in(WarehouseAllocationProcess::getOperationType, TODO_OPERATION_TYPES)
|
||||
.eq(onlyTodo, WarehouseAllocationProcess::getScheduleState, 1)
|
||||
.eq(orderState != null, WarehouseAllocationProcess::getOrderState, orderState)
|
||||
.like(StringUtils.hasText(orderNo), WarehouseAllocationProcess::getAllocationOrderNo,
|
||||
@@ -206,6 +207,7 @@ public class WarehouseTodoTools implements ToolObject {
|
||||
.eq(WarehouseOutProcess::getSoftDelete, 0)
|
||||
.eq(WarehouseOutProcess::getUserId, userId)
|
||||
.in(WarehouseOutProcess::getOperationType, TODO_OPERATION_TYPES)
|
||||
.eq(onlyTodo, WarehouseOutProcess::getScheduleState, 1)
|
||||
.in(WarehouseOutProcess::getWarehouseOutId, ids)
|
||||
.orderByAsc(WarehouseOutProcess::getId)).stream()
|
||||
.collect(Collectors.groupingBy(WarehouseOutProcess::getWarehouseOutId,
|
||||
@@ -221,6 +223,7 @@ public class WarehouseTodoTools implements ToolObject {
|
||||
.eq(WarehouseInProcess::getSoftDelete, 0)
|
||||
.eq(WarehouseInProcess::getUserId, userId)
|
||||
.in(WarehouseInProcess::getOperationType, TODO_OPERATION_TYPES)
|
||||
.eq(onlyTodo, WarehouseInProcess::getScheduleState, 1)
|
||||
.in(WarehouseInProcess::getWarehouseInId, ids)
|
||||
.orderByAsc(WarehouseInProcess::getId)).stream()
|
||||
.collect(Collectors.groupingBy(WarehouseInProcess::getWarehouseInId,
|
||||
@@ -235,7 +238,7 @@ public class WarehouseTodoTools implements ToolObject {
|
||||
new LambdaQueryWrapper<WarehouseAllocationProcess>()
|
||||
.eq(WarehouseAllocationProcess::getSoftDelete, 0)
|
||||
.eq(WarehouseAllocationProcess::getUserId, userId)
|
||||
.in(WarehouseAllocationProcess::getOperationType, TODO_OPERATION_TYPES)
|
||||
.eq(onlyTodo, WarehouseAllocationProcess::getScheduleState, 1)
|
||||
.in(WarehouseAllocationProcess::getWarehouseAllocationId, ids)
|
||||
.orderByAsc(WarehouseAllocationProcess::getId)).stream()
|
||||
.collect(Collectors.groupingBy(WarehouseAllocationProcess::getWarehouseAllocationId,
|
||||
@@ -263,7 +266,8 @@ public class WarehouseTodoTools implements ToolObject {
|
||||
}
|
||||
}
|
||||
|
||||
/** 取代表当前待办环节的行:优先 schedule_state=1 的真待办行,否则取首行(已办/无需待办)。 */
|
||||
/** 取代表当前待办环节的行:onlyTodo 时 assemble 已下推 schedule_state=1,取首行即可;
|
||||
* 否则优先 schedule_state=1 的真待办行,再退回首行(已办/无需待办)。 */
|
||||
private static <T> T pickPending(List<T> rows, boolean onlyTodo) {
|
||||
if (onlyTodo) {
|
||||
return rows.get(0);
|
||||
|
||||
@@ -3,11 +3,13 @@ package me.iiv.agent.mcp.tool;
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import me.iiv.agent.entity.m0.User;
|
||||
import me.iiv.agent.entity.m2.WarehouseAllocation;
|
||||
import me.iiv.agent.entity.m2.WarehouseIn;
|
||||
import me.iiv.agent.entity.m2.WarehouseInAuditProcess;
|
||||
import me.iiv.agent.entity.m2.WarehouseInDetail;
|
||||
import me.iiv.agent.entity.m2.WarehouseInOutPic;
|
||||
import me.iiv.agent.entity.m2.WarehouseType;
|
||||
import me.iiv.agent.mapper.m2.WarehouseAllocationMapper;
|
||||
import me.iiv.agent.mapper.m2.WarehouseInAuditProcessMapper;
|
||||
import me.iiv.agent.mapper.m2.WarehouseInDetailMapper;
|
||||
import me.iiv.agent.mapper.m2.WarehouseInMapper;
|
||||
@@ -49,6 +51,7 @@ class WarehouseInToolsTest {
|
||||
@Mock WarehouseInDetailMapper detailMapper;
|
||||
@Mock WarehouseInAuditProcessMapper processMapper;
|
||||
@Mock WarehouseInOutPicMapper picMapper;
|
||||
@Mock WarehouseAllocationMapper allocationMapper;
|
||||
@Mock WarehouseTypeMapper warehouseTypeMapper;
|
||||
@Mock PermissionService permissionService;
|
||||
|
||||
@@ -61,9 +64,10 @@ class WarehouseInToolsTest {
|
||||
TableInfoHelper.initTableInfo(assistant, WarehouseInDetail.class);
|
||||
TableInfoHelper.initTableInfo(assistant, WarehouseInAuditProcess.class);
|
||||
TableInfoHelper.initTableInfo(assistant, WarehouseInOutPic.class);
|
||||
TableInfoHelper.initTableInfo(assistant, WarehouseAllocation.class);
|
||||
TableInfoHelper.initTableInfo(assistant, WarehouseType.class);
|
||||
tools = new WarehouseInTools(warehouseInMapper, detailMapper, processMapper, picMapper,
|
||||
warehouseTypeMapper, permissionService);
|
||||
allocationMapper, warehouseTypeMapper, permissionService);
|
||||
User user = new User();
|
||||
user.setId(7L);
|
||||
AuthContextHolder.set(new AuthContext(null, user));
|
||||
@@ -153,12 +157,54 @@ class WarehouseInToolsTest {
|
||||
Map<String, Object> files = (Map<String, Object>) result.get("files");
|
||||
assertEquals(1, ((List<?>) files.get("application")).size());
|
||||
assertEquals(1, ((List<?>) files.get("confirmation")).size());
|
||||
assertEquals(0, ((List<?>) files.get("allocationAudit")).size());
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> related = (Map<String, Object>) result.get("relatedOutOrder");
|
||||
assertEquals("CK-001", related.get("outOrderNo"));
|
||||
assertEquals("warehouse.out.detail.query", related.get("queryTool"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailMergesAllocationDimensionPicsForDerivedInOrder() {
|
||||
WarehouseIn order = order();
|
||||
when(warehouseInMapper.selectList(any())).thenReturn(List.of(order));
|
||||
when(detailMapper.selectList(any())).thenReturn(List.of(detail("SN-1", 1)));
|
||||
when(processMapper.selectList(any())).thenReturn(List.of());
|
||||
WarehouseAllocation allocation = new WarehouseAllocation();
|
||||
allocation.setId(40L);
|
||||
allocation.setAllocationOrderNo("DB-001");
|
||||
when(allocationMapper.selectOne(any())).thenReturn(allocation);
|
||||
WarehouseInOutPic own = pic(1L, 0);
|
||||
WarehouseInOutPic allocApply = pic(2L, 0);
|
||||
allocApply.setInOutAllocationType(2);
|
||||
WarehouseInOutPic allocAudit = pic(3L, 3);
|
||||
allocAudit.setInOutAllocationType(2);
|
||||
when(picMapper.selectList(any())).thenReturn(List.of(own), List.of(allocApply, allocAudit));
|
||||
|
||||
Map<String, Object> result = tools.warehouseInDetailQuery(20L, null, false, null);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> files = (Map<String, Object>) result.get("files");
|
||||
assertEquals(2, ((List<?>) files.get("application")).size());
|
||||
assertEquals(0, ((List<?>) files.get("confirmation")).size());
|
||||
assertEquals(1, ((List<?>) files.get("allocationAudit")).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailSkipsAllocationLookupWhenOrderIsNotAllocationDerived() {
|
||||
WarehouseIn order = order();
|
||||
order.setAllocationOrderNo(null);
|
||||
when(warehouseInMapper.selectList(any())).thenReturn(List.of(order));
|
||||
when(detailMapper.selectList(any())).thenReturn(List.of());
|
||||
when(processMapper.selectList(any())).thenReturn(List.of());
|
||||
when(picMapper.selectList(any())).thenReturn(List.of());
|
||||
|
||||
Map<String, Object> result = tools.warehouseInDetailQuery(20L, null, false, null);
|
||||
|
||||
assertEquals(0, ((List<?>) ((Map<?, ?>) result.get("files")).get("application")).size());
|
||||
verifyNoInteractions(allocationMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailDoesNotReturnSerialRowsUnlessRequested() {
|
||||
WarehouseIn order = order();
|
||||
|
||||
@@ -142,6 +142,55 @@ class WarehouseTodoToolsTest {
|
||||
assertEquals("确认出库", records.get(0).get("operationTypeText"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void todoOnlyTruePushesScheduleStateDownToAssembleQuery() {
|
||||
WarehouseOutProcess stub = new WarehouseOutProcess();
|
||||
stub.setWarehouseOutId(20L);
|
||||
stub.setApplyDate(date(2026, 9, 10));
|
||||
WarehouseOutProcess pendingRow = outRow(20L, null);
|
||||
when(outProcessMapper.selectList(any())).thenReturn(List.of(stub), List.of(pendingRow));
|
||||
|
||||
tools.warehouseTodoListQuery("out", true, null, null, 1, 20);
|
||||
|
||||
var captor = org.mockito.ArgumentCaptor.forClass(
|
||||
com.baomidou.mybatisplus.core.conditions.Wrapper.class);
|
||||
verify(outProcessMapper, times(2)).selectList(captor.capture());
|
||||
String assembleSql = captor.getAllValues().get(1).getSqlSegment();
|
||||
assertTrue(assembleSql.contains("schedule_state"), assembleSql);
|
||||
|
||||
tools.warehouseTodoListQuery("out", false, null, null, 1, 20);
|
||||
var captor2 = org.mockito.ArgumentCaptor.forClass(
|
||||
com.baomidou.mybatisplus.core.conditions.Wrapper.class);
|
||||
verify(outProcessMapper, times(4)).selectList(captor2.capture());
|
||||
String assembleSqlAll = captor2.getAllValues().get(3).getSqlSegment();
|
||||
assertTrue(!assembleSqlAll.contains("schedule_state"), assembleSqlAll);
|
||||
}
|
||||
|
||||
@Test
|
||||
void allocationTodoIncludesModifyOperationTypeAndSkipsOperationTypeFilter() {
|
||||
WarehouseAllocationProcess stub = new WarehouseAllocationProcess();
|
||||
stub.setWarehouseAllocationId(40L);
|
||||
stub.setApplyDate(date(2026, 9, 12));
|
||||
WarehouseAllocationProcess modifyRow = allocationRow(40L);
|
||||
modifyRow.setOperationType(3);
|
||||
when(allocationProcessMapper.selectList(any())).thenReturn(List.of(stub), List.of(modifyRow));
|
||||
|
||||
Map<String, Object> result = tools.warehouseTodoListQuery("allocation", true, null, null, 1, 20);
|
||||
|
||||
assertEquals(1, result.get("total"));
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> records = (List<Map<String, Object>>) result.get("records");
|
||||
assertEquals(1, records.size());
|
||||
assertEquals(3, records.get(0).get("operationType"));
|
||||
assertEquals("本人修改", records.get(0).get("operationTypeText"));
|
||||
|
||||
var captor = org.mockito.ArgumentCaptor.forClass(
|
||||
com.baomidou.mybatisplus.core.conditions.Wrapper.class);
|
||||
verify(allocationProcessMapper, times(2)).selectList(captor.capture());
|
||||
String stageOneSql = captor.getAllValues().get(0).getSqlSegment();
|
||||
assertTrue(!stageOneSql.contains("operation_type"), stageOneSql);
|
||||
}
|
||||
|
||||
@Test
|
||||
void listRejectsInvalidDocType() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
|
||||
Reference in New Issue
Block a user