import assert from 'node:assert/strict'; import test from 'node:test'; import { buildMileageAttribution, filterAttributionVehicles, reconcileMileageAttribution, } from './attribution.js'; import type { TargetVehicle } from './types.js'; function vehicle(overrides: Partial): TargetVehicle { return { plateNumber: '粤A00000', todayMileage: 0, totalMileage: 0, completionRate: 0, isQualified: false, currentYearIsQualified: false, dailyRequiredMileage: 100, rentStatus: null, department: null, customer: null, isOnline: false, ...overrides, }; } test('groups vehicles and calculates additive attribution metrics', () => { const result = buildMileageAttribution([ vehicle({ plateNumber: '粤A1', department: '业务一部', todayMileage: 120, totalMileage: 1000, completionRate: 0.8, isOnline: true }), vehicle({ plateNumber: '粤A2', department: '业务一部', todayMileage: 40, totalMileage: 500, completionRate: 0.4 }), vehicle({ plateNumber: '粤A3', department: '业务二部', todayMileage: 40, totalMileage: 400, completionRate: 0.2 }), ], 'department', 1000); const first = result.find(group => group.key === '业务一部'); assert.deepEqual(first, { key: '业务一部', label: '业务一部', vehicleCount: 2, onlineCount: 1, zeroMileageCount: 0, behindCount: 1, todayMileage: 160, totalMileage: 1500, targetMileage: 2000, completedMileage: 1500, shortfallMileage: 500, completionRate: 75, contributionRate: 80, }); }); test('keeps unassigned vehicles visible and sorts largest shortfall first', () => { const result = buildMileageAttribution([ vehicle({ plateNumber: '粤A1', department: null, todayMileage: 0 }), vehicle({ plateNumber: '粤A2', department: '业务一部', todayMileage: 80, totalMileage: 100 }), ], 'department', 100); assert.equal(result[0].label, '未分配部门'); assert.equal(result[0].shortfallMileage, 100); assert.equal(result[0].zeroMileageCount, 1); }); test('reconciles additive group shortfall with the target summary at display precision', () => { const groups = buildMileageAttribution([ vehicle({ plateNumber: '粤A1', department: '业务一部', totalMileage: 1200 }), vehicle({ plateNumber: '粤A2', department: '业务二部', totalMileage: 499.96 }), ], 'department', 1000); assert.deepEqual(reconcileMileageAttribution(groups, 500), { matches: true, attributedShortfall: 500, expectedShortfall: 500, difference: 0, }); assert.equal(reconcileMileageAttribution(groups, 450).difference, 50); }); test('filters a group using the same empty-value semantics as aggregation', () => { const vehicles = [ vehicle({ plateNumber: '粤A1', customer: null }), vehicle({ plateNumber: '粤A2', customer: '客户甲' }), ]; assert.deepEqual( filterAttributionVehicles(vehicles, 'customer', '未绑定客户').map(item => item.plateNumber), ['粤A1'], ); });