装柜回归测试
This commit is contained in:
@@ -8571,7 +8571,7 @@ CREATE TABLE IF NOT EXISTS fl_loading_pick_operation (
|
|||||||
UNIQUE KEY uk_loading_pick_operation_id (operation_id),
|
UNIQUE KEY uk_loading_pick_operation_id (operation_id),
|
||||||
KEY idx_loading_pick_operation_task (pick_task_id, created_at),
|
KEY idx_loading_pick_operation_task (pick_task_id, created_at),
|
||||||
CONSTRAINT fk_loading_pick_operation_task FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id),
|
CONSTRAINT fk_loading_pick_operation_task FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id),
|
||||||
CONSTRAINT chk_loading_pick_operation_action CHECK (action_type IN ('CLAIM', 'ASSIGN', 'REVIEW'))
|
CONSTRAINT chk_loading_pick_operation_action CHECK (action_type IN ('CLAIM', 'ASSIGN', 'ITEM_CLAIM', 'ITEM_ASSIGN', 'REVIEW'))
|
||||||
) ENGINE=InnoDB COMMENT='柜次PDA任务级幂等审计';
|
) ENGINE=InnoDB COMMENT='柜次PDA任务级幂等审计';
|
||||||
|
|
||||||
DROP TRIGGER IF EXISTS trg_loading_pick_review_before_update;
|
DROP TRIGGER IF EXISTS trg_loading_pick_review_before_update;
|
||||||
@@ -8708,5 +8708,46 @@ USE baiyue_firstleg;
|
|||||||
ALTER TABLE fl_loading_pick_item
|
ALTER TABLE fl_loading_pick_item
|
||||||
ADD COLUMN IF NOT EXISTS release_mode VARCHAR(10) NULL COMMENT 'AUTO按比例释放,MANUAL按人工选择释放' AFTER item_status,
|
ADD COLUMN IF NOT EXISTS release_mode VARCHAR(10) NULL COMMENT 'AUTO按比例释放,MANUAL按人工选择释放' AFTER item_status,
|
||||||
ADD COLUMN IF NOT EXISTS release_location_ids JSON NULL COMMENT '人工选择释放库位ID;空数组表示明确释放0个' AFTER release_mode;
|
ADD COLUMN IF NOT EXISTS release_location_ids JSON NULL COMMENT '人工选择释放库位ID;空数组表示明确释放0个' AFTER release_mode;
|
||||||
|
-- PDA 多人协作:领取粒度由整柜任务下沉为单条拣货明细。
|
||||||
|
CREATE TABLE IF NOT EXISTS fl_loading_pick_item_assignment (
|
||||||
|
pick_item_id BIGINT UNSIGNED NOT NULL,
|
||||||
|
assignee_id BIGINT UNSIGNED NOT NULL,
|
||||||
|
assignee_name VARCHAR(64) NOT NULL,
|
||||||
|
assigned_by BIGINT UNSIGNED NOT NULL,
|
||||||
|
assigned_by_name VARCHAR(64) NULL,
|
||||||
|
assignment_type VARCHAR(16) NOT NULL COMMENT 'CLAIMED/ASSIGNED',
|
||||||
|
version INT UNSIGNED NOT NULL DEFAULT 0,
|
||||||
|
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||||
|
PRIMARY KEY (pick_item_id),
|
||||||
|
KEY idx_loading_pick_item_assignee (assignee_id, updated_at),
|
||||||
|
CONSTRAINT fk_loading_pick_item_assignment_item FOREIGN KEY (pick_item_id) REFERENCES fl_loading_pick_item (id),
|
||||||
|
CONSTRAINT chk_loading_pick_item_assignment_type CHECK (assignment_type IN ('CLAIMED', 'ASSIGNED'))
|
||||||
|
) ENGINE=InnoDB COMMENT='PDA拣货明细领取与作业员分配';
|
||||||
|
|
||||||
|
ALTER TABLE fl_loading_pick_operation
|
||||||
|
DROP CHECK IF EXISTS chk_loading_pick_operation_action,
|
||||||
|
ADD CONSTRAINT chk_loading_pick_operation_action
|
||||||
|
CHECK (action_type IN ('CLAIM', 'ASSIGN', 'ITEM_CLAIM', 'ITEM_ASSIGN', 'REVIEW'));
|
||||||
|
|
||||||
|
DROP PROCEDURE IF EXISTS assert_phase3_pda_item_assignment_schema;
|
||||||
|
DELIMITER $$
|
||||||
|
CREATE PROCEDURE assert_phase3_pda_item_assignment_schema()
|
||||||
|
BEGIN
|
||||||
|
IF (SELECT COUNT(*) FROM information_schema.tables
|
||||||
|
WHERE table_schema='baiyue_firstleg' AND table_name='fl_loading_pick_item_assignment') <> 1
|
||||||
|
OR (SELECT COUNT(*) FROM information_schema.statistics
|
||||||
|
WHERE table_schema='baiyue_firstleg' AND table_name='fl_loading_pick_item_assignment'
|
||||||
|
AND index_name='idx_loading_pick_item_assignee') <> 2
|
||||||
|
OR (SELECT COUNT(*) FROM information_schema.referential_constraints
|
||||||
|
WHERE constraint_schema='baiyue_firstleg'
|
||||||
|
AND constraint_name='fk_loading_pick_item_assignment_item') <> 1 THEN
|
||||||
|
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='PDA明细级多人分配表创建失败';
|
||||||
|
END IF;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
||||||
|
CALL assert_phase3_pda_item_assignment_schema();
|
||||||
|
DROP PROCEDURE IF EXISTS assert_phase3_pda_item_assignment_schema;
|
||||||
|
|
||||||
ALTER TABLE fl_inbound_pallet_allocation
|
ALTER TABLE fl_inbound_pallet_allocation
|
||||||
ADD COLUMN IF NOT EXISTS reservation_id BIGINT UNSIGNED NULL COMMENT '仓库预占资源唯一ID,A0共享场景用于区分托盘' AFTER location_code;
|
ADD COLUMN IF NOT EXISTS reservation_id BIGINT UNSIGNED NULL COMMENT '仓库预占资源唯一ID,A0共享场景用于区分托盘' AFTER location_code;
|
||||||
|
|||||||
@@ -12,9 +12,13 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
|
|||||||
"com.baiyue.firstleg.mapper",
|
"com.baiyue.firstleg.mapper",
|
||||||
"com.baiyue.firstleg.split.mapper",
|
"com.baiyue.firstleg.split.mapper",
|
||||||
"com.baiyue.firstleg.loading.mapper",
|
"com.baiyue.firstleg.loading.mapper",
|
||||||
"com.baiyue.firstleg.oceanbooking.mapper"
|
"com.baiyue.firstleg.loading.bridge.mapper",
|
||||||
|
"com.baiyue.firstleg.oceanbooking.mapper",
|
||||||
|
"com.baiyue.firstleg.outbound.dispatch.mapper",
|
||||||
|
"com.baiyue.firstleg.portschedule.mapper",
|
||||||
|
"com.baiyue.firstleg.customs.mapper"
|
||||||
})
|
})
|
||||||
@EnableFeignClients(basePackages = "com.baiyue.firstleg.client")
|
@EnableFeignClients(basePackages = "com.baiyue.firstleg")
|
||||||
@SpringBootApplication(scanBasePackages = "com.baiyue")
|
@SpringBootApplication(scanBasePackages = "com.baiyue")
|
||||||
public class BaiyueFirstlegApplication {
|
public class BaiyueFirstlegApplication {
|
||||||
public static void main(String[] args) { SpringApplication.run(BaiyueFirstlegApplication.class, args); }
|
public static void main(String[] args) { SpringApplication.run(BaiyueFirstlegApplication.class, args); }
|
||||||
|
|||||||
+22
@@ -62,6 +62,28 @@ public class PlatformLoadingPdaController {
|
|||||||
return ApiResponse.success(service.assign(taskId, request, user.userId(), user.username()));
|
return ApiResponse.success(service.assign(taskId, request, user.userId(), user.username()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 同一柜按 PDA 明细领取,多个作业员可分别处理不同入库单明细。 */
|
||||||
|
@PostMapping("/tasks/{taskId}/items/{itemId}/claim")
|
||||||
|
@PreAuthorize("hasAuthority('platform:firstleg:pda:work')")
|
||||||
|
public ApiResponse<Task> claimItem(@AuthenticationPrincipal AuthenticatedUser user,
|
||||||
|
@PathVariable @Positive long taskId,
|
||||||
|
@PathVariable @Positive long itemId,
|
||||||
|
@Valid @RequestBody ClaimRequest request) {
|
||||||
|
FirstlegScopeGuard.platform(user);
|
||||||
|
return ApiResponse.success(service.claimItem(taskId, itemId, request, user.userId(), user.username()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 管理员可将单条 PDA 明细分配给指定仓库作业员。 */
|
||||||
|
@PostMapping("/tasks/{taskId}/items/{itemId}/assign")
|
||||||
|
@PreAuthorize("hasAuthority('platform:firstleg:pda:assign')")
|
||||||
|
public ApiResponse<Task> assignItem(@AuthenticationPrincipal AuthenticatedUser user,
|
||||||
|
@PathVariable @Positive long taskId,
|
||||||
|
@PathVariable @Positive long itemId,
|
||||||
|
@Valid @RequestBody AssignRequest request) {
|
||||||
|
FirstlegScopeGuard.platform(user);
|
||||||
|
return ApiResponse.success(service.assignItem(taskId, itemId, request, user.userId(), user.username()));
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/tasks/{taskId}/items/{itemId}/pick")
|
@PostMapping("/tasks/{taskId}/items/{itemId}/pick")
|
||||||
@PreAuthorize("hasAuthority('platform:firstleg:pda:work')")
|
@PreAuthorize("hasAuthority('platform:firstleg:pda:work')")
|
||||||
public ApiResponse<Task> pick(@AuthenticationPrincipal AuthenticatedUser user,
|
public ApiResponse<Task> pick(@AuthenticationPrincipal AuthenticatedUser user,
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public final class LoadingPickDtos {
|
|||||||
|
|
||||||
public record PickItem(Long id, Long loadingPlanItemId, Long inboundId, Long inboundDetailId,
|
public record PickItem(Long id, Long loadingPlanItemId, Long inboundId, Long inboundDetailId,
|
||||||
Integer plannedPackageCount, Integer pickedPackageCount,
|
Integer plannedPackageCount, Integer pickedPackageCount,
|
||||||
String itemStatus, Integer version) {
|
String itemStatus, Integer version, Assignment assignment) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Assignment(Long assigneeId, String assigneeName, Long assignedBy,
|
public record Assignment(Long assigneeId, String assigneeName, Long assignedBy,
|
||||||
|
|||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package com.baiyue.firstleg.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/** PDA 拣货明细的作业员领取或分配记录。 */
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@TableName("fl_loading_pick_item_assignment")
|
||||||
|
public class LoadingPickItemAssignmentEntity {
|
||||||
|
@TableId
|
||||||
|
private Long pickItemId;
|
||||||
|
private Long assigneeId;
|
||||||
|
private String assigneeName;
|
||||||
|
private Long assignedBy;
|
||||||
|
private String assignedByName;
|
||||||
|
private String assignmentType;
|
||||||
|
private Integer version;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package com.baiyue.firstleg.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.baiyue.firstleg.entity.LoadingPickItemAssignmentEntity;
|
||||||
|
import org.apache.ibatis.annotations.Insert;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Update;
|
||||||
|
|
||||||
|
/** 明细级作业员分配,允许同一柜的不同明细并行领取。 */
|
||||||
|
@Mapper
|
||||||
|
public interface LoadingPickItemAssignmentMapper extends BaseMapper<LoadingPickItemAssignmentEntity> {
|
||||||
|
@Insert("INSERT INTO fl_loading_pick_item_assignment "
|
||||||
|
+ "(pick_item_id,assignee_id,assignee_name,assigned_by,assigned_by_name,assignment_type,version) "
|
||||||
|
+ "VALUES (#{itemId},#{assigneeId},#{assigneeName},#{operatorId},#{operatorName},#{type},0)")
|
||||||
|
int create(@Param("itemId") long itemId, @Param("assigneeId") long assigneeId,
|
||||||
|
@Param("assigneeName") String assigneeName, @Param("operatorId") long operatorId,
|
||||||
|
@Param("operatorName") String operatorName, @Param("type") String type);
|
||||||
|
|
||||||
|
@Update("UPDATE fl_loading_pick_item_assignment SET assignee_id=#{assigneeId},assignee_name=#{assigneeName},"
|
||||||
|
+ "assigned_by=#{operatorId},assigned_by_name=#{operatorName},assignment_type=#{type},version=version+1 "
|
||||||
|
+ "WHERE pick_item_id=#{itemId} AND version=#{expectedVersion}")
|
||||||
|
int reassign(@Param("itemId") long itemId, @Param("assigneeId") long assigneeId,
|
||||||
|
@Param("assigneeName") String assigneeName, @Param("operatorId") long operatorId,
|
||||||
|
@Param("operatorName") String operatorName, @Param("type") String type,
|
||||||
|
@Param("expectedVersion") int expectedVersion);
|
||||||
|
}
|
||||||
@@ -9,9 +9,11 @@ import com.baiyue.firstleg.entity.LoadingPlanItemEntity;
|
|||||||
import com.baiyue.firstleg.mapper.LoadingPickItemMapper;
|
import com.baiyue.firstleg.mapper.LoadingPickItemMapper;
|
||||||
import com.baiyue.firstleg.mapper.LoadingPickTaskMapper;
|
import com.baiyue.firstleg.mapper.LoadingPickTaskMapper;
|
||||||
import com.baiyue.firstleg.mapper.LoadingPickAssignmentMapper;
|
import com.baiyue.firstleg.mapper.LoadingPickAssignmentMapper;
|
||||||
|
import com.baiyue.firstleg.mapper.LoadingPickItemAssignmentMapper;
|
||||||
import com.baiyue.firstleg.mapper.LoadingPickOperationMapper;
|
import com.baiyue.firstleg.mapper.LoadingPickOperationMapper;
|
||||||
import com.baiyue.firstleg.mapper.LoadingPickReviewMapper;
|
import com.baiyue.firstleg.mapper.LoadingPickReviewMapper;
|
||||||
import com.baiyue.firstleg.entity.LoadingPickAssignmentEntity;
|
import com.baiyue.firstleg.entity.LoadingPickAssignmentEntity;
|
||||||
|
import com.baiyue.firstleg.entity.LoadingPickItemAssignmentEntity;
|
||||||
import com.baiyue.firstleg.entity.LoadingPickOperationEntity;
|
import com.baiyue.firstleg.entity.LoadingPickOperationEntity;
|
||||||
import com.baiyue.firstleg.entity.LoadingPickReviewEntity;
|
import com.baiyue.firstleg.entity.LoadingPickReviewEntity;
|
||||||
import com.baiyue.firstleg.loading.mapper.InboundLoadingPickTaskMapper;
|
import com.baiyue.firstleg.loading.mapper.InboundLoadingPickTaskMapper;
|
||||||
@@ -33,6 +35,8 @@ public class LoadingPickRepository {
|
|||||||
@Resource
|
@Resource
|
||||||
private LoadingPickAssignmentMapper assignments;
|
private LoadingPickAssignmentMapper assignments;
|
||||||
@Resource
|
@Resource
|
||||||
|
private LoadingPickItemAssignmentMapper itemAssignments;
|
||||||
|
@Resource
|
||||||
private LoadingPickReviewMapper reviews;
|
private LoadingPickReviewMapper reviews;
|
||||||
@Resource
|
@Resource
|
||||||
private LoadingPickOperationMapper operations;
|
private LoadingPickOperationMapper operations;
|
||||||
@@ -165,6 +169,21 @@ public class LoadingPickRepository {
|
|||||||
type, expectedVersion);
|
type, expectedVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoadingPickItemAssignmentEntity itemAssignment(long itemId) {
|
||||||
|
return itemAssignments.selectById(itemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int createItemAssignment(long itemId, long assigneeId, String assigneeName,
|
||||||
|
long operatorId, String operatorName, String type) {
|
||||||
|
return itemAssignments.create(itemId, assigneeId, assigneeName, operatorId, operatorName, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int reassignItem(long itemId, long assigneeId, String assigneeName, long operatorId,
|
||||||
|
String operatorName, String type, int expectedVersion) {
|
||||||
|
return itemAssignments.reassign(itemId, assigneeId, assigneeName, operatorId, operatorName,
|
||||||
|
type, expectedVersion);
|
||||||
|
}
|
||||||
|
|
||||||
public LoadingPickReviewEntity review(long taskId) {
|
public LoadingPickReviewEntity review(long taskId) {
|
||||||
return reviews.selectById(taskId);
|
return reviews.selectById(taskId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import com.baiyue.firstleg.dto.LoadingPickDtos.AssignRequest;
|
|||||||
import com.baiyue.firstleg.dto.LoadingPickDtos.ReviewRequest;
|
import com.baiyue.firstleg.dto.LoadingPickDtos.ReviewRequest;
|
||||||
import com.baiyue.firstleg.dto.LoadingPickDtos.Operation;
|
import com.baiyue.firstleg.dto.LoadingPickDtos.Operation;
|
||||||
import com.baiyue.firstleg.entity.LoadingPickAssignmentEntity;
|
import com.baiyue.firstleg.entity.LoadingPickAssignmentEntity;
|
||||||
|
import com.baiyue.firstleg.entity.LoadingPickItemAssignmentEntity;
|
||||||
import com.baiyue.firstleg.entity.LoadingPickHistoryEntity;
|
import com.baiyue.firstleg.entity.LoadingPickHistoryEntity;
|
||||||
import com.baiyue.firstleg.entity.LoadingPickItemEntity;
|
import com.baiyue.firstleg.entity.LoadingPickItemEntity;
|
||||||
import com.baiyue.firstleg.entity.LoadingPickOperationEntity;
|
import com.baiyue.firstleg.entity.LoadingPickOperationEntity;
|
||||||
@@ -118,6 +119,38 @@ public class LoadingPickService {
|
|||||||
return view(task, null);
|
return view(task, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 领取单条 PDA 明细。柜次任务只是汇总容器,作业所有权必须以明细为粒度,
|
||||||
|
* 才能让多个仓库人员在同一柜中并行处理不同入库单。
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public Task claimItem(long taskId, long itemId, ClaimRequest request, long operatorId, String operatorName) {
|
||||||
|
String operationId = operation(request.operationId());
|
||||||
|
String requestHash = hash("ITEM_CLAIM|" + taskId + '|' + itemId + '|'
|
||||||
|
+ request.expectedAssignmentVersion());
|
||||||
|
Task replay = replayTaskOperation(taskId, operationId, requestHash, operatorId);
|
||||||
|
if (replay != null) return replay;
|
||||||
|
LoadingPickTaskEntity task = requiredLoadingTaskForUpdate(taskId);
|
||||||
|
LoadingPickItemEntity item = repository.item(taskId, itemId);
|
||||||
|
requireAssignableItem(item);
|
||||||
|
LoadingPickItemAssignmentEntity existing = repository.itemAssignment(itemId);
|
||||||
|
if (existing == null) {
|
||||||
|
try {
|
||||||
|
if (request.expectedAssignmentVersion() != 0 || repository.createItemAssignment(itemId, operatorId,
|
||||||
|
operatorName, operatorId, operatorName, "CLAIMED") != 1) {
|
||||||
|
throw conflict("PDA拣货明细已被其他作业员领取");
|
||||||
|
}
|
||||||
|
} catch (DuplicateKeyException exception) {
|
||||||
|
throw conflict("PDA拣货明细已被其他作业员领取");
|
||||||
|
}
|
||||||
|
} else if (!Long.valueOf(operatorId).equals(existing.getAssigneeId())
|
||||||
|
|| !request.expectedAssignmentVersion().equals(existing.getVersion())) {
|
||||||
|
throw conflict("PDA拣货明细已分配给其他作业员或分配版本已变化");
|
||||||
|
}
|
||||||
|
insertOperation(taskId, operationId, requestHash, "ITEM_CLAIM", operatorId, operatorName);
|
||||||
|
return view(task, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public Task assign(long taskId, AssignRequest request, long operatorId, String operatorName) {
|
public Task assign(long taskId, AssignRequest request, long operatorId, String operatorName) {
|
||||||
String operationId = operation(request.operationId());
|
String operationId = operation(request.operationId());
|
||||||
@@ -139,6 +172,29 @@ public class LoadingPickService {
|
|||||||
return view(task, null);
|
return view(task, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Task assignItem(long taskId, long itemId, AssignRequest request, long operatorId, String operatorName) {
|
||||||
|
String operationId = operation(request.operationId());
|
||||||
|
String assigneeName = request.assigneeName().trim();
|
||||||
|
String requestHash = hash("ITEM_ASSIGN|" + taskId + '|' + itemId + '|'
|
||||||
|
+ request.assigneeId() + '|' + assigneeName + '|' + request.expectedAssignmentVersion());
|
||||||
|
Task replay = replayTaskOperation(taskId, operationId, requestHash, operatorId);
|
||||||
|
if (replay != null) return replay;
|
||||||
|
LoadingPickTaskEntity task = requiredLoadingTaskForUpdate(taskId);
|
||||||
|
LoadingPickItemEntity item = repository.item(taskId, itemId);
|
||||||
|
requireAssignableItem(item);
|
||||||
|
LoadingPickItemAssignmentEntity existing = repository.itemAssignment(itemId);
|
||||||
|
int changed = existing == null
|
||||||
|
? request.expectedAssignmentVersion() == 0
|
||||||
|
? repository.createItemAssignment(itemId, request.assigneeId(), assigneeName, operatorId,
|
||||||
|
operatorName, "ASSIGNED") : 0
|
||||||
|
: repository.reassignItem(itemId, request.assigneeId(), assigneeName, operatorId, operatorName,
|
||||||
|
"ASSIGNED", request.expectedAssignmentVersion());
|
||||||
|
if (changed != 1) throw conflict("PDA拣货明细分配版本已变化");
|
||||||
|
insertOperation(taskId, operationId, requestHash, "ITEM_ASSIGN", operatorId, operatorName);
|
||||||
|
return view(task, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public Task pick(long taskId, long itemId, PickRequest request, long operatorId, String operatorName) {
|
public Task pick(long taskId, long itemId, PickRequest request, long operatorId, String operatorName) {
|
||||||
ReleaseSelection release = releaseSelection(request.releaseMode(), request.locationIds());
|
ReleaseSelection release = releaseSelection(request.releaseMode(), request.locationIds());
|
||||||
@@ -154,11 +210,12 @@ public class LoadingPickService {
|
|||||||
}
|
}
|
||||||
return view(requiredTask(taskId), request.warehouseId());
|
return view(requiredTask(taskId), request.warehouseId());
|
||||||
}
|
}
|
||||||
LoadingPickTaskEntity task = requiredAssignedLoadingTaskForUpdate(taskId, operatorId);
|
LoadingPickTaskEntity task = requiredLoadingTaskForUpdate(taskId);
|
||||||
LoadingPickItemEntity item = repository.item(taskId, itemId, request.warehouseId());
|
LoadingPickItemEntity item = repository.item(taskId, itemId, request.warehouseId());
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
throw new BizException(ResultCode.NOT_FOUND, "PDA拣货明细不存在");
|
throw new BizException(ResultCode.NOT_FOUND, "PDA拣货明细不存在");
|
||||||
}
|
}
|
||||||
|
requireItemAssignee(taskId, itemId, operatorId);
|
||||||
if (repository.pick(itemId, request.pickedPackageCount(), release.mode(), release.json(),
|
if (repository.pick(itemId, request.pickedPackageCount(), release.mode(), release.json(),
|
||||||
request.version(), request.warehouseId()) != 1) {
|
request.version(), request.warehouseId()) != 1) {
|
||||||
throw conflict("拣货数量超过计划箱数或明细版本已变化");
|
throw conflict("拣货数量超过计划箱数或明细版本已变化");
|
||||||
@@ -190,8 +247,9 @@ public class LoadingPickService {
|
|||||||
assertReplay(replay, taskId, itemId, requestHash);
|
assertReplay(replay, taskId, itemId, requestHash);
|
||||||
return view(requiredTask(taskId), request.warehouseId());
|
return view(requiredTask(taskId), request.warehouseId());
|
||||||
}
|
}
|
||||||
LoadingPickTaskEntity task = requiredAssignedLoadingTaskForUpdate(taskId, operatorId);
|
LoadingPickTaskEntity task = requiredLoadingTaskForUpdate(taskId);
|
||||||
requireWarehouseItem(taskId, itemId, request.warehouseId());
|
requireWarehouseItem(taskId, itemId, request.warehouseId());
|
||||||
|
requireItemAssignee(taskId, itemId, operatorId);
|
||||||
if (repository.partial(itemId, request.finalPickedPackageCount(), release.mode(), release.json(),
|
if (repository.partial(itemId, request.finalPickedPackageCount(), release.mode(), release.json(),
|
||||||
request.version(), request.warehouseId()) != 1) {
|
request.version(), request.warehouseId()) != 1) {
|
||||||
throw conflict("部分拣货箱数必须大于已拣箱数且小于计划箱数,或明细版本已变化");
|
throw conflict("部分拣货箱数必须大于已拣箱数且小于计划箱数,或明细版本已变化");
|
||||||
@@ -214,8 +272,9 @@ public class LoadingPickService {
|
|||||||
assertReplay(replay, taskId, itemId, requestHash);
|
assertReplay(replay, taskId, itemId, requestHash);
|
||||||
return view(requiredTask(taskId), request.warehouseId());
|
return view(requiredTask(taskId), request.warehouseId());
|
||||||
}
|
}
|
||||||
LoadingPickTaskEntity task = requiredAssignedLoadingTaskForUpdate(taskId, operatorId);
|
LoadingPickTaskEntity task = requiredLoadingTaskForUpdate(taskId);
|
||||||
LoadingPickItemEntity item = requireWarehouseItem(taskId, itemId, request.warehouseId());
|
LoadingPickItemEntity item = requireWarehouseItem(taskId, itemId, request.warehouseId());
|
||||||
|
requireItemAssignee(taskId, itemId, operatorId);
|
||||||
if (repository.hasPickedOrPartialItem(taskId, item.getInboundId())) {
|
if (repository.hasPickedOrPartialItem(taskId, item.getInboundId())) {
|
||||||
throw conflict("同一入库单已存在已拣或部分拣货明细,不能跳拣并恢复智能装柜候选");
|
throw conflict("同一入库单已存在已拣或部分拣货明细,不能跳拣并恢复智能装柜候选");
|
||||||
}
|
}
|
||||||
@@ -343,14 +402,27 @@ public class LoadingPickService {
|
|||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoadingPickTaskEntity requiredAssignedLoadingTaskForUpdate(long taskId, long operatorId) {
|
private void requireItemAssignee(long taskId, long itemId, long operatorId) {
|
||||||
LoadingPickTaskEntity task = requiredLoadingTaskForUpdate(taskId);
|
LoadingPickItemAssignmentEntity assignment = repository.itemAssignment(itemId);
|
||||||
LoadingPickAssignmentEntity assignment = repository.assignment(taskId);
|
if (assignment != null) {
|
||||||
if (assignment == null) throw conflict("请先领取PDA拣货任务");
|
if (!Long.valueOf(operatorId).equals(assignment.getAssigneeId())) {
|
||||||
if (!Long.valueOf(operatorId).equals(assignment.getAssigneeId())) {
|
throw conflict("PDA拣货明细已分配给其他作业员");
|
||||||
throw conflict("PDA拣货任务已分配给其他作业员");
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 兼容已上线的整柜领取调用;新作业必须走明细领取接口。
|
||||||
|
LoadingPickAssignmentEntity legacy = repository.assignment(taskId);
|
||||||
|
if (legacy == null || !Long.valueOf(operatorId).equals(legacy.getAssigneeId())) {
|
||||||
|
throw conflict("请先领取PDA拣货明细");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void requireAssignableItem(LoadingPickItemEntity item) {
|
||||||
|
if (item == null) throw new BizException(ResultCode.NOT_FOUND, "PDA拣货明细不存在");
|
||||||
|
if ("PICKED".equals(item.getItemStatus()) || "PARTIAL".equals(item.getItemStatus())
|
||||||
|
|| "SKIPPED".equals(item.getItemStatus())) {
|
||||||
|
throw conflict("已完成或已跳拣的PDA明细不能再领取或改派");
|
||||||
}
|
}
|
||||||
return task;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoadingPickItemEntity requireWarehouseItem(long taskId, long itemId, long warehouseId) {
|
private LoadingPickItemEntity requireWarehouseItem(long taskId, long itemId, long warehouseId) {
|
||||||
@@ -364,10 +436,15 @@ public class LoadingPickService {
|
|||||||
private Task view(LoadingPickTaskEntity task, Long warehouseId) {
|
private Task view(LoadingPickTaskEntity task, Long warehouseId) {
|
||||||
List<LoadingPickItemEntity> source = warehouseId == null
|
List<LoadingPickItemEntity> source = warehouseId == null
|
||||||
? repository.items(task.getId()) : repository.items(task.getId(), warehouseId);
|
? repository.items(task.getId()) : repository.items(task.getId(), warehouseId);
|
||||||
List<PickItem> items = source.stream()
|
List<PickItem> items = source.stream().map(item -> {
|
||||||
.map(item -> new PickItem(item.getId(), item.getLoadingPlanItemId(), item.getInboundId(),
|
LoadingPickItemAssignmentEntity assignedItem = repository.itemAssignment(item.getId());
|
||||||
|
Assignment itemAssignment = assignedItem == null ? null : new Assignment(assignedItem.getAssigneeId(),
|
||||||
|
assignedItem.getAssigneeName(), assignedItem.getAssignedBy(), assignedItem.getAssignedByName(),
|
||||||
|
assignedItem.getAssignmentType(), assignedItem.getVersion());
|
||||||
|
return new PickItem(item.getId(), item.getLoadingPlanItemId(), item.getInboundId(),
|
||||||
item.getInboundDetailId(), item.getPlannedPackageCount(), item.getPickedPackageCount(),
|
item.getInboundDetailId(), item.getPlannedPackageCount(), item.getPickedPackageCount(),
|
||||||
item.getItemStatus(), item.getVersion())).toList();
|
item.getItemStatus(), item.getVersion(), itemAssignment);
|
||||||
|
}).toList();
|
||||||
LoadingPickAssignmentEntity assigned = repository.assignment(task.getId());
|
LoadingPickAssignmentEntity assigned = repository.assignment(task.getId());
|
||||||
LoadingPickReviewEntity reviewed = repository.review(task.getId());
|
LoadingPickReviewEntity reviewed = repository.review(task.getId());
|
||||||
Assignment assignment = assigned == null ? null : new Assignment(assigned.getAssigneeId(),
|
Assignment assignment = assigned == null ? null : new Assignment(assigned.getAssigneeId(),
|
||||||
@@ -375,9 +452,11 @@ public class LoadingPickService {
|
|||||||
assigned.getAssignmentType(), assigned.getVersion());
|
assigned.getAssignmentType(), assigned.getVersion());
|
||||||
Review review = reviewed == null ? null : new Review(reviewed.getReviewStatus(),
|
Review review = reviewed == null ? null : new Review(reviewed.getReviewStatus(),
|
||||||
reviewed.getReviewedBy(), reviewed.getReviewedByName(), reviewed.getRemark(), reviewed.getVersion());
|
reviewed.getReviewedBy(), reviewed.getReviewedByName(), reviewed.getRemark(), reviewed.getVersion());
|
||||||
|
boolean hasPickedItem = source.stream().anyMatch(item -> item.getPickedPackageCount() != null
|
||||||
|
&& item.getPickedPackageCount() > 0);
|
||||||
String workflowStatus = "COMPLETED".equals(task.getTaskStatus()) ? "COMPLETED"
|
String workflowStatus = "COMPLETED".equals(task.getTaskStatus()) ? "COMPLETED"
|
||||||
: assigned == null ? "UNASSIGNED"
|
: repository.allTaskItemsResolved(task.getId()) ? "AWAITING_REVIEW"
|
||||||
: repository.allTaskItemsResolved(task.getId()) ? "AWAITING_REVIEW" : "IN_PROGRESS";
|
: hasPickedItem ? "IN_PROGRESS" : "PENDING";
|
||||||
return new Task(task.getId(), task.getLoadingPlanId(), task.getContainerNo(), task.getTaskStatus(),
|
return new Task(task.getId(), task.getLoadingPlanId(), task.getContainerNo(), task.getTaskStatus(),
|
||||||
workflowStatus, task.getVersion(), assignment, review, items);
|
workflowStatus, task.getVersion(), assignment, review, items);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,9 +45,25 @@ CREATE TABLE IF NOT EXISTS fl_loading_pick_operation (
|
|||||||
UNIQUE KEY uk_loading_pick_operation_id (operation_id),
|
UNIQUE KEY uk_loading_pick_operation_id (operation_id),
|
||||||
KEY idx_loading_pick_operation_task (pick_task_id, created_at),
|
KEY idx_loading_pick_operation_task (pick_task_id, created_at),
|
||||||
CONSTRAINT fk_loading_pick_operation_task FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id),
|
CONSTRAINT fk_loading_pick_operation_task FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id),
|
||||||
CONSTRAINT chk_loading_pick_operation_action CHECK (action_type IN ('CLAIM', 'ASSIGN', 'REVIEW'))
|
CONSTRAINT chk_loading_pick_operation_action CHECK (action_type IN ('CLAIM', 'ASSIGN', 'ITEM_CLAIM', 'ITEM_ASSIGN', 'REVIEW'))
|
||||||
) ENGINE=InnoDB COMMENT='柜次PDA任务级幂等审计';
|
) ENGINE=InnoDB COMMENT='柜次PDA任务级幂等审计';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS fl_loading_pick_item_assignment (
|
||||||
|
pick_item_id BIGINT UNSIGNED NOT NULL,
|
||||||
|
assignee_id BIGINT UNSIGNED NOT NULL,
|
||||||
|
assignee_name VARCHAR(64) NOT NULL,
|
||||||
|
assigned_by BIGINT UNSIGNED NOT NULL,
|
||||||
|
assigned_by_name VARCHAR(64) NULL,
|
||||||
|
assignment_type VARCHAR(16) NOT NULL COMMENT 'CLAIMED/ASSIGNED',
|
||||||
|
version INT UNSIGNED NOT NULL DEFAULT 0,
|
||||||
|
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||||
|
PRIMARY KEY (pick_item_id),
|
||||||
|
KEY idx_loading_pick_item_assignee (assignee_id, updated_at),
|
||||||
|
CONSTRAINT fk_loading_pick_item_assignment_item FOREIGN KEY (pick_item_id) REFERENCES fl_loading_pick_item (id),
|
||||||
|
CONSTRAINT chk_loading_pick_item_assignment_type CHECK (assignment_type IN ('CLAIMED', 'ASSIGNED'))
|
||||||
|
) ENGINE=InnoDB COMMENT='PDA拣货明细领取与作业员分配';
|
||||||
|
|
||||||
DROP TRIGGER IF EXISTS trg_loading_pick_review_before_update;
|
DROP TRIGGER IF EXISTS trg_loading_pick_review_before_update;
|
||||||
DELIMITER $$
|
DELIMITER $$
|
||||||
CREATE TRIGGER trg_loading_pick_review_before_update
|
CREATE TRIGGER trg_loading_pick_review_before_update
|
||||||
|
|||||||
@@ -29,6 +29,24 @@ paths:
|
|||||||
content: {application/json: {schema: {$ref: '#/components/schemas/AssignRequest'}}}
|
content: {application/json: {schema: {$ref: '#/components/schemas/AssignRequest'}}}
|
||||||
responses:
|
responses:
|
||||||
'200': {description: Assigned task or A0409 on assignment version conflict}
|
'200': {description: Assigned task or A0409 on assignment version conflict}
|
||||||
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/items/{itemId}/claim:
|
||||||
|
post:
|
||||||
|
summary: 领取单条PDA拣货明细;同柜不同明细可由不同仓库人员并行作业
|
||||||
|
parameters: [{$ref: '#/components/parameters/TaskId'}, {$ref: '#/components/parameters/ItemId'}]
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content: {application/json: {schema: {$ref: '#/components/schemas/ClaimRequest'}}}
|
||||||
|
responses:
|
||||||
|
'200': {description: 返回含明细作业员信息的PDA任务}
|
||||||
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/items/{itemId}/assign:
|
||||||
|
post:
|
||||||
|
summary: 将单条PDA拣货明细分配或改派给指定仓库人员
|
||||||
|
parameters: [{$ref: '#/components/parameters/TaskId'}, {$ref: '#/components/parameters/ItemId'}]
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content: {application/json: {schema: {$ref: '#/components/schemas/AssignRequest'}}}
|
||||||
|
responses:
|
||||||
|
'200': {description: 返回含明细作业员信息的PDA任务}
|
||||||
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/items/{itemId}/pick:
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/items/{itemId}/pick:
|
||||||
post:
|
post:
|
||||||
summary: Increment a picked item owned by the authenticated assignee
|
summary: Increment a picked item owned by the authenticated assignee
|
||||||
|
|||||||
+14
-2
@@ -2,18 +2,30 @@ package com.baiyue.firstleg;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class BaiyueFirstlegApplicationContractTest {
|
class BaiyueFirstlegApplicationContractTest {
|
||||||
@Test
|
@Test
|
||||||
void scansAllFirstlegMapperPackagesIncludingOceanBooking() {
|
void scansAllFirstlegMapperPackagesIncludingCustoms() {
|
||||||
MapperScan scan = BaiyueFirstlegApplication.class.getAnnotation(MapperScan.class);
|
MapperScan scan = BaiyueFirstlegApplication.class.getAnnotation(MapperScan.class);
|
||||||
|
|
||||||
assertThat(scan.basePackages())
|
assertThat(scan.basePackages())
|
||||||
.contains("com.baiyue.firstleg.mapper")
|
.contains("com.baiyue.firstleg.mapper")
|
||||||
.contains("com.baiyue.firstleg.split.mapper")
|
.contains("com.baiyue.firstleg.split.mapper")
|
||||||
.contains("com.baiyue.firstleg.loading.mapper")
|
.contains("com.baiyue.firstleg.loading.mapper")
|
||||||
.contains("com.baiyue.firstleg.oceanbooking.mapper");
|
.contains("com.baiyue.firstleg.loading.bridge.mapper")
|
||||||
|
.contains("com.baiyue.firstleg.oceanbooking.mapper")
|
||||||
|
.contains("com.baiyue.firstleg.outbound.dispatch.mapper")
|
||||||
|
.contains("com.baiyue.firstleg.portschedule.mapper")
|
||||||
|
.contains("com.baiyue.firstleg.customs.mapper");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void scansAllFirstlegFeignClientPackagesIncludingCustoms() {
|
||||||
|
EnableFeignClients clients = BaiyueFirstlegApplication.class.getAnnotation(EnableFeignClients.class);
|
||||||
|
|
||||||
|
assertThat(clients.basePackages()).containsExactly("com.baiyue.firstleg");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -16,6 +16,8 @@ class PdaConvergenceOpenApiContractTest {
|
|||||||
.contains("/api/platform/v1/first-leg/pda-loading/tasks:")
|
.contains("/api/platform/v1/first-leg/pda-loading/tasks:")
|
||||||
.contains("/tasks/{taskId}/claim:")
|
.contains("/tasks/{taskId}/claim:")
|
||||||
.contains("/tasks/{taskId}/assign:")
|
.contains("/tasks/{taskId}/assign:")
|
||||||
|
.contains("/tasks/{taskId}/items/{itemId}/claim:")
|
||||||
|
.contains("/tasks/{taskId}/items/{itemId}/assign:")
|
||||||
.contains("/tasks/{taskId}/items/{itemId}/pick:")
|
.contains("/tasks/{taskId}/items/{itemId}/pick:")
|
||||||
.contains("/tasks/{taskId}/items/{itemId}/partial:")
|
.contains("/tasks/{taskId}/items/{itemId}/partial:")
|
||||||
.contains("/tasks/{taskId}/items/{itemId}/skip:")
|
.contains("/tasks/{taskId}/items/{itemId}/skip:")
|
||||||
|
|||||||
+2
@@ -18,8 +18,10 @@ class PdaConvergenceSqlContractTest {
|
|||||||
.getContentAsString(StandardCharsets.UTF_8);
|
.getContentAsString(StandardCharsets.UTF_8);
|
||||||
assertThat(sql)
|
assertThat(sql)
|
||||||
.contains("CREATE TABLE IF NOT EXISTS fl_loading_pick_assignment")
|
.contains("CREATE TABLE IF NOT EXISTS fl_loading_pick_assignment")
|
||||||
|
.contains("CREATE TABLE IF NOT EXISTS fl_loading_pick_item_assignment")
|
||||||
.contains("CREATE TABLE IF NOT EXISTS fl_loading_pick_review")
|
.contains("CREATE TABLE IF NOT EXISTS fl_loading_pick_review")
|
||||||
.contains("CREATE TABLE IF NOT EXISTS fl_loading_pick_operation")
|
.contains("CREATE TABLE IF NOT EXISTS fl_loading_pick_operation")
|
||||||
|
.contains("'ITEM_CLAIM', 'ITEM_ASSIGN'")
|
||||||
.contains("UNIQUE KEY uk_loading_pick_operation_id")
|
.contains("UNIQUE KEY uk_loading_pick_operation_id")
|
||||||
.contains("trg_loading_pick_review_before_update")
|
.contains("trg_loading_pick_review_before_update")
|
||||||
.contains("trg_loading_pick_operation_before_delete")
|
.contains("trg_loading_pick_operation_before_delete")
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ package com.baiyue.firstleg.service;
|
|||||||
import com.baiyue.common.core.exception.BizException;
|
import com.baiyue.common.core.exception.BizException;
|
||||||
import com.baiyue.firstleg.dto.LoadingPickDtos.PartialPickRequest;
|
import com.baiyue.firstleg.dto.LoadingPickDtos.PartialPickRequest;
|
||||||
import com.baiyue.firstleg.dto.LoadingPickDtos.PickRequest;
|
import com.baiyue.firstleg.dto.LoadingPickDtos.PickRequest;
|
||||||
|
import com.baiyue.firstleg.dto.LoadingPickDtos.ClaimRequest;
|
||||||
import com.baiyue.firstleg.dto.LoadingPickDtos.ReviewRequest;
|
import com.baiyue.firstleg.dto.LoadingPickDtos.ReviewRequest;
|
||||||
import com.baiyue.firstleg.dto.LoadingPickDtos.SkipRequest;
|
import com.baiyue.firstleg.dto.LoadingPickDtos.SkipRequest;
|
||||||
import com.baiyue.firstleg.entity.LoadingPickAssignmentEntity;
|
import com.baiyue.firstleg.entity.LoadingPickAssignmentEntity;
|
||||||
import com.baiyue.firstleg.entity.LoadingPickHistoryEntity;
|
import com.baiyue.firstleg.entity.LoadingPickHistoryEntity;
|
||||||
import com.baiyue.firstleg.entity.LoadingPickItemEntity;
|
import com.baiyue.firstleg.entity.LoadingPickItemEntity;
|
||||||
|
import com.baiyue.firstleg.entity.LoadingPickItemAssignmentEntity;
|
||||||
import com.baiyue.firstleg.entity.LoadingPickOperationEntity;
|
import com.baiyue.firstleg.entity.LoadingPickOperationEntity;
|
||||||
import com.baiyue.firstleg.entity.LoadingPickTaskEntity;
|
import com.baiyue.firstleg.entity.LoadingPickTaskEntity;
|
||||||
import com.baiyue.firstleg.repository.LoadingPickRepository;
|
import com.baiyue.firstleg.repository.LoadingPickRepository;
|
||||||
@@ -110,6 +112,47 @@ class LoadingPickServiceTest {
|
|||||||
verify(settlementService, never()).settle(20L, 9L, "operator");
|
verify(settlementService, never()).settle(20L, 9L, "operator");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void permitsItemAssigneeWithoutCabinetAssignee() {
|
||||||
|
LoadingPickTaskEntity task = task();
|
||||||
|
LoadingPickItemEntity item = item();
|
||||||
|
when(repository.history("item-owner-pick")).thenReturn(null);
|
||||||
|
when(repository.task(20L)).thenReturn(task);
|
||||||
|
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||||
|
when(repository.item(20L, 30L, 101L)).thenReturn(item);
|
||||||
|
when(repository.itemAssignment(30L)).thenReturn(itemAssignment(9L, "operator"));
|
||||||
|
when(repository.pick(30L, 2, "MANUAL", "[]", 0, 101L)).thenReturn(1);
|
||||||
|
when(repository.touchTask(20L, 0)).thenReturn(1);
|
||||||
|
when(repository.items(20L, 101L)).thenReturn(List.of(item));
|
||||||
|
|
||||||
|
service.pick(20L, 30L, new PickRequest(101L, 2, List.of(), 0, "item-owner-pick"),
|
||||||
|
9L, "operator");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void permitsTwoWorkersToClaimDifferentItemsInOneCabinet() {
|
||||||
|
LoadingPickTaskEntity task = task();
|
||||||
|
LoadingPickItemEntity first = item();
|
||||||
|
LoadingPickItemEntity second = item();
|
||||||
|
second.setId(31L);
|
||||||
|
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||||
|
when(repository.task(20L)).thenReturn(task);
|
||||||
|
when(repository.item(20L, 30L)).thenReturn(first);
|
||||||
|
when(repository.item(20L, 31L)).thenReturn(second);
|
||||||
|
when(repository.itemAssignment(30L)).thenReturn(null);
|
||||||
|
when(repository.itemAssignment(31L)).thenReturn(null);
|
||||||
|
when(repository.createItemAssignment(30L, 9L, "worker-a", 9L, "worker-a", "CLAIMED")).thenReturn(1);
|
||||||
|
when(repository.createItemAssignment(31L, 10L, "worker-b", 10L, "worker-b", "CLAIMED")).thenReturn(1);
|
||||||
|
when(repository.items(20L)).thenReturn(List.of(first, second));
|
||||||
|
|
||||||
|
service.claimItem(20L, 30L, new ClaimRequest(0, "claim-a"), 9L, "worker-a");
|
||||||
|
service.claimItem(20L, 31L, new ClaimRequest(0, "claim-b"), 10L, "worker-b");
|
||||||
|
|
||||||
|
verify(repository).createItemAssignment(30L, 9L, "worker-a", 9L, "worker-a", "CLAIMED");
|
||||||
|
verify(repository).createItemAssignment(31L, 10L, "worker-b", 10L, "worker-b", "CLAIMED");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void reviewSettlesResolvedItemsAndCompletesPlan() {
|
void reviewSettlesResolvedItemsAndCompletesPlan() {
|
||||||
LoadingPickTaskEntity task = task();
|
LoadingPickTaskEntity task = task();
|
||||||
@@ -187,4 +230,13 @@ class LoadingPickServiceTest {
|
|||||||
assignment.setVersion(0);
|
assignment.setVersion(0);
|
||||||
return assignment;
|
return assignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static LoadingPickItemAssignmentEntity itemAssignment(long assigneeId, String assigneeName) {
|
||||||
|
LoadingPickItemAssignmentEntity assignment = new LoadingPickItemAssignmentEntity();
|
||||||
|
assignment.setPickItemId(30L);
|
||||||
|
assignment.setAssigneeId(assigneeId);
|
||||||
|
assignment.setAssigneeName(assigneeName);
|
||||||
|
assignment.setVersion(0);
|
||||||
|
return assignment;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user