Михаил обнови решението на 06.11.2011 19:20 (преди около 13 години)
+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class Inventory
+ Min = BigDecimal('0.01')
+ Max = BigDecimal('999.99')
+ attr_accessor :products, :coupons
+
+ def initialize()
+ @products = []
+ @coupons = {}
+ end
+
+ def register(product, product_cost, promotion = "no")
+ cost = product_cost.to_d
+ if(product.length() > 40 || cost < Min || cost > Max || products.include?(product))
+ raise "Invalid parameters passed."
+ else
+ @products << product << product_cost << promotion
+ end
+ end
+
+ def new_cart()
+ cart = Cart.new(@products, @coupons)
+ end
+
+ def register_coupon(name, type)
+ @coupons[name] = type
+ end
+end
+
+class Cart
+ attr_accessor :sum, :counter, :basket, :products, :coupons, :invoice
+ BottomLeft = "| TOTAL "+" ".ljust(41) + "| ".ljust(5)
+ Row = "+".ljust(49,"-") + "+".ljust(11,"-")+"+\n"
+
+ def initialize(*params)
+ @counter = 0
+ @sum = 0
+ @basket = {}
+ @products = params[0]
+ @coupons = params[1]
+ @invoice = ""
+ end
+
+ def add(added_product, quantity = 1)
+ raise "Invalid parameters passed." if !@products.include?(added_product)
+ raise "Invalid parameters passed." if (quantity <= 0 || quantity > 99)
+ if @basket.has_key?(added_product)
+ @basket[added_product] = @basket[added_product] + quantity
+ else
+ @basket[added_product] = quantity
+ end
+ raise "Invalid parameters passed." if @basket[added_product] > 99
+ check_promotions(added_product)
+ end
+
+ def check_promotions(added_product)
+ products.each_slice(3) do |item|
+ if(item[2] != "no" && item[0] == added_product)
+ hash_keys = item[2].keys
+ hash_values = item[2].values
+ add_promotion(added_product, hash_keys[0], hash_values[0])
+ end
+ end
+ end
+
+ def add_promotion(added_product, key, value)
+ promotions = Promotions.new(@products, @basket)
+ promotions.get_one_free(added_product, value) if key.to_s == "get_one_free"
+ promotions.package(added_product, value) if key.to_s == "package"
+ promotions.threshold(added_product, value) if key.to_s == "threshold"
+ end
+
+ def count_sume()
+ @sum = 0
+ basket.each() do |product, quantity|
+ if products.include?(product)
+ price = get_price(product)
+ @sum = @sum + quantity * price.to_d
+ else
+ price = basket[product]
+ @sum = @sum - price
+ end
+ end
+ end
+
+ def get_price(product)
+ product_price = nil
+ products.each_slice(3) do |item|
+ product_price = item[1] if item[0] == product
+ end
+ product_price
+ end
+
+ def total()
+ count_sume()
+ final_result = @sum
+ final_result = 0 if @sum < 0
+ final_result
+ end
+
+ def use(name)
+ count_sume()
+ coupon = Coupons.new(@basket, @coupons)
+ @counter = @counter + 1
+ @sum = coupon.total(name, @sum) if coupons.has_key?(name) && @counter == 1
+ @sum = sprintf("%.2f", @sum)
+ end
+
+ def invoice()
+ count_sume()
+ @invoice = Row + "| Name" + " ".rjust(39) + "qty | price |\n" + Row
+ @basket.each do |key, quantity|
+ res = get_price(key).to_d * quantity if @products.include?(key)
+ res = @basket[key] if !@products.include?(key)
+ put_lines(quantity, key, res)
+ end
+ @sum = 0 if @sum < 0.01
+ @invoice += Row + BottomLeft + sprintf("%.2f",@sum).rjust(5)+" |\n"+ Row
+ end
+
+ def put_lines(quantity, key, res)
+ quantity = sprintf("%.0f", quantity)
+ if @products.include?(key)
+ right_line = " | ".ljust(6) + sprintf("%.2f",res).rjust(5) + " |\n"
+ @invoice += "| " + key.ljust(42) + quantity.rjust(4) + right_line
+ else
+ @invoice += "| " + key.ljust(46) + " | ".ljust(5)
+ @invoice += sprintf("%.2f",-res).rjust(6) + " |\n"
+ end
+ end
+end
+
+class Promotions
+ attr_accessor :basket, :products
+
+ def initialize(*params)
+ @products = params[0]
+ @basket = params[1]
+ end
+
+ def get_one_free(product, value)
+ quantity = @basket[product] / value
+ get_product_price(product)
+ discount = @price * quantity
+ string = " (buy "+sprintf("%.0f",value-1)+", get 1 free)"
+ @basket[string] = discount
+ end
+
+ def package(product, hash)
+ hash.each { |key, value| @count = key, @percent = value }
+ get_product_price(product)
+ counter = @basket[product] / @count[0]
+ left = @basket[product] % @count[0]
+ cut = @price * counter * @count[0] * (100 - @percent) / 100 + @price * left
+ @discount = @price * @basket[product] - cut
+ @basket[package_message()] = @discount if @basket[product] >= @count[0]
+ end
+
+ def threshold(product, hash)
+ hash.each { |key, value| @count = key, @percent = value }
+ get_product_price(product)
+ counter = @basket[product] - @count[0]
+ cut = counter * @price * (100 - @percent) / 100 + @count[0] * @price
+ @discount = @basket[product] * @price - cut
+ @basket[threshold_message()] = @discount if counter > 0
+ end
+
+ def get_product_price(product)
+ @products.each_slice(3) do |item|
+ @price = item[1].to_d if item[0] == product
+ end
+ end
+
+ def package_message()
+ left = " (get "+sprintf("%.0f",@percent)
+ right = "% off for every " + sprintf("%.0f",@count[0])+")"
+ message = left + right
+ end
+
+ def threshold_message()
+ left = " ("+sprintf("%.0f",@percent)
+ middle = "% off of every after the "
+ number_end = get_end(@count[0])
+ right = sprintf("%.0f",@count[0])+number_end+")"
+ message = left + middle + right
+ end
+
+ def get_end(number)
+ last_digit = number % 10
+ result = "st" if last_digit == 1
+ result = "nd" if last_digit == 2
+ result = "rd" if last_digit == 3
+ result = "th" if last_digit != 1 && last_digit != 2 && last_digit != 3
+ result
+ end
+end
+
+class Coupons
+ attr_accessor :basket, :coupons
+
+ def initialize(*params)
+ @basket = params[0]
+ @coupons = params[1]
+ end
+
+ def percent_coupon(total, value)
+ result = total * value / 100
+ end
+
+ def total(used, total)
+ coupons[used].each { |key, result| @type = key.to_s, @value = result }
+ @value = @value.to_d if @type[0] == "amount"
+ coupon = percent_coupon(total, @value) if @type[0] == "percent"
+ coupon = @value if @type[0] == "amount"
+ coupon_value(coupon,total,used,'%') if @type[0] == "percent"
+ coupon_value(coupon,total,used,'') if @type[0] == "amount"
+ total = total - sprintf("%.2f", coupon).to_d
+ end
+
+ def coupon_value(coupon,total,used,percent)
+ if coupon > total && total > BigDecimal('0.00')
+ @basket[message(used,percent)] = total
+ else
+ @basket[message(used,percent)] = coupon
+ end
+ end
+
+ def message(used,percent)
+ if percent == "%"
+ message = "Coupon "+ used + " - " + sprintf("%.0f",@value) + percent + " off"
+ else
+ message = "Coupon "+ used + " - " + sprintf("%.2f",@value) + percent + " off"
+ end
+ end
+end