234 lines
6.9 KiB
JavaScript
234 lines
6.9 KiB
JavaScript
// POST / - create report (validate body, use req.user)
|
|
// GET / - list reports (filters: by user, store, date; admin sees all)
|
|
// PUT /:id - update report (owner or admin)
|
|
// DELETE /:id - delete (admin only)
|
|
|
|
const express = require("express");
|
|
const { body, param, query, validationResult } = require("express-validator");
|
|
const { db } = require("../database/init");
|
|
const verifyToken = require("../middleware/auth");
|
|
|
|
const router = express.Router();
|
|
|
|
// POST /api/reports - create a report
|
|
router.post(
|
|
"/",
|
|
verifyToken,
|
|
[
|
|
body("storeId").isInt({ min: 1 }).withMessage("Valid storeId required"),
|
|
body("reportDate").isISO8601().withMessage("Valid reportDate required"),
|
|
body("income").isFloat().withMessage("Valid income required"),
|
|
body("initialCash").isFloat().withMessage("Valid initialCash required"),
|
|
body("totalIncome").isFloat().withMessage("Valid totalIncome required"),
|
|
body("wages").optional().isString(),
|
|
body("expenses").optional().isString(),
|
|
body("totalWages").isFloat(),
|
|
body("totalExpenses").isFloat(),
|
|
body("envelope").isFloat(),
|
|
body("finalCash").isFloat(),
|
|
],
|
|
async (req, res) => {
|
|
const errors = validationResult(req);
|
|
if (!errors.isEmpty())
|
|
return res.status(400).json({ errors: errors.array() });
|
|
|
|
const {
|
|
storeId,
|
|
reportDate,
|
|
income,
|
|
initialCash,
|
|
totalIncome,
|
|
wages,
|
|
expenses,
|
|
totalWages,
|
|
totalExpenses,
|
|
envelope,
|
|
finalCash,
|
|
} = req.body;
|
|
const userId = req.user.userId;
|
|
|
|
db.run(
|
|
`INSERT INTO reports
|
|
(userId, storeId, reportDate, income, initialCash, totalIncome, wages, expenses, totalWages, totalExpenses, envelope, finalCash)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
[
|
|
userId,
|
|
storeId,
|
|
reportDate,
|
|
income,
|
|
initialCash,
|
|
totalIncome,
|
|
wages,
|
|
expenses,
|
|
totalWages,
|
|
totalExpenses,
|
|
envelope,
|
|
finalCash,
|
|
],
|
|
function (err) {
|
|
if (err) {
|
|
if (
|
|
err.message &&
|
|
err.message.toLowerCase().includes("unique constraint failed")
|
|
) {
|
|
return res.status(409).json({
|
|
error:
|
|
"Отчет за этот магазин и дату уже был отправлен этим пользователем.",
|
|
});
|
|
}
|
|
console.error("DB error:", err);
|
|
return res.status(500).json({ error: "Database error" });
|
|
}
|
|
res.status(201).json({ id: this.lastID });
|
|
}
|
|
);
|
|
}
|
|
);
|
|
|
|
// GET /api/reports - list reports, optional filters
|
|
router.get(
|
|
"/",
|
|
verifyToken,
|
|
[
|
|
query("userId").optional().isInt(),
|
|
query("storeId").optional().isInt(),
|
|
query("reportDate").optional().isISO8601(),
|
|
],
|
|
(req, res) => {
|
|
let sql = `
|
|
SELECT reports.*, stores.name AS storeName, users.username AS username, users.fullName AS fullName
|
|
FROM reports
|
|
JOIN stores ON reports.storeId = stores.id
|
|
JOIN users ON reports.userId = users.id
|
|
WHERE 1=1
|
|
`;
|
|
const params = [];
|
|
|
|
if (req.user.role !== "admin") {
|
|
sql += " AND reports.userId = ?";
|
|
params.push(req.user.userId);
|
|
} else {
|
|
if (req.query.userId) {
|
|
sql += " AND reports.userId = ?";
|
|
params.push(req.query.userId);
|
|
}
|
|
}
|
|
if (req.query.storeId) {
|
|
sql += " AND reports.storeId = ?";
|
|
params.push(req.query.storeId);
|
|
}
|
|
if (req.query.reportDate) {
|
|
sql += " AND reports.reportDate = ?";
|
|
params.push(req.query.reportDate);
|
|
}
|
|
sql += " ORDER BY reports.reportDate DESC";
|
|
|
|
db.all(sql, params, (err, rows) => {
|
|
if (err) return res.status(500).json({ error: "Database error" });
|
|
res.json({ reports: rows });
|
|
});
|
|
}
|
|
);
|
|
|
|
// PUT /api/reports/:id - update a report
|
|
router.put(
|
|
"/:id",
|
|
verifyToken,
|
|
[
|
|
param("id").isInt(),
|
|
body("income").optional().isFloat(),
|
|
body("initialCash").optional().isFloat(),
|
|
body("totalIncome").optional().isFloat(),
|
|
body("wages").optional().isString(),
|
|
body("expenses").optional().isString(),
|
|
body("totalWages").optional().isFloat(),
|
|
body("totalExpenses").optional().isFloat(),
|
|
body("envelope").optional().isFloat(),
|
|
body("finalCash").optional().isFloat(),
|
|
body("isVerified").optional().isInt({ min: 0, max: 1 }),
|
|
],
|
|
(req, res) => {
|
|
const errors = validationResult(req);
|
|
if (!errors.isEmpty())
|
|
return res.status(400).json({ errors: errors.array() });
|
|
|
|
const reportId = req.params.id;
|
|
// Only owner or admin can update
|
|
db.get("SELECT * FROM reports WHERE id = ?", [reportId], (err, report) => {
|
|
if (err || !report)
|
|
return res.status(404).json({ error: "Report not found" });
|
|
if (req.user.role !== "admin" && report.userId !== req.user.userId) {
|
|
return res.status(403).json({ error: "Forbidden" });
|
|
}
|
|
|
|
const fields = [];
|
|
const values = [];
|
|
for (const key of [
|
|
"income",
|
|
"initialCash",
|
|
"totalIncome",
|
|
"wages",
|
|
"expenses",
|
|
"totalWages",
|
|
"totalExpenses",
|
|
"envelope",
|
|
"finalCash",
|
|
"isVerified",
|
|
]) {
|
|
if (req.body[key] !== undefined) {
|
|
fields.push(`${key} = ?`);
|
|
values.push(req.body[key]);
|
|
}
|
|
}
|
|
if (fields.length === 0)
|
|
return res.status(400).json({ error: "No data to update" });
|
|
|
|
values.push(reportId);
|
|
db.run(
|
|
`UPDATE reports SET ${fields.join(
|
|
", "
|
|
)}, updatedAt = CURRENT_TIMESTAMP WHERE id = ?`,
|
|
values,
|
|
function (err) {
|
|
if (err) return res.status(500).json({ error: "Database error" });
|
|
res.json({ updated: this.changes });
|
|
}
|
|
);
|
|
});
|
|
}
|
|
);
|
|
|
|
// POST /api/reports/:id/verify - admin only
|
|
router.post("/:id/verify", verifyToken, [param("id").isInt()], (req, res) => {
|
|
if (req.user.role !== "admin")
|
|
return res.status(403).json({ error: "Admin only" });
|
|
|
|
const reportId = req.params.id;
|
|
const verifiedBy = req.user.userId;
|
|
const verifiedAt = new Date().toISOString();
|
|
|
|
db.run(
|
|
`UPDATE reports SET isVerified = 1, verifiedBy = ?, verifiedAt = ?, updatedAt = CURRENT_TIMESTAMP WHERE id = ?`,
|
|
[verifiedBy, verifiedAt, reportId],
|
|
function (err) {
|
|
if (err) return res.status(500).json({ error: "Database error" });
|
|
if (this.changes === 0)
|
|
return res.status(404).json({ error: "Report not found" });
|
|
res.json({ verified: true, reportId });
|
|
}
|
|
);
|
|
});
|
|
|
|
// DELETE /api/reports/:id - admin only
|
|
router.delete("/:id", verifyToken, [param("id").isInt()], (req, res) => {
|
|
if (req.user.role !== "admin")
|
|
return res.status(403).json({ error: "Admin only" });
|
|
|
|
db.run("DELETE FROM reports WHERE id = ?", [req.params.id], function (err) {
|
|
if (err) return res.status(500).json({ error: "Database error" });
|
|
res.json({ deleted: this.changes });
|
|
});
|
|
});
|
|
|
|
module.exports = router;
|