feat(selfop): reconstruct historical inventory and publish qualified turnover

This commit is contained in:
2026-10-15 13:30:37 +08:00
parent d81d9cbda4
commit 99215bbb79
35 changed files with 1652 additions and 106 deletions
@@ -22,6 +22,19 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
/** Synthetic loopback transport only; no supplier or application services. */
class LingxingWmsClientTest {
@Test void warehouseFlowPageKeepsScopeAndUsesSharedPermit() throws Exception {
response.set(page("current","size",2,1,2,2,"{\"id\":\"F2\",\"productQuality\":0,\"customerCode\":\"C\",\"whCode\":\"W\",\"productSku\":\"SKU\",\"sourceNo\":\"SOURCE\",\"bookChangQty\":\"-1\"}"));
var request=new InventoryFlowPageRequest("W",LocalDateTime.parse("2026-10-01T00:00:00"),LocalDateTime.parse("2026-10-02T00:00:00"),2,1);
var result=client().queryInventoryFlowPage(request,context());
assertThat(result.records().get(0).sourceNo()).isEqualTo("SOURCE");
var business=mapper.readTree(body.get()).path("data");
assertThat(business.path("whCode").asText()).isEqualTo("W");
assertThat(business.has("customerCode")).isFalse();assertThat(business.has("productSku")).isFalse();
assertThat(business.path("current").asInt()).isEqualTo(2);
assertThat(completion.get().operation()).isEqualTo(Operation.INVENTORY_FLOW);
assertThrows(LingxingWmsException.class,()->client().queryInventoryFlowPage(new InventoryFlowPageRequest("W",request.startTime(),request.endTime().plusSeconds(1),1,1000),context()));
assertThat(calls.get()).isEqualTo(1);
}
@Test void customerDirectoryKeepsNamesAndUsesAccountGate() {
response.set(page("page","pageSize",1,1000,1,1,"{\"customerCode\":\"C\",\"customerName\":\"示例货主\",\"status\":\"0\",\"telephoneNo\":\"not-forwarded\"}"));
var result=client().queryCustomers(new WarehouseQueryRequest(1,1000),context());
@@ -315,8 +328,8 @@ class LingxingWmsClientTest {
LocalDateTime.of(2026,9,14,8,0),LocalDateTime.of(2026,10,14,8,0));}
@Test void flowUsesExactScopeSinglePageAndPreservesSignedChangeAndLargeId() throws Exception {
response.set(page("current","size",1,1000,1,1,"""
{"id":9223372036854775806,"customerCode":"C-DEMO","whCode":"W-DEMO","productSku":"SKU-DEMO",
"productQuality":1,"bookQty":-3,"bookChangQty":"-3","closeQty":17,"createTime":"2026-10-13 12:00:00"}
{"id":"9223372036854775806","customerCode":"C-DEMO","whCode":"W-DEMO","productSku":"SKU-DEMO",
"productQuality":0,"bookQty":-3,"bookChangQty":"-3","closeQty":17,"createTime":"2026-10-13 12:00:00"}
"""));
var result=client().queryInventoryFlow(flow(),context());
assertThat(path.get()).isEqualTo("/openapi/v2/stock/stockCellBook/page");
@@ -324,10 +337,45 @@ class LingxingWmsClientTest {
assertThat(sent.path("barcodeAccurate").asText()).isEqualTo("SKU-DEMO");
assertThat(sent.path("size").asInt()).isEqualTo(1000);assertThat(sent.has("productQuality")).isFalse();
assertThat(result.records().get(0).id()).isEqualTo("9223372036854775806");
assertThat(result.records().get(0).productQuality()).isZero();
assertThat(result.records().get(0).bookQty()).isEqualByComparingTo("-3");
assertThat(result.records().get(0).bookChangQty()).isEqualTo("-3");assertThat(calls.get()).isEqualTo(1);
assertThat(completion.get().operation()).isEqualTo(Operation.INVENTORY_FLOW);
}
@Test void flowRejectsNumericIdInsteadOfCoercingUnverifiedSourceTypes() {
response.set(page("current","size",1,1000,1,1,"""
{"id":123,"customerCode":"C-DEMO","whCode":"W-DEMO","productSku":"SKU-DEMO",
"productQuality":0,"bookQty":-1,"bookChangQty":"-1","closeQty":9}
"""));
contract(()->client().queryInventoryFlow(flow(),context()),"TEXT_FIELD_TYPE_NUMBER");
}
@Test void flowAcceptsVerifiedEmptyFirstPageAndPreservesSourcePagination() {
response.set(page("current","size",1,10,0,0,""));
var result=client().queryInventoryFlow(flow(),context());
assertThat(result.records()).isEmpty();
assertThat(result.total()).isZero();
assertThat(result.current()).isEqualTo(1);
assertThat(result.size()).isEqualTo(1000);
assertThat(result.sourceSize()).isEqualTo(10);
assertThat(calls.get()).isEqualTo(1);
assertThat(completion.get().operation()).isEqualTo(Operation.INVENTORY_FLOW);
}
@Test void flowEmptyExceptionDoesNotAcceptNonemptyOrMalformedPagination() {
var c=client();
response.set(page("current","size",1,10,1,1,"{}"));
contract(()->c.queryInventoryFlow(flow(),context()),"SIZE_MISMATCH");
response.set(page("current","size",2,10,0,0,""));
contract(()->c.queryInventoryFlow(flow(),context()),"CURRENT_MISMATCH");
response.set(page("current","size",1,10,0,1,""));
contract(()->c.queryInventoryFlow(flow(),context()),"SIZE_MISMATCH");
response.set(page("current","size",1,20,0,0,""));
contract(()->c.queryInventoryFlow(flow(),context()),"SIZE_MISMATCH");
}
@Test void flowEmptyExceptionDoesNotRelaxInventoryOrOtherOutboundSizes() {
var c=client();response.set(page("current","size",1,10,0,0,""));
contract(()->c.queryInventory(new InventoryQueryRequest("W-DEMO",1,1000),context()),"SIZE_MISMATCH");
contract(()->c.queryPage(query(1,20),context()),"SIZE_MISMATCH");
}
@Test void flowPermissionDenialIsScopedAndDoesNotStopOrdinaryQueries() {
var c=client();response.set("{\"code\":11008,\"msg\":\"private\"}");
var error=assertThrows(LingxingWmsException.class,()->c.queryInventoryFlow(flow(),context()));