Skip to content

Commit

Permalink
Implement feedback from shipments documentation PR
Browse files Browse the repository at this point in the history
Thanks to @swcraig and @mamhoff for additional feedback on my shipments
documentation PR. For the most part, these edits address the need to
differentiate between "packages" and "shipments", especially when
talking about Solidus's split shipment functionality.
  • Loading branch information
benjaminwil committed Dec 8, 2017
1 parent f73558b commit bf9f9bd
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 93 deletions.
29 changes: 20 additions & 9 deletions guides/shipments/custom-shipping-calculators.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Custom shipping calculators
# 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.
Expand All @@ -19,19 +19,30 @@ 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`][solidus-active-shipping] that provides additional
[`solidus_active_shipping`][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.
FedEx. Alternatively, you could develop your own custom calculator.

[solidus-active-shipping]: solidus-active-shipping-extension.md

## Custom calculator requirements

Your calculator should accept an array of `LineItem` objects that are associated
with an order, and then return a cost.
A custom calculator should accept a `Spree::Stock::Package` (a `package`) and
return a cost.

The calculator can look at any reachable data related to the order, but
it typically requires the following information:
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.

<!-- TODO:
So far, the shipments documentation doesn't go into packages in any details.
These references by themselves are not enough. What is the purpose of a
"package" that is distinctive from a "shipment".
-->

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.
Expand All @@ -54,7 +65,7 @@ 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.
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
Expand All @@ -63,6 +74,6 @@ shipping category of "Oversized" differently than it would a product with the
<!-- TODO:
Add an example code block or a link to some Solidus code that shows a
calculator taking advantage of shipping categories and/or tax categories to
produce a specific result.
produce a specific result.
-->

7 changes: 4 additions & 3 deletions guides/shipments/overview-of-shipments.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ supported Solidus Extensions](https://extensions.solidus.io).

This article provides a summary of shipping concepts. If you are interested in
reading about example Solidus shipment setups see
[Shipment setup examples](shipment-setup-examples.html.markdown).
[Shipment setup examples](shipment-setup-examples.md).

<!-- TODO:
Add section that summarizes what Spree::Objects are created related to
shipments and explains what their function is in the larger checkout process.
-->

[solidus-active-shipping]: solidus-active-shipping-extension.html.markdown
[solidus-active-shipping]: solidus-active-shipping-extension.md
[solidus-shipstation]: https://github.com/boomerdigital/solidus_shipstation

## Shipment attributes

Expand Down Expand Up @@ -145,7 +146,7 @@ extension like [`solidus_active_shipping`][solidus-active-shipping]. Or, if you
have other complex needs, you can create a [custom shipping
calculators][custom-shipping-calculators] for more information.

[custom-shipping-calculators]: custom-shipping-calculators.html.markdown
[custom-shipping-calculators]: custom-shipping-calculators.md

### Shipping rates

Expand Down
2 changes: 1 addition & 1 deletion guides/shipments/shipping-method-filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class Calculator::Usps::FirstClassMailParcels < Calculator::Usps::Base
"USPS Bogus First Class International"
end

def available?(order)
def available?(package)
multiplier = 1.3
weight = order.line_items.inject(0) do |weight, line_item|
weight + (line_item.variant.weight ? (line_item.quantity * line_item.variant.weight * multiplier) : 0)
Expand Down
63 changes: 3 additions & 60 deletions guides/shipments/solidus-active-shipping-extension.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,67 +142,10 @@ The string returned by the `description` method must match the name of the USPS
delivery service _exactly_. To determine the exact spelling, you should examine
what the USPS API returns.

The following code block returns the description and the rate of USPS delivery
services:

```ruby
class Calculator::ActiveShipping < Calculator
def compute(line_items)
#....
rates = retrieve_rates(origin, destination, packages(order))
# the key of this hash is the name you need to match
# raise rates.inspect

return nil unless rates
rate = rates[self.description].to_f +
(Spree::ActiveShipping::Config[:handling_fee].to_f || 0.0)
return nil unless rate
# divide by 100 since active_shipping rates are expressed as cents

return rate/100.0
end

def retrieve_rates(origin, destination, packages)
#....
# carrier is an instance of ActiveMerchant::Shipping::USPS
response = carrier.find_rates(origin, destination, packages)
# turn this beastly array into a nice little hash
h = Hash[*response.rates.collect { |rate| [rate.service_name, rate.price]
}.flatten]
#....
end
end
```

<!-- TODO:
Give more context to the code block above. Would you/how would you
practically use this in your app? -->

This returns an array of services with their corresponding prices, which the
`retrieve_rates` method converts into a hash. Below is what would get returned
for an order with an international destination:

```ruby
{
"USPS Bogus First Class International"=>9999,
"USPS Priority Mail International Flat Rate Envelope"=>1345,
"USPS First-Class Mail International Large Envelope"=>376,
"USPS USPS GXG Envelopes"=>4295,
"USPS Express Mail International Flat Rate Envelope"=>2895,
"USPS First-Class Mail International Package"=>396,
"USPS Priority Mail International Medium Flat Rate Box"=>4345,
"USPS Priority Mail International"=>2800,
"USPS Priority Mail International Large Flat Rate Box"=>5595,
"USPS Global Express Guaranteed Non-Document Non-Rectangular"=>4295,
"USPS Global Express Guaranteed Non-Document Rectangular"=>4295,
"USPS Global Express Guaranteed (GXG)"=>4295,
"USPS Express Mail International"=>2895,
"USPS Priority Mail International Small Flat Rate Box"=>1345
}
```

From all of the viable shipping services in this hash, the `compute` method
selects the one that matches the description of the calculator.
Expand this sub-article to make a practical example of a developer matching
the delivery service with its provided name.
-->

### Register the new calculator

Expand Down
36 changes: 21 additions & 15 deletions guides/shipments/split-shipments.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,28 @@ business logic for how stock should be packaged. If your store requires a
specialized flow for handling split shipments, the simple coordinator should
provide a good starting point for customizations.

<!-- TODO:
This article doesn't acknowledge the `Spree::Stock::Package` model, which is
what is being referred to when we talk about splitting shipments by
"packages".
-->

## Creating proposed shipments

An order's shipments are determined by
[`Spree::Stock::SimpleCoordinator`][simple-coordinator] while the
`Spree::Order`'s' state is set to `delivery`. This occurs before the customer
has completed their order at checkout.

The simple coordinator deletes any existing shipments for an order and then
creates all of the shipments currently available for this order.
The `SimpleCoordinator` takes an order and builds as many shipments as are
necessary to fulfill it.

[simple-coordinator]: https://github.com/solidusio/solidus/blob/master/core/app/models/spree/stock/simple_coordinator.rb

The simple coordinator performs a number of tasks in order to create shipment
proposals:

1. The coordinator calculates the availability of the ordered items.
1. The coordinator checks the availability of the ordered items.
2. Inventory is allocated from available stock to the current order.
3. It splits the order into logical packages based on stock locations and
inventory at those locations.
Expand All @@ -44,22 +50,23 @@ first splitter in the sequence takes the array of packages from the order,
splits the order into packages according to its rules, then passes the packages
on to the next splitter in the sequence.

For each generated shipment, a shipping method can be assigned.

### Default splitters

Solidus comes with thre built-in splitters:
Solidus comes with three built-in splitters:

- [Backordered splitter][backordered-splitter]: Splits an order based on the
amount of inventory on hand at each stock location.
- [Shipping category splitter][shipping-category-splitter]: Splits an order into
packages based on a product's shipping categories. This means that each
shipments based on a product's shipping categories. This means that each
package only has items that belongs to the same shipping category.
- [Weight splitter][weight-splitter]: Splits an order into packages based on a
weight
threshold. This means that each package has a maximum weight: if a new item
is added to the order and it causes a package to go over the weight threshold,
a new package is created. All packages need to weigh less than the threshold.
You can set the weight threshold by changing
the `Spree::Stock::Splitter::Weight.threshold` value in an initializer. (It
- [Weight splitter][weight-splitter]: Splits an order into shipments based on a
weight threshold. This means that each shipment has a maximum weight: if a new
item is added to the order and it causes a package to go over the weight
threshold, a new shipment is created. Each shipment needs to weigh less than
the threshold. You can set the weight threshold by changing the
`Spree::Stock::Splitter::Weight.threshold` value in an initializer. (It
defaults to `150`.)

[backordered-splitter]: https://github.com/solidusio/solidus/blob/master/core/app/models/spree/stock/splitter/backordered.rb
Expand All @@ -80,8 +87,7 @@ that Solidus uses. To do this, add the following to your
`config/initializers/spree.rb` file:

```ruby
Rails.application.config.spree.stock_splitters <<
Spree::Stock::Splitter::CustomSplitter
Rails.application.config.spree.stock_splitters << Spree::Stock::Splitter::CustomSplitter
```

You can also override the splitters used in Solidus, rearrange them, or
Expand All @@ -95,7 +101,7 @@ Rails.application.config.spree.stock_splitters = [
```

If you want to add different splitters for each of your `Spree::StockLocation`s,
you can decorate the `Spree::Stock::Coordinator` class and override the
you can decorate the `Spree::Stock::SimpleCoordinator` class and override the
`splitters` method.

#### Turn off split shipments
Expand Down
8 changes: 3 additions & 5 deletions guides/shipments/user-interface-for-shipments.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,10 @@ shipments can be associated with a single order.
Shipments do not have their own dedicated part of the admin UI. Shipments are
viewable and editable from within an order.

In the admin dashboard. the administrator can update each shipment's shipping
In the admin dashboard, the administrator can update each shipment's shipping
cost and add tracking codes when editing an order. They can also perform other
tasks, such as splitting a single shipment into multiple shipments.

In the Solidus demo store, edit any order (from the `/admin/orders` URL) and go
to the "Shipments" tab to see the default interface.
tasks, such as splitting a single shipment into multiple shipments. They can do
all of this from the "Shipments" tab of any order.

### Shipping instructions

Expand Down

0 comments on commit bf9f9bd

Please sign in to comment.