Custom shipping calculators

This article provides context about creating custom shipping calculators in the case that the provided calculators do not meet your store's needs.

Pre-configured calculators

Before developing a custom calculator, you should make sure that the calculator you need doesn't already exist in Solidus or one of its extensions.

Solidus comes with a set of default calculators that account for typical shipping scenarios:

If the calculators that come with Solidus are not enough for your needs, you might want to use an extension like solidus_active_shipping that provides additional API-based rate calculation functionality for common carriers like UPS, USPS, and FedEx. Alternatively, you could develop your own custom calculator.

Custom calculator requirements

A custom calculator should accept a Spree::Stock::Package (a package) and return a cost.

Use the package.order method to access the current order's information, and the package.contents methods to access the current package's contents. As a developer, you should always deal with the package.contents. Otherwise, you may be quoting an entire order when you only want to quote one of many shipments on an order.

Typically, a calculator uses the following order information:

  • The Spree::Address used as the order's shipping address.
  • The Spree::LineItem objects associated with the order.
  • The Spree::Variant product information (such as the weight and dimensions) associated with each line item in the order.

For an example of a typical calculator, we recommend reading the source code for Solidus's stock flat rate calculator .

For a more complicated example of what is possible with custom calculators, see the solidus_active_shipping base calculator . This calculator collects enough information about an order to send to a carrier and get a rate quote back.

Inherit from the Spree::ShippingCalculator class

Your custom shipping calculator should inherit from the existing Spree::ShippingCalculator class. We recommend following the same directory structure that Spree models do, so your new calculator would be created at:

Bash
    
      /app/models/my_store/calculator/shipping/custom_shipping_calculator.rb

    
  

Then, follow the pattern of Spree's built-in calculators and inherit from Spree::ShippingCalculator:

Ruby
    
      module MyStore
  class Calculator::Shipping::CustomShippingCalculator < Spree::ShippingCalculator
  end
end

    
  

Required methods

Your custom shipping calculator requires at least two methods:

  1. A self.description method that provides a name for the custom calculator.
  2. A compute_package(package) that provides the return value for a package being shipped.

For example:

Ruby
    
      module MyStore
  class Calculator::Shipping::CustomShippingCalculator < Spree::ShippingCalculator
    def self.description
      "Custom Shipping Calculator"
    end

    def compute_package(package)
      12.00
    end
  end
end

    
  

Register the new shipping calculator

Once you have created the logic for the new shipping calculator, you need to register it so that administrators can create new shipping methods that use the custom calculator.

For example, you can register it in your /config/application.rb initializer:

/config/application.rb
Ruby
    
      module MyStore
  class Application < Rails::Application
    ...

    initializer 'spree.register.calculators' do |app|
      app.config.spree.calculators.shipping_methods << MyStore::Calculator::Shipping::CustomShippingCalculator
    end
  end
end

    
  

Calculator availability

By default, shipping calculators are always available to be used by shipping methods. This is because the available? method on the base Spree::Calculator class returns true by default.

You may want to make the calculator availability change depending on some aspect of the current order. To do this, you can override the available? method in your custom calculator:

Ruby
    
      module MyStore
  class Calculator::Shipping::CustomShippingCalculator < Spree::ShippingCalculator
    ...

    def available?(order)
      order.currency == "USD"
    end
  end
end

    
  

For more information about availability and filtering shipping methods, see the Shipping method filters article.

Use additional product information

In addition to providing relevant information about shipping addresses and product variants, you can use information about the product itself to inform a calculator's results. A product's tax category or shipping category could be meaningful information for shipping calculations. By default, each package contains only items in the same shipping category.

For example, you might want your calculator to handle your product with a shipping category of "Oversized" differently than it would a product with the "Default" shipping category.

For example, you might want your calculator to handle products with different shipping categories in specific ways: an product with the "Oversized" category should not be treated like a product with the "Default" category.

Feedback

Solidus is an open source platform supported by the community. We encourage everyone using Solius to contribute back to the documentation and the code.

If you’re interested in contributing to the docs, get started with the contributing guidelines. If you see something that needs fixing and can’t do it yourself, please send us an email.