Skip to content

Commit

Permalink
Merge pull request #2511 from benjaminwil/initial_calculators_documen…
Browse files Browse the repository at this point in the history
…tation

Initial calculators documentation
  • Loading branch information
gmacdougall authored Apr 18, 2018
2 parents af30800 + 3f9d968 commit 2395380
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 10 deletions.
128 changes: 128 additions & 0 deletions guides/calculators/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Overview

All calculators in Solidus inherit from the `Spree::Calculator` base class. They
calculate and return the value of promotions, taxes, and shipping charges.
Solidus includes built-in calculators for common types of calculations, such as
flat rate shipping, percentage discounts, sales tax, and value-added tax (VAT).

Whenever you create a new shipping method, tax rate, or promotion action, you
also create a new instance of a `Spree::Calculator`.

For example, if you create a new shipping method called "USPS Ground" that
charges a flat rate of $10 USD, you would be creating an instance of the
`Spree::Calculator::Shipping::FlatRate` calculator:

- The new calculator instance would have `calculable_type` of
`Spree::ShippingMethod`. This is because it has a polymorphic relationship
with a shipping method, rather than a tax rate or promotion action. See
[calculables](#calculables) for more information about this relationship.
- The new calculator instance's `preferences` attribute would have a value of
`{:preference=>10, :currency=>"USD"}`.

## Attributes

`Spree::Calculator` objects have the following attributes:

- `type`: The type of calculator being used. For example, if the object
calculates shipping, it could be any available shipping calculator, such as
`Spree::Calculator::Shipping::FlatRate`.
- `calculable_type` and `calculable_id`: The calculable type and its matching
ID. For example, if the object calculates shipping, the calculable would be
from the `Spree::ShippingMethod` model. See [calculables](#calculables) for
more information.
- `preferences`: A hash of the calculator's preferences and the values of those
preferences. Each type of calculator has its own preferences. See
[preferences](#preferences) for more information.

## Calculables

A calculable is an object that needs to be calculated by a `Spree::Calculator`.
In Rails, this is an example of a [polymorphic association][rails-polymorphic]:
all calculators share a common base class, but they can calculate different
types of objects.

In the case of `Spree::Calculator`s, there are three different
`calculable_types`:

- `Spree::ShippingMethod`
- `Spree::TaxRate`
- `Spree::PromotionAction`

A calculable `include`s the `Spree::CalculatedAdjustments` module. This module
requires that each calculable has one calculator object. So, for each calculable
object, an instance of a `Spree::Calculator` should also be created.

For example, a shipping method called "USPS Ground" charges a flat rate of $10
USD. The shipping method is calculable and requires an associated calculator.
So, the rate for each shipment is calculated by the associated
`Spree::Calculator::Shipping::FlatRate` object.

Similarly, each tax rate in your store is calculable. So, instance of the
`Spree::Calculator::DefaultTax` calculator is created and calculates the amount
of tax that should be applied to line items, shipments, or orders.

[rails-polymorphic]: http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

## Preferences

Each `Spree::Calculator` has [static model preferences][model-preferences]. Each
instances of a calculator has a `preferences` attribute that stores a hash of
preferences.

For example, you may have two flat shipping rates configured in your store.
If you look up each of your `Spree::Calculator::Shipping::FlatRate` calculators,
you can see how the static preferences have different settings:

```ruby
Spree::Calculator::Shipping::FlatRate.find(1).preferences
# => {:amount=>8, :currency=>"USD"}

Spree::Calculator::Shipping::FlatRate.find(2).preferences
# => {:amount=>4, :currency=>"EUR"}
```

Because each type of calculator has different functionality, each calculator has its own
set of preferences.

For example, a calculator that uses a percentage for calculations would not have
a `:currency` preference, but any calculator that uses a specific amount of
currency would have a `:currency` preference::

```ruby
Spree::Calculator::Shipping::FlatRate.find(2).preferences
# => {:amount=>4, :currency=>"EUR"}

Spree::Calculators::Shipping::FlatPercentItemTotal.find(3).preferences
# => {:flat_percent=>0.2e1}
```

[model-preferences]: ../preferences/add-model-preferences.md

### Preferred methods

For each preference on a calculator, you can use a `preferred_<preference>`
method to get or set the value of the preference (where `<preference>` is the
name of the preference). For example

```ruby
Spree::Calculator.find(1).update(preferred_amount: 20)
```

<!-- TODO:
Add more detail about preferences. For example: a list of common preference or
example code in which a custom preference is created.
-->

## Custom calculators

If Solidus's built-in calculators are not sufficient for your store, you can
create your own custom calculators. Because promotion, shipping, and tax
calculators have different requirements, we have an article describing each type
of custom calculator you may want to build:

- Custom promotions calculator [Note: work in progress]
- [Custom shipping calculators][custom-shipping-calculators]
- [Custom tax calculators][custom-tax-calculators]

[custom-shipping-calculators]: ../shipments/custom-shipping-calculators.html
[custom-tax-calculators]: ../taxation/custom-tax-calculators.html
106 changes: 103 additions & 3 deletions guides/shipments/custom-shipping-calculators.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,115 @@ Typically, a calculator uses the following order information:
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][flat-rate-source]. For a more complicated
example of what is possible with custom calculators, see the
[`solidus_active_shipping` base calculator][base-calculator-source]. This
Solidus's [stock flat rate calculator][flat-rate-source].

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

[flat-rate-source]: https://github.com/solidusio/solidus/blob/master/core/app/models/spree/calculator/shipping/flat_rate.rb
[base-calculator-source]: https://github.com/solidusio-contrib/solidus_active_shipping/blob/master/app/models/spree/calculator/shipping/active_shipping/base.rb

### 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:

```shell
/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
```

<!-- TODO:
This example code gets developers some of the way there, but could be improved
to be more realistic. See the built-in shipping calculators for more realistic
`compute_package` definitions.
-->

### 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:

```ruby
# /config/application.rb
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][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][shipping-method-filters] article.

[calculator-class]: https://github.com/solidusio/solidus/blob/master/core/app/models/spree/calculator.rb
[shipping-method-filters]: shipping-method-filters.md

## Use additional product information

In addition to providing relevant information about shipping addresses and
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Custom tax calculator
# Custom tax calculators

*Note that Solidus supports value-added tax (VAT) and sales tax out of the
box. You would only need to create a custom tax calculator in extraordinary
Expand All @@ -8,12 +8,9 @@ By default, Solidus uses a simple tax rate calculator. It multiples against an
item price and adjusts for any promotions to determine tax. However, this tax
calculator can be changed if you need to develop a more specialized tax
calculator for your application.

Every `Spree::TaxRate` you create will be connected to a `Spree::Calculator`
instance that calculates the correct tax amount for a given shipment or line
item. In most cases, you should be able to use the
`Spree::Calculator::DefaultTax` calculator. It is suitable for both sales tax
and VAT scenarios.

But in most cases, you should be able to use the `Spree::Calculator::DefaultTax`
calculator. It is suitable for both sales tax and VAT scenarios.

If you need to change the default tax calculation behavior, see the [default tax
calculator specs][default-tax-calculator-spec] or [its
Expand Down

0 comments on commit 2395380

Please sign in to comment.