console update

This commit is contained in:
2026-08-03 15:36:34 +05:30
parent 8deaf8513b
commit 13db63e219
10 changed files with 395 additions and 254 deletions

View File

@@ -106,6 +106,30 @@ export function num(v: unknown): number {
export const str = (v: unknown): string => (v == null ? '' : String(v));
/**
* Display name for a customer row. `gettenantcustomers` returns `firstname` /
* `lastname` and has NO `customername` or `name` column, so code that read those
* fell through to "Unknown Customer" for every customer on the platform. Delivery
* rows spell the same person `deliverycustomer`, hence the extra fallbacks.
*/
export function customerName(r: Row): string {
const full = `${str(r.firstname).trim()} ${str(r.lastname).trim()}`.trim();
return (
full ||
str(r.deliverycustomer).trim() ||
str(r.customername).trim() ||
str(r.name).trim()
);
}
/**
* The store a customer belongs to. `gettenantcustomers` aliases the
* `tenantcustomers.locationid` link as `tenantlocationid` — plain `locationid`
* is NOT in the response, so filtering on it matched nothing. `deliverylocationid`
* is the saved-address id, a different thing entirely, and must not be used here.
*/
export const customerStoreId = (r: Row): number => num(r.tenantlocationid) || num(r.locationid);
/** Fiesta date params want a bare `YYYY-MM-DD`. */
export const ymd = (d: Date) =>
`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
@@ -352,27 +376,48 @@ export async function getDeliveries(opts: {
pageno?: number;
pagesize?: number;
}): Promise<Row[]> {
const rows = toRows(
await fiestaGet('deliveries/getdeliveries', {
tenantid: opts.tenantid,
// NOTE: do NOT send `locationid` to getdeliveries — the backend's locationid
// filter on THIS endpoint is broken: passing a real outlet id returns []
// (it doesn't match against the row's own `locationid`), even though
// deliverysummary honours the same id and the rows clearly carry it. So we
// fetch tenant-wide here and scope by locationid client-side below; the KPI
// strip (deliverysummary) keeps using the working server-side filter.
applocationid: opts.applocationid,
// The backend treats `status` as a LITERAL orderstatus filter — passing
// 'all' matches nothing (returns []). Send empty to fetch every status and
// let the board filter client-side by its status tabs.
status: !opts.status || opts.status === 'all' ? '' : opts.status,
fromdate: opts.fromdate,
todate: opts.todate,
keyword: opts.keyword,
pageno: opts.pageno ?? 1,
pagesize: opts.pagesize ?? 200,
}),
);
const pagesize = opts.pagesize ?? 200;
const fetchPage = async (pageno: number) =>
toRows(
await fiestaGet('deliveries/getdeliveries', {
tenantid: opts.tenantid,
// NOTE: do NOT send `locationid` to getdeliveries — the backend's locationid
// filter on THIS endpoint is broken: passing a real outlet id returns []
// (it doesn't match against the row's own `locationid`), even though
// deliverysummary honours the same id and the rows clearly carry it. So we
// fetch tenant-wide here and scope by locationid client-side below; the KPI
// strip (deliverysummary) keeps using the working server-side filter.
applocationid: opts.applocationid,
// The backend treats `status` as a LITERAL orderstatus filter — passing
// 'all' matches nothing (returns []). Send empty to fetch every status and
// let the board filter client-side by its status tabs.
status: !opts.status || opts.status === 'all' ? '' : opts.status,
fromdate: opts.fromdate,
todate: opts.todate,
keyword: opts.keyword,
pageno,
pagesize,
}),
);
let rows: Row[];
if (opts.pageno) {
// An explicit page was asked for — honour it and don't walk the rest.
rows = await fetchPage(opts.pageno);
} else {
// Walk every page. The endpoint has no total-count field, so a short page is
// the only end-of-data signal. Previously this fetched page 1 only, which
// silently dropped delivery 201+ for a busy tenant-wide day — the dispatch
// board looked complete while missing stops. MAX_PAGES caps a runaway loop
// if the backend ever ignores `pageno` and keeps returning full pages.
const MAX_PAGES = 25;
rows = [];
for (let page = 1; page <= MAX_PAGES; page++) {
const batch = await fetchPage(page);
rows.push(...batch);
if (batch.length < pagesize) break;
}
}
return opts.locationid ? rows.filter((r) => num(r.locationid) === opts.locationid) : rows;
}
@@ -758,7 +803,14 @@ export async function getTenantCustomers(opts: {
locationid: opts.locationid,
keyword: opts.keyword ?? '',
pageno: opts.pageno ?? 1,
pagesize: opts.pagesize ?? 20,
// When a store is named the backend joins `customerlocations`, so it
// returns one row per SAVED ADDRESS and applies LIMIT to those rows — not
// to customers. Live: locationid=1185 → 12 rows → 2 customers (11 of them
// one person's addresses). The old default of 20 therefore showed a store
// roughly three customers. Ask for enough rows that dedupe still has every
// customer to work with; the backend's DISTINCT ON fix makes this generous
// rather than load-bearing.
pagesize: opts.pagesize ?? 500,
}),
));
}

View File

@@ -423,7 +423,11 @@ export function useFiestaRiderPeriodicLogs(opts: {
return useQuery({
queryKey: fiestaKeys.riderPeriodicLogs(opts),
queryFn: () => getRiderPeriodicLogs(opts),
enabled: Boolean(opts.fromdate && opts.todate),
// A rider is required. Without this guard the query fired on every page load
// with no rider selected — and `riders/getriderperiodiclogs` 404s in both the
// riders/ and partners/ namespaces (no such backend route exists), so every
// load spent a request on a guaranteed failure.
enabled: Boolean((opts.userid || opts.riderid) && opts.fromdate && opts.todate),
});
}