Fixing cart storage and management
parent
ba33ad2f96
commit
03573129b3
|
@ -111,15 +111,24 @@ const clearSessionValue = (session, sessionVar) => {
|
||||||
return temp;
|
return temp;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateTotalCartAmount = (req, res) => {
|
const updateTotalCart = (req, res) => {
|
||||||
const config = getConfig();
|
const config = getConfig();
|
||||||
|
|
||||||
req.session.totalCartAmount = 0;
|
req.session.totalCartAmount = 0;
|
||||||
|
req.session.cartTotalItems = 0;
|
||||||
|
|
||||||
_(req.session.cart).forEach((item) => {
|
// If cart is empty return zero values
|
||||||
req.session.totalCartAmount = req.session.totalCartAmount + item.totalItemPrice;
|
if(!req.session.cart){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(req.session.cart).forEach((item) => {
|
||||||
|
req.session.totalCartAmount = req.session.totalCartAmount + req.session.cart[item].totalItemPrice;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update the total items in cart for the badge
|
||||||
|
req.session.cartTotalItems = Object.keys(req.session.cart).length;
|
||||||
|
|
||||||
// under the free shipping threshold
|
// under the free shipping threshold
|
||||||
if(req.session.totalCartAmount < config.freeShippingAmount){
|
if(req.session.totalCartAmount < config.freeShippingAmount){
|
||||||
req.session.totalCartAmount = req.session.totalCartAmount + parseInt(config.flatShipping);
|
req.session.totalCartAmount = req.session.totalCartAmount + parseInt(config.flatShipping);
|
||||||
|
@ -136,7 +145,7 @@ const updateSubscriptionCheck = (req, res) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
req.session.cart.forEach((item) => {
|
Object.keys(req.session.cart).forEach((item) => {
|
||||||
if(item.productSubscription){
|
if(item.productSubscription){
|
||||||
req.session.cartSubscription = item.productSubscription;
|
req.session.cartSubscription = item.productSubscription;
|
||||||
}else{
|
}else{
|
||||||
|
@ -279,7 +288,7 @@ const updateConfig = (fields) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// delete settings
|
// delete any settings
|
||||||
delete settingsFile.customCss_input;
|
delete settingsFile.customCss_input;
|
||||||
delete settingsFile.footerHtml_input;
|
delete settingsFile.footerHtml_input;
|
||||||
delete settingsFile.googleAnalytics_input;
|
delete settingsFile.googleAnalytics_input;
|
||||||
|
@ -583,7 +592,7 @@ module.exports = {
|
||||||
convertBool,
|
convertBool,
|
||||||
addSitemapProducts,
|
addSitemapProducts,
|
||||||
clearSessionValue,
|
clearSessionValue,
|
||||||
updateTotalCartAmount,
|
updateTotalCart,
|
||||||
updateSubscriptionCheck,
|
updateSubscriptionCheck,
|
||||||
checkDirectorySync,
|
checkDirectorySync,
|
||||||
getThemes,
|
getThemes,
|
||||||
|
|
|
@ -19,6 +19,13 @@ $(document).ready(function (){
|
||||||
$('#offcanvasClose').hide();
|
$('#offcanvasClose').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If cart was open before reload, open it again
|
||||||
|
var isCartOpen = (localStorage.getItem('cartOpen') === 'true');
|
||||||
|
if(isCartOpen === true){
|
||||||
|
localStorage.setItem('cartOpen', false);
|
||||||
|
$('body').addClass('pushy-open-right');
|
||||||
|
}
|
||||||
|
|
||||||
$('#userSetupForm').validator().on('submit', function(e){
|
$('#userSetupForm').validator().on('submit', function(e){
|
||||||
if(!e.isDefaultPrevented()){
|
if(!e.isDefaultPrevented()){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -40,24 +47,6 @@ $(document).ready(function (){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// $('.shipping-form input').each(function(e){
|
|
||||||
// $(this).wrap('<fieldset></fieldset>');
|
|
||||||
// var tag = $(this).attr('placeholder');
|
|
||||||
// $(this).after('<label for="name" class="hidden">' + tag + '</label>');
|
|
||||||
// });
|
|
||||||
|
|
||||||
// $('.shipping-form input').on('focus', function(){
|
|
||||||
// $(this).next().addClass('floatLabel');
|
|
||||||
// $(this).next().removeClass('hidden');
|
|
||||||
// });
|
|
||||||
|
|
||||||
// $('.shipping-form input').on('blur', function(){
|
|
||||||
// if($(this).val() === ''){
|
|
||||||
// $(this).next().addClass('hidden');
|
|
||||||
// $(this).next().removeClass('floatLabel');
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
$(document).on('click', '.menu-btn', function(e){
|
$(document).on('click', '.menu-btn', function(e){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$('body').addClass('pushy-open-right');
|
$('body').addClass('pushy-open-right');
|
||||||
|
@ -68,7 +57,9 @@ $(document).ready(function (){
|
||||||
$(this).addClass('table table-hover');
|
$(this).addClass('table table-hover');
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#productTags').tokenfield();
|
if($('#productTags').length){
|
||||||
|
$('#productTags').tokenfield();
|
||||||
|
}
|
||||||
|
|
||||||
$(document).on('click', '.dashboard_list', function(e){
|
$(document).on('click', '.dashboard_list', function(e){
|
||||||
window.document.location = $(this).attr('href');
|
window.document.location = $(this).attr('href');
|
||||||
|
@ -79,7 +70,6 @@ $(document).ready(function (){
|
||||||
$(document).on('click', '.btn-qty-minus', function(e){
|
$(document).on('click', '.btn-qty-minus', function(e){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var qtyElement = $(e.target).parent().parent().find('.cart-product-quantity');
|
var qtyElement = $(e.target).parent().parent().find('.cart-product-quantity');
|
||||||
// console.log('qtyElement', qtyElement);
|
|
||||||
$(qtyElement).val(parseInt(qtyElement.val()) - 1);
|
$(qtyElement).val(parseInt(qtyElement.val()) - 1);
|
||||||
cartUpdate(qtyElement);
|
cartUpdate(qtyElement);
|
||||||
});
|
});
|
||||||
|
@ -91,11 +81,6 @@ $(document).ready(function (){
|
||||||
cartUpdate(qtyElement);
|
cartUpdate(qtyElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
// $(document).on('change', '.cart-product-quantity', function (e){
|
|
||||||
// console.log('test');
|
|
||||||
// cartUpdate(e.target);
|
|
||||||
// });
|
|
||||||
|
|
||||||
$(document).on('click', '.btn-delete-from-cart', function(e){
|
$(document).on('click', '.btn-delete-from-cart', function(e){
|
||||||
deleteFromCart($(e.target));
|
deleteFromCart($(e.target));
|
||||||
});
|
});
|
||||||
|
@ -172,6 +157,7 @@ $(document).ready(function (){
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#checkoutInformation').validator().on('click', function(e){
|
$('#checkoutInformation').validator().on('click', function(e){
|
||||||
|
console.log('here?');
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if($('#shipping-form').validator('validate').has('.has-error').length === 0){
|
if($('#shipping-form').validator('validate').has('.has-error').length === 0){
|
||||||
// Change route if customer to be saved for later
|
// Change route if customer to be saved for later
|
||||||
|
@ -320,16 +306,15 @@ $(document).ready(function (){
|
||||||
})
|
})
|
||||||
.done(function(msg){
|
.done(function(msg){
|
||||||
$('#cart-count').text(msg.totalCartItems);
|
$('#cart-count').text(msg.totalCartItems);
|
||||||
updateCartDiv();
|
showNotification(msg.message, 'success', true);
|
||||||
showNotification(msg.message, 'success');
|
|
||||||
})
|
})
|
||||||
.fail(function(msg){
|
.fail(function(msg){
|
||||||
showNotification(msg.responseJSON.message, 'danger');
|
showNotification(msg.responseJSON.message, 'danger');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.cart-product-quantity').on('input', function(){
|
$('.cart-product-quantity').on('focusout', function(e){
|
||||||
cartUpdate();
|
cartUpdate($(e.target));
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', '.pushy-link', function(e){
|
$(document).on('click', '.pushy-link', function(e){
|
||||||
|
@ -352,8 +337,7 @@ $(document).ready(function (){
|
||||||
})
|
})
|
||||||
.done(function(msg){
|
.done(function(msg){
|
||||||
$('#cart-count').text(msg.totalCartItems);
|
$('#cart-count').text(msg.totalCartItems);
|
||||||
updateCartDiv();
|
showNotification(msg.message, 'success', true);
|
||||||
showNotification(msg.message, 'success');
|
|
||||||
})
|
})
|
||||||
.fail(function(msg){
|
.fail(function(msg){
|
||||||
showNotification(msg.responseJSON.message, 'danger');
|
showNotification(msg.responseJSON.message, 'danger');
|
||||||
|
@ -368,7 +352,6 @@ $(document).ready(function (){
|
||||||
})
|
})
|
||||||
.done(function(msg){
|
.done(function(msg){
|
||||||
$('#cart-count').text(msg.totalCartItems);
|
$('#cart-count').text(msg.totalCartItems);
|
||||||
updateCartDiv();
|
|
||||||
showNotification(msg.message, 'success', true);
|
showNotification(msg.message, 'success', true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -420,25 +403,11 @@ function deleteFromCart(element){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/product/removefromcart',
|
url: '/product/removefromcart',
|
||||||
data: { cartId: element.attr('data-id') }
|
data: { productId: element.attr('data-id') }
|
||||||
})
|
})
|
||||||
.done(function(msg){
|
.done(function(msg){
|
||||||
$('#cart-count').text(msg.totalCartItems);
|
setCartOpen();
|
||||||
if(msg.totalCartItems === 0){
|
showNotification(msg.message, 'success', true);
|
||||||
$(element).closest('.cart-row').hide('slow', function(){
|
|
||||||
$(element).closest('.cart-row').remove();
|
|
||||||
});
|
|
||||||
$('.cart-contents-shipping').hide('slow', function(){
|
|
||||||
$('.cart-contents-shipping').remove();
|
|
||||||
});
|
|
||||||
showNotification(msg.message, 'success');
|
|
||||||
setTimeout(function(){
|
|
||||||
window.location = '/';
|
|
||||||
}, 3700);
|
|
||||||
}else{
|
|
||||||
$(element).closest('.cart-row').hide('slow', function(){ $(element).closest('.cart-row').remove(); });
|
|
||||||
showNotification(msg.message, 'success');
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.fail(function(msg){
|
.fail(function(msg){
|
||||||
showNotification(msg.responseJSON.message, 'danger');
|
showNotification(msg.responseJSON.message, 'danger');
|
||||||
|
@ -446,61 +415,42 @@ function deleteFromCart(element){
|
||||||
}
|
}
|
||||||
|
|
||||||
function cartUpdate(element){
|
function cartUpdate(element){
|
||||||
console.log('element', element.val());
|
|
||||||
if($(element).val() > 0){
|
if($(element).val() > 0){
|
||||||
if($(element).val() !== ''){
|
if($(element).val() !== ''){
|
||||||
updateCart();
|
updateCart(element);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
$(element).val(1);
|
$(element).val(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCart(){
|
function setCartOpen(){
|
||||||
// gather items of cart
|
if($('body').hasClass('pushy-open-right') === true){
|
||||||
var cartItems = [];
|
localStorage.setItem('cartOpen', true);
|
||||||
$('.cart-product-quantity').each(function(){
|
}else{
|
||||||
cartItems.push({
|
localStorage.setItem('cartOpen', false);
|
||||||
productId: $(this).attr('data-id'),
|
}
|
||||||
quantity: $(this).val()
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('cartItems', cartItems)
|
|
||||||
|
|
||||||
|
function updateCart(element){
|
||||||
// update cart on server
|
// update cart on server
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/product/updatecart',
|
url: '/product/updatecart',
|
||||||
data: { items: JSON.stringify(cartItems) }
|
data: {
|
||||||
|
productId: element.attr('data-id'),
|
||||||
|
quantity: element.val()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.done(function(msg){
|
.done(function(msg){
|
||||||
// update cart items
|
setCartOpen();
|
||||||
updateCartDiv();
|
showNotification(msg.message, 'success', true);
|
||||||
$('#cart-count').text(msg.totalCartItems);
|
|
||||||
})
|
})
|
||||||
.fail(function(msg){
|
.fail(function(msg){
|
||||||
showNotification(msg.responseJSON.message, 'danger', true);
|
showNotification(msg.responseJSON.message, 'danger', true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCartDiv(){
|
|
||||||
// get new cart render
|
|
||||||
var path = window.location.pathname.split('/').length > 0 ? window.location.pathname.split('/')[1] : '';
|
|
||||||
$.ajax({
|
|
||||||
method: 'GET',
|
|
||||||
url: '/cartPartial',
|
|
||||||
data: { path: path }
|
|
||||||
})
|
|
||||||
.done(function(msg){
|
|
||||||
// update cart div
|
|
||||||
$('#cart').html(msg);
|
|
||||||
})
|
|
||||||
.fail(function(msg){
|
|
||||||
showNotification(msg.responseJSON.message, 'danger');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSelectedOptions(){
|
function getSelectedOptions(){
|
||||||
var options = {};
|
var options = {};
|
||||||
$('.product-opt').each(function(){
|
$('.product-opt').each(function(){
|
||||||
|
|
File diff suppressed because one or more lines are too long
172
routes/index.js
172
routes/index.js
|
@ -1,7 +1,6 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const colors = require('colors');
|
const colors = require('colors');
|
||||||
const async = require('async');
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const {
|
const {
|
||||||
getId,
|
getId,
|
||||||
|
@ -11,7 +10,7 @@ const {
|
||||||
getMenu,
|
getMenu,
|
||||||
getPaymentConfig,
|
getPaymentConfig,
|
||||||
getImages,
|
getImages,
|
||||||
updateTotalCartAmount,
|
updateTotalCart,
|
||||||
updateSubscriptionCheck,
|
updateSubscriptionCheck,
|
||||||
getData,
|
getData,
|
||||||
addSitemapProducts,
|
addSitemapProducts,
|
||||||
|
@ -155,6 +154,10 @@ router.get('/checkout/cart', (req, res) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/checkout/cartdata', (req, res) => {
|
||||||
|
res.status(200).json(req.session.cart);
|
||||||
|
});
|
||||||
|
|
||||||
router.get('/checkout/payment', (req, res) => {
|
router.get('/checkout/payment', (req, res) => {
|
||||||
const config = req.app.config;
|
const config = req.app.config;
|
||||||
|
|
||||||
|
@ -244,14 +247,10 @@ router.get('/cart/retrieve', async (req, res, next) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Updates a single product quantity
|
// Updates a single product quantity
|
||||||
router.post('/product/updatecart', (req, res, next) => {
|
router.post('/product/updatecart', async (req, res, next) => {
|
||||||
const db = req.app.db;
|
const db = req.app.db;
|
||||||
const config = req.app.config;
|
const config = req.app.config;
|
||||||
const cartItems = JSON.parse(req.body.items);
|
const cartItem = req.body;
|
||||||
let hasError = false;
|
|
||||||
let stockError = false;
|
|
||||||
|
|
||||||
console.log('cartItems', cartItems);
|
|
||||||
|
|
||||||
// Check cart exists
|
// Check cart exists
|
||||||
if(!req.session.cart){
|
if(!req.session.cart){
|
||||||
|
@ -259,100 +258,84 @@ router.post('/product/updatecart', (req, res, next) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('req.session.cart', req.session.cart);
|
// Calculate the quantity to update
|
||||||
|
let productQuantity = cartItem.quantity ? cartItem.quantity : 1;
|
||||||
|
if(typeof productQuantity === 'string'){
|
||||||
|
productQuantity = parseInt(productQuantity);
|
||||||
|
}
|
||||||
|
|
||||||
async.eachSeries(cartItems, async (cartItem, callback) => {
|
if(productQuantity === 0){
|
||||||
// Find index in cart
|
// quantity equals zero so we remove the item
|
||||||
const cartIndex = _.findIndex(req.session.cart, { productId: cartItem.productId });
|
delete req.session.cart[cartItem.productId];
|
||||||
|
res.status(400).json({ message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate the quantity to update
|
const product = await db.products.findOne({ _id: getId(cartItem.productId) });
|
||||||
let productQuantity = cartItem.quantity ? cartItem.quantity : 1;
|
if(!product){
|
||||||
if(typeof productQuantity === 'string'){
|
res.status(400).json({ message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length });
|
||||||
productQuantity = parseInt(productQuantity);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('productQuantity', productQuantity);
|
// If stock management on check there is sufficient stock for this product
|
||||||
if(productQuantity === 0){
|
if(config.trackStock){
|
||||||
// quantity equals zero so we remove the item
|
if(productQuantity > product.productStock){
|
||||||
req.session.cart.splice(cartIndex, 1);
|
res.status(400).json({ message: 'There is insufficient stock of this product.', totalCartItems: Object.keys(req.session.cart).length });
|
||||||
callback(null);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const product = await db.products.findOne({ _id: getId(cartItem.productId) });
|
}
|
||||||
if(product){
|
|
||||||
// If stock management on check there is sufficient stock for this product
|
|
||||||
if(config.trackStock){
|
|
||||||
if(productQuantity > product.productStock){
|
|
||||||
hasError = true;
|
|
||||||
stockError = true;
|
|
||||||
callback(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const productPrice = parseFloat(product.productPrice).toFixed(2);
|
const productPrice = parseFloat(product.productPrice).toFixed(2);
|
||||||
if(req.session.cart[cartIndex]){
|
if(!req.session.cart[cartItem.productId]){
|
||||||
req.session.cart[cartIndex].quantity = productQuantity;
|
res.status(400).json({ message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length });
|
||||||
req.session.cart[cartIndex].totalItemPrice = productPrice * productQuantity;
|
return;
|
||||||
callback(null);
|
}
|
||||||
}
|
|
||||||
}else{
|
|
||||||
hasError = true;
|
|
||||||
callback(null);
|
|
||||||
}
|
|
||||||
}, async () => {
|
|
||||||
// update total cart amount
|
|
||||||
updateTotalCartAmount(req, res);
|
|
||||||
|
|
||||||
// Update checking cart for subscription
|
// Update the cart
|
||||||
updateSubscriptionCheck(req, res);
|
req.session.cart[cartItem.productId].quantity = productQuantity;
|
||||||
|
req.session.cart[cartItem.productId].totalItemPrice = productPrice * productQuantity;
|
||||||
|
|
||||||
// Update cart to the DB
|
// update total cart amount
|
||||||
await db.cart.updateOne({ sessionId: req.session.id }, {
|
updateTotalCart(req, res);
|
||||||
$set: { cart: req.session.cart }
|
|
||||||
});
|
|
||||||
|
|
||||||
// show response
|
// Update checking cart for subscription
|
||||||
if(hasError === false){
|
updateSubscriptionCheck(req, res);
|
||||||
res.status(200).json({ message: 'Cart successfully updated', totalCartItems: Object.keys(req.session.cart).length });
|
|
||||||
}else{
|
// Update cart to the DB
|
||||||
if(stockError){
|
await db.cart.updateOne({ sessionId: req.session.id }, {
|
||||||
res.status(400).json({ message: 'There is insufficient stock of this product.', totalCartItems: Object.keys(req.session.cart).length });
|
$set: { cart: req.session.cart }
|
||||||
}else{
|
|
||||||
res.status(400).json({ message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
res.status(200).json({ message: 'Cart successfully updated', totalCartItems: Object.keys(req.session.cart).length });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove single product from cart
|
// Remove single product from cart
|
||||||
router.post('/product/removefromcart', async (req, res, next) => {
|
router.post('/product/removefromcart', async (req, res, next) => {
|
||||||
const db = req.app.db;
|
const db = req.app.db;
|
||||||
let itemRemoved = false;
|
|
||||||
|
// Check for item in cart
|
||||||
|
if(!req.session.cart[req.body.productId]){
|
||||||
|
return res.status(400).json({ message: 'Product not found in cart' });
|
||||||
|
}
|
||||||
|
|
||||||
// remove item from cart
|
// remove item from cart
|
||||||
req.session.cart.forEach((item) => {
|
delete req.session.cart[req.body.productId];
|
||||||
if(item){
|
|
||||||
if(item.productId === req.body.cartId){
|
// If not items in cart, empty it
|
||||||
itemRemoved = true;
|
if(Object.keys(req.session.cart).length === 0){
|
||||||
req.session.cart = _.pull(req.session.cart, item);
|
return emptyCart(req, res, 'json');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update cart in DB
|
// Update cart in DB
|
||||||
await db.cart.updateOne({ sessionId: req.session.id }, {
|
await db.cart.updateOne({ sessionId: req.session.id }, {
|
||||||
$set: { cart: req.session.cart }
|
$set: { cart: req.session.cart }
|
||||||
});
|
});
|
||||||
// update total cart amount
|
// update total cart
|
||||||
updateTotalCartAmount(req, res);
|
updateTotalCart(req, res);
|
||||||
|
|
||||||
// Update checking cart for subscription
|
// Update checking cart for subscription
|
||||||
updateSubscriptionCheck(req, res);
|
updateSubscriptionCheck(req, res);
|
||||||
|
|
||||||
if(itemRemoved === false){
|
|
||||||
return res.status(400).json({ message: 'Product not found in cart' });
|
|
||||||
}
|
|
||||||
return res.status(200).json({ message: 'Product successfully removed', totalCartItems: Object.keys(req.session.cart).length });
|
return res.status(200).json({ message: 'Product successfully removed', totalCartItems: Object.keys(req.session.cart).length });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -372,8 +355,8 @@ const emptyCart = async (req, res, type, customMessage) => {
|
||||||
// Remove cart from DB
|
// Remove cart from DB
|
||||||
await db.cart.deleteOne({ sessionId: req.session.id });
|
await db.cart.deleteOne({ sessionId: req.session.id });
|
||||||
|
|
||||||
// update total cart amount
|
// update total cart
|
||||||
updateTotalCartAmount(req, res);
|
updateTotalCart(req, res);
|
||||||
|
|
||||||
// Update checking cart for subscription
|
// Update checking cart for subscription
|
||||||
updateSubscriptionCheck(req, res);
|
updateSubscriptionCheck(req, res);
|
||||||
|
@ -409,7 +392,7 @@ router.post('/product/addtocart', async (req, res, next) => {
|
||||||
|
|
||||||
// setup cart object if it doesn't exist
|
// setup cart object if it doesn't exist
|
||||||
if(!req.session.cart){
|
if(!req.session.cart){
|
||||||
req.session.cart = [];
|
req.session.cart = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the product from the DB
|
// Get the product from the DB
|
||||||
|
@ -425,14 +408,19 @@ router.post('/product/addtocart', async (req, res, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If existing cart isn't empty check if product is a subscription
|
// If existing cart isn't empty check if product is a subscription
|
||||||
if(req.session.cart.length !== 0){
|
if(Object.keys(req.session.cart).length !== 0){
|
||||||
if(product.productSubscription){
|
if(product.productSubscription){
|
||||||
return res.status(400).json({ message: 'You cannot combine scubscription products with existing in your cart. Empty your cart and try again.' });
|
return res.status(400).json({ message: 'You cannot combine subscription products with existing in your cart. Empty your cart and try again.' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 && product.productStock){
|
if(config.trackStock && product.productStock){
|
||||||
|
// If there is more stock than total (ignoring held)
|
||||||
|
if(productQuantity > product.productStock){
|
||||||
|
return res.status(400).json({ message: 'There is insufficient stock of this product.' });
|
||||||
|
}
|
||||||
|
|
||||||
const stockHeld = await db.cart.aggregate(
|
const stockHeld = await db.cart.aggregate(
|
||||||
{
|
{
|
||||||
$match: {
|
$match: {
|
||||||
|
@ -478,22 +466,14 @@ router.post('/product/addtocart', async (req, res, next) => {
|
||||||
}
|
}
|
||||||
}catch(ex){}
|
}catch(ex){}
|
||||||
}
|
}
|
||||||
const findDoc = {
|
|
||||||
productId: req.body.productId,
|
|
||||||
options: options
|
|
||||||
};
|
|
||||||
|
|
||||||
// if exists we add to the existing value
|
// if exists we add to the existing value
|
||||||
const cartIndex = _.findIndex(req.session.cart, findDoc);
|
|
||||||
let cartQuantity = 0;
|
let cartQuantity = 0;
|
||||||
if(cartIndex > -1){
|
if(req.session.cart[product._id]){
|
||||||
cartQuantity = parseInt(req.session.cart[cartIndex].quantity) + productQuantity;
|
cartQuantity = parseInt(req.session.cart[product._id].quantity) + productQuantity;
|
||||||
req.session.cart[cartIndex].quantity = cartQuantity;
|
req.session.cart[product._id].quantity = cartQuantity;
|
||||||
req.session.cart[cartIndex].totalItemPrice = productPrice * parseInt(req.session.cart[cartIndex].quantity);
|
req.session.cart[product._id].totalItemPrice = productPrice * parseInt(req.session.cart[product._id].quantity);
|
||||||
}else{
|
}else{
|
||||||
// Doesnt exist so we add to the cart session
|
|
||||||
req.session.cartTotalItems = req.session.cartTotalItems + productQuantity;
|
|
||||||
|
|
||||||
// Set the card quantity
|
// Set the card quantity
|
||||||
cartQuantity = productQuantity;
|
cartQuantity = productQuantity;
|
||||||
|
|
||||||
|
@ -514,7 +494,7 @@ router.post('/product/addtocart', async (req, res, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge into the current cart
|
// merge into the current cart
|
||||||
req.session.cart.push(productObj);
|
req.session.cart[product._id] = productObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update cart to the DB
|
// Update cart to the DB
|
||||||
|
@ -523,7 +503,7 @@ router.post('/product/addtocart', async (req, res, next) => {
|
||||||
}, { upsert: true });
|
}, { upsert: true });
|
||||||
|
|
||||||
// update total cart amount
|
// update total cart amount
|
||||||
updateTotalCartAmount(req, res);
|
updateTotalCart(req, res);
|
||||||
|
|
||||||
// Update checking cart for subscription
|
// Update checking cart for subscription
|
||||||
updateSubscriptionCheck(req, res);
|
updateSubscriptionCheck(req, res);
|
||||||
|
@ -532,8 +512,6 @@ router.post('/product/addtocart', async (req, res, next) => {
|
||||||
req.session.cartSubscription = product.productSubscription;
|
req.session.cartSubscription = product.productSubscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update how many products in the shopping cart
|
|
||||||
req.session.cartTotalItems = req.session.cart.reduce((a, b) => +a + +b.quantity, 0);
|
|
||||||
return res.status(200).json({ message: 'Cart successfully updated', totalCartItems: req.session.cartTotalItems });
|
return res.status(200).json({ message: 'Cart successfully updated', totalCartItems: req.session.cartTotalItems });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -75,13 +75,13 @@ test('[Success] Update cart', async t => {
|
||||||
.get('/cart/retrieve')
|
.get('/cart/retrieve')
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
// Adjust the quantity of an item
|
const productId = g.products[0]._id;
|
||||||
cart.body.cart[0].quantity = 10;
|
|
||||||
|
|
||||||
const res = await g.request
|
const res = await g.request
|
||||||
.post('/product/updatecart')
|
.post('/product/updatecart')
|
||||||
.send({
|
.send({
|
||||||
items: JSON.stringify(cart.body.cart)
|
productId: productId,
|
||||||
|
quantity: 10
|
||||||
})
|
})
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
|
@ -92,8 +92,8 @@ test('[Success] Update cart', async t => {
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
// Check new quantity and total price has been updated
|
// Check new quantity and total price has been updated
|
||||||
t.deepEqual(checkCart.body.cart[0].quantity, 10);
|
t.deepEqual(checkCart.body.cart[productId].quantity, 10);
|
||||||
t.deepEqual(checkCart.body.cart[0].totalItemPrice, cart.body.cart[0].totalItemPrice * 10);
|
t.deepEqual(checkCart.body.cart[productId].totalItemPrice, cart.body.cart[productId].totalItemPrice * 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[Fail] Cannot add subscripton when other product in cart', async t => {
|
test('[Fail] Cannot add subscripton when other product in cart', async t => {
|
||||||
|
@ -105,7 +105,7 @@ test('[Fail] Cannot add subscripton when other product in cart', async t => {
|
||||||
productOptions: {}
|
productOptions: {}
|
||||||
})
|
})
|
||||||
.expect(400);
|
.expect(400);
|
||||||
t.deepEqual(res.body.message, 'You cannot combine scubscription products with existing in your cart. Empty your cart and try again.');
|
t.deepEqual(res.body.message, 'You cannot combine subscription products with existing in your cart. Empty your cart and try again.');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[Fail] Add product to cart with not enough stock', async t => {
|
test('[Fail] Add product to cart with not enough stock', async t => {
|
||||||
|
@ -132,10 +132,20 @@ test('[Fail] Add incorrect product to cart', async t => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[Success] Remove item previously added to cart', async t => {
|
test('[Success] Remove item previously added to cart', async t => {
|
||||||
|
// Add a second product to cart
|
||||||
|
await g.request
|
||||||
|
.post('/product/addtocart')
|
||||||
|
.send({
|
||||||
|
productId: g.products[1]._id,
|
||||||
|
productQuantity: 1,
|
||||||
|
productOptions: JSON.stringify(g.products[1].productOptions)
|
||||||
|
})
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
const res = await g.request
|
const res = await g.request
|
||||||
.post('/product/removefromcart')
|
.post('/product/removefromcart')
|
||||||
.send({
|
.send({
|
||||||
cartId: g.products[0]._id
|
productId: g.products[0]._id
|
||||||
})
|
})
|
||||||
.expect(200);
|
.expect(200);
|
||||||
t.deepEqual(res.body.message, 'Product successfully removed');
|
t.deepEqual(res.body.message, 'Product successfully removed');
|
||||||
|
|
|
@ -2,51 +2,53 @@
|
||||||
<div class="card top-marg-15 bottom-marg-15">
|
<div class="card top-marg-15 bottom-marg-15">
|
||||||
<div class="card-body cart-body">
|
<div class="card-body cart-body">
|
||||||
<h5 class="card-title">{{ @root.__ "Cart contents" }}</h5>
|
<h5 class="card-title">{{ @root.__ "Cart contents" }}</h5>
|
||||||
{{#each @root.session.cart}}
|
<div id="cartBodyWrapper">
|
||||||
<div class="d-flex flex-row bottom-pad-15">
|
{{#each @root.session.cart}}
|
||||||
<div class="col-xs-4 col-md-3">
|
<div class="d-flex flex-row bottom-pad-15">
|
||||||
{{#if productImage}}
|
<div class="col-xs-4 col-md-3">
|
||||||
<img class="img-fluid" src="{{this.productImage}}" alt="{{this.title}} product image"> {{else}}
|
{{#if productImage}}
|
||||||
<img class="img-fluid" src="/uploads/placeholder.png" alt="{{this.title}} product image"> {{/if}}
|
<img class="img-fluid" src="{{this.productImage}}" alt="{{this.title}} product image"> {{else}}
|
||||||
</div>
|
<img class="img-fluid" src="/uploads/placeholder.png" alt="{{this.title}} product image"> {{/if}}
|
||||||
<div class="col-sm-12 col-md-7">
|
</div>
|
||||||
<div class="row h-200">
|
<div class="col-sm-12 col-md-7">
|
||||||
<div class="col-sm-12 text-left no-pad-left">
|
<div class="row h-200">
|
||||||
<h6><a href="/product/{{this.link}}">{{this.title}}</a></h6>
|
<div class="col-sm-12 text-left no-pad-left">
|
||||||
</div>
|
<h6><a href="/product/{{this.link}}">{{this.title}}</a></h6>
|
||||||
<div class="col-sm-12 text-left no-pad-left">
|
</div>
|
||||||
{{#each this.options}}
|
<div class="col-sm-12 text-left no-pad-left">
|
||||||
{{#if @last}}
|
{{#each this.options}}
|
||||||
{{@key}}: {{this}}
|
{{#if @last}}
|
||||||
{{else}}
|
{{@key}}: {{this}}
|
||||||
{{@key}}: {{this}} /
|
{{else}}
|
||||||
{{/if}}
|
{{@key}}: {{this}} /
|
||||||
{{/each}}
|
{{/if}}
|
||||||
</div>
|
{{/each}}
|
||||||
{{#ifCond cartReadOnly '!=' true}}
|
</div>
|
||||||
<div class="col-md-8 no-pad-left">
|
{{#ifCond cartReadOnly '!=' true}}
|
||||||
<div class="input-group">
|
<div class="col-md-8 no-pad-left">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group">
|
||||||
<button class="btn btn-outline-primary btn-qty-minus" type="button">-</button>
|
<div class="input-group-prepend">
|
||||||
</div>
|
<button class="btn btn-outline-primary btn-qty-minus" type="button">-</button>
|
||||||
<input type="number" class="form-control cart-product-quantity text-center" data-id="{{../this.productId}}" data-index="{{@key}}"
|
</div>
|
||||||
maxlength="2" value="{{../this.quantity}}">
|
<input type="number" class="form-control cart-product-quantity text-center" data-id="{{../this.productId}}" data-index="{{@key}}"
|
||||||
<div class="input-group-append">
|
maxlength="2" value="{{../this.quantity}}">
|
||||||
<button class="btn btn-outline-primary btn-qty-add" type="button">+</button>
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-outline-primary btn-qty-add" type="button">+</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-4 text-right">
|
||||||
|
<button class="btn btn-outline-danger btn-delete-from-cart" data-id="{{../this.productId}}" type="button"><i class="fa fa-trash" data-id="{{../this.productId}}" aria-hidden="true"></i></button>
|
||||||
|
</div>
|
||||||
|
{{/ifCond}}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 text-right">
|
</div>
|
||||||
<button class="btn btn-outline-danger btn-delete-from-cart" data-id="{{../this.productId}}" type="button"><i class="fa fa-trash" data-id="{{../this.productId}}" aria-hidden="true"></i></button>
|
<div class="align-self-center col-sm-12 col-md-2 text-right no-pad-right">
|
||||||
</div>
|
<strong class="my-auto">{{currencySymbol @root.config.currencySymbol}}{{formatAmount this.totalItemPrice}}</strong>
|
||||||
{{/ifCond}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="align-self-center col-sm-12 col-md-2 text-right no-pad-right">
|
{{/each}}
|
||||||
<strong class="my-auto">{{currencySymbol @root.config.currencySymbol}}{{formatAmount this.totalItemPrice}}</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
{{#if @root.session.cart}}
|
{{#if @root.session.cart}}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
Loading…
Reference in New Issue