Skip to content

Commit

Permalink
Add API to order from scanner, display scanner type, profile display …
Browse files Browse the repository at this point in the history
…card and disable ID login
  • Loading branch information
megastary committed Mar 13, 2024
1 parent 8ff6d8d commit f84500e
Show file tree
Hide file tree
Showing 12 changed files with 438 additions and 123 deletions.
2 changes: 2 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import keypadOrderRouter from "./routes/api/keypadOrder.js";
import customerNameRouter from "./routes/api/customerName.js";
import scannerAuthUser from "./routes/api/scannerAuthUser.js";
import scannerProduct from "./routes/api/scannerProduct.js";
import scannerOrder from "./routes/api/scannerOrder.js";
// API routes for clientside javascript
import promptGptRouter from "./routes/api/promptGpt.js";
// Middleware routes
Expand Down Expand Up @@ -169,6 +170,7 @@ app.use("/api/keypadOrder", keypadOrderRouter);
app.use("/api/customerName", customerNameRouter);
app.use("/api/scannerAuthUser", scannerAuthUser);
app.use("/api/scannerProduct", scannerProduct);
app.use("/api/scannerOrder", scannerOrder);
// API routes for clientside javascript
app.use("/api/promptGpt", promptGptRouter);

Expand Down
14 changes: 12 additions & 2 deletions requests.http
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,18 @@ sbf-API-secret: developmentsecret

###

GET https://localhost/api/scannerProduct?product=1234 HTTP/1.1
GET https://localhost/api/scannerProduct?product=123 HTTP/1.1
Content-Type: application/json
sbf-API-secret: developmentsecret

###
###

POST https://localhost/api/scannerOrder HTTP/1.1
Content-Type: application/json
sbf-API-secret: developmentsecret

{
"customer": "123456",
"product": "123",
"price": 5
}
9 changes: 6 additions & 3 deletions routes/api/customerName.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ router.get("/", ensureAuthenticatedAPI, function (req, res, _next) {

// Find user in database
const filter =
req.query.customer.length < 6
? { keypadId: req.query.customer, keypadDisabled: { $in: [null, false] } }
: { card: req.query.customer };
req.body.customer.toString().length < 6
? {
keypadId: Number(req.body.customer),
keypadDisabled: { $in: [null, false] },
}
: { card: req.body.customer.toString() };
User.findOne({
...filter,
})
Expand Down
7 changes: 5 additions & 2 deletions routes/api/keypadOrder.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,11 @@ router.post("/", ensureAuthenticatedAPI, function (req, res, _next) {
// Find user by keypadId
const filter =
req.body.customer.toString().length < 6
? { keypadId: req.body.customer, keypadDisabled: { $in: [null, false] } }
: { card: req.body.customer };
? {
keypadId: Number(req.body.customer),
keypadDisabled: { $in: [null, false] },
}
: { card: req.body.customer.toString() };
User.findOne({
...filter,
})
Expand Down
9 changes: 6 additions & 3 deletions routes/api/scannerAuthUser.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ router.get("/", ensureAuthenticatedAPI, function (req, res, _next) {

// Find user in database
const filter =
req.query.customer.length < 6
? { keypadId: req.query.customer, keypadDisabled: { $in: [null, false] } }
: { card: req.query.customer };
req.query.customer.toString().length < 6
? {
keypadId: Number(req.query.customer),
keypadDisabled: { $in: [null, false] },
}
: { card: req.query.customer.toString() };
User.findOne({
...filter,
})
Expand Down
251 changes: 251 additions & 0 deletions routes/api/scannerOrder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
import { Router } from "express";
import moment from "moment";
import { sendMail } from "../../functions/sendMail.js";
import User from "../../models/user.js";
import Order from "../../models/order.js";
import Product from "../../models/product.js";
import Delivery from "../../models/delivery.js";
import { ensureAuthenticatedAPI } from "../../functions/ensureAuthenticatedAPI.js";
var router = Router();
let responseJson;
moment.locale("cs");

// GET /api/scannerOrder - validate product price and purchase product if it matches
router.post("/", ensureAuthenticatedAPI, function (req, res, _next) {
if (!req.body.customer) {
// Check if request contains 'customer' parameter
res.status(400);
res.set("Content-Type", "application/problem+json");
responseJson = {
type: "https://github.com/houby-studio/small-business-fridge/wiki/API-documentation#scannerOrder",
title: "Your request does not contain parameter 'customer'.",
status: 400,
"detail:": "This function requires parameter 'customer'.",
"invalid-params": [
{
name: "customer",
reason: "must be specified",
},
],
};
res.json(responseJson);
return;
} else if (!req.body.product) {
// Check if request contains 'product' parameter
res.status(400);
res.set("Content-Type", "application/problem+json");
responseJson = {
type: "https://github.com/houby-studio/small-business-fridge/wiki/API-documentation#scannerOrder",
title: "Your request does not contain parameter 'product'.",
status: 400,
"detail:": "This function requires parameter 'product'.",
"invalid-params": [
{
name: "product",
reason: "must be specified",
},
],
};
res.json(responseJson);
return;
} else if (!req.body.price) {
// Check if request contains 'price' parameter
res.status(400);
res.set("Content-Type", "application/problem+json");
responseJson = {
type: "https://github.com/houby-studio/small-business-fridge/wiki/API-documentation#scannerOrder",
title: "Your request does not contain parameter 'price'.",
status: 400,
"detail:": "This function requires parameter 'price'.",
"invalid-params": [
{
name: "price",
reason: "must be specified",
},
],
};
res.json(responseJson);
return;
}

const newOrder = new Order();

// Find user by keypadId
const filter =
req.body.customer.toString().length < 6
? {
keypadId: Number(req.body.customer),
keypadDisabled: { $in: [null, false] },
}
: { card: req.body.customer.toString() };
User.findOne({
...filter,
})
.then((user) => {
if (!user) {
res.status(404);
res.set("Content-Type", "application/json");
res.json("USER_NOT_FOUND");
return;
}

newOrder.buyerId = user._id;
newOrder.scannerOrder = true;

// Get product
Product.aggregate([
{
$match: {
code: req.body.product.toString(),
},
},
{
$lookup: {
from: "deliveries",
localField: "_id",
foreignField: "productId",
as: "stock",
},
},
{
$project: {
keypadId: "$keypadId",
displayName: "$displayName",
description: "$description",
imagePath: "$imagePath",
stock: {
$filter: {
// We filter only the stock object from array where ammount left is greater than 0
input: "$stock",
as: "stock",
cond: {
$gt: ["$$stock.amount_left", 0],
},
},
},
},
},
])
.then((product) => {
if (typeof product[0] === "undefined") {
res.status(404);
res.set("Content-Type", "application/json");
res.json("PRODUCT_NOT_FOUND");
return;
} else if (typeof product[0].stock[0] === "undefined") {
res.status(404);
res.set("Content-Type", "application/json");
res.json("STOCK_NOT_FOUND");
return;
} else if (product[0].stock[0].price !== req.body.price) {
res.status(400);
res.set("Content-Type", "application/json");
res.json("PRICE_MISMATCH");
return;
}

newOrder.deliveryId = product[0].stock[0]._id;
const newAmount = product[0].stock[0].amount_left - 1;

Delivery.findByIdAndUpdate(product[0].stock[0]._id, {
amount_left: newAmount,
})
.then((delivery) => {
newOrder
.save()
.then((order) => {
const subject = `Potvrzení objednávky - ${product[0].displayName}`;
const mailPreview = `Zakoupili jste ${product[0].displayName} za ${delivery.price} Kč přes API.`;
sendMail(
user.email,
"productPurchased",
{
subject,
mailPreview,
orderId: order._id,
productId: delivery.productId,
productName: product[0].displayName,
productPrice: delivery.price,
purchaseDate: moment(order.order_date).format("LLLL"),
},
product[0].imagePath
);

res.status(200);
res.set("Content-Type", "application/json");
responseJson = {
user: {
displayName: user.displayName,
email: user.email,
},
product: {
name: product[0].displayName,
price: product[0].stock[0].price,
},
};
res.json(responseJson);
})
.catch((err) => {
res.status(err.status || 500);
res.set("Content-Type", "application/json");
res.json("SYSTEM_ERROR");

const subject =
"[SYSTEM ERROR] Chyba při zápisu do databáze!";
const message = `Potenciálně se nepodařilo zapsat novou objednávku do databáze, ale již došlo k ponížení skladové zásoby v dodávce ID [${delivery._id}]. Zákazník ID [${user._id}], zobrazované jméno [${user.displayName}] se pokusil koupit produkt ID [${product[0]._id}], zobrazované jméno [${product[0].displayName}] za [${delivery.price}] Kč. Zkontrolujte konzistenci databáze.`;
sendMail("system@system", "systemMessage", {
subject,
message,
messageTime: moment().toISOString(),
errorMessage: err.message,
});

return;
});
})
.catch((err) => {
res.status(err.status || 500);
res.set("Content-Type", "application/json");
res.json("SYSTEM_ERROR");

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 [${newOrder.deliveryId}] a následně vystavit objednávku. Zákazník ID [${user._id}], zobrazované jméno [${user.displayName}] se pokusil koupit produkt ID [${product[0]._id}], zobrazované jméno [${product[0].displayName}]. Zkontrolujte konzistenci databáze.`;
sendMail("system@system", "systemMessage", {
subject,
message,
messageTime: moment().toISOString(),
errorMessage: err.message,
});

return;
});
})
.catch((err) => {
res.status(err.status || 500);
res.set("Content-Type", "application/json");
res.json("SYSTEM_ERROR");
return;
});
})
.catch((_err) => {
res.status(400);
res.set("Content-Type", "application/problem+json");
const responseJson = {
type: "https://github.com/houby-studio/small-business-fridge/wiki/API-documentation#scannerOrder",
title: "Your parameter 'customer' is wrong type.",
status: 400,
"detail:":
"Parameter 'customer' must be a 'Number'. More details can be found in documentation https://git.io/Jey70",
"invalid-params": [
{
name: "customer",
reason: "must be natural number",
},
],
};
res.json(responseJson);
return;
});
});

export default router;
2 changes: 1 addition & 1 deletion routes/api/scannerProduct.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ router.get("/", ensureAuthenticatedAPI, function (req, res, _next) {
[
{
$match: {
code: req.query.product,
code: req.query.product.toString(),
},
},
{
Expand Down
13 changes: 6 additions & 7 deletions routes/kiosk_shop.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,12 @@ router.get("/", ensureAuthenticated, function (req, res) {
}
// Find user by keypadId or card number depending on char length
const filter =
req.query.customer_id.length < 6
req.query.customer_id.toString().length < 6
? {
keypadId: req.query.customer_id,
keypadId: Number(req.query.customer_id),
keypadDisabled: { $in: [null, false] },
}
: { card: req.query.customer_id };
: { card: req.query.customer_id.toString() };
User.findOne({
...filter,
})
Expand Down Expand Up @@ -261,16 +261,15 @@ router.post("/", ensureAuthenticated, function (req, res) {

// Find user by keypadId or card number depending on char length
const filter =
req.body.customer_id.length < 6
req.body.customer_id.toString().length < 6
? {
keypadId: req.body.customer_id,
keypadId: Number(req.body.customer_id),
keypadDisabled: { $in: [null, false] },
}
: { card: req.body.customer_id };
: { card: req.body.customer_id.toString() };
User.findOne({
...filter,
})
// .explain()
.then((user) => {
if (!user) {
logger.error(
Expand Down
6 changes: 3 additions & 3 deletions routes/orders.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ router.get("/", ensureAuthenticated, checkKiosk, function (req, res) {
};
}

Order.listIndexes().then((indexes) => {
console.log(indexes);
});
// Order.listIndexes().then((indexes) => {
// console.log(indexes);
// });

Order.aggregate([
{
Expand Down
Loading

0 comments on commit f84500e

Please sign in to comment.