Skip to content

Instantly share code, notes, and snippets.

@nhevia
Created October 19, 2022 11:38
Show Gist options
  • Save nhevia/4dbcce638a7747e9d52c40c0ecdca6cf to your computer and use it in GitHub Desktop.
Save nhevia/4dbcce638a7747e9d52c40c0ecdca6cf to your computer and use it in GitHub Desktop.
NCC-js
// 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