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

Make code work with one shipment per order (even if the DB still supports Spree's multi shipments per order model) #6433

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
44 changes: 0 additions & 44 deletions app/models/concerns/order_shipment.rb

This file was deleted.

76 changes: 50 additions & 26 deletions app/models/spree/order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@
require 'open_food_network/enterprise_fee_calculator'
require 'open_food_network/feature_toggle'
require 'open_food_network/tag_rule_applicator'
require 'concerns/order_shipment'

module Spree
class Order < ActiveRecord::Base
prepend OrderShipment
include Checkout

checkout_flow do
Expand Down Expand Up @@ -226,7 +224,7 @@ def item_count
end

def backordered?
shipments.any?(&:backordered?)
shipment&.backordered? || false
end

# Returns the relevant zone (if any) to be used for taxation purposes.
Expand Down Expand Up @@ -438,10 +436,8 @@ def finalize!

# update payment and shipment(s) states, and save
updater.update_payment_state
shipments.each do |shipment|
shipment.update!(self)
shipment.finalize!
end
shipment&.update!(self)
shipment&.finalize!

updater.update_shipment_state
updater.before_save_hook
Expand Down Expand Up @@ -547,20 +543,52 @@ def state_changed(name)
)
end

# The Spree 2 db model we use supports multiple shipments per order,
# in OFN we have only one shipment per order.
# A shipment is associated to a shipping_method through a selected shipping_rate.
def shipment
return if shipments.blank?

shipments.first
end

# The shipping method of the shipment in the order
def shipping_method
return if shipment.blank?

shipment.shipping_method
end

# Finds the shipment's shipping_rate for the given shipping_method_id and selects that shipping_rate.
Copy link

Choose a reason for hiding this comment

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

Metrics/LineLength: Line is too long. [105/100]

# If the selection is successful, it persists it in the database by saving the shipment.
# If it fails, it does not clear the current shipping_method selection.
#
# @return [ShippingMethod] the selected shipping method, or nil if the given shipping_method_id is
Copy link

Choose a reason for hiding this comment

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

Metrics/LineLength: Line is too long. [102/100]

# empty or if it cannot find the given shipping_method_id in the order
def select_shipping_method(shipping_method_id)
return if shipping_method_id.blank? || shipment.blank?

shipment = shipments.first

shipping_rate = shipment.shipping_rates.find_by(shipping_method_id: shipping_method_id)
return unless shipping_rate

shipment.selected_shipping_rate_id = shipping_rate.id
shipment.shipping_method
end

def shipped?
%w(partial shipped).include?(shipment_state)
end

# Does this order have shipments that can be shipped?
def ready_to_ship?
shipments.any?(&:can_ship?)
shipment&.can_ship? || false
end

# Ship all pending orders
def ship
shipments.each do |s|
s.ship if s.can_ship?
end
shipment.ship if ready_to_ship?
end

def line_item_variants
Expand All @@ -582,12 +610,10 @@ def create_proposed_shipments
adjustments.shipping.delete_all
shipments.destroy_all

packages = OrderManagement::Stock::Coordinator.new(self).packages
packages.each do |package|
shipments << package.to_shipment
end
package = OrderManagement::Stock::Coordinator.new(self).package
shipments << package.to_shipment

shipments
shipments #
end

# Clean shipments and make order back to address state
Expand All @@ -596,7 +622,7 @@ def create_proposed_shipments
# to delivery again so that proper updated shipments are created.
# e.g. customer goes back from payment step and changes order items
def ensure_updated_shipments
return unless shipments.any?
return if shipment.blank?

shipments.destroy_all
update_columns(
Expand All @@ -606,7 +632,7 @@ def ensure_updated_shipments
end

def refresh_shipment_rates
shipments.map(&:refresh_rates)
shipment.refresh_rates
end

# Check that line_items in the current order are available from a newly selected distribution
Expand All @@ -625,12 +651,10 @@ def disallow_guest_order

# After changing line items of a completed order
def update_shipping_fees!
shipments.each do |shipment|
next if shipment.shipped?
return if shipment.blank? || shipment.shipped?

update_adjustment! shipment.adjustment if shipment.adjustment
save_or_rescue_shipment(shipment)
end
update_adjustment!(shipment.adjustment) if shipment.adjustment
save_or_rescue_shipment(shipment)
end

def save_or_rescue_shipment(shipment)
Expand Down Expand Up @@ -788,20 +812,20 @@ def has_available_shipment
end

def ensure_available_shipping_rates
return unless shipments.empty? || shipments.any? { |shipment| shipment.shipping_rates.blank? }
return unless shipment.blank? || shipment.shipping_rates.blank?

errors.add(:base, Spree.t(:items_cannot_be_shipped)) && (return false)
end

def after_cancel
shipments.each(&:cancel!)
shipment&.cancel!

OrderMailer.cancel_email(id).deliver_later
self.payment_state = 'credit_owed' unless shipped?
end

def after_resume
shipments.each(&:resume!)
shipment&.resume!
end

def use_billing?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,33 @@ def initialize(order)
@order = order
end

def packages
packages = build_packages
packages = prioritize_packages(packages)
estimate_packages(packages)
def package
package = build_package
package = prioritize_package(package)
estimate_package(package)
end

# Build package with default stock location
# No need to check items are in the stock location,
# there is only one stock location so the items will be on that stock location.
#
# Returns an array with a single Package for the default stock location
def build_packages
# Returns a single Package for the default stock location
def build_package
packer = build_packer(order)
[packer.package]
packer.package
end

private

def prioritize_packages(packages)
prioritizer = OrderManagement::Stock::Prioritizer.new(order, packages)
prioritizer.prioritized_packages
def prioritize_package(package)
Copy link

Choose a reason for hiding this comment

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

Lint/UnusedMethodArgument: Unused method argument - package. If it's necessary, use _ or _package as an argument name to indicate that it won't be used. You can also write as prioritize_package(*) if you want the method to accept any arguments but don't care about them.

prioritizer = OrderManagement::Stock::Prioritizer.new(order, package)
prioritizer.prioritized_package
end

def estimate_packages(packages)
def estimate_package(package)
estimator = OrderManagement::Stock::Estimator.new(order)
packages.each do |package|
package.shipping_rates = estimator.shipping_rates(package)
end
packages
package.shipping_rates = estimator.shipping_rates(package)
package
end

def build_packer(order)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,37 @@
module OrderManagement
module Stock
class Prioritizer
attr_reader :packages, :order
attr_reader :package, :order

def initialize(order, packages, adjuster_class = OrderManagement::Stock::Adjuster)
def initialize(order, package, adjuster_class = OrderManagement::Stock::Adjuster)
@order = order
@packages = packages
@package = package
@adjuster_class = adjuster_class
end

def prioritized_packages
adjust_packages
prune_packages
packages
def prioritized_package
adjust_package
return if package.blank?

package
end

private

def adjust_packages
def adjust_package
order.line_items.each do |line_item|
adjuster = @adjuster_class.new(line_item.variant, line_item.quantity, :on_hand)

visit_packages(adjuster)
visit_package(adjuster)

adjuster.status = :backordered
visit_packages(adjuster)
end
end

def visit_packages(adjuster)
packages.each do |package|
item = package.find_item adjuster.variant, adjuster.status
adjuster.adjust(item) if item
visit_package(adjuster)
end
end

def prune_packages
packages.reject!(&:empty?)
def visit_package(adjuster)
item = package.find_item(adjuster.variant, adjuster.status)
adjuster.adjust(item) if item
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ module Stock

subject { Coordinator.new(order) }

context "packages" do
context "package" do
it "builds, prioritizes and estimates" do
expect(subject).to receive(:build_packages).ordered
expect(subject).to receive(:prioritize_packages).ordered
expect(subject).to receive(:estimate_packages).ordered
subject.packages
expect(subject).to receive(:build_package).ordered
expect(subject).to receive(:prioritize_package).ordered
expect(subject).to receive(:estimate_package).ordered
subject.package
end
end
end
Expand Down
Loading