Merge branch 'master' into include-ready-for-in-producer-notification

This commit is contained in:
Cillian O'Ruanaidh
2021-01-22 11:45:01 +00:00
133 changed files with 1463 additions and 1101 deletions

View File

@@ -33,7 +33,6 @@ Layout/LineLength:
- app/controllers/admin/product_import_controller.rb
- app/controllers/admin/schedules_controller.rb
- app/controllers/admin/subscriptions_controller.rb
- app/controllers/admin/variant_overrides_controller.rb
- app/controllers/api/enterprise_attachment_controller.rb
- app/controllers/api/product_images_controller.rb
- app/controllers/spree/paypal_controller_decorator.rb
@@ -71,7 +70,6 @@ Layout/LineLength:
- app/models/spree/variant.rb
- app/models/subscription.rb
- app/models/variant_override.rb
- app/models/variant_override_set.rb
- app/serializers/api/admin/subscription_line_item_serializer.rb
- app/services/cart_service.rb
- app/services/checkout/post_checkout_actions.rb
@@ -254,7 +252,6 @@ Layout/LineLength:
- spec/models/enterprise_relationship_spec.rb
- spec/models/enterprise_spec.rb
- spec/models/exchange_spec.rb
- spec/models/model_set_spec.rb
- spec/models/order_cycle_spec.rb
- spec/models/product_importer_spec.rb
- spec/models/product_import/reset_absent_spec.rb
@@ -385,7 +382,6 @@ Metrics/AbcSize:
- app/models/enterprise_group.rb
- app/models/enterprise.rb
- app/models/enterprise_relationship.rb
- app/models/model_set.rb
- app/models/product_import/entry_processor.rb
- app/models/product_import/entry_validator.rb
- app/models/product_import/product_importer.rb
@@ -413,6 +409,7 @@ Metrics/AbcSize:
- app/serializers/api/variant_serializer.rb
- app/services/cart_service.rb
- app/services/create_order_cycle.rb
- app/services/sets/model_set.rb
- app/services/order_cycle_form.rb
- app/services/order_syncer.rb
- app/services/variant_units/option_value_namer.rb
@@ -538,8 +535,8 @@ Metrics/CyclomaticComplexity:
- app/models/spree/product.rb
- app/models/spree/return_authorization.rb
- app/models/spree/zone.rb
- app/models/variant_override_set.rb
- app/services/cart_service.rb
- app/services/sets/variant_override_set.rb
- engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb
- engines/order_management/app/services/order_management/stock/estimator.rb
- lib/active_merchant/billing/gateways/stripe_payment_intents.rb

View File

@@ -398,7 +398,6 @@ Style/ClassAndModuleChildren:
- 'app/models/calculator/flat_percent_per_item.rb'
- 'app/models/spree/gateway/migs.rb'
- 'app/models/spree/gateway/pin.rb'
- 'app/models/spree/product_set.rb'
- 'app/models/tag_rule/discount_order.rb'
- 'app/models/tag_rule/filter_order_cycles.rb'
- 'app/models/tag_rule/filter_payment_methods.rb'
@@ -575,7 +574,6 @@ Style/FrozenStringLiteralComment:
- 'app/models/calculator/flat_percent_per_item.rb'
- 'app/models/calculator/weight.rb'
- 'app/models/column_preference.rb'
- 'app/models/column_preference_set.rb'
- 'app/models/concerns/address_display.rb'
- 'app/models/concerns/adjustment_scopes.rb'
- 'app/models/concerns/line_item_based_adjustment_handling.rb'
@@ -588,17 +586,13 @@ Style/FrozenStringLiteralComment:
- 'app/models/customer.rb'
- 'app/models/distributor_shipping_method.rb'
- 'app/models/enterprise_fee.rb'
- 'app/models/enterprise_fee_set.rb'
- 'app/models/enterprise_relationship_permission.rb'
- 'app/models/enterprise_role.rb'
- 'app/models/enterprise_set.rb'
- 'app/models/exchange.rb'
- 'app/models/exchange_fee.rb'
- 'app/models/exchange_variant.rb'
- 'app/models/inventory_item.rb'
- 'app/models/model_set.rb'
- 'app/models/order_cycle.rb'
- 'app/models/order_cycle_set.rb'
- 'app/models/preference_sections/footer_and_external_links_section.rb'
- 'app/models/preference_sections/group_signup_page_section.rb'
- 'app/models/preference_sections/header_section.rb'
@@ -623,7 +617,6 @@ Style/FrozenStringLiteralComment:
- 'app/models/spree/gateway/pin.rb'
- 'app/models/spree/gateway/stripe_connect.rb'
- 'app/models/spree/preferences/file_configuration.rb'
- 'app/models/spree/product_set.rb'
- 'app/models/spree/property.rb'
- 'app/models/spree/user.rb'
- 'app/models/stripe_account.rb'
@@ -636,7 +629,6 @@ Style/FrozenStringLiteralComment:
- 'app/models/tag_rule/filter_products.rb'
- 'app/models/tag_rule/filter_shipping_methods.rb'
- 'app/models/variant_override.rb'
- 'app/models/variant_override_set.rb'
- 'app/serializers/api/address_serializer.rb'
- 'app/serializers/api/adjustment_serializer.rb'
- 'app/serializers/api/cached_enterprise_serializer.rb'

View File

@@ -56,12 +56,12 @@ gem 'cancancan', '~> 1.7.0'
gem 'ffaker'
gem 'highline', '2.0.3' # Necessary for the install generator
gem 'json'
gem 'monetize', '~> 1.1'
gem 'monetize', '~> 1.10'
gem 'paranoia', '~> 2.4'
gem 'state_machines-activerecord'
gem 'stringex', '~> 2.8.5'
gem 'paypal-sdk-merchant', '1.106.1'
gem 'paypal-sdk-merchant', '1.117.2'
gem 'stripe'
gem 'devise'
@@ -119,6 +119,7 @@ gem 'mini_racer', '0.2.15'
gem 'uglifier', '>= 1.0.3'
gem 'angular_rails_csrf'
gem 'foundation-icons-sass-rails'
gem 'foundation-rails', '= 5.5.2.1'

View File

@@ -105,6 +105,8 @@ GEM
railties (>= 3.1)
sprockets (~> 2.0)
tilt
angular_rails_csrf (4.2.0)
railties (>= 3, < 7)
angularjs-file-upload-rails (2.4.1)
angularjs-rails (1.5.5)
arel (6.0.4)
@@ -450,9 +452,9 @@ GEM
mini_racer (0.2.15)
libv8 (> 7.3)
minitest (5.14.3)
monetize (1.9.4)
monetize (1.10.0)
money (~> 6.12)
money (6.13.8)
money (6.14.0)
i18n (>= 0.6.4, <= 2)
msgpack (1.3.3)
multi_json (1.15.0)
@@ -483,11 +485,11 @@ GEM
activerecord (>= 4.0, < 6.2)
parser (3.0.0.0)
ast (~> 2.4.1)
paypal-sdk-core (0.2.10)
paypal-sdk-core (0.3.4)
multi_json (~> 1.0)
xml-simple
paypal-sdk-merchant (1.106.1)
paypal-sdk-core (~> 0.2.3)
paypal-sdk-merchant (1.117.2)
paypal-sdk-core (~> 0.3.0)
pg (0.21.0)
power_assert (1.2.0)
pry (0.13.1)
@@ -700,7 +702,7 @@ GEM
unicorn (>= 4, < 6)
warden (1.2.7)
rack (>= 1.0)
webdrivers (4.4.2)
webdrivers (4.5.0)
nokogiri (~> 1.6)
rubyzip (>= 1.3.0)
selenium-webdriver (>= 3.0, < 4.0)
@@ -713,7 +715,7 @@ GEM
wicked_pdf (2.1.0)
activesupport
wkhtmltopdf-binary (0.12.5)
xml-simple (1.1.5)
xml-simple (1.1.8)
xmlrpc (0.3.0)
xpath (3.2.0)
nokogiri (~> 1.8)
@@ -732,6 +734,7 @@ DEPENDENCIES
acts_as_list (= 0.9.19)
andand
angular-rails-templates (~> 0.3.0)
angular_rails_csrf
angularjs-file-upload-rails (~> 2.4.1)
angularjs-rails (= 1.5.5)
atomic
@@ -783,14 +786,14 @@ DEPENDENCIES
knapsack
letter_opener (>= 1.4.1)
mini_racer (= 0.2.15)
monetize (~> 1.1)
monetize (~> 1.10)
oauth2 (~> 1.4.4)
ofn-qz!
order_management!
paper_trail (~> 10.3.1)
paperclip (~> 3.4.1)
paranoia (~> 2.4)
paypal-sdk-merchant (= 1.106.1)
paypal-sdk-merchant (= 1.117.2)
pg (~> 0.21.0)
pry
pry-byebug

View File

@@ -4,14 +4,6 @@ GIT
specs:
custom_error_message (1.1.1)
GIT
remote: https://github.com/openfoodfoundation/better_spree_paypal_express.git
revision: 1a477e9f7763297944cc99b6f4dd3d962aa963e9
branch: 2-1-0-stable
specs:
spree_paypal_express (2.0.3)
paypal-sdk-merchant (= 1.106.1)
GIT
remote: https://github.com/openfoodfoundation/ofn-qz.git
revision: 467f6ea1c44529c7c91cac4c8211bbd863588c0b
@@ -112,6 +104,8 @@ GEM
railties (>= 4.2, < 7)
sprockets (>= 3.0, < 5)
tilt
angular_rails_csrf (4.2.0)
railties (>= 3, < 7)
angularjs-file-upload-rails (2.4.1)
angularjs-rails (1.5.5)
arel (7.1.4)
@@ -130,7 +124,7 @@ GEM
concurrent-ruby (~> 1.0)
builder (3.2.4)
byebug (11.0.1)
cancan (1.6.10)
cancancan (1.7.1)
capybara (3.15.0)
addressable
mini_mime (>= 0.1.3)
@@ -205,11 +199,11 @@ GEM
erubis (2.7.0)
eventmachine (1.2.7)
execjs (2.7.0)
factory_bot (4.10.0)
activesupport (>= 3.0.0)
factory_bot_rails (4.10.0)
factory_bot (~> 4.10.0)
railties (>= 3.0.0)
factory_bot (5.2.0)
activesupport (>= 4.2.0)
factory_bot_rails (5.2.0)
factory_bot (~> 5.2.0)
railties (>= 4.2.0)
faraday (1.0.1)
multipart-post (>= 1.2, < 3)
ffaker (2.11.0)
@@ -222,7 +216,7 @@ GEM
foundation-rails (5.5.2.1)
railties (>= 3.1.0)
sass (>= 3.3.0, < 3.5)
fuubar (2.5.0)
fuubar (2.5.1)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
geocoder (1.6.4)
@@ -291,7 +285,10 @@ GEM
mini_racer (0.2.15)
libv8 (> 7.3)
minitest (5.14.2)
money (3.1.5)
monetize (1.9.4)
money (~> 6.12)
money (6.13.8)
i18n (>= 0.6.4, <= 2)
msgpack (1.3.3)
multi_json (1.15.0)
multi_xml (0.6.0)
@@ -307,10 +304,9 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
oj (3.10.8)
orm_adapter (0.5.0)
paper_trail (7.1.3)
activerecord (>= 4.0, < 5.2)
paper_trail (10.3.1)
activerecord (>= 4.2)
request_store (~> 1.1)
paperclip (3.4.2)
activemodel (>= 3.0.0)
@@ -335,6 +331,9 @@ GEM
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
pry-byebug (3.9.0)
byebug (~> 11.0)
pry (~> 0.13.0)
public_suffix (4.0.6)
rack (2.2.3)
rack-mini-profiler (2.0.2)
@@ -559,6 +558,7 @@ DEPENDENCIES
acts_as_list (= 0.9.19)
andand
angular-rails-templates (>= 0.3.0)
angular_rails_csrf
angularjs-file-upload-rails (~> 2.4.1)
angularjs-rails (= 1.5.5)
atomic
@@ -566,9 +566,9 @@ DEPENDENCIES
awesome_print
aws-sdk (= 1.67.0)
bugsnag
byebug (~> 11.0.0)
cancan (~> 1.6.10)
capybara (= 3.15)
byebug
cancancan (~> 1.7.0)
capybara
catalog!
coffee-rails (~> 4.2.2)
combine_pdf
@@ -586,12 +586,12 @@ DEPENDENCIES
devise-token_authenticatable
dfc_provider!
eventmachine (>= 1.2.3)
factory_bot_rails (= 4.10.0)
factory_bot_rails (= 5.2.0)
ffaker
figaro
foundation-icons-sass-rails
foundation-rails (= 5.5.2.1)
fuubar (~> 2.5.0)
fuubar (~> 2.5.1)
geocoder
gmaps4rails
haml
@@ -605,20 +605,21 @@ DEPENDENCIES
json
json_spec (~> 1.1.4)
jwt (~> 2.2)
kaminari (= 1.2.1)
kaminari (~> 1.2.1)
knapsack
letter_opener (>= 1.4.1)
mini_racer (= 0.2.15)
money (< 6.1.0)
monetize (~> 1.1)
oauth2 (~> 1.4.4)
ofn-qz!
oj
order_management!
paper_trail (~> 7.1.3)
paper_trail (~> 10.3.1)
paperclip (~> 3.4.1)
paranoia (~> 2.0)
paranoia (~> 2.4)
paypal-sdk-merchant (= 1.106.1)
pg (~> 0.21.0)
pry (>= 0.12.0)
pry
pry-byebug
rack-mini-profiler (< 3.0.0)
rack-rewrite
rack-ssl
@@ -641,7 +642,6 @@ DEPENDENCIES
selenium-webdriver
shoulda-matchers
simplecov
spree_paypal_express!
spring
spring-commands-rspec
state_machines-activerecord
@@ -662,7 +662,7 @@ DEPENDENCIES
wkhtmltopdf-binary
RUBY VERSION
ruby 2.3.7p456
ruby 2.4.4p296
BUNDLED WITH
1.17.3

View File

@@ -10,5 +10,4 @@ angular.module("ofn.admin", [
"admin.taxons",
"infinite-scroll"
]).config ($httpProvider) ->
$httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content")
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"

View File

@@ -1,2 +1,2 @@
angular.module("admin.indexUtils", ['admin.resources', 'ngSanitize', 'templates', 'admin.utils']).config ($httpProvider) ->
$httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content");
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"

View File

@@ -1,8 +1,4 @@
angular.module('admin.orderCycles', ['ngTagsInput', 'admin.indexUtils', 'admin.enterprises'])
.config ($httpProvider) ->
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
.directive 'datetimepicker', ($timeout, $parse) ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->

View File

@@ -1,3 +1,2 @@
angular.module("admin.productImport", ["ngResource"]).config ($httpProvider) ->
$httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content")
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"

View File

@@ -40,7 +40,13 @@ $(document).ready(function() {
var variant_id = save.data('variant-id');
var quantity = parseInt(save.parents('tr').find('input.line_item_quantity').val());
var maxQuantity = parseInt(save.parents('tr').find('input.line_item_quantity').attr("max"));
if (quantity > maxQuantity) {
quantity = maxQuantity;
save.parents('tr').find('input.line_item_quantity').val(maxQuantity);
alert('<%= I18n.t("js.admin.orders.quantity_adjusted") %>');
}
toggleItemEdit();
adjustItems(shipment_number, variant_id, quantity);
@@ -77,7 +83,9 @@ adjustItems = function(shipment_number, variant_id, quantity){
}
url += '.json';
if(new_quantity!=0){
if (new_quantity == 0) {
alert('<%= I18n.t("js.admin.orders.quantity_unchanged") %>');
} else {
$.ajax({
type: "PUT",
url: Spree.url(url),

View File

@@ -1 +1,2 @@
angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider) ->
angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider) ->
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"

View File

@@ -12,7 +12,6 @@ window.Darkswarm = angular.module("Darkswarm", [
'angularFileUpload',
'angularSlideables'
]).config ($httpProvider, $tooltipProvider, $locationProvider, $anchorScrollProvider) ->
$httpProvider.defaults.headers['common']['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
$httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest'
$httpProvider.defaults.headers.common.Accept = "application/json, text/javascript, */*"

View File

@@ -16,6 +16,7 @@
}
.icon-email:before { @extend .icon-envelope:before }
.icon-resend_authorization_email:before { @extend .icon-envelope:before }
.icon-resume:before { @extend .icon-refresh:before }
.icon-cancel:before,

View File

@@ -29,7 +29,8 @@ module Admin
collection_hash = Hash[permitted_params[:column_preferences].
each_with_index.map { |cp, i| [i, cp] }]
collection_hash.select!{ |_i, cp| cp[:action_name] == permitted_params[:action_name] }
@cp_set = ColumnPreferenceSet.new @column_preferences, collection_attributes: collection_hash
@cp_set = Sets::ColumnPreferenceSet.new(@column_preferences,
collection_attributes: collection_hash)
end
def collection

View File

@@ -27,7 +27,7 @@ module Admin
end
def bulk_update
@enterprise_fee_set = EnterpriseFeeSet.new(enterprise_fee_bulk_params)
@enterprise_fee_set = Sets::EnterpriseFeeSet.new(enterprise_fee_bulk_params)
if @enterprise_fee_set.save
redirect_to redirect_path, notice: I18n.t(:enterprise_fees_update_notice)
@@ -40,7 +40,7 @@ module Admin
private
def load_enterprise_fee_set
@enterprise_fee_set = EnterpriseFeeSet.new collection: collection
@enterprise_fee_set = Sets::EnterpriseFeeSet.new collection: collection
end
def load_data
@@ -80,7 +80,7 @@ module Admin
end
def enterprise_fee_bulk_params
params.require(:enterprise_fee_set).permit(
params.require(:sets_enterprise_fee_set).permit(
collection_attributes: [
:id, :enterprise_id, :fee_type, :name, :tax_category_id,
:inherits_tax_category, :calculator_type,

View File

@@ -81,7 +81,7 @@ module Admin
end
def bulk_update
@enterprise_set = EnterpriseSet.new(collection, bulk_params)
@enterprise_set = Sets::EnterpriseSet.new(collection, bulk_params)
if @enterprise_set.save
flash[:success] = I18n.t(:enterprise_bulk_update_success_notice)
@@ -129,7 +129,7 @@ module Admin
private
def load_enterprise_set
@enterprise_set = EnterpriseSet.new(collection) if spree_current_user.admin?
@enterprise_set = Sets::EnterpriseSet.new(collection) if spree_current_user.admin?
end
def load_countries
@@ -235,7 +235,7 @@ module Admin
def check_can_change_bulk_sells
unless spree_current_user.admin?
params[:enterprise_set][:collection_attributes].each do |_i, enterprise_params|
params[:sets_enterprise_set][:collection_attributes].each do |_i, enterprise_params|
unless spree_current_user == Enterprise.find_by(id: enterprise_params[:id]).owner
enterprise_params.delete :sells
end
@@ -269,7 +269,7 @@ module Admin
def check_can_change_bulk_owner
unless spree_current_user.admin?
params[:enterprise_set][:collection_attributes].each do |_i, enterprise_params|
params[:sets_enterprise_set][:collection_attributes].each do |_i, enterprise_params|
enterprise_params.delete :owner_id
end
end
@@ -321,7 +321,7 @@ module Admin
end
def bulk_params
params.require(:enterprise_set).permit(
params.require(:sets_enterprise_set).permit(
collection_attributes: PermittedAttributes::Enterprise.attributes
)
end

View File

@@ -223,7 +223,7 @@ module Admin
end
def order_cycle_set
@order_cycle_set ||= OrderCycleSet.new(@order_cycles, order_cycle_bulk_params)
@order_cycle_set ||= Sets::OrderCycleSet.new(@order_cycles, order_cycle_bulk_params)
end
def require_order_cycle_set_params

View File

@@ -60,14 +60,17 @@ module Admin
for_hubs(editable_enterprises.collect(&:id))
options = [{ id: '0', name: 'All' }]
import_dates.collect(&:import_date).map { |i| options.push(id: i.to_date, name: i.to_date.to_formatted_s(:long)) }
import_dates.collect(&:import_date).map { |i|
options.push(id: i.to_date, name: i.to_date.to_formatted_s(:long))
}
options
end
def load_collection
collection_hash = Hash[variant_overrides_params.each_with_index.map { |vo, i| [i, vo] }]
@vo_set = VariantOverrideSet.new @variant_overrides, collection_attributes: collection_hash
@vo_set = Sets::VariantOverrideSet.new(@variant_overrides,
collection_attributes: collection_hash)
end
def collection
@@ -82,8 +85,8 @@ module Admin
[:index, :bulk_update, :bulk_reset]
end
# This has been pulled from ModelSet as it is useful for compiling a list of errors on any generic collection (not necessarily a ModelSet)
# Could be pulled down into a lower level controller if it is useful in other high level controllers
# This method is also present in ModelSet
# This is useful for compiling a list of errors on any generic collection
def collection_errors
errors = ActiveModel::Errors.new self
full_messages = @collection.map { |element| element.errors.full_messages }.flatten

View File

@@ -1,8 +1,13 @@
# frozen_string_literal: true
require "application_responder"
require 'open_food_network/referer_parser'
require_dependency 'spree/authentication_helpers'
require "application_responder"
require 'cancan'
require 'spree/core/controller_helpers/auth'
require 'spree/core/controller_helpers/respond_with'
require 'spree/core/controller_helpers/ssl'
require 'spree/core/controller_helpers/common'
require 'open_food_network/referer_parser'
class ApplicationController < ActionController::Base
self.responder = ApplicationResponder
@@ -10,6 +15,11 @@ class ApplicationController < ActionController::Base
protect_from_forgery
include Spree::Core::ControllerHelpers::Auth
include Spree::Core::ControllerHelpers::RespondWith
include Spree::Core::ControllerHelpers::SSL
include Spree::Core::ControllerHelpers::Common
prepend_before_action :restrict_iframes
before_action :set_cache_headers # prevent cart emptying via cache when using back button #1213
@@ -22,6 +32,8 @@ class ApplicationController < ActionController::Base
raise ActiveModel::ForbiddenAttributesError, params.to_s
end
respond_to :html
def redirect_to(options = {}, response_status = {})
::Rails.logger.error("Redirected by #{begin
caller(1).first
@@ -150,3 +162,5 @@ class ApplicationController < ActionController::Base
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
end
end
require 'spree/i18n/initializer'

View File

@@ -1,17 +1,16 @@
require 'spree/core/controller_helpers/auth'
require 'spree/core/controller_helpers/common'
# frozen_string_literal: true
require 'spree/core/controller_helpers/order'
require 'spree/core/controller_helpers/respond_with'
require 'spree/core/controller_helpers/ssl'
require 'open_food_network/tag_rule_applicator'
class BaseController < ApplicationController
include Spree::Core::ControllerHelpers::Auth
include Spree::Core::ControllerHelpers::Common
layout 'darkswarm'
include Spree::Core::ControllerHelpers::Order
include Spree::Core::ControllerHelpers::RespondWith
include Spree::Core::ControllerHelpers::SSL
include I18nHelper
include EnterprisesHelper
include OrderCyclesHelper
helper 'spree/base'

View File

@@ -2,7 +2,7 @@
require 'open_food_network/address_finder'
class CheckoutController < Spree::StoreController
class CheckoutController < ::BaseController
layout 'darkswarm'
include OrderStockCheck

View File

@@ -0,0 +1,29 @@
# frozen_string_literal: true
class PaymentsController < BaseController
ssl_required :redirect_to_authorize
respond_to :html
prepend_before_action :require_logged_in, only: :redirect_to_authorize
def redirect_to_authorize
@payment = Spree::Payment.find(params[:id])
authorize! :show, @payment.order
if url = @payment.cvv_response_message
redirect_to url
else
redirect_to order_url(@payment.order)
end
end
private
def require_logged_in
return if session[:access_token] || params[:token] || spree_current_user
flash[:error] = I18n.t("spree.orders.edit.login_to_view_order")
redirect_to main_app.root_path(anchor: "login?after_login=#{request.env['PATH_INFO']}")
end
end

View File

@@ -0,0 +1,29 @@
# frozen_string_literal: true
class PaymentsController < BaseController
ssl_required :redirect_to_authorize
respond_to :html
prepend_before_action :require_logged_in, only: :redirect_to_authorize
def redirect_to_authorize
@payment = Spree::Payment.find(params[:id])
authorize! :show, @payment.order
if url = @payment.cvv_response_message
redirect_to url
else
redirect_to order_url(@payment.order)
end
end
private
def require_logged_in
return if session[:access_token] || params[:token] || spree_current_user
flash[:error] = I18n.t("spree.orders.edit.login_to_view_order")
redirect_to main_app.root_path(anchor: "login?after_login=#{request.env['PATH_INFO']}")
end
end

View File

@@ -1,6 +1,6 @@
module Spree
module Admin
class BaseController < Spree::BaseController
class BaseController < ApplicationController
ssl_required
helper 'spree/admin/navigation'

View File

@@ -56,7 +56,7 @@ module Spree
# Because we have a transition method also called void, we do this to avoid conflicts.
event = "void_transaction" if event == "void"
if @payment.public_send("#{event}!")
if allowed_events.include?(event) && @payment.public_send("#{event}!")
flash[:success] = t(:payment_updated)
else
flash[:error] = t(:cannot_perform_operation)
@@ -116,7 +116,7 @@ module Spree
# Only show payments for the order's distributor
@payment_methods = PaymentMethod.
available(:back_end).
select{ |pm| pm.has_distributor? @order.distributor }
for_distributor(@order.distributor)
@payment_method = if @payment&.payment_method
@payment.payment_method
@@ -153,10 +153,18 @@ module Spree
def authorize_stripe_sca_payment
return unless @payment.payment_method.class == Spree::Gateway::StripeSCA
@payment.authorize!
return if @payment.pending? && @payment.cvv_response_message.nil?
@payment.authorize!(spree.order_path(@payment.order, only_path: false))
raise Spree::Core::GatewayError, I18n.t('authorization_failure')
raise Spree::Core::GatewayError, I18n.t('authorization_failure') unless @payment.pending?
return unless @payment.cvv_response_message.present?
PaymentMailer.authorize_payment(@payment).deliver_later
raise Spree::Core::GatewayError, I18n.t('action_required')
end
def allowed_events
%w{capture void_transaction credit refund resend_authorization_email}
end
end
end

View File

@@ -163,7 +163,7 @@ module Spree
def product_set_from_params(_params)
collection_hash = Hash[products_params.each_with_index.map { |p, i| [i, p] }]
Spree::ProductSet.new(collection_attributes: collection_hash)
Sets::ProductSet.new(collection_attributes: collection_hash)
end
def products_params

View File

@@ -1,20 +0,0 @@
# frozen_string_literal: true
require 'cancan'
require 'spree/core/controller_helpers/auth'
require 'spree/core/controller_helpers/respond_with'
require 'spree/core/controller_helpers/ssl'
require 'spree/core/controller_helpers/common'
module Spree
class BaseController < ApplicationController
include Spree::Core::ControllerHelpers::Auth
include Spree::Core::ControllerHelpers::RespondWith
include Spree::Core::ControllerHelpers::SSL
include Spree::Core::ControllerHelpers::Common
respond_to :html
end
end
require 'spree/i18n/initializer'

View File

@@ -7,7 +7,7 @@
# to CheckoutController directly in the routes
# with a slash like "to: '/checkout#edit'", but it does not work in this case.
module Spree
class CheckoutController < Spree::StoreController
class CheckoutController < ::BaseController
def edit
flash.keep
redirect_to main_app.checkout_path

View File

@@ -1,5 +1,5 @@
module Spree
class OrdersController < Spree::StoreController
class OrdersController < ::BaseController
include OrderCyclesHelper
include Rails.application.routes.url_helpers

View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Spree
class PaypalController < StoreController
class PaypalController < ::BaseController
ssl_allowed
include OrderStockCheck

View File

@@ -1,14 +0,0 @@
# frozen_string_literal: true
require 'spree/core/controller_helpers/order'
module Spree
class StoreController < Spree::BaseController
layout 'darkswarm'
include Spree::Core::ControllerHelpers::Order
include I18nHelper
before_action :set_locale
end
end

View File

@@ -1,5 +1,5 @@
module Spree
class UsersController < Spree::StoreController
class UsersController < ::BaseController
layout 'darkswarm'
ssl_required
skip_before_action :set_current_order, only: :show

View File

@@ -19,7 +19,7 @@ module CheckoutHelper
adjustments.reject! { |a| a.originator_type == 'EnterpriseFee' && a.source_type != 'Spree::LineItem' }
unless exclude.include? :admin_and_handling
adjustments << Spree::Adjustment.new(
label: I18n.t(:orders_form_admin), amount: enterprise_fee_adjustments.map(&:amount).sum
label: I18n.t(:orders_form_admin), amount: enterprise_fee_adjustments.sum(&:amount)
)
end
@@ -28,7 +28,7 @@ module CheckoutHelper
def display_checkout_admin_and_handling_adjustments_total_for(order)
adjustments = order.adjustments.eligible.where('originator_type = ? AND source_type != ? ', 'EnterpriseFee', 'Spree::LineItem')
Spree::Money.new adjustments.map(&:amount).sum, currency: order.currency
Spree::Money.new adjustments.sum(:amount), currency: order.currency
end
def checkout_line_item_adjustments(order)
@@ -36,7 +36,7 @@ module CheckoutHelper
end
def checkout_subtotal(order)
order.item_total + checkout_line_item_adjustments(order).map(&:amount).sum
order.item_total + checkout_line_item_adjustments(order).sum(:amount)
end
def display_checkout_subtotal(order)

View File

@@ -1,10 +1,10 @@
module EnterpriseFeesHelper
def angular_name(method)
"enterprise_fee_set[collection_attributes][{{ $index }}][#{method}]"
"sets_enterprise_fee_set[collection_attributes][{{ $index }}][#{method}]"
end
def angular_id(method)
"enterprise_fee_set_collection_attributes_{{ $index }}_#{method}"
"sets_enterprise_fee_set_collection_attributes_{{ $index }}_#{method}"
end
def enterprise_fee_type_options

View File

@@ -0,0 +1,14 @@
# frozen_string_literal: true
class PaymentMailer < Spree::BaseMailer
include I18nHelper
def authorize_payment(payment)
@payment = payment
subject = I18n.t('spree.payment_mailer.authorize_payment.subject',
distributor: @payment.order.distributor.name)
mail(to: payment.order.user.email,
from: from_address,
subject: subject)
end
end

View File

@@ -1,5 +0,0 @@
class ColumnPreferenceSet < ModelSet
def initialize(collection, attributes = {})
super(ColumnPreference, collection, attributes, nil, nil )
end
end

View File

@@ -1,6 +1,8 @@
class EnterpriseFee < ActiveRecord::Base
include Spree::Core::CalculatedAdjustments
acts_as_paranoid
belongs_to :enterprise
belongs_to :tax_category, class_name: 'Spree::TaxCategory', foreign_key: 'tax_category_id'

View File

@@ -1,7 +0,0 @@
class EnterpriseFeeSet < ModelSet
def initialize(attributes = {})
super(EnterpriseFee, EnterpriseFee.all,
attributes,
proc { |attrs| attrs[:name].blank? })
end
end

View File

@@ -1,5 +0,0 @@
class EnterpriseSet < ModelSet
def initialize(collection, attributes = {})
super(Enterprise, collection, attributes)
end
end

View File

@@ -1,65 +0,0 @@
# Tableless model to handle updating multiple models at once from a single form
class ModelSet
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :collection
def initialize(klass, collection, attributes = {}, reject_if = nil, delete_if = nil)
@klass, @collection, @reject_if, @delete_if = klass, collection, reject_if, delete_if
# Set here first, to ensure that we apply collection_attributes to the right collection
@collection = attributes[:collection] if attributes[:collection]
attributes.each do |name, value|
public_send("#{name}=", value)
end
end
def collection_attributes=(collection_attributes)
collection_attributes.each do |_k, attributes|
# attributes == {:id => 123, :next_collection_at => '...'}
found_element = @collection.detect do |element|
element.id.to_s == attributes[:id].to_s && !element.id.nil?
end
if found_element.nil?
@collection << @klass.new(attributes) unless @reject_if.andand.call(attributes)
else
found_element.assign_attributes(attributes.except(:id))
end
end
end
def errors
errors = ActiveModel::Errors.new self
full_messages = @collection
.map { |model| model.errors.full_messages }
.flatten
full_messages.each { |message| errors.add(:base, message) }
errors
end
def save
collection_to_delete.each(&:destroy)
collection_to_keep.all?(&:save)
end
def collection_to_delete
# Remove all elements to be deleted from collection and return them
# Allows us to render @model_set.collection without deleted elements
deleted = []
@collection = collection.to_a
collection.delete_if { |e| deleted << e if @delete_if.andand.call(e.attributes) }
deleted
end
def collection_to_keep
collection.reject { |e| @delete_if.andand.call(e.attributes) }
end
def persisted?
false
end
end

View File

@@ -1,5 +0,0 @@
class OrderCycleSet < ModelSet
def initialize(collection, attributes = {})
super(OrderCycle, collection, attributes)
end
end

View File

@@ -39,6 +39,8 @@ module Spree
belongs_to :adjustable, polymorphic: true
belongs_to :source, polymorphic: true
belongs_to :originator, polymorphic: true
belongs_to :order, class_name: "Spree::Order"
belongs_to :tax_rate, -> { where spree_adjustments: { originator_type: 'Spree::TaxRate' } },
foreign_key: 'originator_id'
@@ -160,6 +162,13 @@ module Spree
result
end
# Allow accessing soft-deleted originator objects
def originator
return if originator_type.blank?
originator_type.constantize.unscoped { super }
end
private
def update_adjustable

View File

@@ -98,7 +98,6 @@ module Spree
preference :intercept_email, :string, default: nil
# Default smtp settings
preference :override_actionmailer_config, :boolean, default: true
preference :mail_host, :string, default: 'localhost'
preference :mail_domain, :string, default: 'localhost'
preference :mail_port, :integer, default: 25

View File

@@ -79,11 +79,17 @@ module Spree
end
def actions
%w{capture void credit}
%w{capture void credit resend_authorization_email}
end
def can_resend_authorization_email?(payment)
payment.pending? && payment.cvv_response_message.present?
end
# Indicates whether its possible to capture the payment
def can_capture?(payment)
return false if payment.cvv_response_message.present?
payment.pending? || payment.checkout?
end

View File

@@ -45,6 +45,11 @@ module Spree
failed_activemerchant_billing_response(e.message)
end
def capture(money, payment_intent_id, gateway_options)
options = basic_options(gateway_options)
provider.capture(money, payment_intent_id, options)
end
# NOTE: the name of this method is determined by Spree::Payment::Processing
def charge_offline(money, creditcard, gateway_options)
customer, payment_method =
@@ -110,7 +115,7 @@ module Spree
def options_for_authorize(money, creditcard, gateway_options)
options = basic_options(gateway_options)
options[:return_url] = full_checkout_path
options[:return_url] = gateway_options[:return_url] || full_checkout_path
customer_id, payment_method_id =
Stripe::CreditCardCloner.new(creditcard, stripe_account_id).find_or_clone

View File

@@ -96,9 +96,6 @@ module Spree
before_save :update_shipping_fees!, if: :complete?
before_save :update_payment_fees!, if: :complete?
class_attribute :update_hooks
self.update_hooks = Set.new
# -- Scopes
scope :managed_by, lambda { |user|
if user.has_spree_role?('admin')
@@ -157,12 +154,6 @@ module Spree
where(completed_at: nil)
end
# Use this method in other gems that wish to register their own custom logic
# that should be called after Order#update
def self.register_update_hook(hook)
update_hooks.add(hook)
end
# For compatiblity with Calculator::PriceSack
def amount
line_items.inject(0.0) { |sum, li| sum + li.amount }
@@ -396,11 +387,11 @@ module Spree
end
def ship_total
adjustments.shipping.map(&:amount).sum
adjustments.shipping.sum(:amount)
end
def tax_total
adjustments.tax.map(&:amount).sum
adjustments.tax.sum(:amount)
end
# Creates new tax charges if there are any applicable rates. If prices already
@@ -450,7 +441,6 @@ module Spree
updater.update_shipment_state
updater.before_save_hook
save
updater.run_hooks
deliver_order_confirmation_email
@@ -765,13 +755,6 @@ module Spree
address
end
# Update attributes of a record in the database without callbacks, validations etc.
# This was originally an extension to ActiveRecord in Spree but only used for Spree::Order
def update_attributes_without_callbacks(attributes)
assign_attributes(attributes)
Spree::Order.where(id: id).update_all(attributes)
end
private
def process_each_payment

View File

@@ -121,6 +121,12 @@ module Spree
actions
end
def resend_authorization_email!
return unless cvv_response_message.present?
PaymentMailer.authorize_payment(self).deliver_later
end
def payment_source
res = source.is_a?(Payment) ? source.source : source
res || payment_method

View File

@@ -5,6 +5,7 @@ module Spree
module Processing
def process!
return unless validate!
return if cvv_response_message.present?
if payment_method.auto_capture?
purchase!
@@ -15,6 +16,7 @@ module Spree
def process_offline!
return unless validate!
return if cvv_response_message.present?
if payment_method.auto_capture?
charge_offline!
@@ -23,9 +25,9 @@ module Spree
end
end
def authorize!
def authorize!(return_url = nil)
started_processing!
gateway_action(source, :authorize, :pend)
gateway_action(source, :authorize, :pend, return_url: return_url)
end
def purchase!
@@ -44,19 +46,7 @@ module Spree
started_processing!
protect_from_connection_error do
check_environment
response = if payment_method.payment_profiles_supported?
# Gateways supporting payment profiles will need access to credit
# card object because this stores the payment profile information
# so supply the authorization itself as well as the credit card,
# rather than just the authorization code
payment_method.capture(self, source, gateway_options)
else
# Standard ActiveMerchant capture usage
payment_method.capture(money.money.cents,
response_code,
gateway_options)
end
response = payment_method.capture(money.money.cents, response_code, gateway_options)
handle_response(response, :complete, :failure)
end
@@ -222,7 +212,7 @@ module Spree
refund_amount.to_f
end
def gateway_action(source, action, success_state)
def gateway_action(source, action, success_state, options = {})
protect_from_connection_error do
check_environment
@@ -230,7 +220,7 @@ module Spree
action,
(amount * 100).round,
source,
gateway_options
gateway_options.merge(options)
)
handle_response(response, success_state, :failure)
end

View File

@@ -1,138 +0,0 @@
class Spree::ProductSet < ModelSet
def initialize(attributes = {})
super(Spree::Product, [], attributes)
end
def save
@collection_hash.each_value.all? do |product_attributes|
update_product_attributes(product_attributes)
end
end
def collection_attributes=(attributes)
@collection = Spree::Product
.where(id: attributes.each_value.map { |product| product[:id] })
@collection_hash = attributes
end
private
# A separate method of updating products was required due to an issue with
# the way Rails' assign_attributes and update behave when
# delegated attributes of a nested object are updated via the parent object
# (ie. price of variants). Updating such attributes by themselves did not
# work using:
#
# product.update(variants_attributes: [{ id: y, price: xx.x }])
#
# and so an explicit call to update on each individual variant was
# required. ie:
#
# variant.update( { price: xx.x } )
#
def update_product_attributes(attributes)
split_taxon_ids!(attributes)
product = find_model(@collection, attributes[:id])
return if product.nil?
update_product(product, attributes)
end
def split_taxon_ids!(attributes)
attributes[:taxon_ids] = attributes[:taxon_ids].split(',') if attributes[:taxon_ids].present?
end
def update_product(product, attributes)
original_supplier = product.supplier_id
return false unless update_product_only_attributes(product, attributes)
ExchangeVariantDeleter.new.delete(product) if original_supplier != product.supplier_id
update_product_variants(product, attributes) &&
update_product_master(product, attributes)
end
def update_product_only_attributes(product, attributes)
variant_related_attrs = [:id, :variants_attributes, :master_attributes]
product_related_attrs = attributes.except(*variant_related_attrs)
return true if product_related_attrs.blank?
product.assign_attributes(product_related_attrs)
validate_presence_of_unit_value_in_product(product)
product.errors.empty? && product.save
end
def validate_presence_of_unit_value_in_product(product)
product.variants.each do |variant|
validate_presence_of_unit_value_in_variant(product, variant)
end
end
def validate_presence_of_unit_value_in_variant(product, variant)
return unless %w(weight volume).include?(product.variant_unit)
return if variant.unit_value.present?
product.errors.add(:unit_value, "can't be blank")
end
def update_product_variants(product, attributes)
return true unless attributes[:variants_attributes]
update_variants_attributes(product, attributes[:variants_attributes])
end
def update_product_master(product, attributes)
return true unless attributes[:master_attributes]
create_or_update_variant(product, attributes[:master_attributes])
end
def update_variants_attributes(product, variants_attributes)
variants_attributes.each do |attributes|
create_or_update_variant(product, attributes)
end
end
def create_or_update_variant(product, variant_attributes)
variant = find_model(product.variants_including_master, variant_attributes[:id])
if variant.present?
variant.update(variant_attributes.except(:id))
else
create_variant(product, variant_attributes)
end
end
def create_variant(product, variant_attributes)
on_hand = variant_attributes.delete(:on_hand)
on_demand = variant_attributes.delete(:on_demand)
variant = product.variants.create(variant_attributes)
begin
variant.on_demand = on_demand if on_demand.present?
variant.on_hand = on_hand.to_i if on_hand.present?
rescue StandardError => e
notify_bugsnag(e, product, variant, variant_attributes)
raise e
end
end
def notify_bugsnag(error, product, variant, variant_attributes)
Bugsnag.notify(error) do |report|
report.add_tab(:product, product.attributes)
report.add_tab(:product_error, product.errors.first) unless product.valid?
report.add_tab(:variant_attributes, variant_attributes)
report.add_tab(:variant, variant.attributes)
report.add_tab(:variant_error, variant.errors.first) unless variant.valid?
end
end
def find_model(collection, model_id)
collection.find do |model|
model.id.to_s == model_id.to_s && model.persisted?
end
end
end

View File

@@ -249,8 +249,11 @@ module Spree
def to_package
package = OrderManagement::Stock::Package.new(stock_location, order)
inventory_units.includes(:variant).each do |inventory_unit|
package.add inventory_unit.variant, 1, inventory_unit.state_name
grouped_inventory_units = inventory_units.includes(:variant).group_by do |iu|
[iu.variant, iu.state_name]
end
grouped_inventory_units.each do |(variant, state_name), inventory_units|
package.add variant, inventory_units.count, state_name
end
package
end

View File

@@ -71,6 +71,7 @@ module Spree
amount: amount,
source: order,
originator: self,
order: order,
state: "closed",
label: label
)

View File

@@ -1,30 +0,0 @@
class VariantOverrideSet < ModelSet
def initialize(collection, attributes = {})
super(VariantOverride, collection, attributes, nil, proc { |attrs, tag_list| deletable?(attrs, tag_list) } )
end
private
def deletable?(attrs, tag_list)
attrs['price'].blank? &&
attrs['count_on_hand'].blank? &&
attrs['default_stock'].blank? &&
attrs['resettable'].blank? &&
attrs['sku'].nil? &&
attrs['on_demand'].nil? &&
tag_list.empty?
end
# Override of ModelSet method to allow us to check presence of a tag_list (which is not an attribute)
# This method will delete VariantOverrides that have no values (see deletable? above)
# If the user sets all values to nil in the UI the VO will be deleted from the DB
def collection_to_delete
deleted = []
collection.delete_if { |e| deleted << e if @delete_if.andand.call(e.attributes, e.tag_list) }
deleted
end
def collection_to_keep
collection.reject { |e| @delete_if.andand.call(e.attributes, e.tag_list) }
end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
module Sets
class ColumnPreferenceSet < ModelSet
def initialize(collection, attributes = {})
super(ColumnPreference, collection, attributes, nil, nil )
end
end
end

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
module Sets
class EnterpriseFeeSet < ModelSet
def initialize(attributes = {})
super(EnterpriseFee, EnterpriseFee.all,
attributes,
proc { |attrs| attrs[:name].blank? })
end
end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
module Sets
class EnterpriseSet < ModelSet
def initialize(collection, attributes = {})
super(Enterprise, collection, attributes)
end
end
end

View File

@@ -0,0 +1,69 @@
# frozen_string_literal: true
# Tableless model to handle updating multiple models at once from a single form
module Sets
class ModelSet
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :collection
def initialize(klass, collection, attributes = {}, reject_if = nil, delete_if = nil)
@klass, @collection, @reject_if, @delete_if = klass, collection, reject_if, delete_if
# Set here first, to ensure that we apply collection_attributes to the right collection
@collection = attributes[:collection] if attributes[:collection]
attributes.each do |name, value|
public_send("#{name}=", value)
end
end
def collection_attributes=(collection_attributes)
collection_attributes.each do |_k, attributes|
# attributes == {:id => 123, :next_collection_at => '...'}
found_element = @collection.detect do |element|
element.id.to_s == attributes[:id].to_s && !element.id.nil?
end
if found_element.nil?
@collection << @klass.new(attributes) unless @reject_if.andand.call(attributes)
else
found_element.assign_attributes(attributes.except(:id))
end
end
end
def errors
errors = ActiveModel::Errors.new self
full_messages = @collection
.map { |model| model.errors.full_messages }
.flatten
full_messages.each { |message| errors.add(:base, message) }
errors
end
def save
collection_to_delete.each(&:destroy)
collection_to_keep.all?(&:save)
end
def collection_to_delete
# Remove all elements to be deleted from collection and return them
# Allows us to render @model_set.collection without deleted elements
deleted = []
@collection = collection.to_a
collection.delete_if { |e| deleted << e if @delete_if.andand.call(e.attributes) }
deleted
end
def collection_to_keep
collection.reject { |e| @delete_if.andand.call(e.attributes) }
end
def persisted?
false
end
end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
module Sets
class OrderCycleSet < ModelSet
def initialize(collection, attributes = {})
super(OrderCycle, collection, attributes)
end
end
end

View File

@@ -0,0 +1,142 @@
# frozen_string_literal: true
module Sets
class ProductSet < ModelSet
def initialize(attributes = {})
super(Spree::Product, [], attributes)
end
def save
@collection_hash.each_value.all? do |product_attributes|
update_product_attributes(product_attributes)
end
end
def collection_attributes=(attributes)
@collection = Spree::Product
.where(id: attributes.each_value.map { |product| product[:id] })
@collection_hash = attributes
end
private
# A separate method of updating products was required due to an issue with
# the way Rails' assign_attributes and update behave when
# delegated attributes of a nested object are updated via the parent object
# (ie. price of variants). Updating such attributes by themselves did not
# work using:
#
# product.update(variants_attributes: [{ id: y, price: xx.x }])
#
# and so an explicit call to update on each individual variant was
# required. ie:
#
# variant.update( { price: xx.x } )
#
def update_product_attributes(attributes)
split_taxon_ids!(attributes)
product = find_model(@collection, attributes[:id])
return if product.nil?
update_product(product, attributes)
end
def split_taxon_ids!(attributes)
attributes[:taxon_ids] = attributes[:taxon_ids].split(',') if attributes[:taxon_ids].present?
end
def update_product(product, attributes)
original_supplier = product.supplier_id
return false unless update_product_only_attributes(product, attributes)
ExchangeVariantDeleter.new.delete(product) if original_supplier != product.supplier_id
update_product_variants(product, attributes) &&
update_product_master(product, attributes)
end
def update_product_only_attributes(product, attributes)
variant_related_attrs = [:id, :variants_attributes, :master_attributes]
product_related_attrs = attributes.except(*variant_related_attrs)
return true if product_related_attrs.blank?
product.assign_attributes(product_related_attrs)
validate_presence_of_unit_value_in_product(product)
product.errors.empty? && product.save
end
def validate_presence_of_unit_value_in_product(product)
product.variants.each do |variant|
validate_presence_of_unit_value_in_variant(product, variant)
end
end
def validate_presence_of_unit_value_in_variant(product, variant)
return unless %w(weight volume).include?(product.variant_unit)
return if variant.unit_value.present?
product.errors.add(:unit_value, "can't be blank")
end
def update_product_variants(product, attributes)
return true unless attributes[:variants_attributes]
update_variants_attributes(product, attributes[:variants_attributes])
end
def update_product_master(product, attributes)
return true unless attributes[:master_attributes]
create_or_update_variant(product, attributes[:master_attributes])
end
def update_variants_attributes(product, variants_attributes)
variants_attributes.each do |attributes|
create_or_update_variant(product, attributes)
end
end
def create_or_update_variant(product, variant_attributes)
variant = find_model(product.variants_including_master, variant_attributes[:id])
if variant.present?
variant.update(variant_attributes.except(:id))
else
create_variant(product, variant_attributes)
end
end
def create_variant(product, variant_attributes)
on_hand = variant_attributes.delete(:on_hand)
on_demand = variant_attributes.delete(:on_demand)
variant = product.variants.create(variant_attributes)
begin
variant.on_demand = on_demand if on_demand.present?
variant.on_hand = on_hand.to_i if on_hand.present?
rescue StandardError => e
notify_bugsnag(e, product, variant, variant_attributes)
raise e
end
end
def notify_bugsnag(error, product, variant, variant_attributes)
Bugsnag.notify(error) do |report|
report.add_tab(:product, product.attributes)
report.add_tab(:product_error, product.errors.first) unless product.valid?
report.add_tab(:variant_attributes, variant_attributes)
report.add_tab(:variant, variant.attributes)
report.add_tab(:variant_error, variant.errors.first) unless variant.valid?
end
end
def find_model(collection, model_id)
collection.find do |model|
model.id.to_s == model_id.to_s && model.persisted?
end
end
end
end

View File

@@ -0,0 +1,38 @@
# frozen_string_literal: true
module Sets
class VariantOverrideSet < ModelSet
def initialize(collection, attributes = {})
super(VariantOverride,
collection,
attributes,
nil,
proc { |attrs, tag_list| deletable?(attrs, tag_list) } )
end
private
def deletable?(attrs, tag_list)
attrs['price'].blank? &&
attrs['count_on_hand'].blank? &&
attrs['default_stock'].blank? &&
attrs['resettable'].blank? &&
attrs['sku'].nil? &&
attrs['on_demand'].nil? &&
tag_list.empty?
end
# Overrides ModelSet method to check presence of a tag_list (which is not an attribute)
# This method will delete VariantOverrides that have no values (see deletable? above)
# If the user sets all values to nil in the UI the VO will be deleted from the DB
def collection_to_delete
deleted = []
collection.delete_if { |e| deleted << e if @delete_if.andand.call(e.attributes, e.tag_list) }
deleted
end
def collection_to_keep
collection.reject { |e| @delete_if.andand.call(e.attributes, e.tag_list) }
end
end
end

View File

@@ -35,7 +35,7 @@
.row{ 'ng-if' => 'shop_id && RequestMonitor.loading' }
.sixteen.columns.alpha#loading
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
%h1
=t :loading_customers

View File

@@ -1,7 +1,7 @@
-# Render only the calculator settings and not the surrounding form
- enterprise_fee_set = ModelSet.new(EnterpriseFee, EnterpriseFee.where(:id => enterprise_fee.id))
- enterprise_fee_set = Sets::ModelSet.new(EnterpriseFee, EnterpriseFee.where(:id => enterprise_fee.id))
- calculator_form_string = nil
- form_for enterprise_fee_set, :as => :enterprise_fee_set, :url => '' do |form|
- form_for enterprise_fee_set, :as => :sets_enterprise_fee_set, :url => '' do |form|
- form.fields_for :collection do |f|
- calculator_form_string = capture do
- if !enterprise_fee.new_record?

View File

@@ -9,7 +9,7 @@
%columns-dropdown{ action: "#{controller_name}_#{action_name}" }
.row{ 'ng-if' => '!loaded' }
.sixteen.columns.alpha#loading
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
%h1= t('.loading_enterprises')
.row{ :class => "sixteen columns alpha", 'ng-show' => 'loaded && filteredEnterprises.length == 0'}
%h1#no_results= t('.no_enterprises_found')

View File

@@ -59,7 +59,8 @@
.six.columns
= f.text_field :permalink, { 'ng-model' => "Enterprise.permalink", placeholder: "eg. your-shop-name", 'ng-model-options' => "{ updateOn: 'default blur', debounce: {'default': 300, 'blur': 0} }" }
.two.columns.omega
%img.spinner{ src: image_path("spinning-circles.svg"), width: "30px", ng: { show: "checking" } }
%div{ng: {show: "checking", cloak: true}, style: "width: 30px; height: 30px;"}
= render partial: "components/spinner"
%span{ ng: { class: 'availability.toLowerCase()', hide: "checking" } }
{{ availability }}
%i{ ng: { class: "{'icon-ok-sign': availability == 'Available', 'icon-remove-sign': availability == 'Unavailable'}" } }

View File

@@ -1,5 +1,5 @@
%div.sixteen.columns.alpha.omega#loading{ ng: { cloak: true, if: 'RequestMonitor.loading' } }
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
%h1{ ng: { hide: 'orderCycles.length > 0' } }
=t('.loading_order_cycles')
%h1{ ng: { show: 'orderCycles.length > 0' } }

View File

@@ -1,3 +1,3 @@
%div.sixteen.columns.alpha.omega#loading{ ng: { cloak: true, if: 'shop_id && RequestMonitor.loading' } }
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
%h1= t('.loading')

View File

@@ -1,3 +1,3 @@
%div.sixteen.columns.alpha.omega#loading{ ng: { cloak: true, if: 'hub_id && products.length == 0 && RequestMonitor.loading' } }
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
%h1= t('.loading_inventory')

View File

@@ -0,0 +1 @@
%img.spinner{ src: image_path("spinning-circles.svg"), style: "max-width: 100%" }

View File

@@ -0,0 +1,2 @@
= t('.instructions', distributor: @payment.order.distributor.name, amount: @payment.display_amount)
= main_app.authorize_payment_url(@payment)

View File

@@ -0,0 +1,3 @@
= t('.instructions', distributor: @payment.order.distributor.name, amount: @payment.display_amount)
= main_app.authorize_payment_url(@payment)

View File

@@ -1,7 +1,7 @@
.row.active_table_row{"ng-if" => "open()", "ng-click" => "toggle($event)", "ng-class" => "{'open' : open()}"}
.columns.small-12.fat.text-center{"ng-show" => "open() && shopfront_loading"}
%p.fullwidth
%img.spinner.text-center{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
.columns.small-12.medium-7.large-7.fat{"ng-show" => "open() && !shopfront_loading"}
/ Will add in long description available once clean up HTML formatting producer.long_description

View File

@@ -41,6 +41,6 @@
.message{ ng: { hide: "imageSrc() || imageUploader.isUploading" } }
= t(".logo_placeholder")
.loading{ ng: { hide: "!imageUploader.isUploading" } }
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
%br/
= t("registration.steps.images.uploading")

View File

@@ -39,6 +39,6 @@
.message{ ng: { hide: "imageSrc() || imageUploader.isUploading" } }
= t(".promo_image_placeholder")
.loading{ ng: { hide: "!imageUploader.isUploading" } }
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
%br/
= t("registration.steps.images.uploading")

View File

@@ -21,7 +21,7 @@
= t :products_loading
.row.full
.small-12.columns.text-center
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
.hide-for-medium-down.large-1.columns
-# Space between products and filters

View File

@@ -1,7 +1,7 @@
.row.active_table_row{"ng-show" => "open()", "ng-click" => "toggle($event)", "ng-class" => "{'open' : open()}"}
.columns.small-12.fat.text-center{"ng-show" => "open() && shopfront_loading"}
%p.fullwidth
%img.spinner.text-center{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
.columns.small-12.medium-6.large-5.fat{"ng-show" => "open() && !shopfront_loading"}
%div{"ng-if" => "::hub.taxons"}

View File

@@ -26,7 +26,8 @@
%a{href: "", "ng-click" => "showDistanceMatches()"}
= t :hubs_distance_filter, location: "{{ nameMatchesFiltered[0].name }}"
.more-controls
%img.spinner.text-center{ng: {show: "closed_shops_loading"}, src: image_path("spinning-circles.svg") }
%span{ng: {show: "closed_shops_loading", cloak: true}}
= render partial: "components/spinner"
%span{ng: {if: "!show_closed", cloak: true}}
%a.button{href: "", ng: {click: "showClosedShops()"}}
= t '.show_closed_shops'

View File

@@ -14,7 +14,7 @@
= "#{count} x #{t(state.humanize.downcase, scope: [:spree, :shipment_states], default: [:missing, "none"])}"
- unless shipment.shipped?
%td.item-qty-edit.hidden
= number_field_tag :quantity, item.quantity, :min => 0, :class => "line_item_quantity", :size => 5, :max => item.variant.on_hand
= number_field_tag :quantity, item.quantity, :min => 0, :class => "line_item_quantity", :size => 5, :max => item.variant.on_hand + item.quantity
%td.item-total.align-center
= line_item_shipment_price(line_item, item.quantity)

View File

@@ -103,7 +103,7 @@
%columns-dropdown{ action: "#{controller_name}_#{action_name}" }
%div.sixteen.columns.alpha#loading{ 'ng-if' => 'RequestMonitor.loading' }
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
%h1
= t("admin.orders.bulk_management.loading")

View File

@@ -81,7 +81,8 @@
%span{'ng-bind-html' => 'order.display_total'}
%td.actions
%div.row-loading-icons
%img.spinner{src: image_path("spinning-circles.svg"), ng: {show: 'rowStatus[order.id] == "loading"'} }
%span{ng: {show: 'rowStatus[order.id] == "loading"', cloak: true}}
= render partial: "components/spinner"
%i.success.icon-ok-sign{ng: {show: 'rowStatus[order.id] == "success"'} }
%i.error.icon-remove-sign.with-tip{ng: {show: 'rowStatus[order.id] == "error"'}, 'ofn-with-tip' => t('.order_not_updated')}
%a.icon_link.with-tip.icon-edit.no-text{'ng-href' => '{{order.edit_path}}', 'data-action' => 'edit', 'ofn-with-tip' => t('.edit')}
@@ -93,7 +94,7 @@
.orders-loading{'ng-show' => 'RequestMonitor.loading'}
.row
.small-12.columns.fullwidth.text-center
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
.row
.small-12.columns.fullwidth.text-center
%span= t('.loading')

View File

@@ -5,8 +5,6 @@
%div{ :class => "toolbar", 'data-hook' => "toolbar" }
%ul{ :class => "actions header-action-links inline-menu" }
%li#new_product_link
= button_link_to t(:new_product), new_object_url, { :remote => true, :icon => 'icon-plus', :id => 'admin_new_product' }
= button_link_to t(:new_product), new_object_url, { :icon => 'icon-plus', :id => 'admin_new_product' }
= render partial: 'spree/admin/shared/product_sub_menu'
%div#new_product(data-hook)

View File

@@ -1,6 +1,6 @@
%div.sixteen.columns.alpha#loading{ 'ng-if' => 'RequestMonitor.loading' }
%br
%img.spinner{ src: image_path("spinning-circles.svg") }
= render partial: "components/spinner"
%h1= t('.title')
%div.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && q.query.length == 0' }

View File

@@ -1,12 +0,0 @@
<%# This chunk is just a copy of Spree's backend/app/views/spree/admin/products/new.js.erb %>
$("#new_product").html('<%= escape_javascript(render :template => "spree/admin/products/new", :formats => [:html], :handlers => [:haml]) %>');
handle_date_picker_fields();
<% unless Rails.env.test? %>
$('.select2').select2();
<% end %>
$("#table-filter").hide();
$("#admin_new_product").parent().hide();
<%# We need to replace the page's title as well. We're navigating to a new page
although through ajax %>
$('.js-admin-page-title').html('<%= t('.title') %>');

View File

@@ -5,8 +5,7 @@
%nav.menu
%ul.sidebar
= configurations_sidebar_menu_item Spree.t(:general_settings), edit_admin_general_settings_path
- if Spree::Config[:override_actionmailer_config]
= configurations_sidebar_menu_item Spree.t(:mail_method_settings), edit_admin_mail_methods_path
= configurations_sidebar_menu_item Spree.t(:mail_method_settings), edit_admin_mail_methods_path
= configurations_sidebar_menu_item Spree.t(:tax_categories), admin_tax_categories_path
= configurations_sidebar_menu_item Spree.t(:tax_rates), admin_tax_rates_path
= configurations_sidebar_menu_item Spree.t(:tax_settings), edit_admin_tax_settings_path

View File

@@ -11,7 +11,7 @@ Openfoodnetwork::Application.configure do
config.action_controller.perform_caching = true
# Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_assets = false
config.serve_static_files = false
# Compress JavaScripts and CSS
config.assets.compress = true

View File

@@ -11,7 +11,7 @@ Openfoodnetwork::Application.configure do
config.action_controller.perform_caching = true
# Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_assets = false
config.serve_static_files = false
# Compress JavaScripts and CSS
config.assets.compress = true

View File

@@ -10,7 +10,7 @@ Openfoodnetwork::Application.configure do
config.eager_load = false
# Configure static asset server for tests with Cache-Control for performance
config.serve_static_assets = true
config.serve_static_files = true
config.static_cache_control = "public, max-age=3600"
# Separate cache stores when running in parallel

View File

@@ -1,7 +1,4 @@
unless Rails.env.production?
# Enable cache instrumentation, which is disabled by default
ActiveSupport::Cache::Store.instrument = true
# Log message in the same default logger
ActiveSupport::Cache::Store.logger = Rails.logger
end

View File

@@ -6,6 +6,7 @@ if ENV['DATADOG_RAILS_APM']
c.analytics_enabled = true
c.runtime_metrics_enabled = true
c.request_queuing = true
c[:rack].request_queuing = true
end
end

View File

@@ -1,15 +1,17 @@
require_relative 'spree'
unless ENV['DEPENDENCIES_NEXT']
require_relative 'spree'
# See: https://github.com/itbeaver/db2fog
DB2Fog.config = {
:aws_access_key_id => Spree::Config[:s3_access_key],
:aws_secret_access_key => Spree::Config[:s3_secret],
:directory => ENV['S3_BACKUPS_BUCKET'],
:provider => 'AWS'
}
# See: https://github.com/itbeaver/db2fog
DB2Fog.config = {
:aws_access_key_id => Spree::Config[:s3_access_key],
:aws_secret_access_key => Spree::Config[:s3_secret],
:directory => ENV['S3_BACKUPS_BUCKET'],
:provider => 'AWS'
}
region = ENV['S3_BACKUPS_REGION'] || ENV['S3_REGION']
region = ENV['S3_BACKUPS_REGION'] || ENV['S3_REGION']
# If no region is defined we leave this config key undefined (instead of nil),
# so that db2fog correctly applies it's default
DB2Fog.config[:region] = region if region
# If no region is defined we leave this config key undefined (instead of nil),
# so that db2fog correctly applies it's default
DB2Fog.config[:region] = region if region
end

View File

@@ -1,13 +1,14 @@
# Make helpers (#t in particular) available to javascript templates
# https://github.com/pitr/angular-rails-templates/issues/45#issuecomment-43229086
Rails.application.assets.context_class.class_eval do
# include ApplicationHelper
# include ActionView::Helpers
# include Rails.application.routes.url_helpers
# Including all of the helpers (above) has caused some intermittent CSS include issues
# (not finding mixins from an @include in sass). Therefore, we're only including the
# bare minimum here.
include ActionView::Helpers::TranslationHelper
if ENV['DEPENDENCIES_NEXT']
Rails.application.config.assets.configure do |env|
env.context_class.class_eval do
include ActionView::Helpers::TranslationHelper
end
end
else
Rails.application.assets.context_class.class_eval do
include ActionView::Helpers::TranslationHelper
end
end

View File

@@ -25,7 +25,6 @@ Spree.config do |config|
# -- spree_paypal_express
# Auto-capture payments. Without this option, payments must be manually captured in the paypal interface.
config.auto_capture = true
#config.override_actionmailer_config = false
# S3 settings
config.s3_bucket = ENV['S3_BUCKET'] if ENV['S3_BUCKET']

View File

@@ -2376,6 +2376,7 @@ ar:
js:
saving: 'حفظ...'
changes_saved: 'تم حفظ التغييرات.'
authorising: "تفويض ..."
save_changes_first: حفظ التغييرات أولا.
all_changes_saved: تم حفظ جميع التغييرات
unsaved_changes: لم تحفظ التغييرات
@@ -3147,6 +3148,7 @@ ar:
payment_state: "حالة الدفعة"
errors:
messages:
included_price_validation: "لا يمكن تحديده إلا إذا قمت بتعيين منطقة ضريبية افتراضية"
blank: "لا يمكن أن تكون فارغة"
layouts:
admin:
@@ -3357,6 +3359,13 @@ ar:
deactivation_warning: "يمكن أن يؤدي إلغاء تنشيط طريقة الدفع إلى اختفاء طريقة الدفع من قائمتك. بدلاً من ذلك ، يمكنك إخفاء طريقة الدفع من صفحة الخروج عن طريق تعيين الخيار \"عرض\" إلى\"المكتب الخلفي فقط\"."
providers:
provider: "مزود"
check: "النقد / التحويل الإلكتروني / إلخ. (المدفوعات التي لا تتطلب المصادقة التلقائية)"
migs: "خدمة بوابة الإنترنت من ( MasterCard Internet Gateway Service MIGS)"
paypalexpress: "باي بال اكسبريس"
stripeconnect: "Stripe SCA"
stripesca: "Stripe SCA"
bogus: "Bogus"
bogussimple: "BogusSimple"
payments:
source_forms:
stripe:
@@ -3592,7 +3601,23 @@ ar:
paused: التعليق
canceled: الالغاء
paypal:
already_refunded: "تم رد هذه الدفعة ولا يمكن اتخاذ أي إجراء آخر بشأنها."
no_payment_via_admin_backend: "لا يمكنك شحن حسابات PayPal من خلال المسؤول للواجهة الخلفية في الوقت الحالي."
transaction: "معاملة PayPal"
payer_id: "معرف الدافع"
transaction_id: "رقم المعاملة"
token: "رمز"
refund: "إعادة المال"
refund_amount: "القيمة"
original_amount: "المبلغ الأصلي: %{amount}"
refund_successful: "تم استرداد المال عبر PayPal بنجاح"
refund_unsuccessful: "استرداد المال عبر PayPal غير ناجح"
actions:
refund: "إعادة المال"
flash:
cancel: "لا تريد استخدام PayPal؟ لا يوجد مشكلة."
connection_failed: "تعذر الاتصال بـ PayPal."
generic_error: "فشل PayPal. %{reasons}"
users:
form:
account_settings: إعدادت الحساب

View File

@@ -2382,6 +2382,7 @@ ca:
js:
saving: 'Desant...'
changes_saved: 'S''han desat els canvis.'
authorising: "Autoritzant..."
save_changes_first: Desa els canvis en primer lloc.
all_changes_saved: S'han desat tots els canvis
unsaved_changes: Teniu canvis sense desar
@@ -2397,6 +2398,7 @@ ca:
resolve_errors: Si us plau, resol els errors següents
more_items: "+ %{count} Més"
default_card_updated: Targeta predeterminada actualitzada
default_card_voids_auth: Si canvieu la targeta predeterminada, seliminaran les autoritzacions existents de les botigues per cobrar-la. Podeu tornar a autoritzar les botigues després d'actualitzar la targeta predeterminada. Voleu canviar la targeta predeterminada?
cart:
add_to_cart_failed: >
S'ha produït un problema en afegir aquest producte a la cistella. Potser
@@ -3062,6 +3064,7 @@ ca:
payment_state: "Estat del pagament"
errors:
messages:
included_price_validation: "no es pot seleccionar tret que hàgiu establert una zona fiscal predeterminada"
blank: "no es pot deixar en blanc"
layouts:
admin:
@@ -3272,6 +3275,14 @@ ca:
deactivation_warning: "Desactivar un mètode de pagament pot fer que el mètode de pagament desapareixi de la vostra llista. De forma alternativa, podeu amagar un mètode de pagament a la pàgina de compra configurant l'opció \"Mostrar\" a \"només a la pàgina d'administració\" (back end)."
providers:
provider: "Proveïdor"
check: "Efectiu / transferència / etc. (pagaments per als quals no és necessària la validació automàtica)"
migs: "MasterCard Internet Gateway Service (MIGS)"
pin: "Pin Payments"
paypalexpress: "PayPal Express"
stripeconnect: "Stripe"
stripesca: "Stripe SCA"
bogus: "Bogus"
bogussimple: "BogusSimple"
payments:
source_forms:
stripe:
@@ -3507,7 +3518,23 @@ ca:
paused: en pausa
canceled: cancel·lat
paypal:
already_refunded: "Aquest pagament s'ha reembossat i no es poden emprendre mesures addicionals."
no_payment_via_admin_backend: "En aquest moment no podeu carregar comptes de PayPal mitjançant el panell dadministració."
transaction: "Transacció PayPal"
payer_id: "Id de pagador"
transaction_id: "Id de transacció"
token: "Token"
refund: "Reembossament"
refund_amount: "Quantitat"
original_amount: "Import original: %{amount}"
refund_successful: "reembossament de PayPal exitós"
refund_unsuccessful: "reembossament de PayPal fallat"
actions:
refund: "Reembossament"
flash:
cancel: "No voleu utilitzar PayPal? Cap problema."
connection_failed: "No s'ha pogut connectar a PayPal."
generic_error: "PayPal ha fallat. %{reasons}"
users:
form:
account_settings: Configuració del compte
@@ -3546,6 +3573,7 @@ ca:
delete?: Suprimeix?
cards:
authorised_shops: Botigues autoritzades
authorised_shops_agreement: Aquesta és la llista de botigues que tenen permís per carregar la vostra targeta de crèdit per defecte per les subscripcions (per exemple, comandes repetides) que tingueu. Les dades de la vostra targeta es mantindran segures i no es compartiran amb els propietaris de botigues. Sempre se us notificarà quan us cobrin. En marcar la casella duna botiga, accepteu autoritzar la botiga perquè enviï instruccions a la institució financera que va emetre la vostra targeta per fer els pagaments dacord amb els termes de qualsevol subscripció que creeu amb aquesta botiga.
saved_cards_popover: Aquesta és la llista de targetes que heu optat per guardar per a un ús posterior. El vostre "valor predeterminat" es seleccionarà automàticament quan valideu una comanda i es pot carregar per qualsevol botiga a la que li hagueu permès fer-ho (vegeu a la dreta).
authorised_shops:
shop_name: "Nom de la botiga"

File diff suppressed because it is too large Load Diff

View File

@@ -2455,6 +2455,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using
payment_processing_failed: "Payment could not be processed, please check the details you entered"
payment_method_not_supported: "That payment method is unsupported. Please choose another one."
payment_updated: "Payment Updated"
cannot_perform_operation: "Could not update the payment"
action_required: "Action required"
inventory_settings: "Inventory Settings"
tag_rules: "Tag Rules"
shop_preferences: "Shop Preferences"
@@ -2717,6 +2719,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using
processing: "processing"
void: "void"
invalid: "invalid"
quantity_adjusted: "Insufficient stock available. Line item updated to maximum available quantity."
quantity_unchanged: "Quantity unchanged from previous amount."
resend_user_email_confirmation:
resend: "Resend"
sending: "Resend..."
@@ -3644,6 +3648,10 @@ See the %{link} to find out more about %{sitename}'s features and to start using
subject: "Reset password instructions"
confirmation_instructions:
subject: "Please confirm your OFN account"
payment_mailer:
authorize_payment:
subject: "Please authorize your payment to %{distributor} on OFN"
instructions: "Your payment of %{amount} to %{distributor} requires additional authentication. Please visit the following URL to authorize your payment:"
shipment_mailer:
shipped_email:
dear_customer: "Dear Customer,"

View File

@@ -1569,6 +1569,7 @@ en_CA:
shopping_groups_part_of: "is part of:"
shopping_producers_of_hub: "%{hub}'s producers:"
enterprises_next_closing: "Next order closing"
enterprises_currently_open: "Orders are currently open"
enterprises_ready_for: "Order for"
enterprises_choose: "Choose from the dropdown:"
maps_open: "Open"
@@ -2085,6 +2086,7 @@ en_CA:
spree_classification_primary_taxon_error: "Taxon %{taxon} is the primary taxon of %{product} and cannot be deleted"
spree_order_availability_error: "Distributor or order cycle cannot supply the products in your cart"
spree_order_populator_error: "That distributor or order cycle can't supply all the products in your cart. Please choose another."
spree_order_cycle_error: "Please choose an order cycle for this order"
spree_order_populator_availability_error: "That product is not available from the chosen distributor or order cycle."
spree_distributors_error: "At least one hub must be selected"
spree_user_enterprise_limit_error: "^%{email} is not permitted to own any more enterprises (limit is %{enterprise_limit})."
@@ -2377,6 +2379,7 @@ en_CA:
js:
saving: 'Saving...'
changes_saved: 'Changes saved.'
authorising: "Authorising..."
save_changes_first: Save changes first.
all_changes_saved: All changes saved
unsaved_changes: You have unsaved changes
@@ -2392,6 +2395,7 @@ en_CA:
resolve_errors: Please resolve the following errors
more_items: "+ %{count} More"
default_card_updated: Default Card Updated
default_card_voids_auth: Changing your default card will remove shops' existing authorizations to charge it. You can re-authorize shops after updating the default card. Do you wish to change the default card?"
cart:
add_to_cart_failed: >
There was a problem adding this product to the cart. Perhaps it has become
@@ -3055,6 +3059,7 @@ en_CA:
payment_state: "Payment State"
errors:
messages:
included_price_validation: "cannot be selected unless you have set a Default Tax Zone"
blank: "can't be blank"
layouts:
admin:
@@ -3265,6 +3270,14 @@ en_CA:
deactivation_warning: "De-activating a payment method can make the payment method disappear from your list. Alternatively, you can hide a payment method from the checkout page by setting the option 'Display' to 'back office only'."
providers:
provider: "Provider"
check: "Cash/EFT/Bank Transfer etc. (payments for which automatic validation is not required)"
migs: "MasterCard Internet Gateway Service (MIGS)"
pin: "Pin Payments (Only applicable to users in Australia)"
paypalexpress: "PayPal Express"
stripeconnect: "Stripe"
stripesca: "Stripe SCA"
bogus: "Bogus"
bogussimple: "BogusSimple"
payments:
source_forms:
stripe:
@@ -3500,7 +3513,23 @@ en_CA:
paused: paused
canceled: cancelled
paypal:
already_refunded: "This payment has been refunded and no further action can be taken on it."
no_payment_via_admin_backend: "You cannot charge PayPal accounts through the admin backend at this time."
transaction: "PayPal Transaction"
payer_id: "Payer ID"
transaction_id: "Transaction ID"
token: "Token"
refund: "Refund"
refund_amount: "Amount"
original_amount: "Original amount: %{amount}"
refund_successful: "PayPal refund successful"
refund_unsuccessful: "PayPal refund unsuccessful"
actions:
refund: "Refund"
flash:
cancel: "Don't want to use PayPal? No problems."
connection_failed: "Could not connect to PayPal."
generic_error: "PayPal failed. %{reasons}"
users:
form:
account_settings: Account Settings
@@ -3539,9 +3568,11 @@ en_CA:
delete?: Delete?
cards:
authorised_shops: Authorised Shops
authorised_shops_agreement: This is the list of shops which are permitted to charge your default credit card for any subscriptions (ie. repeating orders) you may have. Your card details will be kept secure and will not be shared with shop owners. You will always be notified when you are charged. By checking the box for a shop, you are agreeing to authorise that shop to send instructions to the financial institution that issued your card to take payments in accordance with the terms of any subscription you create with that shop.
saved_cards_popover: This is the list of cards you have opted to save for later use. Your 'default' will be selected automatically when you checkout an order, and can be charged by any shops you have allowed to do so (see right).
authorised_shops:
shop_name: "Shop Name"
allow_charges?: "Authorising"
localized_number:
invalid_format: has an invalid format. Please enter a number.
api:

View File

@@ -1571,6 +1571,7 @@ es:
shopping_groups_part_of: "es parte de:"
shopping_producers_of_hub: "productoras de %{hub}:"
enterprises_next_closing: "Los pedidos se cerrarán"
enterprises_currently_open: "Los pedidos están abiertos"
enterprises_ready_for: "Listo para"
enterprises_choose: "Hay más de un ciclo abierto. Escoge en cuál quieres realizar el pedido:"
maps_open: "Abierta"
@@ -2087,6 +2088,7 @@ es:
spree_classification_primary_taxon_error: "La clasificación %{taxon} es la clasificación primaria de %{product} y no puede ser eliminada"
spree_order_availability_error: "El Distribuidor o el Ciclo de Pedido no pueden suministrar los productos en su carrito"
spree_order_populator_error: "Este Distribuidor o Ciclo de Pedido no puede suministrar todos los productos en tu carrito. Por favor, elige otro."
spree_order_cycle_error: "Elija un ciclo de pedido para este pedido."
spree_order_populator_availability_error: "Este producto no está disponible por el distribuidor o Ciclo de Pedido elegido."
spree_distributors_error: "Se debe seleccionar al menos un Grupo"
spree_user_enterprise_limit_error: "^ %{email} no está autorizado a tener más organizaciones (el límite es %{enterprise_limit})."
@@ -2379,6 +2381,7 @@ es:
js:
saving: 'Guardando...'
changes_saved: 'Cambios guardados.'
authorising: "Autorizando..."
save_changes_first: Guarda los cambios primero.
all_changes_saved: Todos los cambios guardados
unsaved_changes: Tienes cambios sin guardar
@@ -2394,6 +2397,7 @@ es:
resolve_errors: Resuelve los siguientes errores
more_items: "+ %{count} Más"
default_card_updated: Tarjeta predeterminada actualizada
default_card_voids_auth: Cambiar su tarjeta predeterminada eliminará las autorizaciones existentes de las tiendas para cargarla. Puede volver a autorizar las tiendas después de actualizar la tarjeta predeterminada. ¿Desea cambiar la tarjeta predeterminada?
cart:
add_to_cart_failed: >
Ha habido un problema al añadir este producto en el carrito. Puede que haya
@@ -3060,6 +3064,7 @@ es:
payment_state: "Estado del pago"
errors:
messages:
included_price_validation: "no se puede seleccionar a menos que haya establecido una zona fiscal predeterminada"
blank: "no puede estar vacío"
layouts:
admin:
@@ -3270,6 +3275,14 @@ es:
deactivation_warning: "La desactivación de un método de pago puede hacer que el método de pago desaparezca de su lista. Alternativamente, puede ocultar un método de pago desde la página de pago configurando la opción 'Mostrar' como 'Solo visible para el administrador'."
providers:
provider: "Proveedor"
check: "Efectivo / transferencia / etc. (pagos para los que no se requiere validación automática)"
migs: "MasterCard Internet Gateway Service (MIGS)"
pin: "Pin Payments"
paypalexpress: "PayPal Express"
stripeconnect: "Stripe"
stripesca: "Stripe SCA"
bogus: "Bogus"
bogussimple: "BogusSimple"
payments:
source_forms:
stripe:
@@ -3505,7 +3518,23 @@ es:
paused: pausado
canceled: cancelado
paypal:
already_refunded: "Este pago ha sido reembolsado y no se pueden realizar más acciones al respecto."
no_payment_via_admin_backend: "No puede cargar cuentas de PayPal a través del panel de administración en este momento."
transaction: "Transacción PayPal"
payer_id: "Id del pagador"
transaction_id: "Id de transacción"
token: "Token"
refund: "Reembolso"
refund_amount: "Cantidad"
original_amount: "Importe original: %{amount}"
refund_successful: "Reembolso de PayPal exitoso"
refund_unsuccessful: "Reembolso de PayPal fallido"
actions:
refund: "Reembolso"
flash:
cancel: "¿No quieres usar PayPal? No hay problema."
connection_failed: "No se pudo conectar a PayPal."
generic_error: "PayPal falló. %{reasons}"
users:
form:
account_settings: Configuración de la cuenta
@@ -3544,9 +3573,11 @@ es:
delete?: ¿Borrar?
cards:
authorised_shops: Tiendas autorizadas
authorised_shops_agreement: Esta es la lista de tiendas que pueden cargar en su tarjeta de crédito predeterminada cualquier suscripción (es decir, pedidos repetidos) que pueda tener. Los datos de su tarjeta se mantendrán seguros y no se compartirán con los propietarios de las tiendas. Siempre se le notificará cuando se le cobre. Al marcar la casilla de una tienda, usted acepta autorizar a esa tienda a enviar instrucciones a la institución financiera que emitió su tarjeta para recibir pagos de acuerdo con los términos de cualquier suscripción que cree con esa tienda.
saved_cards_popover: Esta es la lista de tarjetas que ha optado por guardar para su uso posterior. Su "valor predeterminado" se seleccionará automáticamente al momento de realizar un pedido, y puede ser cobrado por cualquier tienda que tenga permitido hacerlo (ver a la derecha).
authorised_shops:
shop_name: "Nombre de tienda"
allow_charges?: "¿Permitir cargos a la tarjeta predeterminada?"
localized_number:
invalid_format: tiene un formato invalido. Por favor introduzca un numero.
api:

View File

@@ -1571,6 +1571,7 @@ fr_CA:
shopping_groups_part_of: "fait partie de:"
shopping_producers_of_hub: "Les producteurs de %{hub}:"
enterprises_next_closing: "Clôture des commandes pour ce cycle"
enterprises_currently_open: "Les commandes sont ouvertes"
enterprises_ready_for: "Prêt pour"
enterprises_choose: "Choisissez votre option:"
maps_open: "Ouvre"
@@ -2087,6 +2088,7 @@ fr_CA:
spree_classification_primary_taxon_error: "L'intitulé %{taxon}est l'intitulé de base pour %{product} et ne peut être supprimé"
spree_order_availability_error: "Le distributeur ne peut fournir les produits de votre panier pour ce cycle de vente."
spree_order_populator_error: "Le distributeur ne peut fournir tous les produits de votre panier pour ce cycle de vente. Merci de choisir un autre distributeur ou un autre cycle de vente."
spree_order_cycle_error: "Veuillez sélectionner une option pour cette commande."
spree_order_populator_availability_error: "Ce produit n'est pas disponible pour ce cycle de vente / distributeur."
spree_distributors_error: "Veuillez sélectionner au moins un hub"
spree_user_enterprise_limit_error: "^ %{email} ne peut pas créer de nouvelles entreprises (limite actuelle : %{enterprise_limit} entreprises )."
@@ -2381,6 +2383,7 @@ fr_CA:
js:
saving: 'Enregistrement en cours...'
changes_saved: 'Modifications sauvegardées.'
authorising: "Autorisation en cours..."
save_changes_first: Veuillez d'abord sauvegarder les modifications.
all_changes_saved: Toutes les modifications ont été sauvegardées.
unsaved_changes: Des modifications n'ont pas été sauvegardées
@@ -2396,6 +2399,7 @@ fr_CA:
resolve_errors: Veuillez corriger les erreurs suivantes
more_items: "+ %{count} en plus"
default_card_updated: La carte bancaire par défaut a été mise à jour
default_card_voids_auth: Modifier votre carte de paiement par défaut va supprimer les autorisations attribuées à certaines boutiques. vous pouvez les remettre en place dans les paramètres de votre compte. Souhaitez-vous modifier la carte par défaut ?
cart:
add_to_cart_failed: >
Il y a eu un problème lors de l'ajout de votre produit au panier. Peut-être
@@ -3069,6 +3073,7 @@ fr_CA:
payment_state: "Statut du Paiement"
errors:
messages:
included_price_validation: "Ce cycle de vente a déjà été utilisé par un acheteur et ne peut être supprimé. Pour empêcher aux acheteurs d'y accéder, veuillez plutôt le fermer."
blank: "Champ obligatoire"
layouts:
admin:
@@ -3279,6 +3284,14 @@ fr_CA:
deactivation_warning: "Désactiver une méthode de paiement peut faire disparaitre cette méthode de votre liste. Si vous souhaitez uniquement la rendre invisible par l'acheteur, modifiez la méthode et utilisez l'affichage pour l'administrateur uniquement."
providers:
provider: "Fournisseur"
check: "Espèces / chèques / virements / autres "
migs: "MasterCard Internet Gateway Service (MIGS)"
pin: "Méthode de paiement réservée à l'Australie (Pin Payments)"
paypalexpress: "PayPal Express"
stripeconnect: "Stripe"
stripesca: "Stripe SCA"
bogus: "Bogus"
bogussimple: "Bogus Simple"
payments:
source_forms:
stripe:
@@ -3515,7 +3528,23 @@ fr_CA:
paused: mis en pause
canceled: annulé
paypal:
already_refunded: "Ce paiement a été remboursé."
no_payment_via_admin_backend: "Vous ne pouvez pas réaliser de paiements via Paypal depuis l'interface d'administration."
transaction: "Transaction Paypal"
payer_id: "Identifiant du payeur"
transaction_id: "Identifiant de la transaction"
token: "Jeton d'authentification (token)"
refund: "Remboursement"
refund_amount: "Montant"
original_amount: "Montant initial: %{amount}"
refund_successful: "Remboursement PayPal réussi"
refund_unsuccessful: "Echec du remboursement PayPal"
actions:
refund: "Remboursement"
flash:
cancel: "Vous ne voulez pas utiliser PayPal ? Pas de soucis."
connection_failed: "Impossible de se connecter à PayPal."
generic_error: "PayPal a échoué. %{reasons}"
users:
form:
account_settings: Paramètres du Compte
@@ -3554,9 +3583,11 @@ fr_CA:
delete?: Effacer?
cards:
authorised_shops: Boutiques autorisées
authorised_shops_agreement: 'Cette liste de boutique correspond aux boutiques ayant l''autorisation d''utiliser votre carte pour les commandes récurrentes auxquelles vous avez souscrit. Les détails de votre carte ne sont pas partagés avec les gérants des boutiques en question. Vous serez toujours notifié d''un paiement dès qu''il a lieu. '
saved_cards_popover: C'est la liste des cartes que vous avez choisi d'enregistrer pour une utilisation ultérieure. Votre carte 'par défaut' sera automatiquement sélectionnée lorsque vous passerez votre commande, et pourra être facturé par tous les magasins que vous avez autorisés à le faire (voir à droite).
authorised_shops:
shop_name: "Nom de la boutique"
allow_charges?: "Souhaitez-vous autoriser les paiements sur la carte par défaut ?"
localized_number:
invalid_format: n'est pas un format valide. Veuillez entrer un nombre.
api:

View File

@@ -2380,6 +2380,7 @@ it:
js:
saving: 'Salvataggio...'
changes_saved: 'Modifiche salvate.'
authorising: "Autorizzazione..."
save_changes_first: Salva prima le modifiche.
all_changes_saved: Tutte le modifiche sono state salvate
unsaved_changes: Hai modifiche non salvate
@@ -3063,6 +3064,7 @@ it:
payment_state: "Stato Pagamento"
errors:
messages:
included_price_validation: "Non può essere selezionato se non hai impostato una Zona Fiscale Predefinita "
blank: "non può essere lasciato vuoto"
layouts:
admin:
@@ -3273,6 +3275,14 @@ it:
deactivation_warning: "Disattivare un metodo di pagamento può far sparire il metodo di pagamento dalla tua lista. Alternativamente, puoi nascondere il metodo di pagamento dalla pagina di checkout impostando l'opzione 'Visualizza' su 'Solo Back office'."
providers:
provider: "Provider"
check: "Contanti o altri metodi di pagamento per cui non è richiesta una validazione automatica"
migs: "MasterCard Internet Gateway Service (MIGS)"
pin: "Pin Payments"
paypalexpress: "PayPal Express"
stripeconnect: "Stripe"
stripesca: "Stripe SCA"
bogus: "Bogus"
bogussimple: "BogusSimple"
payments:
source_forms:
stripe:
@@ -3508,7 +3518,23 @@ it:
paused: in pausa
canceled: annullato
paypal:
already_refunded: "Questo pagamento è stato rimborsato e non è possibile intraprendere ulteriori azioni."
no_payment_via_admin_backend: "Al momento non è possibile addebitare gli account PayPal tramite il back-end dell'amministratore."
transaction: "Transazione PayPal"
payer_id: "ID pagatore"
transaction_id: "ID Transazione"
token: "Token"
refund: "Rimborsa"
refund_amount: "Quantità"
original_amount: "Importo originale: %{amount}"
refund_successful: "Rimborso PayPal riuscito"
refund_unsuccessful: "Rimborso PayPal non riuscito"
actions:
refund: "Rimborsa"
flash:
cancel: "Non vuoi usare PayPal? Nessun problema."
connection_failed: "Non riesco a collegarmi a PayPal"
generic_error: "PayPal fallito. %{reasons}"
users:
form:
account_settings: Impostazioni account

View File

@@ -29,6 +29,7 @@ Openfoodnetwork::Application.routes.draw do
patch "/cart", :to => "spree/orders#update", :as => :update_cart
put "/cart/empty", :to => 'spree/orders#empty', :as => :empty_cart
get '/orders/:id/token/:token' => 'spree/orders#show', :as => :token_order
get '/payments/:id/authorize' => 'payments#redirect_to_authorize', as: "authorize_payment"
resource :cart, controller: "cart" do
post :populate

View File

@@ -0,0 +1,41 @@
class AddOrderToAdjustments < ActiveRecord::Migration
class Spree::Adjustment < ActiveRecord::Base
belongs_to :adjustable, polymorphic: true
belongs_to :order, class_name: "Spree::Order"
end
class Spree::LineItem < ActiveRecord::Base
belongs_to :order, class_name: "Spree::Order"
end
def up
add_column :spree_adjustments, :order_id, :integer
# Ensure migration can use the new column
Spree::Adjustment.reset_column_information
# Migrate adjustments on orders
Spree::Adjustment.where(order_id: nil, adjustable_type: "Spree::Order").find_each do |adjustment|
adjustment.update_column(:order_id, adjustment.adjustable_id)
end
# Migrate adjustments on line_items
Spree::Adjustment.where(order_id: nil, adjustable_type: "Spree::LineItem").includes(:adjustable).find_each do |adjustment|
line_item = adjustment.adjustable
# In some cases a line item has been deleted but an orphaned adjustment remains in the
# database. There is no way for this orphan to ever be returned or accessed via any scopes,
# and no way to know what order it related to. In this case we can remove the record.
if line_item.nil?
adjustment.delete
next
end
adjustment.update_column(:order_id, line_item.order_id)
end
end
def down
remove_column :spree_adjustments, :order_id
end
end

Some files were not shown because too many files have changed in this diff Show More