const { buildPurchaseAccrualJournalPayload, buildPurchaseArchiveSummaryPayload, buildPurchaseCommitmentJournalPayload, enqueuePurchaseArchiveSummaryExport, hashProcurementPayload, } = require('../../../src/services/integrations/erpProcurement'); describe('ERP Procurement Service', () => { it('builds commitment payload with procurement dimensions', () => { const payload = buildPurchaseCommitmentJournalPayload({ purchaseRequest: { id: 'pr-1', request_no: 'PR-001', applicant_id: 'applicant-1', submitter_id: 'submitter-1', department_id: 'dept-1', total_amount: { toString: () => '880.00' }, currency_code: 'CNY', legal_entity: { code: 'HY' }, cost_center: { code: 'IT001' }, supplier: { code: 'SUP001' }, budget_pool: { code: 'BG-IT-2026' }, document_category: { code: 'PR_STANDARD' }, }, occurredAt: '2026-03-08T10:00:00.000Z', }); expect(payload.journalType).toBe('purchase_request_commitment'); expect(payload.references.requestNo).toBe('PR-001'); expect(payload.dimensions.legalEntityCode).toBe('HY'); expect(payload.dimensions.costCenterCode).toBe('IT001'); expect(payload.dimensions.supplierCode).toBe('SUP001'); expect(payload.dimensions.budgetPoolCode).toBe('BG-IT-2026'); }); it('builds accrual payload from archive facts', () => { const payload = buildPurchaseAccrualJournalPayload({ purchaseRequest: { id: 'pr-1', request_no: 'PR-001', applicant_id: 'applicant-1', submitter_id: 'submitter-1', department_id: 'dept-1', total_amount: '880.00', currency_code: 'CNY', legal_entity: { code: 'HY' }, cost_center: { code: 'IT001' }, supplier: { code: 'SUP001' }, budget_pool: { code: 'BG-IT-2026' }, }, archive: { id: 'archive-1', po_no: 'PO-001', contract_no: 'CT-001', archived_at: '2026-03-08T10:30:00.000Z', }, }); expect(payload.journalType).toBe('purchase_order_accrual'); expect(payload.entityId).toBe('archive-1'); expect(payload.documentNo).toBe('PO-001'); expect(payload.references.contractNo).toBe('CT-001'); }); it('builds archive summary payload and hashes payload changes', () => { const basePayload = buildPurchaseArchiveSummaryPayload({ purchaseRequest: { id: 'pr-1', request_no: 'PR-001', department_id: 'dept-1', total_amount: '880.00', currency_code: 'CNY', legal_entity: { code: 'HY' }, cost_center: { code: 'IT001' }, supplier: { id: 'sup-1', code: 'SUP001', name: '演示供应商' }, }, archive: { id: 'archive-1', po_no: 'PO-001', contract_no: 'CT-001', archived_at: '2026-03-08T10:30:00.000Z', archived_files: [ { storagePath: 'purchase-requests/pr-1/contract.pdf', originalName: 'contract.pdf', mimeType: 'application/pdf', fileSize: 1024, }, ], }, }); const changedPayload = { ...basePayload, contractNo: 'CT-002', }; expect(basePayload.files).toHaveLength(1); expect(hashProcurementPayload(basePayload)).not.toBe(hashProcurementPayload(changedPayload)); }); it('resets archive summary outbox when payload changes', async () => { const prisma = { integrationOutbox: { findUnique: vi.fn().mockResolvedValue({ id: 'outbox-1', status: 'succeeded', payload: { contractNo: 'CT-001', poNo: 'PO-001' }, }), update: vi.fn().mockResolvedValue({ id: 'outbox-1', status: 'pending' }), }, }; const result = await enqueuePurchaseArchiveSummaryExport( prisma, { integrationOutboxMaxAttempts: 5 }, { actorId: 'actor-1', purchaseRequest: { id: 'pr-1', request_no: 'PR-001', department_id: 'dept-1', total_amount: '880.00', currency_code: 'CNY', supplier: { id: 'sup-1', code: 'SUP001', name: '演示供应商' }, legal_entity: { code: 'HY' }, cost_center: { code: 'IT001' }, }, archive: { id: 'archive-1', po_no: 'PO-001', contract_no: 'CT-002', archived_at: '2026-03-08T10:30:00.000Z', archived_files: [], }, }, ); expect(prisma.integrationOutbox.update).toHaveBeenCalled(); expect(result.requeued).toBe(true); expect(result.eventType).toBe('erp.procurement.export'); }); });