Димо обнови решението на 04.11.2011 17:14 (преди около 13 години)
+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class PercentCoupon
+
+ attr_accessor :name, :percent
+
+ def initialize(name, coupon)
+ @name = name
+ @percent = coupon.values[0].to_s.to_d / 100
+ end
+
+ def calc(price)
+ [price - price * @percent, 0].max
+ end
+end
+
+class AmountCoupon
+
+ attr_accessor :name, :amount
+
+ def initialize(name, amount)
+ @name = name
+ @amount = amount.values[0].class == String ? amount.values[0].to_d : amount.values[0]
+ end
+
+ def calc(price)
+ [price - amount, 0].max
+ end
+end
+
+class Inventory
+
+ attr_accessor :products, :coupons
+
+ def initialize
+ @products = {}
+ @coupons = {}
+ end
+
+ def register(name, price, promo = {})
+ if @products.key? name or name.size > 40 or price.to_d < 0.01 or price.to_d > 999.99
+ raise "Invalid parameters passed."
+ end
+ case promo.keys[0]
+ when :get_one_free then @products[name] = GetOneFreeProduct.new(name,price,promo)
+ when :package then @products[name] = PackageDiscountProduct.new(name,price,promo)
+ when :threshold then @products[name] = ThresHoldProduct.new(name,price,promo)
+ else @products[name] = Product.new(name, price)
+ end
+ end
+
+ def register_coupon(name,coupon)
+ case coupon.keys[0]
+ when :percent then @coupons[name] = PercentCoupon.new(name,coupon)
+ when :amount then @coupons[name] = AmountCoupon.new(name,coupon)
+ end
+ end
+
+ def new_cart
+ Cart.new(@products, @coupons)
+ end
+end
+
+class Printer
+
+ def print(product, quantity)
+ if(product.class == Product)
+ return print_product(product, quantity)
+ end
+ if(product.class == GetOneFreeProduct)
+ return print_one_free_product(product, quantity)
+ end
+ if(product.class == PackageDiscountProduct)
+ return print_package_discount_product(product, quantity)
+ end
+ if(product.class == ThresHoldProduct)
+ return print_thres_hold_product(product, quantity)
+ end
+end
+
+ def print_product(product, qty)
+ tmp = 46 - qty.to_s.size
+ price = ("%4.2f" % (product.price * qty) ) + " |"
+
+ "| " + product.name.ljust(tmp) + qty.to_s + " |" + price.rjust(11) + "\n"
+ end
+
+ def print_one_free_product(product, qty)
+ p = print_product(product, qty)
+ tmp = ("| (buy " + (product.number - 1).to_s + ", get 1 free)").ljust(49) + "|"
+ p + tmp + ("%4.2f" % (product.get_discount(qty).to_f).to_s + " |").rjust(11) + "\n"
+ end
+
+ def print_package_discount_product(product, qty)
+ p = print_product(product, qty)
+ k, v = product.package.keys[0], product.package.values[0]
+ tmp = ("| (get " + v.to_s + "% off for every " + k.to_s + ")").ljust(49) + "|"
+ p + tmp + ("%4.2f" % (product.get_discount(qty).to_f).to_s + " |").rjust(11) + "\n"
+ end
+
+ def print_thres_hold_product(product, qty)
+ p = print_product(product, qty)
+ k, v = product.threshold.keys[0], product.threshold.values[0]
+ tmp = ("| (" + v.to_s + "% off of every after the " + suf(k) + ")").ljust(49) + "|"
+ p + tmp + ("%4.2f" % (product.get_discount(qty).to_f).to_s + " |").rjust(11) + "\n"
+ end
+
+ def suf(number)
+ if(number >= 4 and number <= 20)
+ return number.to_s + "th"
+ end
+ case number % 10
+ when 1 then number.to_s + "st"
+ when 2 then number.to_s + "nd"
+ when 3 then number.to_s + "rd"
+ else number.to_s + "th"
+ end
+ end
+
+ def print_total(sum)
+ line = "+------------------------------------------------+----------+\n"
+ total = "| TOTAL |"
+ line + total + (("%4.2f" % sum.to_f).to_s + " |").rjust(11) + "\n" + line
+ end
+
+ def print_coupon(coupon, price)
+ if(coupon.class == AmountCoupon)
+ print_amount_coupon(coupon,price)
+ else
+ print_percent_coupon(coupon,price)
+ end
+ end
+
+ def print_percent_coupon(coupon, price)
+ percent = (coupon.percent * 100).to_f.round(0).to_s
+ tmp = ("| Coupon " + coupon.name + " - " + percent + "% off").ljust(49) + "|"
+ tmp + ("-" + ("%4.2f" % price.to_f).to_s + " |").rjust(11) + "\n"
+ end
+
+ def print_amount_coupon(coupon, price)
+ amount = ("%4.2f" % coupon.amount.to_f).to_s
+ tmp = ("| Coupon " + coupon.name + " - " + amount + " off").ljust(49) + "|"
+ tmp + ("-" + ("%4.2f" % price.to_f).to_s + " |").rjust(11) + "\n"
+ end
+end
+
+class Cart
+
+ attr_accessor :products, :inventary, :coupons, :used_c
+
+ def initialize(inventary, coupons)
+ @products = {}
+ @inventary = inventary
+ @coupons = coupons
+ @used_c = nil
+ end
+
+ def add(product, quantity = 1)
+ if not @inventary.key? product or quantity > 99 or quantity <= 0
+ raise "Invalid parameters passed."
+ end
+ if @products.key? product
+ @products[product] += quantity
+ else
+ @products[product] = quantity
+ end
+ end
+
+ def use(name)
+ if not @coupons.key? name
+ raise "Invalid parameters passed."
+ end
+ if @used_c != nil
+ return
+ end
+ @used_c = @coupons[name]
+ end
+
+ def total
+ price = @products.to_a.inject(0) { |a, b| a + @inventary[b[0]].calc_price(b[1]) }
+ if @used_c != nil
+ price = @used_c.calc(price)
+ end
+ price
+ end
+
+ def get_discount
+ @products.to_a.inject(0) { |a, b| a + @inventary[b[0]].calc_price(b[1]) } - total
+ end
+
+ def invoice
+ start = "+------------------------------------------------+----------+
+| Name qty | price |
++------------------------------------------------+----------+\n"
+ p = Printer.new
+ tmp = @products.to_a.inject(start) { |a, b| a + p.print(@inventary[b[0]], b[1]) }
+ if @used_c != nil
+ tmp += p.print_coupon(@used_c, get_discount)
+ end
+ tmp + p.print_total(total)
+ end
+end
+
+class Product
+
+ attr_accessor :name, :price
+
+ def initialize(name, price)
+ @name = name
+ @price = price.to_d
+ end
+
+ def calc_price(quantity)
+ quantity * @price
+ end
+ def get_discount(quantity)
+ - (quantity * @price - calc_price(quantity))
+ end
+end
+
+class GetOneFreeProduct < Product
+
+ attr_accessor :number
+
+ def initialize(name, price, promotion)
+ super(name, price)
+ @number = promotion[:get_one_free]
+ end
+
+ def calc_price(quantity)
+ (quantity - (quantity / @number)) * @price
+ end
+end
+
+class PackageDiscountProduct < Product
+
+ attr_accessor :package
+
+ def initialize(name, price, promotion)
+ super(name, price)
+ @package = promotion[:package]
+ end
+
+ def calc_price(quantity)
+ number = @package.keys[0]
+ percent = (@package.values[0] / 100.0).to_s.to_d
+ quantity * @price - ((quantity / number) * @price * percent * number)
+ end
+end
+
+class ThresHoldProduct < Product
+
+ attr_accessor :threshold
+
+ def initialize(name, price, promotion)
+ super(name, price)
+ @threshold = promotion[:threshold]
+ end
+
+ def calc_price(quantity)
+ number = @threshold.keys[0]
+ percent = (@threshold.values[0] / 100.0).to_s.to_d
+ if quantity <= number
+ quantity * @price
+ else
+ quantity * @price - ((quantity - number) * @price * percent)
+ end
+ end
+end