diff --git a/Rakefile b/Rakefile index 2f7ef93..180f1a5 100644 --- a/Rakefile +++ b/Rakefile @@ -19,16 +19,16 @@ task :run do item1 = Product.new('001', 'Very Cheap Chair', 9.25) item2 = Product.new('002', 'Little Table', 45.00) item3 = Product.new('003', 'Funky Light', 19.95) - item4 = Product.new('001', 'Very Cheap Chair', 9.25) - puts "first run" + + puts "first basket" co = Checkout.new(promo_rules) puts "scanning" co.scan(item1) co.scan(item2) co.scan(item3) - co.scan(item4) + puts co.total end diff --git a/lib/checkout.rb b/lib/checkout.rb index e763fc1..fcdb253 100644 --- a/lib/checkout.rb +++ b/lib/checkout.rb @@ -1,8 +1,10 @@ class Checkout attr_reader :promo_rules + attr_reader :discount_manager attr_accessor :basket def initialize(promo_rules = []) + @dicount_manager = DiscountManager.new(promo_rules) @promo_rules = promo_rules @basket = [] end @@ -12,16 +14,12 @@ def scan(item) end def total - multi_discount_price = 0 + price = 0 puts basket basket.each do |product| - promo_rules.each do |promo_rule| - if promo_rule.type == PromoRule::TYPE[:multi_discount] - multi_discount_price = promo_rule.apply(product.code, quantity_in_basket(product.code)) - end - end + price += product.price end - multi_discount_price + price.round(2) end def quantity_in_basket(code) diff --git a/lib/discount_manager.rb b/lib/discount_manager.rb new file mode 100644 index 0000000..112c67d --- /dev/null +++ b/lib/discount_manager.rb @@ -0,0 +1,28 @@ +class DiscountManager + + attr_reader :promo_rules + + def initialize(promo_rules = []) + @promo_rules = promo_rules + end + + def discount_price_for(product, quantity) + discount_price = 0 + + promo_rules.each do |promo_rule| + if promo_rule.type == PromoRule::TYPE[:multibuy_discount] #multi_discount + discount_price = promo_rule.apply(product.code, quantity) + end + end + discount_price == 0 ? product.price : discount_price + end + + def discount_total(total) + promo_rules.each do |promo_rule| + if promo_rule.type == PromoRule::TYPE[:discount] # discount + total = promo_rule.apply(total) + end + end + total + end +end diff --git a/spec/checkout_spec.rb b/spec/checkout_spec.rb new file mode 100644 index 0000000..8706b5a --- /dev/null +++ b/spec/checkout_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe Checkout do + ProductStub = Struct.new(:code, :name, :price) + subject { Checkout.new } + + describe "on setup" do + it 'should start with an empty basket' do + expect(subject.basket.count).to eq(0) + end + it 'should start with zero price' do + expect(subject.total).to eq(0) + end + end + + context 'with no promo_rules' do + describe 'scan items' do + let(:product1) { ProductStub.new('001', 'name', 10) } + let(:product2) { ProductStub.new('002', 'here', 10) } + + it 'should calculate total as the product price' do + subject.scan(product1) + subject.scan(product2) + expect(subject.total).to eq(20) + end + + it 'should round total to two decimal places' do + product = ProductStub.new('001', 'name', 25.4552) + subject.scan(product) + expect(subject.total).to eq(25.46) + end + end + end + + context 'with promo_rules' do + let(:product1) { ProductStub.new('001', 'name', 10) } + let(:product2) { ProductStub.new('002', 'here', 10) } + end + +end diff --git a/spec/discount_manager_spec.rb b/spec/discount_manager_spec.rb new file mode 100644 index 0000000..27ba4e3 --- /dev/null +++ b/spec/discount_manager_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' + +describe DiscountManager do + let(:promo_rule) {double('promo_rule')} + let(:product) { Struct.new(:code, :name, :price).new('001', 'foobar', 10.97) } + subject { DiscountManager.new([promo_rule]) } + + describe '#discount_price_for' do + before do + allow(promo_rule).to receive(:type) { PromoRule::TYPE[:multibuy_discount] } + allow(promo_rule).to receive(:apply) { 60 } + end + + it 'should pass the calculation to the promo_rule with params' do + expect(promo_rule).to receive(:type) { PromoRule::TYPE[:multibuy_discount] } + expect(promo_rule).to receive(:apply).with(product.code, 2) + + subject.discount_price_for(product, 2) + end + + it 'should return the discounted price when rule applied' do + price = subject.discount_price_for(product, 1) + expect(price).to eq(60) + end + + it 'should return the original price if no rule applied' do + discount_manager = DiscountManager.new + + price = discount_manager.discount_price_for(product, 1) + expect(price).to eq(product.price) + end + + end + + describe '#discount_total' do + before do + allow(promo_rule).to receive(:type) { PromoRule::TYPE[:discount] } + allow(promo_rule).to receive(:apply) { 100 } + end + + it 'should pass the calculation to the rule with subtotal' do + expect(promo_rule).to receive(:type) { PromoRule::TYPE[:discount] } + expect(promo_rule).to receive(:apply).with(10) + + subject.discount_total(10) + end + + it 'should return the calculated total when rule applied' do + total = subject.discount_total(10) + expect(total).to eq(100) + end + + it 'should return original subtotal if no rule match' do + discount_manager = DiscountManager.new + + total = discount_manager.discount_total(10) + expect(total).to eq(10) + end + + end +end