выполнить
This commit is contained in:
29
public/ui.js
29
public/ui.js
@@ -227,7 +227,32 @@ function openReworkAssignmentModal(taskId, userId, userName) {
|
|||||||
setTimeout(() => textarea.focus(), 100);
|
setTimeout(() => textarea.focus(), 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Функция для принудительного завершения задачи исполнителя
|
||||||
|
async function forceCompleteAssignment(taskId, userId, userName) {
|
||||||
|
if (!confirm(`Вы уверены, что хотите отметить задачу как выполненную для сотрудника ${userName}?\nЭто действие нельзя отменить.`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/tasks/${taskId}/force-complete/${userId}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
alert(`✅ Задача отмечена как выполненная для сотрудника ${userName}`);
|
||||||
|
loadTasks(); // Перезагружаем задачи
|
||||||
|
} else {
|
||||||
|
const error = await response.json();
|
||||||
|
alert(`❌ Ошибка: ${error.error || 'Неизвестная ошибка'}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Ошибка:', error);
|
||||||
|
alert('Сетевая ошибка при выполнении операции');
|
||||||
|
}
|
||||||
|
}
|
||||||
// Вспомогательная функция для экранирования HTML
|
// Вспомогательная функция для экранирования HTML
|
||||||
function escapeHtml(text) {
|
function escapeHtml(text) {
|
||||||
if (!text) return '';
|
if (!text) return '';
|
||||||
@@ -426,7 +451,9 @@ ${isCurrentUser && assignment.status === 'assigned' ?
|
|||||||
${isCurrentUser && (assignment.status === 'in_progress' || assignment.status === 'overdue' || assignment.status === 'rework') ?
|
${isCurrentUser && (assignment.status === 'in_progress' || assignment.status === 'overdue' || assignment.status === 'rework') ?
|
||||||
`<button onclick="updateStatus(${taskId}, ${assignment.user_id}, 'completed')">Выполнено</button>` : ''}
|
`<button onclick="updateStatus(${taskId}, ${assignment.user_id}, 'completed')">Выполнено</button>` : ''}
|
||||||
${isTaskCreator && assignment.status !== 'assigned' ?
|
${isTaskCreator && assignment.status !== 'assigned' ?
|
||||||
`<button class="rework-btn" onclick="openReworkAssignmentModal(${taskId}, ${assignment.user_id}, '${assignment.user_name}')" title="Отправить на доработку">🔄</button>` : ''}
|
`<button class="rework-btn" onclick="openReworkAssignmentModal(${taskId}, ${assignment.user_id}, '${assignment.user_name}')" title="Отправить на доработку">🔄Переделать</button>` : ''}
|
||||||
|
${isTaskCreator && assignment.status !== 'completed' ?
|
||||||
|
`<button class="force-complete-btn" onclick="forceCompleteAssignment(${taskId}, ${assignment.user_id}, '${assignment.user_name}')" title="Принудительно отметить как выполненное">✅ Завершить</button>` : ''}
|
||||||
${canEdit ?
|
${canEdit ?
|
||||||
`<button class="edit-date-btn" onclick="openEditAssignmentModal(${taskId}, ${assignment.user_id})" title="Редактировать сроки">📅</button>` : ''}
|
`<button class="edit-date-btn" onclick="openEditAssignmentModal(${taskId}, ${assignment.user_id})" title="Редактировать сроки">📅</button>` : ''}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1269,12 +1269,18 @@ app.get('/api/document-approval-tasks', requireAuth, (req, res) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// API для отправки конкретного исполнителя на доработку
|
// API для отправки конкретного исполнителя на доработку
|
||||||
app.put('/api/tasks/:taskId/rework-assignment/:userId', requireAuth, (req, res) => {
|
app.put('/api/tasks/:taskId/rework-assignment/:userId', requireAuth, (req, res) => {
|
||||||
const { taskId, userId } = req.params;
|
const { taskId, userId } = req.params;
|
||||||
const { comment } = req.body;
|
const { comment } = req.body;
|
||||||
const currentUserId = req.session.user.id;
|
const currentUserId = req.session.user.id;
|
||||||
|
|
||||||
|
console.log('=== ОТПРАВКА НА ДОРАБОТКУ ===');
|
||||||
|
console.log('taskId:', taskId);
|
||||||
|
console.log('userId:', userId);
|
||||||
|
console.log('comment:', comment);
|
||||||
|
console.log('==============================');
|
||||||
|
|
||||||
if (!comment) {
|
if (!comment) {
|
||||||
return res.status(400).json({ error: 'Комментарий к доработке обязателен' });
|
return res.status(400).json({ error: 'Комментарий к доработке обязателен' });
|
||||||
}
|
}
|
||||||
@@ -1285,11 +1291,22 @@ app.put('/api/tasks/:taskId/rework-assignment/:userId', requireAuth, (req, res)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем права: автор задачи или администратор
|
// Проверяем права: автор задачи или администратор
|
||||||
if (req.session.user.role !== 'admin' && parseInt(task.created_by) !== currentUserId) {
|
if (req.session.user.role !== 'admin' && parseInt(task.created_by) !== parseInt(currentUserId)) {
|
||||||
return res.status(403).json({ error: 'Только автор задачи может отправлять на доработку' });
|
return res.status(403).json({ error: 'Только автор задачи может отправлять на доработку' });
|
||||||
}
|
}
|
||||||
|
|
||||||
db.serialize(() => {
|
// Проверяем существование назначения
|
||||||
|
db.get("SELECT id, status FROM task_assignments WHERE task_id = ? AND user_id = ?",
|
||||||
|
[taskId, userId],
|
||||||
|
(err, assignment) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!assignment) {
|
||||||
|
return res.status(404).json({ error: 'Исполнитель не назначен на эту задачу' });
|
||||||
|
}
|
||||||
|
|
||||||
// Обновляем статус конкретного исполнителя
|
// Обновляем статус конкретного исполнителя
|
||||||
db.run(
|
db.run(
|
||||||
`UPDATE task_assignments
|
`UPDATE task_assignments
|
||||||
@@ -1304,15 +1321,12 @@ app.put('/api/tasks/:taskId/rework-assignment/:userId', requireAuth, (req, res)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.changes === 0) {
|
|
||||||
return res.status(404).json({ error: 'Исполнитель не найден в задаче' });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Логируем действие
|
// Логируем действие
|
||||||
const { logActivity } = require('./database');
|
try {
|
||||||
if (logActivity) {
|
logActivity(parseInt(taskId), currentUserId, 'ASSIGNMENT_REWORK',
|
||||||
logActivity(taskId, currentUserId, 'ASSIGNMENT_REWORK',
|
`Исполнитель ${userId} отправлен на доработку: ${comment.substring(0, 50)}`);
|
||||||
`Исполнитель ${userId} отправлен на доработку: ${comment}`);
|
} catch (e) {
|
||||||
|
console.error('Ошибка логирования:', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получаем данные для уведомления
|
// Получаем данные для уведомления
|
||||||
@@ -1322,7 +1336,7 @@ app.put('/api/tasks/:taskId/rework-assignment/:userId', requireAuth, (req, res)
|
|||||||
WHERE t.id = ?`,
|
WHERE t.id = ?`,
|
||||||
[userId, taskId], (err, taskData) => {
|
[userId, taskId], (err, taskData) => {
|
||||||
if (!err && taskData) {
|
if (!err && taskData) {
|
||||||
const { sendTaskNotifications } = require('./notifications');
|
try {
|
||||||
sendTaskNotifications(
|
sendTaskNotifications(
|
||||||
'rework_assignment',
|
'rework_assignment',
|
||||||
taskId,
|
taskId,
|
||||||
@@ -1332,8 +1346,11 @@ app.put('/api/tasks/:taskId/rework-assignment/:userId', requireAuth, (req, res)
|
|||||||
comment,
|
comment,
|
||||||
'rework',
|
'rework',
|
||||||
req.session.user.name,
|
req.session.user.name,
|
||||||
userId // ID исполнителя для персонального уведомления
|
userId
|
||||||
);
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Ошибка отправки уведомления:', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1343,10 +1360,126 @@ app.put('/api/tasks/:taskId/rework-assignment/:userId', requireAuth, (req, res)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// API для принудительного завершения задачи исполнителем (для автора/админа)
|
||||||
|
app.put('/api/tasks/:taskId/force-complete/:userId', requireAuth, (req, res) => {
|
||||||
|
const { taskId, userId } = req.params;
|
||||||
|
const currentUserId = req.session.user.id;
|
||||||
|
|
||||||
|
console.log('=== ПРИНУДИТЕЛЬНОЕ ЗАВЕРШЕНИЕ ===');
|
||||||
|
console.log('taskId:', taskId);
|
||||||
|
console.log('userId:', userId);
|
||||||
|
console.log('currentUserId:', currentUserId);
|
||||||
|
console.log('==================================');
|
||||||
|
|
||||||
|
db.get("SELECT created_by, title, description, closed_at FROM tasks WHERE id = ?", [taskId], (err, task) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('❌ Ошибка при поиске задачи:', err);
|
||||||
|
return res.status(500).json({ error: 'Ошибка базы данных' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!task) {
|
||||||
|
return res.status(404).json({ error: 'Задача не найдена' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем права: автор задачи или администратор
|
||||||
|
if (req.session.user.role !== 'admin' && parseInt(task.created_by) !== parseInt(currentUserId)) {
|
||||||
|
return res.status(403).json({ error: 'Только автор задачи может принудительно завершать выполнение' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, что задача не закрыта
|
||||||
|
if (task.closed_at) {
|
||||||
|
return res.status(400).json({ error: 'Задача уже закрыта' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем существование назначения
|
||||||
|
db.get("SELECT id, status FROM task_assignments WHERE task_id = ? AND user_id = ?",
|
||||||
|
[taskId, userId],
|
||||||
|
(err, assignment) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('❌ Ошибка при поиске назначения:', err);
|
||||||
|
return res.status(500).json({ error: 'Ошибка базы данных' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!assignment) {
|
||||||
|
return res.status(404).json({ error: 'Исполнитель не назначен на эту задачу' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если уже выполнено, просто возвращаем успех
|
||||||
|
if (assignment.status === 'completed') {
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Задача уже была отмечена как выполненная'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем статус конкретного исполнителя на "completed"
|
||||||
|
db.run(
|
||||||
|
`UPDATE task_assignments
|
||||||
|
SET status = 'completed',
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE task_id = ? AND user_id = ?`,
|
||||||
|
[taskId, userId],
|
||||||
|
function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error('❌ Ошибка при обновлении статуса:', err);
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`✅ Статус исполнителя ${userId} обновлен на completed`);
|
||||||
|
|
||||||
|
// Логируем действие
|
||||||
|
try {
|
||||||
|
logActivity(parseInt(taskId), currentUserId, 'ASSIGNMENT_FORCE_COMPLETED',
|
||||||
|
`Исполнитель ${userId} принудительно отмечен как выполнивший задачу`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Ошибка логирования:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, все ли исполнители выполнили задачу
|
||||||
|
db.all(
|
||||||
|
"SELECT status FROM task_assignments WHERE task_id = ?",
|
||||||
|
[taskId],
|
||||||
|
(err, assignments) => {
|
||||||
|
if (!err && assignments && assignments.length > 0) {
|
||||||
|
const allCompleted = assignments.every(a => a.status === 'completed');
|
||||||
|
|
||||||
|
if (allCompleted) {
|
||||||
|
// Если все исполнители выполнили, автоматически закрываем задачу
|
||||||
|
db.run(
|
||||||
|
"UPDATE tasks SET closed_at = CURRENT_TIMESTAMP, closed_by = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
|
||||||
|
[currentUserId, taskId],
|
||||||
|
function(err) {
|
||||||
|
if (!err) {
|
||||||
|
console.log(`✅ Задача ${taskId} автоматически закрыта`);
|
||||||
|
try {
|
||||||
|
logActivity(parseInt(taskId), currentUserId, 'TASK_CLOSED',
|
||||||
|
'Задача автоматически закрыта после выполнения всеми исполнителями');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Ошибка логирования:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Исполнитель отмечен как выполнивший задачу'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
app.post('/api/tasks/:taskId/files', requireAuth, upload.array('files', 15), (req, res) => {
|
app.post('/api/tasks/:taskId/files', requireAuth, upload.array('files', 15), (req, res) => {
|
||||||
const { taskId } = req.params;
|
const { taskId } = req.params;
|
||||||
const userId = req.session.user.id;
|
const userId = req.session.user.id;
|
||||||
|
|||||||
Reference in New Issue
Block a user