diff --git a/api/app/controllers/spree/api/checkouts_controller.rb b/api/app/controllers/spree/api/checkouts_controller.rb index 5d4da3009c5..7c73c70bd83 100644 --- a/api/app/controllers/spree/api/checkouts_controller.rb +++ b/api/app/controllers/spree/api/checkouts_controller.rb @@ -114,6 +114,7 @@ def state_callback(before_or_after = :before) def after_update_attributes if params[:order] && params[:order][:coupon_code].present? + Spree::Deprecation.warn('This method is deprecated. Please use `Spree::Api::CouponCodesController#create` endpoint instead.') handler = PromotionHandler::Coupon.new(@order) handler.apply diff --git a/api/app/controllers/spree/api/coupon_codes_controller.rb b/api/app/controllers/spree/api/coupon_codes_controller.rb new file mode 100644 index 00000000000..114e9c88f01 --- /dev/null +++ b/api/app/controllers/spree/api/coupon_codes_controller.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Spree + module Api + class CouponCodesController < Spree::Api::BaseController + before_action :load_order, only: :create + around_action :lock_order, only: :create + + def create + authorize! :update, @order, order_token + + @order.coupon_code = params[:coupon_code] + @handler = PromotionHandler::Coupon.new(@order).apply + + if @handler.successful? + render 'spree/api/promotions/handler', status: 200 + else + logger.error("apply_coupon_code_error=#{@handler.error.inspect}") + render 'spree/api/promotions/handler', status: 422 + end + end + + private + + def load_order + @order = Spree::Order.find_by!(number: params[:order_id]) + end + end + end +end diff --git a/api/app/controllers/spree/api/orders_controller.rb b/api/app/controllers/spree/api/orders_controller.rb index af748c39b3d..9433132ef59 100644 --- a/api/app/controllers/spree/api/orders_controller.rb +++ b/api/app/controllers/spree/api/orders_controller.rb @@ -92,6 +92,8 @@ def mine end def apply_coupon_code + Spree::Deprecation.warn('This method is deprecated. Please use `Spree::Api::CouponCodesController#create` endpoint instead.') + authorize! :update, @order, order_token @order.coupon_code = params[:coupon_code] @handler = PromotionHandler::Coupon.new(@order).apply diff --git a/api/config/routes.rb b/api/config/routes.rb index ea13cfefdd0..b533b3b9a41 100644 --- a/api/config/routes.rb +++ b/api/config/routes.rb @@ -67,6 +67,8 @@ put :empty put :apply_coupon_code end + + resources :coupon_codes, only: :create end resources :zones diff --git a/api/spec/features/checkout_spec.rb b/api/spec/features/checkout_spec.rb index eb9ece34eac..8762768bc80 100644 --- a/api/spec/features/checkout_spec.rb +++ b/api/spec/features/checkout_spec.rb @@ -62,7 +62,7 @@ def create_line_item(variant, quantity = 1) def add_promotion(_promotion) expect { - put "/api/orders/#{@order.number}/apply_coupon_code", + post "/api/orders/#{@order.number}/coupon_codes", params: { coupon_code: promotion_code.value } }.to change { @order.promotions.count }.by 1 expect(response).to have_http_status(:ok) diff --git a/api/spec/requests/spree/api/checkouts_controller_spec.rb b/api/spec/requests/spree/api/checkouts_controller_spec.rb index 1252108c21c..530d2bdd884 100644 --- a/api/spec/requests/spree/api/checkouts_controller_spec.rb +++ b/api/spec/requests/spree/api/checkouts_controller_spec.rb @@ -357,6 +357,7 @@ module Spree end it "can apply a coupon code to an order" do + expect(Spree::Deprecation).to receive(:warn) order.update_column(:state, "payment") expect(PromotionHandler::Coupon).to receive(:new).with(order).and_call_original expect_any_instance_of(PromotionHandler::Coupon).to receive(:apply).and_return({ coupon_applied?: true }) @@ -365,6 +366,7 @@ module Spree end it "renders error failing to apply coupon" do + expect(Spree::Deprecation).to receive(:warn) order.update_column(:state, "payment") put spree.api_checkout_path(order.to_param), params: { order_token: order.guest_token, order: { coupon_code: "foobar" } } expect(response.status).to eq(422) diff --git a/api/spec/requests/spree/api/coupon_codes_controller_spec.rb b/api/spec/requests/spree/api/coupon_codes_controller_spec.rb new file mode 100644 index 00000000000..ed85fb8bc9d --- /dev/null +++ b/api/spec/requests/spree/api/coupon_codes_controller_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' + +module Spree + describe Api::CouponCodesController, type: :request do + let(:current_api_user) do + user = Spree.user_class.new(email: "spree@example.com") + user.generate_spree_api_key! + user + end + + before do + stub_authentication! + end + + describe '#create' do + let(:promo) { create(:promotion_with_item_adjustment, code: 'night_melody') } + let(:promo_code) { promo.codes.first } + + before do + allow_any_instance_of(Order).to receive_messages user: current_api_user + end + + context 'when successful' do + let(:order) { create(:order_with_line_items) } + + it 'applies the coupon' do + post spree.api_order_coupon_codes_path(order), params: { coupon_code: promo_code.value } + + expect(response.status).to eq(200) + expect(order.reload.promotions).to eq([promo]) + expect(json_response).to eq({ + "success" => I18n.t('spree.coupon_code_applied'), + "error" => nil, + "successful" => true, + "status_code" => "coupon_code_applied" + }) + end + end + + context 'when unsuccessful' do + let(:order) { create(:order) } + + it 'returns an error' do + post spree.api_order_coupon_codes_path(order), params: { coupon_code: promo_code.value } + + expect(response.status).to eq(422) + expect(order.reload.promotions).to eq([]) + expect(json_response).to eq({ + "success" => nil, + "error" => I18n.t('spree.coupon_code_unknown_error'), + "successful" => false, + "status_code" => "coupon_code_unknown_error" + }) + end + end + end + end +end diff --git a/api/spec/requests/spree/api/orders_controller_spec.rb b/api/spec/requests/spree/api/orders_controller_spec.rb index 3ee865775b6..8bb39cdb5ba 100644 --- a/api/spec/requests/spree/api/orders_controller_spec.rb +++ b/api/spec/requests/spree/api/orders_controller_spec.rb @@ -854,6 +854,8 @@ module Spree let(:order) { create(:order_with_line_items) } it 'applies the coupon' do + expect(Spree::Deprecation).to receive(:warn) + put spree.apply_coupon_code_api_order_path(order), params: { coupon_code: promo_code.value } expect(response.status).to eq 200 @@ -871,6 +873,8 @@ module Spree let(:order) { create(:order) } # no line items to apply the code to it 'returns an error' do + expect(Spree::Deprecation).to receive(:warn) + put spree.apply_coupon_code_api_order_path(order), params: { coupon_code: promo_code.value } expect(response.status).to eq 422 diff --git a/api/spec/requests/spree/api/promotion_application_spec.rb b/api/spec/requests/spree/api/promotion_application_spec.rb index ebf99cf2089..9a84c87b606 100644 --- a/api/spec/requests/spree/api/promotion_application_spec.rb +++ b/api/spec/requests/spree/api/promotion_application_spec.rb @@ -20,7 +20,7 @@ module Spree::Api it "can apply a coupon code to the order" do expect(order.total).to eq(110.00) - put spree.apply_coupon_code_api_order_path(order), params: { coupon_code: "10off", order_token: order.guest_token } + post spree.api_order_coupon_codes_path(order), params: { coupon_code: "10off", order_token: order.guest_token } expect(response.status).to eq(200) expect(order.reload.total).to eq(109.00) expect(json_response["success"]).to eq("The coupon code was successfully applied to your order.") @@ -37,7 +37,7 @@ module Spree::Api end it "fails to apply" do - put spree.apply_coupon_code_api_order_path(order), params: { coupon_code: "10off", order_token: order.guest_token } + post spree.api_order_coupon_codes_path(order), params: { coupon_code: "10off", order_token: order.guest_token } expect(response.status).to eq(422) expect(json_response["success"]).to be_blank expect(json_response["error"]).to eq("The coupon code is expired") diff --git a/backend/app/assets/javascripts/spree/backend/adjustments.js b/backend/app/assets/javascripts/spree/backend/adjustments.js index 6716b2a5f3d..06bf456af53 100644 --- a/backend/app/assets/javascripts/spree/backend/adjustments.js +++ b/backend/app/assets/javascripts/spree/backend/adjustments.js @@ -5,7 +5,7 @@ Spree.ready(function() { } Spree.ajax({ - type: 'PUT', + type: 'POST', url: Spree.routes.apply_coupon_code(window.order_number), data: { coupon_code: $("#coupon_code").val(), diff --git a/core/app/assets/javascripts/spree.js.erb b/core/app/assets/javascripts/spree.js.erb index 1d0d036d958..9b4d5a0ef3d 100644 --- a/core/app/assets/javascripts/spree.js.erb +++ b/core/app/assets/javascripts/spree.js.erb @@ -54,7 +54,7 @@ Spree.ajax = function(url, options) { Spree.routes = { states_search: Spree.pathFor('api/states'), apply_coupon_code: function(order_id) { - return Spree.pathFor("api/orders/" + order_id + "/apply_coupon_code"); + return Spree.pathFor("api/orders/" + order_id + "/coupon_codes"); } }; diff --git a/frontend/app/assets/javascripts/spree/frontend/checkout/coupon-code.js b/frontend/app/assets/javascripts/spree/frontend/checkout/coupon-code.js index 48eb66cf001..d917991cb02 100644 --- a/frontend/app/assets/javascripts/spree/frontend/checkout/coupon-code.js +++ b/frontend/app/assets/javascripts/spree/frontend/checkout/coupon-code.js @@ -14,7 +14,7 @@ Spree.onCouponCodeApply = function(e) { coupon_code: couponCode }; var req = Spree.ajax({ - method: "PUT", + method: 'POST', url: Spree.routes.apply_coupon_code(Spree.current_order_id), data: JSON.stringify(data), contentType: "application/json" diff --git a/frontend/app/controllers/spree/checkout_controller.rb b/frontend/app/controllers/spree/checkout_controller.rb index 98261af11e3..9d2652386e2 100644 --- a/frontend/app/controllers/spree/checkout_controller.rb +++ b/frontend/app/controllers/spree/checkout_controller.rb @@ -168,6 +168,7 @@ def completion_route def apply_coupon_code if update_params[:coupon_code].present? + Spree::Deprecation.warn('This endpoint is deprecated. Please use `Spree::CouponCodesController#create` endpoint instead.') @order.coupon_code = update_params[:coupon_code] handler = PromotionHandler::Coupon.new(@order).apply diff --git a/frontend/app/controllers/spree/coupon_codes_controller.rb b/frontend/app/controllers/spree/coupon_codes_controller.rb new file mode 100644 index 00000000000..4151b8ca7e4 --- /dev/null +++ b/frontend/app/controllers/spree/coupon_codes_controller.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Spree + class CouponCodesController < Spree::StoreController + before_action :load_order, only: :create + around_action :lock_order, only: :create + + def create + authorize! :update, @order, cookies.signed[:guest_token] + + if params[:coupon_code].present? + @order.coupon_code = params[:coupon_code] + handler = PromotionHandler::Coupon.new(@order).apply + + respond_with(@order) do |format| + format.html do + if handler.successful? + flash[:success] = handler.success + redirect_to cart_path + else + flash.now[:error] = handler.error + render 'spree/coupon_codes/new' + end + end + end + end + end + + private + + def load_order + @order = current_order + end + end +end diff --git a/frontend/app/controllers/spree/orders_controller.rb b/frontend/app/controllers/spree/orders_controller.rb index 853eaae9dc7..ee603f0b975 100644 --- a/frontend/app/controllers/spree/orders_controller.rb +++ b/frontend/app/controllers/spree/orders_controller.rb @@ -122,6 +122,7 @@ def assign_order def apply_coupon_code if order_params[:coupon_code].present? + Spree::Deprecation.warn('This endpoint is deprecated. Please use `Spree::CouponCodesController#create` endpoint instead.') @order.coupon_code = order_params[:coupon_code] handler = PromotionHandler::Coupon.new(@order).apply diff --git a/frontend/app/views/spree/coupon_codes/new.html.erb b/frontend/app/views/spree/coupon_codes/new.html.erb new file mode 100644 index 00000000000..1660893b861 --- /dev/null +++ b/frontend/app/views/spree/coupon_codes/new.html.erb @@ -0,0 +1,6 @@ +
+ <%= form_tag order_coupon_codes_path(@order), method: :post do %> + <%= text_field_tag :coupon_code, nil, placeholder: t("spree.coupon_code"), size: 10 %> + <%= submit_tag t("spree.apply_code") %> + <% end %> +
diff --git a/frontend/app/views/spree/orders/edit.html.erb b/frontend/app/views/spree/orders/edit.html.erb index c25033eb451..df11b5d0bcd 100644 --- a/frontend/app/views/spree/orders/edit.html.erb +++ b/frontend/app/views/spree/orders/edit.html.erb @@ -16,19 +16,16 @@
- <%= render 'form', order_form: order_form %> + <%= render 'spree/orders/form', order_form: order_form %>
-
<% end %> @@ -41,6 +38,8 @@ <%= link_to t('spree.continue_shopping'), products_path, class: 'continue button gray' %>

<% end %> + + <%= render template: 'spree/coupon_codes/new' %> <% end %> diff --git a/frontend/config/routes.rb b/frontend/config/routes.rb index 9ab0a2b7447..4c3e3dd37ce 100644 --- a/frontend/config/routes.rb +++ b/frontend/config/routes.rb @@ -18,6 +18,7 @@ resources :orders, except: [:index, :new, :create, :destroy] do post :populate, on: :collection + resources :coupon_codes, only: :create end get '/cart', to: 'orders#edit', as: :cart diff --git a/frontend/spec/controllers/spree/checkout_controller_spec.rb b/frontend/spec/controllers/spree/checkout_controller_spec.rb index d764a030f17..524147e833f 100644 --- a/frontend/spec/controllers/spree/checkout_controller_spec.rb +++ b/frontend/spec/controllers/spree/checkout_controller_spec.rb @@ -501,6 +501,8 @@ def post_address let(:promotion_handler) { instance_double('Spree::PromotionHandler::Coupon', error: nil, success: 'Coupon Applied!') } it "continues checkout flow normally" do + expect(Spree::Deprecation).to receive(:warn) + expect(Spree::PromotionHandler::Coupon) .to receive_message_chain(:new, :apply) .and_return(promotion_handler) @@ -515,6 +517,8 @@ def post_address let(:promotion_handler) { instance_double('Spree::PromotionHandler::Coupon', error: 'Some error', success: false) } it "setups the current step correctly before rendering" do + expect(Spree::Deprecation).to receive(:warn) + expect(Spree::PromotionHandler::Coupon) .to receive_message_chain(:new, :apply) .and_return(promotion_handler) @@ -524,6 +528,8 @@ def post_address end it "render cart with coupon error" do + expect(Spree::Deprecation).to receive(:warn) + expect(Spree::PromotionHandler::Coupon) .to receive_message_chain(:new, :apply) .and_return(promotion_handler) diff --git a/frontend/spec/controllers/spree/orders_controller_spec.rb b/frontend/spec/controllers/spree/orders_controller_spec.rb index e104378e080..3ada5dd1948 100644 --- a/frontend/spec/controllers/spree/orders_controller_spec.rb +++ b/frontend/spec/controllers/spree/orders_controller_spec.rb @@ -149,6 +149,8 @@ let(:promotion_handler) { instance_double('Spree::PromotionHandler::Coupon', error: nil, success: 'Coupon Applied!') } it "continues checkout flow normally" do + expect(Spree::Deprecation).to receive(:warn) + expect(Spree::PromotionHandler::Coupon) .to receive_message_chain(:new, :apply) .and_return(promotion_handler) @@ -163,6 +165,8 @@ let(:promotion_handler) { instance_double('Spree::PromotionHandler::Coupon', error: 'Some error', success: false) } it "render cart with coupon error" do + expect(Spree::Deprecation).to receive(:warn) + expect(Spree::PromotionHandler::Coupon) .to receive_message_chain(:new, :apply) .and_return(promotion_handler) diff --git a/frontend/spec/features/coupon_code_spec.rb b/frontend/spec/features/coupon_code_spec.rb index fc95390be56..9d9e3a1a836 100644 --- a/frontend/spec/features/coupon_code_spec.rb +++ b/frontend/spec/features/coupon_code_spec.rb @@ -130,22 +130,22 @@ def create_basic_coupon_promotion(code) end it "can enter a coupon code and receives success notification" do - fill_in "order_coupon_code", with: "onetwo" - click_button "Update" + fill_in "coupon_code", with: "onetwo" + click_button "Apply Code" expect(page).to have_content(I18n.t('spree.coupon_code_applied')) end it "can enter a promotion code with both upper and lower case letters" do - fill_in "order_coupon_code", with: "ONETwO" - click_button "Update" + fill_in "coupon_code", with: "ONETwO" + click_button "Apply Code" expect(page).to have_content(I18n.t('spree.coupon_code_applied')) end it "informs the user about a coupon code which has exceeded its usage" do expect_any_instance_of(Spree::Promotion).to receive(:usage_limit_exceeded?).and_return(true) - fill_in "order_coupon_code", with: "onetwo" - click_button "Update" + fill_in "coupon_code", with: "onetwo" + click_button "Apply Code" expect(page).to have_content(I18n.t('spree.coupon_code_max_usage')) end @@ -160,8 +160,8 @@ def create_basic_coupon_promotion(code) specify do visit spree.cart_path - fill_in "order_coupon_code", with: "onetwo" - click_button "Update" + fill_in "coupon_code", with: "onetwo" + click_button "Apply Code" expect(page).to have_content(I18n.t(:item_total_less_than_or_equal, scope: [:spree, :eligibility_errors, :messages], amount: "$100.00")) end end @@ -170,8 +170,8 @@ def create_basic_coupon_promotion(code) promotion.expires_at = Date.today.beginning_of_week promotion.starts_at = Date.today.beginning_of_week.advance(day: 3) promotion.save! - fill_in "order_coupon_code", with: "onetwo" - click_button "Update" + fill_in "coupon_code", with: "onetwo" + click_button "Apply Code" expect(page).to have_content(I18n.t('spree.coupon_code_expired')) end @@ -191,8 +191,8 @@ def create_basic_coupon_promotion(code) click_button "add-to-cart-button" visit spree.cart_path - fill_in "order_coupon_code", with: "onetwo" - click_button "Update" + fill_in "coupon_code", with: "onetwo" + click_button "Apply Code" fill_in "order_line_items_attributes_0_quantity", with: 2 fill_in "order_line_items_attributes_1_quantity", with: 2 @@ -237,8 +237,8 @@ def create_basic_coupon_promotion(code) expect(page).to have_content("$30.00") end - fill_in "order_coupon_code", with: "onetwo" - click_button "Update" + fill_in "coupon_code", with: "onetwo" + click_button "Apply Code" within '#cart_adjustments' do expect(page).to have_content("Promotion (Onetwo) -$30.00") diff --git a/frontend/spec/features/promotion_code_invalidation_spec.rb b/frontend/spec/features/promotion_code_invalidation_spec.rb index 5384bf5b057..693a1d8a3f8 100644 --- a/frontend/spec/features/promotion_code_invalidation_spec.rb +++ b/frontend/spec/features/promotion_code_invalidation_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.feature "Promotion Code Invalidation" do +RSpec.feature "Promotion Code Invalidation", js: true do given!(:promotion) do FactoryBot.create( :promotion_with_item_adjustment, @@ -28,7 +28,7 @@ scenario "adding the promotion to a cart with two applicable items" do fill_in "Coupon code", with: "PROMO" - click_button "Update" + click_button "Apply Code" expect(page).to have_content("The coupon code was successfully applied to your order") diff --git a/frontend/spec/features/quantity_promotions_spec.rb b/frontend/spec/features/quantity_promotions_spec.rb index 56d1351922c..6bbb9685112 100644 --- a/frontend/spec/features/quantity_promotions_spec.rb +++ b/frontend/spec/features/quantity_promotions_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.feature "Quantity Promotions" do +RSpec.feature "Quantity Promotions", js: true do given(:action) do Spree::Promotion::Actions::CreateQuantityAdjustments.create( calculator: calculator, @@ -26,8 +26,8 @@ scenario "adding and removing items from the cart" do # Attempt to use the code with too few items. - fill_in "Coupon code", with: "PROMO" - click_button "Update" + fill_in "coupon_code", with: "PROMO" + click_button "Apply Code" expect(page).to have_content("This coupon code could not be applied to the cart at this time") # Add another item to our cart. @@ -36,8 +36,8 @@ click_button "Add To Cart" # Using the code should now succeed. - fill_in "Coupon code", with: "PROMO" - click_button "Update" + fill_in "coupon_code", with: "PROMO" + click_button "Apply Code" expect(page).to have_content("The coupon code was successfully applied to your order") within("#cart_adjustments") do expect(page).to have_content("-$10.00") @@ -70,8 +70,8 @@ click_button "Update" # Apply the promo code and see a $10 discount (for 2 of the 3 items) - fill_in "Coupon code", with: "PROMO" - click_button "Update" + fill_in "coupon_code", with: "PROMO" + click_button "Apply Code" expect(page).to have_content("The coupon code was successfully applied to your order") within("#cart_adjustments") do expect(page).to have_content("-$10.00") @@ -104,8 +104,8 @@ click_button "Update" # Apply the promo code and see a $15 discount - fill_in "Coupon code", with: "PROMO" - click_button "Update" + fill_in "coupon_code", with: "PROMO" + click_button "Apply Code" expect(page).to have_content("The coupon code was successfully applied to your order") within("#cart_adjustments") do expect(page).to have_content("-$15.00")