BIZ8-11 增强采购项目关联
这个提交包含在:
@@ -66,11 +66,12 @@ function buildProjectState(overrides = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
function createPrismaState({ communicationLogs = [], closureChecklistItems = [] } = {}) {
|
||||
function createPrismaState({ communicationLogs = [], closureChecklistItems = [], purchaseRequests = [] } = {}) {
|
||||
const state = {
|
||||
project: buildProjectState(),
|
||||
communicationLogs: communicationLogs.map((item) => ({ ...item })),
|
||||
closureChecklistItems: closureChecklistItems.map((item) => ({ ...item })),
|
||||
purchaseRequests: purchaseRequests.map((item) => ({ ...item })),
|
||||
openRiskCount: 0,
|
||||
openReimbursementCount: 0,
|
||||
openTravelCount: 0,
|
||||
@@ -89,6 +90,7 @@ function createPrismaState({ communicationLogs = [], closureChecklistItems = []
|
||||
tasks: (project.tasks ?? []).length,
|
||||
risks: (project.risks ?? []).length,
|
||||
reimbursements: 0,
|
||||
purchase_requests: state.purchaseRequests.filter((item) => item.project_id === project.id).length,
|
||||
travel_requests: 0,
|
||||
todo_items: 0,
|
||||
weekly_reports: 0,
|
||||
@@ -277,6 +279,13 @@ function createPrismaState({ communicationLogs = [], closureChecklistItems = []
|
||||
projectRisk: { count: vi.fn().mockImplementation(async () => state.openRiskCount) },
|
||||
reimbursementForm: { count: vi.fn().mockImplementation(async () => state.openReimbursementCount) },
|
||||
travelRequest: { count: vi.fn().mockImplementation(async () => state.openTravelCount) },
|
||||
purchaseRequest: {
|
||||
findMany: vi.fn().mockImplementation(async ({ where }) => {
|
||||
return state.purchaseRequests
|
||||
.filter((item) => item.project_id === where.project_id)
|
||||
.sort((left, right) => new Date(right.updated_at).getTime() - new Date(left.updated_at).getTime());
|
||||
}),
|
||||
},
|
||||
projectDeliveryRecord: { count: vi.fn().mockImplementation(async () => state.deliveryRecordCount) },
|
||||
contractRegistry: { count: vi.fn().mockImplementation(async () => state.contractCount) },
|
||||
confidentialDocumentRef: { count: vi.fn().mockImplementation(async () => state.confidentialDocRefCount) },
|
||||
@@ -329,6 +338,79 @@ async function buildApp({ employee, prisma }) {
|
||||
}
|
||||
|
||||
describe("project R23 routes", () => {
|
||||
it("returns purchase request summary only to readable project users", async () => {
|
||||
const { prisma } = createPrismaState({
|
||||
purchaseRequests: [
|
||||
{
|
||||
id: "77777777-7777-4777-8777-777777777777",
|
||||
project_id: PROJECT_ID,
|
||||
request_no: "PR-20260513-001",
|
||||
title: "测试设备采购",
|
||||
status: "approved",
|
||||
total_amount: "1200.50",
|
||||
updated_at: new Date("2026-05-13T08:00:00.000Z"),
|
||||
},
|
||||
{
|
||||
id: "88888888-8888-4888-8888-888888888888",
|
||||
project_id: PROJECT_ID,
|
||||
request_no: null,
|
||||
title: "未编号采购草稿",
|
||||
status: "draft",
|
||||
total_amount: "99.50",
|
||||
updated_at: new Date("2026-05-13T09:00:00.000Z"),
|
||||
},
|
||||
],
|
||||
});
|
||||
const memberApp = await buildApp({
|
||||
employee: { id: MEMBER_ID, department_id: null, permissionCodes: ["project.view_own"] },
|
||||
prisma,
|
||||
});
|
||||
|
||||
const response = await memberApp.inject({
|
||||
method: "GET",
|
||||
url: `/api/projects/${PROJECT_ID}/purchase-requests/summary`,
|
||||
headers: { authorization: "Bearer test-token" },
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json().data).toEqual({
|
||||
totalAmount: "1300.00",
|
||||
items: [
|
||||
{
|
||||
id: "88888888-8888-4888-8888-888888888888",
|
||||
requestNo: "88888888-8888-4888-8888-888888888888",
|
||||
title: "未编号采购草稿",
|
||||
status: "draft",
|
||||
totalAmount: "99.50",
|
||||
updatedAt: "2026-05-13T09:00:00.000Z",
|
||||
},
|
||||
{
|
||||
id: "77777777-7777-4777-8777-777777777777",
|
||||
requestNo: "PR-20260513-001",
|
||||
title: "测试设备采购",
|
||||
status: "approved",
|
||||
totalAmount: "1200.50",
|
||||
updatedAt: "2026-05-13T08:00:00.000Z",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const outsiderApp = await buildApp({
|
||||
employee: { id: OUTSIDER_ID, department_id: null, permissionCodes: ["project.view_own"] },
|
||||
prisma,
|
||||
});
|
||||
const forbiddenResponse = await outsiderApp.inject({
|
||||
method: "GET",
|
||||
url: `/api/projects/${PROJECT_ID}/purchase-requests/summary`,
|
||||
headers: { authorization: "Bearer test-token" },
|
||||
});
|
||||
|
||||
expect(forbiddenResponse.statusCode).toBe(403);
|
||||
expect(forbiddenResponse.json().error.code).toBe("PERM_001");
|
||||
|
||||
await Promise.all([memberApp.close(), outsiderApp.close()]);
|
||||
});
|
||||
|
||||
it("allows project member to create communication logs and blocks outsiders", async () => {
|
||||
const { prisma } = createPrismaState();
|
||||
const memberApp = await buildApp({
|
||||
|
||||
在新工单中引用
屏蔽一个用户