fix: 统一报销付款状态与进度(09-04-reimbursement-state-gap-finance-view)

这个提交包含在:
2026-09-11 09:43:36 +08:00
父节点 fe025e65cf
当前提交 f9aee4c932
共修改 19 个文件,包含 549 行新增和 32 行删除
@@ -0,0 +1,66 @@
const fs = require("node:fs");
const path = require("node:path");
const ts = require("../../../../client/node_modules/typescript");
const { buildPaymentProcessingSemantics } = require("../../../src/services/paymentProcessingProgress");
const filename = path.resolve(__dirname, "../../../../client/src/lib/reimbursementPaymentHandoff.ts");
const compiled = ts.transpileModule(fs.readFileSync(filename, "utf8"), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 },
}).outputText;
const clientModule = { exports: {} };
new Function("exports", compiled)(clientModule.exports);
const { resolveReimbursementPaymentHandoff } = clientModule.exports;
function project(status, options = {}) {
const stages = Object.values(buildPaymentProcessingSemantics({ status, ...options }));
return { stages: stages.map((stage) => ({ ...stage, actorName: null, targetHref: null })) };
}
describe("报销付款下一步消费服务端阶段,不猜测批次存在性", () => {
it.each([
["draft", {}, "待非制单人复核付款"],
["reviewed", { requiresBankReceipt: true }, "等待财务线下付款并上传回单,非审批环节"],
["reviewed", { requiresBankReceipt: true, paymentOrderExportCount: 1 }, "已生成银行制单,等待付款完成并上传回单,非审批环节"],
["reviewed", { requiresBankReceipt: true, receiptCount: 1 }, "银行回单已上传,等待财务登记付款完成,非审批环节"],
["reviewed", {}, "付款复核已通过,等待财务登记付款完成,非审批环节"],
["failed", {}, "付款登记失败,待财务核对后重试,非审批环节"],
["cancelled", {}, "付款安排已取消,流程已终止"],
["cancelled", { reviewedAt: "2026-09-11T00:00:00Z" }, "付款安排已取消,流程已终止"],
])("%s 无读取链接也不允许重新组批 (%j)", (status, options, expected) => {
const result = resolveReimbursementPaymentHandoff({ status: "approved", progress: project(status, options) });
expect(result).toEqual({ nextStep: expected, canCreateBatch: false, targetHref: null });
});
it("只有明确待组批投影才提供资格,入口权限仍由调用者检查", () => {
const progress = project("unknown");
progress.stages[0] = { ...progress.stages[0], state: "current", detail: "待财务组建付款批次" };
expect(resolveReimbursementPaymentHandoff({ status: "approved", progress }).canCreateBatch).toBe(true);
progress.stages[1].targetHref = "/payment-batches?id=synthetic";
expect(resolveReimbursementPaymentHandoff({ status: "approved", progress }).canCreateBatch).toBe(false);
});
it("保留服务端有权的同一个批次入口", () => {
const progress = project("reviewed");
progress.stages[2].targetHref = "/payment-batches?id=synthetic";
expect(resolveReimbursementPaymentHandoff({ status: "approved", progress }).targetHref).toBe(progress.stages[2].targetHref);
});
it.each([undefined, { stages: [] }, { stages: project("reviewed").stages.slice(1) }, project("unknown")])("缺失或未知阶段拒绝猜测", (progress) => {
expect(resolveReimbursementPaymentHandoff({ status: "approved", progress })).toEqual({
nextStep: "付款进度暂不可用,请刷新后重试", canCreateBatch: false, targetHref: null,
});
});
it("已付款只展示完成事实,不把制单当付款", () => {
expect(resolveReimbursementPaymentHandoff({ status: "paid", progress: project("executed") }).nextStep).toBe("付款已登记完成");
expect(resolveReimbursementPaymentHandoff({ status: "paid", progress: project("reviewed") }).targetHref).toBeNull();
});
it.each(["draft", "reviewing", "rejected", "cancelled"])("%s 不生成付款动作", (status) => {
expect(resolveReimbursementPaymentHandoff({ status, progress: project("reviewed") })).toBeNull();
});
it("删除记录不生成付款动作", () => {
expect(resolveReimbursementPaymentHandoff({ status: "approved", deletedAt: "2026-09-11T00:00:00Z", progress: project("reviewed") })).toBeNull();
});
});