feat(P19ERP1-3,P19ERP1-4): 实现ERP凭证导出与管理入口
这个提交包含在:
@@ -1,4 +1,4 @@
|
||||
const { pushPaidReimbursementToErp } = require('./erp');
|
||||
const { pushPaidReimbursementToErp, exportJournalToErp } = require('./erp');
|
||||
const { sendInstantMessageByChannel } = require('../notifications/channels');
|
||||
|
||||
class IntegrationDispatchError extends Error {
|
||||
@@ -22,18 +22,82 @@ function toDispatchError(result) {
|
||||
});
|
||||
}
|
||||
|
||||
async function dispatchOutboxEvent({ prisma, config, eventType, payload }) {
|
||||
if (eventType === 'erp.reimbursement.paid') {
|
||||
const result = await pushPaidReimbursementToErp(config, payload);
|
||||
if (!result.ok) {
|
||||
throw toDispatchError(result);
|
||||
}
|
||||
function extractExternalId(response) {
|
||||
if (!response) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
channel: 'erp',
|
||||
code: result.code,
|
||||
response: result.response ?? null,
|
||||
};
|
||||
if (typeof response.externalId === 'string' && response.externalId.trim()) {
|
||||
return response.externalId.trim();
|
||||
}
|
||||
|
||||
if (
|
||||
typeof response.body?.externalId === 'string' &&
|
||||
response.body.externalId.trim()
|
||||
) {
|
||||
return response.body.externalId.trim();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function upsertErpBindingForPayload(prisma, payload, response) {
|
||||
const entityType = String(payload?.entityType ?? '').trim();
|
||||
const entityId = String(payload?.entityId ?? '').trim();
|
||||
if (!prisma || !entityType || !entityId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entityType !== 'reimbursement_form') {
|
||||
return;
|
||||
}
|
||||
|
||||
await prisma.integrationBinding.upsert({
|
||||
where: {
|
||||
provider_entity_type_local_id: {
|
||||
provider: 'erp',
|
||||
entity_type: 'reimbursement_journal',
|
||||
local_id: entityId,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
provider: 'erp',
|
||||
entity_type: 'reimbursement_journal',
|
||||
local_id: entityId,
|
||||
external_id: extractExternalId(response),
|
||||
external_code: payload?.documentNo ?? payload?.formNumber ?? null,
|
||||
last_synced_at: new Date(),
|
||||
},
|
||||
update: {
|
||||
external_id: extractExternalId(response),
|
||||
external_code: payload?.documentNo ?? payload?.formNumber ?? null,
|
||||
last_synced_at: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function dispatchErpEvent({ prisma, config, eventType, payload }) {
|
||||
const result =
|
||||
eventType === 'erp.journal.export'
|
||||
? await exportJournalToErp(config, payload)
|
||||
: await pushPaidReimbursementToErp(config, payload);
|
||||
|
||||
if (!result.ok) {
|
||||
throw toDispatchError(result);
|
||||
}
|
||||
|
||||
await upsertErpBindingForPayload(prisma, payload, result.response ?? null);
|
||||
|
||||
return {
|
||||
channel: 'erp',
|
||||
code: result.code,
|
||||
response: result.response ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
async function dispatchOutboxEvent({ prisma, config, eventType, payload }) {
|
||||
if (eventType === 'erp.reimbursement.paid' || eventType === 'erp.journal.export') {
|
||||
return dispatchErpEvent({ prisma, config, eventType, payload });
|
||||
}
|
||||
|
||||
if (eventType === 'notification.im') {
|
||||
|
||||
在新工单中引用
屏蔽一个用户