import assert from 'node:assert/strict'; import test from 'node:test'; import { statisticsExportRows, vehicleExportRows } from './analysis-export'; import type { VehicleListItem } from './types'; test('statistics export retains all ordered groups and repeats parent labels for spreadsheet filtering', () => { const groups = Array.from({ length: 105 }, (_, i) => ({ values: ['华东', `城市${i}`], count: 1, extra: { 待交车: 0, 异动: Number(i === 104) } })); const rows = statisticsExportRows(['区域', '城市'], groups, 105); assert.equal(rows.length, 105); assert.deepEqual(rows[104], { 区域: '华东', 城市: '城市104', '数量(台)': 1, 待交车: 0, 异动: 1, '占比(%)': 1 }); assert.equal(rows.reduce((n, row) => n + Number(row['数量(台)']), 0), 105); assert.deepEqual(statisticsExportRows([], [{ values: [], count: 3 }], 3, '车次'), [{ 统计范围: '当前筛选结果', '数量(车次)': 3, '占比(%)': 100 }]); }); test('vehicle exports preserve full records, server masking and pending/movement fields without treating missing time as zero', () => { const vehicles = Array.from({ length: 45 }, (_, id) => ({ id, plateNumber: `车-${id}`, vin: '001234', status: 'Inventory', operationStatus: '3', customerName: '测试***公司' } as VehicleListItem)); vehicles[0].pendingDelivery = { contractNo: '001-CONTRACT', customerName: '待交***公司' }; vehicles[44].abnormalMove = { id: 'move', type: '维修', status: '待结束', work: '更换配件', elapsedDays: null } as NonNullable; const rows = vehicleExportRows(vehicles); assert.equal(rows.length, 45); assert.equal(rows[0].VIN, '001234'); assert.equal(rows[0].客户, '测试***公司'); assert.equal(rows[0].待交车合同, '001-CONTRACT'); assert.equal(rows[44].异动类型, '维修'); assert.equal(rows[44]['已异动时长(天)'], null); assert.equal(rows[44].实际完成时间, '未完成'); assert.equal(rows[0].状态, '在库'); assert.equal(rows[0].运营状态, '可运营'); }); test('one Excel workbook contains statistics and all details in two named sheets', async () => { const { createAnalysisWorkbook } = await import('./analysis-export'); const XLSX = await import('xlsx'); const statistics = statisticsExportRows(['区域'], [{ values: ['华东'], count: 105 }], 105); const details = Array.from({ length: 105 }, (_, i) => ({ VIN: `000${i}`, 合同: i === 104 ? 'LAST-CONTRACT' : 'CONTRACT', 项目名称: '项目甲' })); const workbook = await createAnalysisWorkbook(statistics, details); const saved = XLSX.read(XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' }), { type: 'buffer' }); assert.deepEqual(saved.SheetNames, ['统计表', '详情表']); assert.deepEqual(XLSX.utils.sheet_to_json(saved.Sheets['统计表']), statistics); assert.deepEqual(XLSX.utils.sheet_to_json(saved.Sheets['详情表']), details); const empty = await createAnalysisWorkbook([], []); assert.deepEqual(empty.SheetNames, ['统计表', '详情表']); }); test('inventory export uses assigned operations managers and a dash when unset', () => { const vehicles = ['张三、李四', null, ' '].map(opsManagers => ({ opsManagers, customerManager: '业务负责人', customerName: '客户', status: 'Inventory', } as VehicleListItem)); const rows = vehicleExportRows(vehicles, true); assert.deepEqual(rows.map(row => row.运维负责人), ['张三、李四', '-', '-']); assert.ok(rows.every(row => !('业务负责人' in row) && !('客户' in row))); assert.equal(vehicleExportRows(vehicles)[0].业务负责人, '业务负责人'); });