diff --git a/src/modules/parcel-sync/services/epay-client.ts b/src/modules/parcel-sync/services/epay-client.ts index 3cf98cb..7d23238 100644 --- a/src/modules/parcel-sync/services/epay-client.ts +++ b/src/modules/parcel-sync/services/epay-client.ts @@ -233,9 +233,11 @@ export class EpayClient { const data = response.data as EpayCartResponse; const items = Array.isArray(data?.items) ? data.items : []; - // The freshly added row is the one we didn't know about; ePay returns - // the full cart in `items`, newest typically last. Be defensive. - const added = items[items.length - 1] ?? items[0]; + // ePay returns the full cart NEWEST-FIRST, so the just-added row is + // items[0]. (Taking items[last] broke 2+ item batches: every add + // reported the OLDEST row's id, so two rows collapsed onto one + // basketRowId and metadata was saved to the wrong row — 2026-06-05.) + const added = items[0]; if (!added?.id) { throw new Error(`ePay addToCart failed: ${JSON.stringify(data).slice(0, 200)}`); } @@ -604,28 +606,34 @@ export class EpayClient { }); const html = String(response.data ?? ""); - // Find ALL orderIds on the page + // ePay order numbers are sequential, so a genuinely NEW order is always + // numerically GREATER than the latest order that existed before submit. + // Requiring oid > previousOrderId is what stops us from adopting an + // unrelated OLD order when our submit didn't actually create one — the + // "!= previousOrderId" check alone let an older id through (2026-06-05: + // a new batch grabbed yesterday's order 10009605 and attached its PDFs). + const prevNum = previousOrderId ? Number(previousOrderId) : 0; + const isGenuinelyNew = (oid: string): boolean => + !!oid && + oid !== previousOrderId && + !knownOrderIds?.has(oid) && + (!Number.isFinite(prevNum) || prevNum === 0 || Number(oid) > prevNum); + + // Find ALL orderIds on the page; take the highest genuinely-new one. const allMatches = html.matchAll(/ShowOrderDetails\.action\?orderId=(\d+)/g); + let best = ""; for (const m of allMatches) { const oid = m[1] ?? ""; - if (!oid) continue; - if (oid === previousOrderId) continue; - if (knownOrderIds?.has(oid)) continue; - console.log(`[epay] New orderId: ${oid}`); - return oid; - } - - // If no new orderId found, the latest one might be it (first order) — - // but NEVER adopt the previous/known order: after a submit that timed - // out without creating anything, returning the stale id would attach - // the wrong order and download its old documents. - const latest = html.match(/ShowOrderDetails\.action\?orderId=(\d+)/); - const latestId = latest?.[1]; - if (latestId && latestId !== previousOrderId && !knownOrderIds?.has(latestId)) { - console.log(`[epay] Using latest orderId: ${latestId}`); - return latestId; + if (!isGenuinelyNew(oid)) continue; + if (!best || Number(oid) > Number(best)) best = oid; + } + if (best) { + console.log(`[epay] New orderId: ${best}`); + return best; } + // No genuinely-new order on the dashboard → the submit created nothing. + // Fail (recoverable) rather than adopting a stale/previous/known order. throw new Error("Could not determine orderId after checkout"); }