выполнить
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);
|
||||
}
|
||||
}
|
||||
// Функция для принудительного завершения задачи исполнителя
|
||||
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
|
||||
function escapeHtml(text) {
|
||||
if (!text) return '';
|
||||
@@ -426,7 +451,9 @@ ${isCurrentUser && assignment.status === 'assigned' ?
|
||||
${isCurrentUser && (assignment.status === 'in_progress' || assignment.status === 'overdue' || assignment.status === 'rework') ?
|
||||
`<button onclick="updateStatus(${taskId}, ${assignment.user_id}, 'completed')">Выполнено</button>` : ''}
|
||||
${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 ?
|
||||
`<button class="edit-date-btn" onclick="openEditAssignmentModal(${taskId}, ${assignment.user_id})" title="Редактировать сроки">📅</button>` : ''}
|
||||
</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) => {
|
||||
const { taskId, userId } = req.params;
|
||||
const { comment } = req.body;
|
||||
const currentUserId = req.session.user.id;
|
||||
|
||||
console.log('=== ОТПРАВКА НА ДОРАБОТКУ ===');
|
||||
console.log('taskId:', taskId);
|
||||
console.log('userId:', userId);
|
||||
console.log('comment:', comment);
|
||||
console.log('==============================');
|
||||
|
||||
if (!comment) {
|
||||
return res.status(400).json({ error: 'Комментарий к доработке обязателен' });
|
||||
}
|
||||
@@ -1285,68 +1291,195 @@ 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: 'Только автор задачи может отправлять на доработку' });
|
||||
}
|
||||
|
||||
db.serialize(() => {
|
||||
// Обновляем статус конкретного исполнителя
|
||||
db.run(
|
||||
`UPDATE task_assignments
|
||||
SET status = 'rework',
|
||||
rework_comment = ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE task_id = ? AND user_id = ?`,
|
||||
[comment, taskId, userId],
|
||||
function(err) {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.changes === 0) {
|
||||
return res.status(404).json({ error: 'Исполнитель не найден в задаче' });
|
||||
}
|
||||
|
||||
// Логируем действие
|
||||
const { logActivity } = require('./database');
|
||||
if (logActivity) {
|
||||
logActivity(taskId, currentUserId, 'ASSIGNMENT_REWORK',
|
||||
`Исполнитель ${userId} отправлен на доработку: ${comment}`);
|
||||
}
|
||||
|
||||
// Получаем данные для уведомления
|
||||
db.get(`SELECT t.title, t.description, u.name as executor_name
|
||||
FROM tasks t
|
||||
LEFT JOIN users u ON u.id = ?
|
||||
WHERE t.id = ?`,
|
||||
[userId, taskId], (err, taskData) => {
|
||||
if (!err && taskData) {
|
||||
const { sendTaskNotifications } = require('./notifications');
|
||||
sendTaskNotifications(
|
||||
'rework_assignment',
|
||||
taskId,
|
||||
taskData.title,
|
||||
taskData.description,
|
||||
currentUserId,
|
||||
comment,
|
||||
'rework',
|
||||
req.session.user.name,
|
||||
userId // ID исполнителя для персонального уведомления
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Исполнитель отправлен на доработку'
|
||||
});
|
||||
// Проверяем существование назначения
|
||||
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(
|
||||
`UPDATE task_assignments
|
||||
SET status = 'rework',
|
||||
rework_comment = ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE task_id = ? AND user_id = ?`,
|
||||
[comment, taskId, userId],
|
||||
function(err) {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
|
||||
// Логируем действие
|
||||
try {
|
||||
logActivity(parseInt(taskId), currentUserId, 'ASSIGNMENT_REWORK',
|
||||
`Исполнитель ${userId} отправлен на доработку: ${comment.substring(0, 50)}`);
|
||||
} catch (e) {
|
||||
console.error('Ошибка логирования:', e);
|
||||
}
|
||||
|
||||
// Получаем данные для уведомления
|
||||
db.get(`SELECT t.title, t.description, u.name as executor_name
|
||||
FROM tasks t
|
||||
LEFT JOIN users u ON u.id = ?
|
||||
WHERE t.id = ?`,
|
||||
[userId, taskId], (err, taskData) => {
|
||||
if (!err && taskData) {
|
||||
try {
|
||||
sendTaskNotifications(
|
||||
'rework_assignment',
|
||||
taskId,
|
||||
taskData.title,
|
||||
taskData.description,
|
||||
currentUserId,
|
||||
comment,
|
||||
'rework',
|
||||
req.session.user.name,
|
||||
userId
|
||||
);
|
||||
} catch (e) {
|
||||
console.error('Ошибка отправки уведомления:', e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Исполнитель отправлен на доработку'
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// 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) => {
|
||||
const { taskId } = req.params;
|
||||
const userId = req.session.user.id;
|
||||
|
||||
Reference in New Issue
Block a user