From c2898ba38930d2f851b99d1ff0e34e94863f69f9 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Mon, 15 Jun 2020 20:49:06 +0100 Subject: [PATCH] Bring calculators from spree_core --- app/models/spree/calculator/default_tax.rb | 59 +++++++++++++++++++ .../calculator/flat_percent_item_total.rb | 18 ++++++ app/models/spree/calculator/flat_rate.rb | 16 +++++ app/models/spree/calculator/flexi_rate.rb | 33 +++++++++++ app/models/spree/calculator/per_item.rb | 41 +++++++++++++ app/models/spree/calculator/price_sack.rb | 31 ++++++++++ 6 files changed, 198 insertions(+) create mode 100644 app/models/spree/calculator/default_tax.rb create mode 100644 app/models/spree/calculator/flat_percent_item_total.rb create mode 100644 app/models/spree/calculator/flat_rate.rb create mode 100644 app/models/spree/calculator/flexi_rate.rb create mode 100644 app/models/spree/calculator/per_item.rb create mode 100644 app/models/spree/calculator/price_sack.rb diff --git a/app/models/spree/calculator/default_tax.rb b/app/models/spree/calculator/default_tax.rb new file mode 100644 index 0000000000..dc054f416c --- /dev/null +++ b/app/models/spree/calculator/default_tax.rb @@ -0,0 +1,59 @@ +require_dependency 'spree/calculator' + +module Spree + class Calculator::DefaultTax < Calculator + def self.description + Spree.t(:default_tax) + end + + def compute(computable) + case computable + when Spree::Order + compute_order(computable) + when Spree::LineItem + compute_line_item(computable) + end + end + + + private + + def rate + self.calculable + end + + def compute_order(order) + matched_line_items = order.line_items.select do |line_item| + line_item.tax_category == rate.tax_category + end + + line_items_total = matched_line_items.sum(&:total) + if rate.included_in_price + deduced_total_by_rate(line_items_total, rate) + else + round_to_two_places(line_items_total * rate.amount) + end + end + + def compute_line_item(line_item) + if line_item.tax_category == rate.tax_category + if rate.included_in_price + deduced_total_by_rate(line_item.total, rate) + else + round_to_two_places(line_item.total * rate.amount) + end + else + 0 + end + end + + def round_to_two_places(amount) + BigDecimal.new(amount.to_s).round(2, BigDecimal::ROUND_HALF_UP) + end + + def deduced_total_by_rate(total, rate) + round_to_two_places(total - ( total / (1 + rate.amount) ) ) + end + + end +end diff --git a/app/models/spree/calculator/flat_percent_item_total.rb b/app/models/spree/calculator/flat_percent_item_total.rb new file mode 100644 index 0000000000..145e3555bc --- /dev/null +++ b/app/models/spree/calculator/flat_percent_item_total.rb @@ -0,0 +1,18 @@ +require_dependency 'spree/calculator' + +module Spree + class Calculator::FlatPercentItemTotal < Calculator + preference :flat_percent, :decimal, default: 0 + + def self.description + Spree.t(:flat_percent) + end + + def compute(object) + return unless object.present? and object.respond_to?(:item_total) + item_total = object.item_total + value = item_total * BigDecimal(self.preferred_flat_percent.to_s) / 100.0 + (value * 100).round.to_f / 100 + end + end +end diff --git a/app/models/spree/calculator/flat_rate.rb b/app/models/spree/calculator/flat_rate.rb new file mode 100644 index 0000000000..db4413ba86 --- /dev/null +++ b/app/models/spree/calculator/flat_rate.rb @@ -0,0 +1,16 @@ +require_dependency 'spree/calculator' + +module Spree + class Calculator::FlatRate < Calculator + preference :amount, :decimal, default: 0 + preference :currency, :string, default: Spree::Config[:currency] + + def self.description + Spree.t(:flat_rate_per_order) + end + + def compute(object=nil) + self.preferred_amount + end + end +end diff --git a/app/models/spree/calculator/flexi_rate.rb b/app/models/spree/calculator/flexi_rate.rb new file mode 100644 index 0000000000..b718efea50 --- /dev/null +++ b/app/models/spree/calculator/flexi_rate.rb @@ -0,0 +1,33 @@ +require_dependency 'spree/calculator' + +module Spree + class Calculator::FlexiRate < Calculator + preference :first_item, :decimal, default: 0.0 + preference :additional_item, :decimal, default: 0.0 + preference :max_items, :integer, default: 0 + preference :currency, :string, default: Spree::Config[:currency] + + def self.description + Spree.t(:flexible_rate) + end + + def self.available?(object) + true + end + + def compute(object) + sum = 0 + max = self.preferred_max_items.to_i + items_count = object.line_items.map(&:quantity).sum + items_count.times do |i| + if i == 0 + sum += self.preferred_first_item.to_f + elsif ((max > 0) && (i <= (max - 1))) || (max == 0) + sum += self.preferred_additional_item.to_f + end + end + + sum + end + end +end diff --git a/app/models/spree/calculator/per_item.rb b/app/models/spree/calculator/per_item.rb new file mode 100644 index 0000000000..eec5df7655 --- /dev/null +++ b/app/models/spree/calculator/per_item.rb @@ -0,0 +1,41 @@ +require_dependency 'spree/calculator' + +module Spree + class Calculator::PerItem < Calculator + preference :amount, :decimal, default: 0 + preference :currency, :string, default: Spree::Config[:currency] + + def self.description + Spree.t(:flat_rate_per_item) + end + + def compute(object=nil) + return 0 if object.nil? + self.preferred_amount * object.line_items.reduce(0) do |sum, value| + if matching_products.blank? || matching_products.include?(value.product) + value_to_add = value.quantity + else + value_to_add = 0 + end + sum + value_to_add + end + end + + # Returns all products that match this calculator, but only if the calculator + # is attached to a promotion. If attached to a ShippingMethod, nil is returned. + def matching_products + # Regression check for #1596 + # Calculator::PerItem can be used in two cases. + # The first is in a typical promotion, providing a discount per item of a particular item + # The second is a ShippingMethod, where it applies to an entire order + # + # Shipping methods do not have promotions attached, but promotions do + # Therefore we must check for promotions + if self.calculable.respond_to?(:promotion) + self.calculable.promotion.rules.map do |rule| + rule.respond_to?(:products) ? rule.products : [] + end.flatten + end + end + end +end diff --git a/app/models/spree/calculator/price_sack.rb b/app/models/spree/calculator/price_sack.rb new file mode 100644 index 0000000000..1130410468 --- /dev/null +++ b/app/models/spree/calculator/price_sack.rb @@ -0,0 +1,31 @@ +require_dependency 'spree/calculator' +# For #to_d method on Ruby 1.8 +require 'bigdecimal/util' + +module Spree + class Calculator::PriceSack < Calculator + preference :minimal_amount, :decimal, default: 0 + preference :normal_amount, :decimal, default: 0 + preference :discount_amount, :decimal, default: 0 + preference :currency, :string, default: Spree::Config[:currency] + + def self.description + Spree.t(:price_sack) + end + + # as object we always get line items, as calculable we have Coupon, ShippingMethod + def compute(object) + if object.is_a?(Array) + base = object.map { |o| o.respond_to?(:amount) ? o.amount : BigDecimal(o.to_s) }.sum + else + base = object.respond_to?(:amount) ? object.amount : BigDecimal(object.to_s) + end + + if base < self.preferred_minimal_amount + self.preferred_normal_amount + else + self.preferred_discount_amount + end + end + end +end