Created
October 19, 2022 11:38
-
-
Save nhevia/4dbcce638a7747e9d52c40c0ecdca6cf to your computer and use it in GitHub Desktop.
NCC-js
This file contains hidden or 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
// Product data: fetched from a database/any storage | |
const products = [ | |
{ code: "VOUCHER", name: "Voucher", price: 5 }, | |
{ code: "TSHIRT", name: "T-shirt", price: 20 }, | |
{ code: "MUG", name: "Coffee mug", price: 7.5 }, | |
]; | |
// Rules. This is where business logic lives. | |
const rules = { | |
secondFree: (total, amount) => { | |
if (amount < 2) return total; | |
if (amount % 2 === 0) { | |
// if even: discount 50% | |
return (total * 50) / 100; | |
} else { | |
// if odd: discount 50% and sum rest (1) | |
return ((total - total / amount) * 50) / 100 + total / amount; | |
} | |
}, | |
bulk: (total, amount) => { | |
if (amount >= 3) { | |
return total - 1 * amount; | |
} else { | |
return total; | |
} | |
}, | |
}; | |
// Price rules. Configurable: it could be fetched from a campaign (CMS, etc) | |
// and is agnostic to business logic -> it requires validation. | |
// Each product could contain an array of rules to apply if + than 1 rule is an option | |
const price_rules = { | |
VOUCHER: "secondFree", | |
TSHIRT: "bulk", | |
}; | |
// Provides an interface to scan (add) products. | |
// In charge of mapping products to price rules/campaigns, apply them and | |
// return the total price. | |
class Checkout { | |
_total = 0; | |
products = new Map(); // hashmap to add "scanned" products | |
constructor(rules) { | |
this.rules = rules; | |
} | |
// Getter that returns the checkout total after applying discounts | |
get total() { | |
// Loop over the scanned products | |
for (const [code, amount] of this.products) { | |
// Get missing product information (price, etc) previously fetched from DB | |
const prod = products.find((p) => p.code === code); | |
// guard in case non-existant product is added (could also throw?) | |
if (!prod) continue; | |
const productTotal = prod.price * amount; | |
const productRules = this.rules[code]; // configured rules for current product | |
// If current product has price rules, apply them and add to total | |
if (productRules) { | |
this._total += rules[productRules](productTotal, amount); | |
} else { | |
this._total += productTotal; | |
} | |
} | |
return this._total; | |
} | |
// Saves the scanned products into a hashmap to calculate subtotals easier. | |
scan(product) { | |
const prod = this.products.get(product); | |
if (prod) { | |
this.products.set(product, prod + 1); | |
} else { | |
this.products.set(product, 1); | |
} | |
} | |
} | |
const checkout1 = new Checkout(price_rules); | |
checkout1.scan("VOUCHER"); | |
checkout1.scan("TSHIRT"); | |
checkout1.scan("MUG"); | |
console.log(checkout1.total); // 32.5 | |
const checkout2 = new Checkout(price_rules); | |
checkout2.scan("VOUCHER"); | |
checkout2.scan("TSHIRT"); | |
checkout2.scan("VOUCHER"); | |
console.log(checkout2.total); // 25 | |
const checkout3 = new Checkout(price_rules); | |
checkout3.scan("TSHIRT"); | |
checkout3.scan("TSHIRT"); | |
checkout3.scan("TSHIRT"); | |
checkout3.scan("VOUCHER"); | |
checkout3.scan("TSHIRT"); | |
console.log(checkout3.total); // 81 | |
const checkout4 = new Checkout(price_rules); | |
checkout4.scan("VOUCHER"); | |
checkout4.scan("TSHIRT"); | |
checkout4.scan("VOUCHER"); | |
checkout4.scan("VOUCHER"); | |
checkout4.scan("MUG"); | |
checkout4.scan("TSHIRT"); | |
checkout4.scan("TSHIRT"); | |
console.log(checkout4.total); // 74.5 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment