From 5b9db50250e6b254ad65e15a71495f61be50154a Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 8 Dec 2020 11:21:05 +0000 Subject: [PATCH 01/84] Optimise Shipment#to_package This is done in a later Spree commit to reduce the amount of processing done in larger orders. See: https://github.com/spree/spree/commit/ab01b1ec1e2b08068b3bfcca99c7886c0747514f --- app/models/spree/shipment.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/spree/shipment.rb b/app/models/spree/shipment.rb index 6763de4712..84c717a819 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 From 776a61d1d9a781d64bac013fad02d5b9e9334a39 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bellet Date: Mon, 11 Jan 2021 11:17:33 +0100 Subject: [PATCH 02/84] create loading spinner reusable component --- app/views/components/_loading.html.haml | 1 + 1 file changed, 1 insertion(+) create mode 100644 app/views/components/_loading.html.haml diff --git a/app/views/components/_loading.html.haml b/app/views/components/_loading.html.haml new file mode 100644 index 0000000000..606dbd051a --- /dev/null +++ b/app/views/components/_loading.html.haml @@ -0,0 +1 @@ +%img.spinner{ src: image_path("spinning-circles.svg") } From b02b36b8e83dcbab8fe86bede4f38c447a3d471f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bellet Date: Mon, 11 Jan 2021 11:18:34 +0100 Subject: [PATCH 03/84] replace code by reusable component --- app/views/admin/customers/index.html.haml | 2 +- app/views/admin/enterprises/_enterprise_user_index.html.haml | 2 +- app/views/admin/order_cycles/_loading_flash.html.haml | 2 +- app/views/admin/subscriptions/_loading_flash.html.haml | 2 +- app/views/admin/variant_overrides/_loading_flash.html.haml | 2 +- app/views/registration/steps/_logo.html.haml | 2 +- app/views/registration/steps/_promo.html.haml | 2 +- app/views/shop/products/_form.html.haml | 2 +- app/views/spree/admin/orders/bulk_management.html.haml | 2 +- app/views/spree/admin/orders/index.html.haml | 2 +- app/views/spree/admin/products/index/_indicators.html.haml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/views/admin/customers/index.html.haml b/app/views/admin/customers/index.html.haml index 2ee8bc16a3..456f439f6c 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/loading" %h1 =t :loading_customers diff --git a/app/views/admin/enterprises/_enterprise_user_index.html.haml b/app/views/admin/enterprises/_enterprise_user_index.html.haml index eedfba8d34..a17baa3ba7 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/loading" %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/order_cycles/_loading_flash.html.haml b/app/views/admin/order_cycles/_loading_flash.html.haml index 05aff6b748..d7f1a5071a 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/loading" %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..7d78d296dc 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/loading" %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..0e60423659 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/loading" %h1= t('.loading_inventory') diff --git a/app/views/registration/steps/_logo.html.haml b/app/views/registration/steps/_logo.html.haml index 4082defaec..4a01d5a0a4 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/loading" %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..3934c12d48 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/loading" %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..8b0ab92482 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/loading" .hide-for-medium-down.large-1.columns -# Space between products and filters diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 9949b4495c..06335b73ff 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/loading" %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..a8c66a999d 100644 --- a/app/views/spree/admin/orders/index.html.haml +++ b/app/views/spree/admin/orders/index.html.haml @@ -93,7 +93,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/loading" .row .small-12.columns.fullwidth.text-center %span= t('.loading') diff --git a/app/views/spree/admin/products/index/_indicators.html.haml b/app/views/spree/admin/products/index/_indicators.html.haml index 9c002b1f1f..ad7e67bbe5 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/loading" %h1= t('.title') %div.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && q.query.length == 0' } From 4d953abbe921ed2a7949de7c787497ea613af802 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bellet Date: Mon, 11 Jan 2021 11:18:54 +0100 Subject: [PATCH 04/84] replace code by reusable component in a js file --- .../admin/panels/exchange_products_panel_footer.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/templates/admin/panels/exchange_products_panel_footer.html.haml b/app/assets/javascripts/templates/admin/panels/exchange_products_panel_footer.html.haml index 7db48a869a..5a4ed58fdc 100644 --- a/app/assets/javascripts/templates/admin/panels/exchange_products_panel_footer.html.haml +++ b/app/assets/javascripts/templates/admin/panels/exchange_products_panel_footer.html.haml @@ -6,6 +6,6 @@ .sixteen.columns.alpha#loading{ 'ng-show' => 'productsLoading()' } %br - %img.spinner{ src: "/assets/spinning-circles.svg" } + {{ = render partial: "components/loading" }} %h1 {{ 'js.admin.panels.exchange_products.loading_variants' | t }} From 6f24ecd9732007f9c489fac5fd59cb15f1601e24 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bellet Date: Mon, 11 Jan 2021 11:20:00 +0100 Subject: [PATCH 05/84] replace code by reusable component There is no need to add .text-center as it's already on the parent component --- app/views/producers/_fat.html.haml | 2 +- app/views/shops/_fat.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/producers/_fat.html.haml b/app/views/producers/_fat.html.haml index 227e5dca39..c514271a1a 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/loading" .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/shops/_fat.html.haml b/app/views/shops/_fat.html.haml index 289b1d3dc0..8b56a4a0cd 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/loading" .columns.small-12.medium-6.large-5.fat{"ng-show" => "open() && !shopfront_loading"} %div{"ng-if" => "::hub.taxons"} From 1b4254a5cff6452b14803ff03c69e95ac5001344 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bellet Date: Mon, 11 Jan 2021 16:12:17 +0100 Subject: [PATCH 06/84] Revert "replace code by reusable component" This reverts commit 4d953abbe921ed2a7949de7c787497ea613af802. --- .../admin/panels/exchange_products_panel_footer.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/templates/admin/panels/exchange_products_panel_footer.html.haml b/app/assets/javascripts/templates/admin/panels/exchange_products_panel_footer.html.haml index 5a4ed58fdc..7db48a869a 100644 --- a/app/assets/javascripts/templates/admin/panels/exchange_products_panel_footer.html.haml +++ b/app/assets/javascripts/templates/admin/panels/exchange_products_panel_footer.html.haml @@ -6,6 +6,6 @@ .sixteen.columns.alpha#loading{ 'ng-show' => 'productsLoading()' } %br - {{ = render partial: "components/loading" }} + %img.spinner{ src: "/assets/spinning-circles.svg" } %h1 {{ 'js.admin.panels.exchange_products.loading_variants' | t }} From 544a7407e3c834e9deff4d21c1498113aa3e6b5e Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bellet Date: Mon, 11 Jan 2021 16:22:47 +0100 Subject: [PATCH 07/84] specify max-width to not overflow parent element --- app/views/components/_loading.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/components/_loading.html.haml b/app/views/components/_loading.html.haml index 606dbd051a..8d0a371204 100644 --- a/app/views/components/_loading.html.haml +++ b/app/views/components/_loading.html.haml @@ -1 +1 @@ -%img.spinner{ src: image_path("spinning-circles.svg") } +%img.spinner{ src: image_path("spinning-circles.svg"), style: "max-width: 100%" } From e34050f7cb9f3e7aa67c4e4f1ff15c8af9a5973a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bellet Date: Mon, 11 Jan 2021 16:23:22 +0100 Subject: [PATCH 08/84] add wrapper to show/hide loading spinner --- .../javascripts/templates/admin/modals/image_upload.html.haml | 3 ++- app/views/admin/enterprises/form/_primary_details.html.haml | 3 ++- app/views/shops/_hubs.html.haml | 3 ++- app/views/spree/admin/orders/index.html.haml | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/templates/admin/modals/image_upload.html.haml b/app/assets/javascripts/templates/admin/modals/image_upload.html.haml index f864bbee5f..aebdfe94ff 100644 --- a/app/assets/javascripts/templates/admin/modals/image_upload.html.haml +++ b/app/assets/javascripts/templates/admin/modals/image_upload.html.haml @@ -3,7 +3,8 @@ %form#image_upload{ name: 'form', novalidate: true, enctype: 'multipart/form-data', multipart: true, ng: { controller: "ProductImageCtrl" } } %div.image-preview - %img.spinner{ src: "/assets/spinning-circles.svg", ng: { hide: "!imageUploader.isUploading" }} + %div{ng: {hide: "!imageUploader.isUploading", cloak: true}} + %ng-include{src: "'components/loading'"} %img.preview{ng: {src: "{{imagePreview}}", class: "{'faded': imageUploader.isUploading}"}} %label{for: 'image-upload', class: 'button'} {{ 'admin.products.index.upload_an_image' | t }} diff --git a/app/views/admin/enterprises/form/_primary_details.html.haml b/app/views/admin/enterprises/form/_primary_details.html.haml index a6a09106b3..f948de45b3 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/loading" %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/shops/_hubs.html.haml b/app/views/shops/_hubs.html.haml index 1d19839a03..b9faf935dd 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/loading" %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/index.html.haml b/app/views/spree/admin/orders/index.html.haml index a8c66a999d..d6629f0a3a 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/loading" %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')} From 71dc5a8ff014f189f70ba2cc36aea51ff2425774 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bellet Date: Mon, 11 Jan 2021 16:29:13 +0100 Subject: [PATCH 09/84] remove trailing space --- app/views/spree/admin/products/index/_indicators.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/spree/admin/products/index/_indicators.html.haml b/app/views/spree/admin/products/index/_indicators.html.haml index ad7e67bbe5..233541910a 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 - = render partial: "components/loading" + = render partial: "components/loading" %h1= t('.title') %div.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && q.query.length == 0' } From 26edd83a54c6c450de90b3ba4e62def675757322 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bellet Date: Mon, 11 Jan 2021 16:33:44 +0100 Subject: [PATCH 10/84] do not include inside a javascript asset I made a mistake by using it inside a javascript asset revert part of commit e34050f7c --- .../javascripts/templates/admin/modals/image_upload.html.haml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/javascripts/templates/admin/modals/image_upload.html.haml b/app/assets/javascripts/templates/admin/modals/image_upload.html.haml index aebdfe94ff..f864bbee5f 100644 --- a/app/assets/javascripts/templates/admin/modals/image_upload.html.haml +++ b/app/assets/javascripts/templates/admin/modals/image_upload.html.haml @@ -3,8 +3,7 @@ %form#image_upload{ name: 'form', novalidate: true, enctype: 'multipart/form-data', multipart: true, ng: { controller: "ProductImageCtrl" } } %div.image-preview - %div{ng: {hide: "!imageUploader.isUploading", cloak: true}} - %ng-include{src: "'components/loading'"} + %img.spinner{ src: "/assets/spinning-circles.svg", ng: { hide: "!imageUploader.isUploading" }} %img.preview{ng: {src: "{{imagePreview}}", class: "{'faded': imageUploader.isUploading}"}} %label{for: 'image-upload', class: 'button'} {{ 'admin.products.index.upload_an_image' | t }} From 8399b82bfd1d1d9d13d77a5b405d5f9933050289 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bellet Date: Mon, 11 Jan 2021 16:49:56 +0100 Subject: [PATCH 11/84] rename component to spinner A name for a "thing" rather a state --- app/views/admin/customers/index.html.haml | 2 +- app/views/admin/enterprises/_enterprise_user_index.html.haml | 2 +- app/views/admin/enterprises/form/_primary_details.html.haml | 2 +- app/views/admin/order_cycles/_loading_flash.html.haml | 2 +- app/views/admin/subscriptions/_loading_flash.html.haml | 2 +- app/views/admin/variant_overrides/_loading_flash.html.haml | 2 +- .../components/{_loading.html.haml => _spinner.html.haml} | 0 app/views/producers/_fat.html.haml | 2 +- app/views/registration/steps/_logo.html.haml | 2 +- app/views/registration/steps/_promo.html.haml | 2 +- app/views/shop/products/_form.html.haml | 2 +- app/views/shops/_fat.html.haml | 2 +- app/views/shops/_hubs.html.haml | 2 +- app/views/spree/admin/orders/bulk_management.html.haml | 2 +- app/views/spree/admin/orders/index.html.haml | 4 ++-- app/views/spree/admin/products/index/_indicators.html.haml | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) rename app/views/components/{_loading.html.haml => _spinner.html.haml} (100%) diff --git a/app/views/admin/customers/index.html.haml b/app/views/admin/customers/index.html.haml index 456f439f6c..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 - = render partial: "components/loading" + = render partial: "components/spinner" %h1 =t :loading_customers diff --git a/app/views/admin/enterprises/_enterprise_user_index.html.haml b/app/views/admin/enterprises/_enterprise_user_index.html.haml index a17baa3ba7..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 - = render partial: "components/loading" + = 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 f948de45b3..5fccc81904 100644 --- a/app/views/admin/enterprises/form/_primary_details.html.haml +++ b/app/views/admin/enterprises/form/_primary_details.html.haml @@ -60,7 +60,7 @@ = 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 %div{ng: {show: "checking", cloak: true}, style: "width: 30px; height: 30px;"} - = render partial: "components/loading" + = 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 d7f1a5071a..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' } } - = render partial: "components/loading" + = 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 7d78d296dc..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' } } - = render partial: "components/loading" + = 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 0e60423659..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' } } - = render partial: "components/loading" + = render partial: "components/spinner" %h1= t('.loading_inventory') diff --git a/app/views/components/_loading.html.haml b/app/views/components/_spinner.html.haml similarity index 100% rename from app/views/components/_loading.html.haml rename to app/views/components/_spinner.html.haml diff --git a/app/views/producers/_fat.html.haml b/app/views/producers/_fat.html.haml index c514271a1a..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 - = render partial: "components/loading" + = 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 4a01d5a0a4..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" } } - = render partial: "components/loading" + = 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 3934c12d48..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" } } - = render partial: "components/loading" + = 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 8b0ab92482..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 - = render partial: "components/loading" + = 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 8b56a4a0cd..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 - = render partial: "components/loading" + = 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 b9faf935dd..243f2e7c63 100644 --- a/app/views/shops/_hubs.html.haml +++ b/app/views/shops/_hubs.html.haml @@ -27,7 +27,7 @@ = t :hubs_distance_filter, location: "{{ nameMatchesFiltered[0].name }}" .more-controls %span{ng: {show: "closed_shops_loading", cloak: true}} - = render partial: "components/loading" + = 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/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 06335b73ff..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' } - = render partial: "components/loading" + = 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 d6629f0a3a..b72a0506a6 100644 --- a/app/views/spree/admin/orders/index.html.haml +++ b/app/views/spree/admin/orders/index.html.haml @@ -82,7 +82,7 @@ %td.actions %div.row-loading-icons %span{ng: {show: 'rowStatus[order.id] == "loading"', cloak: true}} - = render partial: "components/loading" + = 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')} @@ -94,7 +94,7 @@ .orders-loading{'ng-show' => 'RequestMonitor.loading'} .row .small-12.columns.fullwidth.text-center - = render partial: "components/loading" + = render partial: "components/spinner" .row .small-12.columns.fullwidth.text-center %span= t('.loading') diff --git a/app/views/spree/admin/products/index/_indicators.html.haml b/app/views/spree/admin/products/index/_indicators.html.haml index 233541910a..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 - = render partial: "components/loading" + = render partial: "components/spinner" %h1= t('.title') %div.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && q.query.length == 0' } From ca4de40fa2c777f345f6909254a0fc93409a379e Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 19 Dec 2020 12:41:01 +0000 Subject: [PATCH 12/84] Associate all adjustments with an order This change is introduced in the adjustments updates from Spree 2.2 and used heavily in new scopes and methods (not included here). --- app/models/spree/adjustment.rb | 2 ++ app/models/spree/tax_rate.rb | 1 + .../20201219120055_add_order_to_adjustments.rb | 18 ++++++++++++++++++ db/schema.rb | 3 ++- lib/spree/core/calculated_adjustments.rb | 12 ++++++++++++ .../spree/core/calculated_adjustments_spec.rb | 5 +++++ 6 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20201219120055_add_order_to_adjustments.rb diff --git a/app/models/spree/adjustment.rb b/app/models/spree/adjustment.rb index cb965b8aef..93f4bbafe2 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' diff --git a/app/models/spree/tax_rate.rb b/app/models/spree/tax_rate.rb index 84a0a7b0dd..0721e127fd 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/db/migrate/20201219120055_add_order_to_adjustments.rb b/db/migrate/20201219120055_add_order_to_adjustments.rb new file mode 100644 index 0000000000..718701d16c --- /dev/null +++ b/db/migrate/20201219120055_add_order_to_adjustments.rb @@ -0,0 +1,18 @@ +class AddOrderToAdjustments < ActiveRecord::Migration + 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| + adjustment.update_column(:order_id, adjustment.adjustable.order_id) + end + end +end diff --git a/db/schema.rb b/db/schema.rb index e71191e567..3d4de3b876 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: 20201219120055) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -393,6 +393,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/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/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 From e83a3ff76d1ae12311130c0b89e18010c8aa5b71 Mon Sep 17 00:00:00 2001 From: Pau Perez Date: Tue, 12 Jan 2021 10:56:11 +0100 Subject: [PATCH 13/84] Revert "Enable request queuing tracking in Datadog" This reverts commit 91e527614002377e4dfcf360522a6244fa530a37. --- config/initializers/datadog.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/initializers/datadog.rb b/config/initializers/datadog.rb index 9ad16355fc..c1265c7802 100644 --- a/config/initializers/datadog.rb +++ b/config/initializers/datadog.rb @@ -3,9 +3,7 @@ if ENV['DATADOG_RAILS_APM'] c.use :rails, service_name: 'rails' c.use :delayed_job, service_name: 'delayed_job' c.use :dalli, service_name: 'memcached' - c.analytics_enabled = true c.runtime_metrics_enabled = true - c.request_queuing = true end end From 561cf23dc1d83a5be4ba05c42d346e49bd7a75a3 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 13 Jan 2021 16:02:21 +0000 Subject: [PATCH 14/84] Remove #update_attributes_without_callbacks This is a hacky Spree method that's removed in Spree 2.2 See: https://github.com/spree/spree/commit/7deba6a152fd63258941a5ca89f06205a5b62cfd --- app/models/spree/order.rb | 7 ------- .../app/services/order_management/order/updater.rb | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/app/models/spree/order.rb b/app/models/spree/order.rb index 4649b5a0e6..fd55aa4777 100644 --- a/app/models/spree/order.rb +++ b/app/models/spree/order.rb @@ -766,13 +766,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/engines/order_management/app/services/order_management/order/updater.rb b/engines/order_management/app/services/order_management/order/updater.rb index e7cb051a4f..6ff7ef5ef6 100644 --- a/engines/order_management/app/services/order_management/order/updater.rb +++ b/engines/order_management/app/services/order_management/order/updater.rb @@ -32,7 +32,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, From 7d0ec48bcfa3b8a2983e0ee7b7fa7b2e71123ddc Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 13 Jan 2021 16:16:49 +0000 Subject: [PATCH 15/84] Improve performance on summing adjustments and payment :amount is a database field in these cases, as opposed to a method that returns a computed result. Calling `.sum(:amount)` is much more efficient here as it computes the sum at database level, as opposed to `.map(&:amount).sum`, which would fetch and instanciate all the objects first, and then sum the amounts after. --- app/helpers/checkout_helper.rb | 6 +++--- app/models/spree/order.rb | 4 ++-- .../app/services/order_management/order/updater.rb | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/helpers/checkout_helper.rb b/app/helpers/checkout_helper.rb index 4ae026bef7..8e54913dc5 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/models/spree/order.rb b/app/models/spree/order.rb index 4649b5a0e6..553ce01340 100644 --- a/app/models/spree/order.rb +++ b/app/models/spree/order.rb @@ -397,11 +397,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 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..e303689617 100644 --- a/engines/order_management/app/services/order_management/order/updater.rb +++ b/engines/order_management/app/services/order_management/order/updater.rb @@ -55,9 +55,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 From 305ae103ce629543cc8f799fef830852aabd15e9 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 13 Jan 2021 17:09:02 +0000 Subject: [PATCH 16/84] Use :create instead of :build in order updater specs The order updater requires a persisted order --- .../spec/services/order_management/order/updater_spec.rb | 2 +- spec/models/spree/order/updating_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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..1af1aa2326 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,7 +5,7 @@ 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 } } diff --git a/spec/models/spree/order/updating_spec.rb b/spec/models/spree/order/updating_spec.rb index 068fde6be9..d31421e53c 100644 --- a/spec/models/spree/order/updating_spec.rb +++ b/spec/models/spree/order/updating_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Spree::Order do - let(:order) { build(:order) } + let(:order) { create(:order) } context "#update!" do let(:line_items) { [build(:line_item, amount: 5)] } From aacd942697c40ca51145a1dbf16892bbe595b570 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 13 Jan 2021 17:32:20 +0000 Subject: [PATCH 17/84] Update specs that stub associations inaccurately These specs fail if the code is using #sum on stubbed objects that don't respond to it nicely. --- .../services/order_management/order/updater_spec.rb | 6 ++---- spec/models/spree/order/adjustments_spec.rb | 10 ++++++---- 2 files changed, 8 insertions(+), 8 deletions(-) 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..e211ac9bea 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 @@ -11,14 +11,12 @@ module OrderManagement 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/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 From 8c4d12a501d6598979822fba0d0c725e84b27be7 Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Wed, 13 Jan 2021 16:23:41 -0800 Subject: [PATCH 18/84] limit item counter to max quantity available even if amount is manually filled in --- .../admin/spree/orders/variant_autocomplete.js.erb | 10 +++++++++- .../spree/admin/orders/_shipment_manifest.html.haml | 2 +- config/locales/en.yml | 2 ++ spec/features/admin/order_spec.rb | 10 ++++++---- 4 files changed, 18 insertions(+), 6 deletions(-) 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/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/config/locales/en.yml b/config/locales/en.yml index 613f2adb45..c2b9b56697 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2717,6 +2717,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..." 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 From be229c90027ce61646e90f1612cd39cdd7190ba5 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 16 Dec 2020 15:08:07 +1100 Subject: [PATCH 19/84] Simplify email config by removing unused option Spree used to give you more options to configure ActionMailer but our setup is much simpler. We can remove unused code. The removed option was never used by OFN and defaulted to true. We don't need a database migration because the value isn't set in the database. --- app/models/spree/app_configuration.rb | 1 - .../shared/_configuration_menu.html.haml | 3 +-- config/initializers/spree.rb | 1 - lib/spree/core/mail_settings.rb | 8 +------ spec/lib/spree/core/mail_settings_spec.rb | 23 +------------------ 5 files changed, 3 insertions(+), 33 deletions(-) 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/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/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/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/spec/lib/spree/core/mail_settings_spec.rb b/spec/lib/spree/core/mail_settings_spec.rb index a8db4d5eed..7d0eb0c102 100644 --- a/spec/lib/spree/core/mail_settings_spec.rb +++ b/spec/lib/spree/core/mail_settings_spec.rb @@ -7,17 +7,7 @@ module Spree describe MailSettings do let!(:subject) { MailSettings.new } - context "override option is true" do - before { Config.override_actionmailer_config = 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 "overriding ActionMailer config" do context "enable delivery" do before { Config.enable_mail_delivery = true } @@ -74,17 +64,6 @@ module Spree it { expect(ActionMailer::Base.perform_deliveries).to be_falsy } 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 - end - end end end end From f1618ec35fd7e1d2693fe5879b62d28fe161ffa8 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 16 Dec 2020 15:45:43 +1100 Subject: [PATCH 20/84] Remove old spec context The specs all stay the same, just changing the indent. --- spec/lib/spree/core/mail_settings_spec.rb | 78 +++++++++++------------ 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/spec/lib/spree/core/mail_settings_spec.rb b/spec/lib/spree/core/mail_settings_spec.rb index 7d0eb0c102..ed799f980f 100644 --- a/spec/lib/spree/core/mail_settings_spec.rb +++ b/spec/lib/spree/core/mail_settings_spec.rb @@ -7,63 +7,61 @@ module Spree describe MailSettings do let!(:subject) { MailSettings.new } - context "overriding ActionMailer config" do - context "enable delivery" do - before { Config.enable_mail_delivery = true } + 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 "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 end From c922a8fd4c01855f541923b315c4cd6071b56cea Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 14 Jan 2021 11:49:01 +1100 Subject: [PATCH 21/84] Fix: Remove unused conditional I forgot to remove this unused conditional in a previous commit. Spree defaulted to overriding the email config and we never changed that. --- lib/spree/core/mail_interceptor.rb | 2 -- 1 file changed, 2 deletions(-) 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] From 8e65d29b023e165c816b96a2e8a06ff936e6a87d Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sat, 31 Oct 2020 21:12:24 +0000 Subject: [PATCH 22/84] Move sets to specific services namespace --- app/{models => services/sets}/column_preference_set.rb | 0 app/{models => services/sets}/enterprise_fee_set.rb | 0 app/{models => services/sets}/enterprise_set.rb | 0 app/{models => services/sets}/model_set.rb | 0 app/{models => services/sets}/order_cycle_set.rb | 0 app/{models/spree => services/sets}/product_set.rb | 0 app/{models => services/sets}/variant_override_set.rb | 0 spec/{models => services/sets}/model_set_spec.rb | 0 spec/{models/spree => services/sets}/product_set_spec.rb | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename app/{models => services/sets}/column_preference_set.rb (100%) rename app/{models => services/sets}/enterprise_fee_set.rb (100%) rename app/{models => services/sets}/enterprise_set.rb (100%) rename app/{models => services/sets}/model_set.rb (100%) rename app/{models => services/sets}/order_cycle_set.rb (100%) rename app/{models/spree => services/sets}/product_set.rb (100%) rename app/{models => services/sets}/variant_override_set.rb (100%) rename spec/{models => services/sets}/model_set_spec.rb (100%) rename spec/{models/spree => services/sets}/product_set_spec.rb (100%) diff --git a/app/models/column_preference_set.rb b/app/services/sets/column_preference_set.rb similarity index 100% rename from app/models/column_preference_set.rb rename to app/services/sets/column_preference_set.rb diff --git a/app/models/enterprise_fee_set.rb b/app/services/sets/enterprise_fee_set.rb similarity index 100% rename from app/models/enterprise_fee_set.rb rename to app/services/sets/enterprise_fee_set.rb diff --git a/app/models/enterprise_set.rb b/app/services/sets/enterprise_set.rb similarity index 100% rename from app/models/enterprise_set.rb rename to app/services/sets/enterprise_set.rb diff --git a/app/models/model_set.rb b/app/services/sets/model_set.rb similarity index 100% rename from app/models/model_set.rb rename to app/services/sets/model_set.rb diff --git a/app/models/order_cycle_set.rb b/app/services/sets/order_cycle_set.rb similarity index 100% rename from app/models/order_cycle_set.rb rename to app/services/sets/order_cycle_set.rb diff --git a/app/models/spree/product_set.rb b/app/services/sets/product_set.rb similarity index 100% rename from app/models/spree/product_set.rb rename to app/services/sets/product_set.rb diff --git a/app/models/variant_override_set.rb b/app/services/sets/variant_override_set.rb similarity index 100% rename from app/models/variant_override_set.rb rename to app/services/sets/variant_override_set.rb diff --git a/spec/models/model_set_spec.rb b/spec/services/sets/model_set_spec.rb similarity index 100% rename from spec/models/model_set_spec.rb rename to spec/services/sets/model_set_spec.rb diff --git a/spec/models/spree/product_set_spec.rb b/spec/services/sets/product_set_spec.rb similarity index 100% rename from spec/models/spree/product_set_spec.rb rename to spec/services/sets/product_set_spec.rb From 187b4a1fc2852634a741d4f4a379240ac3ba2965 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sat, 31 Oct 2020 21:16:14 +0000 Subject: [PATCH 23/84] Add Sets namespace to each set file --- app/services/sets/column_preference_set.rb | 8 +- app/services/sets/enterprise_fee_set.rb | 12 +- app/services/sets/enterprise_set.rb | 8 +- app/services/sets/model_set.rb | 102 ++++----- app/services/sets/order_cycle_set.rb | 8 +- app/services/sets/product_set.rb | 248 +++++++++++---------- app/services/sets/variant_override_set.rb | 50 +++-- 7 files changed, 225 insertions(+), 211 deletions(-) diff --git a/app/services/sets/column_preference_set.rb b/app/services/sets/column_preference_set.rb index 24508d7f9d..006ca9afe8 100644 --- a/app/services/sets/column_preference_set.rb +++ b/app/services/sets/column_preference_set.rb @@ -1,5 +1,7 @@ -class ColumnPreferenceSet < ModelSet - def initialize(collection, attributes = {}) - super(ColumnPreference, collection, attributes, nil, nil ) +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 index ae7ff692ad..9fdb64432c 100644 --- a/app/services/sets/enterprise_fee_set.rb +++ b/app/services/sets/enterprise_fee_set.rb @@ -1,7 +1,9 @@ -class EnterpriseFeeSet < ModelSet - def initialize(attributes = {}) - super(EnterpriseFee, EnterpriseFee.all, - attributes, - proc { |attrs| attrs[:name].blank? }) +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 index 20da64b083..b482da0875 100644 --- a/app/services/sets/enterprise_set.rb +++ b/app/services/sets/enterprise_set.rb @@ -1,5 +1,7 @@ -class EnterpriseSet < ModelSet - def initialize(collection, attributes = {}) - super(Enterprise, collection, attributes) +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 index 8a599b4214..d1ac510d52 100644 --- a/app/services/sets/model_set.rb +++ b/app/services/sets/model_set.rb @@ -1,65 +1,67 @@ # Tableless model to handle updating multiple models at once from a single form -class ModelSet - include ActiveModel::Conversion - extend ActiveModel::Naming +module Sets + class ModelSet + include ActiveModel::Conversion + extend ActiveModel::Naming - attr_accessor :collection + 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 + 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] + # 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)) + attributes.each do |name, value| + public_send("#{name}=", value) end end - end - def errors - errors = ActiveModel::Errors.new self - full_messages = @collection - .map { |model| model.errors.full_messages } - .flatten + 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 - full_messages.each { |message| errors.add(:base, message) } - errors - 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 save - collection_to_delete.each(&:destroy) - collection_to_keep.all?(&:save) - end + def errors + errors = ActiveModel::Errors.new self + full_messages = @collection + .map { |model| model.errors.full_messages } + .flatten - 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 + full_messages.each { |message| errors.add(:base, message) } + errors + end - def collection_to_keep - collection.reject { |e| @delete_if.andand.call(e.attributes) } - end + def save + collection_to_delete.each(&:destroy) + collection_to_keep.all?(&:save) + end - def persisted? - false + 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 index 9f4fb4433e..6459cbf0b3 100644 --- a/app/services/sets/order_cycle_set.rb +++ b/app/services/sets/order_cycle_set.rb @@ -1,5 +1,7 @@ -class OrderCycleSet < ModelSet - def initialize(collection, attributes = {}) - super(OrderCycle, collection, attributes) +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 index 4731c44bf7..dc53224d9b 100644 --- a/app/services/sets/product_set.rb +++ b/app/services/sets/product_set.rb @@ -1,138 +1,140 @@ -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) +module Sets + class Spree::ProductSet < ModelSet + def initialize(attributes = {}) + super(Spree::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) + def save + @collection_hash.each_value.all? do |product_attributes| + update_product_attributes(product_attributes) + end 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) + def collection_attributes=(attributes) + @collection = Spree::Product + .where(id: attributes.each_value.map { |product| product[:id] }) + @collection_hash = 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) + 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 - 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 + def split_taxon_ids!(attributes) + attributes[:taxon_ids] = attributes[:taxon_ids].split(',') if attributes[:taxon_ids].present? 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? + 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 - end - def find_model(collection, model_id) - collection.find do |model| - model.id.to_s == model_id.to_s && model.persisted? + 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 index d380df4144..b16d9b9e4c 100644 --- a/app/services/sets/variant_override_set.rb +++ b/app/services/sets/variant_override_set.rb @@ -1,30 +1,32 @@ -class VariantOverrideSet < ModelSet - def initialize(collection, attributes = {}) - super(VariantOverride, collection, attributes, nil, proc { |attrs, tag_list| deletable?(attrs, tag_list) } ) - end +module Sets + class VariantOverrideSet < ModelSet + def initialize(collection, attributes = {}) + super(VariantOverride, collection, attributes, nil, proc { |attrs, tag_list| deletable?(attrs, tag_list) } ) + end - private + 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 + 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 + # 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) } + def collection_to_keep + collection.reject { |e| @delete_if.andand.call(e.attributes, e.tag_list) } + end end end From 96a351ad0e293d1ecfe1cdef42a79bebf455be91 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sat, 31 Oct 2020 21:20:35 +0000 Subject: [PATCH 24/84] Adapt usage of Sets to their new location --- app/controllers/admin/column_preferences_controller.rb | 2 +- app/controllers/admin/enterprise_fees_controller.rb | 4 ++-- app/controllers/admin/enterprises_controller.rb | 4 ++-- app/controllers/admin/order_cycles_controller.rb | 2 +- app/controllers/admin/variant_overrides_controller.rb | 2 +- app/controllers/spree/admin/products_controller.rb | 2 +- app/services/sets/product_set.rb | 2 +- .../enterprise_fees/_calculator_settings.html.haml | 2 +- spec/controllers/admin/enterprises_controller_spec.rb | 2 +- spec/services/sets/model_set_spec.rb | 10 +++++----- spec/services/sets/product_set_spec.rb | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/controllers/admin/column_preferences_controller.rb b/app/controllers/admin/column_preferences_controller.rb index d8a5ca3e6a..90ba1bd37e 100644 --- a/app/controllers/admin/column_preferences_controller.rb +++ b/app/controllers/admin/column_preferences_controller.rb @@ -29,7 +29,7 @@ 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..9d1536856e 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 diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index c61ab9f30e..b62bb9ddf5 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 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..c4fd99a766 100644 --- a/app/controllers/admin/variant_overrides_controller.rb +++ b/app/controllers/admin/variant_overrides_controller.rb @@ -67,7 +67,7 @@ module Admin 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 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/services/sets/product_set.rb b/app/services/sets/product_set.rb index dc53224d9b..b6054e7125 100644 --- a/app/services/sets/product_set.rb +++ b/app/services/sets/product_set.rb @@ -1,5 +1,5 @@ module Sets - class Spree::ProductSet < ModelSet + class ProductSet < ModelSet def initialize(attributes = {}) super(Spree::Product, [], attributes) end diff --git a/app/views/admin/enterprise_fees/_calculator_settings.html.haml b/app/views/admin/enterprise_fees/_calculator_settings.html.haml index f505f0c38a..4936ee35b2 100644 --- a/app/views/admin/enterprise_fees/_calculator_settings.html.haml +++ b/app/views/admin/enterprise_fees/_calculator_settings.html.haml @@ -1,5 +1,5 @@ -# 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.fields_for :collection do |f| diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 3b4cd1428c..8343ca0d9e 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -397,7 +397,7 @@ 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' } } } } diff --git a/spec/services/sets/model_set_spec.rb b/spec/services/sets/model_set_spec.rb index 2dd5e12c8e..00b71e5d19 100644 --- a/spec/services/sets/model_set_spec.rb +++ b/spec/services/sets/model_set_spec.rb @@ -2,13 +2,13 @@ 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 +22,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,7 +36,7 @@ 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, + ms = Sets::ModelSet.new(Enterprise, Enterprise.all, attributes, nil, proc { |attrs| attrs['name'] == 'deleteme' }) expect { ms.save }.to change(Enterprise, :count).by(-1) @@ -48,7 +48,7 @@ describe ModelSet do it "ignores deletable new records" do attributes = { collection_attributes: { '1' => { name: 'deleteme' } } } - ms = ModelSet.new(Enterprise, Enterprise.all, attributes, nil, + ms = Sets::ModelSet.new(Enterprise, Enterprise.all, attributes, nil, proc { |attrs| attrs['name'] == 'deleteme' }) expect { ms.save }.to change(Enterprise, :count).by(0) diff --git a/spec/services/sets/product_set_spec.rb b/spec/services/sets/product_set_spec.rb index c296ed7a73..a5576d9caa 100644 --- a/spec/services/sets/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 From 5d6d7f7ad07ca306a256d0f8d57832e2eb379fbf Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sun, 1 Nov 2020 12:01:03 +0000 Subject: [PATCH 25/84] Adapt enterprise fees code and specs to new namespace of Sets::EnterpriseFeeSet --- .../admin/enterprise_fees_controller.rb | 2 +- app/helpers/enterprise_fees_helper.rb | 4 +- .../_calculator_settings.html.haml | 2 +- spec/features/admin/enterprise_fees_spec.rb | 60 +++++++++---------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/app/controllers/admin/enterprise_fees_controller.rb b/app/controllers/admin/enterprise_fees_controller.rb index 9d1536856e..73880badf4 100644 --- a/app/controllers/admin/enterprise_fees_controller.rb +++ b/app/controllers/admin/enterprise_fees_controller.rb @@ -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/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/views/admin/enterprise_fees/_calculator_settings.html.haml b/app/views/admin/enterprise_fees/_calculator_settings.html.haml index 4936ee35b2..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 = 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/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 From 19b12092a0692d8115f439995480e4ce130e927a Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sun, 1 Nov 2020 12:13:32 +0000 Subject: [PATCH 26/84] Fix rubocop issues and adapt exceptions file --- .rubocop_manual_todo.yml | 6 ++---- .rubocop_todo.yml | 8 -------- app/services/sets/column_preference_set.rb | 2 ++ app/services/sets/enterprise_fee_set.rb | 2 ++ app/services/sets/enterprise_set.rb | 2 ++ app/services/sets/model_set.rb | 2 ++ app/services/sets/order_cycle_set.rb | 2 ++ app/services/sets/product_set.rb | 2 ++ app/services/sets/variant_override_set.rb | 10 ++++++++-- spec/services/sets/model_set_spec.rb | 8 +++++--- spec/services/sets/product_set_spec.rb | 6 +++++- 11 files changed, 32 insertions(+), 18 deletions(-) diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index cc08ddc7e0..67d1f8a600 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -71,7 +71,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 +253,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 +383,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 +410,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 +536,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/app/services/sets/column_preference_set.rb b/app/services/sets/column_preference_set.rb index 006ca9afe8..13cba40957 100644 --- a/app/services/sets/column_preference_set.rb +++ b/app/services/sets/column_preference_set.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Sets class ColumnPreferenceSet < ModelSet def initialize(collection, attributes = {}) diff --git a/app/services/sets/enterprise_fee_set.rb b/app/services/sets/enterprise_fee_set.rb index 9fdb64432c..d651649232 100644 --- a/app/services/sets/enterprise_fee_set.rb +++ b/app/services/sets/enterprise_fee_set.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Sets class EnterpriseFeeSet < ModelSet def initialize(attributes = {}) diff --git a/app/services/sets/enterprise_set.rb b/app/services/sets/enterprise_set.rb index b482da0875..bb0b2bdc3c 100644 --- a/app/services/sets/enterprise_set.rb +++ b/app/services/sets/enterprise_set.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Sets class EnterpriseSet < ModelSet def initialize(collection, attributes = {}) diff --git a/app/services/sets/model_set.rb b/app/services/sets/model_set.rb index d1ac510d52..5ecd2cddab 100644 --- a/app/services/sets/model_set.rb +++ b/app/services/sets/model_set.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Tableless model to handle updating multiple models at once from a single form module Sets class ModelSet diff --git a/app/services/sets/order_cycle_set.rb b/app/services/sets/order_cycle_set.rb index 6459cbf0b3..663f684062 100644 --- a/app/services/sets/order_cycle_set.rb +++ b/app/services/sets/order_cycle_set.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Sets class OrderCycleSet < ModelSet def initialize(collection, attributes = {}) diff --git a/app/services/sets/product_set.rb b/app/services/sets/product_set.rb index b6054e7125..4563bce0bb 100644 --- a/app/services/sets/product_set.rb +++ b/app/services/sets/product_set.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Sets class ProductSet < ModelSet def initialize(attributes = {}) diff --git a/app/services/sets/variant_override_set.rb b/app/services/sets/variant_override_set.rb index b16d9b9e4c..3240846e54 100644 --- a/app/services/sets/variant_override_set.rb +++ b/app/services/sets/variant_override_set.rb @@ -1,7 +1,13 @@ +# 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) } ) + super(VariantOverride, + collection, + attributes, + nil, + proc { |attrs, tag_list| deletable?(attrs, tag_list) } ) end private @@ -16,7 +22,7 @@ module Sets tag_list.empty? end - # Override of ModelSet method to allow us to check presence of a tag_list (which is not an attribute) + # 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 diff --git a/spec/services/sets/model_set_spec.rb b/spec/services/sets/model_set_spec.rb index 00b71e5d19..d9154c5bf1 100644 --- a/spec/services/sets/model_set_spec.rb +++ b/spec/services/sets/model_set_spec.rb @@ -8,7 +8,9 @@ describe Sets::ModelSet do attrs = { collection_attributes: { '1' => { name: 's1' }, '2' => { name: 's2' } } } - ms = Sets::ModelSet.new(EnterpriseRelationshipPermission, EnterpriseRelationshipPermission.all, attrs) + ms = Sets::ModelSet.new(EnterpriseRelationshipPermission, + EnterpriseRelationshipPermission.all, + attrs) expect { ms.save }.to change(EnterpriseRelationshipPermission, :count).by(2) @@ -37,7 +39,7 @@ describe Sets::ModelSet do '2' => { id: e2.id, name: 'e2' } } } ms = Sets::ModelSet.new(Enterprise, Enterprise.all, attributes, nil, - proc { |attrs| attrs['name'] == 'deleteme' }) + proc { |attrs| attrs['name'] == 'deleteme' }) expect { ms.save }.to change(Enterprise, :count).by(-1) @@ -49,7 +51,7 @@ describe Sets::ModelSet do attributes = { collection_attributes: { '1' => { name: 'deleteme' } } } ms = Sets::ModelSet.new(Enterprise, Enterprise.all, attributes, nil, - proc { |attrs| attrs['name'] == 'deleteme' }) + proc { |attrs| attrs['name'] == 'deleteme' }) expect { ms.save }.to change(Enterprise, :count).by(0) end diff --git a/spec/services/sets/product_set_spec.rb b/spec/services/sets/product_set_spec.rb index a5576d9caa..36aaf9fefb 100644 --- a/spec/services/sets/product_set_spec.rb +++ b/spec/services/sets/product_set_spec.rb @@ -88,7 +88,11 @@ describe Sets::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 From ed0441dc41957e272231dd6b1d59e610b32d5a4d Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sun, 1 Nov 2020 12:19:29 +0000 Subject: [PATCH 27/84] Fix a few more rubocop issues --- .rubocop_manual_todo.yml | 1 - .../admin/column_preferences_controller.rb | 3 ++- app/controllers/admin/variant_overrides_controller.rb | 11 +++++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index 67d1f8a600..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 diff --git a/app/controllers/admin/column_preferences_controller.rb b/app/controllers/admin/column_preferences_controller.rb index 90ba1bd37e..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 = Sets::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/variant_overrides_controller.rb b/app/controllers/admin/variant_overrides_controller.rb index c4fd99a766..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 = Sets::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 From fd0bba19a7c3f7c1a3d3a884594b4f8d3c9fb377 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sun, 1 Nov 2020 13:47:59 +0000 Subject: [PATCH 28/84] Adapt enterprises code and specs to new namespace Sets::EnterpriseSet --- app/controllers/admin/enterprises_controller.rb | 6 +++--- .../admin/enterprises_controller_spec.rb | 8 ++++---- spec/features/admin/enterprises/index_spec.rb | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index b62bb9ddf5..99ae12761f 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -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/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 8343ca0d9e..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 @@ -400,7 +400,7 @@ describe Admin::EnterprisesController, type: :controller do 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/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') From d06c1fa665c1b9d2788ae187a6b314633a90a575 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Thu, 14 Jan 2021 23:44:09 +0000 Subject: [PATCH 29/84] Update Gemfile_next.lock --- Gemfile_next.lock | 61 ++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/Gemfile_next.lock b/Gemfile_next.lock index 71c94db901..290d607b46 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 @@ -130,7 +122,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 +197,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 +214,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 +283,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 +302,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 +329,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) @@ -566,9 +563,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 +583,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 +602,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 +639,6 @@ DEPENDENCIES selenium-webdriver shoulda-matchers simplecov - spree_paypal_express! spring spring-commands-rspec state_machines-activerecord @@ -662,7 +659,7 @@ DEPENDENCIES wkhtmltopdf-binary RUBY VERSION - ruby 2.3.7p456 + ruby 2.4.4p296 BUNDLED WITH 1.17.3 From 02a1116fffb1a7513e39e56ad5ad5adae40e4d5a Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Thu, 14 Jan 2021 23:45:05 +0000 Subject: [PATCH 30/84] Remove cache intrumentation It doesnt work in rails 5 and apparently it was introduced to debug already deleted products cache in staging See this commit that introduced it https://github.com/openfoodfoundation/openfoodnetwork/commit/10a79d5a65f8d00808a5465d805b2c51900411a5 --- config/initializers/cache_store.rb | 3 --- 1 file changed, 3 deletions(-) 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 From 874da929d35713e20a01c999518a391bbd3ae729 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Fri, 15 Jan 2021 00:21:28 +0000 Subject: [PATCH 31/84] Disable db2fog in rails 5, it doesnt work, we need to find an alternative for rails 5 --- config/initializers/db2fog.rb | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) 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 From bc2d966e5fd7b696ba5f2cf2a1242adfd9d12042 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Fri, 15 Jan 2021 00:22:20 +0000 Subject: [PATCH 32/84] Remove commented code --- config/initializers/js_template_helpers.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/config/initializers/js_template_helpers.rb b/config/initializers/js_template_helpers.rb index 27fffc9655..0a00436004 100644 --- a/config/initializers/js_template_helpers.rb +++ b/config/initializers/js_template_helpers.rb @@ -2,12 +2,5 @@ # 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 end From 209e2f20c5bbc5ec71d8ca297da3e3249cd45331 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Fri, 15 Jan 2021 00:23:37 +0000 Subject: [PATCH 33/84] Add rails 5 version for js template helpers --- config/initializers/js_template_helpers.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/config/initializers/js_template_helpers.rb b/config/initializers/js_template_helpers.rb index 0a00436004..7ff1e9a0b0 100644 --- a/config/initializers/js_template_helpers.rb +++ b/config/initializers/js_template_helpers.rb @@ -1,6 +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 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 From 8e4e276995d1cfdffb59b0c2834e38a4148e6879 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Jan 2021 05:49:20 +0000 Subject: [PATCH 34/84] Bump paypal-sdk-merchant from 1.106.1 to 1.117.2 Bumps [paypal-sdk-merchant](https://github.com/paypal/merchant-sdk-ruby) from 1.106.1 to 1.117.2. - [Release notes](https://github.com/paypal/merchant-sdk-ruby/releases) - [Changelog](https://github.com/paypal/merchant-sdk-ruby/blob/master/ChangeLog.txt) - [Commits](https://github.com/paypal/merchant-sdk-ruby/compare/v1.106.1...v1.117.2) Signed-off-by: dependabot[bot] --- Gemfile | 2 +- Gemfile.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index 39c0140964..ef8633f303 100644 --- a/Gemfile +++ b/Gemfile @@ -61,7 +61,7 @@ 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' diff --git a/Gemfile.lock b/Gemfile.lock index c8163c1ea5..6f3ad68b56 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -483,11 +483,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) @@ -713,7 +713,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) @@ -790,7 +790,7 @@ DEPENDENCIES 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 From 79668e06a7dadc7a719ef0d9b8ad0c267f007831 Mon Sep 17 00:00:00 2001 From: Cillian O'Ruanaidh Date: Fri, 15 Jan 2021 10:24:50 +0000 Subject: [PATCH 35/84] Remove ability to create new product from products page, use /admin/products/new instead. It's simpler if there is just one place to add a new product. Closes #6650 This removes the 'creating directly from the new product path' test scenario because we have another 'assigning important attributes' scenario above which provides enough coverage. --- .../admin/products/index/_header.html.haml | 4 +--- app/views/spree/admin/products/new.js.erb | 12 ---------- spec/features/admin/products_spec.rb | 22 ------------------- 3 files changed, 1 insertion(+), 37 deletions(-) delete mode 100644 app/views/spree/admin/products/new.js.erb 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/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/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 From 26e817818f878c1a758ebf882ae500c661a5c7ef Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Fri, 15 Jan 2021 23:34:40 +1100 Subject: [PATCH 36/84] Updating translations for config/locales/ca.yml --- config/locales/ca.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) 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" From dc76f161a3c17400932130d6af9e35ec347f4508 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Fri, 15 Jan 2021 23:42:27 +1100 Subject: [PATCH 37/84] Updating translations for config/locales/es.yml --- config/locales/es.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) 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: From c132a5351f87c3bfdc875f2003654ced0284c3b3 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Fri, 15 Jan 2021 23:46:42 +1100 Subject: [PATCH 38/84] Updating translations for config/locales/it.yml --- config/locales/it.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) 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 From c870af8981ddb85c676f54c6bb6a1f4e9bd3fb58 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 15 Jan 2021 13:21:03 +0000 Subject: [PATCH 39/84] Remove debugging output. --- spec/controllers/spree/credit_cards_controller_spec.rb | 2 -- 1 file changed, 2 deletions(-) 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 From 6be14dfb29cf15694305d61d291ca7ca23e7fe3d Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Sat, 16 Jan 2021 01:58:02 +1100 Subject: [PATCH 40/84] Updating translations for config/locales/fr_CA.yml --- config/locales/fr_CA.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) 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: From 8f8f430e8737a14f91d519549575375bfb2905a0 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Sat, 16 Jan 2021 02:04:42 +1100 Subject: [PATCH 41/84] Updating translations for config/locales/en_CA.yml --- config/locales/en_CA.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) 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: From 1eb08ba31c385b0b25d526586aac09296f7f746d Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 15 Jan 2021 16:15:42 +0000 Subject: [PATCH 42/84] Define basic models in migration --- db/migrate/20201219120055_add_order_to_adjustments.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/db/migrate/20201219120055_add_order_to_adjustments.rb b/db/migrate/20201219120055_add_order_to_adjustments.rb index 718701d16c..85d3048fc3 100644 --- a/db/migrate/20201219120055_add_order_to_adjustments.rb +++ b/db/migrate/20201219120055_add_order_to_adjustments.rb @@ -1,5 +1,13 @@ class AddOrderToAdjustments < ActiveRecord::Migration def up + 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 + add_column :spree_adjustments, :order_id, :integer # Ensure migration can use the new column From 5593da2928e46ad6a048f31274aa128f6cad8ab8 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 15 Jan 2021 02:32:10 +0000 Subject: [PATCH 43/84] Delete dead code #update_hooks These hooks allow Spree core to be modified by other gems and apps without changing Spree code. We don't use it. #YAGNI --- app/models/spree/order.rb | 10 -------- .../order_management/order/updater.rb | 9 ++----- spec/models/spree/order/updating_spec.rb | 20 ---------------- spec/models/spree/order_spec.rb | 24 ------------------- 4 files changed, 2 insertions(+), 61 deletions(-) delete mode 100644 spec/models/spree/order/updating_spec.rb diff --git a/app/models/spree/order.rb b/app/models/spree/order.rb index ce992107fb..07cd78dac7 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 } @@ -450,7 +441,6 @@ module Spree updater.update_shipment_state updater.before_save_hook save - updater.run_hooks deliver_order_confirmation_email 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 6ff7ef5ef6..445f8193ec 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 @@ -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: diff --git a/spec/models/spree/order/updating_spec.rb b/spec/models/spree/order/updating_spec.rb deleted file mode 100644 index d31421e53c..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) { create(: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) } From c0c7c6ec78fbd40a6bb39784cc8f85a5c93b0547 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 15 Jan 2021 17:23:58 +0000 Subject: [PATCH 44/84] Update checkout helper summing and add test coverage --- app/helpers/checkout_helper.rb | 2 +- spec/helpers/checkout_helper_spec.rb | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/app/helpers/checkout_helper.rb b/app/helpers/checkout_helper.rb index 8e54913dc5..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.sum(:amount) + label: I18n.t(:orders_form_admin), amount: enterprise_fee_adjustments.sum(&:amount) ) end 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 From 0eab1b2339350115d0d303868c6763d2ede09169 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sat, 31 Oct 2020 11:06:06 +0000 Subject: [PATCH 45/84] Merge Spree::BaseController with ApplicationController --- app/controllers/application_controller.rb | 18 ++++++++++++++++-- app/controllers/spree/base_controller.rb | 20 -------------------- 2 files changed, 16 insertions(+), 22 deletions(-) delete mode 100644 app/controllers/spree/base_controller.rb 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/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' From ff8a81cee722919df954921390443704ebdd4e20 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sat, 31 Oct 2020 11:08:51 +0000 Subject: [PATCH 46/84] Remove includes already present in parent ApplicationController --- app/controllers/base_controller.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/controllers/base_controller.rb b/app/controllers/base_controller.rb index b039a2447d..de4540f257 100644 --- a/app/controllers/base_controller.rb +++ b/app/controllers/base_controller.rb @@ -1,17 +1,10 @@ -require 'spree/core/controller_helpers/auth' -require 'spree/core/controller_helpers/common' require 'spree/core/controller_helpers/order' -require 'spree/core/controller_helpers/respond_with' require 'open_food_network/tag_rule_applicator' class BaseController < ApplicationController - include Spree::Core::ControllerHelpers::Auth - include Spree::Core::ControllerHelpers::Common include Spree::Core::ControllerHelpers::Order - include Spree::Core::ControllerHelpers::RespondWith include I18nHelper - include EnterprisesHelper include OrderCyclesHelper helper 'spree/base' From 23e6048bde176eaae31f3426c27b6c1195905910 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sat, 31 Oct 2020 11:17:38 +0000 Subject: [PATCH 47/84] Merge StoreController with BaseController --- app/controllers/base_controller.rb | 4 ++++ app/controllers/spree/store_controller.rb | 14 -------------- 2 files changed, 4 insertions(+), 14 deletions(-) delete mode 100644 app/controllers/spree/store_controller.rb diff --git a/app/controllers/base_controller.rb b/app/controllers/base_controller.rb index de4540f257..143370f3e9 100644 --- a/app/controllers/base_controller.rb +++ b/app/controllers/base_controller.rb @@ -1,7 +1,11 @@ +# frozen_string_literal: true + require 'spree/core/controller_helpers/order' require 'open_food_network/tag_rule_applicator' class BaseController < ApplicationController + layout 'darkswarm' + include Spree::Core::ControllerHelpers::Order include I18nHelper 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 From 7a22367b4a0f4b39429a54ae3f4f61e1bcce2faa Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sat, 31 Oct 2020 11:19:03 +0000 Subject: [PATCH 48/84] Make controllers use ::Basecontroller instead of StoreController --- app/controllers/checkout_controller.rb | 2 +- app/controllers/spree/checkout_controller.rb | 2 +- app/controllers/spree/orders_controller.rb | 2 +- app/controllers/spree/users_controller.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) 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/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/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 From 4060e7debf71fce58efb2a26f29c045ffe90f565 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sun, 1 Nov 2020 11:11:28 +0000 Subject: [PATCH 49/84] Replace usages of Spree::BaseController with ApplicationController --- app/controllers/spree/admin/base_controller.rb | 2 +- lib/spree/core/controller_helpers/respond_with.rb | 2 +- lib/spree/i18n/initializer.rb | 2 +- lib/spree/responder.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) 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/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/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 From b713219690d0e4029e502155562bdb98e46a18bb Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 16 Jan 2021 13:19:00 +0000 Subject: [PATCH 50/84] Use deliver_now in Jobs --- .../app/services/order_management/subscriptions/summarizer.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From ea9ebc8a3338aa42dc424be9c47802bef1944be8 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sun, 10 Jan 2021 00:27:17 +0000 Subject: [PATCH 51/84] Update Angular CSRF handling --- Gemfile | 1 + Gemfile.lock | 3 +++ Gemfile_next.lock | 3 +++ app/assets/javascripts/admin/admin_ofn.js.coffee | 1 - .../javascripts/admin/index_utils/index_utils.js.coffee | 2 +- .../javascripts/admin/order_cycles/order_cycles.js.erb.coffee | 4 ---- .../javascripts/admin/product_import/product_import.js.coffee | 1 - app/assets/javascripts/admin/utils/utils.js.coffee | 3 ++- app/assets/javascripts/darkswarm/darkswarm.js.coffee | 1 - 9 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index 39c0140964..f772ac441f 100644 --- a/Gemfile +++ b/Gemfile @@ -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..c6370182c7 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) @@ -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 diff --git a/Gemfile_next.lock b/Gemfile_next.lock index 290d607b46..437a99d5e7 100644 --- a/Gemfile_next.lock +++ b/Gemfile_next.lock @@ -104,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) @@ -556,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 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/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, */*" From a7ad62a735c61352e422491d58f14b9ab163123d Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 16 Jan 2021 13:54:09 +0000 Subject: [PATCH 52/84] Update deprecated devise spec helper Devise has been complaining about this for a while in the test suite: ``` [Devise] including `Devise::TestHelpers` is deprecated and will be removed from Devise. For controller tests, please include `Devise::Test::ControllerHelpers` instead. ``` --- spec/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 41f3d47bd5..52644b5ca4 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 From bc9020a5552e7538b098d39875c38ed8011da7bd Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 18 Jan 2021 12:27:07 +0000 Subject: [PATCH 53/84] Update mail delivery mocks in subscription specs --- .../order_management/subscriptions/summarizer_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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) From 006abee23dd8a0d393c41225a91119391f6c3454 Mon Sep 17 00:00:00 2001 From: Pau Perez Date: Mon, 18 Jan 2021 11:59:57 +0100 Subject: [PATCH 54/84] Update old setting to serve static assets Fixes the following deprecation warning: ``` DEPRECATION WARNING: The configuration option `config.serve_static_assets` has been renamed to `config.serve_static_files` to clarify its role (it merely enables serving everything in the `public` folder and is unrelated to the asset pipeline). The `serve_ static_assets` alias will be removed in Rails 5.0. Please migrate your configuration files accordingly. (called from block in at /usr/src/app/config/environments/test.rb:13) ``` --- config/environments/production.rb | 2 +- config/environments/staging.rb | 2 +- config/environments/test.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 From fd80d4a67fde347fe9b40cdafc378ad7c31b505d Mon Sep 17 00:00:00 2001 From: Pau Perez Date: Mon, 18 Jan 2021 11:54:48 +0100 Subject: [PATCH 55/84] Remove old deprecation warning We're in Rails 4.2 so we can remove it. This gets rid of the following message when running tests: ``` DEPRECATION WARNING: Suppressing Selenium deprecation warnings is not needed any more. (called from block in at /usr/src/app/spec/spec_helper.rb:214) ``` --- spec/spec_helper.rb | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 52644b5ca4..62c67e9417 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -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 From d1eea4654ad2a90c2b66d5587e5e16e82ded7c81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Jan 2021 16:10:24 +0000 Subject: [PATCH 56/84] Bump monetize from 1.9.4 to 1.10.0 Bumps [monetize](https://github.com/RubyMoney/monetize) from 1.9.4 to 1.10.0. - [Release notes](https://github.com/RubyMoney/monetize/releases) - [Changelog](https://github.com/RubyMoney/monetize/blob/master/CHANGELOG.md) - [Commits](https://github.com/RubyMoney/monetize/commits) Signed-off-by: dependabot[bot] --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 39c0140964..6c327975c1 100644 --- a/Gemfile +++ b/Gemfile @@ -56,7 +56,7 @@ 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' diff --git a/Gemfile.lock b/Gemfile.lock index c8163c1ea5..cd73573536 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -450,9 +450,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) @@ -783,7 +783,7 @@ 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! From fc40775ca84b50651dcbe56f5a71bb81f6a32c4f Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Mon, 18 Jan 2021 23:33:05 +0000 Subject: [PATCH 57/84] Make paypal controller inherit from base controller and not from old store controller --- app/controllers/spree/paypal_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 79b86f535d5be5a8b7927bcaedeaf0d8921ab7c6 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 19 Jan 2021 13:16:39 +0000 Subject: [PATCH 58/84] Move class definitions in adjustment migration --- .../20201219120055_add_order_to_adjustments.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/db/migrate/20201219120055_add_order_to_adjustments.rb b/db/migrate/20201219120055_add_order_to_adjustments.rb index 85d3048fc3..9748a4372d 100644 --- a/db/migrate/20201219120055_add_order_to_adjustments.rb +++ b/db/migrate/20201219120055_add_order_to_adjustments.rb @@ -1,13 +1,14 @@ class AddOrderToAdjustments < ActiveRecord::Migration - def up - 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 + 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 From a184075c5c8b8cf6f3e8035456728b92310de8f4 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 19 Jan 2021 15:33:44 +0000 Subject: [PATCH 59/84] Fix performance issue in loading payment methods This was loading and initializing every payment method in the database, and every calculator for each of those payment methods. :fire: --- app/controllers/spree/admin/payments_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/spree/admin/payments_controller.rb b/app/controllers/spree/admin/payments_controller.rb index e0a530a823..5115a9d553 100644 --- a/app/controllers/spree/admin/payments_controller.rb +++ b/app/controllers/spree/admin/payments_controller.rb @@ -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 From c4a8a38c8dfdfa436dcf7e21dd4e11a6305db5b0 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 15 Jan 2021 14:41:59 +0000 Subject: [PATCH 60/84] Soft-delete enterprise fees These objects can hold critical information related to adjustments and tax categories. If they are hard-deleted we lose vital data that's needed by associated records. --- app/models/enterprise_fee.rb | 2 ++ .../20210115143738_add_deleted_at_to_enterprise_fee.rb | 5 +++++ db/schema.rb | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20210115143738_add_deleted_at_to_enterprise_fee.rb 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/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 3d4de3b876..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: 20201219120055) 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: 20201219120055) 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 From 6a9505cf6787c858c979e8e317d73013712db6ee Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 15 Jan 2021 15:16:56 +0000 Subject: [PATCH 61/84] Ensure adjustments can access soft-deleted originators Note: originator is generally an EnterpriseFee or a TaxRate --- app/models/spree/adjustment.rb | 7 +++++++ spec/models/enterprise_fee_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/app/models/spree/adjustment.rb b/app/models/spree/adjustment.rb index 93f4bbafe2..942e7fc149 100644 --- a/app/models/spree/adjustment.rb +++ b/app/models/spree/adjustment.rb @@ -162,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/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 From b9cf5b7a9b051738e9bfbc9bb3a1f30ff60f87d3 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Wed, 20 Jan 2021 12:46:16 +1100 Subject: [PATCH 62/84] Updating translations for config/locales/de_DE.yml --- config/locales/de_DE.yml | 99 ++++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 34 deletions(-) diff --git a/config/locales/de_DE.yml b/config/locales/de_DE.yml index 666f362eea..13edd2a2db 100644 --- a/config/locales/de_DE.yml +++ b/config/locales/de_DE.yml @@ -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" @@ -193,7 +193,7 @@ de_DE: 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..." + 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 Name oder Ort... producers_join: 'Wir laden Deutsche Produzenten ein, jetzt dem Open Food Network beizutreten. ' charges_sales_tax: Berechnet Steuern? @@ -1557,13 +1557,13 @@ de_DE: 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_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 Zyklus wird in %{distance_of_time} geöffnet" shopping_oc_select: "Wä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" @@ -1571,13 +1571,14 @@ de_DE: shopping_groups_part_of: "ist ein Teil von:" shopping_producers_of_hub: "Erzeuger bei%{hub}" enterprises_next_closing: "Nächster Bestellschluß" + 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_shopping_here: "Sie kaufen hier ein" + hubs_orders_closed: "Bestellzyklus geschlossen" hubs_profile_only: "Profil nur" hubs_delivery_options: "Lieferoptionen" hubs_pickup: "Abholen" @@ -1585,8 +1586,8 @@ de_DE: hubs_producers: "Unsere Produzenten" hubs_filter_by: "Filtern nach" hubs_filter_type: "Art" - hubs_filter_delivery: "Lieferung" - hubs_filter_property: "Eigentum" + hubs_filter_delivery: "Lieferoptionen" + hubs_filter_property: "Eigenschaften" hubs_matches: "Meintest Du?" hubs_intro: Regional einkaufen hubs_distance: Am nächsten @@ -1597,7 +1598,7 @@ de_DE: 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 + products_clear: Zurücksetzen products_showing: "Angezeigt:" products_results_for: "Ergebnisse für" products_or: "oder" @@ -1608,8 +1609,8 @@ de_DE: products_filter_by: "Filtern nach" products_filter_selected: "ausgewählt" products_filter_heading: "Filter" - products_filter_clear: "Leeren" - products_filter_done: "Erledigt" + products_filter_clear: "Zurücksetzen" + products_filter_done: "Anwenden" products_loading: "Lade Produkte..." products_updating_cart: "Einkaufswagen aktualisieren..." products_cart_empty: "Leerer Einkaufswagen" @@ -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." @@ -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_property: Eigenschaften producers_title: Produzenten producers_headline: Finden Sie lokale Produzenten producers_signup_title: Melde dich als Produzent an @@ -1703,8 +1704,8 @@ de_DE: 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." 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. @@ -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 @@ -1871,7 +1872,7 @@ de_DE: 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" @@ -1903,7 +1904,7 @@ 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" @@ -1944,7 +1945,7 @@ de_DE: shop_variant_quantity_min: "Mindest" shop_variant_quantity_max: "max" follow: "Folgen" - shop_for_products_html: "Suche nach %{enterprise} Produkten unter:" + shop_for_products_html: "Hier erhalten Sie %{enterprise}-Produkte:" change_shop: "Laden ändern auf:" shop_at: "Kaufe jetzt bei:" admin_fee: "Gebühr Administration" @@ -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" @@ -2087,6 +2088,7 @@ de_DE: 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})." @@ -2102,8 +2104,8 @@ de_DE: 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" + order_cycle: "Bestellzyklus" + order_cycles: "Bestellzyklen" enterprise_relationships: "Unternehmensberechtigungen" remove_tax: "Steuer entfernen" first_name_begins_with: "Der Vorname beginnt mit" @@ -2128,7 +2130,7 @@ 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" @@ -2148,7 +2150,7 @@ 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_status: Status @@ -2379,6 +2381,7 @@ de_DE: js: saving: 'Speichern ...' changes_saved: 'Änderungen gespeichert' + authorising: "Autorisierung ..." save_changes_first: Änderungen zuerst speichern. all_changes_saved: Alle Änderungen gespeichert unsaved_changes: Du hast nicht gespeicherte Änderungen @@ -2394,6 +2397,7 @@ 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 Shops 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. @@ -2693,7 +2697,7 @@ 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: "Wiederverkäufer" customers: select_shop: 'Bitte wählen Sie zuerst einen Laden aus' could_not_create: Es tut uns leid! Konnte nicht ... Erstellen @@ -2706,7 +2710,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: @@ -3059,6 +3063,7 @@ de_DE: payment_state: "Zahlungsstatus" errors: messages: + included_price_validation: "kann nur ausgewählt werden, wenn Sie eine Standardsteuerzone festgelegt haben" blank: "kann nicht leer sein" layouts: admin: @@ -3079,7 +3084,7 @@ de_DE: configuration: "Aufbau" users: "Benutzer" roles: "Rollen" - order_cycles: "Bestellrunden" + order_cycles: "Bestellzyklen" enterprises: "Unternehmen" enterprise_relationships: "Berechtigungen" customers: "Kunden" @@ -3091,11 +3096,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" @@ -3184,7 +3189,7 @@ 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." @@ -3192,7 +3197,7 @@ de_DE: few: "Sie haben %{count} aktive Auftragszyklen." many: "Sie haben %{count} aktive Auftragszyklen." other: "Sie haben %{count} aktive Bestellzyklen." - manage_order_cycles: "BESTELLRUNDEN VERWALTEN" + manage_order_cycles: "BESTELLZYKLEN VERWALTEN" shipping_methods: index: shipping_methods: "Lieferart" @@ -3269,6 +3274,14 @@ de_DE: 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." 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: @@ -3504,7 +3517,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 +3572,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: From a53cc6bc927e2f8b07558388bbaf8b043feb23eb Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Tue, 19 Jan 2021 19:07:37 -0800 Subject: [PATCH 63/84] Update all locales with the latest Transifex translations --- config/locales/ar.yml | 25 ++++++++++ config/locales/de_DE.yml | 99 ++++++++++++++++++++++++++-------------- 2 files changed, 90 insertions(+), 34 deletions(-) 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/de_DE.yml b/config/locales/de_DE.yml index 666f362eea..13edd2a2db 100644 --- a/config/locales/de_DE.yml +++ b/config/locales/de_DE.yml @@ -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" @@ -193,7 +193,7 @@ de_DE: 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..." + 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 Name oder Ort... producers_join: 'Wir laden Deutsche Produzenten ein, jetzt dem Open Food Network beizutreten. ' charges_sales_tax: Berechnet Steuern? @@ -1557,13 +1557,13 @@ de_DE: 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_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 Zyklus wird in %{distance_of_time} geöffnet" shopping_oc_select: "Wä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" @@ -1571,13 +1571,14 @@ de_DE: shopping_groups_part_of: "ist ein Teil von:" shopping_producers_of_hub: "Erzeuger bei%{hub}" enterprises_next_closing: "Nächster Bestellschluß" + 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_shopping_here: "Sie kaufen hier ein" + hubs_orders_closed: "Bestellzyklus geschlossen" hubs_profile_only: "Profil nur" hubs_delivery_options: "Lieferoptionen" hubs_pickup: "Abholen" @@ -1585,8 +1586,8 @@ de_DE: hubs_producers: "Unsere Produzenten" hubs_filter_by: "Filtern nach" hubs_filter_type: "Art" - hubs_filter_delivery: "Lieferung" - hubs_filter_property: "Eigentum" + hubs_filter_delivery: "Lieferoptionen" + hubs_filter_property: "Eigenschaften" hubs_matches: "Meintest Du?" hubs_intro: Regional einkaufen hubs_distance: Am nächsten @@ -1597,7 +1598,7 @@ de_DE: 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 + products_clear: Zurücksetzen products_showing: "Angezeigt:" products_results_for: "Ergebnisse für" products_or: "oder" @@ -1608,8 +1609,8 @@ de_DE: products_filter_by: "Filtern nach" products_filter_selected: "ausgewählt" products_filter_heading: "Filter" - products_filter_clear: "Leeren" - products_filter_done: "Erledigt" + products_filter_clear: "Zurücksetzen" + products_filter_done: "Anwenden" products_loading: "Lade Produkte..." products_updating_cart: "Einkaufswagen aktualisieren..." products_cart_empty: "Leerer Einkaufswagen" @@ -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." @@ -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_property: Eigenschaften producers_title: Produzenten producers_headline: Finden Sie lokale Produzenten producers_signup_title: Melde dich als Produzent an @@ -1703,8 +1704,8 @@ de_DE: 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." 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. @@ -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 @@ -1871,7 +1872,7 @@ de_DE: 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" @@ -1903,7 +1904,7 @@ 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" @@ -1944,7 +1945,7 @@ de_DE: shop_variant_quantity_min: "Mindest" shop_variant_quantity_max: "max" follow: "Folgen" - shop_for_products_html: "Suche nach %{enterprise} Produkten unter:" + shop_for_products_html: "Hier erhalten Sie %{enterprise}-Produkte:" change_shop: "Laden ändern auf:" shop_at: "Kaufe jetzt bei:" admin_fee: "Gebühr Administration" @@ -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" @@ -2087,6 +2088,7 @@ de_DE: 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})." @@ -2102,8 +2104,8 @@ de_DE: 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" + order_cycle: "Bestellzyklus" + order_cycles: "Bestellzyklen" enterprise_relationships: "Unternehmensberechtigungen" remove_tax: "Steuer entfernen" first_name_begins_with: "Der Vorname beginnt mit" @@ -2128,7 +2130,7 @@ 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" @@ -2148,7 +2150,7 @@ 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_status: Status @@ -2379,6 +2381,7 @@ de_DE: js: saving: 'Speichern ...' changes_saved: 'Änderungen gespeichert' + authorising: "Autorisierung ..." save_changes_first: Änderungen zuerst speichern. all_changes_saved: Alle Änderungen gespeichert unsaved_changes: Du hast nicht gespeicherte Änderungen @@ -2394,6 +2397,7 @@ 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 Shops 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. @@ -2693,7 +2697,7 @@ 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: "Wiederverkäufer" customers: select_shop: 'Bitte wählen Sie zuerst einen Laden aus' could_not_create: Es tut uns leid! Konnte nicht ... Erstellen @@ -2706,7 +2710,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: @@ -3059,6 +3063,7 @@ de_DE: payment_state: "Zahlungsstatus" errors: messages: + included_price_validation: "kann nur ausgewählt werden, wenn Sie eine Standardsteuerzone festgelegt haben" blank: "kann nicht leer sein" layouts: admin: @@ -3079,7 +3084,7 @@ de_DE: configuration: "Aufbau" users: "Benutzer" roles: "Rollen" - order_cycles: "Bestellrunden" + order_cycles: "Bestellzyklen" enterprises: "Unternehmen" enterprise_relationships: "Berechtigungen" customers: "Kunden" @@ -3091,11 +3096,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" @@ -3184,7 +3189,7 @@ 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." @@ -3192,7 +3197,7 @@ de_DE: few: "Sie haben %{count} aktive Auftragszyklen." many: "Sie haben %{count} aktive Auftragszyklen." other: "Sie haben %{count} aktive Bestellzyklen." - manage_order_cycles: "BESTELLRUNDEN VERWALTEN" + manage_order_cycles: "BESTELLZYKLEN VERWALTEN" shipping_methods: index: shipping_methods: "Lieferart" @@ -3269,6 +3274,14 @@ de_DE: 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." 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: @@ -3504,7 +3517,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 +3572,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: From ba018df9c521c26cddeb26076df7153a80378cb5 Mon Sep 17 00:00:00 2001 From: Pau Perez Date: Wed, 20 Jan 2021 10:58:07 +0100 Subject: [PATCH 64/84] Pass request_queuing setting to Rack middleware It turns out that this setting belongs to the Rack middleware Datadog comes with to track requests (See https://github.com/DataDog/dd-trace-rb/blob/e4c430a1748acc04cc28c2cdf2a11f491e86cee9/docs/GettingStarted.md#rack). The way to pass this option to it is through `configuration[:rack]` where the `TraceMiddleware` will read it from. See https://github.com/DataDog/dd-trace-rb/blob/f57aefe60a18d1f1e00ee5b1829e893de4db4269/lib/ddtrace/contrib/rack/middlewares.rb#L215-L217 and https://github.com/DataDog/dd-trace-rb/blob/f57aefe60a18d1f1e00ee5b1829e893de4db4269/lib/ddtrace/contrib/rack/middlewares.rb#L30-L43. --- config/initializers/datadog.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/initializers/datadog.rb b/config/initializers/datadog.rb index c1265c7802..d6e1acde4b 100644 --- a/config/initializers/datadog.rb +++ b/config/initializers/datadog.rb @@ -3,7 +3,10 @@ if ENV['DATADOG_RAILS_APM'] c.use :rails, service_name: 'rails' c.use :delayed_job, service_name: 'delayed_job' c.use :dalli, service_name: 'memcached' + c.analytics_enabled = true c.runtime_metrics_enabled = true + + c[:rack].request_queuing = true end end From e1c13bc194934c15f30d276d20022b76c2f53901 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 20 Jan 2021 13:39:01 +0000 Subject: [PATCH 65/84] Fix issue with orphaned adjustments in migration --- .../20201219120055_add_order_to_adjustments.rb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/db/migrate/20201219120055_add_order_to_adjustments.rb b/db/migrate/20201219120055_add_order_to_adjustments.rb index 9748a4372d..d9ece7ee22 100644 --- a/db/migrate/20201219120055_add_order_to_adjustments.rb +++ b/db/migrate/20201219120055_add_order_to_adjustments.rb @@ -21,7 +21,17 @@ class AddOrderToAdjustments < ActiveRecord::Migration # Migrate adjustments on line_items Spree::Adjustment.where(order_id: nil, adjustable_type: "Spree::LineItem").includes(:adjustable).find_each do |adjustment| - adjustment.update_column(:order_id, adjustment.adjustable.order_id) + 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 end From dd38c8b3e209026928735b06ee002e991cffe06a Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Wed, 20 Jan 2021 12:56:03 -0800 Subject: [PATCH 66/84] add down method for migration --- db/migrate/20201219120055_add_order_to_adjustments.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/db/migrate/20201219120055_add_order_to_adjustments.rb b/db/migrate/20201219120055_add_order_to_adjustments.rb index d9ece7ee22..e0d6c32287 100644 --- a/db/migrate/20201219120055_add_order_to_adjustments.rb +++ b/db/migrate/20201219120055_add_order_to_adjustments.rb @@ -34,4 +34,8 @@ class AddOrderToAdjustments < ActiveRecord::Migration adjustment.update_column(:order_id, line_item.order_id) end end + + def down + remove_column :spree_adjustments, :order_id + end end From b6e7307cb99809e75130f485a8d4733de55629b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jan 2021 05:16:36 +0000 Subject: [PATCH 67/84] Bump webdrivers from 4.4.2 to 4.5.0 Bumps [webdrivers](https://github.com/titusfortner/webdrivers) from 4.4.2 to 4.5.0. - [Release notes](https://github.com/titusfortner/webdrivers/releases) - [Changelog](https://github.com/titusfortner/webdrivers/blob/master/CHANGELOG.md) - [Commits](https://github.com/titusfortner/webdrivers/compare/v4.4.2...v4.5.0) Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index cd73573536..1cc60522dd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -700,7 +700,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) From 40f9b063fe2f2e606d0e2ebc2a418da081ac1a7f Mon Sep 17 00:00:00 2001 From: Cillian O'Ruanaidh Date: Fri, 15 Jan 2021 10:24:50 +0000 Subject: [PATCH 68/84] Remove ability to create new product from products page, use /admin/products/new instead. It's simpler if there is just one place to add a new product. Closes #6650 This removes the 'creating directly from the new product path' test scenario because we have another 'assigning important attributes' scenario above which provides enough coverage. --- .../admin/products/index/_header.html.haml | 4 +--- app/views/spree/admin/products/new.js.erb | 12 ---------- spec/features/admin/products_spec.rb | 22 ------------------- 3 files changed, 1 insertion(+), 37 deletions(-) delete mode 100644 app/views/spree/admin/products/new.js.erb 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/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/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 From 802e49bed33c2c5f91b1127a123600c1fb76e213 Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Mon, 14 Dec 2020 10:05:09 -0800 Subject: [PATCH 69/84] add PaymentMailer and send email if payment auth is required --- .../spree/admin/payments_controller.rb | 4 +++ app/controllers/spree/payments_controller.rb | 31 +++++++++++++++++++ app/mailers/spree/payment_mailer.rb | 16 ++++++++++ .../authorize_payment.html.haml | 2 ++ .../payment_mailer/authorize_payment.text.erb | 2 ++ config/locales/en.yml | 5 +++ config/routes.rb | 1 + .../payments/payments_controller_spec.rb | 13 ++++++++ 8 files changed, 74 insertions(+) create mode 100644 app/controllers/spree/payments_controller.rb create mode 100644 app/mailers/spree/payment_mailer.rb create mode 100644 app/views/spree/payment_mailer/authorize_payment.html.haml create mode 100644 app/views/spree/payment_mailer/authorize_payment.text.erb diff --git a/app/controllers/spree/admin/payments_controller.rb b/app/controllers/spree/admin/payments_controller.rb index e0a530a823..8d6fc851f3 100644 --- a/app/controllers/spree/admin/payments_controller.rb +++ b/app/controllers/spree/admin/payments_controller.rb @@ -154,6 +154,10 @@ module Spree return unless @payment.payment_method.class == Spree::Gateway::StripeSCA @payment.authorize! + if @payment.cvv_response_message.present? + PaymentMailer.authorize_payment(@payment).deliver + raise Spree::Core::GatewayError, I18n.t('action_required') + end return if @payment.pending? && @payment.cvv_response_message.nil? raise Spree::Core::GatewayError, I18n.t('authorization_failure') diff --git a/app/controllers/spree/payments_controller.rb b/app/controllers/spree/payments_controller.rb new file mode 100644 index 0000000000..ed93481c25 --- /dev/null +++ b/app/controllers/spree/payments_controller.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Spree + class PaymentsController < Spree::StoreController + 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 + + 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 +end diff --git a/app/mailers/spree/payment_mailer.rb b/app/mailers/spree/payment_mailer.rb new file mode 100644 index 0000000000..b76e91ea83 --- /dev/null +++ b/app/mailers/spree/payment_mailer.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Spree + class PaymentMailer < 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 +end diff --git a/app/views/spree/payment_mailer/authorize_payment.html.haml b/app/views/spree/payment_mailer/authorize_payment.html.haml new file mode 100644 index 0000000000..438a51c416 --- /dev/null +++ b/app/views/spree/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/spree/payment_mailer/authorize_payment.text.erb b/app/views/spree/payment_mailer/authorize_payment.text.erb new file mode 100644 index 0000000000..e722b24997 --- /dev/null +++ b/app/views/spree/payment_mailer/authorize_payment.text.erb @@ -0,0 +1,2 @@ +<%= t('.instructions', distributor: @payment.order.distributor.name, amount: @payment.display_amount) %> +<%= main_app.authorize_payment_url(@payment) %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 3a4700a9f5..9d53a0358d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2455,6 +2455,7 @@ 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" + action_required: "Action required" inventory_settings: "Inventory Settings" tag_rules: "Tag Rules" shop_preferences: "Shop Preferences" @@ -3644,6 +3645,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/routes.rb b/config/routes.rb index a657f7f234..795470cf3a 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' => 'spree/payments#redirect_to_authorize', as: "authorize_payment" resource :cart, controller: "cart" do post :populate 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..e91586e4ce 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,19 @@ 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: "http://redirect_url" + 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| From 8507dacc10a0f2ecd23550a2104832abbf790f6c Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Mon, 14 Dec 2020 14:06:10 -0800 Subject: [PATCH 70/84] pass return_url option to gateway authorize --- app/controllers/spree/admin/payments_controller.rb | 2 +- app/models/spree/gateway/stripe_sca.rb | 2 +- app/models/spree/payment/processing.rb | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/spree/admin/payments_controller.rb b/app/controllers/spree/admin/payments_controller.rb index 8d6fc851f3..fb06010b63 100644 --- a/app/controllers/spree/admin/payments_controller.rb +++ b/app/controllers/spree/admin/payments_controller.rb @@ -153,7 +153,7 @@ module Spree def authorize_stripe_sca_payment return unless @payment.payment_method.class == Spree::Gateway::StripeSCA - @payment.authorize! + @payment.authorize!(spree.order_path(@payment.order, only_path: false)) if @payment.cvv_response_message.present? PaymentMailer.authorize_payment(@payment).deliver raise Spree::Core::GatewayError, I18n.t('action_required') diff --git a/app/models/spree/gateway/stripe_sca.rb b/app/models/spree/gateway/stripe_sca.rb index a3ccde45b2..fa5b4d8d4a 100644 --- a/app/models/spree/gateway/stripe_sca.rb +++ b/app/models/spree/gateway/stripe_sca.rb @@ -110,7 +110,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/payment/processing.rb b/app/models/spree/payment/processing.rb index 36704362a8..108cb1b61d 100644 --- a/app/models/spree/payment/processing.rb +++ b/app/models/spree/payment/processing.rb @@ -23,9 +23,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! @@ -222,7 +222,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 +230,7 @@ module Spree action, (amount * 100).round, source, - gateway_options + gateway_options.merge(options) ) handle_response(response, success_state, :failure) end From 5c0408c68c5fb063683968010485f3c86941529e Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Mon, 14 Dec 2020 14:10:11 -0800 Subject: [PATCH 71/84] pass paymentIntent ID to capture --- app/models/spree/gateway/stripe_sca.rb | 5 +++++ app/models/spree/payment/processing.rb | 14 +------------- spec/models/spree/payment_spec.rb | 2 +- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/app/models/spree/gateway/stripe_sca.rb b/app/models/spree/gateway/stripe_sca.rb index fa5b4d8d4a..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 = diff --git a/app/models/spree/payment/processing.rb b/app/models/spree/payment/processing.rb index 108cb1b61d..aabafeac37 100644 --- a/app/models/spree/payment/processing.rb +++ b/app/models/spree/payment/processing.rb @@ -44,19 +44,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 diff --git a/spec/models/spree/payment_spec.rb b/spec/models/spree/payment_spec.rb index 2420c7766d..2992713e7b 100644 --- a/spec/models/spree/payment_spec.rb +++ b/spec/models/spree/payment_spec.rb @@ -227,7 +227,7 @@ describe Spree::Payment do context "if successful" do before do - payment.payment_method.should_receive(:capture).with(payment, card, anything).and_return(success_response) + payment.payment_method.should_receive(:capture).and_return(success_response) end it "should make payment complete" do From 84b5fcf2ce9105261735338e1a6c03e28429fc1e Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Mon, 14 Dec 2020 19:48:54 -0800 Subject: [PATCH 72/84] add resend-authorization-email button to admin screen --- app/assets/stylesheets/admin/shared/icons.scss | 1 + app/models/spree/credit_card.rb | 8 +++++++- app/models/spree/payment.rb | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) 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/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/payment.rb b/app/models/spree/payment.rb index 578b91e3e5..954dfd5472 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 + end + def payment_source res = source.is_a?(Payment) ? source.source : source res || payment_method From b669ccdc7427a55f0824d557e486d7ac217a85f2 Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Thu, 21 Jan 2021 09:02:27 -0800 Subject: [PATCH 73/84] refactor admin payments controller --- app/controllers/spree/admin/payments_controller.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/spree/admin/payments_controller.rb b/app/controllers/spree/admin/payments_controller.rb index fb06010b63..3924ac9de6 100644 --- a/app/controllers/spree/admin/payments_controller.rb +++ b/app/controllers/spree/admin/payments_controller.rb @@ -154,13 +154,13 @@ module Spree return unless @payment.payment_method.class == Spree::Gateway::StripeSCA @payment.authorize!(spree.order_path(@payment.order, only_path: false)) - if @payment.cvv_response_message.present? - PaymentMailer.authorize_payment(@payment).deliver - raise Spree::Core::GatewayError, I18n.t('action_required') - end - return if @payment.pending? && @payment.cvv_response_message.nil? - 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 + raise Spree::Core::GatewayError, I18n.t('action_required') end end end From 5f1669280c46116ddefef4066e60b80626077915 Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Thu, 7 Jan 2021 12:32:40 -0800 Subject: [PATCH 74/84] update to rspec 3 `expect` syntax --- spec/models/spree/payment_spec.rb | 90 ++++++++++++++++--------------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/spec/models/spree/payment_spec.rb b/spec/models/spree/payment_spec.rb index 2992713e7b..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).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 From d9b27bc556481bf302fe04db9cde83966b937305 Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Thu, 21 Jan 2021 09:10:23 -0800 Subject: [PATCH 75/84] move controller and mailer outside of spree namespace; use haml in template --- app/controllers/payments_controller.rb | 29 +++++++++++++++++ ... base authorization on the payment's order | 29 +++++++++++++++++ app/controllers/spree/payments_controller.rb | 31 ------------------- app/mailers/payment_mailer.rb | 14 +++++++++ app/mailers/spree/payment_mailer.rb | 16 ---------- .../authorize_payment.html.haml | 0 .../authorize_payment.text.haml | 3 ++ .../payment_mailer/authorize_payment.text.erb | 2 -- config/routes.rb | 2 +- 9 files changed, 76 insertions(+), 50 deletions(-) create mode 100644 app/controllers/payments_controller.rb create mode 100644 app/controllers/payments_controller.rb~aa3478fba... base authorization on the payment's order delete mode 100644 app/controllers/spree/payments_controller.rb create mode 100644 app/mailers/payment_mailer.rb delete mode 100644 app/mailers/spree/payment_mailer.rb rename app/views/{spree => }/payment_mailer/authorize_payment.html.haml (100%) create mode 100644 app/views/payment_mailer/authorize_payment.text.haml delete mode 100644 app/views/spree/payment_mailer/authorize_payment.text.erb diff --git a/app/controllers/payments_controller.rb b/app/controllers/payments_controller.rb new file mode 100644 index 0000000000..000ea22ffb --- /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 + + 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/payments_controller.rb b/app/controllers/spree/payments_controller.rb deleted file mode 100644 index ed93481c25..0000000000 --- a/app/controllers/spree/payments_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module Spree - class PaymentsController < Spree::StoreController - 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 - - 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 -end 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/mailers/spree/payment_mailer.rb b/app/mailers/spree/payment_mailer.rb deleted file mode 100644 index b76e91ea83..0000000000 --- a/app/mailers/spree/payment_mailer.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -module Spree - class PaymentMailer < 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 -end diff --git a/app/views/spree/payment_mailer/authorize_payment.html.haml b/app/views/payment_mailer/authorize_payment.html.haml similarity index 100% rename from app/views/spree/payment_mailer/authorize_payment.html.haml rename to app/views/payment_mailer/authorize_payment.html.haml 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/spree/payment_mailer/authorize_payment.text.erb b/app/views/spree/payment_mailer/authorize_payment.text.erb deleted file mode 100644 index e722b24997..0000000000 --- a/app/views/spree/payment_mailer/authorize_payment.text.erb +++ /dev/null @@ -1,2 +0,0 @@ -<%= t('.instructions', distributor: @payment.order.distributor.name, amount: @payment.display_amount) %> -<%= main_app.authorize_payment_url(@payment) %> diff --git a/config/routes.rb b/config/routes.rb index 795470cf3a..5236a42c62 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -29,7 +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' => 'spree/payments#redirect_to_authorize', as: "authorize_payment" + get '/payments/:id/authorize' => 'payments#redirect_to_authorize', as: "authorize_payment" resource :cart, controller: "cart" do post :populate From d76db9ee51cc332eeecc0c119cbb55a5175cb55d Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Fri, 8 Jan 2021 11:29:40 -0800 Subject: [PATCH 76/84] update payment controller spec to move payment to pending --- .../spree/admin/orders/payments/payments_controller_spec.rb | 1 + 1 file changed, 1 insertion(+) 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 e91586e4ce..68cd853908 100644 --- a/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb +++ b/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb @@ -95,6 +95,7 @@ describe Spree::Admin::PaymentsController, type: :controller do before do allow_any_instance_of(Spree::Payment).to receive(:authorize!) do |payment| payment.update cvv_response_message: "http://redirect_url" + payment.update state: "pending" end end it "redirects to new payment page with flash error" do From affc82b2b50359f9e341d1b925f4fde11552137b Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Thu, 21 Jan 2021 09:13:02 -0800 Subject: [PATCH 77/84] update payment jobs delivery methods --- app/controllers/spree/admin/payments_controller.rb | 2 +- app/models/spree/payment.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/spree/admin/payments_controller.rb b/app/controllers/spree/admin/payments_controller.rb index 3924ac9de6..ee6847c10c 100644 --- a/app/controllers/spree/admin/payments_controller.rb +++ b/app/controllers/spree/admin/payments_controller.rb @@ -159,7 +159,7 @@ module Spree return unless @payment.cvv_response_message.present? - PaymentMailer.authorize_payment(@payment).deliver + PaymentMailer.authorize_payment(@payment).deliver_later raise Spree::Core::GatewayError, I18n.t('action_required') end end diff --git a/app/models/spree/payment.rb b/app/models/spree/payment.rb index 954dfd5472..3bd1bcd4a3 100644 --- a/app/models/spree/payment.rb +++ b/app/models/spree/payment.rb @@ -124,7 +124,7 @@ module Spree def resend_authorization_email! return unless cvv_response_message.present? - PaymentMailer.authorize_payment(self).deliver + PaymentMailer.authorize_payment(self).deliver_later end def payment_source From ab5ffead1deb5ffabbc63d581defa8b3c9817567 Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Thu, 7 Jan 2021 11:43:03 -0800 Subject: [PATCH 78/84] require that the redirect url be to stripe.com and over https --- lib/stripe/authorize_response_patcher.rb | 3 ++- .../spree/admin/orders/payments/payments_controller_spec.rb | 2 +- spec/features/admin/payments_stripe_spec.rb | 2 +- spec/lib/stripe/authorize_response_patcher_spec.rb | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) 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/spree/admin/orders/payments/payments_controller_spec.rb b/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb index 68cd853908..f4cb9161c1 100644 --- a/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb +++ b/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb @@ -94,7 +94,7 @@ describe Spree::Admin::PaymentsController, type: :controller do 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: "http://redirect_url" + payment.update cvv_response_message: "https://www.stripe.com/authorize" payment.update state: "pending" end end 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/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 From 903b2e7ff494c7b8ca04cd3de2598503b92390b8 Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Mon, 11 Jan 2021 20:15:24 -0800 Subject: [PATCH 79/84] whitelist allowed events to be sent to a Payment --- .../spree/admin/payments_controller.rb | 6 +++- .../payments/payments_controller_spec.rb | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/app/controllers/spree/admin/payments_controller.rb b/app/controllers/spree/admin/payments_controller.rb index ee6847c10c..648632e537 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) @@ -162,6 +162,10 @@ module Spree 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 end 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 f4cb9161c1..1e1437aee2 100644 --- a/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb +++ b/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb @@ -241,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 From 1635b83c1500334b1b6e19b4de5887c429755ed2 Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Mon, 11 Jan 2021 20:15:51 -0800 Subject: [PATCH 80/84] add missing translation key for payment actions --- config/locales/en.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/locales/en.yml b/config/locales/en.yml index 9d53a0358d..b06e79a89a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2455,6 +2455,7 @@ 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" From 8bcaeff6c890f7cd6921a270be05eba126e09ab6 Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Thu, 14 Jan 2021 10:03:59 -0800 Subject: [PATCH 81/84] resolve merge conflict; add ssl helper to base controller --- app/controllers/base_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/base_controller.rb b/app/controllers/base_controller.rb index 143370f3e9..8ea4718cb5 100644 --- a/app/controllers/base_controller.rb +++ b/app/controllers/base_controller.rb @@ -1,12 +1,14 @@ # frozen_string_literal: true require 'spree/core/controller_helpers/order' +require 'spree/core/controller_helpers/ssl' require 'open_food_network/tag_rule_applicator' class BaseController < ApplicationController layout 'darkswarm' include Spree::Core::ControllerHelpers::Order + include Spree::Core::ControllerHelpers::SSL include I18nHelper include OrderCyclesHelper From ce4621858dfcde9d901e048600cef57c60c8d51e Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Thu, 14 Jan 2021 14:40:37 -0800 Subject: [PATCH 82/84] base authorization on the payment's order --- app/controllers/payments_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/payments_controller.rb b/app/controllers/payments_controller.rb index 000ea22ffb..c0fb7eadcc 100644 --- a/app/controllers/payments_controller.rb +++ b/app/controllers/payments_controller.rb @@ -9,7 +9,7 @@ class PaymentsController < BaseController def redirect_to_authorize @payment = Spree::Payment.find(params[:id]) - authorize! :show, @payment + authorize! :show, @payment.order if url = @payment.cvv_response_message redirect_to url From bba9e550062c69672111945445796e8b633d19e8 Mon Sep 17 00:00:00 2001 From: Andy Brett Date: Wed, 20 Jan 2021 21:10:24 -0800 Subject: [PATCH 83/84] don't try to process a payment if it's pending auth --- app/models/spree/payment/processing.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/spree/payment/processing.rb b/app/models/spree/payment/processing.rb index aabafeac37..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! From 7add9247d5cd30a67238dea96a75b4904c41601f Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 22 Jan 2021 11:58:27 +1100 Subject: [PATCH 84/84] Update translations from Transifex --- config/locales/de_DE.yml | 592 ++++++++++++++++++++------------------- 1 file changed, 297 insertions(+), 295 deletions(-) diff --git a/config/locales/de_DE.yml b/config/locales/de_DE.yml index 13edd2a2db..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" @@ -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, dass 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,13 +1554,13 @@ 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 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 Zyklus wird in %{distance_of_time} geöffnet" - shopping_oc_select: "Wählen..." + 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" @@ -1569,35 +1569,35 @@ de_DE: 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_buy: "Angebot" hubs_shopping_here: "Sie kaufen hier ein" hubs_orders_closed: "Bestellzyklus geschlossen" - hubs_profile_only: "Profil nur" - hubs_delivery_options: "Lieferoptionen" + 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: "Lieferoptionen" + 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. + 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" @@ -1605,14 +1605,14 @@ de_DE: 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: "Zurücksetzen" products_filter_done: "Anwenden" - products_loading: "Lade Produkte..." - products_updating_cart: "Einkaufswagen aktualisieren..." + products_loading: "Produkte werden geladen ..." + products_updating_cart: "Einkaufswagen wird aktualisiert ..." products_cart_empty: "Leerer Einkaufswagen" products_edit_cart: "Einkaufswagen bearbeiten" products_from: von @@ -1629,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 @@ -1668,7 +1668,7 @@ de_DE: producers_contact_social: Folgen producers_buy_at_html: "Hier erhalten Sie %{enterprise}-Produkte:" producers_filter: Filtern nach - producers_filter_type: Art + producers_filter_type: Kategorie producers_filter_property: Eigenschaften producers_title: Produzenten producers_headline: Finden Sie lokale Produzenten @@ -1696,13 +1696,13 @@ 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, 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. @@ -1715,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 @@ -1728,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 @@ -1749,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 @@ -1779,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" @@ -1801,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?" @@ -1826,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" @@ -1868,8 +1868,8 @@ 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" @@ -1881,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." @@ -1891,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: @@ -1908,7 +1908,7 @@ de_DE: 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" @@ -1946,7 +1946,7 @@ de_DE: shop_variant_quantity_max: "max" follow: "Folgen" shop_for_products_html: "Hier erhalten Sie %{enterprise}-Produkte:" - change_shop: "Laden ändern auf:" + change_shop: "In diesen Laden wechseln:" shop_at: "Kaufe jetzt bei:" admin_fee: "Gebühr Administration" sales_fee: "Gebühr Verkauf" @@ -2005,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" @@ -2046,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" @@ -2076,13 +2076,13 @@ 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" @@ -2093,17 +2093,17 @@ de_DE: 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." + edit_profile_details: "Unternehmensdetails bearbeiten" + edit_profile_details_etc: "Ändern Sie Ihre Unternehmensbeschreibung, Bilder usw." order_cycle: "Bestellzyklus" order_cycles: "Bestellzyklen" enterprise_relationships: "Unternehmensberechtigungen" @@ -2132,8 +2132,8 @@ de_DE: hub_sidebar_red: "rot" 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:" @@ -2152,7 +2152,7 @@ de_DE: report_tax_types: Steuerarten 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 @@ -2212,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 @@ -2225,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 @@ -2276,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:" @@ -2297,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!" @@ -2319,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}" @@ -2360,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.' @@ -2379,9 +2379,9 @@ de_DE: pending: steht aus shipped: Wird versendet js: - saving: 'Speichern ...' + saving: 'Wird gespeichert ...' changes_saved: 'Änderungen gespeichert' - authorising: "Autorisierung ..." + authorising: "Wird autorisiert ..." save_changes_first: Änderungen zuerst speichern. all_changes_saved: Alle Änderungen gespeichert unsaved_changes: Du hast nicht gespeicherte Änderungen @@ -2397,38 +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 Shops entfernt. Sie können die Berechtigungen nach dem Aktualisieren der Standardkarte erneut erteilen. Möchten Sie die Standardkarte ändern? + 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" @@ -2450,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 @@ -2467,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 @@ -2484,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 @@ -2505,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, @@ -2514,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 @@ -2538,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:" @@ -2594,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: @@ -2675,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 @@ -2688,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.' @@ -2697,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: "Wiederverkä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 @@ -2853,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" @@ -2867,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" @@ -2896,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" @@ -2965,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" @@ -3006,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" @@ -3023,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" @@ -3034,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!' @@ -3057,14 +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: included_price_validation: "kann nur ausgewählt werden, wenn Sie eine Standardsteuerzone festgelegt haben" - blank: "kann nicht leer sein" + blank: "darf nicht leer sein" layouts: admin: login_nav: @@ -3073,7 +3075,7 @@ de_DE: admin: tab: dashboard: "Übersicht" - orders: "Aufträge" + orders: "Bestellungen" bulk_order_management: "Massenbearbeitung von Bestellungen" subscriptions: "Abonnements" products: "Produkte" @@ -3147,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" @@ -3162,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" @@ -3177,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." @@ -3194,8 +3196,8 @@ de_DE: 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: "BESTELLZYKLEN VERWALTEN" shipping_methods: @@ -3226,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" @@ -3238,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. @@ -3255,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 @@ -3271,7 +3273,7 @@ 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)" @@ -3294,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" @@ -3311,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 @@ -3338,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" @@ -3389,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: @@ -3455,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" @@ -3480,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" @@ -3494,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