mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-25 05:45:15 +00:00
Merge pull request #10913 from abdellani/remove-split-checkout-toggle-and-legacy-checkout
Remove split checkout toggle and legacy checkout
This commit is contained in:
@@ -628,11 +628,16 @@ Metrics/BlockLength:
|
||||
- 'spec/factories/user_factory.rb'
|
||||
- 'spec/factories/variant_factory.rb'
|
||||
- 'spec/requests/api/orders_spec.rb'
|
||||
- 'spec/requests/checkout/failed_checkout_spec.rb'
|
||||
- 'spec/requests/checkout/stripe_sca_spec.rb'
|
||||
- 'spec/support/cancan_helper.rb'
|
||||
- 'spec/support/matchers/select2_matchers.rb'
|
||||
- 'spec/support/matchers/table_matchers.rb'
|
||||
- 'spec/swagger_helper.rb'
|
||||
- 'spec/system/consumer/shopping/checkout_spec.rb'
|
||||
- 'spec/system/consumer/shopping/checkout_stripe_spec.rb'
|
||||
- 'spec/system/consumer/shopping/variant_overrides_spec.rb'
|
||||
- 'spec/system/consumer/split_checkout_tax_not_incl_spec.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# Configuration parameters: CountBlocks, Max.
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
angular.module('Darkswarm').controller "AccordionCtrl", ($scope, localStorageService, $timeout, $document, CurrentHub) ->
|
||||
$scope.accordionSections = ["details", "billing", "shipping", "payment"]
|
||||
$scope.accordion = { details: true, billing: true, shipping: true, payment: true }
|
||||
|
||||
$scope.show = (section) ->
|
||||
$scope.accordion[section] = true
|
||||
|
||||
$scope.scrollTo = (section) ->
|
||||
# Scrolling is confused by our position:fixed top bar - add an offset to scroll
|
||||
# to the correct location, plus 5px buffer
|
||||
offset_height = $("nav.top-bar").height() + 5
|
||||
$document.scrollTo($("##{section}"), offset_height, 400)
|
||||
|
||||
$scope.$on 'purchaseFormInvalid', (event, form) ->
|
||||
# Scroll to first invalid section
|
||||
for section in $scope.accordionSections
|
||||
if not form[section].$valid
|
||||
$scope.show section
|
||||
$timeout ->
|
||||
$scope.scrollTo(section)
|
||||
, 50
|
||||
break
|
||||
@@ -1,12 +0,0 @@
|
||||
angular.module('Darkswarm').controller "BillingCtrl", ($scope, $timeout, $controller) ->
|
||||
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
|
||||
|
||||
$scope.name = "billing"
|
||||
$scope.nextPanel = "shipping"
|
||||
|
||||
$scope.summary = ->
|
||||
[$scope.order.bill_address.address1,
|
||||
$scope.order.bill_address.city,
|
||||
$scope.order.bill_address.zipcode]
|
||||
|
||||
$timeout $scope.onTimeout
|
||||
@@ -1,43 +0,0 @@
|
||||
angular.module('Darkswarm').controller "CheckoutCtrl", ($scope, localStorageService, Checkout, CurrentUser, CurrentHub, $http) ->
|
||||
$scope.Checkout = Checkout
|
||||
$scope.submitted = false
|
||||
|
||||
# Bind to local storage
|
||||
$scope.fieldsToBind = ["bill_address", "email", "payment_method_id", "shipping_method_id", "ship_address"]
|
||||
prefix = "order_#{Checkout.order.id}#{CurrentUser.id or ""}#{CurrentHub.hub.id}"
|
||||
|
||||
for field in $scope.fieldsToBind
|
||||
localStorageService.bind $scope, "Checkout.order.#{field}", Checkout.order[field], "#{prefix}_#{field}"
|
||||
|
||||
localStorageService.bind $scope, "Checkout.ship_address_same_as_billing", true, "#{prefix}_sameasbilling"
|
||||
localStorageService.bind $scope, "Checkout.default_bill_address", false, "#{prefix}_defaultasbilladdress"
|
||||
localStorageService.bind $scope, "Checkout.default_ship_address", false, "#{prefix}_defaultasshipaddress"
|
||||
|
||||
$scope.order = Checkout.order # Ordering is important
|
||||
$scope.secrets = Checkout.secrets
|
||||
|
||||
$scope.enabled = !!CurrentUser.id?
|
||||
|
||||
$scope.purchase = (event, form) ->
|
||||
event.preventDefault()
|
||||
$scope.formdata = form
|
||||
$scope.submitted = true
|
||||
|
||||
if CurrentUser.id
|
||||
$scope.validateForm(form)
|
||||
else
|
||||
$scope.ensureUserIsGuest()
|
||||
|
||||
$scope.validateForm = ->
|
||||
if $scope.formdata.$valid
|
||||
$scope.Checkout.purchase()
|
||||
else
|
||||
$scope.$broadcast 'purchaseFormInvalid', $scope.formdata
|
||||
|
||||
$scope.ensureUserIsGuest = (callback = null) ->
|
||||
$http.post("/user/registered_email", {email: $scope.order.email})
|
||||
.then (response)->
|
||||
window.CableReady.perform(response.data)
|
||||
.catch ->
|
||||
$scope.validateForm() if $scope.submitted
|
||||
callback() if callback
|
||||
@@ -1,8 +0,0 @@
|
||||
angular.module('Darkswarm').controller "CountryCtrl", ($scope, availableCountries) ->
|
||||
|
||||
$scope.countries = availableCountries
|
||||
|
||||
$scope.countriesById = $scope.countries.reduce (obj, country) ->
|
||||
obj[country.id] = country
|
||||
obj
|
||||
, {}
|
||||
@@ -1,24 +0,0 @@
|
||||
angular.module('Darkswarm').controller "DetailsCtrl", ($scope, $timeout, $http, CurrentUser, SpreeUser, $controller) ->
|
||||
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
|
||||
|
||||
$scope.name = "details"
|
||||
$scope.nextPanel = "billing"
|
||||
|
||||
$scope.login_or_next = (event) ->
|
||||
event.preventDefault()
|
||||
unless CurrentUser.id
|
||||
$scope.ensureUserIsGuest($scope.next)
|
||||
return
|
||||
|
||||
$scope.next()
|
||||
|
||||
$scope.summary = ->
|
||||
[$scope.fullName(),
|
||||
$scope.order.email,
|
||||
$scope.order.bill_address.phone]
|
||||
|
||||
$scope.fullName = ->
|
||||
[$scope.order.bill_address.firstname ? null,
|
||||
$scope.order.bill_address.lastname ? null].join(" ").trim()
|
||||
|
||||
$timeout $scope.onTimeout
|
||||
@@ -1,19 +0,0 @@
|
||||
angular.module('Darkswarm').controller "PaymentCtrl", ($scope, $timeout, savedCreditCards, Dates, $controller) ->
|
||||
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
|
||||
|
||||
$scope.savedCreditCards = savedCreditCards
|
||||
$scope.name = "payment"
|
||||
$scope.months = Dates.months
|
||||
$scope.years = Dates.years
|
||||
|
||||
$scope.secrets.card_month = "1"
|
||||
$scope.secrets.card_year = moment().year()
|
||||
|
||||
for card in savedCreditCards when card.is_default
|
||||
$scope.secrets.selected_card = card.id
|
||||
break
|
||||
|
||||
$scope.summary = ->
|
||||
[$scope.Checkout.paymentMethod()?.name]
|
||||
|
||||
$timeout $scope.onTimeout
|
||||
@@ -1,11 +0,0 @@
|
||||
angular.module('Darkswarm').controller "ShippingCtrl", ($scope, $timeout, ShippingMethods, $controller) ->
|
||||
angular.extend this, $controller('FieldsetMixin', {$scope: $scope})
|
||||
|
||||
$scope.ShippingMethods = ShippingMethods
|
||||
$scope.name = "shipping"
|
||||
$scope.nextPanel = "payment"
|
||||
|
||||
$scope.summary = ->
|
||||
[$scope.Checkout.shippingMethod()?.name]
|
||||
|
||||
$timeout $scope.onTimeout
|
||||
@@ -1,187 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/address_finder'
|
||||
|
||||
class CheckoutController < ::BaseController
|
||||
include OrderStockCheck
|
||||
include OrderCompletion
|
||||
include WhiteLabel
|
||||
|
||||
layout 'darkswarm'
|
||||
|
||||
helper 'terms_and_conditions'
|
||||
helper 'checkout'
|
||||
|
||||
# We need pessimistic locking to avoid race conditions.
|
||||
# Otherwise we fail on duplicate indexes or end up with negative stock.
|
||||
prepend_around_action CurrentOrderLocker, only: [:edit, :update]
|
||||
|
||||
prepend_before_action :check_hub_ready_for_checkout
|
||||
prepend_before_action :check_order_cycle_expiry
|
||||
prepend_before_action :require_order_cycle
|
||||
prepend_before_action :require_distributor_chosen
|
||||
|
||||
before_action :load_order
|
||||
|
||||
before_action :handle_insufficient_stock
|
||||
|
||||
before_action :associate_user
|
||||
before_action :check_authorization
|
||||
|
||||
before_action :hide_ofn_navigation, only: :edit
|
||||
|
||||
helper 'spree/orders'
|
||||
|
||||
def edit; end
|
||||
|
||||
def update
|
||||
params_adapter = Checkout::FormDataAdapter.new(permitted_params, @order, spree_current_user)
|
||||
return action_failed unless @order.update(params_adapter.params[:order] || {})
|
||||
|
||||
checkout_workflow(params_adapter.shipping_method_id)
|
||||
rescue Spree::Core::GatewayError => e
|
||||
gateway_error(e)
|
||||
action_failed(e)
|
||||
rescue StandardError => e
|
||||
flash[:error] = I18n.t("checkout.failed")
|
||||
action_failed(e)
|
||||
ensure
|
||||
@order.update_order!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_authorization
|
||||
authorize!(:edit, current_order, session[:access_token])
|
||||
end
|
||||
|
||||
def load_order
|
||||
load_checkout_order
|
||||
|
||||
return handle_invalid_stock unless valid_order_line_items?
|
||||
|
||||
before_address
|
||||
setup_for_current_state
|
||||
end
|
||||
|
||||
def handle_invalid_stock
|
||||
reset_order_to_cart
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to main_app.cart_path
|
||||
end
|
||||
|
||||
format.json do
|
||||
render json: { path: main_app.cart_path }, status: :bad_request
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def setup_for_current_state
|
||||
method_name = :"before_#{@order.state}"
|
||||
__send__(method_name) if respond_to?(method_name, true)
|
||||
end
|
||||
|
||||
def before_address
|
||||
associate_user
|
||||
|
||||
finder = OpenFoodNetwork::AddressFinder.new(@order.email, @order.customer, spree_current_user)
|
||||
|
||||
@order.bill_address = finder.bill_address
|
||||
@order.ship_address = finder.ship_address
|
||||
end
|
||||
|
||||
def before_payment
|
||||
current_order.payments.destroy_all if request.put?
|
||||
end
|
||||
|
||||
def checkout_workflow(shipping_method_id)
|
||||
while @order.state != "complete"
|
||||
if @order.state == "payment"
|
||||
update_payment_total
|
||||
return if redirect_to_payment_gateway
|
||||
|
||||
return action_failed if @order.errors.any?
|
||||
return action_failed unless @order.process_payments!
|
||||
end
|
||||
|
||||
next if OrderWorkflow.new(@order).next({ "shipping_method_id" => shipping_method_id })
|
||||
|
||||
return action_failed
|
||||
end
|
||||
|
||||
update_response
|
||||
end
|
||||
|
||||
def update_payment_total
|
||||
@order.update_totals
|
||||
@order.updater.update_pending_payment
|
||||
end
|
||||
|
||||
def redirect_to_payment_gateway
|
||||
return unless selected_payment_method.external_gateway?
|
||||
return unless (redirect_url = selected_payment_method.external_payment_url(order: @order))
|
||||
|
||||
render json: { path: redirect_url }, status: :ok
|
||||
true
|
||||
end
|
||||
|
||||
def selected_payment_method
|
||||
@selected_payment_method ||= Spree::PaymentMethod.find(
|
||||
params.dig(:order, :payments_attributes, 0, :payment_method_id)
|
||||
)
|
||||
end
|
||||
|
||||
def update_response
|
||||
if order_complete?
|
||||
processing_succeeded
|
||||
update_succeeded_response
|
||||
else
|
||||
action_failed(RuntimeError.new("Order not complete after the checkout workflow"))
|
||||
end
|
||||
end
|
||||
|
||||
def order_complete?
|
||||
@order.state == "complete" || @order.completed?
|
||||
end
|
||||
|
||||
def update_succeeded_response
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
respond_with(@order, location: order_completion_route)
|
||||
end
|
||||
format.json do
|
||||
render json: { path: order_completion_route }, status: :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def action_failed(error = RuntimeError.new(order_processing_error))
|
||||
processing_failed(error)
|
||||
action_failed_response
|
||||
end
|
||||
|
||||
def action_failed_response
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
render :edit
|
||||
end
|
||||
format.json do
|
||||
discard_flash_errors
|
||||
render json: { errors: @order.errors, flash: flash.to_hash }.to_json, status: :bad_request
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
PermittedAttributes::Checkout.new(params).call
|
||||
end
|
||||
|
||||
def discard_flash_errors
|
||||
# Marks flash errors for deletion after the current action has completed.
|
||||
# This ensures flash errors generated during XHR requests are not persisted in the
|
||||
# session for longer than expected.
|
||||
flash.discard(:error)
|
||||
end
|
||||
end
|
||||
@@ -13,8 +13,6 @@ module OrderStockCheck
|
||||
def handle_insufficient_stock
|
||||
return if sufficient_stock?
|
||||
|
||||
reset_order_to_cart
|
||||
|
||||
flash[:error] = Spree.t(:inventory_error_flash_for_insufficient_quantity)
|
||||
redirect_to main_app.cart_path
|
||||
end
|
||||
@@ -43,10 +41,4 @@ module OrderStockCheck
|
||||
def sufficient_stock?
|
||||
@sufficient_stock ||= @order.insufficient_stock_lines.blank?
|
||||
end
|
||||
|
||||
def reset_order_to_cart
|
||||
return if OpenFoodNetwork::FeatureToggle.enabled? :split_checkout, spree_current_user
|
||||
|
||||
OrderCheckoutRestart.new(@order).call
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,11 +29,11 @@ module PaymentGateways
|
||||
flash[:error] =
|
||||
Spree.t('flash.generic_error', scope: 'paypal',
|
||||
reasons: pp_response.errors.map(&:long_message).join(" "))
|
||||
redirect_to main_app.checkout_state_path(:payment)
|
||||
redirect_to main_app.checkout_step_path(:payment)
|
||||
end
|
||||
rescue SocketError
|
||||
flash[:error] = Spree.t('flash.connection_failed', scope: 'paypal')
|
||||
redirect_to main_app.checkout_state_path(:payment)
|
||||
redirect_to main_app.checkout_step_path(:payment)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ module Spree
|
||||
format.html do
|
||||
if params.key?(:checkout)
|
||||
@order.next_transition.run_callbacks if @order.cart?
|
||||
redirect_to main_app.checkout_state_path(@order.checkout_steps.first)
|
||||
redirect_to main_app.checkout_step_path(@order.checkout_steps.first)
|
||||
elsif @order.complete?
|
||||
redirect_to main_app.order_path(@order)
|
||||
else
|
||||
|
||||
@@ -89,16 +89,6 @@ module InjectionHelper
|
||||
render partial: "json/injection_ams", locals: { name: "orderCycleData", json: json }
|
||||
end
|
||||
|
||||
def inject_available_shipping_methods
|
||||
inject_json_array "shippingMethods", available_shipping_methods,
|
||||
Api::ShippingMethodSerializer, current_order: current_order
|
||||
end
|
||||
|
||||
def inject_available_payment_methods
|
||||
inject_json_array "paymentMethods", available_payment_methods,
|
||||
Api::PaymentMethodSerializer, current_order: current_order
|
||||
end
|
||||
|
||||
def inject_taxons
|
||||
inject_json_array "taxons", Spree::Taxon.all.to_a, Api::TaxonSerializer
|
||||
end
|
||||
|
||||
@@ -6,16 +6,6 @@ module TermsAndConditionsHelper
|
||||
rel: "noopener")
|
||||
end
|
||||
|
||||
def render_terms_and_conditions
|
||||
if platform_terms_required? && distributor_terms_required?
|
||||
render("checkout/all_terms_and_conditions")
|
||||
elsif platform_terms_required?
|
||||
render "checkout/platform_terms_of_service"
|
||||
elsif distributor_terms_required?
|
||||
render "checkout/terms_and_conditions"
|
||||
end
|
||||
end
|
||||
|
||||
def any_terms_required?(distributor)
|
||||
TermsOfService.required?(distributor)
|
||||
end
|
||||
|
||||
@@ -25,9 +25,7 @@ module Spree
|
||||
order.update_totals
|
||||
order.payment_required?
|
||||
}
|
||||
go_to_state :confirmation, if: ->(order) {
|
||||
OpenFoodNetwork::FeatureToggle.enabled? :split_checkout, order.created_by
|
||||
}
|
||||
go_to_state :confirmation
|
||||
go_to_state :complete
|
||||
end
|
||||
|
||||
@@ -319,8 +317,7 @@ module Spree
|
||||
# Creates new tax charges if there are any applicable rates. If prices already
|
||||
# include taxes then price adjustments are created instead.
|
||||
def create_tax_charge!
|
||||
return if state.in?(["cart", "address", "delivery"]) &&
|
||||
OpenFoodNetwork::FeatureToggle.enabled?(:split_checkout)
|
||||
return if state.in?(["cart", "address", "delivery"])
|
||||
|
||||
clear_legacy_taxes!
|
||||
|
||||
|
||||
@@ -75,9 +75,9 @@ module Spree
|
||||
|
||||
before_transition to: :delivery, do: :create_proposed_shipments
|
||||
before_transition to: :delivery, do: :ensure_available_shipping_rates
|
||||
before_transition to: :payment, do: :create_tax_charge!
|
||||
before_transition to: :confirmation, do: :validate_payment_method!
|
||||
|
||||
after_transition to: :payment, do: :create_tax_charge!
|
||||
after_transition to: :complete, do: :finalize!
|
||||
after_transition to: :resumed, do: :after_resume
|
||||
after_transition to: :canceled, do: :after_cancel
|
||||
@@ -96,14 +96,6 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
def self.find_transition(options = {})
|
||||
return nil if options.nil? || !options.include?(:from) || !options.include?(:to)
|
||||
|
||||
next_event_transitions.detect do |transition|
|
||||
transition[options[:from].to_sym] == options[:to].to_sym
|
||||
end
|
||||
end
|
||||
|
||||
def self.next_event_transitions
|
||||
@next_event_transitions ||= []
|
||||
end
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Adapts checkout form data (params) so that the order can be directly saved to the database
|
||||
module Checkout
|
||||
class FormDataAdapter
|
||||
attr_reader :params, :shipping_method_id
|
||||
|
||||
def initialize(params, order, current_user)
|
||||
@params = params.deep_dup.to_h.with_indifferent_access
|
||||
@order = order
|
||||
@current_user = current_user
|
||||
|
||||
move_payment_source_to_payment_attributes!
|
||||
|
||||
fill_in_card_type
|
||||
|
||||
set_amount_in_payments_attributes
|
||||
|
||||
construct_saved_card_attributes if @params.dig(:order, :existing_card_id)
|
||||
|
||||
@shipping_method_id = @params[:order]&.delete(:shipping_method_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# For payment step, filter order parameters to produce the expected
|
||||
# nested attributes for a single payment and its source,
|
||||
# discarding attributes for payment methods other than the one selected
|
||||
def move_payment_source_to_payment_attributes!
|
||||
return unless @params[:payment_source].present? &&
|
||||
payment_source_params = delete_payment_source_params!
|
||||
|
||||
@params.dig(:order, :payments_attributes).first[:source_attributes] = payment_source_params
|
||||
end
|
||||
|
||||
# Ensures cc_type is always passed to the model by inferring the type when
|
||||
# the frontend didn't provide it.
|
||||
def fill_in_card_type
|
||||
return unless payment_source_attributes
|
||||
|
||||
return if payment_source_attributes.dig(:number).blank?
|
||||
|
||||
payment_source_attributes[:cc_type] ||= card_brand(payment_source_attributes[:number])
|
||||
end
|
||||
|
||||
def payment_source_attributes
|
||||
@payment_source_attributes ||=
|
||||
@params.dig(:order, :payments_attributes)&.first&.dig(:source_attributes)
|
||||
end
|
||||
|
||||
def card_brand(number)
|
||||
ActiveMerchant::Billing::CreditCard.brand?(number)
|
||||
end
|
||||
|
||||
def delete_payment_source_params!
|
||||
@params.delete(:payment_source)[
|
||||
@params.dig(:order, :payments_attributes).first[:payment_method_id].underscore
|
||||
]
|
||||
end
|
||||
|
||||
def set_amount_in_payments_attributes
|
||||
return unless @params.dig(:order, :payments_attributes)
|
||||
|
||||
@params.dig(:order, :payments_attributes).first[:amount] = @order.total
|
||||
end
|
||||
|
||||
def construct_saved_card_attributes
|
||||
existing_card_id = @params[:order].delete(:existing_card_id)
|
||||
return if existing_card_id.blank?
|
||||
|
||||
add_to_payment_attributes(existing_card_id)
|
||||
|
||||
@params.dig(:order, :payments_attributes).first.delete :source_attributes
|
||||
end
|
||||
|
||||
def add_to_payment_attributes(existing_card_id)
|
||||
credit_card = Spree::CreditCard.find(existing_card_id)
|
||||
if credit_card.try(:user_id).blank? || credit_card.user_id != @current_user.try(:id)
|
||||
raise Spree::Core::GatewayError, I18n.t(:invalid_credit_card)
|
||||
end
|
||||
|
||||
@params.dig(:order, :payments_attributes).first[:source] = credit_card
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,25 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module PermittedAttributes
|
||||
class Checkout
|
||||
def initialize(params)
|
||||
@params = params
|
||||
end
|
||||
|
||||
def call
|
||||
@params.permit(
|
||||
order: [
|
||||
:email, :special_instructions,
|
||||
:existing_card_id, :shipping_method_id,
|
||||
{ payments_attributes: [
|
||||
:payment_method_id,
|
||||
{ source_attributes: PermittedAttributes::PaymentSource.attributes }
|
||||
],
|
||||
ship_address_attributes: PermittedAttributes::Address.attributes,
|
||||
bill_address_attributes: PermittedAttributes::Address.attributes }
|
||||
],
|
||||
payment_source: PermittedAttributes::PaymentSource.attributes
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,17 +0,0 @@
|
||||
%accordion-heading
|
||||
.row
|
||||
.small-8.medium-9.columns
|
||||
%em
|
||||
%small
|
||||
{{ summary() | printArray }}
|
||||
.small-4.medium-3.columns.text-right
|
||||
%span.accordion-up
|
||||
%em
|
||||
%small
|
||||
= t :checkout_hide
|
||||
%i.ofn-i_053-point-up
|
||||
%span.accordion-down
|
||||
%em
|
||||
%small
|
||||
= t :checkout_expand
|
||||
%i.ofn-i_052-point-down
|
||||
@@ -1,4 +0,0 @@
|
||||
%p
|
||||
%input{ type: 'checkbox', id: 'accept_terms', ng: { model: "terms_and_conditions_accepted", init: "terms_and_conditions_accepted = #{all_terms_and_conditions_already_accepted?}" } }
|
||||
%label.small{for: "accept_terms"}
|
||||
= t('.message_html', terms_and_conditions_link: link_to( t(".terms_and_conditions"), current_order.distributor.terms_and_conditions, target: '_blank'), tos_link: link_to_platform_terms)
|
||||
@@ -1,2 +0,0 @@
|
||||
%p.alert-box.info
|
||||
= t '.message_html', cart: link_to(t('.cart'), "#{main_app.cart_path}#bought-products")
|
||||
@@ -1,19 +0,0 @@
|
||||
%section{"ng-show" => "!enabled"}
|
||||
.row
|
||||
.small-12.columns.text-center
|
||||
%h3.pad-top
|
||||
= t :checkout_headline
|
||||
.row.pad-top{ "data-controller": "login-modal" }
|
||||
- if guest_checkout_allowed?
|
||||
.small-5.columns.text-center
|
||||
%button.primary.expand{ "data-action": "click->login-modal#call" }
|
||||
= t :label_login
|
||||
.small-2.columns.text-center
|
||||
%p.pad-top= "#{t :action_or}"
|
||||
.small-5.columns.text-center
|
||||
%button.neutral-btn.dark.expand{"ng-click" => "enabled = true"}
|
||||
= t :checkout_as_guest
|
||||
- else
|
||||
.small-6.columns.small-centered
|
||||
%button.primary.expand{ "data-action": "click->login-modal#call" }
|
||||
= t :label_login
|
||||
@@ -1,46 +0,0 @@
|
||||
%fieldset#billing
|
||||
%ng-form{"ng-controller" => "BillingCtrl", name: "billing"}
|
||||
|
||||
%h5{"ng-class" => "{valid: billing.$valid, dirty: billing.$dirty || submitted}"}
|
||||
%span.right
|
||||
%label.label.round.alert.right
|
||||
%i.ofn-i_009-close
|
||||
%label.label.round.success.right
|
||||
%i.ofn-i_051-check-big
|
||||
= t :checkout_billing
|
||||
|
||||
%accordion-group{"is-open" => "accordion.billing",
|
||||
"ng-class" => "{valid: billing.$valid, open: accordion.billing}"}
|
||||
= render 'checkout/accordion_heading'
|
||||
|
||||
- if spree_current_user
|
||||
.small-12.columns
|
||||
%label
|
||||
%input{type: :checkbox, "ng-model" => "Checkout.default_bill_address"}
|
||||
= t :checkout_default_bill_address
|
||||
|
||||
%div{ "ng-controller" => "CountryCtrl" }
|
||||
= f.fields_for :bill_address, @order.bill_address do |ba|
|
||||
.row
|
||||
.small-12.columns
|
||||
= validated_input t(:address), "order.bill_address.address1", "ofn-focus" => "accordion['billing']"
|
||||
.row
|
||||
.small-12.columns
|
||||
= validated_input t(:address2), "order.bill_address.address2", required: false
|
||||
.row
|
||||
.small-6.columns
|
||||
= validated_input t(:city), "order.bill_address.city"
|
||||
|
||||
.small-6.columns
|
||||
= validated_select t(:state), "order.bill_address.state_id", {}, {"ng-options" => "s.id as s.name for s in countriesById[order.bill_address.country_id].states"}
|
||||
.row
|
||||
.small-6.columns
|
||||
= validated_input t(:postcode), "order.bill_address.zipcode"
|
||||
|
||||
.small-6.columns.right
|
||||
= validated_select t(:country), "order.bill_address.country_id", {}, {"ng-init" => "order.bill_address.country_id = order.bill_address.country_id || #{DefaultCountry.id}", "ng-options" => "c.id as c.name for c in countries"}
|
||||
|
||||
.row
|
||||
.small-12.columns.text-right
|
||||
%button.primary{"ng-disabled" => "billing.$invalid", "ng-click" => "next($event)"}
|
||||
= t :next
|
||||
@@ -1,33 +0,0 @@
|
||||
%fieldset#details
|
||||
%ng-form{"ng-controller" => "DetailsCtrl", name: "details"}
|
||||
|
||||
%h5{"ng-class" => "{valid: details.$valid, dirty: details.$dirty || submitted}"}
|
||||
%span.right
|
||||
%label.label.round.alert.right
|
||||
%i.ofn-i_009-close
|
||||
%label.label.round.success.right
|
||||
%i.ofn-i_051-check-big
|
||||
= t :checkout_details
|
||||
|
||||
%accordion-group{"is-open" => "accordion.details",
|
||||
"ng-class" => "{valid: details.$valid, open: accordion.details}"}
|
||||
= render 'checkout/accordion_heading'
|
||||
|
||||
.row
|
||||
.small-6.columns
|
||||
= validated_input t(:first_name), "order.bill_address.firstname"
|
||||
.small-6.columns
|
||||
= validated_input t(:last_name), "order.bill_address.lastname"
|
||||
|
||||
.row
|
||||
.small-6.columns
|
||||
= validated_input t(:email), 'order.email', type: "email", inputmode: "email", "ofn-focus" => "accordion['details']"
|
||||
|
||||
.small-6.columns
|
||||
= validated_input t(:phone), 'order.bill_address.phone', inputmode: "tel"
|
||||
|
||||
.row
|
||||
.small-12.columns.text-right
|
||||
%button.primary{"ng-disabled" => "details.$invalid", "ng-click" => "login_or_next($event)"}
|
||||
= t :next
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
- content_for :injection_data do
|
||||
= inject_available_shipping_methods
|
||||
= inject_available_payment_methods
|
||||
= inject_saved_credit_cards
|
||||
|
||||
= form_for current_order,
|
||||
url: order_path(current_order),
|
||||
html: {name: "checkout",
|
||||
id: "checkout_form",
|
||||
novalidate: true,
|
||||
"ng-submit" => "purchase($event, checkout)"} do |f|
|
||||
|
||||
= render "checkout/details", f: f
|
||||
= render "checkout/billing", f: f
|
||||
= render "checkout/shipping", f: f
|
||||
= render "checkout/payment", f: f
|
||||
= render "checkout/already_ordered", f: f if show_bought_items?
|
||||
= render_terms_and_conditions
|
||||
%p
|
||||
%button.button.primary{ type: :submit, ng: { disabled: "terms_and_conditions_accepted == false || platform_tos_accepted == false" } }
|
||||
= t :checkout_send
|
||||
/ {{ checkout.$valid }}
|
||||
@@ -1,37 +0,0 @@
|
||||
%fieldset#payment
|
||||
%ng-form{"ng-controller" => "PaymentCtrl", name: "payment"}
|
||||
|
||||
%h5{"ng-class" => "{valid: payment.$valid, dirty: payment.$dirty || submitted}"}
|
||||
%span.right
|
||||
%label.label.round.alert.right
|
||||
%i.ofn-i_009-close
|
||||
%label.label.round.success.right
|
||||
%i.ofn-i_051-check-big
|
||||
= t :checkout_payment
|
||||
|
||||
%accordion-group{"is-open" => "accordion.payment",
|
||||
"ng-class" => "{valid: payment.$valid, open: accordion.payment}"}
|
||||
= render 'checkout/accordion_heading'
|
||||
|
||||
.row
|
||||
.small-12.medium-12.large-6.columns
|
||||
- available_payment_methods.each do |method|
|
||||
.row
|
||||
.small-12.columns
|
||||
%label
|
||||
= radio_button_tag "order[payments_attributes][][payment_method_id]", method.id, false,
|
||||
required: true,
|
||||
name: "order.payment_method_id",
|
||||
"ng-model" => "order.payment_method_id"
|
||||
= method.name
|
||||
= "(#{payment_method_price(method, @order)})"
|
||||
|
||||
%small.error.medium.input-text{"ng-show" => "!fieldValid('order.payment_method_id')"}
|
||||
= "{{ fieldErrors('order.payment_method_id') }}"
|
||||
|
||||
.row{"ng-if" => "order.payment_method_id == #{method.id}"}
|
||||
.small-12.columns
|
||||
= render partial: "spree/checkout/payment/#{method.method_type}", :locals => { :payment_method => method }
|
||||
.small-12.medium-12.large-6.columns
|
||||
#distributor_address.panel{"ng-show" => "Checkout.paymentMethod().description"}
|
||||
%span.pre-wrap {{ Checkout.paymentMethod().description }}
|
||||
@@ -1,10 +0,0 @@
|
||||
%p
|
||||
%input{ type: "checkbox",
|
||||
id: "accept_terms",
|
||||
ng: {
|
||||
model: "platform_tos_accepted",
|
||||
init: "platform_tos_accepted = #{platform_tos_already_accepted?}"
|
||||
}
|
||||
}
|
||||
%label.small{for: "accept_terms"}
|
||||
= t(".message_html", tos_link: link_to_platform_terms)
|
||||
@@ -1,61 +0,0 @@
|
||||
%fieldset#shipping
|
||||
%ng-form{"ng-controller" => "ShippingCtrl", name: "shipping"}
|
||||
|
||||
%h5{"ng-class" => "{valid: shipping.$valid, dirty: shipping.$dirty || submitted}"}
|
||||
%span.right
|
||||
%label.label.round.alert.right
|
||||
%i.ofn-i_009-close
|
||||
%label.label.round.success.right
|
||||
%i.ofn-i_051-check-big
|
||||
= t :checkout_shipping
|
||||
|
||||
%accordion-group{"is-open" => "accordion.shipping",
|
||||
"ng-class" => "{valid: shipping.$valid, open: accordion.shipping}"}
|
||||
= render 'checkout/accordion_heading'
|
||||
|
||||
.small-12.columns.medium-12.columns.large-6.columns
|
||||
%label{"ng-repeat" => "method in ShippingMethods.shipping_methods | orderBy: 'name'"}
|
||||
%input{type: :radio,
|
||||
required: true,
|
||||
name: "order.shipping_method_id",
|
||||
"ng-value" => "method.id",
|
||||
"ng-model" => "order.shipping_method_id"}
|
||||
{{ method.name }}
|
||||
%em.light{"ng-show" => "!method.price || method.price == 0"}
|
||||
= "(#{t(:checkout_method_free)})"
|
||||
%em.light{"ng-hide" => "!method.price || method.price == 0"}
|
||||
({{ method.price | localizeCurrency }})
|
||||
|
||||
%small.error.medium.input-text{"ng-show" => "!fieldValid('order.shipping_method_id')"}
|
||||
= "{{ fieldErrors('order.shipping_method_id') }}"
|
||||
|
||||
%label{"ng-if" => "Checkout.requireShipAddress()"}
|
||||
%input{type: :checkbox, "ng-model" => "Checkout.ship_address_same_as_billing"}
|
||||
= t :checkout_address_same
|
||||
|
||||
- if spree_current_user
|
||||
%label{"ng-if" => "Checkout.requireShipAddress()"}
|
||||
%input{type: :checkbox, "ng-model" => "Checkout.default_ship_address"}
|
||||
= t :checkout_default_ship_address
|
||||
|
||||
.small-12.columns.medium-12.columns.large-6.columns
|
||||
#distributor_address.panel{"ng-show" => "Checkout.shippingMethod().description"}
|
||||
%span{ style: "white-space: pre-wrap;" }{{ Checkout.shippingMethod().description }}
|
||||
%br/
|
||||
%br/
|
||||
- if @order.order_cycle.pickup_time_for(@order.distributor)
|
||||
= t :checkout_ready_for
|
||||
= @order.order_cycle.pickup_time_for(@order.distributor)
|
||||
|
||||
= f.fields_for :ship_address, @order.ship_address do |sa|
|
||||
= render 'checkout/shipping_ship_address'
|
||||
|
||||
.row
|
||||
.small-12.columns
|
||||
%label{ for: 'order_special_instructions'}= t(:checkout_instructions)
|
||||
= f.text_area :special_instructions, size: "60x4", "ng-model" => "order.special_instructions"
|
||||
|
||||
.row
|
||||
.small-12.columns.text-right
|
||||
%button.primary{"ng-disabled" => "shipping.$invalid", "ng-click" => "next($event)"}
|
||||
= t :next
|
||||
@@ -1,28 +0,0 @@
|
||||
.small-12.columns
|
||||
#ship_address{"ng-if" => "Checkout.requireShipAddress()"}
|
||||
%div.visible{"ng-if" => "!Checkout.ship_address_same_as_billing"}
|
||||
%div{ "ng-controller" => "CountryCtrl" }
|
||||
.row
|
||||
.small-6.columns
|
||||
= validated_input t(:first_name), "order.ship_address.firstname", "ofn-focus" => "accordion['shipping']"
|
||||
.small-6.columns
|
||||
= validated_input t(:last_name), "order.ship_address.lastname"
|
||||
.row
|
||||
.small-12.columns
|
||||
= validated_input t(:address), "order.ship_address.address1"
|
||||
.row
|
||||
.small-12.columns
|
||||
= validated_input t(:address2), "order.ship_address.address2", required: false
|
||||
.row
|
||||
.small-6.columns
|
||||
= validated_input t(:city), "order.ship_address.city"
|
||||
.small-6.columns
|
||||
= validated_select t(:state), "order.ship_address.state_id", {}, {"ng-options" => "s.id as s.name for s in countriesById[order.ship_address.country_id].states"}
|
||||
.row
|
||||
.small-6.columns
|
||||
= validated_input t(:postcode), "order.ship_address.zipcode"
|
||||
.small-6.columns.right
|
||||
= validated_select t(:country), "order.ship_address.country_id", {}, {"ng-init" => "order.ship_address.country_id = order.ship_address.country_id || #{DefaultCountry.id}", "ng-options" => "c.id as c.name for c in countries"}
|
||||
.row
|
||||
.small-6.columns
|
||||
= validated_input t(:phone), "order.ship_address.phone"
|
||||
@@ -1,34 +0,0 @@
|
||||
%orderdetails
|
||||
= form_for current_order, url: "#", html: {"ng-submit" => "purchase($event, checkout)"} do |f|
|
||||
%fieldset
|
||||
%legend
|
||||
= t :checkout_your_order
|
||||
%table
|
||||
%tr.subtotal
|
||||
%th
|
||||
= t :checkout_cart_total
|
||||
%td.cart-total.text-right= display_checkout_subtotal(@order)
|
||||
|
||||
- checkout_adjustments_for(current_order, exclude: [:shipping, :payment, :line_item]).each do |adjustment|
|
||||
%tr.adjustment
|
||||
%th= adjustment.label
|
||||
%td.text-right= adjustment.display_amount.to_html
|
||||
|
||||
%tr.shipping
|
||||
%th
|
||||
= t :checkout_shipping_price
|
||||
%td.text-right {{ Checkout.shippingPrice() | localizeCurrency }}
|
||||
|
||||
%tr.transaction-fee
|
||||
%th
|
||||
= t :payment_method_fee
|
||||
%td.text-right {{ Checkout.paymentPrice() | localizeCurrency }}
|
||||
|
||||
%tr.total
|
||||
%th
|
||||
= t :checkout_total_price
|
||||
%td.text-right {{ Checkout.cartTotal() | localizeCurrency }}
|
||||
|
||||
//= f.submit "Purchase", class: "button", "ofn-focus" => "accordion['payment']"
|
||||
%a.button.secondary{href: main_app.cart_url}
|
||||
= t :checkout_back_to_cart
|
||||
@@ -1,3 +0,0 @@
|
||||
%p
|
||||
%input{ type: 'checkbox', id: 'accept_terms', ng: { model: "terms_and_conditions_accepted", init: "terms_and_conditions_accepted=#{terms_and_conditions_already_accepted?}" } }
|
||||
%label.small{for: "accept_terms"}= t('.message_html', terms_and_conditions_link: link_to( t( '.link_text' ), current_order.distributor.terms_and_conditions, target: '_blank'))
|
||||
@@ -1,36 +0,0 @@
|
||||
- content_for(:title) do
|
||||
= t :checkout_title
|
||||
|
||||
- content_for :injection_data do
|
||||
= inject_enterprise_and_relatives
|
||||
= inject_available_countries
|
||||
|
||||
.darkswarm.footer-pad
|
||||
- content_for :order_cycle_form do
|
||||
%closing
|
||||
= t :checkout_now
|
||||
%p
|
||||
= t :checkout_order_ready
|
||||
%strong
|
||||
= pickup_time current_order_cycle
|
||||
|
||||
- content_for :ordercycle_sidebar do
|
||||
.show-for-large-up.large-4.columns
|
||||
= render partial: "shopping_shared/order_cycles"
|
||||
|
||||
= render partial: "shopping_shared/header"
|
||||
|
||||
.sub-header.show-for-medium-down
|
||||
= render partial: "shopping_shared/order_cycles"
|
||||
|
||||
%accordion{"close-others" => "false"}
|
||||
%checkout.row{"ng-controller" => "CheckoutCtrl"}
|
||||
.small-12.medium-8.columns
|
||||
- unless spree_current_user
|
||||
= render partial: "checkout/authentication"
|
||||
%div{"ng-show" => "enabled", "ng-controller" => "AccordionCtrl"}
|
||||
= render partial: "checkout/form"
|
||||
.small-12.medium-4.columns
|
||||
= render partial: "checkout/summary"
|
||||
|
||||
= render partial: "shared/footer"
|
||||
@@ -1,27 +0,0 @@
|
||||
.row
|
||||
.small-6.columns
|
||||
%label
|
||||
= t :first_name
|
||||
-# Changing name not permitted by default (in checkout) - can be enabled by setting an allow_name_change variable in $scope
|
||||
%input{type: :text, "ng-disabled" => "!allow_name_change", "ng-value" => "order.bill_address.firstname"}
|
||||
|
||||
.small-6.columns
|
||||
%label
|
||||
= t :last_name
|
||||
%input{type: :text, "ng-disabled" => "!allow_name_change", "ng-value" => "order.bill_address.lastname"}
|
||||
|
||||
.small-6.columns
|
||||
= validated_input t(:card_number), "secrets.card_number", maxlength: 19, autocomplete: "off"
|
||||
.small-6.columns
|
||||
= validated_input t(:card_securitycode), "secrets.card_verification_value"
|
||||
|
||||
.row
|
||||
.small-12.columns
|
||||
%label{for: "secrets.card_month"}
|
||||
= t :card_expiry_date
|
||||
|
||||
.row
|
||||
.small-6.columns
|
||||
%select{"ng-model" => "secrets.card_month", "ng-options" => "currMonth.value as currMonth.key for currMonth in months", name: "secrets.card_month"}
|
||||
.small-6.columns
|
||||
%select{"ng-model" => "secrets.card_year", "ng-options" => "year for year in years", name: "secrets.card_year"}
|
||||
@@ -1 +0,0 @@
|
||||
-# This file intentionally overrides the view in the spree_paypal_express gem
|
||||
@@ -1,22 +0,0 @@
|
||||
- content_for :injection_data do
|
||||
- if Stripe.publishable_key
|
||||
:javascript
|
||||
angular.module('Darkswarm').value("stripeObject", Stripe("#{Stripe.publishable_key}"))
|
||||
|
||||
.row{ "ng-show" => "savedCreditCards.length > 0" }
|
||||
.small-12.columns
|
||||
%h6= t('.used_saved_card')
|
||||
%select{ name: "selected_card", required: false, ng: { model: "secrets.selected_card", options: "card.id as card.formatted for card in savedCreditCards" } }
|
||||
%option{ value: "" }= "{{ secrets.selected_card ? '#{t('.enter_new_card')}' : '#{t('.choose_one')}' }}"
|
||||
|
||||
%h6{ ng: { if: '!secrets.selected_card' } }
|
||||
= t('.or_enter_new_card')
|
||||
|
||||
%div{ ng: { if: '!secrets.selected_card' } }
|
||||
%stripe-elements
|
||||
|
||||
- if spree_current_user
|
||||
.row
|
||||
.small-12.columns.text-right
|
||||
= check_box_tag 'secrets.save_requested_by_customer', '1', false, 'ng-model' => 'secrets.save_requested_by_customer'
|
||||
= label_tag 'secrets.save_requested_by_customer', t('.remember_this_card')
|
||||
@@ -1,22 +0,0 @@
|
||||
- content_for :injection_data do
|
||||
- if Stripe.publishable_key
|
||||
:javascript
|
||||
angular.module('Darkswarm').value("stripeObject", Stripe("#{Stripe.publishable_key}"))
|
||||
|
||||
.row{ "ng-show" => "savedCreditCards.length > 0" }
|
||||
.small-12.columns
|
||||
%h6= t('.used_saved_card')
|
||||
%select{ name: "selected_card", required: false, ng: { model: "secrets.selected_card", options: "card.id as card.formatted for card in savedCreditCards" } }
|
||||
%option{ value: "" }= "{{ secrets.selected_card ? '#{t('.enter_new_card')}' : '#{t('.choose_one')}' }}"
|
||||
|
||||
%h6{ ng: { if: '!secrets.selected_card' } }
|
||||
= t('.or_enter_new_card')
|
||||
|
||||
%div{ ng: { if: '!secrets.selected_card' } }
|
||||
%stripe-elements
|
||||
|
||||
- if spree_current_user
|
||||
.row
|
||||
.small-12.columns.text-right
|
||||
= check_box_tag 'secrets.save_requested_by_customer', '1', false, 'ng-model' => 'secrets.save_requested_by_customer'
|
||||
= label_tag 'secrets.save_requested_by_customer', t('.remember_this_card')
|
||||
@@ -84,24 +84,15 @@ Openfoodnetwork::Application.routes.draw do
|
||||
get "/stripe/authorize/:order_number", to: "stripe#authorize", as: :authorize_stripe
|
||||
end
|
||||
|
||||
constraints FeatureToggleConstraint.new(:split_checkout) do
|
||||
get '/checkout', to: 'split_checkout#edit'
|
||||
get '/checkout', to: 'split_checkout#edit'
|
||||
|
||||
constraints step: /(details|payment|summary)/ do
|
||||
get '/checkout/:step', to: 'split_checkout#edit', as: :checkout_step
|
||||
put '/checkout/:step', to: 'split_checkout#update', as: :checkout_update
|
||||
end
|
||||
|
||||
# Redirects to the new checkout for any other 'step' (ie. /checkout/cart from the legacy checkout)
|
||||
get '/checkout/:other', to: redirect('/checkout')
|
||||
constraints step: /(details|payment|summary)/ do
|
||||
get '/checkout/:step', to: 'split_checkout#edit', as: :checkout_step
|
||||
put '/checkout/:step', to: 'split_checkout#update', as: :checkout_update
|
||||
end
|
||||
|
||||
# When the split_checkout feature is disabled for the current user, use the legacy checkout
|
||||
constraints FeatureToggleConstraint.new(:split_checkout, negate: true) do
|
||||
get '/checkout', to: 'checkout#edit'
|
||||
put '/checkout', to: 'checkout#update', as: :update_checkout
|
||||
get '/checkout/:state', to: 'checkout#edit', as: :checkout_state
|
||||
end
|
||||
# Redirects to the new checkout for any other 'step' (ie. /checkout/cart from the legacy checkout)
|
||||
get '/checkout/:other', to: redirect('/checkout')
|
||||
|
||||
get 'embedded_shopfront/shopfront_session', to: 'application#shopfront_session'
|
||||
post 'embedded_shopfront/enable', to: 'application#enable_embedded_styles'
|
||||
|
||||
@@ -379,6 +379,7 @@ describe Admin::BulkLineItemsController, type: :controller do
|
||||
|
||||
order.shipments.map(&:refresh_rates)
|
||||
order.select_shipping_method(shipping_method.id)
|
||||
OrderWorkflow.new(order).advance_to_payment
|
||||
order.finalize!
|
||||
order.recreate_all_fees!
|
||||
order.create_tax_charge!
|
||||
|
||||
@@ -1,346 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe CheckoutController, type: :controller do
|
||||
include StripeStubs
|
||||
|
||||
let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) }
|
||||
let(:order_cycle) { create(:simple_order_cycle) }
|
||||
let(:order) { create(:order) }
|
||||
|
||||
before do
|
||||
allow(order).to receive(:checkout_allowed?).and_return true
|
||||
allow(controller).to receive(:check_authorization).and_return true
|
||||
end
|
||||
|
||||
it "redirects home when no distributor is selected" do
|
||||
get :edit
|
||||
expect(response).to redirect_to root_path
|
||||
end
|
||||
|
||||
it "redirects to the shop when no order cycle is selected" do
|
||||
allow(controller).to receive(:current_distributor).and_return(distributor)
|
||||
get :edit
|
||||
expect(response).to redirect_to shop_path
|
||||
end
|
||||
|
||||
it "redirects to shopfront with message if order cycle is expired" do
|
||||
allow(controller).to receive(:current_distributor).and_return(distributor)
|
||||
expect(controller).to receive(:current_order_cycle).and_return(order_cycle).at_least(:once)
|
||||
expect(controller).to receive(:current_order).and_return(order).at_least(:once)
|
||||
expect(order_cycle).to receive(:closed?).and_return(true)
|
||||
expect(order).to receive(:empty!)
|
||||
expect(order).to receive(:set_order_cycle!).with(nil)
|
||||
|
||||
get :edit
|
||||
|
||||
expect(response).to redirect_to shop_url
|
||||
expect(flash[:info]).to eq 'The order cycle you\'ve selected has just closed. Please try again!'
|
||||
end
|
||||
|
||||
it "redirects home with message if hub is not ready for checkout" do
|
||||
allow(distributor).to receive(:ready_for_checkout?) { false }
|
||||
allow(order).to receive_messages(distributor: distributor, order_cycle: order_cycle)
|
||||
allow(controller).to receive(:current_order).and_return(order)
|
||||
|
||||
expect(order).to receive(:empty!)
|
||||
expect(order).to receive(:set_distribution!).with(nil, nil)
|
||||
|
||||
get :edit
|
||||
|
||||
expect(response).to redirect_to root_url
|
||||
expect(flash[:info]).to eq('The hub you have selected is temporarily closed for orders. Please try again later.')
|
||||
end
|
||||
|
||||
describe "#update" do
|
||||
let(:user) { order.user }
|
||||
let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) }
|
||||
let(:order_cycle) { create(:order_cycle, distributors: [distributor]) }
|
||||
let(:order) { create(:order, distributor: distributor, order_cycle: order_cycle) }
|
||||
let(:payment_method) { distributor.payment_methods.first }
|
||||
let(:shipping_method) { distributor.shipping_methods.first }
|
||||
|
||||
before do
|
||||
order.contents.add(order_cycle.variants_distributed_by(distributor).first)
|
||||
|
||||
allow(controller).to receive(:current_distributor).and_return(distributor)
|
||||
allow(controller).to receive(:current_order_cycle).and_return(order_cycle)
|
||||
allow(controller).to receive(:current_order).and_return(order)
|
||||
allow(controller).to receive(:spree_current_user).and_return(user)
|
||||
|
||||
user.bill_address = create(:address)
|
||||
user.ship_address = create(:address)
|
||||
user.save!
|
||||
end
|
||||
|
||||
it "completes the order and redirects to the order confirmation page" do
|
||||
params = {
|
||||
"order" => {
|
||||
"bill_address_attributes" => order.bill_address.attributes,
|
||||
"default_bill_address" => false,
|
||||
"default_ship_address" => false,
|
||||
"email" => user.email,
|
||||
"payments_attributes" => [{ "payment_method_id" => payment_method.id }],
|
||||
"ship_address_attributes" => order.bill_address.attributes,
|
||||
"shipping_method_id" => shipping_method.id
|
||||
}
|
||||
}
|
||||
expect { post :update, params: params }.
|
||||
to change { Customer.count }.by(1)
|
||||
expect(order.completed?).to be true
|
||||
expect(response).to redirect_to order_path(order, order_token: order.token)
|
||||
end
|
||||
end
|
||||
|
||||
describe "running out of stock" do
|
||||
let(:order_cycle_distributed_variants) { double(:order_cycle_distributed_variants) }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:current_order).and_return(order)
|
||||
allow(order).to receive(:distributor).and_return(distributor)
|
||||
order.update(order_cycle: order_cycle)
|
||||
|
||||
allow(OrderCycleDistributedVariants).to receive(:new).and_return(order_cycle_distributed_variants)
|
||||
end
|
||||
|
||||
context "handling stock issues" do
|
||||
it "redirects when some items are out of stock" do
|
||||
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return false
|
||||
|
||||
get :edit
|
||||
expect(response).to redirect_to cart_path
|
||||
end
|
||||
|
||||
it "redirects when some items are not available" do
|
||||
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return true
|
||||
expect(order_cycle_distributed_variants).to receive(:distributes_order_variants?).with(order).and_return(false)
|
||||
|
||||
get :edit
|
||||
expect(response).to redirect_to cart_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "building the order" do
|
||||
before do
|
||||
allow(controller).to receive(:current_distributor).and_return(distributor)
|
||||
allow(controller).to receive(:current_order_cycle).and_return(order_cycle)
|
||||
allow(controller).to receive(:current_order).and_return(order)
|
||||
end
|
||||
|
||||
it "set shipping_address_from_distributor when re-rendering edit" do
|
||||
expect(order.updater).to receive(:shipping_address_from_distributor)
|
||||
allow(order).to receive(:update).and_return false
|
||||
spree_post :update, format: :json, order: {}
|
||||
end
|
||||
|
||||
it "set shipping_address_from_distributor when the order state cannot be advanced" do
|
||||
expect(order.updater).to receive(:shipping_address_from_distributor)
|
||||
allow(order).to receive(:update).and_return true
|
||||
allow(order).to receive(:next).and_return false
|
||||
spree_post :update, format: :json, order: {}
|
||||
end
|
||||
|
||||
context "#update with shipping_method_id" do
|
||||
let(:test_shipping_method_id) { "111" }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:order_completion_reset)
|
||||
allow(order).to receive(:update).and_return true
|
||||
allow(controller).to receive(:current_order).and_return order
|
||||
|
||||
# make order workflow pass through delivery
|
||||
allow(order).to receive(:next).twice do
|
||||
if order.state == 'cart'
|
||||
order.update_column :state, 'delivery'
|
||||
else
|
||||
order.update_column :state, 'complete'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "does not fail to update" do
|
||||
expect(controller).to_not receive(:clear_ship_address)
|
||||
spree_post :update, order: { shipping_method_id: test_shipping_method_id }
|
||||
end
|
||||
|
||||
it "does not send shipping_method_id to the order model as an attribute" do
|
||||
expect(order).to receive(:update).with({})
|
||||
spree_post :update, order: { shipping_method_id: test_shipping_method_id }
|
||||
end
|
||||
|
||||
it "selects the shipping_method in the order" do
|
||||
expect(order).to receive(:select_shipping_method).with(test_shipping_method_id)
|
||||
spree_post :update, order: { shipping_method_id: test_shipping_method_id }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when completing the order' do
|
||||
before do
|
||||
order.state = 'complete'
|
||||
order.save!
|
||||
allow(order).to receive(:update).and_return(true)
|
||||
allow(order).to receive(:next).and_return(true)
|
||||
allow(order).to receive(:set_distributor!).and_return(true)
|
||||
end
|
||||
|
||||
it "sets the new order's token to the same as the old order" do
|
||||
order = controller.current_order(true)
|
||||
spree_post :update, order: {}
|
||||
expect(controller.current_order.token).to eq order.token
|
||||
end
|
||||
|
||||
it 'expires the current order' do
|
||||
allow(controller).to receive(:expire_current_order)
|
||||
put :update, params: { order: {} }
|
||||
expect(controller).to have_received(:expire_current_order)
|
||||
end
|
||||
|
||||
it 'sets the access_token of the session' do
|
||||
put :update, params: { order: {} }
|
||||
expect(session[:access_token]).to eq(controller.current_order.token)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#expire_current_order' do
|
||||
it 'empties the order_id of the session' do
|
||||
expect(session).to receive(:[]=).with(:order_id, nil)
|
||||
controller.send(:expire_current_order)
|
||||
end
|
||||
|
||||
it 'resets the @current_order ivar' do
|
||||
controller.send(:expire_current_order)
|
||||
expect(controller.instance_variable_get(:@current_order)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "via xhr" do
|
||||
before do
|
||||
allow(controller).to receive(:current_distributor).and_return(distributor)
|
||||
|
||||
allow(controller).to receive(:current_order_cycle).and_return(order_cycle)
|
||||
allow(controller).to receive(:current_order).and_return(order)
|
||||
end
|
||||
|
||||
it "returns errors and flash if order.update fails" do
|
||||
spree_post :update, format: :json, order: {}
|
||||
expect(response.status).to eq(400)
|
||||
expect(response.body).to eq({ errors: assigns[:order].errors,
|
||||
flash: { error: order.errors.full_messages.to_sentence } }.to_json)
|
||||
end
|
||||
|
||||
it "returns errors and flash if order.next fails" do
|
||||
allow(order).to receive(:update).and_return true
|
||||
allow(order).to receive(:next).and_return false
|
||||
spree_post :update, format: :json, order: {}
|
||||
expect(response.body).to eq({ errors: assigns[:order].errors,
|
||||
flash: { error: "Payment could not be processed, please check the details you entered" } }.to_json)
|
||||
end
|
||||
|
||||
it "returns order confirmation url on success" do
|
||||
expect(controller).to receive(:expire_current_order)
|
||||
expect(controller).to receive(:build_new_order).with(order.distributor, order.token)
|
||||
|
||||
allow(order).to receive(:update).and_return true
|
||||
allow(order).to receive(:state).and_return "complete"
|
||||
|
||||
spree_post :update, format: :json, order: {}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to eq({ path: order_path(order, order_token: order.token) }.to_json)
|
||||
end
|
||||
|
||||
it "returns an error on unexpected failure" do
|
||||
allow(order).to receive(:update).and_raise
|
||||
|
||||
spree_post :update, format: :json, order: {}
|
||||
expect(response.status).to eq(400)
|
||||
expect(response.body).to eq({ errors: {},
|
||||
flash: { error: 'The checkout failed. Please let us know so that we can process your order.' } }.to_json)
|
||||
end
|
||||
|
||||
it "returns a specific error on Spree::Core::GatewayError" do
|
||||
allow(order).to receive(:update).and_raise(Spree::Core::GatewayError.new("Gateway blow up"))
|
||||
spree_post :update, format: :json, order: {}
|
||||
|
||||
expect(response.status).to eq(400)
|
||||
flash_message = "There was a problem with your payment information: %s" % 'Gateway blow up'
|
||||
expect(json_response["flash"]["error"]).to eq flash_message
|
||||
end
|
||||
|
||||
describe "stale object handling" do
|
||||
it "retries when a stale object error is encountered" do
|
||||
expect(controller).to receive(:expire_current_order)
|
||||
expect(controller).to receive(:build_new_order).with(order.distributor, order.token)
|
||||
|
||||
allow(order).to receive(:update).and_return true
|
||||
allow(controller).to receive(:state_callback)
|
||||
|
||||
# The first time, raise a StaleObjectError. The second time, succeed.
|
||||
allow(order).to receive(:next).once.
|
||||
and_raise(ActiveRecord::StaleObjectError.new(Spree::Variant.new, 'update'))
|
||||
allow(order).to receive(:next).once do
|
||||
order.update_column :state, 'complete'
|
||||
true
|
||||
end
|
||||
|
||||
spree_post :update, format: :json, order: {}
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
it "tries a maximum of 3 times before giving up and returning an error" do
|
||||
allow(order).to receive(:update).and_return true
|
||||
allow(order).to receive(:next) {
|
||||
raise ActiveRecord::StaleObjectError.new(Spree::Variant.new, 'update')
|
||||
}
|
||||
|
||||
spree_post :update, format: :json, order: {}
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Payment redirects" do
|
||||
before do
|
||||
allow(controller).to receive(:current_distributor) { distributor }
|
||||
allow(controller).to receive(:current_order_cycle) { order_cycle }
|
||||
allow(controller).to receive(:current_order) { order }
|
||||
allow(order).to receive(:update) { true }
|
||||
allow(order).to receive(:state) { "payment" }
|
||||
end
|
||||
|
||||
describe "redirecting to an external payment gateway" do
|
||||
let(:payment_method) { create(:payment_method) }
|
||||
|
||||
it "should call Stripe redirect and redirect if a path is provided" do
|
||||
expect(Spree::PaymentMethod).to receive(:find).and_return(payment_method)
|
||||
expect(payment_method).to receive(:external_gateway?).and_return(true)
|
||||
expect(payment_method).to receive(:external_payment_url).and_return("test_path")
|
||||
|
||||
spree_post :update,
|
||||
order: { payments_attributes: [{ payment_method_id: payment_method.id }] }
|
||||
|
||||
expect(response.body).to eq({ path: "test_path" }.to_json)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#action_failed" do
|
||||
let(:restart_checkout) { instance_double(OrderCheckoutRestart, call: true) }
|
||||
|
||||
before do
|
||||
controller.instance_variable_set(:@order, order)
|
||||
allow(OrderCheckoutRestart).to receive(:new) { restart_checkout }
|
||||
allow(controller).to receive(:current_order) { order }
|
||||
end
|
||||
|
||||
it "set shipping_address_from_distributor and restarts the checkout" do
|
||||
expect(order.updater).to receive(:shipping_address_from_distributor)
|
||||
expect(restart_checkout).to receive(:call)
|
||||
expect(controller).to receive(:respond_to)
|
||||
|
||||
controller.send(:action_failed)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -88,8 +88,8 @@ module PaymentGateways
|
||||
context "when processing fails" do
|
||||
let(:response) { false }
|
||||
|
||||
it "redirects to checkout_state_path with a flash error" do
|
||||
expect(post(:express)).to redirect_to checkout_state_path(:payment)
|
||||
it "redirects to checkout_step_path with a flash error" do
|
||||
expect(post(:express)).to redirect_to checkout_step_path(:payment)
|
||||
expect(flash[:error]).to eq "PayPal failed. "
|
||||
end
|
||||
end
|
||||
@@ -99,8 +99,8 @@ module PaymentGateways
|
||||
allow(response_mock).to receive(:success?).and_raise(SocketError)
|
||||
end
|
||||
|
||||
it "redirects to checkout_state_path with a flash error" do
|
||||
expect(post(:express)).to redirect_to checkout_state_path(:payment)
|
||||
it "redirects to checkout_step_path with a flash error" do
|
||||
expect(post(:express)).to redirect_to checkout_step_path(:payment)
|
||||
expect(flash[:error]).to eq "Could not connect to PayPal."
|
||||
end
|
||||
end
|
||||
|
||||
@@ -74,8 +74,6 @@ Please try again!"
|
||||
|
||||
context "using split checkout" do
|
||||
before do
|
||||
Flipper.enable(:split_checkout)
|
||||
|
||||
order.update_attribute :state, "confirmation"
|
||||
end
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@ describe SplitCheckoutController, type: :controller do
|
||||
let(:shipping_method) { distributor.shipping_methods.first }
|
||||
|
||||
before do
|
||||
Flipper.enable(:split_checkout)
|
||||
|
||||
exchange.variants << order.line_items.first.variant
|
||||
allow(controller).to receive(:current_order) { order }
|
||||
allow(controller).to receive(:spree_current_user) { user }
|
||||
|
||||
@@ -37,8 +37,6 @@ describe InjectionHelper, type: :helper do
|
||||
order = create(:order, distributor: current_distributor)
|
||||
allow(helper).to receive(:current_order) { order }
|
||||
allow(helper).to receive(:spree_current_user) { nil }
|
||||
expect(helper.inject_available_shipping_methods).to match sm.id.to_s
|
||||
expect(helper.inject_available_shipping_methods).to match sm.compute_amount(order).to_s
|
||||
end
|
||||
|
||||
it "injects payment methods" do
|
||||
@@ -47,8 +45,6 @@ describe InjectionHelper, type: :helper do
|
||||
order = create(:order, distributor: current_distributor)
|
||||
allow(helper).to receive(:current_order) { order }
|
||||
allow(helper).to receive(:spree_current_user) { nil }
|
||||
expect(helper.inject_available_payment_methods).to match pm.id.to_s
|
||||
expect(helper.inject_available_payment_methods).to match pm.name
|
||||
end
|
||||
|
||||
it "injects current order" do
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
describe "AccordionCtrl", ->
|
||||
ctrl = null
|
||||
scope = null
|
||||
CurrentHubMock =
|
||||
hub:
|
||||
id: 1
|
||||
|
||||
beforeEach ->
|
||||
module "Darkswarm"
|
||||
module ($provide)->
|
||||
$provide.value "CurrentHub", CurrentHubMock
|
||||
null
|
||||
inject ($controller, $rootScope) ->
|
||||
scope = $rootScope.$new()
|
||||
scope.order =
|
||||
id: 129
|
||||
ctrl = $controller 'AccordionCtrl', {$scope: scope}
|
||||
|
||||
it "defaults the details accordion to visible", ->
|
||||
expect(scope.accordion.details).toEqual true
|
||||
|
||||
it "changes accordion", ->
|
||||
scope.show "shipping"
|
||||
expect(scope.accordion["shipping"]).toEqual true
|
||||
@@ -1,100 +0,0 @@
|
||||
describe "CheckoutCtrl", ->
|
||||
ctrl = null
|
||||
scope = null
|
||||
Checkout = null
|
||||
CurrentUser = null
|
||||
CurrentHubMock =
|
||||
hub:
|
||||
id: 1
|
||||
localStorageService = null
|
||||
|
||||
beforeEach ->
|
||||
module("Darkswarm")
|
||||
angular.module('Darkswarm').value('user', {})
|
||||
angular.module('Darkswarm').value('currentHub', {id: 1})
|
||||
module ($provide)->
|
||||
$provide.value "CurrentHub", CurrentHubMock
|
||||
null
|
||||
Checkout =
|
||||
purchase: ->
|
||||
submit: ->
|
||||
navigate: ->
|
||||
bindFieldsToLocalStorage: ->
|
||||
order:
|
||||
id: 1
|
||||
email: "public"
|
||||
user_id: 1
|
||||
bill_address: 'bill_address'
|
||||
ship_address: 'ship address'
|
||||
secrets:
|
||||
card_number: "this is a secret"
|
||||
|
||||
describe "with user", ->
|
||||
beforeEach ->
|
||||
inject ($controller, $rootScope, _localStorageService_) ->
|
||||
localStorageService = _localStorageService_
|
||||
spyOn(localStorageService, "bind").and.callThrough()
|
||||
scope = $rootScope.$new()
|
||||
CurrentUser = { id: 1 }
|
||||
ctrl = $controller 'CheckoutCtrl', {$scope: scope, Checkout: Checkout, CurrentUser: CurrentUser }
|
||||
|
||||
describe "submitting", ->
|
||||
event =
|
||||
preventDefault: ->
|
||||
|
||||
beforeEach ->
|
||||
spyOn(Checkout, "purchase")
|
||||
scope.submitted = false
|
||||
|
||||
it "delegates to the service when valid", ->
|
||||
scope.purchase(event, {$valid: true})
|
||||
expect(Checkout.purchase).toHaveBeenCalled()
|
||||
expect(scope.submitted).toBe(true)
|
||||
|
||||
it "does nothing when invalid", ->
|
||||
scope.purchase(event, {$valid: false})
|
||||
expect(Checkout.purchase).not.toHaveBeenCalled()
|
||||
expect(scope.submitted).toBe(true)
|
||||
|
||||
it "is enabled", ->
|
||||
expect(scope.enabled).toEqual true
|
||||
|
||||
describe "Local storage", ->
|
||||
it "binds to localStorage when given a scope", inject ($timeout) ->
|
||||
prefix = "order_#{scope.order.id}#{CurrentUser.id or ""}#{CurrentHubMock.hub.id}"
|
||||
|
||||
field = scope.fieldsToBind[0]
|
||||
expect(localStorageService.bind).toHaveBeenCalledWith(scope, "Checkout.order.#{field}", Checkout.order[field], "#{prefix}_#{field}")
|
||||
expect(localStorageService.bind).toHaveBeenCalledWith(scope, "Checkout.ship_address_same_as_billing", true, "#{prefix}_sameasbilling")
|
||||
expect(localStorageService.bind).toHaveBeenCalledWith(scope, "Checkout.default_bill_address", false, "#{prefix}_defaultasbilladdress")
|
||||
expect(localStorageService.bind).toHaveBeenCalledWith(scope, "Checkout.default_ship_address", false, "#{prefix}_defaultasshipaddress")
|
||||
|
||||
it "it can retrieve data from localstorage", ->
|
||||
prefix = "order_#{scope.order.id}#{CurrentUser.id or ""}#{CurrentHubMock.hub.id}"
|
||||
scope.$digest()
|
||||
expect(localStorage.getItem("ls.#{prefix}_email")).toMatch "public"
|
||||
|
||||
it "does not store secrets in local storage", ->
|
||||
Checkout.secrets =
|
||||
card_number: "superfuckingsecret"
|
||||
scope.$digest()
|
||||
keys = (localStorage.key(i) for i in [0..localStorage.length])
|
||||
for key in keys
|
||||
expect(localStorage.getItem(key)).not.toMatch Checkout.secrets.card_number
|
||||
|
||||
describe "without user", ->
|
||||
beforeEach ->
|
||||
inject ($controller, $rootScope) ->
|
||||
scope = $rootScope.$new()
|
||||
ctrl = $controller 'CheckoutCtrl', {$scope: scope, Checkout: Checkout, CurrentUser: {}}
|
||||
|
||||
it "is disabled", ->
|
||||
expect(scope.enabled).toEqual false
|
||||
|
||||
it "does not store secrets in local storage", ->
|
||||
Checkout.secrets =
|
||||
card_number: "superfuckingsecret"
|
||||
scope.$digest()
|
||||
keys = (localStorage.key(i) for i in [0..localStorage.length])
|
||||
for key in keys
|
||||
expect(localStorage.getItem(key)).not.toMatch Checkout.secrets.card_number
|
||||
@@ -1,37 +0,0 @@
|
||||
describe "DetailsCtrl", ->
|
||||
ctrl = null
|
||||
scope = null
|
||||
order = null
|
||||
CurrentUser = null
|
||||
|
||||
beforeEach ->
|
||||
module("Darkswarm")
|
||||
inject ($controller, $rootScope) ->
|
||||
scope = $rootScope.$new()
|
||||
CurrentUser = { id: 1 }
|
||||
ctrl = $controller 'DetailsCtrl', { $scope: scope, CurrentUser: CurrentUser }
|
||||
|
||||
|
||||
it "finds a field by path", ->
|
||||
scope.details =
|
||||
path: "test"
|
||||
expect(scope.field('path')).toEqual "test"
|
||||
|
||||
it "tests validity", ->
|
||||
scope.details =
|
||||
path:
|
||||
$dirty: true
|
||||
$invalid: true
|
||||
expect(scope.fieldValid('path')).toEqual false
|
||||
|
||||
it "returns errors by path", ->
|
||||
scope.Order =
|
||||
errors: ->
|
||||
scope.details =
|
||||
path:
|
||||
$error:
|
||||
email: true
|
||||
required: true
|
||||
expect(scope.fieldErrors('path')).toEqual ["must be email address", "can't be blank"].join ", "
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
describe "PaymentCtrl", ->
|
||||
ctrl = null
|
||||
scope = null
|
||||
card1 = { id: 1, is_default: false }
|
||||
card2 = { id: 3, is_default: true }
|
||||
cards = [card1, card2]
|
||||
|
||||
beforeEach ->
|
||||
module("Darkswarm")
|
||||
angular.module('Darkswarm').value('savedCreditCards', cards)
|
||||
inject ($controller, $rootScope) ->
|
||||
scope = $rootScope.$new()
|
||||
scope.secrets = {}
|
||||
ctrl = $controller 'PaymentCtrl', {$scope: scope}
|
||||
|
||||
it "sets the default card id as the selected_card", ->
|
||||
expect(scope.secrets.selected_card).toEqual card2.id
|
||||
@@ -1,164 +0,0 @@
|
||||
describe "ShopVariantCtrl", ->
|
||||
ctrl = null
|
||||
scope = null
|
||||
CartMock = null
|
||||
|
||||
beforeEach ->
|
||||
module 'Darkswarm'
|
||||
|
||||
inject ($rootScope, $controller, $modal)->
|
||||
scope = $rootScope.$new()
|
||||
scope.$watchGroup = ->
|
||||
scope.variant = {
|
||||
on_demand: true
|
||||
product: {group_buy: true}
|
||||
line_item: {
|
||||
quantity: undefined
|
||||
max_quantity: undefined
|
||||
}
|
||||
}
|
||||
CartMock =
|
||||
adjust: ->
|
||||
true
|
||||
ctrl = $controller 'ShopVariantCtrl', {$scope: scope, $modal: $modal, Cart: CartMock, Shopfront: {}}
|
||||
|
||||
it "initializes the quantity for shop display", ->
|
||||
expect(scope.variant.line_item.quantity).toEqual 0
|
||||
|
||||
it "adds an item to the cart", ->
|
||||
scope.add 1
|
||||
expect(scope.variant.line_item.quantity).toEqual 1
|
||||
|
||||
it "adds to the existing quantity", ->
|
||||
scope.add 1
|
||||
scope.add 5
|
||||
expect(scope.variant.line_item.quantity).toEqual 6
|
||||
|
||||
it "adds to an invalid quantity", ->
|
||||
scope.$apply ->
|
||||
scope.variant.line_item.quantity = -5
|
||||
scope.add 1
|
||||
expect(scope.variant.line_item.quantity).toEqual 1
|
||||
|
||||
it "adds to an undefined quantity", ->
|
||||
scope.$apply ->
|
||||
scope.variant.line_item.quantity = undefined
|
||||
scope.add 1
|
||||
expect(scope.variant.line_item.quantity).toEqual 1
|
||||
|
||||
it "adds to the max quantity", ->
|
||||
scope.addMax 5
|
||||
expect(scope.variant.line_item.quantity).toEqual 0
|
||||
expect(scope.variant.line_item.max_quantity).toEqual 5
|
||||
|
||||
it "adds to an undefined max quantity", ->
|
||||
scope.variant.line_item.quantity = 3
|
||||
scope.variant.line_item.max_quantity = undefined
|
||||
scope.addMax 1
|
||||
expect(scope.variant.line_item.max_quantity).toEqual 4
|
||||
|
||||
it "adds to the max quantity to be at least min quantity", ->
|
||||
scope.$apply ->
|
||||
scope.variant.line_item.max_quantity = 2
|
||||
|
||||
scope.$apply ->
|
||||
scope.add 3
|
||||
|
||||
expect(scope.variant.line_item.quantity).toEqual 3
|
||||
expect(scope.variant.line_item.max_quantity).toEqual 3
|
||||
|
||||
it "decreases the min quantity to not exceed max quantity", ->
|
||||
scope.$apply ->
|
||||
scope.variant.line_item.quantity = 3
|
||||
scope.variant.line_item.max_quantity = 5
|
||||
|
||||
scope.$apply ->
|
||||
scope.addMax -3
|
||||
|
||||
expect(scope.variant.line_item.quantity).toEqual 2
|
||||
expect(scope.variant.line_item.max_quantity).toEqual 2
|
||||
|
||||
it "caps at the available quantity", ->
|
||||
scope.$apply ->
|
||||
scope.variant.on_demand = false
|
||||
scope.variant.on_hand = 3
|
||||
scope.variant.line_item.quantity = 5
|
||||
scope.variant.line_item.max_quantity = 7
|
||||
|
||||
expect(scope.variant.line_item.quantity).toEqual 3
|
||||
expect(scope.variant.line_item.max_quantity).toEqual 3
|
||||
|
||||
it "allows adding when variant is on demand", ->
|
||||
expect(scope.canAdd(5000)).toEqual true
|
||||
|
||||
it "denies adding if variant is out of stock", ->
|
||||
scope.variant.on_demand = false
|
||||
scope.variant.on_hand = 0
|
||||
|
||||
expect(scope.canAdd(1)).toEqual false
|
||||
|
||||
it "denies adding if stock is limitted", ->
|
||||
scope.variant.on_demand = false
|
||||
scope.variant.on_hand = 5
|
||||
|
||||
expect(scope.canAdd(4)).toEqual true
|
||||
expect(scope.canAdd(5)).toEqual true
|
||||
expect(scope.canAdd(6)).toEqual false
|
||||
|
||||
scope.add 3
|
||||
expect(scope.canAdd(2)).toEqual true
|
||||
expect(scope.canAdd(3)).toEqual false
|
||||
|
||||
it "denies adding if quantity is too high", ->
|
||||
scope.variant.on_demand = false
|
||||
scope.variant.on_hand = 5
|
||||
scope.variant.line_item.quantity = 7
|
||||
scope.variant.line_item.max_quantity = 7
|
||||
|
||||
expect(scope.canAdd(1)).toEqual false
|
||||
expect(scope.canAddMax(1)).toEqual false
|
||||
|
||||
it "allows decrease when quantity is too high", ->
|
||||
scope.variant.on_demand = false
|
||||
scope.variant.on_hand = 5
|
||||
scope.variant.line_item.quantity = 7
|
||||
scope.variant.line_item.max_quantity = 7
|
||||
expect(scope.canAdd(-1)).toEqual true
|
||||
expect(scope.canAddMax(-1)).toEqual true
|
||||
|
||||
it "allows increase when quantity is negative", ->
|
||||
scope.variant.on_demand = false
|
||||
scope.variant.on_hand = 5
|
||||
scope.variant.line_item.quantity = -3
|
||||
scope.variant.line_item.max_quantity = -3
|
||||
expect(scope.canAdd(1)).toEqual true
|
||||
expect(scope.canAddMax(1)).toEqual false
|
||||
|
||||
scope.variant.line_item.quantity = 1
|
||||
expect(scope.canAddMax(1)).toEqual true
|
||||
|
||||
it "denies decrease when quantity is negative", ->
|
||||
scope.variant.on_demand = false
|
||||
scope.variant.on_hand = 5
|
||||
scope.variant.line_item.quantity = -3
|
||||
scope.variant.line_item.max_quantity = -3
|
||||
expect(scope.canAdd(-1)).toEqual false
|
||||
expect(scope.canAddMax(-1)).toEqual false
|
||||
|
||||
it "denies declaring max quantity before item is in cart", ->
|
||||
expect(scope.canAddMax(1)).toEqual false
|
||||
|
||||
it "allows declaring max quantity when item is in cart", ->
|
||||
scope.add 1
|
||||
expect(scope.canAddMax(1)).toEqual true
|
||||
|
||||
it "denies adding if stock is limitted", ->
|
||||
scope.variant.on_demand = false
|
||||
scope.variant.on_hand = 5
|
||||
scope.variant.line_item.quantity = 1
|
||||
scope.variant.line_item.max_quantity = 1
|
||||
|
||||
expect(scope.canAddMax(3)).toEqual true
|
||||
expect(scope.canAddMax(4)).toEqual true
|
||||
expect(scope.canAddMax(5)).toEqual false
|
||||
expect(scope.canAddMax(6)).toEqual false
|
||||
@@ -224,7 +224,7 @@ module Spree
|
||||
}
|
||||
let(:tax_category) { create(:tax_category, name: "Shipping", tax_rates: [tax_rate] ) }
|
||||
let(:hub) { create(:distributor_enterprise, charges_sales_tax: true) }
|
||||
let(:order) { create(:order, distributor: hub) }
|
||||
let(:order) { create(:order, distributor: hub, state: 'payment') }
|
||||
let(:line_item) { create(:line_item, order: order) }
|
||||
|
||||
let(:shipping_method) {
|
||||
@@ -348,7 +348,7 @@ module Spree
|
||||
let(:line_item) { create(:line_item, variant: variant) }
|
||||
let(:order) {
|
||||
create(:order, line_items: [line_item], order_cycle: order_cycle,
|
||||
distributor: coordinator)
|
||||
distributor: coordinator, state: 'payment')
|
||||
}
|
||||
let(:fee) { order.all_adjustments.reload.enterprise_fee.first }
|
||||
let(:fee_tax) { fee.adjustments.tax.first }
|
||||
@@ -508,7 +508,7 @@ module Spree
|
||||
let!(:zone) { create(:zone_with_member) }
|
||||
let!(:tax_category) { create(:tax_category, name: "Tax Test") }
|
||||
let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) }
|
||||
let(:order) { create(:order, distributor: distributor) }
|
||||
let(:order) { create(:order, distributor: distributor, state: "payment") }
|
||||
let(:included_in_price) { true }
|
||||
let(:tax_rate) {
|
||||
create(:tax_rate, included_in_price: included_in_price, zone: zone,
|
||||
|
||||
@@ -6,44 +6,18 @@ describe Spree::Order::Checkout do
|
||||
let(:order) { Spree::Order.new }
|
||||
|
||||
context "with default state machine" do
|
||||
let(:transitions) do
|
||||
[
|
||||
{ address: :delivery },
|
||||
{ delivery: :payment },
|
||||
{ payment: :complete },
|
||||
{ delivery: :complete }
|
||||
]
|
||||
end
|
||||
|
||||
it "has the following transitions" do
|
||||
transitions.each do |transition|
|
||||
transition = Spree::Order.find_transition(from: transition.keys.first,
|
||||
to: transition.values.first)
|
||||
expect(transition).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
it "does not have a transition from delivery to confirm" do
|
||||
transition = Spree::Order.find_transition(from: :delivery, to: :confirm)
|
||||
expect(transition).to be_nil
|
||||
end
|
||||
|
||||
it '.find_transition when contract was broken' do
|
||||
expect(Spree::Order.find_transition({ foo: :bar, baz: :dog })).to be_falsy
|
||||
end
|
||||
|
||||
context "#checkout_steps" do
|
||||
context "when payment not required" do
|
||||
before { allow(order).to receive_messages payment_required?: false }
|
||||
specify do
|
||||
expect(order.checkout_steps).to eq %w(address delivery complete)
|
||||
expect(order.checkout_steps).to eq %w(address delivery confirmation complete)
|
||||
end
|
||||
end
|
||||
|
||||
context "when payment required" do
|
||||
before { allow(order).to receive_messages payment_required?: true }
|
||||
specify do
|
||||
expect(order.checkout_steps).to eq %w(address delivery payment complete)
|
||||
expect(order.checkout_steps).to eq %w(address delivery payment confirmation complete)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -112,9 +86,9 @@ describe Spree::Order::Checkout do
|
||||
allow(order).to receive_messages payment_required?: false
|
||||
end
|
||||
|
||||
it "transitions to complete" do
|
||||
it "transitions to confirmation" do
|
||||
order.next!
|
||||
expect(order.state).to eq "complete"
|
||||
expect(order.state).to eq 'confirmation'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,6 +24,8 @@ describe Spree::Order do
|
||||
before { allow(order).to receive_messages process_payments!: true }
|
||||
|
||||
it "should finalize order when transitioning to complete state" do
|
||||
order.next
|
||||
expect(order.state).to eq "confirmation"
|
||||
expect(order).to receive(:finalize!)
|
||||
order.next!
|
||||
end
|
||||
@@ -32,6 +34,8 @@ describe Spree::Order do
|
||||
before { allow(order).to receive_messages process_payments!: false }
|
||||
|
||||
it "should still complete the order" do
|
||||
order.next
|
||||
expect(order.state).to eq "confirmation"
|
||||
order.next
|
||||
expect(order.state).to eq "complete"
|
||||
end
|
||||
@@ -42,6 +46,8 @@ describe Spree::Order do
|
||||
before { allow(order).to receive_messages process_payments!: false }
|
||||
|
||||
it "can transition to complete" do
|
||||
order.next
|
||||
expect(order.state).to eq "confirmation"
|
||||
order.next
|
||||
expect(order.state).to eq "complete"
|
||||
end
|
||||
|
||||
@@ -152,6 +152,10 @@ module Spree
|
||||
label: "legacy", originator_type: "Spree::TaxRate")
|
||||
}
|
||||
|
||||
before do
|
||||
order.update(state: "payment")
|
||||
end
|
||||
|
||||
it "removes any legacy tax adjustments on order" do
|
||||
order.create_tax_charge!
|
||||
|
||||
|
||||
@@ -690,6 +690,7 @@ describe Spree::Order do
|
||||
|
||||
before do
|
||||
allow(order).to receive(:tax_zone) { shipping_tax_rate.zone }
|
||||
order.update_attribute(:state, 'payment')
|
||||
order.reload
|
||||
order.create_tax_charge!
|
||||
end
|
||||
@@ -1269,7 +1270,8 @@ describe Spree::Order do
|
||||
order.next!
|
||||
order.payments << create(:payment, order: order)
|
||||
|
||||
expect { order.next! }.to change { order.state }.from("payment").to("complete")
|
||||
expect { order.next! }.to change { order.state }.from("payment").to("confirmation")
|
||||
expect { order.next! }.to change { order.state }.from("confirmation").to("complete")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1303,7 +1305,8 @@ describe Spree::Order do
|
||||
it "skips the payment state" do
|
||||
advance_to_delivery_state(order)
|
||||
|
||||
expect { order.next! }.to change { order.state }.from("delivery").to("complete")
|
||||
expect { order.next! }.to change { order.state }.from("delivery").to("confirmation")
|
||||
expect { order.next! }.to change { order.state }.from("confirmation").to("complete")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -282,61 +282,64 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
context "#adjust" do
|
||||
describe "#adjust" do
|
||||
let!(:country) { create(:country, name: "Default Country") }
|
||||
let!(:state) { create(:state, name: "Default State", country: country) }
|
||||
let!(:zone) { create(:zone_with_member, default_tax: true, member: country ) }
|
||||
let!(:category) { create(:tax_category, name: "Taxable Foo") }
|
||||
let!(:category2) { create(:tax_category, name: "Non Taxable") }
|
||||
let!(:rate1) {
|
||||
create(:tax_rate, amount: 0.10, zone: zone, tax_category: category)
|
||||
}
|
||||
let!(:rate2) {
|
||||
create(:tax_rate, amount: 0.05, zone: zone, tax_category: category)
|
||||
}
|
||||
let(:hub) { create(:distributor_enterprise, charges_sales_tax: true) }
|
||||
let(:address) { create(:address, state: country.states.first, country: country) }
|
||||
let!(:order) {
|
||||
create(:order_with_line_items, line_items_count: 2, distributor: hub,
|
||||
ship_address: address)
|
||||
}
|
||||
let!(:taxable) { order.line_items.first.variant.product }
|
||||
let!(:nontaxable) { order.line_items.last.variant.product }
|
||||
|
||||
before do
|
||||
@country = create(:country)
|
||||
@zone = create(:zone, name: "Country Zone", default_tax: true, zone_members: [])
|
||||
@zone.zone_members.create(zoneable: @country)
|
||||
@category = Spree::TaxCategory.create(name: "Taxable Foo")
|
||||
@category2 = Spree::TaxCategory.create(name: "Non Taxable")
|
||||
@rate1 = Spree::TaxRate.create(
|
||||
amount: 0.10,
|
||||
calculator: ::Calculator::DefaultTax.new,
|
||||
tax_category: @category,
|
||||
zone: @zone
|
||||
)
|
||||
@rate2 = Spree::TaxRate.create(
|
||||
amount: 0.05,
|
||||
calculator: ::Calculator::DefaultTax.new,
|
||||
tax_category: @category,
|
||||
zone: @zone
|
||||
)
|
||||
@order = Spree::Order.create!
|
||||
@taxable = create(:product, tax_category: @category)
|
||||
@nontaxable = create(:product, tax_category: @category2)
|
||||
taxable.update(tax_category: category)
|
||||
nontaxable.update(tax_category: category2)
|
||||
order.line_items.delete_all
|
||||
end
|
||||
|
||||
context "not taxable line item " do
|
||||
let!(:line_item) { @order.contents.add(@nontaxable.variants.first, 1) }
|
||||
let!(:line_item) { order.contents.add(nontaxable.variants.first, 1) }
|
||||
|
||||
it "should not create a tax adjustment" do
|
||||
Spree::TaxRate.adjust(@order, @order.line_items)
|
||||
Spree::TaxRate.adjust(order, order.line_items)
|
||||
expect(line_item.adjustments.tax.charge.count).to eq 0
|
||||
end
|
||||
|
||||
it "should not create a refund" do
|
||||
Spree::TaxRate.adjust(@order, @order.line_items)
|
||||
Spree::TaxRate.adjust(order, order.line_items)
|
||||
expect(line_item.adjustments.credit.count).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context "taxable line item" do
|
||||
let!(:line_item) { @order.contents.add(@taxable.variants.first, 1) }
|
||||
let!(:line_item) { order.contents.add(taxable.variants.first, 1) }
|
||||
|
||||
before do
|
||||
@rate1.update_column(:included_in_price, true)
|
||||
@rate2.update_column(:included_in_price, true)
|
||||
rate1.update_column(:included_in_price, true)
|
||||
rate2.update_column(:included_in_price, true)
|
||||
end
|
||||
|
||||
context "when price includes tax" do
|
||||
context "when zone is contained by default tax zone" do
|
||||
it "should create two adjustments, one for each tax rate" do
|
||||
Spree::TaxRate.adjust(@order, @order.line_items)
|
||||
Spree::TaxRate.adjust(order, order.line_items)
|
||||
expect(line_item.adjustments.count).to eq 2
|
||||
end
|
||||
|
||||
it "should not create a tax refund" do
|
||||
Spree::TaxRate.adjust(@order, @order.line_items)
|
||||
Spree::TaxRate.adjust(order, order.line_items)
|
||||
expect(line_item.adjustments.credit.count).to eq 0
|
||||
end
|
||||
end
|
||||
@@ -344,57 +347,59 @@ module Spree
|
||||
context "when order's zone is neither the default zone, or included in the default zone, but matches the rate's zone" do
|
||||
before do
|
||||
# With no zone members, this zone will not contain anything
|
||||
@zone.zone_members.delete_all
|
||||
zone.zone_members.delete_all
|
||||
end
|
||||
|
||||
it "should create an adjustment" do
|
||||
Spree::TaxRate.adjust(@order, @order.line_items)
|
||||
Spree::TaxRate.adjust(order, order.line_items)
|
||||
expect(line_item.adjustments.charge.count).to eq 2
|
||||
end
|
||||
|
||||
it "should not create a tax refund for each tax rate" do
|
||||
Spree::TaxRate.adjust(@order, @order.line_items)
|
||||
Spree::TaxRate.adjust(order, order.line_items)
|
||||
expect(line_item.adjustments.credit.count).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when order's zone does not match default zone, is not included in the default zone, AND does not match the rate's zone" do
|
||||
before do
|
||||
@new_zone = create(:zone, name: "New Zone", default_tax: false)
|
||||
@new_country = create(:country, name: "New Country")
|
||||
@new_zone.zone_members.create(zoneable: @new_country)
|
||||
@new_state = create(:state, country: @new_country)
|
||||
@order.ship_address = create(:address, country: @new_country, state: @new_state)
|
||||
@order.save
|
||||
end
|
||||
context "when order's zone does not match default zone, is not included in the default zone, AND does not match the rate's zone" do
|
||||
let!(:other_country) { create(:country, name: "Other Country") }
|
||||
let!(:other_state) { create(:state, name: "Other State", country: other_country) }
|
||||
let!(:other_address) { create(:address, state: other_state, country: other_country) }
|
||||
let!(:other_zone) {
|
||||
create(:zone_with_member, name: "Other Zone", default_tax: false,
|
||||
member: other_country)
|
||||
}
|
||||
|
||||
it "should not create positive adjustments" do
|
||||
Spree::TaxRate.adjust(@order, @order.line_items)
|
||||
expect(line_item.adjustments.charge.count).to eq 0
|
||||
end
|
||||
before do
|
||||
order.update(ship_address: other_address)
|
||||
order.all_adjustments.delete_all
|
||||
end
|
||||
|
||||
it "should create a tax refund for each tax rate" do
|
||||
Spree::TaxRate.adjust(@order, @order.line_items)
|
||||
expect(line_item.adjustments.credit.count).to eq 2
|
||||
it "should not create positive adjustments" do
|
||||
Spree::TaxRate.adjust(order, order.line_items)
|
||||
expect(line_item.adjustments.charge.count).to eq 0
|
||||
end
|
||||
|
||||
it "should create a tax refund for each tax rate" do
|
||||
Spree::TaxRate.adjust(order, order.line_items)
|
||||
expect(line_item.adjustments.credit.count).to eq 2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when price does not include tax" do
|
||||
before do
|
||||
allow(@order).to receive(:tax_zone) { @zone }
|
||||
|
||||
[@rate1, @rate2].each do |rate|
|
||||
rate.included_in_price = false
|
||||
rate.zone = @zone
|
||||
rate.save
|
||||
allow(order).to receive(:tax_zone) { zone }
|
||||
[rate1, rate2].each do |rate|
|
||||
rate.update(included_in_price: false, zone: zone)
|
||||
end
|
||||
|
||||
Spree::TaxRate.adjust(order, order.line_items)
|
||||
end
|
||||
|
||||
it "should not delete adjustments for complete order when taxrate is deleted" do
|
||||
@order.update_column :completed_at, Time.now
|
||||
@rate1.destroy!
|
||||
@rate2.destroy!
|
||||
rate1.destroy!
|
||||
rate2.destroy!
|
||||
expect(line_item.adjustments.count).to eq 2
|
||||
end
|
||||
|
||||
@@ -407,10 +412,9 @@ module Spree
|
||||
end
|
||||
|
||||
it "should remove adjustments when tax_zone is removed" do
|
||||
Spree::TaxRate.adjust(@order, @order.line_items)
|
||||
expect(line_item.adjustments.count).to eq 2
|
||||
allow(@order).to receive(:tax_zone) { nil }
|
||||
Spree::TaxRate.adjust(@order, @order.line_items)
|
||||
allow(order).to receive(:tax_zone) { nil }
|
||||
Spree::TaxRate.adjust(order, order.line_items)
|
||||
expect(line_item.adjustments.count).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
# This is the first example of testing concurrency in the Open Food Network.
|
||||
# If we want to do this more often, we should look at:
|
||||
#
|
||||
# https://github.com/forkbreak/fork_break
|
||||
#
|
||||
# The concurrency flag enables multiple threads to see the same database
|
||||
# without isolated transactions.
|
||||
describe "Concurrent checkouts", concurrency: true, type: :request do
|
||||
include AuthenticationHelper
|
||||
include ShopWorkflow
|
||||
|
||||
let(:order_cycle) { create(:order_cycle) }
|
||||
let(:distributor) { order_cycle.distributors.first }
|
||||
let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) }
|
||||
let(:address) { create(:address) }
|
||||
let(:payment_method) { create(:payment_method, distributors: [distributor]) }
|
||||
let(:breakpoint) { Mutex.new }
|
||||
|
||||
let(:address_params) { address.attributes.except("id") }
|
||||
let(:order_params) {
|
||||
{
|
||||
"payments_attributes" => [
|
||||
{
|
||||
"payment_method_id" => payment_method.id,
|
||||
"amount" => order.total
|
||||
}
|
||||
],
|
||||
"bill_address_attributes" => address_params,
|
||||
"ship_address_attributes" => address_params,
|
||||
}
|
||||
}
|
||||
let(:params) { { format: :json, order: order_params } }
|
||||
|
||||
before do
|
||||
# Create a valid order ready for checkout:
|
||||
create(:shipping_method, distributors: [distributor])
|
||||
variant = order_cycle.variants_distributed_by(distributor).first
|
||||
order.line_items << create(:line_item, variant: variant)
|
||||
|
||||
set_order(order)
|
||||
login_as(order.user)
|
||||
end
|
||||
|
||||
it "handles two concurrent orders successfully" do
|
||||
breakpoint.lock
|
||||
breakpoint_reached_counter = 0
|
||||
|
||||
# Set a breakpoint after loading the order and before advancing the order's
|
||||
# state and making payments. If two requests reach this breakpoint at the
|
||||
# same time, they are in a race condition and bad things can happen.
|
||||
# Examples are processing payments twice or selling more than we have.
|
||||
allow_any_instance_of(CheckoutController).
|
||||
to receive(:checkout_workflow).
|
||||
and_wrap_original do |method, *args|
|
||||
|
||||
breakpoint_reached_counter += 1
|
||||
breakpoint.synchronize {}
|
||||
method.call(*args)
|
||||
end
|
||||
|
||||
# Starting two checkout threads. The controller code will determine if
|
||||
# these two threads are synchronised correctly or run into a race condition.
|
||||
#
|
||||
# 1. If the controller synchronises correctly:
|
||||
# The first thread locks required resources and then waits at the
|
||||
# breakpoint. The second thread waits for the first one.
|
||||
# 2. If the controller fails to prevent the race condition:
|
||||
# Both threads load required resources and wait at the breakpoint to do
|
||||
# the same checkout action.
|
||||
threads = [
|
||||
Thread.new { put update_checkout_path, params: params },
|
||||
Thread.new { put update_checkout_path, params: params },
|
||||
]
|
||||
|
||||
# Wait for the first thread to reach the breakpoint:
|
||||
Timeout.timeout(1) do
|
||||
sleep 0.1 while breakpoint_reached_counter < 1
|
||||
end
|
||||
|
||||
# Give the second thread a chance to reach the breakpoint, too.
|
||||
# But we hope that it waits for the first thread earlier and doesn't
|
||||
# reach the breakpoint yet.
|
||||
sleep 1
|
||||
expect(breakpoint_reached_counter).to eq 1
|
||||
|
||||
# Let the requests continue and finish.
|
||||
breakpoint.unlock
|
||||
threads.each(&:join)
|
||||
|
||||
# Verify that the checkout happened once.
|
||||
order.reload
|
||||
expect(order.completed?).to be true
|
||||
expect(order.payments.count).to eq 1
|
||||
end
|
||||
end
|
||||
@@ -44,7 +44,7 @@ describe "checking out an order that initially fails", type: :request do
|
||||
set_order order
|
||||
end
|
||||
|
||||
context "when shipping and payment fees apply" do
|
||||
pending "when shipping and payment fees apply" do
|
||||
let(:calculator) { Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10) }
|
||||
|
||||
before do
|
||||
|
||||
@@ -46,6 +46,7 @@ describe "checking out an order with a paypal express payment method", type: :re
|
||||
payment_method.calculator = calculator
|
||||
payment_method.save!
|
||||
order.payments.create!(payment_method_id: payment_method.id, amount: order.total)
|
||||
order.next
|
||||
end
|
||||
|
||||
it "destroys the old payment and processes the order" do
|
||||
|
||||
@@ -37,28 +37,13 @@ describe 'checkout endpoints', type: :request do
|
||||
context "when getting the cart `/checkout/cart`" do
|
||||
let(:path) { "/checkout/cart" }
|
||||
|
||||
context "using the legacy checkout" do
|
||||
it "do not redirect" do
|
||||
get path
|
||||
puts response.redirect_url
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
it "redirect to the split checkout" do
|
||||
get path
|
||||
expect(response.status).to redirect_to("/checkout")
|
||||
|
||||
context "using the split checkout" do
|
||||
before do
|
||||
# feature toggle is enabled
|
||||
Flipper.enable(:split_checkout)
|
||||
end
|
||||
|
||||
it "redirect to the split checkout" do
|
||||
get path
|
||||
expect(response.status).to redirect_to("/checkout")
|
||||
|
||||
# follow the redirect
|
||||
get response.redirect_url
|
||||
expect(response.status).to redirect_to("/checkout/details")
|
||||
end
|
||||
# follow the redirect
|
||||
get response.redirect_url
|
||||
expect(response.status).to redirect_to("/checkout/details")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -114,7 +114,7 @@ describe "checking out an order with a Stripe SCA payment method", type: :reques
|
||||
stub_add_metadata_request(payment_method: "pm_456", response: {})
|
||||
end
|
||||
|
||||
context "when the user submits a new card and doesn't request that the card is saved for later" do
|
||||
pending "when the user submits a new card and doesn't request that the card is saved for later" do
|
||||
let(:hubs_payment_method_response_mock) do
|
||||
{ status: 200, body: JSON.generate(id: hubs_stripe_payment_method) }
|
||||
end
|
||||
@@ -161,7 +161,7 @@ describe "checking out an order with a Stripe SCA payment method", type: :reques
|
||||
end
|
||||
end
|
||||
|
||||
context "when saving a card or using a stored card is involved" do
|
||||
pending "when saving a card or using a stored card is involved" do
|
||||
let(:hubs_payment_method_response_mock) do
|
||||
{
|
||||
status: 200,
|
||||
|
||||
@@ -10,8 +10,6 @@ describe VoucherAdjustmentsController, type: :request do
|
||||
let!(:adjustment) { voucher.create_adjustment(voucher.code, order) }
|
||||
|
||||
before do
|
||||
Flipper.enable(:split_checkout)
|
||||
|
||||
# Make sure the order is created by the order user, the factory doesn't set ip properly
|
||||
order.created_by = user
|
||||
order.save!
|
||||
|
||||
@@ -6,12 +6,12 @@ describe "routing for Stripe return URLS", type: :routing do
|
||||
context "checkout return URLs" do
|
||||
it "routes /checkout to checkout#edit" do
|
||||
expect(get: "checkout").
|
||||
to route_to("checkout#edit")
|
||||
to route_to("split_checkout#edit")
|
||||
end
|
||||
|
||||
it "routes /checkout?test=123 to checkout#edit" do
|
||||
expect(get: "/checkout?test=123").
|
||||
to route_to(controller: "checkout", action: "edit", test: "123")
|
||||
to route_to(controller: "split_checkout", action: "edit", test: "123")
|
||||
end
|
||||
|
||||
it "routes /checkout?payment_intent=pm_123 to payment_gateways/stripe#confirm" do
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Checkout::FormDataAdapter do
|
||||
describe '#params' do
|
||||
let(:params) { { "order" => { "order_id" => "123" } } }
|
||||
let(:order) { create(:order) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
let(:adapter) { Checkout::FormDataAdapter.new(params, order, user) }
|
||||
|
||||
it "returns the :order item in the params provided" do
|
||||
expect(adapter.params[:order]).to eq params["order"]
|
||||
end
|
||||
|
||||
describe "when payment_attributes are provided" do
|
||||
before { params["order"]["payments_attributes"] = [{ "payment_method_id" => "123" }] }
|
||||
|
||||
describe "and source attributes are provided" do
|
||||
let(:source_attributes) { { "payment_method_name" => "Pay at the farm" } }
|
||||
|
||||
before { params["payment_source"] = { "123" => source_attributes } }
|
||||
|
||||
it "moves payment source attributes to the order payment attributes" do
|
||||
expect(adapter.params[:order][:payments_attributes].
|
||||
first[:source_attributes]).to eq source_attributes
|
||||
end
|
||||
end
|
||||
|
||||
describe "and order total is not zero" do
|
||||
before { order.total = "50.0" }
|
||||
|
||||
it "sets the payment attributes amount to the order total" do
|
||||
expect(adapter.params[:order][:payments_attributes].first[:amount]).to eq order.total
|
||||
end
|
||||
end
|
||||
|
||||
describe "and a credit card is provided" do
|
||||
before do
|
||||
params["order"]["payments_attributes"].first["source_attributes"] =
|
||||
{ "number" => "4444333322221111" }
|
||||
end
|
||||
|
||||
it "fills in missing credit card brand" do
|
||||
expect(adapter.params[:order][:payments_attributes].first[:source_attributes][:cc_type]).to eq "visa"
|
||||
end
|
||||
|
||||
it "leaves an existing credit card brand" do
|
||||
params["order"]["payments_attributes"].first["source_attributes"]["cc_type"] = "test"
|
||||
expect(adapter.params[:order][:payments_attributes].first[:source_attributes][:cc_type]).to eq "test"
|
||||
end
|
||||
|
||||
it "doesn't touch the credit card brand without a number" do
|
||||
params["order"]["payments_attributes"].first["source_attributes"]["number"] = ""
|
||||
expect(adapter.params[:order][:payments_attributes].first[:source_attributes].key?(:cc_type)).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
describe "and existing credit card is provided" do
|
||||
before { params["order"]["existing_card_id"] = credit_card.id }
|
||||
|
||||
describe "and credit card is owned by current user" do
|
||||
let(:credit_card) { create(:credit_card, user_id: user.id) }
|
||||
|
||||
before { params["order"]["existing_card_id"] = credit_card.id }
|
||||
|
||||
it "adds card details to payment attributes" do
|
||||
expect(adapter.params[:order][:payments_attributes].first[:source][:id]).to eq credit_card.id
|
||||
expect(adapter.params[:order][:payments_attributes].
|
||||
first[:source][:last_digits]).to eq credit_card.last_digits
|
||||
end
|
||||
end
|
||||
|
||||
describe "and credit card is not owned by current user" do
|
||||
let(:credit_card) { create(:credit_card) }
|
||||
|
||||
it "raises exception if credit card provided doesnt belong to the current user" do
|
||||
expect { adapter.params[:order] }.to raise_error Spree::Core::GatewayError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -72,7 +72,8 @@ describe OrderTaxAdjustmentsFetcher do
|
||||
line_items: [line_item1, line_item2],
|
||||
bill_address: create(:address),
|
||||
order_cycle: order_cycle,
|
||||
distributor: coordinator
|
||||
distributor: coordinator,
|
||||
state: 'payment'
|
||||
)
|
||||
end
|
||||
let(:shipping_method) do
|
||||
|
||||
@@ -1,19 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module StripeHelper
|
||||
def checkout_with_stripe(guest_checkout: true, remember_card: false)
|
||||
visit checkout_path
|
||||
checkout_as_guest if guest_checkout
|
||||
fill_out_form(
|
||||
free_shipping.name,
|
||||
stripe_sca_payment_method.name,
|
||||
save_default_addresses: false
|
||||
)
|
||||
fill_out_card_details
|
||||
check "Remember this card?" if remember_card
|
||||
place_order
|
||||
end
|
||||
|
||||
def fill_out_card_details
|
||||
fill_in "stripe-cardnumber", with: '4242424242424242'
|
||||
fill_in "exp-date", with: "01/#{DateTime.now.year + 1}"
|
||||
|
||||
@@ -578,7 +578,7 @@ describe '
|
||||
}
|
||||
let(:order1) {
|
||||
create(:order, order_cycle: order_cycle, distributor: user1.enterprises.first,
|
||||
shipments: [shipment], bill_address: bill_address)
|
||||
shipments: [shipment], bill_address: bill_address, state: 'payment')
|
||||
}
|
||||
let(:product1) {
|
||||
create(:taxed_product, zone: zone, price: 12.54, tax_rate_amount: 0, sku: 'sku1')
|
||||
|
||||
@@ -61,13 +61,6 @@ describe 'Multilingual' do
|
||||
expect_menu_and_cookie_in_es
|
||||
expect(page).to have_content 'Precio'
|
||||
end
|
||||
|
||||
it "in the checkout page" do
|
||||
visit checkout_path(locale: 'es')
|
||||
|
||||
expect_menu_and_cookie_in_es
|
||||
expect(page).to have_content 'Total del carrito'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -174,18 +174,6 @@ describe "full-page cart" do
|
||||
end
|
||||
end
|
||||
|
||||
describe "tax" do
|
||||
before do
|
||||
add_enterprise_fee enterprise_fee
|
||||
add_product_to_cart order, product_with_tax
|
||||
visit main_app.cart_path
|
||||
end
|
||||
|
||||
it "shows the total tax for the order, including product tax and tax on fees" do
|
||||
expect(page).to have_selector '.tax-total', text: '11.00' # 10 + 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "updating quantities" do
|
||||
let(:li) { order.line_items.reload.last }
|
||||
let(:variant) { product_with_tax.variants.first }
|
||||
|
||||
@@ -29,66 +29,42 @@ describe "As a consumer I want to check out my cart" do
|
||||
add_product_to_cart order, product
|
||||
end
|
||||
|
||||
shared_examples "with different checkout types" do |checkout_type|
|
||||
context "on #{checkout_type}" do
|
||||
it "does not render the login form when logged in" do
|
||||
login_as user
|
||||
visit checkout_path
|
||||
within "section[role='main']" do
|
||||
expect(page).to have_no_content "Login"
|
||||
expect(page).to have_checkout_details
|
||||
end
|
||||
end
|
||||
|
||||
it "renders the login buttons when logged out" do
|
||||
visit checkout_path
|
||||
within "section[role='main']" do
|
||||
expect(page).to have_content "Login"
|
||||
click_button "Login"
|
||||
end
|
||||
expect(page).to have_login_modal
|
||||
end
|
||||
|
||||
describe "logging in" do
|
||||
before do
|
||||
visit checkout_path
|
||||
within("section[role='main']") { click_button "Login" }
|
||||
expect(page).to have_login_modal
|
||||
fill_in "Email", with: user.email
|
||||
fill_in "Password", with: user.password
|
||||
within(".login-modal") { click_button 'Login' }
|
||||
end
|
||||
|
||||
context "and populating user details on (#{checkout_type})",
|
||||
if: checkout_type.eql?("legacy_checkout") do
|
||||
it "toggles the Details section" do
|
||||
expect(page).to have_content "Your details"
|
||||
page.find(:css, "i.ofn-i_052-point-down").click
|
||||
end
|
||||
end
|
||||
|
||||
context "and populating user details on (#{checkout_type})",
|
||||
if: checkout_type.eql?("split_checkout") do
|
||||
it "should allow proceeding to the next step" do
|
||||
expect(page).to have_content("Logged in successfully")
|
||||
click_button "Next - Payment method"
|
||||
expect(page).to have_button("Next - Order summary")
|
||||
end
|
||||
end
|
||||
context "on split_checkout" do
|
||||
it "does not render the login form when logged in" do
|
||||
login_as user
|
||||
visit checkout_path
|
||||
within "section[role='main']" do
|
||||
expect(page).to have_no_content "Login"
|
||||
expect(page).to have_checkout_details
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "shared examples" do
|
||||
context "legacy checkout" do
|
||||
it_behaves_like "with different checkout types", "legacy_checkout"
|
||||
it "renders the login buttons when logged out" do
|
||||
visit checkout_path
|
||||
within "section[role='main']" do
|
||||
expect(page).to have_content "Login"
|
||||
click_button "Login"
|
||||
end
|
||||
expect(page).to have_login_modal
|
||||
end
|
||||
|
||||
context "split checkout" do
|
||||
describe "logging in" do
|
||||
before do
|
||||
Flipper.enable(:split_checkout)
|
||||
visit checkout_path
|
||||
within("section[role='main']") { click_button "Login" }
|
||||
expect(page).to have_login_modal
|
||||
fill_in "Email", with: user.email
|
||||
fill_in "Password", with: user.password
|
||||
within(".login-modal") { click_button 'Login' }
|
||||
end
|
||||
|
||||
context "and populating user details on (split_checkout)" do
|
||||
it "should allow proceeding to the next step" do
|
||||
expect(page).to have_content("Logged in successfully")
|
||||
click_button "Next - Payment method"
|
||||
expect(page).to have_button("Next - Order summary")
|
||||
end
|
||||
end
|
||||
include_examples "with different checkout types", "split_checkout"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -98,24 +74,6 @@ describe "As a consumer I want to check out my cart" do
|
||||
checkout_as_guest
|
||||
expect(page).to have_checkout_details
|
||||
end
|
||||
|
||||
it "asks the user to log in if they are using a registered email" do
|
||||
visit checkout_path
|
||||
checkout_as_guest
|
||||
|
||||
fill_in 'First Name', with: 'Not'
|
||||
fill_in 'Last Name', with: 'Guest'
|
||||
fill_in 'Email', with: user.email
|
||||
fill_in 'Phone', with: '098712736'
|
||||
|
||||
within '#details' do
|
||||
click_button 'Next'
|
||||
end
|
||||
|
||||
expect(page).to have_selector 'div.login-modal'
|
||||
expect(page).to have_content 'This email address is already registered. Please log in '\
|
||||
'to continue, or go back and use another email address.'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -47,7 +47,7 @@ describe "Check out with Paypal" do
|
||||
end
|
||||
|
||||
shared_examples "checking out with paypal" do |user_type|
|
||||
context user_type.to_s do
|
||||
pending user_type.to_s do
|
||||
before do
|
||||
fill_out_details
|
||||
fill_out_form(free_shipping.name, paypal.name, save_default_addresses: false)
|
||||
@@ -82,7 +82,7 @@ describe "Check out with Paypal" do
|
||||
end
|
||||
|
||||
describe "shared_examples" do
|
||||
context "as a guest user" do
|
||||
pending "as a guest user" do
|
||||
before do
|
||||
visit checkout_path
|
||||
checkout_as_guest
|
||||
@@ -90,7 +90,7 @@ describe "Check out with Paypal" do
|
||||
it_behaves_like "checking out with paypal", "as guest"
|
||||
end
|
||||
|
||||
context "as a logged in user" do
|
||||
pending "as a logged in user" do
|
||||
before do
|
||||
login_as user
|
||||
visit checkout_path
|
||||
|
||||
@@ -71,7 +71,7 @@ describe "As a consumer I want to check out my cart" do
|
||||
distributor.shipping_methods << tagged_shipping
|
||||
end
|
||||
|
||||
describe "when I have an out of stock product in my cart" do
|
||||
pending "when I have an out of stock product in my cart" do
|
||||
before do
|
||||
variant.on_demand = false
|
||||
variant.on_hand = 0
|
||||
@@ -87,7 +87,7 @@ describe "As a consumer I want to check out my cart" do
|
||||
end
|
||||
end
|
||||
|
||||
context 'login in as user' do
|
||||
pending 'login in as user' do
|
||||
let(:user) { create(:user) }
|
||||
let(:pdf_upload) {
|
||||
Rack::Test::UploadedFile.new(
|
||||
@@ -335,7 +335,7 @@ describe "As a consumer I want to check out my cart" do
|
||||
end
|
||||
end
|
||||
|
||||
context "guest checkout" do
|
||||
pending "guest checkout" do
|
||||
before do
|
||||
visit checkout_path
|
||||
checkout_as_guest
|
||||
|
||||
@@ -41,7 +41,7 @@ describe "Check out with Stripe" do
|
||||
distributor.shipping_methods << [shipping_with_fee, free_shipping]
|
||||
end
|
||||
|
||||
describe "using Stripe SCA" do
|
||||
pending "using Stripe SCA" do
|
||||
let!(:stripe_account) { create(:stripe_account, enterprise: distributor) }
|
||||
let!(:stripe_sca_payment_method) {
|
||||
create(:stripe_sca_payment_method, distributors: [distributor])
|
||||
|
||||
@@ -150,17 +150,19 @@ describe "shopping with variant overrides defined" do
|
||||
expect(page).to have_selector "#edit-cart .grand-total", text: with_currency(122.22)
|
||||
end
|
||||
|
||||
it "shows the correct prices in the checkout" do
|
||||
click_add_to_cart product1_variant1, 2
|
||||
click_checkout
|
||||
pending "prices in the checkout" do
|
||||
it "shows the correct prices" do
|
||||
click_add_to_cart product1_variant1, 2
|
||||
click_checkout
|
||||
|
||||
expect(page).to have_selector 'form.edit_order .cart-total', text: with_currency(122.22)
|
||||
expect(page).to have_selector 'form.edit_order .shipping', text: with_currency(0.00)
|
||||
expect(page).to have_selector 'form.edit_order .total', text: with_currency(122.22)
|
||||
expect(page).to have_selector 'form.edit_order .cart-total', text: with_currency(122.22)
|
||||
expect(page).to have_selector 'form.edit_order .shipping', text: with_currency(0.00)
|
||||
expect(page).to have_selector 'form.edit_order .total', text: with_currency(122.22)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "creating orders" do
|
||||
pending "creating orders" do
|
||||
it "creates the order with the correct prices" do
|
||||
click_add_to_cart product1_variant1, 2
|
||||
click_checkout
|
||||
|
||||
@@ -70,8 +70,6 @@ describe "As a consumer, I want to checkout my order" do
|
||||
}
|
||||
|
||||
before do
|
||||
Flipper.enable(:split_checkout)
|
||||
|
||||
add_enterprise_fee enterprise_fee
|
||||
set_order order
|
||||
|
||||
|
||||
@@ -82,158 +82,95 @@ describe "As a consumer, I want to see adjustment breakdown" do
|
||||
end
|
||||
|
||||
describe "for a customer with shipping address within the tax zone" do
|
||||
context "on legacy checkout" do
|
||||
before do
|
||||
set_order order_within_zone
|
||||
login_as(user_within_zone)
|
||||
end
|
||||
|
||||
it "will be charged tax on the order" do
|
||||
visit checkout_path
|
||||
|
||||
find(:xpath, '//*[@id="shipping"]/ng-form/dd').click
|
||||
choose free_shipping.name.to_s
|
||||
|
||||
within "#payment" do
|
||||
choose free_payment.name.to_s
|
||||
end
|
||||
|
||||
click_on "Place order now"
|
||||
|
||||
# UI checks
|
||||
expect(page).to have_selector('#order_total', text: with_currency(10.00))
|
||||
expect(page).to have_selector('#tax-row', text: with_currency(1.15))
|
||||
|
||||
# DB checks
|
||||
assert_db_tax_incl
|
||||
end
|
||||
before do
|
||||
set_order order_within_zone
|
||||
login_as(user_within_zone)
|
||||
end
|
||||
|
||||
context "on split-checkout" do
|
||||
before do
|
||||
Flipper.enable(:split_checkout)
|
||||
it "will be charged tax on the order" do
|
||||
visit checkout_step_path(:details)
|
||||
|
||||
set_order order_within_zone
|
||||
login_as(user_within_zone)
|
||||
choose "Delivery"
|
||||
|
||||
click_button "Next - Payment method"
|
||||
click_on "Next - Order summary"
|
||||
click_on "Complete order"
|
||||
|
||||
# UI checks
|
||||
expect(page).to have_content("Confirmed")
|
||||
expect(page).to have_selector('#order_total', text: with_currency(10.00))
|
||||
expect(page).to have_selector('#tax-row', text: with_currency(1.15))
|
||||
|
||||
# DB checks
|
||||
assert_db_tax_incl
|
||||
end
|
||||
|
||||
context "when using a voucher" do
|
||||
let!(:voucher) do
|
||||
create(:voucher, code: 'some_code', enterprise: distributor, amount: 10)
|
||||
end
|
||||
|
||||
it "will be charged tax on the order" do
|
||||
it "will include a tax included amount on the voucher adjustment" do
|
||||
visit checkout_step_path(:details)
|
||||
|
||||
choose "Delivery"
|
||||
|
||||
click_button "Next - Payment method"
|
||||
|
||||
# add Voucher
|
||||
fill_in "Enter voucher code", with: voucher.code
|
||||
click_button("Apply")
|
||||
|
||||
# Choose payment
|
||||
click_on "Next - Order summary"
|
||||
click_on "Complete order"
|
||||
|
||||
# UI checks
|
||||
expect(page).to have_content("Confirmed")
|
||||
expect(page).to have_selector('#order_total', text: with_currency(10.00))
|
||||
expect(page).to have_selector('#order_total', text: with_currency(0.00))
|
||||
expect(page).to have_selector('#tax-row', text: with_currency(1.15))
|
||||
|
||||
# DB checks
|
||||
assert_db_tax_incl
|
||||
end
|
||||
|
||||
context "when using a voucher" do
|
||||
let!(:voucher) do
|
||||
create(:voucher, code: 'some_code', enterprise: distributor, amount: 10)
|
||||
# Voucher
|
||||
within "#line-items" do
|
||||
expect(page).to have_content(voucher.code)
|
||||
expect(page).to have_content(with_currency(-10.00))
|
||||
end
|
||||
|
||||
it "will include a tax included amount on the voucher adjustment" do
|
||||
visit checkout_step_path(:details)
|
||||
# DB check
|
||||
order_within_zone.reload
|
||||
voucher_adjustment = order_within_zone.voucher_adjustments.first
|
||||
|
||||
choose "Delivery"
|
||||
|
||||
click_button "Next - Payment method"
|
||||
|
||||
# add Voucher
|
||||
fill_in "Enter voucher code", with: voucher.code
|
||||
click_button("Apply")
|
||||
|
||||
# Choose payment
|
||||
click_on "Next - Order summary"
|
||||
click_on "Complete order"
|
||||
|
||||
# UI checks
|
||||
expect(page).to have_content("Confirmed")
|
||||
expect(page).to have_selector('#order_total', text: with_currency(0.00))
|
||||
expect(page).to have_selector('#tax-row', text: with_currency(1.15))
|
||||
|
||||
# Voucher
|
||||
within "#line-items" do
|
||||
expect(page).to have_content(voucher.code)
|
||||
expect(page).to have_content(with_currency(-10.00))
|
||||
end
|
||||
|
||||
# DB check
|
||||
order_within_zone.reload
|
||||
voucher_adjustment = order_within_zone.voucher_adjustments.first
|
||||
|
||||
expect(voucher_adjustment.amount.to_f).to eq(-10)
|
||||
expect(voucher_adjustment.included_tax.to_f).to eq(-1.15)
|
||||
end
|
||||
expect(voucher_adjustment.amount.to_f).to eq(-10)
|
||||
expect(voucher_adjustment.included_tax.to_f).to eq(-1.15)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a customer with shipping address outside the tax zone" do
|
||||
context "on legacy checkout" do
|
||||
before do
|
||||
set_order order_outside_zone
|
||||
login_as(user_outside_zone)
|
||||
end
|
||||
|
||||
it "will not be charged tax on the order" do
|
||||
pending("#7540")
|
||||
visit checkout_path
|
||||
|
||||
find(:xpath, '//*[@id="shipping"]/ng-form/dd').click
|
||||
choose free_shipping.name.to_s
|
||||
|
||||
within "#payment" do
|
||||
choose free_payment.name.to_s
|
||||
end
|
||||
|
||||
click_on "Place order now"
|
||||
|
||||
# UI checks
|
||||
expect(page).to have_selector('#order_total', text: with_currency(10.00))
|
||||
expect(page).not_to have_content("includes tax")
|
||||
|
||||
# DB checks
|
||||
assert_db_no_tax_incl
|
||||
end
|
||||
before do
|
||||
set_order order_outside_zone
|
||||
login_as(user_outside_zone)
|
||||
end
|
||||
|
||||
context "on split-checkout" do
|
||||
before do
|
||||
Flipper.enable(:split_checkout)
|
||||
it "will not be charged tax on the order" do
|
||||
pending("#7540")
|
||||
visit checkout_step_path(:details)
|
||||
|
||||
set_order order_outside_zone
|
||||
login_as(user_outside_zone)
|
||||
end
|
||||
choose "Delivery"
|
||||
check "order_save_bill_address"
|
||||
check "ship_address_same_as_billing"
|
||||
|
||||
it "will not be charged tax on the order" do
|
||||
pending("#7540")
|
||||
visit checkout_step_path(:details)
|
||||
click_button "Next - Payment method"
|
||||
click_on "Next - Order summary"
|
||||
click_on "Complete order"
|
||||
|
||||
choose "Delivery"
|
||||
check "order_save_bill_address"
|
||||
check "ship_address_same_as_billing"
|
||||
# UI checks
|
||||
expect(page).to have_content("Confirmed")
|
||||
expect(page).to have_selector('#order_total', text: with_currency(10.00))
|
||||
expect(page).not_to have_content("includes tax")
|
||||
|
||||
click_button "Next - Payment method"
|
||||
click_on "Next - Order summary"
|
||||
click_on "Complete order"
|
||||
|
||||
# UI checks
|
||||
expect(page).to have_content("Confirmed")
|
||||
expect(page).to have_selector('#order_total', text: with_currency(10.00))
|
||||
expect(page).not_to have_content("includes tax")
|
||||
|
||||
# DB checks
|
||||
assert_db_no_tax_incl
|
||||
end
|
||||
# DB checks
|
||||
assert_db_no_tax_incl
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -62,7 +62,7 @@ describe "As a consumer, I want to see adjustment breakdown" do
|
||||
Spree::Config.set(tax_using_ship_address: true)
|
||||
end
|
||||
|
||||
describe "a not-included tax" do
|
||||
pending "a not-included tax" do
|
||||
before do
|
||||
zone.update!(default_tax: false)
|
||||
tax_rate.update!(included_in_price: false)
|
||||
@@ -88,207 +88,141 @@ describe "As a consumer, I want to see adjustment breakdown" do
|
||||
end
|
||||
|
||||
describe "for a customer with shipping address within the tax zone" do
|
||||
context "on legacy checkout" do
|
||||
before do
|
||||
set_order order_within_zone
|
||||
login_as(user_within_zone)
|
||||
end
|
||||
|
||||
it "will be charged tax on the order" do
|
||||
visit checkout_path
|
||||
|
||||
find(:xpath, '//*[@id="shipping"]/ng-form/dd').click
|
||||
choose free_shipping.name.to_s
|
||||
|
||||
within "#payment" do
|
||||
choose free_payment.name.to_s
|
||||
end
|
||||
|
||||
click_on "Place order now"
|
||||
|
||||
# DB checks
|
||||
order_within_zone.reload
|
||||
expect(order_within_zone.additional_tax_total).to eq(1.3)
|
||||
|
||||
# UI checks
|
||||
expect(page).to have_selector('#order_total', text: with_currency(11.30))
|
||||
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
|
||||
end
|
||||
before do
|
||||
set_order order_within_zone
|
||||
login_as(user_within_zone)
|
||||
end
|
||||
|
||||
context "on split-checkout" do
|
||||
before do
|
||||
Flipper.enable(:split_checkout)
|
||||
it "will be charged tax on the order" do
|
||||
visit checkout_step_path(:details)
|
||||
|
||||
set_order order_within_zone
|
||||
login_as(user_within_zone)
|
||||
choose "Delivery"
|
||||
|
||||
click_button "Next - Payment method"
|
||||
click_on "Next - Order summary"
|
||||
click_on "Complete order"
|
||||
|
||||
# DB checks
|
||||
order_within_zone.reload
|
||||
expect(order_within_zone.additional_tax_total).to eq(1.3)
|
||||
|
||||
# UI checks
|
||||
expect(page).to have_content("Confirmed")
|
||||
expect(page).to have_selector('#order_total', text: with_currency(11.30))
|
||||
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
|
||||
end
|
||||
|
||||
context "when using a voucher" do
|
||||
let!(:voucher) do
|
||||
create(:voucher, code: 'some_code', enterprise: distributor, amount: 10)
|
||||
end
|
||||
|
||||
it "will be charged tax on the order" do
|
||||
it "will include a tax included amount on the voucher adjustment" do
|
||||
visit checkout_step_path(:details)
|
||||
|
||||
choose "Delivery"
|
||||
|
||||
click_button "Next - Payment method"
|
||||
# add Voucher
|
||||
fill_in "Enter voucher code", with: voucher.code
|
||||
click_button("Apply")
|
||||
|
||||
click_on "Next - Order summary"
|
||||
click_on "Complete order"
|
||||
|
||||
# DB checks
|
||||
order_within_zone.reload
|
||||
expect(order_within_zone.additional_tax_total).to eq(1.3)
|
||||
|
||||
# UI checks
|
||||
expect(page).to have_content("Confirmed")
|
||||
expect(page).to have_selector('#order_total', text: with_currency(11.30))
|
||||
expect(page).to have_selector('#order_total', text: with_currency(1.30))
|
||||
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
|
||||
end
|
||||
|
||||
context "when using a voucher" do
|
||||
let!(:voucher) do
|
||||
create(:voucher, code: 'some_code', enterprise: distributor, amount: 10)
|
||||
# Voucher
|
||||
within "#line-items" do
|
||||
expect(page).to have_content(voucher.code)
|
||||
expect(page).to have_content(with_currency(-8.85))
|
||||
|
||||
expect(page).to have_content("Tax #{voucher.code}")
|
||||
expect(page).to have_content(with_currency(-1.15))
|
||||
end
|
||||
|
||||
it "will include a tax included amount on the voucher adjustment" do
|
||||
visit checkout_step_path(:details)
|
||||
# DB check
|
||||
order_within_zone.reload
|
||||
voucher_adjustment = order_within_zone.voucher_adjustments.first
|
||||
voucher_tax_adjustment = order_within_zone.voucher_adjustments.second
|
||||
|
||||
choose "Delivery"
|
||||
|
||||
click_button "Next - Payment method"
|
||||
# add Voucher
|
||||
fill_in "Enter voucher code", with: voucher.code
|
||||
click_button("Apply")
|
||||
|
||||
click_on "Next - Order summary"
|
||||
click_on "Complete order"
|
||||
|
||||
# UI checks
|
||||
expect(page).to have_content("Confirmed")
|
||||
expect(page).to have_selector('#order_total', text: with_currency(1.30))
|
||||
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
|
||||
|
||||
# Voucher
|
||||
within "#line-items" do
|
||||
expect(page).to have_content(voucher.code)
|
||||
expect(page).to have_content(with_currency(-8.85))
|
||||
|
||||
expect(page).to have_content("Tax #{voucher.code}")
|
||||
expect(page).to have_content(with_currency(-1.15))
|
||||
end
|
||||
|
||||
# DB check
|
||||
order_within_zone.reload
|
||||
voucher_adjustment = order_within_zone.voucher_adjustments.first
|
||||
voucher_tax_adjustment = order_within_zone.voucher_adjustments.second
|
||||
|
||||
expect(voucher_adjustment.amount.to_f).to eq(-8.85)
|
||||
expect(voucher_tax_adjustment.amount.to_f).to eq(-1.15)
|
||||
end
|
||||
expect(voucher_adjustment.amount.to_f).to eq(-8.85)
|
||||
expect(voucher_tax_adjustment.amount.to_f).to eq(-1.15)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a customer with shipping address outside the tax zone" do
|
||||
context "on legacy checkout" do
|
||||
before do
|
||||
set_order order_outside_zone
|
||||
login_as(user_outside_zone)
|
||||
end
|
||||
|
||||
it "will not be charged tax on the order" do
|
||||
visit checkout_path
|
||||
|
||||
find(:xpath, '//*[@id="shipping"]/ng-form/dd').click
|
||||
choose free_shipping.name.to_s
|
||||
|
||||
within "#payment" do
|
||||
choose free_payment.name.to_s
|
||||
end
|
||||
|
||||
click_on "Place order now"
|
||||
|
||||
# DB checks
|
||||
order_outside_zone.reload
|
||||
expect(order_outside_zone.included_tax_total).to eq(0.0)
|
||||
expect(order_outside_zone.additional_tax_total).to eq(0.0)
|
||||
|
||||
# UI checks
|
||||
expect(page).to have_content("Confirmed")
|
||||
expect(page).to have_selector('#order_total', text: with_currency(10.00))
|
||||
expect(page).not_to have_content("includes tax")
|
||||
end
|
||||
before do
|
||||
set_order order_outside_zone
|
||||
login_as(user_outside_zone)
|
||||
end
|
||||
|
||||
context "on split-checkout" do
|
||||
it "will not be charged tax on the order" do
|
||||
visit checkout_step_path(:details)
|
||||
|
||||
choose "Delivery"
|
||||
|
||||
click_button "Next - Payment method"
|
||||
click_on "Next - Order summary"
|
||||
click_on "Complete order"
|
||||
|
||||
# DB checks
|
||||
order_outside_zone.reload
|
||||
expect(order_outside_zone.included_tax_total).to eq(0.0)
|
||||
expect(order_outside_zone.additional_tax_total).to eq(0.0)
|
||||
|
||||
# UI checks
|
||||
expect(page).to have_content("Confirmed")
|
||||
expect(page).to have_selector('#order_total', text: with_currency(10.00))
|
||||
expect(page).not_to have_content("includes tax")
|
||||
end
|
||||
|
||||
# reproducing bug #9153
|
||||
context "changing the address on the /details step" do
|
||||
before do
|
||||
Flipper.enable(:split_checkout)
|
||||
|
||||
set_order order_outside_zone
|
||||
login_as(user_outside_zone)
|
||||
end
|
||||
|
||||
it "will not be charged tax on the order" do
|
||||
visit checkout_step_path(:details)
|
||||
|
||||
choose "Delivery"
|
||||
|
||||
click_button "Next - Payment method"
|
||||
click_on "Next - Order summary"
|
||||
|
||||
expect(page).to have_selector('#order_total', text: with_currency(10.00))
|
||||
|
||||
# customer goes back from Summary to Details step, to change Delivery
|
||||
click_on "Your details"
|
||||
end
|
||||
|
||||
it "should re-calculate the tax accordingly" do
|
||||
select "Victoria", from: "order_bill_address_attributes_state_id"
|
||||
|
||||
# it should not be necessary to save as new default bill address
|
||||
check "order_save_bill_address"
|
||||
check "ship_address_same_as_billing"
|
||||
|
||||
choose "Delivery"
|
||||
click_button "Next - Payment method"
|
||||
|
||||
click_on "Next - Order summary"
|
||||
|
||||
# Summary step should reflect changes
|
||||
expect(page).to have_selector('#order_total', text: with_currency(11.30))
|
||||
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
|
||||
|
||||
click_on "Complete order"
|
||||
|
||||
# DB checks
|
||||
order_outside_zone.reload
|
||||
expect(order_outside_zone.included_tax_total).to eq(0.0)
|
||||
expect(order_outside_zone.additional_tax_total).to eq(0.0)
|
||||
expect(order_outside_zone.additional_tax_total).to eq(1.3)
|
||||
|
||||
# UI checks
|
||||
# UI checks - Order confirmation page should reflect changes
|
||||
expect(page).to have_content("Confirmed")
|
||||
expect(page).to have_selector('#order_total', text: with_currency(10.00))
|
||||
expect(page).not_to have_content("includes tax")
|
||||
end
|
||||
|
||||
# reproducing bug #9153
|
||||
context "changing the address on the /details step" do
|
||||
before do
|
||||
visit checkout_step_path(:details)
|
||||
choose "Delivery"
|
||||
|
||||
click_button "Next - Payment method"
|
||||
click_on "Next - Order summary"
|
||||
|
||||
expect(page).to have_selector('#order_total', text: with_currency(10.00))
|
||||
|
||||
# customer goes back from Summary to Details step, to change Delivery
|
||||
click_on "Your details"
|
||||
end
|
||||
|
||||
it "should re-calculate the tax accordingly" do
|
||||
select "Victoria", from: "order_bill_address_attributes_state_id"
|
||||
|
||||
# it should not be necessary to save as new default bill address
|
||||
check "order_save_bill_address"
|
||||
check "ship_address_same_as_billing"
|
||||
|
||||
choose "Delivery"
|
||||
click_button "Next - Payment method"
|
||||
|
||||
click_on "Next - Order summary"
|
||||
|
||||
# Summary step should reflect changes
|
||||
expect(page).to have_selector('#order_total', text: with_currency(11.30))
|
||||
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
|
||||
|
||||
click_on "Complete order"
|
||||
|
||||
# DB checks
|
||||
order_outside_zone.reload
|
||||
expect(order_outside_zone.included_tax_total).to eq(0.0)
|
||||
expect(order_outside_zone.additional_tax_total).to eq(1.3)
|
||||
|
||||
# UI checks - Order confirmation page should reflect changes
|
||||
expect(page).to have_content("Confirmed")
|
||||
expect(page).to have_selector('#order_total', text: with_currency(11.30))
|
||||
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
|
||||
end
|
||||
expect(page).to have_selector('#order_total', text: with_currency(11.30))
|
||||
expect(page).to have_selector('#tax-row', text: with_currency(1.30))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -145,7 +145,7 @@ describe 'White label setting' do
|
||||
set_order(order)
|
||||
end
|
||||
|
||||
shared_examples "hides the OFN navigation when needed only for the checkout" do
|
||||
context "when the split checkout is enabled" do
|
||||
it_behaves_like "hides the OFN navigation when needed only"
|
||||
|
||||
context "for cart path" do
|
||||
@@ -174,18 +174,6 @@ describe 'White label setting' do
|
||||
it_behaves_like "hides the OFN navigation for mobile view as well"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the split checkout is disabled" do
|
||||
it_behaves_like "hides the OFN navigation when needed only for the checkout"
|
||||
end
|
||||
|
||||
context "when the split checkout is enabled" do
|
||||
before do
|
||||
Flipper.enable(:split_checkout)
|
||||
end
|
||||
|
||||
it_behaves_like "hides the OFN navigation when needed only for the checkout"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the user has a complete order" do
|
||||
|
||||
Reference in New Issue
Block a user