updates on the ai bot and assigned by chatbot
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
getHubs,
|
||||
getBookings,
|
||||
getBooking,
|
||||
getConsignments,
|
||||
assignMilerToBooking,
|
||||
cancelBooking,
|
||||
updateConsignmentStatus,
|
||||
@@ -94,6 +95,19 @@ export const mapBookingStatusToDeliveryStatus = (status) => {
|
||||
return BOOKING_STATUS_TO_DELIVERY_STATUS[key] || key;
|
||||
};
|
||||
|
||||
// A consignment's own status, when this booking has one and it looks like a
|
||||
// status. `GET /admin/consignments` has no documented response schema, so this
|
||||
// reads defensively: only a non-empty string on one of the plausible field
|
||||
// names counts, and anything else returns undefined so the caller keeps using
|
||||
// the booking's status.
|
||||
const consignmentStatusFor = (booking, consignmentMap) => {
|
||||
if (!booking?.consignmentid || !consignmentMap?.size) return undefined;
|
||||
const record = consignmentMap.get(String(booking.consignmentid));
|
||||
if (!record) return undefined;
|
||||
const raw = record.status ?? record.consignmentstatus ?? record.currentstatus;
|
||||
return typeof raw === 'string' && raw.trim() ? raw.trim() : undefined;
|
||||
};
|
||||
|
||||
// Haversine straight-line distance in km — used wherever a resource carries
|
||||
// lat/lng but no road-distance field (bookings, and by extension deliveries).
|
||||
const haversineKm = (lat1, lon1, lat2, lon2) => {
|
||||
@@ -565,11 +579,7 @@ export const fetchDeliveries = async ({ pageParam = 1, queryKey }) => {
|
||||
// parameter is documented in express-console-api.md and guessing one risks a
|
||||
// silent 400 or, worse, a silently-ignored filter), so the range is applied
|
||||
// client-side below, after the rows are normalised.
|
||||
// queryKey[11] is an OPT-IN date basis. Four pages share this function
|
||||
// (deliveries, Dispatch, reports/ordersDetails, reports/profitability) and
|
||||
// three of them genuinely want "created in this window", so the default is
|
||||
// unchanged and only the Deliveries page passes 'activity'.
|
||||
const [, , , , startdate, enddate, rowsPerPage, , , , , dateBasis] = queryKey;
|
||||
const [, , , , startdate, enddate, rowsPerPage] = queryKey;
|
||||
// Unlike the 3 joins below (customers/milers/tenants — each individually
|
||||
// guarded so a failed join just degrades a display field, not the whole
|
||||
// page), a failed bookings call is the one thing this function can't
|
||||
@@ -582,11 +592,29 @@ export const fetchDeliveries = async ({ pageParam = 1, queryKey }) => {
|
||||
OpenToast(err.response?.data?.message || err.message || 'Failed to load deliveries', 'error', 2000);
|
||||
return { rows: [], nextPage: undefined };
|
||||
}
|
||||
const [customers, milers, tenants] = await Promise.all([
|
||||
// Consignments are joined for ONE reason: once a booking becomes a
|
||||
// consignment, its lifecycle continues on the CONSIGNMENT record and the
|
||||
// booking's own `status` stops moving. `PUT /admin/consignments/:id/status`
|
||||
// — the Update Status dialog — writes there, so the write succeeded, the
|
||||
// toast said so, and this page went on showing the booking's stale
|
||||
// `Converted_To_Consignment` because that is the only field it read.
|
||||
//
|
||||
// Guarded like the other three joins: if the call fails, or the response
|
||||
// carries no recognisable status, the row falls back to the booking's status
|
||||
// and behaviour is exactly what it was before this join existed.
|
||||
const [customers, milers, tenants, consignments] = await Promise.all([
|
||||
getAdminCustomers().catch(() => []),
|
||||
getMilers().catch(() => []),
|
||||
getAdminTenants().catch(() => [])
|
||||
getAdminTenants().catch(() => []),
|
||||
getConsignments().catch(() => [])
|
||||
]);
|
||||
// The id field on a consignment record has never been captured, so both
|
||||
// plausible names are indexed rather than guessing one.
|
||||
const consignmentMap = new Map();
|
||||
(consignments || []).forEach((c) => {
|
||||
const id = c?.consignmentid ?? c?.id;
|
||||
if (id != null) consignmentMap.set(String(id), c);
|
||||
});
|
||||
const customerMap = new Map((customers || []).map((c) => [c.appcustomerid ?? c.customerid ?? c.id, c]));
|
||||
const milerMap = new Map((milers || []).map((m) => [m.userid ?? m.milerid, m]));
|
||||
const tenantMap = new Map((tenants || []).map((t) => [t.tenantid, t]));
|
||||
@@ -680,7 +708,12 @@ export const fetchDeliveries = async ({ pageParam = 1, queryKey }) => {
|
||||
// remains fine to DISPLAY assigntime as a "last updated" stamp, which is
|
||||
// all the reports use it for.
|
||||
assigntime: b.updatedat,
|
||||
orderstatus: mapBookingStatusToDeliveryStatus(b.status),
|
||||
// The consignment's status WINS when there is one. That is the record
|
||||
// the rider app and the Update Status dialog both advance; the booking's
|
||||
// status is frozen at Converted_To_Consignment from pickup onwards.
|
||||
// Falls back to the booking whenever the consignment is absent or carries
|
||||
// nothing status-shaped — never invents a state.
|
||||
orderstatus: mapBookingStatusToDeliveryStatus(consignmentStatusFor(b, consignmentMap) ?? b.status),
|
||||
droplat: b.deliverylatitude,
|
||||
droplon: b.deliverylongitude
|
||||
};
|
||||
@@ -699,22 +732,23 @@ export const fetchDeliveries = async ({ pageParam = 1, queryKey }) => {
|
||||
// A missing/blank bound means "unbounded on that side", which preserves the
|
||||
// old behaviour for any caller that doesn't pass real dates.
|
||||
//
|
||||
// 'activity' additionally keeps a row whose LAST UPDATE falls in the window.
|
||||
// Scoping the Deliveries page purely by creation date meant an order created
|
||||
// yesterday and picked or delivered today was invisible today — which is
|
||||
// exactly why every tab past Accepted read 0 while the day's fresh orders
|
||||
// filled Pending and Accepted. A delivery board has to show what is moving
|
||||
// now, not only what was booked now.
|
||||
const dayOf = (value) => {
|
||||
const t = parseDoormileTimestamp(value);
|
||||
return t.isValid() ? t.format('YYYY-MM-DD') : null;
|
||||
};
|
||||
|
||||
// ⛔ An "activity" basis — also admitting a row whose `assigntime`
|
||||
// (== `updatedat`) falls in the window — was tried and REVERTED. It let an
|
||||
// order created yesterday evening and merely touched today onto today's
|
||||
// board, but batch bucketing reads `orderdate`, so that row landed in
|
||||
// Evening Batch. The live result was "Evening 6" at 10:41 in the morning on a
|
||||
// day with no orders created at all. Admitting a row on one timestamp while
|
||||
// bucketing it on another cannot produce an honest batch count; if
|
||||
// carried-over work needs to be visible it needs its own bucket, not a
|
||||
// time-of-day wave it does not belong to.
|
||||
const inRange = (row) => {
|
||||
if (!startdate && !enddate) return true;
|
||||
const days = [dayOf(row.orderdate), dateBasis === 'activity' ? dayOf(row.assigntime) : null].filter(Boolean);
|
||||
if (!days.length) return false;
|
||||
return days.some((day) => (!startdate || day >= String(startdate)) && (!enddate || day <= String(enddate)));
|
||||
const t = parseDoormileTimestamp(row.orderdate);
|
||||
if (!t.isValid()) return false;
|
||||
const day = t.format('YYYY-MM-DD');
|
||||
if (startdate && day < String(startdate)) return false;
|
||||
if (enddate && day > String(enddate)) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -803,7 +837,64 @@ export const changeRiderAPI = async (selectedRider, selectedRow) =>
|
||||
// truthy, so `deliveryid ?? consignmentid` never actually fell through to
|
||||
// consignmentid even when it was present, silently calling the endpoint
|
||||
// with the wrong kind of id on every Update Status submit.
|
||||
export const updateDeliveryAPI = async (orderData) => updateConsignmentStatus(orderData.consignmentid ?? orderData.deliveryid, orderData);
|
||||
//
|
||||
// ---- The body ---------------------------------------------------------------
|
||||
//
|
||||
// This used to forward the dialog's WHOLE state object as the request body —
|
||||
// the old jupiter shape (`orderstatus`, `deliveryid`, `orderheaderid`,
|
||||
// `deliveryamt`, `cumulativekms`, `userid`). The endpoint wants one field
|
||||
// called `status`, so every submit came back:
|
||||
//
|
||||
// PUT /admin/consignments/40/status → 400 {"status is required"}
|
||||
//
|
||||
// The status was in the payload the whole time, under the wrong name.
|
||||
//
|
||||
// The VALUE has to be translated too. The dialog's options are this page's own
|
||||
// lifecycle keys (`delivered`, `cancelled`, …); the API speaks the booking enum
|
||||
// (`Delivered`, `Cancelled`, …). Sending `delivered` where `Delivered` is
|
||||
// expected is the same class of bug one layer down.
|
||||
//
|
||||
// Reverse of BOOKING_STATUS_TO_DELIVERY_STATUS, and deliberately NOT derived
|
||||
// from it by inversion: that map is many-to-one (`pending_pickup` and
|
||||
// `miler_assigned` both mean `pending`), so an automatic inversion would pick
|
||||
// whichever happened to be last and silently write the wrong one.
|
||||
const DELIVERY_STATUS_TO_BOOKING_STATUS = {
|
||||
pending: 'Pending_Pickup',
|
||||
accepted: 'Pickup_Scheduled',
|
||||
picked: 'Converted_To_Consignment',
|
||||
// The dialog offers "started", which this API has no separate state for — a
|
||||
// consignment that has started IS out for delivery.
|
||||
started: 'Out_for_Delivery',
|
||||
active: 'Out_for_Delivery',
|
||||
delivered: 'Delivered',
|
||||
cancelled: 'Cancelled',
|
||||
canceled: 'Cancelled'
|
||||
};
|
||||
|
||||
export const updateDeliveryAPI = async (orderData) => {
|
||||
const id = orderData.consignmentid ?? orderData.deliveryid;
|
||||
const chosen = String(orderData.orderstatus || '').toLowerCase();
|
||||
const status = DELIVERY_STATUS_TO_BOOKING_STATUS[chosen];
|
||||
|
||||
// `arrived` and `skipped` have no booking-status equivalent at all (the rider
|
||||
// actions behind them — /miler/bookings/:id/reached and
|
||||
// /miler/consignments/:id/skip — write no booking status). Refusing here with
|
||||
// the reason is honest; guessing a near-enough status would set the wrong one
|
||||
// on a real delivery.
|
||||
if (!status) {
|
||||
return {
|
||||
success: false,
|
||||
message: chosen
|
||||
? `"${orderData.orderstatus}" has no equivalent on the consignment API, so it can't be set from here.`
|
||||
: 'Choose a status first.'
|
||||
};
|
||||
}
|
||||
|
||||
// Only `status` is sent. The dialog's kms / amount / notes have no field on
|
||||
// this endpoint (see the note above), and this request 400s on validation —
|
||||
// so posting the rest is at best ignored and at worst another rejection.
|
||||
return updateConsignmentStatus(id, { status });
|
||||
};
|
||||
|
||||
// ==============================|| getalltenants (tenants) ||============================== //
|
||||
|
||||
|
||||
Reference in New Issue
Block a user