Петър обнови решението на 07.11.2011 15:25 (преди около 13 години)
+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class Item
+ attr_reader :name, :price, :promotion
+
+ def initialize(name, price, promotion = nil)
+ @name, @price, @promotion = name, BigDecimal(price), promotion
+ end
+
+end
+
+class Coupon
+ attr_reader :name, :type
+
+ def initialize(name, type)
+ @name = name
+ @type = type
+ end
+
+end
+
+class Cart
+ def initialize(inventory_items, inventory_coupons)
+ @inventory_items = inventory_items
+ @inventory_coupons = inventory_coupons
+ @cart_items = Hash.new(0)
+ @used_coupon = nil
+ end
+
+ def add(name, amount = 1)
+ validate_item(name, amount)
+ @cart_items[name] += amount
+ end
+
+ def use(name)
+ validate_coupon(name)
+ @used_coupon = @inventory_coupons.select { |coupon| coupon.name == name }[0]
+ end
+
+ def total
+ inv_test = InvoiceCalc.new(@inventory_items, @cart_items, @used_coupon)
+ inv_test.calculate_per_item
+ inv_test.calculate_promotion
+ inv_test.calculate_total.keys[0]
+ end
+
+ def invoice
+ #do somthing
+ #invoiceCalc = InvoiceCalc.new(@inventory_items, @cart_items, @used_coupon)
+ invoice_print = InvoicePrint.new(@inventory_items, @cart_items, @used_coupon)
+ invoice_print.print
+ end
+
+ private
+
+ def validate_item(name, amount)
+ dont_exist = @inventory_items.all? { |item| item.name != name }
+ amount_negative = amount<=0
+ big_amount = @cart_items[name] + amount > 99
+ raise "Invalide parameters passed!" if dont_exist or amount_negative or big_amount
+ end
+
+ def validate_coupon(coupon_name)
+ exist = @inventory_coupons.any? { |coupon| coupon.name == coupon_name }
+ raise "Invalide coupon is used!" if !exist
+ end
+
+end
+
+class Inventory
+
+ def initialize()
+ @items = []
+ @coupons = []
+ end
+
+ def register(name, price, promotion = nil)
+ validate_name(name)
+ validate_price(BigDecimal(price))
+ validate_promotion(promotion)
+ @items << Item.new(name, price, promotion)
+ end
+
+ def register_coupon(name, type)
+ validate_coupon_name(name)
+ validate_coupon_type(type)
+ @coupons << Coupon.new(name, type)
+ end
+
+
+ def new_cart
+ Cart.new(@items, @coupons)
+ end
+
+ private
+
+ def validate_name(name)
+ name_size = name.size>40
+ dublicate_item = @items.any? { |item| item.name == name }
+ raise "Invalide name passed!" if name_size or dublicate_item
+ end
+
+ def validate_price(price)
+ if price < BigDecimal('0.01') or price > BigDecimal('999.99')
+ raise "Invalide price passed!"
+ end
+ end
+
+ def validate_promotion(promotion)
+ #posle
+ #if promotion != nil
+ # is_promotion = [:get_one_free, :package, :threshold].include?(promotion.keys)
+ # raise "Invalude promotion passed!"
+ #end
+ end
+
+ def validate_coupon_name(name)
+ if @coupons.any? { |item| item.name == name }
+ raise "Invalide coupon name passed!"
+ end
+ end
+
+ def validate_coupon_type(type)
+ #nakraq
+ end
+end
+
+class InvoiceCalc
+ def initialize(inventory_items, cart_items, used_coupon)
+ @inventory_items = inventory_items
+ @cart_items = cart_items
+ @used_coupon = used_coupon
+ @per_item_calculations = {}
+ @promotion_calculations = {}
+ end
+
+ def calculate_per_item
+ @cart_items.each do |name, amount|
+ price = @inventory_items.select { |item| item.name == name }[0].price
+ @per_item_calculations[name] = price*BigDecimal(amount.to_s)
+ end
+ @per_item_calculations
+ end
+
+ def calculate_promotion
+ @cart_items.each do |name, amount|
+ item = @inventory_items.select { |item| item.name == name }[0]
+
+ calculate_one_free(item, amount)
+ calculate_package(item, amount)
+ calculate_threshold(item, amount)
+ end
+ @promotion_calculations
+ end
+
+ def calculate_coupon(total_price)
+ return BigDecimal('0.0') if @used_coupon == nil
+ if @used_coupon.type.keys[0] == :percent
+ percent = BigDecimal(@used_coupon.type[:percent].to_s)/BigDecimal('100')
+ total_price*percent
+ else
+ return BigDecimal(@used_coupon.type[:amount].to_s)
+ end
+ end
+
+ def calculate_total
+ total_per_item = total(@per_item_calculations)
+ total_promotion = total(@promotion_calculations)
+ diffrence = total_per_item - total_promotion
+ coupon_price = calculate_coupon(diffrence)
+
+ if diffrence - coupon_price >= BigDecimal('0.0')
+ {diffrence - coupon_price=> coupon_price}
+ else
+ {BigDecimal('0.0') => (diffrence - coupon_price).abs}
+ end
+ end
+
+ private
+
+
+ def total(hash)
+ hash.inject(BigDecimal.new('0.0')) do |memo, (name, price)|
+ memo += price
+ memo
+ end
+ end
+
+ def calculate_threshold(item, amount)
+ return if item.promotion == nil or item.promotion.keys[0] != :threshold
+ threshold = item.promotion[:threshold]
+ threshold_percent = BigDecimal(threshold.values[0].to_s)/BigDecimal('100')
+ diffrence = (item.price*threshold_percent)
+ threshold_amount = amount - threshold.keys[0]
+ final_price = diffrence * BigDecimal(threshold_amount.to_s)
+ @promotion_calculations[item.name] = final_price if threshold_amount>0
+
+ end
+
+ def calculate_package(item, amount)
+ return if item.promotion == nil or item.promotion.keys[0] != :package
+ package = item.promotion[:package]
+ package_percent = BigDecimal(package.values[0].to_s)/BigDecimal('100')
+ promotion_price = item.price*BigDecimal(package.keys[0].to_s) * package_percent
+ package_amount = amount/package.keys[0]
+ @promotion_calculations[item.name] = promotion_price * BigDecimal(package_amount.to_s)
+ end
+
+ def calculate_one_free(item, amount)
+ return if item.promotion == nil or item.promotion.keys[0] != :get_one_free
+ free_count_promotion = item.promotion[:get_one_free]
+ free_count = amount/free_count_promotion
+ @promotion_calculations[item.name] = item.price * BigDecimal(free_count.to_s)
+ end
+end
+
+class InvoicePrint
+ def initialize(inventory_items, cart_items, used_coupon)
+ @inventory_items = inventory_items
+ @cart_items = cart_items
+ @used_coupon = used_coupon
+ @invoice_calculator = InvoiceCalc.new(inventory_items, cart_items, used_coupon)
+ @map_string = {1=>'st', 2=>'nd', 3=>'rd'}
+ end
+
+ def print
+ invoice_string = <<-header
++------------------------------------------------+----------+
+| Name qty | price |
++------------------------------------------------+----------+
+ header
+ invoice_string << create_body
+ invoice_string << create_total
+ end
+
+ private
+
+ #def price_field(price_string)
+ # price_string = "|#{''*(10-price_string.size)}#{price_string} |\n"
+ #end
+
+ def create_body
+ body_string = ""
+ per_item = @invoice_calculator.calculate_per_item
+ promotions = @invoice_calculator.calculate_promotion
+ @cart_items.each do |name, amount|
+ body_string << create_item_row(name, amount, per_item[name])
+ body_string << create_promotion_row(promotions, name)
+ end
+ body_string << create_coupon_row
+ end
+
+ def create_coupon_row
+ return "" if @used_coupon == nil
+ total_coupon = @invoice_calculator.calculate_total
+ char = @used_coupon.type.keys[0]==:percent ? '%':''
+ formated_price = "-%0.2f" % total_coupon.values[0]
+ price_field = "|#{' '*(9-formated_price.size)}#{formated_price} |\n"
+ text = "Coupon #{@used_coupon.name} - #{@used_coupon.type.values[0]}#{char} off"
+ "| #{text}#{' '*(46-text.size)} " << price_field
+ end
+
+ def create_total
+ total_coupon = @invoice_calculator.calculate_total
+ formate_price = "%0.2f" % total_coupon.keys[0]
+ price_field = "|#{' '*(9-formate_price.size)}#{formate_price} |"
+ total_string = <<-TOTAL
++------------------------------------------------+----------+
+| TOTAL #{price_field}
+ TOTAL
+
+ total_string << "+------------------------------------------------+----------+\n"
+ end
+
+ def create_promotion_row(promotion_array, name)
+ return "" if promotion_array[name] == nil
+ item = @inventory_items.select { |item| item.name == name }[0]
+ promotion = item.promotion
+ if promotion.keys[0] == :get_one_free
+ create_get_one_free_row(promotion[:get_one_free], promotion_array[name])
+ elsif promotion.keys[0] == :package
+ create_package_row(promotion[:package], promotion_array[name])
+ else
+ create_threshold_row(promotion[:threshold], promotion_array[name])
+ end
+ end
+
+ def create_item_row(name, amount, price)
+ used_chars = name.size + amount.to_s.size + 2
+ name_amount_string = "| #{name}#{' '*(48-used_chars)}#{amount} "
+ formated_price = "%0.2f" % price
+ price_field = "|#{' '*(9-formated_price.size)}#{formated_price} |\n"
+ name_amount_string+price_field
+ end
+
+ def create_get_one_free_row(promotion, price)
+ formate_price = "-%0.2f" % price
+ price_field = "|#{' '*(9-formate_price.size)}#{formate_price} |\n"
+ text = "(buy #{promotion-1}, get 1 free)"
+ "| #{text}#{' '*(45-text.size)}" << price_field
+ end
+
+ def create_package_row(hash, price)
+ formated_price = "-%0.2f" % price
+ price_field = "|#{' '*(9-formated_price.size)}#{formated_price} |\n"
+ amount = hash.keys[0]
+ text = "(get #{hash.values[0]}% off for every #{amount})"
+ package_row = "| #{text}#{' '*(45-text.size)}"
+ package_row + price_field
+ end
+
+ def create_threshold_row(hash, price)
+ formated_price = "-%0.2f" % price
+ price_field = "|#{' '*(9-formated_price.size)}#{formated_price} |\n"
+ order = @map_string[hash.keys[0]] == nil ? "th" : @map_string[hash.keys[0]]
+ amount = hash.keys[0].to_s + order
+ text = "(#{hash.values[0]}% off of every after the #{amount})"
+ package_row = "| #{text}#{' '*(45-text.size)}"
+ package_row + price_field
+ end
+
+end