diff --git a/app/controllers/spree/admin/shipping_methods_controller.rb b/app/controllers/spree/admin/shipping_methods_controller.rb index 5fa427772e..84b9619589 100644 --- a/app/controllers/spree/admin/shipping_methods_controller.rb +++ b/app/controllers/spree/admin/shipping_methods_controller.rb @@ -88,7 +88,8 @@ module Spree :require_ship_address, :tag_list, :calculator_type, distributor_ids: [], calculator_attributes: [ - :id, :preferred_currency, :preferred_amount, :preferred_per_kg, :preferred_flat_percent, + :id, :preferred_currency, :preferred_amount, :preferred_per_kg, + :preferred_per_lb, :preferred_flat_percent, :preferred_first_item, :preferred_additional_item, :preferred_max_items, :preferred_minimal_amount, :preferred_normal_amount, :preferred_discount_amount ] diff --git a/app/models/calculator/weight_lb.rb b/app/models/calculator/weight_lb.rb new file mode 100644 index 0000000000..2af8af9778 --- /dev/null +++ b/app/models/calculator/weight_lb.rb @@ -0,0 +1,75 @@ +require 'spree/localized_number' + +module Calculator + class WeightLb < Spree::Calculator + extend Spree::LocalizedNumber + preference :per_lb, :decimal, default: 0.0 + localize_number :preferred_per_lb + + def self.description + I18n.t('spree.weight_lb') + end + + def compute(object) + line_items = line_items_for object + (total_weight(line_items) * preferred_per_lb).round(2) + end + + private + + def total_weight(line_items) + line_items.sum do |line_item| + line_item_weight(line_item) + end + end + + def line_item_weight(line_item) + if final_weight_volume_present?(line_item) + weight_per_final_weight_volume(line_item) + else + weight_per_variant(line_item) * line_item.quantity + end + end + + def weight_per_variant(line_item) + if variant_unit(line_item) == 'weight' + # The calculator price is per_lb so we need to convert unit_value to lb + convert_g_to_lb(line_item.variant.andand.unit_value) + else + line_item.variant.andand.weight || 0 + end + end + + def weight_per_final_weight_volume(line_item) + if variant_unit(line_item) == 'weight' + # The calculator price is per_lb so we need to convert final_weight_volume to lb + convert_g_to_lb(line_item.final_weight_volume) + else + weight_per_variant(line_item) * quantity_implied_in_final_weight_volume(line_item) + end + end + + # Example: 2 (line_item.quantity) wine glasses of 125mL (line_item.variant.unit_value) + # Customer ends up getting 350mL (line_item.final_weight_volume) of wine + # that represent 2.8 (quantity_implied_in_final_weight_volume) glasses of wine + def quantity_implied_in_final_weight_volume(line_item) + return line_item.quantity if line_item.variant.unit_value.to_f.zero? + + (1.0 * line_item.final_weight_volume / line_item.variant.unit_value).round(3) + end + + def final_weight_volume_present?(line_item) + line_item.respond_to?(:final_weight_volume) && line_item.final_weight_volume.present? + end + + def variant_unit(line_item) + line_item.variant.product.andand.variant_unit + end + + def convert_g_to_lb(value) + return 0 unless value + + value / 453.6 + end + end +end diff --git a/config/application.rb b/config/application.rb index 3d061b97be..83d92c60ba 100644 --- a/config/application.rb +++ b/config/application.rb @@ -54,7 +54,8 @@ module Openfoodnetwork Calculator::FlexiRate, Calculator::PerItem, Calculator::PriceSack, - Calculator::Weight + Calculator::Weight, + Calculator::WeightLb ] app.config.spree.calculators.add_class('enterprise_fees') @@ -64,7 +65,8 @@ module Openfoodnetwork Calculator::FlexiRate, Calculator::PerItem, Calculator::PriceSack, - Calculator::Weight + Calculator::Weight, + Calculator::WeightLb ] app.config.spree.calculators.add_class('payment_methods') diff --git a/config/locales/en.yml b/config/locales/en.yml index 6cbd131c09..6ff97734eb 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3127,6 +3127,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using inventory: Inventory zipcode: Postcode weight: Weight (per kg) + weight_lb: Weight (per lb) error_user_destroy_with_orders: "Users with completed orders may not be deleted" cannot_create_payment_without_payment_methods: "You cannot create a payment for an order without any payment methods defined." please_define_payment_methods: "Please define some payment methods first." diff --git a/spec/controllers/spree/admin/shipping_methods_controller_spec.rb b/spec/controllers/spree/admin/shipping_methods_controller_spec.rb index f209e87a81..247e5c66e2 100644 --- a/spec/controllers/spree/admin/shipping_methods_controller_spec.rb +++ b/spec/controllers/spree/admin/shipping_methods_controller_spec.rb @@ -38,6 +38,15 @@ describe Spree::Admin::ShippingMethodsController, type: :controller do expect(shipping_method.reload.calculator.preferred_per_kg).to eq 10 end + it "updates preferred_per_lb of a Weight (Lb) calculator" do + shipping_method.calculator = create(:weight_lb_calculator, calculable: shipping_method) + params[:shipping_method][:calculator_attributes][:preferred_per_lb] = 10 + + spree_post :update, params + + expect(shipping_method.reload.calculator.preferred_per_lb).to eq 10 + end + it "updates preferred_flat_percent of a FlatPercentPerItem calculator" do shipping_method.calculator = Calculator::FlatPercentPerItem.new(preferred_flat_percent: 20, diff --git a/spec/factories/calculator_factory.rb b/spec/factories/calculator_factory.rb index 1268a8ccb7..c9ee25a34e 100644 --- a/spec/factories/calculator_factory.rb +++ b/spec/factories/calculator_factory.rb @@ -16,4 +16,9 @@ FactoryBot.define do after(:build) { |c| c.set_preference(:per_kg, 0.5) } after(:create) { |c| c.set_preference(:per_kg, 0.5); c.save! } end + + factory :weight_lb_calculator, class: Calculator::WeightLb do + after(:build) { |c| c.set_preference(:per_lb, 0.5) } + after(:create) { |c| c.set_preference(:per_lb, 0.5); c.save! } + end end