137 lines
4.8 KiB
JavaScript
137 lines
4.8 KiB
JavaScript
import { buildBulkBookingPayload, buildBulkBookingPayloads } from '@/lib/bulkOrderPayload';
|
|
|
|
/**
|
|
* Coordinates on the bulk booking payload.
|
|
*
|
|
* This is the regression guard for the billing defect. The builder used
|
|
* `Number(value) || 0` on all four coordinates, so a row with no usable pin
|
|
* was submitted as (0, 0) — and the backend cannot tell that apart from a
|
|
* deliberate pin in the Gulf of Guinea.
|
|
*
|
|
* Both failure modes came from that one substitution:
|
|
*
|
|
* • With a charge quoted, the console had measured the distance from those
|
|
* same 0,0 coordinates. OSRM refuses to route to open ocean, so the
|
|
* Haversine fallback returned roughly 11,000 km, and adminController
|
|
* honours a supplied `finalprice` verbatim. Massive overcharge.
|
|
* • With no charge quoted, the backend measured for itself — and skipped the
|
|
* calculation entirely, because its guard is
|
|
* `if booking.Deliverylatitude != 0 && ...`. Base rate, no distance
|
|
* component. Undercharge.
|
|
*
|
|
* A coordinate we do not have is now null, which the server can refuse.
|
|
*/
|
|
|
|
const PICKUP = {
|
|
address: 'Doormile Base, Peelamedu',
|
|
pincode: '641004',
|
|
city: 'Coimbatore',
|
|
latitude: 11.0168,
|
|
longitude: 76.9558,
|
|
};
|
|
|
|
const ROW = {
|
|
firstname: 'Priya',
|
|
contactno: '9876543210',
|
|
address: '45 Cross Cut Rd, Gandhipuram',
|
|
postcode: '641012',
|
|
city: 'Coimbatore',
|
|
latitude: 11.0272,
|
|
longitude: 76.9722,
|
|
totalcharge: 180,
|
|
};
|
|
|
|
const build = (row = {}, pickup = PICKUP) =>
|
|
buildBulkBookingPayload({
|
|
row: { ...ROW, ...row },
|
|
sharedPickup: pickup,
|
|
tenantId: 3,
|
|
tenantLocationId: 7,
|
|
deliverytime: '2026-09-18 10:00:00',
|
|
notes: '',
|
|
anchors: [],
|
|
});
|
|
|
|
describe('buildBulkBookingPayload — coordinates', () => {
|
|
it('passes real coordinates through untouched', () => {
|
|
const p = build();
|
|
expect(p.pickuplatitude).toBe(11.0168);
|
|
expect(p.pickuplongitude).toBe(76.9558);
|
|
expect(p.deliverylatitude).toBe(11.0272);
|
|
expect(p.deliverylongitude).toBe(76.9722);
|
|
});
|
|
|
|
it('sends null, NOT 0, for a delivery pin that is missing', () => {
|
|
const p = build({ latitude: null, longitude: null });
|
|
expect(p.deliverylatitude).toBeNull();
|
|
expect(p.deliverylongitude).toBeNull();
|
|
// The specific regression: 0 would have been priced as an 11,000 km run.
|
|
expect(p.deliverylatitude).not.toBe(0);
|
|
});
|
|
|
|
it('sends null for an empty sheet cell', () => {
|
|
// An untouched Excel cell arrives as '' rather than null, and
|
|
// `Number('') || 0` is 0.
|
|
const p = build({ latitude: '', longitude: '' });
|
|
expect(p.deliverylatitude).toBeNull();
|
|
expect(p.deliverylongitude).toBeNull();
|
|
});
|
|
|
|
it('sends null for an unparseable sheet cell', () => {
|
|
const p = build({ latitude: 'N/A', longitude: '--' });
|
|
expect(p.deliverylatitude).toBeNull();
|
|
expect(p.deliverylongitude).toBeNull();
|
|
});
|
|
|
|
it('refuses to forward a literal zero it was handed', () => {
|
|
// A row that already carried 0 — which is what the geocoding shim used to
|
|
// return for a suggestion with no coordinates — must not be laundered
|
|
// into a valid-looking payload.
|
|
const p = build({ latitude: 0, longitude: 0 });
|
|
expect(p.deliverylatitude).toBeNull();
|
|
expect(p.deliverylongitude).toBeNull();
|
|
});
|
|
|
|
it('sends null for a coordinate outside its axis range', () => {
|
|
const p = build({ latitude: 94.912, longitude: 200 });
|
|
expect(p.deliverylatitude).toBeNull();
|
|
expect(p.deliverylongitude).toBeNull();
|
|
});
|
|
|
|
it('nulls the pickup end too, rather than relying on the backend rescue', () => {
|
|
// adminController falls back to the hub or tenant location when pickup is
|
|
// 0,0. That rescue exists, but sending data that needs rescuing is not the
|
|
// same as sending correct data.
|
|
const p = build({}, { ...PICKUP, latitude: null, longitude: undefined });
|
|
expect(p.pickuplatitude).toBeNull();
|
|
expect(p.pickuplongitude).toBeNull();
|
|
});
|
|
|
|
it('treats the two ends independently', () => {
|
|
const p = build({ latitude: null, longitude: null });
|
|
expect(p.pickuplatitude).toBe(11.0168);
|
|
expect(p.deliverylatitude).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('buildBulkBookingPayloads — batch', () => {
|
|
it('nulls only the rows that are missing a pin', () => {
|
|
const payloads = buildBulkBookingPayloads({
|
|
rows: [ROW, { ...ROW, latitude: 0, longitude: 0 }, { ...ROW, latitude: '' }],
|
|
sharedPickup: PICKUP,
|
|
tenantId: 3,
|
|
tenantLocationId: 7,
|
|
deliverytime: '2026-09-18 10:00:00',
|
|
notes: '',
|
|
anchors: [],
|
|
});
|
|
|
|
expect(payloads).toHaveLength(3);
|
|
expect(payloads[0].deliverylatitude).toBe(11.0272);
|
|
expect(payloads[1].deliverylatitude).toBeNull();
|
|
expect(payloads[2].deliverylatitude).toBeNull();
|
|
// The good row keeps its longitude even though a sibling lost its latitude.
|
|
expect(payloads[2].deliverylongitude).toBe(76.9722);
|
|
});
|
|
});
|