feat(P19ERP2-1,P19ERP2-2): 实现采购ERP凭证与摘要出站服务

这个提交包含在:
2026-03-08 23:50:02 +08:00
父节点 8f4a50cdd7
当前提交 486660df06
共修改 15 个文件,包含 1912 行新增和 181 行删除
@@ -33,11 +33,22 @@ describe('ERP Sync Services', () => {
it('returns mock synced when journal export provider is mock', async () => {
const result = await erp.exportJournalToErp(
{ erpSyncEnabled: true, erpSyncProvider: 'mock' },
{ documentNo: 'BX-001' },
{ journalType: 'purchase_request_commitment', documentNo: 'PR-001' },
);
expect(result.ok).toBe(true);
expect(result.code).toBe('mock_synced');
expect(result.response.externalId).toContain('BX-001');
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 () => {
@@ -74,6 +85,70 @@ describe('ERP Sync Services', () => {
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' });