-
-
Notifications
You must be signed in to change notification settings - Fork 729
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
2696,2788 [Spree Upgrade] Fix use of shipping method ID for subscriptions #3560
Changes from all commits
5f571aa
0306cc7
f42eb3d
ce5691d
7a8bf76
54991f8
5b5d196
9316096
a261d91
182fde5
7c34334
0cfee37
56e2ee5
abaf90f
0e69181
8782f20
7505dd0
9460602
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,17 @@ def index | |
# within the page then fetches the data it needs from Api::OrdersController | ||
end | ||
|
||
def edit | ||
@order.shipments.map &:refresh_rates | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there are two points about this method:
these are both out of scope of this PR. |
||
|
||
AdvanceOrderService.new(@order).call | ||
|
||
# The payment step shows an error of 'No pending payments' | ||
# Clearing the errors from the order object will stop this error | ||
# appearing on the edit page where we don't want it to. | ||
@order.errors.clear | ||
end | ||
|
||
# Re-implement spree method so that it redirects to edit instead of rendering edit | ||
# This allows page reloads while adding variants to the order (/edit), without being redirected to customer details page (/update) | ||
def update | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,35 @@ | ||
Spree::Admin::PaymentsController.class_eval do | ||
append_before_filter :filter_payment_methods | ||
|
||
def create | ||
@payment = @order.payments.build(object_params) | ||
if @payment.payment_method.is_a?(Spree::Gateway) && @payment.payment_method.payment_profiles_supported? && params[:card].present? and params[:card] != 'new' | ||
@payment.source = CreditCard.find_by_id(params[:card]) | ||
end | ||
|
||
begin | ||
unless @payment.save | ||
redirect_to admin_order_payments_path(@order) | ||
return | ||
end | ||
|
||
if @order.completed? | ||
@payment.process! | ||
flash[:success] = flash_message_for(@payment, :successfully_created) | ||
|
||
redirect_to admin_order_payments_path(@order) | ||
else | ||
AdvanceOrderService.new(@order).call! | ||
|
||
flash[:success] = Spree.t(:new_order_completed) | ||
redirect_to edit_admin_order_url(@order) | ||
end | ||
|
||
rescue Spree::Core::GatewayError => e | ||
flash[:error] = "#{e.message}" | ||
redirect_to new_admin_order_payment_path(@order) | ||
end | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not good that we need to copy spree code to our code base but I am actually very happy that we are reclaiming this payments code to OFN, because the spree code is not good in v2 and it's a big mess in v3.7 here. this kind of logic must go to a service, it's not the responsibility of the controller. It's out of scope for this PR. |
||
|
||
# When a user fires an event, take them back to where they came from | ||
# (we can't use respond_override because Spree no longer uses respond_with) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
class AdvanceOrderService | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you went for option 4 here |
||
attr_reader :order | ||
|
||
def initialize(order) | ||
@order = order | ||
end | ||
|
||
def call | ||
advance_order(advance_order_options) | ||
end | ||
|
||
def call! | ||
advance_order!(advance_order_options) | ||
end | ||
|
||
private | ||
|
||
def advance_order_options | ||
shipping_method_id = order.shipping_method.id if order.shipping_method.present? | ||
{ shipping_method_id: shipping_method_id } | ||
end | ||
|
||
def advance_order(options) | ||
until order.state == "complete" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It'd be great to stick to the approach in line 24 for consistency IMO There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I gave this a try just now, @sauloperez, and confirmed that a completed but later cancelled order would attach an error here ( We can actually remove |
||
break unless order.next | ||
after_transition_hook(options) | ||
end | ||
end | ||
|
||
def advance_order!(options) | ||
until order.completed? | ||
order.next! | ||
after_transition_hook(options) | ||
end | ||
end | ||
|
||
def after_transition_hook(options) | ||
if order.state == "delivery" | ||
order.select_shipping_method(options[:shipping_method_id]) if options[:shipping_method_id] | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,14 +66,12 @@ def update_payment_for(order) | |
end | ||
|
||
def update_shipment_for(order) | ||
shipment = order.shipments.with_state('pending').where(shipping_method_id: shipping_method_id_was).last | ||
if shipment | ||
shipment.update_attributes(shipping_method_id: shipping_method_id) | ||
order.update_attribute(:shipping_method_id, shipping_method_id) | ||
return if pending_shipment_with?(order, shipping_method_id) # No need to do anything. | ||
|
||
if pending_shipment_with?(order, shipping_method_id_was) | ||
order.select_shipping_method(shipping_method_id) | ||
else | ||
unless order.shipments.with_state('pending').where(shipping_method_id: shipping_method_id).any? | ||
order_update_issues.add(order, I18n.t('admin.shipping_method')) | ||
end | ||
order_update_issues.add(order, I18n.t('admin.shipping_method')) | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see order_shipment.rb, in ofn v2 we have one very useful rule (it makes everything much easier): one order can only have one shipment, there will never be 2 shipments in an order. So you can use order.shipment. also, spree::shipment.selected_shipping_rate is kept to single selection in spree::shipment.selected_shipping_rate_id= also, you can simply use order_shipment.shipping_method that redirects to spree::shipment.shipping_method that redirects to spree::shipment.selected_shipping_rate :-) I think these facts will help simplify this code, right? |
||
end | ||
|
||
|
@@ -106,4 +104,9 @@ def force_ship_address_required?(order) | |
order.ship_address[attr] == distributor_address[attr] | ||
end | ||
end | ||
|
||
def pending_shipment_with?(order, shipping_method_id) | ||
return false unless order.shipment.present? && order.shipment.state == "pending" | ||
order.shipping_method.id == shipping_method_id | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
require "spec_helper" | ||
|
||
describe AdvanceOrderService do | ||
let!(:distributor) { create(:distributor_enterprise) } | ||
let!(:order) do | ||
create(:order_with_totals_and_distribution, distributor: distributor, | ||
bill_address: create(:address), | ||
ship_address: create(:address)) | ||
end | ||
|
||
let(:service) { described_class.new(order) } | ||
|
||
it "transitions the order multiple steps" do | ||
expect(order.state).to eq("cart") | ||
service.call | ||
order.reload | ||
expect(order.state).to eq("complete") | ||
end | ||
|
||
describe "transition from delivery" do | ||
let!(:shipping_method_a) { create(:shipping_method, distributors: [ distributor ]) } | ||
let!(:shipping_method_b) { create(:shipping_method, distributors: [ distributor ]) } | ||
let!(:shipping_method_c) { create(:shipping_method, distributors: [ distributor ]) } | ||
|
||
before do | ||
# Create shipping rates for available shipping methods. | ||
order.shipments.each(&:refresh_rates) | ||
end | ||
|
||
it "retains delivery method of the order" do | ||
order.select_shipping_method(shipping_method_b.id) | ||
service.call | ||
order.reload | ||
expect(order.shipping_method).to eq(shipping_method_b) | ||
end | ||
end | ||
|
||
context "when raising on error" do | ||
it "transitions the order multiple steps" do | ||
service.call! | ||
order.reload | ||
expect(order.state).to eq("complete") | ||
end | ||
|
||
context "when order cannot advance to the next state" do | ||
let!(:order) do | ||
create(:order, distributor: distributor) | ||
end | ||
|
||
it "raises error" do | ||
expect { service.call! }.to raise_error(StateMachine::InvalidTransition) | ||
end | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit surprised to compare a boolean value to a string. Rails does parse JSON to Ruby primitives. How is it possible that we end up with a String and not with a FalseClass instance 🤔 ? I would have expected to read this as
if !params[:guest_checkout]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sauloperez This isn't the case when the string is sent to the server as HTTP form data. However, this string would still be converted to a boolean as we expect when assigned to an
ActiveRecord
boolean attribute. 🙂