fix(dropship): project sealed carton identities into overseas receiving
This commit is contained in:
+9
-3
@@ -144,8 +144,14 @@ public record FirstLegLoadingPlanCompletedV2(
|
||||
/** 头程托盘冻结快照;普通入仓用于复用打印编号,一键代发还冻结 SKU/箱唛库存。 */
|
||||
public record PalletSnapshot(String palletNo, List<SkuPackageSnapshot> skuPackages) {}
|
||||
|
||||
/** 托盘下 SKU、箱数、数量及箱号冻结快照;cartonMarks 中每个箱号都对应 skuCode。 */
|
||||
public record SkuPackageSnapshot(String skuCode, String productName,
|
||||
/** 托盘下来源入库明细、SKU、箱数、数量及箱号冻结快照;同 SKU 多明细按 sourceInboundDetailId 精确区分。 */
|
||||
public record SkuPackageSnapshot(String sourceInboundDetailId, String skuCode, String productName,
|
||||
int packageCount, int productQuantity,
|
||||
List<String> cartonMarks) {}
|
||||
List<String> cartonMarks) {
|
||||
public SkuPackageSnapshot {
|
||||
if (sourceInboundDetailId == null || sourceInboundDetailId.isBlank()) {
|
||||
throw new IllegalArgumentException("sourceInboundDetailId is required for each pallet SKU snapshot");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ components:
|
||||
volumeWeightKg: {type: [number, 'null'], minimum: 0, description: Sum of final loaded item dimensional weight facts; null when unavailable}
|
||||
items: {type: array, items: {type: object}}
|
||||
orderType: {type: string, enum: [FIRST_LEG, ONE_CLICK_DROPSHIP]}
|
||||
pallets: {type: array, items: {type: object}}
|
||||
pallets: {type: array, items: {$ref: '#/components/schemas/FirstLegPalletSnapshotV2'}}
|
||||
destinationType: {type: string, enum: [FBA, OVERSEAS]}
|
||||
FirstLegContainerShippingNodeRecordedV1:
|
||||
type: object
|
||||
@@ -57,6 +57,24 @@ components:
|
||||
providerEventId: {type: string}
|
||||
providerNodeCode: {type: string}
|
||||
rawPayloadHash: {type: string, pattern: '^[0-9a-fA-F]{64}$'}
|
||||
FirstLegPalletSnapshotV2:
|
||||
type: object
|
||||
required: [palletNo, skuPackages]
|
||||
properties:
|
||||
palletNo: {type: string, minLength: 1}
|
||||
skuPackages:
|
||||
type: array
|
||||
items: {$ref: '#/components/schemas/FirstLegSkuPackageSnapshotV2'}
|
||||
FirstLegSkuPackageSnapshotV2:
|
||||
type: object
|
||||
required: [sourceInboundDetailId, skuCode, productName, packageCount, productQuantity, cartonMarks]
|
||||
properties:
|
||||
sourceInboundDetailId: {type: string, minLength: 1, description: Exact source inbound detail identity matching the order item; never infer from SKU because multiple details may share a SKU}
|
||||
skuCode: {type: string}
|
||||
productName: {type: [string, 'null']}
|
||||
packageCount: {type: integer, minimum: 1}
|
||||
productQuantity: {type: integer, minimum: 1}
|
||||
cartonMarks: {type: array, items: {type: string}, minItems: 1}
|
||||
OverseasContainerUnloadedV1:
|
||||
type: object
|
||||
required: [sourceContainerBookingId, containerNo, unloadingCompletedAt, unloadingPhotoUploadedAt]
|
||||
|
||||
+17
-1
@@ -11,6 +11,21 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
class FirstLegLoadingPlanCompletedV2Test {
|
||||
@Test
|
||||
void palletSkuRequiresExplicitSourceDetailIncludingJsonDeserialization() throws Exception {
|
||||
var mapper = new ObjectMapper();
|
||||
var first = new FirstLegLoadingPlanCompletedV2.SkuPackageSnapshot("101", "SAME", "产品", 1, 10, List.of("BOX-101"));
|
||||
var second = new FirstLegLoadingPlanCompletedV2.SkuPackageSnapshot("102", "SAME", "产品", 1, 20, List.of("BOX-102"));
|
||||
assertThat(mapper.readValue(mapper.writeValueAsString(first), FirstLegLoadingPlanCompletedV2.SkuPackageSnapshot.class))
|
||||
.isEqualTo(first).isNotEqualTo(second);
|
||||
for (String invalid : new String[]{null, "", " "}) {
|
||||
assertThatThrownBy(() -> new FirstLegLoadingPlanCompletedV2.SkuPackageSnapshot(invalid, "SAME", "产品", 1, 10, List.of("BOX-101")))
|
||||
.isInstanceOf(IllegalArgumentException.class).hasMessageContaining("sourceInboundDetailId");
|
||||
}
|
||||
assertThatThrownBy(() -> mapper.readValue("{\"skuCode\":\"SAME\",\"packageCount\":1,\"productQuantity\":10,\"cartonMarks\":[\"BOX-101\"]}",
|
||||
FirstLegLoadingPlanCompletedV2.SkuPackageSnapshot.class)).hasMessageContaining("sourceInboundDetailId");
|
||||
}
|
||||
|
||||
@Test
|
||||
void carriesOnlyFrozenContainerTimesAndActualOrderAggregates() {
|
||||
var order = new FirstLegLoadingPlanCompletedV2.Order(
|
||||
@@ -66,7 +81,7 @@ class FirstLegLoadingPlanCompletedV2Test {
|
||||
@Test
|
||||
void carriesDropshipPalletAndCartonSnapshots() throws Exception {
|
||||
var sku = new FirstLegLoadingPlanCompletedV2.SkuPackageSnapshot(
|
||||
"SKU", "产品", 2, 20, List.of("BOX-001", "BOX-002"));
|
||||
"101", "SKU", "产品", 2, 20, List.of("BOX-001", "BOX-002"));
|
||||
var pallet = new FirstLegLoadingPlanCompletedV2.PalletSnapshot("PLT-01", List.of(sku));
|
||||
var order = new FirstLegLoadingPlanCompletedV2.Order(
|
||||
"2", "2", "3", "ORD", "3", "RK", null, null, null,
|
||||
@@ -80,6 +95,7 @@ class FirstLegLoadingPlanCompletedV2Test {
|
||||
assertThat(json.at("/orders/0/customerName").asText()).isEqualTo("客户甲");
|
||||
assertThat(json.at("/orders/0/orderType").asText()).isEqualTo("ONE_CLICK_DROPSHIP");
|
||||
assertThat(json.at("/orders/0/pallets/0/skuPackages/0/cartonMarks")).hasSize(2);
|
||||
assertThat(json.at("/orders/0/pallets/0/skuPackages/0/sourceInboundDetailId").asText()).isEqualTo("101");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user