const erp = require('../../../../src/services/integrations/erp'); describe('ERP Sync Services', () => { const originalFetch = global.fetch; afterEach(() => { global.fetch = originalFetch; vi.restoreAllMocks(); }); it('builds reimbursement journal payload via compat helper', () => { const payload = erp.buildErpPayload({ reimbursement: { id: 'r1', form_number: 'F001', owner_id: 'owner-1', submitter_id: 'submitter-1', department_id: 'dept-1', total_amount: { toString: () => '100.00' }, payment_voucher_no: 'PV001', payment_date: '2026-03-01T12:00:00.000Z', paid_at: '2026-03-02T08:00:00.000Z', owner: { legal_entity: { code: 'HY' } }, }, }); expect(payload.journalType).toBe('reimbursement_paid'); expect(payload.documentNo).toBe('F001'); expect(payload.paymentDate).toBe('2026-03-01'); expect(payload.dimensions.legalEntityCode).toBe('HY'); }); it('returns mock synced when journal export provider is mock', async () => { const result = await erp.exportJournalToErp( { erpSyncEnabled: true, erpSyncProvider: 'mock' }, { journalType: 'purchase_request_commitment', documentNo: 'PR-001' }, ); expect(result.ok).toBe(true); expect(result.code).toBe('mock_synced'); expect(result.response.externalId).toContain('PR-001'); }); it('returns mock procurement result when provider is mock', async () => { const result = await erp.exportProcurementToErp( { erpSyncEnabled: true, erpSyncProvider: 'mock' }, { poNo: 'PO-001', contractNo: 'CT-001' }, ); expect(result.ok).toBe(true); expect(result.code).toBe('mock_synced'); expect(result.response.externalPoNo).toContain('PO-001'); expect(result.response.externalContractNo).toContain('CT-001'); }); it('returns mock master data result when provider is mock', async () => { const result = await erp.pullMasterDataFromErp( { erpSyncEnabled: true, erpSyncProvider: 'mock' }, { resources: ['legal_entity', 'supplier'] }, ); expect(result.ok).toBe(true); expect(result.code).toBe('mock_synced'); expect(result.data.legalEntities).toEqual([]); expect(result.data.suppliers).toEqual([]); }); it('handles http master data 4xx errors', async () => { global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 422, headers: { get: () => 'application/json' }, json: vi.fn().mockResolvedValue({ code: 'invalid_payload' }), }); const result = await erp.pullMasterDataFromErp( { erpSyncEnabled: true, erpSyncProvider: 'http', erpMasterDataEndpoint: 'https://example.com/master-data', erpMasterDataTimeoutMs: 500, }, { resources: ['legal_entity'] }, ); expect(result.ok).toBe(false); expect(result.code).toBe('erp_master_data_http_4xx'); expect(result.retryable).toBe(false); }); it('handles http journal export 5xx errors', async () => { global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 503, headers: { get: () => 'application/json' }, json: vi.fn().mockResolvedValue({ code: 'service_unavailable' }), }); const result = await erp.exportJournalToErp( { erpSyncEnabled: true, erpSyncProvider: 'http', erpJournalExportEndpoint: 'https://example.com/journals', erpSyncTimeoutMs: 500, }, { journalType: 'purchase_order_accrual', documentNo: 'PO-001' }, ); expect(result.ok).toBe(false); expect(result.code).toBe('erp_sync_http_5xx'); expect(result.retryable).toBe(true); }); it('handles http procurement export 4xx errors', async () => { global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 400, headers: { get: () => 'application/json' }, json: vi.fn().mockResolvedValue({ code: 'bad_request' }), }); const result = await erp.exportProcurementToErp( { erpSyncEnabled: true, erpSyncProvider: 'http', erpProcurementExportEndpoint: 'https://example.com/procurement', erpProcurementTimeoutMs: 500, }, { exportType: 'purchase_order_archive', poNo: 'PO-001' }, ); expect(result.ok).toBe(false); expect(result.code).toBe('erp_procurement_http_4xx'); expect(result.retryable).toBe(false); }); it('handles http procurement export timeout', async () => { global.fetch = vi.fn().mockRejectedValue({ name: 'AbortError' }); const result = await erp.exportProcurementToErp( { erpSyncEnabled: true, erpSyncProvider: 'http', erpProcurementExportEndpoint: 'https://example.com/procurement', erpProcurementTimeoutMs: 500, }, { exportType: 'purchase_order_archive', poNo: 'PO-001' }, ); expect(result.ok).toBe(false); expect(result.code).toBe('erp_procurement_timeout'); expect(result.retryable).toBe(true); }); it('handles http journal export timeout', async () => { global.fetch = vi.fn().mockRejectedValue({ name: 'AbortError' }); const result = await erp.exportJournalToErp( { erpSyncEnabled: true, erpSyncProvider: 'http', erpJournalExportEndpoint: 'https://example.com/journals', erpSyncTimeoutMs: 500, }, { documentNo: 'BX-001' }, ); expect(result.ok).toBe(false); expect(result.code).toBe('erp_sync_timeout'); expect(result.retryable).toBe(true); }); it('returns invalid provider if unsupported', async () => { const result = await erp.exportJournalToErp( { erpSyncEnabled: true, erpSyncProvider: 'invalid_xyz' }, {}, ); expect(result.ok).toBe(false); expect(result.code).toBe('erp_sync_provider_invalid'); }); it('returns disabled if ERP sync is disabled', async () => { const result = await erp.exportJournalToErp({ erpSyncEnabled: false }, {}); expect(result.ok).toBe(false); expect(result.code).toBe('erp_sync_disabled'); }); });