370 lines
9.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;