Store cart in the DB and handle the holding of stock across sessions
parent
b76df6c03e
commit
f1b6d1dcc8
20
app.js
20
app.js
|
@ -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')))
|
||||||
|
|
|
@ -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,9 +299,36 @@ 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(
|
||||||
return res.status(400).json({message: 'There is insufficient stock of this product.'});
|
{
|
||||||
|
$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
|
// 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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue