Merge branch 'dev' into dev-lyhfive
# Conflicts: # deploy/sql/头程数据库统一增量-MySQL8.sql # scripts/admin-test-env-modules/phase3-container-loading.mjs # scripts/admin-test-env-modules/phase3-loading.mjs
This commit is contained in:
+2
-1
@@ -10,6 +10,7 @@ import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class InboundPlanPermissionContractTest {
|
||||
private static final String LIST = "platform:overseas-warehouse:inbound-order:list";
|
||||
@@ -37,7 +38,7 @@ class InboundPlanPermissionContractTest {
|
||||
.orElseThrow();
|
||||
|
||||
assertEquals("hasAuthority('" + CREATE + "')", method.getAnnotation(PreAuthorize.class).value());
|
||||
assertEquals("Required authority: " + CREATE, method.getAnnotation(Operation.class).description());
|
||||
assertTrue(method.getAnnotation(Operation.class).description().contains("Required authority: " + CREATE));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+6
-1
@@ -124,8 +124,9 @@ class ReceivingContainerContractTest {
|
||||
String yaml = resource("openapi/overseas-business-v1.yaml");
|
||||
String prefix = "/api/pda/v1/last-leg/receiving-containers";
|
||||
assertTrue(yaml.contains(prefix + "/candidates:"));
|
||||
assertTrue(yaml.contains(prefix + "/warehouse-profile:"));
|
||||
assertTrue(yaml.contains(prefix + "/current/receipts/bulk:"));
|
||||
assertTrue(yaml.contains(prefix + "/current/skus/resolve:"));
|
||||
assertFalse(yaml.contains(prefix + "/current/skus/resolve:"));
|
||||
assertTrue(yaml.contains(prefix + "/current/receipts/carton:"));
|
||||
assertTrue(yaml.contains(prefix + "/current/complete:"));
|
||||
assertTrue(yaml.contains(prefix + "/current/unload-photos/files/{fileId}/download:"));
|
||||
@@ -145,6 +146,10 @@ class ReceivingContainerContractTest {
|
||||
assertTrue(yaml.contains("overseasWarehouseId: { type: string"));
|
||||
assertTrue(yaml.contains("overseasWarehouseName: { type: string"));
|
||||
assertFalse(receivingPaths.contains("photoFileIds"));
|
||||
assertFalse(receivingPaths.contains("skuCode"));
|
||||
assertFalse(yaml.contains("ReceivingSkuResolved"));
|
||||
assertFalse(Arrays.stream(ReceivingContainerRequests.class.getDeclaredClasses())
|
||||
.anyMatch(type -> "SkuResolve".equals(type.getSimpleName())));
|
||||
}
|
||||
|
||||
private String resource(String name) throws IOException {
|
||||
|
||||
+25
@@ -1,6 +1,7 @@
|
||||
package com.baiyue.lastleg.inboundplan.pda.controller;
|
||||
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.lastleg.inboundplan.pda.dto.ReceivingContainerRequests;
|
||||
import com.baiyue.lastleg.inboundplan.pda.dto.ReceivingContainerResponses;
|
||||
import com.baiyue.lastleg.inboundplan.pda.gateway.UnloadPhotoFileGateway;
|
||||
import com.baiyue.lastleg.inboundplan.pda.service.ReceivingContainerService;
|
||||
@@ -45,6 +46,17 @@ class ReceivingContainerControllerTest {
|
||||
verify(service).candidates("42", 900L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void warehouseProfileUsesOnlyTheTokenBoundWarehouse() {
|
||||
when(service.warehouseProfile(900L)).thenReturn(
|
||||
new ReceivingContainerResponses.WarehouseProfile("900", "US-LAX-01", "洛杉矶一号仓"));
|
||||
|
||||
var response = controller.warehouseProfile(picker());
|
||||
|
||||
assertThat(response.data().warehouseName()).isEqualTo("洛杉矶一号仓");
|
||||
verify(service).warehouseProfile(900L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void merchantIdentityCannotEnterPdaReceivingApiEvenWithForgedAuthority() {
|
||||
AuthenticatedUser merchant = new AuthenticatedUser(7L, 100L, 100L, 8L, "merchant", "MERCHANT", "WEB",
|
||||
@@ -85,6 +97,19 @@ class ReceivingContainerControllerTest {
|
||||
.hasMessageContaining("403");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fbaCartonResolutionDelegatesWithoutControllerModeRejection() {
|
||||
ReceivingContainerRequests.CartonResolve request =
|
||||
new ReceivingContainerRequests.CartonResolve("NYEXX2608260001#4-1");
|
||||
when(service.resolveCarton("42", request)).thenReturn(new ReceivingContainerResponses.CartonResolved(
|
||||
"NYEXX2608260001#4-1", "NYEXX2608260001", "FBA", "900", "Shared LAX", null, null, null,
|
||||
"ABE3", 1, 4, 0, 4));
|
||||
|
||||
controller.resolveCarton(picker(), request);
|
||||
|
||||
verify(service).resolveCarton("42", request);
|
||||
}
|
||||
|
||||
private static AuthenticatedUser picker() {
|
||||
return new AuthenticatedUser(42L, 0L, 0L, 8L, "receiver", "PLATFORM", "PDA",
|
||||
Set.of(PdaWarehouseIdentity.PICKER_ROLE, ReceivingContainerController.OPERATE_PERMISSION),
|
||||
|
||||
+111
-6
@@ -182,7 +182,7 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
void destinationModesAreHardRejectedBeforeWarehouseCalls() {
|
||||
stubCurrent("OVERSEAS", "CONT-A");
|
||||
assertThatThrownBy(() -> service.receiveBulk("7", "receiver",
|
||||
new ReceivingContainerRequests.BulkReceive("IN-1", "SKU-1", "PAL-1", 1, "bulk-key")))
|
||||
new ReceivingContainerRequests.BulkReceive("IN-1", "PAL-1", 1, "bulk-key")))
|
||||
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("409");
|
||||
verifyNoInteractions(warehouse);
|
||||
|
||||
@@ -209,7 +209,7 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
stubSku();
|
||||
when(warehouse.resolvePallet("PAL-1")).thenReturn(resource(101L, "FBA-1"));
|
||||
assertThatThrownBy(() -> service.receiveBulk("7", "receiver",
|
||||
new ReceivingContainerRequests.BulkReceive("IN-1", "SKU-1", "PAL-1", 1, "pallet-key")))
|
||||
new ReceivingContainerRequests.BulkReceive("IN-1", "PAL-1", 1, "pallet-key")))
|
||||
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("409");
|
||||
verify(workflow, never()).assignResolvedPallet(any(), any(), any(), any());
|
||||
}
|
||||
@@ -223,7 +223,7 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
doThrow(new ResponseStatusException(org.springframework.http.HttpStatus.CONFLICT, "over receipt"))
|
||||
.when(workflow).validateBulkResolved(any(), any(), any(), anyInt());
|
||||
assertThatThrownBy(() -> service.receiveBulk("7", "receiver",
|
||||
new ReceivingContainerRequests.BulkReceive("IN-1", "SKU-1", "PAL-1", 2, "over-key")))
|
||||
new ReceivingContainerRequests.BulkReceive("IN-1", "PAL-1", 2, "over-key")))
|
||||
.isInstanceOf(ResponseStatusException.class);
|
||||
verify(workflow, never()).assignResolvedPallet(any(), any(), any(), any());
|
||||
|
||||
@@ -291,7 +291,7 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
doThrow(failure).when(workflow).receiveBulkResolved(any(), any(), any(), any(), anyInt(), any(), any());
|
||||
|
||||
assertThatThrownBy(() -> service.receiveBulk("7", "receiver",
|
||||
new ReceivingContainerRequests.BulkReceive("IN-1", "SKU-1", "PAL-1", 1, "compensate-key")))
|
||||
new ReceivingContainerRequests.BulkReceive("IN-1", "PAL-1", 1, "compensate-key")))
|
||||
.isSameAs(failure);
|
||||
verify(workflow).compensateReservation("100", 55L, failure);
|
||||
}
|
||||
@@ -391,7 +391,7 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void bulkRejectsSkuOverReceiptBeforeWarehouseLookup() {
|
||||
void bulkRejectsAggregateSkuOverReceiptBeforeWarehouseLookup() {
|
||||
stubCurrent("FBA", "CONT-A");
|
||||
stubSku();
|
||||
com.baiyue.lastleg.inboundplan.pda.entity.ReceivingSkuReceipt received =
|
||||
@@ -400,11 +400,116 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
when(skuReceipts.listLineSku("100", 10L, "SKU-1")).thenReturn(List.of(received));
|
||||
|
||||
assertThatThrownBy(() -> service.receiveBulk("7", "receiver",
|
||||
new ReceivingContainerRequests.BulkReceive("IN-1", "SKU-1", "PAL-1", 1, "sku-over")))
|
||||
new ReceivingContainerRequests.BulkReceive("IN-1", "PAL-1", 1, "sku-over")))
|
||||
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("409");
|
||||
verifyNoInteractions(warehouse);
|
||||
}
|
||||
|
||||
@Test
|
||||
void fbaCartonResolutionWithoutManifestForNyexx2608260001DoesNotWriteReceiptState() {
|
||||
stubCurrent("FBA", "CONT-A");
|
||||
InboundPlanLine fba = line("FBA");
|
||||
fba.setInboundCode("NYEXX2608260001"); fba.setRequiredPackageCount(4);
|
||||
when(lines.selectList(any())).thenReturn(List.of(fba));
|
||||
|
||||
var resolved = service.resolveCarton("7", new ReceivingContainerRequests.CartonResolve("NYEXX2608260001#4-1"));
|
||||
var replay = service.resolveCarton("7", new ReceivingContainerRequests.CartonResolve("NYEXX2608260001#4-1"));
|
||||
|
||||
assertThat(resolved.cartonMark()).isEqualTo("NYEXX2608260001#4-1");
|
||||
assertThat(resolved.inboundCode()).isEqualTo("NYEXX2608260001");
|
||||
assertThat(resolved.destinationType()).isEqualTo("FBA");
|
||||
assertThat(replay).isEqualTo(resolved);
|
||||
verifyNoInteractions(workflow, operations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void fbaCartonResolutionRejectsCrossContainerAndInvalidPrefixesAsConflicts() {
|
||||
stubCurrent("FBA", "CONT-A");
|
||||
InboundPlanLine current = line("FBA");
|
||||
current.setInboundCode("NYEXX2608260001"); current.setRequiredPackageCount(4);
|
||||
InboundPlanLine foreign = line("FBA");
|
||||
foreign.setInboundCode("NYEXX2608269999"); foreign.setPlanId(99L);
|
||||
when(lines.selectList(any())).thenReturn(List.of(current), List.of(foreign));
|
||||
|
||||
assertThatThrownBy(() -> service.resolveCarton("7",
|
||||
new ReceivingContainerRequests.CartonResolve("NYEXX2608269999#4-1")))
|
||||
.isInstanceOfSatisfying(ResponseStatusException.class,
|
||||
error -> assertThat(error.getStatusCode().value()).isEqualTo(409));
|
||||
assertThatThrownBy(() -> service.resolveCarton("7",
|
||||
new ReceivingContainerRequests.CartonResolve("NYEXX2608260001")))
|
||||
.isInstanceOfSatisfying(ResponseStatusException.class,
|
||||
error -> assertThat(error.getStatusCode().value()).isEqualTo(409));
|
||||
assertThatThrownBy(() -> service.resolveCarton("7",
|
||||
new ReceivingContainerRequests.CartonResolve("#4-1")))
|
||||
.isInstanceOfSatisfying(ResponseStatusException.class,
|
||||
error -> assertThat(error.getStatusCode().value()).isEqualTo(409));
|
||||
|
||||
org.mockito.Mockito.reset(lines, cartons);
|
||||
when(lines.selectList(any())).thenReturn(List.of(current), List.of());
|
||||
assertThatThrownBy(() -> service.resolveCarton("7",
|
||||
new ReceivingContainerRequests.CartonResolve("NYEXX26082600001#4-1")))
|
||||
.isInstanceOfSatisfying(ResponseStatusException.class,
|
||||
error -> assertThat(error.getStatusCode().value()).isEqualTo(404));
|
||||
|
||||
InboundPlanLine duplicate = line("FBA");
|
||||
duplicate.setId(20L); duplicate.setInboundCode("NYEXX2608260001");
|
||||
org.mockito.Mockito.reset(lines);
|
||||
when(lines.selectList(any())).thenReturn(List.of(current, duplicate));
|
||||
assertThatThrownBy(() -> service.resolveCarton("7",
|
||||
new ReceivingContainerRequests.CartonResolve("NYEXX2608260001#any-tail")))
|
||||
.isInstanceOfSatisfying(ResponseStatusException.class,
|
||||
error -> assertThat(error.getStatusCode().value()).isEqualTo(409));
|
||||
}
|
||||
|
||||
@Test
|
||||
void overseasCartonResolutionWithoutManifestIsNotFound() {
|
||||
stubCurrent("OVERSEAS", "CONT-A");
|
||||
|
||||
assertThatThrownBy(() -> service.resolveCarton("7",
|
||||
new ReceivingContainerRequests.CartonResolve("IN-1#2-1")))
|
||||
.isInstanceOfSatisfying(ResponseStatusException.class,
|
||||
error -> assertThat(error.getStatusCode().value()).isEqualTo(404));
|
||||
}
|
||||
|
||||
@Test
|
||||
void fbaBulkDistributesAcrossLinesAndFrozenSkusInStableLineOrder() {
|
||||
InboundPlan plan = plan("CONT-A");
|
||||
InboundPlanLine first = line("FBA");
|
||||
InboundPlanLine second = line("FBA");
|
||||
second.setId(20L); second.setOrderId(2L); second.setRequiredPackageCount(2);
|
||||
LastlegOrder firstOrder = order("FBA");
|
||||
LastlegOrder secondOrder = order("FBA"); secondOrder.setId(2L);
|
||||
when(sessions.lockActiveByOperator("OPERATOR:7")).thenReturn(session("CONT-A", "SIGNED"));
|
||||
when(plans.selectList(any())).thenReturn(List.of(plan));
|
||||
when(lines.selectList(any())).thenReturn(List.of(second, first));
|
||||
when(orders.selectById(1L)).thenReturn(firstOrder);
|
||||
when(orders.selectById(2L)).thenReturn(secondOrder);
|
||||
when(orderDetails.selectList(any())).thenReturn(List.of(detail("SKU-A", 1), detail("SKU-B", 1)),
|
||||
List.of(detail("SKU-C", 2)));
|
||||
when(warehouse.resolvePallet("PAL-1")).thenReturn(resource(100L, "FBA-1"));
|
||||
when(pallets.selectList(any())).thenReturn(List.of(planPallet()));
|
||||
when(workflow.receiveBulkResolved(any(), any(), any(), any(), anyInt(), any(), any()))
|
||||
.thenAnswer(invocation -> invocation.getArgument(4));
|
||||
when(sessions.updateById(any(ReceivingContainerSession.class))).thenReturn(1);
|
||||
when(operations.insert(any(ReceivingContainerOperation.class))).thenReturn(1);
|
||||
|
||||
var result = service.receiveBulk("7", "receiver",
|
||||
new ReceivingContainerRequests.BulkReceive("IN-1", "PAL-1", 3, "split-key"));
|
||||
|
||||
assertThat(result.acceptedPackages()).isEqualTo(3);
|
||||
ArgumentCaptor<InboundPlanLine> capturedLines = ArgumentCaptor.forClass(InboundPlanLine.class);
|
||||
ArgumentCaptor<Integer> capturedCounts = ArgumentCaptor.forClass(Integer.class);
|
||||
verify(workflow, org.mockito.Mockito.times(3)).receiveBulkResolved(any(), any(), capturedLines.capture(), any(),
|
||||
capturedCounts.capture(), any(), any());
|
||||
assertThat(capturedLines.getAllValues()).containsExactly(first, first, second);
|
||||
assertThat(capturedCounts.getAllValues()).containsExactly(1, 1, 1);
|
||||
ArgumentCaptor<com.baiyue.lastleg.inboundplan.pda.entity.ReceivingSkuReceipt> receipts =
|
||||
ArgumentCaptor.forClass(com.baiyue.lastleg.inboundplan.pda.entity.ReceivingSkuReceipt.class);
|
||||
verify(skuReceipts, org.mockito.Mockito.times(3)).insert(receipts.capture());
|
||||
assertThat(receipts.getAllValues()).extracting(value -> value.getSkuCode())
|
||||
.containsExactly("SKU-A", "SKU-B", "SKU-C");
|
||||
}
|
||||
|
||||
private void stubCurrent(String destinationType, String containerNo) {
|
||||
InboundPlan plan = plan(containerNo);
|
||||
InboundPlanLine line = line(destinationType);
|
||||
|
||||
Reference in New Issue
Block a user