feat(assets): refine inventory presentation and abnormal vehicle details

This commit is contained in:
shishengliang
2026-09-23 16:00:59 +08:00
parent 3d4749f28f
commit 0a32392215
15 changed files with 652 additions and 483 deletions
@@ -0,0 +1,109 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { buildVehicleModalRequest, groupInventoryByRegion } from '../../model';
import type { RegionalInventoryStats } from '../../types';
import { inventoryBrandGroups, inventoryGroups, inventoryModelTags, inventorySelection } from './inventory-presentation';
const filters = { region: '华东', city: '上海', brand: '品牌甲', type: '18T', model: '批次一' };
test('filtered inventory totals retain all filters in the vehicle request', () => {
const request = buildVehicleModalRequest(inventorySelection(filters, '筛选结果'), '公司甲');
assert.deepEqual(request, { kind: 'vehicles', params: {
brand: '品牌甲', vehicleType: '18T', model: '批次一', location: '上海',
source: 'asset', category: 'Inventory', subject: '公司甲',
} });
});
test('inventory subsets retain scope while changing category', () => {
for (const category of ['Pending', 'Abnormal'] as const) {
const request = buildVehicleModalRequest(inventorySelection({ ...filters, city: '' }, '子集', { category }), null);
assert.equal(request.kind, 'vehicles');
if (request.kind !== 'vehicles') throw new Error('wrong request type');
assert.equal(request.params.location, '华东');
assert.equal(request.params.category, category);
assert.equal(request.params.vehicleType, '18T');
assert.equal(request.params.brand, '品牌甲');
}
});
test('quantity sorting preserves source order and counts inventory subsets only once', () => {
const rows: RegionalInventoryStats[] = [
{ ...filters, batch: '', quantity: 8, pending: 2, abnormal: 1 },
{ ...filters, city: '苏州', batch: '', quantity: 3 },
{ ...filters, region: '华南', city: '广州', batch: '', quantity: 20 },
];
const source = groupInventoryByRegion(rows);
assert.deepEqual(inventoryGroups(source, 'quantity').map(group => [group.name, group.quantity]), [['华南', 20], ['华东', 11]]);
assert.deepEqual(inventoryGroups(source, 'default').map(group => group.name), ['华东', '华南']);
assert.deepEqual(inventoryGroups({}, 'quantity'), []);
});
test('model tags separate type and qualifiers without inventing a batch', () => {
assert.deepEqual(inventoryModelTags({ type: '4.5T冷链', model: '现代4.5T冷链(交投)', batch: '现代4.5T冷链(交投)' }), [
{ kind: 'type', label: '车型', value: '4.5T冷链' },
{ kind: 'detail', label: '型号说明', value: '交投' },
]);
assert.deepEqual(inventoryModelTags({ type: '18T', model: '飞驰18T(白车)', batch: '2026-01' }).map(tag => tag.value), ['18T', '白车', '2026-01']);
assert.deepEqual(inventoryModelTags({ type: '挂车', model: '挂车', batch: '挂车' }).map(tag => tag.value), ['挂车']);
assert.deepEqual(inventoryModelTags({ type: '', model: '未知型号(未闭合', batch: '' }).map(tag => tag.value), ['未知型号(未闭合']);
});
test('brand removal preserves body style, color and qualifiers in model tags', () => {
assert.deepEqual(inventoryModelTags({ brand: '帕力安牌', type: '18T', model: '现代18T双飞翼(羚牛)', batch: '' }).map(tag => tag.value), ['18T', '双飞翼', '羚牛']);
assert.deepEqual(inventoryModelTags({ brand: '苏龙', type: '18T', model: '苏龙18T飞翼(白色)', batch: '' }).map(tag => tag.value), ['18T', '飞翼', '白色']);
assert.deepEqual(inventoryModelTags({ brand: '飞驰', type: '49T', model: '49T飞驰(红车)', batch: '' }).map(tag => tag.value), ['49T', '红车']);
});
test('brand ordering stays inside city blocks and does not mutate source rows', () => {
const brands = ['其他', '飞驰', '跃进', '帕力安牌', '宇通', '现代', '苏龙', '帕力安', '明威'];
const records = brands.map(brand => ({ ...filters, brand, batch: '', quantity: 1 }));
const source = { 华东: { 上海: records, 苏州: [{ ...records[0], city: '苏州' }] } };
const result = inventoryGroups(source, 'default');
assert.deepEqual(result[0].sections.map(([name]) => name), ['上海', '苏州']);
assert.deepEqual(result[0].sections[0][1].map(row => row.brand), ['现代', '帕力安牌', '帕力安', '宇通', '飞驰', '跃进', '苏龙', '明威', '其他']);
assert.deepEqual(records.map(row => row.brand), brands);
assert.equal(result[0].quantity, 10);
});
test('type view sorts complete model blocks by brand while preserving model grouping', () => {
const row = { ...filters, batch: '', quantity: 1 };
const source = { '18T': {
飞驰白车: [{ ...row, brand: '飞驰' }, { ...row, brand: '飞驰', city: '苏州' }],
现代双飞翼: [{ ...row, brand: '帕力安牌' }],
现代普货: [{ ...row, brand: '现代' }],
} };
const result = inventoryGroups(source, 'default', 'model');
assert.deepEqual(result[0].sections.map(([name]) => name), ['现代普货', '现代双飞翼', '飞驰白车']);
assert.equal(result[0].sections[2][1].length, 2);
});
test('brand and type groups combine nonadjacent variants while retaining all detail records', () => {
const rows: RegionalInventoryStats[] = [
{ ...filters, brand: '飞驰', type: '18T', model: '飞驰18T(白车)', batch: '', quantity: 9 },
{ ...filters, brand: '现代', type: '18T', model: '现代18T双飞翼(羚牛)', batch: '', quantity: 8 },
{ ...filters, brand: '飞驰', type: '49T', model: '49T飞驰', batch: '', quantity: 16 },
{ ...filters, brand: '飞驰', type: '18T', model: '飞驰18T(红车)', batch: '', quantity: 7 },
];
const groups = inventoryBrandGroups(rows, 'type');
assert.deepEqual(groups.map(group => group.brand), ['现代', '飞驰']);
assert.deepEqual(groups[1].variants.map(variant => variant.map(row => row.quantity)), [[9, 7], [16]]);
assert.equal(groups[1].variants[0][0], rows[0]);
assert.equal(groups[1].variants[0][1], rows[3]);
assert.equal(groups.flatMap(group => group.rows).reduce((sum, row) => sum + row.quantity, 0), 40);
assert.deepEqual(rows.map(row => row.quantity), [9, 8, 16, 7]);
assert.deepEqual(groups[1].variants[0].map(row => inventoryModelTags(row).filter(tag => tag.kind !== 'type').map(tag => tag.value)), [['白车'], ['红车']]);
});
test('model grouping merges locations but keeps different brands, colors and batches separate', () => {
const base = { ...filters, brand: '飞驰', type: '18T', model: '飞驰18T(白车)', batch: '一期', quantity: 1 };
const rows = [base, { ...base, city: '苏州' }, { ...base, batch: '二期' }, { ...base, model: '飞驰18T(红车)' }, { ...base, brand: '现代' }];
const groups = inventoryBrandGroups(rows, 'model');
assert.deepEqual(groups.map(group => group.brand), ['现代', '飞驰']);
assert.deepEqual(groups[1].variants.map(variant => variant.length), [2, 1, 1]);
assert.deepEqual(groups[1].variants[0].map(row => row.city), ['上海', '苏州']);
for (const row of groups.flatMap(group => group.rows)) {
assert.equal(inventorySelection(row, '明细').model, row.model);
assert.equal(inventorySelection(row, '明细').brand, row.brand);
assert.equal(inventorySelection(row, '明细').location, row.city);
}
});