feat(selfop): reconstruct historical inventory and publish qualified turnover
This commit is contained in:
+9
@@ -87,6 +87,15 @@ public class LingxingInternalController {
|
||||
tokenGuard.verify(token);return ApiResponse.success(client.queryInventoryFlow(request,context(context)));
|
||||
}
|
||||
|
||||
@Operation(summary = "读取固定单仓最多一天的全部货主品质库存流水单页")
|
||||
@PostMapping("/inventory-flow-page/query")
|
||||
public ApiResponse<QueryPage<InventoryFlow>> inventoryFlowPage(
|
||||
@RequestHeader(value="X-Internal-Service-Token",required=false) String token,
|
||||
@RequestHeader(value="X-Lingxing-Egress-Context",required=false) String context,
|
||||
@Valid @RequestBody InventoryFlowPageRequest request) {
|
||||
tokenGuard.verify(token);return ApiResponse.success(client.queryInventoryFlowPage(request,context(context)));
|
||||
}
|
||||
|
||||
@Operation(summary = "读取单仓全部货主、全部品质且保留零库存的产品库存单页")
|
||||
@PostMapping("/product-inventory/query")
|
||||
public ApiResponse<QueryPage<ProductStock>> inventory(
|
||||
|
||||
+4
-1
@@ -23,9 +23,12 @@ public final class LingxingWmsDtos {
|
||||
public record InventoryFlowRequest(@NotBlank @Size(max=64) String warehouseCode,
|
||||
@NotBlank @Size(max=64) String customerCode, @NotBlank @Size(max=128) String sku,
|
||||
@NotNull LocalDateTime startTime, @NotNull LocalDateTime endTime) { }
|
||||
public record InventoryFlowPageRequest(@NotBlank @Size(max=64) String warehouseCode,
|
||||
@NotNull LocalDateTime startTime,@NotNull LocalDateTime endTime,
|
||||
@Min(1) int current,@Min(1) @Max(1000) int size) { }
|
||||
public record InventoryFlow(String id, String customerCode, String whCode, String productSku,
|
||||
String cellNo, String innerBatchNo, String billNo, String billType, Integer productQuality,
|
||||
BigDecimal bookQty, String bookChangQty, BigDecimal closeQty, String createTime) { }
|
||||
BigDecimal bookQty, String bookChangQty, BigDecimal closeQty, String createTime, String sourceNo) { }
|
||||
public record Customer(String customerCode, String customerName, String status) { }
|
||||
public record Product(String customerCode, String sku, String productName,
|
||||
BigDecimal length, BigDecimal width, BigDecimal height, String sizeUnit,
|
||||
|
||||
+28
-8
@@ -173,16 +173,35 @@ public final class LingxingWmsClient {
|
||||
body.put("barcodeAccurate",request.sku());body.put("startTime",SOURCE_TIME.format(request.startTime()));
|
||||
body.put("endTime",SOURCE_TIME.format(request.endTime()));body.put("sortName","createTime");body.put("sortOrder","asc");
|
||||
return execute("/openapi/v2/stock/stockCellBook/page",body,context,Operation.INVENTORY_FLOW,
|
||||
(data,metadata)->mappedPage(data,1,1000,"current","size",false,metadata,row->{
|
||||
(data,metadata)->mappedPage(data,1,1000,"current","size",true,metadata,row->{
|
||||
if(!request.warehouseCode().equals(requiredText(row,"whCode",64))
|
||||
||!request.customerCode().equals(requiredText(row,"customerCode",64))
|
||||
||!request.sku().equals(requiredText(row,"productSku",128)))throw invalidResponse("FLOW_IDENTITY_MISMATCH",200);
|
||||
JsonNode id=row.get("id");
|
||||
if(id==null||!id.isIntegralNumber())throw invalidResponse("FLOW_ID_MISSING",200);
|
||||
return new InventoryFlow(id.asText(),request.customerCode(),request.warehouseCode(),request.sku(),
|
||||
String id=requiredText(row,"id",64);
|
||||
return new InventoryFlow(id,request.customerCode(),request.warehouseCode(),request.sku(),
|
||||
optionalText(row,"cellNo"),optionalText(row,"innerBatchNo"),optionalText(row,"billNo"),optionalText(row,"billType"),
|
||||
requiredInt(row.get("productQuality"),"PRODUCT_QUALITY"),quantity(row.get("bookQty")),
|
||||
optionalText(row,"bookChangQty"),quantity(row.get("closeQty")),optionalText(row,"createTime"));
|
||||
optionalText(row,"bookChangQty"),quantity(row.get("closeQty")),optionalText(row,"createTime"),optionalText(row,"sourceNo"));
|
||||
}));
|
||||
}
|
||||
|
||||
/** One fixed warehouse interval page. Scheduling and coverage belong to Lastleg. */
|
||||
public QueryPage<InventoryFlow> queryInventoryFlowPage(InventoryFlowPageRequest request,EgressContext context) {
|
||||
if(request==null) throw badRequest();
|
||||
requireInput(request.warehouseCode(),64);validatePagination(request.current(),request.size(),1000);
|
||||
if(request.startTime()==null || request.endTime()==null || !request.startTime().isBefore(request.endTime())
|
||||
|| request.endTime().isAfter(request.startTime().plusDays(1))) throw badRequest();
|
||||
Map<String,Object> body=pagination(request.current(),request.size());
|
||||
body.put("whCode",request.warehouseCode());body.put("startTime",SOURCE_TIME.format(request.startTime()));
|
||||
body.put("endTime",SOURCE_TIME.format(request.endTime()));body.put("sortName","createTime");body.put("sortOrder","asc");
|
||||
return execute("/openapi/v2/stock/stockCellBook/page",body,context,Operation.INVENTORY_FLOW,
|
||||
(data,metadata)->mappedPage(data,request.current(),request.size(),"current","size",true,metadata,row->{
|
||||
String warehouse=requiredText(row,"whCode",64);
|
||||
if(!request.warehouseCode().equals(warehouse)) throw invalidResponse("FLOW_IDENTITY_MISMATCH",200);
|
||||
return new InventoryFlow(requiredText(row,"id",64),requiredText(row,"customerCode",64),warehouse,
|
||||
requiredText(row,"productSku",128),optionalText(row,"cellNo"),optionalText(row,"innerBatchNo"),
|
||||
optionalText(row,"billNo"),optionalText(row,"billType"),requiredInt(row.get("productQuality"),"PRODUCT_QUALITY"),
|
||||
quantity(row.get("bookQty")),optionalText(row,"bookChangQty"),quantity(row.get("closeQty")),optionalText(row,"createTime"),optionalText(row,"sourceNo"));
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -343,7 +362,7 @@ public final class LingxingWmsClient {
|
||||
}
|
||||
|
||||
private PageResult orders(JsonNode data, int current, int size, String exactDeliveryNo, AttemptMetadata metadata) {
|
||||
QueryPage<OrderSummary> page = mappedPage(data, current, size, "current", "size", true, metadata, record -> {
|
||||
QueryPage<OrderSummary> page = mappedPage(data, current, size, "current", "size", size == 100, metadata, record -> {
|
||||
String deliveryNo = requiredText(record, "deliveryNo", 128);
|
||||
if (exactDeliveryNo != null && !exactDeliveryNo.equals(deliveryNo)) throw invalidResponse("LOCATE_DELIVERY_NO_MISMATCH", 200);
|
||||
int status = requiredInt(record.get("status"), "STATUS");
|
||||
@@ -367,7 +386,7 @@ public final class LingxingWmsClient {
|
||||
}
|
||||
|
||||
private <T> QueryPage<T> mappedPage(JsonNode data, int current, int size, String currentField,
|
||||
String sizeField, boolean outbound, AttemptMetadata metadata, Function<JsonNode, T> projection) {
|
||||
String sizeField, boolean allowEmptyDefaultSize, AttemptMetadata metadata, Function<JsonNode, T> projection) {
|
||||
JsonNode records = data.get("records");
|
||||
if (records == null || !records.isArray()) throw invalidResponse("RECORDS_TYPE_" + nodeType(records), 200);
|
||||
if (records.size() > size) throw invalidResponse("RECORDS_OVER_PAGE_SIZE", 200);
|
||||
@@ -377,7 +396,8 @@ public final class LingxingWmsClient {
|
||||
int sourceSize = requiredInt(data.get(sizeField), "SIZE");
|
||||
int pages = requiredInt(data.get("pages"), "PAGES");
|
||||
if (sourceCurrent != current) throw invalidResponse("CURRENT_MISMATCH", 200);
|
||||
boolean emptyException = outbound && current == 1 && size == 100 && sourceCurrent == 1
|
||||
// Verified for outbound size 100 and the bounded inventory-flow query only.
|
||||
boolean emptyException = allowEmptyDefaultSize && current == 1 && sourceCurrent == 1
|
||||
&& sourceSize == 10 && total == 0 && pages == 0 && records.isEmpty();
|
||||
if (sourceSize != size && !emptyException) throw invalidResponse("SIZE_MISMATCH", 200);
|
||||
long expectedPages = total / size + (total % size == 0 ? 0 : 1);
|
||||
|
||||
Reference in New Issue
Block a user