feat(selfop): prefer list SKU quantities with targeted detail fallback
This commit is contained in:
+2
-1
@@ -45,8 +45,9 @@ public final class LingxingWmsDtos {
|
||||
}
|
||||
|
||||
// Preserve source strings; the collector interprets confirmed Asia/Shanghai source time.
|
||||
public record ListSku(String productSku, String productName, BigDecimal qty) { }
|
||||
public record OrderSummary(String deliveryNo, String sourceNo, String customerCode, String whCode,
|
||||
Integer status, String createTime, String outboundTime) { }
|
||||
Integer status, String createTime, String outboundTime, List<ListSku> productList) { }
|
||||
|
||||
public record PageResult(List<OrderSummary> records, Long total, int current, int size,
|
||||
int pages, int sourceCurrent, int sourceSize, int sourceCode, Instant observedAt, AttemptMetadata egress) {
|
||||
|
||||
+12
-1
@@ -304,12 +304,23 @@ public final class LingxingWmsClient {
|
||||
int status = requiredInt(record.get("status"), "STATUS");
|
||||
if (exactDeliveryNo == null && status != 100) throw invalidResponse("DISCOVERY_STATUS_MISMATCH", 200);
|
||||
return new OrderSummary(deliveryNo, optionalText(record, "sourceNo"), requiredText(record, "customerCode", 64),
|
||||
requiredText(record, "whCode", 64), status, optionalText(record, "createTime"), optionalText(record, "outboundTime"));
|
||||
requiredText(record, "whCode", 64), status, optionalText(record, "createTime"), optionalText(record, "outboundTime"), listSkus(record.get("productList")));
|
||||
});
|
||||
return new PageResult(page.records(), page.total(), current, size, page.pages(), page.sourceCurrent(),
|
||||
page.sourceSize(), 200, page.observedAt(), metadata);
|
||||
}
|
||||
|
||||
private List<ListSku> listSkus(JsonNode products) {
|
||||
if (products == null || products.isNull()) return null;
|
||||
if (!products.isArray()) throw invalidResponse("PRODUCT_LIST_TYPE_INVALID", 200);
|
||||
List<ListSku> items = new ArrayList<>();
|
||||
for (JsonNode item : products) {
|
||||
if (!item.isObject()) throw invalidResponse("PRODUCT_LIST_ROW_INVALID", 200);
|
||||
items.add(new ListSku(optionalText(item, "productSku"), optionalText(item, "productName"), quantity(item.get("qty"))));
|
||||
}
|
||||
return List.copyOf(items);
|
||||
}
|
||||
|
||||
private <T> QueryPage<T> mappedPage(JsonNode data, int current, int size, String currentField,
|
||||
String sizeField, boolean outbound, AttemptMetadata metadata, Function<JsonNode, T> projection) {
|
||||
JsonNode records = data.get("records");
|
||||
|
||||
@@ -349,6 +349,16 @@ components:
|
||||
status: {type: integer}
|
||||
createTime: {type: string, nullable: true}
|
||||
outboundTime: {type: string, nullable: true}
|
||||
productList:
|
||||
type: array
|
||||
nullable: true
|
||||
description: Per-SKU list quantities; missing items, SKU or qty require one detail supplement for this order. Zero is a known quantity.
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
productSku: {type: string, nullable: true}
|
||||
productName: {type: string, nullable: true}
|
||||
qty: {type: number, nullable: true}
|
||||
Warehouse:
|
||||
type: object
|
||||
required: [whCode, warehouseType, status]
|
||||
|
||||
+1
-1
@@ -131,7 +131,7 @@ class LingxingInternalControllerTest {
|
||||
void authenticatedReadReturnsOnlyOutboundSummary() throws Exception {
|
||||
when(client.queryPage(any(), any())).thenReturn(new PageResult(
|
||||
List.of(new OrderSummary("TEST-OUT-001", "TEST-SRC-001", "TEST-CUSTOMER", "TEST-WH", 100,
|
||||
"2026-10-01 08:00:00", "2026-10-02 08:00:00")),
|
||||
"2026-10-01 08:00:00", "2026-10-02 08:00:00", null)),
|
||||
1L, 1, 1, 1, 1, 1, 200, Instant.parse("2026-10-12T00:00:00Z"), null));
|
||||
mvc.perform(post(PATH).header("X-Internal-Service-Token", token).header("X-Lingxing-Egress-Context", CONTEXT)
|
||||
.contentType(MediaType.APPLICATION_JSON).content(BODY))
|
||||
|
||||
+11
@@ -81,6 +81,17 @@ class LingxingWmsClientTest {
|
||||
assertThat(result.observedAt()).isEqualTo(NOW);
|
||||
assertThat(result.pages()).isEqualTo(1);
|
||||
}
|
||||
@Test void listProductsPreserveDecimalsZeroAndMissingQuantity() {
|
||||
response.set(page("current","size",1,1,1,1,ORDER.strip().replace("}","""
|
||||
,"productList":[{"productSku":"SKU","qty":2.123456},{"productSku":"ZERO","qty":0},
|
||||
{"productSku":"UNKNOWN"},{"qty":4}]}""")));
|
||||
var products=client().queryPage(query(1,1),context()).records().get(0).productList();
|
||||
assertThat(products).hasSize(4);
|
||||
assertThat(products.get(0).qty()).isEqualByComparingTo("2.123456");
|
||||
assertThat(products.get(1).qty()).isEqualByComparingTo("0");
|
||||
assertThat(products.get(2).qty()).isNull();assertThat(products.get(3).productSku()).isNull();
|
||||
assertThat(calls.get()).isEqualTo(1);
|
||||
}
|
||||
@Test void cancelledLocateOmitsTimeAndStatusAndRejectsFuzzyMatch() throws Exception {
|
||||
response.set(page("current", "size", 1, 1, 1, 1, ORDER.replace("100", "99").replace("2026-10-01 01:00:00", "")));
|
||||
var observer = org.mockito.Mockito.mock(LingxingQuantityCapture.class);
|
||||
|
||||
Reference in New Issue
Block a user