feat: 完成错误原因合同与付款就绪提示(09-05-error-explainability-payment-readiness)
这个提交包含在:
@@ -0,0 +1,60 @@
|
||||
const { getErrorReason } = require("../constants/errorReasons");
|
||||
const { buildBusinessValidationError } = require("../utils/businessValidationError");
|
||||
|
||||
function resolvePaymentBatchCreationReadiness({ entityType, entityIds, paymentChannel = "bank", snapshots, departmentReadiness, accountReadiness, reservedEntityIds, reviewerIds }) {
|
||||
const blockers = [];
|
||||
const addPaymentReadinessBlocker = (reasonCode, snapshot) => {
|
||||
getErrorReason(reasonCode);
|
||||
blockers.push({ reasonCode, ...(snapshot ? { entityId: snapshot.entityId, entityCode: snapshot.entityCode } : {}) });
|
||||
};
|
||||
if (entityType !== "reimbursement") addPaymentReadinessBlocker("payment_batch.travel_payment_frozen");
|
||||
const selected = snapshots.filter((snapshot) => entityIds.includes(snapshot.entityId));
|
||||
if (!entityIds.length || selected.length !== entityIds.length) addPaymentReadinessBlocker("payment_batch.source_not_found");
|
||||
const companies = new Set();
|
||||
for (const snapshot of selected) {
|
||||
if (snapshot.status !== "approved" || snapshot.snapshotJson?.paidAt) addPaymentReadinessBlocker("payment_batch.source_status_invalid", snapshot);
|
||||
if (!departmentReadiness.get(snapshot.departmentId ?? null)?.enabled) addPaymentReadinessBlocker("payment_batch.department_unavailable", snapshot);
|
||||
if (reservedEntityIds.has(snapshot.entityId)) addPaymentReadinessBlocker("payment_batch.active_batch_exists", snapshot);
|
||||
const payee = snapshot.snapshotJson?.payee;
|
||||
if (payee?.bankAccountUnavailable) addPaymentReadinessBlocker("payment_batch.payee_account_unavailable", snapshot);
|
||||
if (paymentChannel !== "bank") continue;
|
||||
if (!snapshot.legalEntityId) addPaymentReadinessBlocker("payment_batch.legal_entity_required", snapshot);
|
||||
else companies.add(snapshot.legalEntityId);
|
||||
if (!payee?.bankInstitutionCode) addPaymentReadinessBlocker("payment_batch.payee_bank_required", snapshot);
|
||||
else if (payee.bankInstitutionCode !== "CCB") addPaymentReadinessBlocker("payment_batch.payee_bank_unsupported", snapshot);
|
||||
if (!payee?.bankAccountEncrypted) addPaymentReadinessBlocker("payment_batch.payee_account_required", snapshot);
|
||||
}
|
||||
if (paymentChannel === "bank") {
|
||||
if (companies.size > 1) addPaymentReadinessBlocker("payment_batch.multiple_legal_entities");
|
||||
for (const companyId of companies) {
|
||||
const account = accountReadiness.get(companyId);
|
||||
if (!account?.paymentAccount) addPaymentReadinessBlocker("payment_batch.payment_account_required");
|
||||
else if (!account.paymentAccountSnapshot) addPaymentReadinessBlocker("payment_batch.payment_account_unavailable");
|
||||
}
|
||||
}
|
||||
if (!reviewerIds.length) addPaymentReadinessBlocker("payment_batch.reviewer_required");
|
||||
return { ready: blockers.length === 0, blockers };
|
||||
}
|
||||
|
||||
function paymentReadinessError(readiness, departmentReadiness) {
|
||||
const blocker = readiness.blockers[0];
|
||||
const detailsByReason = {
|
||||
"payment_batch.travel_payment_frozen": { entityType: "travel_payment_frozen" },
|
||||
"payment_batch.source_not_found": { entityIds: "contains_not_found" },
|
||||
"payment_batch.source_status_invalid": { entity: `${blocker.entityCode}:invalid_pay_status` },
|
||||
"payment_batch.department_unavailable": { featureFlag: "payment_batch_v1_disabled", reason: [...departmentReadiness.values()].find((value) => !value.enabled)?.gate?.reason ?? "department_not_allowed" },
|
||||
"payment_batch.active_batch_exists": { entityIds: "contains_active_payment_batch" },
|
||||
"payment_batch.payee_account_unavailable": { payeeBankAccount: "decrypt_failed" },
|
||||
"payment_batch.legal_entity_required": { legalEntity: "single_legal_entity_required" },
|
||||
"payment_batch.multiple_legal_entities": { legalEntity: "single_legal_entity_required" },
|
||||
"payment_batch.payee_bank_required": { payeeBankAccount: `${blocker.entityCode}:ccb_account_required` },
|
||||
"payment_batch.payee_account_required": { payeeBankAccount: `${blocker.entityCode}:ccb_account_required` },
|
||||
"payment_batch.payee_bank_unsupported": { payeeBankAccount: `${blocker.entityCode}:ccb_account_required` },
|
||||
"payment_batch.payment_account_required": { paymentAccount: "active_ccb_account_required" },
|
||||
"payment_batch.payment_account_unavailable": { paymentAccount: "decrypt_failed" },
|
||||
"payment_batch.reviewer_required": { review: "no_eligible_reviewer" },
|
||||
};
|
||||
return buildBusinessValidationError(getErrorReason(blocker.reasonCode).code, detailsByReason[blocker.reasonCode], blocker.reasonCode === "payment_batch.source_not_found" ? 404 : 409);
|
||||
}
|
||||
|
||||
module.exports = { resolvePaymentBatchCreationReadiness, paymentReadinessError };
|
||||
在新工单中引用
屏蔽一个用户