164 行
6.0 KiB
JavaScript
164 行
6.0 KiB
JavaScript
const { NOTIFICATION_TYPES } = require("../../../src/constants/notificationTypes");
|
|
const workflowRoutes = require("../../../src/routes/workflow");
|
|
|
|
describe("workflow travel helpers", () => {
|
|
const {
|
|
buildApprovedCopy,
|
|
buildOverdueTaskCopy,
|
|
buildPendingTaskCopy,
|
|
buildRejectedCopy,
|
|
buildRemindedTaskCopy,
|
|
buildReturnedCopy,
|
|
buildWorkflowTaskSummaryPayload,
|
|
calculateWorkflowTaskDueAt,
|
|
isWorkflowTaskOverdue,
|
|
buildWorkflowEntityMeta,
|
|
resolveWorkflowOutcomeNotificationType,
|
|
} = workflowRoutes.__testables;
|
|
|
|
it("builds travel entity meta with deep-link detail path", () => {
|
|
const meta = buildWorkflowEntityMeta(
|
|
"travel_request",
|
|
{
|
|
request_no: "CL-20260310-0001",
|
|
estimated_amount: "4200.00",
|
|
applicant: {
|
|
name: "张差旅",
|
|
},
|
|
},
|
|
"11111111-1111-4111-8111-111111111111",
|
|
);
|
|
|
|
expect(meta).toEqual(
|
|
expect.objectContaining({
|
|
label: "差旅申请",
|
|
reference: "CL-20260310-0001",
|
|
targetType: "travel_request",
|
|
detailPath: "/travel-requests?id=11111111-1111-4111-8111-111111111111",
|
|
}),
|
|
);
|
|
|
|
const pendingCopy = buildPendingTaskCopy(meta);
|
|
expect(pendingCopy).toEqual({
|
|
title: "【待办】差旅申请 CL-20260310-0001 待审批",
|
|
body: "申请人:张差旅\n预估金额:4200.00\n单号:CL-20260310-0001\n动作:请及时处理审批任务。",
|
|
});
|
|
});
|
|
|
|
it("builds structured workflow outcome copies for leave and reimbursement", () => {
|
|
const leaveMeta = buildWorkflowEntityMeta(
|
|
"leave",
|
|
{
|
|
start_date: new Date("2026-03-25T00:00:00.000Z"),
|
|
end_date: new Date("2026-03-26T00:00:00.000Z"),
|
|
requested_days: "1.5",
|
|
leave_type: {
|
|
name: "年假",
|
|
},
|
|
owner: {
|
|
name: "王请假",
|
|
},
|
|
},
|
|
"leave-1",
|
|
);
|
|
|
|
expect(buildRejectedCopy(leaveMeta, "请补充交接安排")).toEqual({
|
|
title: "【已驳回】请假单 2026-03-25 ~ 2026-03-26",
|
|
body: "申请人:王请假\n假种:年假\n天数:1.5 天\n起止日期:2026-03-25 ~ 2026-03-26\n结果:已驳回:请补充交接安排",
|
|
});
|
|
|
|
const reimbursementMeta = buildWorkflowEntityMeta(
|
|
"reimbursement",
|
|
{
|
|
form_number: "BX-20260325-001",
|
|
total_amount: "880.00",
|
|
owner: {
|
|
name: "刘报销",
|
|
},
|
|
},
|
|
"reimbursement-1",
|
|
);
|
|
|
|
expect(buildApprovedCopy(reimbursementMeta)).toEqual({
|
|
title: "【已批准】报销单 BX-20260325-001",
|
|
body: "申请人:刘报销\n金额:880.00\n单号:BX-20260325-001\n结果:已审批通过,等待财务付款。",
|
|
});
|
|
expect(buildReturnedCopy(reimbursementMeta, "请补发票")).toEqual({
|
|
title: "【已退回】报销单 BX-20260325-001",
|
|
body: "申请人:刘报销\n金额:880.00\n单号:BX-20260325-001\n结果:已退回,可修改后重新提交:请补发票",
|
|
});
|
|
expect(buildRemindedTaskCopy(reimbursementMeta)).toEqual({
|
|
title: "【催办】报销单 BX-20260325-001 待审批",
|
|
body: "申请人:刘报销\n金额:880.00\n单号:BX-20260325-001\n动作:申请人已催办,请及时处理审批任务。",
|
|
});
|
|
expect(buildOverdueTaskCopy(reimbursementMeta)).toEqual({
|
|
title: "【超时提醒】报销单 BX-20260325-001 待审批",
|
|
body: "申请人:刘报销\n金额:880.00\n单号:BX-20260325-001\n动作:该审批已超时,请尽快处理。",
|
|
});
|
|
});
|
|
|
|
it.each([
|
|
["reimbursement", NOTIFICATION_TYPES.reimbursementReturned],
|
|
["leave", NOTIFICATION_TYPES.leaveReturned],
|
|
["purchase_request", NOTIFICATION_TYPES.purchaseRequestReturned],
|
|
["procurement", NOTIFICATION_TYPES.purchaseRequestReturned],
|
|
["travel_request", NOTIFICATION_TYPES.travelRequestReturned],
|
|
])("resolves returned notification type for %s workflows", (entityType, expectedType) => {
|
|
expect(resolveWorkflowOutcomeNotificationType(entityType, "returned")).toBe(expectedType);
|
|
});
|
|
|
|
it("keeps travel approved/rejected and attendance returned outcome mappings unchanged", () => {
|
|
expect(resolveWorkflowOutcomeNotificationType("travel_request", "approved")).toBe(NOTIFICATION_TYPES.travelRequestApproved);
|
|
expect(resolveWorkflowOutcomeNotificationType("travel_request", "rejected")).toBe(NOTIFICATION_TYPES.travelRequestRejected);
|
|
expect(resolveWorkflowOutcomeNotificationType("attendance_correction", "returned")).toBeNull();
|
|
});
|
|
|
|
it("builds pending task productivity summary with overdue buckets", () => {
|
|
const now = new Date("2026-05-12T08:00:00.000Z");
|
|
const tasks = [
|
|
{
|
|
id: "task-1",
|
|
status: "pending",
|
|
step_name: "经理审批",
|
|
assignee_role: "manager",
|
|
created_at: new Date("2026-05-11T07:00:00.000Z"),
|
|
due_date: null,
|
|
workflow_instance: { entity_type: "reimbursement" },
|
|
},
|
|
{
|
|
id: "task-2",
|
|
status: "pending",
|
|
step_name: "负责人本人申请兜底:finance",
|
|
assignee_role: "finance",
|
|
created_at: new Date("2026-05-12T07:00:00.000Z"),
|
|
due_date: new Date("2026-05-12T07:30:00.000Z"),
|
|
workflow_instance: { entity_type: "purchase_request" },
|
|
},
|
|
];
|
|
|
|
expect(calculateWorkflowTaskDueAt(tasks[0], now).toISOString()).toBe("2026-05-12T07:00:00.000Z");
|
|
expect(isWorkflowTaskOverdue(tasks[0], now)).toBe(true);
|
|
const summary = buildWorkflowTaskSummaryPayload(tasks, "mine", now);
|
|
|
|
expect(summary).toEqual(
|
|
expect.objectContaining({
|
|
scope: "mine",
|
|
pendingTotal: 2,
|
|
overdueTotal: 2,
|
|
earliestPendingAt: "2026-05-11T07:00:00.000Z",
|
|
}),
|
|
);
|
|
expect(summary.byEntityType).toEqual(
|
|
expect.arrayContaining([
|
|
{ key: "reimbursement", pendingTotal: 1, overdueTotal: 1 },
|
|
{ key: "purchase_request", pendingTotal: 1, overdueTotal: 1 },
|
|
]),
|
|
);
|
|
expect(summary.byStep).toEqual(
|
|
expect.arrayContaining([
|
|
{ key: "财务复核", pendingTotal: 1, overdueTotal: 1 },
|
|
]),
|
|
);
|
|
});
|
|
});
|