feat(assets): align flow statistics and separate abnormal inventory
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/push/woodpecker Pipeline failed
Use operations signing times and delivery completion statuses; include replacement deliveries in delivery totals with range drilldowns and exports. Separate abnormal vehicles from inventory, add a date range picker, and enable CI for feature/asset-statistics.
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import router from '../vehicles.js';
|
||||
import { vehicleRepository, type FlowDetailRow } from './repository.js';
|
||||
import type { Vehicle } from '../../types.js';
|
||||
|
||||
test('replacement trips are included in delivery counts without inflating totals or duplicating export rows', async (t) => {
|
||||
t.mock.method(vehicleRepository, 'getVehicles', async () => [{ id: '1' } as Vehicle]);
|
||||
const row = (id: string, type: FlowDetailRow['type'], date: string): FlowDetailRow => ({
|
||||
id, type, type_label: type === 'replaced' ? '替换交车' : type === 'delivered' ? '交车' : '还车',
|
||||
stat_date: date, truck_id: '1', plate_number: '测试车牌',
|
||||
event_time: `${date} 12:00:00`, submit_time: '2026-09-01 12:00:00',
|
||||
department: null, manager: null, customer_name: null,
|
||||
});
|
||||
t.mock.method(vehicleRepository, 'getFlowDetailRows', async () => [
|
||||
row('delivered-1', 'delivered', '2026-09-15'),
|
||||
row('replaced-2', 'replaced', '2026-09-15'),
|
||||
row('returned-1', 'returned', '2026-09-15'),
|
||||
row('replaced-3', 'replaced', '2026-09-16'),
|
||||
]);
|
||||
const response = await router.request('/flow-stats?start=2026-09-15&end=2026-09-17');
|
||||
assert.equal(response.status, 200);
|
||||
const data = await response.json();
|
||||
assert.deepEqual(data.totals, { delivered: 3, returned: 1, replaced: 2, total: 4 });
|
||||
assert.deepEqual(data.daily, [
|
||||
{ date: '2026-09-15', delivered: 2, returned: 1, replaced: 1, total: 3 },
|
||||
{ date: '2026-09-16', delivered: 1, returned: 0, replaced: 1, total: 1 },
|
||||
{ date: '2026-09-17', delivered: 0, returned: 0, replaced: 0, total: 0 },
|
||||
]);
|
||||
assert.equal(data.details.length, 4);
|
||||
assert.equal(new Set(data.details.map((x: { id: string }) => x.id)).size, 4);
|
||||
assert.ok(data.details.filter((x: { type: string }) => x.type === 'replaced')
|
||||
.every((x: { typeLabel: string }) => x.typeLabel === '替换交车'));
|
||||
});
|
||||
|
||||
test('inventory excludes abnormal vehicles in totals, type rollups, regions and drilldowns', async (t) => {
|
||||
const vehicles = ['Inventory', 'Abnormal', 'Pending', 'Operating'].map((status, i) => ({
|
||||
id: String(i), status, operationStatus: status === 'Operating' ? '1' : '3',
|
||||
plateNumber: `测试${i}`, model: '现代4.5T普货', type: '4.5T', location: '广东',
|
||||
subjectOrg: i === 1 ? '乙公司' : '甲公司',
|
||||
} as Vehicle));
|
||||
t.mock.method(vehicleRepository, 'getVehicles', async () => vehicles);
|
||||
t.mock.method(vehicleRepository, 'getWeeklyTruckIds', async () => ({
|
||||
pending: new Set<string>(), delivered: new Set<string>(), returned: new Set<string>(), replaced: new Set<string>(),
|
||||
}));
|
||||
const get = async (path: string) => {
|
||||
const response = await router.request(path);
|
||||
assert.equal(response.status, 200);
|
||||
return response.json();
|
||||
};
|
||||
const summary = await get('/summary');
|
||||
assert.equal(summary.totalAssets, 4);
|
||||
assert.equal(summary.inventory.total, 1);
|
||||
assert.equal(summary.inventory.abnormal, 1);
|
||||
assert.equal(summary.totalAssets, summary.inventory.total + summary.inventory.abnormal + summary.operating.total + summary.pendingDelivery);
|
||||
assert.deepEqual((await get('/list?category=Inventory')).map((v: Vehicle) => v.id), ['0']);
|
||||
assert.deepEqual((await get('/list?category=Abnormal')).map((v: Vehicle) => v.id), ['1']);
|
||||
assert.deepEqual(await get('/list?category=Abnormal&subject=甲公司'), []);
|
||||
const groups = await get('/by-type');
|
||||
assert.equal(groups.reduce((n: number, g: { totalInventory: number }) => n + g.totalInventory, 0), 1);
|
||||
assert.equal(groups.reduce((n: number, g: { totalAbnormal: number }) => n + g.totalAbnormal, 0), 1);
|
||||
const model = groups.flatMap((g: { models: unknown[] }) => g.models).find((m: { abnormal: number }) => m.abnormal === 1);
|
||||
assert.equal(model.inventory, 1);
|
||||
assert.equal(model.inventoryRegions['广东'], 1);
|
||||
const inventory = await get('/inventory-stats');
|
||||
assert.equal(inventory.reduce((n: number, row: { quantity: number }) => n + row.quantity, 0), 1);
|
||||
});
|
||||
Reference in New Issue
Block a user