123 行
4.7 KiB
JavaScript
123 行
4.7 KiB
JavaScript
const {
|
|
PAYMENT_PROCESSING_STAGE_DEFINITIONS,
|
|
buildPaymentProcessingSemantics,
|
|
requiresBankReceipt,
|
|
} = require("../../../src/services/paymentProcessingProgress");
|
|
|
|
const BASE = {
|
|
createdAt: new Date("2026-08-03T01:00:00.000Z"),
|
|
reviewedAt: new Date("2026-08-03T02:00:00.000Z"),
|
|
executedAt: new Date("2026-08-03T03:00:00.000Z"),
|
|
cancelledAt: new Date("2026-08-03T04:00:00.000Z"),
|
|
cancelledReason: "合成取消原因",
|
|
};
|
|
|
|
describe("paymentProcessingProgress", () => {
|
|
it("owns the three payment stage labels", () => {
|
|
expect(PAYMENT_PROCESSING_STAGE_DEFINITIONS).toEqual({
|
|
created: { key: "payment_batch_created", label: "批次创建" },
|
|
review: { key: "payment_batch_review", label: "付款复核" },
|
|
payment: { key: "payment_and_receipt", label: "付款完成" },
|
|
});
|
|
});
|
|
|
|
it.each([
|
|
["draft", "current", "pending"],
|
|
["reviewed", "completed", "current"],
|
|
["executed", "completed", "completed"],
|
|
["failed", "completed", "exception"],
|
|
])("maps %s without a second route-level state matrix", (status, reviewState, paymentState) => {
|
|
const stages = buildPaymentProcessingSemantics({ ...BASE, status });
|
|
expect(stages.review.state).toBe(reviewState);
|
|
expect(stages.payment.state).toBe(paymentState);
|
|
});
|
|
|
|
it("terminates a cancelled draft at review", () => {
|
|
const stages = buildPaymentProcessingSemantics({ ...BASE, status: "cancelled", reviewedAt: null });
|
|
expect(stages.review).toMatchObject({
|
|
state: "exception",
|
|
result: "批次已取消",
|
|
actorSource: "canceller",
|
|
comment: "合成取消原因",
|
|
});
|
|
expect(stages.payment).toMatchObject({ state: "terminated", result: "已终止", detail: null });
|
|
});
|
|
|
|
it("terminates a reviewed cancellation at payment", () => {
|
|
const stages = buildPaymentProcessingSemantics({ ...BASE, status: "cancelled" });
|
|
expect(stages.review).toMatchObject({ state: "completed", actorSource: "reviewer" });
|
|
expect(stages.payment).toMatchObject({
|
|
state: "exception",
|
|
result: "批次已取消",
|
|
actorSource: "canceller",
|
|
});
|
|
});
|
|
|
|
it.each([
|
|
[false, 0, 0, "等待财务登记付款完成"],
|
|
[true, 0, 0, "等待财务线下付款并上传回单"],
|
|
[true, 0, 1, "已生成银行制单"],
|
|
[true, 1, 1, "银行回单已上传"],
|
|
])("uses the receipt contract (%s, %s, %s)", (bankReceiptRequired, receiptCount, exportCount, text) => {
|
|
const stages = buildPaymentProcessingSemantics({
|
|
...BASE,
|
|
status: "reviewed",
|
|
requiresBankReceipt: bankReceiptRequired,
|
|
receiptCount,
|
|
paymentOrderExportCount: exportCount,
|
|
});
|
|
expect(stages.payment.detail).toContain(text);
|
|
});
|
|
|
|
it("derives receipt requirements from the same effective-channel rule", () => {
|
|
expect(requiresBankReceipt({
|
|
entity_type: "reimbursement",
|
|
default_payment_channel: "cash",
|
|
items: [{ status: "pending", payment_channel_override: null }],
|
|
})).toBe(false);
|
|
expect(requiresBankReceipt({
|
|
entity_type: "reimbursement",
|
|
default_payment_channel: "cash",
|
|
items: [{ status: "failed", payment_channel_override: "bank" }],
|
|
})).toBe(true);
|
|
expect(requiresBankReceipt({
|
|
entity_type: "reimbursement",
|
|
default_payment_channel: null,
|
|
items: [{ status: "pending", payment_channel_override: null }],
|
|
})).toBe(true);
|
|
expect(requiresBankReceipt({
|
|
entity_type: "reimbursement",
|
|
default_payment_channel: "bank",
|
|
items: [{ status: "executed", payment_channel_override: null }],
|
|
})).toBe(false);
|
|
});
|
|
|
|
it("keeps route assembly layers free of a second payment status matrix", () => {
|
|
const reimbursementsSource = fs.readFileSync(
|
|
path.resolve(__dirname, "../../../src/routes/reimbursements.js"),
|
|
"utf8",
|
|
);
|
|
const paymentBatchesSource = fs.readFileSync(
|
|
path.resolve(__dirname, "../../../src/routes/paymentBatches.js"),
|
|
"utf8",
|
|
);
|
|
const reimbursementAssembler = reimbursementsSource.slice(
|
|
reimbursementsSource.indexOf("function buildProcessingProgress("),
|
|
reimbursementsSource.indexOf("function ensureApplicantOrAdmin("),
|
|
);
|
|
const paymentBatchAssembler = paymentBatchesSource.slice(
|
|
paymentBatchesSource.indexOf("function buildPaymentBatchProcessingProgress("),
|
|
paymentBatchesSource.indexOf("async function mapBatchForResponse("),
|
|
);
|
|
|
|
for (const assembler of [reimbursementAssembler, paymentBatchAssembler]) {
|
|
expect(assembler).toContain("buildPaymentProcessingSemantics(");
|
|
expect(assembler).not.toMatch(/batch\.status\s*===|\.includes\(batch\.status\)/);
|
|
expect(assembler).not.toContain('label: "付款复核"');
|
|
expect(assembler).not.toContain('label: "付款完成"');
|
|
}
|
|
});
|
|
});
|
|
const fs = require("node:fs");
|
|
const path = require("node:path");
|