mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-28 06:15:17 +00:00
Compare commits
2 Commits
dependabot
...
v5.4.7.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7dfe62a1c7 | ||
|
|
d264fa75e5 |
32
Gemfile.lock
32
Gemfile.lock
@@ -167,7 +167,7 @@ GEM
|
||||
zeitwerk (>= 2.4, < 3.0)
|
||||
acts_as_list (1.0.4)
|
||||
activerecord (>= 4.2)
|
||||
addressable (2.8.9)
|
||||
addressable (2.8.8)
|
||||
public_suffix (>= 2.0.2, < 8.0)
|
||||
aes_key_wrap (1.1.0)
|
||||
afm (1.0.0)
|
||||
@@ -212,7 +212,7 @@ GEM
|
||||
bindex (0.8.1)
|
||||
bootsnap (1.22.0)
|
||||
msgpack (~> 1.2)
|
||||
bugsnag (6.29.0)
|
||||
bugsnag (6.28.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
builder (3.3.0)
|
||||
bullet (8.1.0)
|
||||
@@ -385,11 +385,11 @@ GEM
|
||||
good_migrations (0.3.1)
|
||||
activerecord (>= 3.1)
|
||||
railties (>= 3.1)
|
||||
haml (7.2.0)
|
||||
haml (6.3.0)
|
||||
temple (>= 0.8.2)
|
||||
thor
|
||||
tilt
|
||||
haml_lint (0.72.0)
|
||||
haml_lint (0.68.0)
|
||||
haml (>= 5.0)
|
||||
parallel (~> 1.10)
|
||||
rainbow
|
||||
@@ -402,19 +402,18 @@ GEM
|
||||
highline (3.1.2)
|
||||
reline
|
||||
htmlentities (4.4.2)
|
||||
http_parser.rb (0.8.1)
|
||||
http_parser.rb (0.8.0)
|
||||
i18n (1.14.8)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-js (3.9.2)
|
||||
i18n (>= 0.6.6)
|
||||
i18n-tasks (1.1.2)
|
||||
i18n-tasks (1.0.15)
|
||||
activesupport (>= 4.0.2)
|
||||
ast (>= 2.1.0)
|
||||
erubi
|
||||
highline (>= 3.0.0)
|
||||
highline (>= 2.0.0)
|
||||
i18n
|
||||
parser (>= 3.2.2.1)
|
||||
prism
|
||||
rails-i18n
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
ruby-progressbar (~> 1.8, >= 1.8.1)
|
||||
@@ -515,7 +514,7 @@ GEM
|
||||
money (6.16.0)
|
||||
i18n (>= 0.6.4, <= 2)
|
||||
msgpack (1.8.0)
|
||||
multi_json (1.19.1)
|
||||
multi_json (1.17.0)
|
||||
multi_xml (0.6.0)
|
||||
mutex_m (0.3.0)
|
||||
net-http (0.9.1)
|
||||
@@ -591,12 +590,12 @@ GEM
|
||||
hashery (~> 2.0)
|
||||
ruby-rc4
|
||||
ttfunk
|
||||
pg (1.6.3)
|
||||
pg (1.6.2)
|
||||
pp (0.6.3)
|
||||
prettyprint
|
||||
prettyprint (0.2.0)
|
||||
prism (1.9.0)
|
||||
private_address_check (0.6.0)
|
||||
private_address_check (0.5.0)
|
||||
pry (0.16.0)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
@@ -604,10 +603,9 @@ GEM
|
||||
psych (5.3.1)
|
||||
date
|
||||
stringio
|
||||
public_suffix (7.0.5)
|
||||
puffing-billy (4.0.4)
|
||||
public_suffix (7.0.2)
|
||||
puffing-billy (4.0.2)
|
||||
addressable (~> 2.5)
|
||||
cgi
|
||||
em-http-request (~> 1.1, >= 1.1.0)
|
||||
em-synchrony
|
||||
eventmachine (~> 1.2)
|
||||
@@ -898,7 +896,7 @@ GEM
|
||||
faraday (~> 2.0)
|
||||
faraday-follow_redirects
|
||||
sysexits (1.2.0)
|
||||
taler (0.3.0)
|
||||
taler (0.2.0)
|
||||
temple (0.10.4)
|
||||
terminal-table (4.0.0)
|
||||
unicode-display_width (>= 1.1.1, < 4)
|
||||
@@ -916,7 +914,7 @@ GEM
|
||||
turbo-rails (>= 1.3.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
undercover (0.8.4)
|
||||
undercover (0.8.3)
|
||||
base64
|
||||
bigdecimal
|
||||
imagen (>= 0.2.0)
|
||||
@@ -959,7 +957,7 @@ GEM
|
||||
activesupport
|
||||
faraday (~> 2.0)
|
||||
faraday-follow_redirects
|
||||
webmock (3.26.2)
|
||||
webmock (3.26.1)
|
||||
addressable (>= 2.8.0)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
|
||||
@@ -3,15 +3,7 @@ angular.module('admin.orderCycles').controller 'AdminOrderCycleIncomingCtrl', ($
|
||||
|
||||
$scope.view = 'incoming'
|
||||
# NB: weirdly at this next line $scope.order_cycle.id comes out undefined so we use $scope.order_cycle_id instead
|
||||
$scope.enterprise_fees = null
|
||||
$scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: $scope.order_cycle_id, per_item: true) unless EnterpriseFee.loading
|
||||
|
||||
# We want to make sure to load the filtered EnterpriseFee when any previous request is finished
|
||||
# otherwise the enterprise_fees migh get overriden by non filtered ones.
|
||||
$scope.$watch(( -> EnterpriseFee.loading), (isLoading) =>
|
||||
$scope.enterprise_fees ||= EnterpriseFee.index(order_cycle_id: $scope.order_cycle_id, per_item: true) unless isLoading
|
||||
)
|
||||
|
||||
$scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: $scope.order_cycle_id, per_item: true)
|
||||
$scope.exchangeTotalVariants = (exchange) ->
|
||||
return unless $scope.enterprises? && $scope.enterprises[exchange.enterprise_id]?
|
||||
|
||||
|
||||
@@ -14,14 +14,10 @@ angular.module('admin.orderCycles').factory('EnterpriseFee', ($resource) ->
|
||||
EnterpriseFee: EnterpriseFee
|
||||
enterprise_fees: {}
|
||||
loaded: false
|
||||
loading: false
|
||||
|
||||
index: (params={}) ->
|
||||
return if @loading == true
|
||||
@loading = true
|
||||
EnterpriseFee.index params, (data) =>
|
||||
@enterprise_fees = data
|
||||
@loading = false
|
||||
@loaded = true
|
||||
|
||||
forEnterprise: (enterprise_id) ->
|
||||
|
||||
@@ -6,7 +6,6 @@ angular.module("ofn.admin").factory 'EnterpriseRelationships', ($http, enterpris
|
||||
'manage_products'
|
||||
'edit_profile'
|
||||
'create_variant_overrides'
|
||||
'create_linked_variants'
|
||||
]
|
||||
|
||||
constructor: ->
|
||||
@@ -31,4 +30,3 @@ angular.module("ofn.admin").factory 'EnterpriseRelationships', ($http, enterpris
|
||||
when "manage_products" then t('js.services.manage_products')
|
||||
when "edit_profile" then t('js.services.edit_profile')
|
||||
when "create_variant_overrides" then t('js.services.add_products_to_inventory')
|
||||
when "create_linked_variants" then t('js.services.create_linked_variants')
|
||||
|
||||
@@ -107,33 +107,6 @@ module Admin
|
||||
end
|
||||
end
|
||||
|
||||
# Clone a variant, retaining a link to the "source"
|
||||
def create_linked_variant
|
||||
linked_variant = Spree::Variant.find(params[:variant_id])
|
||||
product_index = params[:product_index]
|
||||
authorize! :create_linked_variant, linked_variant
|
||||
status = :ok
|
||||
|
||||
begin
|
||||
variant = linked_variant.create_linked_variant(spree_current_user)
|
||||
|
||||
flash.now[:success] = t('.success')
|
||||
variant_index = "-#{variant.id}"
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
flash.now[:error] = variant.errors.full_messages.to_sentence
|
||||
status = :unprocessable_entity
|
||||
variant_index = "-1" # Create a unique-enough index
|
||||
end
|
||||
|
||||
respond_with do |format|
|
||||
format.turbo_stream {
|
||||
locals = { linked_variant:, variant:, product_index:, variant_index:,
|
||||
producer_options:, category_options: categories, tax_category_options: }
|
||||
render :create_linked_variant, status:, locals:
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def index_url(params)
|
||||
"/admin/products?#{params.to_query}" # todo: fix routing so this can be automaticly generated
|
||||
end
|
||||
|
||||
@@ -52,7 +52,7 @@ module Spree
|
||||
# (we can't use respond_override because Spree no longer uses respond_with)
|
||||
def fire
|
||||
event = params[:e]
|
||||
return unless event
|
||||
return unless event && @payment.payment_source
|
||||
|
||||
# capture_and_complete_order will complete the order, so we want to try to redeem VINE
|
||||
# voucher first and exit if it fails
|
||||
|
||||
@@ -47,10 +47,5 @@ module Admin
|
||||
def variant_tag_enabled?(user)
|
||||
feature?(:variant_tag, user) || feature?(:variant_tag, *user.enterprises)
|
||||
end
|
||||
|
||||
def allowed_source_producers
|
||||
@allowed_source_producers ||= OpenFoodNetwork::Permissions.new(spree_current_user)
|
||||
.enterprises_granting_linked_variants
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,10 +22,10 @@ class PaymentMailer < ApplicationMailer
|
||||
end
|
||||
end
|
||||
|
||||
def refund_available(amount, payment, taler_order_status_url)
|
||||
def refund_available(payment, taler_order_status_url)
|
||||
@order = payment.order
|
||||
@shop = @order.distributor.name
|
||||
@amount = amount
|
||||
@amount = payment.display_amount
|
||||
@taler_order_status_url = taler_order_status_url
|
||||
|
||||
I18n.with_locale valid_locale(@order.user) do
|
||||
|
||||
@@ -197,16 +197,12 @@ module Spree
|
||||
can [:admin, :index, :destroy], :oidc_setting
|
||||
|
||||
can [:admin, :create], Voucher
|
||||
|
||||
can [:admin, :destroy], EnterpriseRole do |enterprise_role|
|
||||
enterprise_role.enterprise.owner_id == user.id
|
||||
end
|
||||
end
|
||||
|
||||
def add_product_management_abilities(user)
|
||||
# Enterprise User can only access products that they are a supplier for
|
||||
can [:create], Spree::Product
|
||||
# An enterprise user can change a product if they are supplier of at least
|
||||
# An enterperprise user can change a product if they are supplier of at least
|
||||
# one of the product's associated variants
|
||||
can [:admin, :read, :index, :update,
|
||||
:seo, :group_buy_options,
|
||||
@@ -218,16 +214,7 @@ module Spree
|
||||
)
|
||||
end
|
||||
|
||||
# An enterprise user can clone if they have been granted permission to the source variant.
|
||||
# Technically I'd call this permission clone_linked_variant, but it would be less confusing to
|
||||
# use the same name as everywhere else.
|
||||
can [:create_linked_variant], Spree::Variant do |variant|
|
||||
OpenFoodNetwork::Permissions.new(user).
|
||||
enterprises_granting_linked_variants.include? variant.supplier
|
||||
end
|
||||
|
||||
can [:admin, :index, :bulk_update, :destroy, :destroy_variant, :clone,
|
||||
:create_linked_variant], :products_v3
|
||||
can [:admin, :index, :bulk_update, :destroy, :destroy_variant, :clone], :products_v3
|
||||
|
||||
can [:create], Spree::Variant
|
||||
can [:admin, :index, :read, :edit,
|
||||
|
||||
@@ -63,6 +63,35 @@ module Spree
|
||||
"XXXX-XXXX-XXXX-#{last_digits}"
|
||||
end
|
||||
|
||||
def actions
|
||||
%w{capture_and_complete_order void credit resend_authorization_email}
|
||||
end
|
||||
|
||||
def can_resend_authorization_email?(payment)
|
||||
payment.requires_authorization?
|
||||
end
|
||||
|
||||
# Indicates whether its possible to capture the payment
|
||||
def can_capture_and_complete_order?(payment)
|
||||
return false if payment.requires_authorization?
|
||||
|
||||
payment.pending? || payment.checkout?
|
||||
end
|
||||
|
||||
# Indicates whether its possible to void the payment.
|
||||
def can_void?(payment)
|
||||
!payment.void?
|
||||
end
|
||||
|
||||
# Indicates whether its possible to credit the payment. Note that most gateways require that the
|
||||
# payment be settled first which generally happens within 12-24 hours of the transaction.
|
||||
def can_credit?(payment)
|
||||
return false unless payment.completed?
|
||||
return false unless payment.order.payment_state == 'credit_owed'
|
||||
|
||||
payment.credit_allowed.positive?
|
||||
end
|
||||
|
||||
# Allows us to use a gateway_payment_profile_id to store Stripe Tokens
|
||||
def has_payment_profile?
|
||||
gateway_customer_profile_id.present? || gateway_payment_profile_id.present?
|
||||
|
||||
@@ -13,35 +13,6 @@ module Spree
|
||||
preference :server, :string, default: 'live'
|
||||
preference :test_mode, :boolean, default: false
|
||||
|
||||
def actions
|
||||
%w{capture_and_complete_order void credit resend_authorization_email}
|
||||
end
|
||||
|
||||
# Indicates whether its possible to capture the payment
|
||||
def can_capture_and_complete_order?(payment)
|
||||
return false if payment.requires_authorization?
|
||||
|
||||
payment.pending? || payment.checkout?
|
||||
end
|
||||
|
||||
# Indicates whether its possible to void the payment.
|
||||
def can_void?(payment)
|
||||
!payment.void?
|
||||
end
|
||||
|
||||
# Indicates whether its possible to credit the payment. Note that most gateways require that the
|
||||
# payment be settled first which generally happens within 12-24 hours of the transaction.
|
||||
def can_credit?(payment)
|
||||
return false unless payment.completed?
|
||||
return false unless payment.order.payment_state == 'credit_owed'
|
||||
|
||||
payment.credit_allowed.positive?
|
||||
end
|
||||
|
||||
def can_resend_authorization_email?(payment)
|
||||
payment.requires_authorization?
|
||||
end
|
||||
|
||||
def payment_source_class
|
||||
CreditCard
|
||||
end
|
||||
|
||||
@@ -152,10 +152,11 @@ module Spree
|
||||
end
|
||||
|
||||
def actions
|
||||
return [] unless payment_method.respond_to?(:actions)
|
||||
return [] unless payment_source.respond_to?(:actions)
|
||||
|
||||
payment_method.actions.select do |action|
|
||||
payment_method.__send__("can_#{action}?", self)
|
||||
payment_source.actions.select do |action|
|
||||
!payment_source.respond_to?("can_#{action}?") ||
|
||||
payment_source.__send__("can_#{action}?", self)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -165,6 +166,11 @@ module Spree
|
||||
PaymentMailer.authorize_payment(self).deliver_later
|
||||
end
|
||||
|
||||
def payment_source
|
||||
res = source.is_a?(Payment) ? source.source : source
|
||||
res || payment_method
|
||||
end
|
||||
|
||||
def ensure_correct_adjustment
|
||||
revoke_adjustment_eligibility if ['failed', 'invalid', 'void'].include?(state)
|
||||
return if adjustment.try(:finalized?)
|
||||
|
||||
@@ -18,27 +18,15 @@ module Spree
|
||||
# - backend_url: https://backend.demo.taler.net/instances/sandbox
|
||||
# - api_key: sandbox
|
||||
class Taler < PaymentMethod
|
||||
# Demo backend instances will use the KUDOS currency.
|
||||
DEMO_PREFIX = "https://backend.demo.taler.net/instances"
|
||||
|
||||
preference :backend_url, :string
|
||||
preference :api_key, :password
|
||||
|
||||
def actions
|
||||
%w[credit void]
|
||||
%w{void}
|
||||
end
|
||||
|
||||
def can_void?(payment)
|
||||
# The source can be another payment. Then this is an offset payment
|
||||
# like a credit record. We can't void a refund.
|
||||
payment.source == self && payment.state == "completed"
|
||||
end
|
||||
|
||||
def can_credit?(payment)
|
||||
return false unless payment.completed?
|
||||
return false unless payment.order.payment_state == 'credit_owed'
|
||||
|
||||
payment.credit_allowed.positive?
|
||||
payment.state == "completed"
|
||||
end
|
||||
|
||||
# Name of the view to display during checkout
|
||||
@@ -80,23 +68,6 @@ module Spree
|
||||
ActiveMerchant::Billing::Response.new(success, message)
|
||||
end
|
||||
|
||||
def credit(money, response_code, gateway_options)
|
||||
amount = money / 100 # called with cents
|
||||
payment = gateway_options[:payment]
|
||||
taler_order = taler_order(id: response_code)
|
||||
status = taler_order.fetch("order_status")
|
||||
|
||||
raise "Unsupported action" if status != "paid"
|
||||
|
||||
taler_amount = "KUDOS:#{amount}"
|
||||
taler_order.refund(refund: taler_amount, reason: "credit")
|
||||
|
||||
spree_money = Spree::Money.new(amount, currency: payment.currency).to_s
|
||||
PaymentMailer.refund_available(spree_money, payment, taler_order.status_url).deliver_later
|
||||
|
||||
ActiveMerchant::Billing::Response.new(true, "Refund initiated")
|
||||
end
|
||||
|
||||
def void(response_code, gateway_options)
|
||||
payment = gateway_options[:payment]
|
||||
taler_order = taler_order(id: response_code)
|
||||
@@ -111,8 +82,7 @@ module Spree
|
||||
amount = taler_order.fetch("contract_terms")["amount"]
|
||||
taler_order.refund(refund: amount, reason: "void")
|
||||
|
||||
spree_money = payment.money.to_s
|
||||
PaymentMailer.refund_available(spree_money, payment, taler_order.status_url).deliver_later
|
||||
PaymentMailer.refund_available(payment, taler_order.status_url).deliver_later
|
||||
|
||||
ActiveMerchant::Billing::Response.new(true, "Refund initiated")
|
||||
end
|
||||
@@ -126,7 +96,7 @@ module Spree
|
||||
def create_taler_order(payment)
|
||||
# We are ignoring currency for now so that we can test with the
|
||||
# current demo backend only working with the KUDOS currency.
|
||||
taler_amount = "#{currency(payment)}:#{payment.amount}"
|
||||
taler_amount = "KUDOS:#{payment.amount}"
|
||||
urls = Rails.application.routes.url_helpers
|
||||
fulfillment_url = urls.payment_gateways_confirm_taler_url(payment_id: payment.id)
|
||||
taler_order.create(
|
||||
@@ -143,12 +113,6 @@ module Spree
|
||||
id:,
|
||||
)
|
||||
end
|
||||
|
||||
def currency(payment)
|
||||
return "KUDOS" if preferred_backend_url.starts_with?(DEMO_PREFIX)
|
||||
|
||||
payment.order.currency
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,7 +40,6 @@ module Spree
|
||||
belongs_to :shipping_category, class_name: 'Spree::ShippingCategory', optional: false
|
||||
belongs_to :primary_taxon, class_name: 'Spree::Taxon', touch: true, optional: false
|
||||
belongs_to :supplier, class_name: 'Enterprise', optional: false, touch: true
|
||||
belongs_to :hub, class_name: 'Enterprise', optional: true
|
||||
|
||||
delegate :name, :name=, :description, :description=, :meta_keywords, to: :product
|
||||
|
||||
@@ -73,15 +72,6 @@ module Spree
|
||||
has_many :semantic_links, as: :subject, dependent: :delete_all
|
||||
has_many :supplier_properties, through: :supplier, source: :properties
|
||||
|
||||
# Linked variants: I may have one or many sources.
|
||||
has_many :variant_links_as_target, class_name: 'VariantLink', foreign_key: :target_variant_id,
|
||||
dependent: :delete_all, inverse_of: :target_variant
|
||||
has_many :source_variants, through: :variant_links_as_target, source: :source_variant
|
||||
# I may also have one more many targets.
|
||||
has_many :variant_links_as_source, class_name: 'VariantLink', foreign_key: :source_variant_id,
|
||||
dependent: :delete_all, inverse_of: :source_variant
|
||||
has_many :target_variants, through: :variant_links_as_source, source: :target_variant
|
||||
|
||||
localize_number :price, :weight
|
||||
|
||||
validates_lengths_from_database
|
||||
@@ -273,24 +263,6 @@ module Spree
|
||||
@on_hand_desired = ActiveModel::Type::Integer.new.cast(val)
|
||||
end
|
||||
|
||||
# Clone this variant, retaining a 'source' link to it
|
||||
def create_linked_variant(user)
|
||||
# Hub owner is my enterprise which has permission to create variant sourced from that supplier
|
||||
hub_id = EnterpriseRelationship.permitted_by(supplier).permitting(user.enterprises)
|
||||
.with_permission(:create_linked_variants)
|
||||
.pick(:child_id)
|
||||
|
||||
dup.tap do |variant|
|
||||
variant.price = price
|
||||
variant.source_variants = [self]
|
||||
variant.stock_items << Spree::StockItem.new(variant:)
|
||||
variant.hub_id = hub_id
|
||||
variant.on_demand = on_demand
|
||||
variant.on_hand = on_hand
|
||||
variant.save!
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_currency
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class VariantLink < ApplicationRecord
|
||||
belongs_to :source_variant, class_name: 'Spree::Variant'
|
||||
belongs_to :target_variant, class_name: 'Spree::Variant'
|
||||
end
|
||||
@@ -14,6 +14,7 @@ module Checkout
|
||||
apply_strong_parameters
|
||||
set_pickup_address
|
||||
set_address_details
|
||||
set_payment_amount
|
||||
set_existing_card
|
||||
|
||||
@order_params
|
||||
@@ -57,6 +58,12 @@ module Checkout
|
||||
end
|
||||
end
|
||||
|
||||
def set_payment_amount
|
||||
return unless @order_params[:payments_attributes]
|
||||
|
||||
@order_params[:payments_attributes].first[:amount] = order.outstanding_balance.amount
|
||||
end
|
||||
|
||||
def set_existing_card
|
||||
return unless existing_card_selected?
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ module Payments
|
||||
payment: @payment.slice(:updated_at, :amount, :state),
|
||||
enterprise: @enterprise.slice(:abn, :acn, :name)
|
||||
.merge(address: @enterprise.address.slice(:address1, :address2, :city, :zipcode)),
|
||||
order: @order.slice(:number, :total, :currency).merge(line_items: line_items)
|
||||
order: @order.slice(:total, :currency).merge(line_items: line_items)
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
@@ -31,7 +31,6 @@ module Payments
|
||||
|
||||
def self.test_order
|
||||
order = Spree::Order.new(
|
||||
number: "R555555555",
|
||||
total: 0.00,
|
||||
currency: "AUD",
|
||||
)
|
||||
|
||||
@@ -11,10 +11,9 @@
|
||||
|
||||
-# Filter out variant a user has not permission to update, but keep variant with no supplier
|
||||
- next if variant.supplier.present? && !allowed_producers.include?(variant.supplier)
|
||||
|
||||
= form.fields_for("products][#{product_index}][variants_attributes", variant, index: variant_index) do |variant_form|
|
||||
%tr.condensed{ id: dom_id(variant), 'data-controller': "variant", 'class': "nested-form-wrapper", 'data-new-record': variant.new_record? ? "true" : false }
|
||||
= render partial: 'variant_row', locals: { variant:, f: variant_form, product_index:, category_options:, tax_category_options:, producer_options: }
|
||||
= render partial: 'variant_row', locals: { variant:, f: variant_form, category_options:, tax_category_options:, producer_options: }
|
||||
|
||||
= form.fields_for("products][#{product_index}][variants_attributes][NEW_RECORD", prepare_new_variant(product, producer_options)) do |new_variant_form|
|
||||
%template{ 'data-nested-form-target': "template" }
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
-# haml-lint:disable ViewLength (This file is big, but doesn't make sense to split up at this point)
|
||||
-# locals: (variant:, f:, product_index: nil, category_options:, tax_category_options:, producer_options:)
|
||||
-# locals: (variant:, f:, category_options:, tax_category_options:, producer_options:)
|
||||
- method_on_demand, method_on_hand = variant.new_record? ? [:on_demand_desired, :on_hand_desired ]: [:on_demand, :on_hand]
|
||||
%td.col-image
|
||||
-# empty
|
||||
- variant.source_variants.each do |source_variant|
|
||||
= content_tag(:span, "🔗", title: t('admin.products_page.variant_row.sourced_from', source_name: source_variant.display_name, source_id: source_variant.id, hub_name: variant.hub&.name))
|
||||
%td.col-name.field.naked_inputs
|
||||
= f.hidden_field :id
|
||||
= f.text_field :display_name, 'aria-label': t('admin.products_page.columns.name'), placeholder: variant.product.name
|
||||
@@ -91,10 +88,6 @@
|
||||
= render(VerticalEllipsisMenuComponent.new) do
|
||||
- if variant.persisted?
|
||||
= link_to t('admin.products_page.actions.edit'), edit_admin_product_variant_path(variant.product, variant)
|
||||
|
||||
- if allowed_source_producers.include?(variant.supplier)
|
||||
= link_to t('admin.products_page.actions.create_linked_variant'), admin_create_linked_variant_path(variant_id: variant.id, product_index:), 'data-turbo-method': :post
|
||||
|
||||
- if variant.product.variants.size > 1
|
||||
%a{ "data-controller": "modal-link", "data-action": "click->modal-link#setModalDataSetOnConfirm click->modal-link#open",
|
||||
"data-modal-link-target-value": "variant-delete-modal", "class": "delete",
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
-# locals: (variant:, linked_variant:, product_index:, variant_index:, producer_options:, category_options:, tax_category_options:)
|
||||
-# Pre-render the form, because you can't do it inside turbo stream block
|
||||
- variant_row = nil
|
||||
- fields_for("products][#{product_index}][variants_attributes", variant, index: variant_index) do |f|
|
||||
- variant_row = render(partial: 'variant_row', formats: :html,
|
||||
locals: { f:,
|
||||
variant:,
|
||||
producer_options:,
|
||||
category_options:,
|
||||
tax_category_options:})
|
||||
= turbo_stream.after dom_id(linked_variant) do
|
||||
%tr.condensed{ id: dom_id(variant), 'data-controller': "variant", 'class': "nested-form-wrapper slide-in",'data-variant-bulk-form-outlet': "#products-form"}
|
||||
= variant_row
|
||||
|
||||
= turbo_stream.append "flashes" do
|
||||
= render(partial: 'admin/shared/flashes', locals: { flashes: flash })
|
||||
@@ -6,7 +6,7 @@
|
||||
%p= t ".description"
|
||||
|
||||
%fieldset.no-border-top.no-border-bottom
|
||||
.row.field
|
||||
.row
|
||||
= f.label :email, t(:email)
|
||||
= f.email_field :email, placeholder: t('.eg_email_address'), data: { controller: "select-user" }, inputmode: "email", autocomplete: "off"
|
||||
= f.error_message_on :email
|
||||
|
||||
@@ -44,17 +44,12 @@ export default class BulkFormController extends Controller {
|
||||
}
|
||||
|
||||
// Register any new elements (may be called by another controller after dynamically adding fields)
|
||||
// May be called with array of elements to register, otherwise finds all un-registered elements.
|
||||
registerElements(eventOrElements = null) {
|
||||
let newElements;
|
||||
|
||||
if (Array.isArray(eventOrElements)) {
|
||||
newElements = eventOrElements;
|
||||
} else {
|
||||
const registeredElements = Object.values(this.recordElements).flat();
|
||||
// Select only elements that haven't been registered yet
|
||||
newElements = Array.from(this.form.elements).filter((n) => !registeredElements.includes(n));
|
||||
}
|
||||
registerElements() {
|
||||
const registeredElements = Object.values(this.recordElements).flat();
|
||||
// Select only elements that haven't been registered yet
|
||||
const newElements = Array.from(this.form.elements).filter(
|
||||
(n) => !registeredElements.includes(n),
|
||||
);
|
||||
|
||||
this.#registerElements(newElements);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import OptionValueNamer from "js/services/option_value_namer";
|
||||
// Dynamically update related variant fields
|
||||
//
|
||||
export default class VariantController extends Controller {
|
||||
static outlets = ["bulk-form"];
|
||||
|
||||
connect() {
|
||||
// idea: create a helper that includes a nice getter/setter for Rails model attr values, just pass it the attribute name.
|
||||
// It could automatically find (and cache a ref to) each dom element and get/set the values.
|
||||
@@ -42,12 +40,6 @@ export default class VariantController extends Controller {
|
||||
// on display_as changed; update unit_to_display
|
||||
// TODO: optimise to avoid unnecessary OptionValueNamer calc
|
||||
this.displayAs.addEventListener("input", this.#updateUnitDisplay.bind(this), { passive: true });
|
||||
|
||||
// Register with bulk products form to listen for changes. Used when dynamically appending variants.
|
||||
if (this.hasBulkFormOutlet) {
|
||||
const formElements = this.element.querySelectorAll("input, select, textarea, button");
|
||||
this.bulkFormOutlet.registerElements(formElements);
|
||||
}
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
.button, button {
|
||||
@include border-radius(0.5em);
|
||||
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
|
||||
&.x-small {
|
||||
@@ -66,6 +65,7 @@
|
||||
}
|
||||
|
||||
.button.primary, button.primary {
|
||||
font-family: $body-font;
|
||||
background: $orange-450;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@@ -717,11 +717,8 @@ en:
|
||||
delete: Delete
|
||||
remove: Remove
|
||||
preview: Preview
|
||||
create_linked_variant: Create linked variant
|
||||
image:
|
||||
edit: Edit
|
||||
variant_row:
|
||||
sourced_from: "Sourced from: %{source_name} (%{source_id}); Hub: %{hub_name}"
|
||||
product_preview:
|
||||
product_preview: Product preview
|
||||
shop_tab: Shop
|
||||
@@ -1101,8 +1098,6 @@ en:
|
||||
clone:
|
||||
success: Successfully cloned the product
|
||||
error: Unable to clone the product
|
||||
create_linked_variant:
|
||||
success: "Successfully created linked variant"
|
||||
tag_rules:
|
||||
rules_per_tag:
|
||||
one: "%{tag} has 1 rule"
|
||||
@@ -3906,7 +3901,6 @@ en:
|
||||
manage_products: "manage products"
|
||||
edit_profile: "edit profile"
|
||||
add_products_to_inventory: "add products to inventory"
|
||||
create_linked_variants: "create linked variants [BETA]"
|
||||
resources:
|
||||
could_not_delete_customer: 'Could not delete customer'
|
||||
product_import:
|
||||
|
||||
@@ -287,7 +287,7 @@ en_CA:
|
||||
customer_instructions: "Customer instructions"
|
||||
additional_information: "Additional information"
|
||||
connect_app:
|
||||
url: "https://n8n.openfoodnetwork.org/webhook-test/foodjustice/connect-enterprise"
|
||||
url: "https://n8n.openfoodnetwork.org/webhook/foodjustice/connect-enterprise"
|
||||
devise:
|
||||
passwords:
|
||||
spree_user:
|
||||
@@ -668,11 +668,8 @@ en_CA:
|
||||
delete: Delete
|
||||
remove: Remove
|
||||
preview: Preview
|
||||
create_linked_variant: Create linked variant
|
||||
image:
|
||||
edit: Edit
|
||||
variant_row:
|
||||
sourced_from: "Sourced from: %{source_name} (%{source_id}); Hub: %{hub_name}"
|
||||
product_preview:
|
||||
product_preview: Product preview
|
||||
shop_tab: Shop
|
||||
@@ -814,7 +811,6 @@ en_CA:
|
||||
bill_address: "Billing Address"
|
||||
ship_address: "Shipping Address"
|
||||
balance: "Balance"
|
||||
credit: "Available Credit"
|
||||
update_address_success: "Address updated successfully."
|
||||
update_address_error: "Sorry! Please input all of the required fields!"
|
||||
edit_bill_address: "Edit Billing Address"
|
||||
@@ -829,16 +825,12 @@ en_CA:
|
||||
guest_label: "Guest checkout"
|
||||
credit_owed: "Credit Owed"
|
||||
balance_due: "Balance Due"
|
||||
id: Id
|
||||
destroy:
|
||||
has_associated_subscriptions: "Delete failed: This customer has active subscriptions. Cancel them first."
|
||||
customer_account_transaction:
|
||||
index:
|
||||
available_credit: "Available credit: %{available_credit}"
|
||||
transaction_date: Transaction Date
|
||||
description: Description
|
||||
amount: Amount
|
||||
created_by: Created by
|
||||
running_balance: Running balance
|
||||
column_preferences:
|
||||
bulk_update:
|
||||
@@ -1028,8 +1020,6 @@ en_CA:
|
||||
clone:
|
||||
success: Successfully cloned the product
|
||||
error: Unable to clone the product
|
||||
create_linked_variant:
|
||||
success: "Successfully created linked variant"
|
||||
tag_rules:
|
||||
rules_per_tag:
|
||||
one: "%{tag} has 1 rule"
|
||||
@@ -1799,11 +1789,6 @@ en_CA:
|
||||
images: "Images"
|
||||
contact: "Contact"
|
||||
web: "Web Resources"
|
||||
stimulus_pagination:
|
||||
navigation: Pagination
|
||||
page: "Page %{number}"
|
||||
previous: Previous page
|
||||
next: Next page
|
||||
enterprise_issues:
|
||||
create_new: Create New
|
||||
resend_email: Resend Email
|
||||
@@ -2470,7 +2455,6 @@ en_CA:
|
||||
order_total: Total order
|
||||
order_payment: "Paying via:"
|
||||
no_payment_required: "No payment required"
|
||||
credit_used: "Credit used: %{amount}"
|
||||
customer_credit: Credit
|
||||
order_billing_address: Billing address
|
||||
order_delivery_on: Delivery on
|
||||
@@ -3455,7 +3439,6 @@ en_CA:
|
||||
no_orders_found: "No Orders Found"
|
||||
order_information: "Order Information"
|
||||
new_payment: "New Payment"
|
||||
credit_customer: Credit customer
|
||||
create_or_update_invoice: "Create or Update Invoice"
|
||||
date_completed: "Date Completed"
|
||||
amount: "Amount"
|
||||
@@ -3766,7 +3749,6 @@ en_CA:
|
||||
manage_products: "manage products"
|
||||
edit_profile: "edit profile"
|
||||
add_products_to_inventory: "add products to inventory"
|
||||
create_linked_variants: "create linked variants [BETA]"
|
||||
resources:
|
||||
could_not_delete_customer: 'Could not delete customer'
|
||||
product_import:
|
||||
@@ -4047,7 +4029,6 @@ en_CA:
|
||||
items_cannot_be_shipped: "Items cannot be shipped"
|
||||
gateway_config_unavailable: "Gateway config unavailable"
|
||||
gateway_error: "Payment failed"
|
||||
internal_payment_not_voidable: Payment not voidable
|
||||
more: "More"
|
||||
new_adjustment: "New adjustment"
|
||||
new_tax_category: "New Tax Category"
|
||||
@@ -4542,7 +4523,6 @@ en_CA:
|
||||
paypalexpress: "PayPal Express"
|
||||
stripesca: "Stripe SCA"
|
||||
taler: "Taler"
|
||||
customercredit: "Customer Credit"
|
||||
payments:
|
||||
source_forms:
|
||||
stripe:
|
||||
@@ -4550,7 +4530,6 @@ en_CA:
|
||||
submitting_payment: Submitting payment...
|
||||
paypal:
|
||||
no_payment_via_admin_backend: Paypal payments cannot be captured in the backoffice.
|
||||
customer_credit_successful: Customer has been successfully credited!
|
||||
products:
|
||||
image_upload_error: "Please upload the image in JPG, PNG, GIF, SVG or WEBP format."
|
||||
image_not_processable: "Image attachment is not a valid image."
|
||||
@@ -4866,7 +4845,6 @@ en_CA:
|
||||
orders: Orders
|
||||
cards: Credit Cards
|
||||
transactions: Transactions
|
||||
customer_account_transactions: Customer Transactions
|
||||
settings: Account Settings
|
||||
unconfirmed_email: "Pending email confirmation for: %{unconfirmed_email}. Your email address will be updated once the new email is confirmed."
|
||||
orders:
|
||||
@@ -4877,9 +4855,6 @@ en_CA:
|
||||
authorisation_required: Authorisation Required
|
||||
authorise: Authorize
|
||||
customer_account_transactions:
|
||||
title: Customer Transactions
|
||||
credit_available: "Credit available: %{credit}"
|
||||
transaction_date: Transaction Date
|
||||
description: Description
|
||||
amount: Amount
|
||||
running_balance: Running balance
|
||||
@@ -5022,22 +4997,3 @@ en_CA:
|
||||
invisible_captcha:
|
||||
sentence_for_humans: "Please leave empty"
|
||||
timestamp_error_message: "Please try again after 5 seconds."
|
||||
api_customer_credit: "API credit: %{description}"
|
||||
credit_payment_method:
|
||||
name: Customer credit
|
||||
description: Allow customer to pay with credit
|
||||
success: Payment with credit was sucessful
|
||||
void_success: Credit void was sucessful
|
||||
order_payment_description: "Customer credit: Payment for order: %{order_number}"
|
||||
order_void_description: "Customer credit: Refund for order: %{order_number}"
|
||||
errors:
|
||||
customer_not_found: Customer not found
|
||||
missing_payment: Missing payment
|
||||
credit_payment_method_missing: Credit payment method is missing
|
||||
no_credit_available: No credit available
|
||||
not_enough_credit_available: Not enough credit available
|
||||
orders:
|
||||
customer_credit_service:
|
||||
no_credit_owed: No credit owed
|
||||
credit_payment_method_missing: Customer credit payment method is missing, please check configuration
|
||||
refund_sucessful: Refund successful!
|
||||
|
||||
@@ -1432,7 +1432,7 @@ en_GB:
|
||||
legend: "Users"
|
||||
email_confirmation_notice_html: "Email confirmation is pending. We've sent a confirmation email to %{email}."
|
||||
resend: Resend
|
||||
contact: "Notifications"
|
||||
contact: "Contact"
|
||||
manager: "Manager"
|
||||
owner: 'Owner'
|
||||
contact_tip: "The manager who will receive enterprise emails for orders and notifications. Must have a confirmed email adress."
|
||||
|
||||
@@ -669,11 +669,8 @@ fr:
|
||||
delete: Supprimer
|
||||
remove: Supprimer
|
||||
preview: Prévisualisation
|
||||
create_linked_variant: Créer une variante liée
|
||||
image:
|
||||
edit: Modifier
|
||||
variant_row:
|
||||
sourced_from: "Source : %{source_name} ( %{source_id} ) ; Hub : %{hub_name}"
|
||||
product_preview:
|
||||
product_preview: Prévisualisation du produit
|
||||
shop_tab: Boutique
|
||||
@@ -1031,8 +1028,6 @@ fr:
|
||||
clone:
|
||||
success: Le produit a bien été dupliqué
|
||||
error: Impossible de dupliquer le produit
|
||||
create_linked_variant:
|
||||
success: "Variante liée créée avec succès"
|
||||
tag_rules:
|
||||
rules_per_tag:
|
||||
one: "%{tag} comporte une règle"
|
||||
@@ -3804,7 +3799,6 @@ fr:
|
||||
manage_products: "modifier les produits"
|
||||
edit_profile: "modifier le profil"
|
||||
add_products_to_inventory: "ajouter les produits au catalogue boutique"
|
||||
create_linked_variants: "créer des variantes liées [BÊTA]"
|
||||
resources:
|
||||
could_not_delete_customer: 'L''acheteur n''a pas pu être supprimé'
|
||||
product_import:
|
||||
|
||||
@@ -662,11 +662,8 @@ fr_BE:
|
||||
delete: Supprimer
|
||||
remove: Supprimer
|
||||
preview: Aperçu
|
||||
create_linked_variant: Créer une variante liée
|
||||
image:
|
||||
edit: Modifier
|
||||
variant_row:
|
||||
sourced_from: "Source : %{source_name} (%{source_id}); Hub : %{hub_name}"
|
||||
product_preview:
|
||||
product_preview: Aperçu du produit
|
||||
shop_tab: Comptoir
|
||||
@@ -1024,8 +1021,6 @@ fr_BE:
|
||||
clone:
|
||||
success: Le produit a bien été dupliqué
|
||||
error: Impossible de dupliquer le produit
|
||||
create_linked_variant:
|
||||
success: "Variante liée créée avec succès"
|
||||
tag_rules:
|
||||
rules_per_tag:
|
||||
one: "%{tag} comporte 1 règle"
|
||||
@@ -3779,7 +3774,6 @@ fr_BE:
|
||||
manage_products: "modifier les produits"
|
||||
edit_profile: "modifier le profil"
|
||||
add_products_to_inventory: "ajouter les produits au catalogue comptoir"
|
||||
create_linked_variants: "créer des variantes liées [BÊTA]"
|
||||
resources:
|
||||
could_not_delete_customer: 'L''acheteur·euse n''a pas pu être supprimé'
|
||||
product_import:
|
||||
|
||||
@@ -274,10 +274,6 @@ fr_CA:
|
||||
no_default_card: "Pas de carte de paiement par défaut pour cet acheteur"
|
||||
shipping_method:
|
||||
not_available_to_shop: "n'est pas disponible pour %{shop}"
|
||||
user_invitation:
|
||||
attributes:
|
||||
email:
|
||||
is_already_manager: est déjà gestionnaire!
|
||||
card_details: "Détalis de la carte"
|
||||
card_type: "Type de carte"
|
||||
card_type_is: "Type de carte"
|
||||
@@ -670,11 +666,8 @@ fr_CA:
|
||||
delete: Supprimer
|
||||
remove: Supprimer
|
||||
preview: Prévisualisation
|
||||
create_linked_variant: Créer une variante liée
|
||||
image:
|
||||
edit: Modifier
|
||||
variant_row:
|
||||
sourced_from: "Source : %{source_name} (%{source_id}); Hub : %{hub_name}"
|
||||
product_preview:
|
||||
product_preview: Prévisualisation du produit
|
||||
shop_tab: Boutique
|
||||
@@ -816,7 +809,6 @@ fr_CA:
|
||||
bill_address: "Adresse de facturation"
|
||||
ship_address: "Adresse de livraison"
|
||||
balance: "Solde"
|
||||
credit: "Crédit disponible"
|
||||
update_address_success: "Adresse mise à jour avec succès."
|
||||
update_address_error: "Oups! Veuillez remplir tous les champs obligatoires!"
|
||||
edit_bill_address: "Modifier l'adresse de facturation"
|
||||
@@ -831,16 +823,12 @@ fr_CA:
|
||||
guest_label: "Commande en mode invite"
|
||||
credit_owed: "Crédit dû"
|
||||
balance_due: "Solde dû"
|
||||
id: Id
|
||||
destroy:
|
||||
has_associated_subscriptions: "La suppression a planté : cet acheteur a des abonnements actifs. Veuillez d'abord les annuler."
|
||||
customer_account_transaction:
|
||||
index:
|
||||
available_credit: "Crédit disponible : %{available_credit}"
|
||||
transaction_date: Date de la transaction
|
||||
description: Description
|
||||
amount: Montant
|
||||
created_by: Créé par
|
||||
running_balance: Solde courant
|
||||
column_preferences:
|
||||
bulk_update:
|
||||
@@ -1032,8 +1020,6 @@ fr_CA:
|
||||
clone:
|
||||
success: Le produit a bien été dupliqué
|
||||
error: Impossible de dupliquer le produit
|
||||
create_linked_variant:
|
||||
success: "Variante liée créée avec succès"
|
||||
tag_rules:
|
||||
rules_per_tag:
|
||||
one: "%{tag} a 1 règle"
|
||||
@@ -1440,12 +1426,10 @@ fr_CA:
|
||||
show_hide_payment: 'Afficher ou Montrer les méthodes de paiement lors de la finalisation de commande'
|
||||
show_hide_order_cycles: 'Afficher ou Masquer les cycles de vente de ma boutique'
|
||||
users:
|
||||
description: Les utilisateurs autorisés à gérer cette entreprise.
|
||||
legend: "Utilisateurs"
|
||||
email_confirmation_notice_html: "L'email de confirmation n'a pas encore été validé. Il a été envoyé à %{email}."
|
||||
resend: Renvoyer
|
||||
contact: "Contact"
|
||||
manager: "Gestionnaire"
|
||||
owner: 'Manager principal'
|
||||
contact_tip: "Le manager qui recevra les emails de confirmation de commande et autres notifications de l'entreprise. Il doit avoir confirmé son adresse email pour pouvoir être sélectionné."
|
||||
owner_tip: Manager principal de cette entreprise.
|
||||
@@ -1456,8 +1440,6 @@ fr_CA:
|
||||
invite_manager: "Inviter un manager"
|
||||
email_confirmed: "Email confirmé"
|
||||
email_not_confirmed: "Email non confirmé"
|
||||
set_as_contact: "Configurer %{email} comme contact"
|
||||
set_as_owner: "Définir %{email} comme propriétaire"
|
||||
vouchers:
|
||||
legend: Bon de réduction
|
||||
voucher_code: Code promo
|
||||
@@ -1806,11 +1788,6 @@ fr_CA:
|
||||
images: "Images"
|
||||
contact: "Contact"
|
||||
web: "Liens web"
|
||||
stimulus_pagination:
|
||||
navigation: Pagination
|
||||
page: "Page %{number}"
|
||||
previous: Page précédente
|
||||
next: Page suivante
|
||||
enterprise_issues:
|
||||
create_new: Créer Nouveau
|
||||
resend_email: Renvoyer l'email
|
||||
@@ -2041,10 +2018,7 @@ fr_CA:
|
||||
user_invitations:
|
||||
new:
|
||||
back: Retour
|
||||
description: "Invitez un utilisateur à s'inscrire et à devenir gestionnaire de cette entreprise."
|
||||
eg_email_address: 'ex : l''adresse e-mail d''un utilisateur nouveau ou existant'
|
||||
email: Email
|
||||
invite_new_user: Inviter un nouvel utilisateur
|
||||
invite: Inviter
|
||||
vouchers:
|
||||
new:
|
||||
@@ -2480,7 +2454,6 @@ fr_CA:
|
||||
order_total: Total commande
|
||||
order_payment: "Payer via:"
|
||||
no_payment_required: "Pas de paiement requis"
|
||||
credit_used: "Crédit utilisé : %{amount}"
|
||||
customer_credit: Crédit
|
||||
order_billing_address: Adresse de facturation
|
||||
order_delivery_on: Livraison prévue
|
||||
@@ -3468,7 +3441,6 @@ fr_CA:
|
||||
no_orders_found: "Aucune commande trouvée"
|
||||
order_information: "Info commande"
|
||||
new_payment: "Nouveau paiement"
|
||||
credit_customer: Client à crédit
|
||||
create_or_update_invoice: "Créer ou mettre à jour la facture"
|
||||
date_completed: "Date d'opération"
|
||||
amount: "Montant"
|
||||
@@ -3790,7 +3762,6 @@ fr_CA:
|
||||
manage_products: "Gérer les produits"
|
||||
edit_profile: "modifier le profil"
|
||||
add_products_to_inventory: "ajouter les produits au catalogue boutique"
|
||||
create_linked_variants: "créer des variantes liées [BÊTA]"
|
||||
resources:
|
||||
could_not_delete_customer: 'L''acheteur n''a pas pu être supprimé'
|
||||
product_import:
|
||||
@@ -4105,7 +4076,6 @@ fr_CA:
|
||||
items_cannot_be_shipped: "Les produits ne peuvent pas être envoyés"
|
||||
gateway_config_unavailable: "Configuration de la passerelle indisponible"
|
||||
gateway_error: "Le paiement a échoué"
|
||||
internal_payment_not_voidable: Paiement non annulable
|
||||
more: "Plus"
|
||||
new_adjustment: "Nouvel ajustement"
|
||||
new_tax_category: "Nouvelle catégorie de taxe"
|
||||
@@ -4600,7 +4570,6 @@ fr_CA:
|
||||
paypalexpress: "PayPal Express"
|
||||
stripesca: "Stripe SCA"
|
||||
taler: "Taler"
|
||||
customercredit: "Crédit client"
|
||||
payments:
|
||||
source_forms:
|
||||
stripe:
|
||||
@@ -4608,7 +4577,6 @@ fr_CA:
|
||||
submitting_payment: Envoi du paiement...
|
||||
paypal:
|
||||
no_payment_via_admin_backend: 'Il n''est pas encore possible de payer avec Paypal via l''administration. '
|
||||
customer_credit_successful: Le client a été crédité avec succès!
|
||||
products:
|
||||
image_upload_error: "Veuillez utiliser une image au format JPG, PNG, GIF, SVG ou WEBP format."
|
||||
image_not_processable: "L'image n'est pas valide"
|
||||
@@ -4925,7 +4893,6 @@ fr_CA:
|
||||
orders: Commandes
|
||||
cards: Cartes bancaires
|
||||
transactions: Achats
|
||||
customer_account_transactions: Transactions des clients
|
||||
settings: Paramètres du Compte
|
||||
unconfirmed_email: "Attente de validation pour l'email: %{unconfirmed_email}. Votre adresse email sera mise à jour quand le nouvel email aura été confirmé."
|
||||
orders:
|
||||
@@ -4936,9 +4903,6 @@ fr_CA:
|
||||
authorisation_required: Autorisation nécessaire
|
||||
authorise: Autorise
|
||||
customer_account_transactions:
|
||||
title: Transactions des clients
|
||||
credit_available: "Crédit disponible : %{credit}"
|
||||
transaction_date: Date de la transaction
|
||||
description: Description
|
||||
amount: Montant
|
||||
running_balance: Solde courant
|
||||
@@ -5094,22 +5058,3 @@ fr_CA:
|
||||
invisible_captcha:
|
||||
sentence_for_humans: "Merci de laisser ce champ libre"
|
||||
timestamp_error_message: "S'il vous plaît réessayez après 5 secondes."
|
||||
api_customer_credit: "Crédit API : %{description}"
|
||||
credit_payment_method:
|
||||
name: Crédit client
|
||||
description: Autoriser le client à payer par crédit
|
||||
success: Le paiement par crédit a été effectué avec succès.
|
||||
void_success: L'annulation du crédit a réussi.
|
||||
order_payment_description: "Crédit client : Paiement de la commande :%{order_number}"
|
||||
order_void_description: "Crédit client : Remboursement pour la commande :%{order_number}"
|
||||
errors:
|
||||
customer_not_found: Client introuvable
|
||||
missing_payment: Paiement manquant
|
||||
credit_payment_method_missing: Le mode de paiement par crédit est manquant.
|
||||
no_credit_available: Aucune carte de paiement autorisée disponible
|
||||
not_enough_credit_available: Crédit disponible insuffisant
|
||||
orders:
|
||||
customer_credit_service:
|
||||
no_credit_owed: Aucun crédit dû
|
||||
credit_payment_method_missing: Le mode de paiement par crédit client est manquant. Veuillez vérifier la configuration.
|
||||
refund_sucessful: Remboursement effectué avec succès !
|
||||
|
||||
@@ -1298,10 +1298,10 @@ hu:
|
||||
add_new_button: '+ Új alapértelmezett szabály hozzáadása'
|
||||
no_tags_yet: Ehhez a vállalkozáshoz még nem tartozik címke
|
||||
add_new_tag: '+ Új címke hozzáadása'
|
||||
show_hide_variants: 'Termékváltozatok megjelenítése vagy elrejtése a kínálatomban'
|
||||
show_hide_variants: 'Változatok megjelenítése vagy elrejtése a kirakatomban'
|
||||
show_hide_shipping: 'Áruátadási módok megjelenítése vagy elrejtése fizetéskor'
|
||||
show_hide_payment: 'Fizetési módok megjelenítése vagy elrejtése rendelés leadásakor'
|
||||
show_hide_order_cycles: 'Rendelési ciklusok megjelenítése vagy elrejtése a kínálatomban'
|
||||
show_hide_payment: 'Fizetési módok megjelenítése vagy elrejtése a pénztárnál'
|
||||
show_hide_order_cycles: 'Rendelési ciklusok megjelenítése vagy elrejtése az online kirakatban'
|
||||
users:
|
||||
legend: "Felhasználók"
|
||||
email_confirmation_notice_html: "Az email megerősítés függőben van. Megerősítő emailt küldtünk a következő címre: %{email}."
|
||||
@@ -1878,7 +1878,6 @@ hu:
|
||||
new:
|
||||
back: Vissza
|
||||
description: "Hívj meg egy felhasználót, hogy regisztráljon és a vállalkozás menedzsere legyen."
|
||||
eg_email_address: írd be egy új vagy meglévő felhasználó e-mail címét
|
||||
email: Email
|
||||
invite_new_user: Új felhasználó meghívása
|
||||
invite: Meghívás
|
||||
@@ -4777,16 +4776,16 @@ hu:
|
||||
default_placeholder: Adj hozzá egy címkét
|
||||
tag_rule_form:
|
||||
tag_rules:
|
||||
shipping_method_tagged_top: "Ezen címkéjű áruátadási módok:"
|
||||
shipping_method_tagged_bottom: "hatása:"
|
||||
payment_method_tagged_top: "Ezen címkéjű fizetési módok:"
|
||||
payment_method_tagged_bottom: "hatása:"
|
||||
order_cycle_tagged_top: "Ezen címkéjű rendelési ciklusok:"
|
||||
order_cycle_tagged_bottom: "hatása:"
|
||||
inventory_tagged_top: "Ezen címkéjű termékváltozatok:"
|
||||
inventory_tagged_bottom: "hatása:"
|
||||
variant_tagged_top: "Ezen címkéjű termékváltozatok:"
|
||||
variant_tagged_bottom: "hatása:"
|
||||
shipping_method_tagged_top: "Áruátadási módok felcímkézve"
|
||||
shipping_method_tagged_bottom: "vannak:"
|
||||
payment_method_tagged_top: "Fizetési módok felcímkézve"
|
||||
payment_method_tagged_bottom: "vannak:"
|
||||
order_cycle_tagged_top: "Rendelési ciklusok felcímkézve"
|
||||
order_cycle_tagged_bottom: "vannak:"
|
||||
inventory_tagged_top: "Leltár változatok felcímkézve"
|
||||
inventory_tagged_bottom: "vannak:"
|
||||
variant_tagged_top: "Változat felcímkézve"
|
||||
variant_tagged_bottom: "vannak:"
|
||||
visible: LÁTHATÓ
|
||||
not_visible: NEM LÁTHATÓ
|
||||
tag_rule_group_form:
|
||||
|
||||
@@ -82,7 +82,6 @@ Openfoodnetwork::Application.routes.draw do
|
||||
delete 'products_v3/:id', to: 'products_v3#destroy', as: 'product_destroy'
|
||||
delete 'products_v3/destroy_variant/:id', to: 'products_v3#destroy_variant', as: 'destroy_variant'
|
||||
post 'clone/:id', to: 'products_v3#clone', as: 'clone_product'
|
||||
post 'products/create_linked_variant', to: 'products_v3#create_linked_variant', as: 'create_linked_variant'
|
||||
resources :product_preview, only: [:show]
|
||||
|
||||
resources :variant_overrides do
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CreateVariantLinks < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
# Create a join table to join two variants. One is the source of the other.
|
||||
# Primary key index ensures uniqueness and assists querying. target_variant_id is the most
|
||||
# likely subject and so is first in the index.
|
||||
# An additional index for source_variant is also included because it may be helpful
|
||||
# (https://stackoverflow.com/questions/10790518/best-sql-indexes-for-join-table).
|
||||
create_table :variant_links, primary_key: [:target_variant_id, :source_variant_id] do |t|
|
||||
t.integer :source_variant_id, null: false, index: true
|
||||
t.integer :target_variant_id, null: false
|
||||
|
||||
t.datetime :created_at, null: false
|
||||
end
|
||||
add_foreign_key :variant_links, :spree_variants, column: :source_variant_id
|
||||
add_foreign_key :variant_links, :spree_variants, column: :target_variant_id
|
||||
end
|
||||
end
|
||||
@@ -1,7 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AddHubToSpreeVariants < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
add_reference :spree_variants, :hub, foreign_key: { to_table: :enterprises }
|
||||
end
|
||||
end
|
||||
12
db/schema.rb
12
db/schema.rb
@@ -1009,8 +1009,6 @@ ActiveRecord::Schema[7.1].define(version: 2026_03_06_015040) do
|
||||
t.bigint "supplier_id"
|
||||
t.float "variant_unit_scale"
|
||||
t.string "variant_unit_name", limit: 255
|
||||
t.bigint "hub_id"
|
||||
t.index ["hub_id"], name: "index_spree_variants_on_hub_id"
|
||||
t.index ["primary_taxon_id"], name: "index_spree_variants_on_primary_taxon_id"
|
||||
t.index ["product_id"], name: "index_variants_on_product_id"
|
||||
t.index ["shipping_category_id"], name: "index_spree_variants_on_shipping_category_id"
|
||||
@@ -1115,13 +1113,6 @@ ActiveRecord::Schema[7.1].define(version: 2026_03_06_015040) do
|
||||
t.datetime "updated_at", precision: nil, null: false
|
||||
end
|
||||
|
||||
create_table "variant_links", primary_key: ["target_variant_id", "source_variant_id"], force: :cascade do |t|
|
||||
t.integer "source_variant_id", null: false
|
||||
t.integer "target_variant_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.index ["source_variant_id"], name: "index_variant_links_on_source_variant_id"
|
||||
end
|
||||
|
||||
create_table "variant_overrides", id: :serial, force: :cascade do |t|
|
||||
t.integer "variant_id", null: false
|
||||
t.integer "hub_id", null: false
|
||||
@@ -1271,7 +1262,6 @@ ActiveRecord::Schema[7.1].define(version: 2026_03_06_015040) do
|
||||
add_foreign_key "spree_tax_rates", "spree_zones", column: "zone_id", name: "spree_tax_rates_zone_id_fk"
|
||||
add_foreign_key "spree_users", "spree_addresses", column: "bill_address_id", name: "spree_users_bill_address_id_fk"
|
||||
add_foreign_key "spree_users", "spree_addresses", column: "ship_address_id", name: "spree_users_ship_address_id_fk"
|
||||
add_foreign_key "spree_variants", "enterprises", column: "hub_id"
|
||||
add_foreign_key "spree_variants", "enterprises", column: "supplier_id"
|
||||
add_foreign_key "spree_variants", "spree_products", column: "product_id", name: "spree_variants_product_id_fk"
|
||||
add_foreign_key "spree_variants", "spree_shipping_categories", column: "shipping_category_id"
|
||||
@@ -1288,8 +1278,6 @@ ActiveRecord::Schema[7.1].define(version: 2026_03_06_015040) do
|
||||
add_foreign_key "subscriptions", "spree_payment_methods", column: "payment_method_id", name: "subscriptions_payment_method_id_fk"
|
||||
add_foreign_key "subscriptions", "spree_shipping_methods", column: "shipping_method_id", name: "subscriptions_shipping_method_id_fk"
|
||||
add_foreign_key "tag_rules", "enterprises"
|
||||
add_foreign_key "variant_links", "spree_variants", column: "source_variant_id"
|
||||
add_foreign_key "variant_links", "spree_variants", column: "target_variant_id"
|
||||
add_foreign_key "variant_overrides", "enterprises", column: "hub_id", name: "variant_overrides_hub_id_fk"
|
||||
add_foreign_key "variant_overrides", "spree_variants", column: "variant_id", name: "variant_overrides_variant_id_fk"
|
||||
add_foreign_key "vouchers", "enterprises"
|
||||
|
||||
@@ -86,10 +86,6 @@ module OpenFoodNetwork
|
||||
managed_and_related_enterprises_granting :manage_products
|
||||
end
|
||||
|
||||
def enterprises_granting_linked_variants
|
||||
related_enterprises_granting :create_linked_variants
|
||||
end
|
||||
|
||||
def manages_one_enterprise?
|
||||
@user.enterprises.length == 1
|
||||
end
|
||||
|
||||
@@ -1,5 +1,54 @@
|
||||
---
|
||||
http_interactions:
|
||||
- request:
|
||||
method: post
|
||||
uri: https://backend.demo.taler.net/instances/sandbox/private/orders
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: '{"order":{"amount":"KUDOS:10.0","summary":"Open Food Network order","fulfillment_url":"http://test.host/payment_gateways/taler/61"},"create_token":false}'
|
||||
headers:
|
||||
Authorization:
|
||||
- "<HIDDEN-AUTHORIZATION-HEADER>"
|
||||
Accept:
|
||||
- application/json
|
||||
User-Agent:
|
||||
- Taler Ruby
|
||||
Content-Type:
|
||||
- application/json
|
||||
Accept-Encoding:
|
||||
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
||||
response:
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
headers:
|
||||
Server:
|
||||
- nginx/1.26.3
|
||||
Date:
|
||||
- Thu, 22 Jan 2026 04:43:32 GMT
|
||||
Content-Type:
|
||||
- application/json
|
||||
Content-Length:
|
||||
- '42'
|
||||
Connection:
|
||||
- keep-alive
|
||||
Access-Control-Allow-Origin:
|
||||
- "*"
|
||||
Access-Control-Expose-Headers:
|
||||
- "*"
|
||||
Cache-Control:
|
||||
- no-store
|
||||
Via:
|
||||
- 1.1 Caddy
|
||||
Strict-Transport-Security:
|
||||
- max-age=63072000; includeSubDomains; preload
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: |-
|
||||
{
|
||||
"order_id": "2026.022-0284X4GE8WKMJ"
|
||||
}
|
||||
recorded_at: Thu, 22 Jan 2026 04:43:33 GMT
|
||||
- request:
|
||||
method: get
|
||||
uri: https://backend.demo.taler.net/instances/sandbox/private/orders/2026.022-0284X4GE8WKMJ
|
||||
@@ -54,108 +103,4 @@ http_interactions:
|
||||
}
|
||||
}
|
||||
recorded_at: Thu, 22 Jan 2026 04:43:34 GMT
|
||||
- request:
|
||||
method: post
|
||||
uri: https://backend.demo.taler.net/instances/sandbox/private/token
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: '{"scope":"write"}'
|
||||
headers:
|
||||
Authorization:
|
||||
- "<HIDDEN-AUTHORIZATION-HEADER>"
|
||||
Accept:
|
||||
- application/json
|
||||
User-Agent:
|
||||
- Taler Ruby
|
||||
Content-Type:
|
||||
- application/json
|
||||
Accept-Encoding:
|
||||
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
||||
response:
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
headers:
|
||||
Server:
|
||||
- nginx/1.26.3
|
||||
Date:
|
||||
- Fri, 20 Mar 2026 04:31:47 GMT
|
||||
Content-Type:
|
||||
- application/json
|
||||
Content-Length:
|
||||
- '258'
|
||||
Connection:
|
||||
- keep-alive
|
||||
Access-Control-Allow-Origin:
|
||||
- "*"
|
||||
Access-Control-Expose-Headers:
|
||||
- "*"
|
||||
Cache-Control:
|
||||
- no-store
|
||||
Via:
|
||||
- 1.1 Caddy
|
||||
Strict-Transport-Security:
|
||||
- max-age=63072000; includeSubDomains; preload
|
||||
body:
|
||||
encoding: ASCII-8BIT
|
||||
string: |-
|
||||
{
|
||||
"access_token": "secret-token:J38S28NEJ6T07H1WP60F3T6PPWQYNKMR251TEZEX3CXP3SH54210",
|
||||
"token": "secret-token:J38S28NEJ6T07H1WP60F3T6PPWQYNKMR251TEZEX3CXP3SH54210",
|
||||
"scope": "write",
|
||||
"refreshable": false,
|
||||
"expiration": {
|
||||
"t_s": 1774067507
|
||||
}
|
||||
}
|
||||
recorded_at: Fri, 20 Mar 2026 04:31:48 GMT
|
||||
- request:
|
||||
method: post
|
||||
uri: https://backend.demo.taler.net/instances/sandbox/private/orders
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: '{"order":{"amount":"KUDOS:10.0","summary":"Open Food Network order","fulfillment_url":"http://test.host/payment_gateways/taler/198"},"create_token":false}'
|
||||
headers:
|
||||
Authorization:
|
||||
- "<HIDDEN-AUTHORIZATION-HEADER>"
|
||||
Accept:
|
||||
- application/json
|
||||
User-Agent:
|
||||
- Taler Ruby
|
||||
Content-Type:
|
||||
- application/json
|
||||
Accept-Encoding:
|
||||
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
||||
response:
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
headers:
|
||||
Server:
|
||||
- nginx/1.26.3
|
||||
Date:
|
||||
- Fri, 20 Mar 2026 04:31:48 GMT
|
||||
Content-Type:
|
||||
- application/json
|
||||
Content-Length:
|
||||
- '42'
|
||||
Connection:
|
||||
- keep-alive
|
||||
Access-Control-Allow-Origin:
|
||||
- "*"
|
||||
Access-Control-Expose-Headers:
|
||||
- "*"
|
||||
Cache-Control:
|
||||
- no-store
|
||||
Via:
|
||||
- 1.1 Caddy
|
||||
Strict-Transport-Security:
|
||||
- max-age=63072000; includeSubDomains; preload
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: |-
|
||||
{
|
||||
"order_id": "2026.079-0189PJNWMX6JA"
|
||||
}
|
||||
recorded_at: Fri, 20 Mar 2026 04:31:48 GMT
|
||||
recorded_with: VCR 6.4.0
|
||||
recorded_with: VCR 6.3.1
|
||||
|
||||
@@ -47,61 +47,6 @@ http_interactions:
|
||||
"detail": "taler-order-id:12345"
|
||||
}
|
||||
recorded_at: Sat, 24 Jan 2026 00:51:31 GMT
|
||||
- request:
|
||||
method: post
|
||||
uri: https://backend.demo.taler.net/instances/sandbox/private/token
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: '{"scope":"write"}'
|
||||
headers:
|
||||
Authorization:
|
||||
- "<HIDDEN-AUTHORIZATION-HEADER>"
|
||||
Accept:
|
||||
- application/json
|
||||
User-Agent:
|
||||
- Taler Ruby
|
||||
Content-Type:
|
||||
- application/json
|
||||
Accept-Encoding:
|
||||
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
||||
response:
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
headers:
|
||||
Server:
|
||||
- nginx/1.26.3
|
||||
Date:
|
||||
- Fri, 20 Mar 2026 04:52:23 GMT
|
||||
Content-Type:
|
||||
- application/json
|
||||
Content-Length:
|
||||
- '258'
|
||||
Connection:
|
||||
- keep-alive
|
||||
Access-Control-Allow-Origin:
|
||||
- "*"
|
||||
Access-Control-Expose-Headers:
|
||||
- "*"
|
||||
Cache-Control:
|
||||
- no-store
|
||||
Via:
|
||||
- 1.1 Caddy
|
||||
Strict-Transport-Security:
|
||||
- max-age=63072000; includeSubDomains; preload
|
||||
body:
|
||||
encoding: ASCII-8BIT
|
||||
string: |-
|
||||
{
|
||||
"access_token": "secret-token:176N5XTVVSR98FE6V4QR2Y35HKS61ZW5CK1BC7YEZYHX9M41N5GG",
|
||||
"token": "secret-token:176N5XTVVSR98FE6V4QR2Y35HKS61ZW5CK1BC7YEZYHX9M41N5GG",
|
||||
"scope": "write",
|
||||
"refreshable": false,
|
||||
"expiration": {
|
||||
"t_s": 1774068743
|
||||
}
|
||||
}
|
||||
recorded_at: Fri, 20 Mar 2026 04:52:23 GMT
|
||||
- request:
|
||||
method: get
|
||||
uri: https://backend.demo.taler.net/instances/sandbox/private/orders/2026.020-03R3ETNZZ0DVA
|
||||
@@ -125,7 +70,7 @@ http_interactions:
|
||||
Server:
|
||||
- nginx/1.26.3
|
||||
Date:
|
||||
- Fri, 20 Mar 2026 04:52:24 GMT
|
||||
- Sat, 24 Jan 2026 00:55:33 GMT
|
||||
Content-Type:
|
||||
- application/json
|
||||
Content-Length:
|
||||
@@ -260,5 +205,5 @@ http_interactions:
|
||||
"refund_details": [],
|
||||
"order_status_url": "https://backend.demo.taler.net/instances/sandbox/orders/2026.020-03R3ETNZZ0DVA"
|
||||
}
|
||||
recorded_at: Fri, 20 Mar 2026 04:52:24 GMT
|
||||
recorded_with: VCR 6.4.0
|
||||
recorded_at: Sat, 24 Jan 2026 00:55:32 GMT
|
||||
recorded_with: VCR 6.3.1
|
||||
|
||||
@@ -16,4 +16,3 @@ describe "enterprise relationships", ->
|
||||
expect(EnterpriseRelationships.permission_presentation("manage_products")).toEqual "manage products"
|
||||
expect(EnterpriseRelationships.permission_presentation("edit_profile")).toEqual "edit profile"
|
||||
expect(EnterpriseRelationships.permission_presentation("create_variant_overrides")).toEqual "add products to inventory"
|
||||
expect(EnterpriseRelationships.permission_presentation("create_linked_variants")).toEqual "create linked variants [BETA]"
|
||||
|
||||
@@ -56,7 +56,7 @@ RSpec.describe PaymentMailer do
|
||||
payment = build(:payment)
|
||||
payment.order.distributor = build(:enterprise, name: "Carrot Castle")
|
||||
link = "https://taler.example.com/order/1"
|
||||
mail = PaymentMailer.refund_available(payment.money.to_s, payment, link)
|
||||
mail = PaymentMailer.refund_available(payment, link)
|
||||
|
||||
expect(mail.subject).to eq "Refund from Carrot Castle"
|
||||
expect(mail.body).to include "Your payment of $45.75 to Carrot Castle is being refunded."
|
||||
|
||||
@@ -364,19 +364,6 @@ RSpec.describe Spree::Ability do
|
||||
for: p2.variants.first)
|
||||
end
|
||||
|
||||
describe "create_linked_variant" do
|
||||
it "should not be able to create linked variant without permission" do
|
||||
is_expected.not_to have_ability([:create_linked_variant], for: p_related.variants.first)
|
||||
end
|
||||
|
||||
it "should be able to create linked variant when granted permission" do
|
||||
create(:enterprise_relationship, parent: s_related, child: s1,
|
||||
permissions_list: [:create_linked_variants])
|
||||
|
||||
is_expected.to have_ability([:create_linked_variant], for: p_related.variants.first)
|
||||
end
|
||||
end
|
||||
|
||||
it "should not be able to access admin actions on orders" do
|
||||
is_expected.not_to have_ability([:admin], for: Spree::Order)
|
||||
end
|
||||
@@ -742,19 +729,6 @@ RSpec.describe Spree::Ability do
|
||||
it "can request permitted enterprise fees for an order cycle" do
|
||||
is_expected.to have_ability([:for_order_cycle], for: EnterpriseFee)
|
||||
end
|
||||
|
||||
describe "create_linked_variant" do
|
||||
it "should not be able to create linked variant without permission" do
|
||||
is_expected.not_to have_ability([:create_linked_variant], for: p_related.variants.first)
|
||||
end
|
||||
|
||||
it "should be able to create linked variant when granted permission" do
|
||||
create(:enterprise_relationship, parent: s_related, child: d1,
|
||||
permissions_list: [:create_linked_variants])
|
||||
|
||||
is_expected.to have_ability([:create_linked_variant], for: p_related.variants.first)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'Order Cycle co-ordinator, distributor enterprise manager' do
|
||||
@@ -830,19 +804,6 @@ RSpec.describe Spree::Ability do
|
||||
it "has the ability to manage vouchers" do
|
||||
is_expected.to have_ability([:admin, :create], for: Voucher)
|
||||
end
|
||||
|
||||
describe "create_linked_variant for own enterprise" do
|
||||
it "should not be able to create own sourced variant without permission" do
|
||||
is_expected.not_to have_ability([:create_linked_variant], for: p1.variants.first)
|
||||
end
|
||||
|
||||
it "should be able to create own sourced variant when granted self permission" do
|
||||
create(:enterprise_relationship, parent: s1, child: s1,
|
||||
permissions_list: [:create_linked_variants])
|
||||
|
||||
is_expected.to have_ability([:create_linked_variant], for: p1.variants.first)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'enterprise owner' do
|
||||
|
||||
@@ -21,6 +21,53 @@ RSpec.describe Spree::CreditCard do
|
||||
|
||||
let(:credit_card) { described_class.new }
|
||||
|
||||
context "#can_capture?" do
|
||||
it "should be true if payment is pending" do
|
||||
payment = build_stubbed(:payment, created_at: Time.zone.now)
|
||||
allow(payment).to receive(:pending?) { true }
|
||||
expect(credit_card.can_capture_and_complete_order?(payment)).to be_truthy
|
||||
end
|
||||
|
||||
it "should be true if payment is checkout" do
|
||||
payment = build_stubbed(:payment, created_at: Time.zone.now)
|
||||
allow(payment).to receive_messages pending?: false,
|
||||
checkout?: true
|
||||
expect(credit_card.can_capture_and_complete_order?(payment)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context "#can_void?" do
|
||||
it "should be true if payment is not void" do
|
||||
payment = build_stubbed(:payment)
|
||||
allow(payment).to receive(:void?) { false }
|
||||
expect(credit_card.can_void?(payment)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context "#can_credit?" do
|
||||
it "should be false if payment is not completed" do
|
||||
payment = build_stubbed(:payment)
|
||||
allow(payment).to receive(:completed?) { false }
|
||||
expect(credit_card.can_credit?(payment)).to be_falsy
|
||||
end
|
||||
|
||||
it "should be false when order payment_state is not 'credit_owed'" do
|
||||
payment = build_stubbed(:payment,
|
||||
order: create(:order, payment_state: 'paid'))
|
||||
allow(payment).to receive(:completed?) { true }
|
||||
expect(credit_card.can_credit?(payment)).to be_falsy
|
||||
end
|
||||
|
||||
it "should be false when credit_allowed is zero" do
|
||||
payment = build_stubbed(:payment,
|
||||
order: create(:order, payment_state: 'credit_owed'))
|
||||
allow(payment).to receive_messages completed?: true,
|
||||
credit_allowed: 0
|
||||
|
||||
expect(credit_card.can_credit?(payment)).to be_falsy
|
||||
end
|
||||
end
|
||||
|
||||
context "#valid?" do
|
||||
it "should validate presence of number" do
|
||||
credit_card.attributes = valid_credit_card_attributes.except(:number)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Spree::Gateway do
|
||||
subject(:gateway) { test_gateway.new }
|
||||
let(:test_gateway) do
|
||||
Class.new(Spree::Gateway) do
|
||||
def provider_class
|
||||
@@ -16,58 +15,13 @@ RSpec.describe Spree::Gateway do
|
||||
|
||||
it "passes through all arguments on a method_missing call" do
|
||||
expect(Rails.env).to receive(:local?).and_return(false)
|
||||
gateway = test_gateway.new
|
||||
expect(gateway.provider).to receive(:imaginary_method).with('foo')
|
||||
gateway.imaginary_method('foo')
|
||||
end
|
||||
|
||||
it "raises an error in test env" do
|
||||
gateway = test_gateway.new
|
||||
expect { gateway.imaginary_method('foo') }.to raise_error StandardError
|
||||
end
|
||||
|
||||
describe "#can_capture?" do
|
||||
it "should be true if payment is pending" do
|
||||
payment = build_stubbed(:payment, created_at: Time.zone.now)
|
||||
allow(payment).to receive(:pending?) { true }
|
||||
expect(gateway.can_capture_and_complete_order?(payment)).to be_truthy
|
||||
end
|
||||
|
||||
it "should be true if payment is checkout" do
|
||||
payment = build_stubbed(:payment, created_at: Time.zone.now)
|
||||
allow(payment).to receive_messages pending?: false,
|
||||
checkout?: true
|
||||
expect(gateway.can_capture_and_complete_order?(payment)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
describe "#can_void?" do
|
||||
it "should be true if payment is not void" do
|
||||
payment = build_stubbed(:payment)
|
||||
allow(payment).to receive(:void?) { false }
|
||||
expect(gateway.can_void?(payment)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
describe "#can_credit?" do
|
||||
it "should be false if payment is not completed" do
|
||||
payment = build_stubbed(:payment)
|
||||
allow(payment).to receive(:completed?) { false }
|
||||
expect(gateway.can_credit?(payment)).to be_falsy
|
||||
end
|
||||
|
||||
it "should be false when order payment_state is not 'credit_owed'" do
|
||||
payment = build_stubbed(:payment,
|
||||
order: create(:order, payment_state: 'paid'))
|
||||
allow(payment).to receive(:completed?) { true }
|
||||
expect(gateway.can_credit?(payment)).to be_falsy
|
||||
end
|
||||
|
||||
it "should be false when credit_allowed is zero" do
|
||||
payment = build_stubbed(:payment,
|
||||
order: create(:order, payment_state: 'credit_owed'))
|
||||
allow(payment).to receive_messages completed?: true,
|
||||
credit_allowed: 0
|
||||
|
||||
expect(gateway.can_credit?(payment)).to be_falsy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,38 +10,16 @@ RSpec.describe Spree::PaymentMethod::Taler do
|
||||
)
|
||||
}
|
||||
let(:backend_url) { "https://backend.demo.taler.net/instances/sandbox" }
|
||||
let(:token_url) { "#{backend_url}/private/token" }
|
||||
|
||||
describe "#external_payment_url" do
|
||||
it "creates an order reference and retrieves a URL to pay at", vcr: true do
|
||||
describe "#external_payment_url", vcr: true do
|
||||
it "creates an order reference and retrieves a URL to pay at" do
|
||||
order = create(:order_ready_for_confirmation, payment_method: taler)
|
||||
|
||||
url = subject.external_payment_url(order:)
|
||||
expect(url).to start_with "#{backend_url}/orders/"
|
||||
expect(url).to match "orders/20...[0-9A-Z-]{17}$"
|
||||
expect(url).to eq "#{backend_url}/orders/2026.022-0284X4GE8WKMJ"
|
||||
|
||||
payment = order.payments.last.reload
|
||||
expect(payment.response_code).to match "20...[0-9A-Z-]{17}$"
|
||||
end
|
||||
|
||||
it "creates the Taler order with the right currency" do
|
||||
order = create(:order_ready_for_confirmation, payment_method: taler)
|
||||
|
||||
backend_url = "https://taler.example.com"
|
||||
token_url = "https://taler.example.com/private/token"
|
||||
order_url = "https://taler.example.com/private/orders"
|
||||
taler = Spree::PaymentMethod::Taler.new(
|
||||
preferred_backend_url: "https://taler.example.com",
|
||||
preferred_api_key: "sandbox",
|
||||
)
|
||||
|
||||
stub_request(:post, token_url).to_return(body: { token: "1234" }.to_json)
|
||||
stub_request(:post, order_url)
|
||||
.with(body: /"amount":"AUD:10.0"/)
|
||||
.to_return(body: { order_id: "one" }.to_json)
|
||||
|
||||
url = taler.external_payment_url(order:)
|
||||
expect(url).to eq "#{backend_url}/orders/one"
|
||||
expect(payment.response_code).to match "2026.022-0284X4GE8WKMJ"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -51,10 +29,6 @@ RSpec.describe Spree::PaymentMethod::Taler do
|
||||
let(:payment) { build(:payment, response_code: "taler-order-7") }
|
||||
let(:order_url) { "#{backend_url}/private/orders/taler-order-7" }
|
||||
|
||||
before do
|
||||
stub_request(:post, token_url).to_return(body: { token: "12345" }.to_json)
|
||||
end
|
||||
|
||||
it "returns an ActiveMerchant response" do
|
||||
order_status = "paid"
|
||||
stub_request(:get, order_url).to_return(body: { order_status: }.to_json)
|
||||
@@ -76,50 +50,6 @@ RSpec.describe Spree::PaymentMethod::Taler do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#credit" do
|
||||
let(:order_endpoint) { "#{backend_url}/private/orders/taler-order-8" }
|
||||
let(:refund_endpoint) { "#{order_endpoint}/refund" }
|
||||
let(:taler_refund_uri) {
|
||||
"taler://refund/backend.demo.taler.net/instances/sandbox/taler-order-8/"
|
||||
}
|
||||
|
||||
before do
|
||||
stub_request(:post, token_url).to_return(body: { token: "12345" }.to_json)
|
||||
end
|
||||
|
||||
it "starts the refund process" do
|
||||
order_status = { order_status: "paid" }
|
||||
stub_request(:get, order_endpoint).to_return(body: order_status.to_json)
|
||||
stub_request(:post, refund_endpoint).to_return(body: { taler_refund_uri: }.to_json)
|
||||
|
||||
order = create(:completed_order_with_totals)
|
||||
order.payments.create(
|
||||
amount: order.total, state: :completed,
|
||||
payment_method: taler,
|
||||
response_code: "taler-order-8",
|
||||
)
|
||||
expect {
|
||||
response = taler.credit(100, "taler-order-8", { payment: order.payments[0] })
|
||||
expect(response.success?).to eq true
|
||||
}.to enqueue_mail(PaymentMailer, :refund_available)
|
||||
end
|
||||
|
||||
it "raises an error if payment hasn't been taken yet" do
|
||||
order_status = { order_status: "claimed" }
|
||||
stub_request(:get, order_endpoint).to_return(body: order_status.to_json)
|
||||
|
||||
order = create(:completed_order_with_totals)
|
||||
order.payments.create(
|
||||
amount: order.total, state: :completed,
|
||||
payment_method: taler,
|
||||
response_code: "taler-order-8",
|
||||
)
|
||||
expect {
|
||||
taler.credit(100, "taler-order-8", { payment: order.payments[0] })
|
||||
}.to raise_error StandardError, "Unsupported action"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#void" do
|
||||
let(:order_endpoint) { "#{backend_url}/private/orders/taler-order-8" }
|
||||
let(:refund_endpoint) { "#{order_endpoint}/refund" }
|
||||
@@ -127,10 +57,6 @@ RSpec.describe Spree::PaymentMethod::Taler do
|
||||
"taler://refund/backend.demo.taler.net/instances/sandbox/taler-order-8/"
|
||||
}
|
||||
|
||||
before do
|
||||
stub_request(:post, token_url).to_return(body: { token: "12345" }.to_json)
|
||||
end
|
||||
|
||||
it "starts the refund process" do
|
||||
order_status = {
|
||||
order_status: "paid",
|
||||
|
||||
@@ -855,8 +855,7 @@ RSpec.describe Spree::Payment do
|
||||
|
||||
describe "available actions" do
|
||||
context "for most gateways" do
|
||||
let(:payment) { build_stubbed(:payment, payment_method:) }
|
||||
let(:payment_method) { Spree::Gateway::StripeSCA.new }
|
||||
let(:payment) { build_stubbed(:payment, source: build_stubbed(:credit_card)) }
|
||||
|
||||
it "can capture and void" do
|
||||
expect(payment.actions).to match_array %w(capture_and_complete_order void)
|
||||
|
||||
@@ -8,7 +8,6 @@ RSpec.describe Spree::Variant do
|
||||
it { is_expected.to have_many :semantic_links }
|
||||
it { is_expected.to belong_to(:product).required }
|
||||
it { is_expected.to belong_to(:supplier).required }
|
||||
it { is_expected.to belong_to(:hub).optional }
|
||||
it { is_expected.to have_many(:inventory_units) }
|
||||
it { is_expected.to have_many(:line_items) }
|
||||
it { is_expected.to have_many(:stock_items) }
|
||||
@@ -21,9 +20,6 @@ RSpec.describe Spree::Variant do
|
||||
it { is_expected.to have_many(:inventory_items) }
|
||||
it { is_expected.to have_many(:supplier_properties).through(:supplier) }
|
||||
|
||||
it { is_expected.to have_many(:source_variants).through(:variant_links_as_target) }
|
||||
it { is_expected.to have_many(:target_variants).through(:variant_links_as_source) }
|
||||
|
||||
describe "shipping category" do
|
||||
it "sets a shipping category if none provided" do
|
||||
variant = build(:variant, shipping_category: nil)
|
||||
@@ -1005,30 +1001,4 @@ RSpec.describe Spree::Variant do
|
||||
expect(variant.unit_presentation).to eq "My display"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#create_linked_variant" do
|
||||
let(:user) { create(:user, enterprises: [enterprise]) }
|
||||
let(:supplier) { variant.supplier }
|
||||
let(:enterprise) { create(:enterprise) }
|
||||
|
||||
context "with create_linked_variants permissions on supplier" do
|
||||
let!(:enterprise_relationship) {
|
||||
create(:enterprise_relationship,
|
||||
parent: supplier,
|
||||
child: enterprise,
|
||||
permissions_list: [:create_linked_variants])
|
||||
}
|
||||
let(:variant) { create(:variant, price: 10.95, on_demand: false, on_hand: 5) }
|
||||
|
||||
it "clones the variant, retaining a link to the source" do
|
||||
linked_variant = variant.create_linked_variant(user)
|
||||
|
||||
expect(linked_variant.source_variants).to eq [variant]
|
||||
expect(linked_variant.hub).to eq enterprise
|
||||
expect(linked_variant.price).to eq 10.95
|
||||
expect(linked_variant.on_demand).to eq false
|
||||
expect(linked_variant.on_hand).to eq 5
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -59,71 +59,4 @@ RSpec.describe "Admin::ProductsV3" do
|
||||
expect(response).to redirect_to('/unauthorized')
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /admin/products/create_linked_variant" do
|
||||
let(:enterprise) { create(:supplier_enterprise) }
|
||||
let(:user) { create(:user, enterprises: [enterprise]) }
|
||||
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
let(:variant) { create(:variant, display_name: "Original variant", supplier: supplier) }
|
||||
|
||||
before do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
it "checks for permission" do
|
||||
params = { variant_id: variant.id, product_index: 1 }
|
||||
|
||||
expect {
|
||||
post(admin_create_linked_variant_path, as: :turbo_stream, params:)
|
||||
expect(response).to redirect_to('/unauthorized')
|
||||
}.not_to change { variant.product.variants.count }
|
||||
end
|
||||
|
||||
context "With create_linked_variants permissions on supplier" do
|
||||
let!(:enterprise_relationship) {
|
||||
create(:enterprise_relationship,
|
||||
parent: supplier,
|
||||
child: enterprise,
|
||||
permissions_list: [:create_linked_variants])
|
||||
}
|
||||
|
||||
it "clones the variant, retaining link as source" do
|
||||
params = { variant_id: variant.id, product_index: 1 }
|
||||
|
||||
expect {
|
||||
post(admin_create_linked_variant_path, as: :turbo_stream, params:)
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to match "Original variant" # cloned variant name
|
||||
}.to change { variant.product.variants.count }.by(1)
|
||||
|
||||
new_variant = variant.product.variants.order(:id).last
|
||||
# The new variant is a target of the original. It is a "sourced" variant.
|
||||
expect(variant.target_variants.first).to eq new_variant
|
||||
# The new variant's source is the original
|
||||
expect(new_variant.source_variants.first).to eq variant
|
||||
end
|
||||
|
||||
context "and I'm also owner of another enterprise" do
|
||||
let!(:enterprise2) { create(:enterprise) }
|
||||
let(:user) { create(:user, enterprises: [enterprise, enterprise2]) }
|
||||
|
||||
it "clones the variant, owned by my enterprise that has permission" do
|
||||
enterprise2.owner = user
|
||||
params = { variant_id: variant.id, product_index: 1 }
|
||||
|
||||
expect {
|
||||
post(admin_create_linked_variant_path, as: :turbo_stream, params:)
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
}.to change { variant.product.variants.count }.by(1)
|
||||
|
||||
# The new variant is owned by my enterprise that has permission, not the other one
|
||||
new_variant = variant.product.variants.order(:id).last
|
||||
expect(new_variant.hub).to eq enterprise
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -157,6 +157,8 @@ RSpec.describe Spree::Admin::PaymentsController do
|
||||
|
||||
context "with no payment source" do
|
||||
it "redirect to payments page" do
|
||||
allow(payment).to receive(:payment_source).and_return(nil)
|
||||
|
||||
put(
|
||||
"/admin/orders/#{order.number}/payments/#{order.payments.first.id}/fire?e=void",
|
||||
params: {},
|
||||
|
||||
@@ -42,7 +42,6 @@ RSpec.describe Payments::WebhookPayload do
|
||||
}
|
||||
},
|
||||
order: {
|
||||
number: order.number,
|
||||
total: order.total,
|
||||
currency: order.currency,
|
||||
line_items: line_items
|
||||
@@ -73,7 +72,6 @@ RSpec.describe Payments::WebhookPayload do
|
||||
}
|
||||
},
|
||||
order: {
|
||||
number: "R555555555",
|
||||
total: 0.00,
|
||||
currency: "AUD",
|
||||
line_items: [
|
||||
|
||||
@@ -56,7 +56,6 @@ RSpec.describe Payments::WebhookService do
|
||||
}
|
||||
},
|
||||
order: {
|
||||
number: order.number,
|
||||
total: order.total,
|
||||
currency: order.currency,
|
||||
line_items: line_items
|
||||
|
||||
@@ -47,23 +47,18 @@ create(:enterprise)
|
||||
uncheck 'to manage products'
|
||||
check 'to edit profile'
|
||||
check 'to add products to inventory'
|
||||
check 'to create linked variants'
|
||||
select2_select 'Two', from: 'enterprise_relationship_child_id'
|
||||
click_button 'Create'
|
||||
|
||||
# Wait for row to appear since have_relationship doesn't wait
|
||||
expect(page).to have_selector 'tr', count: 2
|
||||
# Permissions appear.. in a different order for some reason.
|
||||
expect_relationship_with_permissions e1, e2,
|
||||
['to add to order cycle',
|
||||
'to create linked variants [BETA]',
|
||||
'to add products to inventory',
|
||||
'to edit profile']
|
||||
'to add products to inventory', 'to edit profile']
|
||||
er = EnterpriseRelationship.where(parent_id: e1, child_id: e2).first
|
||||
expect(er).to be_present
|
||||
expect(er.permissions.map(&:name)).to match_array ['add_to_order_cycle', 'edit_profile',
|
||||
'create_variant_overrides',
|
||||
'create_linked_variants']
|
||||
'create_variant_overrides']
|
||||
end
|
||||
|
||||
it "attempting to create a relationship with invalid data" do
|
||||
|
||||
@@ -885,47 +885,6 @@ RSpec.describe '
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "removing enterprise managers" do
|
||||
let(:existing_user) { create(:user) }
|
||||
|
||||
before do
|
||||
distributor1.users << existing_user
|
||||
login_as logged_in_user
|
||||
visit edit_admin_enterprise_path(distributor1)
|
||||
scroll_to(:bottom)
|
||||
within ".side_menu" do
|
||||
find(:link, "Users").trigger("click")
|
||||
end
|
||||
end
|
||||
|
||||
context "as the enterprise owner" do
|
||||
let(:logged_in_user) { distributor1.owner }
|
||||
|
||||
it 'removes the manager as enterprise owner' do
|
||||
expect(page).to have_content existing_user.email
|
||||
|
||||
within "#manager-#{existing_user.id}" do
|
||||
accept_confirm do
|
||||
page.find("a.icon-trash").click
|
||||
end
|
||||
end
|
||||
|
||||
expect(page).not_to have_content existing_user.email
|
||||
end
|
||||
end
|
||||
|
||||
context "as the enterprise manager" do
|
||||
let(:logged_in_user) { existing_user }
|
||||
|
||||
it "is unable delete any other manager" do
|
||||
expect(page).to have_content existing_user.email
|
||||
within('.edit_enterprise') do
|
||||
expect(page).not_to have_selector('a.icon-trash')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "changing package" do
|
||||
|
||||
@@ -25,17 +25,15 @@ RSpec.describe "Admin -> Order -> Payments" do
|
||||
login_as distributor.owner
|
||||
end
|
||||
|
||||
it "allows to void a Taler payment" do
|
||||
it "allows to refund a Taler payment" do
|
||||
order_status = {
|
||||
order_status: "paid",
|
||||
contract_terms: {
|
||||
amount: "KUDOS:2",
|
||||
}
|
||||
}
|
||||
token_endpoint = "https://taler.example.com/private/token"
|
||||
order_endpoint = "https://taler.example.com/private/orders/taler-id-1"
|
||||
refund_endpoint = "https://taler.example.com/private/orders/taler-id-1/refund"
|
||||
stub_request(:post, token_endpoint).to_return(body: { token: "abc" }.to_json)
|
||||
stub_request(:get, order_endpoint).to_return(body: order_status.to_json)
|
||||
stub_request(:post, refund_endpoint).to_return(body: "{}")
|
||||
|
||||
@@ -51,36 +49,4 @@ RSpec.describe "Admin -> Order -> Payments" do
|
||||
expect(page).not_to have_link "Void"
|
||||
end
|
||||
end
|
||||
|
||||
it "allows to credit a Taler payment" do
|
||||
order_status = {
|
||||
order_status: "paid",
|
||||
contract_terms: {
|
||||
amount: "KUDOS:2",
|
||||
}
|
||||
}
|
||||
token_endpoint = "https://taler.example.com/private/token"
|
||||
order_endpoint = "https://taler.example.com/private/orders/taler-id-1"
|
||||
refund_endpoint = "https://taler.example.com/private/orders/taler-id-1/refund"
|
||||
stub_request(:post, token_endpoint).to_return(body: { token: "abc" }.to_json)
|
||||
stub_request(:get, order_endpoint).to_return(body: order_status.to_json)
|
||||
stub_request(:post, refund_endpoint).to_return(body: "{}")
|
||||
|
||||
visit spree.admin_order_payments_path(order.number)
|
||||
|
||||
within row_containing("Taler") do
|
||||
expect(page).to have_text "COMPLETED"
|
||||
expect(page).to have_link "Credit"
|
||||
|
||||
click_link class: "icon-credit"
|
||||
|
||||
expect(page).to have_text "COMPLETED"
|
||||
expect(page).not_to have_link "Credit"
|
||||
end
|
||||
|
||||
# Our payment system creates a new payment to show the credit.
|
||||
within row_containing("$-9.75") do
|
||||
expect(page).not_to have_link "Void"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require "system_helper"
|
||||
|
||||
RSpec.describe 'As an enterprise user, I can perform actions on the products screen' do
|
||||
RSpec.describe 'As an enterprise user, I can manage my products' do
|
||||
include AdminHelper
|
||||
include WebHelper
|
||||
include AuthenticationHelper
|
||||
@@ -19,6 +19,18 @@ RSpec.describe 'As an enterprise user, I can perform actions on the products scr
|
||||
let(:categories_search_selector) { 'input[placeholder="Select category"]' }
|
||||
let(:tax_categories_search_selector) { 'input[placeholder="Search for tax categories"]' }
|
||||
|
||||
describe "with no products" do
|
||||
before { visit admin_products_url }
|
||||
it "can see the new product page" do
|
||||
expect(page).to have_content "Bulk Edit Products"
|
||||
expect(page).to have_text "No products found"
|
||||
# displays buttons to add products with the correct links
|
||||
expect(page).to have_link(class: "button", text: "New Product", href: "/admin/products/new")
|
||||
expect(page).to have_link(class: "button", text: "Import multiple products",
|
||||
href: admin_product_import_path)
|
||||
end
|
||||
end
|
||||
|
||||
describe "column selector" do
|
||||
let!(:product) { create(:simple_product) }
|
||||
|
||||
@@ -93,6 +105,8 @@ RSpec.describe 'As an enterprise user, I can perform actions on the products scr
|
||||
end
|
||||
end
|
||||
|
||||
describe "columns"
|
||||
|
||||
describe "Changing producers, category and tax category" do
|
||||
let!(:variant_a1) {
|
||||
product_a.variants.first.tap{ |v|
|
||||
@@ -246,24 +260,24 @@ RSpec.describe 'As an enterprise user, I can perform actions on the products scr
|
||||
|
||||
describe "Cloning product" do
|
||||
it "shows the cloned product on page when clicked on the cloned option" do
|
||||
# TODO, variant supplier missing, needs to be copied from variant and not product
|
||||
within "table.products" do
|
||||
# Gather input values, because page.content doesn't include them.
|
||||
input_content = page.find_all('input[type=text]').map(&:value).join
|
||||
|
||||
# Products does not include the cloned product.
|
||||
expect(input_content).not_to match /COPY OF Apples/
|
||||
end
|
||||
|
||||
click_product_clone "Apples"
|
||||
|
||||
expect(page).to have_content "Successfully cloned the product"
|
||||
within "table.products" do
|
||||
# Product list includes the cloned product.
|
||||
expect(all_input_values).to match /COPY OF Apples/
|
||||
# Gather input values, because page.content doesn't include them.
|
||||
input_content = page.find_all('input[type=text]').map(&:value).join
|
||||
|
||||
# And I can perform actions on the new product
|
||||
within row_containing_name "COPY OF Apples" do
|
||||
page.find(".vertical-ellipsis-menu").click
|
||||
expect(page).to have_link "Edit"
|
||||
expect(page).to have_link "Clone"
|
||||
# expect(page).to have_link "Delete" # it's not a proper link :/
|
||||
|
||||
fill_in "Name", with: "My copy of Apples"
|
||||
end
|
||||
|
||||
click_button "Save changes"
|
||||
# Products include the cloned product.
|
||||
expect(input_content).to match /COPY OF Apples/
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -284,88 +298,6 @@ RSpec.describe 'As an enterprise user, I can perform actions on the products scr
|
||||
end
|
||||
end
|
||||
|
||||
describe "Create linked variant" do
|
||||
let!(:variant) {
|
||||
create(:variant, display_name: "My box", supplier: producer)
|
||||
}
|
||||
let!(:other_producer) { create(:supplier_enterprise) }
|
||||
let!(:other_variant) {
|
||||
create(:variant, display_name: "My friends box", supplier: other_producer)
|
||||
}
|
||||
let!(:enterprise_relationship) {
|
||||
# Other producer grants me access to manage their variant
|
||||
create(:enterprise_relationship, parent: other_producer, child: producer,
|
||||
permissions_list: [:manage_products])
|
||||
}
|
||||
|
||||
context "with create_linked_variants permission for my, and other's variants" do
|
||||
it "creates a linked variant" do
|
||||
create(:enterprise_relationship, parent: producer, child: producer,
|
||||
permissions_list: [:create_linked_variants])
|
||||
enterprise_relationship.permissions.create! name: :create_linked_variants
|
||||
|
||||
visit admin_products_url
|
||||
|
||||
# Check my own variant
|
||||
within row_containing_name("My box") do
|
||||
page.find(".vertical-ellipsis-menu").click
|
||||
|
||||
expect(page).to have_link "Create linked variant"
|
||||
end
|
||||
|
||||
# Create linked variant sourced from my friend
|
||||
within row_containing_name("My friends box") do
|
||||
page.find(".vertical-ellipsis-menu").click
|
||||
|
||||
click_link "Create linked variant"
|
||||
end
|
||||
|
||||
expect(page).to have_content "Successfully created linked variant"
|
||||
|
||||
within "table.products" do
|
||||
# There are now two copies
|
||||
expect(all_input_values).to match /My friends box.*My friends box/
|
||||
# One of them is designated as a linked variant
|
||||
expect(page).to have_content "🔗"
|
||||
|
||||
last_box = page.all(row_containing_name("My friends box")).last
|
||||
# Close action menu (shouldn't need this, it should close itself)
|
||||
last_box.click
|
||||
|
||||
# And I can perform actions on the new product
|
||||
within last_box do
|
||||
page.find(".vertical-ellipsis-menu").click
|
||||
expect(page).to have_link "Edit"
|
||||
# expect(page).to have_link "Clone" # tofix: menu is partially obscured
|
||||
# expect(page).to have_link "Delete" # it's not a proper link
|
||||
|
||||
fill_in "Name", with: "My copy of Apples"
|
||||
end
|
||||
click_button "Save changes"
|
||||
|
||||
# initially obscured by the previous message, then disappears before capybara sees it.
|
||||
# expect(page).to have_content "Changes saved"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "without create_linked_variants permission" do
|
||||
it "does not show the option in the menu" do
|
||||
visit admin_products_url
|
||||
|
||||
within row_containing_name("My box") do
|
||||
page.find(".vertical-ellipsis-menu").click
|
||||
expect(page).not_to have_link "Create linked variant"
|
||||
end
|
||||
|
||||
within row_containing_name("My friends box") do
|
||||
page.find(".vertical-ellipsis-menu").click
|
||||
expect(page).not_to have_link "Create linked variant"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete" do
|
||||
let!(:product_a) { create(:simple_product, name: "Apples", sku: "APL-00") }
|
||||
let(:delete_option_selector) { "a[data-controller='modal-link'].delete" }
|
||||
@@ -595,6 +527,90 @@ RSpec.describe 'As an enterprise user, I can perform actions on the products scr
|
||||
end
|
||||
end
|
||||
|
||||
context "as an enterprise manager" do
|
||||
let(:supplier_managed1) { create(:supplier_enterprise, name: 'Supplier Managed 1') }
|
||||
let(:supplier_managed2) { create(:supplier_enterprise, name: 'Supplier Managed 2') }
|
||||
let(:supplier_unmanaged) { create(:supplier_enterprise, name: 'Supplier Unmanaged') }
|
||||
let(:supplier_permitted) { create(:supplier_enterprise, name: 'Supplier Permitted') }
|
||||
let(:distributor_managed) { create(:distributor_enterprise, name: 'Distributor Managed') }
|
||||
let(:distributor_unmanaged) { create(:distributor_enterprise, name: 'Distributor Unmanaged') }
|
||||
let!(:product_supplied) { create(:product, supplier_id: supplier_managed1.id, price: 10.0) }
|
||||
let!(:product_not_supplied) { create(:product, supplier_id: supplier_unmanaged.id) }
|
||||
let!(:product_supplied_permitted) {
|
||||
create(:product, name: 'Product Permitted', supplier_id: supplier_permitted.id, price: 10.0)
|
||||
}
|
||||
let(:product_supplied_inactive) {
|
||||
create(:product, supplier_id: supplier_managed1.id, price: 10.0)
|
||||
}
|
||||
|
||||
let!(:supplier_permitted_relationship) do
|
||||
create(:enterprise_relationship, parent: supplier_permitted, child: supplier_managed1,
|
||||
permissions_list: [:manage_products])
|
||||
end
|
||||
|
||||
before do
|
||||
enterprise_user = create(:user)
|
||||
enterprise_user.enterprise_roles.build(enterprise: supplier_managed1).save
|
||||
enterprise_user.enterprise_roles.build(enterprise: supplier_managed2).save
|
||||
enterprise_user.enterprise_roles.build(enterprise: distributor_managed).save
|
||||
|
||||
login_as enterprise_user
|
||||
end
|
||||
|
||||
it "shows only products that I supply" do
|
||||
visit spree.admin_products_path
|
||||
|
||||
# displays permitted product list only
|
||||
expect(page).to have_selector row_containing_name(product_supplied.name)
|
||||
expect(page).to have_selector row_containing_name(product_supplied_permitted.name)
|
||||
expect(page).not_to have_selector row_containing_name(product_not_supplied.name)
|
||||
end
|
||||
|
||||
it "shows only suppliers that I manage or have permission to" do
|
||||
visit spree.admin_products_path
|
||||
|
||||
within row_containing_placeholder(product_supplied.name) do
|
||||
expect(page).to have_select(
|
||||
'_products_0_variants_attributes_0_supplier_id',
|
||||
options: [
|
||||
'Select producer',
|
||||
supplier_managed1.name, supplier_managed2.name, supplier_permitted.name
|
||||
], selected: supplier_managed1.name
|
||||
)
|
||||
end
|
||||
|
||||
within row_containing_placeholder(product_supplied_permitted.name) do
|
||||
expect(page).to have_select(
|
||||
'_products_1_variants_attributes_0_supplier_id',
|
||||
options: [
|
||||
'Select producer',
|
||||
supplier_managed1.name, supplier_managed2.name, supplier_permitted.name
|
||||
], selected: supplier_permitted.name
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it "shows inactive products that I supply" do
|
||||
product_supplied_inactive
|
||||
|
||||
visit spree.admin_products_path
|
||||
|
||||
expect(page).to have_selector row_containing_name(product_supplied_inactive.name)
|
||||
end
|
||||
|
||||
it "allows me to update a product" do
|
||||
visit spree.admin_products_path
|
||||
|
||||
within row_containing_name(product_supplied.name) do
|
||||
fill_in "Name", with: "Pommes"
|
||||
end
|
||||
click_button "Save changes"
|
||||
|
||||
expect(page).to have_content "Changes saved"
|
||||
expect(page).to have_selector row_containing_name("Pommes")
|
||||
end
|
||||
end
|
||||
|
||||
def open_action_menu
|
||||
page.find(".vertical-ellipsis-menu").click
|
||||
end
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
require "system_helper"
|
||||
|
||||
RSpec.describe 'As an enterprise user, I can browse my products' do
|
||||
RSpec.describe 'As an enterprise user, I can manage my products' do
|
||||
include AdminHelper
|
||||
include WebHelper
|
||||
include AuthenticationHelper
|
||||
include FileHelper
|
||||
|
||||
let(:producer) { create(:supplier_enterprise, name: "My Enterprise") }
|
||||
let(:producer) { create(:supplier_enterprise) }
|
||||
let(:user) { create(:user, enterprises: [producer]) }
|
||||
|
||||
before do
|
||||
@@ -19,25 +19,15 @@ RSpec.describe 'As an enterprise user, I can browse my products' do
|
||||
let(:categories_search_selector) { 'input[placeholder="Search for categories"]' }
|
||||
let(:tax_categories_search_selector) { 'input[placeholder="Search for tax categories"]' }
|
||||
|
||||
describe "with no products" do
|
||||
before { visit admin_products_url }
|
||||
it "can see the new product page" do
|
||||
expect(page).to have_content "Bulk Edit Products"
|
||||
expect(page).to have_text "No products found"
|
||||
# displays buttons to add products with the correct links
|
||||
expect(page).to have_link(class: "button", text: "New Product", href: "/admin/products/new")
|
||||
expect(page).to have_link(class: "button", text: "Import multiple products",
|
||||
href: admin_product_import_path)
|
||||
end
|
||||
end
|
||||
|
||||
describe "listing" do
|
||||
let!(:p1) { create(:product, name: "Product1") }
|
||||
let!(:p2) { create(:product, name: "Product2") }
|
||||
|
||||
it "displays a list of products" do
|
||||
visit admin_products_path
|
||||
before do
|
||||
visit admin_products_url
|
||||
end
|
||||
|
||||
it "displays a list of products" do
|
||||
within ".products" do
|
||||
# displays table header
|
||||
expect(page).to have_selector "th", text: "Name"
|
||||
@@ -139,34 +129,6 @@ RSpec.describe 'As an enterprise user, I can browse my products' do
|
||||
expect(page).to have_select "variant_unit_with_scale", selected: "Items"
|
||||
expect(page).to have_field "variant_unit_name", with: "packet"
|
||||
end
|
||||
|
||||
context "with sourced variant" do
|
||||
let(:source_producer) { create(:supplier_enterprise) }
|
||||
let(:p3) { create(:product, name: "Product3", supplier_id: source_producer.id) }
|
||||
|
||||
let!(:v3_source) { p3.variants.first }
|
||||
let!(:v3_sourced) {
|
||||
create(:variant, display_name: "Variant3-sourced", product: p3, supplier: source_producer,
|
||||
hub: producer)
|
||||
}
|
||||
let!(:enterprise_relationship) {
|
||||
# Other producer grants me access to manage their variant
|
||||
create(:enterprise_relationship, parent: source_producer, child: producer,
|
||||
permissions_list: [:manage_products])
|
||||
}
|
||||
|
||||
before do
|
||||
v3_sourced.source_variants << v3_source
|
||||
visit admin_products_url
|
||||
end
|
||||
|
||||
it "shows sourced variant with indicator" do
|
||||
within row_containing_name("Variant3-sourced") do
|
||||
expect(page).to have_selector 'span[title*="Sourced from: "]'
|
||||
expect(page).to have_selector 'span[title*="Hub: My Enterprise"]'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "sorting" do
|
||||
@@ -501,88 +463,4 @@ RSpec.describe 'As an enterprise user, I can browse my products' do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "as an enterprise manager" do
|
||||
let(:supplier_managed1) { create(:supplier_enterprise, name: 'Supplier Managed 1') }
|
||||
let(:supplier_managed2) { create(:supplier_enterprise, name: 'Supplier Managed 2') }
|
||||
let(:supplier_unmanaged) { create(:supplier_enterprise, name: 'Supplier Unmanaged') }
|
||||
let(:supplier_permitted) { create(:supplier_enterprise, name: 'Supplier Permitted') }
|
||||
let(:distributor_managed) { create(:distributor_enterprise, name: 'Distributor Managed') }
|
||||
let(:distributor_unmanaged) { create(:distributor_enterprise, name: 'Distributor Unmanaged') }
|
||||
let!(:product_supplied) { create(:product, supplier_id: supplier_managed1.id, price: 10.0) }
|
||||
let!(:product_not_supplied) { create(:product, supplier_id: supplier_unmanaged.id) }
|
||||
let!(:product_supplied_permitted) {
|
||||
create(:product, name: 'Product Permitted', supplier_id: supplier_permitted.id, price: 10.0)
|
||||
}
|
||||
let(:product_supplied_inactive) {
|
||||
create(:product, supplier_id: supplier_managed1.id, price: 10.0)
|
||||
}
|
||||
|
||||
let!(:supplier_permitted_relationship) do
|
||||
create(:enterprise_relationship, parent: supplier_permitted, child: supplier_managed1,
|
||||
permissions_list: [:manage_products])
|
||||
end
|
||||
|
||||
before do
|
||||
enterprise_user = create(:user)
|
||||
enterprise_user.enterprise_roles.build(enterprise: supplier_managed1).save
|
||||
enterprise_user.enterprise_roles.build(enterprise: supplier_managed2).save
|
||||
enterprise_user.enterprise_roles.build(enterprise: distributor_managed).save
|
||||
|
||||
login_as enterprise_user
|
||||
end
|
||||
|
||||
it "shows only products that I supply" do
|
||||
visit spree.admin_products_path
|
||||
|
||||
# displays permitted product list only
|
||||
expect(page).to have_selector row_containing_name(product_supplied.name)
|
||||
expect(page).to have_selector row_containing_name(product_supplied_permitted.name)
|
||||
expect(page).not_to have_selector row_containing_name(product_not_supplied.name)
|
||||
end
|
||||
|
||||
it "shows only suppliers that I manage or have permission to" do
|
||||
visit spree.admin_products_path
|
||||
|
||||
within row_containing_placeholder(product_supplied.name) do
|
||||
expect(page).to have_select(
|
||||
'_products_0_variants_attributes_0_supplier_id',
|
||||
options: [
|
||||
'Select producer',
|
||||
supplier_managed1.name, supplier_managed2.name, supplier_permitted.name
|
||||
], selected: supplier_managed1.name
|
||||
)
|
||||
end
|
||||
|
||||
within row_containing_placeholder(product_supplied_permitted.name) do
|
||||
expect(page).to have_select(
|
||||
'_products_1_variants_attributes_0_supplier_id',
|
||||
options: [
|
||||
'Select producer',
|
||||
supplier_managed1.name, supplier_managed2.name, supplier_permitted.name
|
||||
], selected: supplier_permitted.name
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it "shows inactive products that I supply" do
|
||||
product_supplied_inactive
|
||||
|
||||
visit spree.admin_products_path
|
||||
|
||||
expect(page).to have_selector row_containing_name(product_supplied_inactive.name)
|
||||
end
|
||||
|
||||
it "allows me to update a product" do
|
||||
visit spree.admin_products_path
|
||||
|
||||
within row_containing_name(product_supplied.name) do
|
||||
fill_in "Name", with: "Pommes"
|
||||
end
|
||||
click_button "Save changes"
|
||||
|
||||
expect(page).to have_content "Changes saved"
|
||||
expect(page).to have_selector row_containing_name("Pommes")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -370,7 +370,6 @@ RSpec.describe "As a consumer, I want to checkout my order" do
|
||||
Spree::PaymentMethod::Taler.create!(
|
||||
name: "Taler",
|
||||
environment: "test",
|
||||
preferred_backend_url: "https://taler.example.com/",
|
||||
distributors: [distributor]
|
||||
)
|
||||
end
|
||||
|
||||
@@ -6,13 +6,13 @@ RSpec.describe "registration/steps/_details.html.haml" do
|
||||
it "uses Google Maps when it is enabled" do
|
||||
allow(view).to receive_messages(using_google_maps?: true)
|
||||
|
||||
is_expected.to match /<ui-gmap-google-map center="map.center" zoom="map.zoom">/
|
||||
is_expected.to match /<ui-gmap-google-map center='map.center' zoom='map.zoom'>/
|
||||
end
|
||||
|
||||
it "uses OpenStreetMap when it is enabled" do
|
||||
ContentConfig.open_street_map_enabled = true
|
||||
allow(view).to receive_messages(using_google_maps?: false)
|
||||
|
||||
is_expected.to match /<div class="map-container--registration" id="open-street-map">/
|
||||
is_expected.to match /<div class='map-container--registration' id='open-street-map'>/
|
||||
end
|
||||
end
|
||||
|
||||
37
yarn.lock
37
yarn.lock
@@ -816,9 +816,9 @@
|
||||
"@babel/helper-plugin-utils" "^7.28.6"
|
||||
|
||||
"@babel/preset-env@^7.28.5":
|
||||
version "7.29.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.29.2.tgz#5a173f22c7d8df362af1c9fe31facd320de4a86c"
|
||||
integrity sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==
|
||||
version "7.29.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.29.0.tgz#c55db400c515a303662faaefd2d87e796efa08d0"
|
||||
integrity sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.29.0"
|
||||
"@babel/helper-compilation-targets" "^7.28.6"
|
||||
@@ -900,10 +900,15 @@
|
||||
"@babel/types" "^7.4.4"
|
||||
esutils "^2.0.2"
|
||||
|
||||
"@babel/runtime@^7.12.5", "@babel/runtime@^7.28.4", "@babel/runtime@^7.8.4":
|
||||
version "7.29.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.2.tgz#9a6e2d05f4b6692e1801cd4fb176ad823930ed5e"
|
||||
integrity sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==
|
||||
"@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4":
|
||||
version "7.28.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326"
|
||||
integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==
|
||||
|
||||
"@babel/runtime@^7.28.4":
|
||||
version "7.28.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b"
|
||||
integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==
|
||||
|
||||
"@babel/template@^7.28.6":
|
||||
version "7.28.6"
|
||||
@@ -5270,9 +5275,9 @@ node-addon-api@^7.0.0:
|
||||
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
|
||||
|
||||
node-forge@^1:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.4.0.tgz#1c7b7d8bdc2d078739f58287d589d903a11b2fc2"
|
||||
integrity sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.3.tgz#0ad80f6333b3a0045e827ac20b7f735f93716751"
|
||||
integrity sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==
|
||||
|
||||
node-int64@^0.4.0:
|
||||
version "0.4.0"
|
||||
@@ -5477,14 +5482,14 @@ picocolors@1.1.1, picocolors@^1.1.1:
|
||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601"
|
||||
integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
picomatch@^4.0.2, picomatch@^4.0.3:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589"
|
||||
integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042"
|
||||
integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
|
||||
|
||||
pify@^2.3.0:
|
||||
version "2.3.0"
|
||||
|
||||
Reference in New Issue
Block a user