feat(P15B-1,P15B-6): 完成请假V1闭环
这个提交包含在:
@@ -3,6 +3,7 @@ const { AppError } = require("../utils/AppError");
|
||||
const { sendData } = require("../utils/reply");
|
||||
const { resolvePermissionsForRoles, hasPermission, ROLE_PERMISSIONS, resolveEntityPermission } = require("../constants/permissions");
|
||||
const { getWorkflowStepsSnapshot, resolveActionableStep, toMoneyString } = require("../utils/workflow");
|
||||
const { consumeReservedLeave, releaseReservedLeave, formatDateOnly } = require("../services/leave");
|
||||
const {
|
||||
createNotifications,
|
||||
resolveRecipientsForAssignee,
|
||||
@@ -538,6 +539,34 @@ async function workflowRoutes(fastify) {
|
||||
},
|
||||
},
|
||||
},
|
||||
leave_request: {
|
||||
select: {
|
||||
id: true,
|
||||
status: true,
|
||||
start_date: true,
|
||||
end_date: true,
|
||||
start_session: true,
|
||||
end_session: true,
|
||||
requested_days: true,
|
||||
reason: true,
|
||||
attachment_original_name: true,
|
||||
attachment_mime_type: true,
|
||||
leave_type: {
|
||||
select: {
|
||||
id: true,
|
||||
code: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
owner: {
|
||||
select: {
|
||||
id: true,
|
||||
employee_no: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -616,6 +645,7 @@ async function workflowRoutes(fastify) {
|
||||
workflowInstance: task.workflow_instance
|
||||
? {
|
||||
id: task.workflow_instance.id,
|
||||
entityType: task.workflow_instance.entity_type,
|
||||
currentStatus: task.workflow_instance.current_status,
|
||||
currentStepId: task.workflow_instance.current_step_id,
|
||||
startedAt: isoOrNull(task.workflow_instance.started_at),
|
||||
@@ -649,6 +679,40 @@ async function workflowRoutes(fastify) {
|
||||
: null,
|
||||
}
|
||||
: null,
|
||||
leaveRequest: task.workflow_instance?.leave_request
|
||||
? {
|
||||
id: task.workflow_instance.leave_request.id,
|
||||
status: task.workflow_instance.leave_request.status,
|
||||
startDate: formatDateOnly(task.workflow_instance.leave_request.start_date),
|
||||
endDate: formatDateOnly(task.workflow_instance.leave_request.end_date),
|
||||
startSession: task.workflow_instance.leave_request.start_session,
|
||||
endSession: task.workflow_instance.leave_request.end_session,
|
||||
requestedDays: Number(task.workflow_instance.leave_request.requested_days),
|
||||
reason: task.workflow_instance.leave_request.reason ?? null,
|
||||
attachment:
|
||||
task.workflow_instance.leave_request.attachment_original_name ||
|
||||
task.workflow_instance.leave_request.attachment_mime_type
|
||||
? {
|
||||
originalName: task.workflow_instance.leave_request.attachment_original_name ?? null,
|
||||
mimeType: task.workflow_instance.leave_request.attachment_mime_type ?? null,
|
||||
}
|
||||
: null,
|
||||
leaveType: task.workflow_instance.leave_request.leave_type
|
||||
? {
|
||||
id: task.workflow_instance.leave_request.leave_type.id,
|
||||
code: task.workflow_instance.leave_request.leave_type.code,
|
||||
name: task.workflow_instance.leave_request.leave_type.name,
|
||||
}
|
||||
: null,
|
||||
owner: task.workflow_instance.leave_request.owner
|
||||
? {
|
||||
id: task.workflow_instance.leave_request.owner.id,
|
||||
employeeNo: task.workflow_instance.leave_request.owner.employee_no,
|
||||
name: task.workflow_instance.leave_request.owner.name,
|
||||
}
|
||||
: null,
|
||||
}
|
||||
: null,
|
||||
allowedActions: listAllowedActionsForTask(request, task, actionable),
|
||||
history: historyItems.map((item) => ({
|
||||
id: item.id,
|
||||
@@ -885,6 +949,28 @@ async function workflowRoutes(fastify) {
|
||||
},
|
||||
});
|
||||
} else if (workflowInstance.entity_type === "leave") {
|
||||
const leaveRequest = await tx.leaveRequest.findUnique({
|
||||
where: { id: workflowInstance.entity_id },
|
||||
select: {
|
||||
id: true,
|
||||
owner_id: true,
|
||||
leave_type_id: true,
|
||||
requested_days: true,
|
||||
},
|
||||
});
|
||||
const reserveAnchor = await tx.leaveLedger.findFirst({
|
||||
where: {
|
||||
leave_request_id: workflowInstance.entity_id,
|
||||
entry_type: "reserve",
|
||||
},
|
||||
orderBy: { created_at: "desc" },
|
||||
select: {
|
||||
leave_policy_id: true,
|
||||
period_start: true,
|
||||
period_end: true,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.leaveRequest.update({
|
||||
where: { id: workflowInstance.entity_id },
|
||||
data: {
|
||||
@@ -892,6 +978,19 @@ async function workflowRoutes(fastify) {
|
||||
updated_by: request.employee.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (leaveRequest) {
|
||||
await consumeReservedLeave(tx, {
|
||||
leaveRequestId: workflowInstance.entity_id,
|
||||
employeeId: leaveRequest.owner_id,
|
||||
leaveTypeId: leaveRequest.leave_type_id,
|
||||
leavePolicyId: reserveAnchor?.leave_policy_id ?? null,
|
||||
periodStart: reserveAnchor?.period_start ?? null,
|
||||
periodEnd: reserveAnchor?.period_end ?? null,
|
||||
requestedDays: leaveRequest.requested_days,
|
||||
createdBy: request.employee.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await tx.workflowHistory.create({
|
||||
@@ -1016,6 +1115,27 @@ async function workflowRoutes(fastify) {
|
||||
},
|
||||
});
|
||||
} else if (workflowInstance.entity_type === "leave") {
|
||||
const leaveRequest = await tx.leaveRequest.findUnique({
|
||||
where: { id: workflowInstance.entity_id },
|
||||
select: {
|
||||
id: true,
|
||||
owner_id: true,
|
||||
leave_type_id: true,
|
||||
},
|
||||
});
|
||||
const reserveAnchor = await tx.leaveLedger.findFirst({
|
||||
where: {
|
||||
leave_request_id: workflowInstance.entity_id,
|
||||
entry_type: "reserve",
|
||||
},
|
||||
orderBy: { created_at: "desc" },
|
||||
select: {
|
||||
leave_policy_id: true,
|
||||
period_start: true,
|
||||
period_end: true,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.leaveRequest.update({
|
||||
where: { id: workflowInstance.entity_id },
|
||||
data: {
|
||||
@@ -1023,6 +1143,20 @@ async function workflowRoutes(fastify) {
|
||||
updated_by: request.employee.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (leaveRequest) {
|
||||
await releaseReservedLeave(tx, {
|
||||
leaveRequestId: workflowInstance.entity_id,
|
||||
employeeId: leaveRequest.owner_id,
|
||||
leaveTypeId: leaveRequest.leave_type_id,
|
||||
leavePolicyId: reserveAnchor?.leave_policy_id ?? null,
|
||||
periodStart: reserveAnchor?.period_start ?? null,
|
||||
periodEnd: reserveAnchor?.period_end ?? null,
|
||||
note: "rejected_release_leave",
|
||||
payload: { source: "workflow_reject" },
|
||||
createdBy: request.employee.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await tx.workflowHistory.create({
|
||||
@@ -1146,6 +1280,27 @@ async function workflowRoutes(fastify) {
|
||||
},
|
||||
});
|
||||
} else if (workflowInstance.entity_type === "leave") {
|
||||
const leaveRequest = await tx.leaveRequest.findUnique({
|
||||
where: { id: workflowInstance.entity_id },
|
||||
select: {
|
||||
id: true,
|
||||
owner_id: true,
|
||||
leave_type_id: true,
|
||||
},
|
||||
});
|
||||
const reserveAnchor = await tx.leaveLedger.findFirst({
|
||||
where: {
|
||||
leave_request_id: workflowInstance.entity_id,
|
||||
entry_type: "reserve",
|
||||
},
|
||||
orderBy: { created_at: "desc" },
|
||||
select: {
|
||||
leave_policy_id: true,
|
||||
period_start: true,
|
||||
period_end: true,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.leaveRequest.update({
|
||||
where: { id: workflowInstance.entity_id },
|
||||
data: {
|
||||
@@ -1153,6 +1308,20 @@ async function workflowRoutes(fastify) {
|
||||
updated_by: request.employee.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (leaveRequest) {
|
||||
await releaseReservedLeave(tx, {
|
||||
leaveRequestId: workflowInstance.entity_id,
|
||||
employeeId: leaveRequest.owner_id,
|
||||
leaveTypeId: leaveRequest.leave_type_id,
|
||||
leavePolicyId: reserveAnchor?.leave_policy_id ?? null,
|
||||
periodStart: reserveAnchor?.period_start ?? null,
|
||||
periodEnd: reserveAnchor?.period_end ?? null,
|
||||
note: "returned_release_leave",
|
||||
payload: { source: "workflow_return" },
|
||||
createdBy: request.employee.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await tx.workflowHistory.create({
|
||||
|
||||
在新工单中引用
屏蔽一个用户