From f1b6d1dcc857f4be4553e223b92691492875deb0 Mon Sep 17 00:00:00 2001 From: Mark Moffat Date: Sat, 1 Jun 2019 21:52:14 +0930 Subject: [PATCH] Store cart in the DB and handle the holding of stock across sessions --- app.js | 20 +++++++++++++- routes/index.js | 71 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/app.js b/app.js index 41d2976..6ec3909 100644 --- a/app.js +++ b/app.js @@ -5,11 +5,13 @@ const cookieParser = require('cookie-parser'); const bodyParser = require('body-parser'); const session = require('express-session'); const moment = require('moment'); +const _ = require('lodash'); const MongoStore = require('connect-mongodb-session')(session); const MongoClient = require('mongodb').MongoClient; const numeral = require('numeral'); const helmet = require('helmet'); const colors = require('colors'); +const cron = require('node-cron'); const common = require('./lib/common'); const mongodbUri = require('mongodb-uri'); let handlebars = require('express-handlebars'); @@ -234,7 +236,7 @@ app.use(session({ cookie: { path: '/', httpOnly: true, - maxAge: 3600000 * 24 + maxAge: 900000 }, store: store })); @@ -346,6 +348,8 @@ MongoClient.connect(config.databaseConnectionString, {}, (err, client) => { db.pages = db.collection('pages'); db.menu = db.collection('menu'); db.customers = db.collection('customers'); + db.cart = db.collection('cart'); + db.sessions = db.collection('sessions'); // add db to app for routes app.dbClient = client; @@ -353,6 +357,20 @@ MongoClient.connect(config.databaseConnectionString, {}, (err, client) => { app.config = config; app.port = app.get('port'); + // Fire up the cron job to clear temp held stock + cron.schedule('*/1 * * * *', async () => { + const validSessions = await db.sessions.find({}).toArray(); + const validSessionIds = []; + _.forEach(validSessions, (value) => { + validSessionIds.push(value._id); + }); + + // Remove any invalid cart holds + await db.cart.remove({ + sessionId: {$nin: validSessionIds} + }); + }); + // run indexing common.runIndexing(app) .then(app.listen(app.get('port'))) diff --git a/routes/index.js b/routes/index.js index 900b625..7ab9c71 100644 --- a/routes/index.js +++ b/routes/index.js @@ -4,6 +4,7 @@ const colors = require('colors'); const async = require('async'); const _ = require('lodash'); const common = require('../lib/common'); +const ObjectId = require('mongodb').ObjectID; // These is the customer facing routes router.get('/payment/:orderId', async (req, res, next) => { @@ -207,10 +208,15 @@ router.post('/product/updatecart', (req, res, next) => { } }); } - }, () => { + }, async () => { // update total cart amount common.updateTotalCartAmount(req, res); + // Update cart to the DB + await db.cart.update({sessionId: req.session.id}, { + $set: {cart: req.session.cart} + }); + // show response if(hasError === false){ res.status(200).json({message: 'Cart successfully updated', totalCartItems: Object.keys(req.session.cart).length}); @@ -226,6 +232,8 @@ router.post('/product/updatecart', (req, res, next) => { // Remove single product from cart router.post('/product/removefromcart', (req, res, next) => { + const db = req.app.db; + // remove item from cart async.each(req.session.cart, (item, callback) => { if(item){ @@ -234,7 +242,11 @@ router.post('/product/removefromcart', (req, res, next) => { } } callback(); - }, () => { + }, async () => { + // Update cart in DB + await db.cart.update({sessionId: req.session.id}, { + $set: {cart: req.session.cart} + }); // update total cart amount common.updateTotalCartAmount(req, res); res.status(200).json({message: 'Product successfully removed', totalCartItems: Object.keys(req.session.cart).length}); @@ -242,10 +254,16 @@ router.post('/product/removefromcart', (req, res, next) => { }); // Totally empty the cart -router.post('/product/emptycart', (req, res, next) => { +router.post('/product/emptycart', async (req, res, next) => { + const db = req.app.db; + + // Remove from session delete req.session.cart; delete req.session.orderId; + // Remove cart from DB + await db.cart.removeOne({sessionId: req.session.id}); + // update total cart amount common.updateTotalCartAmount(req, res); res.status(200).json({message: 'Cart successfully emptied', totalCartItems: 0}); @@ -269,7 +287,7 @@ router.post('/product/addtocart', (req, res, next) => { } // Get the item from the DB - db.products.findOne({_id: common.getId(req.body.productId)}, (err, product) => { + db.products.findOne({_id: common.getId(req.body.productId)}, async (err, product) => { if(err){ console.error(colors.red('Error adding to cart', err)); return res.status(400).json({message: 'Error updating cart. Please try again.'}); @@ -281,9 +299,36 @@ router.post('/product/addtocart', (req, res, next) => { } // If stock management on check there is sufficient stock for this product - if(config.trackStock){ - if(productQuantity > product.productStock){ - return res.status(400).json({message: 'There is insufficient stock of this product.'}); + if(config.trackStock && product.productStock){ + const stockHeld = await db.cart.aggregate( + { + $match: { + cart: {$elemMatch: {productId: product._id.toString()}} + } + }, + {$unwind: '$cart'}, + { + $group: { + _id: '$cart.productId', + sumHeld: {$sum: '$cart.quantity'} + } + }, + { + $project: { + sumHeld: 1 + } + } + ).toArray(); + + // If there is stock + if(stockHeld.length > 0){ + const totalHeld = _.find(stockHeld, {_id: product._id.toString()}).sumHeld; + const netStock = product.productStock - totalHeld; + + // Check there is sufficient stock + if(productQuantity > netStock){ + return res.status(400).json({message: 'There is insufficient stock of this product.'}); + } } } @@ -301,13 +346,18 @@ router.post('/product/addtocart', (req, res, next) => { // if exists we add to the existing value let cartIndex = _.findIndex(req.session.cart, findDoc); + let cartQuantity = 0; if(cartIndex > -1){ - req.session.cart[cartIndex].quantity = parseInt(req.session.cart[cartIndex].quantity) + productQuantity; + cartQuantity = parseInt(req.session.cart[cartIndex].quantity) + productQuantity; + req.session.cart[cartIndex].quantity = cartQuantity; req.session.cart[cartIndex].totalItemPrice = productPrice * parseInt(req.session.cart[cartIndex].quantity); }else{ // Doesnt exist so we add to the cart session req.session.cartTotalItems = req.session.cartTotalItems + productQuantity; + // Set the card quantity + cartQuantity = productQuantity; + // new product deets let productObj = {}; productObj.productId = req.body.productId; @@ -327,6 +377,11 @@ router.post('/product/addtocart', (req, res, next) => { req.session.cart.push(productObj); } + // Update cart to the DB + await db.cart.update({sessionId: req.session.id}, { + $set: {cart: req.session.cart} + }, {upsert: true}); + // update total cart amount common.updateTotalCartAmount(req, res);