139 行
4.6 KiB
JavaScript
139 行
4.6 KiB
JavaScript
const {
|
|
buildAssetLedgerSummaryDedupeKey,
|
|
buildAssetLedgerSummaryPayload,
|
|
enqueueAssetLedgerSummaryExport,
|
|
hashAssetPayload,
|
|
resolveAssetLedgerSummaryChange,
|
|
} = require('../../../src/services/integrations/erpAsset');
|
|
|
|
describe('ERP Asset Service', () => {
|
|
function createAsset(overrides = {}) {
|
|
return {
|
|
id: 'asset-1',
|
|
asset_no: 'ZC-001',
|
|
name: 'ThinkPad X1',
|
|
status: 'in_use',
|
|
location: '上海办公室-A区',
|
|
activated_at: '2026-03-09T00:00:00.000Z',
|
|
retired_at: null,
|
|
created_at: '2026-03-09T08:00:00.000Z',
|
|
updated_at: '2026-03-09T08:00:00.000Z',
|
|
legal_entity_id: 'le-1',
|
|
cost_center_id: 'cc-1',
|
|
purchase_request_id: 'pr-1',
|
|
assignee_employee_id: 'emp-1',
|
|
category: {
|
|
id: 'cat-1',
|
|
code: 'LAPTOP',
|
|
name: '笔记本电脑',
|
|
document_category: { code: 'ASSET_STD' },
|
|
},
|
|
legal_entity: { id: 'le-1', code: 'HY', name: '海音达' },
|
|
cost_center: { id: 'cc-1', code: 'IT001', name: 'IT中心' },
|
|
purchase_request: { id: 'pr-1', request_no: 'PR-001', title: '办公电脑采购' },
|
|
assignee: { id: 'emp-1', employee_no: 'E0001', name: '张三' },
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
it('builds asset ledger summary payload for create event', () => {
|
|
const payload = buildAssetLedgerSummaryPayload({
|
|
asset: createAsset(),
|
|
sourceEventName: 'asset.card.created',
|
|
changeType: 'created',
|
|
changedFields: [],
|
|
occurredAt: '2026-03-09T08:00:00.000Z',
|
|
});
|
|
|
|
expect(payload.exportType).toBe('asset_ledger_summary');
|
|
expect(payload.sourceEventName).toBe('asset.card.created');
|
|
expect(payload.changeType).toBe('created');
|
|
expect(payload.assetNo).toBe('ZC-001');
|
|
expect(payload.category.code).toBe('LAPTOP');
|
|
expect(payload.legalEntity.code).toBe('HY');
|
|
expect(payload.costCenter.code).toBe('IT001');
|
|
expect(payload.purchaseRequest.requestNo).toBe('PR-001');
|
|
expect(payload.assignee.employeeNo).toBe('E0001');
|
|
});
|
|
|
|
it('detects financial-dimension changes only', () => {
|
|
const previousAsset = createAsset();
|
|
|
|
expect(
|
|
resolveAssetLedgerSummaryChange(previousAsset, {
|
|
...previousAsset,
|
|
name: 'ThinkPad X1 Carbon Gen 12',
|
|
location: '上海办公室-B区',
|
|
}),
|
|
).toBeNull();
|
|
|
|
expect(
|
|
resolveAssetLedgerSummaryChange(previousAsset, {
|
|
...previousAsset,
|
|
cost_center_id: 'cc-2',
|
|
}),
|
|
).toEqual({
|
|
sourceEventName: 'asset.ledger.summary.updated',
|
|
changeType: 'financial_dimensions_changed',
|
|
changedFields: ['costCenterId'],
|
|
});
|
|
|
|
expect(
|
|
resolveAssetLedgerSummaryChange(previousAsset, {
|
|
...previousAsset,
|
|
status: 'retired',
|
|
}),
|
|
).toEqual({
|
|
sourceEventName: 'asset.ledger.summary.updated',
|
|
changeType: 'retired',
|
|
changedFields: ['status'],
|
|
});
|
|
});
|
|
|
|
it('builds deterministic dedupe key and payload hash', () => {
|
|
const asset = createAsset();
|
|
const payload = buildAssetLedgerSummaryPayload({ asset, changedFields: [] });
|
|
const changedPayload = buildAssetLedgerSummaryPayload({
|
|
asset: createAsset({ cost_center: { id: 'cc-2', code: 'IT002', name: 'IT二部' }, cost_center_id: 'cc-2' }),
|
|
sourceEventName: 'asset.ledger.summary.updated',
|
|
changeType: 'financial_dimensions_changed',
|
|
changedFields: ['costCenterId'],
|
|
});
|
|
|
|
expect(buildAssetLedgerSummaryDedupeKey('asset-1')).toBe('asset_card:asset-1:ledger_summary');
|
|
expect(hashAssetPayload(payload)).not.toBe(hashAssetPayload(changedPayload));
|
|
});
|
|
|
|
it('resets same outbox when asset payload changes', async () => {
|
|
const prisma = {
|
|
integrationOutbox: {
|
|
findUnique: vi.fn().mockResolvedValue({
|
|
id: 'outbox-1',
|
|
status: 'succeeded',
|
|
payload: { assetNo: 'ZC-001', costCenter: { id: 'cc-1', code: 'IT001' } },
|
|
}),
|
|
update: vi.fn().mockResolvedValue({ id: 'outbox-1', status: 'pending' }),
|
|
},
|
|
};
|
|
|
|
const result = await enqueueAssetLedgerSummaryExport(
|
|
prisma,
|
|
{ integrationOutboxMaxAttempts: 5 },
|
|
{
|
|
actorId: 'actor-1',
|
|
asset: createAsset({
|
|
cost_center_id: 'cc-2',
|
|
cost_center: { id: 'cc-2', code: 'IT002', name: 'IT二部' },
|
|
}),
|
|
sourceEventName: 'asset.ledger.summary.updated',
|
|
changeType: 'financial_dimensions_changed',
|
|
changedFields: ['costCenterId'],
|
|
},
|
|
);
|
|
|
|
expect(prisma.integrationOutbox.update).toHaveBeenCalled();
|
|
expect(result.eventType).toBe('erp.asset.export');
|
|
expect(result.requeued).toBe(true);
|
|
});
|
|
});
|