Created
September 14, 2015 02:55
-
-
Save evanlh/31179a49a753b0292db2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Creates a new Cart object, with an optional pricing parameter. | |
* @class | |
* @param {object} pricing | |
*/ | |
function Cart(prices){ | |
this.total = 0, | |
this.pricing = {}, | |
this.contents = {}; | |
if (prices){ | |
this.setPricing(prices); | |
} | |
}; | |
Cart.prototype = { | |
/** | |
* Assign pricing details to a Cart object. | |
* @method | |
* @param {object} pricing - A map from item_id to a map of | |
* {quantity1: price1, quantity2: price2} | |
* @returns {object} cart instance | |
*/ | |
setPricing: function (prices){ | |
var self = this; | |
for (var item in prices){ | |
if (!prices.hasOwnProperty(item)) continue; | |
// allow shortcut for qty == 1 items | |
if (typeof prices[item] == 'number'){ | |
this.pricing[item] = { 1: prices[item] }; | |
} | |
// copy qty => price pairs | |
else if (typeof prices[item] == 'object'){ | |
var qtyKeys = Object.keys(prices[item]); | |
this.pricing[item] = {}; | |
qtyKeys.forEach(function(key){ | |
self.pricing[item][key] = prices[item][key]; | |
}); | |
} | |
else { | |
throw new Error('Unsupported price format'); | |
} | |
} | |
return this; | |
}, | |
/** | |
* Scan the specified item_id quantity times | |
* @param {string} item_id - Unique id of the item to scan | |
* @param {integer} quantity - (Optional) number of items, default 1. | |
* @returns {object} cart instance | |
*/ | |
scan: function(item_id, quantity){ | |
if (!quantity) quantity = 1; | |
if (!item_id || typeof this.pricing[item_id] !== 'object') { | |
throw new Error('No pricing data available for ' + item_id + | |
'. Maybe you forgot to setPricing()?'); | |
} | |
if (this.contents[item_id]){ | |
this.contents[item_id] += quantity; | |
} | |
else { | |
this.contents[item_id] = quantity; | |
} | |
return this; | |
}, | |
/** | |
* Calculate the cart total, applying quantity discounts. | |
* Retrieve the result from the .total property. | |
* @returns {object} cart instance | |
*/ | |
calculateTotal: function(){ | |
var cumulative = 0; | |
for (var item in this.contents){ | |
cumulative += this.calculateItemTotal(item, this.contents[item]); | |
} | |
this.total = cumulative; | |
return this; | |
}, | |
/** | |
* Calculate the line-item total for specified item_id & quantity | |
* @returns {number} item total | |
*/ | |
calculateItemTotal: function(item_id, qty){ | |
var itemPricing = this.pricing[item_id], | |
qtyKeys = Object.keys(itemPricing).sort().reverse(), | |
curQty = qty, | |
cumulative = 0; | |
for (var i = 0; i < qtyKeys.length;){ | |
var blockQty = qtyKeys[i]; | |
if (blockQty > curQty) { | |
i++; | |
continue; | |
} | |
cumulative += itemPricing[blockQty]; | |
curQty -= blockQty; | |
if (curQty < blockQty) i++; | |
} | |
return cumulative; | |
} | |
}; | |
function test(a, b){ | |
if (a === b){ | |
console.log("Test PASSED: " + a + " === " + b); | |
} | |
else { | |
console.log("Test FAILED: " + b + " !== " + b); | |
} | |
} | |
var testPricing = { | |
A: { 1: 2, 4: 7}, | |
B: 12, | |
C: { 1: 1.25, 6: 6 }, | |
D: 0.15 | |
}; | |
var cartA = new Cart(testPricing); | |
cartA.scan('A') | |
.scan('B') | |
.scan('C') | |
.scan('D') | |
.scan('A') | |
.scan('B') | |
.scan('A', 2) | |
.calculateTotal(); | |
test(cartA.total, 32.40); | |
var cartB = new Cart(); | |
cartB.setPricing(testPricing) | |
.scan('C', 7) | |
.calculateTotal(); | |
test(cartB.total, 7.25); | |
var cartC = new Cart(testPricing); | |
cartC.scan('A') | |
.scan('B') | |
.scan('C') | |
.scan('D') | |
.calculateTotal(); | |
test(cartC.total, 15.40); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment