文件
hyb-oa/server/src/services/paymentBatchReadiness.js
T
wangsli a837912638
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s
feat: 完成错误原因合同与付款就绪提示(09-05-error-explainability-payment-readiness)
2026-09-05 19:00:35 +08:00

61 行
4.5 KiB
JavaScript

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 };