146 行
4.9 KiB
JavaScript
146 行
4.9 KiB
JavaScript
const {
|
|
normalizeRequestedResources,
|
|
syncErpMasterData,
|
|
} = require('../../../src/services/integrations/erpMasterData');
|
|
|
|
function createPrismaMock() {
|
|
const syncStates = new Map();
|
|
|
|
const prisma = {
|
|
$transaction: async (fn) => fn(prisma),
|
|
integrationSyncState: {
|
|
upsert: vi.fn().mockImplementation(async ({ where, create, update }) => {
|
|
const key = `${where.provider_resource_type.provider}:${where.provider_resource_type.resource_type}`;
|
|
const current = syncStates.get(key) ?? null;
|
|
const next = current ? { ...current, ...update } : { ...create };
|
|
syncStates.set(key, next);
|
|
return next;
|
|
}),
|
|
findMany: vi.fn().mockImplementation(async () => Array.from(syncStates.values())),
|
|
},
|
|
integrationBinding: {
|
|
findFirst: vi.fn().mockResolvedValue(null),
|
|
upsert: vi.fn().mockResolvedValue({ id: 'binding-1' }),
|
|
},
|
|
legalEntity: {
|
|
findMany: vi.fn().mockResolvedValue([]),
|
|
create: vi.fn().mockResolvedValue({ id: 'le-1' }),
|
|
update: vi.fn().mockResolvedValue({ id: 'le-1' }),
|
|
},
|
|
costCenter: {
|
|
findMany: vi.fn().mockResolvedValue([]),
|
|
create: vi.fn().mockResolvedValue({ id: 'cc-1' }),
|
|
update: vi.fn().mockResolvedValue({ id: 'cc-1' }),
|
|
},
|
|
supplier: {
|
|
findMany: vi.fn().mockResolvedValue([]),
|
|
create: vi.fn().mockResolvedValue({ id: 'sup-1' }),
|
|
update: vi.fn().mockResolvedValue({ id: 'sup-1' }),
|
|
},
|
|
};
|
|
|
|
return prisma;
|
|
}
|
|
|
|
describe('ERP Master Data Service', () => {
|
|
const originalFetch = global.fetch;
|
|
|
|
afterEach(() => {
|
|
global.fetch = originalFetch;
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('normalizes resource order and dedupes input', () => {
|
|
expect(normalizeRequestedResources(['supplier', 'legal_entity', 'supplier'])).toEqual([
|
|
'legal_entity',
|
|
'supplier',
|
|
]);
|
|
expect(normalizeRequestedResources([])).toEqual(['legal_entity', 'cost_center', 'supplier']);
|
|
});
|
|
|
|
it('skips supplier rows without stable code', async () => {
|
|
const prisma = createPrismaMock();
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
headers: { get: () => 'application/json' },
|
|
json: vi.fn().mockResolvedValue({
|
|
suppliers: [{ code: '', name: 'bad supplier' }],
|
|
}),
|
|
});
|
|
|
|
const result = await syncErpMasterData(prisma, {
|
|
erpSyncEnabled: true,
|
|
erpSyncProvider: 'http',
|
|
erpMasterDataEndpoint: 'https://example.com/master-data',
|
|
erpMasterDataTimeoutMs: 500,
|
|
}, {
|
|
resources: ['supplier'],
|
|
actorId: 'actor-1',
|
|
logger: { error: vi.fn() },
|
|
});
|
|
|
|
const supplierState = result.items.find((item) => item.resourceType === 'supplier');
|
|
expect(result.ok).toBe(true);
|
|
expect(supplierState.lastResult.skipped).toBe(1);
|
|
expect(prisma.supplier.create).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('skips cost centers when legal entity is missing', async () => {
|
|
const prisma = createPrismaMock();
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
headers: { get: () => 'application/json' },
|
|
json: vi.fn().mockResolvedValue({
|
|
costCenters: [{ code: 'IT001', legalEntityCode: 'MISSING', name: 'IT中心' }],
|
|
}),
|
|
});
|
|
|
|
const result = await syncErpMasterData(prisma, {
|
|
erpSyncEnabled: true,
|
|
erpSyncProvider: 'http',
|
|
erpMasterDataEndpoint: 'https://example.com/master-data',
|
|
erpMasterDataTimeoutMs: 500,
|
|
}, {
|
|
resources: ['cost_center'],
|
|
actorId: 'actor-1',
|
|
logger: { error: vi.fn() },
|
|
});
|
|
|
|
const costCenterState = result.items.find((item) => item.resourceType === 'cost_center');
|
|
expect(costCenterState.lastResult.skipped).toBe(1);
|
|
expect(costCenterState.lastResult.sampleSkipped[0].reason).toBe('legal_entity_missing');
|
|
});
|
|
|
|
it('skips rows when integration binding conflicts', async () => {
|
|
const prisma = createPrismaMock();
|
|
prisma.supplier.findMany.mockResolvedValue([{ id: 'sup-local', code: 'SUP001', enabled: true, archived_at: null }]);
|
|
prisma.integrationBinding.findFirst.mockResolvedValue({ local_id: 'another-local' });
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
headers: { get: () => 'application/json' },
|
|
json: vi.fn().mockResolvedValue({
|
|
suppliers: [{ code: 'SUP001', externalId: 'ERP-SUP-001', name: '供应商' }],
|
|
}),
|
|
});
|
|
|
|
const result = await syncErpMasterData(prisma, {
|
|
erpSyncEnabled: true,
|
|
erpSyncProvider: 'http',
|
|
erpMasterDataEndpoint: 'https://example.com/master-data',
|
|
erpMasterDataTimeoutMs: 500,
|
|
}, {
|
|
resources: ['supplier'],
|
|
actorId: 'actor-1',
|
|
logger: { error: vi.fn() },
|
|
});
|
|
|
|
const supplierState = result.items.find((item) => item.resourceType === 'supplier');
|
|
expect(supplierState.lastResult.skipped).toBe(1);
|
|
expect(supplierState.lastResult.sampleSkipped[0].reason).toBe('binding_conflict');
|
|
expect(prisma.supplier.update).not.toHaveBeenCalled();
|
|
});
|
|
});
|