mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-09 03:20:21 +00:00
Let people choose which payment methods are available to customers on order cycles
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
37
app/services/order_available_payment_methods.rb
Normal file
37
app/services/order_available_payment_methods.rb
Normal 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
|
||||
@@ -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 &
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user