优化
This commit is contained in:
+16
-4
@@ -23,21 +23,33 @@ class InboundMeasurementEvidenceOpenApiContractTest {
|
||||
|
||||
Map<String, Object> schemas = stringMap(stringMap(document.get("components")).get("schemas"));
|
||||
Map<String, Object> detailProperties = properties(schemas, "InboundMeasurementDetail");
|
||||
assertThat(detailProperties).containsKeys("actualGrossWeightKg", "items", "weightResults");
|
||||
assertThat(detailProperties).containsKeys(
|
||||
"actualGrossWeightKg", "actualNetWeightKg", "items", "weightResults");
|
||||
assertThat(stringMap(detailProperties.get("items")))
|
||||
.containsEntry("description", "DIMENSION 尺寸测量明细及其图片证据");
|
||||
assertThat(stringMap(detailProperties.get("weightResults")))
|
||||
.containsEntry("description", "WEIGHT 称重结果及其图片、确认状态和服务端重量事实");
|
||||
assertThat(detailProperties).doesNotContainKey("images");
|
||||
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");
|
||||
assertThat(stringMap(stringMap(draftProperties.get("items")).get("items")))
|
||||
.containsEntry("$ref", "#/components/schemas/InboundMeasurementItemRequest");
|
||||
assertThat(properties(schemas, "InboundMeasurementItemRequest")).containsKeys(
|
||||
"inboundDetailId", "itemNo", "boxSpecification", "packageCount", "lengthCm",
|
||||
"widthCm", "heightCm", "unitWeightKg", "intelligentImageFileId", "remark");
|
||||
|
||||
Map<String, Object> itemProperties = properties(schemas, "InboundMeasurementItem");
|
||||
assertThat(itemProperties).containsKeys("id", "intelligentImageFileId");
|
||||
Map<String, Object> weightProperties = properties(schemas, "InboundWeightResult");
|
||||
assertThat(weightProperties).containsKeys(
|
||||
"id", "fileId", "recognitionStatus", "grossWeightKg", "netWeightKg", "confirmedAt");
|
||||
assertThat(stringMap(properties(schemas, "UnifiedInboundOrderSummary")
|
||||
.get("actualGrossWeightKg")).get("description").toString())
|
||||
.contains("服务端权威货物总重量", "CONFIRMED netWeightKg", "不得从图片重算");
|
||||
assertThat(stringMap(detailProperties.get("actualGrossWeightKg")).get("description").toString())
|
||||
.contains("CONFIRMED", "grossWeightKg", "含托盘毛重");
|
||||
assertThat(stringMap(detailProperties.get("actualNetWeightKg")).get("description").toString())
|
||||
.contains("CONFIRMED", "netWeightKg", "审核与计费以此为准");
|
||||
}
|
||||
|
||||
private static Map<String, Object> properties(Map<String, Object> schemas, String schemaName) {
|
||||
|
||||
+3
-8
@@ -1,7 +1,6 @@
|
||||
package com.baiyue.firstleg.controller;
|
||||
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.firstleg.dto.ExceptionReleaseRequest;
|
||||
import com.baiyue.firstleg.dto.MeasurementApprovalResponse;
|
||||
import com.baiyue.firstleg.dto.MeasurementReviewRequest;
|
||||
import com.baiyue.firstleg.service.InitialInboundPricingService;
|
||||
@@ -24,14 +23,10 @@ import static org.mockito.Mockito.when;
|
||||
@DisplayName("入库测量审核控制器契约")
|
||||
class InboundMeasurementReviewContractTest {
|
||||
@Test
|
||||
@DisplayName("审核、放行和历史接口声明独立按钮权限")
|
||||
@DisplayName("审核和历史接口声明独立按钮权限")
|
||||
void shouldDeclareButtonPermissions() throws Exception {
|
||||
assertAuthority("approve", "hasAuthority('firstleg:inbound:measurement-review')",
|
||||
AuthenticatedUser.class, long.class, long.class, String.class, MeasurementReviewRequest.class);
|
||||
assertAuthority("reject", "hasAuthority('firstleg:inbound:measurement-review')",
|
||||
AuthenticatedUser.class, long.class, long.class, String.class, MeasurementReviewRequest.class);
|
||||
assertAuthority("exceptionRelease", "hasAuthority('firstleg:inbound:exception-release')",
|
||||
AuthenticatedUser.class, long.class, long.class, String.class, ExceptionReleaseRequest.class);
|
||||
assertAuthority("history", "hasAuthority('firstleg:inbound:measurement-review-history')",
|
||||
AuthenticatedUser.class, long.class, long.class, long.class, long.class);
|
||||
}
|
||||
@@ -43,14 +38,14 @@ class InboundMeasurementReviewContractTest {
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
assertThat(openapi)
|
||||
.contains("/api/platform/v1/inbounds/{inboundId}/measurements/approve:")
|
||||
.contains("/api/platform/v1/inbounds/{inboundId}/measurements/reject:")
|
||||
.contains("firstleg:inbound:exception-release")
|
||||
.contains("Idempotency-Key")
|
||||
.contains("初算异常只返回非阻断提示")
|
||||
.contains("data.processStatus 固定为 WAIT_SPLIT")
|
||||
.contains("feeStatus=UNPRICED 且主流程仍成功")
|
||||
.contains("name: inboundId")
|
||||
.contains("schema: {type: string, pattern:")
|
||||
.doesNotContain("/api/platform/v1/inbounds/{inboundId}/measurements/reject:")
|
||||
.doesNotContain("/api/platform/v1/inbounds/{inboundId}/measurements/exception-release:")
|
||||
.doesNotContain("待客户确认");
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -51,7 +51,9 @@ class InboundOrderControllerContractTest {
|
||||
.contains("required: false")
|
||||
.contains("InboundOrderSummary:")
|
||||
.contains("required: [id, tenantId]")
|
||||
.contains("actualNetWeightKg: {type: number, nullable: true}")
|
||||
.contains("actualNetWeightKg: {type: number, nullable: true")
|
||||
.contains("只读取 fl_inbound_order.actual_gross_weight_kg,不以净重回退")
|
||||
.contains("只读取 fl_inbound_order.actual_net_weight_kg,不以毛重回退")
|
||||
.contains("chargeableVolumeCbm: {type: number, nullable: true}")
|
||||
.contains("measureCompletedAt: {type: string, format: date-time, nullable: true}")
|
||||
.contains("pricingTaxMode: {type: string, enum: [TAX_INCLUDED, TAX_EXCLUDED]")
|
||||
|
||||
+21
@@ -2,17 +2,38 @@ package com.baiyue.firstleg.controller;
|
||||
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.firstleg.dto.InboundMeasurementRequests;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.RecordComponent;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
@DisplayName("平台入库测量接口权限契约")
|
||||
class PlatformInboundMeasurementControllerContractTest {
|
||||
@Test
|
||||
void draftRequestOnlyAcceptsEditableGoodsNameAndMeasurementItems() {
|
||||
assertThat(Arrays.stream(InboundMeasurementRequests.SaveDraftRequest.class.getRecordComponents())
|
||||
.map(RecordComponent::getName)).containsExactly(
|
||||
"measurementId", "version", "actualGoodsName", "items");
|
||||
}
|
||||
|
||||
@Test
|
||||
void draftRequestRejectsRemovedDeclarationFieldsAtDeserialization() {
|
||||
String json = """
|
||||
{"version":0,"originalGrossWeightKg":2000,"actualGoodsName":"货物","items":[]}
|
||||
""";
|
||||
assertThatThrownBy(() -> new ObjectMapper().readValue(
|
||||
json, InboundMeasurementRequests.SaveDraftRequest.class))
|
||||
.hasMessageContaining("originalGrossWeightKg");
|
||||
}
|
||||
|
||||
@Test
|
||||
void everyMeasurementOperationUsesDedicatedButtonPermission() throws Exception {
|
||||
assertAuthority("detail", "hasAuthority('platform:firstleg:inbound:measure:view')",
|
||||
|
||||
+2
-1
@@ -41,7 +41,8 @@ class UnifiedInboundOpenApiContractTest {
|
||||
"holdFlag", "actualGrossWeightKg", "actualNetWeightKg", "actualVolumeCbm",
|
||||
"volumeWeightKg", "chargeableWeightKg", "chargeableVolumeCbm", "palletCount",
|
||||
"inboundedAt", "requestedPickupAt", "expressCompanyCode", "expressCompanyName", "containerNo",
|
||||
"cabinetNo", "sailingAt", "portArrivalAt", "createdByName", "createdAt", "updatedAt");
|
||||
"cabinetNo", "estimatedSailingAt", "actualSailingAt", "estimatedPortArrivalAt",
|
||||
"actualPortArrivalAt", "createdByName", "createdAt", "updatedAt");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.baiyue.firstleg.dto;
|
||||
|
||||
import com.baiyue.firstleg.entity.InboundOrderEntity;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class InboundOrderWeightSemanticsTest {
|
||||
@Test
|
||||
void summaryKeepsGrossAndNetWeightAsIndependentDatabaseFacts() {
|
||||
InboundOrderEntity inbound = new InboundOrderEntity();
|
||||
inbound.setActualGrossWeightKg(new BigDecimal("1787.500"));
|
||||
inbound.setActualNetWeightKg(new BigDecimal("1747.500"));
|
||||
|
||||
InboundOrderSummary result = InboundOrderSummary.from(
|
||||
inbound, (CurrentContainerTransport) null, null, null);
|
||||
|
||||
assertThat(result.actualGrossWeightKg()).isEqualByComparingTo("1787.500");
|
||||
assertThat(result.actualNetWeightKg()).isEqualByComparingTo("1747.500");
|
||||
}
|
||||
|
||||
@Test
|
||||
void summaryDoesNotFallbackBetweenGrossAndNetWeight() {
|
||||
InboundOrderEntity inbound = new InboundOrderEntity();
|
||||
inbound.setActualGrossWeightKg(new BigDecimal("1787.500"));
|
||||
|
||||
InboundOrderSummary result = InboundOrderSummary.from(
|
||||
inbound, (CurrentContainerTransport) null, null, null);
|
||||
|
||||
assertThat(result.actualGrossWeightKg()).isEqualByComparingTo("1787.500");
|
||||
assertThat(result.actualNetWeightKg()).isNull();
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -23,13 +23,14 @@ class InboundOrderStatusSqlContractTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsConfirmedWeightNetSumAuthoritativeWhenMeasurementIsApproved() {
|
||||
void keepsConfirmedGrossAndNetSumsSeparateWhenMeasurementIsApproved() {
|
||||
assertThat(sql("approveMeasurement"))
|
||||
.contains("FROM fl_inbound_weight_result weight")
|
||||
.contains("weight.recognition_status = 'CONFIRMED'")
|
||||
.contains("weight.deleted = 0")
|
||||
.contains("SUM(weight.net_weight_kg)")
|
||||
.contains("ELSE (SELECT COALESCE(SUM(item.total_weight_kg), 0)");
|
||||
.contains("actual_gross_weight_kg = (SELECT SUM(weight.gross_weight_kg)")
|
||||
.contains("actual_net_weight_kg = (SELECT SUM(weight.net_weight_kg)")
|
||||
.doesNotContain("item.total_weight_kg");
|
||||
}
|
||||
|
||||
private static String sql(String methodName) {
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.baiyue.firstleg.repository;
|
||||
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.firstleg.dto.CurrentContainerTransport;
|
||||
import com.baiyue.firstleg.mapper.UnifiedInboundTransportMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
|
||||
class UnifiedInboundTransportRepositoryTest {
|
||||
private final UnifiedInboundTransportMapper mapper = mock(UnifiedInboundTransportMapper.class);
|
||||
private final UnifiedInboundReadRepository repository = repository();
|
||||
|
||||
@Test
|
||||
void batchKeepsParentChildAndDepartedUnitOnTheirOwnContainer() {
|
||||
var oldDeparted = fact(11L, 101L, 501L, "OLD", null, LocalDateTime.parse("2026-08-01T08:00"));
|
||||
var splitChild = fact(12L, 101L, 502L, "NEW", LocalDateTime.parse("2026-08-20T08:00"), null);
|
||||
when(mapper.findCurrentByInboundIds(anyList())).thenReturn(List.of(oldDeparted, splitChild));
|
||||
|
||||
var result = repository.findTransports(Map.of(11L, 101L, 12L, 101L));
|
||||
|
||||
assertThat(result.get(11L).containerBookingId()).isEqualTo(501L);
|
||||
assertThat(result.get(11L).actualSailingAt()).isEqualTo(LocalDateTime.parse("2026-08-01T08:00"));
|
||||
assertThat(result.get(12L).containerBookingId()).isEqualTo(502L);
|
||||
assertThat(result.get(12L).estimatedSailingAt()).isEqualTo(LocalDateTime.parse("2026-08-20T08:00"));
|
||||
verify(mapper).findCurrentByInboundIds(anyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
void noContainerReturnsNoFact() {
|
||||
when(mapper.findCurrentByInboundIds(List.of(11L))).thenReturn(List.of());
|
||||
assertThat(repository.findTransports(Map.of(11L, 101L))).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleEffectiveContainersAreAnInvariantConflict() {
|
||||
when(mapper.findCurrentByInboundIds(List.of(11L))).thenReturn(List.of(
|
||||
fact(11L, 101L, 501L, "A", null, null), fact(11L, 101L, 502L, "B", null, null)));
|
||||
assertThatThrownBy(() -> repository.findTransports(Map.of(11L, 101L)))
|
||||
.isInstanceOf(BizException.class).hasMessageContaining("多个有效柜次");
|
||||
}
|
||||
|
||||
@Test
|
||||
void filtersRowsOutsideInboundTenantOwnership() {
|
||||
when(mapper.findCurrentByInboundIds(List.of(11L))).thenReturn(List.of(
|
||||
fact(11L, 999L, 501L, "OTHER", null, null)));
|
||||
assertThat(repository.findTransports(Map.of(11L, 101L))).isEmpty();
|
||||
}
|
||||
|
||||
private UnifiedInboundReadRepository repository() {
|
||||
var value = new UnifiedInboundReadRepository();
|
||||
ReflectionTestUtils.setField(value, "transports", mapper);
|
||||
return value;
|
||||
}
|
||||
|
||||
private CurrentContainerTransport fact(Long inboundId, Long accountId, Long bookingId, String containerNo,
|
||||
LocalDateTime estimatedSailingAt, LocalDateTime actualSailingAt) {
|
||||
return new CurrentContainerTransport(inboundId, accountId, bookingId, containerNo, "CAB", "VESSEL", "V1",
|
||||
estimatedSailingAt, actualSailingAt, null, null);
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.baiyue.firstleg.repository;
|
||||
|
||||
import com.baiyue.firstleg.mapper.UnifiedInboundTransportMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class UnifiedInboundTransportSqlContractTest {
|
||||
@Test
|
||||
void queriesCurrentBusinessUnitContainersInOneBatchFromAuthoritativeBooking() throws Exception {
|
||||
String sql = String.join(" ", UnifiedInboundTransportMapper.class
|
||||
.getMethod("findCurrentByInboundIds", java.util.List.class).getAnnotation(Select.class).value());
|
||||
assertThat(sql).contains("fl_loading_plan_item", "i.deleted = 0",
|
||||
"p.plan_status IN ('LOADING', 'COMPLETED')", "fl_container_booking",
|
||||
"estimated_sailing_at", "actual_sailing_at", "estimated_port_arrival_at",
|
||||
"actual_port_arrival_at", "<foreach");
|
||||
assertThat(sql).doesNotContain("fl_inbound_transport_snapshot", "LIMIT 1");
|
||||
}
|
||||
}
|
||||
+8
-4
@@ -275,7 +275,7 @@ class CartonMeasurementServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void confirmedPackageCountUsesCartonWeightAsFallbackWhenNoConfirmedImageExists() {
|
||||
void confirmedPackageCountDoesNotUseCartonWeightAsWeightFallback() {
|
||||
InboundOrderEntity inbound = inbound("MEASURE_PENDING");
|
||||
InboundMeasurementEntity measurement = measurement(30001L, "SUBMITTED", 2);
|
||||
measurement.setTotalPackageCount(1);
|
||||
@@ -300,7 +300,8 @@ class CartonMeasurementServiceTest {
|
||||
service.updateConfirmedPackageCount(10001L, 70001L, 40001L, 2, 5);
|
||||
|
||||
assertThat(inbound.getTotalPackageCount()).isEqualTo(5);
|
||||
assertThat(inbound.getActualGrossWeightKg()).isEqualByComparingTo("54.400");
|
||||
assertThat(inbound.getActualGrossWeightKg()).isNull();
|
||||
assertThat(inbound.getActualNetWeightKg()).isNull();
|
||||
assertThat(inbound.getActualVolumeCbm()).isEqualByComparingTo("0.7480");
|
||||
assertThat(inbound.getVolumeWeightKg()).isEqualByComparingTo("124.916");
|
||||
verify(repository).updateInbound(inbound);
|
||||
@@ -309,7 +310,8 @@ class CartonMeasurementServiceTest {
|
||||
@Test
|
||||
void cartonMeasurementAfterConfirmedWeightKeepsNetWeightAndUpdatesVolume() {
|
||||
InboundOrderEntity inbound = inbound("MEASURE_PENDING");
|
||||
inbound.setActualGrossWeightKg(new BigDecimal("11.500"));
|
||||
inbound.setActualGrossWeightKg(new BigDecimal("30.000"));
|
||||
inbound.setActualNetWeightKg(new BigDecimal("11.500"));
|
||||
InboundMeasurementEntity measurement = measurement(30001L, "SUBMITTED", 2);
|
||||
measurement.setTotalPackageCount(1);
|
||||
measurement.setTotalVolumeCbm(new BigDecimal("0.0600"));
|
||||
@@ -323,6 +325,7 @@ class CartonMeasurementServiceTest {
|
||||
item.setCalculationRuleVersion("ACTUAL_DIMENSION_V1");
|
||||
InboundWeightResultEntity confirmedWeight = new InboundWeightResultEntity();
|
||||
confirmedWeight.setRecognitionStatus("CONFIRMED");
|
||||
confirmedWeight.setGrossWeightKg(new BigDecimal("30.000"));
|
||||
confirmedWeight.setNetWeightKg(new BigDecimal("11.500"));
|
||||
when(repository.findInbound(10001L, 70001L)).thenReturn(inbound);
|
||||
when(repository.findItem(10001L, 40001L)).thenReturn(item);
|
||||
@@ -335,7 +338,8 @@ class CartonMeasurementServiceTest {
|
||||
|
||||
service.updateConfirmedPackageCount(10001L, 70001L, 40001L, 2, 5);
|
||||
|
||||
assertThat(inbound.getActualGrossWeightKg()).isEqualByComparingTo("11.500");
|
||||
assertThat(inbound.getActualGrossWeightKg()).isEqualByComparingTo("30.000");
|
||||
assertThat(inbound.getActualNetWeightKg()).isEqualByComparingTo("11.500");
|
||||
assertThat(inbound.getActualVolumeCbm()).isEqualByComparingTo("0.7480");
|
||||
assertThat(inbound.getVolumeWeightKg()).isEqualByComparingTo("124.916");
|
||||
verify(repository).updateInbound(inbound);
|
||||
|
||||
@@ -421,7 +421,8 @@ class FbaPricingServiceTest {
|
||||
value.setChannelCode("MATSON_TRUCK"); value.setCarrierId(31L); value.setCarrierName("船司A");
|
||||
value.setTaxMode("BUY_DECLARATION"); value.setPricingTaxMode("TAX_INCLUDED");
|
||||
value.setDestinationType("FBA"); value.setDestinationCode("ONT8");
|
||||
value.setActualGrossWeightKg(new BigDecimal(weight)); value.setActualVolumeCbm(new BigDecimal(volume));
|
||||
value.setActualGrossWeightKg(new BigDecimal(weight).add(new BigDecimal("8.000")));
|
||||
value.setActualNetWeightKg(new BigDecimal(weight)); value.setActualVolumeCbm(new BigDecimal(volume));
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -459,6 +459,7 @@ class FeeManagementServiceTest {
|
||||
inbound.setDestinationName("ONT8");
|
||||
inbound.setTotalPackageCount(10);
|
||||
inbound.setActualGrossWeightKg(new BigDecimal("10.5000"));
|
||||
inbound.setActualNetWeightKg(new BigDecimal("10.5000"));
|
||||
inbound.setActualVolumeCbm(new BigDecimal("1.250000"));
|
||||
inbound.setVolumeWeightKg(new BigDecimal("208.3333"));
|
||||
inbound.setChargeableWeightKg(new BigDecimal("208.3333"));
|
||||
|
||||
+76
-5
@@ -56,8 +56,12 @@ class InboundMeasurementServiceTest {
|
||||
|
||||
@BeforeEach
|
||||
void stubWarehousePalletSnapshots() {
|
||||
when(repository.updateInboundWeightSummary(any())).thenReturn(true);
|
||||
when(warehousePallets.palletSnapshots("internal-token", 100L)).thenReturn(
|
||||
ApiResponse.success(List.of(palletSnapshot())));
|
||||
when(orderCargoClient.measurementDeclaration("internal-token", 1L, 80L)).thenReturn(
|
||||
ApiResponse.success(new OrderCargoClient.MeasurementDeclarationSnapshot(
|
||||
"REF-50", new BigDecimal("2000"), new BigDecimal("20"), "123123")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -98,6 +102,7 @@ class InboundMeasurementServiceTest {
|
||||
void correctingConfirmedWeightImmediatelyRemovesItFromInboundTotal() {
|
||||
InboundOrderEntity inbound = inbound("MEASURING");
|
||||
inbound.setActualGrossWeightKg(new BigDecimal("12.500"));
|
||||
inbound.setActualNetWeightKg(new BigDecimal("12.500"));
|
||||
InboundWeightResultEntity weight = weight(20L, "12.500", "KG", "CONFIRMED", 4);
|
||||
weight.setGrossWeightKg(new BigDecimal("12.500"));
|
||||
weight.setNetWeightKg(new BigDecimal("12.500"));
|
||||
@@ -115,6 +120,7 @@ class InboundMeasurementServiceTest {
|
||||
assertThat(weight.getGrossWeightKg()).isNull();
|
||||
assertThat(weight.getNetWeightKg()).isNull();
|
||||
assertThat(inbound.getActualGrossWeightKg()).isNull();
|
||||
assertThat(inbound.getActualNetWeightKg()).isNull();
|
||||
verify(repository).updateInboundWeightSummary(inbound);
|
||||
}
|
||||
|
||||
@@ -134,13 +140,18 @@ class InboundMeasurementServiceTest {
|
||||
assertThat(result.recommendedPalletType()).isEqualTo("STANDARD");
|
||||
assertThat(result.inboundVersion()).isEqualTo(7);
|
||||
assertThat(result.version()).isEqualTo(4);
|
||||
assertThat(result.originalDocumentNo()).isEqualTo("REF-50");
|
||||
assertThat(result.originalGrossWeightKg()).isEqualByComparingTo("2000");
|
||||
assertThat(result.originalVolumeCbm()).isEqualByComparingTo("20");
|
||||
assertThat(result.actualGoodsName()).isEqualTo("123123");
|
||||
verify(repository, never()).updateInbound(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailKeepsDimensionAndWeightEvidenceOnTheirStableBusinessRecords() {
|
||||
InboundOrderEntity inbound = inbound("MEASURE_PENDING");
|
||||
inbound.setActualGrossWeightKg(new BigDecimal("11.500"));
|
||||
inbound.setActualGrossWeightKg(new BigDecimal("30.000"));
|
||||
inbound.setActualNetWeightKg(new BigDecimal("11.500"));
|
||||
InboundMeasurementEntity measurement = new InboundMeasurementEntity();
|
||||
measurement.setId(30L);
|
||||
measurement.setVersion(4);
|
||||
@@ -161,7 +172,8 @@ class InboundMeasurementServiceTest {
|
||||
|
||||
InboundMeasurementResponses.Detail result = service.detail(1L, 10L);
|
||||
|
||||
assertThat(result.actualGrossWeightKg()).isEqualByComparingTo("11.500");
|
||||
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.intelligentImageFileId()).isEqualTo(140L);
|
||||
@@ -232,6 +244,7 @@ class InboundMeasurementServiceTest {
|
||||
InboundOrderEntity inbound = inbound("WAIT_MEASURE");
|
||||
InboundWeightResultEntity current = weight(20L, "10.000", "LB", "PENDING_CONFIRM", 0);
|
||||
InboundWeightResultEntity previous = weight(21L, "2.000", "KG", "CONFIRMED", 1);
|
||||
previous.setGrossWeightKg(new BigDecimal("2.000"));
|
||||
previous.setNetWeightKg(new BigDecimal("2.000"));
|
||||
when(repository.findInbound(1L, 10L)).thenReturn(inbound);
|
||||
when(repository.findWeight(1L, 10L, 20L)).thenReturn(current);
|
||||
@@ -246,6 +259,7 @@ class InboundMeasurementServiceTest {
|
||||
assertThat(response.grossWeightKg()).isEqualByComparingTo("4.536");
|
||||
assertThat(response.netWeightKg()).isEqualByComparingTo("4.536");
|
||||
assertThat(inbound.getActualGrossWeightKg()).isEqualByComparingTo("6.536");
|
||||
assertThat(inbound.getActualNetWeightKg()).isEqualByComparingTo("6.536");
|
||||
assertThat(inbound.getProcessStatus()).isEqualTo("MEASURING");
|
||||
verify(repository).insertRevision(any(InboundWeightRevisionEntity.class));
|
||||
}
|
||||
@@ -268,7 +282,8 @@ class InboundMeasurementServiceTest {
|
||||
|
||||
assertThat(response.grossWeightKg()).isEqualByComparingTo("30.000");
|
||||
assertThat(response.netWeightKg()).isEqualByComparingTo("11.500");
|
||||
assertThat(inbound.getActualGrossWeightKg()).isEqualByComparingTo("11.500");
|
||||
assertThat(inbound.getActualGrossWeightKg()).isEqualByComparingTo("30.000");
|
||||
assertThat(inbound.getActualNetWeightKg()).isEqualByComparingTo("11.500");
|
||||
ArgumentCaptor<InboundWeightRevisionEntity> revision =
|
||||
ArgumentCaptor.forClass(InboundWeightRevisionEntity.class);
|
||||
verify(repository).insertRevision(revision.capture());
|
||||
@@ -276,6 +291,31 @@ class InboundMeasurementServiceTest {
|
||||
assertThat(revision.getValue().getNetWeightKg()).isEqualByComparingTo("11.500");
|
||||
}
|
||||
|
||||
@Test
|
||||
void confirmingFifthPalletSeparatelyAggregatesGrossAndCargoNetWeight() {
|
||||
InboundOrderEntity inbound = inbound("WAIT_MEASURE");
|
||||
InboundWeightResultEntity current = weight(20L, "357.500", "KG", "PENDING_CONFIRM", 0);
|
||||
current.setPalletTareWeightKg(new BigDecimal("8.000"));
|
||||
List<InboundWeightResultEntity> weights = new java.util.ArrayList<>();
|
||||
weights.add(current);
|
||||
for (long id = 21L; id <= 24L; id++) {
|
||||
InboundWeightResultEntity confirmed = weight(id, "357.500", "KG", "CONFIRMED", 1);
|
||||
confirmed.setGrossWeightKg(new BigDecimal("357.500"));
|
||||
confirmed.setNetWeightKg(new BigDecimal("349.500"));
|
||||
weights.add(confirmed);
|
||||
}
|
||||
when(repository.findInbound(1L, 10L)).thenReturn(inbound);
|
||||
when(repository.findWeight(1L, 10L, 20L)).thenReturn(current);
|
||||
when(repository.updateWeight(current)).thenReturn(true);
|
||||
when(repository.listWeights(1L, 10L)).thenReturn(weights);
|
||||
|
||||
service.confirmWeightResult(1L, 10L, 20L, 9L, "操作员",
|
||||
new InboundMeasurementRequests.ConfirmWeightResultRequest(0, null, null, null));
|
||||
|
||||
assertThat(inbound.getActualGrossWeightKg()).isEqualByComparingTo("1787.500");
|
||||
assertThat(inbound.getActualNetWeightKg()).isEqualByComparingTo("1747.500");
|
||||
}
|
||||
|
||||
@Test
|
||||
void confirmingWeightRejectsGrossWeightNotAboveFrozenTare() {
|
||||
InboundOrderEntity inbound = inbound("WAIT_MEASURE");
|
||||
@@ -545,7 +585,7 @@ class InboundMeasurementServiceTest {
|
||||
|
||||
verify(repository).deleteWeight(1L, 10L, 20L, 1);
|
||||
verify(imageClient).delete("internal-token", 9L, 1L, 10L, 120L);
|
||||
verify(repository, never()).updateInboundWeightSummary(any());
|
||||
verify(repository).updateInboundWeightSummary(inbound);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -566,6 +606,7 @@ class InboundMeasurementServiceTest {
|
||||
service.deleteWeightResult(1L, 10L, 20L, 2, 9L);
|
||||
|
||||
assertThat(inbound.getActualGrossWeightKg()).isNull();
|
||||
assertThat(inbound.getActualNetWeightKg()).isNull();
|
||||
verify(repository).updateInboundWeightSummary(inbound);
|
||||
}
|
||||
|
||||
@@ -574,8 +615,10 @@ class InboundMeasurementServiceTest {
|
||||
InboundOrderEntity inbound = inbound("MEASURING");
|
||||
inbound.setActualGrossWeightKg(new BigDecimal("15.000"));
|
||||
InboundWeightResultEntity deleted = weight(20L, "10.000", "KG", "CONFIRMED", 2);
|
||||
deleted.setGrossWeightKg(new BigDecimal("10.000"));
|
||||
deleted.setNetWeightKg(new BigDecimal("10.000"));
|
||||
InboundWeightResultEntity remaining = weight(21L, "5.000", "KG", "CONFIRMED", 1);
|
||||
remaining.setGrossWeightKg(new BigDecimal("5.000"));
|
||||
remaining.setNetWeightKg(new BigDecimal("5.000"));
|
||||
when(repository.findInbound(1L, 10L)).thenReturn(inbound);
|
||||
when(repository.findWeight(1L, 10L, 20L)).thenReturn(deleted);
|
||||
@@ -588,6 +631,7 @@ class InboundMeasurementServiceTest {
|
||||
service.deleteWeightResult(1L, 10L, 20L, 2, 9L);
|
||||
|
||||
assertThat(inbound.getActualGrossWeightKg()).isEqualByComparingTo("5.000");
|
||||
assertThat(inbound.getActualNetWeightKg()).isEqualByComparingTo("5.000");
|
||||
verify(repository).updateInboundWeightSummary(inbound);
|
||||
}
|
||||
|
||||
@@ -622,6 +666,7 @@ class InboundMeasurementServiceTest {
|
||||
item.setPackageCount(3);
|
||||
item.setSourceImageFileId(500L);
|
||||
InboundWeightResultEntity weight = weight(20L, "20.000", "KG", "CONFIRMED", 1);
|
||||
weight.setGrossWeightKg(new BigDecimal("20.000"));
|
||||
weight.setNetWeightKg(new BigDecimal("20.000"));
|
||||
when(repository.findInbound(1L, 10L)).thenReturn(inbound);
|
||||
when(repository.findMeasurement(1L, 10L, 30L)).thenReturn(measurement);
|
||||
@@ -638,6 +683,7 @@ class InboundMeasurementServiceTest {
|
||||
assertThat(inbound.getMeasureReviewStatus()).isEqualTo("PENDING");
|
||||
assertThat(measurement.getStatus()).isEqualTo("SUBMITTED");
|
||||
assertThat(inbound.getActualGrossWeightKg()).isEqualByComparingTo("20.000");
|
||||
assertThat(inbound.getActualNetWeightKg()).isEqualByComparingTo("20.000");
|
||||
assertThat(inbound.getPalletCount()).isEqualTo(1);
|
||||
assertThat(inbound.getActualVolumeCbm()).isEqualByComparingTo("1.2345");
|
||||
}
|
||||
@@ -711,7 +757,7 @@ class InboundMeasurementServiceTest {
|
||||
when(batchService.saveBatch(any())).thenReturn(saved);
|
||||
|
||||
InboundMeasurementResponses.DraftSaved result = service.saveDraft(1L, 10L, 9L,
|
||||
new InboundMeasurementRequests.SaveDraftRequest(null, 0, null, null, null, null,
|
||||
new InboundMeasurementRequests.SaveDraftRequest(null, 0, null,
|
||||
List.of(new InboundMeasurementRequests.ItemRequest(80001L, "001", "A", 2,
|
||||
new BigDecimal("40"), new BigDecimal("30"), new BigDecimal("20"),
|
||||
new BigDecimal("3.250"), null, null, null, null, null, null))));
|
||||
@@ -720,13 +766,38 @@ class InboundMeasurementServiceTest {
|
||||
ArgumentCaptor<MeasurementBatchCommand> command = ArgumentCaptor.forClass(MeasurementBatchCommand.class);
|
||||
verify(batchService).saveBatch(command.capture());
|
||||
assertThat(command.getValue().items().get(0).inboundDetailId()).isEqualTo(80001L);
|
||||
assertThat(command.getValue().originalDocumentNo()).isEqualTo("REF-50");
|
||||
assertThat(command.getValue().originalGrossWeightKg()).isEqualByComparingTo("2000");
|
||||
assertThat(command.getValue().originalVolumeCbm()).isEqualByComparingTo("20");
|
||||
assertThat(command.getValue().actualGoodsName()).isEqualTo("123123");
|
||||
verify(orderCargoClient).detail("internal-token", 1L, 80L, 80001L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void savingDraftRejectsOverlongDefaultGoodsNameBeforeDatabaseWrite() {
|
||||
InboundOrderEntity inbound = inbound("WAIT_MEASURE");
|
||||
InboundMeasurementRequests.ItemRequest item = new InboundMeasurementRequests.ItemRequest(
|
||||
80001L, "001", "A", 2, new BigDecimal("40"), new BigDecimal("30"),
|
||||
new BigDecimal("20"), new BigDecimal("3.250"), null, null, null, null, null, null);
|
||||
when(repository.findInbound(1L, 10L)).thenReturn(inbound);
|
||||
when(orderCargoClient.detail("internal-token", 1L, 80L, 80001L)).thenReturn(
|
||||
ApiResponse.success(new OrderCargoClient.CargoBinding(
|
||||
80001L, 80L, 1, "Test", 2, new BigDecimal("6.5"), new BigDecimal("0.1"))));
|
||||
when(orderCargoClient.measurementDeclaration("internal-token", 1L, 80L)).thenReturn(
|
||||
ApiResponse.success(new OrderCargoClient.MeasurementDeclarationSnapshot(
|
||||
"REF", new BigDecimal("6.5"), new BigDecimal("0.1"), "A".repeat(256))));
|
||||
|
||||
assertThatThrownBy(() -> service.saveDraft(1L, 10L, 9L,
|
||||
new InboundMeasurementRequests.SaveDraftRequest(null, 0, null, List.of(item))))
|
||||
.isInstanceOf(BizException.class).hasMessageContaining("超过255字符");
|
||||
verify(batchService, never()).saveBatch(any());
|
||||
}
|
||||
|
||||
private InboundOrderEntity inbound(String status) {
|
||||
InboundOrderEntity inbound = new InboundOrderEntity();
|
||||
inbound.setId(10L);
|
||||
inbound.setTenantId(1L);
|
||||
inbound.setOrderId(80L);
|
||||
inbound.setWarehouseId(100L);
|
||||
inbound.setProcessStatus(status);
|
||||
inbound.setTotalPackageCount(3);
|
||||
|
||||
+19
-1
@@ -9,6 +9,8 @@ import com.baiyue.firstleg.entity.InboundMeasurementEntity;
|
||||
import com.baiyue.firstleg.entity.InboundMeasurementItemEntity;
|
||||
import com.baiyue.firstleg.entity.InboundOrderEntity;
|
||||
import com.baiyue.firstleg.entity.InboundStatusHistoryEntity;
|
||||
import com.baiyue.firstleg.entity.InboundWeightResultEntity;
|
||||
import com.baiyue.firstleg.repository.InboundMeasurementRepository;
|
||||
import com.baiyue.firstleg.repository.InboundWorkflowRepository;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
@@ -41,6 +43,7 @@ import static org.mockito.Mockito.when;
|
||||
@SpringJUnitConfig(MeasurementReviewService.class)
|
||||
class MeasurementReviewServiceTest {
|
||||
@MockBean private InboundWorkflowRepository repository;
|
||||
@MockBean private InboundMeasurementRepository measurements;
|
||||
@MockBean private InboundIdempotencyService idempotency;
|
||||
@MockBean private InboundProperties properties;
|
||||
@Resource private MeasurementReviewService service;
|
||||
@@ -212,7 +215,8 @@ class MeasurementReviewServiceTest {
|
||||
order.setProcessStatus("MEASURE_PENDING");
|
||||
order.setMeasureReviewStatus("PENDING");
|
||||
order.setTotalPackageCount(5);
|
||||
order.setActualGrossWeightKg(new BigDecimal("11.500"));
|
||||
order.setActualGrossWeightKg(new BigDecimal("1787.500"));
|
||||
order.setActualNetWeightKg(new BigDecimal("1747.500"));
|
||||
InboundMeasurementEntity measurement = new InboundMeasurementEntity();
|
||||
measurement.setId(20L);
|
||||
measurement.setStatus("SUBMITTED");
|
||||
@@ -224,9 +228,23 @@ class MeasurementReviewServiceTest {
|
||||
when(repository.findOwned(10001L, 10L)).thenReturn(order);
|
||||
when(repository.findMeasurement(10001L, 10L, 20L)).thenReturn(measurement);
|
||||
when(repository.listMeasurementItems(10001L, 20L)).thenReturn(List.of(item));
|
||||
when(measurements.listWeights(10001L, 10L)).thenReturn(List.of(
|
||||
confirmedWeight("357.500", "349.500"),
|
||||
confirmedWeight("357.500", "349.500"),
|
||||
confirmedWeight("357.500", "349.500"),
|
||||
confirmedWeight("357.500", "349.500"),
|
||||
confirmedWeight("357.500", "349.500")));
|
||||
return new Fixture(service, repository, idempotency, order, measurement);
|
||||
}
|
||||
|
||||
private InboundWeightResultEntity confirmedWeight(String grossWeightKg, String netWeightKg) {
|
||||
InboundWeightResultEntity weight = new InboundWeightResultEntity();
|
||||
weight.setRecognitionStatus("CONFIRMED");
|
||||
weight.setGrossWeightKg(new BigDecimal(grossWeightKg));
|
||||
weight.setNetWeightKg(new BigDecimal(netWeightKg));
|
||||
return weight;
|
||||
}
|
||||
|
||||
private record Fixture(MeasurementReviewService service, InboundWorkflowRepository repository,
|
||||
InboundIdempotencyService idempotency, InboundOrderEntity order,
|
||||
InboundMeasurementEntity measurement) {
|
||||
|
||||
Reference in New Issue
Block a user