fix: align PDA inventory with pallet location blocks
This commit is contained in:
@@ -46,13 +46,6 @@ public interface WarehousePalletLocationClient {
|
||||
ApiResponse<BlockResult> releaseBlock(@RequestHeader("X-Internal-Service-Token") String token,
|
||||
@RequestBody BlockReleaseRequest request);
|
||||
|
||||
@GetMapping("/pallet-locations/occupied-resources")
|
||||
ApiResponse<List<OccupiedResource>> occupiedResources(
|
||||
@RequestHeader("X-Internal-Service-Token") String token,
|
||||
@RequestParam("tenantId") long tenantId,
|
||||
@RequestParam("sourceService") String sourceService,
|
||||
@RequestParam("businessId") long businessId);
|
||||
|
||||
record BlockReserveRequest(long warehouseId, String sourceService, long businessId,
|
||||
String inboundCode, int requiredSlotCount, String idempotencyKey,
|
||||
long operatorId, LocalDateTime expiresAt) {}
|
||||
@@ -65,9 +58,6 @@ public interface WarehousePalletLocationClient {
|
||||
record BlockResource(long blockId, long reservationId, long locationId, String locationCode,
|
||||
int slotNo, boolean sharedOverflow, String resourceStatus) {}
|
||||
record BlockResult(long blockId, String blockStatus, List<BlockResource> resources) {}
|
||||
record OccupiedResource(long reservationId, long occupancyId, int allocationNo, long warehouseId,
|
||||
long palletId, long locationId, String locationCode, boolean sharedOverflow,
|
||||
int allocatedPackageCount) {}
|
||||
record PalletSnapshot(long palletId, String palletCode, String palletType,
|
||||
BigDecimal capacityCbm, BigDecimal tareWeightKg) {}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ public final class LoadingPickDtos {
|
||||
|
||||
public record PickItem(Long id, Long loadingPlanItemId, Long tenantId, Long inboundId, Long inboundDetailId,
|
||||
String orderNo, String inboundCode, String cargoName, List<String> boxCodes,
|
||||
List<InventoryResource> inventoryResources,
|
||||
Integer plannedPackageCount, Integer pickedPackageCount,
|
||||
String itemStatus, Integer version) {
|
||||
}
|
||||
@@ -131,9 +132,10 @@ public final class LoadingPickDtos {
|
||||
Integer itemCount, Integer pendingItemCount) {
|
||||
}
|
||||
|
||||
public record InventoryResource(Long reservationId, Long occupancyId, Long palletId,
|
||||
public record InventoryResource(Long blockId, Integer slotNo, Long reservationId,
|
||||
Long weightResultId, Integer palletSequence, Long palletId,
|
||||
Long locationId, String locationCode, Boolean sharedOverflow,
|
||||
Integer availablePackageCount) {
|
||||
String slotStatus, Integer availablePackageCount) {
|
||||
}
|
||||
|
||||
public record InboundInventory(Long inboundId, Integer availablePackageCount,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
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.WarehousePalletLocationClient;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.PickItem;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.PickRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.PartialPickRequest;
|
||||
@@ -34,6 +32,7 @@ import com.baiyue.firstleg.entity.LoadingPickTaskEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPlanItemEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPlanHistoryEntity;
|
||||
import com.baiyue.firstleg.entity.InboundStatusHistoryEntity;
|
||||
import com.baiyue.firstleg.entity.InboundPalletAllocationEntity;
|
||||
import com.baiyue.firstleg.entity.ContainerBookingEntity;
|
||||
import com.baiyue.firstleg.loading.mapper.LoadingInboundMapper;
|
||||
import com.baiyue.firstleg.loading.service.LoadingDeliveryService;
|
||||
@@ -46,12 +45,11 @@ import com.baiyue.firstleg.mapper.InboundStatusHistoryMapper;
|
||||
import com.baiyue.firstleg.mapper.LoadingPickItemMapper.CartonLabelRow;
|
||||
import com.baiyue.firstleg.mapper.LoadingPickItemMapper;
|
||||
import com.baiyue.firstleg.repository.LoadingPickRepository;
|
||||
import com.baiyue.firstleg.repository.InboundPalletAllocationRepository;
|
||||
import com.baiyue.firstleg.repository.LoadingCompletionCommandRepository;
|
||||
import com.baiyue.firstleg.repository.SmartLoadingRepository;
|
||||
import com.baiyue.firstleg.oceanbooking.repository.OceanBookingRepository;
|
||||
import jakarta.annotation.Resource;
|
||||
import feign.FeignException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
@@ -80,8 +78,6 @@ import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
@Service
|
||||
public class LoadingPickService {
|
||||
private static final Logger log = LoggerFactory.getLogger(LoadingPickService.class);
|
||||
private static final long PLATFORM_WAREHOUSE_RESOURCE_TENANT_ID = 0L;
|
||||
private static final String SOURCE_SERVICE = "firstleg-service";
|
||||
@Resource
|
||||
private LoadingPickRepository repository;
|
||||
@Resource
|
||||
@@ -107,9 +103,7 @@ public class LoadingPickService {
|
||||
@Resource
|
||||
private PdaPickSettlementService settlementService;
|
||||
@Resource
|
||||
private WarehousePalletLocationClient warehouse;
|
||||
@Value("${baiyue.security.internal-service-token:test-internal-service-token}")
|
||||
private String internalToken;
|
||||
private InboundPalletAllocationRepository palletAllocations;
|
||||
|
||||
/** Opens the field task after planning and ocean release; the real container number may be filled later. */
|
||||
@Transactional
|
||||
@@ -906,14 +900,19 @@ public class LoadingPickService {
|
||||
? repository.items(task.getId()) : repository.items(task.getId(), warehouseId);
|
||||
Map<Long, List<CartonLabelRow>> cartonsByItem = repository.cartonLabels(task.getId()).stream()
|
||||
.collect(Collectors.groupingBy(CartonLabelRow::getPickItemId));
|
||||
Map<Long, Long> tenantByInbound = tenantByInbound(task.getId());
|
||||
Map<Long, List<InventoryResource>> inventoryByInbound = inventoryByInbound(
|
||||
booking.getWarehouseId(), source, tenantByInbound);
|
||||
List<PickItem> items = source.stream().map(item -> {
|
||||
List<CartonLabelRow> cartons = cartonsByItem.getOrDefault(item.getId(), List.of());
|
||||
CartonLabelRow first = cartons.isEmpty() ? null : cartons.get(0);
|
||||
return new PickItem(item.getId(), item.getLoadingPlanItemId(), first == null ? null : first.getTenantId(),
|
||||
Long tenantId = first == null ? tenantByInbound.get(item.getInboundId()) : first.getTenantId();
|
||||
return new PickItem(item.getId(), item.getLoadingPlanItemId(), tenantId,
|
||||
item.getInboundId(),
|
||||
item.getInboundDetailId(), first == null ? null : first.getOrderNo(),
|
||||
first == null ? null : first.getInboundCode(), first == null ? null : first.getCargoName(),
|
||||
cartons.stream().map(CartonLabelRow::getBoxCode).toList(), item.getPlannedPackageCount(),
|
||||
cartons.stream().map(CartonLabelRow::getBoxCode).toList(),
|
||||
inventoryByInbound.getOrDefault(item.getInboundId(), List.of()), item.getPlannedPackageCount(),
|
||||
item.getPickedPackageCount(), item.getItemStatus(), item.getVersion());
|
||||
}).toList();
|
||||
LoadingPickAssignmentEntity assigned = repository.assignment(task.getId());
|
||||
@@ -1036,17 +1035,11 @@ public class LoadingPickService {
|
||||
|
||||
private InventorySnapshot buildInventorySnapshot(LoadingPickTaskEntity task, long warehouseId,
|
||||
List<LoadingPickItemEntity> items) {
|
||||
Map<Long, List<InventoryResource>> inventoryByInbound = inventoryByInbound(
|
||||
warehouseId, items, tenantByInbound(task.getId()));
|
||||
List<InboundInventory> inbounds = items.stream().map(LoadingPickItemEntity::getInboundId)
|
||||
.filter(Objects::nonNull).distinct().sorted().map(inboundId -> {
|
||||
List<WarehousePalletLocationClient.OccupiedResource> current = occupiedResources(inboundId);
|
||||
List<InventoryResource> resources = current.stream()
|
||||
.filter(resource -> resource.warehouseId() == warehouseId)
|
||||
.sorted(Comparator.comparingLong(WarehousePalletLocationClient.OccupiedResource::reservationId)
|
||||
.thenComparingLong(WarehousePalletLocationClient.OccupiedResource::occupancyId))
|
||||
.map(resource -> new InventoryResource(resource.reservationId(), resource.occupancyId(),
|
||||
resource.palletId(), resource.locationId(), resource.locationCode(),
|
||||
resource.sharedOverflow(), resource.allocatedPackageCount()))
|
||||
.toList();
|
||||
List<InventoryResource> resources = inventoryByInbound.getOrDefault(inboundId, List.of());
|
||||
int available = resources.stream().mapToInt(InventoryResource::availablePackageCount).sum();
|
||||
return new InboundInventory(inboundId, available, resources);
|
||||
}).toList();
|
||||
@@ -1057,20 +1050,51 @@ public class LoadingPickService {
|
||||
LocalDateTime.now(), token, inbounds);
|
||||
}
|
||||
|
||||
private List<WarehousePalletLocationClient.OccupiedResource> occupiedResources(long inboundId) {
|
||||
ApiResponse<List<WarehousePalletLocationClient.OccupiedResource>> response;
|
||||
try {
|
||||
response = warehouse.occupiedResources(internalToken, PLATFORM_WAREHOUSE_RESOURCE_TENANT_ID,
|
||||
SOURCE_SERVICE, inboundId);
|
||||
} catch (FeignException exception) {
|
||||
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "仓储实时库存查询失败,请刷新后重试");
|
||||
private Map<Long, Long> tenantByInbound(long taskId) {
|
||||
return repository.inboundOwners(taskId).stream().collect(Collectors.toMap(
|
||||
LoadingPickItemMapper.InboundOwner::inboundId,
|
||||
LoadingPickItemMapper.InboundOwner::tenantId,
|
||||
(left, right) -> left));
|
||||
}
|
||||
|
||||
private Map<Long, List<InventoryResource>> inventoryByInbound(long warehouseId,
|
||||
List<LoadingPickItemEntity> items,
|
||||
Map<Long, Long> tenantByInbound) {
|
||||
Map<Long, List<InventoryResource>> result = new HashMap<>();
|
||||
for (Long inboundId : items.stream().map(LoadingPickItemEntity::getInboundId)
|
||||
.filter(Objects::nonNull).distinct().toList()) {
|
||||
Long tenantId = tenantByInbound.get(inboundId);
|
||||
if (tenantId == null) throw conflict("PDA任务缺少入库单客户归属");
|
||||
List<InventoryResource> resources = palletAllocations.list(tenantId, inboundId).stream()
|
||||
.filter(resource -> Objects.equals(resource.getWarehouseId(), warehouseId))
|
||||
.filter(resource -> "CONFIRMED".equals(resource.getAllocationStatus()))
|
||||
.sorted(Comparator.comparing(InboundPalletAllocationEntity::getBlockId,
|
||||
Comparator.nullsLast(Long::compareTo))
|
||||
.thenComparing(InboundPalletAllocationEntity::getSlotNo,
|
||||
Comparator.nullsLast(Integer::compareTo)))
|
||||
.map(LoadingPickService::inventoryResource)
|
||||
.toList();
|
||||
result.put(inboundId, resources);
|
||||
}
|
||||
if (response == null || !response.successful() || response.data() == null) {
|
||||
ResultCode code = response == null ? ResultCode.SERVICE_UNAVAILABLE : ResultCode.of(response.code());
|
||||
throw new BizException(code, response == null || response.message() == null
|
||||
? "仓储实时库存查询失败,请刷新后重试" : response.message());
|
||||
return result;
|
||||
}
|
||||
|
||||
private static InventoryResource inventoryResource(InboundPalletAllocationEntity resource) {
|
||||
if (resource.getBlockId() == null || resource.getSlotNo() == null
|
||||
|| resource.getReservationId() == null || resource.getLocationId() == null) {
|
||||
throw conflict("入库单库位块快照不完整,请重新确认测量入库");
|
||||
}
|
||||
return response.data();
|
||||
return new InventoryResource(resource.getBlockId(), resource.getSlotNo(),
|
||||
resource.getReservationId(), resource.getWeightResultId(), resource.getPalletSequence(),
|
||||
resource.getPalletId(), resource.getLocationId(), resource.getLocationCode(),
|
||||
Boolean.TRUE.equals(resource.getSharedOverflow()), resource.getSlotStatus(), available(resource));
|
||||
}
|
||||
|
||||
private static int available(InboundPalletAllocationEntity resource) {
|
||||
int allocated = count(resource.getAllocatedPackageCount());
|
||||
int picked = count(resource.getPickedPackageCount());
|
||||
if (picked > allocated) throw conflict("整票库位槽已拣箱数超过分配箱数");
|
||||
return allocated - picked;
|
||||
}
|
||||
|
||||
private static String inventoryToken(LoadingPickTaskEntity task, long warehouseId,
|
||||
@@ -1080,8 +1104,9 @@ public class LoadingPickService {
|
||||
for (InboundInventory inbound : inbounds) {
|
||||
canonical.append('|').append(inbound.inboundId()).append('|').append(inbound.availablePackageCount());
|
||||
for (InventoryResource resource : inbound.resources()) {
|
||||
canonical.append('|').append(resource.reservationId()).append('|').append(resource.occupancyId())
|
||||
.append('|').append(resource.locationId()).append('|').append(resource.availablePackageCount());
|
||||
canonical.append('|').append(resource.blockId()).append('|').append(resource.slotNo())
|
||||
.append('|').append(resource.reservationId()).append('|').append(resource.locationId())
|
||||
.append('|').append(resource.availablePackageCount());
|
||||
}
|
||||
}
|
||||
return hash(canonical.toString());
|
||||
|
||||
@@ -201,7 +201,7 @@ components:
|
||||
items: {$ref: '#/components/schemas/PickItem'}
|
||||
PickItem:
|
||||
type: object
|
||||
required: [id, loadingPlanItemId, tenantId, inboundId, inboundDetailId, boxCodes, plannedPackageCount, pickedPackageCount, itemStatus, version]
|
||||
required: [id, loadingPlanItemId, tenantId, inboundId, inboundDetailId, boxCodes, inventoryResources, plannedPackageCount, pickedPackageCount, itemStatus, version]
|
||||
properties:
|
||||
id: {type: integer, format: int64}
|
||||
loadingPlanItemId: {type: integer, format: int64}
|
||||
@@ -215,6 +215,10 @@ components:
|
||||
type: array
|
||||
description: Stable carton label codes; one successful scan represents one carton
|
||||
items: {type: string}
|
||||
inventoryResources:
|
||||
type: array
|
||||
description: Current authoritative whole-block slot snapshots for this inbound; never projected from legacy occupancy rows
|
||||
items: {$ref: '#/components/schemas/InventoryResource'}
|
||||
plannedPackageCount: {type: integer, minimum: 1}
|
||||
pickedPackageCount: {type: integer, minimum: 0}
|
||||
itemStatus: {type: string, enum: [PENDING, PICKING, PICKED, PARTIAL, SKIPPED]}
|
||||
@@ -334,12 +338,16 @@ components:
|
||||
items: {$ref: '#/components/schemas/InventoryResource'}
|
||||
InventoryResource:
|
||||
type: object
|
||||
required: [reservationId, occupancyId, palletId, locationId, sharedOverflow, availablePackageCount]
|
||||
required: [blockId, slotNo, reservationId, locationId, sharedOverflow, slotStatus, availablePackageCount]
|
||||
properties:
|
||||
blockId: {type: integer, format: int64}
|
||||
slotNo: {type: integer, minimum: 1}
|
||||
reservationId: {type: integer, format: int64}
|
||||
occupancyId: {type: integer, format: int64}
|
||||
palletId: {type: integer, format: int64}
|
||||
weightResultId: {type: integer, format: int64, nullable: true}
|
||||
palletSequence: {type: integer, minimum: 1, nullable: true}
|
||||
palletId: {type: integer, format: int64, nullable: true}
|
||||
locationId: {type: integer, format: int64}
|
||||
locationCode: {type: string, nullable: true}
|
||||
sharedOverflow: {type: boolean}
|
||||
availablePackageCount: {type: integer, minimum: 1}
|
||||
slotStatus: {type: string, enum: [RESERVED_EMPTY, BOUND]}
|
||||
availablePackageCount: {type: integer, minimum: 0}
|
||||
|
||||
+7
@@ -52,6 +52,8 @@ class PdaConvergenceOpenApiContractTest {
|
||||
String batchItemRequest = yaml.substring(
|
||||
yaml.indexOf(" BatchItemRequest:"),
|
||||
yaml.indexOf(" ReviewRequest:"));
|
||||
String inventoryResource = yaml.substring(
|
||||
yaml.indexOf(" InventoryResource:"));
|
||||
assertThat(taskLookup)
|
||||
.contains("name: cabinetNo", "name: loadingPlanId")
|
||||
.doesNotContain("name: containerNo");
|
||||
@@ -64,5 +66,10 @@ class PdaConvergenceOpenApiContractTest {
|
||||
.doesNotContain("containerNo:");
|
||||
assertThat(batchItemRequest)
|
||||
.doesNotContain("releaseMode:", "reservationIds:", "locationIds:");
|
||||
assertThat(inventoryResource)
|
||||
.contains("blockId:", "slotNo:", "reservationId:", "weightResultId:", "slotStatus:")
|
||||
.doesNotContain("occupancyId:");
|
||||
assertThat(yaml).contains("inventoryResources:",
|
||||
"never projected from legacy occupancy rows");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ import com.baiyue.firstleg.dto.LoadingPickDtos.ClaimRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.AssignRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.CompleteLoadingRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.Task;
|
||||
import com.baiyue.firstleg.client.WarehousePalletLocationClient;
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.firstleg.entity.InboundPalletAllocationEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPickAssignmentEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPickHistoryEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPickItemEntity;
|
||||
@@ -33,6 +32,7 @@ import com.baiyue.firstleg.loading.coload.model.ColoadGroupModels.Member;
|
||||
import com.baiyue.firstleg.mapper.InboundStatusHistoryMapper;
|
||||
import com.baiyue.firstleg.mapper.LoadingPickItemMapper;
|
||||
import com.baiyue.firstleg.repository.LoadingPickRepository;
|
||||
import com.baiyue.firstleg.repository.InboundPalletAllocationRepository;
|
||||
import com.baiyue.firstleg.repository.LoadingCompletionCommandRepository;
|
||||
import com.baiyue.firstleg.repository.SmartLoadingRepository;
|
||||
import com.baiyue.firstleg.oceanbooking.repository.OceanBookingRepository;
|
||||
@@ -100,7 +100,7 @@ class LoadingPickServiceTest {
|
||||
@MockBean
|
||||
private PdaPickSettlementService settlementService;
|
||||
@MockBean
|
||||
private WarehousePalletLocationClient warehouse;
|
||||
private InboundPalletAllocationRepository palletAllocations;
|
||||
@Resource
|
||||
private LoadingPickService service;
|
||||
|
||||
@@ -122,6 +122,10 @@ class LoadingPickServiceTest {
|
||||
when(completionCommands.insert(any())).thenReturn(1);
|
||||
when(completionCommands.renew(anyLong(), anyString(), any(java.time.LocalDateTime.class))).thenReturn(true);
|
||||
when(repository.acceptCompletion(anyLong(), anyInt())).thenReturn(1);
|
||||
when(repository.inboundOwners(anyLong())).thenReturn(List.of(
|
||||
new LoadingPickItemMapper.InboundOwner(101L, 50L)));
|
||||
when(repository.inbound(anyLong(), anyLong())).thenAnswer(invocation ->
|
||||
new LoadingInboundMapper.Snapshot(invocation.getArgument(1), invocation.getArgument(0), "LOADING", 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -437,6 +441,7 @@ class LoadingPickServiceTest {
|
||||
when(repository.allTaskItemsResolved(20L)).thenReturn(false);
|
||||
when(repository.inboundOwners(20L)).thenReturn(List.of(
|
||||
new LoadingPickItemMapper.InboundOwner(101L, 50L)));
|
||||
when(palletAllocations.list(101L, 50L)).thenReturn(List.of(allocation(50L, 2)));
|
||||
when(repository.inbound(101L, 50L)).thenReturn(
|
||||
new LoadingInboundMapper.Snapshot(50L, 101L, "LOADING", 7));
|
||||
var result = service.task(20L, 101L);
|
||||
@@ -449,6 +454,14 @@ class LoadingPickServiceTest {
|
||||
assertThat(result.sourceType()).isEqualTo("SIMULATION");
|
||||
assertThat(result.recordStatus()).isEqualTo("ACTIVE");
|
||||
assertThat(result.mergedIntoContainerBookingId()).isNull();
|
||||
assertThat(result.items().get(0).inventoryResources()).singleElement().satisfies(resource -> {
|
||||
assertThat(resource.blockId()).isEqualTo(71L);
|
||||
assertThat(resource.slotNo()).isEqualTo(1);
|
||||
assertThat(resource.reservationId()).isEqualTo(72L);
|
||||
assertThat(resource.locationId()).isEqualTo(74L);
|
||||
assertThat(resource.availablePackageCount()).isEqualTo(2);
|
||||
});
|
||||
verify(palletAllocations).list(101L, 50L);
|
||||
verify(completionCommands, never()).latest(20L);
|
||||
}
|
||||
|
||||
@@ -458,7 +471,9 @@ class LoadingPickServiceTest {
|
||||
when(repository.task(20L)).thenReturn(task);
|
||||
when(repository.items(20L, 101L)).thenReturn(List.of(item()));
|
||||
when(repository.cartonLabels(20L)).thenReturn(List.of());
|
||||
when(repository.inboundOwners(20L)).thenReturn(List.of());
|
||||
when(repository.inboundOwners(20L)).thenReturn(List.of(
|
||||
new LoadingPickItemMapper.InboundOwner(101L, 50L)));
|
||||
when(palletAllocations.list(101L, 50L)).thenReturn(List.of());
|
||||
doThrow(new IllegalStateException("completion command schema unavailable"))
|
||||
.when(completionCommands).latest(20L);
|
||||
|
||||
@@ -1123,10 +1138,17 @@ class LoadingPickServiceTest {
|
||||
when(repository.allTaskItemsResolved(20L)).thenReturn(true);
|
||||
when(repository.touchTask(20L, 0)).thenReturn(1);
|
||||
when(repository.allPlanItemsPicked(10L)).thenReturn(true);
|
||||
when(warehouse.occupiedResources(any(), any(Long.class), any(), any(Long.class))).thenReturn(
|
||||
ApiResponse.success(List.of(new WarehousePalletLocationClient.OccupiedResource(
|
||||
71L, 72L, 0, 101L, 73L, 74L, "A-01", false, 2))));
|
||||
String snapshotToken = service.inventorySnapshot(20L, 101L).snapshotToken();
|
||||
when(repository.inboundOwners(20L)).thenReturn(List.of(
|
||||
new LoadingPickItemMapper.InboundOwner(1L, 50L),
|
||||
new LoadingPickItemMapper.InboundOwner(1L, 51L)));
|
||||
when(palletAllocations.list(anyLong(), anyLong())).thenAnswer(invocation ->
|
||||
List.of(allocation(invocation.getArgument(1), 2)));
|
||||
var snapshot = service.inventorySnapshot(20L, 101L);
|
||||
String snapshotToken = snapshot.snapshotToken();
|
||||
assertThat(snapshot.inbounds()).hasSize(2);
|
||||
assertThat(snapshot.inbounds().get(0).resources().get(0))
|
||||
.extracting("blockId", "slotNo", "reservationId", "locationId", "availablePackageCount")
|
||||
.containsExactly(71L, 1, 72L, 74L, 2);
|
||||
BatchRequest request = new BatchRequest(101L, 10L, 99L, 0, snapshotToken, List.of(
|
||||
new BatchItemRequest(30L, "PICK", 2, null, 0, null),
|
||||
new BatchItemRequest(31L, "SKIP", null, null, 0, "damaged carton")));
|
||||
@@ -1164,9 +1186,10 @@ class LoadingPickServiceTest {
|
||||
when(repository.touchTask(20L, 0)).thenReturn(1);
|
||||
when(smartLoadingRepository.startPicking(10L, 9L, "operator")).thenReturn(1);
|
||||
when(smartLoadingRepository.operation("PDA-START-20")).thenReturn(existingStart);
|
||||
when(warehouse.occupiedResources(any(), any(Long.class), any(), any(Long.class))).thenReturn(
|
||||
ApiResponse.success(List.of(new WarehousePalletLocationClient.OccupiedResource(
|
||||
71L, 72L, 0, 101L, 73L, 74L, "A-01", false, 100))));
|
||||
when(repository.inboundOwners(20L)).thenReturn(List.of(
|
||||
new LoadingPickItemMapper.InboundOwner(1L, 50L)));
|
||||
when(palletAllocations.list(anyLong(), anyLong())).thenAnswer(invocation ->
|
||||
List.of(allocation(invocation.getArgument(1), 100)));
|
||||
String snapshotToken = service.inventorySnapshot(20L, 101L).snapshotToken();
|
||||
BatchRequest request = new BatchRequest(101L, 10L, 99L, 0, snapshotToken, List.of(
|
||||
new BatchItemRequest(30L, "PICK", 100, null, 0, null)));
|
||||
@@ -1215,9 +1238,10 @@ class LoadingPickServiceTest {
|
||||
when(repository.assignment(20L)).thenReturn(assignment());
|
||||
when(repository.items(20L)).thenReturn(List.of(picked));
|
||||
when(repository.items(20L, 101L)).thenReturn(List.of(picked));
|
||||
when(warehouse.occupiedResources(any(), any(Long.class), any(), any(Long.class))).thenReturn(
|
||||
ApiResponse.success(List.of(new WarehousePalletLocationClient.OccupiedResource(
|
||||
71L, 72L, 0, 101L, 73L, 74L, "A-01", false, 2))));
|
||||
when(repository.inboundOwners(20L)).thenReturn(List.of(
|
||||
new LoadingPickItemMapper.InboundOwner(1L, 50L)));
|
||||
when(palletAllocations.list(anyLong(), anyLong())).thenAnswer(invocation ->
|
||||
List.of(allocation(invocation.getArgument(1), 2)));
|
||||
BatchRequest request = new BatchRequest(101L, 10L, 99L, 0, "0".repeat(64), List.of(
|
||||
new BatchItemRequest(30L, "PICK", 2, null, 0, null)));
|
||||
|
||||
@@ -1258,9 +1282,10 @@ class LoadingPickServiceTest {
|
||||
when(repository.pick(30L, 2, 0, 101L)).thenReturn(1);
|
||||
when(repository.allTaskItemsResolved(20L)).thenReturn(true);
|
||||
when(repository.touchTask(20L, 0)).thenReturn(1);
|
||||
when(warehouse.occupiedResources(any(), any(Long.class), any(), any(Long.class))).thenReturn(
|
||||
ApiResponse.success(List.of(new WarehousePalletLocationClient.OccupiedResource(
|
||||
71L, 72L, 0, 101L, 73L, 74L, "A-01", false, 2))));
|
||||
when(repository.inboundOwners(20L)).thenReturn(List.of(
|
||||
new LoadingPickItemMapper.InboundOwner(1L, 50L)));
|
||||
when(palletAllocations.list(anyLong(), anyLong())).thenAnswer(invocation ->
|
||||
List.of(allocation(invocation.getArgument(1), 2)));
|
||||
String snapshotToken = service.inventorySnapshot(20L, 101L).snapshotToken();
|
||||
BatchItemRequest line = new BatchItemRequest(30L, "PICK", 2, null, 0, null);
|
||||
|
||||
@@ -1378,6 +1403,27 @@ class LoadingPickServiceTest {
|
||||
return item;
|
||||
}
|
||||
|
||||
private static InboundPalletAllocationEntity allocation(long inboundId, int packageCount) {
|
||||
InboundPalletAllocationEntity allocation = new InboundPalletAllocationEntity();
|
||||
allocation.setId(72L);
|
||||
allocation.setTenantId(1L);
|
||||
allocation.setInboundId(inboundId);
|
||||
allocation.setBlockId(71L);
|
||||
allocation.setSlotNo(1);
|
||||
allocation.setAllocationNo(1);
|
||||
allocation.setWarehouseId(101L);
|
||||
allocation.setPalletId(73L);
|
||||
allocation.setLocationId(74L);
|
||||
allocation.setLocationCode("A-01");
|
||||
allocation.setSharedOverflow(false);
|
||||
allocation.setReservationId(72L);
|
||||
allocation.setSlotStatus("BOUND");
|
||||
allocation.setAllocationStatus("CONFIRMED");
|
||||
allocation.setAllocatedPackageCount(packageCount);
|
||||
allocation.setPickedPackageCount(0);
|
||||
return allocation;
|
||||
}
|
||||
|
||||
private static LoadingPickAssignmentEntity assignment() {
|
||||
LoadingPickAssignmentEntity assignment = new LoadingPickAssignmentEntity();
|
||||
assignment.setPickTaskId(20L);
|
||||
|
||||
@@ -124,8 +124,10 @@ function inventorySignature(snapshot) {
|
||||
inboundId: id(inbound?.inboundId),
|
||||
availablePackageCount: Number(inbound?.availablePackageCount),
|
||||
resources: (inbound?.resources || []).map((resource) => ({
|
||||
reservationId: id(resource?.reservationId), occupancyId: id(resource?.occupancyId),
|
||||
palletId: id(resource?.palletId), locationId: id(resource?.locationId),
|
||||
blockId: id(resource?.blockId), slotNo: Number(resource?.slotNo),
|
||||
reservationId: id(resource?.reservationId), weightResultId: id(resource?.weightResultId),
|
||||
palletSequence: Number(resource?.palletSequence), palletId: id(resource?.palletId),
|
||||
locationId: id(resource?.locationId), slotStatus: resource?.slotStatus || null,
|
||||
sharedOverflow: resource?.sharedOverflow === true,
|
||||
availablePackageCount: Number(resource?.availablePackageCount),
|
||||
})),
|
||||
@@ -622,8 +624,9 @@ export async function run(context) {
|
||||
&& id(inventory?.warehouseId) === warehouseId
|
||||
&& Boolean(inventory?.capturedAt) && typeof inventory?.snapshotToken === 'string'
|
||||
&& inventory.snapshotToken.length === 64 && Number(inventoryInbound?.availablePackageCount) >= 1
|
||||
&& (inventoryInbound?.resources || []).every((resource) => id(resource?.reservationId)
|
||||
&& id(resource?.occupancyId) && id(resource?.palletId) && id(resource?.locationId)
|
||||
&& (inventoryInbound?.resources || []).every((resource) => id(resource?.blockId)
|
||||
&& Number(resource?.slotNo) >= 1 && id(resource?.reservationId) && id(resource?.locationId)
|
||||
&& ['RESERVED_EMPTY', 'BOUND'].includes(resource?.slotStatus)
|
||||
&& Number(resource?.availablePackageCount) >= 0)
|
||||
&& Number(inventoryInbound?.availablePackageCount) === (inventoryInbound?.resources || [])
|
||||
.reduce((total, resource) => total + Number(resource?.availablePackageCount || 0), 0)
|
||||
|
||||
Reference in New Issue
Block a user