PC6 完善 PC 工作台与审批信息架构
这个提交包含在:
@@ -164,6 +164,7 @@ async function buildApp(options = {}) {
|
||||
);
|
||||
const travelRows = options.travelRows ?? [];
|
||||
const permissionCodes = options.permissionCodes ?? ["expense.view_own"];
|
||||
const roleCodes = options.roleCodes ?? [];
|
||||
const employeeId = options.employeeId ?? "employee-1";
|
||||
|
||||
resolvePresenceForEmployee.mockImplementation(async (_prisma, targetEmployeeId) => {
|
||||
@@ -189,6 +190,15 @@ async function buildApp(options = {}) {
|
||||
assetCard: {
|
||||
count: vi.fn(async () => 1),
|
||||
},
|
||||
paymentBatch: {
|
||||
count: vi.fn(async () => options.pendingFinanceBatchCount ?? 0),
|
||||
},
|
||||
paymentBatchItem: {
|
||||
count: vi.fn(async () => options.failedFinanceItemCount ?? 0),
|
||||
},
|
||||
entityRiskFlag: {
|
||||
count: vi.fn(async () => options.financeRiskCount ?? 0),
|
||||
},
|
||||
employee: {
|
||||
count: vi.fn(async () => employees.length),
|
||||
findMany: vi.fn(async () => employees),
|
||||
@@ -295,7 +305,7 @@ async function buildApp(options = {}) {
|
||||
name: "测试用户",
|
||||
department_id: "department-1",
|
||||
permissionCodes,
|
||||
roleCodes: [],
|
||||
roleCodes,
|
||||
};
|
||||
});
|
||||
app.decorate(
|
||||
@@ -472,4 +482,64 @@ describe("portal routes", () => {
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it("returns roleCapabilities and financeSummary only for finance roles", async () => {
|
||||
featureFlagState.paymentBatch = true;
|
||||
const { app } = await buildApp({
|
||||
roleCodes: ["employee", "finance"],
|
||||
permissionCodes: ["expense.view_own", "payment.batch.manage", "expense.finance.pay", "expense.approve"],
|
||||
pendingFinanceBatchCount: 3,
|
||||
failedFinanceItemCount: 2,
|
||||
});
|
||||
|
||||
const response = await app.inject({
|
||||
method: "GET",
|
||||
url: "/api/portal/home",
|
||||
headers: { authorization: "Bearer test" },
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
const payload = response.json().data;
|
||||
expect(payload.roleCapabilities).toMatchObject({
|
||||
canApproveTasks: true,
|
||||
canUseFinance: true,
|
||||
canManageSystem: false,
|
||||
});
|
||||
expect(payload.financeSummary).toEqual({
|
||||
pendingBatchCount: 3,
|
||||
failedItemCount: 2,
|
||||
riskCount: 0,
|
||||
href: "/ap-operations",
|
||||
});
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it("does not expose financeSummary to admin-only accounts", async () => {
|
||||
featureFlagState.paymentBatch = true;
|
||||
const { app } = await buildApp({
|
||||
roleCodes: ["admin"],
|
||||
permissionCodes: ["*"],
|
||||
pendingFinanceBatchCount: 3,
|
||||
failedFinanceItemCount: 2,
|
||||
});
|
||||
|
||||
const response = await app.inject({
|
||||
method: "GET",
|
||||
url: "/api/portal/home",
|
||||
headers: { authorization: "Bearer test" },
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
const payload = response.json().data;
|
||||
expect(payload.roleCapabilities).toMatchObject({
|
||||
canManageSystem: true,
|
||||
canUseFinance: false,
|
||||
isAdminOnly: true,
|
||||
});
|
||||
expect(payload.financeSummary).toBeNull();
|
||||
expect(payload.cards.map((card) => card.key)).not.toContain("pending_approvals");
|
||||
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const workflowRoutes = require("../../../src/routes/workflow");
|
||||
|
||||
describe("Workflow Route Permissions", () => {
|
||||
const { listAllowedActionsForTask } = workflowRoutes.__testables;
|
||||
const { listAllowedActionsForTask, parseTaskListQuery } = workflowRoutes.__testables;
|
||||
|
||||
it("allows leave approvers to act on leave tasks", () => {
|
||||
const actions = listAllowedActionsForTask(
|
||||
@@ -107,4 +107,17 @@ describe("Workflow Route Permissions", () => {
|
||||
|
||||
expect(actions).toEqual([]);
|
||||
});
|
||||
|
||||
it("parses optional workflow entityType filter for PC approval center", () => {
|
||||
expect(parseTaskListQuery({ status: "pending", entityType: "purchase_request", page: "2" })).toMatchObject({
|
||||
status: "pending",
|
||||
entityType: "purchase_request",
|
||||
page: 2,
|
||||
pageSize: 20,
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects unsupported workflow entityType filters", () => {
|
||||
expect(() => parseTaskListQuery({ entityType: "invoice_pool" })).toThrow("参数校验失败");
|
||||
});
|
||||
});
|
||||
|
||||
在新工单中引用
屏蔽一个用户