Store cart in the DB and handle the holding of stock across sessions

master
Mark Moffat 2019-06-01 21:52:14 +09:30
parent b76df6c03e
commit f1b6d1dcc8
2 changed files with 82 additions and 9 deletions

20
app.js
View File

@ -5,11 +5,13 @@ const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const session = require('express-session'); const session = require('express-session');
const moment = require('moment'); const moment = require('moment');
const _ = require('lodash');
const MongoStore = require('connect-mongodb-session')(session); const MongoStore = require('connect-mongodb-session')(session);
const MongoClient = require('mongodb').MongoClient; const MongoClient = require('mongodb').MongoClient;
const numeral = require('numeral'); const numeral = require('numeral');
const helmet = require('helmet'); const helmet = require('helmet');
const colors = require('colors'); const colors = require('colors');
const cron = require('node-cron');
const common = require('./lib/common'); const common = require('./lib/common');
const mongodbUri = require('mongodb-uri'); const mongodbUri = require('mongodb-uri');
let handlebars = require('express-handlebars'); let handlebars = require('express-handlebars');
@ -234,7 +236,7 @@ app.use(session({
cookie: { cookie: {
path: '/', path: '/',
httpOnly: true, httpOnly: true,
maxAge: 3600000 * 24 maxAge: 900000
}, },
store: store store: store
})); }));
@ -346,6 +348,8 @@ MongoClient.connect(config.databaseConnectionString, {}, (err, client) => {
db.pages = db.collection('pages'); db.pages = db.collection('pages');
db.menu = db.collection('menu'); db.menu = db.collection('menu');
db.customers = db.collection('customers'); db.customers = db.collection('customers');
db.cart = db.collection('cart');
db.sessions = db.collection('sessions');
// add db to app for routes // add db to app for routes
app.dbClient = client; app.dbClient = client;
@ -353,6 +357,20 @@ MongoClient.connect(config.databaseConnectionString, {}, (err, client) => {
app.config = config; app.config = config;
app.port = app.get('port'); 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 // run indexing
common.runIndexing(app) common.runIndexing(app)
.then(app.listen(app.get('port'))) .then(app.listen(app.get('port')))

View File

@ -4,6 +4,7 @@ const colors = require('colors');
const async = require('async'); const async = require('async');
const _ = require('lodash'); const _ = require('lodash');
const common = require('../lib/common'); const common = require('../lib/common');
const ObjectId = require('mongodb').ObjectID;
// These is the customer facing routes // These is the customer facing routes
router.get('/payment/:orderId', async (req, res, next) => { router.get('/payment/:orderId', async (req, res, next) => {
@ -207,10 +208,15 @@ router.post('/product/updatecart', (req, res, next) => {
} }
}); });
} }
}, () => { }, async () => {
// update total cart amount // update total cart amount
common.updateTotalCartAmount(req, res); common.updateTotalCartAmount(req, res);
// Update cart to the DB
await db.cart.update({sessionId: req.session.id}, {
$set: {cart: req.session.cart}
});
// show response // show response
if(hasError === false){ if(hasError === false){
res.status(200).json({message: 'Cart successfully updated', totalCartItems: Object.keys(req.session.cart).length}); 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 // Remove single product from cart
router.post('/product/removefromcart', (req, res, next) => { router.post('/product/removefromcart', (req, res, next) => {
const db = req.app.db;
// remove item from cart // remove item from cart
async.each(req.session.cart, (item, callback) => { async.each(req.session.cart, (item, callback) => {
if(item){ if(item){
@ -234,7 +242,11 @@ router.post('/product/removefromcart', (req, res, next) => {
} }
} }
callback(); callback();
}, () => { }, async () => {
// Update cart in DB
await db.cart.update({sessionId: req.session.id}, {
$set: {cart: req.session.cart}
});
// update total cart amount // update total cart amount
common.updateTotalCartAmount(req, res); common.updateTotalCartAmount(req, res);
res.status(200).json({message: 'Product successfully removed', totalCartItems: Object.keys(req.session.cart).length}); 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 // 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.cart;
delete req.session.orderId; delete req.session.orderId;
// Remove cart from DB
await db.cart.removeOne({sessionId: req.session.id});
// update total cart amount // update total cart amount
common.updateTotalCartAmount(req, res); common.updateTotalCartAmount(req, res);
res.status(200).json({message: 'Cart successfully emptied', totalCartItems: 0}); 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 // 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){ if(err){
console.error(colors.red('Error adding to cart', err)); console.error(colors.red('Error adding to cart', err));
return res.status(400).json({message: 'Error updating cart. Please try again.'}); return res.status(400).json({message: 'Error updating cart. Please try again.'});
@ -281,11 +299,38 @@ router.post('/product/addtocart', (req, res, next) => {
} }
// If stock management on check there is sufficient stock for this product // If stock management on check there is sufficient stock for this product
if(config.trackStock){ if(config.trackStock && product.productStock){
if(productQuantity > 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.'}); return res.status(400).json({message: 'There is insufficient stock of this product.'});
} }
} }
}
let productPrice = parseFloat(product.productPrice).toFixed(2); let productPrice = parseFloat(product.productPrice).toFixed(2);
@ -301,13 +346,18 @@ router.post('/product/addtocart', (req, res, next) => {
// if exists we add to the existing value // if exists we add to the existing value
let cartIndex = _.findIndex(req.session.cart, findDoc); let cartIndex = _.findIndex(req.session.cart, findDoc);
let cartQuantity = 0;
if(cartIndex > -1){ 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); req.session.cart[cartIndex].totalItemPrice = productPrice * parseInt(req.session.cart[cartIndex].quantity);
}else{ }else{
// Doesnt exist so we add to the cart session // Doesnt exist so we add to the cart session
req.session.cartTotalItems = req.session.cartTotalItems + productQuantity; req.session.cartTotalItems = req.session.cartTotalItems + productQuantity;
// Set the card quantity
cartQuantity = productQuantity;
// new product deets // new product deets
let productObj = {}; let productObj = {};
productObj.productId = req.body.productId; productObj.productId = req.body.productId;
@ -327,6 +377,11 @@ router.post('/product/addtocart', (req, res, next) => {
req.session.cart.push(productObj); 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 // update total cart amount
common.updateTotalCartAmount(req, res); common.updateTotalCartAmount(req, res);