Let people choose which payment methods are available to customers on order cycles

This commit is contained in:
Cillian O'Ruanaidh
2022-10-07 17:14:11 +01:00
parent 5718f9f00c
commit 4e6d64c0a1
18 changed files with 789 additions and 340 deletions

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

@@ -399,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
@@ -177,6 +178,9 @@ class OrderCycle < ApplicationRecord
oc.schedule_ids = schedule_ids
oc.save!
exchanges.each { |e| e.clone!(oc) }
oc.selected_distributor_payment_method_ids = (
attachable_distributor_payment_methods.map(&:id) & selected_distributor_payment_method_ids
)
oc.selected_distributor_shipping_method_ids = (
attachable_distributor_shipping_methods.map(&:id) & selected_distributor_shipping_method_ids
)
@@ -293,6 +297,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

@@ -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

@@ -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

@@ -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