updates on the profitability page updates on the rider kms and actual kms
This commit is contained in:
@@ -23,7 +23,6 @@ import {
|
||||
MdMyLocation,
|
||||
MdCalendarMonth,
|
||||
MdPerson,
|
||||
MdOutlineLocalShipping,
|
||||
MdOutlineCurrencyRupee,
|
||||
MdStraighten,
|
||||
MdPayments,
|
||||
@@ -281,26 +280,32 @@ export default function ProfitabilityReport() {
|
||||
// Calculate profitability metrics for all riders
|
||||
const stats = useMemo(() => {
|
||||
let activeRiders = 0;
|
||||
let totalOrders = 0;
|
||||
let totalRevenue = 0;
|
||||
let totalCost = 0;
|
||||
let profitableRiders = 0;
|
||||
let lossRiders = 0;
|
||||
let totalKms = 0;
|
||||
let totalPlannedKms = 0;
|
||||
let totalActualKms = 0;
|
||||
|
||||
const list = ridersList
|
||||
.map((r) => {
|
||||
let rRevenue = 0;
|
||||
let rKms = 0;
|
||||
let rPlannedKms = 0;
|
||||
let rActualKms = 0;
|
||||
const slotsByDate = {};
|
||||
let ordersInSlots = 0;
|
||||
|
||||
r.orders.forEach((o) => {
|
||||
const status = String(o.orderstatus || '').toLowerCase();
|
||||
if (status === 'cancelled' || status === 'skipped') return;
|
||||
|
||||
const slot = getRowBatch(o, customBatches);
|
||||
if (!slot) return;
|
||||
|
||||
const oKms = parseFloat(o.riderkms || 0);
|
||||
rKms += oKms;
|
||||
rPlannedKms += parseFloat(o.kms || 0);
|
||||
rActualKms += parseFloat(o.actualkms || 0);
|
||||
rRevenue += oKms <= 8 ? 30 : 30 + (oKms - 8) * 6;
|
||||
|
||||
const dateStr = o.assigntime
|
||||
@@ -339,15 +344,17 @@ export default function ProfitabilityReport() {
|
||||
lossRiders++;
|
||||
}
|
||||
|
||||
totalOrders += ordersInSlots;
|
||||
totalRevenue += rRevenue;
|
||||
totalCost += rTotalCost;
|
||||
totalKms += rKms;
|
||||
totalPlannedKms += rPlannedKms;
|
||||
totalActualKms += rActualKms;
|
||||
activeRiders++;
|
||||
|
||||
return {
|
||||
...r,
|
||||
orderCount: ordersInSlots,
|
||||
kms: rKms,
|
||||
plannedKms: rPlannedKms,
|
||||
actualKms: rActualKms,
|
||||
revenue: rRevenue,
|
||||
varCost: rVarCost,
|
||||
fixedCost: rFixedCost,
|
||||
@@ -358,19 +365,13 @@ export default function ProfitabilityReport() {
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
const totalNet = totalRevenue - totalCost;
|
||||
const totalMargin = totalRevenue > 0 ? (totalNet / totalRevenue) * 100 : 0;
|
||||
|
||||
return {
|
||||
activeRiders,
|
||||
totalOrders,
|
||||
totalRevenue,
|
||||
totalCost,
|
||||
totalNet,
|
||||
totalMargin,
|
||||
profitableRiders,
|
||||
lossRiders,
|
||||
totalKms,
|
||||
totalPlannedKms,
|
||||
totalActualKms,
|
||||
enrichedRiders: list
|
||||
};
|
||||
}, [ridersList, customBatches]);
|
||||
@@ -396,38 +397,36 @@ export default function ProfitabilityReport() {
|
||||
detail: `${stats?.profitableRiders ?? 0} in profit · ${stats?.lossRiders ?? 0} at loss`
|
||||
},
|
||||
{
|
||||
key: 'kms',
|
||||
key: 'planned-kms',
|
||||
label: 'Planned KMs',
|
||||
color: '#0ea5e9',
|
||||
icon: MdRoute,
|
||||
value: `${(stats?.totalPlannedKms ?? 0).toFixed(1)} km`,
|
||||
detail: 'Solver-planned distance'
|
||||
},
|
||||
{
|
||||
key: 'rider-kms',
|
||||
label: 'Rider KMs',
|
||||
color: BRAND,
|
||||
icon: MdStraighten,
|
||||
value: `${(stats?.totalKms ?? 0).toFixed(1)} km`,
|
||||
detail: 'Billed to riders'
|
||||
},
|
||||
{
|
||||
key: 'actual-kms',
|
||||
label: 'Actual KMs',
|
||||
color: '#f59e0b',
|
||||
icon: MdMyLocation,
|
||||
value: `${(stats?.totalActualKms ?? 0).toFixed(1)} km`,
|
||||
detail: 'GPS-tracked distance'
|
||||
},
|
||||
{
|
||||
key: 'total-distance',
|
||||
label: 'Total Distance',
|
||||
color: '#10b981',
|
||||
icon: MdStraighten,
|
||||
value: `${(stats?.totalKms ?? 0).toFixed(1)} km`,
|
||||
detail: 'Cumulative travel distance'
|
||||
},
|
||||
{
|
||||
key: 'revenue',
|
||||
label: 'Slot Revenue',
|
||||
color: '#0ea5e9',
|
||||
icon: MdOutlineLocalShipping,
|
||||
value: formatNumberToRupees(stats?.totalRevenue ?? 0),
|
||||
detail: `From ${stats?.totalOrders ?? 0} orders`
|
||||
},
|
||||
{
|
||||
key: 'cost',
|
||||
label: 'Slot Cost',
|
||||
color: '#f59e0b',
|
||||
icon: MdPayments,
|
||||
value: formatNumberToRupees(stats?.totalCost ?? 0),
|
||||
detail: 'Fixed + variable'
|
||||
},
|
||||
{
|
||||
key: 'net',
|
||||
label: 'Slot Net',
|
||||
color: (stats?.totalNet ?? 0) >= 0 ? '#10b981' : '#ef4444',
|
||||
icon: (stats?.totalNet ?? 0) >= 0 ? MdTrendingUp : MdTrendingDown,
|
||||
value: `${(stats?.totalNet ?? 0) >= 0 ? '+' : ''}${formatNumberToRupees(stats?.totalNet ?? 0)}`,
|
||||
detail: `${(stats?.totalRevenue ?? 0) > 0 ? ((stats?.totalNet ?? 0) / (stats?.totalRevenue ?? 1) >= 0 ? '+' : '') : ''}${(
|
||||
stats?.totalMargin ?? 0
|
||||
).toFixed(0)}% margin`
|
||||
}
|
||||
];
|
||||
|
||||
@@ -610,8 +609,8 @@ export default function ProfitabilityReport() {
|
||||
}
|
||||
>
|
||||
<MobileFieldGrid columns={2}>
|
||||
<MobileField label="Orders" value={row.orders.length} />
|
||||
<MobileField label="Rider KMs" value={`${row.kms.toFixed(2)} km`} />
|
||||
<MobileField label="Orders" value={row.orderCount} />
|
||||
<MobileField label="Rider KMs" value={`${Math.round(row.kms)} km`} />
|
||||
<MobileField label="Revenue" value={formatNumberToRupees(row.revenue)} />
|
||||
<MobileField label="Fixed Cost" value={formatNumberToRupees(row.fixedCost)} />
|
||||
<MobileField label="Variable Cost" value={formatNumberToRupees(row.varCost)} />
|
||||
@@ -720,11 +719,11 @@ export default function ProfitabilityReport() {
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Typography variant="body2" sx={{ fontWeight: 700, color: DT.textPrimary }}>
|
||||
{row.orders.length}
|
||||
{row.orderCount}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<MetricPill color="#10b981" icon={<MdStraighten size={11} />} label={`${row.kms.toFixed(2)} km`} tooltip="KMS" />
|
||||
<MetricPill color="#10b981" icon={<MdStraighten size={11} />} label={`${Math.round(row.kms)} km`} tooltip="KMS" />
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<MetricPill
|
||||
|
||||
Reference in New Issue
Block a user