370 lines
9.8 KiB
JavaScript
370 lines
9.8 KiB
JavaScript
const express = require("express");
|
||
const { TimeEntry, User } = require("../models");
|
||
const { logActivity } = require("./activityLogs");
|
||
const { calculateWorkHours } = require("../utils/timeCalculator");
|
||
const {
|
||
authenticate,
|
||
authorizeAdmin,
|
||
authorizeManager,
|
||
} = require("../middleware/auth");
|
||
const { sequelize } = require("../models");
|
||
|
||
const router = express.Router();
|
||
|
||
// Get time entries for current user
|
||
router.get("/", authenticate, async (req, res) => {
|
||
try {
|
||
console.log("Fetching entries for user:", req.user.id);
|
||
|
||
const entries = await TimeEntry.findAll({
|
||
where: { userId: req.user.id },
|
||
order: [["date", "DESC"]],
|
||
attributes: [
|
||
"id",
|
||
"userId",
|
||
"date",
|
||
"reason",
|
||
"hours",
|
||
"startDate",
|
||
"startTime",
|
||
"endDate",
|
||
"endTime",
|
||
"status",
|
||
"createdAt",
|
||
"updatedAt",
|
||
],
|
||
}).catch(async (error) => {
|
||
// Если ошибка с колонками, используем только базовые поля
|
||
if (error.message && error.message.includes("column")) {
|
||
console.log("Fallback to basic attributes due to column error");
|
||
return await TimeEntry.findAll({
|
||
where: { userId: req.user.id },
|
||
order: [["date", "DESC"]],
|
||
attributes: [
|
||
"id",
|
||
"userId",
|
||
"date",
|
||
"reason",
|
||
"hours",
|
||
"createdAt",
|
||
"updatedAt",
|
||
],
|
||
});
|
||
}
|
||
throw error;
|
||
});
|
||
|
||
console.log("Found entries:", entries.length);
|
||
res.json(entries);
|
||
} catch (error) {
|
||
console.error("Error fetching entries:", error);
|
||
res.status(500).json({ message: "Server error" });
|
||
}
|
||
});
|
||
|
||
// Get all time entries (admin and manager)
|
||
router.get("/all", authenticate, authorizeManager, async (req, res) => {
|
||
try {
|
||
const entries = await TimeEntry.findAll({
|
||
attributes: [
|
||
"id",
|
||
"userId",
|
||
"date",
|
||
"reason",
|
||
"hours",
|
||
"createdAt",
|
||
"updatedAt",
|
||
],
|
||
include: [{ model: User, attributes: ["username"] }],
|
||
order: [["date", "DESC"]],
|
||
}).catch(async (error) => {
|
||
// Если ошибка с колонками, используем только базовые поля
|
||
if (error.message && error.message.includes("column")) {
|
||
console.log(
|
||
"Fallback to basic attributes for admin view due to column error"
|
||
);
|
||
return await TimeEntry.findAll({
|
||
attributes: [
|
||
"id",
|
||
"userId",
|
||
"date",
|
||
"reason",
|
||
"hours",
|
||
"startDate",
|
||
"startTime",
|
||
"endDate",
|
||
"endTime",
|
||
"status",
|
||
"createdAt",
|
||
"updatedAt",
|
||
],
|
||
include: [{ model: User, attributes: ["username"] }],
|
||
order: [["date", "DESC"]],
|
||
});
|
||
}
|
||
throw error;
|
||
});
|
||
|
||
// Log the export action
|
||
await logActivity(
|
||
req.user.id,
|
||
"Экспорт общей таблицы",
|
||
`Просмотрена общая таблица записей времени`
|
||
);
|
||
|
||
res.json(entries);
|
||
} catch (error) {
|
||
res.status(500).json({ message: "Server error" });
|
||
}
|
||
});
|
||
|
||
// Get time entries for specific user (admin and manager)
|
||
router.get(
|
||
"/user/:userId",
|
||
authenticate,
|
||
authorizeManager,
|
||
async (req, res) => {
|
||
try {
|
||
const { userId } = req.params;
|
||
console.log("Fetching entries for userId:", userId);
|
||
const entries = await TimeEntry.findAll({
|
||
where: { userId },
|
||
order: [["date", "DESC"]],
|
||
});
|
||
console.log("Found entries:", entries);
|
||
|
||
// Get username for logging
|
||
const user = await User.findByPk(userId);
|
||
const username = user ? user.username : "Unknown";
|
||
|
||
// Log the export action for specific user
|
||
await logActivity(
|
||
req.user.id,
|
||
"Экспорт таблицы пользователя",
|
||
`Просмотрена таблица записей времени для пользователя: ${username}`
|
||
);
|
||
|
||
res.json(entries);
|
||
} catch (error) {
|
||
console.error("Error fetching user entries:", error);
|
||
res.status(500).json({ message: "Server error" });
|
||
}
|
||
}
|
||
);
|
||
|
||
// Create time entry
|
||
router.post("/", authenticate, async (req, res) => {
|
||
try {
|
||
const {
|
||
date,
|
||
reason,
|
||
hours,
|
||
startDate,
|
||
startTime,
|
||
endDate,
|
||
endTime,
|
||
isAuto = false,
|
||
} = req.body;
|
||
|
||
console.log("Received data:", {
|
||
date,
|
||
reason,
|
||
hours,
|
||
startDate,
|
||
startTime,
|
||
endDate,
|
||
endTime,
|
||
isAuto,
|
||
});
|
||
|
||
let calculatedHours = parseFloat(hours);
|
||
let status = "closed";
|
||
|
||
// Если автоматический расчет
|
||
if (isAuto && startDate && startTime) {
|
||
try {
|
||
console.log("Calculating work hours for:", {
|
||
startDate,
|
||
startTime,
|
||
endDate,
|
||
endTime,
|
||
});
|
||
|
||
calculatedHours = calculateWorkHours(
|
||
startDate,
|
||
startTime,
|
||
endDate,
|
||
endTime
|
||
);
|
||
|
||
console.log("Calculated hours:", calculatedHours);
|
||
|
||
// Если нет даты окончания - запись активна (открытая)
|
||
if (!endDate || !endTime) {
|
||
status = "active";
|
||
}
|
||
} catch (calcError) {
|
||
console.error("Calculation error:", calcError);
|
||
return res.status(400).json({ message: calcError.message });
|
||
}
|
||
}
|
||
|
||
console.log("Creating entry with data:", {
|
||
userId: req.user.id,
|
||
date: date || startDate,
|
||
reason,
|
||
hours: calculatedHours,
|
||
startDate,
|
||
startTime,
|
||
endDate,
|
||
endTime,
|
||
status,
|
||
});
|
||
|
||
const entryData = {
|
||
userId: req.user.id,
|
||
date: date || startDate, // Для совместимости с ручным вводом
|
||
reason,
|
||
hours: calculatedHours,
|
||
};
|
||
|
||
// Добавляем новые поля только если они существуют в базе данных
|
||
try {
|
||
await sequelize.query('SELECT "startDate" FROM "TimeEntries" LIMIT 1;', {
|
||
type: sequelize.QueryTypes.SELECT,
|
||
});
|
||
entryData.startDate = startDate;
|
||
entryData.startTime = startTime;
|
||
entryData.endDate = endDate;
|
||
entryData.endTime = endTime;
|
||
entryData.status = status;
|
||
} catch (columnError) {
|
||
console.log("New columns do not exist yet, using basic fields only");
|
||
}
|
||
|
||
const entry = await TimeEntry.create(entryData);
|
||
|
||
console.log("Entry created successfully:", entry.id);
|
||
res.status(201).json(entry);
|
||
} catch (error) {
|
||
console.error("Error creating time entry:", error);
|
||
console.error("Error stack:", error.stack);
|
||
console.error("Error message:", error.message);
|
||
if (error.name === "SequelizeValidationError") {
|
||
console.error("Validation errors:", error.errors);
|
||
}
|
||
res.status(500).json({
|
||
message: "Server error",
|
||
error: error.message,
|
||
stack: error.stack,
|
||
});
|
||
}
|
||
});
|
||
|
||
// Update time entry (own or admin)
|
||
router.put("/:id", authenticate, async (req, res) => {
|
||
try {
|
||
const {
|
||
date,
|
||
reason,
|
||
hours,
|
||
startDate,
|
||
startTime,
|
||
endDate,
|
||
endTime,
|
||
isAuto = false,
|
||
} = req.body;
|
||
|
||
const entry = await TimeEntry.findByPk(req.params.id);
|
||
if (!entry) {
|
||
return res.status(404).json({ message: "Entry not found" });
|
||
}
|
||
if (entry.userId !== req.user.id && req.user.role !== "admin") {
|
||
return res.status(403).json({ message: "Access denied" });
|
||
}
|
||
|
||
let calculatedHours = parseFloat(hours);
|
||
let status = entry.status;
|
||
|
||
// Если автоматический расчет или обновление активной записи
|
||
if (isAuto || entry.status === "active") {
|
||
try {
|
||
calculatedHours = calculateWorkHours(
|
||
startDate || entry.startDate,
|
||
startTime || entry.startTime,
|
||
endDate,
|
||
endTime
|
||
);
|
||
|
||
// Если добавили дату окончания - закрываем запись
|
||
if (
|
||
(endDate && endTime) ||
|
||
(endDate && !endTime) ||
|
||
(!endDate && endTime)
|
||
) {
|
||
status = "closed";
|
||
} else {
|
||
status = "active";
|
||
}
|
||
} catch (calcError) {
|
||
return res.status(400).json({ message: calcError.message });
|
||
}
|
||
}
|
||
|
||
await entry.update({
|
||
date: date || entry.date,
|
||
reason,
|
||
hours: calculatedHours,
|
||
startDate,
|
||
startTime,
|
||
endDate,
|
||
endTime,
|
||
status,
|
||
});
|
||
|
||
res.json(entry);
|
||
} catch (error) {
|
||
console.error("Error updating time entry:", error);
|
||
res.status(500).json({ message: "Server error" });
|
||
}
|
||
});
|
||
|
||
// Delete all time entries for current user
|
||
router.delete("/delete-all", authenticate, async (req, res) => {
|
||
try {
|
||
const deletedCount = await TimeEntry.destroy({
|
||
where: { userId: req.user.id },
|
||
});
|
||
|
||
// Log the delete all action
|
||
await logActivity(
|
||
req.user.id,
|
||
"Удаление всех записей",
|
||
`Удалены все записи времени (${deletedCount} записей)`
|
||
);
|
||
|
||
res.json({ message: "All entries deleted", deletedCount });
|
||
} catch (error) {
|
||
res.status(500).json({ message: "Server error" });
|
||
}
|
||
});
|
||
|
||
// Delete time entry (own or admin)
|
||
router.delete("/:id", authenticate, async (req, res) => {
|
||
try {
|
||
const entry = await TimeEntry.findByPk(req.params.id);
|
||
if (!entry) {
|
||
return res.status(404).json({ message: "Entry not found" });
|
||
}
|
||
if (entry.userId !== req.user.id && req.user.role !== "admin") {
|
||
return res.status(403).json({ message: "Access denied" });
|
||
}
|
||
await entry.destroy();
|
||
res.json({ message: "Entry deleted" });
|
||
} catch (error) {
|
||
res.status(500).json({ message: "Server error" });
|
||
}
|
||
});
|
||
|
||
module.exports = router;
|