Merge pull request #9755 from cillian/order-cycle-payment-methods

Let people choose which payment methods are available to customers on order cycles
This commit is contained in:
Rachel Arnould
2022-10-26 20:10:34 +02:00
committed by GitHub
34 changed files with 1042 additions and 476 deletions

View File

@@ -478,6 +478,7 @@ Metrics/ClassLength:
- 'app/serializers/api/enterprise_shopfront_serializer.rb'
- 'app/services/cart_service.rb'
- 'app/services/order_syncer.rb'
- 'app/services/order_cycle_form.rb'
- 'engines/order_management/app/services/order_management/order/updater.rb'
- 'lib/open_food_network/enterprise_fee_calculator.rb'
- 'lib/open_food_network/order_cycle_form_applicator.rb'

View File

@@ -22,12 +22,8 @@ class BaseController < ApplicationController
end
# Default to the only order cycle if there's only one
#
# Here we need to use @order_cycles.size not @order_cycles.count
# because OrderCyclesList returns a modified ActiveRecord::Relation
# and these modifications are not seen if it is reloaded with count
def set_order_cycle
return if @order_cycles.size != 1
return if @order_cycles.count != 1
current_order(true).set_order_cycle! @order_cycles.first
end

View File

@@ -1,7 +1,5 @@
# frozen_string_literal: true
require 'open_food_network/available_payment_method_filter'
module EnterprisesHelper
def current_distributor
@current_distributor ||= current_order(false)&.distributor
@@ -18,18 +16,7 @@ module EnterprisesHelper
end
def available_payment_methods
return [] if current_distributor.blank?
payment_methods = current_distributor.payment_methods.available(:both).to_a
filter = OpenFoodNetwork::AvailablePaymentMethodFilter.new
filter.filter!(payment_methods)
applicator = OpenFoodNetwork::TagRuleApplicator.new(current_distributor,
"FilterPaymentMethods", current_customer&.tag_list)
applicator.filter!(payment_methods)
payment_methods
OrderAvailablePaymentMethods.new(current_order, current_customer).to_a
end
def managed_enterprises

View File

@@ -9,9 +9,9 @@ module PaymentMethodDistributors
extend ActiveSupport::Concern
included do
has_and_belongs_to_many :distributors, join_table: 'distributors_payment_methods',
class_name: 'Enterprise',
foreign_key: 'payment_method_id',
association_foreign_key: 'distributor_id'
has_many :distributor_payment_methods, dependent: :destroy
has_many :distributors, through: :distributor_payment_methods,
class_name: 'Enterprise',
foreign_key: 'distributor_id'
end
end

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
class DistributorPaymentMethod < ApplicationRecord
self.table_name = "distributors_payment_methods"
belongs_to :payment_method, class_name: "Spree::PaymentMethod", touch: true
belongs_to :distributor, class_name: "Enterprise", touch: true
end

View File

@@ -69,10 +69,9 @@ class Enterprise < ApplicationRecord
has_many :users, through: :enterprise_roles
belongs_to :owner, class_name: 'Spree::User',
inverse_of: :owned_enterprises
has_and_belongs_to_many :payment_methods, join_table: 'distributors_payment_methods',
class_name: 'Spree::PaymentMethod',
foreign_key: 'distributor_id'
has_many :distributor_payment_methods, foreign_key: :distributor_id
has_many :distributor_shipping_methods, foreign_key: :distributor_id
has_many :payment_methods, through: :distributor_payment_methods
has_many :shipping_methods, through: :distributor_shipping_methods
has_many :customers
has_many :inventory_items
@@ -400,7 +399,7 @@ class Enterprise < ApplicationRecord
end
def ready_for_checkout?
shipping_methods.frontend.any? && payment_methods.available.any?
shipping_methods.frontend.any? && payment_methods.available.any?(&:configured?)
end
def self.find_available_permalink(test_permalink)

View File

@@ -24,6 +24,9 @@ class OrderCycle < ApplicationRecord
has_many :distributors, -> { distinct }, source: :receiver, through: :cached_outgoing_exchanges
has_many :order_cycle_schedules
has_many :schedules, through: :order_cycle_schedules
has_and_belongs_to_many :selected_distributor_payment_methods,
class_name: 'DistributorPaymentMethod',
join_table: 'order_cycles_distributor_payment_methods'
has_and_belongs_to_many :selected_distributor_shipping_methods,
class_name: 'DistributorShippingMethod',
join_table: 'order_cycles_distributor_shipping_methods'
@@ -152,12 +155,10 @@ class OrderCycle < ApplicationRecord
]
end
def attachable_payment_methods
Spree::PaymentMethod.available(:both).
joins("INNER JOIN distributors_payment_methods
ON payment_method_id = spree_payment_methods.id").
where("distributor_id IN (?)", distributor_ids).
distinct
def attachable_distributor_payment_methods
DistributorPaymentMethod.joins(:payment_method).
merge(Spree::PaymentMethod.available).
where("distributor_id IN (?)", distributor_ids)
end
def attachable_distributor_shipping_methods
@@ -167,21 +168,7 @@ class OrderCycle < ApplicationRecord
end
def clone!
oc = dup
oc.name = I18n.t("models.order_cycle.cloned_order_cycle_name", order_cycle: oc.name)
oc.orders_open_at = oc.orders_close_at = oc.mails_sent = oc.processed_at = nil
oc.coordinator_fee_ids = coordinator_fee_ids
# rubocop:disable Layout/LineLength
oc.preferred_product_selection_from_coordinator_inventory_only = preferred_product_selection_from_coordinator_inventory_only
# rubocop:enable Layout/LineLength
oc.schedule_ids = schedule_ids
oc.save!
exchanges.each { |e| e.clone!(oc) }
oc.selected_distributor_shipping_method_ids = (
attachable_distributor_shipping_methods.map(&:id) & selected_distributor_shipping_method_ids
)
sync_subscriptions
oc.reload
OrderCycleClone.new(self).create
end
def variants
@@ -293,6 +280,18 @@ class OrderCycle < ApplicationRecord
items.each { |li| scoper.scope(li.variant) }
end
def distributor_payment_methods
if simple? || selected_distributor_payment_methods.none?
attachable_distributor_payment_methods
else
attachable_distributor_payment_methods.where(
"distributors_payment_methods.id IN (?) OR distributor_id NOT IN (?)",
selected_distributor_payment_methods.map(&:id),
selected_distributor_payment_methods.map(&:distributor_id)
)
end
end
def distributor_shipping_methods
if simple? || selected_distributor_shipping_methods.none?
attachable_distributor_shipping_methods

View File

@@ -376,10 +376,6 @@ module Spree
payment_state == 'paid' || payment_state == 'credit_owed'
end
def available_payment_methods
@available_payment_methods ||= PaymentMethod.available(:both)
end
# "Checkout" is the initial state and, for card payments, "pending" is the state after auth
# These are both valid states to process the payment
def pending_payments

View File

@@ -20,6 +20,8 @@ module Spree
after_initialize :init
scope :inactive_or_backend, -> { where("active = false OR display_on = 'back_end'") }
scope :production, -> { where(environment: 'production') }
scope :managed_by, lambda { |user|
@@ -57,6 +59,10 @@ module Spree
Rails.application.config.spree.payment_methods
end
def configured?
!stripe? || stripe_configured?
end
def provider_class
raise 'You must implement provider_class method for this gateway.'
end
@@ -71,6 +77,10 @@ module Spree
nil
end
def frontend?
active? && display_on != "back_end"
end
# The class that will process payments for this payment type, used for @payment.source
# e.g. CreditCard in the case of a the Gateway payment type
# nil means the payment method doesn't require a source e.g. check
@@ -120,5 +130,17 @@ module Spree
def distributor_validation
validates_with DistributorsValidator
end
def stripe?
type.ends_with?("StripeSCA")
end
def stripe_configured?
Spree::Config.stripe_connect_enabled &&
Stripe.publishable_key &&
preferred_enterprise_id.present? &&
preferred_enterprise_id > 0 &&
stripe_account_id.present?
end
end
end

View File

@@ -0,0 +1,37 @@
# frozen_string_literal: true
class OrderAvailablePaymentMethods
attr_reader :order, :customer
delegate :distributor,
:order_cycle,
to: :order
def initialize(order, customer = nil)
@order, @customer = order, customer
end
def to_a
return [] if distributor.blank?
payment_methods = payment_methods_before_tag_rules_applied
applicator = OpenFoodNetwork::TagRuleApplicator.new(distributor,
"FilterPaymentMethods", customer&.tag_list)
applicator.filter!(payment_methods)
payment_methods.uniq
end
private
def payment_methods_before_tag_rules_applied
if order_cycle.nil? || order_cycle.simple?
distributor.payment_methods
else
distributor.payment_methods.where(
id: order_cycle.distributor_payment_methods.select(:payment_method_id)
)
end.available.select(&:configured?)
end
end

View File

@@ -0,0 +1,45 @@
# frozen_string_literal: true
require 'order_management/subscriptions/proxy_order_syncer'
class OrderCycleClone
def initialize(order_cycle)
@original_order_cycle = order_cycle
end
def create
oc = @original_order_cycle.dup
oc.name = I18n.t("models.order_cycle.cloned_order_cycle_name", order_cycle: oc.name)
oc.orders_open_at = oc.orders_close_at = oc.mails_sent = oc.processed_at = nil
oc.coordinator_fee_ids = @original_order_cycle.coordinator_fee_ids
oc.preferred_product_selection_from_coordinator_inventory_only =
@original_order_cycle.preferred_product_selection_from_coordinator_inventory_only
oc.schedule_ids = @original_order_cycle.schedule_ids
oc.save!
@original_order_cycle.exchanges.each { |e| e.clone!(oc) }
oc.selected_distributor_payment_method_ids = selected_distributor_payment_method_ids
oc.selected_distributor_shipping_method_ids = selected_distributor_shipping_method_ids
sync_subscriptions
oc.reload
end
private
def selected_distributor_payment_method_ids
@original_order_cycle.attachable_distributor_payment_methods.map(&:id) &
@original_order_cycle.selected_distributor_payment_method_ids
end
def selected_distributor_shipping_method_ids
@original_order_cycle.attachable_distributor_shipping_methods.map(&:id) &
@original_order_cycle.selected_distributor_shipping_method_ids
end
def sync_subscriptions
return unless @original_order_cycle.schedule_ids.any?
OrderManagement::Subscriptions::ProxyOrderSyncer.new(
Subscription.where(schedule_id: @original_order_cycle.schedule_ids)
).sync!
end
end

View File

@@ -12,6 +12,9 @@ class OrderCycleForm
@user = user
@permissions = OpenFoodNetwork::Permissions.new(user)
@schedule_ids = order_cycle_params.delete(:schedule_ids)
@selected_distributor_payment_method_ids = order_cycle_params.delete(
:selected_distributor_payment_method_ids
)
@selected_distributor_shipping_method_ids = order_cycle_params.delete(
:selected_distributor_shipping_method_ids
)
@@ -27,6 +30,7 @@ class OrderCycleForm
order_cycle.schedule_ids = schedule_ids if parameter_specified?(:schedule_ids)
order_cycle.save!
apply_exchange_changes
attach_selected_distributor_payment_methods
attach_selected_distributor_shipping_methods
sync_subscriptions
true
@@ -49,16 +53,29 @@ class OrderCycleForm
return if exchanges_unchanged?
OpenFoodNetwork::OrderCycleFormApplicator.new(order_cycle, user).go!
# reload so outgoing exchanges are up-to-date for shipping/payment method validations
order_cycle.reload
end
def attach_selected_distributor_payment_methods
return if @selected_distributor_payment_method_ids.nil?
order_cycle.selected_distributor_payment_method_ids = selected_distributor_payment_method_ids
order_cycle.save!
end
def attach_selected_distributor_shipping_methods
return if @selected_distributor_shipping_method_ids.nil?
order_cycle.reload # so outgoing exchanges are up-to-date for shipping method validations
order_cycle.selected_distributor_shipping_method_ids = selected_distributor_shipping_method_ids
order_cycle.save!
end
def attachable_distributor_payment_method_ids
@attachable_distributor_payment_method_ids ||= order_cycle.attachable_distributor_payment_methods.map(&:id)
end
def attachable_distributor_shipping_method_ids
@attachable_distributor_shipping_method_ids ||= order_cycle.attachable_distributor_shipping_methods.map(&:id)
end
@@ -69,6 +86,19 @@ class OrderCycleForm
end
end
def selected_distributor_payment_method_ids
@selected_distributor_payment_method_ids = (
attachable_distributor_payment_method_ids &
@selected_distributor_payment_method_ids.reject(&:blank?).map(&:to_i)
)
if attachable_distributor_payment_method_ids.sort == @selected_distributor_payment_method_ids.sort
@selected_distributor_payment_method_ids = []
end
@selected_distributor_payment_method_ids
end
def selected_distributor_shipping_method_ids
@selected_distributor_shipping_method_ids = (
attachable_distributor_shipping_method_ids &

View File

@@ -17,7 +17,8 @@ module PermittedAttributes
:name, :orders_open_at, :orders_close_at, :coordinator_id,
:preferred_product_selection_from_coordinator_inventory_only,
:automatic_notifications,
{ schedule_ids: [], selected_distributor_shipping_method_ids: [], coordinator_fee_ids: [] }
{ schedule_ids: [], selected_distributor_payment_method_ids: [],
selected_distributor_shipping_method_ids: [], coordinator_fee_ids: [] }
]
end

View File

@@ -8,9 +8,11 @@ module Shop
end
def self.ready_for_checkout_for(distributor, customer)
return OrderCycle.none unless distributor.ready_for_checkout?
new(distributor, customer).call
new(distributor, customer).call.select do |order_cycle|
order = Spree::Order.new(distributor: distributor, order_cycle: order_cycle)
OrderAvailablePaymentMethods.new(order, customer).to_a.any? &&
OrderAvailableShippingMethods.new(order, customer).to_a.any?
end
end
def initialize(distributor, customer)

View File

@@ -10,8 +10,6 @@
%fieldset.no-border-bottom
%legend{ align: 'center'}= t('.checkout_options')
= hidden_field_tag "order_cycle[selected_distributor_shipping_method_ids][]", ""
.row
.three.columns
&nbsp;
@@ -19,10 +17,12 @@
%table.checkout-options
%thead
%tr
%th{ colspan: 2 }= t('.shipping_methods')
%th{ colspan: 2 }
= t('.shipping_methods')
= hidden_field_tag "order_cycle[selected_distributor_shipping_method_ids][]", ""
- @order_cycle.distributors.each do |distributor|
- distributor_shipping_methods = @order_cycle.attachable_distributor_shipping_methods.where("distributor_id = ?", distributor.id).includes(:shipping_method)
%tr{ "data-controller": "select-all" }
%tr{ class: "distributor-#{distributor.id}-shipping-methods", "data-controller": "select-all" }
%td.text-center
- if distributor_shipping_methods.many?
%label
@@ -48,17 +48,36 @@
%p
= t('.no_shipping_methods')
%tr
%th{ colspan: 2 }= t('.payment_methods')
%tr
%td
%td
- if @order_cycle.attachable_payment_methods.available(:both).any?
%ul
- @order_cycle.attachable_payment_methods.available(:both).each do |payment_method|
%li= payment_method.name
- else
%p
= t('.no_payment_methods')
%th{ colspan: 2 }
= t('.payment_methods')
= hidden_field_tag "order_cycle[selected_distributor_payment_method_ids][]", ""
- @order_cycle.distributors.each do |distributor|
- distributor_payment_methods = @order_cycle.attachable_distributor_payment_methods.where("distributor_id = ?", distributor.id).includes(:payment_method)
%tr{ class: "distributor-#{distributor.id}-payment-methods", "data-controller": "select-all" }
%td.text-center
- if distributor_payment_methods.many?
%label
= check_box_tag nil, nil, nil, { "data-action": "change->select-all#toggleAll", "data-select-all-target": "all" }
= t(".select_all")
%td
%em= distributor.name
- distributor_payment_methods.each do |distributor_payment_method|
%p
%label{ class: ("disabled" if distributor_payment_methods.one? || !distributor_payment_method.payment_method.frontend?) }
= check_box_tag "order_cycle[selected_distributor_payment_method_ids][]",
distributor_payment_method.id,
@order_cycle.distributor_payment_methods.include?(distributor_payment_method),
id: "order_cycle_selected_distributor_payment_method_ids_#{distributor_payment_method.id}",
data: ({ "action" => "change->select-all#toggleCheckbox", "select-all-target" => "checkbox" } if distributor_payment_method.payment_method.frontend?)
= distributor_payment_method.payment_method.name
- distributor.payment_methods.inactive_or_backend.each do |payment_method|
%label.disabled
= check_box_tag nil, nil, false, disabled: true
= payment_method.name
= "(#{t('.back_end')})"
- if distributor.payment_methods.available.none?
%p
= t('.no_payment_methods')
%div#save-bar
%div.container

View File

@@ -0,0 +1,7 @@
class AddIdAndTimestampsToDistributorPaymentMethods < ActiveRecord::Migration[6.1]
def change
add_column :distributors_payment_methods, :id, :primary_key
add_column :distributors_payment_methods, :created_at, :datetime
add_column :distributors_payment_methods, :updated_at, :datetime
end
end

View File

@@ -0,0 +1,13 @@
class CreateOrderCyclesDistributorPaymentMethods < ActiveRecord::Migration[6.1]
def change
create_table :order_cycles_distributor_payment_methods, id: false do |t|
t.belongs_to :order_cycle,
index: { name: "index_oc_id_on_order_cycles_distributor_payment_methods" }
t.belongs_to :distributor_payment_method,
index: { name: "index_dpm_id_on_order_cycles_distributor_payment_methods" }
t.index [:order_cycle_id, :distributor_payment_method_id],
name: "order_cycles_distributor_payment_methods_join_index",
unique: true
end
end
end

View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2022_10_04_165343) do
ActiveRecord::Schema.define(version: 2022_10_07_105052) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
@@ -107,9 +107,11 @@ ActiveRecord::Schema.define(version: 2022_10_04_165343) do
t.index ["priority", "run_at"], name: "delayed_jobs_priority"
end
create_table "distributors_payment_methods", id: false, force: :cascade do |t|
create_table "distributors_payment_methods", force: :cascade do |t|
t.integer "distributor_id"
t.integer "payment_method_id"
t.datetime "created_at"
t.datetime "updated_at"
t.index ["distributor_id"], name: "index_distributors_payment_methods_on_distributor_id"
t.index ["payment_method_id"], name: "index_distributors_payment_methods_on_payment_method_id"
end
@@ -310,6 +312,14 @@ ActiveRecord::Schema.define(version: 2022_10_04_165343) do
t.boolean "mails_sent", default: false
end
create_table "order_cycles_distributor_payment_methods", id: false, force: :cascade do |t|
t.bigint "order_cycle_id"
t.bigint "distributor_payment_method_id"
t.index ["distributor_payment_method_id"], name: "index_dpm_id_on_order_cycles_distributor_payment_methods"
t.index ["order_cycle_id", "distributor_payment_method_id"], name: "order_cycles_distributor_payment_methods_join_index", unique: true
t.index ["order_cycle_id"], name: "index_oc_id_on_order_cycles_distributor_payment_methods"
end
create_table "order_cycles_distributor_shipping_methods", id: false, force: :cascade do |t|
t.bigint "order_cycle_id"
t.bigint "distributor_shipping_method_id"

View File

@@ -1,30 +0,0 @@
# frozen_string_literal: true
module OpenFoodNetwork
class AvailablePaymentMethodFilter
def filter!(payment_methods)
if stripe_enabled?
payment_methods.to_a.reject! do |payment_method|
payment_method.type.ends_with?("StripeSCA") &&
stripe_configuration_incomplete?(payment_method)
end
else
payment_methods.to_a.reject! do |payment_method|
payment_method.type.ends_with?("StripeSCA")
end
end
end
private
def stripe_enabled?
Spree::Config.stripe_connect_enabled && Stripe.publishable_key
end
def stripe_configuration_incomplete?(payment_method)
payment_method.preferred_enterprise_id.nil? ||
payment_method.preferred_enterprise_id.zero? ||
payment_method.stripe_account_id.blank?
end
end
end

View File

@@ -79,15 +79,9 @@ describe Admin::StripeAccountsController, type: :controller do
describe "#status" do
let(:params) { { format: :json, enterprise_id: enterprise.id } }
around do |example|
original_stripe_connect_enabled = Spree::Config[:stripe_connect_enabled]
example.run
Spree::Config.set(stripe_connect_enabled: original_stripe_connect_enabled)
end
before do
Stripe.api_key = "sk_test_12345"
Spree::Config.set(stripe_connect_enabled: false)
allow(Spree::Config).to receive(:stripe_connect_enabled).and_return(false)
end
context "when I don't manage the specified enterprise" do
@@ -117,7 +111,7 @@ describe Admin::StripeAccountsController, type: :controller do
end
context "when Stripe is enabled" do
before { Spree::Config.set(stripe_connect_enabled: true) }
before { allow(Spree::Config).to receive(:stripe_connect_enabled).and_return(true) }
context "when no stripe account is associated with the specified enterprise" do
it "returns with a status of 'account_missing'" do

View File

@@ -1,175 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
describe EnterprisesHelper, type: :helper do
let(:user) { create(:user) }
let(:distributor) { create(:distributor_enterprise) }
let(:some_other_distributor) { create(:distributor_enterprise) }
before { allow(helper).to receive(:spree_current_user) { user } }
describe "loading available payment methods" do
let!(:pm1) { create(:payment_method, distributors: [distributor]) }
let!(:pm2) { create(:payment_method, distributors: [some_other_distributor]) }
context "when the order has no current_distributor" do
before do
allow(helper).to receive(:current_distributor) { nil }
end
it "returns an empty array" do
expect(helper.available_payment_methods).to eq []
end
end
context "when no tag rules are in effect" do
before { allow(helper).to receive(:current_distributor) { distributor } }
it "finds the payment methods for the current distributor" do
expect(helper.available_payment_methods).to_not include pm2
expect(helper.available_payment_methods).to include pm1
end
end
context "when FilterPaymentMethods tag rules are in effect" do
let(:customer) { create(:customer, user: user, enterprise: distributor) }
let!(:tag_rule) {
create(:filter_payment_methods_tag_rule,
enterprise: distributor,
preferred_customer_tags: "trusted",
preferred_payment_method_tags: "trusted")
}
let!(:default_tag_rule) {
create(:filter_payment_methods_tag_rule,
enterprise: distributor,
is_default: true,
preferred_payment_method_tags: "trusted")
}
let(:tagged_pm) { pm1 }
let(:untagged_pm) { pm2 }
before do
tagged_pm.update_attribute(:tag_list, 'trusted')
distributor.payment_methods = [tagged_pm, untagged_pm]
allow(helper).to receive(:current_distributor) { distributor }
end
context "with a preferred visiblity of 'visible', default visibility of 'hidden'" do
before {
tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'visible')
}
before {
default_tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'hidden')
}
context "when the customer is nil" do
it "applies default action (hide)" do
expect(helper.current_customer).to be nil
expect(helper.available_payment_methods).to include untagged_pm
expect(helper.available_payment_methods).to_not include tagged_pm
end
end
context "when the customer's tags match" do
before { customer.update_attribute(:tag_list, 'trusted') }
it "applies the action (show)" do
expect(helper.current_customer).to eq customer
expect(helper.available_payment_methods).to include tagged_pm, untagged_pm
end
end
context "when the customer's tags don't match" do
before { customer.update_attribute(:tag_list, 'something') }
it "applies the default action (hide)" do
expect(helper.current_customer).to eq customer
expect(helper.available_payment_methods).to include untagged_pm
expect(helper.available_payment_methods).to_not include tagged_pm
end
end
end
context "with a preferred visiblity of 'hidden', default visibility of 'visible'" do
before {
tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'hidden')
}
before {
default_tag_rule.update_attribute(:preferred_matched_payment_methods_visibility,
'visible')
}
context "when the customer is nil" do
it "applies default action (show)" do
expect(helper.current_customer).to be nil
expect(helper.available_payment_methods).to include tagged_pm, untagged_pm
end
end
context "when the customer's tags match" do
before { customer.update_attribute(:tag_list, 'trusted') }
it "applies the action (hide)" do
expect(helper.current_customer).to eq customer
expect(helper.available_payment_methods).to include untagged_pm
expect(helper.available_payment_methods).to_not include tagged_pm
end
end
context "when the customer's tags don't match" do
before { customer.update_attribute(:tag_list, 'something') }
it "applies the default action (show)" do
expect(helper.current_customer).to eq customer
expect(helper.available_payment_methods).to include tagged_pm, untagged_pm
end
end
end
end
context "when Stripe payment methods are present" do
let!(:pm3) {
create(:stripe_sca_payment_method, distributors: [distributor],
preferred_enterprise_id: distributor.id)
}
let!(:pm4) {
create(:stripe_sca_payment_method, distributors: [distributor],
preferred_enterprise_id: some_other_distributor.id)
}
let(:available_payment_methods) { helper.available_payment_methods }
around do |example|
original_stripe_connect_enabled = Spree::Config[:stripe_connect_enabled]
example.run
Spree::Config.set(stripe_connect_enabled: original_stripe_connect_enabled)
end
before do
allow(helper).to receive(:current_distributor) { distributor }
end
context "and Stripe Connect is disabled" do
before { Spree::Config.set(stripe_connect_enabled: false) }
it "ignores Stripe payment methods" do
expect(available_payment_methods).to_not include pm3, pm4
end
end
context "and Stripe Connect is enabled" do
let!(:stripe_account) { create(:stripe_account, enterprise_id: distributor.id) }
before do
Spree::Config.set(stripe_connect_enabled: true)
Stripe.publishable_key = "some_key"
end
it "includes Stripe payment methods with a valid stripe accounts" do
expect(available_payment_methods).to include pm3
expect(available_payment_methods).to_not include pm4
end
end
end
end
end

View File

@@ -276,7 +276,7 @@ describe Enterprise do
expect(Enterprise.ready_for_checkout).not_to include e
end
it "does not show enterprises wchich only have backend shipping methods" do
it "does not show enterprises which only have backend shipping methods" do
create(:shipping_method, distributors: [e],
display_on: Spree::ShippingMethod::DISPLAY_ON_OPTIONS[:back_end])
create(:payment_method, distributors: [e])
@@ -354,6 +354,13 @@ describe Enterprise do
create(:payment_method, distributors: [e])
expect(e.reload).to be_ready_for_checkout
end
it "returns false for enterprises with payment methods that are available but not configured
correctly" do
create(:shipping_method, distributors: [e])
create(:stripe_sca_payment_method, distributors: [e])
expect(e.reload).not_to be_ready_for_checkout
end
end
describe "distributors_with_active_order_cycles" do

View File

@@ -368,68 +368,6 @@ describe OrderCycle do
end
end
describe "clone!" do
it "clones itself" do
coordinator = create(:enterprise);
oc = create(:simple_order_cycle,
coordinator_fees: [create(:enterprise_fee, enterprise: coordinator)],
preferred_product_selection_from_coordinator_inventory_only: true,
automatic_notifications: true, processed_at: Time.zone.now, mails_sent: true)
schedule = create(:schedule, order_cycles: [oc])
ex1 = create(:exchange, order_cycle: oc)
ex2 = create(:exchange, order_cycle: oc)
oc.clone!
occ = OrderCycle.last
expect(occ.name).to eq("COPY OF #{oc.name}")
expect(occ.orders_open_at).to be_nil
expect(occ.orders_close_at).to be_nil
expect(occ.coordinator).not_to be_nil
expect(occ.preferred_product_selection_from_coordinator_inventory_only).to be true
expect(occ.automatic_notifications).to eq(oc.automatic_notifications)
expect(occ.processed_at).to eq(nil)
expect(occ.mails_sent).to eq(nil)
expect(occ.coordinator).to eq(oc.coordinator)
expect(occ.coordinator_fee_ids).not_to be_empty
expect(occ.coordinator_fee_ids).to eq(oc.coordinator_fee_ids)
expect(occ.preferred_product_selection_from_coordinator_inventory_only).to eq(oc.preferred_product_selection_from_coordinator_inventory_only)
expect(occ.schedule_ids).not_to be_empty
expect(occ.schedule_ids).to eq(oc.schedule_ids)
# Check that the exchanges have been cloned.
original_exchange_attributes = oc.exchanges.map { |ex| core_exchange_attributes(ex) }
cloned_exchange_attributes = occ.exchanges.map { |ex| core_exchange_attributes(ex) }
expect(cloned_exchange_attributes).to match_array original_exchange_attributes
end
context "when it has preferred shipping methods which can longer be applied validly
e.g. shipping method is backoffice only" do
it "only attaches the valid ones to the clone" do
distributor = create(:distributor_enterprise)
distributor_shipping_method_i = create(
:shipping_method,
distributors: [distributor]
).distributor_shipping_methods.first
distributor_shipping_method_ii = create(
:shipping_method,
distributors: [distributor],
display_on: Spree::ShippingMethod::DISPLAY_ON_OPTIONS[:back_end]
).distributor_shipping_methods.first
order_cycle = create(:distributor_order_cycle, distributors: [distributor])
order_cycle.selected_distributor_shipping_methods = [
distributor_shipping_method_i,
distributor_shipping_method_ii
]
cloned_order_cycle = order_cycle.clone!
expect(cloned_order_cycle.distributor_shipping_methods).to eq [distributor_shipping_method_i]
end
end
end
describe "finding recently closed order cycles" do
it "should give the most recently closed order cycle for a distributor" do
distributor = create(:distributor_enterprise)
@@ -637,6 +575,32 @@ describe OrderCycle do
end
end
describe "#attachable_distributor_payment_methods" do
it "includes distributor payment methods from the distributors on the order cycle" do
payment_method = create(:payment_method)
oc = create(:simple_order_cycle, distributors: [payment_method.distributors.first])
distributor_payment_method = payment_method.distributor_payment_methods.first
expect(oc.attachable_distributor_payment_methods).to eq([distributor_payment_method])
end
it "does not include backoffice only distributor payment methods" do
payment_method = create(:payment_method, display_on: "back_end")
enterprise = create(:enterprise, payment_methods: [payment_method])
oc = create(:simple_order_cycle, distributors: [enterprise])
expect(oc.attachable_distributor_payment_methods).to be_empty
end
it "does not include inactive distributor payment methods" do
payment_method = create(:payment_method, active: false)
enterprise = create(:enterprise, payment_methods: [payment_method])
oc = create(:simple_order_cycle, distributors: [enterprise])
expect(oc.attachable_distributor_payment_methods).to be_empty
end
end
describe "#attachable_distributor_shipping_methods" do
it "includes distributor shipping methods from the distributors on the order cycle" do
shipping_method = create(:shipping_method)
@@ -655,6 +619,80 @@ describe OrderCycle do
end
end
describe "#distributor_payment_methods" do
let(:distributor) { create(:distributor_enterprise) }
it "returns all attachable distributor payment methods if the order cycle is simple" do
oc = create(:sells_own_order_cycle, distributors: [distributor])
distributor_payment_method = create(
:payment_method,
distributors: [distributor]
).distributor_payment_methods.first
expect(oc.distributor_payment_methods).to eq [distributor_payment_method]
end
context "distributor order cycle i.e. non-simple" do
let(:oc) { create(:distributor_order_cycle, distributors: [distributor]) }
it "returns all attachable distributor payment methods if no distributor payment methods
have been selected specifically" do
distributor_payment_method = create(
:payment_method,
distributors: [distributor]
).distributor_payment_methods.first
expect(oc.selected_distributor_payment_methods).to be_empty
expect(oc.distributor_payment_methods).to eq [distributor_payment_method]
end
it "returns selected distributor payment methods if they have been specified" do
distributor_payment_method_i = create(
:payment_method,
distributors: [distributor]
).distributor_payment_methods.first
distributor_payment_method_ii = create(
:payment_method,
distributors: [distributor]
).distributor_payment_methods.first
oc.selected_distributor_payment_methods << distributor_payment_method_ii
expect(oc.distributor_payment_methods).to eq [distributor_payment_method_ii]
end
context "with multiple distributors" do
let(:other_distributor) { create(:distributor_enterprise) }
let(:oc) { create(:distributor_order_cycle, distributors: [distributor, other_distributor]) }
it "returns all attachable distributor payment methods for a distributor if no distributor
payment methods have been selected specifically for that distributor, even if
distributor payment methods have been selected specifically for a different distributor
on the order cycle" do
distributor_payment_method = create(
:payment_method,
distributors: [distributor]
).distributor_payment_methods.first
other_distributor_payment_method_i = create(
:payment_method,
distributors: [other_distributor]
).distributor_payment_methods.first
other_distributor_payment_method_ii = create(
:payment_method,
distributors: [other_distributor]
).distributor_payment_methods.first
oc.selected_distributor_payment_methods << other_distributor_payment_method_i
expect(oc.distributor_payment_methods).to eq [
distributor_payment_method,
other_distributor_payment_method_i
]
end
end
end
end
describe "#distributor_shipping_methods" do
let(:distributor) { create(:distributor_enterprise) }
@@ -742,14 +780,4 @@ describe OrderCycle do
expect(order_cycle).not_to be_simple
end
end
def core_exchange_attributes(exchange)
exterior_attribute_keys = %w(id order_cycle_id created_at updated_at)
exchange.attributes.
reject { |k| exterior_attribute_keys.include? k }.
merge(
'variant_ids' => exchange.variant_ids.sort,
'enterprise_fee_ids' => exchange.enterprise_fee_ids.sort
)
end
end

View File

@@ -5,100 +5,160 @@ require 'spec_helper'
class Spree::Gateway::Test < Spree::Gateway
end
module Spree
describe PaymentMethod do
describe "#available" do
let(:enterprise) { create(:enterprise) }
describe Spree::PaymentMethod do
describe "#available" do
let(:enterprise) { create(:enterprise) }
before do
Spree::PaymentMethod.delete_all
[nil, 'both', 'back_end'].each do |display_on|
Spree::Gateway::Test.create(
name: 'Display Both',
display_on: display_on,
active: true,
environment: 'test',
description: 'foofah',
distributors: [enterprise]
)
end
expect(Spree::PaymentMethod.all.size).to eq 3
end
it "should return all methods available to front-end/back-end when no parameter is passed" do
expect(Spree::PaymentMethod.available.size).to eq 2
end
it "should return all methods available to front-end/back-end when display_on = :both" do
expect(Spree::PaymentMethod.available(:both).size).to eq 2
end
it "should return all methods available to back-end when display_on = :back_end" do
expect(Spree::PaymentMethod.available(:back_end).size).to eq 2
end
end
describe "#configured?" do
context "non-Stripe payment method" do
let(:payment_method) { build(:payment_method) }
it "returns true" do
expect(payment_method).to be_configured
end
end
context "Stripe payment method" do
let(:payment_method) { create(:stripe_sca_payment_method) }
before do
Spree::PaymentMethod.delete_all
allow(Spree::Config).to receive(:stripe_connect_enabled).and_return(true)
allow(Stripe).to receive(:publishable_key) { "some_key" }
end
[nil, 'both', 'back_end'].each do |display_on|
Spree::Gateway::Test.create(
name: 'Display Both',
display_on: display_on,
active: true,
environment: 'test',
description: 'foofah',
distributors: [enterprise]
)
context "and Stripe Connect is enabled and a Stripe publishable key, account id, account
owner are all present" do
it "returns true" do
expect(payment_method).to be_configured
end
expect(Spree::PaymentMethod.all.size).to eq 3
end
it "should return all methods available to front-end/back-end when no parameter is passed" do
expect(Spree::PaymentMethod.available.size).to eq 2
context "and Stripe Connect is disabled" do
before { allow(Spree::Config).to receive(:stripe_connect_enabled).and_return(false) }
it "returns false" do
expect(payment_method).not_to be_configured
end
end
it "should return all methods available to front-end/back-end when display_on = :both" do
expect(Spree::PaymentMethod.available(:both).size).to eq 2
context "and a Stripe publishable key is not present" do
before { allow(Stripe).to receive(:publishable_key) { nil } }
it "returns false" do
expect(payment_method).not_to be_configured
end
end
it "should return all methods available to back-end when display_on = :back_end" do
expect(Spree::PaymentMethod.available(:back_end).size).to eq 2
context "and a Stripe account owner is not present" do
before { payment_method.preferred_enterprise_id = nil }
it "returns false" do
expect(payment_method).not_to be_configured
end
end
end
it "orders payment methods by name" do
pm1 = create(:payment_method, name: 'ZZ')
pm2 = create(:payment_method, name: 'AA')
pm3 = create(:payment_method, name: 'BB')
context "and a Stripe account ID is not present" do
before do
StripeAccount.find_by(
enterprise_id: payment_method.preferred_enterprise_id
).update_column(:stripe_user_id, nil)
end
expect(PaymentMethod.by_name).to eq([pm2, pm3, pm1])
end
it "raises errors when required fields are missing" do
pm = PaymentMethod.new
pm.save
expect(pm.errors.to_a).to eq(["Name can't be blank", "At least one hub must be selected"])
end
it "generates a clean name for known Payment Method types" do
expect(Spree::PaymentMethod::Check.clean_name).to eq(I18n.t("spree.admin.payment_methods.providers.check"))
expect(Spree::Gateway::PayPalExpress.clean_name).to eq(I18n.t("spree.admin.payment_methods.providers.paypalexpress"))
expect(Spree::Gateway::StripeSCA.clean_name).to eq(I18n.t("spree.admin.payment_methods.providers.stripesca"))
expect(Spree::Gateway::BogusSimple.clean_name).to eq(I18n.t("spree.admin.payment_methods.providers.bogussimple"))
expect(Spree::Gateway::Bogus.clean_name).to eq(I18n.t("spree.admin.payment_methods.providers.bogus"))
end
it "computes the amount of fees" do
order = create(:order)
free_payment_method = create(:payment_method) # flat rate calculator with preferred_amount of 0
expect(free_payment_method.compute_amount(order)).to eq 0
flat_rate_payment_method = create(:payment_method,
calculator: ::Calculator::FlatRate.new(preferred_amount: 10))
expect(flat_rate_payment_method.compute_amount(order)).to eq 10
flat_percent_payment_method = create(:payment_method,
calculator: ::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10))
expect(flat_percent_payment_method.compute_amount(order)).to eq 0
product = create(:product)
order.contents.add(product.variants.first)
expect(flat_percent_payment_method.compute_amount(order)).to eq 2.0
end
describe "scope" do
describe "filtering to specified distributors" do
let!(:distributor_a) { create(:distributor_enterprise) }
let!(:distributor_b) { create(:distributor_enterprise) }
let!(:distributor_c) { create(:distributor_enterprise) }
let!(:payment_method_a) {
create(:payment_method, distributors: [distributor_a, distributor_b])
}
let!(:payment_method_b) { create(:payment_method, distributors: [distributor_b]) }
let!(:payment_method_c) { create(:payment_method, distributors: [distributor_c]) }
it "includes only unique records under specified distributors" do
result = described_class.for_distributors([distributor_a, distributor_b])
expect(result.length).to eq(2)
expect(result).to include(payment_method_a)
expect(result).to include(payment_method_b)
it "returns false" do
expect(payment_method).not_to be_configured
end
end
end
end
it "orders payment methods by name" do
pm1 = create(:payment_method, name: 'ZZ')
pm2 = create(:payment_method, name: 'AA')
pm3 = create(:payment_method, name: 'BB')
expect(Spree::PaymentMethod.by_name).to eq([pm2, pm3, pm1])
end
it "raises errors when required fields are missing" do
pm = Spree::PaymentMethod.new
pm.save
expect(pm.errors.to_a).to eq(["Name can't be blank", "At least one hub must be selected"])
end
it "generates a clean name for known Payment Method types" do
expect(Spree::PaymentMethod::Check.clean_name).to eq(I18n.t("spree.admin.payment_methods.providers.check"))
expect(Spree::Gateway::PayPalExpress.clean_name).to eq(I18n.t("spree.admin.payment_methods.providers.paypalexpress"))
expect(Spree::Gateway::StripeSCA.clean_name).to eq(I18n.t("spree.admin.payment_methods.providers.stripesca"))
expect(Spree::Gateway::BogusSimple.clean_name).to eq(I18n.t("spree.admin.payment_methods.providers.bogussimple"))
expect(Spree::Gateway::Bogus.clean_name).to eq(I18n.t("spree.admin.payment_methods.providers.bogus"))
end
it "computes the amount of fees" do
order = create(:order)
free_payment_method = create(:payment_method) # flat rate calculator with preferred_amount of 0
expect(free_payment_method.compute_amount(order)).to eq 0
flat_rate_payment_method = create(:payment_method,
calculator: ::Calculator::FlatRate.new(preferred_amount: 10))
expect(flat_rate_payment_method.compute_amount(order)).to eq 10
flat_percent_payment_method = create(:payment_method,
calculator: ::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10))
expect(flat_percent_payment_method.compute_amount(order)).to eq 0
product = create(:product)
order.contents.add(product.variants.first)
expect(flat_percent_payment_method.compute_amount(order)).to eq 2.0
end
describe "scope" do
describe "filtering to specified distributors" do
let!(:distributor_a) { create(:distributor_enterprise) }
let!(:distributor_b) { create(:distributor_enterprise) }
let!(:distributor_c) { create(:distributor_enterprise) }
let!(:payment_method_a) {
create(:payment_method, distributors: [distributor_a, distributor_b])
}
let!(:payment_method_b) { create(:payment_method, distributors: [distributor_b]) }
let!(:payment_method_c) { create(:payment_method, distributors: [distributor_c]) }
it "includes only unique records under specified distributors" do
result = described_class.for_distributors([distributor_a, distributor_b])
expect(result.length).to eq(2)
expect(result).to include(payment_method_a)
expect(result).to include(payment_method_b)
end
end
end
end

View File

@@ -86,7 +86,8 @@ describe "checking out an order with a Stripe SCA payment method", type: :reques
order_cycle_distributed_variants = double(:order_cycle_distributed_variants)
allow(OrderCycleDistributedVariants).to receive(:new) { order_cycle_distributed_variants }
allow(order_cycle_distributed_variants).to receive(:distributes_order_variants?) { true }
allow(Stripe).to receive(:publishable_key).and_return("some_token")
allow(Spree::Config).to receive(:stripe_connect_enabled).and_return(true)
Stripe.api_key = "sk_test_12345"
order.update(distributor_id: enterprise.id, order_cycle_id: order_cycle.id)
order.reload.update_totals

View File

@@ -0,0 +1,217 @@
# frozen_string_literal: true
require 'spec_helper'
describe OrderAvailablePaymentMethods do
context "when the order has no current_distributor" do
it "returns an empty array" do
order_cycle = create(:sells_own_order_cycle)
order = build(:order, distributor: nil, order_cycle: order_cycle)
expect(OrderAvailablePaymentMethods.new(order).to_a).to eq []
end
end
it "does not return 'back office only' payment method" do
distributor = create(:distributor_enterprise)
frontend_payment_method = create(:payment_method, distributors: [distributor])
backoffice_only_payment_method = create(:payment_method,
distributors: [distributor],
display_on: 'back_end')
order_cycle = create(:sells_own_order_cycle)
order = build(:order, distributor: distributor, order_cycle: order_cycle)
available_payment_methods = OrderAvailablePaymentMethods.new(order).to_a
expect(available_payment_methods).to eq [frontend_payment_method]
end
it "does not return payment methods which are not configured correctly" do
distributor = create(:distributor_enterprise)
frontend_payment_method = create(:payment_method, distributors: [distributor])
unconfigured_payment_method = create(:stripe_sca_payment_method,
distributors: [distributor],
display_on: 'back_end')
order_cycle = create(:sells_own_order_cycle)
order = build(:order, distributor: distributor, order_cycle: order_cycle)
available_payment_methods = OrderAvailablePaymentMethods.new(order).to_a
expect(available_payment_methods).to eq [frontend_payment_method]
end
context "when no tag rules are in effect" do
context "sells own order cycle i.e. simple" do
it "only returns the payment methods which are available on the order cycle
and belong to the order distributor" do
distributor_i = create(:distributor_enterprise)
distributor_ii = create(:distributor_enterprise)
distributor_iii = create(:distributor_enterprise)
payment_method_i = create(:payment_method, distributors: [distributor_i])
payment_method_ii = create(:payment_method, distributors: [distributor_ii])
payment_method_iii = create(:payment_method, distributors: [distributor_iii])
order_cycle = create(:sells_own_order_cycle, distributors: [distributor_i, distributor_ii])
order = build(:order, distributor: distributor_i, order_cycle: order_cycle)
available_payment_methods = OrderAvailablePaymentMethods.new(order).to_a
expect(available_payment_methods).to eq [payment_method_i]
end
end
context "distributor order cycle i.e. not simple" do
it "only returns the payment methods which are available on the order cycle
and belong to the order distributor" do
distributor_i = create(:distributor_enterprise, payment_methods: [])
distributor_ii = create(:distributor_enterprise, payment_methods: [])
payment_method_i = create(:payment_method, distributors: [distributor_i])
payment_method_ii = create(:payment_method, distributors: [distributor_i])
payment_method_iii = create(:payment_method, distributors: [distributor_ii])
payment_method_iv = create(:payment_method, distributors: [distributor_ii])
order_cycle = create(:distributor_order_cycle,
distributors: [distributor_i, distributor_ii])
order_cycle.selected_distributor_payment_methods << [
distributor_i.distributor_payment_methods.first,
distributor_ii.distributor_payment_methods.first,
]
order = build(:order, distributor: distributor_i, order_cycle: order_cycle)
available_payment_methods = OrderAvailablePaymentMethods.new(order).to_a
expect(available_payment_methods).to eq [payment_method_i]
end
end
end
context "when FilterPaymentMethods tag rules are in effect" do
let(:user) { create(:user) }
let(:distributor) { create(:distributor_enterprise) }
let(:other_distributor) { create(:distributor_enterprise) }
let!(:distributor_payment_method) { create(:payment_method, distributors: [distributor]) }
let!(:other_distributor_payment_method) do
create(:payment_method, distributors: [other_distributor])
end
let(:customer) { create(:customer, user: user, enterprise: distributor) }
let!(:tag_rule) {
create(:filter_payment_methods_tag_rule,
enterprise: distributor,
preferred_customer_tags: "local",
preferred_payment_method_tags: "local-delivery")
}
let!(:default_tag_rule) {
create(:filter_payment_methods_tag_rule,
enterprise: distributor,
is_default: true,
preferred_payment_method_tags: "local-delivery")
}
let!(:tagged_payment_method) { distributor_payment_method }
let!(:untagged_payment_method) { other_distributor_payment_method }
before do
tagged_payment_method.update_attribute(:tag_list, 'local-delivery')
distributor.payment_methods = [tagged_payment_method, untagged_payment_method]
end
context "with a preferred visiblity of 'visible', default visibility of 'hidden'" do
before {
tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'visible')
}
before {
default_tag_rule.update_attribute(:preferred_matched_payment_methods_visibility,
'hidden')
}
let(:order_cycle) { create(:sells_own_order_cycle) }
let(:order) { build(:order, distributor: distributor, order_cycle: order_cycle) }
context "when the customer is nil" do
let(:available_payment_methods) { OrderAvailablePaymentMethods.new(order).to_a }
it "applies default action (hide)" do
expect(available_payment_methods).to include untagged_payment_method
expect(available_payment_methods).to_not include tagged_payment_method
end
end
context "when a customer is present" do
let(:available_payment_methods) { OrderAvailablePaymentMethods.new(order, customer).to_a }
context "and the customer's tags match" do
before do
customer.update_attribute(:tag_list, 'local')
end
it "applies the action (show)" do
expect(available_payment_methods).to include(
tagged_payment_method,
untagged_payment_method
)
end
end
context "and the customer's tags don't match" do
before do
customer.update_attribute(:tag_list, 'something')
end
it "applies the default action (hide)" do
expect(available_payment_methods).to include untagged_payment_method
expect(available_payment_methods).to_not include tagged_payment_method
end
end
end
end
context "with a preferred visiblity of 'hidden', default visibility of 'visible'" do
before {
tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'hidden')
}
before {
default_tag_rule.update_attribute(:preferred_matched_payment_methods_visibility,
'visible')
}
let(:order_cycle) { create(:sells_own_order_cycle) }
let(:order) { build(:order, distributor: distributor, order_cycle: order_cycle) }
context "when the customer is nil" do
let(:available_payment_methods) { OrderAvailablePaymentMethods.new(order).to_a }
it "applies default action (show)" do
expect(available_payment_methods).to include(
tagged_payment_method,
untagged_payment_method
)
end
end
context "when a customer is present" do
let(:available_payment_methods) { OrderAvailablePaymentMethods.new(order, customer).to_a }
context "and the customer's tags match" do
before do
customer.update_attribute(:tag_list, 'local')
end
it "applies the action (hide)" do
expect(available_payment_methods).to include untagged_payment_method
expect(available_payment_methods).to_not include tagged_payment_method
end
end
context "and the customer's tags don't match" do
before do
customer.update_attribute(:tag_list, 'something')
end
it "applies the default action (show)" do
expect(available_payment_methods).to include(
tagged_payment_method,
untagged_payment_method
)
end
end
end
end
end
end

View File

@@ -0,0 +1,107 @@
# frozen_string_literal: true
require 'spec_helper'
describe OrderCycleForm do
describe "#create" do
it "clones the order cycle" do
coordinator = create(:enterprise);
oc = create(:simple_order_cycle,
coordinator_fees: [create(:enterprise_fee, enterprise: coordinator)],
preferred_product_selection_from_coordinator_inventory_only: true,
automatic_notifications: true, processed_at: Time.zone.now, mails_sent: true)
schedule = create(:schedule, order_cycles: [oc])
ex1 = create(:exchange, order_cycle: oc)
ex2 = create(:exchange, order_cycle: oc)
occ = OrderCycleClone.new(oc).create
expect(occ.name).to eq("COPY OF #{oc.name}")
expect(occ.orders_open_at).to be_nil
expect(occ.orders_close_at).to be_nil
expect(occ.coordinator).not_to be_nil
expect(occ.preferred_product_selection_from_coordinator_inventory_only).to be true
expect(occ.automatic_notifications).to eq(oc.automatic_notifications)
expect(occ.processed_at).to eq(nil)
expect(occ.mails_sent).to eq(nil)
expect(occ.coordinator).to eq(oc.coordinator)
expect(occ.coordinator_fee_ids).not_to be_empty
expect(occ.coordinator_fee_ids).to eq(oc.coordinator_fee_ids)
expect(occ.preferred_product_selection_from_coordinator_inventory_only).to eq(
oc.preferred_product_selection_from_coordinator_inventory_only
)
expect(occ.schedule_ids).not_to be_empty
expect(occ.schedule_ids).to eq(oc.schedule_ids)
# Check that the exchanges have been cloned.
original_exchange_attributes = oc.exchanges.map { |ex| core_exchange_attributes(ex) }
cloned_exchange_attributes = occ.exchanges.map { |ex| core_exchange_attributes(ex) }
expect(cloned_exchange_attributes).to match_array original_exchange_attributes
end
context "when it has selected payment methods which can longer be applied validly
e.g. payment method is backoffice only" do
it "only attaches the valid ones to the clone" do
distributor = create(:distributor_enterprise)
distributor_payment_method_i = create(
:payment_method,
distributors: [distributor]
).distributor_payment_methods.first
distributor_payment_method_ii = create(
:payment_method,
distributors: [distributor],
display_on: "back_end"
).distributor_payment_methods.first
order_cycle = create(:distributor_order_cycle, distributors: [distributor])
order_cycle.selected_distributor_payment_methods = [
distributor_payment_method_i,
distributor_payment_method_ii
]
cloned_order_cycle = order_cycle.clone!
expect(cloned_order_cycle.distributor_payment_methods).to eq [distributor_payment_method_i]
end
end
context "when it has selected shipping methods which can longer be applied validly
e.g. shipping method is backoffice only" do
it "only attaches the valid ones to the clone" do
distributor = create(:distributor_enterprise)
distributor_shipping_method_i = create(
:shipping_method,
distributors: [distributor]
).distributor_shipping_methods.first
distributor_shipping_method_ii = create(
:shipping_method,
distributors: [distributor],
display_on: Spree::ShippingMethod::DISPLAY_ON_OPTIONS[:back_end]
).distributor_shipping_methods.first
order_cycle = create(:distributor_order_cycle, distributors: [distributor])
order_cycle.selected_distributor_shipping_methods = [
distributor_shipping_method_i,
distributor_shipping_method_ii
]
cloned_order_cycle = order_cycle.clone!
expect(cloned_order_cycle.distributor_shipping_methods).to eq [
distributor_shipping_method_i
]
end
end
end
private
def core_exchange_attributes(exchange)
exterior_attribute_keys = %w(id order_cycle_id created_at updated_at)
exchange.attributes.
reject { |k| exterior_attribute_keys.include? k }.
merge(
'variant_ids' => exchange.variant_ids.sort,
'enterprise_fee_ids' => exchange.enterprise_fee_ids.sort
)
end
end

View File

@@ -135,7 +135,9 @@ describe OrderCycleForm do
let(:distributor) { order_cycle.coordinator }
let(:supplier) { create(:supplier_enterprise) }
let(:user) { distributor.owner }
let(:payment_method) { create(:payment_method, distributors: [distributor]) }
let(:shipping_method) { create(:shipping_method, distributors: [distributor]) }
let(:distributor_payment_method) { payment_method.distributor_payment_methods.first }
let(:distributor_shipping_method) { shipping_method.distributor_shipping_methods.first }
let(:variant) { create(:variant, product: create(:product, supplier: supplier)) }
let(:params) { { name: 'Some new name' } }
@@ -151,14 +153,14 @@ describe OrderCycleForm do
}
end
context "basic update i.e. without exchanges or shipping methods" do
context "basic update i.e. without exchanges or payment/shipping methods" do
it do
expect(form.save).to be true
expect(order_cycle.name).to eq 'Some new name'
end
end
context "updating basics, incoming exchanges, outcoming exchanges
context "updating basics, incoming exchanges, outcoming exchanges, payment_methods
and shipping methods simultaneously" do
before do
params.merge!(
@@ -171,21 +173,31 @@ describe OrderCycleForm do
enterprise_fee_ids: []
}],
outgoing_exchanges: [outgoing_exchange_params],
selected_distributor_payment_method_ids: [distributor_payment_method.id],
selected_distributor_shipping_method_ids: [distributor_shipping_method.id]
)
end
it "saves everything i.e. the basics, incoming and outgoing exchanges and shipping methods" do
it "saves everything i.e. the basics, incoming and outgoing exchanges, payment methods and
shipping methods" do
expect(form.save).to be true
expect(order_cycle.name).to eq 'Some new name'
expect(order_cycle.cached_incoming_exchanges.count).to eq 1
expect(order_cycle.cached_outgoing_exchanges.count).to eq 1
expect(order_cycle.distributor_payment_methods).to eq [distributor_payment_method]
expect(order_cycle.distributor_shipping_methods).to eq [distributor_shipping_method]
end
end
context "updating outgoing exchanges and shipping methods simultaneously but the shipping
method doesn't belong to the new or any existing order cycle distributor" do
context "updating outgoing exchanges and shipping methods simultaneously but the payment
and shipping methods don't belong to the new or any existing order cycle
distributor" do
let(:other_distributor_payment_method) do
create(
:payment_method,
distributors: [create(:distributor_enterprise)]
).distributor_payment_methods.first
end
let(:other_distributor_shipping_method) do
create(
:shipping_method,
@@ -196,6 +208,7 @@ describe OrderCycleForm do
before do
params.merge!(
outgoing_exchanges: [outgoing_exchange_params],
selected_distributor_payment_method_ids: [other_distributor_payment_method.id],
selected_distributor_shipping_method_ids: [other_distributor_shipping_method.id]
)
end
@@ -203,10 +216,59 @@ describe OrderCycleForm do
it "saves the outgoing exchange but ignores the shipping method" do
expect(form.save).to be true
expect(order_cycle.distributors).to eq [distributor]
expect(order_cycle.distributor_payment_methods).to be_empty
expect(order_cycle.distributor_shipping_methods).to be_empty
end
end
context "updating payment methods" do
context "and it's valid" do
it "saves the changes" do
distributor = create(:distributor_enterprise)
distributor_payment_method = create(
:payment_method,
distributors: [distributor]
).distributor_payment_methods.first
order_cycle = create(:distributor_order_cycle, distributors: [distributor])
form = OrderCycleForm.new(
order_cycle,
{ selected_distributor_payment_method_ids: [distributor_payment_method.id] },
order_cycle.coordinator
)
expect(form.save).to be true
expect(order_cycle.distributor_payment_methods).to eq [distributor_payment_method]
end
end
context "with a payment method which doesn't belong to any distributor on the order cycle" do
it "ignores it" do
distributor_i = create(:distributor_enterprise)
distributor_ii = create(:distributor_enterprise)
distributor_payment_method_i = create(
:payment_method,
distributors: [distributor_i]
).distributor_payment_methods.first
distributor_payment_method_ii = create(
:payment_method,
distributors: [distributor_ii]
).distributor_payment_methods.first
order_cycle = create(:distributor_order_cycle,
distributors: [distributor_i])
form = OrderCycleForm.new(
order_cycle,
{ selected_distributor_payment_method_ids: [distributor_payment_method_ii.id] },
order_cycle.coordinator
)
expect(form.save).to be true
expect(order_cycle.distributor_payment_methods).to eq [distributor_payment_method_i]
end
end
end
context "updating shipping methods" do
context "and it's valid" do
it "saves the changes" do

View File

@@ -41,15 +41,93 @@ describe Shop::OrderCyclesList do
let(:customer) { nil }
context "when the order cycle is open and belongs to the distributor" do
context "and the distributor is ready for checkout" do
context "and the distributor has some payment and shipping methods" do
let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) }
it "returns the order cycle" do
open_order_cycle = create(:open_order_cycle, distributors: [distributor])
context "and there are some payment and shipping methods available to the customer" do
it "returns the order cycle" do
open_order_cycle = create(:open_order_cycle, distributors: [distributor])
expect(Shop::OrderCyclesList.ready_for_checkout_for(distributor, customer)).to eq [
open_order_cycle
]
expect(Shop::OrderCyclesList.ready_for_checkout_for(distributor, customer)).to eq [
open_order_cycle
]
end
end
context "when payment method tag rules are present" do
let!(:open_order_cycle) { create(:open_order_cycle, distributors: [distributor]) }
before do
create(:filter_payment_methods_tag_rule,
enterprise: distributor,
is_default: true,
preferred_payment_method_tags: "local-delivery",
preferred_matched_payment_methods_visibility: "hidden")
create(:filter_payment_methods_tag_rule,
enterprise: distributor,
preferred_customer_tags: "local",
preferred_payment_method_tags: "local-delivery",
preferred_matched_payment_methods_visibility: "visible")
distributor.payment_methods.first.update_attribute(:tag_list, "local-delivery")
end
context "and some payment methods are available to the customer due to the tag rule" do
it "returns the order cycle" do
customer = create(:customer, tag_list: "local")
expect(Shop::OrderCyclesList.ready_for_checkout_for(distributor, customer)).to eq [
open_order_cycle
]
end
end
context "but no payment methods are available to the customer due to the tag rule" do
it "doesn't return the order cycle" do
customer = create(:customer, tag_list: "not-local")
expect(
Shop::OrderCyclesList.ready_for_checkout_for(distributor, customer)
).to be_empty
end
end
end
context "when shipping method tag rules are present" do
let!(:open_order_cycle) { create(:open_order_cycle, distributors: [distributor]) }
before do
create(:filter_shipping_methods_tag_rule,
enterprise: distributor,
is_default: true,
preferred_shipping_method_tags: "local-delivery",
preferred_matched_shipping_methods_visibility: "hidden")
create(:filter_shipping_methods_tag_rule,
enterprise: distributor,
preferred_customer_tags: "local",
preferred_shipping_method_tags: "local-delivery",
preferred_matched_shipping_methods_visibility: "visible")
distributor.shipping_methods.first.update_attribute(:tag_list, "local-delivery")
end
context "and some shipping methods are available to the customer due to the tag rule" do
it "returns the order cycle" do
customer = create(:customer, tag_list: "local")
expect(Shop::OrderCyclesList.ready_for_checkout_for(distributor, customer)).to eq [
open_order_cycle
]
end
end
context "but no shipping methods are available to the customer due to the tag rule" do
it "doesn't return the order cycle" do
customer = create(:customer, tag_list: "not-local")
expect(
Shop::OrderCyclesList.ready_for_checkout_for(distributor, customer)
).to be_empty
end
end
end
end

View File

@@ -31,6 +31,6 @@ module StripeHelper
def setup_stripe
Stripe.api_key = "sk_test_12345"
Stripe.publishable_key = "pk_test_12345"
Spree::Config.set(stripe_connect_enabled: true)
allow(Spree::Config).to receive(:stripe_connect_enabled).and_return(true)
end
end

View File

@@ -22,6 +22,8 @@ describe '
let!(:distributor) {
create(:distributor_enterprise, name: 'My distributor', with_payment_and_shipping: true)
}
let!(:payment_method_i) { distributor.payment_methods.first }
let!(:payment_method_ii) { create(:payment_method, distributors: [distributor]) }
let!(:shipping_method_i) { distributor.shipping_methods.first }
let!(:shipping_method_ii) { create(:shipping_method, distributors: [distributor]) }
let(:oc) { OrderCycle.last }
@@ -44,6 +46,7 @@ describe '
shipping_method_i.update!(name: "Pickup - always available")
shipping_method_ii.update!(name: "Delivery - sometimes available")
payment_method_ii.update!(name: "Cash")
end
it "creating an order cycle with full interface", js: true do
@@ -68,6 +71,8 @@ describe '
add_supplier_with_fees
add_distributor_with_fees
select_distributor_shipping_methods
select_distributor_payment_methods
click_button 'Save and Back to List'
expect_all_data_saved
end
@@ -161,22 +166,50 @@ describe '
click_button 'Save and Next'
end
def select_distributor_payment_methods
within("tr.distributor-#{distributor.id}-payment-methods") do
expect(page).to have_checked_field "Select all"
expect(page).to have_checked_field "Check"
expect(page).to have_checked_field "Cash"
uncheck "Cash"
expect(page).to have_unchecked_field "Select all"
expect_checking_select_all_payment_methods_works
expect_unchecking_select_all_payment_methods_works
# Our final selection:
check "Check"
end
end
def select_distributor_shipping_methods
expect(page).to have_checked_field "Select all"
within("tr.distributor-#{distributor.id}-shipping-methods") do
expect(page).to have_checked_field "Select all"
expect(page).to have_checked_field "Pickup - always available"
expect(page).to have_checked_field "Delivery - sometimes available"
expect(page).to have_checked_field "Pickup - always available"
expect(page).to have_checked_field "Delivery - sometimes available"
uncheck "Delivery - sometimes available"
uncheck "Delivery - sometimes available"
expect(page).to have_unchecked_field "Select all"
expect(page).to have_unchecked_field "Select all"
expect_checking_select_all_shipping_methods_works
expect_unchecking_select_all_shipping_methods_works
expect_checking_select_all_shipping_methods_works
expect_unchecking_select_all_shipping_methods_works
# Our final selection:
check "Pickup - always available"
click_button 'Save and Back to List'
# Our final selection:
check "Pickup - always available"
end
end
def expect_checking_select_all_payment_methods_works
# Now test that the "Select all" input is doing what it's supposed to:
check "Select all"
expect(page).to have_checked_field "Check"
expect(page).to have_checked_field "Cash"
end
def expect_checking_select_all_shipping_methods_works
@@ -187,6 +220,13 @@ describe '
expect(page).to have_checked_field "Delivery - sometimes available"
end
def expect_unchecking_select_all_payment_methods_works
uncheck "Select all"
expect(page).to have_unchecked_field "Check"
expect(page).to have_unchecked_field "Cash"
end
def expect_unchecking_select_all_shipping_methods_works
uncheck "Select all"
@@ -208,6 +248,7 @@ describe '
expect_receival_instructions_saved
expect_pickup_time_and_instructions_saved
expect_distributor_shipping_methods_saved
expect_distributor_payment_methods_saved
end
def expect_opening_and_closing_times_saved
@@ -240,6 +281,10 @@ describe '
expect(exchange.tag_list).to eq(['wholesale'])
end
def expect_distributor_payment_methods_saved
expect(oc.distributor_payment_methods).to eq(payment_method_i.distributor_payment_methods)
end
def expect_distributor_shipping_methods_saved
expect(oc.distributor_shipping_methods).to eq(shipping_method_i.distributor_shipping_methods)
end

View File

@@ -272,6 +272,8 @@ describe '
expect_shipping_methods_to_be_checked_for(distributor_managed)
expect_shipping_methods_to_be_checked_for(distributor_permitted)
expect_payment_methods_to_be_checked_for(distributor_managed)
expect_payment_methods_to_be_checked_for(distributor_permitted)
click_button 'Save and Back to List'
order_cycle = OrderCycle.find_by(name: 'My order cycle')
@@ -286,6 +288,9 @@ describe '
expect(order_cycle.distributor_shipping_methods).to match_array(
order_cycle.attachable_distributor_shipping_methods
)
expect(order_cycle.distributor_payment_methods).to match_array(
order_cycle.attachable_distributor_payment_methods
)
end
context "editing an order cycle" do
@@ -724,6 +729,14 @@ describe '
private
def expect_payment_methods_to_be_checked_for(distributor)
distributor.distributor_payment_method_ids.each do |distributor_payment_method_id|
expect(page).to have_checked_field(
"order_cycle_selected_distributor_payment_method_ids_#{distributor_payment_method_id}"
)
end
end
def expect_shipping_methods_to_be_checked_for(distributor)
distributor.distributor_shipping_method_ids.each do |distributor_shipping_method_id|
expect(page).to have_checked_field(

View File

@@ -53,14 +53,8 @@ describe '
{ id: "acc_connected123", business_name: "My Org", charges_enabled: true }
}
around do |example|
original_stripe_connect_enabled = Spree::Config[:stripe_connect_enabled]
example.run
Spree::Config.set(stripe_connect_enabled: original_stripe_connect_enabled)
end
before do
Spree::Config.set(stripe_connect_enabled: true)
allow(Spree::Config).to receive(:stripe_connect_enabled).and_return(true)
Stripe.api_key = "sk_test_12345"
stub_request(:get,
"https://api.stripe.com/v1/accounts/acc_connected123").to_return(body: JSON.generate(stripe_account_mock))

View File

@@ -18,19 +18,13 @@ describe "Credit Cards", js: true do
create(:stored_credit_card, user_id: user.id, gateway_customer_profile_id: 'cus_FDTG')
}
around do |example|
original_stripe_connect_enabled = Spree::Config[:stripe_connect_enabled]
example.run
Spree::Config.set(stripe_connect_enabled: original_stripe_connect_enabled)
end
before do
login_as user
allow(Stripe).to receive(:api_key).and_return("sk_test_12345")
allow(Stripe.config).to receive(:api_key).and_return("sk_test_12345")
allow(Stripe).to receive(:publishable_key).and_return("some_token")
Spree::Config.set(stripe_connect_enabled: true)
allow(Spree::Config).to receive(:stripe_connect_enabled).and_return(true)
stub_request(:get, "https://api.stripe.com/v1/customers/cus_AZNMJ").
to_return(status: 200, body: JSON.generate(id: "cus_AZNMJ"))