fix: согласующие перехода — только авторизация, без автоназначения
assigneeUserId/assigneeConditions перехода раньше и давали право нажать кнопку, и молча переназначали задачу на согласующего; кликнувший также всегда добавлялся в task_assignees (список «Ответственный»). Теперь ответственный при переходе всегда сохраняется, кликнувший в ответственные не добавляется (доступ трекается через recordTransitionAccess). В FormEditor добавлена подсказка о семантике блока «Согласующие».
This commit is contained in:
@@ -3,7 +3,6 @@ import { webhookService } from "./webhook.service";
|
||||
import { eventBus, resolveTaskFieldTitles } from "../routes/shared";
|
||||
import { pushTaskUpdated } from "../utils/pushTaskUpdated";
|
||||
import { tasksMinimalCache } from "../utils/cache";
|
||||
import { notifyTaskAssigned } from "../utils/notifyAssignee";
|
||||
import { indexTaskAsync } from "../routes/task-helpers";
|
||||
import { fieldConditionsMatch, resolveFieldDisplayText, isFieldValueEmpty, SYSTEM_TASK_FIELDS, buildSystemFieldValuesForConditions } from "../utils/field-conditions";
|
||||
import { validateRequiredFieldsDetailed } from "../utils/validate-required-fields";
|
||||
@@ -181,7 +180,6 @@ export async function executeTaskTransition(options: ExecuteTaskTransitionOption
|
||||
let isAuthorized = false;
|
||||
let resolvedRoleId: number | null = null; // track role to assign after transition
|
||||
let resolvedAssigneeId: number | null = null; // old-format resolved user
|
||||
let resolvedUserId: number | null = null; // new-format resolved user
|
||||
|
||||
// Check direct assigneeUserId (Type A simple — backward compat)
|
||||
if (!isAuthorized && transition.assigneeUserId !== null && transition.assigneeUserId !== undefined) {
|
||||
@@ -203,7 +201,6 @@ export async function executeTaskTransition(options: ExecuteTaskTransitionOption
|
||||
if (!rulesMatch) continue;
|
||||
|
||||
if (assignee.type === 'user' && assignee.id === user?.id) {
|
||||
resolvedUserId = assignee.id;
|
||||
isAuthorized = true;
|
||||
break;
|
||||
}
|
||||
@@ -227,7 +224,6 @@ export async function executeTaskTransition(options: ExecuteTaskTransitionOption
|
||||
userId = fieldValue?.value ? parseInt(String(fieldValue.value)) : undefined;
|
||||
}
|
||||
if (userId && userId > 0 && userId === user?.id) {
|
||||
resolvedUserId = userId;
|
||||
isAuthorized = true;
|
||||
break;
|
||||
}
|
||||
@@ -243,7 +239,6 @@ export async function executeTaskTransition(options: ExecuteTaskTransitionOption
|
||||
if (row) {
|
||||
const userId = parseInt(String((row.values as unknown[])[assignee.columnIndex as number] ?? ''));
|
||||
if (!isNaN(userId) && userId > 0 && userId === user?.id) {
|
||||
resolvedUserId = userId;
|
||||
isAuthorized = true;
|
||||
break;
|
||||
}
|
||||
@@ -401,19 +396,10 @@ export async function executeTaskTransition(options: ExecuteTaskTransitionOption
|
||||
// Check if target status is final
|
||||
const isFinalStatus = toStatus?.isFinal || false;
|
||||
|
||||
// Determine assignee based on transition rules (priority: direct > old format > new format > preserve current)
|
||||
let targetAssigneeId: number | null = null;
|
||||
if (transition.assigneeUserId !== null && transition.assigneeUserId !== undefined) {
|
||||
targetAssigneeId = transition.assigneeUserId;
|
||||
} else if (resolvedAssigneeId !== null) {
|
||||
targetAssigneeId = resolvedAssigneeId;
|
||||
} else if (resolvedUserId !== null) {
|
||||
targetAssigneeId = resolvedUserId;
|
||||
} else {
|
||||
// If the transition does not explicitly specify an assignee, keep the existing one
|
||||
// instead of defaulting to the user who clicked the button.
|
||||
targetAssigneeId = existingTask.assignedTo ?? null;
|
||||
}
|
||||
// «Согласующие» перехода (assigneeUserId / assigneeConditions) — это ТОЛЬКО
|
||||
// авторизация (кто может выполнить переход). Они НЕ меняют ответственного задачи:
|
||||
// ответственный всегда сохраняется, автоназначения при переходе нет.
|
||||
const targetAssigneeId: number | null = existingTask.assignedTo ?? null;
|
||||
|
||||
// Update task status
|
||||
const updateData: Record<string, unknown> = {
|
||||
@@ -440,12 +426,6 @@ export async function executeTaskTransition(options: ExecuteTaskTransitionOption
|
||||
});
|
||||
}
|
||||
|
||||
// Уведомление новому ответственному, если он изменился
|
||||
if (targetAssigneeId && targetAssigneeId !== existingTask.assignedTo) {
|
||||
notifyTaskAssigned(updatedTask, targetAssigneeId, user?.id ?? null, organizationId)
|
||||
.catch((err) => console.error('[Transition] notifyTaskAssigned error:', err));
|
||||
}
|
||||
|
||||
// If a role was resolved as assignee — assign it to the task
|
||||
if (resolvedRoleId !== null) {
|
||||
await storage.addTaskRole(taskId, resolvedRoleId, organizationId).catch(err => {
|
||||
@@ -453,12 +433,10 @@ export async function executeTaskTransition(options: ExecuteTaskTransitionOption
|
||||
});
|
||||
}
|
||||
|
||||
// Add current user as task assignee (upsert — no duplicates)
|
||||
// Доступ по переходу (grantedVia='transition', со statusId и roleId).
|
||||
// ВНИМАНИЕ: кликнувшего НЕ добавляем в task_assignees — это список ответственных,
|
||||
// а не участников; иначе любой выполнивший переход «становился ответственным».
|
||||
if (user?.id) {
|
||||
storage.addTaskAssignee(taskId, user.id, organizationId).catch(err => {
|
||||
console.error('addTaskAssignee (transition) error:', err);
|
||||
});
|
||||
// Record transition access (grantedVia='transition', with statusId and roleId)
|
||||
storage.recordTransitionAccess(taskId, user.id, organizationId, resolvedRoleId, targetStatusId).catch(err => {
|
||||
console.error('recordTransitionAccess error:', err);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user