311 lines
9.5 KiB
TypeScript
311 lines
9.5 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import type { FlowStatsResponse, WeeklyDetailItem } from './api';
|
|
import type {
|
|
CustomerStats,
|
|
DeptGroup,
|
|
ManagerStats,
|
|
RegionalInventoryStats,
|
|
VehicleListItem,
|
|
} from './types';
|
|
import {
|
|
buildCustomerPieData,
|
|
buildVehicleModalRequest,
|
|
deriveCustomerView,
|
|
deriveDepartmentView,
|
|
deriveInventoryView,
|
|
deriveModalVehicleView,
|
|
formatLocalDate,
|
|
formatLocalDateTime,
|
|
getWeeklyFlowRange,
|
|
groupInventoryByModel,
|
|
selectFlowDetails,
|
|
} from './model';
|
|
|
|
const emptyInventoryFilters = { region: '', city: '', brand: '', type: '', model: '' };
|
|
const emptyCustomerFilters = { customer: [], brand: '', department: '', manager: '', region: '' };
|
|
const emptyModalFilters = { plateNumber: '', model: '', brand: '', location: '' };
|
|
|
|
function inventory(overrides: Partial<RegionalInventoryStats>): RegionalInventoryStats {
|
|
return {
|
|
region: '广东',
|
|
city: '广州',
|
|
brand: '现代',
|
|
type: '18T',
|
|
model: 'M1',
|
|
batch: 'B1',
|
|
quantity: 1,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function manager(overrides: Partial<ManagerStats>): ManagerStats {
|
|
return {
|
|
manager: '负责人甲',
|
|
department: '业务一部',
|
|
t4_5: 0,
|
|
t4_5c: 0,
|
|
t18: 0,
|
|
t49: 0,
|
|
trailer: 0,
|
|
other: 0,
|
|
total: 0,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function department(name: string, managers: ManagerStats[]): DeptGroup {
|
|
return {
|
|
department: name,
|
|
totalAssets: managers.reduce((sum, item) => sum + item.total, 0),
|
|
operatingCount: 0,
|
|
idleCount: 0,
|
|
attendanceRate: 0,
|
|
avgMileage: 0,
|
|
managers,
|
|
};
|
|
}
|
|
|
|
function customer(overrides: Partial<CustomerStats>): CustomerStats {
|
|
return {
|
|
customer: '客户甲',
|
|
manager: '负责人甲',
|
|
brand: '现代',
|
|
department: '业务一部',
|
|
region: '广东',
|
|
city: '广州',
|
|
t4_5: 0,
|
|
t4_5c: 0,
|
|
t18: 0,
|
|
t49: 0,
|
|
trailer: 0,
|
|
other: 0,
|
|
total: 1,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function vehicle(overrides: Partial<VehicleListItem>): VehicleListItem {
|
|
return {
|
|
id: 1,
|
|
plateNumber: '粤A00001',
|
|
vin: 'VIN-1',
|
|
type: '18T',
|
|
model: 'M1',
|
|
location: '广州',
|
|
province: '广东',
|
|
city: '广州',
|
|
status: '运营',
|
|
ownership: '自营',
|
|
contractNo: null,
|
|
customerName: null,
|
|
subjectOrg: null,
|
|
departmentName: null,
|
|
customerManager: null,
|
|
brandLabel: '现代',
|
|
orgName: null,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test('本地日期格式不引入 UTC 偏移', () => {
|
|
const date = new Date(2026, 7, 3, 9, 7, 5);
|
|
assert.equal(formatLocalDate(date), '2026-08-03');
|
|
assert.equal(formatLocalDateTime(date), '2026-08-03 09:07:05');
|
|
});
|
|
|
|
test('周流转区间按周六至周五计算', () => {
|
|
assert.deepEqual(getWeeklyFlowRange(new Date(2026, 7, 12)), {
|
|
start: '2026-08-08',
|
|
end: '2026-08-14',
|
|
});
|
|
assert.deepEqual(getWeeklyFlowRange(new Date(2026, 7, 15)), {
|
|
start: '2026-08-08',
|
|
end: '2026-08-14',
|
|
});
|
|
assert.deepEqual(getWeeklyFlowRange(new Date(2026, 7, 16)), {
|
|
start: '2026-08-08',
|
|
end: '2026-08-14',
|
|
});
|
|
});
|
|
|
|
test('交还替明细走周接口且原样保留周筛选参数', () => {
|
|
assert.deepEqual(
|
|
buildVehicleModalRequest({
|
|
batch: 'All',
|
|
model: 'M1',
|
|
location: '广州',
|
|
category: 'Delivered',
|
|
source: 'asset',
|
|
}, '主体甲'),
|
|
{
|
|
kind: 'weekly',
|
|
type: 'delivered',
|
|
filters: { model: 'M1', batch: 'All', location: '广州', source: 'asset' },
|
|
},
|
|
);
|
|
});
|
|
|
|
test('待交付与车型映射继续走车辆列表参数', () => {
|
|
assert.deepEqual(
|
|
buildVehicleModalRequest({
|
|
batch: 'B1',
|
|
model: 'M1',
|
|
location: '广州',
|
|
category: 'Pending',
|
|
type: '4.5T',
|
|
isColdChain: true,
|
|
isTrailer: false,
|
|
manager: '负责人甲',
|
|
}, '主体甲'),
|
|
{
|
|
kind: 'vehicles',
|
|
params: {
|
|
batch: 'B1',
|
|
model: 'M1',
|
|
location: '广州',
|
|
category: 'Pending',
|
|
manager: '负责人甲',
|
|
vehicleType: '4.5T冷链',
|
|
subject: '主体甲',
|
|
},
|
|
},
|
|
);
|
|
|
|
assert.deepEqual(
|
|
buildVehicleModalRequest({
|
|
batch: 'All',
|
|
model: 'All',
|
|
location: 'All',
|
|
isColdChain: false,
|
|
isTrailer: true,
|
|
}, null),
|
|
{
|
|
kind: 'vehicles',
|
|
params: { isColdChain: 'false', isTrailer: 'true', subject: null },
|
|
},
|
|
);
|
|
});
|
|
|
|
test('库存筛选与区域、车型分组保持既有顺序', () => {
|
|
const rows = [
|
|
inventory({ region: '广东', city: '广州', type: '18T', model: 'M1', quantity: 2 }),
|
|
inventory({ region: '浙江', city: '嘉兴', type: '4.5T普货', model: 'M2', quantity: 3 }),
|
|
inventory({ region: '广东', city: '佛山', type: '18T', model: 'M3', quantity: 4 }),
|
|
inventory({ region: '广东', city: '广州', type: '新增车型', model: 'M4', quantity: 5 }),
|
|
];
|
|
const view = deriveInventoryView(
|
|
rows,
|
|
{ ...emptyInventoryFilters, region: '广东', type: '18T' },
|
|
'18T',
|
|
);
|
|
|
|
assert.deepEqual(view.filtered.map((item) => item.model), ['M1', 'M3']);
|
|
assert.deepEqual(view.modelsForType, ['M1', 'M3']);
|
|
assert.deepEqual(Object.keys(view.byRegion), ['广东']);
|
|
assert.deepEqual(Object.keys(view.byRegion['广东']), ['广州', '佛山']);
|
|
assert.deepEqual(Object.keys(view.byModel), ['18T']);
|
|
assert.deepEqual(
|
|
Object.keys(groupInventoryByModel(rows)),
|
|
['4.5T普货', '18T', '新增车型'],
|
|
);
|
|
});
|
|
|
|
test('部门负责人去重、分组并按车辆数倒序', () => {
|
|
const departments = [
|
|
department('业务二部', [
|
|
manager({ manager: '负责人乙', department: '业务二部', total: 3 }),
|
|
manager({ manager: '负责人甲', department: '业务二部', total: 8 }),
|
|
]),
|
|
department('业务一部', [manager({ manager: '负责人甲', total: 5 })]),
|
|
];
|
|
const view = deriveDepartmentView(departments, 'All');
|
|
|
|
assert.deepEqual(view.managers, ['负责人乙', '负责人甲']);
|
|
assert.deepEqual(view.groupedManagers, [
|
|
{ department: '业务二部', managers: ['负责人乙', '负责人甲'] },
|
|
{ department: '业务一部', managers: ['负责人甲'] },
|
|
]);
|
|
assert.deepEqual(view.managerStats.map((item) => item.total), [8, 5, 3]);
|
|
});
|
|
|
|
test('客户筛选与负责人分组合并部门和客户来源', () => {
|
|
const departments = [
|
|
department('业务二部', [manager({ manager: '负责人乙', department: '业务二部' })]),
|
|
department('业务一部', [manager({ manager: '负责人甲' })]),
|
|
];
|
|
const customers = [
|
|
customer({ customer: '客户甲', department: '业务一部', manager: '负责人甲', total: 2 }),
|
|
customer({ customer: '客户乙', department: '业务二部', manager: '负责人丙', region: '浙江', city: '嘉兴', total: 3 }),
|
|
customer({ customer: '公务车', department: '公务车', manager: '负责人丁', total: 1 }),
|
|
];
|
|
const view = deriveCustomerView(
|
|
customers,
|
|
departments,
|
|
{ ...emptyCustomerFilters, customer: ['客户乙'], region: '浙江' },
|
|
);
|
|
|
|
assert.deepEqual(view.filtered.map((item) => item.customer), ['客户乙']);
|
|
assert.deepEqual(view.departments, ['业务一部', '业务二部', '公务车']);
|
|
assert.deepEqual(view.managersByDepartment, [
|
|
{ department: '业务一部', managers: ['负责人甲'] },
|
|
{ department: '业务二部', managers: ['负责人乙', '负责人丙'] },
|
|
{ department: '公务车', managers: ['负责人丁'] },
|
|
]);
|
|
});
|
|
|
|
test('弹窗筛选沿用车牌为空时回退 VIN 的规则', () => {
|
|
const vehicles = [
|
|
vehicle({ id: 1 }),
|
|
vehicle({ id: 2, plateNumber: '', vin: 'VIN-2', model: 'M2', brandLabel: '福田', location: '嘉兴' }),
|
|
];
|
|
const weeklyDetails: WeeklyDetailItem[] = [
|
|
{ truck_id: 1, plate_number: '粤A00001', handover_date: null, contract_type: null, customer_name: null },
|
|
{ truck_id: 2, plate_number: 'VIN-2', handover_date: null, contract_type: null, customer_name: null },
|
|
];
|
|
const view = deriveModalVehicleView(
|
|
vehicles,
|
|
weeklyDetails,
|
|
{ ...emptyModalFilters, plateNumber: 'VIN-2' },
|
|
);
|
|
|
|
assert.deepEqual(view.plates, ['粤A00001', 'VIN-2']);
|
|
assert.deepEqual(view.filteredVehicles.map((item) => item.id), [2]);
|
|
assert.deepEqual(view.filteredWeeklyDetails.map((item) => item.truck_id), [2]);
|
|
});
|
|
|
|
test('流转明细按日期和类型同时筛选,客户饼图按区域汇总', () => {
|
|
const flowStats: FlowStatsResponse = {
|
|
start: '2026-08-08',
|
|
end: '2026-08-14',
|
|
daily: [],
|
|
totals: { delivered: 1, returned: 1, replaced: 0, total: 2 },
|
|
details: [
|
|
{
|
|
id: '1', type: 'delivered', typeLabel: '交车', date: '2026-08-12', truckId: '1',
|
|
plateNumber: '粤A00001', eventTime: null, submitTime: null, department: '', manager: '', customerName: null,
|
|
},
|
|
{
|
|
id: '2', type: 'returned', typeLabel: '还车', date: '2026-08-12', truckId: '2',
|
|
plateNumber: '粤A00002', eventTime: null, submitTime: null, department: '', manager: '', customerName: null,
|
|
},
|
|
],
|
|
};
|
|
assert.deepEqual(
|
|
selectFlowDetails(flowStats, { date: '2026-08-12', type: 'delivered' }).map((item) => item.id),
|
|
['1'],
|
|
);
|
|
|
|
const customers = [
|
|
customer({ region: '广东', total: 2 }),
|
|
customer({ customer: '客户乙', region: '浙江', total: 5 }),
|
|
customer({ customer: '客户丙', region: '广东', total: 3 }),
|
|
];
|
|
assert.deepEqual(buildCustomerPieData(customers, 'region', []), [
|
|
{ name: '广东', value: 5 },
|
|
{ name: '浙江', value: 5 },
|
|
]);
|
|
const provinceData = [{ name: '广东省', value: 10 }];
|
|
assert.equal(buildCustomerPieData(customers, 'province', provinceData), provinceData);
|
|
});
|