fix: 财务本人报销由总经理独立审批(09-04-reimbursement-state-gap-finance-view)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-09-12 20:03:29 +08:00
父节点 31ab9fbb2b
当前提交 229867f5fd
共修改 18 个文件,包含 629 行新增和 20 行删除
+44
查看文件
@@ -131,6 +131,50 @@ describe("workflow assignee resolution", () => {
});
describe("buildApprovalWorkflowSnapshot", () => {
it.each([
["small", "finance", null],
["large", "ceo", "finance"],
])("uses exactly one CEO step for finance-owned reimbursement %s", async (_name, level2, level3) => {
const tx = buildTx({ ownerRoles: ["finance", "employee"], managerId: MANAGER_ID });
const steps = await workflowUtils.buildApprovalWorkflowSnapshot({
tx, entityType: "reimbursement", ownerId: OWNER_ID, submitterId: MANAGER_ID,
approvalRule: { level1_role: "manager", level2_role: level2, level3_role: level3 },
});
expect(steps).toEqual([{
id: "approval_1_ceo_finance_owner", name: "总经理审批", type: "approval",
assignee_rule: { type: "role", value: "ceo" },
}]);
expect(tx.employeeRole.findFirst).toHaveBeenCalledWith({
where: {
role_code: "ceo",
employee_id: { notIn: [OWNER_ID, MANAGER_ID] },
employee: { status: "active", archived_at: null },
},
select: { employee_id: true },
});
});
it.each([["finance"], ["finance", "ceo"]])("blocks finance owner roles %j without non-applicant CEO", async (...ownerRoles) => {
const tx = buildTx({ ownerRoles, fallbackHasHolder: false });
await expect(workflowUtils.buildApprovalWorkflowSnapshot({
tx, entityType: "reimbursement", ownerId: OWNER_ID, submitterId: OWNER_ID,
approvalRule: { level1_role: "finance" },
})).rejects.toMatchObject({
statusCode: 409,
details: { workflow: "no_actionable_approver", reason: "finance_owner_ceo_unavailable" },
});
});
it("does not simplify other entities or a finance submitter's employee-owned form", async () => {
for (const [entityType, ownerRoles] of [["purchase_request", ["finance"]], ["reimbursement", ["employee"]]]) {
const steps = await workflowUtils.buildApprovalWorkflowSnapshot({
tx: buildTx({ ownerRoles }), entityType, ownerId: OWNER_ID, submitterId: FINANCE_MGR_ID,
approvalRule: { level1_role: "manager", level2_role: "ceo", level3_role: "finance" },
});
expect(steps).toHaveLength(3);
}
});
it("keeps ordinary employee approval snapshots unchanged", async () => {
const tx = buildTx({ ownerRoles: ["employee"], managerId: MANAGER_ID });