feat(lastleg): expose inbound PDA work instructions
This commit is contained in:
+9
@@ -26,6 +26,7 @@ public class InboundPalletAssignmentController {
|
||||
@PathVariable String planNo, @PathVariable long planPalletId,
|
||||
@RequestParam(required = false) String customerId,
|
||||
@Valid @RequestBody PdaRequests.Assign request) {
|
||||
requireOperate(user);
|
||||
service.assign(scope(user, customerId), planNo, planPalletId, request);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
@@ -40,4 +41,12 @@ public class InboundPalletAssignmentController {
|
||||
}
|
||||
return String.valueOf(user.tenantId());
|
||||
}
|
||||
|
||||
private void requireOperate(AuthenticatedUser user) {
|
||||
if (user == null || user.tenantId() == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
if (user.permissions() == null || !user.permissions().contains(InboundPdaController.OPERATE_PERMISSION)) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN,
|
||||
InboundPdaController.OPERATE_PERMISSION + " permission is required");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -10,7 +10,19 @@ public final class PdaResponses {
|
||||
public record Task(long taskId, String taskNo, String planNo, String status,
|
||||
LocalDateTime pushedAt, LocalDateTime startedAt, LocalDateTime completedAt,
|
||||
List<Long> unloadPhotoFileIds, LocalDateTime unloadPhotoUploadedAt,
|
||||
LocalDateTime estimatedEmptyReturnAt) {}
|
||||
LocalDateTime estimatedEmptyReturnAt,
|
||||
List<Pallet> pallets, List<Allocation> allocations) {}
|
||||
|
||||
public record Pallet(long planPalletId, String palletNo, String destinationWarehouseId,
|
||||
String destinationWarehouseCode, String destinationWarehouseName,
|
||||
long warehousePalletId, long warehouseLocationId,
|
||||
String assignmentStatus, int actualPackageCount) {}
|
||||
|
||||
public record Allocation(long allocationId, long lineId, long planPalletId,
|
||||
long orderId, String inboundCode, String fbaWarehouseCode,
|
||||
int requiredPackageCount, int plannedPackageCount,
|
||||
int countedPackageCount, int pickedPackageCount,
|
||||
String status, int version) {}
|
||||
|
||||
public record UnloadPhotoFile(long fileId, String fileName, String contentType, long sizeBytes) {}
|
||||
|
||||
|
||||
+22
-1
@@ -629,9 +629,30 @@ public class InboundPdaWorkflowService {
|
||||
}
|
||||
|
||||
private PdaResponses.Task task(InboundPdaTask task, String planNo) {
|
||||
List<InboundPlanPallet> planPallets = repository.pallets(task.getPlanId());
|
||||
List<InboundPlanPalletAllocation> planAllocations = repository.allocations(task.getPlanId());
|
||||
Map<Long, InboundPlanLine> lines = repository.lines(task.getPlanId()).stream()
|
||||
.collect(java.util.stream.Collectors.toMap(InboundPlanLine::getId, value -> value));
|
||||
List<PdaResponses.Pallet> pallets = planPallets.stream().map(value -> new PdaResponses.Pallet(
|
||||
value.getId(), value.getPalletNo(), value.getDestinationWarehouseId(),
|
||||
value.getDestinationWarehouseCode(), value.getDestinationWarehouseName(),
|
||||
Objects.requireNonNullElse(value.getWarehousePalletId(), 0L),
|
||||
Objects.requireNonNullElse(value.getWarehouseLocationId(), 0L),
|
||||
value.getAssignmentStatus(), InboundPdaWorkflowService.value(value.getActualPackageCount()))).toList();
|
||||
List<PdaResponses.Allocation> allocations = planAllocations.stream().map(value -> {
|
||||
InboundPlanLine line = lines.get(value.getLineId());
|
||||
if (line == null) throw conflict("Inbound allocation line is missing");
|
||||
return new PdaResponses.Allocation(value.getId(), value.getLineId(), value.getPalletId(),
|
||||
line.getOrderId(), line.getInboundCode(), line.getFbaWarehouseCode(),
|
||||
InboundPdaWorkflowService.value(line.getRequiredPackageCount()),
|
||||
InboundPdaWorkflowService.value(value.getPlannedPackageCount()),
|
||||
InboundPdaWorkflowService.value(value.getCountedPackageCount()),
|
||||
InboundPdaWorkflowService.value(value.getPickedPackageCount()), value.getStatus(),
|
||||
InboundPdaWorkflowService.value(value.getVersion()));
|
||||
}).toList();
|
||||
return new PdaResponses.Task(task.getId(), task.getTaskNo(), planNo, task.getStatus(),
|
||||
task.getPushedAt(), task.getStartedAt(), task.getCompletedAt(), unloadPhotoFileIds(task),
|
||||
task.getUnloadPhotoUploadedAt(), task.getEstimatedEmptyReturnAt());
|
||||
task.getUnloadPhotoUploadedAt(), task.getEstimatedEmptyReturnAt(), pallets, allocations);
|
||||
}
|
||||
|
||||
private List<Long> unloadPhotoFileIds(InboundPdaTask task) {
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.baiyue.lastleg.inboundplan.pda.controller;
|
||||
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.lastleg.inboundplan.pda.dto.PdaRequests;
|
||||
import com.baiyue.lastleg.inboundplan.pda.service.InboundPdaWorkflowService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
class InboundPalletAssignmentControllerTest {
|
||||
private final InboundPdaWorkflowService service = mock(InboundPdaWorkflowService.class);
|
||||
private final InboundPalletAssignmentController controller = controller();
|
||||
private final PdaRequests.Assign request = new PdaRequests.Assign(1L, 2L, 3L, "assign-key");
|
||||
|
||||
@Test
|
||||
void assignmentRequiresPdaOperatePermissionAndExplicitPlatformCustomer() {
|
||||
assertThrows(ResponseStatusException.class,
|
||||
() -> controller.assign(user(Set.of()), "PLAN-1", 4L, "100", request));
|
||||
assertThrows(ResponseStatusException.class,
|
||||
() -> controller.assign(user(Set.of(InboundPdaController.OPERATE_PERMISSION)), "PLAN-1", 4L, null, request));
|
||||
|
||||
controller.assign(user(Set.of(InboundPdaController.OPERATE_PERMISSION)), "PLAN-1", 4L, "100", request);
|
||||
|
||||
verify(service).assign("100", "PLAN-1", 4L, request);
|
||||
}
|
||||
|
||||
private AuthenticatedUser user(Set<String> permissions) {
|
||||
return new AuthenticatedUser(7L, 0L, 0L, 8L, "operator", "PLATFORM", "PDA", permissions);
|
||||
}
|
||||
|
||||
private InboundPalletAssignmentController controller() {
|
||||
InboundPalletAssignmentController value = new InboundPalletAssignmentController();
|
||||
ReflectionTestUtils.setField(value, "service", service);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+16
-1
@@ -95,6 +95,21 @@ class InboundPdaWorkflowServiceTest {
|
||||
verify(repository, never()).insert(any(InboundPdaTask.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void taskReturnsPalletAndAllocationWorkInstructions() {
|
||||
when(repository.task("100", plan.getId())).thenReturn(task);
|
||||
when(repository.pallets(plan.getId())).thenReturn(List.of(targetPallet));
|
||||
when(repository.allocations(plan.getId())).thenReturn(List.of(target));
|
||||
when(repository.lines(plan.getId())).thenReturn(List.of(targetLine));
|
||||
|
||||
var result = service.task("100", "LIP1");
|
||||
|
||||
assertEquals(targetPallet.getId(), result.pallets().get(0).planPalletId());
|
||||
assertEquals(target.getId(), result.allocations().get(0).allocationId());
|
||||
assertEquals("RK-A", result.allocations().get(0).inboundCode());
|
||||
assertEquals(3, result.allocations().get(0).plannedPackageCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void assignmentReservesWarehouseResourcesAndPersistsTheLock() {
|
||||
targetPallet.setDestinationWarehouseId("88");
|
||||
@@ -427,7 +442,7 @@ class InboundPdaWorkflowServiceTest {
|
||||
|
||||
assertEquals("COMPLETED", result.status());
|
||||
verify(stockReceipts, never()).stockIn(any(), any(), any(), any(), any(), any(), any());
|
||||
verify(repository, never()).allocations(plan.getId());
|
||||
verify(repository, never()).update(task);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user