Shipping rate model #step7

Shipping rates nowadays

In Spree 1.3 a ShippingRate object is built for each shipping method available to the current order. This is used to calculate the price the customer will pay for that shipping method. See https://github.com/spree/spree/blob/1-3-stable/core/app/models/spree/order.rb#L418 and https://github.com/spree/spree/blob/1-3-stable/core/app/models/spree/order.rb#L423

In OFN instead, we associate shipping methods to an enterprise through the distributor_shipping_methods table and these are the ones the customer can choose from when placing the order. See https://github.com/openfoodfoundation/openfoodnetwork/blob/master/app/helpers/enterprises_helper.rb#L11

We don’t see where we calculate the shipping rate in OFN. Is it somewhere in the angular code @oeoeaio @RohanM?

Shipping rates in Spree 2.0

Spree 2.0 introduces a whole new approach to calculate the shipping rates of an order. As you already know orders have many shipments. Each of these have a shipping rate that stems from the shipping category of the variants.

This relates to the upgrade on the fact that we still use the association order.shipping_method, which one way or another will have to become order.shipments.

Would be great to have an introduction on how exactly we overrode the Spree checkout. In the original version Spree performs an update call for each step in the checkout (address, delivery, payment, confirm, etc) while in OFN we only perform one update call.

Why is this? Can’t we still follow the “Spree way” while overriding the UI?

In Spree 2.0 those multiple updates seem to be required to correctly perform the checkout.

Hey @enricostn,

Our version of Spree does allow for multiple shipments on each order, and each of these has its own ShippingMethod, so in a lot of ways order.shipping_method= behaves pretty much like an instance variable that is used to set the shipping method on the first shipment. order.shipping_method probably shouldn’t be being used at all because it doesn’t mean anything (the shipping method on the first shipment can be changed, and this will not be reflected by order.shipping_method). There is a convenience method Order.shipment which just pulls up the most recent shipment. We have definitely abused order.shipping_method a lot, but when it comes down to it, I think we can pretty much use order.shipment.shipping_method (or equivalent) as a drop in replacement for order.shipping_method. What do you think?

I didn’t even know that Spree had a ShippingRate! It looks like it is just used for formatting of the rates in the checkout right? After having a look around in the OFN, I found that the rate for each shipping method for the current order is calculated in the shipping method serializer: app/serializers/api/shipping_method_serializer.rb. This is made available to the angular on the checkout page, and is used to dynamically update the cost of delivery for the order based on the user’s selection. NOTE: This shipping rate is not submitted with the request, the shipping amount is recalculated on the server when the customer submits the order from the checkout.

My understanding of the way this happens is that the ‘cost’ for shipping is added to an order is via adjustments. These use calculators (defined by the shipping method) to determine the amount that should be charged for shipping (in the same way that the rates displayed to the customer are calculated). I’m not sure whether each shipment adds a new shipping adjustment to the order, or whether the total is just aggregated as one bulk shipping adjustment. I suspect it is the former, but I am not certain.

I’m not 100% clear on what the Spree 2.0 approach to this is, but to the best of my knowledge, we have not made any major changes to the existing system for calculating and applying the cost of shipping, so transitioning to the new approach will hopefully not be too crazy.

Does that help?

We use order.next to ‘step’ the order through the checkout in the CheckoutContoller (see CheckoutContoller#advance_order_state). Is order.next still used in Spree 2.0? Does order.update do something that order.next doesn’t? If we are really having problems, it might be worth looking at overriding the state machine so reflect the reality of our update process (ie. all at once).

Thanks @oeoeaio this definitely helps a lot. Then, we should ideally be able to remove all traces of order.shipping_method and move towards using order.shipments.

I think we can pretty much use order.shipment.shipping_method (or equivalent) as a drop in replacement for order.shipping_method. What do you think?

IMO the way to go is to use order.shipments.shipping_method regardless of the number of shipments the order might have, one or many. If we decide to allow only one shipment (It doesn’t probably make sense) we could do so from the view layer.

As for adjustments we haven’t checked them but with all this info we’ll investigate OFN’s implementation furhter and look for the differences; it must be possible to just extend Spree’s mechanism in 2.0 with our Enterprise.

Is order.next still used in Spree 2.0?

Yes, it is. Spree does also call order.next on each update request executing a transition of the state machine each time. We suspect that the fact of OFN doing all the checkout at once has some performance implications and brings some complexity besides complicating the upgrades.

We’re trying to understand why we did these overrides on the first place to see if we can use most of Spree 2.0 checkout flow. The less code we need to maintain the better for future upgrades.

I opened a little PR top stop depending on Order#shipment https://github.com/openfoodfoundation/openfoodnetwork/pull/1650

The “heavy” work though is happening in https://github.com/openfoodfoundation/openfoodnetwork/pull/1651

AFAIK, the only reason we needed to override the checkout flow was that we wanted a single page checkout, rather than Spree’s default 4/5 page checkout. Single page is a much better UX. We did this by making a single Ajax request to CheckoutController#update, which uses order.next to process the order. That was the most obvious way to do what we wanted at the time. Spree does allow the state machine to be overridden, but you’re right to question the maintainability of this option…

Does that answer your ‘why’ question?

1 Like

Totally @oeoeaio. This provides us a lot of information on how to tackle the upgrade regarding the checkout.

The upgrade to 2.0 is long finished, this thread is now out dated. I will archive it.