diff --git a/src/modules/scheduling/SuggestionDetail.tsx b/src/modules/scheduling/SuggestionDetail.tsx
index 19b17e3..96f6ff3 100644
--- a/src/modules/scheduling/SuggestionDetail.tsx
+++ b/src/modules/scheduling/SuggestionDetail.tsx
@@ -229,7 +229,7 @@ export default function SuggestionDetail({ suggestion: s, onClose, onNotifySucce
{c.region}
{c.vehicleType}
{c.targetName || '库存'}
- 剩余{v.daysLeft}天
+ 剩余{c.daysLeft}天
{c.canQualifyAfterSwap ? (
diff --git a/src/modules/scheduling/types.ts b/src/modules/scheduling/types.ts
index a90bbbd..d7a0c18 100644
--- a/src/modules/scheduling/types.ts
+++ b/src/modules/scheduling/types.ts
@@ -25,6 +25,7 @@ export interface CandidateVehicle {
totalMileage: number;
completionRate: number;
yearTarget: number | null;
+ daysLeft: number;
region: string;
province: string;
mileageGap: number;
diff --git a/src/server/routes/scheduling/algorithm.ts b/src/server/routes/scheduling/algorithm.ts
index 4caa611..5dea032 100644
--- a/src/server/routes/scheduling/algorithm.ts
+++ b/src/server/routes/scheduling/algorithm.ts
@@ -90,13 +90,10 @@ export function generateSuggestions(
// Among those, prefer the one with the smallest gap (easiest to finish).
// Exclude already-qualified (>= 100%) — no value in swapping those.
for (const vehicle of hopeless) {
- const customerCanAdd = vehicle.customerAvgDaily * vehicle.daysLeft;
-
const candidates: CandidateVehicle[] = inventoryVehicles
.filter((inv) => {
if (!isTypeCompatible(vehicle.vehicleType, inv.vehicleType)) return false;
if (inv.region !== vehicle.region) return false;
- // Exclude already fully qualified
const effectiveTarget = inv.yearTarget ?? vehicle.yearTarget;
if (effectiveTarget > 0 && inv.totalMileage >= effectiveTarget) return false;
return true;
@@ -104,7 +101,9 @@ export function generateSuggestions(
.map((inv) => {
const effectiveTarget = inv.yearTarget ?? vehicle.yearTarget;
const mileageGap = Math.max(0, effectiveTarget - inv.totalMileage);
- const predictedAfterSwap = inv.totalMileage + customerCanAdd;
+ // Use candidate's own daysLeft for prediction
+ const candidateCanAdd = vehicle.customerAvgDaily * inv.daysLeft;
+ const predictedAfterSwap = inv.totalMileage + candidateCanAdd;
const canQualifyAfterSwap = predictedAfterSwap >= effectiveTarget;
return {
plateNumber: inv.plateNumber,
@@ -114,6 +113,7 @@ export function generateSuggestions(
totalMileage: inv.totalMileage,
completionRate: inv.completionRate,
yearTarget: inv.yearTarget ?? vehicle.yearTarget,
+ daysLeft: inv.daysLeft,
region: inv.region,
province: inv.province,
mileageGap,
@@ -133,7 +133,7 @@ export function generateSuggestions(
const gap = Math.max(0, vehicle.yearTarget - vehicle.currentYearMileage);
const dailyReq = vehicle.daysLeft > 0 ? Math.round(gap / vehicle.daysLeft) : 0;
- const predictedTotal = Math.round(vehicle.currentYearMileage + customerCanAdd);
+ const predictedTotal = Math.round(vehicle.currentYearMileage + vehicle.customerAvgDaily * vehicle.daysLeft);
const reason = `客户日均 ${Math.round(vehicle.customerAvgDaily)} km | 考核周期剩余 ${vehicle.daysLeft} 天 · 日均需 ${fmtKmSimple(dailyReq)} km\n!!预估无法达标,需替换`;
suggestions.push({
@@ -165,8 +165,9 @@ export function generateSuggestions(
.map((inv) => {
const effectiveTarget = inv.yearTarget ?? vehicle.yearTarget;
const mileageGap = Math.max(0, effectiveTarget - inv.totalMileage);
- const predictedAfterSwap =
- inv.totalMileage + vehicle.customerAvgDaily * vehicle.daysLeft;
+ // Use candidate's own daysLeft for prediction
+ const candidateCanAdd = vehicle.customerAvgDaily * inv.daysLeft;
+ const predictedAfterSwap = inv.totalMileage + candidateCanAdd;
const canQualifyAfterSwap = predictedAfterSwap >= effectiveTarget;
return {
plateNumber: inv.plateNumber,
@@ -176,6 +177,7 @@ export function generateSuggestions(
totalMileage: inv.totalMileage,
completionRate: inv.completionRate,
yearTarget: inv.yearTarget ?? vehicle.yearTarget,
+ daysLeft: inv.daysLeft,
region: inv.region,
province: inv.province,
mileageGap,
diff --git a/src/server/routes/scheduling/suggestions.ts b/src/server/routes/scheduling/suggestions.ts
index d0b3924..5c9f892 100644
--- a/src/server/routes/scheduling/suggestions.ts
+++ b/src/server/routes/scheduling/suggestions.ts
@@ -250,12 +250,21 @@ app.get('/', async (c) => {
// Cross-reference with assessment data
const assessment = assessmentByPlate.get(plate);
+ // Compute this vehicle's own daysLeft from its assessment end date
+ let invDaysLeft = 0;
+ if (assessment?.current_year_assessment_end_date) {
+ const endDate = new Date(assessment.current_year_assessment_end_date);
+ invDaysLeft = Math.max(1, Math.ceil((endDate.getTime() - now.getTime()) / 86400000));
+ } else {
+ invDaysLeft = Math.max(1, Math.ceil((yearEnd.getTime() - now.getTime()) / 86400000));
+ }
inventoryVehicles.push({
plateNumber: plate,
vehicleType,
region,
province,
totalMileage: assessment ? Number(assessment.vehicle_total_mileage) || 0 : 0,
+ daysLeft: invDaysLeft,
targetId: assessment ? (assessment.target_id as number) : null,
targetName: assessment ? (targetMap.get(assessment.target_id)?.targetName ?? null) : null,
yearTarget: assessment ? Number(assessment.current_year_mileage_task) || null : null,
diff --git a/src/server/routes/scheduling/types.ts b/src/server/routes/scheduling/types.ts
index 5cf7b43..65a998b 100644
--- a/src/server/routes/scheduling/types.ts
+++ b/src/server/routes/scheduling/types.ts
@@ -25,6 +25,7 @@ export interface CandidateVehicle {
totalMileage: number;
completionRate: number;
yearTarget: number | null;
+ daysLeft: number;
region: string;
province: string;
mileageGap: number;
@@ -97,6 +98,7 @@ export interface InventoryVehicle {
region: string;
province: string;
totalMileage: number;
+ daysLeft: number;
targetId: number | null;
targetName: string | null;
yearTarget: number | null;