Merge remote-tracking branch 'origin/test' into dev
# Conflicts: # firstleg/src/main/java/com/baiyue/firstleg/client/OrderCargoClient.java # order/src/main/java/com/baiyue/order/controller/InternalOrderCargoController.java # order/src/test/java/com/baiyue/order/controller/InternalOrderCargoControllerTest.java
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",
|
||||
|
||||
+9
-2
@@ -16,6 +16,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.RecordComponent;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
@@ -145,9 +146,15 @@ class LoadingListControllerContractTest {
|
||||
.doesNotContainKey("internalContainerNo");
|
||||
assertThat(properties(schemas, "ConfirmedSummary"))
|
||||
.containsKeys("planId", "containerBookingId", "loadingListNo");
|
||||
assertThat(properties(schemas, "LoadingItem")).containsKeys(
|
||||
"itemId", "accountId", "inboundId", "inboundDetailId", "skuCode",
|
||||
Map<String, Object> loadingItem = properties(schemas, "LoadingItem");
|
||||
assertThat(loadingItem).containsKeys(
|
||||
"itemId", "accountId", "customsTaxMode", "taxRefundFlag", "inboundId", "inboundDetailId", "skuCode",
|
||||
"destinationWarehouse", "productQuantity", "frozenAt");
|
||||
assertThat(stringMap(loadingItem.get("customsTaxMode")))
|
||||
.containsEntry("nullable", true)
|
||||
.containsEntry("enum", List.of("TAX_REFUND", "BUY_DECLARATION"));
|
||||
assertThat(stringMap(loadingItem.get("taxRefundFlag")))
|
||||
.containsEntry("nullable", true);
|
||||
assertThat(properties(schemas, "OperationHistory")).containsKeys(
|
||||
"historyId", "itemId", "operationId", "actionType", "fromStatus", "toStatus",
|
||||
"beforeSnapshot", "afterSnapshot", "operatorId", "occurredAt");
|
||||
|
||||
+2
@@ -24,6 +24,8 @@ class FeeItemMigrationSqlContractTest {
|
||||
.contains("matched_seed.trade_scope = f.trade_scope")
|
||||
.contains("193000000001000111, 0, 'FBA_HEAD_FREIGHT'")
|
||||
.contains("fee_code = 'FBA_HEAD_FREIGHT'")
|
||||
.contains("193000000001000112, 0, 'PRIVATE_LAST_MILE_DELIVERY'")
|
||||
.contains("fee_code = 'PRIVATE_LAST_MILE_DELIVERY'")
|
||||
.contains("warehouse_receivable = 1")
|
||||
.doesNotContain("LEFT JOIN tmp_fee_item_seed by_code")
|
||||
.doesNotContain("LEFT JOIN tmp_fee_item_seed by_semantic")
|
||||
|
||||
+5
@@ -59,6 +59,11 @@ class LoadingListQueryMapperContractTest {
|
||||
String items = sql("selectItems", Select.class);
|
||||
assertThat(items)
|
||||
.contains("i.order_id", "i.order_no", "b.container_no")
|
||||
.contains("inbound.tax_mode AS customs_tax_mode")
|
||||
.contains("WHEN 'TAX_REFUND' THEN TRUE")
|
||||
.contains("WHEN 'BUY_DECLARATION' THEN FALSE")
|
||||
.contains("ELSE NULL")
|
||||
.doesNotContain("fl_customs_container_document", "fl_customs_container_document_link")
|
||||
.contains("b.record_status='ACTIVE'")
|
||||
.contains("NULLIF(i.pallet_count,0)")
|
||||
.contains("fl_inbound_weight_result weight")
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.baiyue.firstleg.dto.LoadingListDtos.ConfirmedQuery;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.PreloadQuery;
|
||||
import com.baiyue.firstleg.mapper.LoadingListQueryMapper;
|
||||
import com.baiyue.firstleg.mapper.LoadingListQueryMapper.AggregateRow;
|
||||
import com.baiyue.firstleg.mapper.LoadingListQueryMapper.ItemRow;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -96,4 +97,47 @@ class LoadingListRepositoryTest {
|
||||
var confirmed = repository.preloadDetail(row).summary().readiness();
|
||||
assertThat(confirmed.readyForPda()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void mapsFrozenCustomsTaxFactsPerInboundAcrossCustomers() {
|
||||
AggregateRow aggregate = new AggregateRow();
|
||||
aggregate.setPlanId(41L);
|
||||
aggregate.setContainerBookingId(61L);
|
||||
aggregate.setPlanNo("LP-41");
|
||||
aggregate.setPlanStatus("COMPLETED");
|
||||
aggregate.setVersion(3);
|
||||
ItemRow refund = item(101L, 11L, 201L, "IN-REFUND", "TAX_REFUND", true);
|
||||
ItemRow declaration = item(102L, 12L, 202L, "IN-BUY", "BUY_DECLARATION", false);
|
||||
ItemRow unknown = item(103L, 12L, 203L, "IN-UNKNOWN", null, null);
|
||||
when(mapper.selectItems(41L)).thenReturn(List.of(refund, declaration, unknown));
|
||||
when(mapper.selectHistories(41L)).thenReturn(List.of());
|
||||
|
||||
var items = repository.confirmedDetail(aggregate).items();
|
||||
|
||||
assertThat(items).extracting(value -> value.inboundCode(), value -> value.accountId(),
|
||||
value -> value.customsTaxMode(), value -> value.taxRefundFlag())
|
||||
.containsExactly(
|
||||
org.assertj.core.groups.Tuple.tuple("IN-REFUND", 11L, "TAX_REFUND", true),
|
||||
org.assertj.core.groups.Tuple.tuple("IN-BUY", 12L, "BUY_DECLARATION", false),
|
||||
org.assertj.core.groups.Tuple.tuple("IN-UNKNOWN", 12L, null, null));
|
||||
}
|
||||
|
||||
private static ItemRow item(long itemId, long accountId, long inboundId, String inboundCode,
|
||||
String customsTaxMode, Boolean taxRefundFlag) {
|
||||
ItemRow row = new ItemRow();
|
||||
row.setItemId(itemId);
|
||||
row.setAccountId(accountId);
|
||||
row.setCustomerName("customer-" + accountId);
|
||||
row.setCustomsTaxMode(customsTaxMode);
|
||||
row.setTaxRefundFlag(taxRefundFlag);
|
||||
row.setOrderId(300L + inboundId);
|
||||
row.setOrderNo("ORDER-" + inboundId);
|
||||
row.setInboundId(inboundId);
|
||||
row.setInboundDetailId(400L + inboundId);
|
||||
row.setInboundCode(inboundCode);
|
||||
row.setPackageCount(1);
|
||||
row.setLoadSequence(1);
|
||||
row.setItemVersion(0);
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.baiyue.firstleg.entity.InboundPricingEntity;
|
||||
import com.baiyue.firstleg.entity.InboundPricingIdempotencyEntity;
|
||||
import com.baiyue.firstleg.entity.InboundMeasurementEntity;
|
||||
import com.baiyue.firstleg.entity.InboundMeasurementItemEntity;
|
||||
import com.baiyue.firstleg.entity.CustomerChargeRuleEntity;
|
||||
import com.baiyue.firstleg.pricing.ActivePriceBookEstimator;
|
||||
import com.baiyue.firstleg.repository.FbaPricingRepository;
|
||||
import com.baiyue.firstleg.pricebookquery.application.model.PriceBookQueryRows.RateRow;
|
||||
@@ -37,6 +38,7 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.never;
|
||||
@@ -217,6 +219,10 @@ class FbaPricingServiceTest {
|
||||
InboundPricingEntity inbound = inbound("363.001", "0.5000");
|
||||
inbound.setPricingTaxMode("TAX_EXCLUDED");
|
||||
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
|
||||
CustomerChargeRuleEntity rule = new CustomerChargeRuleEntity();
|
||||
rule.setId(601L); rule.setRuleCode("CBM-RULE"); rule.setRuleVersion(1);
|
||||
rule.setChargeMode("CBM"); rule.setDefaultRule(true);
|
||||
when(repository.matchRules(eq(inbound), any())).thenReturn(List.of(rule));
|
||||
when(priceBookEstimator.estimate(any())).thenReturn(
|
||||
estimate("CBM", "1.0001", "100.000000", "100.01", "0.00"));
|
||||
|
||||
@@ -228,6 +234,8 @@ class FbaPricingServiceTest {
|
||||
assertThat(response.conversionFactor()).isEqualByComparingTo("363");
|
||||
assertThat(response.roundingScale()).isEqualTo(6);
|
||||
assertThat(response.roundingMode()).isEqualTo("HALF_UP");
|
||||
assertThat(response.chargeRuleId()).isEqualTo(601L);
|
||||
assertThat(response.ruleCode()).isEqualTo("CBM-RULE");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -483,15 +491,19 @@ class FbaPricingServiceTest {
|
||||
|
||||
FeeSnapshotResponse response = service.adjustPublished(TENANT, OPERATOR, INBOUND,
|
||||
new com.baiyue.firstleg.dto.FbaPricingDtos.AdjustFeeRequest(
|
||||
1, new BigDecimal("12.5"), new BigDecimal("8.00"),
|
||||
new BigDecimal("20.00"), "客服修正"), "adjust-1");
|
||||
1, new BigDecimal("160"), new BigDecimal("8.00"),
|
||||
new BigDecimal("20.00"), "客服修正", true), "adjust-1");
|
||||
|
||||
assertThat(response.feeVersion()).isEqualTo(2);
|
||||
assertThat(response.feeStatus()).isEqualTo("SENT");
|
||||
assertThat(response.totalAmount()).isEqualByComparingTo("120.00");
|
||||
assertThat(response.totalAmount()).isEqualByComparingTo("1300.00");
|
||||
assertThat(response.baselineChargeableQuantity()).isEqualByComparingTo("167.000000");
|
||||
assertThat(response.lowerChargeableQuantity()).isEqualByComparingTo("150.300000");
|
||||
assertThat(response.upperChargeableQuantity()).isEqualByComparingTo("183.700000");
|
||||
assertThat(response.tolerancePercent()).isEqualByComparingTo("10.00");
|
||||
assertThat(response.nextProcessStatus()).isEqualTo("WAIT_SPLIT");
|
||||
assertThat(inbound.getProcessStatus()).isEqualTo("WAIT_SPLIT");
|
||||
assertThat(inserted.get().getChargeableQuantity()).isEqualByComparingTo("12.5000");
|
||||
assertThat(inserted.get().getChargeableQuantity()).isEqualByComparingTo("160.0000");
|
||||
verify(repository).supersedeCurrent(TENANT, 501L, "SENT");
|
||||
verify(financeOutbox).publishInbound(eq(FinanceFeeSourceOutboxService.ADJUSTED),
|
||||
eq(TENANT), eq(INBOUND), eq(502L), eq(2), eq(NOW));
|
||||
@@ -538,7 +550,7 @@ class FbaPricingServiceTest {
|
||||
assertThatThrownBy(() -> service.adjustPublished(TENANT, OPERATOR, INBOUND,
|
||||
new com.baiyue.firstleg.dto.FbaPricingDtos.AdjustFeeRequest(
|
||||
1, new BigDecimal("12.5"), new BigDecimal("8.00"),
|
||||
BigDecimal.ZERO, "客服修正"), "locked-adjust"))
|
||||
BigDecimal.ZERO, "客服修正", true), "locked-adjust"))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("客户已确认");
|
||||
|
||||
@@ -574,11 +586,11 @@ class FbaPricingServiceTest {
|
||||
|
||||
FeeSnapshotResponse response = service.adjustPublished(TENANT, OPERATOR, INBOUND,
|
||||
new com.baiyue.firstleg.dto.FbaPricingDtos.AdjustFeeRequest(
|
||||
1, new BigDecimal("12.5"), new BigDecimal("8.00"),
|
||||
new BigDecimal("20.00"), "客服修正"), "adjust-calculated");
|
||||
1, new BigDecimal("160"), new BigDecimal("8.00"),
|
||||
new BigDecimal("20.00"), "客服修正", true), "adjust-calculated");
|
||||
|
||||
assertThat(response.feeStatus()).isEqualTo("CALCULATED");
|
||||
assertThat(response.totalAmount()).isEqualByComparingTo("120.00");
|
||||
assertThat(response.totalAmount()).isEqualByComparingTo("1300.00");
|
||||
assertThat(response.nextProcessStatus()).isEqualTo("WAIT_SPLIT");
|
||||
verify(repository).supersedeCurrent(TENANT, 501L, "CALCULATED");
|
||||
verify(feeDetails).materializeCalculatedFreight(TENANT, INBOUND, 503L, OPERATOR, NOW);
|
||||
@@ -598,7 +610,7 @@ class FbaPricingServiceTest {
|
||||
assertThatThrownBy(() -> service.adjustPublished(TENANT, OPERATOR, INBOUND,
|
||||
new com.baiyue.firstleg.dto.FbaPricingDtos.AdjustFeeRequest(
|
||||
1, new BigDecimal("12.5"), new BigDecimal("8.00"),
|
||||
BigDecimal.ZERO, "客服修正"), "stale-adjust"))
|
||||
BigDecimal.ZERO, "客服修正", true), "stale-adjust"))
|
||||
.isInstanceOf(BizException.class)
|
||||
.extracting(error -> ((BizException) error).getResultCode())
|
||||
.isEqualTo(ResultCode.CONFLICT);
|
||||
@@ -607,6 +619,64 @@ class FbaPricingServiceTest {
|
||||
verify(repository, never()).supersedeCurrent(anyLong(), anyLong(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("人工计费量允许上下10%含边界")
|
||||
void acceptsInclusiveToleranceBoundaries() {
|
||||
InboundPricingEntity inbound = inbound("10", "1");
|
||||
inbound.setProcessStatus("WAIT_SPLIT");
|
||||
InboundFeeEntity current = savedFee("CALCULATED");
|
||||
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
|
||||
when(repository.findLatestFee(TENANT, INBOUND)).thenReturn(current);
|
||||
when(repository.findIdempotency(TENANT, OPERATOR, "FEE_ADJUST", "lower-bound")).thenReturn(null);
|
||||
doAnswer(invocation -> { ((InboundPricingIdempotencyEntity) invocation.getArgument(0)).setId(903L); return null; })
|
||||
.when(repository).insertIdempotency(any());
|
||||
when(repository.nextFeeVersion(TENANT, INBOUND)).thenReturn(2);
|
||||
when(repository.supersedeCurrent(TENANT, 501L, "CALCULATED")).thenReturn(1);
|
||||
when(repository.markInboundAdjusted(eq(TENANT), eq(INBOUND), eq("CALCULATED"), eq("KG"), any()))
|
||||
.thenReturn(1);
|
||||
doAnswer(invocation -> { ((InboundFeeEntity) invocation.getArgument(0)).setId(504L); return null; })
|
||||
.when(repository).insertFee(any());
|
||||
when(repository.completeIdempotency(any(), eq(504L), eq(NOW))).thenReturn(1);
|
||||
when(repository.findFee(TENANT, 504L)).thenAnswer(invocation -> current);
|
||||
|
||||
service.adjustPublished(TENANT, OPERATOR, INBOUND,
|
||||
new com.baiyue.firstleg.dto.FbaPricingDtos.AdjustFeeRequest(
|
||||
1, new BigDecimal("150.3"), new BigDecimal("2"), BigDecimal.ZERO,
|
||||
"下界确认", true), "lower-bound");
|
||||
|
||||
verify(repository).insertFee(argThat(fee ->
|
||||
fee.getChargeableQuantity().compareTo(new BigDecimal("150.3000")) == 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("人工计费量越过10%或未二次确认返回冲突")
|
||||
void rejectsOutOfToleranceOrUnconfirmedAdjustment() {
|
||||
InboundPricingEntity inbound = inbound("10", "1");
|
||||
inbound.setProcessStatus("WAIT_SPLIT");
|
||||
when(repository.findInbound(TENANT, INBOUND)).thenReturn(inbound);
|
||||
when(repository.findLatestFee(TENANT, INBOUND)).thenReturn(savedFee("CALCULATED"));
|
||||
when(repository.findIdempotency(anyLong(), anyLong(), any(), any())).thenReturn(null);
|
||||
doAnswer(invocation -> { ((InboundPricingIdempotencyEntity) invocation.getArgument(0)).setId(904L); return null; })
|
||||
.when(repository).insertIdempotency(any());
|
||||
when(repository.nextFeeVersion(TENANT, INBOUND)).thenReturn(2);
|
||||
|
||||
assertThatThrownBy(() -> service.adjustPublished(TENANT, OPERATOR, INBOUND,
|
||||
new com.baiyue.firstleg.dto.FbaPricingDtos.AdjustFeeRequest(
|
||||
1, new BigDecimal("183.7001"), new BigDecimal("2"), BigDecimal.ZERO,
|
||||
"越界", true), "outside"))
|
||||
.isInstanceOf(BizException.class)
|
||||
.extracting(error -> ((BizException) error).getResultCode())
|
||||
.isEqualTo(ResultCode.CONFLICT);
|
||||
assertThatThrownBy(() -> service.adjustPublished(TENANT, OPERATOR, INBOUND,
|
||||
new com.baiyue.firstleg.dto.FbaPricingDtos.AdjustFeeRequest(
|
||||
1, new BigDecimal("167"), new BigDecimal("2"), BigDecimal.ZERO,
|
||||
"未确认", false), "unconfirmed"))
|
||||
.isInstanceOf(BizException.class)
|
||||
.extracting(error -> ((BizException) error).getResultCode())
|
||||
.isEqualTo(ResultCode.CONFLICT);
|
||||
verify(repository, never()).supersedeCurrent(anyLong(), anyLong(), any());
|
||||
}
|
||||
|
||||
private void readyRepository() {
|
||||
when(repository.findIdempotency(anyLong(), anyLong(), any(), any())).thenReturn(null);
|
||||
doAnswer(invocation -> { ((InboundPricingIdempotencyEntity) invocation.getArgument(0)).setId(900L); return null; })
|
||||
|
||||
@@ -337,6 +337,29 @@ class FeeManagementServiceTest {
|
||||
.hasMessageContaining("仓储应收/应付");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("私人仓尾程派送费只允许PRIVATE目的地")
|
||||
void restrictsPrivateLastMileDeliveryFeeByDestinationType() {
|
||||
InboundOrderEntity fbaInbound = inbound();
|
||||
fbaInbound.setDestinationType("FBA");
|
||||
FeeItemEntity privateDelivery = item(true, true);
|
||||
privateDelivery.setFeeCode("PRIVATE_LAST_MILE_DELIVERY");
|
||||
when(repository.findInbound(10001L, 21L)).thenReturn(fbaInbound);
|
||||
when(repository.findItem(11L)).thenReturn(privateDelivery);
|
||||
|
||||
assertThatThrownBy(() -> details.create(10001L, 21L, 9L,
|
||||
detailRequest("RECEIVABLE")))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("目的地类型为PRIVATE");
|
||||
verify(repository, never()).insertDetail(any());
|
||||
|
||||
fbaInbound.setDestinationType("PRIVATE");
|
||||
doAnswer(invocation -> { ((InboundFeeDetailEntity) invocation.getArgument(0)).setId(32L); return null; })
|
||||
.when(repository).insertDetail(any());
|
||||
assertThat(details.create(10001L, 21L, 9L, detailRequest("RECEIVABLE")).getId())
|
||||
.isEqualTo(32L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("审核只更新待审核明细")
|
||||
void approvesPendingDetail() {
|
||||
|
||||
+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
|
||||
|
||||
@@ -292,6 +292,7 @@ class MerchantPriceBookServiceTest {
|
||||
String weight, String volume) {
|
||||
return new EstimateRequest(10L, channelCode, destinationCode, destinationType,
|
||||
destinationWarehouseId, null, destinationRegion, pricingTaxMode,
|
||||
pricingTaxMode == PricingTaxMode.TAX_INCLUDED ? "KG" : "CBM",
|
||||
decimal(weight), decimal(volume));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user