实现 B-2a 报销审批内容工作台(reimbursement-approval-content-workbench)
这个提交包含在:
@@ -99,6 +99,8 @@ function buildReimbursementRecord(overrides = {}) {
|
||||
async function buildApp({
|
||||
employeeId = OWNER_ID,
|
||||
permissionCodes = ["expense.view_own"],
|
||||
roleCodes = [],
|
||||
reviewerTask = null,
|
||||
reimbursement = buildReimbursementRecord(),
|
||||
latestWorkflowInstance = {
|
||||
id: PENDING_INSTANCE_ID,
|
||||
@@ -273,6 +275,12 @@ async function buildApp({
|
||||
budgetPool: {
|
||||
findMany: vi.fn(async () => []),
|
||||
},
|
||||
delegationGrant: {
|
||||
findMany: vi.fn(async () => []),
|
||||
},
|
||||
workflowTask: {
|
||||
findFirst: vi.fn(async () => reviewerTask),
|
||||
},
|
||||
};
|
||||
|
||||
app.decorate("prisma", prisma);
|
||||
@@ -289,7 +297,7 @@ async function buildApp({
|
||||
request.employee = {
|
||||
id: employeeId,
|
||||
permissionCodes,
|
||||
roleCodes: [],
|
||||
roleCodes,
|
||||
department_id: DEPARTMENT_ID,
|
||||
};
|
||||
});
|
||||
@@ -329,6 +337,48 @@ async function buildApp({
|
||||
}
|
||||
|
||||
describe("GET /api/reimbursements/:id detail enhancements", () => {
|
||||
it("allows an assigned workflow reviewer to read evidence without expense view permissions", async () => {
|
||||
const { app, prisma } = await buildApp({
|
||||
employeeId: "99999999-9999-4999-8999-999999999999",
|
||||
permissionCodes: ["expense.approve"],
|
||||
roleCodes: ["finance"],
|
||||
reviewerTask: { id: "review-task" },
|
||||
});
|
||||
|
||||
const response = await app.inject({
|
||||
method: "GET",
|
||||
url: `/api/reimbursements/${REIMBURSEMENT_ID}`,
|
||||
headers: { authorization: "Bearer test" },
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(prisma.workflowTask.findFirst).toHaveBeenCalledWith(expect.objectContaining({
|
||||
where: expect.objectContaining({
|
||||
workflow_instance: { entity_type: "reimbursement", entity_id: REIMBURSEMENT_ID },
|
||||
}),
|
||||
select: { id: true },
|
||||
}));
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it("keeps an unrelated employee outside the reimbursement evidence projection", async () => {
|
||||
const { app } = await buildApp({
|
||||
employeeId: "99999999-9999-4999-8999-999999999999",
|
||||
permissionCodes: ["expense.approve"],
|
||||
reviewerTask: null,
|
||||
});
|
||||
|
||||
const response = await app.inject({
|
||||
method: "GET",
|
||||
url: `/api/reimbursements/${REIMBURSEMENT_ID}`,
|
||||
headers: { authorization: "Bearer test" },
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(403);
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it("returns department, submittedAt, expanded invoices and printable approvalHistory", async () => {
|
||||
const { app } = await buildApp();
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户