fix(scheduling): only show candidates that can actually qualify after swap
replace_qualified (换下): - Exclude already-qualified inventory (totalMileage >= yearTarget, gap=0) - Only keep candidates where canQualifyAfterSwap=true - Skip suggestions with no qualifiable candidates (e.g., too few days left) - Reason text now shows customer's remaining capacity: "日均 318km × 53天 ≈ 1.7万km" Before: showed 粤AGP9738 (缺口 0, already at target) — pointless After: shows 粤AGQ5808 (缺口 1.7万, 换后 3.0万, 可达标) — meaningful All replace_qualified candidates now guaranteed canQualifyAfterSwap=true. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -144,15 +144,21 @@ export function generateSuggestions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- replace_qualified (medium priority) ---
|
// --- replace_qualified (medium priority) ---
|
||||||
|
// Swap out the qualified car, swap in a car that NEEDS mileage.
|
||||||
|
// The high-mileage customer will drive it hard → helps it reach target.
|
||||||
|
// Exclude candidates already at target (gap <= 0) — swapping those in is pointless.
|
||||||
for (const vehicle of qualified) {
|
for (const vehicle of qualified) {
|
||||||
if (vehicle.customerAvgDaily <= vehicle.dailyRequiredMileage) continue;
|
if (vehicle.customerAvgDaily <= vehicle.dailyRequiredMileage) continue;
|
||||||
|
|
||||||
const candidates: CandidateVehicle[] = inventoryVehicles
|
const candidates: CandidateVehicle[] = inventoryVehicles
|
||||||
.filter(
|
.filter((inv) => {
|
||||||
(inv) =>
|
if (!isTypeCompatible(vehicle.vehicleType, inv.vehicleType)) return false;
|
||||||
isTypeCompatible(vehicle.vehicleType, inv.vehicleType) &&
|
if (inv.region !== vehicle.region) return false;
|
||||||
inv.region === vehicle.region,
|
// Must still need mileage — exclude already-qualified inventory
|
||||||
)
|
const effectiveTarget = inv.yearTarget ?? vehicle.yearTarget;
|
||||||
|
if (effectiveTarget > 0 && inv.totalMileage >= effectiveTarget) return false;
|
||||||
|
return true;
|
||||||
|
})
|
||||||
.map((inv) => {
|
.map((inv) => {
|
||||||
const effectiveTarget = inv.yearTarget ?? vehicle.yearTarget;
|
const effectiveTarget = inv.yearTarget ?? vehicle.yearTarget;
|
||||||
const mileageGap = Math.max(0, effectiveTarget - inv.totalMileage);
|
const mileageGap = Math.max(0, effectiveTarget - inv.totalMileage);
|
||||||
@@ -175,15 +181,23 @@ export function generateSuggestions(
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
|
// 1. canQualifyAfterSwap first
|
||||||
if (a.canQualifyAfterSwap !== b.canQualifyAfterSwap)
|
if (a.canQualifyAfterSwap !== b.canQualifyAfterSwap)
|
||||||
return a.canQualifyAfterSwap ? -1 : 1;
|
return a.canQualifyAfterSwap ? -1 : 1;
|
||||||
|
// 2. Among qualifiable: biggest gap first (most value from the swap)
|
||||||
return b.mileageGap - a.mileageGap;
|
return b.mileageGap - a.mileageGap;
|
||||||
})
|
})
|
||||||
|
// Only keep candidates that can actually qualify at this customer
|
||||||
|
.filter(c => c.canQualifyAfterSwap)
|
||||||
.slice(0, 5);
|
.slice(0, 5);
|
||||||
|
|
||||||
|
// Skip if no candidate can reach target — swap would be pointless
|
||||||
|
if (candidates.length === 0) continue;
|
||||||
|
|
||||||
const yearRate = vehicle.yearTarget > 0 ? Math.round((vehicle.currentYearMileage / vehicle.yearTarget) * 100) : 0;
|
const yearRate = vehicle.yearTarget > 0 ? Math.round((vehicle.currentYearMileage / vehicle.yearTarget) * 100) : 0;
|
||||||
const reason = `该车在客户「${vehicle.customer}」处已达标(完成率 ${yearRate}%),客户日均 ${Math.round(vehicle.customerAvgDaily)} km,属于高里程客户。`
|
const canAddKm = vehicle.customerAvgDaily * vehicle.daysLeft;
|
||||||
+ `\n建议:将此车换下,换上一辆里程少的车,利用该客户的高日均里程帮助新车快速达标。`;
|
const reason = `该车在客户「${vehicle.customer}」处已达标(完成率 ${yearRate}%),客户日均 ${Math.round(vehicle.customerAvgDaily)} km × ${vehicle.daysLeft} 天 ≈ ${fmtKmSimple(canAddKm)} km。`
|
||||||
|
+ `\n建议:换上里程未达标的车,利用该客户的高日均帮新车快速冲线。`;
|
||||||
|
|
||||||
suggestions.push({
|
suggestions.push({
|
||||||
id: `qualified-${vehicle.plateNumber}`,
|
id: `qualified-${vehicle.plateNumber}`,
|
||||||
|
|||||||
Reference in New Issue
Block a user