Adam
Adam

Reputation: 464

How to create custom shipping costs - Spree

What I have is a website which has multiple different user roles. Normally there is just admin and user. I have multiple for different users (retail, trade etc).

What the client is wanting, is that each product has a seperate shipping cost. The actual costs differ for users (so trade shipping might be $8 where retail would be $10). If multiple products are selected, there will be 100% shipping on the first product, and 50% for each additional product. This price differs based on the customers shipping location. It also differs based on what category the item is (eg. They have a seats category and a fish box category. The seats category might be $15 and the fish box might be $17.

The way I'm thinking is that each category has its own shipping category and then the shipping methods are just the locations. This would work fine but the tricky part is how to adjust this based on what user is logged in. The prices I have been given are consistent with the difference between retail shipping and trade shipping (Trade shipping is always 80% of retail shipping).

Basically, what would be great is if I could find out where the shipping calculation is done. That way I can test if the user is a trade customer and change the value to actually be value * .80, else the value is normal.

If any of you could spare the time to help me out with this I would be most appreciative.

Upvotes: 1

Views: 1938

Answers (2)

Adam
Adam

Reputation: 464

Ok this is what I have so far.

In app/models/spree/calculator/shipping/per_item.rb (Spree standard model which I copied from \vendor\bundle\ruby\1.9.1\gems\spree_core-2.4.7\app\models\spree\calculator\shipping\per_item.rb)

require_dependency spree/shipping_calculator

module Spree
  module Calculator::Shipping
    class PerItem < ShippingCalculator
    preference :amount, :decimal, default: 0
    preference :currency, :string, default: ->{ Spree::Config[:currency] }

  def self.description
    Spree.t(:shipping_flat_rate_per_item)
  end

  def compute_package(package)
    if package.contents.sum(&:quantity) > 1
      compute_from_multiple(package.contents.sum(&:quantity))
    else
      compute_from_single(package.contents.sum(&:quantity))
    end
  end

  def compute_from_multiple(quantity)
    (self.preferred_amount / 2) + ((self.preferred_amount * quantity) / 2
  end

  def compute_from_single(quantity)
    self.preferred_amount * quantity
  end

end

This will test the number of items in the cart and if there are more than 1, it will give one item 100% shipping and the rest 50%.

What I now can't figure out is how to access the current user.

eg.

    if current_spree_user.has_spree_role?("admin")
      (self.preferred_amount / 2) + ((self.preferred_amount * quantity) / 2)
    else
      ((self.preferred_amount / 2) + ((self.preferred_amount * quantity) / 2)) * 0.87
    end

This gives an error:

 undefined local variable or method `spree_current_user'

Does anyone know how to check for the current user in a model???

Upvotes: 0

zrl3dx
zrl3dx

Reputation: 7869

Basically, what would be great is if I could find out where the shipping calculation is done.

Calculations are done in Spree::Calculator::Shipping classes (FlatRate, 'PerItem`, etc.) - check the source.

You probably want custom calculator (or decorate existing method), so add file in models/spree/calculator/shipping/my_custom_method.rb:

module Spree
  module Calculator::Shipping
    class MyCustomMethod < ShippingCalculator
      preference :amount, :decimal, default: 0
      preference :something_i_need, :string, default: :foo #and so on, those preferences will be ready to set/change in admin panel

      def self.description
        Spree.t(:my_custom_method) # just a label
      end

      # here is where magic happens
      def compute_package(package)
        package.order.shipment_total
      end

    end
  end
end

As you can see method compute_package is being called on package object which has several useful methods (source) but you can call directly package.order to get all line_items, shipments, order's user and all things you need to calculate valid amount.

If you want to create new method don't forget to register it so it will appear in admin panel so you can change settings - of course you can do that programatically if you want:

initializers/shipping_methods.rb:

 Rails.application.config.spree.calculators.shipping_methods << Spree::Calculator::Shipping::MyCustomMethod

Upvotes: 4

Related Questions