Деян обнови решението на 07.11.2011 14:13 (преди около 13 години)
+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class Inventory
+ attr_accessor :wares
+
+ def initialize
+ @wares = []
+ @coupons = []
+ end
+
+ def register(name, price, *promotion)
+ if(name.length > 40 or price.to_d < 0.01 or price.to_d > 999.99)
+ raise "Invalid parameters passed."
+ end
+ if(@wares.select{|x| x.name == name} != [] and @wares != [])
+ raise "Product already exist."
+ end
+ @wares << Ware.new(name, price, promotion)
+ end
+
+ def new_cart
+ ShopingCart.new(@wares, @coupons)
+ end
+
+ def register_coupon(name, hash)
+ @coupons << Coupon.new(name, hash)
+ end
+end
+
+class Coupon
+ attr_accessor :name, :type
+
+ def initialize(name, hash)
+ @name = name
+ @type = hash
+ end
+end
+
+class Ware
+ attr_accessor :name, :price, :promotion
+
+ def initialize(name, price, promotion)
+ @name = name
+ @price = price
+ @promotion = promotion
+ end
+end
+
+class ShopingCart
+ def initialize(wares, coupons)
+ @wares = wares
+ @coupons = coupons
+ @purchases = []
+ @coupon_discount = 0
+ @coupon_type
+ @coupon_name
+ end
+
+ def add(name,*quantity)
+ qty = quantity == [] ? 1 : quantity[0]
+ unless (@wares.map{|n| n.name}.include? name)
+ raise "Unexisiting product."
+ end
+ if (qty < 1 or qty > 99)
+ raise "Invalid parameters passed."
+ end
+ unless (@purchases.index{|x| x.name == name}.nil?)
+ @purchases[@purchases.index{|x| x.name == name}].quantity += qty
+ else
+ @purchases << Purchase.new(name, qty, @wares.find{|x| x.name == name}.price)
+ end
+ end
+
+ def promotion_price(purchase)
+ promo = @wares.find{|x| x.name == purchase.name}.promotion[0]
+ if(promo.nil? or @wares.find{|x| x.name == purchase.name}.promotion == [])
+ purchase.new_price = purchase.price.to_d * purchase.quantity
+ else
+ case promo.first.first
+ when :get_one_free then purchase.new_price = promo_get_one_free(purchase,promo)
+ when :threshold then purchase.new_price = promo_treshold(purchase,promo)
+ when :package then purchase.new_price = promo_package(purchase,promo)
+ end
+ end
+ end
+
+ def promo_get_one_free(purchase,promo)
+ purchase.price.to_d * (purchase.quantity - purchase.quantity / promo[:get_one_free])
+ end
+
+ def promo_treshold(purchase,promo)
+ key, value = promo[:threshold].first
+ qty = purchase.quantity
+ purchase.quantity > key ?
+ purchase.price.to_d * (qty - (qty - key) * value / 100.00)
+ : purchase.price.to_d * qty
+ end
+
+ def promo_package(purchase,promo)
+ key, value = promo[:package].first
+ pack = purchase.quantity / key
+ purchase.price.to_d * ((100 - value) / 100.00) * (key * pack) +
+ (purchase.quantity - (key * pack)) * purchase.price.to_d
+ end
+
+ def total
+ unless (@coupons == [])
+ use_coupon(@coupon_name)
+ end
+ result = @purchases.map{|x| promotion_price(x)}.inject(&:+) - @coupon_discount
+ sprintf("%.2f", result).to_d
+ end
+
+ def use_coupon(name_coupon)
+ if (@coupon_discount != 0) then raise "One coupon per cart available."
+ end
+ sum = @purchases.map{|x| promotion_price(x)}.inject(&:+)
+ @coupon_type = @coupons.find{|x| x.name == name_coupon}.type
+ @coupon_discount = case @coupon_type.first.first
+ when :percent
+ sum * (@coupon_type.first.last / 100.00)
+ when :amount
+ (sum - @coupon_type.first.last.to_d) < 0 ? sum : @coupon_type.first.last.to_d
+ end
+ end
+
+ def use(coupon_name)
+ @coupon_name = coupon_name
+ end
+
+ def invoice
+ total
+ r = Reciept.new(@purchases,@wares,@coupon_discount,@coupon_type,@coupon_name)
+ r.print_it
+ end
+end
+
+class Purchase
+ attr_accessor :name, :quantity, :price, :new_price
+
+ def initialize(name, quantity, price)
+ @name = name
+ @quantity = quantity
+ @price = price
+ @new_price
+ end
+end
+
+class Reciept
+ def initialize(purchases,wares,coupon_discount,coupon_type,coupon_name)
+ @purchases = purchases
+ @wares = wares
+ @coupon_discount = coupon_discount
+ @coupon_type = coupon_type
+ @coupon_name = coupon_name
+ end
+
+ def head_lines
+ lines = "+------------------------------------------------+----------+\n"
+ lines << "| Name qty | price |\n"
+ lines << "+------------------------------------------------+----------+\n"
+ lines
+ end
+
+ def product_lines(name,qty,price)
+ cost = price.to_d * qty
+ lines = "| #{sprintf("%-44s", name)}#{sprintf("%2d", qty)}"
+ lines << " |#{sprintf("%9.2f", cost)} |\n"
+ if (@wares.find{|n| n.name == name}.promotion[0].nil?) then lines << ""
+ else
+ case @wares.find{|n| n.name == name}.promotion[0].first.first
+ when :get_one_free then lines << line_get_one_free(name)
+ when :threshold then lines << line_threshold(name)
+ when :package then lines << line_package(name)
+ end
+ end
+ end
+
+ def line_get_one_free(name)
+ n = @wares.find{|n| n.name == name}.promotion[0][:get_one_free]
+ purchase = @purchases.find{|n| n.name == name}
+ discount = -(purchase.price.to_d * purchase.quantity - purchase.new_price)
+ txt ="(buy#{sprintf("%2d", n-1)}, get 1 free)"
+ "| #{sprintf("%-45s", txt)}|#{sprintf("%9.2f", discount)} |\n"
+ end
+
+ def line_threshold(name)
+ n = @wares.find{|n| n.name == name}.promotion[0][:threshold]
+ key, value = @wares.find{|n| n.name == name}.promotion[0][:threshold].first
+ purchase = @purchases.find{|n| n.name == name}
+ discount = -(purchase.price.to_d * purchase.quantity - purchase.new_price)
+ txt = "(#{sprintf("%2d", value)}% off of every after the#{print_count(key)})"
+ "| #{sprintf("%-45s", txt)}|#{sprintf("%9.2f", discount)} |\n"
+ end
+
+ def print_count(key)
+ case key
+ when 1 then "#{sprintf("%2d",key)}st"
+ when 2 then "#{sprintf("%2d",key)}nd"
+ when 3 then "#{sprintf("%2d",key)}rd"
+ when 4..9 then "#{sprintf("%2d",key)}th"
+ else "#{sprintf("%3d",key)}th"
+ end
+ end
+
+ def line_package(name)
+ n = @wares.find{|n| n.name == name}.promotion[0][:package]
+ key, value = @wares.find{|n| n.name == name}.promotion[0][:package].first
+ purchase = @purchases.find{|n| n.name == name}
+ discount = -(purchase.price.to_d * purchase.quantity - purchase.new_price.to_d)
+ txt ="(get#{sprintf("%3d", value)}% off for every#{sprintf("%2d", key)})"
+ "| #{sprintf("%-45s", txt)}|#{sprintf("%9.2f", discount)} |\n"
+ end
+
+ def total_line
+ result = (@purchases.map{|x| x.new_price}.inject(&:+) - @coupon_discount).to_s
+ lines = "+------------------------------------------------+----------+\n"
+ lines << "| #{sprintf("%-47s", "TOTAL")}|#{sprintf("%9.2f", result)} |\n"
+ lines << "+------------------------------------------------+----------+\n"
+ lines
+ end
+
+ def coupon_line
+ if (@coupon_discount == 0)
+ ""
+ elsif (@coupon_type.first.first == :percent)
+ txt = "#{@coupon_name} -#{sprintf("%3d", @coupon_type.first.last)}% off"
+ "| Coupon #{sprintf("%-40s", txt)}|#{sprintf("%9.2f", -@coupon_discount)} |\n"
+ elsif (@coupon_type.first.first == :amount)
+ txt = "#{@coupon_name} -#{sprintf("%6.2f", @coupon_type.first.last.to_d)} off"
+ "| Coupon #{sprintf("%-40s", txt)}|#{sprintf("%9.2f", -@coupon_discount)} |\n"
+ end
+ end
+
+ def print_it
+ reciep = ''
+ reciep<<head_lines
+ @purchases.each do |n|
+ reciep<<product_lines(n.name, n.quantity, n.price)
+ end
+ reciep<<coupon_line
+ reciep<<total_line
+ reciep
+ end
+end