test(pda): bind receiving fixtures to exact source container booking
This commit is contained in:
@@ -480,48 +480,56 @@ export async function uploadPhotos(context, token) {
|
||||
return photos
|
||||
}
|
||||
|
||||
export async function awaitProducerConsumption(context, producers) {
|
||||
const expected = [...new Set(producers.map((item) => id(item?.containerNo)).filter(nonBlank))]
|
||||
export async function awaitProducerConsumption(context, producers, { attempts = 90, delay = ms => new Promise(resolve => setTimeout(resolve, ms)) } = {}) {
|
||||
const expected = [...new Map(producers.map(item => [id(item?.containerBookingId), item])).values()]
|
||||
if (!expected.length) return true
|
||||
if (expected.some(item => !longId(item?.containerBookingId) || !nonBlank(item?.containerNo))) return false
|
||||
const consumed = new Set()
|
||||
const lastResponses = new Map()
|
||||
for (let attempt = 1; attempt <= 90; attempt += 1) {
|
||||
const missing = expected.filter((containerNo) => !consumed.has(containerNo))
|
||||
const results = await Promise.all(missing.map(async (containerNo) => {
|
||||
const response = await context.request('GET', `/api/platform/v1/last-leg/orders?page=1&size=200&containerNo=${encodeURIComponent(containerNo)}`)
|
||||
return { containerNo, response }
|
||||
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
||||
const missing = expected.filter(item => !consumed.has(id(item.containerBookingId)))
|
||||
const results = await Promise.all(missing.map(async (producer) => {
|
||||
const response = await context.request('GET', `/api/platform/v1/last-leg/orders/container-summaries/${encodeURIComponent(producer.containerBookingId)}`)
|
||||
return { producer, response }
|
||||
}))
|
||||
for (const { containerNo, response } of results) {
|
||||
lastResponses.set(containerNo, response)
|
||||
const rows = records(response?.body?.data)
|
||||
if (success(response) && rows.some((item) => id(item?.containerNo) === containerNo)) {
|
||||
consumed.add(containerNo)
|
||||
for (const { producer, response } of results) {
|
||||
const bookingId = id(producer.containerBookingId)
|
||||
lastResponses.set(bookingId, response)
|
||||
const summary = response?.body?.data?.summary
|
||||
if (success(response) && id(summary?.sourceContainerBookingId) === bookingId
|
||||
&& summary?.containerNo === producer.containerNo && Number(summary?.totalOrderCount) > 0) {
|
||||
consumed.add(bookingId)
|
||||
}
|
||||
}
|
||||
if (expected.every((containerNo) => consumed.has(containerNo))) break
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||
if (expected.every(item => consumed.has(id(item.containerBookingId)))) break
|
||||
if (attempt < attempts) await delay(1000)
|
||||
}
|
||||
for (const containerNo of expected) {
|
||||
const response = lastResponses.get(containerNo) || null
|
||||
for (const producer of expected) {
|
||||
const containerNo = producer.containerNo, bookingId = id(producer.containerBookingId)
|
||||
const response = lastResponses.get(bookingId) || null
|
||||
context.addStep(MODULE, `跨区广播消费观察 ${containerNo}`, response,
|
||||
true,
|
||||
`containerNo=${containerNo}, consumed=${consumed.has(containerNo)}; ${trace(response)}`, OBSERVATION_OPTIONS)
|
||||
`containerNo=${containerNo}, sourceContainerBookingId=${bookingId}, consumed=${consumed.has(bookingId)}; ${trace(response)}`, OBSERVATION_OPTIONS)
|
||||
}
|
||||
return expected.every((containerNo) => consumed.has(containerNo))
|
||||
return expected.every(item => consumed.has(id(item.containerBookingId)))
|
||||
}
|
||||
|
||||
export async function convertReceivingContainers(context, producers) {
|
||||
for (const producer of producers) {
|
||||
const containerNo = id(producer?.containerNo)
|
||||
const summaryResponse = await context.request('GET', `/api/platform/v1/last-leg/orders/container-summaries?page=1&size=20&containerNo=${encodeURIComponent(containerNo)}`, undefined, context.token, {})
|
||||
const summaries = success(summaryResponse) && Array.isArray(summaryResponse.body?.data?.records)
|
||||
? summaryResponse.body.data.records : []
|
||||
const summary = summaries.find((item) => id(item?.containerNo) === containerNo)
|
||||
const bookingId = id(producer?.containerBookingId)
|
||||
if (!longId(bookingId) || !nonBlank(containerNo)) {
|
||||
context.addStep(MODULE, '本轮生产柜次身份完整', null, false,
|
||||
`containerNo=${containerNo || 'missing'}, sourceContainerBookingId=${bookingId || 'missing'}`, OPTIONS)
|
||||
return false
|
||||
}
|
||||
const summaryResponse = await context.request('GET', `/api/platform/v1/last-leg/orders/container-summaries/${encodeURIComponent(bookingId)}`, undefined, context.token, {})
|
||||
const summary = success(summaryResponse) ? summaryResponse.body?.data?.summary : null
|
||||
const totalOrders = Number(summary?.totalOrderCount)
|
||||
const inboundCodes = Number(summary?.inboundCodeCount)
|
||||
const existingPlans = Number(summary?.planCount)
|
||||
const pendingPlans = Number(summary?.pendingPlanCount)
|
||||
const convertible = nonBlank(summary?.sourceContainerBookingId)
|
||||
const convertible = id(summary?.sourceContainerBookingId) === bookingId && summary?.containerNo === containerNo
|
||||
&& totalOrders > 0 && inboundCodes === totalOrders
|
||||
&& Number(summary?.missingInboundCodeCount) === 0
|
||||
&& ((existingPlans === 0 && pendingPlans === totalOrders)
|
||||
@@ -530,7 +538,6 @@ export async function convertReceivingContainers(context, producers) {
|
||||
convertible,
|
||||
`sourceContainerBookingId=${id(summary?.sourceContainerBookingId) || 'missing'}, allPlansReady=${summary?.allPlansReady === true}, orders=${totalOrders}, inboundCodes=${inboundCodes}, plans=${existingPlans}, pending=${pendingPlans}; ${trace(summaryResponse)}`, OPTIONS)
|
||||
if (!convertible) return false
|
||||
const bookingId = id(summary.sourceContainerBookingId)
|
||||
const arrivalQueryResponse = await context.request('POST', '/api/platform/v1/last-leg/orders/container-arrival-times/query', { sourceContainerBookingIds: [bookingId] }, context.token, {})
|
||||
const arrivalTimes = success(arrivalQueryResponse) && Array.isArray(arrivalQueryResponse.body?.data)
|
||||
? arrivalQueryResponse.body.data : []
|
||||
@@ -539,7 +546,12 @@ export async function convertReceivingContainers(context, producers) {
|
||||
Boolean(arrivalTime) && Number.isInteger(Number(arrivalTime?.version)),
|
||||
`sourceContainerBookingId=${bookingId}, estimatedWarehouseArrivalAt=${arrivalTime?.estimatedWarehouseArrivalAt || 'missing'}, version=${arrivalTime?.version ?? 'missing'}; ${trace(arrivalQueryResponse)}`,
|
||||
OPTIONS)
|
||||
if (!arrivalTime) return false
|
||||
if (!arrivalTime || !Number.isInteger(Number(arrivalTime.version))) return false
|
||||
if (existingPlans > 0 && !nonBlank(arrivalTime.estimatedWarehouseArrivalAt)) {
|
||||
context.addStep(MODULE, `已生成计划的整柜预计到仓时间不可补写 ${containerNo}`, arrivalQueryResponse,
|
||||
false, `sourceContainerBookingId=${bookingId}, plans=${existingPlans}, estimatedWarehouseArrivalAt=missing`, OPTIONS)
|
||||
return false
|
||||
}
|
||||
if (!nonBlank(arrivalTime.estimatedWarehouseArrivalAt)) {
|
||||
const estimatedWarehouseArrivalAt = new Date(Date.now() + 2 * 86400000).toISOString().slice(0, 19)
|
||||
const arrivalUpdateResponse = await context.request('PUT', `/api/platform/v1/last-leg/orders/container-summaries/${encodeURIComponent(bookingId)}/expected-arrival`, { estimatedWarehouseArrivalAt, expectedVersion: Number(arrivalTime.version),
|
||||
@@ -623,29 +635,35 @@ export async function convertReceivingContainers(context, producers) {
|
||||
return true
|
||||
}
|
||||
|
||||
async function awaitPickupReadyCandidates(context, token, producers, allProducers) {
|
||||
export async function awaitPickupReadyCandidates(context, token, producers, allProducers,
|
||||
{ attempts = 30, delay = ms => new Promise(resolve => setTimeout(resolve, ms)) } = {}) {
|
||||
const expected = [...new Set(producers.map((item) => id(item?.containerNo)).filter(nonBlank))]
|
||||
const expectedSet = new Set(expected)
|
||||
const awaiting = [...new Set(allProducers
|
||||
.filter((item) => item?.shippingReady === false && !expectedSet.has(id(item?.containerNo)))
|
||||
.map((item) => id(item?.containerNo)).filter(nonBlank))]
|
||||
const known = new Set(allProducers.map((item) => id(item?.containerNo)).filter(nonBlank))
|
||||
const isKnownProducer = candidate => allProducers.some(producer => longId(producer?.containerBookingId)
|
||||
&& id(candidate?.sourceContainerBookingId) === id(producer.containerBookingId)
|
||||
&& candidate?.containerNo === producer.containerNo)
|
||||
if (!expected.length) return false
|
||||
let response = null
|
||||
let candidatesByContainer = new Map()
|
||||
for (let attempt = 1; attempt <= 30; attempt += 1) {
|
||||
let unexpectedHistorical = []
|
||||
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
||||
response = await context.request('GET', '/api/pda/v1/last-leg/receiving-containers/candidates', undefined, token, {})
|
||||
const candidateRows = success(response) && Array.isArray(response.body?.data) ? response.body.data : []
|
||||
unexpectedHistorical = candidateRows.filter(item => !isKnownProducer(item))
|
||||
.map(item => `${id(item?.containerNo)}:${id(item?.sourceContainerBookingId)}`)
|
||||
candidatesByContainer = new Map(candidateRows
|
||||
.filter(isKnownProducer)
|
||||
.map((item) => [id(item?.containerNo), item]).filter(([containerNo]) => nonBlank(containerNo)))
|
||||
if (expected.every((containerNo) => candidatesByContainer.get(containerNo)?.operable === true)
|
||||
&& awaiting.every((containerNo) => candidatesByContainer.get(containerNo)?.operable !== true)) break
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||
if (attempt < attempts) await delay(1000)
|
||||
}
|
||||
const seenExpected = expected.filter((containerNo) => candidatesByContainer.has(containerNo))
|
||||
const missingPickupReady = expected.filter((containerNo) => candidatesByContainer.get(containerNo)?.operable !== true)
|
||||
const unexpectedAwaiting = awaiting.filter((containerNo) => candidatesByContainer.get(containerNo)?.operable === true)
|
||||
const unexpectedHistorical = [...candidatesByContainer.keys()].filter((containerNo) => !known.has(containerNo))
|
||||
const targetCorrelated = producers.every((item) => nonBlank(item?.resourceKey)
|
||||
&& item?.shippingReady !== false && candidatesByContainer.get(id(item?.containerNo))?.operable === true)
|
||||
const ready = missingPickupReady.length === 0 && unexpectedAwaiting.length === 0 && targetCorrelated
|
||||
|
||||
Reference in New Issue
Block a user