feat(P19ERP1-3,P19ERP1-4): 实现ERP凭证导出与管理入口

这个提交包含在:
2026-03-08 16:29:05 +08:00
父节点 a1b1d7cfc9
当前提交 855bcdffeb
共修改 14 个文件,包含 1361 行新增和 145 行删除
@@ -0,0 +1,80 @@
const {
buildReimbursementJournalDedupeKey,
buildReimbursementJournalPayload,
enqueueReimbursementJournalExport,
} = require('../../../src/services/integrations/erpJournal');
describe('ERP Journal Service', () => {
afterEach(() => {
vi.clearAllMocks();
});
it('prefers owner legal entity code and keeps cost center null', () => {
const payload = buildReimbursementJournalPayload({
reimbursement: {
id: 'rb-1',
form_number: 'BX-001',
owner_id: 'owner-1',
submitter_id: 'submitter-1',
department_id: 'dept-1',
total_amount: { toString: () => '88.80' },
payment_date: '2026-03-08T00:00:00.000Z',
paid_at: '2026-03-08T12:00:00.000Z',
owner: { legal_entity: { code: 'OWNER_LE' } },
submitter: { legal_entity: { code: 'SUBMITTER_LE' } },
},
});
expect(payload.dimensions.legalEntityCode).toBe('OWNER_LE');
expect(payload.dimensions.costCenterCode).toBeNull();
expect(payload.currencyCode).toBe('CNY');
});
it('falls back to submitter legal entity code', () => {
const payload = buildReimbursementJournalPayload({
reimbursement: {
id: 'rb-1',
form_number: 'BX-001',
owner: { legal_entity: null },
submitter: { legal_entity: { code: 'SUBMITTER_LE' } },
},
});
expect(payload.dimensions.legalEntityCode).toBe('SUBMITTER_LE');
});
it('builds dedupe key and enqueues standard export event', async () => {
const prisma = {
integrationOutbox: {
findUnique: vi.fn().mockResolvedValue(null),
create: vi.fn().mockResolvedValue({ id: 'outbox-1', status: 'pending' }),
},
};
const config = { integrationOutboxMaxAttempts: 5 };
const reimbursement = {
id: 'rb-1',
form_number: 'BX-001',
owner_id: 'owner-1',
submitter_id: 'submitter-1',
department_id: 'dept-1',
};
const result = await enqueueReimbursementJournalExport(prisma, config, {
reimbursement,
actorId: 'actor-1',
});
expect(buildReimbursementJournalDedupeKey('rb-1')).toBe('reimbursement:rb-1:paid');
expect(prisma.integrationOutbox.findUnique).toHaveBeenCalledWith({
where: {
event_type_dedupe_key: {
event_type: 'erp.journal.export',
dedupe_key: 'reimbursement:rb-1:paid',
},
},
select: { id: true, status: true },
});
expect(prisma.integrationOutbox.create).toHaveBeenCalled();
expect(result.eventType).toBe('erp.journal.export');
});
});