diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index cc08ddc7e0..934e12057c 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -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 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 2713d9b86d..95485b26e2 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -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' diff --git a/Gemfile b/Gemfile index 39c0140964..187c525b32 100644 --- a/Gemfile +++ b/Gemfile @@ -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' diff --git a/Gemfile.lock b/Gemfile.lock index c8163c1ea5..f67e3394f1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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 diff --git a/Gemfile_next.lock b/Gemfile_next.lock index 71c94db901..437a99d5e7 100644 --- a/Gemfile_next.lock +++ b/Gemfile_next.lock @@ -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 diff --git a/app/assets/javascripts/admin/admin_ofn.js.coffee b/app/assets/javascripts/admin/admin_ofn.js.coffee index 1a5e1c79bf..bca7364525 100644 --- a/app/assets/javascripts/admin/admin_ofn.js.coffee +++ b/app/assets/javascripts/admin/admin_ofn.js.coffee @@ -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, */*" diff --git a/app/assets/javascripts/admin/index_utils/index_utils.js.coffee b/app/assets/javascripts/admin/index_utils/index_utils.js.coffee index 980b021307..e3fce2cb00 100644 --- a/app/assets/javascripts/admin/index_utils/index_utils.js.coffee +++ b/app/assets/javascripts/admin/index_utils/index_utils.js.coffee @@ -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, */*"; + $httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*" diff --git a/app/assets/javascripts/admin/order_cycles/order_cycles.js.erb.coffee b/app/assets/javascripts/admin/order_cycles/order_cycles.js.erb.coffee index 2343f90c28..69735c4a33 100644 --- a/app/assets/javascripts/admin/order_cycles/order_cycles.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycles/order_cycles.js.erb.coffee @@ -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) -> diff --git a/app/assets/javascripts/admin/product_import/product_import.js.coffee b/app/assets/javascripts/admin/product_import/product_import.js.coffee index 5eb83204e1..86c18bd5d7 100644 --- a/app/assets/javascripts/admin/product_import/product_import.js.coffee +++ b/app/assets/javascripts/admin/product_import/product_import.js.coffee @@ -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, */*" diff --git a/app/assets/javascripts/admin/spree/orders/variant_autocomplete.js.erb b/app/assets/javascripts/admin/spree/orders/variant_autocomplete.js.erb index c7237e6553..0bc4ba3f92 100644 --- a/app/assets/javascripts/admin/spree/orders/variant_autocomplete.js.erb +++ b/app/assets/javascripts/admin/spree/orders/variant_autocomplete.js.erb @@ -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), diff --git a/app/assets/javascripts/admin/utils/utils.js.coffee b/app/assets/javascripts/admin/utils/utils.js.coffee index 862ca1a317..f509d32c1f 100644 --- a/app/assets/javascripts/admin/utils/utils.js.coffee +++ b/app/assets/javascripts/admin/utils/utils.js.coffee @@ -1 +1,2 @@ -angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider) -> $httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content"); $httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"; +angular.module("admin.utils", ["templates", "ngSanitize"]).config ($httpProvider) -> + $httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*" diff --git a/app/assets/javascripts/darkswarm/darkswarm.js.coffee b/app/assets/javascripts/darkswarm/darkswarm.js.coffee index eb724ac400..76ecd388d9 100644 --- a/app/assets/javascripts/darkswarm/darkswarm.js.coffee +++ b/app/assets/javascripts/darkswarm/darkswarm.js.coffee @@ -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, */*" diff --git a/app/assets/stylesheets/admin/shared/icons.scss b/app/assets/stylesheets/admin/shared/icons.scss index 04a3fb74c0..a2de27ba99 100644 --- a/app/assets/stylesheets/admin/shared/icons.scss +++ b/app/assets/stylesheets/admin/shared/icons.scss @@ -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, diff --git a/app/controllers/admin/column_preferences_controller.rb b/app/controllers/admin/column_preferences_controller.rb index d8a5ca3e6a..90b4cb850c 100644 --- a/app/controllers/admin/column_preferences_controller.rb +++ b/app/controllers/admin/column_preferences_controller.rb @@ -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 diff --git a/app/controllers/admin/enterprise_fees_controller.rb b/app/controllers/admin/enterprise_fees_controller.rb index 0aaa2957be..73880badf4 100644 --- a/app/controllers/admin/enterprise_fees_controller.rb +++ b/app/controllers/admin/enterprise_fees_controller.rb @@ -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, diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index c61ab9f30e..99ae12761f 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -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 diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index f7de30662a..3482be399f 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -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 diff --git a/app/controllers/admin/variant_overrides_controller.rb b/app/controllers/admin/variant_overrides_controller.rb index 333455c57b..3c6778640c 100644 --- a/app/controllers/admin/variant_overrides_controller.rb +++ b/app/controllers/admin/variant_overrides_controller.rb @@ -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 diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c4ab6b9bed..f39af8eff0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -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' diff --git a/app/controllers/base_controller.rb b/app/controllers/base_controller.rb index b039a2447d..8ea4718cb5 100644 --- a/app/controllers/base_controller.rb +++ b/app/controllers/base_controller.rb @@ -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' diff --git a/app/controllers/checkout_controller.rb b/app/controllers/checkout_controller.rb index 4cedfbbac9..ca7df8be49 100644 --- a/app/controllers/checkout_controller.rb +++ b/app/controllers/checkout_controller.rb @@ -2,7 +2,7 @@ require 'open_food_network/address_finder' -class CheckoutController < Spree::StoreController +class CheckoutController < ::BaseController layout 'darkswarm' include OrderStockCheck diff --git a/app/controllers/payments_controller.rb b/app/controllers/payments_controller.rb new file mode 100644 index 0000000000..c0fb7eadcc --- /dev/null +++ b/app/controllers/payments_controller.rb @@ -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 diff --git a/app/controllers/payments_controller.rb~aa3478fba... base authorization on the payment's order b/app/controllers/payments_controller.rb~aa3478fba... base authorization on the payment's order new file mode 100644 index 0000000000..c0fb7eadcc --- /dev/null +++ b/app/controllers/payments_controller.rb~aa3478fba... base authorization on the payment's order @@ -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 diff --git a/app/controllers/spree/admin/base_controller.rb b/app/controllers/spree/admin/base_controller.rb index 4f9cdbb4d0..8d002b583c 100644 --- a/app/controllers/spree/admin/base_controller.rb +++ b/app/controllers/spree/admin/base_controller.rb @@ -1,6 +1,6 @@ module Spree module Admin - class BaseController < Spree::BaseController + class BaseController < ApplicationController ssl_required helper 'spree/admin/navigation' diff --git a/app/controllers/spree/admin/payments_controller.rb b/app/controllers/spree/admin/payments_controller.rb index e0a530a823..af913d50a8 100644 --- a/app/controllers/spree/admin/payments_controller.rb +++ b/app/controllers/spree/admin/payments_controller.rb @@ -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 diff --git a/app/controllers/spree/admin/products_controller.rb b/app/controllers/spree/admin/products_controller.rb index 3f9cdd92ac..1cc3f5ae6d 100644 --- a/app/controllers/spree/admin/products_controller.rb +++ b/app/controllers/spree/admin/products_controller.rb @@ -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 diff --git a/app/controllers/spree/base_controller.rb b/app/controllers/spree/base_controller.rb deleted file mode 100644 index b2e4628bc3..0000000000 --- a/app/controllers/spree/base_controller.rb +++ /dev/null @@ -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' diff --git a/app/controllers/spree/checkout_controller.rb b/app/controllers/spree/checkout_controller.rb index 71a45de4e6..e639505e29 100644 --- a/app/controllers/spree/checkout_controller.rb +++ b/app/controllers/spree/checkout_controller.rb @@ -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 diff --git a/app/controllers/spree/orders_controller.rb b/app/controllers/spree/orders_controller.rb index 616300c2b1..48e65ddbb1 100644 --- a/app/controllers/spree/orders_controller.rb +++ b/app/controllers/spree/orders_controller.rb @@ -1,5 +1,5 @@ module Spree - class OrdersController < Spree::StoreController + class OrdersController < ::BaseController include OrderCyclesHelper include Rails.application.routes.url_helpers diff --git a/app/controllers/spree/paypal_controller.rb b/app/controllers/spree/paypal_controller.rb index ca44867165..81f7433c22 100644 --- a/app/controllers/spree/paypal_controller.rb +++ b/app/controllers/spree/paypal_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Spree - class PaypalController < StoreController + class PaypalController < ::BaseController ssl_allowed include OrderStockCheck diff --git a/app/controllers/spree/store_controller.rb b/app/controllers/spree/store_controller.rb deleted file mode 100644 index 277cf55a63..0000000000 --- a/app/controllers/spree/store_controller.rb +++ /dev/null @@ -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 diff --git a/app/controllers/spree/users_controller.rb b/app/controllers/spree/users_controller.rb index c6ce064b57..1fb03af1f9 100644 --- a/app/controllers/spree/users_controller.rb +++ b/app/controllers/spree/users_controller.rb @@ -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 diff --git a/app/helpers/checkout_helper.rb b/app/helpers/checkout_helper.rb index 4ae026bef7..cae6bc1657 100644 --- a/app/helpers/checkout_helper.rb +++ b/app/helpers/checkout_helper.rb @@ -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) diff --git a/app/helpers/enterprise_fees_helper.rb b/app/helpers/enterprise_fees_helper.rb index cc22f07326..55647b6413 100644 --- a/app/helpers/enterprise_fees_helper.rb +++ b/app/helpers/enterprise_fees_helper.rb @@ -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 diff --git a/app/mailers/payment_mailer.rb b/app/mailers/payment_mailer.rb new file mode 100644 index 0000000000..b0c4a4bbff --- /dev/null +++ b/app/mailers/payment_mailer.rb @@ -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 diff --git a/app/models/column_preference_set.rb b/app/models/column_preference_set.rb deleted file mode 100644 index 24508d7f9d..0000000000 --- a/app/models/column_preference_set.rb +++ /dev/null @@ -1,5 +0,0 @@ -class ColumnPreferenceSet < ModelSet - def initialize(collection, attributes = {}) - super(ColumnPreference, collection, attributes, nil, nil ) - end -end diff --git a/app/models/enterprise_fee.rb b/app/models/enterprise_fee.rb index e973419ece..f1161bb8ab 100644 --- a/app/models/enterprise_fee.rb +++ b/app/models/enterprise_fee.rb @@ -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' diff --git a/app/models/enterprise_fee_set.rb b/app/models/enterprise_fee_set.rb deleted file mode 100644 index ae7ff692ad..0000000000 --- a/app/models/enterprise_fee_set.rb +++ /dev/null @@ -1,7 +0,0 @@ -class EnterpriseFeeSet < ModelSet - def initialize(attributes = {}) - super(EnterpriseFee, EnterpriseFee.all, - attributes, - proc { |attrs| attrs[:name].blank? }) - end -end diff --git a/app/models/enterprise_set.rb b/app/models/enterprise_set.rb deleted file mode 100644 index 20da64b083..0000000000 --- a/app/models/enterprise_set.rb +++ /dev/null @@ -1,5 +0,0 @@ -class EnterpriseSet < ModelSet - def initialize(collection, attributes = {}) - super(Enterprise, collection, attributes) - end -end diff --git a/app/models/model_set.rb b/app/models/model_set.rb deleted file mode 100644 index 8a599b4214..0000000000 --- a/app/models/model_set.rb +++ /dev/null @@ -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 diff --git a/app/models/order_cycle_set.rb b/app/models/order_cycle_set.rb deleted file mode 100644 index 9f4fb4433e..0000000000 --- a/app/models/order_cycle_set.rb +++ /dev/null @@ -1,5 +0,0 @@ -class OrderCycleSet < ModelSet - def initialize(collection, attributes = {}) - super(OrderCycle, collection, attributes) - end -end diff --git a/app/models/spree/adjustment.rb b/app/models/spree/adjustment.rb index cb965b8aef..942e7fc149 100644 --- a/app/models/spree/adjustment.rb +++ b/app/models/spree/adjustment.rb @@ -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 diff --git a/app/models/spree/app_configuration.rb b/app/models/spree/app_configuration.rb index faabc386e8..1edba583de 100644 --- a/app/models/spree/app_configuration.rb +++ b/app/models/spree/app_configuration.rb @@ -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 diff --git a/app/models/spree/credit_card.rb b/app/models/spree/credit_card.rb index 948dcf829d..2a10e78794 100644 --- a/app/models/spree/credit_card.rb +++ b/app/models/spree/credit_card.rb @@ -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 diff --git a/app/models/spree/gateway/stripe_sca.rb b/app/models/spree/gateway/stripe_sca.rb index a3ccde45b2..351adf019d 100644 --- a/app/models/spree/gateway/stripe_sca.rb +++ b/app/models/spree/gateway/stripe_sca.rb @@ -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 diff --git a/app/models/spree/order.rb b/app/models/spree/order.rb index 58e1c7304b..ceb603e275 100644 --- a/app/models/spree/order.rb +++ b/app/models/spree/order.rb @@ -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 diff --git a/app/models/spree/payment.rb b/app/models/spree/payment.rb index 578b91e3e5..3bd1bcd4a3 100644 --- a/app/models/spree/payment.rb +++ b/app/models/spree/payment.rb @@ -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 diff --git a/app/models/spree/payment/processing.rb b/app/models/spree/payment/processing.rb index 36704362a8..1648b0ce36 100644 --- a/app/models/spree/payment/processing.rb +++ b/app/models/spree/payment/processing.rb @@ -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 diff --git a/app/models/spree/product_set.rb b/app/models/spree/product_set.rb deleted file mode 100644 index 4731c44bf7..0000000000 --- a/app/models/spree/product_set.rb +++ /dev/null @@ -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 diff --git a/app/models/spree/shipment.rb b/app/models/spree/shipment.rb index e706c3dbac..637982f118 100644 --- a/app/models/spree/shipment.rb +++ b/app/models/spree/shipment.rb @@ -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 diff --git a/app/models/spree/tax_rate.rb b/app/models/spree/tax_rate.rb index d8b118ce57..064baf8852 100644 --- a/app/models/spree/tax_rate.rb +++ b/app/models/spree/tax_rate.rb @@ -71,6 +71,7 @@ module Spree amount: amount, source: order, originator: self, + order: order, state: "closed", label: label ) diff --git a/app/models/variant_override_set.rb b/app/models/variant_override_set.rb deleted file mode 100644 index d380df4144..0000000000 --- a/app/models/variant_override_set.rb +++ /dev/null @@ -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 diff --git a/app/services/sets/column_preference_set.rb b/app/services/sets/column_preference_set.rb new file mode 100644 index 0000000000..13cba40957 --- /dev/null +++ b/app/services/sets/column_preference_set.rb @@ -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 diff --git a/app/services/sets/enterprise_fee_set.rb b/app/services/sets/enterprise_fee_set.rb new file mode 100644 index 0000000000..d651649232 --- /dev/null +++ b/app/services/sets/enterprise_fee_set.rb @@ -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 diff --git a/app/services/sets/enterprise_set.rb b/app/services/sets/enterprise_set.rb new file mode 100644 index 0000000000..bb0b2bdc3c --- /dev/null +++ b/app/services/sets/enterprise_set.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Sets + class EnterpriseSet < ModelSet + def initialize(collection, attributes = {}) + super(Enterprise, collection, attributes) + end + end +end diff --git a/app/services/sets/model_set.rb b/app/services/sets/model_set.rb new file mode 100644 index 0000000000..5ecd2cddab --- /dev/null +++ b/app/services/sets/model_set.rb @@ -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 diff --git a/app/services/sets/order_cycle_set.rb b/app/services/sets/order_cycle_set.rb new file mode 100644 index 0000000000..663f684062 --- /dev/null +++ b/app/services/sets/order_cycle_set.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Sets + class OrderCycleSet < ModelSet + def initialize(collection, attributes = {}) + super(OrderCycle, collection, attributes) + end + end +end diff --git a/app/services/sets/product_set.rb b/app/services/sets/product_set.rb new file mode 100644 index 0000000000..4563bce0bb --- /dev/null +++ b/app/services/sets/product_set.rb @@ -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 diff --git a/app/services/sets/variant_override_set.rb b/app/services/sets/variant_override_set.rb new file mode 100644 index 0000000000..3240846e54 --- /dev/null +++ b/app/services/sets/variant_override_set.rb @@ -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 diff --git a/app/views/admin/customers/index.html.haml b/app/views/admin/customers/index.html.haml index 2ee8bc16a3..6653b9d386 100644 --- a/app/views/admin/customers/index.html.haml +++ b/app/views/admin/customers/index.html.haml @@ -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 diff --git a/app/views/admin/enterprise_fees/_calculator_settings.html.haml b/app/views/admin/enterprise_fees/_calculator_settings.html.haml index f505f0c38a..be05330d6c 100644 --- a/app/views/admin/enterprise_fees/_calculator_settings.html.haml +++ b/app/views/admin/enterprise_fees/_calculator_settings.html.haml @@ -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? diff --git a/app/views/admin/enterprises/_enterprise_user_index.html.haml b/app/views/admin/enterprises/_enterprise_user_index.html.haml index eedfba8d34..b8426b104b 100644 --- a/app/views/admin/enterprises/_enterprise_user_index.html.haml +++ b/app/views/admin/enterprises/_enterprise_user_index.html.haml @@ -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') diff --git a/app/views/admin/enterprises/form/_primary_details.html.haml b/app/views/admin/enterprises/form/_primary_details.html.haml index a6a09106b3..5fccc81904 100644 --- a/app/views/admin/enterprises/form/_primary_details.html.haml +++ b/app/views/admin/enterprises/form/_primary_details.html.haml @@ -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'}" } } diff --git a/app/views/admin/order_cycles/_loading_flash.html.haml b/app/views/admin/order_cycles/_loading_flash.html.haml index 05aff6b748..ffd05c59b1 100644 --- a/app/views/admin/order_cycles/_loading_flash.html.haml +++ b/app/views/admin/order_cycles/_loading_flash.html.haml @@ -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' } } diff --git a/app/views/admin/subscriptions/_loading_flash.html.haml b/app/views/admin/subscriptions/_loading_flash.html.haml index ab37f06a71..130d92bb18 100644 --- a/app/views/admin/subscriptions/_loading_flash.html.haml +++ b/app/views/admin/subscriptions/_loading_flash.html.haml @@ -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') diff --git a/app/views/admin/variant_overrides/_loading_flash.html.haml b/app/views/admin/variant_overrides/_loading_flash.html.haml index d9b2bf46bc..7832fd2160 100644 --- a/app/views/admin/variant_overrides/_loading_flash.html.haml +++ b/app/views/admin/variant_overrides/_loading_flash.html.haml @@ -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') diff --git a/app/views/components/_spinner.html.haml b/app/views/components/_spinner.html.haml new file mode 100644 index 0000000000..8d0a371204 --- /dev/null +++ b/app/views/components/_spinner.html.haml @@ -0,0 +1 @@ +%img.spinner{ src: image_path("spinning-circles.svg"), style: "max-width: 100%" } diff --git a/app/views/payment_mailer/authorize_payment.html.haml b/app/views/payment_mailer/authorize_payment.html.haml new file mode 100644 index 0000000000..438a51c416 --- /dev/null +++ b/app/views/payment_mailer/authorize_payment.html.haml @@ -0,0 +1,2 @@ += t('.instructions', distributor: @payment.order.distributor.name, amount: @payment.display_amount) += main_app.authorize_payment_url(@payment) diff --git a/app/views/payment_mailer/authorize_payment.text.haml b/app/views/payment_mailer/authorize_payment.text.haml new file mode 100644 index 0000000000..956e63ec9f --- /dev/null +++ b/app/views/payment_mailer/authorize_payment.text.haml @@ -0,0 +1,3 @@ += t('.instructions', distributor: @payment.order.distributor.name, amount: @payment.display_amount) + += main_app.authorize_payment_url(@payment) diff --git a/app/views/producers/_fat.html.haml b/app/views/producers/_fat.html.haml index 227e5dca39..02c9bf6fef 100644 --- a/app/views/producers/_fat.html.haml +++ b/app/views/producers/_fat.html.haml @@ -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 diff --git a/app/views/registration/steps/_logo.html.haml b/app/views/registration/steps/_logo.html.haml index 4082defaec..06de18ff4d 100644 --- a/app/views/registration/steps/_logo.html.haml +++ b/app/views/registration/steps/_logo.html.haml @@ -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") diff --git a/app/views/registration/steps/_promo.html.haml b/app/views/registration/steps/_promo.html.haml index efe29f6994..3a9d94db75 100644 --- a/app/views/registration/steps/_promo.html.haml +++ b/app/views/registration/steps/_promo.html.haml @@ -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") diff --git a/app/views/shop/products/_form.html.haml b/app/views/shop/products/_form.html.haml index 9506cd4bcd..00255beefc 100644 --- a/app/views/shop/products/_form.html.haml +++ b/app/views/shop/products/_form.html.haml @@ -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 diff --git a/app/views/shops/_fat.html.haml b/app/views/shops/_fat.html.haml index 289b1d3dc0..a2522271ca 100644 --- a/app/views/shops/_fat.html.haml +++ b/app/views/shops/_fat.html.haml @@ -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"} diff --git a/app/views/shops/_hubs.html.haml b/app/views/shops/_hubs.html.haml index 1d19839a03..243f2e7c63 100644 --- a/app/views/shops/_hubs.html.haml +++ b/app/views/shops/_hubs.html.haml @@ -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' diff --git a/app/views/spree/admin/orders/_shipment_manifest.html.haml b/app/views/spree/admin/orders/_shipment_manifest.html.haml index 8798fd00b6..51d1571735 100644 --- a/app/views/spree/admin/orders/_shipment_manifest.html.haml +++ b/app/views/spree/admin/orders/_shipment_manifest.html.haml @@ -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) diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 9949b4495c..50fff333aa 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -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") diff --git a/app/views/spree/admin/orders/index.html.haml b/app/views/spree/admin/orders/index.html.haml index d2871fe540..b72a0506a6 100644 --- a/app/views/spree/admin/orders/index.html.haml +++ b/app/views/spree/admin/orders/index.html.haml @@ -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') diff --git a/app/views/spree/admin/products/index/_header.html.haml b/app/views/spree/admin/products/index/_header.html.haml index 943abc021c..e48b5b7a89 100644 --- a/app/views/spree/admin/products/index/_header.html.haml +++ b/app/views/spree/admin/products/index/_header.html.haml @@ -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) diff --git a/app/views/spree/admin/products/index/_indicators.html.haml b/app/views/spree/admin/products/index/_indicators.html.haml index 9c002b1f1f..ae68aa7b00 100644 --- a/app/views/spree/admin/products/index/_indicators.html.haml +++ b/app/views/spree/admin/products/index/_indicators.html.haml @@ -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' } diff --git a/app/views/spree/admin/products/new.js.erb b/app/views/spree/admin/products/new.js.erb deleted file mode 100644 index 33c38aac13..0000000000 --- a/app/views/spree/admin/products/new.js.erb +++ /dev/null @@ -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') %>'); diff --git a/app/views/spree/admin/shared/_configuration_menu.html.haml b/app/views/spree/admin/shared/_configuration_menu.html.haml index 3e5790beb5..d9d97f7915 100644 --- a/app/views/spree/admin/shared/_configuration_menu.html.haml +++ b/app/views/spree/admin/shared/_configuration_menu.html.haml @@ -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 diff --git a/config/environments/production.rb b/config/environments/production.rb index 7541e78ae1..695f61a0a1 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -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 diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 7541e78ae1..695f61a0a1 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -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 diff --git a/config/environments/test.rb b/config/environments/test.rb index f84b7f5f7f..c607e32bdc 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -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 diff --git a/config/initializers/cache_store.rb b/config/initializers/cache_store.rb index 009ebbaab6..a67a4dd6f2 100644 --- a/config/initializers/cache_store.rb +++ b/config/initializers/cache_store.rb @@ -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 diff --git a/config/initializers/datadog.rb b/config/initializers/datadog.rb index 9ad16355fc..d6e1acde4b 100644 --- a/config/initializers/datadog.rb +++ b/config/initializers/datadog.rb @@ -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 diff --git a/config/initializers/db2fog.rb b/config/initializers/db2fog.rb index ba5b9c7e1d..5da84a30e4 100644 --- a/config/initializers/db2fog.rb +++ b/config/initializers/db2fog.rb @@ -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 diff --git a/config/initializers/js_template_helpers.rb b/config/initializers/js_template_helpers.rb index 27fffc9655..7ff1e9a0b0 100644 --- a/config/initializers/js_template_helpers.rb +++ b/config/initializers/js_template_helpers.rb @@ -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 diff --git a/config/initializers/spree.rb b/config/initializers/spree.rb index 56f2e3325c..09e404fc9f 100644 --- a/config/initializers/spree.rb +++ b/config/initializers/spree.rb @@ -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'] diff --git a/config/locales/ar.yml b/config/locales/ar.yml index 527ad3590c..75f99d1519 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -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: إعدادت الحساب diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 158d5ad619..b6aa00bd8f 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -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, s’eliminaran 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 d’administració." + 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 d’una botiga, accepteu autoritzar la botiga perquè enviï instruccions a la institució financera que va emetre la vostra targeta per fer els pagaments d’acord 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" diff --git a/config/locales/de_DE.yml b/config/locales/de_DE.yml index 666f362eea..a4819724fa 100644 --- a/config/locales/de_DE.yml +++ b/config/locales/de_DE.yml @@ -17,7 +17,7 @@ de_DE: source: Quelle spree/product: primary_taxon: "Produktkategorie" - supplier: "Anbieter" + supplier: "Lieferant" shipping_category_id: "Versandkategorie" variant_unit: "Varianteneinheit" variant_unit_name: "Name der Varianteneinheit" @@ -59,7 +59,7 @@ de_DE: end_at: "Ende" distributor_ids: "Hubs" producer_ids: "Erzeuger" - order_cycle_ids: "Bestellrunden" + order_cycle_ids: "Bestellzyklen" enterprise_fee_ids: "Gebührennamen" shipping_method_ids: "Lieferart" payment_method_ids: "Zahlungsarten" @@ -90,7 +90,7 @@ de_DE: send_instructions: "Sie erhalten in einigen Minuten eine E-Mail mit Anweisungen zur Bestätigung Ihres Kontos." failed_to_send: "Beim Senden der Bestätigungs-E-Mail ist ein Fehler aufgetreten." resend_confirmation_email: "Bestätigungsmail erneut senden." - confirmed: "Danke für die Bestätigung Ihrer E-Mail! Sie können sich jetzt einloggen." + confirmed: "Danke für die Bestätigung Ihrer E-Mail-Adresse! Sie können sich jetzt einloggen." not_confirmed: "Ihre E-Mail-Adresse konnte nicht bestätigt werden. Vielleicht haben Sie diesen Schritt bereits abgeschlossen?" user_confirmations: spree_user: @@ -111,7 +111,7 @@ de_DE: logged_in_succesfully: "Erfolgreich eingeloggt" user_passwords: spree_user: - updated_not_active: "Ihr Passwort wurde zurückgesetzt, aber ihre E-Mail muss noch bestätigt werden." + updated_not_active: "Ihr Passwort wurde zurückgesetzt, aber ihre E-Mail-Adresse muss noch bestätigt werden." updated: "Ihr Kennwort wurde erfolgreich geändert. Sie sind jetzt angemeldet." send_instructions: "Sie erhalten in einigen Minuten eine E-Mail mit Anweisungen zur Bestätigung Ihres Kontos." models: @@ -126,7 +126,7 @@ de_DE: invalid_element_error: "muss nur gültige Ganzzahlen enthalten" datetime_picker_ui: current_text: Jetzt - close_text: Erledigt + close_text: Fertig time_text: Zeit enterprise_mailer: confirmation_instructions: @@ -135,9 +135,9 @@ de_DE: subject: "%{enterprise} ist jetzt auf %{sitename}" email_welcome: "Willkommen" email_registered: "ist jetzt Teil von" - email_userguide_html: "Das Benutzerhandbuch mit detaillierter Unterstützung für die Einrichtung Ihres Produzenten oder Hubs finden Sie hier: %{link}" - userguide: "Öffnen Sie das Food Network-Benutzerhandbuch" - email_admin_html: "Sie können Ihr Konto verwalten, indem Sie sich bei %{link} anmelden oder auf das Zahnrad oben rechts auf der Startseite klicken und Administration auswählen." + email_userguide_html: "Das Benutzerhandbuch mit detaillierter Unterstützung für die Einrichtung Ihres Erzeugerprofils oder Ladens finden Sie hier: %{link}" + userguide: "Open Food Network Benutzerhandbuch" + email_admin_html: "Sie können Ihr Konto verwalten, indem Sie sich direkt über den Link %{link} anmelden oder indem Sie sich auf der Startseite einloggen und im Menü \"Verwaltung\" auswählen." admin_panel: "Administrationsmenü" email_community_html: "Wir haben auch ein Online-Forum für Community-Diskussionen in Bezug auf OFN-Software und die einzigartigen Herausforderungen eines Lebensmittelunternehmens. Reden Sie doch mit. Wir entwickeln uns ständig weiter und Ihr Beitrag in diesem Forum prägt, was als nächstes passiert. %{link}" join_community: "Treten Sie der Community bei" @@ -145,10 +145,10 @@ de_DE: subject: "%{enterprise} hat Sie eingeladen, ein Manager zu sein" producer_mailer: order_cycle: - subject: "Bestellungszyklusreport für %{producer}" + subject: "Bestellzyklusbericht für %{producer}" shipment_mailer: shipped_email: - dear_customer: "Sehr geehrter Kunde," + dear_customer: "Sehr geehrte Kundin, sehr geehrter Kunde," instructions: "Ihre Bestellung wurde versandt" shipment_summary: "Übersicht" subject: "Versandbenachrichtigung" @@ -183,18 +183,18 @@ de_DE: explainer: Diese Bestellungen wurden bereits als vollständig markiert und daher nicht verändert processing: title: Fehler aufgetreten (%{count} Bestellungen) - explainer: Die automatische Verarbeitung dieser Aufträge ist aufgrund eines Fehlers fehlgeschlagen. Der Fehler wurde soweit möglich aufgelistet. + explainer: Die automatische Bearbeitung dieser Bestellungen ist aufgrund eines Fehlers fehlgeschlagen. Der Fehler wurde soweit möglich aufgelistet. failed_payment: title: Fehlgeschlagene Zahlung (%{count} Bestellungen) explainer: Die automatische Zahlungsabwicklung für diese Bestellungen ist aufgrund eines Fehlers fehlgeschlagen. Der Fehler wurde soweit möglich aufgelistet. other: title: Andere Fehler (%{count} Bestellungen) - explainer: Die automatische Verarbeitung dieser Aufträge ist aus einem unbekannten Grund fehlgeschlagen. Dies sollte nicht geschehen, bitte kontaktieren Sie uns, wenn Sie dies sehen. + explainer: Die automatische Bearbeitung dieser Bestellungen ist aus einem unbekannten Grund fehlgeschlagen. Dies sollte nicht geschehen, bitte kontaktieren Sie uns, wenn Sie dies sehen. home: "OFN" title: "Open Food Network" welcome_to: "Willkommen bei" - site_meta_description: "Wir starten ganz grundsätzlich. Mit LandwirtInnen und GärtnerInnen, die stolz und ehrlich ihre Geschichte erzählen. Mit Lieferanten, die Menschen fair und vertrauenswürdig mit Produkten verbinden. Mit KonsumentInnen, die glauben, daß ihre wöchentlichen Einkaufsentscheidungen..." - search_by_name: Suche nach Name oder Ort... + site_meta_description: "Wir starten ganz grundsätzlich. Mit LandwirtInnen und GärtnerInnen, die stolz und ehrlich ihre Geschichte erzählen. Mit Lieferanten, die Menschen fair und vertrauenswürdig mit Produkten verbinden. Mit KonsumentInnen, die glauben, dass ihre wöchentlichen Einkaufsentscheidungen ..." + search_by_name: Suche nach Ort oder Name des Ladens/Erzeugers... producers_join: 'Wir laden Deutsche Produzenten ein, jetzt dem Open Food Network beizutreten. ' charges_sales_tax: Berechnet Steuern? print_invoice: "Rechnung drucken" @@ -216,20 +216,20 @@ de_DE: ongoing: Laufend bill_address: Rechnungsadresse ship_address: Lieferanschrift - sort_order_cycles_on_shopfront_by: "Sortiere die Bestellungszyklen des Ladens nach" + sort_order_cycles_on_shopfront_by: "Sortiere die Bestellzyklen des Ladens nach" required_fields: Die erforderlichen Felder sind mit * markiert select_continue: Auswählen und fortfahren remove: Entfernen or: oder collapse_all: Alle Details verbergen expand_all: Alle Details anzeigen - loading: Laden... + loading: Wird geladen ... show_more: Mehr anzeigen show_all: Alles anzeigen show_all_with_more: "Alles anzeigen (%{num} mehr)" cancel: Abbrechen - edit: Bearbeite - clone: Klonen + edit: Bearbeiten + clone: Duplizieren distributors: Verteiler bulk_order_management: Massenbearbeitung von Bestellungen enterprises: Unternehmen @@ -247,8 +247,8 @@ de_DE: unused: ungebraucht admin_and_handling: Verwaltung & Handhabung profile: Profil - supplier_only: Nur Anbieter - has_shopfront: Hat Shopfront + supplier_only: Nur Lieferant + has_shopfront: mit Laden weight: Gewicht volume: Volumen items: Artikel @@ -264,10 +264,10 @@ de_DE: allow_cookies: "Cookies erlauben" notes: Anmerkungen error: Fehler - processing_payment: "Bezahlung wird verarbeitet..." + processing_payment: "Bezahlung wird verarbeitet ..." no_pending_payments: "Keine ausstehenden Zahlungen" invalid_payment_state: "Ungültiger Zahlungsstatus" - filter_results: Ergebnisse filtern + filter_results: Suchen quantity: Menge pick_up: Abholen copy: Kopieren @@ -277,7 +277,7 @@ de_DE: reset_password_token: Passwort-Token zurücksetzen expired: abgelaufen ist, fordern Sie bitte ein neues an back_to_payments_list: "Zurück zur Zahlungsliste" - maestro_or_solo_cards: "Maestro / Solo-Karten" + maestro_or_solo_cards: "Maestro/Solo-Karten" backordered: "Nachbestellt" on hand: "Verfügbar" ship: "Liefern" @@ -311,7 +311,7 @@ de_DE: on_hand: Verfügbar on_demand: Unbegrenzt on_demand?: Unbegrenzt? - order_cycle: Bestellungszyklus + order_cycle: Bestellzyklus payment: Zahlung payment_method: Zahlungsart phone: Telefonnummer @@ -333,19 +333,19 @@ de_DE: items: Artikel select_all: Alles auswählen quick_search: Schnellsuche - clear_all: Alle leeren + clear_all: Alle zurücksetzen start_date: "Anfangsdatum" end_date: "Enddatum" form_invalid: "Das Formular beinhaltet fehlende oder ungültige Felder" - clear_filters: Filter zurücksetzen - clear: Leeren + clear_filters: Zurücksetzen + clear: Zurücksetzen save: Speichern cancel: Abrechen back: Zurück show_more: Mehr zeigen show_n_more: '%{num} mehr zeigen' - choose: "Wählen..." - please_select: Bitte auswählen... + choose: "Auswählen ..." + please_select: Bitte auswählen ... column_save_as_default: Als Standard speichern columns: Spalten actions: Aktionen @@ -377,7 +377,7 @@ de_DE: settings: "Einstellungen" stripe_connect_enabled: Läden erlauben, Zahlungen über Stripe Connect anzunehmen? no_api_key_msg: Für dieses Unternehmen existiert kein Stripe-Konto. - configuration_explanation_html: Detaillierte Anweisungen zur Konfiguration der Stripe Connect-Integration finden Sie unter der Anleitung . + configuration_explanation_html: Detaillierte Anweisungen zur Konfiguration der Stripe Connect-Integration finden Sie in der Anleitung. status: Status ok: OK instance_secret_key: Instanzgeheimschlüssel @@ -393,14 +393,14 @@ de_DE: matomo_url: "Matomo-URL" matomo_site_id: "Matomo-Site-ID" matomo_tag_manager_url: "Matomo Tag Manager URL" - info_html: "Matomo ist eine Web- und Mobile Analytics-Anwendung. Sie können Matomo entweder lokal hosten oder einen in der Cloud gehosteten Dienst verwenden. Weitere Informationen finden Sie unter matomo.org ." + info_html: "Matomo ist eine Web- und Mobile Analytics-Anwendung. Sie können Matomo entweder lokal hosten oder einen in der Cloud gehosteten Dienst verwenden. Weitere Informationen finden Sie unter matomo.org." config_instructions_html: "Hier können Sie die OFN Matomo Integration konfigurieren. Die unten angegebene Matomo-URL sollte auf die Matomo-Instanz verweisen, an die die Benutzerverfolgungsinformationen gesendet werden. Wenn es leer bleibt, wird das Matomo-Benutzer-Tracking deaktiviert. Das Feld Site-ID ist nicht obligatorisch, aber nützlich, wenn Sie mehr als eine Website in einer einzelnen Matomo-Instanz verfolgen. Es kann auf der Matomo-Instanzkonsole gefunden werden." config_instructions_tag_manager_html: "Durch Festlegen der Matomo Tag Manager-URL wird Matomo Tag Manager aktiviert. Mit diesem Tool können Sie Analyseereignisse einrichten. Die Matomo Tag Manager-URL wird aus dem Abschnitt Installationscode von Matomo Tag Manager kopiert. Stellen Sie sicher, dass Sie den richtigen Container und die richtige Umgebung auswählen, da diese Optionen die URL ändern." customers: index: new_customer: "Neuer Kunde" - code: Kode - duplicate_code: "Dieser Kode wird bereits verwendet." + code: Code + duplicate_code: "Dieser Code wird bereits verwendet." bill_address: "Rechnungsadresse" ship_address: "Lieferadresse" balance: "Saldo" @@ -414,7 +414,7 @@ de_DE: edit: "Bearbeiten" update_address: "Adresse aktualisieren" confirm_delete: "Sicher zu löschen?" - search_by_email: "Suche nach Email/Kode" + search_by_email: "Suche nach E-Mail/Code" guest_label: "Gäste Kasse" credit_owed: "Gutschrift geschuldet" balance_due: "Restbetrag fällig" @@ -443,7 +443,7 @@ de_DE: calculator: "Rechner" calculator_values: "Werte des Rechners" search: "Suche" - name_placeholder: "zB Verpackungsgebühr" + name_placeholder: "z.B. Verpackungsgebühr" enterprise_groups: index: new_button: Neue Unternehmensgruppe @@ -457,7 +457,7 @@ de_DE: index: unit: Einheit display_as: Angezeigt als - category: Kategorie + category: Produktkategorie tax_category: Steuerkategorie inherits_properties?: Vererbt Eigenschaften available_on: Verfügbar am @@ -497,7 +497,7 @@ de_DE: no_product: hat keine Produkte in der Datenbank gefunden not_found: nicht in der Datenbank gefunden not_updatable: kann über den Produktimport nicht auf bestehende Produkte aktualisiert werden - blank: kann nicht leer sein + blank: darf nicht leer sein products_no_permission: Sie sind nicht berechtigt, Produkte für dieses Unternehmen zu verwalten inventory_no_permission: Sie sind nicht berechtigt, Bestand für diesen Produzenten zu erstellen none_saved: hat keine Produkte erfolgreich gespeichert @@ -617,19 +617,19 @@ de_DE: order_date: "Abgeschlossen am" max: "Max" product_unit: "Produkt: Einheit" - weight_volume: "Gewicht / Volumen (g)" + weight_volume: "Gewicht/Volumen (g)" ask: "Fragen?" page_title: "Massenbearbeitung von Bestellungen" actions_delete: "Ausgewählte löschen" loading: "Bestellungen werden geladen" no_results: "Keine Bestellungen gefunden." - group_buy_unit_size: "Gruppen-Kauf Einheitsgröße" + group_buy_unit_size: "Gruppenkauf Einheit" total_qtt_ordered: "Gesamtmenge bestellt" max_qtt_ordered: "Max Menge bestellt" current_fulfilled_units: "Aktuelle erfüllte Einheiten" max_fulfilled_units: "Max erfüllte Einheiten" - order_error: "Fehler müssen behoben werden, bevor Sie Aufträge aktualisieren können.\nFelder mit roten Rahmen enthalten Fehler." - variants_without_unit_value: "WARNUNG: Manche Varianten haben keinen Einheitswert" + order_error: "Bevor Sie Bestellungen aktualisieren können, müssen Fehler behoben werden.\nFelder mit roten Rahmen enthalten Fehler." + variants_without_unit_value: "WARNUNG: Manche Varianten haben keine Einheit" select_variant: "Wählen Sie eine Variante" enterprise: select_outgoing_oc_products_from: Wählen Sie ausgehende BZ-Produkte von @@ -685,8 +685,8 @@ de_DE: text1: Sie verwalten optional Ihre Lagerbestände und Preise auch in Ihrem inventory: Katalog text2: > - Wenn Sie das Katalog-Tool verwenden, können Sie auswählen, ob neue Produkte, - die von Ihren Anbieter hinzugefügt wurden, zu Ihrem Katalog hinzugefügt + Wenn Sie den Katalog verwenden, können Sie auswählen, ob neue Produkte, + die von Ihren Lieferanten hinzugefügt wurden, zu Ihrem Katalog hinzugefügt werden müssen, bevor sie verkauft werden können. Wenn Sie Ihren Katalog nicht zur Verwaltung Ihrer Produkte verwenden, sollten Sie die folgende "empfohlene" Option auswählen: @@ -718,7 +718,7 @@ de_DE: visible: öffentlich sichtbar not_visible: nicht sichtbar permalink: Permalink (keine Leerzeichen) - permalink_tip: "Dieser Permalink wird verwendet, um die URL zu Ihrem Laden zu erstellen: %{link}name-ihres-ladens / laden" + permalink_tip: "Dieser Permalink wird verwendet, um die URL zu Ihrem Laden zu erstellen: %{link}name-ihres-ladens/laden" link_to_front: Link zum Laden link_to_front_tip: Ein direkter Link zu Ihrem Laden im Open Food Network. ofn_uid: OFN UID @@ -729,10 +729,10 @@ de_DE: manage: "Lieferarten verwalten" create_button: "Neue Lieferart erstellen" create_one_button: "Erstelle jetzt eine" - no_method_yet: "Sie haben noch keine Lieferarten." + no_method_yet: "Sie haben noch keine Lieferarten angelegt." shop_preferences: shopfront_requires_login: "Öffentlich sichtbarer Laden?" - shopfront_requires_login_tip: "Wählen Sie aus, ob sich Kunden anmelden müssen, um den Laden zu sehen oder ob sie für alle sichtbar sind." + shopfront_requires_login_tip: "Wählen Sie aus, ob sich Kunden einloggen müssen, um den Laden zu sehen oder ob er für alle sichtbar ist." shopfront_requires_login_false: "Öffentlich" shopfront_requires_login_true: "Nur für registrierte Nutzer sichtbar" recommend_require_login: "Wenn Bestellungen nachträglich geändert werden dürfen, empfehlen wir Einkauf nur für eingeloggte Nutzer." @@ -742,7 +742,7 @@ de_DE: allow_guest_orders_true: "Gasteinkauf erlauben" allow_order_changes: "Bestellungen nachträglich ändern" allow_order_changes_tip: "Kunden erlauben, ihre Bestellung zu ändern, solange der Bestellzyklus offen ist." - allow_order_changes_false: "Bestellungen können nicht geändert / storniert werden" + allow_order_changes_false: "Bestellungen können nicht geändert/storniert werden" allow_order_changes_true: "Kunden können Bestellungen ändern oder stornieren, während der Bestellzyklus geöffnet ist" enable_subscriptions: "Abonnements" enable_subscriptions_tip: "Abo-Funktionalität aktivieren?" @@ -752,20 +752,20 @@ de_DE: customer_names_tip: "Ermöglichen Sie Ihren Lieferanten, die Namen Ihrer Kunden in Berichten anzuzeigen" customer_names_false: "deaktiviert" customer_names_true: "aktiviert" - shopfront_message: "Laden-Nachricht" + shopfront_message: "'Willkommen'-Nachricht im Laden" shopfront_message_placeholder: > Eine optionale Nachricht, um Kunden willkommen zu heißen und zu erklären, wie Sie bei Ihnen einkaufen können. Wenn hier Text eingegeben wird, - wird dieser in einem Home-Tab in Ihrem Shop angezeigt, wenn Kunden zum - ersten Mal ihren Shop besuchen. - shopfront_message_link_tooltip: "Link einfügen / bearbeiten" - shopfront_message_link_prompt: "Bitte geben Sie eine einzufügende URL ein" - shopfront_closed_message: "Laden Geschlossen Nachricht" + wird dieser in einem Home-Tab in Ihrem Laden angezeigt, wenn Kunden + ihn zum ersten Mal besuchen. + shopfront_message_link_tooltip: "Link einfügen/bearbeiten" + shopfront_message_link_prompt: "Bitte geben Sie die einzufügende URL ein:" + shopfront_closed_message: "'Laden geschlossen'-Nachricht" shopfront_closed_message_placeholder: > Eine Nachricht, die eine detailliertere Erklärung liefert, warum Ihr - Laden geschlossen ist und / oder wann Kunden erwarten können, dass er + Laden geschlossen ist und/oder wann Kunden erwarten können, dass er wieder geöffnet wird. Dies wird in Ihrem Laden nur angezeigt, wenn Sie - keine aktiven Bestellzyklen haben (d.h. Laden ist geschlossen). + keine aktiven Bestellzyklen haben (d.h. der Laden ist geschlossen). shopfront_category_ordering: "Ordnung der Produktkategorien im Laden" shopfront_category_ordering_note: "(oben nach unten)" open_date: "Öffnungsdatum" @@ -782,18 +782,18 @@ de_DE: disconnect: "Trennen Sie das Konto" confirm_modal: title: Stripe integrieren - part1: Stripe ist ein Zahlungsverarbeitungsdienst, der es Geschäften im OFN ermöglicht, Kreditkartenzahlungen von Kunden zu akzeptieren. - part2: Um diese Funktion zu verwenden, müssen Sie Ihr Stripe-Konto mit dem OFN verbinden. Klicken Sie "Zustimmen", um auf die Stripe-Website weitergeleitet zu werden, wo Sie ein bestehendes Stripe-Konto verbinden oder ein neues erstellen können. + part1: Stripe ist ein Zahlungsdienst, der es Läden im Open Food Network ermöglicht, Kreditkartenzahlungen von Kunden zu akzeptieren. + part2: Um diese Funktion zu verwenden, müssen Sie Ihr Stripe-Konto mit dem Open Food Network verbinden. Klicken Sie "Zustimmen", um auf die Stripe-Website weitergeleitet zu werden, wo Sie ein bestehendes Stripe-Konto verbinden oder ein neues erstellen können. part3: Dadurch kann das Open Food Network Kreditkartenzahlungen von Kunden in Ihrem Namen akzeptieren. Bitte beachten Sie, dass Sie ein eigenes Stripe-Konto unterhalten müssen, die Gebühren für Stripe-Gebühren bezahlen und etwaige Rückbuchungen und Kundenservice selbst vornehmen müssen. i_agree: Zustimmen cancel: Stornieren tag_rules: default_rules: by_default: Standardmäßig - no_rules_yet: Es gelten noch keine Standardregeln + no_rules_yet: Es gelten noch keine Standardregeln. add_new_button: '+ Fügen Sie eine neue Standardregel hinzu' - no_tags_yet: Für dieses Unternehmen sind noch keine Stichwörter vorhanden - no_rules_yet: Für dieses Stichwort gelten noch keine Regeln + no_tags_yet: Für dieses Unternehmen sind noch keine Stichwörter vorhanden. + no_rules_yet: Für dieses Stichwort gelten noch keine Regeln. for_customers_tagged: 'Für Kunden mit dem Stichwort:' add_new_rule: '+ Neue Regel hinzufügen' add_new_tag: '+ Neues Stichwort hinzufügen' @@ -832,27 +832,27 @@ de_DE: owner: Inhaber producer: Erzeuger change_type_form: - producer_profile: Erzeuger Profil - connect_ofn: Verbindung über OFN herstellen + producer_profile: Erzeugerprofil + connect_ofn: Stellen Sie sich und Ihre Produkte vor always_free: IMMER KOSTENLOS - producer_description_text: Fügen Sie Ihre Produkte zum Open Food Network hinzu, damit Hubs Ihre Produkte in ihren Läden anbieten können. + producer_description_text: "Stellen Sie sich und Ihre Produkte im Open Food Network vor und vernetzen Sie sich mit anderen Erzeugern und Läden. \nFügen Sie Ihre Produkte dem Profil hinzu, damit andere Läden sie zum Verkauf anbieten können. Wenn Sie Ihre Produkte selbst verkaufen möchten, wählen Sie \"Erzeugerladen\"." producer_shop: Erzeugerladen sell_your_produce: Verkaufen Sie Ihre eigenen Produkte - producer_shop_description_text: Verkaufen Sie Ihre Produkte direkt an Ihre Kunden durch Ihren eigenen Laden im Open Food Network. + producer_shop_description_text: Verkaufen Sie Ihre eigenen Produkte direkt an Ihre Kunden durch Ihren eigenen Erzeugerladen im Open Food Network. producer_shop_description_text2: Ein Erzeugerladen ist nur für Ihre eigenen Produkte bestimmt. Wenn Sie Produkte anderer verkaufen möchten, wählen Sie "Hub". producer_hub: Hub producer_hub_text: Verkaufen Sie eigene Produkte und Produkte anderer - producer_hub_description_text: Ihr Unternehmen ist das Rückgrat Ihres lokalen Lebensmittelsystems. Sie können sowohl Ihre eigenen Produkte, als auch Produkte von anderen Unternehmen über Ihren Laden im Open Food Network verkaufen. + producer_hub_description_text: Verkaufen Sie eigene Produkte und Produkte anderer Erzeuger oder Läden im Open Food Network. Mit Ihrem Laden sind Sie ein zentraler Bestandteil Ihres lokalen Lebensmittelsystems. profile: Nur Profil get_listing: Erstellen Sie einen Eintrag profile_description_text: Sie können im Open Food Network gefunden und kontaktiert werden. Ihr Unternehmen wird auf der Karte und in Suchergebnissen angezeigt. hub_shop: Hub hub_shop_text: Verkaufen Sie Produkte anderer - hub_shop_description_text: Ihr Unternehmen ist das Rückgrat Ihres lokalen Lebensmittelsystems. Sie aggregieren Produkte von anderen Unternehmen und verkaufen sie über Ihren Laden im Open Food Network. + hub_shop_description_text: Verkaufen Sie Produkte anderer Erzeuger oder Läden im Open Food Network. Als Händler führen Sie die Produkte zusammen und sind mit Ihrem Laden ein zentraler Bestandteil Ihres lokalen Lebensmittelsystems. choose_option: Bitte wählen Sie eine der oben aufgeführten Optionen. change_now: Jetzt ändern enterprise_user_index: - loading_enterprises: UNTERNEHMEN WERDEN GELADEN + loading_enterprises: UNTERNEHMEN WERDEN GELADEN ... no_enterprises_found: Keine Unternehmen gefunden. search_placeholder: Suche nach Name manage: Verwalten @@ -877,14 +877,14 @@ de_DE: next_step: Nächster Schritt choose_starting_point: 'Wählen Sie Ihr Paket:' profile: 'Profil' - producer_profile: 'Erzeuger Profil' + producer_profile: 'Erzeugerprofil' invite_manager: user_already_exists: "Benutzer existiert bereits" error: "Etwas ist schief gelaufen" order_cycles: loading_flash: - loading_order_cycles: LADEN VON AUFTRAGSZYKLEN - loading: WIRD GELADEN... + loading_order_cycles: LADE BESTELLZYKLEN + loading: WIRD GELADEN ... new: create: "Neu" cancel: "Abbrechen" @@ -900,7 +900,7 @@ de_DE: choose_products_from: "Wählen Sie Produkte von:" incoming: incoming: "Eingehend" - supplier: "Anbieter" + supplier: "Lieferant" products: "Produkte" receival_details: "Empfangsdetails" fees: "Gebühren" @@ -916,7 +916,7 @@ de_DE: tags: "Stichwörter" delivery_details: "Lieferdetails" fees: "Gebühren" - previous: "Bisherige" + previous: "Vorherige" save: "Speichern" save_and_back_to_list: "Speichern und zurück zur Liste" cancel: "Abbrechen" @@ -926,16 +926,16 @@ de_DE: incoming: "2. Eingehende Produkte" outgoing: "3. Ausgehende Produkte" exchange_form: - pickup_time_tip: Wenn Bestellungen von diesem OC für den Kunden bereit sind + pickup_time_tip: Wenn Bestellungen dieses Bestellzylus für den Kunden bereit sind pickup_instructions_placeholder: "Abholungsinformationen" pickup_instructions_tip: Diese Informationen werden Kunden nach Abschluss einer Bestellung angezeigt - pickup_time_placeholder: "Bereit für (dh Datum / Uhrzeit)" + pickup_time_placeholder: "Bereit für (d.h. Datum/Uhrzeit)" receival_instructions_placeholder: "Lieferinformation" add_fee: 'Gebühr hinzufügen' remove: 'Entfernen' selected: 'ausgewählt' add_exchange_form: - add_supplier: 'Anbieter hinzufügen' + add_supplier: 'Lieferant hinzufügen' add_distributor: 'Verteiler hinzufügen' advanced_settings: title: Erweiterte Einstellungen @@ -953,7 +953,7 @@ de_DE: form: general_settings: "Allgemeine Einstellungen" incoming: Eingehend - supplier: Anbieter + supplier: Lieferant receival_details: Lieferinformation fees: Gebühren outgoing: Ausgehend @@ -961,23 +961,23 @@ de_DE: products: Produkte tags: Stichwörter add_a_tag: Stichwort hinzufügen - delivery_details: Abhol- / Lieferinformationen + delivery_details: Abhol-/Lieferinformationen index: schedule: Zeitplan schedules: Zeitpläne new_schedule: Neuer Zeitplan name_and_timing_form: name: Name - orders_open: Bestellungen öffnen um + orders_open: Bestellzyklus öffnet um coordinator: Koordinator orders_close: Bestellungen schließen row: - suppliers: Anbieter + suppliers: Lieferanten distributors: Verteiler variants: Varianten simple_form: ready_for: Bereit am - ready_for_placeholder: Datum / Uhrzeit + ready_for_placeholder: Datum/Uhrzeit customer_instructions: Kundeninformation customer_instructions_placeholder: Abhol- oder Lieferscheine products: Produkte @@ -988,7 +988,7 @@ de_DE: bulk_update: no_data: Hm, etwas ist schief gelaufen. Keine Bestellzyklusdaten gefunden. date_warning: - msg: Dieser Bestellzyklus enthält %{n} offene Abonementbestellungen. Das Ändern des Datums wird bereits erteilte Bestellungen nicht beeinträchtigen, sollte wenn möglich aber vermieden werden. Sind Sie sicher, daß Sie fortfahren wollen? + msg: Dieser Bestellzyklus enthält %{n} offene Abonnementbestellungen. Das Ändern des Datums wird bereits erteilte Bestellungen nicht beeinträchtigen, sollte wenn möglich aber vermieden werden. Sind Sie sicher, dass Sie fortfahren wollen? cancel: Abbrechen proceed: Fortfahren producer_properties: @@ -1015,13 +1015,13 @@ de_DE: not_visible: "%{enterprise} ist nicht sichtbar und kann daher nicht auf der Karte oder in Suchen gefunden werden" reports: hidden: Ausgeblendet - unitsize: EINHEITSGRÖSSE + unitsize: EINHEIT total: SUMME total_items: GESAMTANZAHL - supplier_totals: Anbieter-Gesamtsummen - supplier_totals_by_distributor: Anbieter-Gesamtsummen nach Verteiler - totals_by_supplier: Verteiler-Gesamtsummen nach Anbieter - customer_totals: Kunden-Gesamtsummen + supplier_totals: Lieferanten-Gesamtsummen + supplier_totals_by_distributor: Lieferanten-Gesamtsummen nach Verteiler + totals_by_supplier: Verteiler-Gesamtsummen nach Lieferanten + customer_totals: Kundengesamtsummen all_products: Alle Produkte inventory: Aktueller Bestand lettuce_share: LettuceShare @@ -1032,7 +1032,7 @@ de_DE: tax_types: Steuerarten tax_rates: Steuersätze pack_by_customer: Packliste nach Kunde - pack_by_supplier: Packliste nach Anbieter + pack_by_supplier: Packliste nach Lieferanten orders_and_distributors: name: Bestellungen und Verteiler description: Bestellungen mit Verteilerdetails @@ -1073,7 +1073,7 @@ de_DE: title: "Abonnement bearbeiten" table: edit_subscription: Abonnement bearbeiten - pause_subscription: Abonement pausieren + pause_subscription: Abonnement pausieren unpause_subscription: Abonnement fortsetzen cancel_subscription: Abonnement beenden filters: @@ -1120,7 +1120,7 @@ de_DE: begins_at_placeholder: "Wählen Sie ein Datum" ends_at_placeholder: "Wahlweise" loading_flash: - loading: ABONNEMENTS WERDEN GELADEN + loading: ABONNEMENTS WERDEN GELADEN ... review: details: Einzelheiten address: Adresse @@ -1227,12 +1227,12 @@ de_DE: login: "Anmeldung" signup: "Anmelden" contact: "Kontakt" - require_customer_login: "Nur angemeldete Kunden können auf diesen Shop zugreifen." + require_customer_login: "Nur angemeldete Kunden können auf diesen Laden zugreifen." require_login_html: "Wenn Sie bereits ein angemeldeter Kunde sind, fahren Sie mit %{login} oder %{signup} fort." require_login_2_html: "Möchten Sie hier einkaufen? Bitte %{contact} %{enterprise} und fragen Sie nach dem Beitritt." require_customer_html: "Wenn Sie hier einkaufen möchten, fragen Sie bitte %{contact} %{enterprise} nach dem Beitritt." select_oc: - select_oc_html: "Bitte wählen Sie , wann Sie Ihre Bestellung wünschen, um zu sehen, welche Produkte verfügbar sind." + select_oc_html: "Bitte wählen Sie, wann Sie Ihre Bestellung wünschen, um zu sehen, welche Produkte verfügbar sind." products: summary: bulk: "Bulk" @@ -1253,7 +1253,7 @@ de_DE: tax_invoice: "Steuerrechnung" tax_total: "Steuersumme (%{rate}):" total_excl_tax: "Summe (ohne Steuern):" - total_incl_tax: "Gesamt (Inkl. Steuern):" + total_incl_tax: "Gesamt (inkl. Steuern):" abn: "USt-IdNr." acn: "St.-Nr." invoice_issued_on: "Rechnung ausgestellt am:" @@ -1271,7 +1271,7 @@ de_DE: menu_3_url: "/producers" menu_4_title: "Gruppen" menu_4_url: "/groups" - menu_5_title: "Über Uns" + menu_5_title: "Über uns" menu_5_url: "https://wp.openfoodnetwork.de/" menu_6_title: "Support" menu_6_url: "https://wp.openfoodnetwork.de/support/" @@ -1291,16 +1291,16 @@ de_DE: footer_pinterest_url: "Pinterest URL" footer_email: "E-Mail:" footer_links_md: "Links" - footer_about_url: "Über Uns URL" + footer_about_url: "Über uns URL" user_guide_link: "Benutzerhandbuch-Link" name: Name first_name: Vorname last_name: Nachname email: 'E-Mail:' phone: Telefonnummer - next: Weiter + next: Nächste address: Straße + Hausnummer - address_placeholder: z.B. Gartenstrasse 123 + address_placeholder: z.B. Gartenstraße 123 address2: Adresse (Fortsetzung) city: Ort city_placeholder: z.B. Nordwestheim @@ -1336,7 +1336,7 @@ de_DE: label_logout: "Ausloggen" label_signup: "Registrieren" label_administration: "Verwaltung" - label_admin: "Verwalter" + label_admin: "Verwaltung" label_account: "Konto" label_more: "Mehr anzeigen" label_less: "Weniger anzeigen" @@ -1344,7 +1344,7 @@ de_DE: cart_items: "Artikel" cart_headline: "Ihr Warenkorb" total: "Total" - cart_updating: "Warenkorb aktualisieren..." + cart_updating: "Warenkorb wird aktualisiert ..." cart_empty: "Warenkorb leer" cart_edit: "Warenkorb bearbeiten" item: "Artikel" @@ -1361,7 +1361,7 @@ de_DE: add_a_card: Karte hinzufügen add_card: Karte hinzufügen you_have_no_saved_cards: Sie haben noch keine Karten gespeichert - saving_credit_card: Kreditkarte speichern ... + saving_credit_card: Kreditkarte wird gespeichert ... card_has_been_removed: "Ihre Karte wurde entfernt (Nummer: %{number})" card_could_not_be_removed: Die Karte konnte nicht entfernt werden invalid_credit_card: "Ungültige Kreditkarte" @@ -1375,7 +1375,7 @@ de_DE: cookies_policy: header: "Wie wir Cookies verwenden" desc_part_1: "Cookies sind sehr kleine Textdateien, die beim Besuch mancher Websiten auf Ihrem Computer gespeichert werden." - desc_part_2: "In OFN respektieren wir Ihre Privatsphäre. Wir verwenden nur die Cookies, die notwendig sind, um Ihnen den Service des Online-Kaufs / -Verkaufs von Lebensmitteln zu bieten. Wir verkaufen keine Ihrer Daten. Wir könnten Ihnen in Zukunft vorschlagen, einige Ihrer Daten zu teilen, um neue Commons-Dienste zu entwickeln, die für das Ökosystem nützlich sein könnten (wie Logistikdienstleistungen für kurze Nahrungsmittelsysteme), aber wir sind noch nicht dort, und wir werden es nicht ohne Ihr tun Genehmigung :-)" + desc_part_2: "Das Open Food Network respektiert Ihre Privatsphäre. Wir verwenden nur die Cookies, die notwendig sind, um Ihnen den Service des Online-Kaufs/-Verkaufs von Lebensmitteln zu bieten. Wir verkaufen keine Ihrer Daten. Wir könnten Ihnen in Zukunft vorschlagen, einige Ihrer Daten zu teilen, um neue Commons-Dienste zu entwickeln, die für das Ökosystem nützlich sein könnten (wie Logistikdienstleistungen für regionale Nahrungsmittelsysteme), aber wir sind noch nicht dort, und wir werden es nicht ohne Ihre Genehmigung tun. :-)" desc_part_3: "Wir verwenden Cookies hauptsächlich, um sich daran zu erinnern, wer Sie sind, wenn Sie sich bei dem Dienst anmelden oder sich die Artikel merken können, die Sie in Ihren Warenkorb legen, auch wenn Sie nicht eingeloggt sind \"Cookies akzeptieren\" nehmen wir an, dass Sie uns die Speicherung von Cookies erlauben, die für das Funktionieren der Website notwendig sind. Hier ist die Liste der von uns verwendeten Cookies!" essential_cookies: "Essentielle Kekse" essential_cookies_desc: "Die folgenden Cookies sind für den Betrieb unserer Website unbedingt erforderlich." @@ -1385,10 +1385,10 @@ de_DE: cookie_consent_desc: "Wird verwendet, um den Status der Benutzerzustimmung zum Speichern von Cookies beizubehalten" cookie_remember_me_desc: "Wird verwendet, wenn der Benutzer die Website aufgefordert hat, sich an ihn zu erinnern. Dieser Cookie wird nach 12 Tagen automatisch gelöscht. Wenn Sie als Benutzer möchten, dass dieser Cookie gelöscht wird, müssen Sie sich nur abmelden. Wenn Sie nicht möchten, dass der Cookie auf Ihrem Computer installiert wird, sollten Sie das Kontrollkästchen \"An mich erinnern\" nicht aktivieren, wenn Sie sich anmelden." cookie_openstreemap_desc: "Wird von unserem freundlichen Open-Source-Mapping-Anbieter (OpenStreetMap) verwendet, um sicherzustellen, dass während eines bestimmten Zeitraums nicht zu viele Anfragen eingehen, um den Missbrauch ihrer Dienste zu verhindern." - cookie_stripe_desc: "Daten gesammelt von unserem Zahlungsabwickler Stripe für die Betrugserkennung https://stripe.com/cookies-policy/legal. Nicht alle Geschäfte verwenden Stripe als Zahlungsmethode, aber es ist eine gute Vorgehensweise, sie auf alle Seiten anzuwenden. Stripe erstellt wahrscheinlich ein Bild davon, welche unserer Seiten normalerweise mit ihrer API interagieren und merkt, wenn etwas Ungewöhnliches passiert. Das Festlegen des Stripe-Cookies hat also eine breitere Funktion als die Bereitstellung einer Zahlungsmethode für einen Benutzer. Das Entfernen könnte die Sicherheit des Dienstes selbst beeinträchtigen. Sie können mehr über Stripe erfahren und dessen Datenschutzrichtlinie unter https://stripe.com/privacy lesen." + cookie_stripe_desc: "Daten, gesammelt von unserem Zahlungsabwickler Stripe für die Betrugserkennung https://stripe.com/cookies-policy/legal. Nicht alle Läden verwenden Stripe als Zahlungsart, aber es ist eine gute Vorgehensweise, es auf alle Seiten anzuwenden. Stripe erstellt wahrscheinlich ein Bild davon, welche unserer Seiten normalerweise mit ihrer API interagieren und merkt, wenn etwas Ungewöhnliches passiert. Das Festlegen des Stripe-Cookies hat also eine breitere Funktion als die Bereitstellung einer Zahlungsart für einen Benutzer. Das Entfernen könnte die Sicherheit des Dienstes selbst beeinträchtigen. Sie können mehr über Stripe erfahren und dessen Datenschutzrichtlinie unter https://stripe.com/privacy lesen." statistics_cookies: "Statistik-Cookies" statistics_cookies_desc: "Die folgenden Punkte sind nicht unbedingt erforderlich, helfen Ihnen jedoch, die beste Benutzererfahrung zu bieten, indem wir das Benutzerverhalten analysieren, die am häufigsten verwendeten Funktionen identifizieren oder nicht verwenden, Probleme mit der Benutzerfreundlichkeit verstehen usw." - statistics_cookies_matomo_desc_html: "Um Daten zur Nutzung der Plattform zu erfassen und zu analysieren, verwenden wir Matomo (ex Piwik), ein Open-Source-Analysetool, das der DSGVO-Richtlinie entspricht schützt Ihre Privatsphäre." + statistics_cookies_matomo_desc_html: "Um Daten zur Nutzung der Plattform zu erfassen und zu analysieren, verwenden wir Matomo (ex Piwik), ein Open-Source-Analysetool, das der DSGVO-Richtlinie entspricht und Ihre Privatsphäre schützt." statistics_cookies_matomo_optout: "Möchten Sie Matomo Analytics deaktivieren? Wir sammeln keine persönlichen Daten und Matomo hilft uns, unseren Service zu verbessern, aber wir respektieren Ihre Wahl :-)" cookie_matomo_basics_desc: "Matomo First Party Cookies zum Sammeln von Statistiken." cookie_matomo_heatmap_desc: "Matomo Heatmap & Session Aufnahme-Cookie." @@ -1403,13 +1403,13 @@ de_DE: cookies_banner: cookies_usage: "Diese Website verwendet Cookies, um Ihre Navigation reibungslos und sicher zu gestalten und um zu verstehen, wie Sie sie verwenden, um die von uns angebotenen Funktionen zu verbessern." cookies_definition: "Cookies sind sehr kleine Textdateien, die beim Besuch einiger Websites auf Ihrem Computer gespeichert werden." - cookies_desc: "Wir verwenden nur die Cookies, die notwendig sind, um Ihnen den Service des Online-Kaufs / -Verkaufs von Lebensmitteln zu bieten. Wir verkaufen keine Ihrer Daten. Wir verwenden Cookies hauptsächlich, um sich daran zu erinnern, wer Sie sind, wenn Sie sich bei dem Dienst anmelden oder sich die Artikel merken können, die Sie in Ihren Warenkorb legen, auch wenn Sie nicht eingeloggt sind \"Cookies akzeptieren\" nehmen wir an, dass Sie uns die Speicherung von Cookies erlauben, die für das Funktionieren der Website notwendig sind." + cookies_desc: "Wir verwenden nur die Cookies, die notwendig sind, um Ihnen den Service des Online-Kaufs/-Verkaufs von Lebensmitteln zu bieten. Wir verkaufen keine Ihrer Daten. Wir verwenden Cookies hauptsächlich, um uns daran zu erinnern, wer Sie sind, wenn Sie sich bei unserem Dienst einloggen, oder um uns die Artikel merken zu können, die Sie in Ihren Warenkorb legen, auch wenn Sie nicht eingeloggt sind. Wenn Sie die Weite nutzen, ohne auf \"Cookies akzeptieren\" zu klicken, nehmen wir an, dass Sie uns die Speicherung von Cookies erlauben, die für das Funktionieren der Website notwendig sind." cookies_policy_link_desc: "Wenn Sie mehr erfahren möchten, besuchen Sie unsere" cookies_policy_link: "Hinweise zu Cookies" cookies_accept_button: "Cookies akzeptieren" home_shop: Jetzt einkaufen brandstory_headline: "Lebensmittel Direktvermarktung" - brandstory_intro: "Manchmal ist der beste Weg, das System zu reparieren, ein neues zu starten ..." + brandstory_intro: "Manchmal ist der beste Weg, das System zu reparieren, ein neues aufzubauen ..." brandstory_part1: "Wir beginnen von Grund auf. Mit Bauern und Züchtern, die bereit sind, ihre Geschichten stolz und wahrhaftig zu erzählen. Mit Händlern, die bereit sind, Menschen mit Produkten fair und ehrlich zu verbinden. Mit Käufern, die glauben, dass bessere wöchentliche Einkaufsentscheidungen die Welt ernsthaft verändern können." brandstory_part2: "Dann brauchen wir einen Weg, um es real zu machen. Ein Weg, jeden zu stärken, der Lebensmittel anbaut, verkauft und kauft. Ein Weg, um alle Geschichten zu erzählen, um die gesamte Logistik zu bewältigen. Eine Möglichkeit, Transaktionen jeden Tag in Transformation umzuwandeln." brandstory_part3: "Also bauen wir einen Online-Marktplatz auf, der das Spielfeld ebnet. Es ist transparent und schafft echte Beziehungen. Es ist Open Source, also gehört es allen. Es skaliert zu Regionen und Nationen, so dass Leute Versionen auf der ganzen Welt starten." @@ -1425,7 +1425,7 @@ de_DE: system_step1_text: "Durchsuchen Sie unsere vielfältigen, unabhängigen Läden nach saisonal und regional erzeugten Lebensmitteln. Suchen Sie nach Region und Lebensmittelkategorie, oder ob Sie Lieferung oder Abholung bevorzugen." system_step2: "2. Laden" system_step2_text: "Transformieren Sie Ihre Transaktionen mit erschwinglichen lokalen Lebensmitteln von verschiedenen Erzeugern und Hubs. Erfahren Sie die Geschichten hinter Ihrem Essen und den Menschen, die es erzeugen." - system_step3: "3. Abholung / Lieferung" + system_step3: "3. Abholung/Lieferung" system_step3_text: "Erhalten Sie eine Lieferung oder besuchen Sie Ihren Hersteller oder Ihr Hub für eine persönlichere Verbindung mit Ihrem Essen. Lebensmitteleinkauf so vielfältig wie die Natur es beabsichtigt hat." cta_headline: "Einkaufen, das die Welt verbessert." cta_label: "Ich bin bereit" @@ -1443,7 +1443,7 @@ de_DE: checkout_as_guest: "Als Gast zur Kasse" checkout_details: "Ihre Details" checkout_billing: "Rechnungsinfo" - checkout_default_bill_address: "Als Standard-Rechnungsadresse speichern" + checkout_default_bill_address: "Als Standardrechnungsadresse speichern" checkout_shipping: Versandinformation checkout_default_ship_address: "Als Standardversandadresse speichern" checkout_method_free: kostenlos @@ -1466,7 +1466,7 @@ de_DE: order_delivery_on: Lieferung am order_delivery_address: Lieferadresse order_delivery_time: Lieferzeit - order_special_instructions: "Deine Notizen:" + order_special_instructions: "Ihre Notizen:" order_pickup_time: abholbereit order_pickup_instructions: Abholinformationen order_produce: Produkte @@ -1484,24 +1484,24 @@ de_DE: products_at: "bei %{distributor}" products_elsewhere: "Produkte an anderer Stelle" email_confirmed: "Vielen Dank für die Bestätigung Ihrer E-Mail-Adresse." - email_confirmation_activate_account: "Bevor wir Ihr neues Konto aktivieren können, müssen wir Ihre E-Mail-Adresse bestätigen." + email_confirmation_activate_account: "Bevor wir Ihr neues Konto aktivieren, bestätigen Sie bitte Ihre E-Mail-Adresse." email_confirmation_greeting: "Hallo, %{contact}!" email_confirmation_profile_created: "Ein Profil für %{name} wurde erfolgreich erstellt! Um Ihr Profil zu aktivieren, müssen wir diese E-Mail-Adresse bestätigen." - email_confirmation_click_link: "Bitte klicken Sie auf den unten stehenden Link, um Ihre E-Mail zu bestätigen und mit der Einrichtung Ihres Profils fortzufahren." + email_confirmation_click_link: "Bitte klicken Sie auf den unten stehenden Link, um Ihre E-Mail-Adresse zu bestätigen und mit der Einrichtung Ihres Profils fortzufahren." email_confirmation_link_label: "E-Mail-Adresse bestätigen" - email_confirmation_help_html: "Nachdem Sie Ihre E-Mail-Adresse bestätigt haben, können Sie auf Ihr Verwaltungskonto für dieses Unternehmen zugreifen. Sehen Sie sich die %{link} an, um mehr über die Funktionen von %{sitename} zu erfahren und Ihr Profil oder Ihren Online-Shop zu verwenden." - email_confirmation_notice_unexpected: "Sie haben diese Nachricht erhalten, weil Sie sich unter %{sitename} angemeldet haben oder von einer Person eingeladen wurden, die Sie wahrscheinlich kennen. Wenn Sie nicht verstehen, warum Sie diese E-Mail erhalten, schreiben Sie bitte an %{contact}." - email_social: "Verbinde dich mit uns:" + email_confirmation_help_html: "Nachdem Sie Ihre E-Mail-Adresse bestätigt haben, können Sie auf Ihr Verwaltungskonto für dieses Unternehmen zugreifen. Sehen Sie sich die %{link} an, um mehr über die Funktionen von %{sitename} zu erfahren und Ihr Profil oder Ihren Laden zu verwenden." + email_confirmation_notice_unexpected: "Sie haben diese Nachricht erhalten, weil Sie sich bei %{sitename} angemeldet haben oder von einer Person eingeladen wurden, die Sie wahrscheinlich kennen. Wenn Sie nicht verstehen, warum Sie diese E-Mail erhalten, schreiben Sie bitte an %{contact}." + email_social: "Verbinden Sie sich mit uns:" email_contact: "Schreiben Sie uns eine E-Mail:" email_signoff: "Danke" email_signature: "%{sitename} Team" email_confirm_customer_greeting: "Hallo %{name}," email_confirm_customer_intro_html: "Vielen Dank für ihren Einkauf bei %{distributor} !" email_confirm_customer_number_html: "Bestellbestätigung # %{number}" - email_confirm_customer_details_html: "Hier sind Ihre Bestelldaten von %{distributor} :" + email_confirm_customer_details_html: "Hier sind Ihre Bestelldaten von %{distributor}:" email_confirm_customer_signoff: "Mit freundlichen Grüßen," email_confirm_shop_greeting: "Hallo %{name}," - email_confirm_shop_order_html: "Gut gemacht! Sie haben eine neue Bestellung für %{distributor} !" + email_confirm_shop_order_html: "Gut gemacht! Sie haben eine neue Bestellung für %{distributor}!" email_confirm_shop_number_html: "Bestellbestätigung # %{number}" email_order_summary_item: "Artikel" email_order_summary_quantity: "Menge" @@ -1514,37 +1514,37 @@ de_DE: email_payment_not_paid: NICHT BEZAHLT email_payment_summary: Zahlungsübersicht email_payment_method: "Bezahlen per:" - email_so_placement_intro_html: "Sie haben eine neue Bestellung mit %{distributor} " - email_so_placement_details_html: "Hier sind die Details Ihrer Bestellung für %{distributor} :" + email_so_placement_intro_html: "Sie haben eine neue Bestellung für %{distributor}" + email_so_placement_details_html: "Hier sind die Details Ihrer Bestellung für %{distributor}:" email_so_placement_changes: "Leider waren nicht alle von Ihnen angeforderten Produkte verfügbar. Die von Ihnen angeforderten Originalmengen sind unten durchgestrichen." - email_so_payment_success_intro_html: "Eine automatische Zahlung wurde für Ihre Bestellung von %{distributor} verarbeitet." + email_so_payment_success_intro_html: "Eine automatische Zahlung für Ihre Bestellung bei %{distributor} wurde verarbeitet." email_so_placement_explainer_html: "Diese Bestellung wurde automatisch für Sie erstellt." - email_so_edit_true_html: "Sie können Änderungen vornehmen , bis Bestellungen auf %{orders_close_at} schließen." - email_so_edit_false_html: "Sie können jederzeit Details zu dieser Bestellung anzeigen ." - email_so_contact_distributor_html: "Wenn Sie Fragen haben, können Sie sich mit %{distributor} über %{email} in Verbindung setzen." - email_so_contact_distributor_to_change_order_html: "Diese Bestellung wurde automatisch für Sie erstellt. Sie können Änderungen vornehmen, bis Bestellungen auf %{orders_close_at} geschlossen werden, indem Sie %{distributor} über %{email} kontaktieren." - email_so_confirmation_intro_html: "Ihre Bestellung mit %{distributor} ist jetzt bestätigt" + email_so_edit_true_html: "Sie können Änderungen vornehmen, bis Bestellungen am %{orders_close_at} schließen." + email_so_edit_false_html: "Sie können jederzeit Details zu dieser Bestellung anzeigen." + email_so_contact_distributor_html: "Wenn Sie Fragen haben, können Sie sich mit %{distributor} über %{email} in Verbindung setzen." + email_so_contact_distributor_to_change_order_html: "Diese Bestellung wurde automatisch für Sie erstellt. Sie können Änderungen vornehmen, bis die Bestellungen am %{orders_close_at} geschlossen werden, indem Sie %{distributor} über %{email} kontaktieren." + email_so_confirmation_intro_html: "Ihre Bestellung bei %{distributor} ist jetzt bestätigt" email_so_confirmation_explainer_html: "Diese Bestellung wurde automatisch für Sie aufgegeben und ist nun abgeschlossen." - email_so_confirmation_details_html: "Hier finden Sie alles, was Sie über Ihre Bestellung wissen müssen: %{distributor} :" - email_so_empty_intro_html: "Wir haben versucht, eine neue Bestellung mit %{distributor} zu platzieren, hatten aber einige Probleme ..." + email_so_confirmation_details_html: "Hier finden Sie alles, was Sie über Ihre Bestellung bei %{distributor} wissen müssen:" + email_so_empty_intro_html: "Wir haben versucht, eine neue Bestellung bei %{distributor}aufzugeben, hatten aber einige Probleme ..." email_so_empty_explainer_html: "Leider war keines der von Ihnen bestellten Produkte verfügbar, daher wurde keine Bestellung aufgegeben. Die von Ihnen angeforderten Originalmengen sind unten durchgestrichen." - email_so_empty_details_html: "Hier sind die Details der nicht platzierten Bestellung für %{distributor} :" + email_so_empty_details_html: "Hier sind die Details der nicht aufgegebenen Bestellung bei %{distributor}:" email_so_failed_payment_intro_html: "Wir haben versucht, eine Zahlung zu verarbeiten, hatten aber einige Probleme ..." - email_so_failed_payment_explainer_html: "Die Zahlung für Ihr Abonnement mit %{distributor} ist aufgrund eines Problems mit Ihrer Kreditkarte fehlgeschlagen. %{distributor} wurde über diese fehlgeschlagene Zahlung benachrichtigt." - email_so_failed_payment_details_html: "Hier sind die Details des vom Zahlungs-Gateway bereitgestellten Fehlers:" + email_so_failed_payment_explainer_html: "Die Zahlung für Ihr Abonnement bei %{distributor} ist aufgrund eines Problems mit Ihrer Kreditkarte fehlgeschlagen. %{distributor} wurde über diese fehlgeschlagene Zahlung benachrichtigt." + email_so_failed_payment_details_html: "Hier sind die Details des vom Zahlungsgateway bereitgestellten Fehlers:" email_shipping_delivery_details: Lieferdetails email_shipping_delivery_time: "Lieferung am:" email_shipping_delivery_address: "Lieferadresse:" email_shipping_collection_details: Sammlungsdetails email_shipping_collection_time: "Bereit zur Abholung" email_shipping_collection_instructions: "Sammlung Anweisungen:" - email_special_instructions: "Deine Noten:" + email_special_instructions: "Ihre Notizen:" email_signup_greeting: Hallo! email_signup_welcome: "Willkommen bei %{sitename}!" - email_signup_confirmed_email: "Vielen Dank für die Bestätigung Ihrer E-Mail." + email_signup_confirmed_email: "Vielen Dank für die Bestätigung Ihrer E-Mail-Adresse." email_signup_shop_html: "Sie können sich jetzt unter %{link} anmelden." - email_signup_text: "Danke, dass Sie dem Netzwerk beigetreten sind. Wenn Sie ein Kunde sind, freuen wir uns, Ihnen viele fantastische Bauern, wunderbare Essenszentren und leckeres Essen vorzustellen! Wenn Sie ein Produzent oder ein Lebensmittelunternehmen sind, freuen wir uns, Sie als Teil des Netzwerks zu haben." - email_signup_help_html: "Wir begrüßen alle Ihre Fragen und Rückmeldungen; Sie können die Schaltfläche Feedback senden auf der Website verwenden oder uns eine E-Mail an %{email} senden" + email_signup_text: "Danke, dass Sie dem Netzwerk beigetreten sind. Wenn Sie ein Kunde sind, freuen wir uns, Ihnen viele fantastische Bauern, wunderbare Läden und leckeres Essen vorzustellen! Wenn Sie ein Produzent oder ein Lebensmittelunternehmer sind, freuen wir uns, Sie als Teil des Netzwerks zu begrüßen." + email_signup_help_html: "Wir freuen und über Ihre Fragen und Rückmeldungen. Schreiben Sie uns gerne eine E-Mail an %{email}." invite_email: greeting: "Hallo!" invited_to_manage: "Sie wurden eingeladen, %{enterprise} auf %{instance} zu verwalten." @@ -1554,64 +1554,65 @@ de_DE: producer_mail_greeting: "Liebe/r" producer_mail_text_before: "Wir haben jetzt alle Verbraucherbestellungen für die nächste Auslieferung." producer_mail_order_text: "Hier finden Sie eine Zusammenfassung der Bestellungen für Ihre Produkte:" - producer_mail_delivery_instructions: "Lagerabholung / Lieferanweisungen:" + producer_mail_delivery_instructions: "Lagerabholung/Lieferanweisungen:" producer_mail_signoff: "Danke und die besten Wünsche" shopping_oc_closed: Bestellungen sind geschlossen - shopping_oc_closed_description: "Bitte warten Sie, bis der nächste Zyklus beginnt (oder kontaktieren Sie uns direkt, um zu sehen, ob wir verspätete Bestellungen annehmen können)" - shopping_oc_last_closed: "Der letzte Zyklus wurde vor %{distance_of_time} geschlossen" - shopping_oc_next_open: "Der nächste Zyklus wird in %{distance_of_time} geöffnet" - shopping_oc_select: "Wählen..." + shopping_oc_closed_description: "Bitte warten Sie, bis der nächste Bestellzyklus öffnet (oder kontaktieren Sie uns direkt, um zu sehen, ob wir verspätete Bestellungen annehmen können)." + shopping_oc_last_closed: "Der letzte Bestellzyklus wurde vor %{distance_of_time} geschlossen." + shopping_oc_next_open: "Der nächste Bestellzyklus wird in %{distance_of_time} geöffnet" + shopping_oc_select: "Auswählen ..." shopping_tabs_home: "Startseite" shopping_tabs_shop: "Laden" - shopping_tabs_about: "Über Uns" + shopping_tabs_about: "Über uns" shopping_tabs_contact: "Kontakt" shopping_contact_address: "Adresse" shopping_contact_web: "Kontakt" shopping_contact_social: "Folgen" shopping_groups_part_of: "ist ein Teil von:" - shopping_producers_of_hub: "Erzeuger bei%{hub}" - enterprises_next_closing: "Nächster Bestellschluß" + shopping_producers_of_hub: "Erzeuger bei %{hub}:" + enterprises_next_closing: "Nächster Bestellschluss" + enterprises_currently_open: "Bestellzyklus ist geöffnet" enterprises_ready_for: "Fertig am" enterprises_choose: "Wählen Sie, wann Sie Ihre Bestellung wollen:" maps_open: "Offen" maps_closed: "Geschlossen" - hubs_buy: "Suche nach:" - hubs_shopping_here: "Hier einkaufen" - hubs_orders_closed: "Momentan keine Bestellungen" - hubs_profile_only: "Profil nur" - hubs_delivery_options: "Lieferoptionen" + hubs_buy: "Angebot" + hubs_shopping_here: "Sie kaufen hier ein" + hubs_orders_closed: "Bestellzyklus geschlossen" + hubs_profile_only: "nur Profil" + hubs_delivery_options: "Lieferarten" hubs_pickup: "Abholen" hubs_delivery: "Lieferung" hubs_producers: "Unsere Produzenten" hubs_filter_by: "Filtern nach" hubs_filter_type: "Art" - hubs_filter_delivery: "Lieferung" - hubs_filter_property: "Eigentum" + hubs_filter_delivery: "Lieferarten" + hubs_filter_property: "Eigenschaften" hubs_matches: "Meintest Du?" hubs_intro: Regional einkaufen hubs_distance: Am nächsten hubs_distance_filter: "Läden in der Nähe von %{location}suchen" shop_changeable_orders_alert_html: - one: Ihre Bestellung mit %{shop} / %{order} kann überprüft werden. Sie können Änderungen bis %{oc_close} vornehmen. - few: Sie haben %{count} Bestellungen mit %{shop}, die derzeit zur Überprüfung geöffnet sind. Sie können Änderungen bis %{oc_close} vornehmen. - many: Sie haben %{count} Bestellungen mit %{shop}, die derzeit zur Überprüfung geöffnet sind. Sie können Änderungen bis %{oc_close} vornehmen. - other: Sie haben %{count} Bestellungen mit %{shop}, die derzeit zur Überprüfung geöffnet sind. Sie können Änderungen bis %{oc_close} vornehmen. - orders_changeable_orders_alert_html: Diese Bestellung wurde bestätigt, Sie können jedoch bis %{oc_close} Änderungen vornehmen. - products_clear: Leeren + one: Prüfen Sie Ihre Bestellung unter %{shop}/%{order}. Sie können Änderungen bis %{oc_close} vornehmen. + few: Sie haben %{count}Bestellungen bei %{shop}, die derzeit zur Überprüfung geöffnet sind. Sie können Änderungen bis %{oc_close} vornehmen. + many: Sie haben %{count}Bestellungen bei %{shop}, die derzeit zur Überprüfung geöffnet sind. Sie können Änderungen bis %{oc_close} vornehmen. + other: Sie haben %{count}Bestellungen bei %{shop}, die derzeit zur Überprüfung geöffnet sind. Sie können Änderungen bis %{oc_close} vornehmen. + orders_changeable_orders_alert_html: Diese Bestellung wurde bestätigt, Sie können jedoch bis %{oc_close} Änderungen vornehmen. + products_clear: Zurücksetzen products_showing: "Angezeigt:" products_results_for: "Ergebnisse für" products_or: "oder" products_and: "und" products_filters_in: "im" products_with: mit - products_search: "Suche..." + products_search: "Suche ..." products_filter_by: "Filtern nach" products_filter_selected: "ausgewählt" products_filter_heading: "Filter" - products_filter_clear: "Leeren" - products_filter_done: "Erledigt" - products_loading: "Lade Produkte..." - products_updating_cart: "Einkaufswagen aktualisieren..." + products_filter_clear: "Zurücksetzen" + products_filter_done: "Anwenden" + products_loading: "Produkte werden geladen ..." + products_updating_cart: "Einkaufswagen wird aktualisiert ..." products_cart_empty: "Leerer Einkaufswagen" products_edit_cart: "Einkaufswagen bearbeiten" products_from: von @@ -1620,7 +1621,7 @@ de_DE: products_update_error_msg: "Speichern Fail" products_update_error_data: "Speichern wegen ungültiger Daten fehlgeschlagen:" products_changes_saved: "Änderungen gespeichert" - products_no_results_html: "Für %{query} wurden leider keine Ergebnisse gefunden" + products_no_results_html: "Leider wurden keine Ergebnisse gefunden für %{query}." products_clear_search: "Suche zurücksetzen" search_no_results_html: "Es wurden leider keine Ergebnisse für %{query} gefunden. Versuchen Sie eine andere Suche?" components_profiles_popover: "Profile haben keinen Laden im Open Food Network, verkaufen möglicherweise aber anderswo online oder offline." @@ -1628,13 +1629,13 @@ de_DE: components_filters_nofilters: "Keine Filter" components_filters_clearfilters: "Alle Filter löschen" groups_title: Gruppen - groups_headline: Gruppen / Regionen + groups_headline: Gruppen/Regionen groups_text: "Jeder Produzent ist einzigartig. Jedes Unternehmen hat etwas anderes zu bieten. Unsere Gruppen sind Kollektive von Produzenten, Hubs und Verteilern, die etwas gemeinsam haben, wie z B Standort, Bauernmarkt oder Philosophie. Dies erleichtert Ihr Einkaufserlebnis." groups_search: "Suchen Sie nach Name oder Stichwort" groups_no_groups: "Keine Gruppen gefunden" groups_about: "Über uns" groups_producers: "Unsere Produzenten" - groups_hubs: "Unsere Hubs" + groups_hubs: "Unsere Läden" groups_contact_web: Kontakt groups_contact_social: Folgen groups_contact_address: Adresse @@ -1665,10 +1666,10 @@ de_DE: producers_contact: Kontakt producers_contact_phone: Anruf producers_contact_social: Folgen - producers_buy_at_html: "Suche nach %{enterprise} Produkten unter:" + producers_buy_at_html: "Hier erhalten Sie %{enterprise}-Produkte:" producers_filter: Filtern nach - producers_filter_type: Art - producers_filter_property: Eigentum + producers_filter_type: Kategorie + producers_filter_property: Eigenschaften producers_title: Produzenten producers_headline: Finden Sie lokale Produzenten producers_signup_title: Melde dich als Produzent an @@ -1695,16 +1696,16 @@ de_DE: sell_producers: "Produzenten" sell_hubs: "Hubs" sell_groups: "Gruppen" - sell_producers_detail: "Richten Sie in wenigen Minuten ein Profil für Ihr Unternehmen auf dem OFN ein. Sie können Ihr Profil jederzeit auf einen Online-Shop aktualisieren und Ihre Produkte direkt an Kunden verkaufen." - sell_hubs_detail: "Richten Sie im OFN ein Profil für Ihr Lebensmittelunternehmen oder Ihre Organisation ein. Sie können Ihr Profil jederzeit auf einen Multi-Producer-Shop upgraden." + sell_producers_detail: "Richten Sie in wenigen Minuten für Ihr Unternehmen ein Profil im Open Food Network ein. Sie können Ihr Profil jederzeit auf einen Laden erweitern und Ihre Produkte direkt an Kunden verkaufen." + sell_hubs_detail: "Richten Sie im Open Food Network ein Profil für Ihr Lebensmittelunternehmen oder Ihre Organisation ein. Sie können Ihr Profil jederzeit auf einen Multi-Erzeugerladen erweitern." sell_groups_detail: "Richten Sie ein maßgeschneidertes Verzeichnis von Unternehmen (Produzenten und andere Lebensmittelunternehmen) für Ihre Region oder für Ihre Organisation ein." sell_user_guide: "Erfahren Sie mehr in unserem Benutzerhandbuch." - sell_listing_price: "Das Listing im OFN ist kostenlos. Das Eröffnen und Betreiben eines OFN-Shops ist bis zu 500 US-Dollar monatlich kostenlos. Wenn Sie mehr verkaufen, können Sie Ihren Community-Beitrag zwischen 1% und 3% des Umsatzes wählen. Weitere Informationen zur Preisgestaltung finden Sie im Abschnitt Software Platform über den Link About im oberen Menü." - sell_embed: "Wir können auch einen OFN-Laden in Ihre eigene Website einbetten oder eine maßgeschneiderte Webseite für Ihr Lokalität oder Region erstellen." - sell_ask_services: "Fragen Sie uns nach OFN-Diensten." + sell_listing_price: "Das Aufnahme ins Open Food Network ist kostenlos. Das Eröffnen und Betreiben eines Ladens im OFN ist derzeit kostenlos." + sell_embed: "Wir können auch einen OFN-Laden in Ihre eigene Website einbetten oder eine maßgeschneiderte Website für Ihre Region erstellen." + sell_ask_services: "Fragen Sie uns nach OFN-Dienstleistungen." shops_title: Läden - shops_headline: Einkaufen, verwandelt. - shops_text: Nahrung wächst in Zyklen, Bauern ernten in Zyklen und wir bestellen Nahrung in Zyklen. Wenn Sie feststellen, dass ein Bestellzyklus geschlossen ist, schauen Sie bald wieder vorbei. + shops_headline: Einkaufen, neu gedacht. + shops_text: Nahrung wächst in Zyklen, Bauern ernten in Zyklen und wir bestellen Nahrung in Zyklen. Ist ein Bestellzyklus gerade geschlossen, schauen Sie bald wieder vorbei. shops_signup_title: Als Hub registrieren shops_signup_headline: Lebensmittel-Hubs, unbegrenzt. shops_signup_motivation: Was auch immer Ihr Modell ist, wir unterstützen Sie. Wie auch immer Sie sich ändern, wir sind bei Ihnen. Wir sind gemeinnützig, unabhängig und Open-Source. Wir sind die Software-Partner, von denen Sie schon immer geträumt haben. @@ -1714,8 +1715,8 @@ de_DE: shops_signup_help: Wir sind bereit zu helfen. shops_signup_help_text: Du brauchst eine bessere Rendite. Sie brauchen neue Käufer und Logistikpartner. Sie müssen Ihre Geschichte über den Großhandel, den Einzelhandel und den Küchentisch erzählen. shops_signup_detail: Hier ist das Detail. - orders: Aufträge - orders_fees: Gebühren... + orders: Bestellungen + orders_fees: Gebühren ... orders_edit_title: Warenkorb orders_edit_headline: Ihr Warenkorb orders_edit_time: Bestellung bereit für @@ -1727,9 +1728,9 @@ de_DE: orders_form_total: Total orders_oc_expired_headline: Bestellungen wurden für diesen Bestellzyklus geschlossen orders_oc_expired_text: "Entschuldigung, Bestellungen für diesen Bestellzyklus wurden vor %{time} geschlossen! Bitte kontaktieren Sie Ihr Hub direkt, um zu sehen, ob sie verspätete Bestellungen annehmen können." - orders_oc_expired_text_others_html: "Entschuldigung, Bestellungen für diesen Bestellzyklus haben %{time} geschlossen! Wenden Sie sich direkt an Ihren Hub, um zu sehen, ob er verspätete Bestellungen annehmen kann %{link} ." + orders_oc_expired_text_others_html: "Entschuldigung, der Bestellzyklus wurde vor %{time} geschlossen! Wenden Sie sich bitte direkt an den Laden, um zu sehen, ob er verspätete Bestellungen annehmen kann (%{link})." orders_oc_expired_text_link: "oder sehen Sie die anderen Bestellzyklen an diesem Hub" - orders_oc_expired_email: "Email:" + orders_oc_expired_email: "E-Mail-Adresse:" orders_oc_expired_phone: "Telefon:" orders_show_title: Bestellbestätigung orders_show_time: Bestellung bereit am @@ -1748,13 +1749,13 @@ de_DE: orders_bought_already_confirmed: "* schon bestätigt" orders_confirm_cancel: Sind Sie sicher, dass Sie diese Bestellung stornieren möchten? order_processed_successfully: "Ihre Bestellung wurde erfolgreich bearbeitet" - products_cart_distributor_choice: "Fairteiler Deiner Bestellung" + products_cart_distributor_choice: "Verteiler Ihrer Bestellung:" products_cart_distributor_change: "Ihr Händler für diese Bestellung wird in %{name} geändert, wenn Sie dieses Produkt zu Ihrem Warenkorb hinzufügen." products_cart_distributor_is: "Ihr Händler für diese Bestellung ist %{name}." products_distributor_error: "Bitte schließen Sie Ihre Bestellung bei %{link} ab, bevor Sie bei einem anderen Händler einkaufen." products_oc: "Bestellzyklus für Ihre Bestellung:" products_oc_change: "Ihr Bestellzyklus für diese Bestellung wird in %{name} geändert, wenn Sie dieses Produkt zu Ihrem Warenkorb hinzufügen." - products_oc_is: "Ihr Bestellzyklus für diesen Auftrag lautet %{name}." + products_oc_is: "Ihr Bestellzyklus für diese Bestellung lautet %{name}." products_oc_error: "Bitte schließen Sie Ihre Bestellung bei %{link} ab, bevor Sie in einem anderen Bestellzyklus einkaufen." products_oc_current: "Ihr aktueller Bestellzyklus" products_max_quantity: Max Menge @@ -1763,8 +1764,8 @@ de_DE: password: Passwort remember_me: Erinnere dich an mich are_you_sure: "Bist du sicher?" - orders_open: "Bestellungen öffnen" - closing: "Schließen" + orders_open: "Geöffnet" + closing: "Schließt" going_back_to_home_page: "Bring dich zurück auf die Homepage" creating: Erstellen updating: Aktualisierung @@ -1778,14 +1779,14 @@ de_DE: one_filter_applied: "1 Filter angewendet" x_filters_applied: " Filter angewendet" submitting_order: "Bestellung abschicken: Bitte warten" - confirm_hub_change: "Sind Sie sicher? Dadurch wird das ausgewählte Hub geändert und alle Artikel vom Warenkorb entfernt." + confirm_hub_change: "Sind Sie sicher? Dadurch wechseln Sie in einen anderen Laden und alle Artikel aus dem Warenkorb werden entfernt." confirm_oc_change: "Sind Sie sicher? Dies ändert den ausgewählten Bestellzyklus und entfernt alle Artikel von Ihrem Warenkorb." location_placeholder: "Geben Sie einen Ort ein ..." - error_required: "kann nicht leer sein" + error_required: "darf nicht leer sein" error_number: "muss nummer sein" error_email: "muss eine E-Mail-Adresse sein" error_not_found_in_database: "%{name} nicht in Datenbank gefunden" - error_not_primary_producer: "%{name} ist nicht als Producer aktiviert" + error_not_primary_producer: "%{name} ist nicht als Erzeuger aktiviert" error_no_permission_for_enterprise: "\"%{name}\": Sie sind nicht berechtigt, Produkte für dieses Unternehmen zu verwalten" item_handling_fees: "Artikel Bearbeitungsgebühren (in den Gesamtsummen enthalten)" january: "Januar" @@ -1800,12 +1801,12 @@ de_DE: october: "Oktober" november: "November" december: "Dezember" - email_not_found: "Emailadresse wurde nicht gefunden" + email_not_found: "E-Mail-Adresse wurde nicht gefunden" email_unconfirmed: "Sie müssen Ihre E-Mail-Adresse bestätigen, bevor Sie Ihr Passwort zurücksetzen können." email_required: "Sie müssen eine E-Mail-Adresse angeben" logging_in: "Moment, wir melden uns an" - signup_email: "Deine E-Mail-Adresse" - choose_password: "Dein Passwort" + signup_email: "Ihre E-Mail-Adresse" + choose_password: "Ihr Passwort" confirm_password: "Passwort wiederholen" action_signup: "Anmelden" forgot_password: "Passwort vergessen?" @@ -1825,9 +1826,9 @@ de_DE: registration_promo_image: "Landschaftsbild für Ihr Profil" registration_about_us: "\"Über uns\" Text" registration_outcome_headline: "Was bekomme ich?" - registration_outcome1_html: "Ihr Profil hilft den Menschen Sie zu finden und im Open Food Network zu kontaktieren." + registration_outcome1_html: "Ihr Profil hilft dabei, Sie im Open Food Network zu finden und zu kontaktieren." registration_outcome2: "Nutzen Sie diesen Bereich, um die Geschichte Ihres Unternehmens zu erzählen und um Verbindungen zu Ihrem Social-Media- und Web-Auftritt herzustellen." - registration_outcome3: "Dies ist auch der erste Schritt, um im Open Food Network zu handeln oder einen Online-Shop zu eröffnen." + registration_outcome3: "Dies ist auch der erste Schritt, um im Open Food Network zu handeln oder einen Laden zu eröffnen." registration_action: "Lass uns anfangen!" details: title: "Einzelheiten" @@ -1867,11 +1868,11 @@ de_DE: yes_producer: "Ja, ich bin ein Produzent." no_producer: "Nein, ich bin kein Produzent" producer_field_error: "Bitte wählen Sie: Sind Sie ein Produzent?" - yes_producer_help: "Die Produzenten machen leckere Sachen zum Essen und / oder Trinken. Sie sind ein Produzent, wenn Sie anbauen, brauen, backen, fermentieren, melken oder sonst wie Lebenmittel produzieren." - no_producer_help: "Wenn Sie kein Produzent sind, sind Sie wahrscheinlich jemand, der Lebensmittel verkauft und verteilt. Sie könnten ein Foodhub, eine Coop, eine Einkaufsgruppe, Einzelhändler, ein Hofladen, Großhändler oder vergleichbares sein." + yes_producer_help: "Die Produzenten stellen leckere Lebensmittel und/oder Getränke her. Sie sind ein Produzent, wenn Sie anbauen, brauen, backen, fermentieren, melken oder sonst wie Lebenmittel produzieren." + no_producer_help: "Wenn Sie kein Produzent sind, sind Sie wahrscheinlich jemand, der Lebensmittel verkauft und verteilt. Sie könnten ein Foodhub, eine Coop, eine Einkaufsgruppe, Einzelhändler, ein Hofladen, Großhändler oder Vergleichbares sein." create_profile: "Profil erstellen" about: - title: "Über Uns" + title: "Über uns" headline: "Schön!" message: "Lassen Sie uns nun die Details überarbeiten" success: "Erfolg! %{enterprise} wurde dem Open Food Network hinzugefügt" @@ -1880,7 +1881,7 @@ de_DE: enterprise_description_placeholder: "Ein kurzer Satz, der Ihr Unternehmen beschreibt" enterprise_long_desc: "Ausführliche Beschreibung" enterprise_long_desc_placeholder: "Dies ist Ihre Gelegenheit, die Geschichte Ihres Unternehmens zu erzählen - was macht Sie anders und wunderbar? Wir empfehlen, die Beschreibung unter 600 Zeichen oder 150 Wörtern zu halten." - enterprise_long_desc_length: "%{num} Zeichen / bis zu 600 empfohlen" + enterprise_long_desc_length: "%{num} Zeichen/bis zu 600 empfohlen" enterprise_abn: "USt-IdNr." enterprise_abn_placeholder: "z.B. DE999999999" enterprise_acn: "St.-Nr." @@ -1890,7 +1891,7 @@ de_DE: title: "Bilder" headline: "Dankeschön!" description: "Lassen Sie uns einige schöne Bilder hochladen, damit Ihr Profil gut aussieht! :)" - uploading: "Hochladen ..." + uploading: "WIrd hochgeladen ..." continue: "Fortsetzen" back: "Zurück" logo: @@ -1903,11 +1904,11 @@ de_DE: logo_placeholder: "Ihr Logo wird hier zur Überprüfung angezeigt, sobald es hochgeladen wurde" promo: select_promo_image: "Schritt 3. Wählen Sie Promo Image" - promo_image_tip: "Tipp: Die als Banner dargestellte bevorzugte Größe beträgt 1200 × 260px" + promo_image_tip: "Tipp: Die als Banner dargestellte bevorzugte Größe beträgt 1200 × 260 px" promo_image_label: "Wählen Sie ein Promobild" promo_image_drag: "Verschieben Sie Ihre Promo hier" review_promo_image: "Schritt 4. Überprüfen Sie Ihr Promo-Banner" - review_promo_image_tip: "Tipp: Für ein optimales Ergebnis sollte Ihr Promo-Image den verfügbaren Platz ausfüllen" + review_promo_image_tip: "Tipp: Für ein optimales Ergebnis sollte Ihr Promo-Bild den verfügbaren Platz ausfüllen" promo_image_placeholder: "Ihr Logo wird hier zur Überprüfung angezeigt, sobald es hochgeladen wurde" social: title: "Sozial" @@ -1944,8 +1945,8 @@ de_DE: shop_variant_quantity_min: "Mindest" shop_variant_quantity_max: "max" follow: "Folgen" - shop_for_products_html: "Suche nach %{enterprise} Produkten unter:" - change_shop: "Laden ändern auf:" + shop_for_products_html: "Hier erhalten Sie %{enterprise}-Produkte:" + change_shop: "In diesen Laden wechseln:" shop_at: "Kaufe jetzt bei:" admin_fee: "Gebühr Administration" sales_fee: "Gebühr Verkauf" @@ -1989,7 +1990,7 @@ de_DE: admin_enterprise_groups_data_powertip_promo_image: "Dieses Bild wird oben im Gruppenprofil angezeigt" admin_enterprise_groups_contact: "Kontakt" admin_enterprise_groups_contact_phone_placeholder: "z.B. 98 7654 3210" - admin_enterprise_groups_contact_address1_placeholder: "z.B. Gartenstrasse 123" + admin_enterprise_groups_contact_address1_placeholder: "z.B. Gartenstraße 123" admin_enterprise_groups_contact_city: "Vorort" admin_enterprise_groups_contact_city_placeholder: "z.B. Nordwestheim" admin_enterprise_groups_contact_zipcode: "Postleitzahl" @@ -2004,12 +2005,12 @@ de_DE: close: "Abschließen" create: "Neu" search: "Suche" - supplier: "Anbieter" + supplier: "Lieferant" product_name: "Produktname" product_description: "Produktbeschreibung" permalink: "Permalink" shipping_categories: "Versandkategorien" - units: "Einheitsgröße" + units: "Einheit" coordinator: "Koordinator" distributor: "Verteiler" enterprise_fees: "Zuschläge" @@ -2045,11 +2046,11 @@ de_DE: scheduled_for: "Eingetragen für" customers: "Kunden" please_select_hub: "Wähle einen Hub" - loading_customers: "Kunden werden geladen" + loading_customers: "Kunden werden geladen ..." no_customers_found: "Keine Kunden gefunden" go: "Gehen" hub: "Hub" - producer: "Produzent" + producer: "Erzeuger" product: "Produkt" price: "Preis" on_hand: "Verfügbar" @@ -2075,35 +2076,36 @@ de_DE: spree_admin_unit_value: Einheitswert spree_admin_unit_description: Einheit Beschreibung spree_admin_variant_unit: Varianteneinheit - spree_admin_variant_unit_scale: Einheitsmaß der Variante - spree_admin_supplier: Anbieter + spree_admin_variant_unit_scale: Einheit der Variante + spree_admin_supplier: Lieferant spree_admin_product_category: Produktkategorie spree_admin_variant_unit_name: Einheit der Variante unit_name: "Einheitenname" change_package: "Paket ändern" - spree_admin_single_enterprise_hint: "Tipp: Um es Ihnen zu ermöglichen, Sie zu finden, aktivieren Sie Ihre Sichtbarkeit unter" + spree_admin_single_enterprise_hint: "Tipp: Damit andere Nutzer Sie finden können, aktivieren Sie Ihre Sichtbarkeit unter:" spree_admin_eg_pickup_from_school: "z.B. \"Abholung von der Grundschule\"" spree_admin_eg_collect_your_order: "z.B. 'Bitte sammeln Sie Ihre Bestellung von 123 Imaginary St, Northcote, 3070'" spree_classification_primary_taxon_error: "Taxon %{taxon} ist das primäre Taxon von %{product} und kann nicht gelöscht werden" spree_order_availability_error: "Distributor oder Bestellzyklus kann die Produkte in Ihrem Warenkorb nicht liefern" spree_order_populator_error: "Dieser Verteiler oder Bestellzyklus kann nicht alle Produkte in Ihrem Einkaufswagen liefern. Bitte wählen Sie ein anderes." + spree_order_cycle_error: "Bitte wählen Sie einen Bestellzyklus für diese Bestellung aus." spree_order_populator_availability_error: "Dieses Produkt ist im ausgewählten Distributor oder Bestellzyklus nicht verfügbar." spree_distributors_error: "Mindestens ein Hub muss ausgewählt sein" spree_user_enterprise_limit_error: "^ %{email} darf keine weiteren Unternehmen besitzen (Limit ist %{enterprise_limit})." spree_variant_product_error: muss mindestens eine Variante haben - your_profil_live: "Dein Profil live" - see: "Sehen" - live: "Leben" + your_profil_live: "Ihr Profil live" + see: "Sehen Sie" + live: "live" manage: "Verwalten" resend: "Erneut senden" add_and_manage_products: "Produkte hinzufügen und verwalten" add_and_manage_order_cycles: "Bestellzyklen hinzufügen und verwalten" - manage_order_cycles: "Auftragszyklen verwalten" + manage_order_cycles: "Bestellzyklen verwalten" manage_products: "Produkte verwalten" - edit_profile_details: "Profildetails bearbeiten" - edit_profile_details_etc: "Ändern Sie Ihre Profilbeschreibung, Bilder usw." - order_cycle: "Bestellungszyklus" - order_cycles: "Bestellrunden" + edit_profile_details: "Unternehmensdetails bearbeiten" + edit_profile_details_etc: "Ändern Sie Ihre Unternehmensbeschreibung, Bilder usw." + order_cycle: "Bestellzyklus" + order_cycles: "Bestellzyklen" enterprise_relationships: "Unternehmensberechtigungen" remove_tax: "Steuer entfernen" first_name_begins_with: "Der Vorname beginnt mit" @@ -2128,10 +2130,10 @@ de_DE: hub_sidebar_at_least: "Mindestens ein Hub muss ausgewählt sein" hub_sidebar_blue: "Blau" hub_sidebar_red: "rot" - order_cycles_closed_for_hub: "Der von Ihnen ausgewählte Hub ist vorübergehend für Bestellungen geschlossen. Bitte versuchen Sie es später noch einmal." + order_cycles_closed_for_hub: "Der Bestellzyklus des von Ihnen ausgewählten Ladens ist derzeit geschlossen. Bitte versuchen Sie es später noch einmal." report_customers_distributor: "Verteiler" - report_customers_supplier: "Anbieter" - report_customers_cycle: "Bestellungszyklus" + report_customers_supplier: "Lieferant" + report_customers_cycle: "Bestellzyklus" report_customers_type: "Berichtsart" report_customers_csv: "Download als CSV" report_producers: "Erzeuger:" @@ -2148,9 +2150,9 @@ de_DE: report_users: "Benutzer:" report_tax_rates: Steuersätze report_tax_types: Steuerarten - report_header_order_cycle: Bestellrunde + report_header_order_cycle: Bestellzyklus report_header_user: Benutzer - report_header_email: Email + report_header_email: E-Mail-Adresse report_header_status: Status report_header_comments: Kommentare report_header_first_name: Vorname @@ -2210,11 +2212,11 @@ de_DE: report_header_unallocated: Nicht zugewiesen report_header_max_quantity_excess: Max Menge Überschuss report_header_taxons: Taxonen - report_header_supplier: Anbieter + report_header_supplier: Lieferant report_header_producer: Erzeuger report_header_producer_suburb: Erzeuger Vorort report_header_unit: Einheit - report_header_group_buy_unit_quantity: Gruppen-Kaufeinheitsmenge + report_header_group_buy_unit_quantity: Gruppenkauf Einheitsmenge report_header_cost: Kosten report_header_shipping_cost: Versandkosten report_header_curr_cost_per_unit: Curr. Kosten pro Einheit @@ -2223,14 +2225,14 @@ de_DE: report_header_sells: vertreibt report_header_visible: öffentlich sichtbar report_header_price: Preis - report_header_unit_size: Einheitsgröße + report_header_unit_size: Einheit report_header_distributor: Verteiler report_header_distributor_address: Verteileradresse report_header_distributor_city: Verteilerstadt report_header_distributor_postcode: Verteilerpostleitzahl report_header_delivery_address: Lieferadresse report_header_delivery_postcode: Liefer-Postleitzahl - report_header_bulk_unit_size: Bulk-Einheitsgröße + report_header_bulk_unit_size: Bulk-Einheit report_header_weight: Gewicht report_header_sum_total: Gesamtsumme report_header_date_of_order: Datum der Bestellung @@ -2274,14 +2276,14 @@ de_DE: report_header_not_confirmed: Nicht bestätigt report_header_gst_on_income: Umsatzsteuer auf Einkommen report_header_gst_free_income: Unbesteuertes Einkommen - report_header_total_untaxable_produce: Total unversteuerbares Produkt (keine Steuer) - report_header_total_taxable_produce: Gesamtsteuerpflichtiges Produkt (inklusive Steuern) + report_header_total_untaxable_produce: Summe nicht steuerpflichtiger Produkte (keine Steuer) + report_header_total_taxable_produce: Summe steuerpflichtiger Produkte (inkl. Steuern) report_header_total_untaxable_fees: Summe nicht steuerpflichtiger Gebühren (keine Steuern) - report_header_total_taxable_fees: Summe der steuerpflichtigen Gebühren (inklusive Steuern) + report_header_total_taxable_fees: Summe steuerpflichtiger Gebühren (inkl. Steuern) report_header_delivery_shipping_cost: Lieferung Versandkosten (inkl. MwSt.) report_header_transaction_fee: Transaktionsgebühr (keine Steuern) - report_header_total_untaxable_admin: Total nicht steuerbare Admin-Anpassungen (keine Steuern) - report_header_total_taxable_admin: Total steuerpflichtige Admin-Anpassungen (inklusive Steuern) + report_header_total_untaxable_admin: Summe nicht steuerpflichtiger Admin-Anpassungen (keine Steuern) + report_header_total_taxable_admin: Summe steuerpflichtiger Admin-Anpassungen (inkl. Steuern) initial_invoice_number: "Anfangsrechnungsnummer:" invoice_date: "Rechnungsdatum:" due_date: "Geburtstermin:" @@ -2295,7 +2297,7 @@ de_DE: saving: "Speichern .." success: "Erfolg" failure: "Fehler" - unsaved_changes_confirmation: "Nicht gespeicherte Änderungen gehen verloren. Mache trotzdem weiter?" + unsaved_changes_confirmation: "Nicht gespeicherte Änderungen gehen verloren. Trotzdem fortfahren?" one_product_unsaved: "Änderungen an einem Produkt bleiben ungesichert." products_unsaved: "Änderungen an %{n} Produkten bleiben ungesichert." is_already_manager: "ist schon ein Manager!" @@ -2317,10 +2319,10 @@ de_DE: payment_methods: "Zahlungsarten" payment_method_fee: "Transaktionsgebühr" payment_processing_failed: "Die Zahlung konnte nicht verarbeitet werden. Bitte überprüfen Sie die eingegebenen Daten" - payment_method_not_supported: "Diese Zahlungsmethode wird nicht unterstützt. Bitte wählen Sie einen anderen." + payment_method_not_supported: "Diese Zahlungsart wird nicht unterstützt. Bitte wählen Sie eine andere." payment_updated: "Zahlung wurde aktualisiert" inventory_settings: "Katalogeinstellungen" - tag_rules: "Stichwort-Regeln" + tag_rules: "Stichwortregeln" shop_preferences: "Ladeneinstellungen" enterprise_fee_whole_order: Ganze Bestellung enterprise_fee_by: "%{type} Gebühr von %{role} %{enterprise_name}" @@ -2358,7 +2360,7 @@ de_DE: enterprise_register_success_notice: "Herzliche Glückwünsche! Registrierung für %{enterprise} ist abgeschlossen!" enterprise_bulk_update_success_notice: "Unternehmen wurden erfolgreich aktualisiert" enterprise_bulk_update_error: 'Update fehlgeschlagen' - enterprise_shop_show_error: "Der gesuchte Shop existiert nicht oder ist auf OFN inaktiv. Bitte schauen Sie nach anderen Shops!" + enterprise_shop_show_error: "Der gesuchte Laden existiert nicht oder ist im Open Food Network inaktiv. Bitte schauen Sie nach anderen Läden!" order_cycles_create_notice: 'Ihr Bestellzyklus wurde erstellt.' order_cycles_update_notice: 'Ihr Bestellzyklus wurde aktualisiert.' order_cycles_bulk_update_notice: 'Bestellzyklen wurden aktualisiert.' @@ -2377,8 +2379,9 @@ de_DE: pending: steht aus shipped: Wird versendet js: - saving: 'Speichern ...' + saving: 'Wird gespeichert ...' changes_saved: 'Änderungen gespeichert' + authorising: "Wird autorisiert ..." save_changes_first: Änderungen zuerst speichern. all_changes_saved: Alle Änderungen gespeichert unsaved_changes: Du hast nicht gespeicherte Änderungen @@ -2394,37 +2397,40 @@ de_DE: resolve_errors: Bitte beheben Sie die folgenden Fehler more_items: "+ %{count} Mehr" default_card_updated: Standardkarte aktualisiert + default_card_voids_auth: Durch das Ändern Ihrer Standardkarte werden die vorhandenen Berechtigungen für Abbuchungen des Ladens entfernt. Sie können die Berechtigungen nach dem Aktualisieren der Standardkarte erneut erteilen. Möchten Sie die Standardkarte ändern? cart: add_to_cart_failed: > Beim Hinzufügen dieses Produkts zum Warenkorb ist ein Problem aufgetreten. - Möglicherweise ist es nicht mehr verfügbar oder der Shop schließt. + Möglicherweise ist es nicht mehr verfügbar oder der Laden schließt gerade. admin: enterprise_limit_reached: "Sie haben die Standardgrenze für Unternehmen pro Konto erreicht. Schreiben Sie an %{contact_email}, wenn Sie es erhöhen müssen." modals: - got_it: "Ich habs" + got_it: "Verstanden" close: "Schließen" continue: "Fortsetzen" invite: "Einladen" invite_title: "Laden Sie einen nicht registrierten Benutzer ein" tag_rule_help: - title: Stichwort-Regeln + title: Stichwortregeln overview: Überblick overview_text: > - Mit StichwortßRegeln können Sie beschreiben, welche Elemente für welche + Mit Stichwortregeln können Sie beschreiben, welche Elemente für welche Kunden sichtbar sind. Elemente können Versandarten, Zahlungsarten, Produkte und Bestellzyklen sein. - by_default_rules: "\"Standardmäßig ...\" Regeln" + by_default_rules: "'Standardregeln'" by_default_rules_text: > - Mit Standardregeln können Sie Elemente verbergen, sodass sie standardmäßig - nicht sichtbar sind. Dieses Verhalten kann dann durch nicht standardmäßige - Regeln für Kunden mit bestimmten Stichwörtern überschrieben werden. - customer_tagged_rules: "'Kunden mit Stichwort ...' Regeln" + Mit 'Standardregeln' können Sie Elemente verbergen, so dass sie standardmäßig + nicht sichtbar sind. Durch die Verwendung von 'Regeln für Kunden mit + Stichwort', können diese Elemente für bestimmte Kunden wieder angezeigt + werden. + customer_tagged_rules: "'Regeln für Kunden mit Stichwort'" customer_tagged_rules_text: > - Durch das Erstellen von Regeln für ein bestimmtes Stichwort können Sie - die Standardregeln für bestimmte Kunden überschreiben. + Durch das Erstellen von 'Regeln für Kunden mit Stichwort' können Sie + Elemente für diese Kunden verbergen oder die 'Standardregel' zum Ausblenden + überschreiben und Elemente für bestimmte Kunden wieder anzeigen. terms_and_conditions_info: title: "Allgemeine Geschäftsbedingungen hochladen" - message_1: "Allgemeine Geschäftsbedingungen sind der Vertrag zwischen Ihnen, dem Verkäufer und dem Käufer. Wenn Sie hier eine Datei hochladen, müssen Käufer Ihre Allgemeinen Geschäftsbedingungen akzeptieren, um die Kaufabwicklung abzuschließen. Für den Käufer wird dies als Kontrollkästchen an der Kasse angezeigt, das aktiviert werden muss, um mit der Kasse fortzufahren. Wir empfehlen Ihnen dringend, die Allgemeinen Geschäftsbedingungen in Übereinstimmung mit den nationalen Gesetzen hochzuladen." + message_1: "Allgemeine Geschäftsbedingungen sind der Vertrag zwischen Ihnen, dem Verkäufer und dem Käufer. Wenn Sie hier eine Datei hochladen, müssen Käufer Ihre Allgemeinen Geschäftsbedingungen akzeptieren, um die Kaufabwicklung abzuschließen. Für den Käufer wird dies als Kontrollkästchen an der Kasse angezeigt, das aktiviert werden muss, um mit der Bezahlung fortzufahren. Wir empfehlen Ihnen dringend, Allgemeine Geschäftsbedingungen in Übereinstimmung mit den nationalen Gesetzen hochzuladen." message_2: "Käufer müssen die Allgemeinen Geschäftsbedingungen nur einmal akzeptieren. Wenn Sie jedoch Ihre Allgemeinen Geschäftsbedingungen ändern, müssen Käufer diese erneut akzeptieren, bevor sie zur Kasse gehen können." terms_and_conditions_warning: title: "Allgemeine Geschäftsbedingungen hochladen" @@ -2446,9 +2452,9 @@ de_DE: über das Open Food Network herzustellen, wird immer kostenlos sein. hub_shop: Hub hub_shop_text1: > - Ihr Unternehmen ist das Rückgrat Ihres lokalen Lebensmittelsystems. - Sie aggregieren Produkte von anderen Unternehmen und können sie über - Ihren Laden im Open Food Network verkaufen. + Verkaufen Sie Produkte anderer Erzeuger oder Läden im Open Food Network. + Als Händler führen Sie die Produkte zusammen und sind mit Ihrem Laden + ein zentraler Bestandteil Ihres lokalen Lebensmittelsystems. hub_shop_text2: > Hubs können viele Formen annehmen, egal ob es sich um eine Lebensmittelkooperative, eine Einkaufsgruppe, ein Gemüsekistenprogramm oder ein lokales Lebensmittelgeschäft @@ -2463,11 +2469,11 @@ de_DE: choose_package_text2: > Klicken Sie auf ein Paket, um weitere Informationen zu erhalten. Klicken Sie SPEICHERN, wenn Sie fertig sind! - profile_only: Profil nur + profile_only: nur Profil profile_only_cost: "KOSTEN: IMMER KOSTENLOS" profile_only_text1: > - Ein Profil macht dich sichtbar und kontaktierbar für andere und ist - eine Möglichkeit, deine Geschichte zu teilen. + Ein Profil macht Sie sichtbar und kontaktierbar für andere und ist eine + Möglichkeit, Ihre Geschichte zu erzählen. profile_only_text2: > Wenn Sie es vorziehen, sich auf die Lebensmittelerzeugung zu konzentrieren, und den Verkauf anderen zu überlassen, benötigen Sie keinen Laden im @@ -2480,16 +2486,16 @@ de_DE: Verkaufen Sie Ihre Produkte direkt an Kunden über Ihren eigenen Laden im Open Food Network. producer_shop_text2: > - Ein Erzeugerladen ist nur für Ihre Produkte gedacht. Wenn Sie Produkte - anderer verkaufen möchten, wählen Sie bitte "Hub". + Ein Erzeugerladen ist nur für Ihre eigenen Produkte bestimmt. Wenn Sie + Produkte anderer verkaufen möchten, wählen Sie "Hub". producer_hub: Produzent Hub producer_hub_text1: > - Ihr Unternehmen ist das Rückgrat Ihres lokalen Lebensmittelsystems. - Sie können sowohl Ihre eigenen Produkte als auch Produkte anderer Unternehmen - über Ihren Laden im Open Food Network verkaufen. + Verkaufen Sie eigene Produkte und Produkte anderer Erzeuger oder Läden + im Open Food Network. Mit Ihrem Laden sind Sie ein zentraler Bestandteil + Ihres lokalen Lebensmittelsystems. producer_hub_text2: > - Producer Hubs können viele Formen annehmen, sei es ein CSA, ein Veggie-Box-Programm - oder eine Food Coop mit einem Dachgarten. + Erzeugerläden können viele Formen annehmen, sei es eine solidarische + Landwirtschaft, eine Gemüse-Abo oder eine Food Coop mit einem Dachgarten. producer_hub_text3: > Das Open Food Network zielt darauf ab, so viele Hub-Modelle wie möglich zu unterstützen. Daher möchten wir Ihnen unabhängig von Ihrer Situation @@ -2501,7 +2507,7 @@ de_DE: sell_own_produce: Verkaufen Sie Ihre eigenen Produkte sell_both: Verkaufe Produkte von dir selbst und anderen enterprise_producer: - producer: Produzent + producer: Erzeuger producer_text1: > Produzenten machen leckere Dinge zum essen oder zum trinken. Sie sind ein Produzent, wenn Sie Nutzpflanzen anbauen, Nutztiere aufziehen, brauen, @@ -2510,13 +2516,13 @@ de_DE: Hersteller können auch andere Funktionen übernehmen, wie z. B. die Zusammenführung von Nahrungsmitteln anderer Unternehmen und den Verkauf über ein Geschäft im Open Food Network. - non_producer: Nicht-Produzent + non_producer: Händler non_producer_text1: > - Nichtproduzenten produzieren selbst keine Lebensmittel, dh sie können - ihre eigenen Produkte nicht über das Open Food Network herstellen. + Händler produzieren selbst keine Lebensmittel, d.h. sie können keine + eigenen Produkte über das Open Food Network anbieten. non_producer_text2: > - Stattdessen spezialisieren sich Nichtproduzenten darauf, Produzenten - mit dem Endverbraucher zu verbinden, sei es durch Aggregation, Sortierung, + Stattdessen spezialisieren sich Händler darauf, Produzenten mit dem + Endverbraucher zu verbinden, sei es durch Zusammenführung, Sortierung, Verpackung, Verkauf oder Lieferung von Lebensmitteln. producer_desc: Produzenten von Lebensmitteln producer_example: z.B. Züchter, Bäcker, Brauer, Hersteller @@ -2534,13 +2540,13 @@ de_DE: variants_loaded: "%{num_of_variants_loaded} von %{total_number_of_variants} Varianten geladen" loading_variants: "lade Varianten" tag_rules: - shipping_method_tagged_top: "Versandarten markiert" + shipping_method_tagged_top: "Versandarten markiert mit" shipping_method_tagged_bottom: "sind:" - payment_method_tagged_top: "Zahlungsmethoden markiert" + payment_method_tagged_top: "Zahlungsarten markiert mit" payment_method_tagged_bottom: "sind:" - order_cycle_tagged_top: "Bestellzyklen markiert" + order_cycle_tagged_top: "Bestellzyklen markiert mit" order_cycle_tagged_bottom: "sind:" - inventory_tagged_top: "Inventarvarianten markiert" + inventory_tagged_top: "Produktvarianten markiert mit" inventory_tagged_bottom: "sind:" new_tag_rule_dialog: select_rule_type: "Wählen Sie einen Regelart aus:" @@ -2590,7 +2596,7 @@ de_DE: invalid: "ungültig" resend_user_email_confirmation: resend: "Erneut senden" - sending: "Erneut senden..." + sending: "Erneut senden ..." done: "Erneut senden ✓" failed: "Erneut senden fehlgeschlagen ✗" order_cycles: @@ -2671,7 +2677,7 @@ de_DE: show_hide_payment: 'Zahlungsarten an der Kasse anzeigen?' show_hide_order_cycles: 'Bestellzyklen in meinem Laden anzeigen?' visible: SICHTBAR - not_visible: UNSICHTBAR + not_visible: NICHT SICHTBAR services: unsaved_changes_message: Noch nicht gespeicherte Änderungen vorhanden, jetzt speichern oder ignorieren? save: SPEICHERN @@ -2684,8 +2690,8 @@ de_DE: could_not_delete_customer: 'Der Kunde konnte nicht gelöscht werden' product_import: confirmation: | - Dadurch wird der Lagerbestand für alle Produkte auf Null gesetzt - Unternehmen, die in der hochgeladenen Datei nicht vorhanden sind. + Dadurch wird der Lagerbestand für alle Produkte dieses Unternehmens, + die in der hochgeladenen Datei nicht vorhanden sind, auf Null gesetzt. order_cycles: create_failure: "Fehler beim Erstellen des Bestellzyklus" update_success: 'Ihr Bestellzyklus wurde aktualisiert.' @@ -2693,10 +2699,10 @@ de_DE: no_distributors: In diesem Bestellzyklus gibt es keine Distributoren. Dieser Bestellzyklus ist für Kunden erst sichtbar, wenn Sie einen hinzufügen. Möchten Sie diesen Bestellzyklus weiterhin speichern? enterprises: producer: "Erzeuger" - non_producer: "Widerverkäufer" + non_producer: "Händler" customers: select_shop: 'Bitte wählen Sie zuerst einen Laden aus' - could_not_create: Es tut uns leid! Konnte nicht ... Erstellen + could_not_create: 'Es tut uns leid! Konnte nicht erstellt werden:' subscriptions: closes: schließt closed: geschlossen @@ -2706,7 +2712,7 @@ de_DE: order: "Bestellung" registration: welcome_to_ofn: "Willkommen im Open Food Network!" - signup_or_login: "Beginnen Sie mit der Anmeldung (oder melden Sie sich an)" + signup_or_login: "Beginnen Sie mit der Registrierung (oder melden Sie sich an)" have_an_account: "Hast du schon ein Konto?" action_login: "Jetzt einloggen." stripe_elements: @@ -2849,9 +2855,9 @@ de_DE: shipment: "Sendung" shipment_inc_vat: "Versand inklusive Mehrwertsteuer" shipping_tax_rate: "Versandsteuersatz" - category: "Kategorie" + category: "Produktkategorie" delivery: "Lieferung" - temperature_controlled: "Temperaturgesteuert" + temperature_controlled: "Temperaturüberwacht" new_product: "Neues Produkt" administration: "Verwaltung" logged_in_as: "Eingeloggt als" @@ -2863,9 +2869,9 @@ de_DE: start: "Start" end: "Ende" stop: "Halt" - first: "Zuerst" - previous: "Bisherige" - last: "Zuletzt" + first: "Erste" + previous: "Vorherige" + last: "Letzte" spree: more: "Mehr" your_order_is_empty_add_product: "Ihre Bestellung ist leer. Suchen Sie oben ein Produkt und fügen Sie es hinzu" @@ -2892,7 +2898,7 @@ de_DE: no_tracking_present: "Keine Tracking-Details angegeben." tracking: "Sendungsverfolgung" tracking_number: "Sendungscode" - order_total: "Auftrag insgesamt" + order_total: "Bestellung insgesamt" customer_details: "Kundendetails" customer_search: "Kundensuche" choose_a_customer: "Wählen Sie einen Kunden aus" @@ -2961,7 +2967,7 @@ de_DE: new_tax_rate: "Neuer Steuersatz" tax_category: "Steuerkategorie" rate: "Bewertung" - tax_rate_amount_explanation: "Steuersätze sind ein Dezimalbetrag für Berechnungshilfen (d. H. Wenn der Steuersatz 5% beträgt, geben Sie 0,05 ein)." + tax_rate_amount_explanation: "Steuersätze sind ein Dezimalbetrag zur Vereinfachung der Berechnung (d.h. wenn der Steuersatz 5 % beträgt, geben Sie 0,05 ein)." included_in_price: "Im Preis inbegriffen" show_rate_in_label: "Rate im Etikett anzeigen" back_to_tax_rates_list: "Zurück zur Liste der Steuersätze" @@ -3002,7 +3008,7 @@ de_DE: nore: "Mehr" no_results: "Keine Ergebnisse" create: "Neu" - loading: "Wird geladen" + loading: "Wird geladen ..." flat_percent: "Flacher Prozentsatz" per_kg: "Pro kg" amount: "Betrag" @@ -3019,9 +3025,9 @@ de_DE: alt_text: "alternativer Text" thumbnail: "Miniaturansicht" back_to_images_list: "Zurück zur Bilderliste" - email: Email + email: E-Mail-Adresse account_updated: "Konto aktualisiert!" - email_updated: "Das Konto wird aktualisiert, sobald die neue E-Mail bestätigt wurde." + email_updated: "Das Konto wird aktualisiert, sobald die neue E-Mail-Adresse bestätigt wurde." my_account: "Mein Konto" date: "Datum" time: "Zeit" @@ -3030,8 +3036,8 @@ de_DE: zipcode: Postleitzahl weight: Gewicht (pro kg oder lb) error_user_destroy_with_orders: "Benutzer mit abgeschlossenen Bestellungen dürfen nicht gelöscht werden" - cannot_create_payment_without_payment_methods: "Sie können keine Zahlung für eine Bestellung erstellen, ohne dass Zahlungsmethoden definiert sind." - please_define_payment_methods: "Bitte definieren Sie zunächst die Zahlungsmethoden." + cannot_create_payment_without_payment_methods: "Sie können keine Zahlung für eine Bestellung erstellen, ohne dass Zahlungsarten definiert sind." + please_define_payment_methods: "Bitte definieren Sie zunächst die Zahlungsarten." options: "Optionen" has_no_shipped_units: "hat keine versendeten Einheiten" successfully_created: '%{resource} wurde erfolgreich erstellt!' @@ -3053,13 +3059,14 @@ de_DE: other: "%{count} Fehler haben das Speichern dieses Datensatzes verhindert:" there_were_problems_with_the_following_fields: "Es gab Probleme mit folgenden Feldern" payments_list: - date_time: "Datum / Uhrzeit" + date_time: "Datum/Uhrzeit" amount: "Betrag" payment_method: "Zahlungsart" payment_state: "Zahlungsstatus" errors: messages: - blank: "kann nicht leer sein" + included_price_validation: "kann nur ausgewählt werden, wenn Sie eine Standardsteuerzone festgelegt haben" + blank: "darf nicht leer sein" layouts: admin: login_nav: @@ -3068,7 +3075,7 @@ de_DE: admin: tab: dashboard: "Übersicht" - orders: "Aufträge" + orders: "Bestellungen" bulk_order_management: "Massenbearbeitung von Bestellungen" subscriptions: "Abonnements" products: "Produkte" @@ -3079,7 +3086,7 @@ de_DE: configuration: "Aufbau" users: "Benutzer" roles: "Rollen" - order_cycles: "Bestellrunden" + order_cycles: "Bestellzyklen" enterprises: "Unternehmen" enterprise_relationships: "Berechtigungen" customers: "Kunden" @@ -3091,11 +3098,11 @@ de_DE: properties: index: properties: "Eigenschaften" - new_property: "Neues Eigentum" + new_property: "Neue Eigenschaft" name: "Name" presentation: "Präsentation" new: - new_property: "Neues Eigentum" + new_property: "Neue Eigenschaft" edit: editing_property: "Eigenschaft bearbeiten" back_to_properties_list: "Zurück zur Eigenschaftenliste" @@ -3142,14 +3149,14 @@ de_DE: edit: "Bearbeiten" order_not_updated: "Die Bestellung konnte nicht aktualisiert werden" note: "Hinweis" - first: "Zuerst" + first: "Erste" last: "Letzte" - previous: "Bisherige" + previous: "Vorherige" next: "Weiter" - loading: "Wird geladen" + loading: "Wird geladen ..." no_orders_found: "Keine Bestellungen gefunden" results_found: "%{number} Ergebnisse gefunden." - viewing: "Anzeigen von %{start} bis %{end}." + viewing: "Angezeigt wird %{start} bis %{end}." print_invoices: "Rechnungen drucken" sortable_header: payment_state: "Zahlungsstatus" @@ -3157,7 +3164,7 @@ de_DE: completed_at: "Erfolgt am" number: "Nummer" state: "Status" - email: "E-Mail des Kunden" + email: "E-Mail-Adresse des Kunden" invoice: issued_on: "Ausgegeben am" tax_invoice: "Steuerrechnung" @@ -3172,10 +3179,10 @@ de_DE: order_cycle: "Bestellzyklus:" line_item_adjustments: "Anpassungen der Werbebuchung" order_adjustments: "Bestellanpassungen" - order_total: "Auftrag insgesamt" + order_total: "Bestellung insgesamt" overview: enterprises_header: - ofn_with_tip: Unternehmen sind Erzeuger und / oder Hubs und sind die grundlegende Organisationseinheit innerhalb des Open Food Network. + ofn_with_tip: Unternehmen sind Erzeuger und/oder Hubs und sind die grundlegende Organisationseinheit innerhalb des Open Food Network. products: active_products: zero: "Sie haben keine aktiven Produkte." @@ -3184,15 +3191,15 @@ de_DE: many: "Sie haben %{count} aktive Produkte" other: "Sie haben %{count} aktive Produkte" order_cycles: - order_cycles: "Bestellrunden" + order_cycles: "Bestellzyklen" order_cycles_tip: "Bestellzyklen bestimmen, wann und wo Ihre Produkte für Kunden verfügbar sind." you_have_active: zero: "Sie haben keine aktiven Bestellzyklen." one: "Sie haben einen aktiven Bestellzyklus." - few: "Sie haben %{count} aktive Auftragszyklen." - many: "Sie haben %{count} aktive Auftragszyklen." + few: "Sie haben %{count} aktive Bestellzyklen." + many: "Sie haben %{count} aktive Bestellzyklen." other: "Sie haben %{count} aktive Bestellzyklen." - manage_order_cycles: "BESTELLRUNDEN VERWALTEN" + manage_order_cycles: "BESTELLZYKLEN VERWALTEN" shipping_methods: index: shipping_methods: "Lieferart" @@ -3221,7 +3228,7 @@ de_DE: payment_methods: index: payment_methods: "Zahlungsarten" - new_payment_method: "Neue Zahlungsmethode" + new_payment_method: "Neue Zahlungsart" name: "Name" products_distributor: "Verteiler" provider: "Anbieter" @@ -3233,16 +3240,16 @@ de_DE: back_end: "Nur Backoffice" active_yes: "Ja" active_no: "Nein" - no_payment_methods_found: "Keine Zahlungsmethoden gefunden" + no_payment_methods_found: "Keine Zahlungsarten gefunden" new: new_payment_method: "Neue Zahlungsart" - back_to_payment_methods_list: "Zurück zur Liste der Zahlungsmethoden" + back_to_payment_methods_list: "Zurück zur Liste der Zahlungsarten" edit: new: "Neu" - editing_payment_method: "Zahlungsmethode bearbeiten" - back_to_payment_methods_list: "Zurück zur Liste der Zahlungsmethoden" + editing_payment_method: "Zahlungsart bearbeiten" + back_to_payment_methods_list: "Zurück zur Liste der Zahlungsarten" stripe_connect: - enterprise_select_placeholder: Wählen... + enterprise_select_placeholder: Auswählen ... loading_account_information_msg: Kontoinformationen von Stripe werden geladen, bitte warten ... stripe_disabled_msg: Streifenzahlungen wurden vom Systemadministrator deaktiviert. request_failed_msg: Es tut uns leid. Beim Versuch, Kontodaten mit Stripe zu überprüfen, ist ein Fehler aufgetreten. @@ -3250,7 +3257,7 @@ de_DE: connect_one: Verbinde eins access_revoked_msg: Der Zugriff auf dieses Stripe-Konto wurde widerrufen. Bitte verbinden Sie Ihr Konto erneut. status: Status - connected: In Verbindung gebracht + connected: Verbunden account_id: Konto-ID business_name: Geschäftsname charges_enabled: Gebühren aktiviert @@ -3266,9 +3273,17 @@ de_DE: front_end: "Nur zur Kasse" back_end: "Nur Backoffice" tags: "Stichwörter" - deactivation_warning: "Durch Deaktivieren einer Zahlungsmethode kann die Zahlungsmethode aus Ihrer Liste verschwinden. Alternativ können Sie eine Zahlungsmethode auf der Checkout-Seite ausblenden, indem Sie die Option \"Anzeige\" auf \"Nur Backoffice\" setzen." + deactivation_warning: "Durch Deaktivieren einer Zahlungsart kann die Zahlungsart aus Ihrer Liste verschwinden. Alternativ können Sie eine Zahlungsart auf der Checkout-Seite ausblenden, indem Sie die Option \"Anzeige\" auf \"Nur Backoffice\" setzen." providers: provider: "Anbieter" + check: "Bargeld, EC etc. (Zahlungen, für die keine automatische Validierung erforderlich ist)" + migs: "MasterCard Internet Gateway Service (MIGS)" + pin: "Pin Zahlungen" + paypalexpress: "PayPal Express" + stripeconnect: "Stripe" + stripesca: "Stripe SCA" + bogus: "Bogus" + bogussimple: "BogusSimple" payments: source_forms: stripe: @@ -3281,10 +3296,10 @@ de_DE: new: title: "Neues Produkt" new_product: "Neues Produkt" - supplier: "Anbieter" + supplier: "Lieferant" product_name: "Produktname" - units: "Einheitsgröße" - value: "Wert" + units: "Einheit" + value: "Menge" unit_name: "Einheitenname" price: "Preis" on_hand: "Verfügbar" @@ -3298,7 +3313,7 @@ de_DE: title: Massenbearbeitung von Produkten indicators: title: LADE PRODUKTE - no_products: "Bisher sind keine Produkte gewählt worden. Warum fügen Sie nicht einige hinzu?" + no_products: "Bisher gibt es noch keine Produkte. Warum fügen Sie nicht einige hinzu?" no_results: "Entschuldigung, keine Ergebnisse stimmen überein" products_head: name: Name @@ -3325,12 +3340,12 @@ de_DE: table: select_and_search: "Wählen Sie Filter aus und klicken Sie auf %{option}, um auf Ihre Daten zuzugreifen." bulk_coop: - bulk_coop_supplier_report: 'Massen-Kooperative - Summen nach Anbieter' - bulk_coop_allocation: 'Massenkoop - Zuteilung' - bulk_coop_packing_sheets: 'Massenkoop - Verpackungsblätter' - bulk_coop_customer_payments: 'Massenkoop - Kundenzahlungen' + bulk_coop_supplier_report: 'Massen-Kooperative - Summen nach Lieferanten' + bulk_coop_allocation: 'Massen-Kooperative - Zuteilung' + bulk_coop_packing_sheets: 'Massen-Kooperative - Packlisten' + bulk_coop_customer_payments: 'Massen-Kooperative - Kundenzahlungen' customer_names_message: - customer_names_tip: "Wenn Kundennamen für von Ihnen gelieferte Bestellungen ausgeblendet sind, können Sie sich an den Händler wenden und ihn fragen, ob er seine Shop-Einstellungen aktualisieren kann, damit seine Lieferanten Kundennamen anzeigen können." + customer_names_tip: "Wenn Kundennamen für von Ihnen gelieferte Bestellungen ausgeblendet sind, können Sie sich an den Händler wenden und ihn bitten, seine Ladeneinstellungen anzupassen, damit seine Lieferanten Kundennamen anzeigen können." users: index: listing_users: "Benutzer auflisten" @@ -3376,7 +3391,7 @@ de_DE: display_name_placeholder: 'z.B. Tomaten' autocomplete: out_of_stock: "Nicht vorrättig" - producer_name: "Produzent" + producer_name: "Erzeuger" unit: "Einheit" shared: sortable_header: @@ -3442,9 +3457,9 @@ de_DE: order_mailer: cancel_email: customer_greeting: "Lieber %{name}," - instructions_html: "Ihre Bestellung mit %{distributor} wurde storniert. Bitte bewahren Sie diese Stornierungsinformationen für Ihre Unterlagen auf." + instructions_html: "Ihre Bestellung bei %{distributor} wurde storniert. Bitte bewahren Sie diese Stornierungsinformationen für Ihre Unterlagen auf." dont_cancel: "Wenn Sie Ihre Meinung geändert haben oder diese Bestellung nicht stornieren möchten, wenden Sie sich bitte an %{email}" - order_summary_canceled_html: "Bestellübersicht # %{number} [ABGESAGT]" + order_summary_canceled_html: "Bestellübersicht # %{number} [STORNIERT]" details: "Hier sind die Details Ihrer Bestellung:" unpaid_order: "Ihre Bestellung wurde nicht bezahlt, daher wurde keine Rückerstattung vorgenommen" paid_order: "Ihre Bestellung wurde bezahlt, sodass %{distributor} den vollen Betrag zurückerstattet hat" @@ -3467,10 +3482,10 @@ de_DE: Falls die URL nicht funktioniert, bitte den Link kopieren und in die Adresszeile Ihres Browsers einfügen subject: "Anweisungen zum Zurücksetzen des Passworts" confirmation_instructions: - subject: "Bitte OFN-Konto bestätigen" + subject: "Bitte bestätigen Sie Ihre Anmeldung bei Open Food Network DE!" shipment_mailer: shipped_email: - dear_customer: "Sehr geehrter Kunde," + dear_customer: "Sehr geehrte Kundin, sehr geehrter Kunde," instructions: "Ihre Bestellung wurde versandt" shipment_summary: "Übersicht" subject: "Versandbenachrichtigung" @@ -3481,7 +3496,7 @@ de_DE: test_email: greeting: "Herzliche Glückwünsche!" message: "Wenn Sie diese E-Mail erhalten haben, sind Ihre E-Mail-Einstellungen korrekt." - subject: "Test Email" + subject: "Test-E-Mail" order_state: address: Adresse adjustments: Verbesserungen @@ -3504,7 +3519,23 @@ de_DE: paused: pausiert canceled: storniert paypal: + already_refunded: "Diese Zahlung wurde zurückerstattet und es können keine weiteren Maßnahmen ergriffen werden." + no_payment_via_admin_backend: "Sie können derzeit keine PayPal-Konten über das Administrationsmenü belasten." + transaction: "PayPal-Transaktion" + payer_id: "Zahler-ID" + transaction_id: "Transaktions-ID" + token: "Token" + refund: "Rückerstattung" refund_amount: "Betrag" + original_amount: "Ursprünglicher Betrag: %{amount}" + refund_successful: "PayPal-Rückerstattung erfolgreich" + refund_unsuccessful: "PayPal-Rückerstattung nicht erfolgreich" + actions: + refund: "Rückerstattung" + flash: + cancel: "Sie möchten PayPal nicht nutzen? Kein Problem." + connection_failed: "Es konnte keine Verbindung zu PayPal hergestellt werden." + generic_error: "PayPal ist fehlgeschlagen. %{reasons}" users: form: account_settings: Konto Einstellungen @@ -3543,9 +3574,11 @@ de_DE: delete?: Löschen? cards: authorised_shops: Bevollmächtigte Läden + authorised_shops_agreement: Dies ist die Liste der Läden, in denen Ihre Standardkreditkarte für eventuelle Abonnements (d.h. wiederholte Bestellungen) belastet werden darf. Ihre Kartendaten werden sicher aufbewahrt und nicht an Ladenbesitzer weitergegeben. Sie werden immer benachrichtigt, wenn Ihre Kreditkarte belastet wird. Wenn Sie das Kontrollkästchen für einen Laden aktivieren, erklären Sie sich damit einverstanden, diesen Laden zu autorisieren, Anweisungen an das Finanzinstitut, das Ihre Kreditkarte ausgestellt hat, zu senden, um Zahlungen gemäß den Bedingungen eines Abonnements entgegenzunehmen, das Sie mit diesem Laden erstellen. saved_cards_popover: Dies ist die Liste der Karten, die Sie für spätere Verwendung gespeichert haben. Ihr "Standard" wird automatisch beim Abschließen einer Bestellung ausgewählt und kann von allen Geschäften belastet werden, die Sie dazu berechtigt haben (siehe rechts). authorised_shops: shop_name: "Ladenname" + allow_charges?: "Abbuchungen für die Standardkarte zulassen?" localized_number: invalid_format: hat ein ungültiges Format. Bitte Ziffern eingeben. api: diff --git a/config/locales/en.yml b/config/locales/en.yml index 39ed7508e4..e24f65f92c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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," diff --git a/config/locales/en_CA.yml b/config/locales/en_CA.yml index dad9ad185f..169bd8a352 100644 --- a/config/locales/en_CA.yml +++ b/config/locales/en_CA.yml @@ -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: diff --git a/config/locales/es.yml b/config/locales/es.yml index e6a4213707..3dbed9d222 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -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: diff --git a/config/locales/fr_CA.yml b/config/locales/fr_CA.yml index 1e7f92e3f9..d37b729121 100644 --- a/config/locales/fr_CA.yml +++ b/config/locales/fr_CA.yml @@ -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: diff --git a/config/locales/it.yml b/config/locales/it.yml index 36d063d6bf..71cd491b74 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -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 diff --git a/config/routes.rb b/config/routes.rb index a657f7f234..5236a42c62 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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 diff --git a/db/migrate/20201219120055_add_order_to_adjustments.rb b/db/migrate/20201219120055_add_order_to_adjustments.rb new file mode 100644 index 0000000000..e0d6c32287 --- /dev/null +++ b/db/migrate/20201219120055_add_order_to_adjustments.rb @@ -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 diff --git a/db/migrate/20210115143738_add_deleted_at_to_enterprise_fee.rb b/db/migrate/20210115143738_add_deleted_at_to_enterprise_fee.rb new file mode 100644 index 0000000000..f3afbf28d9 --- /dev/null +++ b/db/migrate/20210115143738_add_deleted_at_to_enterprise_fee.rb @@ -0,0 +1,5 @@ +class AddDeletedAtToEnterpriseFee < ActiveRecord::Migration + def change + add_column :enterprise_fees, :deleted_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index e71191e567..fc360b11e1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20201113163227) do +ActiveRecord::Schema.define(version: 20210115143738) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -108,6 +108,7 @@ ActiveRecord::Schema.define(version: 20201113163227) do t.datetime "updated_at", null: false t.integer "tax_category_id" t.boolean "inherits_tax_category", default: false, null: false + t.datetime "deleted_at" end add_index "enterprise_fees", ["enterprise_id"], name: "index_enterprise_fees_on_enterprise_id", using: :btree @@ -393,6 +394,7 @@ ActiveRecord::Schema.define(version: 20201113163227) do t.string "adjustable_type", limit: 255 t.decimal "included_tax", precision: 10, scale: 2, default: 0.0, null: false t.string "state", limit: 255 + t.integer "order_id" end add_index "spree_adjustments", ["adjustable_id"], name: "index_adjustments_on_order_id", using: :btree diff --git a/engines/order_management/app/services/order_management/order/updater.rb b/engines/order_management/app/services/order_management/order/updater.rb index e7cb051a4f..d17bc72727 100644 --- a/engines/order_management/app/services/order_management/order/updater.rb +++ b/engines/order_management/app/services/order_management/order/updater.rb @@ -4,7 +4,8 @@ module OrderManagement module Order class Updater attr_reader :order - delegate :payments, :line_items, :adjustments, :shipments, :update_hooks, to: :order + + delegate :payments, :line_items, :adjustments, :shipments, to: :order def initialize(order) @order = order @@ -32,7 +33,7 @@ module OrderManagement # update totals a second time in case updated adjustments have an effect on the total update_totals - order.update_attributes_without_callbacks( + order.update_columns( payment_state: order.payment_state, shipment_state: order.shipment_state, item_total: order.item_total, @@ -40,12 +41,6 @@ module OrderManagement payment_total: order.payment_total, total: order.total ) - - run_hooks - end - - def run_hooks - update_hooks.each { |hook| order.__send__(hook) } end # Updates the following Order total values: @@ -55,9 +50,9 @@ module OrderManagement # - adjustment_total - total value of all adjustments # - total - order total, it's the equivalent to item_total plus adjustment_total def update_totals - order.payment_total = payments.completed.map(&:amount).sum + order.payment_total = payments.completed.sum(:amount) order.item_total = line_items.map(&:amount).sum - order.adjustment_total = adjustments.eligible.map(&:amount).sum + order.adjustment_total = adjustments.eligible.sum(:amount) order.total = order.item_total + order.adjustment_total end diff --git a/engines/order_management/app/services/order_management/subscriptions/summarizer.rb b/engines/order_management/app/services/order_management/subscriptions/summarizer.rb index 24221c9c38..fe02a9ad9c 100644 --- a/engines/order_management/app/services/order_management/subscriptions/summarizer.rb +++ b/engines/order_management/app/services/order_management/subscriptions/summarizer.rb @@ -37,13 +37,13 @@ module OrderManagement def send_placement_summary_emails @summaries.values.each do |summary| - SubscriptionMailer.placement_summary_email(summary).deliver_later + SubscriptionMailer.placement_summary_email(summary).deliver_now end end def send_confirmation_summary_emails @summaries.values.each do |summary| - SubscriptionMailer.confirmation_summary_email(summary).deliver_later + SubscriptionMailer.confirmation_summary_email(summary).deliver_now end end diff --git a/engines/order_management/spec/services/order_management/order/updater_spec.rb b/engines/order_management/spec/services/order_management/order/updater_spec.rb index 1bd901d37c..438da6961d 100644 --- a/engines/order_management/spec/services/order_management/order/updater_spec.rb +++ b/engines/order_management/spec/services/order_management/order/updater_spec.rb @@ -5,20 +5,18 @@ require 'spec_helper' module OrderManagement module Order describe Updater do - let(:order) { build(:order) } + let(:order) { create(:order) } let(:updater) { OrderManagement::Order::Updater.new(order) } before { allow(order).to receive(:backordered?) { false } } it "updates totals" do - payments = [double(amount: 5), double(amount: 5)] - allow(order).to receive_message_chain(:payments, :completed).and_return(payments) + allow(order).to receive_message_chain(:payments, :completed, :sum).and_return(10) line_items = [double(amount: 10), double(amount: 20)] allow(order).to receive_messages line_items: line_items - adjustments = [double(amount: 10), double(amount: -20)] - allow(order).to receive_message_chain(:adjustments, :eligible).and_return(adjustments) + allow(order).to receive_message_chain(:adjustments, :eligible, :sum).and_return(-10) updater.update_totals expect(order.payment_total).to eq 10 diff --git a/engines/order_management/spec/services/order_management/subscriptions/summarizer_spec.rb b/engines/order_management/spec/services/order_management/subscriptions/summarizer_spec.rb index f0d8f9dbe0..0c47277e47 100644 --- a/engines/order_management/spec/services/order_management/subscriptions/summarizer_spec.rb +++ b/engines/order_management/spec/services/order_management/subscriptions/summarizer_spec.rb @@ -100,7 +100,7 @@ module OrderManagement let(:summary1) { double(:summary) } let(:summary2) { double(:summary) } let(:summaries) { { 1 => summary1, 2 => summary2 } } - let(:mail_mock) { double(:mail, deliver_later: true) } + let(:mail_mock) { double(:mail, deliver_now: true) } before do summarizer.instance_variable_set(:@summaries, summaries) @@ -116,7 +116,7 @@ module OrderManagement let(:summary1) { double(:summary) } let(:summary2) { double(:summary) } let(:summaries) { { 1 => summary1, 2 => summary2 } } - let(:mail_mock) { double(:mail, deliver_later: true) } + let(:mail_mock) { double(:mail, deliver_now: true) } before do summarizer.instance_variable_set(:@summaries, summaries) diff --git a/lib/spree/core/calculated_adjustments.rb b/lib/spree/core/calculated_adjustments.rb index c502c00099..6388369209 100644 --- a/lib/spree/core/calculated_adjustments.rb +++ b/lib/spree/core/calculated_adjustments.rb @@ -40,6 +40,7 @@ module Spree amount: amount, source: old_calculable, originator: self, + order: order_object_for(target), label: label, mandatory: mandatory, state: state @@ -74,6 +75,17 @@ module Spree Rails.application.config.spree.calculators end private_class_method :spree_calculators + + private + + def order_object_for(target) + # Temporary method for adjustments transition. + if target.is_a? Spree::Order + target + elsif target.respond_to?(:order) + target.order + end + end end end end diff --git a/lib/spree/core/controller_helpers/respond_with.rb b/lib/spree/core/controller_helpers/respond_with.rb index 5fcd4bc2cb..c35a071f25 100644 --- a/lib/spree/core/controller_helpers/respond_with.rb +++ b/lib/spree/core/controller_helpers/respond_with.rb @@ -16,7 +16,7 @@ module ActionController # Fix spree issues #3531 and #2210 (patch provided by leiyangyou) if (defined_response = collector.response) && - !Spree::BaseController.spree_responders[self.class.to_s.to_sym].try(:[], + !ApplicationController.spree_responders[self.class.to_s.to_sym].try(:[], action_name.to_sym) if action = options.delete(:action) render action: action diff --git a/lib/spree/core/mail_interceptor.rb b/lib/spree/core/mail_interceptor.rb index 87ae2e33ab..27f7126218 100644 --- a/lib/spree/core/mail_interceptor.rb +++ b/lib/spree/core/mail_interceptor.rb @@ -8,8 +8,6 @@ module Spree module Core class MailInterceptor def self.delivering_email(message) - return unless MailSettings.override? - if Config[:intercept_email].present? message.subject = "#{message.to} #{message.subject}" message.to = Config[:intercept_email] diff --git a/lib/spree/core/mail_settings.rb b/lib/spree/core/mail_settings.rb index 80664d1c64..5fd94c8fa3 100644 --- a/lib/spree/core/mail_settings.rb +++ b/lib/spree/core/mail_settings.rb @@ -7,14 +7,8 @@ module Spree SECURE_CONNECTION_TYPES = ['None', 'SSL', 'TLS'].freeze # Override the Rails application mail settings based on preferences - # This makes it possible to configure the mail settings through an admin - # interface instead of requiring changes to the Rails envrionment file def self.init - new.override! if override? - end - - def self.override? - Config.override_actionmailer_config + new.override! end def override! diff --git a/lib/spree/i18n/initializer.rb b/lib/spree/i18n/initializer.rb index 64b649f708..9be3e4df0d 100644 --- a/lib/spree/i18n/initializer.rb +++ b/lib/spree/i18n/initializer.rb @@ -1,3 +1,3 @@ # frozen_string_literal: true -Spree::BaseController.include(Spree::ViewContext) +ApplicationController.include(Spree::ViewContext) diff --git a/lib/spree/responder.rb b/lib/spree/responder.rb index 3c0e60aa5b..2714599530 100644 --- a/lib/spree/responder.rb +++ b/lib/spree/responder.rb @@ -10,7 +10,7 @@ module Spree class_name = controller.class.name.to_sym action_name = options.delete(:action_name) - result = Spree::BaseController.spree_responders[class_name]. + result = ApplicationController.spree_responders[class_name]. try(:[], action_name). try(:[], self.format.to_sym) return unless result diff --git a/lib/stripe/authorize_response_patcher.rb b/lib/stripe/authorize_response_patcher.rb index 7614ff47be..d67b8dbca9 100644 --- a/lib/stripe/authorize_response_patcher.rb +++ b/lib/stripe/authorize_response_patcher.rb @@ -24,7 +24,8 @@ module Stripe next_action.present? && next_action["type"] == "authorize_with_url" - next_action["authorize_with_url"]["url"] + url = next_action["authorize_with_url"]["url"] + return url if url.match(%r{https?:\/\/[\S]+}) && url.include?("stripe.com") end # This field is used because the Spree code recognizes and stores it diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 3b4cd1428c..a19cada4d4 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -385,7 +385,7 @@ describe Admin::EnterprisesController, type: :controller do profile_enterprise1.enterprise_roles.build(user: new_owner).save profile_enterprise2.enterprise_roles.build(user: new_owner).save allow(controller).to receive_messages spree_current_user: new_owner - bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, sells: 'any', owner_id: new_owner.id }, '1' => { id: profile_enterprise2.id, sells: 'any', owner_id: new_owner.id } } } } + bulk_enterprise_params = { sets_enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, sells: 'any', owner_id: new_owner.id }, '1' => { id: profile_enterprise2.id, sells: 'any', owner_id: new_owner.id } } } } spree_put :bulk_update, bulk_enterprise_params profile_enterprise1.reload @@ -397,10 +397,10 @@ describe Admin::EnterprisesController, type: :controller do end it "cuts down the list of enterprises displayed when error received on bulk update" do - allow_any_instance_of(EnterpriseSet).to receive(:save) { false } + allow_any_instance_of(Sets::EnterpriseSet).to receive(:save) { false } profile_enterprise1.enterprise_roles.build(user: new_owner).save allow(controller).to receive_messages spree_current_user: new_owner - bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, visible: 'false' } } } } + bulk_enterprise_params = { sets_enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, visible: 'false' } } } } spree_put :bulk_update, bulk_enterprise_params expect(assigns(:enterprise_set).collection).to eq [profile_enterprise1] end @@ -409,7 +409,7 @@ describe Admin::EnterprisesController, type: :controller do context "as the owner of an enterprise" do it "allows 'sells' and 'owner' to be changed" do allow(controller).to receive_messages spree_current_user: original_owner - bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, sells: 'any', owner_id: new_owner.id }, '1' => { id: profile_enterprise2.id, sells: 'any', owner_id: new_owner.id } } } } + bulk_enterprise_params = { sets_enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, sells: 'any', owner_id: new_owner.id }, '1' => { id: profile_enterprise2.id, sells: 'any', owner_id: new_owner.id } } } } spree_put :bulk_update, bulk_enterprise_params profile_enterprise1.reload @@ -426,7 +426,7 @@ describe Admin::EnterprisesController, type: :controller do profile_enterprise1.enterprise_roles.build(user: new_owner).save profile_enterprise2.enterprise_roles.build(user: new_owner).save allow(controller).to receive_messages spree_current_user: admin_user - bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, sells: 'any', owner_id: new_owner.id }, '1' => { id: profile_enterprise2.id, sells: 'any', owner_id: new_owner.id } } } } + bulk_enterprise_params = { sets_enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, sells: 'any', owner_id: new_owner.id }, '1' => { id: profile_enterprise2.id, sells: 'any', owner_id: new_owner.id } } } } spree_put :bulk_update, bulk_enterprise_params profile_enterprise1.reload diff --git a/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb b/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb index 2f327e2de2..1e1437aee2 100644 --- a/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb +++ b/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb @@ -91,6 +91,20 @@ describe Spree::Admin::PaymentsController, type: :controller do end end + context "where further action is required" do + before do + allow_any_instance_of(Spree::Payment).to receive(:authorize!) do |payment| + payment.update cvv_response_message: "https://www.stripe.com/authorize" + payment.update state: "pending" + end + end + it "redirects to new payment page with flash error" do + spree_post :create, payment: params, order_id: order.number + + redirects_to_new_payment_page_with_flash_error(I18n.t('action_required')) + end + end + context "where both payment.process! and payment.authorize! work" do before do allow_any_instance_of(Spree::Payment).to receive(:authorize!) do |payment| @@ -227,5 +241,41 @@ describe Spree::Admin::PaymentsController, type: :controller do expect(flash[:success]).to eq(I18n.t(:payment_updated)) end end + + context 'on resend_authorization_email event' do + let(:params) { { e: 'resend_authorization_email', order_id: order.number, id: payment.id } } + let(:mail_mock) { double(:mailer_mock, deliver_later: true) } + + before do + allow(PaymentMailer).to receive(:authorize_payment) { mail_mock } + allow(request).to receive(:referer) { 'http://foo.com' } + allow(Spree::Payment).to receive(:find).with(payment.id.to_s) { payment } + allow(payment).to receive(:cvv_response_message).and_return("https://www.stripe.com/authorize") + end + + it "resends the authorization email" do + spree_put :fire, params + + expect(flash[:success]).to eq(I18n.t(:payment_updated)) + expect(PaymentMailer).to have_received(:authorize_payment) + expect(mail_mock).to have_received(:deliver_later) + end + end + + context 'on an unrecognized event' do + let(:params) { { e: 'unrecognized_event', order_id: order.number, id: payment.id } } + + before do + allow(request).to receive(:referer) { 'http://foo.com' } + allow(Spree::Payment).to receive(:find).with(payment.id.to_s) { payment } + end + + it 'does not process the event' do + spree_put :fire, params + + expect(payment).to_not receive(:unrecognized_event) + expect(flash[:error]).to eq(I18n.t(:cannot_perform_operation)) + end + end end end diff --git a/spec/controllers/spree/credit_cards_controller_spec.rb b/spec/controllers/spree/credit_cards_controller_spec.rb index eb5a3529b1..867fe89861 100644 --- a/spec/controllers/spree/credit_cards_controller_spec.rb +++ b/spec/controllers/spree/credit_cards_controller_spec.rb @@ -35,8 +35,6 @@ describe Spree::CreditCardsController, type: :controller do it "saves the card locally" do spree_post :new_from_token, params - pp response - expect{ spree_post :new_from_token, params }.to change(Spree::CreditCard, :count).by(1) card = Spree::CreditCard.last diff --git a/spec/features/admin/enterprise_fees_spec.rb b/spec/features/admin/enterprise_fees_spec.rb index 862a338f15..057840525b 100644 --- a/spec/features/admin/enterprise_fees_spec.rb +++ b/spec/features/admin/enterprise_fees_spec.rb @@ -18,11 +18,11 @@ feature ' login_as_admin_and_visit spree.edit_admin_general_settings_path click_link 'Enterprise Fees' - expect(page).to have_select "enterprise_fee_set_collection_attributes_0_enterprise_id" - expect(page).to have_select "enterprise_fee_set_collection_attributes_0_fee_type", selected: 'Packing fee' + expect(page).to have_select "sets_enterprise_fee_set_collection_attributes_0_enterprise_id" + expect(page).to have_select "sets_enterprise_fee_set_collection_attributes_0_fee_type", selected: 'Packing fee' expect(page).to have_selector "input[value='$0.50 / kg']" - expect(page).to have_select "enterprise_fee_set_collection_attributes_0_tax_category_id", selected: 'GST' - expect(page).to have_select "enterprise_fee_set_collection_attributes_0_calculator_type", selected: 'Flat Rate (per item)' + expect(page).to have_select "sets_enterprise_fee_set_collection_attributes_0_tax_category_id", selected: 'GST' + expect(page).to have_select "sets_enterprise_fee_set_collection_attributes_0_calculator_type", selected: 'Flat Rate (per item)' expect(page).to have_selector "input[value='#{amount}']" end @@ -34,11 +34,11 @@ feature ' login_as_admin_and_visit admin_enterprise_fees_path # And I fill in the fields for a new enterprise fee and click update - select 'Feedme', from: 'enterprise_fee_set_collection_attributes_0_enterprise_id' - select 'Admin', from: 'enterprise_fee_set_collection_attributes_0_fee_type' - fill_in 'enterprise_fee_set_collection_attributes_0_name', with: 'Hello!' - select 'GST', from: 'enterprise_fee_set_collection_attributes_0_tax_category_id' - select 'Flat Percent', from: 'enterprise_fee_set_collection_attributes_0_calculator_type' + select 'Feedme', from: 'sets_enterprise_fee_set_collection_attributes_0_enterprise_id' + select 'Admin', from: 'sets_enterprise_fee_set_collection_attributes_0_fee_type' + fill_in 'sets_enterprise_fee_set_collection_attributes_0_name', with: 'Hello!' + select 'GST', from: 'sets_enterprise_fee_set_collection_attributes_0_tax_category_id' + select 'Flat Percent', from: 'sets_enterprise_fee_set_collection_attributes_0_calculator_type' click_button 'Update' # Then I should see my fee and fields for the calculator @@ -46,11 +46,11 @@ feature ' expect(page).to have_selector "input[value='Hello!']" # When I fill in the calculator fields and click update - fill_in 'enterprise_fee_set_collection_attributes_0_calculator_attributes_preferred_flat_percent', with: '12.34' + fill_in 'sets_enterprise_fee_set_collection_attributes_0_calculator_attributes_preferred_flat_percent', with: '12.34' click_button 'Update' # Then I should see the correct values in my calculator fields - expect(page).to have_selector "#enterprise_fee_set_collection_attributes_0_calculator_attributes_preferred_flat_percent[value='12.34']" + expect(page).to have_selector "#sets_enterprise_fee_set_collection_attributes_0_calculator_attributes_preferred_flat_percent[value='12.34']" end scenario "editing an enterprise fee" do @@ -62,18 +62,18 @@ feature ' login_as_admin_and_visit admin_enterprise_fees_path # And I update the fields for the enterprise fee and click update - select 'Foo', from: 'enterprise_fee_set_collection_attributes_0_enterprise_id' - select 'Admin', from: 'enterprise_fee_set_collection_attributes_0_fee_type' - fill_in 'enterprise_fee_set_collection_attributes_0_name', with: 'Greetings!' - select 'Inherit From Product', from: 'enterprise_fee_set_collection_attributes_0_tax_category_id' - select 'Flat Percent', from: 'enterprise_fee_set_collection_attributes_0_calculator_type' + select 'Foo', from: 'sets_enterprise_fee_set_collection_attributes_0_enterprise_id' + select 'Admin', from: 'sets_enterprise_fee_set_collection_attributes_0_fee_type' + fill_in 'sets_enterprise_fee_set_collection_attributes_0_name', with: 'Greetings!' + select 'Inherit From Product', from: 'sets_enterprise_fee_set_collection_attributes_0_tax_category_id' + select 'Flat Percent', from: 'sets_enterprise_fee_set_collection_attributes_0_calculator_type' click_button 'Update' # Then I should see the updated fields for my fee - expect(page).to have_select "enterprise_fee_set_collection_attributes_0_enterprise_id", selected: 'Foo' - expect(page).to have_select "enterprise_fee_set_collection_attributes_0_fee_type", selected: 'Admin fee' + expect(page).to have_select "sets_enterprise_fee_set_collection_attributes_0_enterprise_id", selected: 'Foo' + expect(page).to have_select "sets_enterprise_fee_set_collection_attributes_0_fee_type", selected: 'Admin fee' expect(page).to have_selector "input[value='Greetings!']" - expect(page).to have_select 'enterprise_fee_set_collection_attributes_0_tax_category_id', selected: 'Inherit From Product' + expect(page).to have_select 'sets_enterprise_fee_set_collection_attributes_0_tax_category_id', selected: 'Inherit From Product' expect(page).to have_selector "option[selected]", text: 'Flat Percent (per item)' fee.reload @@ -123,17 +123,17 @@ feature ' within(".side_menu") { click_link 'Enterprise Fees' } click_link "Create One Now" - select distributor1.name, from: 'enterprise_fee_set_collection_attributes_0_enterprise_id' - select 'Packing', from: 'enterprise_fee_set_collection_attributes_0_fee_type' - fill_in 'enterprise_fee_set_collection_attributes_0_name', with: 'foo' - select 'GST', from: 'enterprise_fee_set_collection_attributes_0_tax_category_id' - select 'Flat Percent', from: 'enterprise_fee_set_collection_attributes_0_calculator_type' + select distributor1.name, from: 'sets_enterprise_fee_set_collection_attributes_0_enterprise_id' + select 'Packing', from: 'sets_enterprise_fee_set_collection_attributes_0_fee_type' + fill_in 'sets_enterprise_fee_set_collection_attributes_0_name', with: 'foo' + select 'GST', from: 'sets_enterprise_fee_set_collection_attributes_0_tax_category_id' + select 'Flat Percent', from: 'sets_enterprise_fee_set_collection_attributes_0_calculator_type' click_button 'Update' expect(flash_message).to eq('Your enterprise fees have been updated.') # After saving, we should be redirected to the fees for our chosen enterprise - expect(page).not_to have_select 'enterprise_fee_set_collection_attributes_1_enterprise_id', selected: 'Second Distributor' + expect(page).not_to have_select 'sets_enterprise_fee_set_collection_attributes_1_enterprise_id', selected: 'Second Distributor' enterprise_fee = EnterpriseFee.find_by name: 'foo' expect(enterprise_fee.enterprise).to eq(distributor1) @@ -146,14 +146,14 @@ feature ' visit edit_admin_enterprise_path(distributor1) within(".side_menu") { click_link 'Enterprise Fees' } click_link "Manage Enterprise Fees" - expect(page).to have_field 'enterprise_fee_set_collection_attributes_0_name', with: 'One' - expect(page).not_to have_field 'enterprise_fee_set_collection_attributes_1_name', with: 'Two' + expect(page).to have_field 'sets_enterprise_fee_set_collection_attributes_0_name', with: 'One' + expect(page).not_to have_field 'sets_enterprise_fee_set_collection_attributes_1_name', with: 'Two' visit edit_admin_enterprise_path(distributor2) within(".side_menu") { click_link 'Enterprise Fees' } click_link "Manage Enterprise Fees" - expect(page).not_to have_field 'enterprise_fee_set_collection_attributes_0_name', with: 'One' - expect(page).to have_field 'enterprise_fee_set_collection_attributes_0_name', with: 'Two' + expect(page).not_to have_field 'sets_enterprise_fee_set_collection_attributes_0_name', with: 'One' + expect(page).to have_field 'sets_enterprise_fee_set_collection_attributes_0_name', with: 'Two' end it "only allows me to select enterprises I have access to" do @@ -164,7 +164,7 @@ feature ' visit edit_admin_enterprise_path(distributor2) within(".side_menu") { click_link 'Enterprise Fees' } click_link "Manage Enterprise Fees" - expect(page).to have_select('enterprise_fee_set_collection_attributes_0_enterprise_id', + expect(page).to have_select('sets_enterprise_fee_set_collection_attributes_0_enterprise_id', selected: 'Second Distributor', options: ['', 'First Distributor', 'Second Distributor']) end diff --git a/spec/features/admin/enterprises/index_spec.rb b/spec/features/admin/enterprises/index_spec.rb index a79fbaab3c..2709dd2201 100644 --- a/spec/features/admin/enterprises/index_spec.rb +++ b/spec/features/admin/enterprises/index_spec.rb @@ -15,7 +15,7 @@ feature 'Enterprises Index' do within("tr.enterprise-#{s.id}") do expect(page).to have_content s.name - expect(page).to have_select "enterprise_set_collection_attributes_1_sells" + expect(page).to have_select "sets_enterprise_set_collection_attributes_1_sells" expect(page).to have_content "Settings" expect(page).to have_content "Delete" expect(page).to have_no_content "Payment Methods" @@ -25,7 +25,7 @@ feature 'Enterprises Index' do within("tr.enterprise-#{d.id}") do expect(page).to have_content d.name - expect(page).to have_select "enterprise_set_collection_attributes_0_sells" + expect(page).to have_select "sets_enterprise_set_collection_attributes_0_sells" expect(page).to have_content "Settings" expect(page).to have_content "Delete" expect(page).to have_content "Payment Methods" @@ -51,10 +51,10 @@ feature 'Enterprises Index' do it "updates the enterprises" do within("tr.enterprise-#{d.id}") do - expect(page).to have_checked_field "enterprise_set_collection_attributes_0_visible" - uncheck "enterprise_set_collection_attributes_0_visible" - select 'any', from: "enterprise_set_collection_attributes_0_sells" - select d_manager.email, from: 'enterprise_set_collection_attributes_0_owner_id' + expect(page).to have_checked_field "sets_enterprise_set_collection_attributes_0_visible" + uncheck "sets_enterprise_set_collection_attributes_0_visible" + select 'any', from: "sets_enterprise_set_collection_attributes_0_sells" + select d_manager.email, from: 'sets_enterprise_set_collection_attributes_0_owner_id' end click_button "Update" expect(flash_message).to eq('Enterprises updated successfully') @@ -83,12 +83,12 @@ feature 'Enterprises Index' do it "does not update the enterprises and displays errors" do d_row_index = enterprise_row_index(d.name) within("tr.enterprise-#{d.id}") do - select d_manager.email, from: "enterprise_set_collection_attributes_#{d_row_index}_owner_id" + select d_manager.email, from: "sets_enterprise_set_collection_attributes_#{d_row_index}_owner_id" end second_distributor_row_index = enterprise_row_index(second_distributor.name) within("tr.enterprise-#{second_distributor.id}") do - select d_manager.email, from: "enterprise_set_collection_attributes_#{second_distributor_row_index}_owner_id" + select d_manager.email, from: "sets_enterprise_set_collection_attributes_#{second_distributor_row_index}_owner_id" end click_button "Update" expect(flash_message).to eq('Update failed') diff --git a/spec/features/admin/order_spec.rb b/spec/features/admin/order_spec.rb index e34a0e6d79..1a85278b2d 100644 --- a/spec/features/admin/order_spec.rb +++ b/spec/features/admin/order_spec.rb @@ -127,21 +127,23 @@ feature ' login_as_admin_and_visit spree.edit_admin_order_path(order) quantity = order.line_items.first.quantity + max_quantity = 0 total = order.display_total within("tr.stock-item", text: order.products.first.name) do find("a.edit-item").click expect(page).to have_input(:quantity) - fill_in(:quantity, with: order.line_items.first.product.on_hand + 1) + max_quantity = find("input[name='quantity']")["max"].to_i + fill_in(:quantity, with: max_quantity + 1) find("a.save-item").click end + accept_js_alert expect(page).to_not have_content "Loading..." within("tr.stock-item", text: order.products.first.name) do - expect(page).to have_text("#{quantity} x") + expect(page).to have_text("#{max_quantity} x") end - expect(order.reload.display_total).to eq(total) - expect(order.reload.line_items.first.quantity).to eq(quantity) + expect(order.reload.line_items.first.quantity).to eq(max_quantity) end scenario "can't change distributor or order cycle once order has been finalized" do diff --git a/spec/features/admin/payments_stripe_spec.rb b/spec/features/admin/payments_stripe_spec.rb index 00c110195a..a4467a4f5a 100644 --- a/spec/features/admin/payments_stripe_spec.rb +++ b/spec/features/admin/payments_stripe_spec.rb @@ -70,7 +70,7 @@ feature ' context "with a card that fails on registration because it requires(redirects) extra auth" do before do stub_payment_intents_post_request_with_redirect order: order, - redirect_url: "www.dummy.org" + redirect_url: "https://www.stripe.com/authorize" end it "fails to add a payment due to card error" do diff --git a/spec/features/admin/products_spec.rb b/spec/features/admin/products_spec.rb index 60adf49939..e2a3b8a3c3 100644 --- a/spec/features/admin/products_spec.rb +++ b/spec/features/admin/products_spec.rb @@ -68,28 +68,6 @@ feature ' expect(product.master.options_text).to eq("5kg") end - scenario "creating directly from the new product path", js: true do - login_as_admin_and_visit spree.new_admin_product_path - - select 'New supplier', from: 'product_supplier_id' - fill_in 'product_name', with: 'A new product !!!' - select "Weight (kg)", from: 'product_variant_unit_with_scale' - fill_in 'product_unit_value_with_description', with: 5 - select taxon.name, from: "product_primary_taxon_id" - fill_in 'product_price', with: '19.99' - fill_in 'product_on_hand', with: 5 - select 'Test Tax Category', from: 'product_tax_category_id' - page.find("div[id^='taTextElement']").native.send_keys('A description...') - - click_button 'Create' - - expect(current_path).to eq spree.admin_products_path - expect(flash_message).to eq('Product "A new product !!!" has been successfully created!') - product = Spree::Product.find_by(name: 'A new product !!!') - expect(product.variant_unit).to eq('weight') - expect(product.variant_unit_scale).to eq(1000) - end - scenario "creating an on-demand product", js: true do login_as_admin_and_visit spree.admin_products_path diff --git a/spec/helpers/checkout_helper_spec.rb b/spec/helpers/checkout_helper_spec.rb index 6665af4458..7dcf64b0c5 100644 --- a/spec/helpers/checkout_helper_spec.rb +++ b/spec/helpers/checkout_helper_spec.rb @@ -31,4 +31,29 @@ describe CheckoutHelper, type: :helper do order.distributor.allow_guest_orders = false expect(helper.guest_checkout_allowed?).to be false end + + describe "#checkout_adjustments_for" do + let(:order) { create(:order_with_totals_and_distribution) } + let(:enterprise_fee) { create(:enterprise_fee, amount: 123) } + let!(:fee_adjustment) { + create(:adjustment, originator: enterprise_fee, adjustable: order, source: order) + } + + before do + # Sanity check initial adjustments state + expect(order.adjustments.shipping.count).to eq 1 + expect(order.adjustments.enterprise_fee.count).to eq 1 + end + + it "collects adjustments on the order" do + adjustments = helper.checkout_adjustments_for(order) + + shipping_adjustment = order.adjustments.shipping.first + expect(adjustments).to include shipping_adjustment + + admin_fee_summary = adjustments.last + expect(admin_fee_summary.label).to eq I18n.t(:orders_form_admin) + expect(admin_fee_summary.amount).to eq 123 + end + end end diff --git a/spec/lib/spree/core/calculated_adjustments_spec.rb b/spec/lib/spree/core/calculated_adjustments_spec.rb index b9210c4f29..484654e2eb 100644 --- a/spec/lib/spree/core/calculated_adjustments_spec.rb +++ b/spec/lib/spree/core/calculated_adjustments_spec.rb @@ -28,6 +28,11 @@ describe Spree::Core::CalculatedAdjustments do tax_rate.create_adjustment("foo", target, order) end + it "should be associated with the order" do + tax_rate.create_adjustment("foo", target, order) + expect(target.adjustments.first.order_id).to eq order.id + end + it "should have the correct originator and an amount derived from the calculator and supplied calculable" do adjustment = tax_rate.create_adjustment("foo", target, order) expect(adjustment).not_to be_nil diff --git a/spec/lib/spree/core/mail_settings_spec.rb b/spec/lib/spree/core/mail_settings_spec.rb index a8db4d5eed..ed799f980f 100644 --- a/spec/lib/spree/core/mail_settings_spec.rb +++ b/spec/lib/spree/core/mail_settings_spec.rb @@ -7,83 +7,60 @@ module Spree describe MailSettings do let!(:subject) { MailSettings.new } - context "override option is true" do - before { Config.override_actionmailer_config = true } + context "enable delivery" do + before { Config.enable_mail_delivery = true } - context "init" do - it "calls override!" do - expect(MailSettings).to receive(:new).and_return(subject) - expect(subject).to receive(:override!) - MailSettings.init - end - end - - context "enable delivery" do - before { Config.enable_mail_delivery = true } - - context "overrides appplication defaults" do - context "authentication method is none" do - before do - Config.mail_host = "smtp.example.com" - Config.mail_domain = "example.com" - Config.mail_port = 123 - Config.mail_auth_type = MailSettings::SECURE_CONNECTION_TYPES[0] - Config.smtp_username = "schof" - Config.smtp_password = "hellospree!" - Config.secure_connection_type = "TLS" - subject.override! - end - - it { expect(ActionMailer::Base.smtp_settings[:address]).to eq "smtp.example.com" } - it { expect(ActionMailer::Base.smtp_settings[:domain]).to eq "example.com" } - it { expect(ActionMailer::Base.smtp_settings[:port]).to eq 123 } - it { expect(ActionMailer::Base.smtp_settings[:authentication]).to eq "None" } - it { expect(ActionMailer::Base.smtp_settings[:enable_starttls_auto]).to be_truthy } - - it "doesnt touch user name config" do - expect(ActionMailer::Base.smtp_settings[:user_name]).to be_nil - end - - it "doesnt touch password config" do - expect(ActionMailer::Base.smtp_settings[:password]).to be_nil - end - end - end - - context "when mail_auth_type is other than none" do + context "overrides appplication defaults" do + context "authentication method is none" do before do - Config.mail_auth_type = "login" + Config.mail_host = "smtp.example.com" + Config.mail_domain = "example.com" + Config.mail_port = 123 + Config.mail_auth_type = MailSettings::SECURE_CONNECTION_TYPES[0] Config.smtp_username = "schof" Config.smtp_password = "hellospree!" + Config.secure_connection_type = "TLS" subject.override! end - context "overrides user credentials" do - it { expect(ActionMailer::Base.smtp_settings[:user_name]).to eq "schof" } - it { expect(ActionMailer::Base.smtp_settings[:password]).to eq "hellospree!" } + it { expect(ActionMailer::Base.smtp_settings[:address]).to eq "smtp.example.com" } + it { expect(ActionMailer::Base.smtp_settings[:domain]).to eq "example.com" } + it { expect(ActionMailer::Base.smtp_settings[:port]).to eq 123 } + it { expect(ActionMailer::Base.smtp_settings[:authentication]).to eq "None" } + it { expect(ActionMailer::Base.smtp_settings[:enable_starttls_auto]).to be_truthy } + + it "doesnt touch user name config" do + expect(ActionMailer::Base.smtp_settings[:user_name]).to be_nil + end + + it "doesnt touch password config" do + expect(ActionMailer::Base.smtp_settings[:password]).to be_nil end end end - context "do not enable delivery" do + context "when mail_auth_type is other than none" do before do - Config.enable_mail_delivery = false + Config.mail_auth_type = "login" + Config.smtp_username = "schof" + Config.smtp_password = "hellospree!" subject.override! end - it { expect(ActionMailer::Base.perform_deliveries).to be_falsy } + context "overrides user credentials" do + it { expect(ActionMailer::Base.smtp_settings[:user_name]).to eq "schof" } + it { expect(ActionMailer::Base.smtp_settings[:password]).to eq "hellospree!" } + end end end - context "override option is false" do - before { Config.override_actionmailer_config = false } - - context "init" do - it "doesnt calls override!" do - expect(subject).not_to receive(:override!) - MailSettings.init - end + context "do not enable delivery" do + before do + Config.enable_mail_delivery = false + subject.override! end + + it { expect(ActionMailer::Base.perform_deliveries).to be_falsy } end end end diff --git a/spec/lib/stripe/authorize_response_patcher_spec.rb b/spec/lib/stripe/authorize_response_patcher_spec.rb index 1572c1ced5..9ecb800ee2 100644 --- a/spec/lib/stripe/authorize_response_patcher_spec.rb +++ b/spec/lib/stripe/authorize_response_patcher_spec.rb @@ -20,12 +20,12 @@ module Stripe let(:params) { { "status" => "requires_source_action", "next_source_action" => { "type" => "authorize_with_url", - "authorize_with_url" => { "url" => "test_url" } } } + "authorize_with_url" => { "url" => "https://www.stripe.com/authorize" } } } } it "patches response.cvv_result.message with the url in the response" do new_response = patcher.call! - expect(new_response.cvv_result['message']).to eq "test_url" + expect(new_response.cvv_result['message']).to eq "https://www.stripe.com/authorize" end end end diff --git a/spec/models/enterprise_fee_spec.rb b/spec/models/enterprise_fee_spec.rb index 7bd90db7c4..b5065ecee7 100644 --- a/spec/models/enterprise_fee_spec.rb +++ b/spec/models/enterprise_fee_spec.rb @@ -139,4 +139,24 @@ describe EnterpriseFee do end.to change(order.adjustments, :count).by(0) end end + + describe "soft-deletion" do + let(:tax_category) { create(:tax_category) } + let(:enterprise_fee) { create(:enterprise_fee, tax_category: tax_category ) } + let!(:adjustment) { create(:adjustment, originator: enterprise_fee) } + + before do + enterprise_fee.destroy + enterprise_fee.reload + end + + it "soft-deletes the enterprise fee" do + expect(enterprise_fee.deleted_at).to_not be_nil + end + + it "can be accessed by old adjustments" do + expect(adjustment.reload.originator).to eq enterprise_fee + expect(adjustment.originator.tax_category).to eq enterprise_fee.tax_category + end + end end diff --git a/spec/models/spree/order/adjustments_spec.rb b/spec/models/spree/order/adjustments_spec.rb index b3ad28b40d..6b327f464e 100644 --- a/spec/models/spree/order/adjustments_spec.rb +++ b/spec/models/spree/order/adjustments_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'spec_helper' + describe Spree::Order do let(:order) { Spree::Order.new } @@ -21,19 +22,20 @@ describe Spree::Order do end context "totaling adjustments" do - let(:adjustment1) { build(:adjustment, amount: 5) } - let(:adjustment2) { build(:adjustment, amount: 10) } + let!(:adjustment1) { create(:adjustment, amount: 5) } + let!(:adjustment2) { create(:adjustment, amount: 10) } + let(:adjustments) { Spree::Adjustment.where(id: [adjustment1, adjustment2]) } context "#ship_total" do it "should return the correct amount" do - allow(order).to receive_message_chain :adjustments, shipping: [adjustment1, adjustment2] + allow(order).to receive_message_chain :adjustments, shipping: adjustments expect(order.ship_total).to eq 15 end end context "#tax_total" do it "should return the correct amount" do - allow(order).to receive_message_chain :adjustments, tax: [adjustment1, adjustment2] + allow(order).to receive_message_chain :adjustments, tax: adjustments expect(order.tax_total).to eq 15 end end diff --git a/spec/models/spree/order/updating_spec.rb b/spec/models/spree/order/updating_spec.rb deleted file mode 100644 index 068fde6be9..0000000000 --- a/spec/models/spree/order/updating_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Spree::Order do - let(:order) { build(:order) } - - context "#update!" do - let(:line_items) { [build(:line_item, amount: 5)] } - - context "when there are update hooks" do - before { Spree::Order.register_update_hook :foo } - after { Spree::Order.update_hooks.clear } - it "should call each of the update hooks" do - expect(order).to receive :foo - order.update! - end - end - end -end diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 7eb83f60aa..73f2b228e1 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -436,30 +436,6 @@ describe Spree::Order do end end - context "add_update_hook" do - before do - Spree::Order.class_eval do - register_update_hook :add_awesome_sauce - end - end - - after do - Spree::Order.update_hooks = Set.new - end - - it "calls hook during update" do - order = create(:order) - expect(order).to receive(:add_awesome_sauce) - order.update! - end - - it "calls hook during finalize" do - order = create(:order) - expect(order).to receive(:add_awesome_sauce) - order.finalize! - end - end - context "ensure shipments will be updated" do before { Spree::Shipment.create!(order: order) } diff --git a/spec/models/spree/payment_spec.rb b/spec/models/spree/payment_spec.rb index 2420c7766d..cbba2b4b9a 100644 --- a/spec/models/spree/payment_spec.rb +++ b/spec/models/spree/payment_spec.rb @@ -83,25 +83,25 @@ describe Spree::Payment do context "#process!" do it "should purchase if with auto_capture" do payment = build_stubbed(:payment, payment_method: gateway) - payment.payment_method.should_receive(:auto_capture?).and_return(true) - payment.should_receive(:purchase!) + expect(payment.payment_method).to receive(:auto_capture?).and_return(true) + expect(payment).to receive(:purchase!) payment.process! end it "should authorize without auto_capture" do payment = build_stubbed(:payment, payment_method: gateway) - payment.payment_method.should_receive(:auto_capture?).and_return(false) - payment.should_receive(:authorize!) + expect(payment.payment_method).to receive(:auto_capture?).and_return(false) + expect(payment).to receive(:authorize!) payment.process! end it "should make the state 'processing'" do - payment.should_receive(:started_processing!) + expect(payment).to receive(:started_processing!) payment.process! end it "should invalidate if payment method doesnt support source" do - payment.payment_method.should_receive(:supports?).with(payment.source).and_return(false) + expect(payment.payment_method).to receive(:supports?).with(payment.source).and_return(false) expect { payment.process! }.to raise_error(Spree::Core::GatewayError) expect(payment.state).to eq('invalid') end @@ -109,17 +109,17 @@ describe Spree::Payment do context "#authorize" do it "should call authorize on the gateway with the payment amount" do - payment.payment_method.should_receive(:authorize).with(amount_in_cents, - card, - anything).and_return(success_response) + expect(payment.payment_method).to receive(:authorize).with( + amount_in_cents, card, anything + ).and_return(success_response) payment.authorize! end it "should call authorize on the gateway with the currency code" do payment.stub currency: 'GBP' - payment.payment_method.should_receive(:authorize).with(amount_in_cents, - card, - hash_including({ currency: "GBP" })).and_return(success_response) + expect(payment.payment_method).to receive(:authorize).with( + amount_in_cents, card, hash_including({ currency: "GBP" }) + ).and_return(success_response) payment.authorize! end @@ -137,9 +137,9 @@ describe Spree::Payment do context "if successful" do before do - payment.payment_method.should_receive(:authorize).with(amount_in_cents, - card, - anything).and_return(success_response) + expect(payment.payment_method).to receive(:authorize).with( + amount_in_cents, card, anything + ).and_return(success_response) end it "should store the response_code, avs_response and cvv_response fields" do @@ -151,7 +151,7 @@ describe Spree::Payment do end it "should make payment pending" do - payment.should_receive(:pend!) + expect(payment).to receive(:pend!) payment.authorize! end end @@ -159,8 +159,8 @@ describe Spree::Payment do context "if unsuccessful" do it "should mark payment as failed" do gateway.stub(:authorize).and_return(failed_response) - payment.should_receive(:failure) - payment.should_not_receive(:pend) + expect(payment).to receive(:failure) + expect(payment).to_not receive(:pend) expect { payment.authorize! }.to raise_error(Spree::Core::GatewayError) @@ -170,7 +170,7 @@ describe Spree::Payment do context "purchase" do it "should call purchase on the gateway with the payment amount" do - gateway.should_receive(:purchase).with(amount_in_cents, card, anything).and_return(success_response) + expect(gateway).to receive(:purchase).with(amount_in_cents, card, anything).and_return(success_response) payment.purchase! end @@ -188,9 +188,9 @@ describe Spree::Payment do context "if successful" do before do - payment.payment_method.should_receive(:purchase).with(amount_in_cents, - card, - anything).and_return(success_response) + expect(payment.payment_method).to receive(:purchase).with( + amount_in_cents, card, anything + ).and_return(success_response) end it "should store the response_code and avs_response" do @@ -200,7 +200,7 @@ describe Spree::Payment do end it "should make payment complete" do - payment.should_receive(:complete!) + expect(payment).to receive(:complete!) payment.purchase! end end @@ -208,8 +208,8 @@ describe Spree::Payment do context "if unsuccessful" do it "should make payment failed" do gateway.stub(:purchase).and_return(failed_response) - payment.should_receive(:failure) - payment.should_not_receive(:pend) + expect(payment).to receive(:failure) + expect(payment).not_to receive(:pend) expect { payment.purchase! }.to raise_error(Spree::Core::GatewayError) end end @@ -227,11 +227,11 @@ describe Spree::Payment do context "if successful" do before do - payment.payment_method.should_receive(:capture).with(payment, card, anything).and_return(success_response) + expect(payment.payment_method).to receive(:capture).and_return(success_response) end it "should make payment complete" do - payment.should_receive(:complete) + expect(payment).to receive(:complete) payment.capture! end @@ -245,8 +245,8 @@ describe Spree::Payment do context "if unsuccessful" do it "should not make payment complete" do gateway.stub capture: failed_response - payment.should_receive(:failure) - payment.should_not_receive(:complete) + expect(payment).to receive(:failure) + expect(payment).to_not receive(:complete) expect { payment.capture! }.to raise_error(Spree::Core::GatewayError) end end @@ -256,9 +256,9 @@ describe Spree::Payment do context "when payment is completed" do it "should do nothing" do payment = build_stubbed(:payment, state: 'completed') - payment.should_not_receive(:complete) - payment.payment_method.should_not_receive(:capture) - payment.log_entries.should_not_receive(:create) + expect(payment).to_not receive(:complete) + expect(payment.payment_method).to_not receive(:capture) + expect(payment.log_entries).to_not receive(:create) payment.capture! end end @@ -273,7 +273,7 @@ describe Spree::Payment do context "when profiles are supported" do it "should call payment_gateway.void with the payment's response_code" do gateway.stub payment_profiles_supported?: true - gateway.should_receive(:void).with('123', card, anything).and_return(success_response) + expect(gateway).to receive(:void).with('123', card, anything).and_return(success_response) payment.void_transaction! end end @@ -281,7 +281,7 @@ describe Spree::Payment do context "when profiles are not supported" do it "should call payment_gateway.void with the payment's response_code" do gateway.stub payment_profiles_supported?: false - gateway.should_receive(:void).with('123', anything).and_return(success_response) + expect(gateway).to receive(:void).with('123', anything).and_return(success_response) payment.void_transaction! end end @@ -311,7 +311,7 @@ describe Spree::Payment do context "if unsuccessful" do it "should not void the payment" do gateway.stub void: failed_response - payment.should_not_receive(:void) + expect(payment).to_not receive(:void) expect { payment.void_transaction! }.to raise_error(Spree::Core::GatewayError) end end @@ -320,7 +320,7 @@ describe Spree::Payment do context "if payment is already voided" do it "should not void the payment" do payment = build_stubbed(:payment, payment_method: gateway, state: 'void') - payment.payment_method.should_not_receive(:void) + expect(payment.payment_method).to_not receive(:void) payment.void_transaction! end end @@ -339,7 +339,7 @@ describe Spree::Payment do end it "should call credit on the gateway with the credit amount and response_code" do - gateway.should_receive(:credit).with(1000, card, '123', anything).and_return(success_response) + expect(gateway).to receive(:credit).with(1000, card, '123', anything).and_return(success_response) payment.credit! end end @@ -350,7 +350,9 @@ describe Spree::Payment do end it "should call credit on the gateway with the credit amount and response_code" do - gateway.should_receive(:credit).with(amount_in_cents, card, '123', anything).and_return(success_response) + expect(gateway).to receive(:credit).with( + amount_in_cents, card, '123', anything + ).and_return(success_response) payment.credit! end end @@ -361,7 +363,9 @@ describe Spree::Payment do end it "should call credit on the gateway with the original payment amount and response_code" do - gateway.should_receive(:credit).with(amount_in_cents.to_f, card, '123', anything).and_return(success_response) + expect(gateway).to receive(:credit).with( + amount_in_cents.to_f, card, '123', anything + ).and_return(success_response) payment.credit! end end @@ -431,8 +435,8 @@ describe Spree::Payment do payment = build_stubbed(:payment) payment.state = 'processing' - payment.should_not_receive(:authorize!) - payment.should_not_receive(:purchase!) + expect(payment).to_not receive(:authorize!) + expect(payment).to_not receive(:purchase!) expect(payment.process!).to be_nil end end @@ -496,7 +500,7 @@ describe Spree::Payment do it "calls credit on the source with the payment and amount" do payment.state = 'completed' payment.stub(:credit_allowed).and_return(10) - payment.should_receive(:credit!).with(10) + expect(payment).to receive(:credit!).with(10) payment.partial_credit(10) end end @@ -519,7 +523,7 @@ describe Spree::Payment do order = create(:order) payment = Spree::Payment.create(amount: 100, order: order, payment_method: gateway) - order.should_receive(:update!) + expect(order).to receive(:update!) payment.save end diff --git a/spec/models/model_set_spec.rb b/spec/services/sets/model_set_spec.rb similarity index 72% rename from spec/models/model_set_spec.rb rename to spec/services/sets/model_set_spec.rb index 2dd5e12c8e..d9154c5bf1 100644 --- a/spec/models/model_set_spec.rb +++ b/spec/services/sets/model_set_spec.rb @@ -2,13 +2,15 @@ require 'spec_helper' -describe ModelSet do +describe Sets::ModelSet do describe "updating" do it "creates new models" do attrs = { collection_attributes: { '1' => { name: 's1' }, '2' => { name: 's2' } } } - ms = ModelSet.new(EnterpriseRelationshipPermission, EnterpriseRelationshipPermission.all, attrs) + ms = Sets::ModelSet.new(EnterpriseRelationshipPermission, + EnterpriseRelationshipPermission.all, + attrs) expect { ms.save }.to change(EnterpriseRelationshipPermission, :count).by(2) @@ -22,7 +24,7 @@ describe ModelSet do attrs = { collection_attributes: { '1' => { id: e1.id, name: 'e1zz', description: 'foo' }, '2' => { id: e2.id, name: 'e2yy', description: 'bar' } } } - ms = ModelSet.new(EnterpriseGroup, EnterpriseGroup.all, attrs) + ms = Sets::ModelSet.new(EnterpriseGroup, EnterpriseGroup.all, attrs) expect { ms.save }.to change(EnterpriseGroup, :count).by(0) @@ -36,8 +38,8 @@ describe ModelSet do attributes = { collection_attributes: { '1' => { id: e1.id, name: 'deleteme' }, '2' => { id: e2.id, name: 'e2' } } } - ms = ModelSet.new(Enterprise, Enterprise.all, attributes, nil, - proc { |attrs| attrs['name'] == 'deleteme' }) + ms = Sets::ModelSet.new(Enterprise, Enterprise.all, attributes, nil, + proc { |attrs| attrs['name'] == 'deleteme' }) expect { ms.save }.to change(Enterprise, :count).by(-1) @@ -48,8 +50,8 @@ describe ModelSet do it "ignores deletable new records" do attributes = { collection_attributes: { '1' => { name: 'deleteme' } } } - ms = ModelSet.new(Enterprise, Enterprise.all, attributes, nil, - proc { |attrs| attrs['name'] == 'deleteme' }) + ms = Sets::ModelSet.new(Enterprise, Enterprise.all, attributes, nil, + proc { |attrs| attrs['name'] == 'deleteme' }) expect { ms.save }.to change(Enterprise, :count).by(0) end diff --git a/spec/models/spree/product_set_spec.rb b/spec/services/sets/product_set_spec.rb similarity index 95% rename from spec/models/spree/product_set_spec.rb rename to spec/services/sets/product_set_spec.rb index c296ed7a73..36aaf9fefb 100644 --- a/spec/models/spree/product_set_spec.rb +++ b/spec/services/sets/product_set_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Spree::ProductSet do +describe Sets::ProductSet do describe '#save' do context 'when passing :collection_attributes' do let(:product_set) do @@ -88,7 +88,11 @@ describe Spree::ProductSet do end let(:distributor) { create(:distributor_enterprise) } - let!(:order_cycle) { create(:simple_order_cycle, variants: [product.variants.first], coordinator: distributor, distributors: [distributor]) } + let!(:order_cycle) { + create(:simple_order_cycle, variants: [product.variants.first], + coordinator: distributor, + distributors: [distributor]) + } it 'updates the product and removes the product from order cycles' do product_set.save diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 41f3d47bd5..62c67e9417 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -182,7 +182,7 @@ RSpec.configure do |config| config.include Spree::MoneyHelper config.include PreferencesHelper config.include ControllerRequestsHelper, type: :controller - config.include Devise::TestHelpers, type: :controller + config.include Devise::Test::ControllerHelpers, type: :controller config.include OpenFoodNetwork::ApiHelper, type: :controller config.include OpenFoodNetwork::ControllerHelper, type: :controller config.include Features::DatepickerHelper, type: :feature @@ -206,16 +206,6 @@ RSpec.configure do |config| config.include JsonSpec::Helpers - # Suppress Selenium deprecation warnings. Stops a flood of pointless warnings filling the - # test output. We can remove this in the future after upgrading Rails, Rack, and Capybara. - if Rails::VERSION::MAJOR == 4 && Rails::VERSION::MINOR == 0 - Selenium::WebDriver.logger.level = :error - else - ActiveSupport::Deprecation.warn( - "Suppressing Selenium deprecation warnings is not needed any more." - ) - end - # Profiling # # This code shouldn't be run in normal circumstances. But if you want to know