feat(assets): refine inventory presentation and abnormal vehicle details
This commit is contained in:
@@ -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);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user