implemenation on the bot

This commit is contained in:
2026-08-19 17:08:45 +05:30
parent bdb21766f2
commit 37ca2352e2
52 changed files with 7074 additions and 3568 deletions

View File

@@ -46,9 +46,14 @@ import {
// right after an assign-miler call — see orders.js's ORDERS_STATUS_TABS
// comment) are confirmed; the rest of this mapping is still a best-effort
// guess. Unmapped statuses pass through lowercased, which the page's own
// fallback renders as an "unknown" badge rather than crashing. There's no
// confirmed equivalent for arrived/picked/skipped at all, so those tabs will
// show a 0 count until the real enum is confirmed.
// fallback renders as an "unknown" badge rather than crashing, AND the
// Deliveries page now surfaces those rows under an "Other" tab that names the
// unmapped enum — previously such a row was counted nowhere and shown nowhere.
// `picked` now has a confirmed source (Converted_To_Consignment, below).
// `arrived` and `skipped` still have none: the rider actions that produce them
// (POST /miler/bookings/:id/reached, POST /miler/consignments/:id/skip) have no
// booking-status equivalent in the confirmed enum, so those two tabs stay at 0
// until the backend is confirmed to expose them.
//
// Miler_Assigned is deliberately kept on 'pending', NOT bumped to 'accepted'.
// Assigning a rider is an OPERATOR action (POST /admin/bookings/:id/assign-miler
@@ -62,11 +67,20 @@ import {
// accepted" in the currently-confirmed enum, so that one maps to 'accepted'.
// If the backend turns out to have a distinct status specifically for the
// accept action, add it here rather than reusing Miler_Assigned for it.
// Converted_To_Consignment is PICKED, not accepted. doormile-flow.md §5 is
// explicit: `POST /miler/bookings/:bookingid/pickup-complete` "converts the
// booking into a consignment" — so the status is written at the moment the
// rider completes pickup, and the parcel is in their hands. deliveries.js says
// the same thing from the other side ("this order hasn't been picked up yet —
// status can only be updated once it becomes a consignment").
//
// It sat on 'accepted', which held a picked-up parcel in the Accepted tab and
// left Picked permanently empty — nothing else in the enum maps to it.
const BOOKING_STATUS_TO_DELIVERY_STATUS = {
pending_pickup: 'pending',
miler_assigned: 'pending',
pickup_scheduled: 'accepted',
converted_to_consignment: 'accepted',
converted_to_consignment: 'picked',
out_for_delivery: 'active',
delivered: 'delivered',
cancelled: 'cancelled'
@@ -135,7 +149,10 @@ export const getRiderPeriodicLogs = async (userid) => {
// seeing every hub, unchanged. Fails open (full list) whenever the tenant's
// city can't be determined or nothing matches, rather than ever locking an
// operator out with an empty picker.
const normCity = (s) => String(s || '').trim().toLowerCase();
const normCity = (s) =>
String(s || '')
.trim()
.toLowerCase();
export const fetchAppLocations = async () => {
try {
@@ -202,7 +219,16 @@ export const fetchPercentageData = async ({ queryKey }) => {
};
} catch (err) {
OpenToast(err.response?.data?.message || err.message || 'Failed to load order percentages', 'error', 2000);
return { created: '0', uncoveredOrders: '0', coveredOrders: '0', cancelled: '0', percentage1: '0', percentage2: '0', percentage3: '0', percentage4: '0' };
return {
created: '0',
uncoveredOrders: '0',
coveredOrders: '0',
cancelled: '0',
percentage1: '0',
percentage2: '0',
percentage3: '0',
percentage4: '0'
};
}
};
@@ -327,7 +353,10 @@ export const fetchBatchEfficiency = async ({ batch, tenantId }) => {
// jupiter2doormile.md comparison). Falls back to matching the rider's NAME
// (also echoed by the solver, see flattenRiders' rider_name) against each
// miler's displayname/authname — the only other correlatable field.
const normMilerName = (s) => String(s || '').trim().toLowerCase();
const normMilerName = (s) =>
String(s || '')
.trim()
.toLowerCase();
export const buildMilerLookup = (milers) => {
const byUserId = new Map((milers || []).map((m) => [String(m.userid), m]));
@@ -348,10 +377,10 @@ export const resolveMilerForOrder = (order, lookup) => {
const matchedVia = lookup.byUserId.has(String(riderUserId))
? 'userid'
: lookup.byProfileId.has(String(riderUserId))
? 'milerprofileid'
: lookup.byName.has(normMilerName(riderName))
? 'name'
: null;
? 'milerprofileid'
: lookup.byName.has(normMilerName(riderName))
? 'name'
: null;
const rider =
lookup.byUserId.get(String(riderUserId)) ?? lookup.byProfileId.get(String(riderUserId)) ?? lookup.byName.get(normMilerName(riderName));
return rider?.milerprofileid ? { rider, matchedVia } : null;
@@ -407,7 +436,11 @@ export const finalCreatedeliveries = async (deliveryData) => {
realBookingIds = new Set(realBookings.map((b) => String(b.bookingid)));
logger.debug(`finalCreatedeliveries: ${realBookingIds.size} real booking id(s) fetched for validation`);
} catch (err) {
logger.error('finalCreatedeliveries: GET /admin/bookings failed — cannot validate booking ids', err.response?.status, err.response?.data || err.message);
logger.error(
'finalCreatedeliveries: GET /admin/bookings failed — cannot validate booking ids',
err.response?.status,
err.response?.data || err.message
);
}
const results = await Promise.allSettled(
@@ -464,11 +497,7 @@ export const finalCreatedeliveries = async (deliveryData) => {
if (failed.length) {
OpenToast(`${failed.length} of ${deliveries.length} order(s) couldn't be assigned — check Orders/Deliveries`, 'warning', 4000);
}
const resolvedMilerProfileIds = [
...new Set(
results.filter((r) => r.status === 'fulfilled').map((r) => r.value.milerprofileid)
)
];
const resolvedMilerProfileIds = [...new Set(results.filter((r) => r.status === 'fulfilled').map((r) => r.value.milerprofileid))];
return { success: true, assigned: deliveries.length - failed.length, failed: failed.length, resolvedMilerProfileIds };
};
// ==============================|| createAutomationDeliveries (orders) Auto rider Assign ||============================== //
@@ -536,7 +565,11 @@ 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.
const [, , , , startdate, enddate, rowsPerPage] = queryKey;
// 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;
// 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
@@ -665,14 +698,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;
};
const inRange = (row) => {
if (!startdate && !enddate) return true;
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;
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)));
};
return {
@@ -815,9 +857,7 @@ export const getallridersummary = async () => {
// silently inflating one of the two new buckets.
const active = milers.filter((m) => !['offline', 'blocked'].includes(String(m.availabilitystatus || '').toLowerCase())).length;
const available = milers.filter((m) => String(m.availabilitystatus || '').toLowerCase() === 'available').length;
const onDelivery = milers.filter((m) =>
['assigned', 'on_pickup'].includes(String(m.availabilitystatus || '').toLowerCase())
).length;
const onDelivery = milers.filter((m) => ['assigned', 'on_pickup'].includes(String(m.availabilitystatus || '').toLowerCase())).length;
return { total: milers.length, active, inactive: milers.length - active, available, onDelivery };
} catch (err) {
OpenToast(err.response?.data?.message || err.message || 'Failed to load rider summary', 'error', 2000);