Дарина обнови решението на 05.11.2011 00:34 (преди около 13 години)
+#!/usr/local/rvm/rubies/ruby-1.9.2-p290/bin/ruby
+
+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class Product
+
+ attr_accessor :name, :value, :promotion
+
+ def initialize(name, value, promotion=nil)
+ if name.length <= 40 and value.to_d >= 0.01 and value.to_d <= 999.99
+ @name = name
+ @value = value.to_d
+ else raise "Invalid parameters passed."
+ end
+ @promotion = promotion
+ end
+
+ def promotion?
+ return true if @promotion
+ false
+ end
+
+ def get_one_free?
+ return true if @promotion[:get_one_free]
+ false
+ end
+
+ def package?
+ return true if @promotion[:package]
+ false
+ end
+
+end
+
+
+class Inventory
+
+ attr_accessor :all_products, :coupons
+
+ def initialize
+ @all_products = []
+ @coupons=[]
+ end
+
+ def register(name, value, promotion=nil)
+ if @all_products.any? { |product| product.name == name }
+ raise "Dublicate names."
+ else @all_products << Product.new(name, value, promotion)
+ end
+ end
+
+ def new_cart
+ cart = Cart.new(@all_products, @coupons)
+ end
+
+ def register_coupon(name, type)
+ if @coupons.any? { |coupon| coupon.name == name }
+ raise "Dublicate names of coupons."
+ else @coupons << Coupon.new(name, type)
+ end
+ end
+
+end
+
+class Coupon
+
+ attr_accessor :name, :type, :value
+
+ def initialize(name, type)
+ @name = name
+ @type = type
+ @value = type.values[0]
+ end
+
+ def amount?
+ return true if @type[:amount]
+ false
+ end
+
+end
+
+class Cart
+
+ attr_accessor :inventory, :coupons, :coupon_name, :cart_products, :invoice, :total
+
+ def initialize(inventory, coupons)
+ @cart_products = []
+ @inventory = inventory
+ @coupons = coupons
+ end
+
+ def add(product_name, count = 1)
+ if @inventory.none? { |product| product.name == product_name }
+ raise "There isn't such a product in the inventory."
+ elsif not_allowable_count?(count)
+ raise "Quantity must be between 1 and 99 inclusive."
+ else helper_add(product_name, count)
+ end
+ end
+
+ def helper_add(name, count)
+ if @cart_products.any? { |prod| prod[:product] == name }
+ current = @cart_products.select { |pr| pr[:product] == name }
+ current[0][:count]+=count
+ else @cart_products << { product: name, count: count }
+ end
+ end
+
+ def use(coupon_name)
+ if @coupon_name
+ raise "Caupon has been already add to this cart."
+ elsif @coupons.none? { |coupon| coupon.name == coupon_name}
+ raise "There isn't such an coupon in the inventory"
+ else @coupon_name = coupon_name
+ end
+ end
+
+ def not_allowable_count?(count)
+ return false if count >=1 and count <= 99
+ true
+ end
+
+ def init_invoice
+ @invoice = Invoice.new(self)
+ end
+
+ def invoice
+ init_invoice
+ @invoice.print_invoice
+ end
+
+ def total
+ init_invoice
+ @invoice.total
+ end
+
+end
+
+class Invoice
+
+ attr_accessor :cart, :printer
+
+ def initialize(cart)
+ @cart = cart
+ @printer = Printer.new
+ end
+
+ def total
+ invoice_total=0
+ @cart.cart_products.each do |product|
+ current = @cart.inventory.select { |prod| product[:product] == prod.name }
+ invoice_total += current[0].value * product[:count]
+ count = product[:count]
+ current[0].promotion? ? invoice_total -= cur_promotion(current[0], count) : next
+ end
+ cart.coupon_name ? coupon_total(invoice_total) : invoice_total
+ end
+
+ def cur_promotion(current, count)
+ if current.get_one_free?
+ calc_one_free(current, count)
+ elsif current.package?
+ calc_package(current, count)
+ else calc_threshold(current, count)
+ end
+ end
+
+ def calc_one_free(current, count)
+ count / current.promotion.values[0] * current.value
+ end
+
+ def calc_package(current, count)
+ package = current.promotion.values[0].keys[0]
+ percent = current.promotion.values[0].values[0]
+ ((count/package) * package) * ("0.#{percent}".to_d * current.value)
+ end
+
+ def calc_threshold(current, count)
+ package = current.promotion.values[0].keys[0]
+ percent = current.promotion.values[0].values[0]
+ count > package ? (count - package) * ("0.#{percent}".to_d * current.value) : 0
+ end
+
+ def coupon_total(total_without_coupon)
+ tmp = @cart.coupons.select { |coupon| coupon.name == @cart.coupon_name}
+ if tmp[0].amount?
+ total = total_without_coupon - tmp[0].value.to_d
+ else
+ @without_coupon2 = "0.#{tmp[0].value}".to_d * total_without_coupon
+ total = total_without_coupon - @without_coupon2
+ end
+ @without_coupon1 = total
+ total < 0 ? '0.00'.to_d : total
+ end
+
+ def print_invoice
+ invoice = @printer.invoice_top
+ @cart.cart_products.each do |product|
+ count = product[:count]
+ current = @cart.inventory.select { |prod| product[:product] == prod.name }
+ invoice << @printer.pr_product(product[:product], count, current[0].value * count)
+ current[0].promotion? ? invoice << print_promotion(current[0], count) : next
+ end
+ if @cart.coupon_name then invoice << print_coupon(total)
+ end
+ invoice << @printer.invoice_bottom(total)
+ end
+
+ def print_promotion(current, count)
+ if current.get_one_free?
+ @printer.promotion_get(current.promotion.values[0], cur_promotion(current, count))
+ else
+ broika = current.promotion.values[0].values[0]
+ percent = current.promotion.values[0].keys[0]
+ if current.package?
+ @printer.promotion_package(broika, percent, cur_promotion(current, count))
+ else
+ @printer.promotion_threshold(broika, percent, cur_promotion(current, count))
+ end
+ end
+ end
+
+ def print_coupon(total)
+ current = @cart.coupons.select { |coupon| @cart.coupon_name == coupon.name }
+ if current[0].amount?
+ @printer.coupon_amount(current[0].name, current[0].value, @without_coupon1)
+ else @printer.coupon_percent(current[0].name, current[0].value, @without_coupon2)
+ end
+ end
+
+end
+
+
+class Printer
+
+ attr_accessor :lines, :symbol
+
+ def initialize
+ @lines = "+------------------------------------------------+----------+\n"
+ @symbol = "%"
+ end
+
+ def invoice_top
+ sprintf("%s| Name qty | price |\n%s", @lines, @lines)
+ end
+
+ def invoice_bottom(total)
+ sprintf("%s| TOTAL | %.2f |\n%s", @lines, total, @lines)
+ end
+
+ def pr_product(name, qty, price)
+ sprintf("| %s %d | %.2f |\n", name, qty, price)
+ end
+
+ def coupon_amount(name, price, discount)
+ sprintf("| Coupon %s - %.2f off | %.2f |\n", name, price, discount)
+ end
+
+ def coupon_percent(name, price, discount)
+ sprintf("| Coupon %s - %d%s off | -%.2f |\n", name, price, @symbol, discount)
+ end
+
+ def promotion_get(value, discount)
+ sprintf("| (buy %d, get 1 free) | -%.2f |\n", value - 1, discount)
+ end
+
+ def promotion_package(percent, value, discount)
+ package = "| (get #{percent}% off for every #{value}) |"
+ sprintf("%s -%.2f |\n",package, discount)
+ end
+
+ def promotion_threshold(percent, value, discount)
+ symbol = "%"
+ case value
+ when 1 then ending = "st"
+ when 2 then ending = "nd"
+ when 3 then ending = "rd"
+ else ending = "th"
+ end
+ threshold = "| (#{percent}% off of every after the #{value}"
+ sprintf("%s%s) | -%.2f |\n", threshold, ending, discount)
+ end
+
+end
+