feat: замещение пользователей (substitution) — заместители по умолчанию и по формам, контур ознакомления, inbox reasons
- таблица user_delegations (миграция 0067) + колонки delegated_from_user_id/pending_review в task_assignees - delegation.service: резолвер с цепочкой и защитой от циклов, bulk-до-назначение чанками, чистка истёкших замещений - центральный хук в addTaskAssignee: заместитель добавляется при любом назначении (ручном, автопереходе, user-поле, MCP) - контур ознакомления: после перехода отсутствующий остаётся в исполнителях с pending_review, снимается кнопкой «Ознакомлен» - inbox reasons delegation/delegation_review + фильтр «Замещение» на главной - блок «Замещение» в профиле пользователя, бейдж «за <имя>» и баннер ознакомления в карточке задачи
This commit is contained in:
@@ -6,6 +6,7 @@ import { evaluateAutoTransitions } from "../utils/auto-transitions";
|
||||
import { tasksMinimalCache } from "../utils/cache";
|
||||
import { eventBus } from "./shared";
|
||||
import { notifyTaskAssigned } from "../utils/notifyAssignee";
|
||||
import { acknowledgeDelegationReview } from "../services/delegation.service";
|
||||
|
||||
export function registerTaskRoleRoutes(router: ReturnType<typeof import("express").Router>): void {
|
||||
// === TASK ROLES ===
|
||||
@@ -242,6 +243,34 @@ export function registerTaskRoleRoutes(router: ReturnType<typeof import("express
|
||||
}
|
||||
);
|
||||
|
||||
// Подтверждение ознакомления по замещению: текущий пользователь (отсутствующий
|
||||
// исполнитель, вернувшийся в задачу с pending_review) снимает свою строку.
|
||||
router.post('/api/tasks/:id/delegation-ack',
|
||||
authenticateToken,
|
||||
tenantIsolation,
|
||||
async (req: AuthenticatedRequest, res) => {
|
||||
try {
|
||||
const taskId = parseInt(req.params.id);
|
||||
if (isNaN(taskId)) return res.status(400).json({ error: 'Неверный ID задачи' });
|
||||
const task = await storage.getTask(taskId, req.organizationId!);
|
||||
if (!task) return res.status(404).json({ error: 'Задача не найдена' });
|
||||
const acknowledged = await acknowledgeDelegationReview(taskId, req.user!.id, req.organizationId!);
|
||||
if (!acknowledged) {
|
||||
return res.status(404).json({ error: 'Задача не ожидает вашего ознакомления' });
|
||||
}
|
||||
eventBus.publishEvent({
|
||||
type: 'task_updated',
|
||||
organizationId: req.organizationId!,
|
||||
data: { taskId, formId: task.formId, task }
|
||||
});
|
||||
res.json({ success: true });
|
||||
} catch (err) {
|
||||
console.error('Delegation ack error:', err);
|
||||
res.status(500).json({ error: 'Ошибка при подтверждении ознакомления' });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// === TASK USER ACCESS ===
|
||||
|
||||
router.get('/api/tasks/:id/access',
|
||||
|
||||
Reference in New Issue
Block a user