/** * The route-plan chain, run against the live optimiser. * * Not a test — the unit tests pin `routePlan.ts` against a frozen fixture. This * calls the real service with real order rows and pushes the answer through the * same functions the drawer uses, which is the only way to notice the service * changing shape under us. * * npm run check:optimiser */ import { optimiserApi } from '../src/api/optimiser'; import type { OrderRow, RiderInfo } from '../src/api/types'; import { applyReconcile, commitProblem, dirtyRiders, planFromSequence, splitRoutable, planKms, reorderStops, unplaced, } from '../src/features/store-admin/routePlan'; const line = (s = '') => console.log(s); /** Real Suriya Store geography: the RS Puram branch out to four drops. */ const ORDERS: OrderRow[] = [ { orderheaderid: 1, orderid: 'N-1', pickuplat: '11.0118', pickuplong: '76.9456', deliverylat: '11.0284', deliverylong: '77.0120', deliverycustomer: 'Peelamedu' }, { orderheaderid: 2, orderid: 'N-2', pickuplat: '11.0118', pickuplong: '76.9456', deliverylat: '11.0050', deliverylong: '76.9508', deliverycustomer: 'RS Puram' }, { orderheaderid: 3, orderid: 'N-3', pickuplat: '11.0118', pickuplong: '76.9456', deliverylat: '10.9877', deliverylong: '76.9620', deliverycustomer: 'Ukkadam' }, { orderheaderid: 4, orderid: 'N-4', pickuplat: '11.0118', pickuplong: '76.9456', deliverylat: '11.0183', deliverylong: '76.9724', deliverycustomer: 'Gandhipuram' }, // No coordinates — must come back as unplaced rather than vanishing. { orderheaderid: 5, orderid: 'N-5', deliverycustomer: 'No location on file' }, ] as OrderRow[]; const RIDER: RiderInfo = { userid: 9701, fullname: 'Meera Raj', contactno: '9000000001' }; line('─'.repeat(74)); line('1 · SEQUENCE — send in a deliberately bad order, see what comes back'); line('─'.repeat(74)); const { routable, unroutable } = splitRoutable(ORDERS); const stops = await optimiserApi.sequence(routable); let plan = planFromSequence(stops, RIDER); for (const stop of plan.riders[0]?.orders ?? []) { line( ` ${String(stop.step).padStart(2)} ${(stop.orderid ?? '').padEnd(5)} ` + `${(stop.deliverycustomer ?? '').padEnd(22)} ` + `${String(stop.previouskms ?? '').padStart(5)} km ` + `cum ${String(stop.cumulativekms ?? '').padStart(5)} km ` + `eta ${stop.eta ?? '-'}m actualkms ${stop.actualkms ?? '-'}`, ); } line(` total ${planKms(plan).toFixed(1)} km`); const missed = [...unroutable, ...unplaced(routable, stops)]; line(` unplaced: ${missed.length ? missed.map((o) => o.orderid).join(', ') : 'none'}`); line(` commit allowed? ${commitProblem(plan) === '' ? 'YES' : 'no — ' + commitProblem(plan)}`); line(); line('─'.repeat(74)); line('2 · EDIT — move the first stop to last, which breaks the step numbers'); line('─'.repeat(74)); plan = reorderStops(plan, RIDER.userid, 0, (plan.riders[0]?.orders.length ?? 1) - 1); line(` dirty rounds: ${[...plan.dirty].join(', ')}`); line(` steps now: ${plan.riders[0]?.orders.map((s) => s.step).join(' → ')} <- out of order`); line(` commit allowed? ${commitProblem(plan) === '' ? 'YES' : 'NO'}`); line(` reason: ${commitProblem(plan)}`); line(); line('─'.repeat(74)); line('3 · RECONCILE — the service repairs the step numbers'); line('─'.repeat(74)); try { const response = await optimiserApi.reconcile(dirtyRiders(plan)); plan = applyReconcile(plan, response); line(` steps now: ${plan.riders[0]?.orders.map((s) => s.step).join(' → ')}`); line(` dirty rounds: ${plan.dirty.size === 0 ? 'none' : [...plan.dirty].join(', ')}`); line(` commit allowed? ${commitProblem(plan) === '' ? 'YES' : 'no — ' + commitProblem(plan)}`); } catch (error) { line(` reconcile failed: ${error instanceof Error ? error.message : String(error)}`); line(` commit still blocked? ${commitProblem(plan) !== '' ? 'YES — correct' : 'NO — WRONG'}`); } line();