diff --git a/models/order.js b/models/order.js
index f95bfc7..a81111f 100644
--- a/models/order.js
+++ b/models/order.js
@@ -8,7 +8,7 @@ var schema = new Schema({
},
deliveryId: {
type: Schema.Types.ObjectId,
- ref: "Stock",
+ ref: "Delivery",
required: true,
},
order_date: {
diff --git a/public/javascripts/admin_orders.js b/public/javascripts/admin_orders.js
new file mode 100644
index 0000000..8f52201
--- /dev/null
+++ b/public/javascripts/admin_orders.js
@@ -0,0 +1,13 @@
+const myModal = new bootstrap.Modal("#confirm-modal");
+
+// Display confirmation dialog
+async function showConfirm(id) {
+ const submit = document.getElementById("modal_confirm");
+ submit.dataset.submit_id = "storno_" + id;
+ myModal.show();
+}
+
+// Submit form from modal to confirm order storno
+function submitFromModal(ctx) {
+ document.getElementById(ctx.dataset.submit_id).submit();
+}
diff --git a/routes/orders.js b/routes/orders.js
index 5f62ac1..d7a108a 100644
--- a/routes/orders.js
+++ b/routes/orders.js
@@ -1,10 +1,17 @@
import { Router } from "express";
import moment from "moment";
+import User from "../models/user.js";
+import Product from "../models/product.js";
import Order from "../models/order.js";
+import Delivery from "../models/delivery.js";
+import { sendMail } from "../functions/sendMail.js";
import { ensureAuthenticated } from "../functions/ensureAuthenticated.js";
import { checkKiosk } from "../functions/checkKiosk.js";
+import csrf from "csurf";
import logger from "../functions/logger.js";
var router = Router();
+const csrfProtection = csrf();
+router.use(csrfProtection);
moment.locale("cs");
/* GET orders page. */
@@ -30,9 +37,6 @@ router.get("/", ensureAuthenticated, checkKiosk, function (req, res) {
};
}
- // Order.listIndexes().then((indexes) => {
- // console.log(indexes);
- // });
if (req.session.alert) {
var alert = req.session.alert;
delete req.session.alert;
@@ -152,6 +156,7 @@ router.get("/", ensureAuthenticated, checkKiosk, function (req, res) {
admin: filter,
user: req.user,
alert: alert,
+ csrfToken: req.csrfToken(),
});
})
.catch((err) => {
@@ -163,4 +168,189 @@ router.get("/", ensureAuthenticated, checkKiosk, function (req, res) {
});
});
+/* POST admin_orders page - orders page has no POST method. */
+router.post("/", ensureAuthenticated, function (req, res, _next) {
+ if (!req.user.admin) {
+ logger.warn(
+ `server.routes.adminorders.post__User tried to cancel order on admin page without permission.`,
+ {
+ metadata: {
+ result: req.user,
+ },
+ }
+ );
+ res.redirect("/");
+ return;
+ }
+ if (req.body.name === "storno") {
+ // TODO: Handle already invoiced orders and orders older than 15 minutes - Possibly replace with FindOneAndDelete
+ Order.findByIdAndDelete({
+ _id: req.body.order,
+ })
+ .populate("buyerId", "email")
+ .populate({
+ path: "deliveryId",
+ populate: {
+ path: "productId",
+ select: ["displayName", "imagePath"],
+ },
+ select: ["productId", "price"],
+ })
+ .then((order) => {
+ logger.debug(
+ `server.routes.adminorders.post__Order [${order._id}] with product [${order.deliveryId.productId.displayName}] purchased by user [${order.buyerId.email}] has been deleted.`,
+ {
+ metadata: {
+ object: order,
+ },
+ }
+ );
+ Delivery.findByIdAndUpdate(
+ {
+ _id: order.deliveryId._id,
+ },
+ {
+ $inc: {
+ amount_left: 1,
+ },
+ }
+ )
+ .then((delivery) => {
+ logger.debug(
+ `server.routes.adminorders.post__Returned product's stock amount incremented in delivery [${delivery._id}] by one.`,
+ {
+ metadata: {
+ object: delivery,
+ },
+ }
+ );
+ logger.info(
+ `server.routes.adminorders.post__User [${req.user.displayName}] succesfully returned product [${order.deliveryId.productId.displayName}] for [${order.deliveryId.price}].`,
+ {
+ metadata: {
+ order: delivery,
+ },
+ }
+ );
+
+ const alert = {
+ type: "success",
+ message: `Stornovali jste ${order.deliveryId.productId.displayName} za ${order.deliveryId.price} Kč a e-mail byl zaslán zákazníkovi na adresu ${order.buyerId.email}.`,
+ success: 1,
+ };
+ req.session.alert = alert;
+ res.redirect("/admin_orders");
+ const subject = `Stornování objednávky - ${order.deliveryId.productId.displayName}`;
+ const mailPreview = `Administrátor ${req.user.displayName} stornoval ${order.deliveryId.productId.displayName} za ${order.deliveryId.price} Kč.`;
+ sendMail(
+ order.buyerId.email,
+ "productReturned",
+ {
+ subject,
+ mailPreview,
+ orderId: order._id,
+ productId: order.deliveryId.productId._id,
+ productName: order.deliveryId.productId.displayName,
+ productPrice: order.deliveryId.price,
+ purchaseDate: moment(order.order_date).format("LLLL"),
+ },
+ order.deliveryId.productId.imagePath
+ );
+ return;
+ })
+ .catch((err) => {
+ logger.error(
+ "server.routes.adminorders.post__Failed to increment stock in the database, but order has been already deleted!",
+ {
+ metadata: {
+ error: err.message,
+ order: order,
+ },
+ }
+ );
+ const alert = {
+ type: "danger",
+ component: "db",
+ message: err.message,
+ danger: 1,
+ };
+ req.session.alert = alert;
+ res.redirect("/admin_orders");
+ const subject = "[SYSTEM ERROR] Chyba při zápisu do databáze!";
+ const message = `Potenciálně se nepodařilo zapsat navýšení stavu dodávky do databáze, ale již došlo k odstranění objednávky. Dodávka ID [${order.deliveryId._id}]. Administrátor [${req.user.displayName}] se pokusil zákazníkovi ID [${order.buyerId._id}], e-mail [${order.buyerId.email}] stornovat produkt ID [${order.deliveryId.productId._id}], zobrazované jméno [${order.deliveryId.productId.displayName}] za [${order.deliveryId.price}] Kč. Zkontrolujte konzistenci databáze.`;
+ sendMail("system@system", "systemMessage", {
+ subject,
+ message,
+ messageTime: moment().toISOString(),
+ errorMessage: err.message,
+ });
+ return;
+ });
+
+ // newOrder
+ // .save()
+ // .then((order) => {
+
+ // req.session.alert = alert;
+ // res.redirect("/shop");
+ // if (req.user.sendMailOnEshopPurchase) {
+
+ // }
+ // return;
+ // })
+ // .catch((err) => {
+ // });
+ // })
+ // .catch((err) => {
+ // logger.error(
+ // "server.routes.shop.post__Failed to decrement stock amount from the delivery.",
+ // {
+ // metadata: {
+ // error: err.message,
+ // },
+ // }
+ // );
+ // const alert = {
+ // type: "danger",
+ // component: "db",
+ // message: err.message,
+ // danger: 1,
+ // };
+ // req.session.alert = alert;
+ // res.redirect("/shop");
+ // const subject = "[SYSTEM ERROR] Chyba při zápisu do databáze!";
+ // const message = `Potenciálně se nepodařilo snížit skladovou zásobu v dodávce ID [${delivery._id}] a následně vystavit objednávku. Zákazník ID [${req.user._id}], zobrazované jméno [${req.user.displayName}] se pokusil koupit produkt ID [${delivery.productId}], zobrazované jméno [${req.body.display_name}] za [${delivery.price}] Kč. Zkontrolujte konzistenci databáze.`;
+ // sendMail("system@system", "systemMessage", {
+ // subject,
+ // message,
+ // messageTime: moment().toISOString(),
+ // errorMessage: err.message,
+ // });
+ // return;
+ // });
+ })
+ .catch((err) => {
+ logger.error(
+ "server.routes.adminorders.post__Failed delete order from database.",
+ {
+ metadata: {
+ error: err.message,
+ },
+ }
+ );
+ const alert = {
+ type: "danger",
+ component: "db",
+ message: err.message,
+ danger: 1,
+ };
+ req.session.alert = alert;
+ res.redirect("/admin_orders");
+ return;
+ });
+ } else {
+ res.status(400).send();
+ }
+});
+
export default router;
diff --git a/views/email/productReturned.handlebars b/views/email/productReturned.handlebars
new file mode 100644
index 0000000..f07db8e
--- /dev/null
+++ b/views/email/productReturned.handlebars
@@ -0,0 +1,158 @@
+{{> title title="Stornování objednávky" }}
+{{! Single column template }}
+
+
+ {{! Spacer }}
+
+
+
+
+
+
+
+
+
+ {{! 1st row - text }}
+
+ {{! Middle column }}
+
+ {{! Single column template }}
+
+
+ {{! Spacer }}
+
+
+
+
+
+
+
+
+
+
+ {{! Middle column }}
+
+
Vážený zákazníku,
+
mrzí nás, že nejste spokojený se zakoupeným produktem {{productName}}.
+
Administrátor stornoval Vaši objednávku. Prosíme Vás, pokud jste tak ještě neučinili, vraťte produkt do Lednice IT a ověřte skladovou zásobu, zda souhlasí se skutečným stavem.
+
Přejeme, ať se Váš další výběr produktu podaří a těsíme se na Vaši další návštěvu.
\ No newline at end of file
diff --git a/views/partials/confirm_storno_modal.hbs b/views/partials/confirm_storno_modal.hbs
new file mode 100644
index 0000000..758db79
--- /dev/null
+++ b/views/partials/confirm_storno_modal.hbs
@@ -0,0 +1,34 @@
+
+
+
+
+
Opravdu stornovat objednávku?
+
+
+
+
Proveďte pouze pokud produkt nebyl odebrán!
+
Záznam o nákupu bude odstraněn a dojde k navýšení skladové zásoby v
+ záznamu dodávky o jeden kus.
+
+
+
+
+
\ No newline at end of file
diff --git a/views/shop/orders.hbs b/views/shop/orders.hbs
index a19ff67..7660d2b 100644
--- a/views/shop/orders.hbs
+++ b/views/shop/orders.hbs
@@ -1,3 +1,6 @@
+{{#unless admin.buyerId}}
+ {{> confirm_storno_modal }}
+{{/unless}}
+ {{/unless}}
{{/each}}
@@ -88,4 +104,7 @@
{{> datatables }}
-
\ No newline at end of file
+
+{{#unless admin.buyerId}}
+
+{{/unless}}
\ No newline at end of file