fix: 财务本人报销由总经理独立审批(09-04-reimbursement-state-gap-finance-view)
这个提交包含在:
@@ -1,10 +1,12 @@
|
||||
const { z } = require("zod");
|
||||
const { AppError } = require("../utils/AppError");
|
||||
const { buildBusinessValidationError } = require("../utils/businessValidationError");
|
||||
const { buildAuditLogData } = require("../utils/audit");
|
||||
const { sendData } = require("../utils/reply");
|
||||
const { hasPermission, ROLE_PERMISSIONS, resolveEntityPermission } = require("../constants/permissions");
|
||||
const {
|
||||
BUSINESS_APPROVAL_ROLE_CODES,
|
||||
FINANCE_OWNER_APPROVAL_STEP_ID,
|
||||
getWorkflowStepsSnapshot,
|
||||
isBusinessApprovalRoleCode,
|
||||
resolveActionableStep,
|
||||
@@ -685,6 +687,11 @@ function listAllowedActionsForTask(request, task, actionable) {
|
||||
if (task.status !== "pending" || !actionable) {
|
||||
return [];
|
||||
}
|
||||
if (task.workflow_instance?.entity_type === "reimbursement"
|
||||
&& task.step_id === FINANCE_OWNER_APPROVAL_STEP_ID
|
||||
&& !request.employee.roleCodes.includes("ceo")) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const actions = [];
|
||||
const approvePermission = resolveEntityPermission(task.workflow_instance?.entity_type, "approve");
|
||||
@@ -1066,10 +1073,32 @@ function ensureNoSelfApproval(actorIds, applicantIds) {
|
||||
});
|
||||
}
|
||||
|
||||
async function ensureFinanceOwnerCeoActors(tx, workflowInstance, actorIds) {
|
||||
if (workflowInstance.entity_type !== "reimbursement"
|
||||
|| workflowInstance.current_step_id !== FINANCE_OWNER_APPROVAL_STEP_ID) {
|
||||
return;
|
||||
}
|
||||
const eligible = await tx.employeeRole.findMany({
|
||||
where: {
|
||||
employee_id: { in: actorIds },
|
||||
role_code: "ceo",
|
||||
employee: { status: "active", archived_at: null },
|
||||
},
|
||||
select: { employee_id: true },
|
||||
});
|
||||
if (actorIds.some((actorId) => !eligible.some((holder) => holder.employee_id === actorId))) {
|
||||
throw buildBusinessValidationError("reimbursement.finance_owner_ceo_required", {
|
||||
workflow: "finance_owner_ceo_required",
|
||||
}, 403);
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureWorkflowApprovalAllowed(tx, workflowInstance, request, assignment) {
|
||||
ensureBusinessApproveRole(request.employee, workflowInstance.entity_type);
|
||||
const applicantIds = await loadWorkflowApplicantIds(tx, workflowInstance);
|
||||
ensureNoSelfApproval(uniq([request.employee.id, assignment?.principalId]), applicantIds);
|
||||
const actorIds = uniq([request.employee.id, assignment?.principalId]);
|
||||
ensureNoSelfApproval(actorIds, applicantIds);
|
||||
await ensureFinanceOwnerCeoActors(tx, workflowInstance, actorIds);
|
||||
return applicantIds;
|
||||
}
|
||||
|
||||
@@ -3197,6 +3226,7 @@ async function workflowRoutes(fastify) {
|
||||
const transferCount = await ensureTransferChainNotExceeded(tx, taskId);
|
||||
await ensureTransferTargetEligible(tx, toApproverId, approvePermission);
|
||||
ensureNoSelfApproval([toApproverId], applicantIds);
|
||||
await ensureFinanceOwnerCeoActors(tx, workflowInstance, [toApproverId]);
|
||||
|
||||
if (toApproverId === request.employee.id) {
|
||||
throw new AppError({
|
||||
|
||||
在新工单中引用
屏蔽一个用户