fix(epay): CRITICAL multi-item batch regressions — wrong basketRowId + stale order match

Found via a real 2-item batch (280067 + 327649) on 2026-06-05 that produced a
wrong PDF (correctly caught as "De verificat" by the R4 safety net) and a
failed download:

1. addToCartDetailed took items[items.length-1], but ePay returns the cart
   NEWEST-FIRST, so the just-added row is items[0]. On a 2+ item batch every
   add reported the OLDEST row's id → two rows collapsed onto one basketRowId
   → metadata saved to the wrong row → broken cart. Single-item orders were
   unaffected (one element). Reverted to items[0].

2. findNewOrderId accepted any id != previousOrderId, so when our submit
   created nothing it adopted an unrelated OLDER order (yesterday's 10009605)
   and attached its 15 Feleacu PDFs to today's parcels. ePay order numbers are
   sequential, so a genuinely-new order must be numerically GREATER than the
   latest pre-submit order; otherwise fail (recoverable) instead of matching a
   stale order. Take the highest genuinely-new id. Removed the now-dead
   latest-id fallback.

The R4 "review" flag did its job — the wrong PDF was flagged for verification,
never shown as valid.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude VM
2026-06-05 15:50:11 +03:00
parent 5ad8870dc5
commit 1c8d7ea59c
+28 -20
View File
@@ -233,9 +233,11 @@ export class EpayClient {
const data = response.data as EpayCartResponse; const data = response.data as EpayCartResponse;
const items = Array.isArray(data?.items) ? data.items : []; const items = Array.isArray(data?.items) ? data.items : [];
// The freshly added row is the one we didn't know about; ePay returns // ePay returns the full cart NEWEST-FIRST, so the just-added row is
// the full cart in `items`, newest typically last. Be defensive. // items[0]. (Taking items[last] broke 2+ item batches: every add
const added = items[items.length - 1] ?? items[0]; // 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) { if (!added?.id) {
throw new Error(`ePay addToCart failed: ${JSON.stringify(data).slice(0, 200)}`); throw new Error(`ePay addToCart failed: ${JSON.stringify(data).slice(0, 200)}`);
} }
@@ -604,28 +606,34 @@ export class EpayClient {
}); });
const html = String(response.data ?? ""); 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); const allMatches = html.matchAll(/ShowOrderDetails\.action\?orderId=(\d+)/g);
let best = "";
for (const m of allMatches) { for (const m of allMatches) {
const oid = m[1] ?? ""; const oid = m[1] ?? "";
if (!oid) continue; if (!isGenuinelyNew(oid)) continue;
if (oid === previousOrderId) continue; if (!best || Number(oid) > Number(best)) best = oid;
if (knownOrderIds?.has(oid)) continue; }
console.log(`[epay] New orderId: ${oid}`); if (best) {
return oid; console.log(`[epay] New orderId: ${best}`);
} return best;
// 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;
} }
// 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"); throw new Error("Could not determine orderId after checkout");
} }