Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Add FulfillmentChanger #2070

Merged
merged 6 commits into from
Jul 28, 2017

Conversation

mamhoff
Copy link
Contributor

@mamhoff mamhoff commented Jul 7, 2017

(built on top of #1906, without which this does not work either)

This class takes two shipments and moves a certain amount of stuff
between them. This used to be called "splitting" a shipment, but really we
are changing fulfillment for those variants, hence the name.

This will replace the transfer_to_* methods on Spree::Shipment.

It is designed to be quite a bit faster, since it does not move inventory units one by one, but in bulk.

There's a slight change in behaviour: When splitting a shipment such that the new shipment would have more inventory than the original one, that's not possible anymore, because I can't imagine a situation where that would be beneficial.

I had to adjust one spec for the aforementioned change in behaviour. I also had to amend one spec so that the order starts out with only on_hand inventory units.

end

context "if the desired shipment is invalid" do
let(:desired_shipment) { order.shipments.build(stock_location_id: 99999999) }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use underscores(_) as decimal mark and separate every 3 digits with them.

[current_shipment, desired_shipment].map { |s| s.inventory_units.reload }

# If the current shipment now has no inventory units left, we won't need it any longer.
if current_shipment.inventory_units.count == 0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use current_shipment.inventory_units.count.zero? instead of current_shipment.inventory_units.count == 0.

@mamhoff mamhoff force-pushed the introduce-fulfilment-changer branch 3 times, most recently from 2ca373b to 80132ab Compare July 8, 2017 12:37
@mamhoff mamhoff changed the title Work in Progress: Add FulfillmentChanger [RfC] Add FulfillmentChanger Jul 8, 2017
Copy link
Member

@tvdeyen tvdeyen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great stuff. The amount of specs alone is worth it. I like the usage of active model validations to render meaningful error responses to the user 👍

I have some small minor nits.

validate :current_shipment_not_already_shipped
validate :desired_shipment_different_from_current
validates :desired_stock_location, presence: true
validate :enough_stock_at_desired_location, if: :handle_stock_location_counts?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name confuses me. Maybe just handle_stock? or handle_stock_count?

return false if invalid?
desired_shipment.save! if desired_shipment.new_record?

current_on_hand_quantity = [current_shipment.inventory_units.on_hand.size, quantity].min
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we move this query into the if block on line 26?

limit(new_on_hand_quantity).
update_all(shipment_id: desired_shipment.id, state: :on_hand)

current_shipment.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we spare this query if no backordered units are left?

render json: { success: true, message: Spree.t(:shipment_transfer_success) }, status: 201
@desired_shipment ||= Spree::Shipment.find_by!(number: params[:target_shipment_number])

splitter = Spree::FulfilmentChanger.new(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe don't call this var splitter

)

if splitter.run!
render json: { success: true, message: Spree.t(:shipment_transfer_success) }, status: :accepted
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changed status code is maybe worth noting in the change log.

if (response.success) {
window.Spree.advanceOrder();
} else {
alert(response.message);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use show_flash instead of an alert here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using show_flash resulted in quite a lot of testing trouble for some reason: Locked Sqlite databases, hung phantomjs, weird things. Can we move that to a separate PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

current_on_hand_quantity = [current_shipment.inventory_units.on_hand.size, quantity].min
new_on_hand_quantity = [desired_shipment.stock_location.count_on_hand(variant), quantity].min

if handle_stock_location_counts?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it makes sense to put all the stock and shipment changing code into a transaction to avoid potential stock count mismatches?

@@ -0,0 +1,92 @@
module Spree
class FulfilmentChanger
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see some documentation on this class.

@mamhoff mamhoff force-pushed the introduce-fulfilment-changer branch 4 times, most recently from f0b432a to 7adba79 Compare July 11, 2017 12:35
@tvdeyen tvdeyen changed the title [RfC] Add FulfillmentChanger [RFC] Add FulfillmentChanger Jul 12, 2017
@jhawthorn jhawthorn added this to the 2.4.0 milestone Jul 12, 2017
CHANGELOG.md Outdated
@@ -1,5 +1,7 @@
## Solidus 2.3.0 (master, unreleased)

- Change HTTP Status code for Api::ShipmentsController#transfer_to_* to be always 202 Accepted rather than 201 Created or 500.
Speed up changing fulfilment for parts of a shipment [\#2070](https://github.com/solidusio/solidus/pull/2070) ([mamhoff](https://github.com/mamhoff))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These will be in 2.4.0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed

This spec had an implicit expectation of all inventory units
of the first shipment to be setup as on hand, but somehow managed to
get into that state by what I think is whacky magic.

Updating the two items to be on hand at the beginning of the spec
makes sure that the remaining item after the first split is also on hand.
This class takes two shipments and moves a certain amount of stuff
between them. This used to be called "splitting" a shipment, but really we
are changing fulfillment for those variants, hence the name.

The class has the following features:

1. Speed

It only does four or five requests to the database, instead on one
for every inventory unit moved.

2. Refresh rates after changing fulfilment

If we change how things are fulfilled, the costs for
those fulfilments will change. This change reflects that.

3. Make sure that backordered items are moved first

When changing the fulfilment of an item, we want backordered items
to be changed to a new shipment first. There, we have a chance of them
becoming on_hand (shipped faster).

Because "backordered" is earlier in the alphabet than "on_hand", we can
use an `order` clause in the update SQL statement to achieve this.
With the new FulfilmentChanger, we can stop using
Shipment#tranfer_to_*.

This comes with a few minor API changes: The transfer_to_*
endpoints now respond with a 202 Accepted instead of
201 Created. They only return 4xx responses if the shipment
can not be found or the user is unauthorized.

When changing fulfilment for a shipment, the backend now
generates readable errors and will not return a 4xx JSON response
unless something goes badly wrong. This commit changes the
response handling so our JS code knows whether a split has
been successful, and displays a meaningful error message to
the user.
Let's remove this interface from the codebase.
@mamhoff mamhoff force-pushed the introduce-fulfilment-changer branch from 7adba79 to aabfaf5 Compare July 14, 2017 17:17
@jhawthorn jhawthorn merged commit 22cf072 into solidusio:master Jul 28, 2017
@mamhoff
Copy link
Contributor Author

mamhoff commented Jul 28, 2017

Thank you John for updating the changelog. I've had too much on my plate.

@mamhoff mamhoff deleted the introduce-fulfilment-changer branch July 28, 2017 20:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants