优化
This commit is contained in:
@@ -6,6 +6,8 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** 获取平台仓库主数据,供虚拟柜校验国内中转仓使用。 */
|
||||
@FeignClient(name = "warehouse-service", contextId = "firstlegWarehouseMasterDataClient",
|
||||
url = "${baiyue.clients.warehouse.url:}", path = "/internal/warehouses")
|
||||
@@ -15,10 +17,17 @@ public interface WarehouseMasterDataClient {
|
||||
@PathVariable long tenantId,
|
||||
@PathVariable long warehouseId);
|
||||
|
||||
@GetMapping("/domestic")
|
||||
ApiResponse<List<WarehouseOption>> domestic(
|
||||
@RequestHeader("X-Internal-Service-Token") String token);
|
||||
|
||||
record Detail(Warehouse warehouse) {
|
||||
}
|
||||
|
||||
record Warehouse(Long id, Long tenantId, String warehouseCode, String internalCode, String warehouseName,
|
||||
String warehouseType, String status) {
|
||||
}
|
||||
|
||||
record WarehouseOption(Long id, String warehouseCode, String warehouseName) {
|
||||
}
|
||||
}
|
||||
|
||||
+17
-1
@@ -39,9 +39,10 @@ public class PlatformDropshipPalletizationController {
|
||||
@PreAuthorize("hasAuthority('platform:firstleg:pda:view')")
|
||||
public ApiResponse<List<DropshipPalletizationDtos.PendingTask>> pending(
|
||||
@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@RequestParam @Positive long warehouseId,
|
||||
@RequestParam(required = false) @Size(max = 64) String keyword) {
|
||||
FirstlegScopeGuard.platform(user);
|
||||
return ApiResponse.success(service.pending(user.userId(), keyword));
|
||||
return ApiResponse.success(service.pending(user.userId(), warehouseId, keyword));
|
||||
}
|
||||
|
||||
@GetMapping("/tasks/{taskId}")
|
||||
@@ -121,6 +122,21 @@ public class PlatformDropshipPalletizationController {
|
||||
return ApiResponse.success(service.seal(taskId, palletId, request, user.userId(), user.username()));
|
||||
}
|
||||
|
||||
@GetMapping("/tasks/{taskId}/pallets/{palletId}/print")
|
||||
@PreAuthorize("hasAuthority('platform:firstleg:pda:view')")
|
||||
public ResponseEntity<byte[]> printTaskPallet(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@PathVariable @Positive long taskId,
|
||||
@PathVariable @Positive long palletId,
|
||||
@RequestParam @Positive long warehouseId,
|
||||
@RequestHeader(value = "X-Request-Id", required = false) String requestId) {
|
||||
FirstlegScopeGuard.platform(user);
|
||||
Pallet pallet = service.task(taskId, warehouseId, user.userId()).pallets().stream()
|
||||
.filter(item -> item.palletId() == palletId).findFirst()
|
||||
.orElseThrow(() -> new com.baiyue.common.core.exception.BizException(
|
||||
com.baiyue.common.core.api.ResultCode.NOT_FOUND, "托盘不存在"));
|
||||
return printService.generateDropshipPallet(pallet, requestId);
|
||||
}
|
||||
|
||||
@GetMapping("/inbound-orders/{inboundId}/pallets")
|
||||
@PreAuthorize("hasAuthority('firstleg:inbound:list')")
|
||||
public ApiResponse<List<Pallet>> inboundPallets(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
|
||||
+13
-1
@@ -20,6 +20,8 @@ import java.util.List;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.Task;
|
||||
import com.baiyue.firstleg.service.FirstlegScopeGuard;
|
||||
import com.baiyue.firstleg.service.LoadingPickService;
|
||||
import com.baiyue.firstleg.service.PdaWarehouseCatalogService;
|
||||
import com.baiyue.firstleg.client.WarehouseMasterDataClient.WarehouseOption;
|
||||
import com.baiyue.firstleg.loading.service.CabinetLoadingEvidenceService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -51,6 +53,8 @@ public class PlatformLoadingPdaController {
|
||||
@Resource
|
||||
private LoadingPickService service;
|
||||
@Resource
|
||||
private PdaWarehouseCatalogService warehouseCatalog;
|
||||
@Resource
|
||||
private CabinetLoadingEvidenceService evidenceService;
|
||||
|
||||
@GetMapping("/tasks")
|
||||
@@ -66,10 +70,18 @@ public class PlatformLoadingPdaController {
|
||||
@GetMapping("/tasks/pending")
|
||||
@PreAuthorize("hasAuthority('platform:firstleg:pda:view')")
|
||||
public ApiResponse<List<PendingTask>> pendingTasks(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@RequestParam @Positive long warehouseId,
|
||||
@RequestParam(required = false)
|
||||
@Size(max = 64) String keyword) {
|
||||
FirstlegScopeGuard.platform(user);
|
||||
return ApiResponse.success(service.pendingTasks(user.userId(), keyword));
|
||||
return ApiResponse.success(service.pendingTasks(user.userId(), warehouseId, keyword));
|
||||
}
|
||||
|
||||
@GetMapping("/warehouses")
|
||||
@PreAuthorize("hasAuthority('platform:firstleg:pda:view')")
|
||||
public ApiResponse<List<WarehouseOption>> warehouses(@AuthenticationPrincipal AuthenticatedUser user) {
|
||||
FirstlegScopeGuard.platform(user);
|
||||
return ApiResponse.success(warehouseCatalog.activeDomesticWarehouses());
|
||||
}
|
||||
|
||||
@GetMapping("/tasks/{taskId}")
|
||||
|
||||
@@ -30,10 +30,12 @@ public interface DropshipPalletizationMapper {
|
||||
o.warehouse_name,t.total_cartons,t.counted_cartons,t.allocated_cartons bound_cartons,
|
||||
t.counted_cartons-t.allocated_cartons unbound_cartons,t.discrepancy_cartons,t.task_status,t.created_at
|
||||
FROM fl_dropship_pallet_task t JOIN fl_inbound_order o ON o.id=t.inbound_id AND o.tenant_id=t.tenant_id
|
||||
WHERE t.task_status IN ('PENDING','CLAIMED') AND (t.assignee_id IS NULL OR t.assignee_id=#{operatorId})
|
||||
WHERE t.task_status IN ('PENDING','CLAIMED') AND t.warehouse_id=#{warehouseId}
|
||||
AND (t.assignee_id IS NULL OR t.assignee_id=#{operatorId})
|
||||
AND (#{keyword} IS NULL OR o.inbound_code LIKE CONCAT('%',#{keyword},'%') OR o.customer_name LIKE CONCAT('%',#{keyword},'%'))
|
||||
ORDER BY t.created_at,t.id""")
|
||||
List<PendingTask> pending(@Param("operatorId") long operatorId,@Param("keyword") String keyword);
|
||||
List<PendingTask> pending(@Param("operatorId") long operatorId,@Param("warehouseId") long warehouseId,
|
||||
@Param("keyword") String keyword);
|
||||
|
||||
@Select("""
|
||||
SELECT t.*,o.inbound_code,o.customer_name,o.warehouse_name,o.process_status,o.fulfillment_type
|
||||
|
||||
@@ -100,6 +100,7 @@ public interface LoadingPickTaskMapper extends BaseMapper<LoadingPickTaskEntity>
|
||||
JOIN fl_loading_pick_item pick_item ON pick_item.pick_task_id = task.id
|
||||
LEFT JOIN fl_loading_pick_assignment assignment ON assignment.pick_task_id = task.id
|
||||
WHERE task.task_status IN ('LOADING','COMPLETING','COMPLETION_FAILED') AND booking.record_status = 'ACTIVE'
|
||||
AND booking.warehouse_id = #{warehouseId}
|
||||
AND (assignment.pick_task_id IS NULL OR assignment.assignee_id = #{operatorId})
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND (booking.container_no LIKE CONCAT('%', #{keyword}, '%')
|
||||
@@ -115,6 +116,7 @@ public interface LoadingPickTaskMapper extends BaseMapper<LoadingPickTaskEntity>
|
||||
</script>
|
||||
""")
|
||||
List<PendingTaskRow> selectPendingForOperator(@Param("operatorId") long operatorId,
|
||||
@Param("warehouseId") long warehouseId,
|
||||
@Param("keyword") String keyword);
|
||||
|
||||
class PendingTaskRow {
|
||||
|
||||
@@ -90,8 +90,8 @@ public class LoadingPickRepository {
|
||||
return tasks.selectLoadingByPlan(loadingPlanId, warehouseId);
|
||||
}
|
||||
|
||||
public List<PendingTaskRow> pendingTasks(long operatorId, String keyword) {
|
||||
return tasks.selectPendingForOperator(operatorId, keyword);
|
||||
public List<PendingTaskRow> pendingTasks(long operatorId, long warehouseId, String keyword) {
|
||||
return tasks.selectPendingForOperator(operatorId, warehouseId, keyword);
|
||||
}
|
||||
|
||||
public ContainerBookingEntity activeBookingByTask(long taskId) {
|
||||
|
||||
+9
-3
@@ -36,6 +36,7 @@ public class DropshipPalletizationService {
|
||||
return view(task);
|
||||
}
|
||||
if(!"CLAIMED".equals(task.getTaskStatus())||!"WAIT_PALLETIZE".equals(task.getProcessStatus())) throw conflict("当前任务不允许创建托盘");
|
||||
if(!Objects.equals(task.getCountedCartons(),task.getTotalCartons())) throw conflict("请先完成全部箱码清点,再生成托盘码");
|
||||
int expected=mapper.unboundCount(taskId);
|
||||
if(expected==0) throw conflict("当前没有待绑托箱,请先清点箱码");
|
||||
long palletId=IdWorker.getId();
|
||||
@@ -43,6 +44,10 @@ public class DropshipPalletizationService {
|
||||
mapper.insertPallet(palletId,taskId,task.getTenantId(),task.getInboundId(),task.getWarehouseId(),palletNo,operation,operatorId,operatorName);
|
||||
int bound=mapper.bindPending(taskId,palletId,operation,operatorId,operatorName);
|
||||
if(bound!=expected||mapper.adjustBound(taskId,operatorId,bound)!=1) throw conflict("待绑托箱已变化,请刷新后重试");
|
||||
if(mapper.seal(taskId,palletId,1,request.warehouseId(),operation,operatorId,operatorName)!=1)
|
||||
throw conflict("托盘完成状态已变化,请刷新后重试");
|
||||
if(mapper.completeTask(taskId)!=1 || mapper.completeInbound(taskId,task.getTenantId(),task.getInboundId(),operatorId)!=1)
|
||||
throw conflict("一件代发入库完成状态已变化,请刷新后重试");
|
||||
return view(requireTask(taskId,request.warehouseId()));
|
||||
}
|
||||
|
||||
@@ -52,7 +57,7 @@ public class DropshipPalletizationService {
|
||||
mapper.insertTask(IdWorker.getId(),inbound.getTenantId(),inbound.getId(),inbound.getWarehouseId(),inbound.getTotalPackageCount());
|
||||
}
|
||||
|
||||
public List<PendingTask> pending(long operatorId,String keyword){ return mapper.pending(operatorId,normalize(keyword)); }
|
||||
public List<PendingTask> pending(long operatorId,long warehouseId,String keyword){ return mapper.pending(operatorId,warehouseId,normalize(keyword)); }
|
||||
|
||||
@Transactional
|
||||
public Task claim(long taskId,long warehouseId,long operatorId,String operatorName){
|
||||
@@ -99,8 +104,8 @@ public class DropshipPalletizationService {
|
||||
if(!mismatch && request.discrepancyAction()!=DiscrepancyAction.MATCH) throw invalid("实收数量一致时差异动作必须为 MATCH");
|
||||
if(mismatch && request.discrepancyAction()!=DiscrepancyAction.ACCEPT_VARIANCE) throw conflict("实收数量存在差异,请确认差异处理结果");
|
||||
if(mismatch && !StringUtils.hasText(remark)) throw invalid("接受数量差异时必须填写处理说明");
|
||||
PalletRow pallet=StringUtils.hasText(request.palletNo())
|
||||
? getOrCreatePallet(task,request.palletNo().trim(),operation,operatorId,operatorName):null;
|
||||
if(StringUtils.hasText(request.palletNo())) throw invalid("逐箱清点阶段不能提前绑定托盘码");
|
||||
PalletRow pallet=null;
|
||||
long cartonId=IdWorker.getId();
|
||||
mapper.insertCarton(cartonId,taskId,pallet==null?null:pallet.getId(),task.getTenantId(),task.getInboundId(),
|
||||
label.getInboundDetailId(),label.getCartonLabelId(),label.getSkuCode(),label.getCargoName(),label.getBoxCode(),
|
||||
@@ -113,6 +118,7 @@ public class DropshipPalletizationService {
|
||||
@Transactional
|
||||
public Task bindPending(long taskId,BindPendingRequest request,long operatorId,String operatorName){
|
||||
TaskRow task=requireWorkingTask(taskId,request.warehouseId(),operatorId);
|
||||
if(!Objects.equals(task.getCountedCartons(),task.getTotalCartons())) throw conflict("请先完成全部箱码清点,再绑定托盘码");
|
||||
String operation=operation(request.operationId());
|
||||
if(mapper.bindOperationCount(taskId,operation)>0) return view(requireTask(taskId,request.warehouseId()));
|
||||
PalletRow pallet=getOrCreatePallet(task,request.palletNo().trim(),operation,operatorId,operatorName);
|
||||
|
||||
+2
-1
@@ -115,7 +115,8 @@ public class InboundCartonLabelPrintService {
|
||||
|
||||
/** 一件代发托盘标签:托盘号、入仓码、SKU箱数和托盘二维码。 */
|
||||
public ResponseEntity<byte[]> generateDropshipPallet(Pallet pallet, String requestId) {
|
||||
if (pallet == null || !StringUtils.hasText(pallet.palletNo()) || pallet.items().isEmpty()) {
|
||||
if (pallet == null || !"SEALED".equals(pallet.status())
|
||||
|| !StringUtils.hasText(pallet.palletNo()) || pallet.items().isEmpty()) {
|
||||
throw new BizException(ResultCode.CONFLICT, "只有已完成且包含货物的一件代发托盘可以打印");
|
||||
}
|
||||
String inboundCodes = pallet.items().stream().map(item -> item.inboundCode())
|
||||
|
||||
@@ -233,9 +233,9 @@ public class LoadingPickService {
|
||||
return view(requiredTask(taskId), warehouseId);
|
||||
}
|
||||
|
||||
public List<PendingTask> pendingTasks(long operatorId, String keyword) {
|
||||
public List<PendingTask> pendingTasks(long operatorId, long warehouseId, String keyword) {
|
||||
String normalizedKeyword = StringUtils.hasText(keyword) ? keyword.trim() : null;
|
||||
return repository.pendingTasks(operatorId, normalizedKeyword).stream()
|
||||
return repository.pendingTasks(operatorId, warehouseId, normalizedKeyword).stream()
|
||||
.map(row -> new PendingTask(row.getTaskId(), row.getLoadingPlanId(), row.getContainerBookingId(),
|
||||
row.getCabinetNo(), row.getContainerNo(),
|
||||
row.getSourceType(), row.getRecordStatus(), row.getMergedIntoContainerBookingId(),
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.baiyue.firstleg.service;
|
||||
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.firstleg.client.WarehouseMasterDataClient;
|
||||
import com.baiyue.firstleg.client.WarehouseMasterDataClient.WarehouseOption;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** 为国内 PDA 提供启用中的国内仓选择项,主档仍由 Warehouse Service 唯一维护。 */
|
||||
@Service
|
||||
public class PdaWarehouseCatalogService {
|
||||
@Resource private WarehouseMasterDataClient warehouses;
|
||||
@Value("${baiyue.security.internal-service-token:}") private String internalToken;
|
||||
|
||||
public List<WarehouseOption> activeDomesticWarehouses() {
|
||||
try {
|
||||
ApiResponse<List<WarehouseOption>> response = warehouses.domestic(internalToken);
|
||||
if (response == null || !response.successful() || response.data() == null) {
|
||||
throw unavailable();
|
||||
}
|
||||
return response.data();
|
||||
} catch (BizException exception) {
|
||||
throw exception;
|
||||
} catch (RuntimeException exception) {
|
||||
throw unavailable();
|
||||
}
|
||||
}
|
||||
|
||||
private static BizException unavailable() {
|
||||
return new BizException(ResultCode.SERVICE_UNAVAILABLE, "国内仓目录暂时不可用,请稍后重试");
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,13 @@ openapi: 3.0.3
|
||||
info:
|
||||
title: 一件代发国内清点组托 API
|
||||
version: 2.0.0
|
||||
description: 测量、计费后独立下发。箱码逐箱确认 SKU 实收数量;托盘码可先扫或后扫,封托后作为不可拆分柜单元。
|
||||
description: 测量、计费后独立下发。先逐箱确认 SKU 实收数量;全部箱码清点完成后才原子生成、绑定并封存正式 DP 托盘码,随后可打印托盘标签。
|
||||
paths:
|
||||
/api/platform/v1/first-leg/pda-dropship/tasks/pending:
|
||||
get:
|
||||
summary: 查询待领取或本人已领取任务
|
||||
x-required-authority: platform:firstleg:pda:view
|
||||
parameters: [{$ref: '#/components/parameters/WarehouseId'}, {name: keyword, in: query, required: false, schema: {type: string, maxLength: 64}}]
|
||||
responses: {'200': {description: OK}}
|
||||
/api/platform/v1/first-leg/pda-dropship/tasks/{taskId}:
|
||||
get:
|
||||
@@ -33,8 +34,8 @@ paths:
|
||||
responses: {'200': {description: OK}}
|
||||
/api/platform/v1/first-leg/pda-dropship/tasks/{taskId}/cartons/count:
|
||||
post:
|
||||
summary: 逐箱确认 SKU 实收数量,可同时绑定当前托盘
|
||||
description: palletNo 为空时进入 COUNTED_UNBOUND;不为空时幂等获取或创建 OPEN 托盘并绑定。数量不一致必须 ACCEPT_VARIANCE 且填写说明。
|
||||
summary: 逐箱确认 SKU 实收数量
|
||||
description: 清点阶段只记录箱级实收并进入 COUNTED_UNBOUND,不得提前生成或绑定托盘码。数量不一致必须 ACCEPT_VARIANCE 且填写说明。
|
||||
x-required-authority: platform:firstleg:pda:work
|
||||
parameters: [{$ref: '#/components/parameters/TaskId'}]
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/CountCartonRequest'}}}}
|
||||
@@ -48,8 +49,8 @@ paths:
|
||||
responses: {'200': {description: OK}}
|
||||
/api/platform/v1/first-leg/pda-dropship/tasks/{taskId}/pallets/create-and-bind:
|
||||
post:
|
||||
summary: 系统生成 DP 托盘码并原子绑定全部待绑箱
|
||||
description: 仅当前仓库本人已领取的待组托任务可执行。没有待绑箱时不发码、不建空托。operationId 在同任务和仓库重放不重复创建;不同任务使用同幂等号拒绝。返回当前 Task。
|
||||
summary: 完成一件代发入库并生成正式 DP 托盘码
|
||||
description: 仅全部箱码已经清点的本人任务可执行;同一事务内生成 DP 托盘码、绑定全部箱、封托并把入仓单推进到待分柜。未完成清点或没有待绑箱时不发码、不建空托。operationId 重放不重复创建;返回已完成 Task。
|
||||
x-required-authority: platform:firstleg:pda:work
|
||||
parameters: [{$ref: '#/components/parameters/TaskId'}]
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/CreateAndBindRequest'}}}}
|
||||
@@ -72,6 +73,17 @@ paths:
|
||||
- {name: palletId, in: path, required: true, schema: {type: integer, format: int64}}
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/SealPalletRequest'}}}}
|
||||
responses: {'200': {description: OK}}
|
||||
/api/platform/v1/first-leg/pda-dropship/tasks/{taskId}/pallets/{palletId}/print:
|
||||
get:
|
||||
summary: PDA 打印已封托的一件代发托盘码
|
||||
description: 仅当前仓库、当前操作员可访问;未封托托盘拒绝打印。返回 100mm × 100mm PDF。
|
||||
x-required-authority: platform:firstleg:pda:view
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/TaskId'}
|
||||
- {name: palletId, in: path, required: true, schema: {type: integer, format: int64}}
|
||||
- {$ref: '#/components/parameters/WarehouseId'}
|
||||
responses:
|
||||
'200': {description: 托盘码 PDF, content: {application/pdf: {schema: {type: string, format: binary}}}}
|
||||
/api/platform/v1/first-leg/pda-dropship/inbound-orders/{inboundId}/pallets:
|
||||
get:
|
||||
summary: Admin 查询入仓单权威托盘结果
|
||||
@@ -134,7 +146,6 @@ components:
|
||||
actualSkuQuantity: {type: integer, minimum: 1}
|
||||
discrepancyAction: {type: string, enum: [MATCH, ACCEPT_VARIANCE]}
|
||||
discrepancyRemark: {type: string, maxLength: 500, nullable: true}
|
||||
palletNo: {$ref: '#/components/schemas/NullablePalletNo'}
|
||||
operationId: {type: string, maxLength: 128}
|
||||
BindPendingRequest:
|
||||
type: object
|
||||
|
||||
@@ -3,6 +3,17 @@ info:
|
||||
title: Phase 3 Container PDA Picking API
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/api/platform/v1/first-leg/pda-loading/warehouses:
|
||||
get:
|
||||
summary: List active domestic warehouses available to the PDA operator
|
||||
responses:
|
||||
'200':
|
||||
description: Warehouse master-data options used to scope PDA pending-task lists
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items: {$ref: '#/components/schemas/WarehouseOption'}
|
||||
/api/platform/v1/first-leg/pda-loading/tasks:
|
||||
get:
|
||||
summary: Find the active container picking task in one platform warehouse
|
||||
@@ -18,6 +29,7 @@ paths:
|
||||
get:
|
||||
summary: List active tasks available to the authenticated PDA operator
|
||||
parameters:
|
||||
- {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
- {name: keyword, in: query, required: false, schema: {type: string, maxLength: 64}}
|
||||
responses:
|
||||
'200':
|
||||
@@ -175,6 +187,13 @@ components:
|
||||
TaskId: {name: taskId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
ItemId: {name: itemId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
schemas:
|
||||
WarehouseOption:
|
||||
type: object
|
||||
required: [id, warehouseCode, warehouseName]
|
||||
properties:
|
||||
id: {type: integer, format: int64}
|
||||
warehouseCode: {type: string}
|
||||
warehouseName: {type: string}
|
||||
Task:
|
||||
type: object
|
||||
required: [id, loadingPlanId, containerBookingId, inboundId, inboundVersion, loadingVersion, cabinetNo, sourceType, recordStatus, taskStatus, workflowStatus, version, items]
|
||||
|
||||
Reference in New Issue
Block a user