Files
baiyue-firstleg/integration-service/src/main/resources/openapi/lingxing-wms-v1.yaml
T

647 lines
33 KiB
YAML

openapi: 3.0.3
info:
title: Lingxing WMS Internal Read API
version: 1.0.0
description: >-
Single-page or single-order adapter. No scheduling, business persistence or public Gateway route.
Source times remain uninterpreted strings; warehouse timeZone does not certify source timezone.
Server-side collection mode defaults to OFF. VALIDATION requires an externally confirmed exclusive
account window; BUSINESS requires environmentLabel exactly production or test. TEST business task creation is restricted by Lastleg to controlled commands and their legitimate derived work; the adapter does not schedule work. Provider-query endpoints apply the same gate.
Missing exclusive proof, persistent account budget authorization or control context fails closed before egress.
At most one account request is in flight; request starts obey the bound minStartInterval (VALIDATION at least 5 seconds).
A JVM lock is only local protection and is not proof of cross-environment account exclusivity.
Each invocation performs at most one supplier HTTP attempt. No mode uses implicit HTTP retries;
429 is never retried, immediately pauses the account and returns persistent cooldown metadata.
VALIDATION page size must not exceed 10. Lastleg owns root/session budgets and cooldown persistence.
External server configuration supplies externalExclusiveGrantId and a VALIDATION externalExclusiveUntil;
the nonempty grant must match context.exclusiveGrantId. BUSINESS has no grant, session or root time cutoff;
explicit stops, persistent account protections and each short single-attempt permit remain enforced.
A BUSINESS rate-limit pause is recoverable after both the persistently returned
cooldown and the local minimum fallback cooldown have elapsed, and after the authority issues a new
strictly valid single-attempt permit confirming account budget, cooldown, stop limits and root state.
Time alone never permits egress. VALIDATION 429 does not automatically recover. Unavailable, lost
or malformed authorization and uncertain completion hold an unrecoverable local pause until
operations verify persistent account state. Completion failure never automatically recovers.
A well-formed explicit authorization denial is deterministic and does not cause a permanent pause.
sourceAccount defaults to lingxing-primary and budgetAuthorityBaseUri defaults internally to
http://lastleg-service:9303; neither default enables egress.
x-lastleg-egress-coordination:
description: >-
Integration calls these Lastleg endpoints with X-Internal-Service-Token. These are dependencies,
not Integration Controller paths. Lastleg prepares attemptId server-side and binds context and operation
before authorization. authorize only consumes an already prepared attempt, first binds the opaque
requestHash and atomically consumes persistent account/root/session budget. Duplicate authorization
is refused; arbitrary client-created attempts are refused. complete verifies the same bindings.
A permit is bound to its full context, operation and requestHash, requires exclusiveConfirmed=true
and budgetConsumed=true, expires after now but no later than now plus 30 seconds (also within externalExclusiveUntil for VALIDATION),
and cannot be reused. Explicit denial uses HTTP 200 with ApiResponse code string B0001 and
AuthorizationDenial data. A valid denial is not an uncertain authorization and does not impose
a permanent local pause. Unavailable, lost or malformed authorization or an unconfirmed callback blocks further egress;
pending or ambiguous attempts remain conservatively consumed, including after restart.
authorize:
method: POST
path: /internal/lastleg/self-operated-analytics/egress/authorize
requestSchema: '#/components/schemas/AuthorizationRequest'
responseSchema: '#/components/schemas/AttemptPermitResponse'
complete:
method: POST
path: /internal/lastleg/self-operated-analytics/egress/complete
requestSchema: '#/components/schemas/CompletionRequest'
responseSchema: '#/components/schemas/CompletionResponse'
security:
- InternalServiceToken: []
paths:
/internal/integrations/lingxing/wms/inventory-flow-page/query:
post:
summary: One frozen warehouse inventory-flow page
description: >-
Shared INVENTORY_FLOW permit, one upstream request, no automatic retry. Fixed warehouse and
inclusive Beijing start/end bounds at most one day, current>=1, size 1..1000, sorted by createTime asc.
Customer/SKU filters are absent. Lastleg validates persisted task/page binding, page counts,
duplicate IDs and ordering before admitting coverage. Only quantity/identity whitelist fields return.
Empty first-page current=1,size=10,total=0,pages=0,records=[] is normalized to the requested size.
parameters:
- {$ref: '#/components/parameters/LingxingEgressContext'}
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required: [warehouseCode, startTime, endTime, current, size]
properties:
warehouseCode: {type: string, minLength: 1, maxLength: 64}
startTime: {type: string, format: date-time, description: Beijing local datetime without offset}
endTime: {type: string, format: date-time, description: Inclusive bound, at most one day after start}
current: {type: integer, minimum: 1}
size: {type: integer, minimum: 1, maximum: 1000}
responses:
'200':
description: Envelope of QueryPage with current/size/total/pages/records and source pagination evidence, or sanitized ProviderFailure. Rows retain string id, customerCode, whCode, productSku, cellNo, innerBatchNo, billNo, sourceNo, billType, productQuality, numeric bookQty/closeQty and string bookChangQty/createTime.
/internal/integrations/lingxing/wms/inventory-flow/query:
post:
summary: One SKU inventory-flow diagnostic page
description: >-
Calls stock/stockCellBook/page through the existing INVENTORY_FLOW permit. Exact warehouse/customer/SKU,
at most 31 days, current=1 and size=1000 fixed. No automatic next page or retry. Source quality and signed
quantities are preserved, not translated to inventory-snapshot quality. Source 11008 only rejects this optional
operation; authentication, rate-limit and uncertain-result account protection remain effective.
Source movement IDs must be strings. An empty first response with current=1, size=10, total=0,
pages=0 and records=[] is normalized to requested size=1000 while retaining sourceSize=10.
Other pagination mismatches remain errors.
parameters:
- {$ref: '#/components/parameters/LingxingEgressContext'}
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [warehouseCode, customerCode, sku, startTime, endTime]
properties:
warehouseCode: {type: string, maxLength: 64}
customerCode: {type: string, maxLength: 64}
sku: {type: string, maxLength: 128}
startTime: {type: string, format: date-time, description: Beijing local datetime without offset}
endTime: {type: string, format: date-time, description: Beijing local datetime without offset}
responses:
'200':
description: Standard envelope with QueryPage or sanitized ProviderFailure. Flow rows retain id as string, customerCode, whCode, productSku, cellNo, innerBatchNo, billNo, sourceNo, billType, productQuality, numeric bookQty/closeQty, string bookChangQty/createTime. Unknown source fields remain null; no calculated inventory is published.
/internal/integrations/lingxing/wms/customers/query:
post:
summary: Query one page of customers using the existing account egress permit
description: Calls /openapi/v2/customer/list. BUSINESS MASTER_DATA tasks only; no retries or separate egress path. Dimensions and units remain separate; blanks are null.
parameters:
- {$ref: '#/components/parameters/LingxingEgressContext'}
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/WarehouseQueryRequest'}
responses:
'200':
description: Standard envelope with one page or sanitized ProviderFailure.
content:
application/json:
schema: {$ref: '#/components/schemas/CustomerResponse'}
/internal/integrations/lingxing/wms/products/query:
post:
summary: Query one page of products using the existing account egress permit
description: Calls /openapi/v2/product/page. BUSINESS MASTER_DATA tasks only; no retries or separate egress path. Dimensions and units remain separate; blanks are null.
parameters:
- {$ref: '#/components/parameters/LingxingEgressContext'}
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/ProductQueryRequest'}
responses:
'200':
description: Standard envelope with one page or sanitized ProviderFailure.
content:
application/json:
schema: {$ref: '#/components/schemas/ProductResponse'}
/internal/integrations/lingxing/wms/egress/runtime:
get:
operationId: lingxingEgressRuntime
summary: Read sanitized configuration from the active egress gate
description: >-
Internal token authentication only. No provider, database, budget-authority, permit or persistence side effect.
Returns actual bound configuration, not a verified claim or a commit identity. Fingerprints use lowercase
SHA-256 of UTF-8 "selfop-runtime:v1:<domain>\n" plus the exact original nonblank value, without trimming.
Domains are source-account, grant and budget-authority. Missing/blank values yield null, never an empty-value hash.
security: [{InternalServiceToken: []}]
responses:
'200':
description: ApiResponse code 200 with runtime snapshot, or A0401 for missing/invalid internal token
content:
application/json:
schema:
type: object
properties:
code: {type: string}
data: {$ref: '#/components/schemas/EgressRuntimeSnapshot'}
/internal/integrations/lingxing/wms/warehouses/query:
post:
summary: Query one page of normal warehouses
parameters:
- {$ref: '#/components/parameters/LingxingEgressContext'}
description: Calls /openapi/v2/warehouse/page without filters. Maps page/pageSize into current/size; preserves original metadata.
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/WarehouseQueryRequest'}
responses:
'200':
description: Standard envelope with WarehousePage or sanitized ProviderFailure.
content:
application/json:
schema: {$ref: '#/components/schemas/WarehouseResponse'}
/internal/integrations/lingxing/wms/outbound-orders/query:
post:
summary: Query one page of completed outbound orders by source outbound time
parameters:
- {$ref: '#/components/parameters/LingxingEgressContext'}
description: Calls /openapi/v2/order/delivery/page with timeType=outboundTime and status=100. No external warehouse filter. At current=1,size=100 only, records=[],total=0,pages=0,sourceCurrent=1,sourceSize=10 is accepted and internal size remains 100. All other pagination metadata is strictly required. No completeness claim across pages.
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/QueryRequest'}
responses:
'200':
description: Standard envelope with OrderPage or sanitized ProviderFailure.
content:
application/json:
schema: {$ref: '#/components/schemas/OrderResponse'}
/internal/integrations/lingxing/wms/outbound-orders/locate:
post:
summary: Locate one page by delivery number without time or status filters
parameters:
- {$ref: '#/components/parameters/LingxingEgressContext'}
description: Sends only current,size,deliveryNo to delivery/page. Every returned deliveryNo must match exactly. Caller reads all required pages and verifies original customer identity and uniqueness before accepting the current warehouse. Missing results do not prove cancellation.
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/LocateRequest'}
responses:
'200':
description: Standard envelope with OrderPage or sanitized ProviderFailure.
content:
application/json:
schema: {$ref: '#/components/schemas/OrderResponse'}
/internal/integrations/lingxing/wms/outbound-orders/detail:
post:
summary: Read one order using warehouse, customer and delivery identifiers
parameters:
- {$ref: '#/components/parameters/LingxingEgressContext'}
description: Maps warehouseCode to whCode for delivery/detail. All three returned identifiers must match. Returns only skuVOList; packageList is never added to quantities. Duplicate SKU lines remain separate for caller normalization. Missing quantities remain null; no quantity unit is inferred.
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/DetailRequest'}
responses:
'200':
description: Standard envelope with OrderDetail or sanitized ProviderFailure.
content:
application/json:
schema: {$ref: '#/components/schemas/DetailResponse'}
/internal/integrations/lingxing/wms/product-inventory/query:
post:
summary: Query one page of all qualities and customers in a required warehouse
parameters:
- {$ref: '#/components/parameters/LingxingEgressContext'}
description: Calls stock/stockSku/page with whCode and isHideInventory=0. No SKU, quality, customer or quantity filters. Warehouse mismatch and duplicate business keys within a page fail closed. Caller validates cross-page keys and counts. Unknown quantities stay null, negative quantities remain evidence, stockCompare is not accumulated.
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/InventoryQueryRequest'}
responses:
'200':
description: Standard envelope with InventoryPage or sanitized ProviderFailure.
content:
application/json:
schema: {$ref: '#/components/schemas/InventoryResponse'}
components:
parameters:
LingxingEgressContext:
name: X-Lingxing-Egress-Context
in: header
required: true
description: >-
JSON control context; no credentials. Maximum 2048 characters, all printable ASCII (32 through 126).
Unknown fields, duplicate fields and trailing JSON are rejected. Identifiers are strings supplied
by the server-side coordinator and do not confer authorization by themselves.
content:
application/json:
schema: {$ref: '#/components/schemas/EgressContext'}
securitySchemes:
InternalServiceToken:
type: apiKey
in: header
name: X-Internal-Service-Token
schemas:
CustomerPage:
allOf:
- {$ref: '#/components/schemas/Pagination'}
- type: object
required: [records]
properties:
records: {type: array, items: {$ref: '#/components/schemas/Customer'}}
CustomerResponse:
allOf:
- {$ref: '#/components/schemas/Envelope'}
- type: object
properties:
data:
nullable: true
oneOf:
- {$ref: '#/components/schemas/CustomerPage'}
- {$ref: '#/components/schemas/ProviderFailure'}
ProductPage:
allOf:
- {$ref: '#/components/schemas/Pagination'}
- type: object
required: [records]
properties:
records: {type: array, items: {$ref: '#/components/schemas/Product'}}
ProductResponse:
allOf:
- {$ref: '#/components/schemas/Envelope'}
- type: object
properties:
data:
nullable: true
oneOf:
- {$ref: '#/components/schemas/ProductPage'}
- {$ref: '#/components/schemas/ProviderFailure'}
ProductQueryRequest:
type: object
required: [current, size]
properties:
current: {type: integer, minimum: 1}
size: {type: integer, minimum: 1, maximum: 100}
Customer:
type: object
required: [customerCode]
properties:
customerCode: {type: string}
customerName: {type: string, nullable: true}
status: {type: string, nullable: true}
Product:
type: object
required: [customerCode, sku]
properties:
customerCode: {type: string}
sku: {type: string}
productName: {type: string, nullable: true}
length: {type: number, nullable: true}
width: {type: number, nullable: true}
height: {type: number, nullable: true}
wmsLength: {type: number, nullable: true}
wmsWidth: {type: number, nullable: true}
wmsHeight: {type: number, nullable: true}
sizeUnit: {type: string, nullable: true}
wmsSizeUnit: {type: string, nullable: true}
EgressRuntimeSnapshot:
type: object
additionalProperties: false
required: [schemaVersion, service, observedAt, mode, environment, continuousEnabled, grantPresent, intervalMs, permitMaxSeconds, providerEnabled]
properties:
schemaVersion: {type: integer, enum: [1]}
service: {type: string, enum: [integration]}
observedAt: {type: string, format: date-time}
mode: {type: string, enum: [OFF, VALIDATION, BUSINESS]}
environment: {type: string}
continuousEnabled: {type: boolean}
sourceAccountFingerprint: {type: string, nullable: true, pattern: '^[a-f0-9]{64}$'}
grantPresent: {type: boolean}
grantFingerprint: {type: string, nullable: true, pattern: '^[a-f0-9]{64}$'}
intervalMs:
oneOf: [{type: integer, format: int64}, {type: string, pattern: '^[0-9]+$'}]
description: Actual bound interval; shared long-value JSON serialization may encode it as a decimal string.
permitMaxSeconds: {type: integer, enum: [30]}
providerEnabled: {type: boolean}
budgetAuthorityFingerprint: {type: string, nullable: true, pattern: '^[a-f0-9]{64}$'}
EgressContext:
type: object
additionalProperties: false
required: [sourceAccount, executionPurpose, rootTaskId, sessionId, attemptId, exclusiveGrantId]
properties:
sourceAccount: {type: string, minLength: 1, maxLength: 64, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]*$', description: 'Server-configured account alias, never App Key or Secret.'}
executionPurpose: {type: string, enum: [VALIDATION, BUSINESS]}
rootTaskId: {type: string, minLength: 1, maxLength: 128, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]*$', description: Stable root control identifier; child tasks share root budget.}
sessionId: {type: string, minLength: 1, maxLength: 128, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]*$', description: Stable session control identifier; reopening or restart does not reset it.}
attemptId: {type: string, minLength: 1, maxLength: 128, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]*$', description: Prepared unique single-attempt control identifier; duplicate authorization is refused.}
exclusiveGrantId: {type: string, minLength: 1, maxLength: 128, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]*$', description: Must equal the valid externalExclusiveGrantId configured server-side.}
EgressRequestHash:
type: string
pattern: '^[0-9a-f]{64}$'
description: >-
Lowercase hexadecimal SHA256 of UTF-8 bytes of OMP path, one LF character and compact
insertion-ordered external business JSON. Lastleg treats this digest as opaque and binds
it on the first authorization of an already prepared attempt.
example: fa60a92c78b4803878930e5914e6db03591acc31b7c09abad53c91912fa3d627
x-preimage-example: |-
/openapi/v2/stock/stockSku/page
{"current":1,"size":2,"whCode":"WH-DEMO","isHideInventory":0}
EgressOperation:
type: string
enum: [WAREHOUSES, OUTBOUND_QUERY, OUTBOUND_LOCATE, OUTBOUND_DETAIL, PRODUCT_INVENTORY, CUSTOMERS, PRODUCTS, INVENTORY_FLOW]
EgressMetadata:
type: object
required: [attemptId, actualHttpAttempts, accountPaused]
properties:
attemptId: {type: string, nullable: true, description: Null only if no valid control context was accepted.}
actualHttpAttempts: {type: integer, minimum: 0, maximum: 1, description: 'Supplier HTTP attempts actually started, including failed calls; authorization callbacks do not count.'}
accountPaused: {type: boolean}
cooldownUntil: {type: string, format: date-time, nullable: true, description: Account cooldown deadline to persist; valid supplier retry time must not be shortened.}
AuthorizationRequest:
type: object
required: [context, operation, requestHash]
properties:
context: {$ref: '#/components/schemas/EgressContext'}
operation: {$ref: '#/components/schemas/EgressOperation'}
requestHash: {$ref: '#/components/schemas/EgressRequestHash'}
AuthorizationDenial:
type: object
required: [errorCode, sampleDone]
properties:
errorCode: {type: string, pattern: '^[A-Z][A-Z0-9_]{0,63}$'}
sampleDone: {type: boolean, description: Strict JSON boolean; validation budget normal completion is distinct from a failure.}
waitUntil: {type: string, format: date-time, nullable: true, description: Nullable ISO-8601 Instant from the persistent authority.}
AuthorizationDenialResponse:
allOf:
- {$ref: '#/components/schemas/Envelope'}
- type: object
properties:
code: {type: string, enum: [B0001]}
data: {$ref: '#/components/schemas/AuthorizationDenial'}
AttemptPermit:
type: object
required: [context, operation, requestHash, allowedAttempts, expiresAt, exclusiveConfirmed, budgetConsumed]
properties:
context: {$ref: '#/components/schemas/EgressContext'}
operation: {$ref: '#/components/schemas/EgressOperation'}
requestHash: {$ref: '#/components/schemas/EgressRequestHash'}
allowedAttempts: {type: integer, enum: [1]}
expiresAt: {type: string, format: date-time, description: Must be after now and no later than now plus 30 seconds and configured externalExclusiveUntil.}
exclusiveConfirmed: {type: boolean, enum: [true]}
budgetConsumed: {type: boolean, enum: [true], description: Lastleg has atomically consumed persistent account and root/session budget before returning this permit.}
CompletionRequest:
type: object
required: [context, operation, requestHash, actualHttpAttempts, accountPaused]
properties:
context: {$ref: '#/components/schemas/EgressContext'}
operation: {$ref: '#/components/schemas/EgressOperation'}
requestHash: {$ref: '#/components/schemas/EgressRequestHash'}
actualHttpAttempts: {type: integer, minimum: 0, maximum: 1}
errorCode: {type: string, nullable: true, description: Sanitized local error code; null on success.}
upstreamStatus: {type: integer, nullable: true}
sourceCode: {type: integer, nullable: true}
accountPaused: {type: boolean, description: True on 429 or explicit provider rate limiting; all account request families must pause.}
cooldownUntil: {type: string, format: date-time, nullable: true}
AttemptPermitResponse:
allOf:
- {$ref: '#/components/schemas/Envelope'}
- type: object
properties:
code: {type: string, enum: ['200', B0001]}
data:
oneOf:
- {$ref: '#/components/schemas/AttemptPermit'}
- {$ref: '#/components/schemas/AuthorizationDenial'}
description: Code 200 requires AttemptPermit; code B0001 requires AuthorizationDenial. Lost or malformed responses are uncertain and fail closed with a local pause.
CompletionResponse:
allOf:
- {$ref: '#/components/schemas/Envelope'}
- type: object
properties:
data: {type: boolean, enum: [true], description: Successful acknowledgement confirms durable completion and cooldown persistence; any other result fails closed.}
WarehouseQueryRequest:
type: object
required: [current, size]
properties:
current: {type: integer, minimum: 1}
size: {type: integer, minimum: 1, maximum: 1000}
QueryRequest:
type: object
required: [startTime, endTime, current, size]
properties:
startTime: {type: string, example: '2026-09-12T08:00:00', description: Source local datetime. Must precede endTime and be no earlier than endTime minus one calendar month. No source timezone inference.}
endTime: {type: string, example: '2026-10-12T08:00:00'}
current: {type: integer, minimum: 1}
size: {type: integer, minimum: 1, maximum: 100, description: 'Local request cap, not supplier quota.'}
LocateRequest:
type: object
required: [deliveryNo, current, size]
properties:
deliveryNo: {type: string, minLength: 1, maxLength: 128}
current: {type: integer, minimum: 1}
size: {type: integer, minimum: 1, maximum: 1000}
DetailRequest:
type: object
required: [warehouseCode, customerCode, deliveryNo]
properties:
warehouseCode: {type: string, minLength: 1, maxLength: 64}
customerCode: {type: string, minLength: 1, maxLength: 64}
deliveryNo: {type: string, minLength: 1, maxLength: 128}
InventoryQueryRequest:
type: object
required: [warehouseCode, current, size]
properties:
warehouseCode: {type: string, minLength: 1, maxLength: 64}
current: {type: integer, minimum: 1}
size: {type: integer, minimum: 1, maximum: 1000}
Pagination:
type: object
required: [total, current, size, pages, sourceCurrent, sourceSize, sourceCode, observedAt, egress]
properties:
egress: {$ref: '#/components/schemas/EgressMetadata'}
total: {type: string, pattern: '^[0-9]+$', description: Required provider total. Long follows platform string serialization.}
current: {type: integer}
size: {type: integer}
pages: {type: integer, minimum: 0}
sourceCurrent: {type: integer}
sourceSize: {type: integer}
sourceCode: {type: integer, enum: [200]}
observedAt: {type: string, format: date-time}
OrderSummary:
type: object
required: [deliveryNo, customerCode, whCode, status]
properties:
deliveryNo: {type: string}
sourceNo: {type: string, nullable: true}
customerCode: {type: string}
whCode: {type: string}
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]
properties:
whCode: {type: string}
whNameCn: {type: string, nullable: true}
warehouseType: {type: integer}
status: {type: integer}
timeZone: {type: string, nullable: true, description: Warehouse metadata only.}
SkuLine:
type: object
required: [productSku]
properties:
productSku: {type: string}
productName: {type: string, nullable: true}
actualDeliveryQty: {type: number, nullable: true, description: 'BigDecimal actual quantity, unknown is null.'}
deliveryQty: {type: number, nullable: true, description: Forecast only; never a replacement for actual quantity.}
OrderDetail:
type: object
required: [deliveryNo, customerCode, whCode, status, skuVOList, sourceCode, observedAt, egress]
properties:
egress: {$ref: '#/components/schemas/EgressMetadata'}
deliveryNo: {type: string}
sourceNo: {type: string, nullable: true}
customerCode: {type: string}
whCode: {type: string}
status: {type: integer}
orderCreateTime: {type: string, nullable: true}
outboundTime: {type: string, nullable: true}
skuVOList: {type: array, minItems: 1, items: {$ref: '#/components/schemas/SkuLine'}}
sourceCode: {type: integer, enum: [200]}
observedAt: {type: string, format: date-time}
ProductStock:
type: object
required: [customerCode, whCode, productSku, productQuality]
properties:
customerCode: {type: string}
whCode: {type: string}
productSku: {type: string}
productName: {type: string, nullable: true}
productQuality: {type: integer}
stkQty: {type: number, nullable: true}
freezeQty: {type: number, nullable: true}
useStkQty: {type: number, nullable: true}
transitQty: {type: number, nullable: true}
innerBatchNo: {type: string, nullable: true}
outBatchNo: {type: string, nullable: true}
uniqueKey4tenant: {type: string, nullable: true}
OrderPage:
allOf:
- {$ref: '#/components/schemas/Pagination'}
- type: object
required: [records]
properties:
records: {type: array, items: {$ref: '#/components/schemas/OrderSummary'}}
WarehousePage:
allOf:
- {$ref: '#/components/schemas/Pagination'}
- type: object
required: [records]
properties:
records: {type: array, items: {$ref: '#/components/schemas/Warehouse'}}
InventoryPage:
allOf:
- {$ref: '#/components/schemas/Pagination'}
- type: object
required: [records]
properties:
records: {type: array, items: {$ref: '#/components/schemas/ProductStock'}}
ProviderFailure:
type: object
required: [errorCode, retryable, egress]
properties:
egress: {$ref: '#/components/schemas/EgressMetadata'}
denial:
nullable: true
allOf:
- {$ref: '#/components/schemas/AuthorizationDenial'}
description: Well-formed deterministic authority denial, alongside egress; null for other failures.
errorCode: {type: string}
retryable: {type: boolean, description: Caller scheduling hint only; never authorizes implicit retries or reuse of an attempt. False for 429 and validation session failure.}
upstreamStatus: {type: integer, nullable: true}
sourceCode: {type: integer, nullable: true}
Envelope:
type: object
required: [code, message, data, timestamp]
properties:
code: {type: string, description: '200 success; A0401 identity; A0422 validation; B0001 or S0503 provider/configuration failure.'}
message: {type: string, description: 'Sanitized fixed local message, never raw supplier content.'}
requestId: {type: string, nullable: true}
traceId: {type: string, nullable: true}
timestamp: {type: string, format: date-time}
OrderResponse:
allOf:
- {$ref: '#/components/schemas/Envelope'}
- type: object
properties:
data:
nullable: true
oneOf:
- {$ref: '#/components/schemas/OrderPage'}
- {$ref: '#/components/schemas/ProviderFailure'}
WarehouseResponse:
allOf:
- {$ref: '#/components/schemas/Envelope'}
- type: object
properties:
data:
nullable: true
oneOf:
- {$ref: '#/components/schemas/WarehousePage'}
- {$ref: '#/components/schemas/ProviderFailure'}
DetailResponse:
allOf:
- {$ref: '#/components/schemas/Envelope'}
- type: object
properties:
data:
nullable: true
oneOf:
- {$ref: '#/components/schemas/OrderDetail'}
- {$ref: '#/components/schemas/ProviderFailure'}
InventoryResponse:
allOf:
- {$ref: '#/components/schemas/Envelope'}
- type: object
properties:
data:
nullable: true
oneOf:
- {$ref: '#/components/schemas/InventoryPage'}
- {$ref: '#/components/schemas/ProviderFailure'}