feat(firstleg): expose authoritative measurement identifiers
This commit is contained in:
+16
-4
@@ -32,8 +32,10 @@ class InboundMeasurementEvidenceOpenApiContractTest {
|
||||
Map<String, Object> schemas = stringMap(stringMap(document.get("components")).get("schemas"));
|
||||
Map<String, Object> detailProperties = properties(schemas, "InboundMeasurementDetail");
|
||||
assertThat(detailProperties).containsKeys(
|
||||
"actualGrossWeightKg", "actualNetWeightKg", "latestDeclaration", "items", "weightResults",
|
||||
"inboundCode", "actualGrossWeightKg", "actualNetWeightKg", "latestDeclaration", "items", "weightResults",
|
||||
"palletLocationGuide");
|
||||
assertThat(stringMap(detailProperties.get("inboundCode")).get("description").toString())
|
||||
.contains("唯一入仓码", "UI业务主展示字段");
|
||||
assertThat(stringMap(detailProperties.get("latestDeclaration")))
|
||||
.containsEntry("$ref", "#/components/schemas/InboundMeasurementLatestDeclaration");
|
||||
assertThat(properties(schemas, "InboundMeasurementLatestDeclaration"))
|
||||
@@ -49,8 +51,11 @@ class InboundMeasurementEvidenceOpenApiContractTest {
|
||||
Map<String, Object> draftProperties = properties(schemas, "InboundMeasurementSaveDraftRequest");
|
||||
assertThat(draftProperties).containsKeys("measurementId", "version", "actualGoodsName", "items")
|
||||
.doesNotContainKeys("originalDocumentNo", "originalGrossWeightKg", "originalVolumeCbm");
|
||||
assertThat(paths).containsKey(
|
||||
"/api/platform/v1/first-leg/inbounds/{inboundId}/measurement/draft");
|
||||
String draftPath = "/api/platform/v1/first-leg/inbounds/{inboundId}/measurement/draft";
|
||||
assertThat(paths).containsKey(draftPath);
|
||||
Map<String, Object> draftOperation = stringMap(stringMap(paths.get(draftPath)).get("put"));
|
||||
assertThat(draftOperation.get("description").toString())
|
||||
.contains("MEASURE_PENDING", "冻结", "package-count");
|
||||
assertThat(stringMap(stringMap(draftProperties.get("items")).get("items")))
|
||||
.containsEntry("$ref", "#/components/schemas/InboundMeasurementItemRequest");
|
||||
assertThat(properties(schemas, "InboundMeasurementItemRequest")).containsKeys(
|
||||
@@ -58,7 +63,14 @@ class InboundMeasurementEvidenceOpenApiContractTest {
|
||||
"widthCm", "heightCm", "intelligentImageFileId", "remark");
|
||||
|
||||
Map<String, Object> itemProperties = properties(schemas, "InboundMeasurementItem");
|
||||
assertThat(itemProperties).containsKeys("id", "intelligentImageFileId");
|
||||
assertThat(itemProperties).containsKeys(
|
||||
"id", "inboundCode", "inboundDetailId", "intelligentImageFileId", "fbaShipmentNo", "scannedShipmentId");
|
||||
assertThat(stringMap(itemProperties.get("inboundCode")).get("description").toString())
|
||||
.contains("唯一权威入仓码", "根对象inboundCode一致");
|
||||
assertThat(stringMap(itemProperties.get("fbaShipmentNo")).get("description").toString())
|
||||
.contains("Order货物明细", "inboundDetailId", "匹配失败为空");
|
||||
assertThat(stringMap(itemProperties.get("scannedShipmentId")).get("description").toString())
|
||||
.contains("设备实扫", "不覆盖或回退fbaShipmentNo");
|
||||
Map<String, Object> weightProperties = properties(schemas, "InboundWeightResult");
|
||||
assertThat(weightProperties).containsKeys(
|
||||
"id", "fileId", "recognitionStatus", "grossWeightKg", "netWeightKg", "confirmedAt",
|
||||
|
||||
+84
-4
@@ -75,8 +75,8 @@ class InboundMeasurementServiceTest {
|
||||
"REF-50", new BigDecimal("2000"), new BigDecimal("20"), "123123")));
|
||||
when(orderCargoClient.listForCartonLabels("internal-token", 1L, 80L)).thenReturn(
|
||||
ApiResponse.success(List.of(new OrderCargoClient.CargoLabelBinding(
|
||||
80001L, 80L, 1, "123123", 3, new BigDecimal("40"),
|
||||
new BigDecimal("30"), new BigDecimal("20")))));
|
||||
80001L, 80L, 1, "123123", null, 3, new BigDecimal("40"),
|
||||
new BigDecimal("30"), new BigDecimal("20"), "FBA-DECLARED-001"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -196,7 +196,9 @@ class InboundMeasurementServiceTest {
|
||||
item.setId(40L);
|
||||
item.setTenantId(1L);
|
||||
item.setMeasurementId(30L);
|
||||
item.setInboundDetailId(80001L);
|
||||
item.setSourceImageFileId(140L);
|
||||
item.setScannedShipmentId("FBA-SCANNED-001");
|
||||
item.setOuterLengthCm(new BigDecimal("64.000"));
|
||||
item.setOuterWidthCm(new BigDecimal("54.000"));
|
||||
item.setOuterHeightCm(new BigDecimal("54.000"));
|
||||
@@ -217,11 +219,15 @@ class InboundMeasurementServiceTest {
|
||||
|
||||
InboundMeasurementResponses.Detail result = service.detail(1L, 10L);
|
||||
|
||||
assertThat(result.inboundCode()).isEqualTo("RK-001");
|
||||
assertThat(result.actualGrossWeightKg()).isEqualByComparingTo("30.000");
|
||||
assertThat(result.actualNetWeightKg()).isEqualByComparingTo("11.500");
|
||||
assertThat(result.items()).singleElement().satisfies(dimension -> {
|
||||
assertThat(dimension.id()).isEqualTo(40L);
|
||||
assertThat(dimension.inboundCode()).isEqualTo("RK-001");
|
||||
assertThat(dimension.intelligentImageFileId()).isEqualTo(140L);
|
||||
assertThat(dimension.fbaShipmentNo()).isEqualTo("FBA-DECLARED-001");
|
||||
assertThat(dimension.scannedShipmentId()).isEqualTo("FBA-SCANNED-001");
|
||||
assertThat(dimension.outerLengthCm()).isNull();
|
||||
assertThat(dimension.outerWidthCm()).isNull();
|
||||
assertThat(dimension.outerHeightCm()).isNull();
|
||||
@@ -246,6 +252,63 @@ class InboundMeasurementServiceTest {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailMapsMultipleDeclaredShipmentIdsByInboundDetailWithoutFallingBackToScannedValue() {
|
||||
InboundOrderEntity inbound = inbound("MEASURE_PENDING");
|
||||
InboundMeasurementEntity measurement = new InboundMeasurementEntity();
|
||||
measurement.setId(30L);
|
||||
InboundMeasurementItemEntity first = new InboundMeasurementItemEntity();
|
||||
first.setId(41L);
|
||||
first.setInboundDetailId(80001L);
|
||||
first.setScannedShipmentId("SCAN-DIFFERENT-001");
|
||||
InboundMeasurementItemEntity second = new InboundMeasurementItemEntity();
|
||||
second.setId(42L);
|
||||
second.setInboundDetailId(80002L);
|
||||
InboundMeasurementItemEntity unmatched = new InboundMeasurementItemEntity();
|
||||
unmatched.setId(43L);
|
||||
unmatched.setInboundDetailId(89999L);
|
||||
unmatched.setScannedShipmentId("SCAN-ONLY-999");
|
||||
when(repository.findInbound(1L, 10L)).thenReturn(inbound);
|
||||
when(repository.findLatestMeasurement(1L, 10L)).thenReturn(measurement);
|
||||
when(repository.listItems(1L, 30L)).thenReturn(List.of(first, second, unmatched));
|
||||
when(repository.listWeights(1L, 10L)).thenReturn(List.of());
|
||||
when(orderCargoClient.listForCartonLabels("internal-token", 1L, 80L)).thenReturn(
|
||||
ApiResponse.success(List.of(
|
||||
new OrderCargoClient.CargoLabelBinding(80001L, 80L, 1, "货物1", null,
|
||||
1, null, null, null, "FBA-DECLARED-001"),
|
||||
new OrderCargoClient.CargoLabelBinding(80002L, 80L, 2, "货物2", null,
|
||||
1, null, null, null, "FBA-DECLARED-002"))));
|
||||
|
||||
InboundMeasurementResponses.Detail result = service.detail(1L, 10L);
|
||||
|
||||
assertThat(result.inboundCode()).isEqualTo("RK-001");
|
||||
assertThat(result.items())
|
||||
.extracting(InboundMeasurementResponses.Item::inboundDetailId,
|
||||
InboundMeasurementResponses.Item::inboundCode,
|
||||
InboundMeasurementResponses.Item::fbaShipmentNo,
|
||||
InboundMeasurementResponses.Item::scannedShipmentId)
|
||||
.containsExactly(
|
||||
tuple(80001L, "RK-001", "FBA-DECLARED-001", "SCAN-DIFFERENT-001"),
|
||||
tuple(80002L, "RK-001", "FBA-DECLARED-002", null),
|
||||
tuple(89999L, "RK-001", null, "SCAN-ONLY-999"));
|
||||
verify(orderCargoClient, times(1)).listForCartonLabels("internal-token", 1L, 80L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailRejectsCargoBatchWhoseOrderOwnershipDoesNotMatchInbound() {
|
||||
InboundOrderEntity inbound = inbound("MEASURE_PENDING");
|
||||
when(repository.findInbound(1L, 10L)).thenReturn(inbound);
|
||||
when(repository.listWeights(1L, 10L)).thenReturn(List.of());
|
||||
when(orderCargoClient.listForCartonLabels("internal-token", 1L, 80L)).thenReturn(
|
||||
ApiResponse.success(List.of(new OrderCargoClient.CargoLabelBinding(
|
||||
80001L, 81L, 1, "其他订单货物", null, 1, null, null, null,
|
||||
"FBA-OTHER-001"))));
|
||||
|
||||
assertThatThrownBy(() -> service.detail(1L, 10L))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("货物明细归属不一致");
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailReturnsWarehouseOuterDimensionsOnlyAfterManualOverrideWasSaved() {
|
||||
InboundOrderEntity inbound = inbound("MEASURING");
|
||||
@@ -333,8 +396,8 @@ class InboundMeasurementServiceTest {
|
||||
"NEW-REF", new BigDecimal("40.000"), new BigDecimal("0.8000"), "最新申报品名")));
|
||||
when(orderCargoClient.listForCartonLabels("internal-token", 1L, 80L)).thenReturn(
|
||||
ApiResponse.success(List.of(new OrderCargoClient.CargoLabelBinding(
|
||||
80001L, 80L, 1, "最新申报品名", 4, new BigDecimal("50"),
|
||||
new BigDecimal("40"), new BigDecimal("30")))));
|
||||
80001L, 80L, 1, "最新申报品名", null, 4, new BigDecimal("50"),
|
||||
new BigDecimal("40"), new BigDecimal("30"), "FBA-LATEST-001"))));
|
||||
|
||||
InboundMeasurementResponses.Detail result = service.detail(1L, 10L);
|
||||
|
||||
@@ -356,6 +419,7 @@ class InboundMeasurementServiceTest {
|
||||
assertThat(result.items()).singleElement().satisfies(measured -> {
|
||||
assertThat(measured.id()).isEqualTo(40L);
|
||||
assertThat(measured.boxCode()).isEqualTo("RK-001#2-1");
|
||||
assertThat(measured.fbaShipmentNo()).isEqualTo("FBA-LATEST-001");
|
||||
assertThat(measured.packageCount()).isEqualTo(2);
|
||||
assertThat(measured.lengthCm()).isEqualByComparingTo("45");
|
||||
});
|
||||
@@ -1116,6 +1180,21 @@ class InboundMeasurementServiceTest {
|
||||
verify(orderCargoClient).detail("internal-token", 1L, 80L, 80001L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void savingDraftCannotReopenSubmittedMeasurementWhileReviewIsPending() {
|
||||
InboundOrderEntity inbound = inbound("MEASURE_PENDING");
|
||||
inbound.setMeasureReviewStatus("PENDING");
|
||||
when(repository.findInbound(1L, 10L)).thenReturn(inbound);
|
||||
|
||||
assertThatThrownBy(() -> service.saveDraft(1L, 10L, 9L,
|
||||
new InboundMeasurementRequests.SaveDraftRequest(30L, 10, null, List.of())))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("已提交待审核");
|
||||
|
||||
verifyNoInteractions(batchService);
|
||||
verify(orderCargoClient, never()).detail(any(), anyLong(), anyLong(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void savingDraftRejectsOverlongDefaultGoodsNameBeforeDatabaseWrite() {
|
||||
InboundOrderEntity inbound = inbound("WAIT_MEASURE");
|
||||
@@ -1157,6 +1236,7 @@ class InboundMeasurementServiceTest {
|
||||
inbound.setTenantId(1L);
|
||||
inbound.setOrderId(80L);
|
||||
inbound.setWarehouseId(100L);
|
||||
inbound.setInboundCode("RK-001");
|
||||
inbound.setProcessStatus(status);
|
||||
inbound.setTotalPackageCount(3);
|
||||
inbound.setVersion(0);
|
||||
|
||||
+12
-9
@@ -392,28 +392,31 @@ class MeasurementBatchServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void approvedManualMeasurementCanBeEditedAsDraftBeforeReviewCompletes() {
|
||||
void submittedMeasurementCannotBeResetToDraftWhileReviewIsPending() {
|
||||
InboundMeasurementEntity existing = new InboundMeasurementEntity();
|
||||
existing.setId(100L);
|
||||
existing.setTenantId(1L);
|
||||
existing.setInboundId(10L);
|
||||
existing.setSourceType("MANUAL");
|
||||
existing.setStatus("APPROVED");
|
||||
existing.setStatus("SUBMITTED");
|
||||
existing.setVersion(3);
|
||||
InboundOrderEntity inbound = inbound();
|
||||
inbound.setProcessStatus("MEASURE_PENDING");
|
||||
when(repository.findInbound(1L, 10L)).thenReturn(inbound);
|
||||
when(repository.findMeasurement(1L, 10L, 100L)).thenReturn(existing);
|
||||
when(repository.updateMeasurement(existing)).thenReturn(true);
|
||||
|
||||
MeasurementBatchCommand command = new MeasurementBatchCommand(
|
||||
1L, 10L, 100L, 3, "MANUAL", "PLATFORM", null, null, null,
|
||||
LocalDateTime.now(), null, "CKW-2", new BigDecimal("21"),
|
||||
new BigDecimal("0.2"), "服装", 9L, List.of(item(BigDecimal.ONE)));
|
||||
|
||||
InboundMeasurementEntity saved = service.saveBatch(command);
|
||||
|
||||
assertThat(saved.getStatus()).isEqualTo("DRAFT");
|
||||
verify(repository).updateMeasurement(existing);
|
||||
verify(repository).replaceItems(org.mockito.ArgumentMatchers.eq(1L),
|
||||
org.mockito.ArgumentMatchers.eq(100L), any());
|
||||
assertThatThrownBy(() -> service.saveBatch(command))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("已提交批次必须保持冻结");
|
||||
assertThat(existing.getStatus()).isEqualTo("SUBMITTED");
|
||||
verify(repository, never()).updateMeasurement(any());
|
||||
verify(repository, never()).replaceItems(
|
||||
org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user