From 4e1521fd6ae24f5c5f180fa9a14d78d74f2920f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 2 Oct 2018 19:13:34 +0000 Subject: [PATCH 01/65] Bump haml from 4.0.4 to 4.0.7 Bumps [haml](https://github.com/haml/haml) from 4.0.4 to 4.0.7. - [Release notes](https://github.com/haml/haml/releases) - [Changelog](https://github.com/haml/haml/blob/master/CHANGELOG.md) - [Commits](https://github.com/haml/haml/compare/4.0.4...4.0.7) Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9beb761a25..4c5fb2ae27 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -470,7 +470,7 @@ GEM guard (~> 2.1) guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) - haml (4.0.4) + haml (4.0.7) tilt hashdiff (0.3.7) highline (1.6.15) From e87075aed70980f5902302a9f7532174b7e85d63 Mon Sep 17 00:00:00 2001 From: "fabricio.albarnaz" Date: Thu, 11 Oct 2018 15:27:46 -0300 Subject: [PATCH 02/65] Add before save to check the instagram link pattern --- app/models/enterprise.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index ea1724f20a..024bc04d9a 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -80,6 +80,8 @@ class Enterprise < ActiveRecord::Base before_validation :set_unused_address_fields after_validation :geocode_address + before_save :check_instagram_pattern + after_touch :touch_distributors after_create :set_default_contact after_create :relate_to_owners_enterprises @@ -425,4 +427,9 @@ class Enterprise < ActiveRecord::Base where('enterprises.id != ?', self.id). each(&:touch) end + + def check_instagram_pattern + return if self.instagram.blank? || self.instagram.exclude?('www.instagram.com') + self.instagram = "@#{self.instagram.split('/').last}" + end end From 48720c835cbfd9bec679449cf6c9005de0e4c993 Mon Sep 17 00:00:00 2001 From: "fabricio.albarnaz" Date: Thu, 11 Oct 2018 16:30:17 -0300 Subject: [PATCH 03/65] Add spec to instagram pattern --- spec/models/enterprise_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index 73cd3e1c5b..28ce82b103 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -147,6 +147,14 @@ describe Enterprise do it "sets the enterprise contact to the owner by default" do enterprise.contact.should eq enterprise.owner end + + context "prevent an wrong instagram link pattern" do + let!(:e) { create(:enterprise, instagram: 'www.instagram.com/my_user') } + + it "expects the instagram attribute to be in the correct pattern" do + expect(e.instagram).to eq('@my_user') + end + end end describe "preferred_shopfront_taxon_order" do From e46b3566c3c71a7d155715406772b3a14ab882ba Mon Sep 17 00:00:00 2001 From: "fabricio.albarnaz" Date: Thu, 11 Oct 2018 16:51:43 -0300 Subject: [PATCH 04/65] Remove the check for www.instagram.com --- app/models/enterprise.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 024bc04d9a..a92e617303 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -429,7 +429,7 @@ class Enterprise < ActiveRecord::Base end def check_instagram_pattern - return if self.instagram.blank? || self.instagram.exclude?('www.instagram.com') + return if self.instagram.blank? self.instagram = "@#{self.instagram.split('/').last}" end end From 6ad32fb66df762f3b760226b8d3df5f5e3ab0e40 Mon Sep 17 00:00:00 2001 From: "fabricio.albarnaz" Date: Thu, 11 Oct 2018 18:05:18 -0300 Subject: [PATCH 05/65] Add check for instagram.com --- app/models/enterprise.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index a92e617303..7d4650ad0e 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -429,7 +429,7 @@ class Enterprise < ActiveRecord::Base end def check_instagram_pattern - return if self.instagram.blank? + return if self.instagram.blank? || self.instagram.exclude?('instagram.com') self.instagram = "@#{self.instagram.split('/').last}" end end From e7a909e828dbf8234cdebcbacd8591801caf7518 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 2 Oct 2018 12:25:57 +0100 Subject: [PATCH 06/65] Rename supplier to enterprise --- .../import_form_controller.js.coffee | 4 +- .../controllers/import_options_form.js.coffee | 2 +- .../filters/filter_entries.js.coffee | 8 +-- .../services/product_import_service.js.coffee | 8 +-- .../admin/product_import_controller.rb | 2 +- app/models/product_import/entry_processor.rb | 34 +++++------ app/models/product_import/entry_validator.rb | 56 +++++++++---------- app/models/product_import/product_importer.rb | 22 ++++---- app/models/product_import/settings.rb | 4 +- app/models/product_import/spreadsheet_data.rb | 18 +++--- .../product_import/spreadsheet_entry.rb | 4 +- .../product_import/_import_options.html.haml | 4 +- config/locales/en.yml | 2 +- 13 files changed, 84 insertions(+), 84 deletions(-) diff --git a/app/assets/javascripts/admin/product_import/controllers/import_form_controller.js.coffee b/app/assets/javascripts/admin/product_import/controllers/import_form_controller.js.coffee index d9b73cdabf..95e6a67a5d 100644 --- a/app/assets/javascripts/admin/product_import/controllers/import_form_controller.js.coffee +++ b/app/assets/javascripts/admin/product_import/controllers/import_form_controller.js.coffee @@ -3,7 +3,7 @@ angular.module("admin.productImport").controller "ImportFormCtrl", ($scope, $htt $scope.entries = {} $scope.update_counts = {} $scope.reset_counts = {} - $scope.supplier_product_counts = ams_data.supplier_product_counts + $scope.enterprise_product_counts = ams_data.enterprise_product_counts $scope.updates = {} $scope.updated_total = 0 @@ -19,7 +19,7 @@ angular.module("admin.productImport").controller "ImportFormCtrl", ($scope, $htt } $scope.countResettable = () -> - angular.forEach $scope.supplier_product_counts, (value, key) -> + angular.forEach $scope.enterprise_product_counts, (value, key) -> $scope.reset_counts[key] = value if $scope.update_counts[key] $scope.reset_counts[key] -= $scope.update_counts[key] diff --git a/app/assets/javascripts/admin/product_import/controllers/import_options_form.js.coffee b/app/assets/javascripts/admin/product_import/controllers/import_options_form.js.coffee index 79764ecc30..67037014d5 100644 --- a/app/assets/javascripts/admin/product_import/controllers/import_options_form.js.coffee +++ b/app/assets/javascripts/admin/product_import/controllers/import_options_form.js.coffee @@ -17,7 +17,7 @@ angular.module("admin.productImport").controller "ImportOptionsFormCtrl", ($scop confirmed = confirm t('js.product_import.confirmation') if checked if confirmed or !checked - ProductImportService.updateResetAbsent($scope.supplierId, $scope.reset_counts[$scope.supplierId], checked) + ProductImportService.updateResetAbsent($scope.enterpriseId, $scope.reset_counts[$scope.enterpriseId], checked) else $scope.settings['reset_all_absent'] = false diff --git a/app/assets/javascripts/admin/product_import/filters/filter_entries.js.coffee b/app/assets/javascripts/admin/product_import/filters/filter_entries.js.coffee index efbcf5652b..d5b68e8a45 100644 --- a/app/assets/javascripts/admin/product_import/filters/filter_entries.js.coffee +++ b/app/assets/javascripts/admin/product_import/filters/filter_entries.js.coffee @@ -18,15 +18,15 @@ angular.module("admin.productImport").filter 'entriesFilterValid', -> filtered -angular.module("admin.productImport").filter 'entriesFilterSupplier', -> - (entries, supplier) -> - if supplier == 'all' +angular.module("admin.productImport").filter 'entriesFilterEnterprise', -> + (entries, enterprise) -> + if enterprise == 'all' return entries filtered = {} angular.forEach entries, (entry, line_number) -> - if supplier == entry.attributes['supplier'] + if enterprise == entry.attributes['enterprise'] filtered[line_number] = entry filtered diff --git a/app/assets/javascripts/admin/product_import/services/product_import_service.js.coffee b/app/assets/javascripts/admin/product_import/services/product_import_service.js.coffee index af0f464df1..045438f32e 100644 --- a/app/assets/javascripts/admin/product_import/services/product_import_service.js.coffee +++ b/app/assets/javascripts/admin/product_import/services/product_import_service.js.coffee @@ -1,15 +1,15 @@ angular.module("admin.productImport").factory "ProductImportService", ($rootScope) -> new class ProductImportService - suppliers: {} + enterprises: {} resetTotal: 0 settings: {} - updateResetAbsent: (supplierId, resetCount, resetAbsent) -> + updateResetAbsent: (enterpriseId, resetCount, resetAbsent) -> if resetAbsent - @suppliers[supplierId] = resetCount + @enterprises[enterpriseId] = resetCount @resetTotal += resetCount else - @suppliers[supplierId] = null + @enterprises[enterpriseId] = null @resetTotal -= resetCount $rootScope.resetTotal = @resetTotal diff --git a/app/controllers/admin/product_import_controller.rb b/app/controllers/admin/product_import_controller.rb index e8ae8951e9..aa55fac115 100644 --- a/app/controllers/admin/product_import_controller.rb +++ b/app/controllers/admin/product_import_controller.rb @@ -90,7 +90,7 @@ module Admin { filepath: @filepath, item_count: @importer.item_count, - supplier_product_counts: @importer.supplier_products, + enterprise_product_counts: @importer.enterprise_products, import_url: main_app.admin_product_import_process_async_path, save_url: main_app.admin_product_import_save_async_path, reset_url: main_app.admin_product_import_reset_async_path, diff --git a/app/models/product_import/entry_processor.rb b/app/models/product_import/entry_processor.rb index 22bae53907..66c0d40d8f 100644 --- a/app/models/product_import/entry_processor.rb +++ b/app/models/product_import/entry_processor.rb @@ -5,8 +5,8 @@ module ProductImport class EntryProcessor attr_reader :inventory_created, :inventory_updated, :products_created, - :variants_created, :variants_updated, :supplier_products, - :total_supplier_products, :products_reset_count + :variants_created, :variants_updated, :enterprise_products, + :total_enterprise_products, :products_reset_count def initialize(importer, validator, import_settings, spreadsheet_data, editable_enterprises, import_time, updated_ids) @importer = importer @@ -23,8 +23,8 @@ module ProductImport @variants_created = 0 @variants_updated = 0 @products_reset_count = 0 - @supplier_products = {} - @total_supplier_products = 0 + @enterprise_products = {} + @total_enterprise_products = 0 end def save_all(entries) @@ -40,24 +40,24 @@ module ProductImport end def count_existing_items - @spreadsheet_data.suppliers_index.each do |_supplier_name, attrs| - supplier_id = attrs[:id] - next unless supplier_id && permission_by_id?(supplier_id) + @spreadsheet_data.enterprises_index.each do |_enterprise_name, attrs| + enterprise_id = attrs[:id] + next unless enterprise_id && permission_by_id?(enterprise_id) products_count = if settings.importing_into_inventory? - VariantOverride.where('variant_overrides.hub_id IN (?)', supplier_id).count + VariantOverride.where('variant_overrides.hub_id IN (?)', enterprise_id).count else Spree::Variant. not_deleted. not_master. joins(:product). - where('spree_products.supplier_id IN (?)', supplier_id). + where('spree_products.supplier_id IN (?)', enterprise_id). count end - @supplier_products[supplier_id] = products_count - @total_supplier_products += products_count + @enterprise_products[enterprise_id] = products_count + @total_enterprise_products += products_count end end @@ -88,8 +88,8 @@ module ProductImport @products_created + @variants_created + @variants_updated + @inventory_created + @inventory_updated end - def permission_by_id?(supplier_id) - @editable_enterprises.value?(Integer(supplier_id)) + def permission_by_id?(enterprise_id) + @editable_enterprises.value?(Integer(enterprise_id)) end private @@ -122,7 +122,7 @@ module ProductImport end def import_into_inventory?(entry) - entry.supplier_id && settings.importing_into_inventory? + entry.enterprise_id && settings.importing_into_inventory? end def save_new_inventory_item(entry) @@ -158,8 +158,8 @@ module ProductImport # If we've already added a new product with these attributes # from this spreadsheet, mark this entry as a new variant with # the new product id, as this is a now variant of that product... - if @already_created[entry.supplier_id] && @already_created[entry.supplier_id][entry.name] - product_id = @already_created[entry.supplier_id][entry.name] + if @already_created[entry.enterprise_id] && @already_created[entry.enterprise_id][entry.name] + product_id = @already_created[entry.enterprise_id][entry.name] @validator.mark_as_new_variant(entry, product_id) return end @@ -176,7 +176,7 @@ module ProductImport @importer.errors.add("#{I18n.t('admin.product_import.model.line')} #{line_number}:", product.errors.full_messages) end - @already_created[entry.supplier_id] = { entry.name => product.id } + @already_created[entry.enterprise_id] = { entry.name => product.id } end def save_variant(entry) diff --git a/app/models/product_import/entry_validator.rb b/app/models/product_import/entry_validator.rb index 700c1e4308..a4574bffda 100644 --- a/app/models/product_import/entry_validator.rb +++ b/app/models/product_import/entry_validator.rb @@ -27,10 +27,10 @@ module ProductImport def validate_all(entries) entries.each do |entry| - supplier_validation(entry) + enterprise_validation(entry) unit_fields_validation(entry) - next if entry.supplier_id.blank? + next if entry.enterprise_id.blank? if import_into_inventory? producer_validation(entry) @@ -59,37 +59,37 @@ module ProductImport private - def supplier_validation(entry) + def enterprise_validation(entry) return if name_presence_error entry return if enterprise_not_found_error entry return if permissions_error entry return if primary_producer_error entry - entry.supplier_id = @spreadsheet_data.suppliers_index[entry.supplier][:id] + entry.enterprise_id = @spreadsheet_data.enterprises_index[entry.enterprise][:id] end def name_presence_error(entry) - return if entry.supplier.present? - mark_as_invalid(entry, attribute: "supplier", error: I18n.t(:error_required)) + return if entry.enterprise.present? + mark_as_invalid(entry, attribute: "enterprise", error: I18n.t(:error_required)) true end def enterprise_not_found_error(entry) - return if @spreadsheet_data.suppliers_index[entry.supplier][:id] - mark_as_invalid(entry, attribute: "supplier", error: I18n.t(:error_not_found_in_database, name: entry.supplier)) + return if @spreadsheet_data.enterprises_index[entry.enterprise][:id] + mark_as_invalid(entry, attribute: "enterprise", error: I18n.t(:error_not_found_in_database, name: entry.enterprise)) true end def permissions_error(entry) - return if permission_by_name?(entry.supplier) - mark_as_invalid(entry, attribute: "supplier", error: I18n.t(:error_no_permission_for_enterprise, name: entry.supplier)) + return if permission_by_name?(entry.enterprise) + mark_as_invalid(entry, attribute: "enterprise", error: I18n.t(:error_no_permission_for_enterprise, name: entry.enterprise)) true end def primary_producer_error(entry) return if import_into_inventory? - return if @spreadsheet_data.suppliers_index[entry.supplier][:is_primary_producer] - mark_as_invalid(entry, attribute: "supplier", error: I18n.t(:error_not_primary_producer, name: entry.supplier)) + return if @spreadsheet_data.enterprises_index[entry.enterprise][:is_primary_producer] + mark_as_invalid(entry, attribute: "enterprise", error: I18n.t(:error_not_primary_producer, name: entry.enterprise)) true end @@ -126,7 +126,7 @@ module ProductImport return end - unless inventory_permission?(entry.supplier_id, @spreadsheet_data.producers_index[producer_name]) + unless inventory_permission?(entry.enterprise_id, @spreadsheet_data.producers_index[producer_name]) mark_as_invalid(entry, attribute: "producer", error: "\"#{producer_name}\": #{I18n.t('admin.product_import.model.inventory_no_permission')}") return end @@ -186,7 +186,7 @@ module ProductImport end def product_validation(entry) - products = Spree::Product.where(supplier_id: entry.supplier_id, name: entry.name, deleted_at: nil) + products = Spree::Product.where(supplier_id: entry.enterprise_id, name: entry.name, deleted_at: nil) if products.empty? mark_as_new_product(entry) @@ -222,7 +222,7 @@ module ProductImport if existing_variant.valid? entry.product_object = existing_variant entry.validates_as = 'existing_variant' unless entry.errors? - updates_count_per_supplier(entry.supplier_id) unless entry.errors? + updates_count_per_enterprise(entry.enterprise_id) unless entry.errors? else mark_as_invalid(entry, product_validations: existing_variant.errors) end @@ -243,16 +243,16 @@ module ProductImport existing_product.public_send(attribute).blank? && entry.public_send(attribute).blank? end - def permission_by_name?(supplier_name) - @editable_enterprises.key?(supplier_name) + def permission_by_name?(enterprise_name) + @editable_enterprises.key?(enterprise_name) end - def permission_by_id?(supplier_id) - @editable_enterprises.value?(Integer(supplier_id)) + def permission_by_id?(enterprise_id) + @editable_enterprises.value?(Integer(enterprise_id)) end - def inventory_permission?(supplier_id, producer_id) - @current_user.admin? || ( @inventory_permissions[supplier_id] && @inventory_permissions[supplier_id].include?(producer_id) ) + def inventory_permission?(enterprise_id, producer_id) + @current_user.admin? || ( @inventory_permissions[enterprise_id] && @inventory_permissions[enterprise_id].include?(producer_id) ) end def mark_as_invalid(entry, options = {}) @@ -273,9 +273,9 @@ module ProductImport end def create_inventory_item(entry, existing_variant) - existing_variant_override = VariantOverride.where(variant_id: existing_variant.id, hub_id: entry.supplier_id).first + existing_variant_override = VariantOverride.where(variant_id: existing_variant.id, hub_id: entry.enterprise_id).first - variant_override = existing_variant_override || VariantOverride.new(variant_id: existing_variant.id, hub_id: entry.supplier_id) + variant_override = existing_variant_override || VariantOverride.new(variant_id: existing_variant.id, hub_id: entry.enterprise_id) variant_override.assign_attributes(count_on_hand: entry.on_hand, import_date: @import_time) check_on_hand_nil(entry, variant_override) variant_override.assign_attributes(entry.attributes.slice('price', 'on_demand')) @@ -287,18 +287,18 @@ module ProductImport if variant_override.id entry.validates_as = 'existing_inventory_item' entry.product_object = variant_override - updates_count_per_supplier(entry.supplier_id) unless entry.errors? + updates_count_per_enterprise(entry.enterprise_id) unless entry.errors? else entry.validates_as = 'new_inventory_item' entry.product_object = variant_override end end - def updates_count_per_supplier(supplier_id) - if @reset_counts[supplier_id] && @reset_counts[supplier_id][:updates_count] - @reset_counts[supplier_id][:updates_count] += 1 + def updates_count_per_enterprise(enterprise_id) + if @reset_counts[enterprise_id] && @reset_counts[enterprise_id][:updates_count] + @reset_counts[enterprise_id][:updates_count] += 1 else - @reset_counts[supplier_id] = { updates_count: 1 } + @reset_counts[enterprise_id] = { updates_count: 1 } end end diff --git a/app/models/product_import/product_importer.rb b/app/models/product_import/product_importer.rb index 6cabcc9e2f..87ae50f73b 100644 --- a/app/models/product_import/product_importer.rb +++ b/app/models/product_import/product_importer.rb @@ -65,27 +65,27 @@ module ProductImport end def reset_counts - # Return indexed data about existing product count, reset count, and updates count per supplier - @reset_counts.each do |supplier_id, values| + # Return indexed data about existing product count, reset count, and updates count per enterprise + @reset_counts.each do |enterprise_id, values| values[:updates_count] = 0 if values[:updates_count].blank? if values[:updates_count] && values[:existing_products] - @reset_counts[supplier_id][:reset_count] = values[:existing_products] - values[:updates_count] + @reset_counts[enterprise_id][:reset_count] = values[:existing_products] - values[:updates_count] end end @reset_counts end - def suppliers_index - @spreadsheet_data.suppliers_index + def enterprises_index + @spreadsheet_data.enterprises_index end - def supplier_products - @processor.andand.supplier_products + def enterprise_products + @processor.andand.enterprise_products end - def total_supplier_products - @processor.total_supplier_products + def total_enterprise_products + @processor.total_enterprise_products end def all_entries @@ -165,8 +165,8 @@ module ProductImport @processor.reset_absent_items end - def permission_by_id?(supplier_id) - @editable_enterprises.value?(Integer(supplier_id)) + def permission_by_id?(enterprise_id) + @editable_enterprises.value?(Integer(enterprise_id)) end private diff --git a/app/models/product_import/settings.rb b/app/models/product_import/settings.rb index 4a0df965cb..c0028138f8 100644 --- a/app/models/product_import/settings.rb +++ b/app/models/product_import/settings.rb @@ -6,8 +6,8 @@ module ProductImport def defaults(entry) @import_settings.key?(:settings) && - settings[entry.supplier_id.to_s] && - settings[entry.supplier_id.to_s]['defaults'] + settings[entry.enterprise_id.to_s] && + settings[entry.enterprise_id.to_s]['defaults'] end def settings diff --git a/app/models/product_import/spreadsheet_data.rb b/app/models/product_import/spreadsheet_data.rb index 04203606b0..de4dc65ab4 100644 --- a/app/models/product_import/spreadsheet_data.rb +++ b/app/models/product_import/spreadsheet_data.rb @@ -11,8 +11,8 @@ module ProductImport @entries = entries end - def suppliers_index - @suppliers_index || create_suppliers_index + def enterprises_index + @enterprises_index || create_enterprises_index end def producers_index @@ -33,15 +33,15 @@ module ProductImport private - def create_suppliers_index - @suppliers_index = {} + def create_enterprises_index + @enterprises_index = {} @entries.each do |entry| - supplier_name = entry.supplier - next if @suppliers_index.key? supplier_name - enterprise = Enterprise.find_by_name(supplier_name, select: 'id, name, is_primary_producer') - @suppliers_index[supplier_name] = { id: enterprise.try(:id), is_primary_producer: enterprise.try(:is_primary_producer) } + enterprise_name = entry.enterprise + next if @enterprises_index.key? enterprise_name + enterprise = Enterprise.find_by_name(enterprise_name, select: 'id, name, is_primary_producer') + @enterprises_index[enterprise_name] = { id: enterprise.try(:id), is_primary_producer: enterprise.try(:is_primary_producer) } end - @suppliers_index + @enterprises_index end def create_producers_index diff --git a/app/models/product_import/spreadsheet_entry.rb b/app/models/product_import/spreadsheet_entry.rb index fed45a6645..f74e11e8b8 100644 --- a/app/models/product_import/spreadsheet_entry.rb +++ b/app/models/product_import/spreadsheet_entry.rb @@ -11,7 +11,7 @@ module ProductImport attr_accessor :line_number, :valid, :validates_as, :product_object, :product_validations, :on_hand_nil, :has_overrides, :units, :unscaled_units, :unit_type, :tax_category, :shipping_category - attr_accessor :id, :product_id, :producer, :producer_id, :supplier, :supplier_id, :name, :display_name, :sku, + attr_accessor :id, :product_id, :producer, :producer_id, :enterprise, :enterprise_id, :name, :display_name, :sku, :unit_value, :unit_description, :variant_unit, :variant_unit_scale, :variant_unit_name, :display_as, :category, :primary_taxon_id, :price, :on_hand, :count_on_hand, :on_demand, :tax_category_id, :shipping_category_id, :description, :import_date @@ -77,7 +77,7 @@ module ProductImport end def non_display_attributes - ['id', 'product_id', 'unscaled_units', 'variant_id', 'supplier_id', 'primary_taxon', 'primary_taxon_id', 'category_id', 'shipping_category_id', 'tax_category_id', 'variant_unit_scale', 'variant_unit', 'unit_value'] + ['id', 'product_id', 'unscaled_units', 'variant_id', 'enterprise_id', 'primary_taxon', 'primary_taxon_id', 'category_id', 'shipping_category_id', 'tax_category_id', 'variant_unit_scale', 'variant_unit', 'unit_value'] end def non_product_attributes diff --git a/app/views/admin/product_import/_import_options.html.haml b/app/views/admin/product_import/_import_options.html.haml index 2debc9a86c..c5c3e32c38 100644 --- a/app/views/admin/product_import/_import_options.html.haml +++ b/app/views/admin/product_import/_import_options.html.haml @@ -1,7 +1,7 @@ %h5= t('admin.product_import.import.options_and_defaults') %br -- @importer.suppliers_index.each do |name, attrs| +- @importer.enterprises_index.each do |name, attrs| - if name and attrs[:id] and @importer.permission_by_id?(attrs[:id]) %div.panel-section.import-settings %div.panel-header{ng: {click: 'togglePanel()', class: '{active: active}'}} @@ -32,7 +32,7 @@ %i.fa.fa-warning %div.header-description = t('admin.product_import.import.no_name') - %span.header-error= " - #{t('admin.product_import.import.blank_supplier')}" + %span.header-error= " - #{t('admin.product_import.import.blank_enterprise')}" %br.panels.clearfix %br diff --git a/config/locales/en.yml b/config/locales/en.yml index ea6a746d97..a6fcd94028 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -554,7 +554,7 @@ en: no_permission: you do not have permission to manage this enterprise not_found: enterprise could not be found in database no_name: No name - blank_supplier: some products have blank supplier name + blank_enterprise: some products do not have an enterprise defined reset_absent?: Reset absent products reset_absent_tip: Set stock to zero for all exiting products not present in the file overwrite_all: Overwrite all From 5c2b5377bc4d636a9d32a4ffa48f23cfefb79e39 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 2 Oct 2018 13:15:18 +0100 Subject: [PATCH 07/65] Assign supplier_id when saving new product to products list --- app/models/product_import/entry_processor.rb | 1 + app/models/product_import/entry_validator.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/app/models/product_import/entry_processor.rb b/app/models/product_import/entry_processor.rb index 66c0d40d8f..68ecf0f19d 100644 --- a/app/models/product_import/entry_processor.rb +++ b/app/models/product_import/entry_processor.rb @@ -166,6 +166,7 @@ module ProductImport product = Spree::Product.new product.assign_attributes(entry.attributes.except('id')) + product.supplier_id = entry.enterprise_id assign_defaults(product, entry) if product.save diff --git a/app/models/product_import/entry_validator.rb b/app/models/product_import/entry_validator.rb index a4574bffda..71d1497da7 100644 --- a/app/models/product_import/entry_validator.rb +++ b/app/models/product_import/entry_validator.rb @@ -207,6 +207,7 @@ module ProductImport def mark_as_new_product(entry) new_product = Spree::Product.new new_product.assign_attributes(entry.attributes.except('id')) + new_product.supplier_id = entry.enterprise_id if new_product.valid? entry.validates_as = 'new_product' unless entry.errors? From 2c9dc8899a1e58f9e2f1b655839213612a3b7a95 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 2 Oct 2018 13:16:55 +0100 Subject: [PATCH 08/65] Update fields in specs --- spec/features/admin/product_import_spec.rb | 16 +++++----- spec/models/product_importer_spec.rb | 36 +++++++++++----------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/spec/features/admin/product_import_spec.rb b/spec/features/admin/product_import_spec.rb index 4d3480d32d..5fb763c918 100644 --- a/spec/features/admin/product_import_spec.rb +++ b/spec/features/admin/product_import_spec.rb @@ -33,7 +33,7 @@ feature "Product Import", js: true do it "validates entries and saves them if they are all valid and allows viewing new items in Bulk Products" do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] csv << ["Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g"] csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "6.50", "1", "kg"] end @@ -76,7 +76,7 @@ feature "Product Import", js: true do it "displays info about invalid entries but no save button if all items are invalid" do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] csv << ["Bad Carrots", "Unkown Enterprise", "Mouldy vegetables", "666", "3.20", "", "g"] csv << ["Bad Potatoes", "", "Vegetables", "6", "6", "6", ""] end @@ -100,7 +100,7 @@ feature "Product Import", js: true do it "handles saving of named tax and shipping categories" do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type", "tax_category", "shipping_category"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "tax_category", "shipping_category"] csv << ["Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g", tax_category.name, shipping_category.name] end File.write('/tmp/test.csv', csv_data) @@ -129,7 +129,7 @@ feature "Product Import", js: true do it "records a timestamp on import that can be viewed and filtered under Bulk Edit Products" do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] csv << ["Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g"] csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "6.50", "1", "kg"] end @@ -176,7 +176,7 @@ feature "Product Import", js: true do it "can reset product stock to zero for products not present in the CSV" do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] csv << ["Carrots", "User Enterprise", "Vegetables", "500", "3.20", "500", "g"] end File.write('/tmp/test.csv', csv_data) @@ -203,7 +203,7 @@ feature "Product Import", js: true do it "can save a new product and variant of that product at the same time, add variant to existing product" do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type", "display_name"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "display_name"] csv << ["Potatoes", "User Enterprise", "Vegetables", "5", "3.50", "500", "g", "Small Bag"] csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "5.50", "2", "kg", "Big Bag"] csv << ["Beans", "User Enterprise", "Vegetables", "7", "2.50", "250", "g", nil] @@ -241,7 +241,7 @@ feature "Product Import", js: true do it "can import items into inventory" do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "producer", "category", "on_hand", "price", "units"] + csv << ["name", "enterprise", "producer", "category", "on_hand", "price", "units"] csv << ["Beans", "Another Enterprise", "User Enterprise", "Vegetables", "5", "3.20", "500"] csv << ["Sprouts", "Another Enterprise", "User Enterprise", "Vegetables", "6", "6.50", "500"] csv << ["Cabbage", "Another Enterprise", "User Enterprise", "Vegetables", "2001", "1.50", "500"] @@ -338,7 +338,7 @@ feature "Product Import", js: true do it "only allows product import into enterprises the user is permitted to manage" do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] csv << ["My Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g"] csv << ["Your Potatoes", "Another Enterprise", "Vegetables", "6", "6.50", "1", "kg"] end diff --git a/spec/models/product_importer_spec.rb b/spec/models/product_importer_spec.rb index 538532471e..efd87d434e 100644 --- a/spec/models/product_importer_spec.rb +++ b/spec/models/product_importer_spec.rb @@ -45,7 +45,7 @@ describe ProductImport::ProductImporter do describe "importing products from a spreadsheet" do before do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type", "variant_unit_name", "on_demand"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "variant_unit_name", "on_demand"] csv << ["Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g", "", ""] csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "6.50", "2", "kg", "", ""] csv << ["Pea Soup", "User Enterprise", "Vegetables", "8", "5.50", "750", "ml", "", "0"] @@ -135,7 +135,7 @@ describe ProductImport::ProductImporter do describe "when uploading a spreadsheet with some invalid entries" do before do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] csv << ["Good Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g"] csv << ["Bad Potatoes", "", "Vegetables", "6", "6.50", "1", ""] end @@ -176,7 +176,7 @@ describe ProductImport::ProductImporter do describe "when enterprises are not valid" do before do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] csv << ["Product 1", "Non-existent Enterprise", "Vegetables", "5", "5.50", "500", "g"] csv << ["Product 2", "Non-Producer", "Vegetables", "5", "5.50", "500", "g"] end @@ -191,15 +191,15 @@ describe ProductImport::ProductImporter do @importer.validate_entries entries = JSON.parse(@importer.entries_json) - expect(entries['2']['errors']['supplier']).to include "not found in database" - expect(entries['3']['errors']['supplier']).to include "not enabled as a producer" + expect(entries['2']['errors']['enterprise']).to include "not found in database" + expect(entries['3']['errors']['enterprise']).to include "not enabled as a producer" end end describe "adding new variants to existing products and updating exiting products" do before do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type", "display_name"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "display_name"] csv << ["Hypothetical Cake", "Another Enterprise", "Cake", "5", "5.50", "500", "g", "Preexisting Banana"] csv << ["Hypothetical Cake", "Another Enterprise", "Cake", "6", "3.50", "500", "g", "Emergent Coffee"] end @@ -245,7 +245,7 @@ describe ProductImport::ProductImporter do describe "adding new product and sub-variant at the same time" do before do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type", "display_name"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "display_name"] csv << ["Potatoes", "User Enterprise", "Vegetables", "5", "3.50", "500", "g", "Small Bag"] csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "5.50", "2", "kg", "Big Bag"] end @@ -289,7 +289,7 @@ describe ProductImport::ProductImporter do describe "updating various fields" do before do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type", "on_demand", "sku"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "on_demand", "sku"] csv << ["Beetroot", "And Another Enterprise", "Vegetables", "5", "3.50", "500", "g", "0", nil] csv << ["Tomato", "And Another Enterprise", "Vegetables", "6", "5.50", "500", "g", "1", "TOMS"] end @@ -331,7 +331,7 @@ describe ProductImport::ProductImporter do describe "updating non-updatable fields on existing products" do before do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] csv << ["Beetroot", "And Another Enterprise", "Meat", "5", "3.50", "500", "g"] csv << ["Tomato", "And Another Enterprise", "Vegetables", "6", "5.50", "500", "Kg"] end @@ -358,7 +358,7 @@ describe ProductImport::ProductImporter do describe "when more than one product of the same name already exists with multiple variants each" do before do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "description", "on_hand", "price", "units", "unit_type", "display_name"] + csv << ["name", "enterprise", "category", "description", "on_hand", "price", "units", "unit_type", "display_name"] csv << ["Oats", "User Enterprise", "Cereal", "", "50", "3.50", "500", "g", "Rolled Oats"] # Update csv << ["Oats", "User Enterprise", "Cereal", "", "80", "3.75", "500", "g", "Flaked Oats"] # Update csv << ["Oats", "User Enterprise", "Cereal", "", "60", "5.50", "500", "g", "Magic Oats"] # Add @@ -398,7 +398,7 @@ describe ProductImport::ProductImporter do describe "when importer processes create and update across multiple stages" do before do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type", "display_name"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "display_name"] csv << ["Bag of Oats", "User Enterprise", "Cereal", "60", "5.50", "500", "g", "Magic Oats"] # Add csv << ["Bag of Oats", "User Enterprise", "Cereal", "70", "8.50", "500", "g", "French Oats"] # Add csv << ["Bag of Oats", "User Enterprise", "Cereal", "80", "9.50", "500", "g", "Organic Oats"] # Add @@ -467,7 +467,7 @@ describe ProductImport::ProductImporter do describe "importing items into inventory" do before do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "producer", "on_hand", "price", "units", "unit_type"] + csv << ["name", "enterprise", "producer", "on_hand", "price", "units", "unit_type"] csv << ["Beans", "Another Enterprise", "User Enterprise", "5", "3.20", "500", "g"] csv << ["Sprouts", "Another Enterprise", "User Enterprise", "6", "6.50", "500", "g"] csv << ["Cabbage", "Another Enterprise", "User Enterprise", "2001", "1.50", "500", "g"] @@ -517,7 +517,7 @@ describe ProductImport::ProductImporter do it "only allows product import into enterprises the user is permitted to manage" do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] csv << ["My Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g"] csv << ["Your Potatoes", "Another Enterprise", "Vegetables", "6", "6.50", "1", "kg"] end @@ -545,7 +545,7 @@ describe ProductImport::ProductImporter do it "allows creating inventories for producers that a user's hub has permission for" do csv_data = CSV.generate do |csv| - csv << ["name", "producer", "supplier", "on_hand", "price", "units", "unit_type"] + csv << ["name", "producer", "enterprise", "on_hand", "price", "units", "unit_type"] csv << ["Beans", "User Enterprise", "Another Enterprise", "777", "3.20", "500", "g"] end File.write('/tmp/test-m.csv', csv_data) @@ -572,7 +572,7 @@ describe ProductImport::ProductImporter do it "does not allow creating inventories for producers that a user's hubs don't have permission for" do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "on_hand", "price", "units", "unit_type"] + csv << ["name", "enterprise", "on_hand", "price", "units", "unit_type"] csv << ["Beans", "User Enterprise", "5", "3.20", "500", "g"] csv << ["Sprouts", "User Enterprise", "6", "6.50", "500", "g"] end @@ -601,7 +601,7 @@ describe ProductImport::ProductImporter do it "can reset all products for an enterprise that are not present in the uploaded file to zero stock" do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] csv << ["Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g"] csv << ["Beans", "User Enterprise", "Vegetables", "6", "6.50", "500", "g"] end @@ -640,7 +640,7 @@ describe ProductImport::ProductImporter do it "can reset all inventory items for an enterprise that are not present in the uploaded file to zero stock" do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "producer", "on_hand", "price", "units", "unit_type"] + csv << ["name", "enterprise", "producer", "on_hand", "price", "units", "unit_type"] csv << ["Beans", "Another Enterprise", "User Enterprise", "6", "3.20", "500", "g"] csv << ["Sprouts", "Another Enterprise", "User Enterprise", "7", "6.50", "500", "g"] end @@ -681,7 +681,7 @@ describe ProductImport::ProductImporter do it "can overwrite fields with selected defaults when importing to product list" do csv_data = CSV.generate do |csv| - csv << ["name", "supplier", "category", "on_hand", "price", "units", "unit_type", "tax_category_id", "available_on"] + csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "tax_category_id", "available_on"] csv << ["Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g", tax_category.id, ""] csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "6.50", "1", "kg", "", ""] end From 1c85de6f5835fd6a025e0cfb25a8f7e02117e560 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 2 Oct 2018 15:06:37 +0100 Subject: [PATCH 09/65] Use producer and distributor for field names --- app/models/product_import/entry_processor.rb | 2 +- app/models/product_import/entry_validator.rb | 22 ++++++++---- app/models/product_import/product_importer.rb | 2 +- app/models/product_import/spreadsheet_data.rb | 9 +++-- .../product_import/spreadsheet_entry.rb | 6 ++-- spec/features/admin/product_import_spec.rb | 16 ++++----- spec/models/product_importer_spec.rb | 36 +++++++++---------- 7 files changed, 54 insertions(+), 39 deletions(-) diff --git a/app/models/product_import/entry_processor.rb b/app/models/product_import/entry_processor.rb index 68ecf0f19d..f3c5b0a8e5 100644 --- a/app/models/product_import/entry_processor.rb +++ b/app/models/product_import/entry_processor.rb @@ -166,7 +166,7 @@ module ProductImport product = Spree::Product.new product.assign_attributes(entry.attributes.except('id')) - product.supplier_id = entry.enterprise_id + product.supplier_id = entry.producer_id assign_defaults(product, entry) if product.save diff --git a/app/models/product_import/entry_validator.rb b/app/models/product_import/entry_validator.rb index 71d1497da7..c53b4fbe2c 100644 --- a/app/models/product_import/entry_validator.rb +++ b/app/models/product_import/entry_validator.rb @@ -27,6 +27,7 @@ module ProductImport def validate_all(entries) entries.each do |entry| + assign_enterprise_field(entry) enterprise_validation(entry) unit_fields_validation(entry) @@ -44,6 +45,14 @@ module ProductImport end end + def assign_enterprise_field(entry) + entry.enterprise = entry.public_send(enterprise_field) + end + + def enterprise_field + import_into_inventory? ? :distributor : :producer + end + def mark_as_new_variant(entry, product_id) new_variant = Spree::Variant.new(entry.attributes.except('id', 'product_id')) new_variant.product_id = product_id @@ -66,30 +75,31 @@ module ProductImport return if primary_producer_error entry entry.enterprise_id = @spreadsheet_data.enterprises_index[entry.enterprise][:id] + entry.public_send "#{enterprise_field}_id=", @spreadsheet_data.enterprises_index[entry.enterprise][:id] end def name_presence_error(entry) return if entry.enterprise.present? - mark_as_invalid(entry, attribute: "enterprise", error: I18n.t(:error_required)) + mark_as_invalid(entry, attribute: enterprise_field, error: I18n.t(:error_required)) true end def enterprise_not_found_error(entry) return if @spreadsheet_data.enterprises_index[entry.enterprise][:id] - mark_as_invalid(entry, attribute: "enterprise", error: I18n.t(:error_not_found_in_database, name: entry.enterprise)) + mark_as_invalid(entry, attribute: enterprise_field, error: I18n.t(:error_not_found_in_database, name: entry.enterprise)) true end def permissions_error(entry) return if permission_by_name?(entry.enterprise) - mark_as_invalid(entry, attribute: "enterprise", error: I18n.t(:error_no_permission_for_enterprise, name: entry.enterprise)) + mark_as_invalid(entry, attribute: enterprise_field, error: I18n.t(:error_no_permission_for_enterprise, name: entry.enterprise)) true end def primary_producer_error(entry) return if import_into_inventory? return if @spreadsheet_data.enterprises_index[entry.enterprise][:is_primary_producer] - mark_as_invalid(entry, attribute: "enterprise", error: I18n.t(:error_not_primary_producer, name: entry.enterprise)) + mark_as_invalid(entry, attribute: enterprise_field, error: I18n.t(:error_not_primary_producer, name: entry.enterprise)) true end @@ -207,7 +217,7 @@ module ProductImport def mark_as_new_product(entry) new_product = Spree::Product.new new_product.assign_attributes(entry.attributes.except('id')) - new_product.supplier_id = entry.enterprise_id + new_product.supplier_id = entry.producer_id if new_product.valid? entry.validates_as = 'new_product' unless entry.errors? @@ -262,7 +272,7 @@ module ProductImport end def import_into_inventory? - @import_settings[:settings]['import_into'] == 'inventories' + @import_settings[:settings] && @import_settings[:settings]['import_into'] == 'inventories' end def validate_inventory_item(entry, variant_override) diff --git a/app/models/product_import/product_importer.rb b/app/models/product_import/product_importer.rb index 87ae50f73b..35a490c8d6 100644 --- a/app/models/product_import/product_importer.rb +++ b/app/models/product_import/product_importer.rb @@ -180,7 +180,7 @@ module ProductImport build_entries end - @spreadsheet_data = SpreadsheetData.new(@entries) + @spreadsheet_data = SpreadsheetData.new(@entries, @import_settings) @validator = EntryValidator.new(@current_user, @import_time, @spreadsheet_data, @editable_enterprises, @inventory_permissions, @reset_counts, @import_settings) @processor = EntryProcessor.new(self, @validator, @import_settings, @spreadsheet_data, @editable_enterprises, @import_time, @updated_ids) diff --git a/app/models/product_import/spreadsheet_data.rb b/app/models/product_import/spreadsheet_data.rb index de4dc65ab4..8903944284 100644 --- a/app/models/product_import/spreadsheet_data.rb +++ b/app/models/product_import/spreadsheet_data.rb @@ -7,8 +7,9 @@ module ProductImport class SpreadsheetData - def initialize(entries) + def initialize(entries, import_settings) @entries = entries + @import_settings = import_settings end def enterprises_index @@ -33,10 +34,14 @@ module ProductImport private + def import_into_inventory? + @import_settings[:settings] && @import_settings[:settings]['import_into'] == 'inventories' + end + def create_enterprises_index @enterprises_index = {} @entries.each do |entry| - enterprise_name = entry.enterprise + enterprise_name = import_into_inventory? ? entry.distributor : entry.producer next if @enterprises_index.key? enterprise_name enterprise = Enterprise.find_by_name(enterprise_name, select: 'id, name, is_primary_producer') @enterprises_index[enterprise_name] = { id: enterprise.try(:id), is_primary_producer: enterprise.try(:is_primary_producer) } diff --git a/app/models/product_import/spreadsheet_entry.rb b/app/models/product_import/spreadsheet_entry.rb index f74e11e8b8..dcfb35e65f 100644 --- a/app/models/product_import/spreadsheet_entry.rb +++ b/app/models/product_import/spreadsheet_entry.rb @@ -11,10 +11,10 @@ module ProductImport attr_accessor :line_number, :valid, :validates_as, :product_object, :product_validations, :on_hand_nil, :has_overrides, :units, :unscaled_units, :unit_type, :tax_category, :shipping_category - attr_accessor :id, :product_id, :producer, :producer_id, :enterprise, :enterprise_id, :name, :display_name, :sku, + attr_accessor :id, :product_id, :producer, :producer_id, :distributor, :distributor_id, :name, :display_name, :sku, :unit_value, :unit_description, :variant_unit, :variant_unit_scale, :variant_unit_name, :display_as, :category, :primary_taxon_id, :price, :on_hand, :count_on_hand, :on_demand, - :tax_category_id, :shipping_category_id, :description, :import_date + :tax_category_id, :shipping_category_id, :description, :import_date, :enterprise, :enterprise_id def initialize(attrs) @validates_as = '' @@ -77,7 +77,7 @@ module ProductImport end def non_display_attributes - ['id', 'product_id', 'unscaled_units', 'variant_id', 'enterprise_id', 'primary_taxon', 'primary_taxon_id', 'category_id', 'shipping_category_id', 'tax_category_id', 'variant_unit_scale', 'variant_unit', 'unit_value'] + ['id', 'product_id', 'unscaled_units', 'variant_id', 'enterprise', 'enterprise_id', 'producer_id', 'distributor_id', 'primary_taxon', 'primary_taxon_id', 'category_id', 'shipping_category_id', 'tax_category_id', 'variant_unit_scale', 'variant_unit', 'unit_value'] end def non_product_attributes diff --git a/spec/features/admin/product_import_spec.rb b/spec/features/admin/product_import_spec.rb index 5fb763c918..3a337d93dd 100644 --- a/spec/features/admin/product_import_spec.rb +++ b/spec/features/admin/product_import_spec.rb @@ -33,7 +33,7 @@ feature "Product Import", js: true do it "validates entries and saves them if they are all valid and allows viewing new items in Bulk Products" do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type"] csv << ["Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g"] csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "6.50", "1", "kg"] end @@ -76,7 +76,7 @@ feature "Product Import", js: true do it "displays info about invalid entries but no save button if all items are invalid" do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type"] csv << ["Bad Carrots", "Unkown Enterprise", "Mouldy vegetables", "666", "3.20", "", "g"] csv << ["Bad Potatoes", "", "Vegetables", "6", "6", "6", ""] end @@ -100,7 +100,7 @@ feature "Product Import", js: true do it "handles saving of named tax and shipping categories" do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "tax_category", "shipping_category"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type", "tax_category", "shipping_category"] csv << ["Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g", tax_category.name, shipping_category.name] end File.write('/tmp/test.csv', csv_data) @@ -129,7 +129,7 @@ feature "Product Import", js: true do it "records a timestamp on import that can be viewed and filtered under Bulk Edit Products" do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type"] csv << ["Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g"] csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "6.50", "1", "kg"] end @@ -176,7 +176,7 @@ feature "Product Import", js: true do it "can reset product stock to zero for products not present in the CSV" do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type"] csv << ["Carrots", "User Enterprise", "Vegetables", "500", "3.20", "500", "g"] end File.write('/tmp/test.csv', csv_data) @@ -203,7 +203,7 @@ feature "Product Import", js: true do it "can save a new product and variant of that product at the same time, add variant to existing product" do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "display_name"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type", "display_name"] csv << ["Potatoes", "User Enterprise", "Vegetables", "5", "3.50", "500", "g", "Small Bag"] csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "5.50", "2", "kg", "Big Bag"] csv << ["Beans", "User Enterprise", "Vegetables", "7", "2.50", "250", "g", nil] @@ -241,7 +241,7 @@ feature "Product Import", js: true do it "can import items into inventory" do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "producer", "category", "on_hand", "price", "units"] + csv << ["name", "distributor", "producer", "category", "on_hand", "price", "units"] csv << ["Beans", "Another Enterprise", "User Enterprise", "Vegetables", "5", "3.20", "500"] csv << ["Sprouts", "Another Enterprise", "User Enterprise", "Vegetables", "6", "6.50", "500"] csv << ["Cabbage", "Another Enterprise", "User Enterprise", "Vegetables", "2001", "1.50", "500"] @@ -338,7 +338,7 @@ feature "Product Import", js: true do it "only allows product import into enterprises the user is permitted to manage" do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type"] csv << ["My Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g"] csv << ["Your Potatoes", "Another Enterprise", "Vegetables", "6", "6.50", "1", "kg"] end diff --git a/spec/models/product_importer_spec.rb b/spec/models/product_importer_spec.rb index efd87d434e..f05816e538 100644 --- a/spec/models/product_importer_spec.rb +++ b/spec/models/product_importer_spec.rb @@ -45,7 +45,7 @@ describe ProductImport::ProductImporter do describe "importing products from a spreadsheet" do before do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "variant_unit_name", "on_demand"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type", "variant_unit_name", "on_demand"] csv << ["Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g", "", ""] csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "6.50", "2", "kg", "", ""] csv << ["Pea Soup", "User Enterprise", "Vegetables", "8", "5.50", "750", "ml", "", "0"] @@ -135,7 +135,7 @@ describe ProductImport::ProductImporter do describe "when uploading a spreadsheet with some invalid entries" do before do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type"] csv << ["Good Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g"] csv << ["Bad Potatoes", "", "Vegetables", "6", "6.50", "1", ""] end @@ -176,7 +176,7 @@ describe ProductImport::ProductImporter do describe "when enterprises are not valid" do before do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type"] csv << ["Product 1", "Non-existent Enterprise", "Vegetables", "5", "5.50", "500", "g"] csv << ["Product 2", "Non-Producer", "Vegetables", "5", "5.50", "500", "g"] end @@ -191,15 +191,15 @@ describe ProductImport::ProductImporter do @importer.validate_entries entries = JSON.parse(@importer.entries_json) - expect(entries['2']['errors']['enterprise']).to include "not found in database" - expect(entries['3']['errors']['enterprise']).to include "not enabled as a producer" + expect(entries['2']['errors']['producer']).to include "not found in database" + expect(entries['3']['errors']['producer']).to include "not enabled as a producer" end end describe "adding new variants to existing products and updating exiting products" do before do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "display_name"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type", "display_name"] csv << ["Hypothetical Cake", "Another Enterprise", "Cake", "5", "5.50", "500", "g", "Preexisting Banana"] csv << ["Hypothetical Cake", "Another Enterprise", "Cake", "6", "3.50", "500", "g", "Emergent Coffee"] end @@ -245,7 +245,7 @@ describe ProductImport::ProductImporter do describe "adding new product and sub-variant at the same time" do before do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "display_name"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type", "display_name"] csv << ["Potatoes", "User Enterprise", "Vegetables", "5", "3.50", "500", "g", "Small Bag"] csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "5.50", "2", "kg", "Big Bag"] end @@ -289,7 +289,7 @@ describe ProductImport::ProductImporter do describe "updating various fields" do before do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "on_demand", "sku"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type", "on_demand", "sku"] csv << ["Beetroot", "And Another Enterprise", "Vegetables", "5", "3.50", "500", "g", "0", nil] csv << ["Tomato", "And Another Enterprise", "Vegetables", "6", "5.50", "500", "g", "1", "TOMS"] end @@ -331,7 +331,7 @@ describe ProductImport::ProductImporter do describe "updating non-updatable fields on existing products" do before do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type"] csv << ["Beetroot", "And Another Enterprise", "Meat", "5", "3.50", "500", "g"] csv << ["Tomato", "And Another Enterprise", "Vegetables", "6", "5.50", "500", "Kg"] end @@ -358,7 +358,7 @@ describe ProductImport::ProductImporter do describe "when more than one product of the same name already exists with multiple variants each" do before do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "description", "on_hand", "price", "units", "unit_type", "display_name"] + csv << ["name", "producer", "category", "description", "on_hand", "price", "units", "unit_type", "display_name"] csv << ["Oats", "User Enterprise", "Cereal", "", "50", "3.50", "500", "g", "Rolled Oats"] # Update csv << ["Oats", "User Enterprise", "Cereal", "", "80", "3.75", "500", "g", "Flaked Oats"] # Update csv << ["Oats", "User Enterprise", "Cereal", "", "60", "5.50", "500", "g", "Magic Oats"] # Add @@ -398,7 +398,7 @@ describe ProductImport::ProductImporter do describe "when importer processes create and update across multiple stages" do before do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "display_name"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type", "display_name"] csv << ["Bag of Oats", "User Enterprise", "Cereal", "60", "5.50", "500", "g", "Magic Oats"] # Add csv << ["Bag of Oats", "User Enterprise", "Cereal", "70", "8.50", "500", "g", "French Oats"] # Add csv << ["Bag of Oats", "User Enterprise", "Cereal", "80", "9.50", "500", "g", "Organic Oats"] # Add @@ -467,7 +467,7 @@ describe ProductImport::ProductImporter do describe "importing items into inventory" do before do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "producer", "on_hand", "price", "units", "unit_type"] + csv << ["name", "distributor", "producer", "on_hand", "price", "units", "unit_type"] csv << ["Beans", "Another Enterprise", "User Enterprise", "5", "3.20", "500", "g"] csv << ["Sprouts", "Another Enterprise", "User Enterprise", "6", "6.50", "500", "g"] csv << ["Cabbage", "Another Enterprise", "User Enterprise", "2001", "1.50", "500", "g"] @@ -517,7 +517,7 @@ describe ProductImport::ProductImporter do it "only allows product import into enterprises the user is permitted to manage" do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type"] csv << ["My Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g"] csv << ["Your Potatoes", "Another Enterprise", "Vegetables", "6", "6.50", "1", "kg"] end @@ -545,7 +545,7 @@ describe ProductImport::ProductImporter do it "allows creating inventories for producers that a user's hub has permission for" do csv_data = CSV.generate do |csv| - csv << ["name", "producer", "enterprise", "on_hand", "price", "units", "unit_type"] + csv << ["name", "producer", "distributor", "on_hand", "price", "units", "unit_type"] csv << ["Beans", "User Enterprise", "Another Enterprise", "777", "3.20", "500", "g"] end File.write('/tmp/test-m.csv', csv_data) @@ -572,7 +572,7 @@ describe ProductImport::ProductImporter do it "does not allow creating inventories for producers that a user's hubs don't have permission for" do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "on_hand", "price", "units", "unit_type"] + csv << ["name", "producer", "on_hand", "price", "units", "unit_type"] csv << ["Beans", "User Enterprise", "5", "3.20", "500", "g"] csv << ["Sprouts", "User Enterprise", "6", "6.50", "500", "g"] end @@ -601,7 +601,7 @@ describe ProductImport::ProductImporter do it "can reset all products for an enterprise that are not present in the uploaded file to zero stock" do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type"] csv << ["Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g"] csv << ["Beans", "User Enterprise", "Vegetables", "6", "6.50", "500", "g"] end @@ -640,7 +640,7 @@ describe ProductImport::ProductImporter do it "can reset all inventory items for an enterprise that are not present in the uploaded file to zero stock" do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "producer", "on_hand", "price", "units", "unit_type"] + csv << ["name", "distributor", "producer", "on_hand", "price", "units", "unit_type"] csv << ["Beans", "Another Enterprise", "User Enterprise", "6", "3.20", "500", "g"] csv << ["Sprouts", "Another Enterprise", "User Enterprise", "7", "6.50", "500", "g"] end @@ -681,7 +681,7 @@ describe ProductImport::ProductImporter do it "can overwrite fields with selected defaults when importing to product list" do csv_data = CSV.generate do |csv| - csv << ["name", "enterprise", "category", "on_hand", "price", "units", "unit_type", "tax_category_id", "available_on"] + csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type", "tax_category_id", "available_on"] csv << ["Carrots", "User Enterprise", "Vegetables", "5", "3.20", "500", "g", tax_category.id, ""] csv << ["Potatoes", "User Enterprise", "Vegetables", "6", "6.50", "1", "kg", "", ""] end From bd4f0507cee458a51893600adf0c01bc8bcdc25d Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 8 Oct 2018 17:00:55 +0100 Subject: [PATCH 10/65] Adjustments for new Settings and ResetAbsent classes --- app/models/product_import/inventory_reset_strategy.rb | 10 +++++----- app/models/product_import/products_reset_strategy.rb | 10 +++++----- spec/models/product_import/settings_spec.rb | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/models/product_import/inventory_reset_strategy.rb b/app/models/product_import/inventory_reset_strategy.rb index c22cb6218a..a8647ce250 100644 --- a/app/models/product_import/inventory_reset_strategy.rb +++ b/app/models/product_import/inventory_reset_strategy.rb @@ -4,10 +4,10 @@ module ProductImport @excluded_items_ids = excluded_items_ids end - def reset(supplier_ids) - @supplier_ids = supplier_ids + def reset(enterprise_ids) + @enterprise_ids = enterprise_ids - if supplier_ids.present? + if enterprise_ids.present? relation.update_all(count_on_hand: 0) else 0 @@ -16,10 +16,10 @@ module ProductImport private - attr_reader :excluded_items_ids, :supplier_ids + attr_reader :excluded_items_ids, :enterprise_ids def relation - relation = VariantOverride.where(hub_id: supplier_ids) + relation = VariantOverride.where(hub_id: enterprise_ids) return relation if excluded_items_ids.blank? relation.where('id NOT IN (?)', excluded_items_ids) diff --git a/app/models/product_import/products_reset_strategy.rb b/app/models/product_import/products_reset_strategy.rb index 80dd6a448c..c1ce9af695 100644 --- a/app/models/product_import/products_reset_strategy.rb +++ b/app/models/product_import/products_reset_strategy.rb @@ -4,10 +4,10 @@ module ProductImport @excluded_items_ids = excluded_items_ids end - def reset(supplier_ids) - @supplier_ids = supplier_ids + def reset(enterprise_ids) + @enterprise_ids = enterprise_ids - if supplier_ids.present? + if enterprise_ids.present? relation.update_all(count_on_hand: 0) else 0 @@ -16,13 +16,13 @@ module ProductImport private - attr_reader :excluded_items_ids, :supplier_ids + attr_reader :excluded_items_ids, :enterprise_ids def relation relation = Spree::Variant .joins(:product) .where( - spree_products: { supplier_id: supplier_ids }, + spree_products: { supplier_id: enterprise_ids }, spree_variants: { is_master: false, deleted_at: nil } ) diff --git a/spec/models/product_import/settings_spec.rb b/spec/models/product_import/settings_spec.rb index a7f823b2d8..b485127f0b 100644 --- a/spec/models/product_import/settings_spec.rb +++ b/spec/models/product_import/settings_spec.rb @@ -15,7 +15,7 @@ describe ProductImport::Settings do context 'when there are settings' do let(:entry) do - instance_double(ProductImport::SpreadsheetEntry, supplier_id: 1) + instance_double(ProductImport::SpreadsheetEntry, enterprise_id: 1) end let(:import_settings) { { settings: {} } } From 8fcaa54de7c2371b25d38359ac5f7cfd72fe3ded Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 8 Oct 2018 17:06:37 +0100 Subject: [PATCH 11/65] Update CSV templates --- public/inventory_template.csv | 2 +- public/product_list_template.csv | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/inventory_template.csv b/public/inventory_template.csv index dd7329a89c..d52cecded6 100644 --- a/public/inventory_template.csv +++ b/public/inventory_template.csv @@ -1 +1 @@ -producer,supplier,name,display_name,units,unit_type,price,on_hand +producer,distributor,name,display_name,units,unit_type,price,on_hand diff --git a/public/product_list_template.csv b/public/product_list_template.csv index f97e4f80d7..6eeae3701e 100644 --- a/public/product_list_template.csv +++ b/public/product_list_template.csv @@ -1 +1 @@ -supplier,sku,name,display_name,category,units,unit_type,variant_unit_name,price,on_hand,available_on,on_demand,shipping_category,tax_category +producer,sku,name,display_name,category,units,unit_type,variant_unit_name,price,on_hand,available_on,on_demand,shipping_category,tax_category From 3cb43121d5e901b8bcf1d395694eb4fce47e38b0 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 8 Oct 2018 17:41:27 +0100 Subject: [PATCH 12/65] Refactor long lines for new character limit --- app/models/product_import/entry_processor.rb | 25 ++++++-- app/models/product_import/entry_validator.rb | 63 ++++++++++++++----- app/models/product_import/product_importer.rb | 6 +- app/models/product_import/spreadsheet_data.rb | 11 +++- .../product_import/spreadsheet_entry.rb | 25 +++++--- 5 files changed, 98 insertions(+), 32 deletions(-) diff --git a/app/models/product_import/entry_processor.rb b/app/models/product_import/entry_processor.rb index f3c5b0a8e5..d96a9d36c7 100644 --- a/app/models/product_import/entry_processor.rb +++ b/app/models/product_import/entry_processor.rb @@ -46,7 +46,10 @@ module ProductImport products_count = if settings.importing_into_inventory? - VariantOverride.where('variant_overrides.hub_id IN (?)', enterprise_id).count + VariantOverride.where( + 'variant_overrides.hub_id IN (?)', + enterprise_id + ).count else Spree::Variant. not_deleted. @@ -158,7 +161,9 @@ module ProductImport # If we've already added a new product with these attributes # from this spreadsheet, mark this entry as a new variant with # the new product id, as this is a now variant of that product... - if @already_created[entry.enterprise_id] && @already_created[entry.enterprise_id][entry.name] + if @already_created[entry.enterprise_id] && + @already_created[entry.enterprise_id][entry.name] + product_id = @already_created[entry.enterprise_id][entry.name] @validator.mark_as_new_variant(entry, product_id) return @@ -207,7 +212,10 @@ module ProductImport when 'overwrite_all' object.assign_attributes(attribute => setting['value']) when 'overwrite_empty' - if object.public_send(attribute).blank? || ((attribute == 'on_hand' || attribute == 'count_on_hand') && entry.on_hand_nil) + if object.public_send(attribute).blank? || + ((attribute == 'on_hand' || attribute == 'count_on_hand') && + entry.on_hand_nil) + object.assign_attributes(attribute => setting['value']) end end @@ -216,7 +224,10 @@ module ProductImport def display_in_inventory(variant_override, is_new = false) unless is_new - existing_item = InventoryItem.where(variant_id: variant_override.variant_id, enterprise_id: variant_override.hub_id).first + existing_item = InventoryItem.where( + variant_id: variant_override.variant_id, + enterprise_id: variant_override.hub_id + ).first if existing_item existing_item.assign_attributes(visible: true) @@ -225,7 +236,11 @@ module ProductImport end end - InventoryItem.new(variant_id: variant_override.variant_id, enterprise_id: variant_override.hub_id, visible: true).save + InventoryItem.new( + variant_id: variant_override.variant_id, + enterprise_id: variant_override.hub_id, + visible: true + ).save end def ensure_variant_updated(product, entry) diff --git a/app/models/product_import/entry_validator.rb b/app/models/product_import/entry_validator.rb index c53b4fbe2c..b7f055e445 100644 --- a/app/models/product_import/entry_validator.rb +++ b/app/models/product_import/entry_validator.rb @@ -74,32 +74,50 @@ module ProductImport return if permissions_error entry return if primary_producer_error entry - entry.enterprise_id = @spreadsheet_data.enterprises_index[entry.enterprise][:id] - entry.public_send "#{enterprise_field}_id=", @spreadsheet_data.enterprises_index[entry.enterprise][:id] + entry.enterprise_id = + @spreadsheet_data.enterprises_index[entry.enterprise][:id] + + entry.public_send( + "#{enterprise_field}_id=", + @spreadsheet_data.enterprises_index[entry.enterprise][:id] + ) end def name_presence_error(entry) return if entry.enterprise.present? - mark_as_invalid(entry, attribute: enterprise_field, error: I18n.t(:error_required)) + mark_as_invalid(entry, + attribute: enterprise_field, + error: I18n.t(:error_required)) true end def enterprise_not_found_error(entry) return if @spreadsheet_data.enterprises_index[entry.enterprise][:id] - mark_as_invalid(entry, attribute: enterprise_field, error: I18n.t(:error_not_found_in_database, name: entry.enterprise)) + mark_as_invalid(entry, + attribute: enterprise_field, + error: I18n.t(:error_not_found_in_database, + name: entry.enterprise)) true end def permissions_error(entry) return if permission_by_name?(entry.enterprise) - mark_as_invalid(entry, attribute: enterprise_field, error: I18n.t(:error_no_permission_for_enterprise, name: entry.enterprise)) + mark_as_invalid(entry, + attribute: enterprise_field, + error: I18n.t(:error_no_permission_for_enterprise, + name: entry.enterprise)) true end def primary_producer_error(entry) return if import_into_inventory? - return if @spreadsheet_data.enterprises_index[entry.enterprise][:is_primary_producer] - mark_as_invalid(entry, attribute: enterprise_field, error: I18n.t(:error_not_primary_producer, name: entry.enterprise)) + return if @spreadsheet_data. + enterprises_index[entry.enterprise][:is_primary_producer] + + mark_as_invalid(entry, + attribute: enterprise_field, + error: I18n.t(:error_not_primary_producer, + name: entry.enterprise)) true end @@ -136,7 +154,11 @@ module ProductImport return end - unless inventory_permission?(entry.enterprise_id, @spreadsheet_data.producers_index[producer_name]) + unless inventory_permission?( + entry.enterprise_id, + @spreadsheet_data.producers_index[producer_name] + ) + mark_as_invalid(entry, attribute: "producer", error: "\"#{producer_name}\": #{I18n.t('admin.product_import.model.inventory_no_permission')}") return end @@ -196,7 +218,9 @@ module ProductImport end def product_validation(entry) - products = Spree::Product.where(supplier_id: entry.enterprise_id, name: entry.name, deleted_at: nil) + products = Spree::Product.where(supplier_id: entry.enterprise_id, + name: entry.name, + deleted_at: nil) if products.empty? mark_as_new_product(entry) @@ -263,7 +287,9 @@ module ProductImport end def inventory_permission?(enterprise_id, producer_id) - @current_user.admin? || ( @inventory_permissions[enterprise_id] && @inventory_permissions[enterprise_id].include?(producer_id) ) + @current_user.admin? || + ( @inventory_permissions[enterprise_id] && + @inventory_permissions[enterprise_id].include?(producer_id) ) end def mark_as_invalid(entry, options = {}) @@ -272,7 +298,7 @@ module ProductImport end def import_into_inventory? - @import_settings[:settings] && @import_settings[:settings]['import_into'] == 'inventories' + @import_settings[:settings].andand['import_into'] == 'inventories' end def validate_inventory_item(entry, variant_override) @@ -284,9 +310,16 @@ module ProductImport end def create_inventory_item(entry, existing_variant) - existing_variant_override = VariantOverride.where(variant_id: existing_variant.id, hub_id: entry.enterprise_id).first + existing_variant_override = VariantOverride.where( + variant_id: existing_variant.id, + hub_id: entry.enterprise_id + ).first + + variant_override = existing_variant_override || VariantOverride.new( + variant_id: existing_variant.id, + hub_id: entry.enterprise_id + ) - variant_override = existing_variant_override || VariantOverride.new(variant_id: existing_variant.id, hub_id: entry.enterprise_id) variant_override.assign_attributes(count_on_hand: entry.on_hand, import_date: @import_time) check_on_hand_nil(entry, variant_override) variant_override.assign_attributes(entry.attributes.slice('price', 'on_demand')) @@ -306,7 +339,9 @@ module ProductImport end def updates_count_per_enterprise(enterprise_id) - if @reset_counts[enterprise_id] && @reset_counts[enterprise_id][:updates_count] + if @reset_counts[enterprise_id] && + @reset_counts[enterprise_id][:updates_count] + @reset_counts[enterprise_id][:updates_count] += 1 else @reset_counts[enterprise_id] = { updates_count: 1 } diff --git a/app/models/product_import/product_importer.rb b/app/models/product_import/product_importer.rb index 35a490c8d6..3bc66fa815 100644 --- a/app/models/product_import/product_importer.rb +++ b/app/models/product_import/product_importer.rb @@ -65,12 +65,14 @@ module ProductImport end def reset_counts - # Return indexed data about existing product count, reset count, and updates count per enterprise + # Return indexed data about existing product count, reset count, and + # updates count per enterprise @reset_counts.each do |enterprise_id, values| values[:updates_count] = 0 if values[:updates_count].blank? if values[:updates_count] && values[:existing_products] - @reset_counts[enterprise_id][:reset_count] = values[:existing_products] - values[:updates_count] + @reset_counts[enterprise_id][:reset_count] = + values[:existing_products] - values[:updates_count] end end @reset_counts diff --git a/app/models/product_import/spreadsheet_data.rb b/app/models/product_import/spreadsheet_data.rb index 8903944284..1f8ff56ad3 100644 --- a/app/models/product_import/spreadsheet_data.rb +++ b/app/models/product_import/spreadsheet_data.rb @@ -35,16 +35,21 @@ module ProductImport private def import_into_inventory? - @import_settings[:settings] && @import_settings[:settings]['import_into'] == 'inventories' + @import_settings[:settings].andand['import_into'] == 'inventories' end def create_enterprises_index @enterprises_index = {} @entries.each do |entry| enterprise_name = import_into_inventory? ? entry.distributor : entry.producer + next if @enterprises_index.key? enterprise_name - enterprise = Enterprise.find_by_name(enterprise_name, select: 'id, name, is_primary_producer') - @enterprises_index[enterprise_name] = { id: enterprise.try(:id), is_primary_producer: enterprise.try(:is_primary_producer) } + + enterprise = Enterprise.find_by_name(enterprise_name, select: 'id, is_primary_producer') + + @enterprises_index[enterprise_name] = + { id: enterprise.try(:id), + is_primary_producer: enterprise.try(:is_primary_producer) } end @enterprises_index end diff --git a/app/models/product_import/spreadsheet_entry.rb b/app/models/product_import/spreadsheet_entry.rb index dcfb35e65f..c87f46ec0e 100644 --- a/app/models/product_import/spreadsheet_entry.rb +++ b/app/models/product_import/spreadsheet_entry.rb @@ -8,13 +8,17 @@ module ProductImport include ActiveModel::Conversion include ActiveModel::Validations - attr_accessor :line_number, :valid, :validates_as, :product_object, :product_validations, :on_hand_nil, - :has_overrides, :units, :unscaled_units, :unit_type, :tax_category, :shipping_category + attr_accessor :line_number, :valid, :validates_as, :product_object, + :product_validations, :on_hand_nil, :has_overrides, :units, + :unscaled_units, :unit_type, :tax_category, :shipping_category - attr_accessor :id, :product_id, :producer, :producer_id, :distributor, :distributor_id, :name, :display_name, :sku, - :unit_value, :unit_description, :variant_unit, :variant_unit_scale, :variant_unit_name, - :display_as, :category, :primary_taxon_id, :price, :on_hand, :count_on_hand, :on_demand, - :tax_category_id, :shipping_category_id, :description, :import_date, :enterprise, :enterprise_id + attr_accessor :id, :product_id, :producer, :producer_id, :distributor, + :distributor_id, :name, :display_name, :sku, :unit_value, + :unit_description, :variant_unit, :variant_unit_scale, + :variant_unit_name, :display_as, :category, :primary_taxon_id, + :price, :on_hand, :count_on_hand, :on_demand, + :tax_category_id, :shipping_category_id, :description, + :import_date, :enterprise, :enterprise_id def initialize(attrs) @validates_as = '' @@ -77,11 +81,16 @@ module ProductImport end def non_display_attributes - ['id', 'product_id', 'unscaled_units', 'variant_id', 'enterprise', 'enterprise_id', 'producer_id', 'distributor_id', 'primary_taxon', 'primary_taxon_id', 'category_id', 'shipping_category_id', 'tax_category_id', 'variant_unit_scale', 'variant_unit', 'unit_value'] + ['id', 'product_id', 'unscaled_units', 'variant_id', 'enterprise', + 'enterprise_id', 'producer_id', 'distributor_id', 'primary_taxon', + 'primary_taxon_id', 'category_id', 'shipping_category_id', + 'tax_category_id', 'variant_unit_scale', 'variant_unit', 'unit_value'] end def non_product_attributes - ['line_number', 'valid', 'errors', 'product_object', 'product_validations', 'inventory_validations', 'validates_as', 'save_type', 'on_hand_nil', 'has_overrides'] + ['line_number', 'valid', 'errors', 'product_object', + 'product_validations', 'inventory_validations', 'validates_as', + 'save_type', 'on_hand_nil', 'has_overrides'] end end end From 123d1963adbbfb932b3eda4ce78d53ad91401506 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 9 Oct 2018 11:18:52 +0100 Subject: [PATCH 13/65] Remove duplicate method --- app/models/product_import/entry_processor.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/models/product_import/entry_processor.rb b/app/models/product_import/entry_processor.rb index d96a9d36c7..dd6a8c3b3d 100644 --- a/app/models/product_import/entry_processor.rb +++ b/app/models/product_import/entry_processor.rb @@ -29,7 +29,7 @@ module ProductImport def save_all(entries) entries.each do |entry| - if import_into_inventory?(entry) + if settings.importing_into_inventory? save_to_inventory(entry) else save_to_product_list(entry) @@ -124,10 +124,6 @@ module ProductImport @variants_updated += 1 end - def import_into_inventory?(entry) - entry.enterprise_id && settings.importing_into_inventory? - end - def save_new_inventory_item(entry) new_item = entry.product_object assign_defaults(new_item, entry) From a2828ea3054bf85127afe2bbbf33cb1514c8202d Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 9 Oct 2018 11:21:21 +0100 Subject: [PATCH 14/65] Use :for_hubs scope --- app/models/product_import/entry_processor.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/models/product_import/entry_processor.rb b/app/models/product_import/entry_processor.rb index dd6a8c3b3d..2e8ed9592c 100644 --- a/app/models/product_import/entry_processor.rb +++ b/app/models/product_import/entry_processor.rb @@ -46,10 +46,7 @@ module ProductImport products_count = if settings.importing_into_inventory? - VariantOverride.where( - 'variant_overrides.hub_id IN (?)', - enterprise_id - ).count + VariantOverride.for_hubs([enterprise_id]).count else Spree::Variant. not_deleted. From dc6c8ec455316073161c068e6a8800c63770240e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 15 Oct 2018 19:14:26 +0000 Subject: [PATCH 15/65] Bump figaro from 0.7.0 to 1.1.1 Bumps [figaro](https://github.com/laserlemon/figaro) from 0.7.0 to 1.1.1. - [Release notes](https://github.com/laserlemon/figaro/releases) - [Changelog](https://github.com/laserlemon/figaro/blob/master/CHANGELOG.md) - [Commits](https://github.com/laserlemon/figaro/compare/v0.7.0...v1.1.1) Signed-off-by: dependabot[bot] --- Gemfile.lock | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1809931fb9..dc3c17b068 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -288,9 +288,8 @@ GEM multipart-post (>= 1.2, < 3) ffaker (1.15.0) ffi (1.9.25) - figaro (0.7.0) - bundler (~> 1.0) - rails (>= 3, < 5) + figaro (1.1.1) + thor (~> 0.14) fission (0.5.0) CFPropertyList (~> 2.2) fog (1.41.0) @@ -769,7 +768,6 @@ DEPENDENCIES capybara (>= 2.15.4) coffee-rails (~> 3.2.1) compass-rails - web! custom_error_message! daemons dalli @@ -846,6 +844,7 @@ DEPENDENCIES uglifier (>= 1.0.3) unicorn unicorn-rails + web! webmock whenever wicked_pdf From 02bc134c593ad9617ac8137bfc0fa6b9aa85c087 Mon Sep 17 00:00:00 2001 From: "fabricio.albarnaz" Date: Mon, 15 Oct 2018 17:51:33 -0300 Subject: [PATCH 16/65] Change befor save check to format validation --- app/models/enterprise.rb | 11 +++++------ spec/models/enterprise_spec.rb | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 7d4650ad0e..4f62d9f2f1 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -80,7 +80,7 @@ class Enterprise < ActiveRecord::Base before_validation :set_unused_address_fields after_validation :geocode_address - before_save :check_instagram_pattern + validates :instagram, format: /\A@[a-zA-Z0-9._]{1,30}\z/, :allow_blank => true after_touch :touch_distributors after_create :set_default_contact @@ -332,6 +332,10 @@ class Enterprise < ActiveRecord::Base abn.present? end + def instagram=(value) + write_attribute(:instagram, value.try(:gsub, %r{\Ahttps?://(?:www.)?instagram.com/([a-zA-Z0-9._]{1,30})/?\z}, '@\1')) + end + protected def devise_mailer @@ -427,9 +431,4 @@ class Enterprise < ActiveRecord::Base where('enterprises.id != ?', self.id). each(&:touch) end - - def check_instagram_pattern - return if self.instagram.blank? || self.instagram.exclude?('instagram.com') - self.instagram = "@#{self.instagram.split('/').last}" - end end diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index 28ce82b103..fa4877ff3b 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -149,10 +149,10 @@ describe Enterprise do end context "prevent an wrong instagram link pattern" do - let!(:e) { create(:enterprise, instagram: 'www.instagram.com/my_user') } + let(:e) { build(:enterprise, instagram: 'instagram.com/my_user') } - it "expects the instagram attribute to be in the correct pattern" do - expect(e.instagram).to eq('@my_user') + it "expects the instagram attribute to not be valid" do + expect(e).to_not be_valid end end end From 93a051bf5a3a21bd80aef23ca41e2377f5350989 Mon Sep 17 00:00:00 2001 From: "fabricio.albarnaz" Date: Tue, 16 Oct 2018 15:33:10 -0300 Subject: [PATCH 17/65] Regex ajust --- app/models/enterprise.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 4f62d9f2f1..c91187a325 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -80,7 +80,7 @@ class Enterprise < ActiveRecord::Base before_validation :set_unused_address_fields after_validation :geocode_address - validates :instagram, format: /\A@[a-zA-Z0-9._]{1,30}\z/, :allow_blank => true + validates :instagram, format: /\A@[a-zA-Z0-9._]{1,30}\z/, allow_blank: true after_touch :touch_distributors after_create :set_default_contact @@ -333,7 +333,8 @@ class Enterprise < ActiveRecord::Base end def instagram=(value) - write_attribute(:instagram, value.try(:gsub, %r{\Ahttps?://(?:www.)?instagram.com/([a-zA-Z0-9._]{1,30})/?\z}, '@\1')) + regex = %r{\A(?:https?://)?(?:www\.)?instagram\.com/([a-zA-Z0-9._]{1,30})/?\z} + write_attribute(:instagram, value.try(:gsub, regex, '@\1')) end protected From 8e4adbd23a2dcb6cabda5edee4c91e307a94b8e1 Mon Sep 17 00:00:00 2001 From: "fabricio.albarnaz" Date: Tue, 16 Oct 2018 15:34:46 -0300 Subject: [PATCH 18/65] Add more test cases --- spec/models/enterprise_spec.rb | 62 ++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index fa4877ff3b..3c489ec413 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -149,11 +149,67 @@ describe Enterprise do end context "prevent an wrong instagram link pattern" do - let(:e) { build(:enterprise, instagram: 'instagram.com/my_user') } - - it "expects the instagram attribute to not be valid" do + it "expects to be invalid the instagram attribute @my-user" do + e = build(:enterprise, instagram: '@my-user') expect(e).to_not be_valid end + + it "expects to be invalid the instagram attribute https://facebook.com/user" do + e = build(:enterprise, instagram: 'https://facebook.com/user') + expect(e).to_not be_valid + end + + it "expects to be invalid the instagram attribute tagram.com/user" do + e = build(:enterprise, instagram: 'tagram.com/user') + expect(e).to_not be_valid + end + + it "expects to be invalid the instagram attribute https://instagram.com/user/preferences" do + e = build(:enterprise, instagram: 'https://instagram.com/user/preferences') + expect(e).to_not be_valid + end + end + + context "accepted pattern" do + it "expects to be valid empty instagram attribute" do + e = build(:enterprise) + expect(e).to be_valid + end + + it "expects be valid the instagram attribute @user" do + e = build(:enterprise, instagram: '@user') + expect(e).to be_valid + end + + it "expects be valid the instagram attribute @my_www5.example" do + e = build(:enterprise, instagram: '@my_www5.example') + expect(e).to be_valid + end + + it "expects be valid the instagram attribute http://instagram.com/user" do + e = build(:enterprise, instagram: 'http://instagram.com/user') + expect(e).to be_valid + end + + it "expects be valid the instagram attribute https://instagram.com/user/" do + e = build(:enterprise, instagram: 'https://instagram.com/user/') + expect(e).to be_valid + end + + it "expects be valid the instagram attribute https://www.instagram.com/user" do + e = build(:enterprise, instagram: 'https://www.instagram.com/user') + expect(e).to be_valid + end + + it "expects be valid the instagram attribute instagram.com/user" do + e = build(:enterprise, instagram: 'instagram.com/user') + expect(e).to be_valid + end + + it "renders the expected pattern" do + e = build(:enterprise, instagram: 'instagram.com/user') + expect(e.instagram).to eq('@user') + end end end From 419493f1534d2e1781c0ac2c10c0e6547874d207 Mon Sep 17 00:00:00 2001 From: "fabricio.albarnaz" Date: Tue, 16 Oct 2018 16:09:15 -0300 Subject: [PATCH 19/65] Move instagram regex to an method to fix codeclimate --- app/models/enterprise.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index c91187a325..7ea1b069fa 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -333,8 +333,7 @@ class Enterprise < ActiveRecord::Base end def instagram=(value) - regex = %r{\A(?:https?://)?(?:www\.)?instagram\.com/([a-zA-Z0-9._]{1,30})/?\z} - write_attribute(:instagram, value.try(:gsub, regex, '@\1')) + write_attribute(:instagram, value.try(:gsub, instagram_regex, '@\1')) end protected @@ -345,6 +344,10 @@ class Enterprise < ActiveRecord::Base private + def instagram_regex + %r{\A(?:https?://)?(?:www\.)?instagram\.com/([a-zA-Z0-9._]{1,30})/?\z} + end + def name_is_unique dups = Enterprise.where(name: name) dups = dups.where('id != ?', id) unless new_record? From feaf16d8784b3686abb34dbb0431f47b52bb443b Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 16 Oct 2018 17:30:57 +0100 Subject: [PATCH 20/65] Fix bug in subscriptions address controller where the country id lookup was not exact and states returned were incorrect. Add unit tests to cover different cases --- .../controllers/address_controller.js.coffee | 18 +++++--- ...ee => providers_controller_spec.js.coffee} | 0 .../address_controller_spec.js.coffee | 43 +++++++++++++++++++ 3 files changed, 54 insertions(+), 7 deletions(-) rename spec/javascripts/unit/admin/controllers/{providers_controller_decorator.js.coffee => providers_controller_spec.js.coffee} (100%) create mode 100644 spec/javascripts/unit/admin/subscriptions/controllers/address_controller_spec.js.coffee diff --git a/app/assets/javascripts/admin/subscriptions/controllers/address_controller.js.coffee b/app/assets/javascripts/admin/subscriptions/controllers/address_controller.js.coffee index 02c78e3615..01b5e3d9c2 100644 --- a/app/assets/javascripts/admin/subscriptions/controllers/address_controller.js.coffee +++ b/app/assets/javascripts/admin/subscriptions/controllers/address_controller.js.coffee @@ -1,11 +1,21 @@ angular.module("admin.subscriptions").controller "AddressController", ($scope, $filter, StatusMessage, availableCountries) -> $scope.countries = availableCountries + $scope.statesFor = (country_id) -> return [] unless country_id - $filter('filter')(availableCountries, {id: country_id})[0].states + country = $filter('filter')(availableCountries, {id: country_id}, true)[0] + return [] unless country + country.states + $scope.billStates = $scope.statesFor($scope.subscription.bill_address.country_id) $scope.shipStates = $scope.statesFor($scope.subscription.ship_address.country_id) + $scope.$watch 'subscription.bill_address.country_id', (newValue, oldValue) -> + $scope.billStates = $scope.statesFor(newValue) if newValue? + + $scope.$watch 'subscription.ship_address.country_id', (newValue, oldValue) -> + $scope.shipStates = $scope.statesFor(newValue) if newValue? + $scope.registerNextCallback 'address', -> $scope.subscription_form.$submitted = true if $scope.subscription_address_form.$valid @@ -18,9 +28,3 @@ angular.module("admin.subscriptions").controller "AddressController", ($scope, $ $scope.registerBackCallback 'address', -> StatusMessage.clear() $scope.setView('details') - - $scope.$watch 'subscription.bill_address.country_id', (newValue, oldValue) -> - $scope.billStates = $scope.statesFor(newValue) if newValue? - - $scope.$watch 'subscription.ship_address.country_id', (newValue, oldValue) -> - $scope.shipStates = $scope.statesFor(newValue) if newValue? diff --git a/spec/javascripts/unit/admin/controllers/providers_controller_decorator.js.coffee b/spec/javascripts/unit/admin/controllers/providers_controller_spec.js.coffee similarity index 100% rename from spec/javascripts/unit/admin/controllers/providers_controller_decorator.js.coffee rename to spec/javascripts/unit/admin/controllers/providers_controller_spec.js.coffee diff --git a/spec/javascripts/unit/admin/subscriptions/controllers/address_controller_spec.js.coffee b/spec/javascripts/unit/admin/subscriptions/controllers/address_controller_spec.js.coffee new file mode 100644 index 0000000000..c622906893 --- /dev/null +++ b/spec/javascripts/unit/admin/subscriptions/controllers/address_controller_spec.js.coffee @@ -0,0 +1,43 @@ +describe "AddressController", -> + scope = null + subscription = { id: 1 } + + states_in_spain = [{id: 55, name: "CAT", abbr: "CAT"}] + states_in_portugal = [{id: 55, name: "ACT", abbr: "ACT"}, {id: 5, name: "BFT", abbr: "BFT"}] + availableCountries = [ + {id: 9, name: "Australia", states: []}, + {id: 119, name: "Spain", states: states_in_spain}, + {id: 19, name: "Portugal", states: states_in_portugal} + ] + + beforeEach -> + module('admin.subscriptions') + + inject ($controller, $rootScope) -> + scope = $rootScope + + scope.registerNextCallback = () -> + scope.registerBackCallback = () -> + scope.subscription = subscription + subscription.bill_address = {country_id: 1} + subscription.ship_address = {country_id: 2} + $controller 'AddressController', {$scope: scope, availableCountries: availableCountries} + + describe "statesFor", -> + it "returns empty array for nil country id", -> + expect(scope.statesFor(null)).toEqual [] + + it "returns empty array for country id not in availableCountries", -> + expect(scope.statesFor(10)).toEqual [] + + it "returns empty array for country id in availableCountries but without states", -> + expect(scope.statesFor(9)).toEqual [] + + it "returns states for country id in availableCountries with states", -> + expect(scope.statesFor(119)).toEqual states_in_spain + + it "returns empty array for country id (11) in availableCountries but only as part of other country id (119)", -> + expect(scope.statesFor(11)).toEqual [] + + it "returns states for country id (19) in availableCountries with states even if other country ids contain the requested id (119)", -> + expect(scope.statesFor(19)).toEqual states_in_portugal From 5c5a2194d6e74ff791e67764dbf220df0c37dfdc Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 16 Oct 2018 21:58:27 +0100 Subject: [PATCH 21/65] Extract country states logic out of subscriptions address controller into new service CountryStates --- .../admin/services/country_states.js.coffee | 8 +++++ .../controllers/address_controller.js.coffee | 16 +++------- .../subscriptions/subscriptions.js.coffee | 2 +- .../country_states_spec.js.coffee} | 31 +++++++------------ 4 files changed, 25 insertions(+), 32 deletions(-) create mode 100644 app/assets/javascripts/admin/services/country_states.js.coffee rename spec/javascripts/unit/admin/{subscriptions/controllers/address_controller_spec.js.coffee => services/country_states_spec.js.coffee} (54%) diff --git a/app/assets/javascripts/admin/services/country_states.js.coffee b/app/assets/javascripts/admin/services/country_states.js.coffee new file mode 100644 index 0000000000..a29645d55f --- /dev/null +++ b/app/assets/javascripts/admin/services/country_states.js.coffee @@ -0,0 +1,8 @@ +angular.module("ofn.admin").factory "CountryStates", ($filter) -> + new class CountryStates + + statesFor: (countries, country_id) -> + return [] unless country_id + country = $filter('filter')(countries, {id: country_id}, true)[0] + return [] unless country + country.states diff --git a/app/assets/javascripts/admin/subscriptions/controllers/address_controller.js.coffee b/app/assets/javascripts/admin/subscriptions/controllers/address_controller.js.coffee index 01b5e3d9c2..30dff9975a 100644 --- a/app/assets/javascripts/admin/subscriptions/controllers/address_controller.js.coffee +++ b/app/assets/javascripts/admin/subscriptions/controllers/address_controller.js.coffee @@ -1,20 +1,14 @@ -angular.module("admin.subscriptions").controller "AddressController", ($scope, $filter, StatusMessage, availableCountries) -> +angular.module("admin.subscriptions").controller "AddressController", ($scope, StatusMessage, availableCountries, CountryStates) -> $scope.countries = availableCountries - $scope.statesFor = (country_id) -> - return [] unless country_id - country = $filter('filter')(availableCountries, {id: country_id}, true)[0] - return [] unless country - country.states - - $scope.billStates = $scope.statesFor($scope.subscription.bill_address.country_id) - $scope.shipStates = $scope.statesFor($scope.subscription.ship_address.country_id) + $scope.billStates = CountryStates.statesFor(availableCountries, $scope.subscription.bill_address.country_id) + $scope.shipStates = CountryStates.statesFor(availableCountries, $scope.subscription.ship_address.country_id) $scope.$watch 'subscription.bill_address.country_id', (newValue, oldValue) -> - $scope.billStates = $scope.statesFor(newValue) if newValue? + $scope.billStates = CountryStates.statesFor(availableCountries, newValue) if newValue? $scope.$watch 'subscription.ship_address.country_id', (newValue, oldValue) -> - $scope.shipStates = $scope.statesFor(newValue) if newValue? + $scope.shipStates = CountryStates.statesFor(availableCountries, newValue) if newValue? $scope.registerNextCallback 'address', -> $scope.subscription_form.$submitted = true diff --git a/app/assets/javascripts/admin/subscriptions/subscriptions.js.coffee b/app/assets/javascripts/admin/subscriptions/subscriptions.js.coffee index 9e2c3d5696..066552b598 100644 --- a/app/assets/javascripts/admin/subscriptions/subscriptions.js.coffee +++ b/app/assets/javascripts/admin/subscriptions/subscriptions.js.coffee @@ -1 +1 @@ -angular.module("admin.subscriptions", ['ngResource','admin.indexUtils','admin.dropdown']) +angular.module("admin.subscriptions", ['ngResource','admin.indexUtils','admin.dropdown', 'ofn.admin']) diff --git a/spec/javascripts/unit/admin/subscriptions/controllers/address_controller_spec.js.coffee b/spec/javascripts/unit/admin/services/country_states_spec.js.coffee similarity index 54% rename from spec/javascripts/unit/admin/subscriptions/controllers/address_controller_spec.js.coffee rename to spec/javascripts/unit/admin/services/country_states_spec.js.coffee index c622906893..5b3683b9d7 100644 --- a/spec/javascripts/unit/admin/subscriptions/controllers/address_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/services/country_states_spec.js.coffee @@ -1,6 +1,5 @@ -describe "AddressController", -> - scope = null - subscription = { id: 1 } +describe "CountryStates service", -> + countryStates = null states_in_spain = [{id: 55, name: "CAT", abbr: "CAT"}] states_in_portugal = [{id: 55, name: "ACT", abbr: "ACT"}, {id: 5, name: "BFT", abbr: "BFT"}] @@ -11,33 +10,25 @@ describe "AddressController", -> ] beforeEach -> - module('admin.subscriptions') - - inject ($controller, $rootScope) -> - scope = $rootScope - - scope.registerNextCallback = () -> - scope.registerBackCallback = () -> - scope.subscription = subscription - subscription.bill_address = {country_id: 1} - subscription.ship_address = {country_id: 2} - $controller 'AddressController', {$scope: scope, availableCountries: availableCountries} + module('ofn.admin') + inject (CountryStates) -> + countryStates = CountryStates describe "statesFor", -> it "returns empty array for nil country id", -> - expect(scope.statesFor(null)).toEqual [] + expect(countryStates.statesFor(availableCountries, null)).toEqual [] it "returns empty array for country id not in availableCountries", -> - expect(scope.statesFor(10)).toEqual [] + expect(countryStates.statesFor(availableCountries, 10)).toEqual [] it "returns empty array for country id in availableCountries but without states", -> - expect(scope.statesFor(9)).toEqual [] + expect(countryStates.statesFor(availableCountries, 9)).toEqual [] it "returns states for country id in availableCountries with states", -> - expect(scope.statesFor(119)).toEqual states_in_spain + expect(countryStates.statesFor(availableCountries, 119)).toEqual states_in_spain it "returns empty array for country id (11) in availableCountries but only as part of other country id (119)", -> - expect(scope.statesFor(11)).toEqual [] + expect(countryStates.statesFor(availableCountries, 11)).toEqual [] it "returns states for country id (19) in availableCountries with states even if other country ids contain the requested id (119)", -> - expect(scope.statesFor(19)).toEqual states_in_portugal + expect(countryStates.statesFor(availableCountries, 19)).toEqual states_in_portugal From 1804bf5a2b1bf1124f03a236edc75d2f52c87a27 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 16 Oct 2018 22:45:38 +0100 Subject: [PATCH 22/65] Extract countryStates logic from customer_address modal and re-use new CountryStates service --- .../admin/customers/customers.js.coffee | 2 +- .../directives/edit_address_dialog.js.coffee | 17 +++++------------ .../admin/services/country_states.js.coffee | 5 ++++- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/admin/customers/customers.js.coffee b/app/assets/javascripts/admin/customers/customers.js.coffee index fe8ae1de5b..aa8a1cfc19 100644 --- a/app/assets/javascripts/admin/customers/customers.js.coffee +++ b/app/assets/javascripts/admin/customers/customers.js.coffee @@ -1 +1 @@ -angular.module("admin.customers", ['ngResource', 'admin.tagRules', 'admin.indexUtils', 'admin.utils', 'admin.dropdown']) +angular.module("admin.customers", ['ngResource', 'admin.tagRules', 'admin.indexUtils', 'admin.utils', 'admin.dropdown', 'ofn.admin']) diff --git a/app/assets/javascripts/admin/customers/directives/edit_address_dialog.js.coffee b/app/assets/javascripts/admin/customers/directives/edit_address_dialog.js.coffee index eab83167a7..ca2d2ca817 100644 --- a/app/assets/javascripts/admin/customers/directives/edit_address_dialog.js.coffee +++ b/app/assets/javascripts/admin/customers/directives/edit_address_dialog.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.customers").directive 'editAddressDialog', ($compile, $templateCache, $filter, DialogDefaults, Customers, StatusMessage) -> +angular.module("admin.customers").directive 'editAddressDialog', ($compile, $templateCache, DialogDefaults, Customers, StatusMessage, CountryStates) -> restrict: 'A' scope: true link: (scope, element, attr) -> @@ -6,9 +6,9 @@ angular.module("admin.customers").directive 'editAddressDialog', ($compile, $tem scope.errors = [] scope.$watch 'address.country_id', (newCountryID) -> - if newCountryID - scope.states = scope.filterStates(newCountryID) - scope.clearState() unless scope.addressStateMatchesCountry() + return unless newCountryID + scope.states = CountryStates.statesFor(scope.availableCountries, newCountryID) + scope.clearState() unless CountryStates.addressStateMatchesCountryStates(scope.states, scope.address.state_id) scope.updateAddress = -> scope.edit_address_form.$setPristine() @@ -27,19 +27,12 @@ angular.module("admin.customers").directive 'editAddressDialog', ($compile, $tem else scope.addressType = 'ship_address' scope.address = scope.customer[scope.addressType] - scope.states = scope.filterStates(scope.address?.country_id) + scope.states = CountryStates.statesFor(scope.availableCountries, scope.address?.country_id) template = $compile($templateCache.get('admin/edit_address_dialog.html'))(scope) template.dialog(DialogDefaults) template.dialog('open') scope.$apply() - scope.filterStates = (countryID) -> - return [] unless countryID - $filter('filter')(scope.availableCountries, {id: parseInt(countryID)}, true)[0].states - scope.clearState = -> scope.address.state_id = "" - - scope.addressStateMatchesCountry = -> - scope.states.some (state) -> state.id == scope.address.state_id \ No newline at end of file diff --git a/app/assets/javascripts/admin/services/country_states.js.coffee b/app/assets/javascripts/admin/services/country_states.js.coffee index a29645d55f..7977bc3535 100644 --- a/app/assets/javascripts/admin/services/country_states.js.coffee +++ b/app/assets/javascripts/admin/services/country_states.js.coffee @@ -3,6 +3,9 @@ angular.module("ofn.admin").factory "CountryStates", ($filter) -> statesFor: (countries, country_id) -> return [] unless country_id - country = $filter('filter')(countries, {id: country_id}, true)[0] + country = $filter('filter')(countries, {id: parseInt(country_id)}, true)[0] return [] unless country country.states + + addressStateMatchesCountryStates: (countryStates, stateId) -> + countryStates.some (state) -> state.id == stateId From c281927372f96ac208c8497ecb0a5daec7cccd6b Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 16 Oct 2018 23:02:26 +0100 Subject: [PATCH 23/65] Fix subscription address form. It now clears the state selection when a different country is selected --- .../directives/edit_address_dialog.js.coffee | 6 ++---- .../controllers/address_controller.js.coffee | 14 ++++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/admin/customers/directives/edit_address_dialog.js.coffee b/app/assets/javascripts/admin/customers/directives/edit_address_dialog.js.coffee index ca2d2ca817..f52f4379da 100644 --- a/app/assets/javascripts/admin/customers/directives/edit_address_dialog.js.coffee +++ b/app/assets/javascripts/admin/customers/directives/edit_address_dialog.js.coffee @@ -8,7 +8,8 @@ angular.module("admin.customers").directive 'editAddressDialog', ($compile, $tem scope.$watch 'address.country_id', (newCountryID) -> return unless newCountryID scope.states = CountryStates.statesFor(scope.availableCountries, newCountryID) - scope.clearState() unless CountryStates.addressStateMatchesCountryStates(scope.states, scope.address.state_id) + unless CountryStates.addressStateMatchesCountryStates(scope.states, scope.address.state_id) + scope.address.state_id = "" scope.updateAddress = -> scope.edit_address_form.$setPristine() @@ -33,6 +34,3 @@ angular.module("admin.customers").directive 'editAddressDialog', ($compile, $tem template.dialog(DialogDefaults) template.dialog('open') scope.$apply() - - scope.clearState = -> - scope.address.state_id = "" diff --git a/app/assets/javascripts/admin/subscriptions/controllers/address_controller.js.coffee b/app/assets/javascripts/admin/subscriptions/controllers/address_controller.js.coffee index 30dff9975a..a9ba992b5c 100644 --- a/app/assets/javascripts/admin/subscriptions/controllers/address_controller.js.coffee +++ b/app/assets/javascripts/admin/subscriptions/controllers/address_controller.js.coffee @@ -4,11 +4,17 @@ angular.module("admin.subscriptions").controller "AddressController", ($scope, S $scope.billStates = CountryStates.statesFor(availableCountries, $scope.subscription.bill_address.country_id) $scope.shipStates = CountryStates.statesFor(availableCountries, $scope.subscription.ship_address.country_id) - $scope.$watch 'subscription.bill_address.country_id', (newValue, oldValue) -> - $scope.billStates = CountryStates.statesFor(availableCountries, newValue) if newValue? + $scope.$watch 'subscription.bill_address.country_id', (newCountryID) -> + return unless newCountryID + $scope.billStates = CountryStates.statesFor(availableCountries, newCountryID) + unless CountryStates.addressStateMatchesCountryStates($scope.billStates, $scope.subscription.bill_address.state_id) + $scope.subscription.bill_address.state_id = "" - $scope.$watch 'subscription.ship_address.country_id', (newValue, oldValue) -> - $scope.shipStates = CountryStates.statesFor(availableCountries, newValue) if newValue? + $scope.$watch 'subscription.ship_address.country_id', (newCountryID) -> + return unless newCountryID + $scope.shipStates = CountryStates.statesFor(availableCountries, newCountryID) + unless CountryStates.addressStateMatchesCountryStates($scope.shipStates, $scope.subscription.ship_address.state_id) + $scope.subscription.ship_address.state_id = "" $scope.registerNextCallback 'address', -> $scope.subscription_form.$submitted = true From fe9f4a1c95d6201bc9b8a22c6f6d0d851f08b8da Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Wed, 17 Oct 2018 12:16:33 +0100 Subject: [PATCH 24/65] Move countryStates service from ofn.admin to admin.utils to remove bad dependency from admin modules (customers and subscriptions) to main ofn.admin module. Now the dependency admin.utils is used instead --- app/assets/javascripts/admin/customers/customers.js.coffee | 2 +- .../javascripts/admin/subscriptions/subscriptions.js.coffee | 2 +- .../admin/{ => utils}/services/country_states.js.coffee | 2 +- .../admin/{ => utils}/services/country_states_spec.js.coffee | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename app/assets/javascripts/admin/{ => utils}/services/country_states.js.coffee (84%) rename spec/javascripts/unit/admin/{ => utils}/services/country_states_spec.js.coffee (98%) diff --git a/app/assets/javascripts/admin/customers/customers.js.coffee b/app/assets/javascripts/admin/customers/customers.js.coffee index aa8a1cfc19..fe8ae1de5b 100644 --- a/app/assets/javascripts/admin/customers/customers.js.coffee +++ b/app/assets/javascripts/admin/customers/customers.js.coffee @@ -1 +1 @@ -angular.module("admin.customers", ['ngResource', 'admin.tagRules', 'admin.indexUtils', 'admin.utils', 'admin.dropdown', 'ofn.admin']) +angular.module("admin.customers", ['ngResource', 'admin.tagRules', 'admin.indexUtils', 'admin.utils', 'admin.dropdown']) diff --git a/app/assets/javascripts/admin/subscriptions/subscriptions.js.coffee b/app/assets/javascripts/admin/subscriptions/subscriptions.js.coffee index 066552b598..9e2c3d5696 100644 --- a/app/assets/javascripts/admin/subscriptions/subscriptions.js.coffee +++ b/app/assets/javascripts/admin/subscriptions/subscriptions.js.coffee @@ -1 +1 @@ -angular.module("admin.subscriptions", ['ngResource','admin.indexUtils','admin.dropdown', 'ofn.admin']) +angular.module("admin.subscriptions", ['ngResource','admin.indexUtils','admin.dropdown']) diff --git a/app/assets/javascripts/admin/services/country_states.js.coffee b/app/assets/javascripts/admin/utils/services/country_states.js.coffee similarity index 84% rename from app/assets/javascripts/admin/services/country_states.js.coffee rename to app/assets/javascripts/admin/utils/services/country_states.js.coffee index 7977bc3535..30161b5bb0 100644 --- a/app/assets/javascripts/admin/services/country_states.js.coffee +++ b/app/assets/javascripts/admin/utils/services/country_states.js.coffee @@ -1,4 +1,4 @@ -angular.module("ofn.admin").factory "CountryStates", ($filter) -> +angular.module("admin.utils").factory "CountryStates", ($filter) -> new class CountryStates statesFor: (countries, country_id) -> diff --git a/spec/javascripts/unit/admin/services/country_states_spec.js.coffee b/spec/javascripts/unit/admin/utils/services/country_states_spec.js.coffee similarity index 98% rename from spec/javascripts/unit/admin/services/country_states_spec.js.coffee rename to spec/javascripts/unit/admin/utils/services/country_states_spec.js.coffee index 5b3683b9d7..6e5247cdb7 100644 --- a/spec/javascripts/unit/admin/services/country_states_spec.js.coffee +++ b/spec/javascripts/unit/admin/utils/services/country_states_spec.js.coffee @@ -10,7 +10,7 @@ describe "CountryStates service", -> ] beforeEach -> - module('ofn.admin') + module('admin.utils') inject (CountryStates) -> countryStates = CountryStates From c3cef699ca5ff507fc7d9a1defd4129712561bc3 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 19 Oct 2018 16:49:28 +0100 Subject: [PATCH 25/65] Use https on all links in emails in production --- config/environments/production.rb | 3 +++ config/environments/staging.rb | 3 +++ 2 files changed, 6 insertions(+) diff --git a/config/environments/production.rb b/config/environments/production.rb index 229221e118..0ee6be4930 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -30,6 +30,9 @@ Openfoodnetwork::Application.configure do # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. config.force_ssl = true + # Use https when creating links in emails + config.action_mailer.default_url_options = { protocol: 'https' } + # See everything in the log (default is :info) config.log_level = :info diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 303d7d2913..9c7a40e8e1 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -30,6 +30,9 @@ Openfoodnetwork::Application.configure do # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. config.force_ssl = true + # Use https when creating links in emails + config.action_mailer.default_url_options = { protocol: 'https' } + # See everything in the log (default is :info) # config.log_level = :debug From 5ae0ad87a7812d6b99dbd0fae457f2b4ad8ca1a1 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Sat, 20 Oct 2018 11:12:00 +0100 Subject: [PATCH 26/65] Refactor EnterpriseRelationship before save hook: some renames and extract methods --- app/models/enterprise_relationship.rb | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb index 1f87b8737b..c5806cae8c 100644 --- a/app/models/enterprise_relationship.rb +++ b/app/models/enterprise_relationship.rb @@ -6,7 +6,7 @@ class EnterpriseRelationship < ActiveRecord::Base validates_presence_of :parent_id, :child_id validates_uniqueness_of :child_id, scope: :parent_id, message: I18n.t('validation_msg_relationship_already_established') - after_save :apply_variant_override_permissions + after_save :update_permissions_of_child_variant_overrides scope :with_enterprises, joins('LEFT JOIN enterprises AS parent_enterprises ON parent_enterprises.id = enterprise_relationships.parent_id'). @@ -26,7 +26,6 @@ class EnterpriseRelationship < ActiveRecord::Base scope :by_name, with_enterprises.order('child_enterprises.name, parent_enterprises.name') - # Load an array of the relatives of each enterprise (ie. any enterprise related to it in # either direction). This array is split into distributors and producers, and has the format: # {enterprise_id => {distributors: [id, ...], producers: [id, ...]} } @@ -76,14 +75,24 @@ class EnterpriseRelationship < ActiveRecord::Base private - def apply_variant_override_permissions - variant_overrides = VariantOverride.unscoped.for_hubs(child) - .joins(variant: :product).where("spree_products.supplier_id IN (?)", parent) - + def update_permissions_of_child_variant_overrides if has_permission?(:create_variant_overrides) - variant_overrides.update_all(permission_revoked_at: nil) + allow_all_child_variant_overrides else - variant_overrides.update_all(permission_revoked_at: Time.now) + revoke_all_child_variant_overrides end end + + def allow_all_child_variant_overrides + child_variant_overrides.update_all(permission_revoked_at: nil) + end + + def revoke_all_child_variant_overrides + child_variant_overrides.update_all(permission_revoked_at: Time.now) + end + + def child_variant_overrides + VariantOverride.unscoped.for_hubs(child) + .joins(variant: :product).where("spree_products.supplier_id IN (?)", parent) + end end From 9079437284aab3b83895b8c5625dca0189b00118 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Sat, 20 Oct 2018 11:12:53 +0100 Subject: [PATCH 27/65] Add before_destroy to enterprise_relationship so that variant overrides are revoked when permission is deleted --- app/models/enterprise_relationship.rb | 1 + spec/models/enterprise_relationship_spec.rb | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb index c5806cae8c..7af3d180a3 100644 --- a/app/models/enterprise_relationship.rb +++ b/app/models/enterprise_relationship.rb @@ -7,6 +7,7 @@ class EnterpriseRelationship < ActiveRecord::Base validates_uniqueness_of :child_id, scope: :parent_id, message: I18n.t('validation_msg_relationship_already_established') after_save :update_permissions_of_child_variant_overrides + before_destroy :revoke_all_child_variant_overrides scope :with_enterprises, joins('LEFT JOIN enterprises AS parent_enterprises ON parent_enterprises.id = enterprise_relationships.parent_id'). diff --git a/spec/models/enterprise_relationship_spec.rb b/spec/models/enterprise_relationship_spec.rb index 5dbdf1f3be..650e4f1fa3 100644 --- a/spec/models/enterprise_relationship_spec.rb +++ b/spec/models/enterprise_relationship_spec.rb @@ -140,7 +140,7 @@ describe EnterpriseRelationship do end describe "callbacks" do - context "applying variant override permissions" do + context "updating variant override permissions" do let(:hub) { create(:distributor_enterprise) } let(:producer) { create(:supplier_enterprise) } let(:some_other_producer) { create(:supplier_enterprise) } @@ -152,6 +152,17 @@ describe EnterpriseRelationship do let!(:vo2) { create(:variant_override, hub: hub, variant: create(:variant, product: create(:product, supplier: producer))) } let!(:vo3) { create(:variant_override, hub: hub, variant: create(:variant, product: create(:product, supplier: some_other_producer))) } + context "revoking variant override permissions" do + context "when the enterprise relationship is destroyed" do + before { er.destroy } + it "should set permission_revoked_at to the current time for all variant overrides of the relationship" do + expect(vo1.reload.permission_revoked_at).to_not be_nil + expect(vo2.reload.permission_revoked_at).to_not be_nil + expect(vo2.reload.permission_revoked_at).to_not be_nil + end + end + end + context "and is then removed" do before { er.permissions_list = [:add_to_order_cycles]; er.save! } it "should set permission_revoked_at to the current time for all relevant variant overrides" do From d375bb8c556950cdf56ebb704dc374e579331613 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Sat, 20 Oct 2018 11:48:23 +0100 Subject: [PATCH 28/65] Migration: Revoke variant overrides without permissions --- ...voke_variant_overrideswithout_permissions.rb | 17 +++++++++++++++++ db/schema.rb | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20181020103501_revoke_variant_overrideswithout_permissions.rb diff --git a/db/migrate/20181020103501_revoke_variant_overrideswithout_permissions.rb b/db/migrate/20181020103501_revoke_variant_overrideswithout_permissions.rb new file mode 100644 index 0000000000..bd2d0e750b --- /dev/null +++ b/db/migrate/20181020103501_revoke_variant_overrideswithout_permissions.rb @@ -0,0 +1,17 @@ +class RevokeVariantOverrideswithoutPermissions < ActiveRecord::Migration + def up + # This process was executed when the permission_revoked_at colum was created (see AddPermissionRevokedAtToVariantOverrides) + # It needs to be repeated due to #2739 + variant_override_hubs = Enterprise.where(id: VariantOverride.all.map(&:hub_id).uniq) + + variant_override_hubs.each do |hub| + permitting_producer_ids = hub.relationships_as_child + .with_permission(:create_variant_overrides).map(&:parent_id) + + variant_overrides_with_revoked_permissions = VariantOverride.for_hubs(hub) + .joins(variant: :product).where("spree_products.supplier_id NOT IN (?)", permitting_producer_ids) + + variant_overrides_with_revoked_permissions.update_all(permission_revoked_at: Time.now) + end + end +end diff --git a/db/schema.rb b/db/schema.rb index c29fa95b9e..3e60568a4a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20181010093850) do +ActiveRecord::Schema.define(:version => 20181020103501) do create_table "account_invoices", :force => true do |t| t.integer "user_id", :null => false From c5f6af95c7a649b11cdd88f19614b634fe3aa85a Mon Sep 17 00:00:00 2001 From: Hugo Daniel Date: Tue, 23 Oct 2018 10:40:20 +0200 Subject: [PATCH 29/65] Add password confirmation, token and expired keys --- config/locales/en.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 0d02aa3933..f7272070e8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -248,6 +248,9 @@ en: pick_up: Pick up ok: Ok copy: Copy + password_confirmation: Password Confirmation + reset_password_token: Reset password token + expired: has expired, please request a new one actions: create_and_add_another: "Create and Add Another" @@ -1481,7 +1484,7 @@ To activate your Profile we need to confirm this email address." email_confirmation_link_label: "Confirm this email address »" email_confirmation_help_html: "After confirming your email you can access your administration account for this enterprise. See the %{link} to find out more about %{sitename}'s features and to start using your profile or online store." - email_confirmation_notice_unexpected: "You received this message because you signed up on %{sitename}, or were invited to sign up by someone you probably know. If you don't understand why you are receiving this email, please write to %{contact}." + email_confirmation_notice_unexpected: "You received this message because you signed up on %{sitename}, or were invited to sign up by someone you probably know. If you don't understand why you are receiving this email, please write to %{contact}." email_social: "Connect with Us:" email_contact: "Email us:" email_signoff: "Cheers," From 9d9f5d38d70e960cd43bff110547f826220d6790 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 23 Oct 2018 13:35:31 +0100 Subject: [PATCH 30/65] Fix multi lingual issue in cart and checkout: the user language selection was not being used --- .../spree/store_controller_decorator.rb | 3 + spec/features/consumer/multilingual_spec.rb | 71 +++++++++++++------ 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/app/controllers/spree/store_controller_decorator.rb b/app/controllers/spree/store_controller_decorator.rb index 09c2efc7b4..d1d8fb8277 100644 --- a/app/controllers/spree/store_controller_decorator.rb +++ b/app/controllers/spree/store_controller_decorator.rb @@ -1,6 +1,9 @@ class Spree::StoreController layout 'darkswarm' + include I18nHelper + before_filter :set_locale + def unauthorized render 'shared/unauthorized', :status => 401 end diff --git a/spec/features/consumer/multilingual_spec.rb b/spec/features/consumer/multilingual_spec.rb index b690f5c802..98d2c385ca 100644 --- a/spec/features/consumer/multilingual_spec.rb +++ b/spec/features/consumer/multilingual_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' feature 'Multilingual', js: true do include AuthenticationWorkflow include WebHelper + include ShopWorkflow it 'has two locales available' do expect(Rails.application.config.i18n[:default_locale]).to eq 'en' @@ -16,28 +17,58 @@ feature 'Multilingual', js: true do expect(get_i18n_translation('label_shops')).to eq 'Shops' end - it 'can switch language by params' do - visit root_path - expect(get_i18n_locale).to eq 'en' - expect(get_i18n_translation('label_shops')).to eq 'Shops' - expect(page.driver.browser.cookies['locale']).to be_nil - expect(page).to have_content 'Interested in getting on the Open Food Network?' - expect(page).to have_content 'SHOPS' + context 'can switch language by params' do + scenario 'in root path' do + visit root_path + expect(get_i18n_locale).to eq 'en' + expect(get_i18n_translation('label_shops')).to eq 'Shops' + expect(page.driver.browser.cookies['locale']).to be_nil + expect(page).to have_content 'Interested in getting on the Open Food Network?' + expect(page).to have_content 'SHOPS' - visit root_path(locale: 'es') - expect(get_i18n_locale).to eq 'es' - expect(get_i18n_translation('label_shops')).to eq 'Tiendas' - expect(page.driver.browser.cookies['locale'].value).to eq 'es' - expect(page).to have_content '¿Estás interesada en entrar en Open Food Network?' - expect(page).to have_content 'TIENDAS' + visit root_path(locale: 'es') + expect(get_i18n_locale).to eq 'es' + expect(get_i18n_translation('label_shops')).to eq 'Tiendas' + expect(page.driver.browser.cookies['locale'].value).to eq 'es' + expect(page).to have_content '¿Estás interesada en entrar en Open Food Network?' + expect(page).to have_content 'TIENDAS' - # it is not in the list of available of available_locales - visit root_path(locale: 'it') - expect(get_i18n_locale).to eq 'es' - expect(get_i18n_translation('label_shops')).to eq 'Tiendas' - expect(page.driver.browser.cookies['locale'].value).to eq 'es' - expect(page).to have_content '¿Estás interesada en entrar en Open Food Network?' - expect(page).to have_content 'TIENDAS' + # it is not in the list of available of available_locales + visit root_path(locale: 'it') + expect(get_i18n_locale).to eq 'es' + expect(get_i18n_translation('label_shops')).to eq 'Tiendas' + expect(page.driver.browser.cookies['locale'].value).to eq 'es' + expect(page).to have_content '¿Estás interesada en entrar en Open Food Network?' + expect(page).to have_content 'TIENDAS' + end + + context 'with a product in the cart' do + let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) } + let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], variants: [product.variants.first]) } + let(:product) { create(:simple_product) } + let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) } + + before do + set_order order + add_product_to_cart order, product, quantity: 1 + end + + scenario "in the cart page" do + visit spree.cart_path(locale: 'es') + + expect(page).to have_content 'TIENDAS' + expect(page.driver.browser.cookies['locale'].value).to eq 'es' + expect(page).to have_content 'Precio' + end + + scenario "in the checkout page" do + visit checkout_path(locale: 'es') + + expect(page).to have_content 'TIENDAS' + expect(page.driver.browser.cookies['locale'].value).to eq 'es' + expect(page).to have_content 'Total del carrito' + end + end end context 'with user' do From 034d9ef72a871bf9d9b168e8e928f347cf5ebacd Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 23 Oct 2018 13:59:33 +0100 Subject: [PATCH 31/65] Refactor multilingual spec: extract method with repeated assertion --- spec/features/consumer/multilingual_spec.rb | 27 ++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/spec/features/consumer/multilingual_spec.rb b/spec/features/consumer/multilingual_spec.rb index 98d2c385ca..e0c0793154 100644 --- a/spec/features/consumer/multilingual_spec.rb +++ b/spec/features/consumer/multilingual_spec.rb @@ -29,17 +29,15 @@ feature 'Multilingual', js: true do visit root_path(locale: 'es') expect(get_i18n_locale).to eq 'es' expect(get_i18n_translation('label_shops')).to eq 'Tiendas' - expect(page.driver.browser.cookies['locale'].value).to eq 'es' + expect_menu_and_cookie_in_es expect(page).to have_content '¿Estás interesada en entrar en Open Food Network?' - expect(page).to have_content 'TIENDAS' # it is not in the list of available of available_locales visit root_path(locale: 'it') expect(get_i18n_locale).to eq 'es' expect(get_i18n_translation('label_shops')).to eq 'Tiendas' - expect(page.driver.browser.cookies['locale'].value).to eq 'es' + expect_menu_and_cookie_in_es expect(page).to have_content '¿Estás interesada en entrar en Open Food Network?' - expect(page).to have_content 'TIENDAS' end context 'with a product in the cart' do @@ -56,16 +54,14 @@ feature 'Multilingual', js: true do scenario "in the cart page" do visit spree.cart_path(locale: 'es') - expect(page).to have_content 'TIENDAS' - expect(page.driver.browser.cookies['locale'].value).to eq 'es' + expect_menu_and_cookie_in_es expect(page).to have_content 'Precio' end scenario "in the checkout page" do visit checkout_path(locale: 'es') - expect(page).to have_content 'TIENDAS' - expect(page.driver.browser.cookies['locale'].value).to eq 'es' + expect_menu_and_cookie_in_es expect(page).to have_content 'Total del carrito' end end @@ -77,12 +73,12 @@ feature 'Multilingual', js: true do it 'updates user locale from cookie if it is empty' do visit root_path(locale: 'es') - expect(page.driver.browser.cookies['locale'].value).to eq 'es' + expect_menu_and_cookie_in_es expect(user.locale).to be_nil quick_login_as user visit root_path - expect(page.driver.browser.cookies['locale'].value).to eq 'es' + expect_menu_and_cookie_in_es end it 'updates user locale and stays in cookie after logout' do @@ -94,9 +90,8 @@ feature 'Multilingual', js: true do logout - expect(page.driver.browser.cookies['locale'].value).to eq 'es' + expect_menu_and_cookie_in_es expect(page).to have_content '¿Estás interesada en entrar en Open Food Network?' - expect(page).to have_content 'TIENDAS' end end @@ -134,9 +129,13 @@ feature 'Multilingual', js: true do find('li a[href="?locale=es"]').click end - expect(page.driver.browser.cookies['locale'].value).to eq 'es' - expect(page).to have_content 'TIENDAS' + expect_menu_and_cookie_in_es end end end end + +def expect_menu_and_cookie_in_es + expect(page.driver.browser.cookies['locale'].value).to eq 'es' + expect(page).to have_content 'TIENDAS' +end From c9784a5eda3524d5a4091a382043efce310fa083 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 23 Oct 2018 17:53:08 +0100 Subject: [PATCH 32/65] Add Portuguese and German to list of moment locales. The main usage is in the order cycle closure time in the shopfront --- app/assets/javascripts/admin/all.js | 2 ++ app/assets/javascripts/darkswarm/all.js.coffee | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js index eddb8a0dfb..8bc986290a 100644 --- a/app/assets/javascripts/admin/all.js +++ b/app/assets/javascripts/admin/all.js @@ -54,12 +54,14 @@ //= require i18n/translations //= require darkswarm/i18n.translate.js //= require moment +//= require moment/de.js //= require moment/en-gb.js //= require moment/es.js //= require moment/fr.js //= require moment/it.js //= require moment/nb.js //= require moment/pt-br.js +//= require moment/pt.js //= require moment/sv.js //= require ../shared/mm-foundation-tpls-0.9.0-20180826174721.min.js //= require angularjs-file-upload diff --git a/app/assets/javascripts/darkswarm/all.js.coffee b/app/assets/javascripts/darkswarm/all.js.coffee index f781481627..7a745448c3 100644 --- a/app/assets/javascripts/darkswarm/all.js.coffee +++ b/app/assets/javascripts/darkswarm/all.js.coffee @@ -25,12 +25,14 @@ #= require angular-flash.min.js # #= require moment +#= require moment/de.js #= require moment/en-gb.js #= require moment/es.js #= require moment/fr.js #= require moment/it.js #= require moment/nb.js #= require moment/pt-br.js +#= require moment/pt.js #= require moment/sv.js # #= require modernizr From dc5302ca0896394c4020b46f82102d6465f4ca54 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 25 Oct 2018 11:33:14 +1100 Subject: [PATCH 33/65] Speed up database queries and make them scale This commit makes use of three ActiveRecord features: 1. Using `select` instead of `all.map` enables ActiveRecord to nest one select into the other, resulting in one more efficient query instead of two. 2. Using `find_each` saves memory by loading records in batches. https://api.rubyonrails.org/classes/ActiveRecord/Batches.html#method-i-find_each 3. Using `pluck` creates only an array, avoiding loading all the other columns of the records into objects. Running this on the current Canadian database, fixes the following variant overrides: ``` [] [] [] [] [] [] [925, 924, 966, 965] [] [] [] [] [462, 863, 464, 822, 949, 947, 944, 939, 942, 946, 945, 943, 438, 937, 938, 941, 940, 467, 952, 875, 453, 953, 454, 951, 487, 460, 457, 528, 527, 486, 459, 458, 461, 529, 530, 950, 642, 384, 380, 643, 385, 381, 644, 386, 382, 960, 959, 379, 640, 377, 375, 532, 639, 376, 374, 646, 390, 389, 637, 406, 408, 647, 391, 393, 633, 396, 400, 398, 645, 388, 387, 648, 394, 392, 536, 632, 399, 397, 395, 634, 403, 401, 635, 404, 402, 636, 407, 405, 535, 534, 638, 410, 409, 948, 533, 537, 531, 877, 880, 894, 893, 672, 671, 673, 674, 703, 714, 715, 716, 717, 862, 864, 879, 876, 865, 881, 878, 463, 954, 866, 823, 957, 958, 955, 956, 899, 897] [] [969] ``` --- ...020103501_revoke_variant_overrideswithout_permissions.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/db/migrate/20181020103501_revoke_variant_overrideswithout_permissions.rb b/db/migrate/20181020103501_revoke_variant_overrideswithout_permissions.rb index bd2d0e750b..d8140c9bf3 100644 --- a/db/migrate/20181020103501_revoke_variant_overrideswithout_permissions.rb +++ b/db/migrate/20181020103501_revoke_variant_overrideswithout_permissions.rb @@ -2,11 +2,11 @@ class RevokeVariantOverrideswithoutPermissions < ActiveRecord::Migration def up # This process was executed when the permission_revoked_at colum was created (see AddPermissionRevokedAtToVariantOverrides) # It needs to be repeated due to #2739 - variant_override_hubs = Enterprise.where(id: VariantOverride.all.map(&:hub_id).uniq) + variant_override_hubs = Enterprise.where(id: VariantOverride.select(:hub_id).uniq) - variant_override_hubs.each do |hub| + variant_override_hubs.find_each do |hub| permitting_producer_ids = hub.relationships_as_child - .with_permission(:create_variant_overrides).map(&:parent_id) + .with_permission(:create_variant_overrides).pluck(:parent_id) variant_overrides_with_revoked_permissions = VariantOverride.for_hubs(hub) .joins(variant: :product).where("spree_products.supplier_id NOT IN (?)", permitting_producer_ids) From e6adb8a3b9997ac9b518e26ca1b37421c742d393 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 25 Oct 2018 13:53:22 +1100 Subject: [PATCH 34/65] Extract email setup in specs for easy upgrade The way we set up email sending completely changes with Spree 2. This change encapsulates that code in a single method so that it can be changed easily and doesn't create further merge conflicts while we are still working on the master branch and the Spree upgrade. --- .../admin/manager_invitations_controller_spec.rb | 6 ++++-- .../admin/subscriptions_controller_spec.rb | 6 ++---- .../controllers/spree/admin/orders_controller_spec.rb | 6 ++---- spec/controllers/spree/orders_controller_spec.rb | 7 +++---- .../controllers/user_confirmations_controller_spec.rb | 4 +++- spec/controllers/user_passwords_controller_spec.rb | 4 +++- .../controllers/user_registrations_controller_spec.rb | 3 ++- spec/features/admin/enterprise_roles_spec.rb | 3 ++- spec/features/admin/users_spec.rb | 3 ++- spec/features/consumer/account/settings_spec.rb | 3 ++- spec/features/consumer/authentication_spec.rb | 3 ++- spec/features/consumer/confirm_invitation_spec.rb | 3 ++- spec/features/consumer/shopping/orders_spec.rb | 6 ++---- spec/jobs/subscription_confirm_job_spec.rb | 7 +++---- spec/mailers/enterprise_mailer_spec.rb | 4 +++- spec/mailers/order_mailer_spec.rb | 3 ++- spec/mailers/producer_mailer_spec.rb | 7 +++---- spec/mailers/subscription_mailer_spec.rb | 3 ++- spec/mailers/user_mailer_spec.rb | 4 +++- spec/models/order_cycle_spec.rb | 7 +++---- spec/models/spree/order_spec.rb | 7 +++---- spec/models/spree/user_spec.rb | 6 ++++-- spec/support/email_helper.rb | 11 +++++++++++ 23 files changed, 68 insertions(+), 48 deletions(-) create mode 100644 spec/support/email_helper.rb diff --git a/spec/controllers/admin/manager_invitations_controller_spec.rb b/spec/controllers/admin/manager_invitations_controller_spec.rb index 6a2623220f..0b86ef3b66 100644 --- a/spec/controllers/admin/manager_invitations_controller_spec.rb +++ b/spec/controllers/admin/manager_invitations_controller_spec.rb @@ -2,6 +2,8 @@ require 'spec_helper' module Admin describe ManagerInvitationsController, type: :controller do + include OpenFoodNetwork::EmailHelper + let!(:enterprise_owner) { create(:user) } let!(:other_enterprise_user) { create(:user) } let!(:existing_user) { create(:user) } @@ -25,7 +27,7 @@ module Admin context "signing up a new user" do before do - create(:mail_method) + setup_email controller.stub spree_current_user: admin end @@ -46,7 +48,7 @@ module Admin describe "with enterprise permissions" do context "as user with proper enterprise permissions" do before do - create(:mail_method) + setup_email controller.stub spree_current_user: enterprise_owner end diff --git a/spec/controllers/admin/subscriptions_controller_spec.rb b/spec/controllers/admin/subscriptions_controller_spec.rb index 28834f50f6..5f83bf0529 100644 --- a/spec/controllers/admin/subscriptions_controller_spec.rb +++ b/spec/controllers/admin/subscriptions_controller_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Admin::SubscriptionsController, type: :controller do include AuthenticationWorkflow + include OpenFoodNetwork::EmailHelper describe 'index' do let!(:user) { create(:user, enterprise_limit: 10) } @@ -626,10 +627,7 @@ describe Admin::SubscriptionsController, type: :controller do context "when at least one associate orders is 'canceled'" do before do - Spree::MailMethod.create!( - environment: Rails.env, - preferred_mails_from: 'spree@example.com' - ) + setup_email proxy_order.cancel end diff --git a/spec/controllers/spree/admin/orders_controller_spec.rb b/spec/controllers/spree/admin/orders_controller_spec.rb index 8d76ea3a8d..3013225a14 100644 --- a/spec/controllers/spree/admin/orders_controller_spec.rb +++ b/spec/controllers/spree/admin/orders_controller_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Spree::Admin::OrdersController, type: :controller do include AuthenticationWorkflow + include OpenFoodNetwork::EmailHelper context "updating an order with line items" do let!(:order) { create(:order) } @@ -181,10 +182,7 @@ describe Spree::Admin::OrdersController, type: :controller do context "when the distributor's ABN has been set" do before { distributor.update_attribute(:abn, "123") } before do - Spree::MailMethod.create!( - environment: Rails.env, - preferred_mails_from: 'spree@example.com' - ) + setup_email end it "should allow me to send order invoices" do expect do diff --git a/spec/controllers/spree/orders_controller_spec.rb b/spec/controllers/spree/orders_controller_spec.rb index d2f2cbf8da..bf574a55bd 100644 --- a/spec/controllers/spree/orders_controller_spec.rb +++ b/spec/controllers/spree/orders_controller_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Spree::OrdersController, type: :controller do + include OpenFoodNetwork::EmailHelper + let(:distributor) { double(:distributor) } let(:order) { create(:order) } let(:order_cycle) { create(:simple_order_cycle) } @@ -372,10 +374,7 @@ describe Spree::OrdersController, type: :controller do let(:order) { create(:completed_order_with_totals, user: user) } before do - Spree::MailMethod.create!( - environment: Rails.env, - preferred_mails_from: 'spree@example.com' - ) + setup_email end it "responds with success" do diff --git a/spec/controllers/user_confirmations_controller_spec.rb b/spec/controllers/user_confirmations_controller_spec.rb index f8f38883c8..854a5c1d9e 100644 --- a/spec/controllers/user_confirmations_controller_spec.rb +++ b/spec/controllers/user_confirmations_controller_spec.rb @@ -2,6 +2,8 @@ require 'spec_helper' describe UserConfirmationsController, type: :controller do include AuthenticationWorkflow + include OpenFoodNetwork::EmailHelper + let!(:user) { create_enterprise_user } let!(:confirmed_user) { create_enterprise_user(confirmed_at: nil) } let!(:unconfirmed_user) { create_enterprise_user(confirmed_at: nil) } @@ -57,7 +59,7 @@ describe UserConfirmationsController, type: :controller do end context "requesting confirmation instructions to be resent" do - before { create(:mail_method) } + before { setup_email } it "redirects the user to login" do spree_post :create, { spree_user: { email: unconfirmed_user.email } } diff --git a/spec/controllers/user_passwords_controller_spec.rb b/spec/controllers/user_passwords_controller_spec.rb index 42e505e172..1fa79519e9 100644 --- a/spec/controllers/user_passwords_controller_spec.rb +++ b/spec/controllers/user_passwords_controller_spec.rb @@ -2,6 +2,8 @@ require 'spec_helper' require 'spree/api/testing_support/helpers' describe UserPasswordsController, type: :controller do + include OpenFoodNetwork::EmailHelper + let(:user) { create(:user) } let(:unconfirmed_user) { create(:user, confirmed_at: nil) } @@ -32,7 +34,7 @@ describe UserPasswordsController, type: :controller do end it "renders Darkswarm" do - Spree::MailMethod.create!(environment: 'test') + setup_email clear_jobs user.send_reset_password_instructions diff --git a/spec/controllers/user_registrations_controller_spec.rb b/spec/controllers/user_registrations_controller_spec.rb index cb9d278774..7ed7951c50 100644 --- a/spec/controllers/user_registrations_controller_spec.rb +++ b/spec/controllers/user_registrations_controller_spec.rb @@ -2,9 +2,10 @@ require 'spec_helper' require 'spree/api/testing_support/helpers' describe UserRegistrationsController, type: :controller do + include OpenFoodNetwork::EmailHelper before(:all) do - create(:mail_method) + setup_email end before do diff --git a/spec/features/admin/enterprise_roles_spec.rb b/spec/features/admin/enterprise_roles_spec.rb index a0d075741b..8d001c52a2 100644 --- a/spec/features/admin/enterprise_roles_spec.rb +++ b/spec/features/admin/enterprise_roles_spec.rb @@ -6,6 +6,7 @@ feature %q{ }, js: true do include AuthenticationWorkflow include WebHelper + include OpenFoodNetwork::EmailHelper context "as a site administrator" do @@ -137,7 +138,7 @@ feature %q{ end it "can invite unregistered users to be managers" do - create(:mail_method) + setup_email find('a.button.help-modal').click expect(page).to have_css '#invite-manager-modal' diff --git a/spec/features/admin/users_spec.rb b/spec/features/admin/users_spec.rb index 6b2cbc2177..1e57a95285 100644 --- a/spec/features/admin/users_spec.rb +++ b/spec/features/admin/users_spec.rb @@ -2,10 +2,11 @@ require "spec_helper" feature "Managing users" do include AuthenticationWorkflow + include OpenFoodNetwork::EmailHelper context "as super-admin" do before do - create(:mail_method) + setup_email quick_login_as_admin end diff --git a/spec/features/consumer/account/settings_spec.rb b/spec/features/consumer/account/settings_spec.rb index aaea521671..28f97b9071 100644 --- a/spec/features/consumer/account/settings_spec.rb +++ b/spec/features/consumer/account/settings_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' feature "Account Settings", js: true do include AuthenticationWorkflow + include OpenFoodNetwork::EmailHelper describe "as a logged in user" do let(:user) do @@ -12,7 +13,7 @@ feature "Account Settings", js: true do end before do - create(:mail_method) + setup_email quick_login_as user visit "/account" click_link I18n.t('spree.users.show.tabs.settings') diff --git a/spec/features/consumer/authentication_spec.rb b/spec/features/consumer/authentication_spec.rb index 24a03e4d7e..be29540e94 100644 --- a/spec/features/consumer/authentication_spec.rb +++ b/spec/features/consumer/authentication_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' feature "Authentication", js: true, retry: 3 do include UIComponentHelper + include OpenFoodNetwork::EmailHelper # Attempt to address intermittent failures in these specs around do |example| @@ -75,7 +76,7 @@ feature "Authentication", js: true, retry: 3 do end scenario "Signing up successfully" do - create(:mail_method) + setup_email fill_in "Email", with: "test@foo.com" fill_in "Choose a password", with: "test12345" fill_in "Confirm password", with: "test12345" diff --git a/spec/features/consumer/confirm_invitation_spec.rb b/spec/features/consumer/confirm_invitation_spec.rb index c42c2ff8f0..dd5499cbb0 100644 --- a/spec/features/consumer/confirm_invitation_spec.rb +++ b/spec/features/consumer/confirm_invitation_spec.rb @@ -2,13 +2,14 @@ require "spec_helper" feature "Confirm invitation as manager" do include UIComponentHelper # for be_logged_in_as + include OpenFoodNetwork::EmailHelper describe "confirm email and set password" do let(:email) { "test@example.org" } let(:user) { Spree::User.create(email: email, unconfirmed_email: email, password: "secret") } before do - create(:mail_method) + setup_email user.reset_password_token = Devise.friendly_token user.reset_password_sent_at = Time.now.utc user.save! diff --git a/spec/features/consumer/shopping/orders_spec.rb b/spec/features/consumer/shopping/orders_spec.rb index 4f0adfe772..3d1182e95e 100644 --- a/spec/features/consumer/shopping/orders_spec.rb +++ b/spec/features/consumer/shopping/orders_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' feature "Order Management", js: true do include AuthenticationWorkflow + include OpenFoodNetwork::EmailHelper describe "viewing a completed order" do let!(:distributor) { create(:distributor_enterprise) } @@ -115,10 +116,7 @@ feature "Order Management", js: true do context "when the distributor allows changes to be made to orders" do before do - Spree::MailMethod.create!( - environment: Rails.env, - preferred_mails_from: 'spree@example.com' - ) + setup_email end before do order.distributor.update_attributes(allow_order_changes: true) diff --git a/spec/jobs/subscription_confirm_job_spec.rb b/spec/jobs/subscription_confirm_job_spec.rb index 7556a30478..3c6383f196 100644 --- a/spec/jobs/subscription_confirm_job_spec.rb +++ b/spec/jobs/subscription_confirm_job_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe SubscriptionConfirmJob do + include OpenFoodNetwork::EmailHelper + let(:job) { SubscriptionConfirmJob.new } describe "finding proxy_orders that are ready to be confirmed" do @@ -114,10 +116,7 @@ describe SubscriptionConfirmJob do while !order.completed? do break unless order.next! end allow(job).to receive(:send_confirm_email).and_call_original job.instance_variable_set(:@order, order) - Spree::MailMethod.create!( - environment: Rails.env, - preferred_mails_from: 'spree@example.com' - ) + setup_email expect(job).to receive(:record_order).with(order) end diff --git a/spec/mailers/enterprise_mailer_spec.rb b/spec/mailers/enterprise_mailer_spec.rb index a31a9ec0e2..ab92a123d2 100644 --- a/spec/mailers/enterprise_mailer_spec.rb +++ b/spec/mailers/enterprise_mailer_spec.rb @@ -1,12 +1,14 @@ require 'spec_helper' describe EnterpriseMailer do + include OpenFoodNetwork::EmailHelper + let!(:enterprise) { create(:enterprise) } let!(:user) { create(:user) } before do ActionMailer::Base.deliveries = [] - Spree::MailMethod.create!(environment: 'test') + setup_email end describe "#welcome" do diff --git a/spec/mailers/order_mailer_spec.rb b/spec/mailers/order_mailer_spec.rb index 49de985325..e21edceebd 100644 --- a/spec/mailers/order_mailer_spec.rb +++ b/spec/mailers/order_mailer_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Spree::OrderMailer do - let!(:mail_method) { create(:mail_method, preferred_mails_from: 'spree@example.com') } + include OpenFoodNetwork::EmailHelper describe "order confimation" do after do @@ -9,6 +9,7 @@ describe Spree::OrderMailer do end before do + setup_email ActionMailer::Base.delivery_method = :test ActionMailer::Base.perform_deliveries = true ActionMailer::Base.deliveries = [] diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index 5fd1fc172d..aeb4359d24 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -2,11 +2,10 @@ require 'spec_helper' require 'yaml' describe ProducerMailer do + include OpenFoodNetwork::EmailHelper + before do - Spree::MailMethod.create!( - environment: Rails.env, - preferred_mails_from: 'spree@example.com' - ) + setup_email end let!(:zone) { create(:zone_with_member) } let!(:tax_rate) { create(:tax_rate, included_in_price: true, calculator: Spree::Calculator::DefaultTax.new, zone: zone, amount: 0.1) } diff --git a/spec/mailers/subscription_mailer_spec.rb b/spec/mailers/subscription_mailer_spec.rb index 7936cd54bd..907eb51141 100644 --- a/spec/mailers/subscription_mailer_spec.rb +++ b/spec/mailers/subscription_mailer_spec.rb @@ -2,8 +2,9 @@ require 'spec_helper' describe SubscriptionMailer do include ActionView::Helpers::SanitizeHelper + include OpenFoodNetwork::EmailHelper - let!(:mail_method) { create(:mail_method, preferred_mails_from: 'spree@example.com') } + before { setup_email } describe "order placement" do let(:shop) { create(:enterprise) } diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index d8d9b58744..8082d0f6a6 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Spree::UserMailer do + include OpenFoodNetwork::EmailHelper + let(:user) { build(:user) } after do @@ -12,7 +14,7 @@ describe Spree::UserMailer do ActionMailer::Base.perform_deliveries = true ActionMailer::Base.deliveries = [] - Spree::MailMethod.create!(environment: 'test') + setup_email end it "sends an email when given a user" do diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index efd0f3ba03..3ffd650ddd 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe OrderCycle do + include OpenFoodNetwork::EmailHelper + it "should be valid when built from factory" do build(:simple_order_cycle).should be_valid end @@ -525,10 +527,7 @@ describe OrderCycle do let!(:order5) { create(:completed_order_with_totals, distributor: shop, user: user, order_cycle: oc) } before do - Spree::MailMethod.create!( - environment: Rails.env, - preferred_mails_from: 'spree@example.com' - ) + setup_email end before { order5.cancel } diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index e07fa41d54..d81b4b8596 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Spree::Order do + include OpenFoodNetwork::EmailHelper + describe "setting variant attributes" do it "sets attributes on line items for variants" do d = create(:distributor_enterprise) @@ -495,10 +497,7 @@ describe Spree::Order do describe "scopes" do describe "not_state" do before do - Spree::MailMethod.create!( - environment: Rails.env, - preferred_mails_from: 'spree@example.com' - ) + setup_email end it "finds only orders not in specified state" do diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb index ae54a9eca0..7169930489 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Spree.user_class do + include OpenFoodNetwork::EmailHelper + describe "associations" do it { should have_many(:owned_enterprises) } @@ -72,7 +74,7 @@ describe Spree.user_class do context "#create" do it "should send a confirmation email" do - create(:mail_method) + setup_email expect do create(:user, email: 'new_user@example.com', confirmation_sent_at: nil, confirmed_at: nil) @@ -100,7 +102,7 @@ describe Spree.user_class do context "confirming email" do it "should send a welcome email" do - create(:mail_method) + setup_email expect do create(:user, confirmed_at: nil).confirm! diff --git a/spec/support/email_helper.rb b/spec/support/email_helper.rb new file mode 100644 index 0000000000..7ba3c19f1c --- /dev/null +++ b/spec/support/email_helper.rb @@ -0,0 +1,11 @@ +module OpenFoodNetwork + module EmailHelper + # Some specs trigger actions that send emails, for example creating an order. + # But sending emails doesn't work out-of-the-box. This code sets it up. + # It's here in a single place to allow an easy upgrade to Spree 2 which + # needs a different implementation of this method. + def setup_email + create(:mail_method) + end + end +end From 14eaa4920407df84ec3ddfa31aeae1759b120178 Mon Sep 17 00:00:00 2001 From: Kristina Lim Date: Thu, 25 Oct 2018 19:29:07 +0800 Subject: [PATCH 35/65] Disable hiding the off-canvas upon window resize. In some browsers for mobile devices, the address bar is automatically hidden when scrolling down the page. This is not workable if the height of the contents of the off-canvas exceeds the height of the screen, because the latter portion of the contents stays hidden to the user. https://github.com/openfoodfoundation/angular-foundation/blob/0.9.0-20180826174721/src/offcanvas/offcanvas.js --- .../directives/off_canvas_wrap.js.coffee | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 app/assets/javascripts/darkswarm/directives/off_canvas_wrap.js.coffee diff --git a/app/assets/javascripts/darkswarm/directives/off_canvas_wrap.js.coffee b/app/assets/javascripts/darkswarm/directives/off_canvas_wrap.js.coffee new file mode 100644 index 0000000000..68c4b331d8 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/off_canvas_wrap.js.coffee @@ -0,0 +1,22 @@ +# Extend the "offCanvasWrap" directive in "angular-foundation" to disable hiding of the off-canvas +# upon window resize. +# +# In some browsers for mobile devices, the address bar is automatically hidden when scrolling down +# the page. This is not workable if the height of the contents of the off-canvas exceeds the height +# of the screen, because the latter portion of the contents stays hidden to the user. +# +# https://github.com/openfoodfoundation/angular-foundation/blob/0.9.0-20180826174721/src/offcanvas/offcanvas.js +angular.module('mm.foundation.offcanvas').directive 'offCanvasWrap', ($window) -> + { + restrict: 'C' + priority: 1 + link: ($scope, element, attrs) -> + win = angular.element($window) + + # Get the scope used by the "offCanvasWrap" directive: + # https://github.com/openfoodfoundation/angular-foundation/blob/0.9.0-20180826174721/src/offcanvas/offcanvas.js#L2 + isolatedScope = element.isolateScope() + + # Unbind hiding of the off-canvas upon window resize. + win.unbind('resize.body', isolatedScope.hide) + } From 2cc594dc98c2ef7ef522bbbbf84eeee024b69b07 Mon Sep 17 00:00:00 2001 From: Kristina Lim Date: Thu, 25 Oct 2018 20:34:52 +0800 Subject: [PATCH 36/65] Proceed to hide off-canvas when resized to >1024px --- .../darkswarm/directives/off_canvas_wrap.js.coffee | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/assets/javascripts/darkswarm/directives/off_canvas_wrap.js.coffee b/app/assets/javascripts/darkswarm/directives/off_canvas_wrap.js.coffee index 68c4b331d8..dea2448d34 100644 --- a/app/assets/javascripts/darkswarm/directives/off_canvas_wrap.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/off_canvas_wrap.js.coffee @@ -5,6 +5,9 @@ # the page. This is not workable if the height of the contents of the off-canvas exceeds the height # of the screen, because the latter portion of the contents stays hidden to the user. # +# However, for screens over 1024px width for which the off-canvas is not styled to be visible, we +# can proceed to hide this. +# # https://github.com/openfoodfoundation/angular-foundation/blob/0.9.0-20180826174721/src/offcanvas/offcanvas.js angular.module('mm.foundation.offcanvas').directive 'offCanvasWrap', ($window) -> { @@ -19,4 +22,8 @@ angular.module('mm.foundation.offcanvas').directive 'offCanvasWrap', ($window) - # Unbind hiding of the off-canvas upon window resize. win.unbind('resize.body', isolatedScope.hide) + + # Bind hiding of the off-canvas that only happens when screen width is over 1024px. + win.bind 'resize.body', -> + isolatedScope.hide() if $(window).width() > 1024 } From 09f8df6340f617cb8c6469c5773f85d58c0d12f3 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Thu, 25 Oct 2018 18:06:48 +0100 Subject: [PATCH 37/65] Reorganize LineItemsController's destroy line item specs to separate contexts with different test orders (completed_with_total and completed_with_fees) --- .../controllers/line_items_controller_spec.rb | 78 ++++++++++--------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/spec/controllers/line_items_controller_spec.rb b/spec/controllers/line_items_controller_spec.rb index 24f69423f5..411a77c36a 100644 --- a/spec/controllers/line_items_controller_spec.rb +++ b/spec/controllers/line_items_controller_spec.rb @@ -27,70 +27,72 @@ describe LineItemsController, type: :controller do end end - describe "destroying a line item on a completed order" do - let(:item) do - order = create(:completed_order_with_totals) - item = create(:line_item, order: order) - while !order.completed? do break unless order.next! end - item - end - - let(:order) { item.order } - let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], variants: [order.line_item_variants]) } - - before { controller.stub spree_current_user: item.order.user } - - context "without a line item id" do - it "fails and raises an error" do - delete :destroy - expect(response.status).to eq 404 + describe "destroying a line item" do + context "on a completed order" do + let(:item) do + order = create(:completed_order_with_totals) + item = create(:line_item, order: order) + while !order.completed? do break unless order.next! end + item end - end - context "with a line item id" do - let(:params) { { format: :json, id: item } } + let(:order) { item.order } + let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], variants: [order.line_item_variants]) } - context "where the item's order is not associated with the user" do - it "denies deletion" do - delete :destroy, params - expect(response.status).to eq 403 + before { controller.stub spree_current_user: item.order.user } + + context "without a line item id" do + it "fails and raises an error" do + delete :destroy + expect(response.status).to eq 404 end end - context "where the item's order is associated with the current user" do - before { order.update_attributes!(user_id: user.id) } + context "with a line item id" do + let(:params) { { format: :json, id: item } } - context "without an order cycle or distributor" do + context "where the item's order is not associated with the user" do it "denies deletion" do delete :destroy, params expect(response.status).to eq 403 end end - context "with an order cycle and distributor" do - before { order.update_attributes!(order_cycle_id: order_cycle.id, distributor_id: distributor.id) } + context "where the item's order is associated with the current user" do + before { order.update_attributes!(user_id: user.id) } - context "where changes are not allowed" do + context "without an order cycle or distributor" do it "denies deletion" do delete :destroy, params expect(response.status).to eq 403 end end - context "where changes are allowed" do - before { distributor.update_attributes!(allow_order_changes: true) } + context "with an order cycle and distributor" do + before { order.update_attributes!(order_cycle_id: order_cycle.id, distributor_id: distributor.id) } - it "deletes the line item" do - delete :destroy, params - expect(response.status).to eq 204 - expect { item.reload }.to raise_error ActiveRecord::RecordNotFound + context "where changes are not allowed" do + it "denies deletion" do + delete :destroy, params + expect(response.status).to eq 403 + end + end + + context "where changes are allowed" do + before { distributor.update_attributes!(allow_order_changes: true) } + + it "deletes the line item" do + delete :destroy, params + expect(response.status).to eq 204 + expect { item.reload }.to raise_error ActiveRecord::RecordNotFound + end end end end end end - context "where shipping and payment fees apply" do + context "on a completed order with shipping and payment fees" do let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true, allow_order_changes: true) } let(:shipping_fee) { 3 } let(:payment_fee) { 5 } @@ -125,7 +127,7 @@ describe LineItemsController, type: :controller do end end - context "where enterprise fees apply" do + context "on a completed order with enterprise fees" do let(:user) { create(:user) } let(:variant) { create(:variant) } let(:distributor) { create(:distributor_enterprise, allow_order_changes: true) } From 77251848eed2e504b282ff8bf5e7862ba6be9fc5 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 22 Oct 2018 22:09:24 +0100 Subject: [PATCH 38/65] Refactor Spree::Admin::OrdersController --- .../admin/orders_controller_decorator.rb | 80 ++----------------- app/services/search_orders.rb | 41 ++++++++++ .../spree/admin/orders/_filters.html.haml | 22 ++--- .../spree/admin/orders_controller_spec.rb | 7 +- 4 files changed, 63 insertions(+), 87 deletions(-) create mode 100644 app/services/search_orders.rb diff --git a/app/controllers/spree/admin/orders_controller_decorator.rb b/app/controllers/spree/admin/orders_controller_decorator.rb index fe8dc46445..4344ce98f7 100644 --- a/app/controllers/spree/admin/orders_controller_decorator.rb +++ b/app/controllers/spree/admin/orders_controller_decorator.rb @@ -20,50 +20,17 @@ Spree::Admin::OrdersController.class_eval do before_filter :require_distributor_abn, only: :invoice - respond_to :html, :json - # Mostly the original Spree method, tweaked to allow us to ransack with completed_at in a sane way def index - params[:q] ||= {} - params[:q][:completed_at_not_null] ||= '1' if Spree::Config[:show_only_complete_orders_by_default] - @show_only_completed = params[:q][:completed_at_not_null].present? - params[:q][:s] ||= @show_only_completed ? 'completed_at desc' : 'created_at desc' + @results = SearchOrders.new(params, spree_current_user) if json_request? - # As date params are deleted if @show_only_completed, store - # the original date so we can restore them into the params - # after the search - created_at_gt = params[:q][:created_at_gt] - created_at_lt = params[:q][:created_at_lt] - - params[:q].delete(:inventory_units_shipment_id_null) if params[:q][:inventory_units_shipment_id_null] == "0" - - if !params[:q][:created_at_gt].blank? - params[:q][:created_at_gt] = Time.zone.parse(params[:q][:created_at_gt]).beginning_of_day rescue "" - end - - if !params[:q][:created_at_lt].blank? - params[:q][:created_at_lt] = Time.zone.parse(params[:q][:created_at_lt]).end_of_day rescue "" - end - - # Changed this to stop completed_at being overriden when present - if @show_only_completed - params[:q][:completed_at_gt] = params[:q].delete(:created_at_gt) unless params[:q][:completed_at_gt] - params[:q][:completed_at_lt] = params[:q].delete(:created_at_lt) unless params[:q][:completed_at_gt] - end - - @orders = orders - - # Restore dates - params[:q][:created_at_gt] = created_at_gt - params[:q][:created_at_lt] = created_at_lt - - respond_with(@orders) do |format| + respond_to do |format| format.html format.json do render json: { - orders: ActiveModel::ArraySerializer.new(@orders, each_serializer: Api::Admin::OrderSerializer), - pagination: pagination_data + orders: serialized_orders, + pagination: @results.pagination_data } end end @@ -102,42 +69,6 @@ Spree::Admin::OrdersController.class_eval do private - def orders - if json_request? - @search = OpenFoodNetwork::Permissions.new(spree_current_user).editable_orders.ransack(params[:q]) - else - @search = Spree::Order.accessible_by(current_ability, :index).ransack(params[:q]) - - # Replaced this search to filter orders to only show those distributed by current user (or all for admin user) - @search.result.includes([:user, :shipments, :payments]).distributed_by_user(spree_current_user) - end - - search_results - end - - def search_results - if using_pagination? - @search.result.page(params[:page]).per(params[:per_page] || Spree::Config[:orders_per_page]) - else - @search.result - end - end - - def using_pagination? - params[:per_page] - end - - def pagination_data - if using_pagination? - { - results: @orders.total_count, - pages: @orders.num_pages, - page: params[:page].to_i, - per_page: params[:per_page].to_i - } - end - end - def require_distributor_abn unless @order.distributor.abn.present? flash[:error] = t(:must_have_valid_business_number, enterprise_name: @order.distributor.name) @@ -166,4 +97,7 @@ Spree::Admin::OrdersController.class_eval do end end + def serialized_orders + ActiveModel::ArraySerializer.new(@results.orders, each_serializer: Api::Admin::OrderSerializer) + end end diff --git a/app/services/search_orders.rb b/app/services/search_orders.rb new file mode 100644 index 0000000000..b4ae35e2e4 --- /dev/null +++ b/app/services/search_orders.rb @@ -0,0 +1,41 @@ +class SearchOrders + attr_reader :orders + + def initialize(params, current_user) + @params = params + @current_user = current_user + + @orders = fetch_orders + end + + def pagination_data + return unless using_pagination? + { + results: @orders.total_count, + pages: @orders.num_pages, + page: params[:page].to_i, + per_page: params[:per_page].to_i + } + end + + private + + attr_reader :params, :current_user + + def fetch_orders + @search = OpenFoodNetwork::Permissions.new(current_user).editable_orders.ransack(params[:q]) + + return paginated_results if using_pagination? + @search.result + end + + def paginated_results + @search.result + .page(params[:page]) + .per(params[:per_page] || Spree::Config[:orders_per_page]) + end + + def using_pagination? + params[:per_page] + end +end diff --git a/app/views/spree/admin/orders/_filters.html.haml b/app/views/spree/admin/orders/_filters.html.haml index 5ebea2a4cd..29b16b19a0 100644 --- a/app/views/spree/admin/orders/_filters.html.haml +++ b/app/views/spree/admin/orders/_filters.html.haml @@ -1,38 +1,40 @@ %div{"data-hook" => "admin_orders_index_search"} - = search_form_for [:admin, @search], html: { name: "orders_form", "ng-submit" => "fetchResults()"} do |f| + = form_tag false, {name: "orders_form", "ng-submit" => "fetchResults()"} do .field-block.alpha.four.columns .date-range-filter.field = label_tag nil, t(:date_range) .date-range-fields - = f.text_field :created_at_gt, class: 'datepicker', datepicker: 'q.created_at_gt', 'ng-model' => 'q.created_at_gt', :value => params[:q][:created_at_gt], :placeholder => t(:start) + = text_field_tag "q[created_at_gt]", nil, class: 'datepicker', datepicker: 'q.created_at_gt', 'ng-model' => 'q.created_at_gt', :placeholder => t(:start) %span.range-divider %i.icon-arrow-right - = f.text_field :created_at_lt, class: 'datepicker', datepicker: 'q.created_at_lt', 'ng-model' => 'q.created_at_lt', :value => params[:q][:created_at_lt], :placeholder => t(:stop) + = text_field_tag "q[created_at_lt]", nil, class: 'datepicker', datepicker: 'q.created_at_lt', 'ng-model' => 'q.created_at_lt', :placeholder => t(:stop) .field = label_tag nil, t(:status) - = f.select :state_eq, Spree::Order.state_machines[:state].states.collect {|s| [t("order_state.#{s.name}"), s.value]}, {:include_blank => true}, :class => 'select2', 'ng-model' => 'q.state_eq' + = select_tag("q[state_eq]", + options_for_select(Spree::Order.state_machines[:state].states.collect {|s| [t("order_state.#{s.name}"), s.value]}), + {include_blank: true, class: 'select2', 'ng-model' => 'q.state_eq'}) .four.columns .field = label_tag nil, t(:order_number) - = f.text_field :number_cont, 'ng-model' => 'q.number_cont' + = text_field_tag "q[number_cont]", nil, 'ng-model' => 'q.number_cont' .field = label_tag nil, t(:email) - = f.email_field :email_cont, 'ng-model' => 'q.email_cont' + = email_field_tag "q[email_cont", nil, 'ng-model' => 'q.email_cont' .four.columns .field = label_tag nil, t(:first_name_begins_with) - = f.text_field :bill_address_firstname_start, :size => 25, 'ng-model' => 'q.bill_address_firstname_start' + = text_field_tag "q[bill_address_firstname_start]", nil, size: 25, 'ng-model' => 'q.bill_address_firstname_start' .field = label_tag nil, t(:last_name_begins_with) - = f.text_field :bill_address_lastname_start, :size => 25, 'ng-model' => 'q.bill_address_lastname_start' + = text_field_tag "q[bill_address_lastname_start]", nil, size: 25, 'ng-model' => 'q.bill_address_lastname_start' .omega.four.columns .field.checkbox %label - = f.check_box :completed_at_not_null, {:checked => @show_only_completed, 'ng-model' => 'q.completed_at_not_null'}, '1', '' + = check_box_tag "q[completed_at_not_null]", 1, true, {'ng-model' => 'q.completed_at_not_null'} = t(:show_only_complete_orders) .field.checkbox %label - = f.check_box :inventory_units_shipment_id_null, {'ng-model' => 'q.inventory_units_shipment_id_null'}, '1', '0' + = check_box_tag "q[inventory_units_shipment_id_null]", 1, false, {'ng-model' => 'q.inventory_units_shipment_id_null'} = t(:show_only_unfulfilled_orders) .field-block.alpha.eight.columns = label_tag nil, t(:distributors) diff --git a/spec/controllers/spree/admin/orders_controller_spec.rb b/spec/controllers/spree/admin/orders_controller_spec.rb index 8d76ea3a8d..1685b9506a 100644 --- a/spec/controllers/spree/admin/orders_controller_spec.rb +++ b/spec/controllers/spree/admin/orders_controller_spec.rb @@ -70,14 +70,13 @@ describe Spree::Admin::OrdersController, type: :controller do order_attributes.all?{ |attr| keys.include? attr }.should == true end - it "sorts orders in descending id order" do + it "sorts orders in ascending id order" do ids = json_response['orders'].map{ |order| order['id'] } - ids[0].should > ids[1] - ids[1].should > ids[2] + expect(ids[0]).to be < ids[1] + expect(ids[1]).to be < ids[2] end it "formats completed_at to 'yyyy-mm-dd hh:mm'" do - pp json_response json_response['orders'].map{ |order| order['completed_at'] }.all?{ |a| a == order1.completed_at.strftime('%B %d, %Y') }.should == true end From 0bd67bd06a618edb02ca839e1f1ffccf2bcb5fa8 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 23 Oct 2018 08:18:49 +0100 Subject: [PATCH 39/65] Remove unnecessary Spree::Config fallback value --- app/services/search_orders.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/search_orders.rb b/app/services/search_orders.rb index b4ae35e2e4..127a53625e 100644 --- a/app/services/search_orders.rb +++ b/app/services/search_orders.rb @@ -32,7 +32,7 @@ class SearchOrders def paginated_results @search.result .page(params[:page]) - .per(params[:per_page] || Spree::Config[:orders_per_page]) + .per(params[:per_page]) end def using_pagination? From 334eebeab1cb9f8d3e11d372989b4ba35c5bdc0e Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 23 Oct 2018 10:34:42 +0100 Subject: [PATCH 40/65] Move json to API and strip index action --- .../resources/order_resource.js.coffee | 1 + app/controllers/api/orders_controller.rb | 23 +++++++++++++++++++ .../admin/orders_controller_decorator.rb | 16 +------------ config/routes.rb | 2 ++ 4 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 app/controllers/api/orders_controller.rb diff --git a/app/assets/javascripts/admin/resources/resources/order_resource.js.coffee b/app/assets/javascripts/admin/resources/resources/order_resource.js.coffee index d5679c629e..9bf7ad5838 100644 --- a/app/assets/javascripts/admin/resources/resources/order_resource.js.coffee +++ b/app/assets/javascripts/admin/resources/resources/order_resource.js.coffee @@ -1,6 +1,7 @@ angular.module("admin.resources").factory 'OrderResource', ($resource) -> $resource('/admin/orders/:id/:action.json', {}, { 'index': + url: '/api/orders.json' method: 'GET' 'update': method: 'PUT' diff --git a/app/controllers/api/orders_controller.rb b/app/controllers/api/orders_controller.rb new file mode 100644 index 0000000000..a03eb3b36b --- /dev/null +++ b/app/controllers/api/orders_controller.rb @@ -0,0 +1,23 @@ +module Api + class OrdersController < BaseController + def index + authorize! :admin, Spree::Order + + search_results = SearchOrders.new(params, spree_current_user) + + render json: { + orders: serialized_orders(search_results.orders), + pagination: search_results.pagination_data + } + end + + private + + def serialized_orders(orders) + ActiveModel::ArraySerializer.new( + orders, + each_serializer: Api::Admin::OrderSerializer + ) + end + end +end diff --git a/app/controllers/spree/admin/orders_controller_decorator.rb b/app/controllers/spree/admin/orders_controller_decorator.rb index 4344ce98f7..2ef5245af1 100644 --- a/app/controllers/spree/admin/orders_controller_decorator.rb +++ b/app/controllers/spree/admin/orders_controller_decorator.rb @@ -23,17 +23,7 @@ Spree::Admin::OrdersController.class_eval do respond_to :html, :json def index - @results = SearchOrders.new(params, spree_current_user) if json_request? - - respond_to do |format| - format.html - format.json do - render json: { - orders: serialized_orders, - pagination: @results.pagination_data - } - end - end + # Moved to api. Overriding the action so we only render the page template. end # Overwrite to use confirm_email_for_customer instead of confirm_email. @@ -96,8 +86,4 @@ Spree::Admin::OrdersController.class_eval do render 'set_distribution', locals: { order: @order } end end - - def serialized_orders - ActiveModel::ArraySerializer.new(@results.orders, each_serializer: Api::Admin::OrderSerializer) - end end diff --git a/config/routes.rb b/config/routes.rb index bc3fc303f0..1196e950ee 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -103,6 +103,8 @@ Openfoodnetwork::Application.routes.draw do get :accessible, on: :collection end + resources :orders, only: [:index] + resource :status do get :job_queue end From 46b131715931c1af48d1f520b8ffc4852f148a13 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 23 Oct 2018 12:34:16 +0100 Subject: [PATCH 41/65] Update orders resource js spec --- .../line_items/controllers/line_items_controller_spec.js.coffee | 2 +- .../unit/admin/orders/services/orders_spec.js.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee b/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee index 1b23dea59d..3b04893b34 100644 --- a/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee @@ -37,7 +37,7 @@ describe "LineItemsCtrl", -> order = { id: 9, order_cycle: { id: 4 }, distributor: { id: 5 }, number: "R123456" } lineItem = { id: 7, quantity: 3, order: { id: 9 }, supplier: { id: 1 } } - httpBackend.expectGET("/admin/orders.json?q%5Bcompleted_at_gteq%5D=SomeDate&q%5Bcompleted_at_lt%5D=SomeDate&q%5Bcompleted_at_not_null%5D=true&q%5Bstate_not_eq%5D=canceled").respond {orders: [order], pagination: {page: 1, pages: 1, results: 1}} + httpBackend.expectGET("/api/orders.json?q%5Bcompleted_at_gteq%5D=SomeDate&q%5Bcompleted_at_lt%5D=SomeDate&q%5Bcompleted_at_not_null%5D=true&q%5Bstate_not_eq%5D=canceled").respond {orders: [order], pagination: {page: 1, pages: 1, results: 1}} httpBackend.expectGET("/admin/bulk_line_items.json?q%5Border%5D%5Bcompleted_at_gteq%5D=SomeDate&q%5Border%5D%5Bcompleted_at_lt%5D=SomeDate&q%5Border%5D%5Bcompleted_at_not_null%5D=true&q%5Border%5D%5Bstate_not_eq%5D=canceled").respond [lineItem] httpBackend.expectGET("/admin/enterprises/visible.json?ams_prefix=basic&q%5Bsells_in%5D%5B%5D=own&q%5Bsells_in%5D%5B%5D=any").respond [distributor] httpBackend.expectGET("/admin/order_cycles.json?ams_prefix=basic&as=distributor&q%5Borders_close_at_gt%5D=SomeDate").respond [orderCycle] diff --git a/spec/javascripts/unit/admin/orders/services/orders_spec.js.coffee b/spec/javascripts/unit/admin/orders/services/orders_spec.js.coffee index 68bc3beb1f..8372ee6819 100644 --- a/spec/javascripts/unit/admin/orders/services/orders_spec.js.coffee +++ b/spec/javascripts/unit/admin/orders/services/orders_spec.js.coffee @@ -19,7 +19,7 @@ describe "Orders service", -> beforeEach -> response = { orders: [{ id: 5, name: 'Order 1'}], pagination: {page: 1, pages: 1, results: 1} } - $httpBackend.expectGET('/admin/orders.json').respond 200, response + $httpBackend.expectGET('/api/orders.json').respond 200, response result = Orders.index() $httpBackend.flush() From 1f8f7b0495efe3c0a48c1ede480bf0c8560071a4 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 23 Oct 2018 12:26:16 +0100 Subject: [PATCH 42/65] Add new Api::OrdersController spec --- .../controllers/api/orders_controller_spec.rb | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 spec/controllers/api/orders_controller_spec.rb diff --git a/spec/controllers/api/orders_controller_spec.rb b/spec/controllers/api/orders_controller_spec.rb new file mode 100644 index 0000000000..acd41e044c --- /dev/null +++ b/spec/controllers/api/orders_controller_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +module Api + describe OrdersController, type: :controller do + include AuthenticationWorkflow + render_views + + describe '#index' do + let!(:distributor) { create(:distributor_enterprise) } + let!(:order1) { create(:order, distributor: distributor) } + let!(:order2) { create(:order, distributor: distributor) } + let!(:order3) { create(:order, distributor: distributor) } + + let(:enterprise_user) { distributor.owner } + let(:regular_user) { create(:user) } + + context 'as a regular user' do + before do + allow(controller).to receive(:spree_current_user) { regular_user } + end + + it "returns unauthorized" do + get :index + assert_unauthorized! + end + end + + context 'as an enterprise user' do + before do + allow(controller).to receive(:spree_current_user) { enterprise_user } + end + + it "returns serialized orders" do + get :index + + expect(response.status).to eq 200 + expect(json_response['orders'].count).to eq 3 + expect(json_response['orders'].first.keys).to include 'id', 'number', 'email', 'distributor' + expect(json_response['pagination']).to be_nil + end + end + + context 'with pagination' do + before do + allow(controller).to receive(:spree_current_user) { enterprise_user } + end + + it 'returns pagination data when query params contain :per_page]' do + get :index, per_page: 15, page: 1 + + expect(json_response['pagination']).to eq pagination_data + end + end + end + + def pagination_data + { + 'results' => 3, + 'pages' => 1, + 'page' => 1, + 'per_page' => 15 + } + end + end +end From 033ae4f57071b34a30e008172dd35eb52fecbcf1 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 23 Oct 2018 14:22:07 +0100 Subject: [PATCH 43/65] Add basic SearchOrders service spec --- spec/services/search_orders_spec.rb | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 spec/services/search_orders_spec.rb diff --git a/spec/services/search_orders_spec.rb b/spec/services/search_orders_spec.rb new file mode 100644 index 0000000000..1f58e400a3 --- /dev/null +++ b/spec/services/search_orders_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe SearchOrders do + let!(:distributor) { create(:distributor_enterprise) } + let!(:order1) { create(:order, distributor: distributor) } + let!(:order2) { create(:order, distributor: distributor) } + let!(:order3) { create(:order, distributor: distributor) } + + let(:enterprise_user) { distributor.owner } + + describe '#orders' do + let(:params) { {} } + let(:service) { SearchOrders.new(params, enterprise_user) } + + it 'returns orders' do + expect(service.orders.count).to eq 3 + end + end + + describe '#pagination_data' do + let(:params) { { per_page: 15, page: 1 } } + let(:service) { SearchOrders.new(params, enterprise_user) } + + it 'returns pagination data' do + expect(service.pagination_data).to eq pagination_data + end + end + + def pagination_data + { + results: 3, + pages: 1, + page: 1, + per_page: 15 + } + end +end From 8c19d1afec49c63bf76623297ed4328324d9a0da Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 23 Oct 2018 14:23:28 +0100 Subject: [PATCH 44/65] Move tests from spree/admin/orders_controller_spec to api/orders_controller_spec and tidy up --- .../controllers/api/orders_controller_spec.rb | 129 +++++++++++++++--- .../spree/admin/orders_controller_spec.rb | 101 +------------- 2 files changed, 119 insertions(+), 111 deletions(-) diff --git a/spec/controllers/api/orders_controller_spec.rb b/spec/controllers/api/orders_controller_spec.rb index acd41e044c..2838d587ff 100644 --- a/spec/controllers/api/orders_controller_spec.rb +++ b/spec/controllers/api/orders_controller_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require 'spree/api/testing_support/helpers' module Api describe OrdersController, type: :controller do @@ -7,42 +8,120 @@ module Api describe '#index' do let!(:distributor) { create(:distributor_enterprise) } - let!(:order1) { create(:order, distributor: distributor) } - let!(:order2) { create(:order, distributor: distributor) } - let!(:order3) { create(:order, distributor: distributor) } - - let(:enterprise_user) { distributor.owner } - let(:regular_user) { create(:user) } + let!(:distributor2) { create(:distributor_enterprise) } + let!(:supplier) { create(:supplier_enterprise) } + let!(:coordinator) { create(:distributor_enterprise) } + let!(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator) } + let!(:order1) do + create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.zone.now, + distributor: distributor, billing_address: create(:address) ) + end + let!(:order2) do + create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.zone.now, + distributor: distributor2, billing_address: create(:address) ) + end + let!(:order3) do + create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.zone.now, + distributor: distributor, billing_address: create(:address) ) + end + let!(:line_item1) do + create(:line_item, order: order1, + product: create(:product, supplier: supplier)) + end + let!(:line_item2) do + create(:line_item, order: order2, + product: create(:product, supplier: supplier)) + end + let!(:line_item3) do + create(:line_item, order: order2, + product: create(:product, supplier: supplier)) + end + let!(:line_item4) do + create(:line_item, order: order3, + product: create(:product, supplier: supplier)) + end + let!(:regular_user) { create(:user) } + let!(:admin_user) { create(:admin_user) } context 'as a regular user' do before do allow(controller).to receive(:spree_current_user) { regular_user } + get :index end it "returns unauthorized" do - get :index assert_unauthorized! end end - context 'as an enterprise user' do + context 'as an admin user' do before do - allow(controller).to receive(:spree_current_user) { enterprise_user } + allow(controller).to receive(:spree_current_user) { admin_user } + get :index end - it "returns serialized orders" do - get :index + it "retrieves a list of orders with appropriate attributes, + including line items with appropriate attributes" do - expect(response.status).to eq 200 - expect(json_response['orders'].count).to eq 3 - expect(json_response['orders'].first.keys).to include 'id', 'number', 'email', 'distributor' - expect(json_response['pagination']).to be_nil + returns_orders(json_response) + end + + it "formats completed_at to 'yyyy-mm-dd hh:mm'" do + completed_dates = json_response['orders'].map{ |order| order['completed_at'] } + correct_formats = completed_dates.all?{ |a| a == order1.completed_at.strftime('%B %d, %Y') } + + expect(correct_formats).to be_truthy + end + + it "returns distributor object with id key" do + distributors = json_response['orders'].map{ |order| order['distributor'] } + expect(distributors.all?{ |d| d.key?('id') }).to be_truthy + end + + it "returns the order number" do + order_numbers = json_response['orders'].map{ |order| order['number'] } + expect(order_numbers.all?{ |number| number.match("^R\\d{5,10}$") }).to be_truthy + end + end + + context 'as an enterprise user' do + context 'producer enterprise' do + before do + allow(controller).to receive(:spree_current_user) { supplier.owner } + get :index + end + + it "does not display line items for which my enterprise is a supplier" do + assert_unauthorized! + end + end + + context 'coordinator enterprise' do + before do + allow(controller).to receive(:spree_current_user) { coordinator.owner } + get :index + end + + it "retrieves a list of orders" do + returns_orders(json_response) + end + end + + context 'hub enterprise' do + before do + allow(controller).to receive(:spree_current_user) { distributor.owner } + get :index + end + + it "retrieves a list of orders" do + returns_orders(json_response) + end end end context 'with pagination' do before do - allow(controller).to receive(:spree_current_user) { enterprise_user } + allow(controller).to receive(:spree_current_user) { distributor.owner } end it 'returns pagination data when query params contain :per_page]' do @@ -53,9 +132,25 @@ module Api end end + private + + def returns_orders(response) + keys = response['orders'].first.keys.map(&:to_sym) + expect(order_attributes.all?{ |attr| keys.include? attr }).to be_truthy + end + + def order_attributes + [ + :id, :number, :full_name, :email, :phone, :completed_at, :display_total, + :show_path, :edit_path, :state, :payment_state, :shipment_state, + :payments_path, :shipments_path, :ship_path, :ready_to_ship, :created_at, + :distributor_name, :special_instructions, :payment_capture_path + ] + end + def pagination_data { - 'results' => 3, + 'results' => 2, 'pages' => 1, 'page' => 1, 'per_page' => 15 diff --git a/spec/controllers/spree/admin/orders_controller_spec.rb b/spec/controllers/spree/admin/orders_controller_spec.rb index 1685b9506a..2ed23a2118 100644 --- a/spec/controllers/spree/admin/orders_controller_spec.rb +++ b/spec/controllers/spree/admin/orders_controller_spec.rb @@ -30,111 +30,24 @@ describe Spree::Admin::OrdersController, type: :controller do end describe "#index" do - render_views - - let(:order_attributes) { [:id, :full_name, :email, :phone, :completed_at, :distributor, :order_cycle, :number] } - - def self.make_simple_data! - let!(:dist1) { FactoryBot.create(:distributor_enterprise) } - let!(:order1) { FactoryBot.create(:order, state: 'complete', completed_at: Time.zone.now, distributor: dist1, billing_address: FactoryBot.create(:address) ) } - let!(:order2) { FactoryBot.create(:order, state: 'complete', completed_at: Time.zone.now, distributor: dist1, billing_address: FactoryBot.create(:address) ) } - let!(:order3) { FactoryBot.create(:order, state: 'complete', completed_at: Time.zone.now, distributor: dist1, billing_address: FactoryBot.create(:address) ) } - let!(:line_item1) { FactoryBot.create(:line_item, order: order1) } - let!(:line_item2) { FactoryBot.create(:line_item, order: order2) } - let!(:line_item3) { FactoryBot.create(:line_item, order: order2) } - let!(:line_item4) { FactoryBot.create(:line_item, order: order3) } - let(:line_item_attributes) { [:id, :quantity, :max_quantity, :supplier, :units_product, :units_variant] } - end - - context "as a normal user" do + context "as a regular user" do before { controller.stub spree_current_user: create_enterprise_user } - make_simple_data! - it "should deny me access to the index action" do - spree_get :index, :format => :json + spree_get :index expect(response).to redirect_to spree.unauthorized_path end end - context "as an administrator" do - make_simple_data! + context "as an enterprise user" do + let!(:order) { create(:order_with_distributor) } before do - controller.stub spree_current_user: quick_login_as_admin - spree_get :index, :format => :json + controller.stub spree_current_user: order.distributor.owner end - it "retrieves a list of orders with appropriate attributes, including line items with appropriate attributes" do - keys = json_response['orders'].first.keys.map{ |key| key.to_sym } - order_attributes.all?{ |attr| keys.include? attr }.should == true - end - - it "sorts orders in ascending id order" do - ids = json_response['orders'].map{ |order| order['id'] } - expect(ids[0]).to be < ids[1] - expect(ids[1]).to be < ids[2] - end - - it "formats completed_at to 'yyyy-mm-dd hh:mm'" do - json_response['orders'].map{ |order| order['completed_at'] }.all?{ |a| a == order1.completed_at.strftime('%B %d, %Y') }.should == true - end - - it "returns distributor object with id key" do - json_response['orders'].map{ |order| order['distributor'] }.all?{ |d| d.has_key?('id') }.should == true - end - - it "retrieves the order number" do - json_response['orders'].map{ |order| order['number'] }.all?{ |number| number.match("^R\\d{5,10}$") }.should == true - end - end - - context "as an enterprise user" do - let(:supplier) { create(:supplier_enterprise) } - let(:distributor1) { create(:distributor_enterprise) } - let(:distributor2) { create(:distributor_enterprise) } - let(:coordinator) { create(:distributor_enterprise) } - let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator) } - let!(:order1) { FactoryBot.create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.zone.now, distributor: distributor1, billing_address: FactoryBot.create(:address) ) } - let!(:line_item1) { FactoryBot.create(:line_item, order: order1, product: FactoryBot.create(:product, supplier: supplier)) } - let!(:line_item2) { FactoryBot.create(:line_item, order: order1, product: FactoryBot.create(:product, supplier: supplier)) } - let!(:order2) { FactoryBot.create(:order, order_cycle: order_cycle, state: 'complete', completed_at: Time.zone.now, distributor: distributor2, billing_address: FactoryBot.create(:address) ) } - let!(:line_item3) { FactoryBot.create(:line_item, order: order2, product: FactoryBot.create(:product, supplier: supplier)) } - - context "producer enterprise" do - - before do - controller.stub spree_current_user: supplier.owner - spree_get :index, :format => :json - end - - it "does not display line items for which my enterprise is a supplier" do - expect(response).to redirect_to spree.unauthorized_path - end - end - - context "coordinator enterprise" do - before do - controller.stub spree_current_user: coordinator.owner - spree_get :index, :format => :json - end - - it "retrieves a list of orders" do - keys = json_response['orders'].first.keys.map{ |key| key.to_sym } - order_attributes.all?{ |attr| keys.include? attr }.should == true - end - end - - context "hub enterprise" do - before do - controller.stub spree_current_user: distributor1.owner - spree_get :index, :format => :json - end - - it "retrieves a list of orders" do - keys = json_response['orders'].first.keys.map{ |key| key.to_sym } - order_attributes.all?{ |attr| keys.include? attr }.should == true - end + it "should allow access" do + expect(response.status).to eq 200 end end end From 29492d61f96cc7dfdbe2d83f5e6dd15f77c6218a Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 26 Oct 2018 21:29:58 +0100 Subject: [PATCH 45/65] Use #allow syntax --- .../spree/admin/orders_controller_spec.rb | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/controllers/spree/admin/orders_controller_spec.rb b/spec/controllers/spree/admin/orders_controller_spec.rb index 2ed23a2118..83e455771c 100644 --- a/spec/controllers/spree/admin/orders_controller_spec.rb +++ b/spec/controllers/spree/admin/orders_controller_spec.rb @@ -31,7 +31,7 @@ describe Spree::Admin::OrdersController, type: :controller do describe "#index" do context "as a regular user" do - before { controller.stub spree_current_user: create_enterprise_user } + before { allow(controller).to receive(:spree_current_user) { create_enterprise_user } } it "should deny me access to the index action" do spree_get :index @@ -42,9 +42,7 @@ describe Spree::Admin::OrdersController, type: :controller do context "as an enterprise user" do let!(:order) { create(:order_with_distributor) } - before do - controller.stub spree_current_user: order.distributor.owner - end + before { allow(controller).to receive(:spree_current_user) { order.distributor.owner } } it "should allow access" do expect(response.status).to eq 200 @@ -60,7 +58,7 @@ describe Spree::Admin::OrdersController, type: :controller do let(:params) { { id: order.number } } context "as a normal user" do - before { controller.stub spree_current_user: user } + before { allow(controller).to receive(:spree_current_user) { user } } it "should prevent me from sending order invoices" do spree_get :invoice, params @@ -70,7 +68,8 @@ describe Spree::Admin::OrdersController, type: :controller do context "as an enterprise user" do context "which is not a manager of the distributor for an order" do - before { controller.stub spree_current_user: user } + before { allow(controller).to receive(:spree_current_user) { user } } + it "should prevent me from sending order invoices" do spree_get :invoice, params expect(response).to redirect_to spree.unauthorized_path @@ -78,7 +77,8 @@ describe Spree::Admin::OrdersController, type: :controller do end context "which is a manager of the distributor for an order" do - before { controller.stub spree_current_user: distributor.owner } + before { allow(controller).to receive(:spree_current_user) { distributor.owner } } + context "when the distributor's ABN has not been set" do before { distributor.update_attribute(:abn, "") } it "should allow me to send order invoices" do @@ -117,7 +117,7 @@ describe Spree::Admin::OrdersController, type: :controller do let(:params) { { id: order.number } } context "as a normal user" do - before { controller.stub spree_current_user: user } + before { allow(controller).to receive(:spree_current_user) { user } } it "should prevent me from sending order invoices" do spree_get :print, params @@ -127,7 +127,7 @@ describe Spree::Admin::OrdersController, type: :controller do context "as an enterprise user" do context "which is not a manager of the distributor for an order" do - before { controller.stub spree_current_user: user } + before { allow(controller).to receive(:spree_current_user) { user } } it "should prevent me from sending order invoices" do spree_get :print, params expect(response).to redirect_to spree.unauthorized_path @@ -135,7 +135,7 @@ describe Spree::Admin::OrdersController, type: :controller do end context "which is a manager of the distributor for an order" do - before { controller.stub spree_current_user: distributor.owner } + before { allow(controller).to receive(:spree_current_user) { distributor.owner } } it "should allow me to send order invoices" do spree_get :print, params expect(response).to render_template :invoice From b826beb0d78b440243cb016c212253e853357682 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 26 Oct 2018 21:34:27 +0100 Subject: [PATCH 46/65] Inline hashes in tests --- spec/controllers/api/orders_controller_spec.rb | 16 +++++++--------- spec/services/search_orders_spec.rb | 16 +++++++--------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/spec/controllers/api/orders_controller_spec.rb b/spec/controllers/api/orders_controller_spec.rb index 2838d587ff..9bcdbeb7ad 100644 --- a/spec/controllers/api/orders_controller_spec.rb +++ b/spec/controllers/api/orders_controller_spec.rb @@ -127,6 +127,13 @@ module Api it 'returns pagination data when query params contain :per_page]' do get :index, per_page: 15, page: 1 + pagination_data = { + 'results' => 2, + 'pages' => 1, + 'page' => 1, + 'per_page' => 15 + } + expect(json_response['pagination']).to eq pagination_data end end @@ -147,14 +154,5 @@ module Api :distributor_name, :special_instructions, :payment_capture_path ] end - - def pagination_data - { - 'results' => 2, - 'pages' => 1, - 'page' => 1, - 'per_page' => 15 - } - end end end diff --git a/spec/services/search_orders_spec.rb b/spec/services/search_orders_spec.rb index 1f58e400a3..481ef75601 100644 --- a/spec/services/search_orders_spec.rb +++ b/spec/services/search_orders_spec.rb @@ -22,16 +22,14 @@ describe SearchOrders do let(:service) { SearchOrders.new(params, enterprise_user) } it 'returns pagination data' do + pagination_data = { + results: 3, + pages: 1, + page: 1, + per_page: 15 + } + expect(service.pagination_data).to eq pagination_data end end - - def pagination_data - { - results: 3, - pages: 1, - page: 1, - per_page: 15 - } - end end From f3124e5472fb2734d08ef5b8a50c391670be3479 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 26 Oct 2018 22:42:47 +0100 Subject: [PATCH 47/65] Explicitly add :host to actionmailer config --- config/environments/production.rb | 2 +- config/environments/staging.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/environments/production.rb b/config/environments/production.rb index 0ee6be4930..88c743071a 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -31,7 +31,7 @@ Openfoodnetwork::Application.configure do config.force_ssl = true # Use https when creating links in emails - config.action_mailer.default_url_options = { protocol: 'https' } + config.action_mailer.default_url_options = { protocol: 'https', host: Spree::Config[:site_url] } # See everything in the log (default is :info) config.log_level = :info diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 9c7a40e8e1..f43d42f998 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -31,7 +31,7 @@ Openfoodnetwork::Application.configure do config.force_ssl = true # Use https when creating links in emails - config.action_mailer.default_url_options = { protocol: 'https' } + config.action_mailer.default_url_options = { protocol: 'https', host: Spree::Config[:site_url] } # See everything in the log (default is :info) # config.log_level = :debug From 443515dae44ef04cb43e73e30d757ab5ead1ccec Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 29 Oct 2018 09:33:43 +0000 Subject: [PATCH 48/65] Improve comment in index action --- app/controllers/spree/admin/orders_controller_decorator.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/spree/admin/orders_controller_decorator.rb b/app/controllers/spree/admin/orders_controller_decorator.rb index 2ef5245af1..f7e1b56929 100644 --- a/app/controllers/spree/admin/orders_controller_decorator.rb +++ b/app/controllers/spree/admin/orders_controller_decorator.rb @@ -23,7 +23,8 @@ Spree::Admin::OrdersController.class_eval do respond_to :html, :json def index - # Moved to api. Overriding the action so we only render the page template. + # Overriding the action so we only render the page template. An angular request + # within the page then fetches the data it needs from Api::OrdersController end # Overwrite to use confirm_email_for_customer instead of confirm_email. From 91e4f99c19787ecd48328554acf09be55a7b1e96 Mon Sep 17 00:00:00 2001 From: Pedro Costa Date: Sun, 28 Oct 2018 20:20:15 +0000 Subject: [PATCH 49/65] Fix new preferences migration Why: * In a clean environment, running the new preferences migration fails, due to a missing file from Spree core being manually required. This file has now been missing since ab707cf. This change addresses the issue by: * Copying the missing file from Spree Core 1.3.6.beta into the migration. This fixes the issue for now, but also means that a migration merge and/or rewrite might be in order for the future. --- db/migrate/20120327000645_new_preferences.rb | 37 ++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/db/migrate/20120327000645_new_preferences.rb b/db/migrate/20120327000645_new_preferences.rb index 60fd79519e..86e5dbbf57 100644 --- a/db/migrate/20120327000645_new_preferences.rb +++ b/db/migrate/20120327000645_new_preferences.rb @@ -1,4 +1,37 @@ -require 'spree/core/preference_rescue' +# Spree 1.3.6.beta preference rescue implementation, required for the new +# preferences migration, which is broken since commit ab707cf due to the +# absence of this file. +# +# Migration: db/migrate/20120327000645_new_preferences.rb +# Source: https://raw.githubusercontent.com/spree/spree/1-3-stable/core/lib/spree/core/preference_rescue.rb +# +# rubocop:disable all +module Spree + class OldPrefs < ActiveRecord::Base + self.table_name = "spree_preferences" + belongs_to :owner, :polymorphic => true + attr_accessor :owner_klass + end + + class PreferenceRescue + def self.try + OldPrefs.where(:key => nil).each do |old_pref| + next unless owner = (old_pref.owner rescue nil) + unless old_pref.owner_type == "Spree::Activator" || old_pref.owner_type == "Spree::Configuration" + begin + old_pref.key = [owner.class.name, old_pref.name, owner.id].join('::').underscore + old_pref.value_type = owner.preference_type(old_pref.name) + puts "Migrating Preference: #{old_pref.key}" + old_pref.save + rescue NoMethodError => ex + puts ex.message + end + end + end + end + end +end +# rubocop:enable all class NewPreferences < ActiveRecord::Migration @@ -45,4 +78,4 @@ class NewPreferences < ActiveRecord::Migration add_column :spree_preferences, :group_id, :integer add_column :spree_preferences, :group_type, :string end -end \ No newline at end of file +end From 7f177598f27c34de0ea6607fac4156e72ee65374 Mon Sep 17 00:00:00 2001 From: Pau Perez Date: Tue, 30 Oct 2018 15:13:57 +0100 Subject: [PATCH 50/65] Update all locales with the latest Transifex translations --- config/locales/de_DE.yml | 22 ++++++++---- config/locales/en_GB.yml | 14 +++++++- config/locales/en_US.yml | 15 +++++++- config/locales/es.yml | 20 +++++++---- config/locales/fr.yml | 76 +++++++++++++++++++++++++++++++--------- config/locales/fr_CA.yml | 30 +++++++++++++--- config/locales/it.yml | 11 +++--- config/locales/nb.yml | 54 ++++++++++++++++++++++++---- config/locales/pt.yml | 13 +++---- config/locales/sv.yml | 8 +++-- 10 files changed, 208 insertions(+), 55 deletions(-) diff --git a/config/locales/de_DE.yml b/config/locales/de_DE.yml index c3bf6a6e68..a30abc8975 100644 --- a/config/locales/de_DE.yml +++ b/config/locales/de_DE.yml @@ -464,7 +464,6 @@ de_DE: 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 - line: Zeile index: select_file: Wählen Sie eine Tabelle zum Hochladen spreadsheet: Kalkulationstabelle @@ -498,7 +497,6 @@ de_DE: no_permission: Sie sind nicht berechtigt, dieses Unternehmen zu verwalten not_found: Unternehmen konnte nicht in der Datenbank gefunden werden no_name: Kein Name - blank_supplier: Manche Produkte haben einen leeren Anbieternamen reset_absent?: Fehlende Produkte zurücksetzen reset_absent_tip: Den Bestand für alle nicht in der Datei vorhandenen Produkte auf Null setzen overwrite_all: Alles überschreiben @@ -520,6 +518,9 @@ de_DE: item_line: Artikelzeile import_review: not_updatable_tip: "Die folgenden Felder können nicht über den Bulk-Import für bestehende Produkte aktualisiert werden:" + fields_ignored: Diese Felder werden ignoriert, wenn die importierten Produkte gespeichert werden. + entries_table: + not_updatable: Dieses Feld kann nicht über den Bulk-Import für vorhandene Produkte aktualisiert werden save_results: final_results: Endgültige Ergebnisse importieren products_created: Produkte erstellt @@ -1139,13 +1140,13 @@ de_DE: ticket_column_unit_price: "Stückpreis" ticket_column_total_price: "Gesamtpreis" menu_1_title: "Läden" - menu_1_url: "/ läden" + menu_1_url: "/ shops" menu_2_title: "Karte" - menu_2_url: "/karte" + menu_2_url: "/map" menu_3_title: "Erzeuger" - menu_3_url: "/ erzeuger" + menu_3_url: "/ producers" menu_4_title: "Gruppen" - menu_4_url: "/ gruppen" + menu_4_url: "/ groups" menu_5_title: "Über Uns" menu_5_url: "http://www.openfoodnetwork.org/" menu_6_title: "Verbinden" @@ -1284,6 +1285,7 @@ de_DE: cookies_policy_link: "Hinweise zu Cookies" cookies_accept_button: "Cookies akzeptieren" home_shop: Jetzt einkaufen + brandstory_headline: "Essen, ohne eigene Rechtspersönlichkeit." brandstory_intro: "Manchmal ist der beste Weg, das System zu reparieren, ein neues zu starten ..." 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." @@ -2500,10 +2502,18 @@ de_DE: inherits_properties_checkbox_hint: "Vererben Eigenschaften von %{supplier}? (außer oben aufgehoben)" orders: index: + listing_orders: "Bestellungen auflisten" + new_order: "Neue Bestellung" capture: "Erfassung" ship: "Liefern" edit: "Bearbeiten" + note: "Hinweis" + first: "Zuerst" + last: "Letzte" + previous: "Bisherige" next: "Weiter" + loading: "Wird geladen" + no_orders_found: "Keine Bestellungen gefunden" invoice: issued_on: Ausgegeben am tax_invoice: Steuerrechnung diff --git a/config/locales/en_GB.yml b/config/locales/en_GB.yml index 8a89b35ac7..1d5e7094d7 100644 --- a/config/locales/en_GB.yml +++ b/config/locales/en_GB.yml @@ -498,7 +498,6 @@ en_GB: no_permission: you do not have permission to manage this enterprise not_found: enterprise could not be found in database no_name: No name - blank_supplier: some products have blank supplier name reset_absent?: Reset absent products reset_absent_tip: Set stock to zero for all exiting products not present in the file overwrite_all: Overwrite all @@ -2503,11 +2502,19 @@ en_GB: orders: index: listing_orders: "Listing Orders" + new_order: "New Order" capture: "Capture" ship: "Ship" edit: "Edit" + note: "Note" + first: "First" + last: "Last" + previous: "Previous" next: "Next" + loading: "Loading" no_orders_found: "No Orders Found" + results_found: "%{number} Results found." + viewing: "Viewing %{start} to %{end}." invoice: issued_on: Issued on tax_invoice: TAX INVOICE @@ -2615,6 +2622,8 @@ en_GB: js_format: 'yy-mm-dd' inventory: Inventory orders: + edit: + login_to_view_order: "Please log in to view your order." bought: item: "Already ordered in this order cycle" order_mailer: @@ -2713,5 +2722,8 @@ en_GB: authorised_shops: Authorised Shops authorised_shops_popover: 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. 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?: "Allow Charges?" localized_number: invalid_format: has an invalid format. Please enter a number. diff --git a/config/locales/en_US.yml b/config/locales/en_US.yml index c6573470f5..47ba9990e5 100644 --- a/config/locales/en_US.yml +++ b/config/locales/en_US.yml @@ -498,7 +498,6 @@ en_US: no_permission: you do not have permission to manage this enterprise not_found: enterprise could not be found in database no_name: No name - blank_supplier: some products have blank supplier name reset_absent?: Reset absent products reset_absent_tip: Set stock to zero for all existing products not present in the file overwrite_all: Overwrite all @@ -521,6 +520,8 @@ en_US: import_review: not_updatable_tip: "The following fields cannot be updated via bulk import for existing products:" fields_ignored: These fields will be ignored when the imported products are saved. + entries_table: + not_updatable: This field is not updatable via bulk import on existing products save_results: final_results: Import final results products_created: Products created @@ -2416,6 +2417,9 @@ en_US: resolve: Resolve new_tag_rule_dialog: select_rule_type: "Select a rule type:" + orders: + index: + per_page: "%{results} per page" resend_user_email_confirmation: resend: "Resend" sending: "Resend..." @@ -2497,11 +2501,20 @@ en_US: inherits_properties_checkbox_hint: "Inherit properties from %{supplier}? (unless overridden above)" orders: index: + listing_orders: "Listing Orders" + new_order: "New Order" capture: "Capture" ship: "Ship" edit: "Edit" + note: "Note" + first: "First" + last: "Last" + previous: "Previous" next: "Next" + loading: "Loading" no_orders_found: "No Orders Found" + results_found: "%{number} Results found." + viewing: "Viewing %{start} to %{end}." invoice: issued_on: Issued on tax_invoice: TAX INVOICE diff --git a/config/locales/es.yml b/config/locales/es.yml index 198942497a..07d72f7c3a 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -464,7 +464,6 @@ es: products_no_permission: no tienes permiso para administrar productos para esta organización inventory_no_permission: no tienes permiso para crear inventario para esta productora none_saved: No se guardó ningún producto con éxito - line: Línea index: select_file: Selecciona una hoja de cálculo para subir spreadsheet: Hoja de cálculo @@ -498,7 +497,6 @@ es: no_permission: no tienes permiso para administrar esta organización not_found: no se pudo encontrar la organización en la base de datos no_name: Sin nombre - blank_supplier: algunos productos tienen un nombre de proveedor en blanco reset_absent?: Restablecer productos ausentes reset_absent_tip: Establezca el stock en cero para todos los productos existentes que no estén presentes en el archivo. overwrite_all: Sobrescribir todo @@ -1142,13 +1140,13 @@ es: ticket_column_unit_price: "Precio por unidad" ticket_column_total_price: "Precio total" menu_1_title: "Tiendas" - menu_1_url: "/tiendas" + menu_1_url: "/shops" menu_2_title: "Mapa" - menu_2_url: "/mapa" + menu_2_url: "/map" menu_3_title: "Productoras" - menu_3_url: "/productoras" + menu_3_url: "/producers" menu_4_title: "Redes" - menu_4_url: "/redes" + menu_4_url: "/groups" menu_5_title: "Acerca de" menu_5_url: "http://katuma.org/" menu_6_title: "Conectar" @@ -1272,6 +1270,10 @@ es: cookie_matomo_ignore_desc: "Cookie utilizada para excluir al usuario de ser rastreado." disabling_cookies_header: "Advertencia sobre la desactivación de cookies" disabling_cookies_desc: "Como usuario, siempre puede permitir, bloquear o eliminar las cookies de Open Food Network o cualquier otra página web cuando lo desee a través del control de configuración de su navegador. Cada navegador tiene una operativa diferente. Aquí están los enlaces:" + disabling_cookies_firefox_link: "https://support.mozilla.org/es/kb/habilitar-y-deshabilitar-cookies-sitios-web-rastrear-preferencias" + disabling_cookies_chrome_link: "https://support.google.com/chrome/answer/95647" + disabling_cookies_ie_link: "https://support.microsoft.com/es-es/help/17442/windows-internet-explorer-delete-manage-cookies" + disabling_cookies_safari_link: "https://www.apple.com/legal/privacy/es/cookies/" disabling_cookies_note: "Pero tenga en cuenta que si elimina o modifica las cookies esenciales utilizadas por Open Food Network, el sitio web no funcionará, no podrá agregar nada a su carrito ni realizar pedidos, por ejemplo." cookies_banner: cookies_usage: "Este sitio utiliza cookies para que su navegación sea fluida y segura, y para ayudarnos a comprender cómo lo usa para mejorar las funciones que ofrecemos." @@ -2712,8 +2714,14 @@ es: paid?: ¿Pagado? view: Ver saved_cards: + default?: ¿Por defecto? delete?: ¿Borrar? cards: authorised_shops: Tiendas autorizadas + authorised_shops_popover: Esta es la lista de tiendas a las que se les permite cobrar a su tarjeta de crédito predeterminada por sus suscripciones (es decir, pedidos repetidos) que pueda tener. Los detalles 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 cobra. + 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?" localized_number: invalid_format: tiene un formato invalido. Por favor introduzca un numero. diff --git a/config/locales/fr.yml b/config/locales/fr.yml index e7980fab89..fe63d1e709 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -55,6 +55,7 @@ fr: user_registrations: spree_user: signed_up_but_unconfirmed: "Un message avec un lien de confirmation a été envoyé à l'adresse email indiquée. Veuillez cliquer sur ce lien pour activer votre compte." + unknown_error: "Une erreur s'est glissée lors de la création de votre compte. Vérifiez votre addresse email et recommencez." failure: invalid: | Email / mot de passe incorrect. @@ -458,11 +459,12 @@ fr: conditional_blank: ne peut pas être vide si unit_type est vide no_product: n'a pu être associé à aucun produit existant dans la base de données not_found: non trouvé dans le base de données + not_updatable: ne peut pas être mis à jour pour des produits existants via la fonctionnalité d'import de fichier produits blank: Champ obligatoire products_no_permission: vous n'avez pas l'autorisation de gérer les produits de cette entreprise inventory_no_permission: vous n'avez pas l'autorisation d'ajouter les produits de ce producteur à votre catalogue boutique none_saved: n'a pu sauvegarder aucun produit :-( - line: Ligne + line_number: "Ligne %{number} :" index: select_file: Sélectionner le fichier (tableur sous format csv) à importer spreadsheet: Tableur csv @@ -496,7 +498,6 @@ fr: no_permission: vous n'avez pas l'autorisation de gérer cette entreprise not_found: l'entreprise n'a pas été trouvée dans la base de données no_name: Pas de nom - blank_supplier: certains produits ne sont associés à aucun fournisseur reset_absent?: Mettre à zéro le produits absents du fichier reset_absent_tip: Remettre le sock à zero pour les produits non présents dans le fichier. overwrite_all: Modifier pour tous @@ -516,6 +517,11 @@ fr: inventory_to_reset: Dans le catalogue boutique, le stock des produits existants va être remis à zéro line: Ligne item_line: Ligne produit concernée + import_review: + not_updatable_tip: "Les champs suivants ne peuvent pas être mis à jour pour des produits existants via la fonctionnalité d'import de fichier produits :" + fields_ignored: Ces champs seront ignorés à l'enregistrement des produits importés. + entries_table: + not_updatable: Ce champs ne peut pas être mis à jour pour des produits existants via la fonctionnalité d'import de fichier produits save_results: final_results: Importer les informations produits confirmées products_created: produits crées @@ -556,9 +562,6 @@ fr: controls: back_to_my_inventory: Retour à mon catalogue boutique orders: - index: - capture: "Payée" - ship: "Expédier" invoice_email_sent: 'L''email de facturation a bien été envoyé' order_email_resent: 'L''email de commande a de nouveau été envoyé' bulk_management: @@ -846,6 +849,8 @@ fr: filters: search_by_order_cycle_name: "Recherche par nom de Cycle de Vente..." involving: "Concernant" + any_enterprise: "Toutes les entreprises" + any_schedule: "Tous" form: incoming: Produits entrants (pouvant être mis en vente par les hubs) supplier: Fournisseur @@ -1050,6 +1055,11 @@ fr: stripe_connect_fail: Désolé, la connexion de votre compte Stripe a échoué :-( stripe_connect_settings: resource: Configuration de Stripe Connect + api: + enterprise_logo: + destroy_attachment_does_not_exist: "Aucun logo trouvé :-(" + enterprise_promo_image: + destroy_attachment_does_not_exist: "Aucune bannière trouvée :-(" checkout: already_ordered: cart: "panier" @@ -1158,6 +1168,7 @@ fr: footer_email: "Email" footer_links_md: "Liens" footer_about_url: "A propos URL" + user_guide_link: "Lien vers le guide utilisateur" name: Nom first_name: Prénom last_name: Nom de famille @@ -1240,7 +1251,7 @@ fr: essential_cookies: "Cookies essentiels" essential_cookies_desc: "Les cookies suivants sont nécessaires au fonctionnement du site openfoodfrance.org." essential_cookies_note: "Les cookies contiennent un identifiant unique, mais pas d'autres données. Vos emails et mots de passe par exemple ne sont jamais exposés dans les cookies!" - cookie_domain: "Déposé par!" + cookie_domain: "Déposé par" cookie_session_desc: "Utilisé pour garder en mémoire l'utilisateur d'une page à l'autre lors de la navigation sur le site, ou pour se souvenir des produits dans le panier." cookie_consent_desc: "Utilisé pour se souvenir du consentement de l'utilisation à l'utilisation de cookies." cookie_remember_me_desc: "Utilisé si l'utilisateur a cliqué sur \"se souvenir de moi\" (pour ne pas avoir à se reconnecter à chaque fois). Ce cookie est automatiquement supprimé après 12 jours. Si l'utilisateur souhaite supprimer ce cookie, il n'a qu'à se déconnecter. Si l'utilisateur ne souhaite pas que ce cookie soit installé sur son terminal, il suffit de ne pas cocher la case \"se souvenir de moi\" au moment de la connexion." @@ -1250,6 +1261,7 @@ fr: statistics_cookies_desc: "Ces cookies ne sont pas obligatoires, mais nous permettent de mieux comprendre votre usage de la plateforme, les endroits où vous bloquez, les fonctionnalités qui semblent vous manquer, ou que vous n'utilisez jamais, afin de fournir le service le plus adapté possible aux besoins des utilisateurs." statistics_cookies_analytics_desc_html: "Pour analyser les données concernant votre usage de la plateforme, nous utilisons Google Analytics, pas vraiment par choix, mais simplement parce que c'était l'outil d'analyse connecté par défaut via Spree, le logiciel e-commerce open source sur lequel nous avons construit. Mais nous espérons pouvoir rapidement migrer vers Matomo (anciennement Piwik), outil d'analyse open source compatible RGPD et engagé sur le respect de la vie privée des utilisateurs." statistics_cookies_matomo_desc_html: "Pour analyser les données concernant votre usage de la plateforme, nous utilisons Matomo(anciennement Piwik), outil d'analyse open source compatible RGPD et engagé sur le respect de la vie privée des utilisateurs." + statistics_cookies_matomo_optout: "Vous ne voulez pas que vos données soient analysées par Matomo ? Nous ne collectons aucune donnée personnelle, et Matomo nous aide à améliorer le service que nous vous offrons, mais nous respectons votre choix :-)" cookie_analytics_utma_desc: "Utilisé pour distinguer les utilisateurs et les sessions. Ce cookie est installé quand la librairie Javascript s'exécute et qu'aucun cookie __utma n'existe déjà. Le cookie est mis à jour à chaque fois que des données sont envoyées à Google Analytics." cookie_analytics_utmt_desc: "Utilisé pour limiter le taux de requêtes." cookie_analytics_utmb_desc: "Utilisé pour distinguer les nouvelles sessions/visites. Ce cookie est installé quand la librairie Javascript s'exécute et qu'aucun cookie __utmb n'existe déjà. Le cookie est mis à jour à chaque fois que des données sont envoyées à Google Analytics. " @@ -1372,7 +1384,7 @@ fr: email_confirm_customer_details_html: "Détails de votre commande chez %{distributor}:" email_confirm_customer_signoff: "Cordialement," email_confirm_shop_greeting: "Bonjour %{name}," - email_confirm_shop_order_html: "Bravo! Vous avez reçu une nouvelle commande pour %{distributor}!" + email_confirm_shop_order_html: "Bravo! Vous avez reçu une nouvelle commande automatique pour %{distributor}!" email_confirm_shop_number_html: "Confirmation de commande #%{number}" email_order_summary_item: "Produit" email_order_summary_quantity: "Qté" @@ -1384,7 +1396,7 @@ fr: email_payment_not_paid: NON RÉGLÉ email_payment_summary: Résumé du paiement email_payment_method: "Payer via :" - email_so_placement_intro_html: "Vous avez une nouvelle commande pour %{distributor}" + email_so_placement_intro_html: "Une nouvelle commande automatique a été passée pour vous chez %{distributor}" email_so_placement_details_html: "Voici les détails de votre commande pour %{distributor}:" email_so_placement_changes: "Malheureusement, certains produits demandés n'étaient pas disponibles. Les quantités d'origine demandées apparaissent comme barrées ci-dessous." email_so_payment_success_intro_html: "Un paiement automatique a été effectué pour votre commande auprès de %{distributor}." @@ -1459,8 +1471,8 @@ fr: hubs_distance: Le plus près de hubs_distance_filter: "Afficher les boutiques près de %{location}" shop_changeable_orders_alert_html: - one: Votre commande avec %{shop} / %{order} est ouverte pour vérification. Vous pouvez effectuer des modification jusqu'à %{oc_close}. - other: Vous avez %{count} commandes avec %{shop}ouvertes à la vérification. Vous pouvez effectuer des modifications jusqu'à %{oc_close}. + one: Votre commande chez %{shop} / %{order} est ouverte pour vérification. Vous pouvez effectuer des modifications jusqu'au %{oc_close}. + other: Vous avez %{count} commandes avec %{shop} ouvertes à la vérification. Vous pouvez effectuer des modifications jusqu'au %{oc_close}. orders_changeable_orders_alert_html: Cette commande a été confirmée, mais vous pouvez effectuer des modifications jusqu'à %{oc_close}. products_clear_all: Vider products_showing: "Afficher:" @@ -1772,8 +1784,8 @@ fr: logo_placeholder: "Votre logo apparaîtra ici pour validation une fois uploadé" enterprise_about_headline: "Bien joué!" enterprise_about_message: "A présent, allons un peu plus dans les détails concernant" - enterprise_success: "Opération réussie! %{enterprise} a été ajoutée à Open Food France" - enterprise_registration_exit_message: "Si vous quittez ce module, vous pourrez continuer la création de votre profile via l'interface d'administration." + enterprise_success: "Opération réussie ! %{enterprise} a été ajoutée à Open Food France" + enterprise_registration_exit_message: "Si vous quittez ce module, vous pourrez continuer la création de votre profil via l'interface d'administration." enterprise_description: "Description courte" enterprise_description_placeholder: "Une phrase pour décrire votre organisation" enterprise_long_desc: "Description longue" @@ -2416,6 +2428,9 @@ fr: resolve: Résoudre new_tag_rule_dialog: select_rule_type: "Choisir le type de règle:" + orders: + index: + per_page: "%{results} par page" resend_user_email_confirmation: resend: "Renvoyer" sending: "Renvoi...." @@ -2465,7 +2480,9 @@ fr: Cette action remettra tous les niveaux de stock à zero pour cette entreprise pour les produits non présents dans ce fichier. order_cycles: + create_failure: "La création du cycle de vente a échoué" update_success: 'Votre cycle de vente a été mis à jour.' + update_failure: "La mise à jour du cycle de vente à échoué" no_distributors: Il n'y a pas de distributeur pour ce cycle de vente. Il ne sera pas visible aux acheteurs tant qu'il n'y aura pas de distributeur. Voulez-vous tout de même sauvegarder ce cycle de vente ? enterprises: producer: "Producteur" @@ -2474,7 +2491,7 @@ fr: select_shop: 'Veuillez d''abord choisir une boutique' could_not_create: Oups ! Création impossible... subscriptions: - closes: ferme + closes: fermer closed: fermé close_date_not_set: Date de fin non renseignée producers: @@ -2486,8 +2503,30 @@ fr: my_account: "Mon compte" date: "Date" time: "Heure" + layouts: + admin: + header: + store: Vue acheteur admin: + product_properties: + index: + inherits_properties_checkbox_hint: "Hériter des propriétés de %{supplier} ? (non applicable si information de remplacement déjà saisie)" orders: + index: + listing_orders: "Liste des commandes" + new_order: "Nouvelle commande" + capture: "Payée" + ship: "Expédier" + edit: "Modifier" + note: "Note" + first: "Début" + last: "Fin" + previous: "Précédent" + next: "Suivant" + loading: "Chargement en cours" + no_orders_found: "Aucune commande trouvée pour ces critères" + results_found: "%{number} résultats trouvés" + viewing: "Résultats %{start} à %{end} affichés." invoice: issued_on: Editée le tax_invoice: FACTURE @@ -2595,6 +2634,8 @@ fr: js_format: 'yy-mm-dd' inventory: Catalogue boutique orders: + edit: + login_to_view_order: "Veuillez vous connecter pour voir votre commande." bought: item: "Déjà commandé dans ce cycle de vente" order_mailer: @@ -2673,16 +2714,16 @@ fr: order: Commander shop: Faire mes courses changes_allowed_until: Modifications permises jusqu'à - items: Pièce + items: Produits à commander total: Total edit: Modifier cancel: Annuler closed: Fermée until: Jusqu'à past_orders: - order: Commande + order: Commandes à venir shop: Boutique - completed_at: 'Passée à :' + completed_at: Date items: Produits total: Total paid?: Payé ? @@ -2694,5 +2735,8 @@ fr: authorised_shops: Boutiques autorisées. authorised_shops_popover: Voilà la liste des boutiques que vous avez autorisées à débiter votre carte de paiement par défaut dans le cadre de vos abonnements en cours (commandes récurrentes). Les informations concernant votre carte de paiement sont sécurisées et ne sont pas accessibles par le gérant de la boutique. Vous recevrez systématiquement une notification avant tout débit sur votre carte. saved_cards_popover: Voilà la liste des cartes de paiement que vous avez enregistrées. Votre carte par défaut sera automatiquement sélectionnée au moment de la finalisation d'une commande, et pourra être débitée par les boutiques auxquelles vous avez donné cette autorisation (voir à droite). + authorised_shops: + shop_name: "Nom de la boutique" + allow_charges?: "Autoriser les prélèvements ?" localized_number: invalid_format: n'est pas un format valide. Veuillez entrer un nombre. diff --git a/config/locales/fr_CA.yml b/config/locales/fr_CA.yml index 521ce83241..a7c38a0712 100644 --- a/config/locales/fr_CA.yml +++ b/config/locales/fr_CA.yml @@ -463,7 +463,6 @@ fr_CA: products_no_permission: vous n'avez pas les droits requis pour gérer les produits de cette entreprise inventory_no_permission: Vous n'avez pas la permission de créer un catalogue boutique pour ce producteur none_saved: n'a pu sauvegarder aucun produit :-( - line: Ligne index: select_file: Sélectionner une feuille de calcul à uploader spreadsheet: Feuille de calcul @@ -497,7 +496,6 @@ fr_CA: no_permission: vous n'avez pas les droits requis pour gérer les produits de cette entreprise not_found: entreprise non trouvée dans la base de donnée no_name: Pas de nom - blank_supplier: certains produits ne sont associés à aucun fournisseur reset_absent?: Mettre à zéro le produits absents du fichier reset_absent_tip: Remettre le sock à zero pour les produits non présents dans le fichier. overwrite_all: Modifier tous @@ -557,9 +555,6 @@ fr_CA: controls: back_to_my_inventory: Retour à mon catalogue boutique orders: - index: - capture: "Payée" - ship: "Expédier" invoice_email_sent: 'L''email de facturation a bien été envoyé' order_email_resent: 'L''email de commande a de nouveau été envoyé' bulk_management: @@ -2472,7 +2467,9 @@ fr_CA: Cette action remettra tous les niveaux de stock à zero pour cette entreprises pour les produits non présents dans ce fichier. order_cycles: + create_failure: "La création du cycle de vente a échoué" update_success: 'Votre cycle de vente a été mis à jour.' + update_failure: "La mise à jour du cycle de vente à échoué" no_distributors: Il n'y a pas de distributeur pour ce cycle de vente. Il ne sera pas visible aux acheteurs tant qu'il n'y aura pas de distributeur. Voulez-vous tout de même sauvegarder ce cycle de vente ? enterprises: producer: "Producteur" @@ -2493,8 +2490,21 @@ fr_CA: my_account: "Mon compte" date: "Date" time: "Heure" + layouts: + admin: + header: + store: Vue acheteur admin: + product_properties: + index: + inherits_properties_checkbox_hint: "Hériter des propriétés de %{supplier}? (non applicable si information de remplacement déjà saisie)" orders: + index: + capture: "Payée" + ship: "Expédier" + edit: "Modifier" + next: "Suivant" + no_orders_found: "Aucune commande trouvée" invoice: issued_on: Editée le tax_invoice: FACTURE @@ -2529,6 +2539,11 @@ fr_CA: account_id: Identifiant Compte business_name: Nom de l'entreprise charges_enabled: Frais activés + payments: + source_forms: + stripe: + error_saving_payment: Erreur à l'enregistrement du paiement + submitting_payment: Envoi du paiement... products: new: title: 'Nouveau Produit' @@ -2597,6 +2612,8 @@ fr_CA: js_format: 'yy-mm-dd' inventory: Catalogue boutique orders: + edit: + login_to_view_order: "Veuillez vous connecter pour voir votre commande." bought: item: "Déjà commandé dans ce cycle de vente" order_mailer: @@ -2696,5 +2713,8 @@ fr_CA: authorised_shops: Boutiques autorisées authorised_shops_popover: Ceci est la liste des magasins qui sont autorisés à facturer votre carte de crédit par défaut pour les abonnements (c'est-à-dire les commandes répétées) que vous pourriez avoir. Les détails de votre carte seront conservés en sécurité et ne seront pas partagés avec les propriétaires de boutique. Vous serez toujours informé lorsque vous êtes facturé. 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?: "Autoriser les prélèvements ?" localized_number: invalid_format: n'est pas un format valide. Veuillez entrer un nombre. diff --git a/config/locales/it.yml b/config/locales/it.yml index 9d46f3e777..db43e218e3 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -1,5 +1,5 @@ it: - language_name: "Inglese" + language_name: "Italiano" activerecord: attributes: spree/order: @@ -358,7 +358,6 @@ it: products_no_permission: non sei abilitato a gestire i prodotti per questa azienda inventory_no_permission: non sei abilitato a creare l'inventario per questo produttore none_saved: Nessun prodotto salvato con successo - line: Linea index: select_file: Seleziona un foglio di calcolo da caricare spreadsheet: Foglio di calcolo @@ -392,7 +391,6 @@ it: no_permission: Non hai il permesso di gestire questa attività not_found: l'azienda non è stata trovata nel database no_name: Nessun nome - blank_supplier: Alcuni prodotti non hanno il nome del produttore reset_absent?: Elimina i prodotti assenti reset_absent_tip: Imposta la scorta a zero per tutti i prodotti esistenti non presenti in questo file overwrite_all: Sovrascrivi tutto @@ -448,8 +446,6 @@ it: controls: back_to_my_inventory: Indietro al mio inventario orders: - index: - ship: "Spedizione" invoice_email_sent: 'La mail con la fattura è stata inviata' order_email_resent: 'La mail con la gentile richiesta è stata re-inviata' bulk_management: @@ -2021,6 +2017,11 @@ it: time: "Ora" admin: orders: + index: + ship: "Spedizione" + edit: "Modifica" + next: "Prossimo" + no_orders_found: "Nessuna gentile richiesta trovata" invoice: code: Codice from: Da diff --git a/config/locales/nb.yml b/config/locales/nb.yml index 51d7efc2c5..99c29e3bce 100644 --- a/config/locales/nb.yml +++ b/config/locales/nb.yml @@ -55,6 +55,7 @@ nb: user_registrations: spree_user: signed_up_but_unconfirmed: "En melding med en bekreftelseslink er sendt til epostadressen din. Vennligst åpne lenken for å aktivere kontoen din." + unknown_error: "Noe gikk galt da du opprettet kontoen din. Sjekk epostadressen din og prøv igjen." failure: invalid: | Ugyldig epost eller passord. @@ -458,11 +459,12 @@ nb: conditional_blank: kan ikke være tom hvis unit_type er tom no_product: samsvarte ikke med noen produkter i databasen not_found: ikke funnet i databasen + not_updatable: kan ikke oppdateres på eksisterende produkter via produktimport blank: kan ikke være tomt products_no_permission: du har ikke tillatelse til å administrere produkter for denne bedriften inventory_no_permission: du har ikke tillatelse til å opprette lager for denne produsenten none_saved: kunne ikke lagre noen produkter - line: Linje + line_number: "Linje %{number}:" index: select_file: Velg et regneark for å laste opp spreadsheet: Regneark @@ -496,7 +498,6 @@ nb: no_permission: du har ikke tillatelse til å administrere denne bedriften not_found: bedriften kunne ikke bli funnet i databasen no_name: Ingen navn - blank_supplier: noen produkter har tomt leverandørnavn reset_absent?: Tilbakestill fraværende produkter reset_absent_tip: Sett lager til null for alle gjeldende produkter som ikke er til stede i filen overwrite_all: Overskrive alt @@ -516,6 +517,11 @@ nb: inventory_to_reset: Eksisterende vareobjekter vil få lager satt til null line: Linje item_line: Artikkellinje + import_review: + not_updatable_tip: "Følgende felt kan ikke oppdateres via bulkimport for eksisterende produkter:" + fields_ignored: Disse feltene blir ignorert når de importerte produktene er lagret. + entries_table: + not_updatable: Dette feltet er ikke oppdaterbart via bulkimport på eksisterende produkter save_results: final_results: Importer endelige resultater products_created: Produkter opprettet @@ -556,9 +562,6 @@ nb: controls: back_to_my_inventory: Tilbake til min varelager orders: - index: - capture: "Fang" - ship: "Levere" invoice_email_sent: 'Faktura-e-post er sendt' order_email_resent: 'Bestillings-e-post har blitt angitt' bulk_management: @@ -1051,6 +1054,11 @@ nb: stripe_connect_fail: Beklager, forbindelsen til Stripe-kontoen din mislyktes stripe_connect_settings: resource: Stripe Connect-konfigurasjon + api: + enterprise_logo: + destroy_attachment_does_not_exist: "Logo eksisterer ikke" + enterprise_promo_image: + destroy_attachment_does_not_exist: "Promo-bilde eksisterer ikke" checkout: already_ordered: cart: "handlekurv" @@ -1159,6 +1167,7 @@ nb: footer_email: "Epost" footer_links_md: "Linker" footer_about_url: "Om URL" + user_guide_link: "Lenke Brukerhåndbok" name: Navn first_name: Fornavn last_name: Etternavn @@ -1251,6 +1260,7 @@ nb: statistics_cookies_desc: "Følgende er ikke strengt nødvendige, men hjelper deg med å gi deg den beste brukeropplevelsen ved å tillate oss å analysere brukeradferd, identifisere hvilke funksjoner du bruker mest, eller ikke bruker, forstå brukeropplevelsesproblemer osv." statistics_cookies_analytics_desc_html: "For å samle og analysere plattformbruksdata bruker vi Google Analytics, da det var standardtjenesten som var koblet til Spree (ehandel open source programvare som vi bygde på), men visjonen vår er å bytte til Matomo (ex Piwik, open source analyseverktøy som er GDPR-kompatibelt og beskytter ditt privatliv) så snart vi kan." statistics_cookies_matomo_desc_html: "For å samle og analysere plattformbruksdata bruker vi Matomo (ex Piwik), et åpen kildekodeanalyseverktøy som er kompatibelt med GDPR og beskytter personvernet ditt." + statistics_cookies_matomo_optout: "Ønsker du å melde deg av Matomo analytics? Vi samler ikke inn personlige data, og Matomo hjelper oss med å forbedre vår tjeneste, men vi respekterer ditt valg :-)" cookie_analytics_utma_desc: "Brukes til å skille mellom brukere og økter. Kapselen er opprettet når javascriptbiblioteket utføres, og ingen eksisterende __utma-informasjonskapsler eksisterer. Cookien oppdateres hver gang data sendes til Google Analytics." cookie_analytics_utmt_desc: "Brukes til pådragsforespørselsrate." cookie_analytics_utmb_desc: "Brukes til å bestemme nye økter/besøk. Kapselen blir opprettet når javascriptbiblioteket kjøres, og ingen eksisterende __utmb-cookies eksisterer. Cookien oppdateres hver gang data sendes til Google Analytics." @@ -1539,7 +1549,7 @@ nb: producers_buy_at_html: "Handle produkter fra %{enterprise} hos:" producers_filter: Filtrer på producers_filter_type: Type - producers_filter_property: Egenskape + producers_filter_property: Egenskap producers_title: Produsenter producers_headline: Finn lokale produsenter producers_signup_title: Bli med som produsent @@ -2407,6 +2417,9 @@ nb: resolve: Løse new_tag_rule_dialog: select_rule_type: "Velg en regeltype:" + orders: + index: + per_page: "%{results} per side" resend_user_email_confirmation: resend: "Send på nytt" sending: "Send på nytt ..." @@ -2454,7 +2467,9 @@ nb: confirmation: | Dette vil sette varebeholdning til null på alle produkter for denne virksomheten som ikke er til stede i den nedlastede filen. order_cycles: + create_failure: "Kunne ikke opprette bestillingsrunde" update_success: 'Din bestillingsrunde har blitt oppdatert.' + update_failure: "Kunne ikke oppdatere bestillingsrunde" no_distributors: Det er ingen distributører i denne bestillingsrunden. Denne bestillingsrunden vil ikke være synlig for kundene før du legger til en. Vil du fortsette å lagre denne bestillingsrunden? ' enterprises: producer: "Produsent" @@ -2475,8 +2490,30 @@ nb: my_account: "Min konto" date: "Dato" time: "Tid" + layouts: + admin: + header: + store: Butikk admin: + product_properties: + index: + inherits_properties_checkbox_hint: "Arve egenskaper fra %{supplier}? (med mindre overstyrt over)" orders: + index: + listing_orders: "Lister opp bestillinger" + new_order: "Ny bestilling" + capture: "Fang" + ship: "Levere" + edit: "Rediger" + note: "Merk" + first: "Først" + last: "Siste" + previous: "Tidligere" + next: "Neste" + loading: "Laster" + no_orders_found: "Ingen bestillinger funnet" + results_found: "%{number} Resultater funnet." + viewing: "Ser %{start} til %{end}." invoice: issued_on: Utstedt på tax_invoice: AVGIFTSFAKTURA @@ -2584,6 +2621,8 @@ nb: js_format: 'yy-mm-dd' inventory: Varelager orders: + edit: + login_to_view_order: "Vennligst logg inn for å se bestillingen din." bought: item: "Allerede bestilt i denne bestillingssyklusen" order_mailer: @@ -2682,5 +2721,8 @@ nb: authorised_shops: Autoriserte Butikker authorised_shops_popover: Dette er listen over butikker som har lov til å belaste ditt standard kredittkort for eventuelle abonnementer (dvs. gjentatte ordre) du måtte ha. Kortinformasjonen din vil bli holdt sikker og vil ikke bli delt med butikkeiere. Du vil alltid bli varslet når du blir belastet. saved_cards_popover: Dette er listen over kort du har valgt å lagre for senere bruk. Din standard vil bli valgt automatisk når du gjør en bestilling, og kan belastes av butikker du har gitt lov til å gjøre det (se høyre). + authorised_shops: + shop_name: "Butikknavn" + allow_charges?: "Tillat å belaste?" localized_number: invalid_format: har et ugyldig format. Vennligst skriv inn et nummer. diff --git a/config/locales/pt.yml b/config/locales/pt.yml index f387287608..765de5b861 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -429,7 +429,6 @@ pt: products_no_permission: não tem permissões para gerir produtos desta organização inventory_no_permission: não tem permissões para criar inventário para este produtor none_saved: não gravou nenhum produto com sucesso - line: Linha index: select_file: Selecione uma folha de cálculo para carregar spreadsheet: Folha de cálculo @@ -452,7 +451,6 @@ pt: no_permission: não tem permissões para gerir esta organização not_found: organização não encontrada no_name: Sem nome - blank_supplier: alguns produtos têm o nome do fornecedor vazio overwrite_all: Substituir todos overwrite_empty: Substituir se vazio default_stock: Definir nível de stock @@ -507,9 +505,6 @@ pt: controls: back_to_my_inventory: Voltar ao inventário orders: - index: - capture: "Capturar" - ship: "Enviar" invoice_email_sent: 'Email de facturação enviado' order_email_resent: 'Email de encomenda reenviado' bulk_management: @@ -1317,7 +1312,7 @@ pt: shopping_contact_social: "Seguir" shopping_groups_part_of: "é parte de:" shopping_producers_of_hub: "Produtores de %{hub}:" - enterprises_next_closing: "As encomendas fecham em" + enterprises_next_closing: "As encomendas fecham" enterprises_ready_for: "Pronto para" enterprises_choose: "Escolha para quando quer a sua encomenda:" maps_open: "Aberto" @@ -2351,6 +2346,12 @@ pt: time: "Hora" admin: orders: + index: + capture: "Capturar" + ship: "Enviar" + edit: "Editar" + next: "Seguinte" + no_orders_found: "Nenhuma encomenda encontrada" invoice: issued_on: Emitido em tax_invoice: FACTURA FISCAL diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 742aabdc17..95d19b9aa8 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -304,9 +304,6 @@ sv: controls: back_to_my_inventory: Tillbaka till mitt lager orders: - index: - capture: "Fånga" - ship: "Frakta" bulk_management: tip: "Använd den här sidan för att ändra produktkvantitet på ett antal order. Om så önskas kan produkter tas bort helt från flera order." shared: "Delad Resurs?" @@ -1868,6 +1865,11 @@ sv: spree: admin: orders: + index: + capture: "Fånga" + ship: "Frakta" + edit: "Redigera" + next: "Näst" invoice: issued_on: Utfärdat den tax_invoice: FAKTURA From 2e2c7c56cbf804c639af3836898cbe1678866d98 Mon Sep 17 00:00:00 2001 From: Pau Perez Date: Tue, 30 Oct 2018 18:45:33 +0100 Subject: [PATCH 51/65] Move default_url_options set up to an initializer For some reason running `bundle exec rake db:migrate RAILS_ENV=staging` fails with: ``` rake aborted! NameError: uninitialized constant Spree::Config ``` Running `bundle exec rails server` for instance, does not. There must be a difference on the way a rake task and the rails commands load the app. Moving this configuration to an initializer, at the end of the initialization process, fixes it. The constant `Spree::Config` is already loaded. **This is preventing the release v1.22.0 from being staged and tested** --- config/environments/production.rb | 3 --- config/environments/staging.rb | 3 --- config/initializers/action_mailer.rb | 6 ++++++ 3 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 config/initializers/action_mailer.rb diff --git a/config/environments/production.rb b/config/environments/production.rb index 88c743071a..229221e118 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -30,9 +30,6 @@ Openfoodnetwork::Application.configure do # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. config.force_ssl = true - # Use https when creating links in emails - config.action_mailer.default_url_options = { protocol: 'https', host: Spree::Config[:site_url] } - # See everything in the log (default is :info) config.log_level = :info diff --git a/config/environments/staging.rb b/config/environments/staging.rb index f43d42f998..303d7d2913 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -30,9 +30,6 @@ Openfoodnetwork::Application.configure do # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. config.force_ssl = true - # Use https when creating links in emails - config.action_mailer.default_url_options = { protocol: 'https', host: Spree::Config[:site_url] } - # See everything in the log (default is :info) # config.log_level = :debug diff --git a/config/initializers/action_mailer.rb b/config/initializers/action_mailer.rb new file mode 100644 index 0000000000..cd2a638567 --- /dev/null +++ b/config/initializers/action_mailer.rb @@ -0,0 +1,6 @@ +ActionMailer::Base.configure do |config| + if Rails.env.production? || Rails.env.staging? + # Use https when creating links in emails + config.default_url_options = { protocol: 'https', host: Spree::Config[:site_url] } + end +end From 1ddbc24a8b6adab78d5452cda33c80ec76d76495 Mon Sep 17 00:00:00 2001 From: Pedro Costa Date: Sun, 28 Oct 2018 18:35:36 +0000 Subject: [PATCH 52/65] Fix default time zone config Why: * Starting the environment, even with just `bundle exec rake`, results in the following error: ```sh Value assigned to config.time_zone not recognized.Run "rake -D time" for a list of tasks for finding appropriate time zone names. ``` This change addresses the issue by: * Adding the time zone setting to environment specific configurations, defaulting to UTC in development, and using Melbourne in tests. --- config/environments/development.rb | 6 ++++++ config/environments/test.rb | 2 ++ 2 files changed, 8 insertions(+) diff --git a/config/environments/development.rb b/config/environments/development.rb index 22bf2a1945..70e48c9aac 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -29,6 +29,12 @@ Openfoodnetwork::Application.configure do # Expands the lines which load the assets config.assets.debug = false + # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. + # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. + # + # To override this, set the appropriate locale in application.yml + config.time_zone = ENV.fetch("TIMEZONE", "UTC") + config.i18n.fallbacks = [:en] # Show emails using Letter Opener diff --git a/config/environments/test.rb b/config/environments/test.rb index 7ecc7ae3c2..299e2d9c19 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -33,6 +33,8 @@ Openfoodnetwork::Application.configure do # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test + config.time_zone = ENV.fetch("TIMEZONE", "Melbourne") + # Tests assume English text on the site. config.i18n.default_locale = "en" config.i18n.available_locales = ['en', 'es'] From f16897a0b8c43e8914c410b540d638b9e9c5042f Mon Sep 17 00:00:00 2001 From: Pau Perez Date: Wed, 31 Oct 2018 08:39:25 +0100 Subject: [PATCH 53/65] Fix DE translations manually We don't want :de: to have a broken menu. --- config/locales/de_DE.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/locales/de_DE.yml b/config/locales/de_DE.yml index a30abc8975..459ffe1b73 100644 --- a/config/locales/de_DE.yml +++ b/config/locales/de_DE.yml @@ -1140,13 +1140,13 @@ de_DE: ticket_column_unit_price: "Stückpreis" ticket_column_total_price: "Gesamtpreis" menu_1_title: "Läden" - menu_1_url: "/ shops" + menu_1_url: "/shops" menu_2_title: "Karte" menu_2_url: "/map" menu_3_title: "Erzeuger" - menu_3_url: "/ producers" + menu_3_url: "/producers" menu_4_title: "Gruppen" - menu_4_url: "/ groups" + menu_4_url: "/groups" menu_5_title: "Über Uns" menu_5_url: "http://www.openfoodnetwork.org/" menu_6_title: "Verbinden" From 079d4e0bf500615d0eca293f6433c77a77c018ed Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Wed, 31 Oct 2018 11:23:29 +0000 Subject: [PATCH 54/65] Fix variant_overrides permissions for overrides that belong to the supplier herself --- ...5158_allow_all_suppliers_own_variant_overrides.rb | 12 ++++++++++++ db/schema.rb | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20181031105158_allow_all_suppliers_own_variant_overrides.rb diff --git a/db/migrate/20181031105158_allow_all_suppliers_own_variant_overrides.rb b/db/migrate/20181031105158_allow_all_suppliers_own_variant_overrides.rb new file mode 100644 index 0000000000..a04bd5dfff --- /dev/null +++ b/db/migrate/20181031105158_allow_all_suppliers_own_variant_overrides.rb @@ -0,0 +1,12 @@ +class AllowAllSuppliersOwnVariantOverrides < ActiveRecord::Migration + def up + # This migration is fixing a detail of previous migration RevokeVariantOverrideswithoutPermissions + # Here we allow all variant_overrides where hub_id is the products supplier_id + # This is needed when the supplier herself uses the inventory to manage stock and not the catalog + owned_variant_overrides = VariantOverride.unscoped + .joins(variant: :product).where("spree_products.supplier_id = variant_overrides.hub_id") + + owned_variant_overrides.update_all(permission_revoked_at: nil) + end +end + diff --git a/db/schema.rb b/db/schema.rb index 3e60568a4a..18f7177b86 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20181020103501) do +ActiveRecord::Schema.define(:version => 20181031105158) do create_table "account_invoices", :force => true do |t| t.integer "user_id", :null => false From 6416d0e1c38394db33a796b735a581fca015ef61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 1 Nov 2018 19:15:40 +0000 Subject: [PATCH 55/65] Bump awesome_print from 1.0.2 to 1.8.0 Bumps [awesome_print](https://github.com/awesome-print/awesome_print) from 1.0.2 to 1.8.0. - [Release notes](https://github.com/awesome-print/awesome_print/releases) - [Changelog](https://github.com/awesome-print/awesome_print/blob/master/CHANGELOG.md) - [Commits](https://github.com/awesome-print/awesome_print/compare/v1.0.2...v1.8.0) Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c1ade46da7..43608b37f1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -193,7 +193,7 @@ GEM atomic (1.1.101) awesome_nested_set (2.1.5) activerecord (>= 3.0.0) - awesome_print (1.0.2) + awesome_print (1.8.0) aws-sdk (1.11.1) json (~> 1.4) nokogiri (>= 1.4.4) @@ -769,7 +769,6 @@ DEPENDENCIES capybara (>= 2.15.4) coffee-rails (~> 3.2.1) compass-rails - web! custom_error_message! daemons dalli @@ -846,6 +845,7 @@ DEPENDENCIES uglifier (>= 1.0.3) unicorn unicorn-rails + web! webmock whenever wicked_pdf From e798532a2f5f21a86b221351429196d87aa765d2 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 26 Oct 2018 16:15:03 +1100 Subject: [PATCH 56/65] Add offending files to rubocop config The file .rubocop_todo.yml is generated automatically and contains a configuration to make all files pass. For a lot of cops it just lists the offending files. But for some cops it sets a different metric. Since we don't want these lax metrics, we override them in our config file .rubocop.yml. This leads to a lot of offences again. This patch lists all offending files for each cop to make rubocop pass. We can improve the code over time and remove items from the list. --- .rubocop.yml | 742 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 742 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 2bf1f9b60e..cc46db7805 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -200,3 +200,745 @@ Metrics/ParameterLists: Metrics/PerceivedComplexity: Max: 7 + +# Additional todo list not generated by --autogen-config +Metrics/LineLength: + Max: 100 + Exclude: + - app/controllers/admin/accounts_and_billing_settings_controller.rb + - app/controllers/admin/bulk_line_items_controller.rb + - app/controllers/admin/business_model_configuration_controller.rb + - app/controllers/admin/cache_settings_controller.rb + - app/controllers/admin/contents_controller.rb + - app/controllers/admin/customers_controller.rb + - app/controllers/admin/enterprise_fees_controller.rb + - app/controllers/admin/enterprise_groups_controller.rb + - app/controllers/admin/enterprise_relationships_controller.rb + - app/controllers/admin/enterprises_controller.rb + - app/controllers/admin/inventory_items_controller.rb + - app/controllers/admin/manager_invitations_controller.rb + - app/controllers/admin/order_cycles_controller.rb + - app/controllers/admin/product_import_controller.rb + - app/controllers/admin/proxy_orders_controller.rb + - app/controllers/admin/schedules_controller.rb + - app/controllers/admin/subscription_line_items_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/application_controller.rb + - app/controllers/base_controller.rb + - app/controllers/cart_controller.rb + - app/controllers/checkout_controller.rb + - app/controllers/enterprises_controller.rb + - app/controllers/shop_controller.rb + - app/controllers/spree/admin/adjustments_controller_decorator.rb + - app/controllers/spree/admin/base_controller_decorator.rb + - app/controllers/spree/admin/line_items_controller_decorator.rb + - app/controllers/spree/admin/orders_controller_decorator.rb + - app/controllers/spree/admin/payment_methods_controller_decorator.rb + - app/controllers/spree/admin/products_controller_decorator.rb + - app/controllers/spree/admin/reports_controller_decorator.rb + - app/controllers/spree/admin/shipping_methods_controller_decorator.rb + - app/controllers/spree/api/products_controller_decorator.rb + - app/controllers/spree/credit_cards_controller.rb + - app/controllers/spree/orders_controller_decorator.rb + - app/controllers/spree/paypal_controller_decorator.rb + - app/controllers/stripe/callbacks_controller.rb + - app/controllers/user_confirmations_controller.rb + - app/helpers/admin/account_helper.rb + - app/helpers/admin/business_model_configuration_helper.rb + - app/helpers/admin/injection_helper.rb + - app/helpers/angular_form_builder.rb + - app/helpers/angular_form_helper.rb + - app/helpers/application_helper.rb + - app/helpers/checkout_helper.rb + - app/helpers/enterprises_helper.rb + - app/helpers/footer_links_helper.rb + - app/helpers/injection_helper.rb + - app/helpers/markdown_helper.rb + - app/helpers/order_cycles_helper.rb + - app/helpers/shop_helper.rb + - app/helpers/spree/admin/base_helper_decorator.rb + - app/helpers/spree/admin/navigation_helper_decorator.rb + - app/helpers/spree/admin/orders_helper_decorator.rb + - app/helpers/spree/orders_helper.rb + - app/jobs/finalize_account_invoices.rb + - app/jobs/products_cache_integrity_checker_job.rb + - app/jobs/subscription_confirm_job.rb + - app/jobs/subscription_placement_job.rb + - app/jobs/update_account_invoices.rb + - app/jobs/update_billable_periods.rb + - app/mailers/producer_mailer.rb + - app/mailers/spree/order_mailer_decorator.rb + - app/mailers/subscription_mailer.rb + - app/models/billable_period.rb + - app/models/column_preference.rb + - app/models/content_configuration.rb + - app/models/customer.rb + - app/models/enterprise_fee.rb + - app/models/enterprise.rb + - app/models/enterprise_relationship.rb + - app/models/enterprise_role.rb + - app/models/exchange.rb + - app/models/inventory_item.rb + - app/models/order_cycle.rb + - app/models/product_distribution.rb + - app/models/product_import/entry_processor.rb + - app/models/product_import/entry_validator.rb + - app/models/product_import/product_importer.rb + - app/models/product_import/spreadsheet_data.rb + - app/models/product_import/spreadsheet_entry.rb + - app/models/product_import/unit_converter.rb + - app/models/proxy_order.rb + - app/models/schedule.rb + - app/models/spree/ability_decorator.rb + - app/models/spree/adjustment_decorator.rb + - app/models/spree/calculator/default_tax_decorator.rb + - app/models/spree/calculator/flexi_rate_decorator.rb + - app/models/spree/classification_decorator.rb + - app/models/spree/gateway/stripe_connect.rb + - app/models/spree/line_item_decorator.rb + - app/models/spree/order_decorator.rb + - app/models/spree/payment_decorator.rb + - app/models/spree/payment_method_decorator.rb + - app/models/spree/product_decorator.rb + - app/models/spree/shipment_decorator.rb + - app/models/spree/shipping_method_decorator.rb + - app/models/spree/taxon_decorator.rb + - app/models/spree/user_decorator.rb + - app/models/spree/variant_decorator.rb + - app/models/subscription.rb + - app/models/variant_override.rb + - app/models/variant_override_set.rb + - app/overrides/add_distributor_details_js_to_product.rb + - app/overrides/add_enterprise_fees_to_admin_configurations_menu.rb + - app/overrides/replace_checkout_payment_button.rb + - app/overrides/replace_payment_name_with_description.rb + - app/serializers/api/admin/basic_enterprise_serializer.rb + - app/serializers/api/admin/enterprise_fee_serializer.rb + - app/serializers/api/admin/enterprise_serializer.rb + - app/serializers/api/admin/exchange_serializer.rb + - app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb + - app/serializers/api/admin/index_enterprise_serializer.rb + - app/serializers/api/admin/index_order_cycle_serializer.rb + - app/serializers/api/admin/line_item_serializer.rb + - app/serializers/api/admin/order_cycle_serializer.rb + - app/serializers/api/admin/product_serializer.rb + - app/serializers/api/admin/subscription_serializer.rb + - app/serializers/api/admin/tag_rule_serializer.rb + - app/serializers/api/admin/variant_override_serializer.rb + - app/serializers/api/admin/variant_serializer.rb + - app/services/cart_service.rb + - app/services/embedded_page_service.rb + - app/services/line_item_syncer.rb + - app/services/order_cycle_form.rb + - app/services/order_factory.rb + - app/services/order_syncer.rb + - app/services/subscriptions_count.rb + - app/views/json/_groups.rabl + - app/views/json/partials/_enterprise.rabl + - app/views/json/_producer.rabl + - app/views/spree/api/products/bulk_show.v1.rabl + - app/views/spree/api/variants/bulk_show.v1.rabl + - engines/web/app/helpers/web/cookies_policy_helper.rb + - Gemfile + - lib/discourse/single_sign_on.rb + - lib/open_food_network/accounts_and_billing_settings_validator.rb + - lib/open_food_network/available_payment_method_filter.rb + - lib/open_food_network/bill_calculator.rb + - lib/open_food_network/bulk_coop_report.rb + - lib/open_food_network/business_model_configuration_validator.rb + - lib/open_food_network/customers_report.rb + - lib/open_food_network/distribution_change_validator.rb + - lib/open_food_network/enterprise_fee_applicator.rb + - lib/open_food_network/enterprise_fee_calculator.rb + - lib/open_food_network/enterprise_issue_validator.rb + - lib/open_food_network/group_buy_report.rb + - lib/open_food_network/lettuce_share_report.rb + - lib/open_food_network/locking.rb + - lib/open_food_network/order_and_distributor_report.rb + - lib/open_food_network/order_cycle_form_applicator.rb + - lib/open_food_network/order_cycle_management_report.rb + - lib/open_food_network/order_cycle_permissions.rb + - lib/open_food_network/order_grouper.rb + - lib/open_food_network/orders_and_fulfillments_report.rb + - lib/open_food_network/payments_report.rb + - lib/open_food_network/permalink_generator.rb + - lib/open_food_network/permissions.rb + - lib/open_food_network/products_and_inventory_report_base.rb + - lib/open_food_network/products_cache.rb + - lib/open_food_network/proxy_order_syncer.rb + - lib/open_food_network/reports/bulk_coop_allocation_report.rb + - lib/open_food_network/reports/line_items.rb + - lib/open_food_network/sales_tax_report.rb + - lib/open_food_network/users_and_enterprises_report.rb + - lib/open_food_network/variant_and_line_item_naming.rb + - lib/open_food_network/xero_invoices_report.rb + - lib/spree/core/calculated_adjustments_decorator.rb + - lib/spree/core/controller_helpers/respond_with_decorator.rb + - lib/spree/localized_number.rb + - lib/spree/product_filters.rb + - lib/stripe/profile_storer.rb + - lib/tasks/cache.rake + - lib/tasks/data.rake + - lib/tasks/dev.rake + - lib/tasks/enterprises.rake + - spec/archive/features/consumer/checkout_spec.rb + - spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb + - spec/controllers/admin/bulk_line_items_controller_spec.rb + - spec/controllers/admin/column_preferences_controller_spec.rb + - spec/controllers/admin/customers_controller_spec.rb + - spec/controllers/admin/enterprises_controller_spec.rb + - spec/controllers/admin/inventory_items_controller_spec.rb + - spec/controllers/admin/manager_invitations_controller_spec.rb + - spec/controllers/admin/order_cycles_controller_spec.rb + - spec/controllers/admin/proxy_orders_controller_spec.rb + - spec/controllers/admin/schedules_controller_spec.rb + - spec/controllers/admin/stripe_accounts_controller_spec.rb + - spec/controllers/admin/stripe_connect_settings_controller_spec.rb + - spec/controllers/admin/subscription_line_items_controller_spec.rb + - spec/controllers/admin/subscriptions_controller_spec.rb + - spec/controllers/admin/variant_overrides_controller_spec.rb + - spec/controllers/api/logos_controller_spec.rb + - spec/controllers/api/order_cycles_controller_spec.rb + - spec/controllers/api/product_images_controller_spec.rb + - spec/controllers/api/promo_images_controller_spec.rb + - spec/controllers/cart_controller_spec.rb + - spec/controllers/checkout_controller_spec.rb + - spec/controllers/enterprises_controller_spec.rb + - spec/controllers/line_items_controller_spec.rb + - spec/controllers/shop_controller_spec.rb + - spec/controllers/shops_controller_spec.rb + - spec/controllers/spree/admin/adjustments_controller_spec.rb + - spec/controllers/spree/admin/base_controller_spec.rb + - spec/controllers/spree/admin/line_items_controller_spec.rb + - spec/controllers/spree/admin/orders_controller_spec.rb + - spec/controllers/spree/admin/orders/customer_details_controller_spec.rb + - spec/controllers/spree/admin/payment_methods_controller_spec.rb + - spec/controllers/spree/admin/payments_controller_spec.rb + - spec/controllers/spree/admin/reports_controller_spec.rb + - spec/controllers/spree/api/products_controller_spec.rb + - spec/controllers/spree/api/variants_controller_spec.rb + - spec/controllers/spree/credit_cards_controller_spec.rb + - spec/controllers/spree/orders_controller_spec.rb + - spec/controllers/spree/users_controller_spec.rb + - spec/controllers/spree/user_sessions_controller_spec.rb + - spec/controllers/stripe/callbacks_controller_spec.rb + - spec/controllers/stripe/webhooks_controller_spec.rb + - spec/controllers/user_confirmations_controller_spec.rb + - spec/controllers/user_registrations_controller_spec.rb + - spec/features/admin/accounts_and_billing_settings_spec.rb + - spec/features/admin/adjustments_spec.rb + - spec/features/admin/bulk_order_management_spec.rb + - spec/features/admin/bulk_product_update_spec.rb + - spec/features/admin/customers_spec.rb + - spec/features/admin/enterprise_fees_spec.rb + - spec/features/admin/enterprise_relationships_spec.rb + - spec/features/admin/enterprise_roles_spec.rb + - spec/features/admin/enterprises/images_spec.rb + - spec/features/admin/enterprises/index_spec.rb + - spec/features/admin/enterprises_spec.rb + - spec/features/admin/enterprise_user_spec.rb + - spec/features/admin/image_settings_spec.rb + - spec/features/admin/multilingual_spec.rb + - spec/features/admin/order_cycles_spec.rb + - spec/features/admin/orders_spec.rb + - spec/features/admin/overview_spec.rb + - spec/features/admin/payment_method_spec.rb + - spec/features/admin/product_import_spec.rb + - spec/features/admin/products_spec.rb + - spec/features/admin/reports_spec.rb + - spec/features/admin/schedules_spec.rb + - spec/features/admin/shipping_methods_spec.rb + - spec/features/admin/subscriptions_spec.rb + - spec/features/admin/tag_rules_spec.rb + - spec/features/admin/variant_overrides_spec.rb + - spec/features/consumer/account/cards_spec.rb + - spec/features/consumer/account/settings_spec.rb + - spec/features/consumer/account_spec.rb + - spec/features/consumer/authentication_spec.rb + - spec/features/consumer/cookies_spec.rb + - spec/features/consumer/groups_spec.rb + - spec/features/consumer/multilingual_spec.rb + - spec/features/consumer/registration_spec.rb + - spec/features/consumer/shopping/cart_spec.rb + - spec/features/consumer/shopping/checkout_auth_spec.rb + - spec/features/consumer/shopping/checkout_spec.rb + - spec/features/consumer/shopping/embedded_groups_spec.rb + - spec/features/consumer/shopping/embedded_shopfronts_spec.rb + - spec/features/consumer/shopping/orders_spec.rb + - spec/features/consumer/shopping/products_spec.rb + - spec/features/consumer/shopping/shopping_spec.rb + - spec/features/consumer/shopping/variant_overrides_spec.rb + - spec/features/consumer/shops_spec.rb + - spec/helpers/admin/business_model_configuration_helper_spec.rb + - spec/helpers/admin/subscriptions_helper_spec.rb + - spec/helpers/checkout_helper_spec.rb + - spec/helpers/enterprises_helper_spec.rb + - spec/helpers/groups_helper_spec.rb + - spec/helpers/injection_helper_spec.rb + - spec/helpers/order_cycles_helper_spec.rb + - spec/helpers/spree/admin/base_helper_spec.rb + - spec/jobs/confirm_order_job_spec.rb + - spec/jobs/finalize_account_invoices_spec.rb + - spec/jobs/refresh_products_cache_job_spec.rb + - spec/jobs/subscription_confirm_job_spec.rb + - spec/jobs/subscription_placement_job_spec.rb + - spec/jobs/update_account_invoices_spec.rb + - spec/jobs/update_billable_periods_spec.rb + - spec/lib/open_food_network/address_finder_spec.rb + - spec/lib/open_food_network/bulk_coop_report_spec.rb + - spec/lib/open_food_network/cached_products_renderer_spec.rb + - spec/lib/open_food_network/customers_report_spec.rb + - spec/lib/open_food_network/distribution_change_validator_spec.rb + - spec/lib/open_food_network/enterprise_fee_applicator_spec.rb + - spec/lib/open_food_network/enterprise_fee_calculator_spec.rb + - spec/lib/open_food_network/enterprise_injection_data_spec.rb + - spec/lib/open_food_network/group_buy_report_spec.rb + - spec/lib/open_food_network/lettuce_share_report_spec.rb + - spec/lib/open_food_network/option_value_namer_spec.rb + - spec/lib/open_food_network/order_and_distributor_report_spec.rb + - spec/lib/open_food_network/order_cycle_form_applicator_spec.rb + - spec/lib/open_food_network/order_cycle_permissions_spec.rb + - spec/lib/open_food_network/order_grouper_spec.rb + - spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb + - spec/lib/open_food_network/packing_report_spec.rb + - spec/lib/open_food_network/permissions_spec.rb + - spec/lib/open_food_network/products_and_inventory_report_spec.rb + - spec/lib/open_food_network/products_cache_spec.rb + - spec/lib/open_food_network/products_renderer_spec.rb + - spec/lib/open_food_network/proxy_order_syncer_spec.rb + - spec/lib/open_food_network/scope_variant_to_hub_spec.rb + - spec/lib/open_food_network/subscription_payment_updater_spec.rb + - spec/lib/open_food_network/subscription_summarizer_spec.rb + - spec/lib/open_food_network/tag_rule_applicator_spec.rb + - spec/lib/open_food_network/users_and_enterprises_report_spec.rb + - spec/lib/open_food_network/xero_invoices_report_spec.rb + - spec/lib/stripe/account_connector_spec.rb + - spec/lib/stripe/webhook_handler_spec.rb + - spec/mailers/order_mailer_spec.rb + - spec/mailers/producer_mailer_spec.rb + - spec/mailers/subscription_mailer_spec.rb + - spec/models/billable_period_spec.rb + - spec/models/column_preference_spec.rb + - spec/models/customer_spec.rb + - spec/models/enterprise_caching_spec.rb + - spec/models/enterprise_fee_spec.rb + - spec/models/enterprise_group_spec.rb + - 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/producer_property_spec.rb + - spec/models/product_distribution_spec.rb + - spec/models/product_importer_spec.rb + - spec/models/proxy_order_spec.rb + - spec/models/spree/ability_spec.rb + - spec/models/spree/adjustment_spec.rb + - spec/models/spree/calculator/flexi_rate_spec.rb + - spec/models/spree/calculator/price_sack_spec.rb + - spec/models/spree/classification_spec.rb + - spec/models/spree/gateway/stripe_connect_spec.rb + - spec/models/spree/image_spec.rb + - spec/models/spree/line_item_spec.rb + - spec/models/spree/order_spec.rb + - spec/models/spree/payment_method_spec.rb + - spec/models/spree/payment_spec.rb + - spec/models/spree/product_spec.rb + - spec/models/spree/property_spec.rb + - spec/models/spree/shipping_method_spec.rb + - spec/models/spree/taxon_spec.rb + - spec/models/spree/tax_rate_spec.rb + - spec/models/spree/user_spec.rb + - spec/models/spree/variant_spec.rb + - spec/models/stripe_account_spec.rb + - spec/models/tag_rule/discount_order_spec.rb + - spec/models/tag_rule/filter_order_cycles_spec.rb + - spec/models/tag_rule/filter_payment_methods_spec.rb + - spec/models/tag_rule/filter_products_spec.rb + - spec/models/tag_rule/filter_shipping_methods_spec.rb + - spec/models/variant_override_spec.rb + - spec/performance/orders_controller_spec.rb + - spec/performance/proxy_order_syncer_spec.rb + - spec/performance/shop_controller_spec.rb + - spec/requests/checkout/failed_checkout_spec.rb + - spec/requests/checkout/paypal_spec.rb + - spec/requests/checkout/stripe_connect_spec.rb + - spec/requests/embedded_shopfronts_headers_spec.rb + - spec/requests/shop_spec.rb + - spec/serializers/admin/customer_serializer_spec.rb + - spec/serializers/admin/exchange_serializer_spec.rb + - spec/serializers/admin/for_order_cycle/enterprise_serializer_spec.rb + - spec/serializers/admin/for_order_cycle/supplied_product_serializer_spec.rb + - spec/serializers/admin/subscription_customer_serializer_spec.rb + - spec/serializers/admin/variant_override_serializer_spec.rb + - spec/serializers/current_order_serializer.rb + - spec/serializers/enterprise_serializer_spec.rb + - spec/serializers/order_serializer_spec.rb + - spec/services/cart_service_spec.rb + - spec/services/embedded_page_service_spec.rb + - spec/services/order_cycle_form_spec.rb + - spec/services/order_factory_spec.rb + - spec/services/order_syncer_spec.rb + - spec/services/subscription_estimator_spec.rb + - spec/services/subscription_form_spec.rb + - spec/services/subscription_validator_spec.rb + - spec/spec_helper.rb + - spec/support/cancan_helper.rb + - spec/support/delayed_job_helper.rb + - spec/support/matchers/delegate_matchers.rb + - spec/support/matchers/select2_matchers.rb + - spec/support/matchers/table_matchers.rb + - spec/support/request/authentication_workflow.rb + - spec/support/request/shop_workflow.rb + - spec/support/request/web_helper.rb + - spec/support/seeds.rb + - spec/support/spree/init.rb + +Metrics/AbcSize: + Max: 15 + Exclude: + - app/controllers/admin/bulk_line_items_controller.rb + - app/controllers/admin/customers_controller.rb + - app/controllers/admin/enterprise_fees_controller.rb + - app/controllers/admin/enterprises_controller.rb + - app/controllers/admin/order_cycles_controller.rb + - app/controllers/admin/product_import_controller.rb + - app/controllers/admin/schedules_controller.rb + - app/controllers/admin/stripe_accounts_controller.rb + - app/controllers/admin/subscription_line_items_controller.rb + - app/controllers/admin/subscriptions_controller.rb + - app/controllers/api/enterprises_controller.rb + - app/controllers/api/order_cycles_controller.rb + - app/controllers/api/product_images_controller.rb + - app/controllers/base_controller.rb + - app/controllers/cart_controller.rb + - app/controllers/checkout_controller.rb + - app/controllers/discourse_sso_controller.rb + - app/controllers/enterprises_controller.rb + - app/controllers/spree/admin/adjustments_controller_decorator.rb + - app/controllers/spree/admin/line_items_controller_decorator.rb + - app/controllers/spree/admin/orders_controller_decorator.rb + - app/controllers/spree/admin/overview_controller_decorator.rb + - app/controllers/spree/admin/payment_methods_controller_decorator.rb + - app/controllers/spree/admin/payments_controller_decorator.rb + - app/controllers/spree/admin/products_controller_decorator.rb + - app/controllers/spree/admin/reports_controller_decorator.rb + - app/controllers/spree/admin/search_controller_decorator.rb + - app/controllers/spree/admin/shipping_methods_controller_decorator.rb + - app/controllers/spree/api/products_controller_decorator.rb + - app/controllers/spree/credit_cards_controller.rb + - app/controllers/spree/orders_controller_decorator.rb + - app/controllers/spree/user_sessions_controller_decorator.rb + - app/controllers/stripe/callbacks_controller.rb + - app/controllers/user_confirmations_controller.rb + - app/controllers/user_passwords_controller.rb + - app/controllers/user_registrations_controller.rb + - app/helpers/admin/business_model_configuration_helper.rb + - app/helpers/checkout_helper.rb + - app/helpers/i18n_helper.rb + - app/helpers/order_cycles_helper.rb + - app/helpers/spree/admin/orders_helper_decorator.rb + - app/helpers/spree/orders_helper.rb + - app/jobs/finalize_account_invoices.rb + - app/jobs/subscription_placement_job.rb + - app/jobs/update_account_invoices.rb + - app/jobs/update_billable_periods.rb + - app/mailers/producer_mailer.rb + - app/models/billable_period.rb + - app/models/calculator/flat_percent_per_item.rb + - app/models/column_preference.rb + - 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/proxy_order.rb + - app/models/spree/ability_decorator.rb + - app/models/spree/adjustment_decorator.rb + - app/models/spree/calculator/default_tax_decorator.rb + - app/models/spree/calculator/flexi_rate_decorator.rb + - app/models/spree/line_item_decorator.rb + - app/models/spree/order_decorator.rb + - app/models/spree/payment_decorator.rb + - app/models/spree/product_decorator.rb + - app/models/spree/product_set.rb + - app/models/spree/taxon_decorator.rb + - app/serializers/api/admin/enterprise_serializer.rb + - app/serializers/api/enterprise_serializer.rb + - app/serializers/api/product_serializer.rb + - app/serializers/api/variant_serializer.rb + - app/services/cart_service.rb + - app/services/create_order_cycle.rb + - app/services/order_syncer.rb + - app/services/subscription_validator.rb + - lib/discourse/single_sign_on.rb + - lib/open_food_network/bill_calculator.rb + - lib/open_food_network/bulk_coop_report.rb + - lib/open_food_network/customers_report.rb + - lib/open_food_network/enterprise_issue_validator.rb + - lib/open_food_network/group_buy_report.rb + - lib/open_food_network/lettuce_share_report.rb + - lib/open_food_network/option_value_namer.rb + - lib/open_food_network/order_and_distributor_report.rb + - lib/open_food_network/order_cycle_form_applicator.rb + - lib/open_food_network/order_cycle_management_report.rb + - lib/open_food_network/order_cycle_permissions.rb + - lib/open_food_network/orders_and_fulfillments_report.rb + - lib/open_food_network/packing_report.rb + - lib/open_food_network/payments_report.rb + - lib/open_food_network/permissions.rb + - lib/open_food_network/products_and_inventory_report.rb + - lib/open_food_network/reports/line_items.rb + - lib/open_food_network/sales_tax_report.rb + - lib/open_food_network/users_and_enterprises_report.rb + - lib/open_food_network/variant_and_line_item_naming.rb + - lib/open_food_network/xero_invoices_report.rb + - lib/spree/core/controller_helpers/respond_with_decorator.rb + - lib/spree/localized_number.rb + - lib/stripe/account_connector.rb + - lib/tasks/enterprises.rake + - spec/archive/features/consumer/checkout_spec.rb + - spec/controllers/spree/admin/orders_controller_spec.rb + - spec/features/admin/reports_spec.rb + - spec/features/consumer/shopping/checkout_spec.rb + - spec/features/consumer/shopping/variant_overrides_spec.rb + - spec/models/enterprise_spec.rb + - spec/models/product_importer_spec.rb + - spec/support/performance_helper.rb + +Metrics/CyclomaticComplexity: + Max: 6 + Exclude: + - app/controllers/admin/enterprise_fees_controller.rb + - app/controllers/admin/enterprises_controller.rb + - app/controllers/checkout_controller.rb + - app/controllers/spree/admin/orders_controller_decorator.rb + - app/controllers/spree/orders_controller_decorator.rb + - app/helpers/admin/business_model_configuration_helper.rb + - app/helpers/checkout_helper.rb + - app/helpers/i18n_helper.rb + - app/helpers/order_cycles_helper.rb + - app/helpers/spree/admin/orders_helper_decorator.rb + - app/jobs/update_account_invoices.rb + - app/jobs/update_billable_periods.rb + - app/models/enterprise.rb + - app/models/enterprise_relationship.rb + - app/models/product_import/entry_processor.rb + - app/models/product_import/entry_validator.rb + - app/models/spree/ability_decorator.rb + - app/models/spree/adjustment_decorator.rb + - app/models/spree/payment_decorator.rb + - app/models/spree/product_decorator.rb + - app/models/spree/product_set.rb + - app/models/variant_override_set.rb + - app/services/cart_service.rb + - lib/discourse/single_sign_on.rb + - lib/open_food_network/bill_calculator.rb + - lib/open_food_network/bulk_coop_report.rb + - lib/open_food_network/enterprise_issue_validator.rb + - lib/open_food_network/orders_and_fulfillments_report.rb + - lib/spree/core/controller_helpers/order_decorator.rb + - lib/spree/core/controller_helpers/respond_with_decorator.rb + - lib/spree/localized_number.rb + - spec/models/product_importer_spec.rb + +Metrics/PerceivedComplexity: + Max: 7 + Exclude: + - app/controllers/admin/enterprises_controller.rb + - app/controllers/checkout_controller.rb + - app/controllers/spree/admin/orders_controller_decorator.rb + - app/controllers/spree/orders_controller_decorator.rb + - app/helpers/admin/business_model_configuration_helper.rb + - app/helpers/checkout_helper.rb + - app/helpers/i18n_helper.rb + - app/helpers/order_cycles_helper.rb + - app/helpers/spree/admin/orders_helper_decorator.rb + - app/jobs/update_account_invoices.rb + - app/models/enterprise_relationship.rb + - app/models/product_import/entry_processor.rb + - app/models/product_import/entry_validator.rb + - app/models/spree/ability_decorator.rb + - app/models/spree/line_item_decorator.rb + - app/models/spree/order_decorator.rb + - app/models/spree/product_decorator.rb + - app/models/spree/product_set.rb + - lib/discourse/single_sign_on.rb + - lib/open_food_network/bulk_coop_report.rb + - lib/open_food_network/enterprise_issue_validator.rb + - lib/open_food_network/orders_and_fulfillments_report.rb + - lib/spree/core/controller_helpers/order_decorator.rb + - lib/spree/core/controller_helpers/respond_with_decorator.rb + - lib/spree/localized_number.rb + - spec/models/product_importer_spec.rb + +Metrics/MethodLength: + Max: 10 + Exclude: + - app/controllers/admin/customers_controller.rb + - app/controllers/admin/enterprise_fees_controller.rb + - app/controllers/admin/enterprises_controller.rb + - app/controllers/admin/manager_invitations_controller.rb + - app/controllers/admin/order_cycles_controller.rb + - app/controllers/admin/stripe_accounts_controller.rb + - app/controllers/admin/subscriptions_controller.rb + - app/controllers/base_controller.rb + - app/controllers/cart_controller.rb + - app/controllers/checkout_controller.rb + - app/controllers/shop_controller.rb + - app/controllers/spree/admin/line_items_controller_decorator.rb + - app/controllers/spree/admin/orders_controller_decorator.rb + - app/controllers/spree/admin/payment_methods_controller_decorator.rb + - app/controllers/spree/admin/payments_controller_decorator.rb + - app/controllers/spree/admin/products_controller_decorator.rb + - app/controllers/spree/admin/reports_controller_decorator.rb + - app/controllers/spree/admin/search_controller_decorator.rb + - app/controllers/spree/admin/shipping_methods_controller_decorator.rb + - app/controllers/spree/credit_cards_controller.rb + - app/controllers/spree/orders_controller_decorator.rb + - app/controllers/spree/user_sessions_controller_decorator.rb + - app/controllers/stripe/callbacks_controller.rb + - app/controllers/user_confirmations_controller.rb + - app/controllers/user_passwords_controller.rb + - app/controllers/user_registrations_controller.rb + - app/helpers/checkout_helper.rb + - app/helpers/order_cycles_helper.rb + - app/helpers/spree/admin/orders_helper_decorator.rb + - app/jobs/finalize_account_invoices.rb + - app/jobs/subscription_placement_job.rb + - app/jobs/update_account_invoices.rb + - app/jobs/update_billable_periods.rb + - app/mailers/producer_mailer.rb + - app/models/billable_period.rb + - app/models/column_preference.rb + - app/models/enterprise.rb + - app/models/enterprise_relationship.rb + - app/models/preference_sections/footer_and_external_links_section.rb + - app/models/preference_sections/main_links_section.rb + - app/models/product_import/entry_processor.rb + - app/models/product_import/entry_validator.rb + - app/models/product_import/product_importer.rb + - app/models/spree/ability_decorator.rb + - app/models/spree/adjustment_decorator.rb + - app/models/spree/calculator/default_tax_decorator.rb + - app/models/spree/calculator/flexi_rate_decorator.rb + - app/models/spree/line_item_decorator.rb + - app/models/spree/order_decorator.rb + - app/models/spree/payment_decorator.rb + - app/models/spree/payment_method_decorator.rb + - app/models/spree/product_set.rb + - app/models/spree/taxon_decorator.rb + - app/serializers/api/admin/order_cycle_serializer.rb + - app/services/cart_service.rb + - app/services/order_cycle_form.rb + - lib/discourse/single_sign_on.rb + - lib/open_food_network/bulk_coop_report.rb + - lib/open_food_network/cached_products_renderer.rb + - lib/open_food_network/column_preference_defaults.rb + - lib/open_food_network/customers_report.rb + - lib/open_food_network/enterprise_fee_calculator.rb + - lib/open_food_network/group_buy_report.rb + - lib/open_food_network/lettuce_share_report.rb + - lib/open_food_network/option_value_namer.rb + - lib/open_food_network/order_and_distributor_report.rb + - lib/open_food_network/order_cycle_form_applicator.rb + - lib/open_food_network/order_cycle_management_report.rb + - lib/open_food_network/order_cycle_permissions.rb + - lib/open_food_network/order_grouper.rb + - lib/open_food_network/orders_and_fulfillments_report.rb + - lib/open_food_network/packing_report.rb + - lib/open_food_network/payments_report.rb + - lib/open_food_network/permissions.rb + - lib/open_food_network/products_and_inventory_report.rb + - lib/open_food_network/products_renderer.rb + - lib/open_food_network/rack_request_blocker.rb + - lib/open_food_network/reports/bulk_coop_allocation_report.rb + - lib/open_food_network/reports/bulk_coop_supplier_report.rb + - lib/open_food_network/reports/line_items.rb + - lib/open_food_network/sales_tax_report.rb + - lib/open_food_network/users_and_enterprises_report.rb + - lib/open_food_network/xero_invoices_report.rb + - lib/spree/core/controller_helpers/respond_with_decorator.rb + - lib/spree/localized_number.rb + - lib/stripe/profile_storer.rb + - spec/archive/features/consumer/checkout_spec.rb + - spec/features/consumer/shopping/checkout_spec.rb + - spec/features/consumer/shopping/variant_overrides_spec.rb + - spec/models/product_importer_spec.rb + - spec/support/request/authentication_workflow.rb + +Metrics/ClassLength: + Max: 100 + Exclude: + - app/controllers/admin/enterprises_controller.rb + - app/controllers/admin/order_cycles_controller.rb + - app/controllers/admin/subscriptions_controller.rb + - app/controllers/checkout_controller.rb + - app/models/enterprise.rb + - app/models/order_cycle.rb + - app/models/product_import/entry_processor.rb + - app/models/product_import/entry_validator.rb + - app/models/product_import/product_importer.rb + - app/models/spree/ability_decorator.rb + - app/serializers/api/enterprise_serializer.rb + - app/services/cart_service.rb + - lib/open_food_network/bulk_coop_report.rb + - lib/open_food_network/enterprise_fee_calculator.rb + - lib/open_food_network/order_cycle_form_applicator.rb + - lib/open_food_network/order_cycle_management_report.rb + - lib/open_food_network/order_cycle_permissions.rb + - lib/open_food_network/orders_and_fulfillments_report.rb + - lib/open_food_network/packing_report.rb + - lib/open_food_network/payments_report.rb + - lib/open_food_network/permissions.rb + - lib/open_food_network/products_cache.rb + - lib/open_food_network/xero_invoices_report.rb + +Metrics/ModuleLength: + Max: 100 + Exclude: + - lib/open_food_network/column_preference_defaults.rb + - spec/controllers/admin/enterprises_controller_spec.rb + - spec/controllers/admin/order_cycles_controller_spec.rb + - spec/controllers/api/order_cycles_controller_spec.rb + - spec/controllers/spree/api/products_controller_spec.rb + - spec/lib/open_food_network/address_finder_spec.rb + - spec/lib/open_food_network/customers_report_spec.rb + - spec/lib/open_food_network/enterprise_fee_calculator_spec.rb + - spec/lib/open_food_network/option_value_namer_spec.rb + - spec/lib/open_food_network/order_cycle_form_applicator_spec.rb + - spec/lib/open_food_network/order_cycle_permissions_spec.rb + - spec/lib/open_food_network/order_grouper_spec.rb + - spec/lib/open_food_network/permissions_spec.rb + - spec/lib/open_food_network/products_and_inventory_report_spec.rb + - spec/lib/open_food_network/products_cache_spec.rb + - spec/lib/open_food_network/proxy_order_syncer_spec.rb + - spec/lib/open_food_network/scope_variant_to_hub_spec.rb + - spec/lib/open_food_network/subscription_payment_updater_spec.rb + - spec/lib/open_food_network/tag_rule_applicator_spec.rb + - spec/lib/open_food_network/users_and_enterprises_report_spec.rb + - spec/models/spree/ability_spec.rb + - spec/models/spree/adjustment_spec.rb + - spec/models/spree/line_item_spec.rb + - spec/models/spree/payment_spec.rb + - spec/models/spree/product_spec.rb + - spec/models/spree/variant_spec.rb + - spec/support/request/web_helper.rb + +Metrics/ParameterLists: + Max: 5 + Exclude: + - app/helpers/angular_form_builder.rb + - app/models/product_import/entry_processor.rb + - app/models/product_import/entry_validator.rb + - lib/open_food_network/xero_invoices_report.rb + - spec/features/admin/reports_spec.rb + +Metrics/BlockNesting: + Max: 3 + Exclude: + - app/controllers/checkout_controller.rb From 7ad40074a589c8c068fb1210e319e04c5c46ff01 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 2 Nov 2018 08:23:13 +1100 Subject: [PATCH 57/65] Make Code Climate show our real score ans split rubocop config Our rubocop config hides all current violations. It allows us to have a passing rubocop run on the current code and improve it gradually. It detects new violations, but doesn't annoy us with all the existing ones. Code Climate has its own way of remembering all current violations which is more sophisticated than ours. The new config for Code Climate doesn't hide any violations so that Code Climate can give us a realistic score of code quality and warn us about every new violation. Splitting the configuration into the main three parts gives a quicker overview and makes maintaining the parts easier. --- .codeclimate.yml | 2 + .rubocop.yml | 956 +-------------------------------------- .rubocop_manual_todo.yml | 763 +++++++++++++++++++++++++++++++ .rubocop_styleguide.yml | 208 +++++++++ 4 files changed, 989 insertions(+), 940 deletions(-) create mode 100644 .rubocop_manual_todo.yml create mode 100644 .rubocop_styleguide.yml diff --git a/.codeclimate.yml b/.codeclimate.yml index 60c015c60e..c72c2e5543 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -3,6 +3,8 @@ plugins: rubocop: enabled: true channel: "rubocop-0-57" + config: + file: ".rubocop_styleguide.yml" scss-lint: enabled: true checks: diff --git a/.rubocop.yml b/.rubocop.yml index cc46db7805..c68377cb20 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,944 +1,20 @@ +# This is our main Rubocop configuration for developers. It is used when you run: +# +# bundle exec rubocop +# +# The configuration is split into three files. Look into those files for more details. +# inherit_from: + + # The automatically generated todo list to ignore all current violations. - .rubocop_todo.yml -AllCops: - TargetRubyVersion: 2.1 - TargetRailsVersion: 3.2 - Exclude: - - 'db/**/*' - - 'config/**/*' - - 'script/**/*' - - 'vendor/**/*' - - 'node_modules/**/*' - # The parser gem fails to parse this file with out current Ruby version. - - 'spec/factories.rb' - # Excluding: inadequate Naming/FileName rule rejects GemFile name with camelcase - - 'engines/web/Gemfile' + # Our Open Food Network style guide. It's used by Code Climate. If you want to see all violations, + # then use only that configuration (like Code Climate): + # + # bundle exec rubocop -c .rubocop_styleguide.yml + # + - .rubocop_styleguide.yml -# OFN SETTINGS -# Cop settings that have been agreed upon by the OFN community - -Rails: - Enabled: true - -Style/Documentation: - Enabled: false - -Style/StringLiterals: - Enabled: false - -Style/HashSyntax: - Enabled: true - EnforcedStyle: ruby19_no_mixed_keys - -Style/Send: - Enabled: true - -Layout/MultilineMethodCallIndentation: - Enabled: true - EnforcedStyle: indented - -# TEMPORARY/CONTESTED SETTINGS -# These are still to be decided upon, but recommended for inclusion by -# oeoeaio after scrutinising offenses the codebase - -# Don't think this is a big issue, mostly picking up RPSEC scope definitions -# with lamdas and RSpec '.to change{}' blocks -Lint/AmbiguousBlockAssociation: - Enabled: false - -# Heaps of offences (> 100) in specs, mostly in situations where two or more -# instances of a model are required, but only one is referenced. Difficult to -# fix without making the spec look messy or rewriting it. -# Should definitely fix at some point. -Lint/UselessAssignment: - Exclude: - - spec/**/* - -# AFAIK, there is no good alternative to dynamic matchers until we upgrade -# to Rails 4 and can use #find_by. If there is a better approach, let's do it. -Rails/DynamicFindBy: - Enabled: false - -# Same as above, #find_by is not available until Rails 4 -Rails/FindBy: - Enabled: false - -# Same as above, #update! is not available until Rails 4 -Rails/ActiveRecordAliases: - Enabled: false - -# This should be the programmer's discretion, perhaps we should review all of -# the uses of it an make specific exceptions though. -Rails/SkipsModelValidations: - Enabled: false - -# Relaxed.Ruby.Style SETTINGS -# These styles are a starting point for the conversation around conventions -# They should be removed or tweaked and moved above as decisions are made -# NOTE: Cops which did not fail at the time of writing were removed - -Layout/DotPosition: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#styledotposition - -Layout/SpaceBeforeBlockBraces: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#stylespacebeforeblockbraces - -Layout/SpaceInsideParens: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#stylespaceinsideparens - -Style/Alias: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#stylealias - -Style/BlockDelimiters: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#styleblockdelimiters - -Style/CommentAnnotation: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#stylecommentannotation - -Style/DoubleNegation: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#styledoublenegation - -Style/FormatString: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#styleformatstring - -Style/IfUnlessModifier: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#styleifunlessmodifier - -Style/Lambda: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#stylelambda - -Style/MultilineBlockChain: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#stylemultilineblockchain - -Style/NegatedIf: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#stylenegatedif - -Style/NegatedWhile: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#stylenegatedwhile - -Style/ParallelAssignment: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#styleparallelassignment - -Style/PercentLiteralDelimiters: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#stylepercentliteraldelimiters - -Style/Semicolon: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#stylesemicolon - -Style/SingleLineMethods: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#stylesinglelinemethods - -Style/TrailingCommaInArguments: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#styletrailingcommainarguments - -Style/TrailingCommaInArrayLiteral: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#styletrailingcommainliteral - -Style/TrailingCommaInHashLiteral: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#styletrailingcommainliteral - -Style/WordArray: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#stylewordarray - -Style/SymbolArray: - Enabled: false - StyleGuide: https://rubocop.readthedocs.io/en/latest/cops_style/#stylesymbolarray - -Lint/AmbiguousRegexpLiteral: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#lintambiguousregexpliteral - -Lint/AssignmentInCondition: - Enabled: false - StyleGuide: http://relaxed.ruby.style/#lintassignmentincondition - -Metrics/AbcSize: - Max: 15 - -Metrics/BlockNesting: - Max: 3 - -Metrics/ClassLength: - Max: 100 - -Metrics/ModuleLength: - Max: 100 - -Metrics/CyclomaticComplexity: - Max: 6 - -Metrics/LineLength: - Max: 100 - -Metrics/MethodLength: - Max: 10 - -Metrics/ParameterLists: - Max: 5 - -Metrics/PerceivedComplexity: - Max: 7 - -# Additional todo list not generated by --autogen-config -Metrics/LineLength: - Max: 100 - Exclude: - - app/controllers/admin/accounts_and_billing_settings_controller.rb - - app/controllers/admin/bulk_line_items_controller.rb - - app/controllers/admin/business_model_configuration_controller.rb - - app/controllers/admin/cache_settings_controller.rb - - app/controllers/admin/contents_controller.rb - - app/controllers/admin/customers_controller.rb - - app/controllers/admin/enterprise_fees_controller.rb - - app/controllers/admin/enterprise_groups_controller.rb - - app/controllers/admin/enterprise_relationships_controller.rb - - app/controllers/admin/enterprises_controller.rb - - app/controllers/admin/inventory_items_controller.rb - - app/controllers/admin/manager_invitations_controller.rb - - app/controllers/admin/order_cycles_controller.rb - - app/controllers/admin/product_import_controller.rb - - app/controllers/admin/proxy_orders_controller.rb - - app/controllers/admin/schedules_controller.rb - - app/controllers/admin/subscription_line_items_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/application_controller.rb - - app/controllers/base_controller.rb - - app/controllers/cart_controller.rb - - app/controllers/checkout_controller.rb - - app/controllers/enterprises_controller.rb - - app/controllers/shop_controller.rb - - app/controllers/spree/admin/adjustments_controller_decorator.rb - - app/controllers/spree/admin/base_controller_decorator.rb - - app/controllers/spree/admin/line_items_controller_decorator.rb - - app/controllers/spree/admin/orders_controller_decorator.rb - - app/controllers/spree/admin/payment_methods_controller_decorator.rb - - app/controllers/spree/admin/products_controller_decorator.rb - - app/controllers/spree/admin/reports_controller_decorator.rb - - app/controllers/spree/admin/shipping_methods_controller_decorator.rb - - app/controllers/spree/api/products_controller_decorator.rb - - app/controllers/spree/credit_cards_controller.rb - - app/controllers/spree/orders_controller_decorator.rb - - app/controllers/spree/paypal_controller_decorator.rb - - app/controllers/stripe/callbacks_controller.rb - - app/controllers/user_confirmations_controller.rb - - app/helpers/admin/account_helper.rb - - app/helpers/admin/business_model_configuration_helper.rb - - app/helpers/admin/injection_helper.rb - - app/helpers/angular_form_builder.rb - - app/helpers/angular_form_helper.rb - - app/helpers/application_helper.rb - - app/helpers/checkout_helper.rb - - app/helpers/enterprises_helper.rb - - app/helpers/footer_links_helper.rb - - app/helpers/injection_helper.rb - - app/helpers/markdown_helper.rb - - app/helpers/order_cycles_helper.rb - - app/helpers/shop_helper.rb - - app/helpers/spree/admin/base_helper_decorator.rb - - app/helpers/spree/admin/navigation_helper_decorator.rb - - app/helpers/spree/admin/orders_helper_decorator.rb - - app/helpers/spree/orders_helper.rb - - app/jobs/finalize_account_invoices.rb - - app/jobs/products_cache_integrity_checker_job.rb - - app/jobs/subscription_confirm_job.rb - - app/jobs/subscription_placement_job.rb - - app/jobs/update_account_invoices.rb - - app/jobs/update_billable_periods.rb - - app/mailers/producer_mailer.rb - - app/mailers/spree/order_mailer_decorator.rb - - app/mailers/subscription_mailer.rb - - app/models/billable_period.rb - - app/models/column_preference.rb - - app/models/content_configuration.rb - - app/models/customer.rb - - app/models/enterprise_fee.rb - - app/models/enterprise.rb - - app/models/enterprise_relationship.rb - - app/models/enterprise_role.rb - - app/models/exchange.rb - - app/models/inventory_item.rb - - app/models/order_cycle.rb - - app/models/product_distribution.rb - - app/models/product_import/entry_processor.rb - - app/models/product_import/entry_validator.rb - - app/models/product_import/product_importer.rb - - app/models/product_import/spreadsheet_data.rb - - app/models/product_import/spreadsheet_entry.rb - - app/models/product_import/unit_converter.rb - - app/models/proxy_order.rb - - app/models/schedule.rb - - app/models/spree/ability_decorator.rb - - app/models/spree/adjustment_decorator.rb - - app/models/spree/calculator/default_tax_decorator.rb - - app/models/spree/calculator/flexi_rate_decorator.rb - - app/models/spree/classification_decorator.rb - - app/models/spree/gateway/stripe_connect.rb - - app/models/spree/line_item_decorator.rb - - app/models/spree/order_decorator.rb - - app/models/spree/payment_decorator.rb - - app/models/spree/payment_method_decorator.rb - - app/models/spree/product_decorator.rb - - app/models/spree/shipment_decorator.rb - - app/models/spree/shipping_method_decorator.rb - - app/models/spree/taxon_decorator.rb - - app/models/spree/user_decorator.rb - - app/models/spree/variant_decorator.rb - - app/models/subscription.rb - - app/models/variant_override.rb - - app/models/variant_override_set.rb - - app/overrides/add_distributor_details_js_to_product.rb - - app/overrides/add_enterprise_fees_to_admin_configurations_menu.rb - - app/overrides/replace_checkout_payment_button.rb - - app/overrides/replace_payment_name_with_description.rb - - app/serializers/api/admin/basic_enterprise_serializer.rb - - app/serializers/api/admin/enterprise_fee_serializer.rb - - app/serializers/api/admin/enterprise_serializer.rb - - app/serializers/api/admin/exchange_serializer.rb - - app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb - - app/serializers/api/admin/index_enterprise_serializer.rb - - app/serializers/api/admin/index_order_cycle_serializer.rb - - app/serializers/api/admin/line_item_serializer.rb - - app/serializers/api/admin/order_cycle_serializer.rb - - app/serializers/api/admin/product_serializer.rb - - app/serializers/api/admin/subscription_serializer.rb - - app/serializers/api/admin/tag_rule_serializer.rb - - app/serializers/api/admin/variant_override_serializer.rb - - app/serializers/api/admin/variant_serializer.rb - - app/services/cart_service.rb - - app/services/embedded_page_service.rb - - app/services/line_item_syncer.rb - - app/services/order_cycle_form.rb - - app/services/order_factory.rb - - app/services/order_syncer.rb - - app/services/subscriptions_count.rb - - app/views/json/_groups.rabl - - app/views/json/partials/_enterprise.rabl - - app/views/json/_producer.rabl - - app/views/spree/api/products/bulk_show.v1.rabl - - app/views/spree/api/variants/bulk_show.v1.rabl - - engines/web/app/helpers/web/cookies_policy_helper.rb - - Gemfile - - lib/discourse/single_sign_on.rb - - lib/open_food_network/accounts_and_billing_settings_validator.rb - - lib/open_food_network/available_payment_method_filter.rb - - lib/open_food_network/bill_calculator.rb - - lib/open_food_network/bulk_coop_report.rb - - lib/open_food_network/business_model_configuration_validator.rb - - lib/open_food_network/customers_report.rb - - lib/open_food_network/distribution_change_validator.rb - - lib/open_food_network/enterprise_fee_applicator.rb - - lib/open_food_network/enterprise_fee_calculator.rb - - lib/open_food_network/enterprise_issue_validator.rb - - lib/open_food_network/group_buy_report.rb - - lib/open_food_network/lettuce_share_report.rb - - lib/open_food_network/locking.rb - - lib/open_food_network/order_and_distributor_report.rb - - lib/open_food_network/order_cycle_form_applicator.rb - - lib/open_food_network/order_cycle_management_report.rb - - lib/open_food_network/order_cycle_permissions.rb - - lib/open_food_network/order_grouper.rb - - lib/open_food_network/orders_and_fulfillments_report.rb - - lib/open_food_network/payments_report.rb - - lib/open_food_network/permalink_generator.rb - - lib/open_food_network/permissions.rb - - lib/open_food_network/products_and_inventory_report_base.rb - - lib/open_food_network/products_cache.rb - - lib/open_food_network/proxy_order_syncer.rb - - lib/open_food_network/reports/bulk_coop_allocation_report.rb - - lib/open_food_network/reports/line_items.rb - - lib/open_food_network/sales_tax_report.rb - - lib/open_food_network/users_and_enterprises_report.rb - - lib/open_food_network/variant_and_line_item_naming.rb - - lib/open_food_network/xero_invoices_report.rb - - lib/spree/core/calculated_adjustments_decorator.rb - - lib/spree/core/controller_helpers/respond_with_decorator.rb - - lib/spree/localized_number.rb - - lib/spree/product_filters.rb - - lib/stripe/profile_storer.rb - - lib/tasks/cache.rake - - lib/tasks/data.rake - - lib/tasks/dev.rake - - lib/tasks/enterprises.rake - - spec/archive/features/consumer/checkout_spec.rb - - spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb - - spec/controllers/admin/bulk_line_items_controller_spec.rb - - spec/controllers/admin/column_preferences_controller_spec.rb - - spec/controllers/admin/customers_controller_spec.rb - - spec/controllers/admin/enterprises_controller_spec.rb - - spec/controllers/admin/inventory_items_controller_spec.rb - - spec/controllers/admin/manager_invitations_controller_spec.rb - - spec/controllers/admin/order_cycles_controller_spec.rb - - spec/controllers/admin/proxy_orders_controller_spec.rb - - spec/controllers/admin/schedules_controller_spec.rb - - spec/controllers/admin/stripe_accounts_controller_spec.rb - - spec/controllers/admin/stripe_connect_settings_controller_spec.rb - - spec/controllers/admin/subscription_line_items_controller_spec.rb - - spec/controllers/admin/subscriptions_controller_spec.rb - - spec/controllers/admin/variant_overrides_controller_spec.rb - - spec/controllers/api/logos_controller_spec.rb - - spec/controllers/api/order_cycles_controller_spec.rb - - spec/controllers/api/product_images_controller_spec.rb - - spec/controllers/api/promo_images_controller_spec.rb - - spec/controllers/cart_controller_spec.rb - - spec/controllers/checkout_controller_spec.rb - - spec/controllers/enterprises_controller_spec.rb - - spec/controllers/line_items_controller_spec.rb - - spec/controllers/shop_controller_spec.rb - - spec/controllers/shops_controller_spec.rb - - spec/controllers/spree/admin/adjustments_controller_spec.rb - - spec/controllers/spree/admin/base_controller_spec.rb - - spec/controllers/spree/admin/line_items_controller_spec.rb - - spec/controllers/spree/admin/orders_controller_spec.rb - - spec/controllers/spree/admin/orders/customer_details_controller_spec.rb - - spec/controllers/spree/admin/payment_methods_controller_spec.rb - - spec/controllers/spree/admin/payments_controller_spec.rb - - spec/controllers/spree/admin/reports_controller_spec.rb - - spec/controllers/spree/api/products_controller_spec.rb - - spec/controllers/spree/api/variants_controller_spec.rb - - spec/controllers/spree/credit_cards_controller_spec.rb - - spec/controllers/spree/orders_controller_spec.rb - - spec/controllers/spree/users_controller_spec.rb - - spec/controllers/spree/user_sessions_controller_spec.rb - - spec/controllers/stripe/callbacks_controller_spec.rb - - spec/controllers/stripe/webhooks_controller_spec.rb - - spec/controllers/user_confirmations_controller_spec.rb - - spec/controllers/user_registrations_controller_spec.rb - - spec/features/admin/accounts_and_billing_settings_spec.rb - - spec/features/admin/adjustments_spec.rb - - spec/features/admin/bulk_order_management_spec.rb - - spec/features/admin/bulk_product_update_spec.rb - - spec/features/admin/customers_spec.rb - - spec/features/admin/enterprise_fees_spec.rb - - spec/features/admin/enterprise_relationships_spec.rb - - spec/features/admin/enterprise_roles_spec.rb - - spec/features/admin/enterprises/images_spec.rb - - spec/features/admin/enterprises/index_spec.rb - - spec/features/admin/enterprises_spec.rb - - spec/features/admin/enterprise_user_spec.rb - - spec/features/admin/image_settings_spec.rb - - spec/features/admin/multilingual_spec.rb - - spec/features/admin/order_cycles_spec.rb - - spec/features/admin/orders_spec.rb - - spec/features/admin/overview_spec.rb - - spec/features/admin/payment_method_spec.rb - - spec/features/admin/product_import_spec.rb - - spec/features/admin/products_spec.rb - - spec/features/admin/reports_spec.rb - - spec/features/admin/schedules_spec.rb - - spec/features/admin/shipping_methods_spec.rb - - spec/features/admin/subscriptions_spec.rb - - spec/features/admin/tag_rules_spec.rb - - spec/features/admin/variant_overrides_spec.rb - - spec/features/consumer/account/cards_spec.rb - - spec/features/consumer/account/settings_spec.rb - - spec/features/consumer/account_spec.rb - - spec/features/consumer/authentication_spec.rb - - spec/features/consumer/cookies_spec.rb - - spec/features/consumer/groups_spec.rb - - spec/features/consumer/multilingual_spec.rb - - spec/features/consumer/registration_spec.rb - - spec/features/consumer/shopping/cart_spec.rb - - spec/features/consumer/shopping/checkout_auth_spec.rb - - spec/features/consumer/shopping/checkout_spec.rb - - spec/features/consumer/shopping/embedded_groups_spec.rb - - spec/features/consumer/shopping/embedded_shopfronts_spec.rb - - spec/features/consumer/shopping/orders_spec.rb - - spec/features/consumer/shopping/products_spec.rb - - spec/features/consumer/shopping/shopping_spec.rb - - spec/features/consumer/shopping/variant_overrides_spec.rb - - spec/features/consumer/shops_spec.rb - - spec/helpers/admin/business_model_configuration_helper_spec.rb - - spec/helpers/admin/subscriptions_helper_spec.rb - - spec/helpers/checkout_helper_spec.rb - - spec/helpers/enterprises_helper_spec.rb - - spec/helpers/groups_helper_spec.rb - - spec/helpers/injection_helper_spec.rb - - spec/helpers/order_cycles_helper_spec.rb - - spec/helpers/spree/admin/base_helper_spec.rb - - spec/jobs/confirm_order_job_spec.rb - - spec/jobs/finalize_account_invoices_spec.rb - - spec/jobs/refresh_products_cache_job_spec.rb - - spec/jobs/subscription_confirm_job_spec.rb - - spec/jobs/subscription_placement_job_spec.rb - - spec/jobs/update_account_invoices_spec.rb - - spec/jobs/update_billable_periods_spec.rb - - spec/lib/open_food_network/address_finder_spec.rb - - spec/lib/open_food_network/bulk_coop_report_spec.rb - - spec/lib/open_food_network/cached_products_renderer_spec.rb - - spec/lib/open_food_network/customers_report_spec.rb - - spec/lib/open_food_network/distribution_change_validator_spec.rb - - spec/lib/open_food_network/enterprise_fee_applicator_spec.rb - - spec/lib/open_food_network/enterprise_fee_calculator_spec.rb - - spec/lib/open_food_network/enterprise_injection_data_spec.rb - - spec/lib/open_food_network/group_buy_report_spec.rb - - spec/lib/open_food_network/lettuce_share_report_spec.rb - - spec/lib/open_food_network/option_value_namer_spec.rb - - spec/lib/open_food_network/order_and_distributor_report_spec.rb - - spec/lib/open_food_network/order_cycle_form_applicator_spec.rb - - spec/lib/open_food_network/order_cycle_permissions_spec.rb - - spec/lib/open_food_network/order_grouper_spec.rb - - spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb - - spec/lib/open_food_network/packing_report_spec.rb - - spec/lib/open_food_network/permissions_spec.rb - - spec/lib/open_food_network/products_and_inventory_report_spec.rb - - spec/lib/open_food_network/products_cache_spec.rb - - spec/lib/open_food_network/products_renderer_spec.rb - - spec/lib/open_food_network/proxy_order_syncer_spec.rb - - spec/lib/open_food_network/scope_variant_to_hub_spec.rb - - spec/lib/open_food_network/subscription_payment_updater_spec.rb - - spec/lib/open_food_network/subscription_summarizer_spec.rb - - spec/lib/open_food_network/tag_rule_applicator_spec.rb - - spec/lib/open_food_network/users_and_enterprises_report_spec.rb - - spec/lib/open_food_network/xero_invoices_report_spec.rb - - spec/lib/stripe/account_connector_spec.rb - - spec/lib/stripe/webhook_handler_spec.rb - - spec/mailers/order_mailer_spec.rb - - spec/mailers/producer_mailer_spec.rb - - spec/mailers/subscription_mailer_spec.rb - - spec/models/billable_period_spec.rb - - spec/models/column_preference_spec.rb - - spec/models/customer_spec.rb - - spec/models/enterprise_caching_spec.rb - - spec/models/enterprise_fee_spec.rb - - spec/models/enterprise_group_spec.rb - - 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/producer_property_spec.rb - - spec/models/product_distribution_spec.rb - - spec/models/product_importer_spec.rb - - spec/models/proxy_order_spec.rb - - spec/models/spree/ability_spec.rb - - spec/models/spree/adjustment_spec.rb - - spec/models/spree/calculator/flexi_rate_spec.rb - - spec/models/spree/calculator/price_sack_spec.rb - - spec/models/spree/classification_spec.rb - - spec/models/spree/gateway/stripe_connect_spec.rb - - spec/models/spree/image_spec.rb - - spec/models/spree/line_item_spec.rb - - spec/models/spree/order_spec.rb - - spec/models/spree/payment_method_spec.rb - - spec/models/spree/payment_spec.rb - - spec/models/spree/product_spec.rb - - spec/models/spree/property_spec.rb - - spec/models/spree/shipping_method_spec.rb - - spec/models/spree/taxon_spec.rb - - spec/models/spree/tax_rate_spec.rb - - spec/models/spree/user_spec.rb - - spec/models/spree/variant_spec.rb - - spec/models/stripe_account_spec.rb - - spec/models/tag_rule/discount_order_spec.rb - - spec/models/tag_rule/filter_order_cycles_spec.rb - - spec/models/tag_rule/filter_payment_methods_spec.rb - - spec/models/tag_rule/filter_products_spec.rb - - spec/models/tag_rule/filter_shipping_methods_spec.rb - - spec/models/variant_override_spec.rb - - spec/performance/orders_controller_spec.rb - - spec/performance/proxy_order_syncer_spec.rb - - spec/performance/shop_controller_spec.rb - - spec/requests/checkout/failed_checkout_spec.rb - - spec/requests/checkout/paypal_spec.rb - - spec/requests/checkout/stripe_connect_spec.rb - - spec/requests/embedded_shopfronts_headers_spec.rb - - spec/requests/shop_spec.rb - - spec/serializers/admin/customer_serializer_spec.rb - - spec/serializers/admin/exchange_serializer_spec.rb - - spec/serializers/admin/for_order_cycle/enterprise_serializer_spec.rb - - spec/serializers/admin/for_order_cycle/supplied_product_serializer_spec.rb - - spec/serializers/admin/subscription_customer_serializer_spec.rb - - spec/serializers/admin/variant_override_serializer_spec.rb - - spec/serializers/current_order_serializer.rb - - spec/serializers/enterprise_serializer_spec.rb - - spec/serializers/order_serializer_spec.rb - - spec/services/cart_service_spec.rb - - spec/services/embedded_page_service_spec.rb - - spec/services/order_cycle_form_spec.rb - - spec/services/order_factory_spec.rb - - spec/services/order_syncer_spec.rb - - spec/services/subscription_estimator_spec.rb - - spec/services/subscription_form_spec.rb - - spec/services/subscription_validator_spec.rb - - spec/spec_helper.rb - - spec/support/cancan_helper.rb - - spec/support/delayed_job_helper.rb - - spec/support/matchers/delegate_matchers.rb - - spec/support/matchers/select2_matchers.rb - - spec/support/matchers/table_matchers.rb - - spec/support/request/authentication_workflow.rb - - spec/support/request/shop_workflow.rb - - spec/support/request/web_helper.rb - - spec/support/seeds.rb - - spec/support/spree/init.rb - -Metrics/AbcSize: - Max: 15 - Exclude: - - app/controllers/admin/bulk_line_items_controller.rb - - app/controllers/admin/customers_controller.rb - - app/controllers/admin/enterprise_fees_controller.rb - - app/controllers/admin/enterprises_controller.rb - - app/controllers/admin/order_cycles_controller.rb - - app/controllers/admin/product_import_controller.rb - - app/controllers/admin/schedules_controller.rb - - app/controllers/admin/stripe_accounts_controller.rb - - app/controllers/admin/subscription_line_items_controller.rb - - app/controllers/admin/subscriptions_controller.rb - - app/controllers/api/enterprises_controller.rb - - app/controllers/api/order_cycles_controller.rb - - app/controllers/api/product_images_controller.rb - - app/controllers/base_controller.rb - - app/controllers/cart_controller.rb - - app/controllers/checkout_controller.rb - - app/controllers/discourse_sso_controller.rb - - app/controllers/enterprises_controller.rb - - app/controllers/spree/admin/adjustments_controller_decorator.rb - - app/controllers/spree/admin/line_items_controller_decorator.rb - - app/controllers/spree/admin/orders_controller_decorator.rb - - app/controllers/spree/admin/overview_controller_decorator.rb - - app/controllers/spree/admin/payment_methods_controller_decorator.rb - - app/controllers/spree/admin/payments_controller_decorator.rb - - app/controllers/spree/admin/products_controller_decorator.rb - - app/controllers/spree/admin/reports_controller_decorator.rb - - app/controllers/spree/admin/search_controller_decorator.rb - - app/controllers/spree/admin/shipping_methods_controller_decorator.rb - - app/controllers/spree/api/products_controller_decorator.rb - - app/controllers/spree/credit_cards_controller.rb - - app/controllers/spree/orders_controller_decorator.rb - - app/controllers/spree/user_sessions_controller_decorator.rb - - app/controllers/stripe/callbacks_controller.rb - - app/controllers/user_confirmations_controller.rb - - app/controllers/user_passwords_controller.rb - - app/controllers/user_registrations_controller.rb - - app/helpers/admin/business_model_configuration_helper.rb - - app/helpers/checkout_helper.rb - - app/helpers/i18n_helper.rb - - app/helpers/order_cycles_helper.rb - - app/helpers/spree/admin/orders_helper_decorator.rb - - app/helpers/spree/orders_helper.rb - - app/jobs/finalize_account_invoices.rb - - app/jobs/subscription_placement_job.rb - - app/jobs/update_account_invoices.rb - - app/jobs/update_billable_periods.rb - - app/mailers/producer_mailer.rb - - app/models/billable_period.rb - - app/models/calculator/flat_percent_per_item.rb - - app/models/column_preference.rb - - 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/proxy_order.rb - - app/models/spree/ability_decorator.rb - - app/models/spree/adjustment_decorator.rb - - app/models/spree/calculator/default_tax_decorator.rb - - app/models/spree/calculator/flexi_rate_decorator.rb - - app/models/spree/line_item_decorator.rb - - app/models/spree/order_decorator.rb - - app/models/spree/payment_decorator.rb - - app/models/spree/product_decorator.rb - - app/models/spree/product_set.rb - - app/models/spree/taxon_decorator.rb - - app/serializers/api/admin/enterprise_serializer.rb - - app/serializers/api/enterprise_serializer.rb - - app/serializers/api/product_serializer.rb - - app/serializers/api/variant_serializer.rb - - app/services/cart_service.rb - - app/services/create_order_cycle.rb - - app/services/order_syncer.rb - - app/services/subscription_validator.rb - - lib/discourse/single_sign_on.rb - - lib/open_food_network/bill_calculator.rb - - lib/open_food_network/bulk_coop_report.rb - - lib/open_food_network/customers_report.rb - - lib/open_food_network/enterprise_issue_validator.rb - - lib/open_food_network/group_buy_report.rb - - lib/open_food_network/lettuce_share_report.rb - - lib/open_food_network/option_value_namer.rb - - lib/open_food_network/order_and_distributor_report.rb - - lib/open_food_network/order_cycle_form_applicator.rb - - lib/open_food_network/order_cycle_management_report.rb - - lib/open_food_network/order_cycle_permissions.rb - - lib/open_food_network/orders_and_fulfillments_report.rb - - lib/open_food_network/packing_report.rb - - lib/open_food_network/payments_report.rb - - lib/open_food_network/permissions.rb - - lib/open_food_network/products_and_inventory_report.rb - - lib/open_food_network/reports/line_items.rb - - lib/open_food_network/sales_tax_report.rb - - lib/open_food_network/users_and_enterprises_report.rb - - lib/open_food_network/variant_and_line_item_naming.rb - - lib/open_food_network/xero_invoices_report.rb - - lib/spree/core/controller_helpers/respond_with_decorator.rb - - lib/spree/localized_number.rb - - lib/stripe/account_connector.rb - - lib/tasks/enterprises.rake - - spec/archive/features/consumer/checkout_spec.rb - - spec/controllers/spree/admin/orders_controller_spec.rb - - spec/features/admin/reports_spec.rb - - spec/features/consumer/shopping/checkout_spec.rb - - spec/features/consumer/shopping/variant_overrides_spec.rb - - spec/models/enterprise_spec.rb - - spec/models/product_importer_spec.rb - - spec/support/performance_helper.rb - -Metrics/CyclomaticComplexity: - Max: 6 - Exclude: - - app/controllers/admin/enterprise_fees_controller.rb - - app/controllers/admin/enterprises_controller.rb - - app/controllers/checkout_controller.rb - - app/controllers/spree/admin/orders_controller_decorator.rb - - app/controllers/spree/orders_controller_decorator.rb - - app/helpers/admin/business_model_configuration_helper.rb - - app/helpers/checkout_helper.rb - - app/helpers/i18n_helper.rb - - app/helpers/order_cycles_helper.rb - - app/helpers/spree/admin/orders_helper_decorator.rb - - app/jobs/update_account_invoices.rb - - app/jobs/update_billable_periods.rb - - app/models/enterprise.rb - - app/models/enterprise_relationship.rb - - app/models/product_import/entry_processor.rb - - app/models/product_import/entry_validator.rb - - app/models/spree/ability_decorator.rb - - app/models/spree/adjustment_decorator.rb - - app/models/spree/payment_decorator.rb - - app/models/spree/product_decorator.rb - - app/models/spree/product_set.rb - - app/models/variant_override_set.rb - - app/services/cart_service.rb - - lib/discourse/single_sign_on.rb - - lib/open_food_network/bill_calculator.rb - - lib/open_food_network/bulk_coop_report.rb - - lib/open_food_network/enterprise_issue_validator.rb - - lib/open_food_network/orders_and_fulfillments_report.rb - - lib/spree/core/controller_helpers/order_decorator.rb - - lib/spree/core/controller_helpers/respond_with_decorator.rb - - lib/spree/localized_number.rb - - spec/models/product_importer_spec.rb - -Metrics/PerceivedComplexity: - Max: 7 - Exclude: - - app/controllers/admin/enterprises_controller.rb - - app/controllers/checkout_controller.rb - - app/controllers/spree/admin/orders_controller_decorator.rb - - app/controllers/spree/orders_controller_decorator.rb - - app/helpers/admin/business_model_configuration_helper.rb - - app/helpers/checkout_helper.rb - - app/helpers/i18n_helper.rb - - app/helpers/order_cycles_helper.rb - - app/helpers/spree/admin/orders_helper_decorator.rb - - app/jobs/update_account_invoices.rb - - app/models/enterprise_relationship.rb - - app/models/product_import/entry_processor.rb - - app/models/product_import/entry_validator.rb - - app/models/spree/ability_decorator.rb - - app/models/spree/line_item_decorator.rb - - app/models/spree/order_decorator.rb - - app/models/spree/product_decorator.rb - - app/models/spree/product_set.rb - - lib/discourse/single_sign_on.rb - - lib/open_food_network/bulk_coop_report.rb - - lib/open_food_network/enterprise_issue_validator.rb - - lib/open_food_network/orders_and_fulfillments_report.rb - - lib/spree/core/controller_helpers/order_decorator.rb - - lib/spree/core/controller_helpers/respond_with_decorator.rb - - lib/spree/localized_number.rb - - spec/models/product_importer_spec.rb - -Metrics/MethodLength: - Max: 10 - Exclude: - - app/controllers/admin/customers_controller.rb - - app/controllers/admin/enterprise_fees_controller.rb - - app/controllers/admin/enterprises_controller.rb - - app/controllers/admin/manager_invitations_controller.rb - - app/controllers/admin/order_cycles_controller.rb - - app/controllers/admin/stripe_accounts_controller.rb - - app/controllers/admin/subscriptions_controller.rb - - app/controllers/base_controller.rb - - app/controllers/cart_controller.rb - - app/controllers/checkout_controller.rb - - app/controllers/shop_controller.rb - - app/controllers/spree/admin/line_items_controller_decorator.rb - - app/controllers/spree/admin/orders_controller_decorator.rb - - app/controllers/spree/admin/payment_methods_controller_decorator.rb - - app/controllers/spree/admin/payments_controller_decorator.rb - - app/controllers/spree/admin/products_controller_decorator.rb - - app/controllers/spree/admin/reports_controller_decorator.rb - - app/controllers/spree/admin/search_controller_decorator.rb - - app/controllers/spree/admin/shipping_methods_controller_decorator.rb - - app/controllers/spree/credit_cards_controller.rb - - app/controllers/spree/orders_controller_decorator.rb - - app/controllers/spree/user_sessions_controller_decorator.rb - - app/controllers/stripe/callbacks_controller.rb - - app/controllers/user_confirmations_controller.rb - - app/controllers/user_passwords_controller.rb - - app/controllers/user_registrations_controller.rb - - app/helpers/checkout_helper.rb - - app/helpers/order_cycles_helper.rb - - app/helpers/spree/admin/orders_helper_decorator.rb - - app/jobs/finalize_account_invoices.rb - - app/jobs/subscription_placement_job.rb - - app/jobs/update_account_invoices.rb - - app/jobs/update_billable_periods.rb - - app/mailers/producer_mailer.rb - - app/models/billable_period.rb - - app/models/column_preference.rb - - app/models/enterprise.rb - - app/models/enterprise_relationship.rb - - app/models/preference_sections/footer_and_external_links_section.rb - - app/models/preference_sections/main_links_section.rb - - app/models/product_import/entry_processor.rb - - app/models/product_import/entry_validator.rb - - app/models/product_import/product_importer.rb - - app/models/spree/ability_decorator.rb - - app/models/spree/adjustment_decorator.rb - - app/models/spree/calculator/default_tax_decorator.rb - - app/models/spree/calculator/flexi_rate_decorator.rb - - app/models/spree/line_item_decorator.rb - - app/models/spree/order_decorator.rb - - app/models/spree/payment_decorator.rb - - app/models/spree/payment_method_decorator.rb - - app/models/spree/product_set.rb - - app/models/spree/taxon_decorator.rb - - app/serializers/api/admin/order_cycle_serializer.rb - - app/services/cart_service.rb - - app/services/order_cycle_form.rb - - lib/discourse/single_sign_on.rb - - lib/open_food_network/bulk_coop_report.rb - - lib/open_food_network/cached_products_renderer.rb - - lib/open_food_network/column_preference_defaults.rb - - lib/open_food_network/customers_report.rb - - lib/open_food_network/enterprise_fee_calculator.rb - - lib/open_food_network/group_buy_report.rb - - lib/open_food_network/lettuce_share_report.rb - - lib/open_food_network/option_value_namer.rb - - lib/open_food_network/order_and_distributor_report.rb - - lib/open_food_network/order_cycle_form_applicator.rb - - lib/open_food_network/order_cycle_management_report.rb - - lib/open_food_network/order_cycle_permissions.rb - - lib/open_food_network/order_grouper.rb - - lib/open_food_network/orders_and_fulfillments_report.rb - - lib/open_food_network/packing_report.rb - - lib/open_food_network/payments_report.rb - - lib/open_food_network/permissions.rb - - lib/open_food_network/products_and_inventory_report.rb - - lib/open_food_network/products_renderer.rb - - lib/open_food_network/rack_request_blocker.rb - - lib/open_food_network/reports/bulk_coop_allocation_report.rb - - lib/open_food_network/reports/bulk_coop_supplier_report.rb - - lib/open_food_network/reports/line_items.rb - - lib/open_food_network/sales_tax_report.rb - - lib/open_food_network/users_and_enterprises_report.rb - - lib/open_food_network/xero_invoices_report.rb - - lib/spree/core/controller_helpers/respond_with_decorator.rb - - lib/spree/localized_number.rb - - lib/stripe/profile_storer.rb - - spec/archive/features/consumer/checkout_spec.rb - - spec/features/consumer/shopping/checkout_spec.rb - - spec/features/consumer/shopping/variant_overrides_spec.rb - - spec/models/product_importer_spec.rb - - spec/support/request/authentication_workflow.rb - -Metrics/ClassLength: - Max: 100 - Exclude: - - app/controllers/admin/enterprises_controller.rb - - app/controllers/admin/order_cycles_controller.rb - - app/controllers/admin/subscriptions_controller.rb - - app/controllers/checkout_controller.rb - - app/models/enterprise.rb - - app/models/order_cycle.rb - - app/models/product_import/entry_processor.rb - - app/models/product_import/entry_validator.rb - - app/models/product_import/product_importer.rb - - app/models/spree/ability_decorator.rb - - app/serializers/api/enterprise_serializer.rb - - app/services/cart_service.rb - - lib/open_food_network/bulk_coop_report.rb - - lib/open_food_network/enterprise_fee_calculator.rb - - lib/open_food_network/order_cycle_form_applicator.rb - - lib/open_food_network/order_cycle_management_report.rb - - lib/open_food_network/order_cycle_permissions.rb - - lib/open_food_network/orders_and_fulfillments_report.rb - - lib/open_food_network/packing_report.rb - - lib/open_food_network/payments_report.rb - - lib/open_food_network/permissions.rb - - lib/open_food_network/products_cache.rb - - lib/open_food_network/xero_invoices_report.rb - -Metrics/ModuleLength: - Max: 100 - Exclude: - - lib/open_food_network/column_preference_defaults.rb - - spec/controllers/admin/enterprises_controller_spec.rb - - spec/controllers/admin/order_cycles_controller_spec.rb - - spec/controllers/api/order_cycles_controller_spec.rb - - spec/controllers/spree/api/products_controller_spec.rb - - spec/lib/open_food_network/address_finder_spec.rb - - spec/lib/open_food_network/customers_report_spec.rb - - spec/lib/open_food_network/enterprise_fee_calculator_spec.rb - - spec/lib/open_food_network/option_value_namer_spec.rb - - spec/lib/open_food_network/order_cycle_form_applicator_spec.rb - - spec/lib/open_food_network/order_cycle_permissions_spec.rb - - spec/lib/open_food_network/order_grouper_spec.rb - - spec/lib/open_food_network/permissions_spec.rb - - spec/lib/open_food_network/products_and_inventory_report_spec.rb - - spec/lib/open_food_network/products_cache_spec.rb - - spec/lib/open_food_network/proxy_order_syncer_spec.rb - - spec/lib/open_food_network/scope_variant_to_hub_spec.rb - - spec/lib/open_food_network/subscription_payment_updater_spec.rb - - spec/lib/open_food_network/tag_rule_applicator_spec.rb - - spec/lib/open_food_network/users_and_enterprises_report_spec.rb - - spec/models/spree/ability_spec.rb - - spec/models/spree/adjustment_spec.rb - - spec/models/spree/line_item_spec.rb - - spec/models/spree/payment_spec.rb - - spec/models/spree/product_spec.rb - - spec/models/spree/variant_spec.rb - - spec/support/request/web_helper.rb - -Metrics/ParameterLists: - Max: 5 - Exclude: - - app/helpers/angular_form_builder.rb - - app/models/product_import/entry_processor.rb - - app/models/product_import/entry_validator.rb - - lib/open_food_network/xero_invoices_report.rb - - spec/features/admin/reports_spec.rb - -Metrics/BlockNesting: - Max: 3 - Exclude: - - app/controllers/checkout_controller.rb + # A manually compiled todo list to ignore metrics violations on a file-by-file basis. + - .rubocop_manual_todo.yml diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml new file mode 100644 index 0000000000..390010ef06 --- /dev/null +++ b/.rubocop_manual_todo.yml @@ -0,0 +1,763 @@ +# A manually compiled todo list to ignore metrics violations on a file-by-file basis. +# +# The file .rubocop_todo.yml is generated automatically and contains a +# configuration to make all files pass. For a lot of cops it just lists +# the offending files. But for metrics cops it sets a different metric. +# +# Since we don't want these lax metrics, we override them in our config +# file .rubocop.yml which results in a lot of offences again. This file +# lists all offending files for each cop to make rubocop pass. We can +# improve the code over time and remove items from the list. +# +# This file was manually created by using the following tools: +# +# rubocop > rubo.log +# # inspect log file to see which cops are failing +# # copy cop configurations and add Exclude parameter +# grep ExampleCop rubo.log | cut -d ":" -f 1 | sort -u >> .rubocop.yml +# # use vim to add `- ` before each line +# +# This process probably doesn't need repeating. Otherwise there is plenty +# of room for improvements and automation. +Metrics/LineLength: + Max: 100 + Exclude: + - app/controllers/admin/accounts_and_billing_settings_controller.rb + - app/controllers/admin/bulk_line_items_controller.rb + - app/controllers/admin/business_model_configuration_controller.rb + - app/controllers/admin/cache_settings_controller.rb + - app/controllers/admin/contents_controller.rb + - app/controllers/admin/customers_controller.rb + - app/controllers/admin/enterprise_fees_controller.rb + - app/controllers/admin/enterprise_groups_controller.rb + - app/controllers/admin/enterprise_relationships_controller.rb + - app/controllers/admin/enterprises_controller.rb + - app/controllers/admin/inventory_items_controller.rb + - app/controllers/admin/manager_invitations_controller.rb + - app/controllers/admin/order_cycles_controller.rb + - app/controllers/admin/product_import_controller.rb + - app/controllers/admin/proxy_orders_controller.rb + - app/controllers/admin/schedules_controller.rb + - app/controllers/admin/subscription_line_items_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/application_controller.rb + - app/controllers/base_controller.rb + - app/controllers/cart_controller.rb + - app/controllers/checkout_controller.rb + - app/controllers/enterprises_controller.rb + - app/controllers/shop_controller.rb + - app/controllers/spree/admin/adjustments_controller_decorator.rb + - app/controllers/spree/admin/base_controller_decorator.rb + - app/controllers/spree/admin/line_items_controller_decorator.rb + - app/controllers/spree/admin/orders_controller_decorator.rb + - app/controllers/spree/admin/payment_methods_controller_decorator.rb + - app/controllers/spree/admin/products_controller_decorator.rb + - app/controllers/spree/admin/reports_controller_decorator.rb + - app/controllers/spree/admin/shipping_methods_controller_decorator.rb + - app/controllers/spree/api/products_controller_decorator.rb + - app/controllers/spree/credit_cards_controller.rb + - app/controllers/spree/orders_controller_decorator.rb + - app/controllers/spree/paypal_controller_decorator.rb + - app/controllers/stripe/callbacks_controller.rb + - app/controllers/user_confirmations_controller.rb + - app/helpers/admin/account_helper.rb + - app/helpers/admin/business_model_configuration_helper.rb + - app/helpers/admin/injection_helper.rb + - app/helpers/angular_form_builder.rb + - app/helpers/angular_form_helper.rb + - app/helpers/application_helper.rb + - app/helpers/checkout_helper.rb + - app/helpers/enterprises_helper.rb + - app/helpers/footer_links_helper.rb + - app/helpers/injection_helper.rb + - app/helpers/markdown_helper.rb + - app/helpers/order_cycles_helper.rb + - app/helpers/shop_helper.rb + - app/helpers/spree/admin/base_helper_decorator.rb + - app/helpers/spree/admin/navigation_helper_decorator.rb + - app/helpers/spree/admin/orders_helper_decorator.rb + - app/helpers/spree/orders_helper.rb + - app/jobs/finalize_account_invoices.rb + - app/jobs/products_cache_integrity_checker_job.rb + - app/jobs/subscription_confirm_job.rb + - app/jobs/subscription_placement_job.rb + - app/jobs/update_account_invoices.rb + - app/jobs/update_billable_periods.rb + - app/mailers/producer_mailer.rb + - app/mailers/spree/order_mailer_decorator.rb + - app/mailers/subscription_mailer.rb + - app/models/billable_period.rb + - app/models/column_preference.rb + - app/models/content_configuration.rb + - app/models/customer.rb + - app/models/enterprise_fee.rb + - app/models/enterprise.rb + - app/models/enterprise_relationship.rb + - app/models/enterprise_role.rb + - app/models/exchange.rb + - app/models/inventory_item.rb + - app/models/order_cycle.rb + - app/models/product_distribution.rb + - app/models/product_import/entry_processor.rb + - app/models/product_import/entry_validator.rb + - app/models/product_import/product_importer.rb + - app/models/product_import/spreadsheet_data.rb + - app/models/product_import/spreadsheet_entry.rb + - app/models/product_import/unit_converter.rb + - app/models/proxy_order.rb + - app/models/schedule.rb + - app/models/spree/ability_decorator.rb + - app/models/spree/adjustment_decorator.rb + - app/models/spree/calculator/default_tax_decorator.rb + - app/models/spree/calculator/flexi_rate_decorator.rb + - app/models/spree/classification_decorator.rb + - app/models/spree/gateway/stripe_connect.rb + - app/models/spree/line_item_decorator.rb + - app/models/spree/order_decorator.rb + - app/models/spree/payment_decorator.rb + - app/models/spree/payment_method_decorator.rb + - app/models/spree/product_decorator.rb + - app/models/spree/shipment_decorator.rb + - app/models/spree/shipping_method_decorator.rb + - app/models/spree/taxon_decorator.rb + - app/models/spree/user_decorator.rb + - app/models/spree/variant_decorator.rb + - app/models/subscription.rb + - app/models/variant_override.rb + - app/models/variant_override_set.rb + - app/overrides/add_distributor_details_js_to_product.rb + - app/overrides/add_enterprise_fees_to_admin_configurations_menu.rb + - app/overrides/replace_checkout_payment_button.rb + - app/overrides/replace_payment_name_with_description.rb + - app/serializers/api/admin/basic_enterprise_serializer.rb + - app/serializers/api/admin/enterprise_fee_serializer.rb + - app/serializers/api/admin/enterprise_serializer.rb + - app/serializers/api/admin/exchange_serializer.rb + - app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb + - app/serializers/api/admin/index_enterprise_serializer.rb + - app/serializers/api/admin/index_order_cycle_serializer.rb + - app/serializers/api/admin/line_item_serializer.rb + - app/serializers/api/admin/order_cycle_serializer.rb + - app/serializers/api/admin/product_serializer.rb + - app/serializers/api/admin/subscription_serializer.rb + - app/serializers/api/admin/tag_rule_serializer.rb + - app/serializers/api/admin/variant_override_serializer.rb + - app/serializers/api/admin/variant_serializer.rb + - app/services/cart_service.rb + - app/services/embedded_page_service.rb + - app/services/line_item_syncer.rb + - app/services/order_cycle_form.rb + - app/services/order_factory.rb + - app/services/order_syncer.rb + - app/services/subscriptions_count.rb + - app/views/json/_groups.rabl + - app/views/json/partials/_enterprise.rabl + - app/views/json/_producer.rabl + - app/views/spree/api/products/bulk_show.v1.rabl + - app/views/spree/api/variants/bulk_show.v1.rabl + - engines/web/app/helpers/web/cookies_policy_helper.rb + - Gemfile + - lib/discourse/single_sign_on.rb + - lib/open_food_network/accounts_and_billing_settings_validator.rb + - lib/open_food_network/available_payment_method_filter.rb + - lib/open_food_network/bill_calculator.rb + - lib/open_food_network/bulk_coop_report.rb + - lib/open_food_network/business_model_configuration_validator.rb + - lib/open_food_network/customers_report.rb + - lib/open_food_network/distribution_change_validator.rb + - lib/open_food_network/enterprise_fee_applicator.rb + - lib/open_food_network/enterprise_fee_calculator.rb + - lib/open_food_network/enterprise_issue_validator.rb + - lib/open_food_network/group_buy_report.rb + - lib/open_food_network/lettuce_share_report.rb + - lib/open_food_network/locking.rb + - lib/open_food_network/order_and_distributor_report.rb + - lib/open_food_network/order_cycle_form_applicator.rb + - lib/open_food_network/order_cycle_management_report.rb + - lib/open_food_network/order_cycle_permissions.rb + - lib/open_food_network/order_grouper.rb + - lib/open_food_network/orders_and_fulfillments_report.rb + - lib/open_food_network/payments_report.rb + - lib/open_food_network/permalink_generator.rb + - lib/open_food_network/permissions.rb + - lib/open_food_network/products_and_inventory_report_base.rb + - lib/open_food_network/products_cache.rb + - lib/open_food_network/proxy_order_syncer.rb + - lib/open_food_network/reports/bulk_coop_allocation_report.rb + - lib/open_food_network/reports/line_items.rb + - lib/open_food_network/sales_tax_report.rb + - lib/open_food_network/users_and_enterprises_report.rb + - lib/open_food_network/variant_and_line_item_naming.rb + - lib/open_food_network/xero_invoices_report.rb + - lib/spree/core/calculated_adjustments_decorator.rb + - lib/spree/core/controller_helpers/respond_with_decorator.rb + - lib/spree/localized_number.rb + - lib/spree/product_filters.rb + - lib/stripe/profile_storer.rb + - lib/tasks/cache.rake + - lib/tasks/data.rake + - lib/tasks/dev.rake + - lib/tasks/enterprises.rake + - spec/archive/features/consumer/checkout_spec.rb + - spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb + - spec/controllers/admin/bulk_line_items_controller_spec.rb + - spec/controllers/admin/column_preferences_controller_spec.rb + - spec/controllers/admin/customers_controller_spec.rb + - spec/controllers/admin/enterprises_controller_spec.rb + - spec/controllers/admin/inventory_items_controller_spec.rb + - spec/controllers/admin/manager_invitations_controller_spec.rb + - spec/controllers/admin/order_cycles_controller_spec.rb + - spec/controllers/admin/proxy_orders_controller_spec.rb + - spec/controllers/admin/schedules_controller_spec.rb + - spec/controllers/admin/stripe_accounts_controller_spec.rb + - spec/controllers/admin/stripe_connect_settings_controller_spec.rb + - spec/controllers/admin/subscription_line_items_controller_spec.rb + - spec/controllers/admin/subscriptions_controller_spec.rb + - spec/controllers/admin/variant_overrides_controller_spec.rb + - spec/controllers/api/logos_controller_spec.rb + - spec/controllers/api/order_cycles_controller_spec.rb + - spec/controllers/api/orders_controller_spec.rb + - spec/controllers/api/product_images_controller_spec.rb + - spec/controllers/api/promo_images_controller_spec.rb + - spec/controllers/cart_controller_spec.rb + - spec/controllers/checkout_controller_spec.rb + - spec/controllers/enterprises_controller_spec.rb + - spec/controllers/line_items_controller_spec.rb + - spec/controllers/shop_controller_spec.rb + - spec/controllers/shops_controller_spec.rb + - spec/controllers/spree/admin/adjustments_controller_spec.rb + - spec/controllers/spree/admin/base_controller_spec.rb + - spec/controllers/spree/admin/line_items_controller_spec.rb + - spec/controllers/spree/admin/orders_controller_spec.rb + - spec/controllers/spree/admin/orders/customer_details_controller_spec.rb + - spec/controllers/spree/admin/payment_methods_controller_spec.rb + - spec/controllers/spree/admin/payments_controller_spec.rb + - spec/controllers/spree/admin/reports_controller_spec.rb + - spec/controllers/spree/api/products_controller_spec.rb + - spec/controllers/spree/api/variants_controller_spec.rb + - spec/controllers/spree/credit_cards_controller_spec.rb + - spec/controllers/spree/orders_controller_spec.rb + - spec/controllers/spree/users_controller_spec.rb + - spec/controllers/spree/user_sessions_controller_spec.rb + - spec/controllers/stripe/callbacks_controller_spec.rb + - spec/controllers/stripe/webhooks_controller_spec.rb + - spec/controllers/user_confirmations_controller_spec.rb + - spec/controllers/user_registrations_controller_spec.rb + - spec/features/admin/accounts_and_billing_settings_spec.rb + - spec/features/admin/adjustments_spec.rb + - spec/features/admin/bulk_order_management_spec.rb + - spec/features/admin/bulk_product_update_spec.rb + - spec/features/admin/customers_spec.rb + - spec/features/admin/enterprise_fees_spec.rb + - spec/features/admin/enterprise_relationships_spec.rb + - spec/features/admin/enterprise_roles_spec.rb + - spec/features/admin/enterprises/images_spec.rb + - spec/features/admin/enterprises/index_spec.rb + - spec/features/admin/enterprises_spec.rb + - spec/features/admin/enterprise_user_spec.rb + - spec/features/admin/image_settings_spec.rb + - spec/features/admin/multilingual_spec.rb + - spec/features/admin/order_cycles_spec.rb + - spec/features/admin/orders_spec.rb + - spec/features/admin/overview_spec.rb + - spec/features/admin/payment_method_spec.rb + - spec/features/admin/product_import_spec.rb + - spec/features/admin/products_spec.rb + - spec/features/admin/reports_spec.rb + - spec/features/admin/schedules_spec.rb + - spec/features/admin/shipping_methods_spec.rb + - spec/features/admin/subscriptions_spec.rb + - spec/features/admin/tag_rules_spec.rb + - spec/features/admin/variant_overrides_spec.rb + - spec/features/consumer/account/cards_spec.rb + - spec/features/consumer/account/settings_spec.rb + - spec/features/consumer/account_spec.rb + - spec/features/consumer/authentication_spec.rb + - spec/features/consumer/cookies_spec.rb + - spec/features/consumer/groups_spec.rb + - spec/features/consumer/multilingual_spec.rb + - spec/features/consumer/registration_spec.rb + - spec/features/consumer/shopping/cart_spec.rb + - spec/features/consumer/shopping/checkout_auth_spec.rb + - spec/features/consumer/shopping/checkout_spec.rb + - spec/features/consumer/shopping/embedded_groups_spec.rb + - spec/features/consumer/shopping/embedded_shopfronts_spec.rb + - spec/features/consumer/shopping/orders_spec.rb + - spec/features/consumer/shopping/products_spec.rb + - spec/features/consumer/shopping/shopping_spec.rb + - spec/features/consumer/shopping/variant_overrides_spec.rb + - spec/features/consumer/shops_spec.rb + - spec/helpers/admin/business_model_configuration_helper_spec.rb + - spec/helpers/admin/subscriptions_helper_spec.rb + - spec/helpers/checkout_helper_spec.rb + - spec/helpers/enterprises_helper_spec.rb + - spec/helpers/groups_helper_spec.rb + - spec/helpers/injection_helper_spec.rb + - spec/helpers/order_cycles_helper_spec.rb + - spec/helpers/spree/admin/base_helper_spec.rb + - spec/jobs/confirm_order_job_spec.rb + - spec/jobs/finalize_account_invoices_spec.rb + - spec/jobs/refresh_products_cache_job_spec.rb + - spec/jobs/subscription_confirm_job_spec.rb + - spec/jobs/subscription_placement_job_spec.rb + - spec/jobs/update_account_invoices_spec.rb + - spec/jobs/update_billable_periods_spec.rb + - spec/lib/open_food_network/address_finder_spec.rb + - spec/lib/open_food_network/bulk_coop_report_spec.rb + - spec/lib/open_food_network/cached_products_renderer_spec.rb + - spec/lib/open_food_network/customers_report_spec.rb + - spec/lib/open_food_network/distribution_change_validator_spec.rb + - spec/lib/open_food_network/enterprise_fee_applicator_spec.rb + - spec/lib/open_food_network/enterprise_fee_calculator_spec.rb + - spec/lib/open_food_network/enterprise_injection_data_spec.rb + - spec/lib/open_food_network/group_buy_report_spec.rb + - spec/lib/open_food_network/lettuce_share_report_spec.rb + - spec/lib/open_food_network/option_value_namer_spec.rb + - spec/lib/open_food_network/order_and_distributor_report_spec.rb + - spec/lib/open_food_network/order_cycle_form_applicator_spec.rb + - spec/lib/open_food_network/order_cycle_permissions_spec.rb + - spec/lib/open_food_network/order_grouper_spec.rb + - spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb + - spec/lib/open_food_network/packing_report_spec.rb + - spec/lib/open_food_network/permissions_spec.rb + - spec/lib/open_food_network/products_and_inventory_report_spec.rb + - spec/lib/open_food_network/products_cache_spec.rb + - spec/lib/open_food_network/products_renderer_spec.rb + - spec/lib/open_food_network/proxy_order_syncer_spec.rb + - spec/lib/open_food_network/scope_variant_to_hub_spec.rb + - spec/lib/open_food_network/subscription_payment_updater_spec.rb + - spec/lib/open_food_network/subscription_summarizer_spec.rb + - spec/lib/open_food_network/tag_rule_applicator_spec.rb + - spec/lib/open_food_network/users_and_enterprises_report_spec.rb + - spec/lib/open_food_network/xero_invoices_report_spec.rb + - spec/lib/stripe/account_connector_spec.rb + - spec/lib/stripe/webhook_handler_spec.rb + - spec/mailers/order_mailer_spec.rb + - spec/mailers/producer_mailer_spec.rb + - spec/mailers/subscription_mailer_spec.rb + - spec/models/billable_period_spec.rb + - spec/models/column_preference_spec.rb + - spec/models/customer_spec.rb + - spec/models/enterprise_caching_spec.rb + - spec/models/enterprise_fee_spec.rb + - spec/models/enterprise_group_spec.rb + - 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/producer_property_spec.rb + - spec/models/product_distribution_spec.rb + - spec/models/product_importer_spec.rb + - spec/models/proxy_order_spec.rb + - spec/models/spree/ability_spec.rb + - spec/models/spree/adjustment_spec.rb + - spec/models/spree/calculator/flexi_rate_spec.rb + - spec/models/spree/calculator/price_sack_spec.rb + - spec/models/spree/classification_spec.rb + - spec/models/spree/gateway/stripe_connect_spec.rb + - spec/models/spree/image_spec.rb + - spec/models/spree/line_item_spec.rb + - spec/models/spree/order_spec.rb + - spec/models/spree/payment_method_spec.rb + - spec/models/spree/payment_spec.rb + - spec/models/spree/product_spec.rb + - spec/models/spree/property_spec.rb + - spec/models/spree/shipping_method_spec.rb + - spec/models/spree/taxon_spec.rb + - spec/models/spree/tax_rate_spec.rb + - spec/models/spree/user_spec.rb + - spec/models/spree/variant_spec.rb + - spec/models/stripe_account_spec.rb + - spec/models/tag_rule/discount_order_spec.rb + - spec/models/tag_rule/filter_order_cycles_spec.rb + - spec/models/tag_rule/filter_payment_methods_spec.rb + - spec/models/tag_rule/filter_products_spec.rb + - spec/models/tag_rule/filter_shipping_methods_spec.rb + - spec/models/variant_override_spec.rb + - spec/performance/orders_controller_spec.rb + - spec/performance/proxy_order_syncer_spec.rb + - spec/performance/shop_controller_spec.rb + - spec/requests/checkout/failed_checkout_spec.rb + - spec/requests/checkout/paypal_spec.rb + - spec/requests/checkout/stripe_connect_spec.rb + - spec/requests/embedded_shopfronts_headers_spec.rb + - spec/requests/shop_spec.rb + - spec/serializers/admin/customer_serializer_spec.rb + - spec/serializers/admin/exchange_serializer_spec.rb + - spec/serializers/admin/for_order_cycle/enterprise_serializer_spec.rb + - spec/serializers/admin/for_order_cycle/supplied_product_serializer_spec.rb + - spec/serializers/admin/subscription_customer_serializer_spec.rb + - spec/serializers/admin/variant_override_serializer_spec.rb + - spec/serializers/current_order_serializer.rb + - spec/serializers/enterprise_serializer_spec.rb + - spec/serializers/order_serializer_spec.rb + - spec/services/cart_service_spec.rb + - spec/services/embedded_page_service_spec.rb + - spec/services/order_cycle_form_spec.rb + - spec/services/order_factory_spec.rb + - spec/services/order_syncer_spec.rb + - spec/services/subscription_estimator_spec.rb + - spec/services/subscription_form_spec.rb + - spec/services/subscription_validator_spec.rb + - spec/spec_helper.rb + - spec/support/cancan_helper.rb + - spec/support/delayed_job_helper.rb + - spec/support/matchers/delegate_matchers.rb + - spec/support/matchers/select2_matchers.rb + - spec/support/matchers/table_matchers.rb + - spec/support/request/authentication_workflow.rb + - spec/support/request/shop_workflow.rb + - spec/support/request/web_helper.rb + - spec/support/seeds.rb + - spec/support/spree/init.rb + +Metrics/AbcSize: + Max: 15 + Exclude: + - app/controllers/admin/bulk_line_items_controller.rb + - app/controllers/admin/customers_controller.rb + - app/controllers/admin/enterprise_fees_controller.rb + - app/controllers/admin/enterprises_controller.rb + - app/controllers/admin/order_cycles_controller.rb + - app/controllers/admin/product_import_controller.rb + - app/controllers/admin/schedules_controller.rb + - app/controllers/admin/stripe_accounts_controller.rb + - app/controllers/admin/subscription_line_items_controller.rb + - app/controllers/admin/subscriptions_controller.rb + - app/controllers/api/enterprises_controller.rb + - app/controllers/api/order_cycles_controller.rb + - app/controllers/api/product_images_controller.rb + - app/controllers/base_controller.rb + - app/controllers/cart_controller.rb + - app/controllers/checkout_controller.rb + - app/controllers/discourse_sso_controller.rb + - app/controllers/enterprises_controller.rb + - app/controllers/spree/admin/adjustments_controller_decorator.rb + - app/controllers/spree/admin/line_items_controller_decorator.rb + - app/controllers/spree/admin/orders_controller_decorator.rb + - app/controllers/spree/admin/overview_controller_decorator.rb + - app/controllers/spree/admin/payment_methods_controller_decorator.rb + - app/controllers/spree/admin/payments_controller_decorator.rb + - app/controllers/spree/admin/products_controller_decorator.rb + - app/controllers/spree/admin/reports_controller_decorator.rb + - app/controllers/spree/admin/search_controller_decorator.rb + - app/controllers/spree/admin/shipping_methods_controller_decorator.rb + - app/controllers/spree/api/products_controller_decorator.rb + - app/controllers/spree/credit_cards_controller.rb + - app/controllers/spree/orders_controller_decorator.rb + - app/controllers/spree/user_sessions_controller_decorator.rb + - app/controllers/stripe/callbacks_controller.rb + - app/controllers/user_confirmations_controller.rb + - app/controllers/user_passwords_controller.rb + - app/controllers/user_registrations_controller.rb + - app/helpers/admin/business_model_configuration_helper.rb + - app/helpers/checkout_helper.rb + - app/helpers/i18n_helper.rb + - app/helpers/order_cycles_helper.rb + - app/helpers/spree/admin/orders_helper_decorator.rb + - app/helpers/spree/orders_helper.rb + - app/jobs/finalize_account_invoices.rb + - app/jobs/subscription_placement_job.rb + - app/jobs/update_account_invoices.rb + - app/jobs/update_billable_periods.rb + - app/mailers/producer_mailer.rb + - app/models/billable_period.rb + - app/models/calculator/flat_percent_per_item.rb + - app/models/column_preference.rb + - 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/proxy_order.rb + - app/models/spree/ability_decorator.rb + - app/models/spree/adjustment_decorator.rb + - app/models/spree/calculator/default_tax_decorator.rb + - app/models/spree/calculator/flexi_rate_decorator.rb + - app/models/spree/line_item_decorator.rb + - app/models/spree/order_decorator.rb + - app/models/spree/payment_decorator.rb + - app/models/spree/product_decorator.rb + - app/models/spree/product_set.rb + - app/models/spree/taxon_decorator.rb + - app/serializers/api/admin/enterprise_serializer.rb + - app/serializers/api/enterprise_serializer.rb + - app/serializers/api/product_serializer.rb + - app/serializers/api/variant_serializer.rb + - app/services/cart_service.rb + - app/services/create_order_cycle.rb + - app/services/order_syncer.rb + - app/services/subscription_validator.rb + - lib/discourse/single_sign_on.rb + - lib/open_food_network/bill_calculator.rb + - lib/open_food_network/bulk_coop_report.rb + - lib/open_food_network/customers_report.rb + - lib/open_food_network/enterprise_issue_validator.rb + - lib/open_food_network/group_buy_report.rb + - lib/open_food_network/lettuce_share_report.rb + - lib/open_food_network/option_value_namer.rb + - lib/open_food_network/order_and_distributor_report.rb + - lib/open_food_network/order_cycle_form_applicator.rb + - lib/open_food_network/order_cycle_management_report.rb + - lib/open_food_network/order_cycle_permissions.rb + - lib/open_food_network/orders_and_fulfillments_report.rb + - lib/open_food_network/packing_report.rb + - lib/open_food_network/payments_report.rb + - lib/open_food_network/permissions.rb + - lib/open_food_network/products_and_inventory_report.rb + - lib/open_food_network/reports/line_items.rb + - lib/open_food_network/sales_tax_report.rb + - lib/open_food_network/users_and_enterprises_report.rb + - lib/open_food_network/variant_and_line_item_naming.rb + - lib/open_food_network/xero_invoices_report.rb + - lib/spree/core/controller_helpers/respond_with_decorator.rb + - lib/spree/localized_number.rb + - lib/stripe/account_connector.rb + - lib/tasks/enterprises.rake + - spec/archive/features/consumer/checkout_spec.rb + - spec/controllers/spree/admin/orders_controller_spec.rb + - spec/features/admin/reports_spec.rb + - spec/features/consumer/shopping/checkout_spec.rb + - spec/features/consumer/shopping/variant_overrides_spec.rb + - spec/models/enterprise_spec.rb + - spec/models/product_importer_spec.rb + - spec/support/performance_helper.rb + +Metrics/CyclomaticComplexity: + Max: 6 + Exclude: + - app/controllers/admin/enterprise_fees_controller.rb + - app/controllers/admin/enterprises_controller.rb + - app/controllers/checkout_controller.rb + - app/controllers/spree/admin/orders_controller_decorator.rb + - app/controllers/spree/orders_controller_decorator.rb + - app/helpers/admin/business_model_configuration_helper.rb + - app/helpers/checkout_helper.rb + - app/helpers/i18n_helper.rb + - app/helpers/order_cycles_helper.rb + - app/helpers/spree/admin/orders_helper_decorator.rb + - app/jobs/update_account_invoices.rb + - app/jobs/update_billable_periods.rb + - app/models/enterprise.rb + - app/models/enterprise_relationship.rb + - app/models/product_import/entry_processor.rb + - app/models/product_import/entry_validator.rb + - app/models/spree/ability_decorator.rb + - app/models/spree/adjustment_decorator.rb + - app/models/spree/payment_decorator.rb + - app/models/spree/product_decorator.rb + - app/models/spree/product_set.rb + - app/models/variant_override_set.rb + - app/services/cart_service.rb + - lib/discourse/single_sign_on.rb + - lib/open_food_network/bill_calculator.rb + - lib/open_food_network/bulk_coop_report.rb + - lib/open_food_network/enterprise_issue_validator.rb + - lib/open_food_network/orders_and_fulfillments_report.rb + - lib/spree/core/controller_helpers/order_decorator.rb + - lib/spree/core/controller_helpers/respond_with_decorator.rb + - lib/spree/localized_number.rb + - spec/models/product_importer_spec.rb + +Metrics/PerceivedComplexity: + Max: 7 + Exclude: + - app/controllers/admin/enterprises_controller.rb + - app/controllers/checkout_controller.rb + - app/controllers/spree/admin/orders_controller_decorator.rb + - app/controllers/spree/orders_controller_decorator.rb + - app/helpers/admin/business_model_configuration_helper.rb + - app/helpers/checkout_helper.rb + - app/helpers/i18n_helper.rb + - app/helpers/order_cycles_helper.rb + - app/helpers/spree/admin/orders_helper_decorator.rb + - app/jobs/update_account_invoices.rb + - app/models/enterprise_relationship.rb + - app/models/product_import/entry_processor.rb + - app/models/product_import/entry_validator.rb + - app/models/spree/ability_decorator.rb + - app/models/spree/line_item_decorator.rb + - app/models/spree/order_decorator.rb + - app/models/spree/product_decorator.rb + - app/models/spree/product_set.rb + - lib/discourse/single_sign_on.rb + - lib/open_food_network/bulk_coop_report.rb + - lib/open_food_network/enterprise_issue_validator.rb + - lib/open_food_network/orders_and_fulfillments_report.rb + - lib/spree/core/controller_helpers/order_decorator.rb + - lib/spree/core/controller_helpers/respond_with_decorator.rb + - lib/spree/localized_number.rb + - spec/models/product_importer_spec.rb + +Metrics/MethodLength: + Max: 10 + Exclude: + - app/controllers/admin/customers_controller.rb + - app/controllers/admin/enterprise_fees_controller.rb + - app/controllers/admin/enterprises_controller.rb + - app/controllers/admin/manager_invitations_controller.rb + - app/controllers/admin/order_cycles_controller.rb + - app/controllers/admin/stripe_accounts_controller.rb + - app/controllers/admin/subscriptions_controller.rb + - app/controllers/base_controller.rb + - app/controllers/cart_controller.rb + - app/controllers/checkout_controller.rb + - app/controllers/shop_controller.rb + - app/controllers/spree/admin/line_items_controller_decorator.rb + - app/controllers/spree/admin/orders_controller_decorator.rb + - app/controllers/spree/admin/payment_methods_controller_decorator.rb + - app/controllers/spree/admin/payments_controller_decorator.rb + - app/controllers/spree/admin/products_controller_decorator.rb + - app/controllers/spree/admin/reports_controller_decorator.rb + - app/controllers/spree/admin/search_controller_decorator.rb + - app/controllers/spree/admin/shipping_methods_controller_decorator.rb + - app/controllers/spree/credit_cards_controller.rb + - app/controllers/spree/orders_controller_decorator.rb + - app/controllers/spree/user_sessions_controller_decorator.rb + - app/controllers/stripe/callbacks_controller.rb + - app/controllers/user_confirmations_controller.rb + - app/controllers/user_passwords_controller.rb + - app/controllers/user_registrations_controller.rb + - app/helpers/checkout_helper.rb + - app/helpers/order_cycles_helper.rb + - app/helpers/spree/admin/orders_helper_decorator.rb + - app/jobs/finalize_account_invoices.rb + - app/jobs/subscription_placement_job.rb + - app/jobs/update_account_invoices.rb + - app/jobs/update_billable_periods.rb + - app/mailers/producer_mailer.rb + - app/models/billable_period.rb + - app/models/column_preference.rb + - app/models/enterprise.rb + - app/models/enterprise_relationship.rb + - app/models/preference_sections/footer_and_external_links_section.rb + - app/models/preference_sections/main_links_section.rb + - app/models/product_import/entry_processor.rb + - app/models/product_import/entry_validator.rb + - app/models/product_import/product_importer.rb + - app/models/spree/ability_decorator.rb + - app/models/spree/adjustment_decorator.rb + - app/models/spree/calculator/default_tax_decorator.rb + - app/models/spree/calculator/flexi_rate_decorator.rb + - app/models/spree/line_item_decorator.rb + - app/models/spree/order_decorator.rb + - app/models/spree/payment_decorator.rb + - app/models/spree/payment_method_decorator.rb + - app/models/spree/product_set.rb + - app/models/spree/taxon_decorator.rb + - app/serializers/api/admin/order_cycle_serializer.rb + - app/services/cart_service.rb + - app/services/order_cycle_form.rb + - lib/discourse/single_sign_on.rb + - lib/open_food_network/bulk_coop_report.rb + - lib/open_food_network/cached_products_renderer.rb + - lib/open_food_network/column_preference_defaults.rb + - lib/open_food_network/customers_report.rb + - lib/open_food_network/enterprise_fee_calculator.rb + - lib/open_food_network/group_buy_report.rb + - lib/open_food_network/lettuce_share_report.rb + - lib/open_food_network/option_value_namer.rb + - lib/open_food_network/order_and_distributor_report.rb + - lib/open_food_network/order_cycle_form_applicator.rb + - lib/open_food_network/order_cycle_management_report.rb + - lib/open_food_network/order_cycle_permissions.rb + - lib/open_food_network/order_grouper.rb + - lib/open_food_network/orders_and_fulfillments_report.rb + - lib/open_food_network/packing_report.rb + - lib/open_food_network/payments_report.rb + - lib/open_food_network/permissions.rb + - lib/open_food_network/products_and_inventory_report.rb + - lib/open_food_network/products_renderer.rb + - lib/open_food_network/rack_request_blocker.rb + - lib/open_food_network/reports/bulk_coop_allocation_report.rb + - lib/open_food_network/reports/bulk_coop_supplier_report.rb + - lib/open_food_network/reports/line_items.rb + - lib/open_food_network/sales_tax_report.rb + - lib/open_food_network/users_and_enterprises_report.rb + - lib/open_food_network/xero_invoices_report.rb + - lib/spree/core/controller_helpers/respond_with_decorator.rb + - lib/spree/localized_number.rb + - lib/stripe/profile_storer.rb + - spec/archive/features/consumer/checkout_spec.rb + - spec/features/consumer/shopping/checkout_spec.rb + - spec/features/consumer/shopping/variant_overrides_spec.rb + - spec/models/product_importer_spec.rb + - spec/support/request/authentication_workflow.rb + +Metrics/ClassLength: + Max: 100 + Exclude: + - app/controllers/admin/enterprises_controller.rb + - app/controllers/admin/order_cycles_controller.rb + - app/controllers/admin/subscriptions_controller.rb + - app/controllers/checkout_controller.rb + - app/models/enterprise.rb + - app/models/order_cycle.rb + - app/models/product_import/entry_processor.rb + - app/models/product_import/entry_validator.rb + - app/models/product_import/product_importer.rb + - app/models/spree/ability_decorator.rb + - app/serializers/api/enterprise_serializer.rb + - app/services/cart_service.rb + - lib/open_food_network/bulk_coop_report.rb + - lib/open_food_network/enterprise_fee_calculator.rb + - lib/open_food_network/order_cycle_form_applicator.rb + - lib/open_food_network/order_cycle_management_report.rb + - lib/open_food_network/order_cycle_permissions.rb + - lib/open_food_network/orders_and_fulfillments_report.rb + - lib/open_food_network/packing_report.rb + - lib/open_food_network/payments_report.rb + - lib/open_food_network/permissions.rb + - lib/open_food_network/products_cache.rb + - lib/open_food_network/xero_invoices_report.rb + +Metrics/ModuleLength: + Max: 100 + Exclude: + - lib/open_food_network/column_preference_defaults.rb + - spec/controllers/admin/enterprises_controller_spec.rb + - spec/controllers/admin/order_cycles_controller_spec.rb + - spec/controllers/api/order_cycles_controller_spec.rb + - spec/controllers/api/orders_controller_spec.rb + - spec/controllers/spree/api/products_controller_spec.rb + - spec/lib/open_food_network/address_finder_spec.rb + - spec/lib/open_food_network/customers_report_spec.rb + - spec/lib/open_food_network/enterprise_fee_calculator_spec.rb + - spec/lib/open_food_network/option_value_namer_spec.rb + - spec/lib/open_food_network/order_cycle_form_applicator_spec.rb + - spec/lib/open_food_network/order_cycle_permissions_spec.rb + - spec/lib/open_food_network/order_grouper_spec.rb + - spec/lib/open_food_network/permissions_spec.rb + - spec/lib/open_food_network/products_and_inventory_report_spec.rb + - spec/lib/open_food_network/products_cache_spec.rb + - spec/lib/open_food_network/proxy_order_syncer_spec.rb + - spec/lib/open_food_network/scope_variant_to_hub_spec.rb + - spec/lib/open_food_network/subscription_payment_updater_spec.rb + - spec/lib/open_food_network/tag_rule_applicator_spec.rb + - spec/lib/open_food_network/users_and_enterprises_report_spec.rb + - spec/models/spree/ability_spec.rb + - spec/models/spree/adjustment_spec.rb + - spec/models/spree/line_item_spec.rb + - spec/models/spree/payment_spec.rb + - spec/models/spree/product_spec.rb + - spec/models/spree/variant_spec.rb + - spec/support/request/web_helper.rb + +Metrics/ParameterLists: + Max: 5 + Exclude: + - app/helpers/angular_form_builder.rb + - app/models/product_import/entry_processor.rb + - app/models/product_import/entry_validator.rb + - lib/open_food_network/xero_invoices_report.rb + - spec/features/admin/reports_spec.rb + +Metrics/BlockNesting: + Max: 3 + Exclude: + - app/controllers/checkout_controller.rb diff --git a/.rubocop_styleguide.yml b/.rubocop_styleguide.yml new file mode 100644 index 0000000000..ddf8e236b8 --- /dev/null +++ b/.rubocop_styleguide.yml @@ -0,0 +1,208 @@ +# Our Open Food Network style guide. +# +# These are the rules we agreed upon and we work towards. Code Climate uses +# these rules to rate our code and detect new violations. But when you run +# rubocop locally, the default configuration file `.rubocop.yml` loads +# our "todo lists" to ignore all current violations. +AllCops: + TargetRubyVersion: 2.1 + TargetRailsVersion: 3.2 + Exclude: + - 'db/**/*' + - 'config/**/*' + - 'script/**/*' + - 'vendor/**/*' + - 'node_modules/**/*' + # The parser gem fails to parse this file with out current Ruby version. + - 'spec/factories.rb' + # Excluding: inadequate Naming/FileName rule rejects GemFile name with camelcase + - 'engines/web/Gemfile' + +## OFN SETTINGS +# +# Cop settings that have been agreed upon by the OFN community + +Rails: + Enabled: true + +Style/Documentation: + Enabled: false + +Style/StringLiterals: + Enabled: false + +Style/HashSyntax: + Enabled: true + EnforcedStyle: ruby19_no_mixed_keys + +Style/Send: + Enabled: true + +Layout/MultilineMethodCallIndentation: + Enabled: true + EnforcedStyle: indented + +Metrics/LineLength: + Max: 100 + +## TEMPORARY/CONTESTED SETTINGS +# +# These are still to be decided upon, but recommended for inclusion by +# oeoeaio after scrutinising offenses the codebase + +# Don't think this is a big issue, mostly picking up RPSEC scope definitions +# with lamdas and RSpec '.to change{}' blocks +Lint/AmbiguousBlockAssociation: + Enabled: false + +# Heaps of offences (> 100) in specs, mostly in situations where two or more +# instances of a model are required, but only one is referenced. Difficult to +# fix without making the spec look messy or rewriting it. +# Should definitely fix at some point. +Lint/UselessAssignment: + Exclude: + - spec/**/* + +# AFAIK, there is no good alternative to dynamic matchers until we upgrade +# to Rails 4 and can use #find_by. If there is a better approach, let's do it. +Rails/DynamicFindBy: + Enabled: false + +# Same as above, #find_by is not available until Rails 4 +Rails/FindBy: + Enabled: false + +# Same as above, #update! is not available until Rails 4 +Rails/ActiveRecordAliases: + Enabled: false + +# This should be the programmer's discretion, perhaps we should review all of +# the uses of it an make specific exceptions though. +Rails/SkipsModelValidations: + Enabled: false + +## Relaxed.Ruby.Style SETTINGS +# +# These styles are a starting point for the conversation around conventions +# They should be removed or tweaked and moved above as decisions are made +# NOTE: Cops which did not fail at the time of writing were removed + +Layout/DotPosition: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#styledotposition + +Layout/SpaceBeforeBlockBraces: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#stylespacebeforeblockbraces + +Layout/SpaceInsideParens: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#stylespaceinsideparens + +Style/Alias: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#stylealias + +Style/BlockDelimiters: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#styleblockdelimiters + +Style/CommentAnnotation: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#stylecommentannotation + +Style/DoubleNegation: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#styledoublenegation + +Style/FormatString: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#styleformatstring + +Style/IfUnlessModifier: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#styleifunlessmodifier + +Style/Lambda: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#stylelambda + +Style/MultilineBlockChain: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#stylemultilineblockchain + +Style/NegatedIf: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#stylenegatedif + +Style/NegatedWhile: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#stylenegatedwhile + +Style/ParallelAssignment: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#styleparallelassignment + +Style/PercentLiteralDelimiters: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#stylepercentliteraldelimiters + +Style/Semicolon: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#stylesemicolon + +Style/SingleLineMethods: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#stylesinglelinemethods + +Style/TrailingCommaInArguments: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#styletrailingcommainarguments + +Style/TrailingCommaInArrayLiteral: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#styletrailingcommainliteral + +Style/TrailingCommaInHashLiteral: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#styletrailingcommainliteral + +Style/WordArray: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#stylewordarray + +Style/SymbolArray: + Enabled: false + StyleGuide: https://rubocop.readthedocs.io/en/latest/cops_style/#stylesymbolarray + +Lint/AmbiguousRegexpLiteral: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#lintambiguousregexpliteral + +Lint/AssignmentInCondition: + Enabled: false + StyleGuide: http://relaxed.ruby.style/#lintassignmentincondition + +Metrics/AbcSize: + Max: 15 + +Metrics/BlockNesting: + Max: 3 + +Metrics/ClassLength: + Max: 100 + +Metrics/ModuleLength: + Max: 100 + +Metrics/CyclomaticComplexity: + Max: 6 + +Metrics/MethodLength: + Max: 10 + +Metrics/ParameterLists: + Max: 5 + +Metrics/PerceivedComplexity: + Max: 7 From 98d79ec939476d9b6a32b4faae330bb4dc3083a3 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Mon, 5 Nov 2018 18:46:40 +1100 Subject: [PATCH 58/65] Updating translations for config/locales/fr_BE.yml --- config/locales/fr_BE.yml | 2742 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 2742 insertions(+) create mode 100644 config/locales/fr_BE.yml diff --git a/config/locales/fr_BE.yml b/config/locales/fr_BE.yml new file mode 100644 index 0000000000..01b5072b33 --- /dev/null +++ b/config/locales/fr_BE.yml @@ -0,0 +1,2742 @@ +fr_BE: + language_name: "Français" + activerecord: + attributes: + spree/order: + payment_state: Statut du paiement + shipment_state: Statut livraison + completed_at: 'Passée à ' + number: N° commande + email: Email acheteur + spree/payment: + amount: Montant + order_cycle: + orders_close_at: Date de fermeture + errors: + models: + spree/user: + attributes: + email: + taken: "Un compte existe déjà pour cet e-mail. Connectez-vous ou demandez un nouveau mot de passe." + spree/order: + no_card: Aucune carte de paiement autorisée disponible + order_cycle: + attributes: + orders_close_at: + after_orders_open_at: doit être postérieure à Date d'ouverture + activemodel: + errors: + models: + subscription_validator: + attributes: + subscription_line_items: + at_least_one_product: "^Veuillez ajouter au moins un produit" + not_available: "^%{name} n'est pas disponible pour le rythme d'abonnement sélectionné" + ends_at: + after_begins_at: "doit être après début le" + customer: + does_not_belong_to_shop: "n'appartient pas à %{shop}" + schedule: + not_coordinated_by_shop: "n'est pas coordonné par %{shop}" + payment_method: + not_available_to_shop: "n'est pas disponible pour %{shop}" + invalid_type: "doit être une méthode de paiement de type \"cash\" ou \"Stripe\"" + charges_not_allowed: "Le débit automatique sur carte de paiement n'a pas été autorisé par l'acheteur" + no_default_card: "Pas de carte de paiement par défaut pour cet acheteur" + shipping_method: + not_available_to_shop: "n'est pas disponible pour %{shop}" + devise: + confirmations: + send_instructions: "Un email a été envoyé avec des instructions pour confirmer votre adresse email. Vérifiez votre boite mail!" + failed_to_send: "Une erreur est survenue lors de l'envoi de l'email de confirmation." + resend_confirmation_email: "Renvoyer l'email de confirmation." + confirmed: "Merci d'avoir confirmé votre adresse email. Vous pouvez maintenant vous connecter." + not_confirmed: "Votre adresse email n'a pas pu être confirmée. Peut-être avez-vous déjà confirmé cette adresse email?" + user_registrations: + spree_user: + signed_up_but_unconfirmed: "Un message avec un lien de confirmation a été envoyé à l'adresse email indiquée. Veuillez cliquer sur ce lien pour activer votre compte." + unknown_error: "Une erreur s'est glissée lors de la création de votre compte. Vérifiez votre addresse email et recommencez." + failure: + invalid: | + Email / mot de passe incorrect. + Créez votre compte ou réinitialisez votre mot de passe. + unconfirmed: "Veuillez valider le lien envoyé par email pour pouvoir continuer." + already_registered: "Cet email est déjà associé à un utilisateur et a déjà été validé. Veuillez vous connecter pour continuer, ou utiliser un autre email." + user_passwords: + spree_user: + updated_not_active: "Votre mot de passe a bien été réinitialisé, mais votre email n'a pas encore été confirmé." + models: + order_cycle: + cloned_order_cycle_name: "Copie de %{order_cycle}" + enterprise_mailer: + confirmation_instructions: + subject: "Confirmez l'adresse email pour %{enterprise}" + welcome: + subject: "%{enterprise} est maintenant sur %{sitename}" + invite_manager: + subject: "%{enterprise} vous a invité comme manager" + order_mailer: + cancel_email: + dear_customer: "Cher Acheteur," + instructions: "Votre commande a été ANNULEE. Veuillez en prendre note et conserver pour preuve si besoin cette confirmation." + order_summary_canceled: "Résumé de la commande [ANNULEE]" + subject: "Annulation de Commande" + subtotal: "Sous-total : %{subtotal}" + total: "Total Commande : %{total}" + producer_mailer: + order_cycle: + subject: "Rapport de cycle de vente pour %{producer}" + shipment_mailer: + shipped_email: + dear_customer: "Cher Acheteur," + instructions: "Votre commande a été expédiée" + shipment_summary: "Résumé de l'envoi" + subject: "Notification d'expédition" + thanks: "Merci pour votre commande." + track_information: "Informations de suivi : %{tracking}" + track_link: "Lien de suivi : %{url}" + subscription_mailer: + placement_summary_email: + subject: Un résumé des dernières commandes récurrentes passées + greeting: "Bonjour %{name}," + intro: "Voici le résumé des commandes récurrentes qui viennent d'être passées pour la boutique %{shop}." + confirmation_summary_email: + subject: Un résumé des dernières commandes récurrentes confirmées + greeting: "Bonjour %{name}," + intro: "Voici le résumé des commandes récurrentes qui viennent d'être finalisées pour la boutique %{shop}." + summary_overview: + total: Un total de %{count} commandes récurrentes ont été paramétrées pour traitement automatique. + success_zero: Sur celles-ci, aucune n'a été traitée avec succès. + success_some: Sur celles-ci, %{count} ont été traitées avec succès. + success_all: Toutes ont été traitées avec succès. + issues: Les détails sur les problèmes rencontrés sont affichés ci-dessous. + summary_detail: + no_message_provided: Aucun message d'erreur à afficher + changes: + title: Stock insuffisant (%{count} commandes) + explainer: Ces commandes ont été traitées mais pour certains produits, le stock était insuffisant + empty: + title: Pas de stock (%{count} commandes) + explainer: Ces commandes n'ont pas pu être traitées car les produits souhaités étaient en rupture de stok + complete: + title: Déjà traité (%{count} commandes) + explainer: Ces commandes étaient déjà marquées comme passées, et n'ont donc pas été retouchées + processing: + title: Erreur rencontrée (%{count} commandes) + explainer: Le traitement automatique de ces commandes a échoué. L'erreur a été affichée à l'endroit pertinent. + failed_payment: + title: Paiement échoué (%{count} commandes) + explainer: Le traitement automatique des paiements pour ces commandes a échoué. L'erreur a été affichée à l'endroit pertinent. + other: + title: Autre échec (%{count} commandes) + explainer: Le traitement automatique de ces commandes a échoué pour une raison inconnue. Cela n'aurait pas dû arriver, veuillez nous contacter si vous constatez quelque chose d'anormal. + home: "OFF" + title: Open Food France + welcome_to: 'Bienvenue sur ' + site_meta_description: "Tout commence dans le sol. Avec ces paysans, agriculteurs, producteurs, engagés pour une agriculture durable et régénératrice, et désireux de partager leur histoire et leur passion avec fierté. Avec ces distributeurs souhaitant reconnecter les individus à leurs aliments et aux gens qui les produisent, soutenir les prises de conscience, dans une démarche de transparence, d'honnêteté, en assurant une juste rémunération des producteurs. Avec ces acheteurs qui croient que de meilleures décisions d'achats peuvent ..." + search_by_name: Recherche par nom ou département... + producers_join: Les producteurs et autres hubs basés en France sont invités à rejoindre Open Food France. + charges_sales_tax: Soumis à la TVA? + print_invoice: "Imprimer la facture" + print_ticket: "Imprimer ticket de caisse" + select_ticket_printer: "Choisir l'imprimante tickets" + send_invoice: "Envoyer la facture" + resend_confirmation: "Renvoyer la confirmation" + view_order: "Voir la commande" + edit_order: "Editer la commande" + ship_order: "Envoyer la commande" + cancel_order: "Annuler la commande" + confirm_send_invoice: "La facture de cette commande va être transmise au client. Etes-vous sûr de vouloir continuer ?" + confirm_resend_order_confirmation: "Etes-vous sûr de vouloir renvoyer le mail de confirmation de commande ?" + must_have_valid_business_number: "%{enterprise_name} doit avoir un SIRET valide avant que les factures puissent être envoyées." + invoice: "Facture" + percentage_of_sales: "%{percentage} des ventes" + capped_at_cap: "plafonné à %{cap}" + per_month: "par mois" + free: "gratuit" + free_trial: "Utilisation contre contribution libre" + plus_tax: "plus TVA" + min_bill_turnover_desc: "Quand le chiffre d'affaire dépasse %{mbt_amount}" + more: "Plus" + say_no: "Non" + say_yes: "Oui" + then: puis + ongoing: En cours + bill_address: Adresse de facturation + ship_address: Adresse de livraison + sort_order_cycles_on_shopfront_by: "Trier les cycles de vente par" + required_fields: Les champs obligatoires sont mentionnés par un asterisk + select_continue: Choisir et continuer + remove: Supprimer + or: ou + collapse_all: Tout masquer + expand_all: Tout afficher + loading: Chargement en cours... + show_more: Voir plus + show_all: Tout voir + show_all_with_more: "Voir tous (%{num} en plus)" + cancel: Annuler + edit: Modifier + clone: Dupliquer + distributors: Distributeurs + distribution: Distribution + bulk_order_management: Gestion des commandes par lot + enterprises: Entreprises + enterprise_groups: Groupes + reports: Rapports + variant_overrides: Catalogue boutique + spree_products: Produits + all: Tous + current: Actuel + available: Disponible + dashboard: Tableau de bord + undefined: indéfini + unused: inutilisé + admin_and_handling: Frais si par commande + profile: Profil + supplier_only: Uniquement Producteur + weight: Poids + volume: Volume + items: Pièces + summary: Résumé + detailed: Détaillé + updated: Mis à jour + 'yes': "Oui" + 'no': "Non" + y: 'O' + n: 'N' + powered_by: Propulsé par + blocked_cookies_alert: "Votre navigateur semble bloquer des cookies nécessaires à l'utilisation de ce site. Cliquez ci-dessous pour autoriser les cookies et rechargez la page." + allow_cookies: "Autoriser les cookies" + notes: Commentaires + error: Erreur + processing_payment: Paiement en cours... + show_only_unfulfilled_orders: Ne montrer que les commandes non finalisées + filter_results: Filtrer les résultats + quantity: Quantité + pick_up: Retrait + copy: Copier + actions: + create_and_add_another: "Créer et ajouter nouveau" + admin: + begins_at: Commence + begins_on: Commence le + customer: Acheteur + date: Date + email: Email + ends_at: Termine + ends_on: Termine le + name: Nom + on_hand: En stock + on_demand: A volonté + on_demand?: A volonté? + order_cycle: Cycle de vente + payment: Paiement + payment_method: Méthode de paiement + phone: N° tel + price: Prix + producer: Producteur + image: Image + product: Produit + quantity: Quantité + schedule: Rythme d'abonnement + shipping: Expédition + shipping_method: Option d'expédition + shop: Boutique + sku: Référence produit + status_state: Département + tags: Tags + variant: Variante + weight: Poids + volume: Volume + items: Pièce + select_all: Tout sélectionner + obsolete_master: Master obsolète + quick_search: Recherche rapide + clear_all: Vider + start_date: "Date de début" + end_date: "Date de fin" + form_invalid: "Le formulaire contient des champs manquants ou invalides" + clear_filters: Annuler les filtres + clear: Annuler + save: Sauvergarder + cancel: Annuler + back: Retour + show_more: Afficher plus + show_n_more: Montrer + %{num} + choose: "Choisir..." + please_select: Veuillez choisir... + columns: Colonnes + actions: Actions + viewing: "Vous regardez: %{current_view_name}" + description: Description + whats_this: Qu'est-ce que c'est? + tag_has_rules: "Règles existantes pour ce tag: %{num}" + has_one_rule: "a une règle" + has_n_rules: "a %{num} règles" + unsaved_confirm_leave: "Des modifications n'ont pas été sauvegardées et seront perdues si vous quittez la page. Souhaitez-vous quitter la page?" + unsaved_changes: "Des modifications n'ont pas été sauvegardées." + accounts_and_billing_settings: + method_settings: + default_accounts_payment_method: "Méthode de paiement par défault" + default_accounts_shipping_method: "Méthode d'envoi par défault" + edit: + accounts_and_billing: "Comptes & Factures" + accounts_administration_distributor: "Entreprise d'administration des comptes (facturation des hubs)" + admin_settings: "Paramètres" + update_invoice: "Mettre à jour les factures" + auto_update_invoices: "Mettre à jour automatiquement les factures chaque nuit à 01:00" + finalise_invoice: "Finaliser les factures" + auto_finalise_invoices: "Finaliser automatiquement les factures le 2 de chaque mois à 01:30" + manually_run_task: "Tâche exécutée manuellement" + update_user_invoice_explained: "Cliquez ici pour mettre à jour immédiatement les factures pour le mois en cours pour toutes les entreprises utilisant le système. Cette tache peut être définie pour s'effectuer automatiquement chaque nuit." + finalise_user_invoices: "Finaliser les factures utilisateurs" + finalise_user_invoice_explained: "Cliquez ici pour finaliser toutes les factures pour le mois calendaire précédent. Cette tâche peut-être définie pour être opérée automatiquement une fois par mois." + update_user_invoices: "Mettre à jour les factures utilisateurs" + errors: + accounts_distributor: 'doit être défini si vous souhaitez générer des factures pour les utilisateurs entreprises. ' + default_payment_method: 'doit être défini si vous souhaitez générer des factures pour les utilisateurs entreprises. ' + default_shipping_method: doit être défini si vous souhaitez générer des factures pour les utilisateurs entreprises. + shopfront_settings: + embedded_shopfront_settings: "Paramètres Boutiques Intégrées" + enable_embedded_shopfronts: "Autoriser l'intégration des boutiques" + embedded_shopfronts_whitelist: "Liste blanche des Domaines Externes" + number_localization: + number_localization_settings: "Gestion localisation des nombres" + enable_localized_number: "Utiliser le traitement international des séparateurs de milliers/centimes" + business_model_configuration: + edit: + business_model_configuration: "Modèle économique" + business_model_configuration_tip: "Configurer la fréquence à laquelle les boutiques seront facturées chaque mois pour l'utilisation d'Open Food Network" + bill_calculation_settings: "Paramètres du calcul des frais" + bill_calculation_settings_tip: "Définir le montant qui sera facturé aux hubs tous les mois pour leur utilisation d'Open Food France." + shop_trial_length: "Durée de la période de test (jours)" + shop_trial_length_tip: "La durée (en jours) de la période d'essai." + fixed_monthly_charge: "Charge mensuelle fixe" + fixed_monthly_charge_tip: "Le montant fixe mensuel facturé pour tous les hubs qui dépassent le seuil de chiffre d'affaire facturable (si défini)." + percentage_of_turnover: "Pourcentage du chiffre d'affaire" + percentage_of_turnover_tip: "Quand supérieur à zéro, ce taux (0.0 - 1.0) sera appliqué au chiffre d'affaire du hub pour déterminer la commission à facturer, qui sera ajoutée aux autres charges (à gauche) pour calculer le montant à facturer pour le mois." + monthly_cap_excl_tax: "plafond mensuel (sans TVA)" + monthly_cap_excl_tax_tip: "Quand supérieure à zéro, cette valeur sert de limite supérieure facturable pour un mois." + tax_rate: "TVA applicable" + tax_rate_tip: "TVA applicable sur le service facturé par Open Food France." + minimum_monthly_billable_turnover: "Chiffre d'affaire minimum facturable (mensuel)" + minimum_monthly_billable_turnover_tip: "Chiffre d'affaire mensuel au delà duquel le hub devra payer le service Open Food France. Les hubs n'atteignant pas ce chiffre d'affaire mensuel ne seront pas facturés, ni sur le montant fixe ni sur la commission variable." + example_bill_calculator: "Exemple de calcul de facture" + example_bill_calculator_legend: "Changer le chiffre d'affaire pour voir l'impact des paramètres définis à gauche." + example_monthly_turnover: "Exemple de CA mensuel" + example_monthly_turnover_tip: "Exemple de chiffre d'affaire mensuel qui sert de base de calcul pour voir quel est le montant qui sera facturé au hub concerné." + cap_reached?: "Seuil atteint?" + cap_reached?_tip: "On voit ici si le seuil (défini à gauche) a été atteint, en fonction du chiffre d'affaire et du paramétrage du seuil." + included_tax: "Inclut TVA" + included_tax_tip: "TVA inclue dans l'exemple en cours, dépend du chiffre d'affaire et des paramétrages à gauche." + total_monthly_bill_incl_tax: "Facture mensuelle totale (taxes incluses)" + total_monthly_bill_incl_tax_tip: "Exemple du total TTC facturé pour le mois, selon paramétrages et chiffre d'affaire du mois." + cache_settings: + show: + title: Mise en cache + distributor: Hub (distributeur) + order_cycle: Cycle de vente + status: Statut + diff: Diff + error: Erreur + invoice_settings: + edit: + title: Paramètres de facturation + invoice_style2?: Utiliser le modèle de facture alternatif qui détaille le montant de TVA agrégé par taux et l'information du taux de TVA par produit (pas adapté pour les instances affichant les prix HT) + enable_receipt_printing?: Afficher les options d'impression de tickets de caisse dans le menu déroulant des commandes? + stripe_connect_settings: + edit: + title: "Stripe Connect" + settings: "Paramètres" + stripe_connect_enabled: Permettre aux boutiques d'accepter les paiements via Stripe Connect ? + no_api_key_msg: Aucun compte Stripe n'existe pour cette entreprise. + configuration_explanation_html: Pour des instructions précises sur comment configurer Stripe Connect, veuillez consulter ce guide. + status: Statut + ok: Ok + instance_secret_key: Clé Secrète de l'Instance + account_id: Identifiant Compte + business_name: Nom de l'entreprise + charges_enabled: Frais activés + charges_enabled_warning: "Attention : les Frais ne sont pas activés pour votre compte" + auth_fail_error: La clé de l'API est invalide + empty_api_key_error_html: Aucune clé d'API Stripe n'a été fournie. Pour mettre en place votre clé d'API, veuillez suivre ces instructions + matomo_settings: + edit: + title: "Configuration Matomo" + matomo_url: "URL de l'instance sur Matomo" + matomo_site_id: "ID de l'instance sur Matomo" + info_html: "Matomo est un outil d'analyse de trafic web (ordinateur et téléphone). Vous pouvez installer vous-même Matomo ou utiliser une version hébergée. Voir matomo.org pour plus d'information." + config_instructions_html: "Pour utiliser Matomo, vous devez configurer l'intégration avec Open Food France. L'URL de l'instance sur Matomo correspond à l'url du site internet visé par le suivi de la navigation utilisateur. Si le champ est vide, Matomo n'effectuera aucune analyse sur ce site. L'ID de l'instance sur Matomo n'est pas obligatoire, mais nécessaire si vous souhaitez analyser plusieurs sites web sur une seule instance Matomo. Cet ID peut être trouvé sur l'espace administrateur Matomo." + customers: + index: + add_customer: "Ajouter un acheteur" + new_customer: "Nouvel acheteur" + customer_placeholder: "acheteur@exemple.org" + valid_email_error: Veuillez entrer un email valide + add_a_new_customer_for: Ajouter un nouvel acheteur pour %{shop_name} + code: Code + duplicate_code: "Ce code est déjà utilisé." + bill_address: "Adresse de facturation" + ship_address: "Adresse de livraison" + update_address_success: 'Adresse mise à jour avec succès.' + update_address_error: 'Oups! Veuillez remplir tous les champs obligatoires!' + edit_bill_address: 'Modifier l''adresse de facturation' + edit_ship_address: 'Modifier l''adresse de livraison' + required_fileds: 'Les champs obligatoires sont indiqués avec un astérisque *' + select_country: 'Choisir le pays' + select_state: 'Choisir le département' + edit: 'Modifier' + update_address: 'Mettre à jour l''adresse' + confirm_delete: 'Confirmer suppression?' + search_by_email: "Recherche par email/code..." + guest_label: 'Commande en mode invité' + destroy: + has_associated_orders: 'Suppression impossible: des commandes sont associées à cet acheteur pour cette boutique' + contents: + edit: + title: Contenu + header: Titre + home_page: Page d'accueil + producer_signup_page: Page d'inscription Producteur + hub_signup_page: Page d'inscription Hub + group_signup_page: Page d'inscription Groupe + main_links: Liens du menu principal + footer_and_external_links: Pied de page et Liens Externes + your_content: Votre contenu + user_guide: Guide utilisateur + enterprise_fees: + index: + title: Marges et Commissions + enterprise: Entreprise + fee_type: Type de marge + name: Nom + tax_category: TVA applicable + calculator: Calculateur + calculator_values: Montants pour calculs + enterprise_groups: + index: + new_button: Nouveau groupe d'entreprises + enterprise_roles: + form: + manages: gère + enterprise_role: + manages: gère + products: + unit_name_placeholder: 'ex: botte' + index: + unit: Unité + display_as: Unité affichéé + category: Catégorie + tax_category: TVA applicable + inherits_properties?: Hériter des propriétés? + available_on: Disponible via + av_on: "Disp. via" + import_date: Importé + upload_an_image: Importer une image + product_search_keywords: Mots-clés de recherche produits + product_search_tip: Saisissez des mots qui peuvent simplifier la recherche de vo produits dans les boutiques. Laissez un espace entre chaque mot-clé. + SEO_keywords: Mot-clés de référencement web + seo_tip: Saisissez des mots qui peuvent simplifier la recherche de vos produits sur le web. Laissez un espace entre chaque mot-clé. + Search: Rechercher + properties: + property_name: Nom du label + inherited_property: Label producteur appliqué par défaut + variants: + to_order_tip: "Les articles fabriqués sur commande n'ont pas un niveau de stock défini, comme des pains faits à la main." + product_distributions: "Lieux de distribution" + group_buy_options: "Options d'achat par lot" + back_to_products_list: "Retour à la liste produits" + product_import: + title: Import liste produits + file_not_found: Fichier non trouvé ou impossible à ouvrir + no_data: Aucune donnée trouvée dans le tableau + confirm_reset: "Cette action remettra tous les niveaux de stock à zero pour cette\nentreprise pour les produits non présents dans ce fichier." + model: + no_file: "erreur : aucun document importé" + could_not_process: "impossible de traiter le fichier : type de fichier invalide" + incorrect_value: Valeur non conforme + conditional_blank: ne peut pas être vide si unit_type est vide + no_product: n'a pu être associé à aucun produit existant dans la base de données + not_found: non trouvé dans le base de données + not_updatable: ne peut pas être mis à jour pour des produits existants via la fonctionnalité d'import de fichier produits + blank: Champ obligatoire + products_no_permission: vous n'avez pas l'autorisation de gérer les produits de cette entreprise + inventory_no_permission: vous n'avez pas l'autorisation d'ajouter les produits de ce producteur à votre catalogue boutique + none_saved: n'a pu sauvegarder aucun produit :-( + line_number: "Ligne %{number} :" + index: + select_file: Sélectionner le fichier (tableur sous format csv) à importer + spreadsheet: Tableur csv + choose_import_type: Choisir le type d'import + import_into: Type d'import + product_list: Catalogues produits des producteurs + inventories: Catalogues boutiques des hubs distributeurs + import: Importer + upload: Télécharger + csv_templates: Modèles de fichiers csv + product_list_template: Télécharger le modèle pour import dans catalogue producteur + inventory_template: Télécharger le modèle pour import dans catalogue boutique + category_values: Valeurs disponibles pour les catégories + product_categories: Catégorie Produit + tax_categories: TVA applicable + shipping_categories: Condition de transport + import: + review: Vérifier + import: Importer + save: Sauvergarder + results: Résultats + save_imported: Sauvegarder les produits importés + no_valid_entries: Aucune entrée valide trouvée + none_to_save: Il n'y a pas aucune information pouvant être sauvegardée + some_invalid_entries: Les fichiers importés contiennent des valeurs invalides + fix_before_import: Veuillez corriger ces erreurs et recommencer l'import + save_valid?: Sauvegarder les informations valides et détruire les autres? + no_errors: Aucune erreur détectée! + save_all_imported?: Sauvegarder tous les produits importés? + options_and_defaults: Options d'import + no_permission: vous n'avez pas l'autorisation de gérer cette entreprise + not_found: l'entreprise n'a pas été trouvée dans la base de données + no_name: Pas de nom + reset_absent?: Mettre à zéro le produits absents du fichier + reset_absent_tip: Remettre le sock à zero pour les produits non présents dans le fichier. + overwrite_all: Modifier pour tous + overwrite_empty: Modifier si vide + default_stock: Indiquer niveau de stock + default_tax_cat: Indiquer taux de taxe + default_shipping_cat: Indiquer condition de transport + default_available_date: Indiquer date de disponibilité + validation_overview: Aperçu des entrées produits créées/modifiées + entries_found: Informations trouvées dans le fichier importé + entries_with_errors: Certaines lignes contiennent des erreurs et les produits correspondant ne seront pas importés + products_to_create: Ces produits vont être crées + products_to_update: Ces produits vont être mis à jour + inventory_to_create: Ces produits vont être ajoutés au catalogue boutique + inventory_to_update: Les informations de ces produits dans le catalogue boutique vont être mises à jour + products_to_reset: Le stock des produits existants va être remis à zero + inventory_to_reset: Dans le catalogue boutique, le stock des produits existants va être remis à zéro + line: Ligne + item_line: Ligne produit concernée + import_review: + not_updatable_tip: "Les champs suivants ne peuvent pas être mis à jour pour des produits existants via la fonctionnalité d'import de fichier produits :" + fields_ignored: Ces champs seront ignorés à l'enregistrement des produits importés. + entries_table: + not_updatable: Ce champs ne peut pas être mis à jour pour des produits existants via la fonctionnalité d'import de fichier produits + save_results: + final_results: Importer les informations produits confirmées + products_created: produits crées + products_updated: produits mis à jour + inventory_created: produits ajoutés dans le catalogue boutique + inventory_updated: produits mis à jour dans le catalogue boutique + products_reset: produits ont vu leur niveau de stock remis à zéro + inventory_reset: produits ont vu leur niveau de stock remis à zéro dans le catalogue boutique + all_saved: "Tous les produits ont été sauvegardés avec succès" + some_saved: "produits sauvegardés avec succès" + save_errors: Sauvegarder les erreurs + import_again: Importer un autre fichier + view_products: Aller à la page Produits + view_inventory: Aller à la page Catalogues Boutiques + variant_overrides: + loading_flash: + loading_inventory: Catalogue boutique en cours de chargement... + index: + title: Catalogue boutique + description: Utilisez cette page pour gérer le catalogue de votre entreprise. Les détails produits saisis ici remplaceront ceux de la page "Produit" pour votre entreprise uniquement. + enable_reset?: Autoriser réinitialisation du stock (retour configurations par défaut)? + inherit?: Hériter? + add: Ajouter + hide: Masquer + import_date: Importé + select_a_shop: Choisir une boutique + review_now: Vérifier maintenant + new_products_alert_message: Il y a %{new_product_count} nouveaux produits disponibles pouvant être ajoutés à votre catalogue. + currently_empty: Votre catalogue est actuellement vide + no_matching_products: Pas de produits correspondants dans votre catalogue + no_hidden_products: Aucun produit masqué dans ce catalogue + no_matching_hidden_products: Aucune produit masqué ne répond à la recherche + no_new_products: Pas de nouveaux produits à ajouter à ce catalogue + no_matching_new_products: Pas de nouveaux produits répondant à la recherche + inventory_powertip: Ceci est votre catalogue produits. Pour ajouter des produits à votre catalogue, sélectionnez "Nouveaux Produits" dans le menu déroulant. + hidden_powertip: Ces produits ont été masqués de votre catalogue, vous ne pourrez pas les proposer dans votre boutique. Vous pouvez cliquer sur "Ajouter" pour ajouter un produit à votre catalogue. + new_powertip: Ces produits peuvent être ajoutés à votre catalogue. Cliquez sur "Ajouter" pour ajouter un produit à votre catalogue, ou 'Masquer" pour ne plus l'afficher. Vous pourrez changer d'avis plus tard! + controls: + back_to_my_inventory: Retour à mon catalogue boutique + orders: + invoice_email_sent: 'L''email de facturation a bien été envoyé' + order_email_resent: 'L''email de commande a de nouveau été envoyé' + bulk_management: + tip: "Utilisez cette page pour changer les quantités d'un produit sur plusieurs commandes. Les produits peuvent aussi être supprimés de toutes les commandes, si nécessaire." + shared: "Ressource partagée?" + order_no: "N° commande" + order_date: "Date commande" + max: "Max" + product_unit: "Produit: Unité" + weight_volume: "Poids/Volume" + ask: "Demander?" + page_title: "Gestion des commandes" + actions_delete: "Supprimer la sélection" + loading: "Commandes en cours de chargement" + no_results: "Aucune commande trouvée." + group_buy_unit_size: "Quantité totale du lot" + total_qtt_ordered: "Quantité totale commandée" + max_qtt_ordered: "Quantité max commandée" + current_fulfilled_units: "Nombre d'unités commandées" + max_fulfilled_units: "Nombre max d'unités commandées" + order_error: "Des erreurs doivent être résolues avant de pouvoir mettre à jour les commandes.\nLes champs entourés en rouge contiennent des erreurs." + variants_without_unit_value: "ATTENTION: certaines variantes n'ont pas de nombre d'unités" + select_variant: "Choisir une variante" + enterprise: + select_outgoing_oc_products_from: Sélectionner les produits sortants pour le cycle de vente parmi + enterprises: + index: + title: Entreprises + new_enterprise: Nouvelle entreprise + producer?: "Producteur?" + package: Pack + status: Statut + manage: Gérer + form: + about_us: + desc_short: Description (en bref) + desc_short_placeholder: Parlez de votre entreprise en une ou deux phrases + desc_long: A propos + desc_long_placeholder: Parlez de vous à vos acheteurs ! Ces informations seront visibles sur votre profil public. + business_details: + abn: SIRET + abn_placeholder: 'ex: 404 833 048 00022' + acn: n° TVA intracommunautaire + acn_placeholder: 'ex: 404 833 048' + display_invoice_logo: Afficher le logo sur la facture + invoice_text: Ajouter une mention spécifique en bas des factures + contact: + name: Nom + name_placeholder: 'ex: Bernard Michelet' + email_address: Adresse email publique + email_address_placeholder: 'ex: labelleferme@maferme.fr' + email_address_tip: "Cette adresse email sera affichée sur votre profil public" + phone: n° téléphone + phone_placeholder: 'ex: 06 13 24 35 46' + website: Site internet + website_placeholder: 'ex: www.maferme.fr' + enterprise_fees: + name: Nom + fee_type: Type de marge + manage_fees: Gérer les marges et commissions + no_fees_yet: Vous n'avez pas encore défini de commissions + create_button: Créer une commission + images: + logo: Logo + promo_image_placeholder: 'Cette image est affichée dans "A propos"' + promo_image_note1: 'ATTENTION:' + promo_image_note2: Votre bannière doit mesurer 1200 x 260, toute image non conforme sera rognée. + promo_image_note3: La bannière est affichée en haut de la page de votre entreprise et dans sa version condensée (pop-up). + inventory_settings: + text1: Vous pouvez choisir de gérer vos stocks et prix via votre + inventory: catalogue boutique + text2: > + Si vous utilisez l'outil "catalogue boutique", vous pouvez choisir si + les nouveaux produits ajoutés par vos fournisseurs doivent être référencés + dans votre catalogue boutique avant qu'ils puissent mis en vente dans + votre boutique. Si vous n'utilisez pas cet outil, choisissez l'option + indiquant "recommandé" ci-dessous: + preferred_product_selection_from_inventory_only_yes: Les nouveaux produits des producteurs peuvent être ajoutés à ma boutique en ligne (recommandé) + preferred_product_selection_from_inventory_only_no: Les nouveaux produits des producteurs doivent être ajoutés à mon catalogue boutique avant de pouvoir être ajoutés à ma boutique en ligne + payment_methods: + name: Nom + applies: Active? + manage: Gérer les méthodes de paiement + not_method_yet: Vous n'avez pas encore de méthode de paiement. + create_button: Créer une nouvelle méthode de paiement + create_one_button: En créer une maintenant + primary_details: + name: Nom + name_placeholder: 'ex: La ferme bio de Bernard' + groups: Groupes + groups_tip: Sélectionnez les groupes desquels vous êtes membres. Cela améliorera votre visibilité et permettra aux acheteurs de vous trouver plus facilement. + groups_placeholder: Commencer à taper pour voir les groupes disponibles... + primary_producer: Producteur? + primary_producer_tip: Cochez "producteur" si vous vendez des aliments que vous produisez vous-même (bruts ou transformés) + producer: Producteur + any: Tous + none: Aucun + own: Les miens + sells: Produits vendus + sells_tip: "Aucun - l'entreprise ne vend pas en direct aux acheteurs.
Les miens - l'entreprise vend ses propres produits aux acheteurs.
Tous - l'entreprise vend ses propres produits et/ou les produits d'autres entreprises.
" + visible_in_search: Apparaît sur la plateforme? + visible_in_search_tip: Indiquez si vous souhaitez ou ne souhaitez pas que votre entreprise apparaisse sur la carte et dans la liste des boutiques. + visible: Visible + not_visible: Invisible + permalink: Nom pour URL (sans espace) + permalink_tip: "Ce nom permanent est utilisé pour créer l'url de votre boutique: %{link}ma-boutique/shop" + link_to_front: Lien URL de la boutique + link_to_front_tip: C'est le lien qui permet d'accéder en direct à votre boutique sur Open Food France. + shipping_methods: + name: Nom + applies: Active? + manage: Gérer les méthodes de livraison + create_button: Créer nouvelle méthode de livraison + create_one_button: En créer une maintenant + no_method_yet: Vous n'avez pas encore paramétré de méthode de livraison. + shop_preferences: + shopfront_requires_login: "Boutique visible par tous?" + shopfront_requires_login_tip: "Choisissez si les acheteurs doivent être connectés pour voir la boutique ou si la boutique est visible par tout le monde." + shopfront_requires_login_false: "Visible par tous" + shopfront_requires_login_true: "Visible uniquement pour les acheteurs logués" + recommend_require_login: "Nous recommandons de demander aux utilisateurs de se connecter si vous souhaitez leur permettre de modifier leur commande." + allow_guest_orders: "Commandes des invités" + allow_guest_orders_tip: "Autoriser la commande en tant qu'invité ou demander que l'acheteur soit logué." + allow_guest_orders_false: "Demander que l'acheteur se logue pour pouvoir commander" + allow_guest_orders_true: "Autoriser les commandes en mode invité" + allow_order_changes: "Modifier la commande" + allow_order_changes_tip: "Permettre aux acheteurs de modifier leur commande tant que le cycle de vente est ouvert." + allow_order_changes_false: "Les commandes validées ne peuvent plus être modifiées / annulées" + allow_order_changes_true: "Les acheteurs peuvent modifier / valider leurs commandes tant que le cycle de vente est ouvert" + enable_subscriptions: "Abonnements" + enable_subscriptions_tip: "Activer la fonction abonnements?" + enable_subscriptions_false: "Désactivé" + enable_subscriptions_true: "Activé" + shopfront_message: Message d'accueil boutique ouverte + shopfront_message_placeholder: > + Vous pouvez ici expliquer à vos acheteurs comment votre boutique fonctionne. + Ce texte s'affiche dans votre boutique, au-dessus de la liste de produits. + shopfront_closed_message: Message d'accueil boutique fermée + shopfront_closed_message_placeholder: > + Vous pouvez ici expliquer à vos acheteurs pourquoi votre boutique est + fermée et/ou quand elle ouvrira. Ce texte s'affiche uniquement quand + il n'y a pas de cycle de vente en cours (donc quand votre boutique est + fermée). + shopfront_category_ordering: Ordre d'affichage des catégories + open_date: Date d'ouverture + close_date: Date de fermeture + social: + twitter_placeholder: ex. @OpenFoodNet_fr + stripe_connect: + connect_with_stripe: "Connecter avec Stripe" + stripe_connect_intro: "Pour accepter des paiements utilisant la carte bancaire, vous devez connecter votre compte Stripe à Open Food France. Cliquez sur le bouton à droite pour commencer." + stripe_account_connected: "Compte Stripe connecté." + disconnect: "Déconnecter le compte" + confirm_modal: + title: Connecter avec Stripe + part1: Stripe est un système de paiement qui permet aux boutiques sur Open Food France d'accepter des paiements par carte bancaire de leurs acheteurs. + part2: Pour utiliser cette fonctionnalité, vous devez connecter votre compte Stripe à Open Food France. En cliquant sur "J'accepte" ci-dessous, vous serez redirigé vers le site internet de Stripe, où vous pourrez connecter votre compte existant ou en créer un si vous n'en avez pas encore. + part3: Cela permettra à Open Food France d'accepter en votre nom les paiements par carte de crédit en provenance de vos acheteurs. Veuillez noter que c'est à vous de gérer votre compte Stripe, de payer les frais dus à Stripe et de gérer les éventuels remboursements et le service après vente. + i_agree: J'accepte + cancel: Annuler + tag_rules: + default_rules: + by_default: Règles à appliquer "par défaut" + no_rules_yet: Aucune règle par défaut + add_new_button: '+ Ajouter une règle par défaut' + no_tags_yet: Aucun tag défini par cette entreprise pour le moment + no_rules_yet: Aucune règle ne concerne ce tag pour le moment + for_customers_tagged: 'Pour les acheteurs avec le tag:' + add_new_rule: '+ Ajouter une nouvelle règle' + add_new_tag: '+ Ajouter un nouveau tag' + users: + email_confirmation_notice_html: "L'email de confirmation n'a pas encore été validé. Il a été envoyé à %{email}." + resend: Renvoyer + owner: 'Manager principal' + contact: "Contact" + contact_tip: "Le manager qui recevra les emails de confirmation de commande et autres notifications de l'entreprise. Il doit avoir confirmé son adresse email pour pouvoir être sélectionné." + owner_tip: Manager principal de cette entreprise. + notifications: Notifications + notifications_tip: Une notification de commande sera envoyée à cette adresse email pour chaque commande passée dans votre boutique. + notifications_placeholder: 'ex: bernard@maferme.fr' + notifications_note: 'A noter: si vous saisissez une nouvelle adresse, un email de confirmation sera envoyé à cette adresse avec un lien de validation à cliquer.' + managers: Managers + managers_tip: Sélectionner ici les utilisateurs ayant la permission de gérer cette entreprise. Ils doivent avoir un compte sur Open Food France pour être sélectionnés. + invite_manager: "Inviter un manager" + invite_manager_tip: "Inviter un nouvel utilisateur à créer son compte et le nommer comme manager de cette entreprise." + add_unregistered_user: "Ajouter un nouvel utilisateur" + email_confirmed: "Email confirmé" + email_not_confirmed: "Email non confirmé" + actions: + edit_profile: Paramètres + properties: Labels / propriétés + payment_methods: Méthodes de paiement + payment_methods_tip: Cette entreprise n'a pas paramétré de méthode de paiement + shipping_methods: Méthodes de livraison + shipping_methods_tip: Cette entreprise a paramétré des méthodes de paiement + enterprise_fees: Marges et commissions + enterprise_fees_tip: Cette entreprise n'a pas paramétré de marges et commissions + admin_index: + name: Nom + role: Role + sells: Produits vendus + visible: Visible? + owner: Manager principal + producer: Producteur + change_type_form: + producer_profile: Profil producteur + connect_ofn: Gagnez en visibilité via OFFrance + always_free: GRATUIT + producer_description_text: Saisissez votre catalogue produits sur Open Food France, ce qui permettra aux hubs-distributeurs utilisant la plateforme de les proposer dans leurs boutiques (sur votre autorisation). + producer_shop: Boutique Producteur + sell_your_produce: Vendez vos propres produits + producer_shop_description_text: Vendez vos produits en direct aux mangeurs/restaurateurs/etc. via votre propre Boutique Producteur sur Open Food France. + producer_shop_description_text2: Une Boutique Producteur vous permet de vendre uniquement vos propres produits. Si vous voulez vendre d'autres produits, sélectionnez "Hub Producteur" + producer_hub: Hub Producteur + producer_hub_text: Vendez vos produits et ceux d'autres fournisseurs + producer_hub_description_text: Vous vendez non seulement vos produits, mais aussi des produits d'autres producteurs de votre région, artisans, ou distributeurs afin de proposer une offre complète dans votre boutique. Vous soutenez ainsi le développement de votre système alimentaire territorial ! + profile: Profil uniquement + get_listing: Référencez votre hub/point de vente + profile_description_text: Les visiteurs peuvent vous trouver sur Open Food France et vous contacter. Votre entreprise sera visible sur la carte. + hub_shop: Boutique Hub + hub_shop_text: Vendez des produits de multiples fournisseurs différents + hub_shop_description_text: Vous proposez des produits de différents producteurs de votre région, artisans, ou distributeurs afin de proposer une offre complète dans votre boutique. Vous soutenez ainsi le développement de votre système alimentaire territorial ! + choose_option: Veuilliez choisir l'une des options ci-dessus. + change_now: Changer + enterprise_user_index: + loading_enterprises: CHARGEMENT DES ENTREPRISES + no_enterprises_found: Aucune entreprise trouvée. + search_placeholder: Recherche par nom + manage: Gérer + manage_link: Paramètres + producer?: "Producteur ?" + package: "Pack" + status: "Statut" + new_form: + owner: Manager principal + owner_tip: Le manager principal est l'individu qui porte la responsabilité principale de l'entreprise dans le contexte de l'utilisation d'Open Food France. + i_am_producer: Je suis un producteur + contact_name: Nom du contact principal + edit: + editing: 'Configuration:' + back_link: Revenir à la liste des entreprises + new: + title: Nouvelle entreprise + back_link: Revenir à la liste des entreprises + remove_logo: + remove: "Supprimer l'image" + removed_successfully: "Logo supprimé avec succès" + immediate_removal_warning: "Le logo sera supprimé juste après votre confirmation." + remove_promo_image: + remove: "Supprimer l'image" + removed_successfully: "Bannière supprimée avec succès" + immediate_removal_warning: "La bannière sera supprimée juste après votre confirmation." + welcome: + welcome_title: Bienvenue sur Open Food France ! + welcome_text: 'Vous avez créé avec succès ' + next_step: Etape suivante + choose_starting_point: 'Choisir votre type d''entreprise:' + invite_manager: + user_already_exists: "Le compte existe déjà" + error: "Un problème est survenu" + order_cycles: + edit: + advanced_settings: Paramétrages avancés + update_and_close: Mettre à jour et fermer + choose_products_from: 'Choisir produits depuis :' + exchange_form: + pickup_time_tip: Quand des commandes liées à ce cycle de vente seront prêtes à être soumises à l'acheteur + pickup_instructions_placeholder: "Modalités de retrait/livraison" + pickup_instructions_tip: Ces instructions sont affichées aux acheteurs après passage d'une commande + pickup_time_placeholder: "Prêt pour (ex : jour + créneau horaire)" + receival_instructions_placeholder: "Modalités de livraison" + add_fee: 'Ajouter une commission' + selected: 'sélectionné' + add_exchange_form: + add_supplier: 'Ajouter un fournisseur' + add_distributor: 'Ajouter un distributeur' + advanced_settings: + title: Paramétrages avancés + choose_product_tip: Vous pouvez choisir de limiter le choix des produits pouvant être mis en vente dans votre boutique à ceux figurant dans le catalogue boutique de %{inventory}. + preferred_product_selection_from_coordinator_inventory_only_here: Uniquement les produits du catalogue boutique + preferred_product_selection_from_coordinator_inventory_only_all: Tous les produits disponibles dans les catalogues producteurs + save_reload: Sauvegarder et rafraichir la page + coordinator_fees: + add: Ajouter commission coordinateur + filters: + search_by_order_cycle_name: "Recherche par nom de Cycle de Vente..." + involving: "Concernant" + any_enterprise: "Toutes les entreprises" + any_schedule: "Tous" + form: + incoming: Produits entrants (pouvant être mis en vente par les hubs) + supplier: Fournisseur + receival_details: Détails livraison produits + fees: Commission + outgoing: Produits sortants (mis en vente par/via un ou plusieurs hubs) + distributor: Hub (distributeur) + products: Produits + tags: Tags + add_a_tag: Ajouter un tag + delivery_details: Précisions retrait / livraison + debug_info: Informations de débogage + index: + schedule: Rythme d'abonnement + schedules: Rythmes d'abonnement + adding_a_new_schedule: Ajouter un nouveau rythme d'abonnement + updating_a_schedule: Mettre à jour un rythme d'abonnement + new_schedule: Nouveau rythme d'abonnement + create_schedule: Créer rythme d'abonnement + update_schedule: Mettre à jour rythme d'abonnement + delete_schedule: Supprimer rythme d'abonnement + created_schedule: Créer rythme d'abonnement + updated_schedule: Mettre à jour rythme d'abonnement + deleted_schedule: Supprimer rythme d'abonnement + schedule_name_placeholder: Nom du rythme d'abonnement + name_required_error: Veuillez saisir un nom pour ce rythme d'abonnement + no_order_cycles_error: Veuillez saisir au moins un cycle de vente (glisser déposer) + name_and_timing_form: + name: Nom + orders_open: Commandes à partir de + coordinator: Coordinateur + orders_close: Commandes jusqu'au + row: + suppliers: fournisseurs + distributors: hubs-distributeurs + variants: variantes + simple_form: + ready_for: 'Prêt pour ' + ready_for_placeholder: Date / Heure + customer_instructions: Précisions pour l'acheteur + customer_instructions_placeholder: Commentaires pour le retrait / la livraison + products: Produits + fees: Commissions + destroy_errors: + orders_present: 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. + schedule_present: Ce cycle de vente est lié à un rythme d'abonnement et ne peut pas être supprimé. Veuillez d'abord supprimer ce lien ou supprimer le rythme d'abonnement. + bulk_update: + no_data: Une erreur s'est produite. Aucune donnée trouvée. + date_warning: + msg: Ce cycle de vente est lié à %{n}abonnements ouverts. Changer cette date maintenant n'impactera pas les commandes déjà réalisée, mais nous vous déconseillons cette action néanmoins. Etes-vous sûrs de vouloir poursuivre ? + cancel: Annuler + proceed: Continuer + producer_properties: + index: + title: Propriétés / labels du producteur + proxy_orders: + cancel: + could_not_cancel_the_order: La commande n'a pas pu être supprimée + resume: + could_not_resume_the_order: La commande n'a pas pu être reprise + shared: + user_guide_link: + user_guide: Guide utilisateur + overview: + enterprises_header: + ofn_with_tip: Les Entreprises sont des Producteurs et/ou Hubs distributeurs, et sont donc les organisations de base qui utilisent Open Food France. + enterprises_hubs_tabs: + has_no_payment_methods: "%{enterprise} n'a pas défini de méthode de paiement" + has_no_shipping_methods: "%{enterprise} n'a pas défini de méthode de livraison" + has_no_enterprise_fees: "%{enterprise} n'a pas défini de marges et commissions" + enterprise_issues: + create_new: Créer Nouveau + resend_email: Renvoyer l'email + has_no_payment_methods: "%{enterprise} n'a pas de méthode de paiement active" + has_no_shipping_methods: "%{enterprise} n'a pas de méthode de livraison active" + email_confirmation: "L'adresse e-mail doit être confirmée. Nous avons envoyé un lien de confirmation à %{email}." + not_visible: "%{enterprise}n'est pas visible et ne peut être trouvé sur la carte ou dans les recherches sur le site." + reports: + hidden: Masqué + unitsize: Unité de mesure + total: Total + total_items: Nb Articles + supplier_totals: Totaux Cycle de Vente par Producteur + supplier_totals_by_distributor: Totaux Cycle de Vente par Producteur pour chaque Hub Distributeur + totals_by_supplier: Totaux Cycle de Vente par Hub Distributeur pour chaque Producteur + customer_totals: Totaux Cycle de Vente par Acheteur + all_products: Tous les produits + inventory: Catalogue boutique (en stock) + lettuce_share: LettuceShare + mailing_list: Liste de mails + addresses: Adresses + payment_methods: Rapport Méthodes de Paiement + delivery: Rapport de Livraison + tax_types: Par type de taxe + tax_rates: Par taux de taxe + pack_by_customer: Préparation des commandes par Acheteur + pack_by_supplier: Préparation des commandes par Producteur + orders_and_distributors: + name: Commandes et Hubs Distributeurs + description: Liste des Commandes avec les détails des Hubs Ditributeurs + bulk_coop: + name: Achat groupés en vrac (non utilisé) + description: Rapports achats groupés vrac (non utilisé) + payments: + name: Rapports des paiements + description: Rapports des paiements reçus + orders_and_fulfillment: + name: Rapports des commandes + customers: + name: Acheteurs + products_and_inventory: + name: Produits et Catalogues + sales_total: + name: Total des Ventes + description: Total des Ventes pour toutes les Commandes + users_and_enterprises: + name: Managers & Entreprises + description: Managers de l'Entreprise & Droits + order_cycle_management: + name: Gestion des Cycles de Vente + sales_tax: + name: TVA + xero_invoices: + name: Facture Xero + description: Factures pour import dans Xero + packing: + name: Rapports de préparation des paniers + subscriptions: + subscriptions: Abonnements + new: Nouvel abonnement + create: Créer abonnement + index: + please_select_a_shop: Veuillez choisir une boutique + edit_subscription: Mettre à jour Abonnement + pause_subscription: Mettre en pause Abonnement + unpause_subscription: Reprendre Abonnement + cancel_subscription: Annuler Abonnement + setup_explanation: + just_a_few_more_steps: 'Encore quelques étapes avant de pouvoir commencer:' + enable_subscriptions: "Activez la fonction abonnements pour au moins une de vos boutiques" + enable_subscriptions_step_1_html: 1. Allez à %{enterprises_link}, trouvez votre boutique, et cliquez sur "Gérer" + enable_subscriptions_step_2: 2. Sous "Préférences boutiques", activez la fonction Abonnements + set_up_shipping_and_payment_methods_html: Paramétrez au moins une méthode d'%{shipping_link} et une méthode de %{payment_link} + set_up_shipping_and_payment_methods_note_html: Notez bien que seules des méthodes de paiement de type "cash" ou "Stripe" pourront
être utilisées pour les Abonnements + ensure_at_least_one_customer_html: Assurez-vous qu'au moins un %{customer_link} est enregistré dans votre liste d'acheteurs. + create_at_least_one_schedule: Créez au moins un rythme d'abonnement + create_at_least_one_schedule_step_1_html: 1. Allez à la page %{order_cycles_link} + create_at_least_one_schedule_step_2: 2. Créez un cycle de vente si ce n'est pas encore fait + create_at_least_one_schedule_step_3: 3. Cliquez sur "+ Nouveau Rythme d'abonnement", et remplissez le formulaire + once_you_are_done_you_can_html: Une fois que c'est fait, vous pouvez %{reload_this_page_link} + reload_this_page: recharger cette page + steps: + details: 1. Informations de base + address: 2. Adresse + products: 3. Ajouter des produits + review: 4. Vérifier et Enregistrer + subscription_line_items: + this_is_an_estimate: | + Les prix affichés ne sont qu'une estimation calculée au moment de la dernière modification de l'abonnement. + Si vous modifiez les prix ou marges et commissions appliquées, les commandes seront mises à jour, mais l'abonnement affichera toujours les anciennes valeurs. + details: + details: Informations + invalid_error: Oups! Veuillez remplir tous les champs obligatoires... + allowed_payment_method_types_tip: Seules des méthodes de paiement de type "cash" ou "Stripe" peuvent être utilisées pour le moment + credit_card: Carte de crédit + charges_not_allowed: Le débit automatique n'a pas été autorisé par cet acheteur + no_default_card: L'acheteur n'a pas de carte de paiement disponible pour le débit + card_ok: L'acheteur a une carte de paiement disponible pour le débit + loading_flash: + loading: Abonnements en cours de chargement + review: + details: Informations + address: Adresse + products: Produits + product_already_in_order: Ce produit a déjà été ajouté à la commande. Veuillez directement modifier la quantité. + orders: + number: Nombre + confirm_edit: Voulez-vous vraiment modifier cette commande? Si vous poursuivez, la synchronisation automatique des modifications de l'abonnement pourrait être plus difficile à l'avenir. + confirm_cancel_msg: Voulez-vous vraiment annuler cet abonnement? Cette action sera irréversible. + cancel_failure_msg: 'Désolé, l''annulation a échoué!' + confirm_pause_msg: Voulez-vous vraiment mettre en pause cet abonnement? + pause_failure_msg: 'Désolé, la mise en pause a échoué!' + confirm_unpause_msg: Voulez-vous vraiment annuler la mise en pause de cet abonnement? + unpause_failure_msg: 'Désolé, l''annulation de la mise en pause a échoué!' + confirm_cancel_open_orders_msg: "Cet abonnement a des commandes ouvertes. Les acheteurs ont été notifiés que leur commande allait être passée. Voulez-vous annulez ces commandes ou les conserver?" + resume_canceled_orders_msg: "Certaines commandes pour cet abonnement peuvent être réouvertes dès maintenant. Vous pouvez les réouvrir depuis la liste des commandes." + yes_cancel_them: Les annuler + no_keep_them: Les conserver + yes_i_am_sure: Oui, je confirme + order_update_issues_msg: Certaines commandes n'ont pas pu être mises à jour automatiquement, probablement car elles ont été manuellement modifiées. Veuillez revoir les erreurs listées ci-dessous et effectuer si nécessaire les ajustements nécessaires sur les commandes individuelles. + no_results: + no_subscriptions: Pas encore d'abonnements... + why_dont_you_add_one: Pourquoi ne pas en créer un? :) + no_matching_subscriptions: Aucun abonnement correspondant trouvé + schedules: + destroy: + associated_subscriptions_error: Ce rythme d'abonnement ne peut pas être supprimé car il est associé à des abonnements. + controllers: + enterprises: + stripe_connect_cancelled: "La connexion avec Stripe a été annulée" + stripe_connect_success: "Compte Stripe connecté avec succès" + stripe_connect_fail: Désolé, la connexion de votre compte Stripe a échoué :-( + stripe_connect_settings: + resource: Configuration de Stripe Connect + api: + enterprise_logo: + destroy_attachment_does_not_exist: "Aucun logo trouvé :-(" + enterprise_promo_image: + destroy_attachment_does_not_exist: "Aucune bannière trouvée :-(" + checkout: + already_ordered: + cart: "panier" + message_html: "Vous avez déjà passé une commande pour ce cycle de vente. Vérifiez votre %{cart} pour voir les produits commandés. Vous pouvez annuler ou modifier votre commande jusqu'à la fermeture du cycle de vente." + shops: + hubs: + show_closed_shops: "Afficher les boutiques fermées" + hide_closed_shops: "Masquer les boutiques fermées" + show_on_map: "Tout afficher sur la carte" + shared: + menu: + cart: + checkout: "Poursuivre la commande" + already_ordered_products: "Déjà commandé dans ce cycle de vente" + register_call: + selling_on_ofn: "Vous souhaitez proposer vos produits sur Open Food France?" + register: "Démarrez ici" + footer: + footer_global_headline: "OFN Global" + footer_global_home: "Accueil" + footer_global_news: "News" + footer_global_about: "A propos" + footer_global_contact: "Contact" + footer_sites_headline: "Sites OFN" + footer_sites_developer: "Developpeur" + footer_sites_community: "Communauté" + footer_sites_userguide: "Guide utilisateur" + footer_secure: "Fiable et sécurisé." + footer_secure_text: "Open Food France utilise un certificat type SSL (2048 bit RSA) pour garantir la confidentialité de votre commandes et données bancaires. Nos serveurs ne conservent pas vos données bancaires et les paiements sont effectués conformément aux normes de sécurité PCI." + footer_contact_headline: "Restez en contact" + footer_contact_email: "Nous écrire" + footer_nav_headline: "Naviguer" + footer_join_headline: "Nous rejoindre" + footer_join_body: "Créer un profil, une boutique ou un groupe sur Open Food France." + footer_join_cta: "Je veux en savoir plus!" + footer_legal_call: "Lire nos" + footer_legal_tos: "Termes et conditions" + footer_legal_visit: "Nous trouver sur" + footer_legal_text_html: "Open Food Network est une plateforme logicielle open source, libre et gratuite. Nos données sont protégées sous licence %{content_license} et notre code sous %{code_license}." + footer_data_text_with_privacy_policy_html: "Nous prenons soin de vos données. Voir notre %{privacy_policy} et %{cookies_policy}." + footer_data_text_without_privacy_policy_html: "Nous prenons soin de vos données. Voir notre %{cookies_policy}." + footer_data_privacy_policy: "politique de confidentialité" + footer_data_cookies_policy: "politique de cookies" + footer_skylight_dashboard_html: Les informations de performance sont disponibles sur %{dashboard}. + shop: + messages: + login: "Se connecter" + register: "s'inscrire" + contact: "contacter" + require_customer_login: "La boutique est réservée aux membres." + require_login_html: "Déjà inscrit? %{login}. Sinon, %{register} pour pouvoir faire vos achats." + require_customer_html: "Veuillez %{contact} %{enterprise} pour devenir membre." + card_could_not_be_updated: La carte bancaire n'a pas pu être mise à jour + card_could_not_be_saved: la carte n'a pas pu être sauvegardée + spree_gateway_error_flash_for_checkout: "Il y a eu un problème avec vos informations de paiement : %{error}" + invoice_billing_address: "Adresse de facturation :" + invoice_column_tax: "TVA" + invoice_column_price: "Prix" + invoice_column_item: "Produit" + invoice_column_qty: "Qté" + invoice_column_unit_price_with_taxes: "Prix unitaire TTC" + invoice_column_unit_price_without_taxes: "Prix unitaire HT" + invoice_column_price_with_taxes: "Prix total TTC" + invoice_column_price_without_taxes: "Prix total HT" + invoice_column_tax_rate: "TVA applicable" + invoice_tax_total: "Total TVA :" + tax_invoice: "FACTURE" + tax_total: "Total taxe (%{rate}) :" + total_excl_tax: "Total HT :" + total_incl_tax: "Total TTC :" + abn: "SIRET" + acn: "n° TVA intracommunautaire" + invoice_issued_on: "Date de facture :" + order_number: "N° de facture :" + date_of_transaction: "Date de la transaction :" + ticket_column_qty: "Qté" + ticket_column_item: "Produit" + ticket_column_unit_price: "Prix unitaire" + ticket_column_total_price: "Prix total" + menu_1_title: "Boutiques" + menu_1_url: "/shops" + menu_2_title: "Carte" + menu_2_url: "/map" + menu_3_title: "Producteurs" + menu_3_url: "/producers" + menu_4_title: "Groupes" + menu_4_url: "/groups" + menu_5_title: "A propos" + menu_5_url: "https://apropos.openfoodfrance.org/" + menu_6_title: "Blog" + menu_6_url: "https://apropos.openfoodfrance.org/blog/" + menu_7_title: "Support" + menu_7_url: "https://apropos.openfoodfrance.org/support/" + logo: "Logo (640x130)" + logo_mobile: "Logo smartphone (75x26)" + logo_mobile_svg: "Logo smartphone (SVG)" + home_hero: "Bannière" + home_show_stats: "Afficher statistiques" + footer_logo: "Logo (220x76)" + footer_facebook_url: "Facebook URL" + footer_twitter_url: "Twitter URL" + footer_instagram_url: "Instagram URL" + footer_linkedin_url: "LinkedIn URL" + footer_googleplus_url: "Google Plus URL" + footer_pinterest_url: "Pinterest URL" + footer_email: "Email" + footer_links_md: "Liens" + footer_about_url: "A propos URL" + user_guide_link: "Lien vers le guide utilisateur" + name: Nom + first_name: Prénom + last_name: Nom de famille + email: Email + phone: Téléphone + next: Suivant + address: Adresse + address_placeholder: 'ex: 24 rue de la croix verte' + address2: Adresse (suite) + city: Ville + city_placeholder: 'ex: Nantes' + postcode: Code postal + postcode_placeholder: 'ex: 44000' + state: Département + country: Pays + unauthorized: Non authorisé + terms_of_service: "Conditions d'utilisation" + on_demand: A volonté + none: Aucun + not_allowed: Non autorisé + no_shipping: pas de méthode de livraison + no_payment: pas de méthode de paiement + no_shipping_or_payment: pas de méthode de livraison ou de paiement + unconfirmed: non confirmé + days: jours + label_shop: "Boutique" + label_shops: "Boutiques" + label_map: "Carte" + label_producer: "Producteur" + label_producers: "Producteurs" + label_groups: "Groupes" + label_about: "A propos" + label_connect: "Se connecter" + label_learn: "Apprendre" + label_blog: "Blog" + label_support: "Soutien" + label_shopping: "Achats" + label_login: "Se connecter" + label_logout: "Déconnexion" + label_signup: "Inscription" + label_administration: "Administration" + label_admin: "Admin" + label_account: "Compte" + label_more: "Afficher plus" + label_less: "Masquer" + label_notices: "Informations" + cart_items: "Produits" + cart_headline: "Votre panier" + total: "Total" + cart_updating: "Mettre à jour le panier" + cart_empty: "Panier vide" + cart_edit: "Modifier votre panier" + card_number: Numéro de carte + card_securitycode: "Cryptogramme visuel" + card_expiry_date: Date d'expiration + card_masked_digit: "X" + card_expiry_abbreviation: "Exp" + new_credit_card: "Nouvelle carte de crédit" + my_credit_cards: Mes cartes bancaires + add_new_credit_card: Ajouter nouvelle carte de crédit + saved_cards: Sauvegarder cartes + add_a_card: Ajouter une Carte + add_card: Ajouter Carte + you_have_no_saved_cards: Vous n'avez pas encore sauvegardé de carte + saving_credit_card: Enregistrement de la carte de crédit... + card_has_been_removed: "Votre carte a été supprimée (numéro : %{number})" + card_could_not_be_removed: Désolée, la carte n'a pas pu être supprimée :-( + ie_warning_headline: "Votre navigateur n'est pas à jour :-(" + ie_warning_text: "Pour une expérience optimale sur Open Food France, nous vous recommandons fortement de mettre à jour votre navigateur:" + ie_warning_chrome: Télécharger Chrome + ie_warning_firefox: Télécharger Firefox + ie_warning_ie: Mettre à jour Internet Explorer + ie_warning_other: "Impossible de mettre à jour votre navigateur? Essayez Open Food France sur votre smartphone :-)" + legal: + cookies_policy: + header: "Notre usage des Cookies" + desc_part_1: "Les cookies sont de tout petits fichiers texte qui sont stockés sur votre ordinateur quand vous naviguez sur certains sites web." + desc_part_2: "Chez Open Food France nous sommes particulièrement soucieux de votre vie privée. Nous n'utilisons que les cookies nécessaires pour vous offrir un service d'achat/vente en ligne performant. Nous ne vendons aucunes de vos données. Peut-être nous proposerons-vous dans le futur de jolies projets qui peuvent être utiles pour faire progresser la vente en circuits courts, et pour lesquels vos données pourraient nous permettre de trouver de nouvelles solutions aux problèmes que vous rencontrez, comme par exemple la construction de services logistiques mutualisés. Mais nous n'y sommes pas encore, et nous ne ferons rien sans votre autorisation :-)" + desc_part_3: "Nous utilisons des cookies principalement pour vous permettre de rester connecté(e) après votre première connexion si vous le souhaitez (et éviter de saisir votre mot de passe à chaque fois!), ou encore pour mémoriser les produits dans votre panier. Si vous naviguez sur le site sans cliquer sur \"Accepter les cookies\", cela vaut consentement à l'utilisation des cookies nécessaires au bon fonctionnement du site openfoodfrance.org. Voilà la liste détaillée des cookies que nous utilisons!" + essential_cookies: "Cookies essentiels" + essential_cookies_desc: "Les cookies suivants sont nécessaires au fonctionnement du site openfoodfrance.org." + essential_cookies_note: "Les cookies contiennent un identifiant unique, mais pas d'autres données. Vos emails et mots de passe par exemple ne sont jamais exposés dans les cookies!" + cookie_domain: "Déposé par" + cookie_session_desc: "Utilisé pour garder en mémoire l'utilisateur d'une page à l'autre lors de la navigation sur le site, ou pour se souvenir des produits dans le panier." + cookie_consent_desc: "Utilisé pour se souvenir du consentement de l'utilisation à l'utilisation de cookies." + cookie_remember_me_desc: "Utilisé si l'utilisateur a cliqué sur \"se souvenir de moi\" (pour ne pas avoir à se reconnecter à chaque fois). Ce cookie est automatiquement supprimé après 12 jours. Si l'utilisateur souhaite supprimer ce cookie, il n'a qu'à se déconnecter. Si l'utilisateur ne souhaite pas que ce cookie soit installé sur son terminal, il suffit de ne pas cocher la case \"se souvenir de moi\" au moment de la connexion." + cookie_openstreemap_desc: "Utilisé par le logiciel de cartographie open source et ami Open Street Map (qui permet l'affichage de la carte sur Open Food France) pour assurer qu'il ne reçoit pas trop de requêtes sur un laps de temps déterminé, et éviter ainsi les risques d'abus de leurs services." + cookie_stripe_desc: "Utilisé par le terminal de payement en ligne Stripe (proposé aux utilisateurs d'Open Food France) https://stripe.com/fr/cookies-policy/legal. Même si toutes les boutiques n'utilisent pas Stripe, c'est une bonne pratique en matière de sécurité d'appliquer ce cookie sur toutes les pages vues. Stripe construit probablement une image des pages qui ont un quelconque lien avec l'API connectant Open Food France à leur système de paiement pour détecter les comportements anormaux pouvant suggérer un risque de fraude. Donc ce cookie a un rôle qui va au-delà de la simple fourniture d'un système de paiement. Le supprimer pourrait affecter la sécurité du service. Pour en savoir plus sur la politique de confidentialité de Stripe: https://stripe.com/fr/privacy." + statistics_cookies: "Cookies d'analyse de navigation" + statistics_cookies_desc: "Ces cookies ne sont pas obligatoires, mais nous permettent de mieux comprendre votre usage de la plateforme, les endroits où vous bloquez, les fonctionnalités qui semblent vous manquer, ou que vous n'utilisez jamais, afin de fournir le service le plus adapté possible aux besoins des utilisateurs." + statistics_cookies_analytics_desc_html: "Pour analyser les données concernant votre usage de la plateforme, nous utilisons Google Analytics, pas vraiment par choix, mais simplement parce que c'était l'outil d'analyse connecté par défaut via Spree, le logiciel e-commerce open source sur lequel nous avons construit. Mais nous espérons pouvoir rapidement migrer vers Matomo (anciennement Piwik), outil d'analyse open source compatible RGPD et engagé sur le respect de la vie privée des utilisateurs." + statistics_cookies_matomo_desc_html: "Pour analyser les données concernant votre usage de la plateforme, nous utilisons Matomo(anciennement Piwik), outil d'analyse open source compatible RGPD et engagé sur le respect de la vie privée des utilisateurs." + statistics_cookies_matomo_optout: "Vous ne voulez pas que vos données soient analysées par Matomo ? Nous ne collectons aucune donnée personnelle, et Matomo nous aide à améliorer le service que nous vous offrons, mais nous respectons votre choix :-)" + cookie_analytics_utma_desc: "Utilisé pour distinguer les utilisateurs et les sessions. Ce cookie est installé quand la librairie Javascript s'exécute et qu'aucun cookie __utma n'existe déjà. Le cookie est mis à jour à chaque fois que des données sont envoyées à Google Analytics." + cookie_analytics_utmt_desc: "Utilisé pour limiter le taux de requêtes." + cookie_analytics_utmb_desc: "Utilisé pour distinguer les nouvelles sessions/visites. Ce cookie est installé quand la librairie Javascript s'exécute et qu'aucun cookie __utmb n'existe déjà. Le cookie est mis à jour à chaque fois que des données sont envoyées à Google Analytics. " + cookie_analytics_utmc_desc: "Non utilisé dans ga.js. Utilisé pour l'interopérabilité avec urchin.js. Historiquement, ce cookie fonctionnait en conjonction avec le cookie __utmb pour déterminer si l'utilisateur était dans une nouvelle session/visite ou pas." + cookie_analytics_utmz_desc: "Ce cookie stocke les sources de trafic qui expliquent comment l'utilisateur est arrivé sur le site (via une campagne, une recherche sur un moteur de recherche, etc.). Ce cookie est installé quand la librairie Javascript s'exécute et mis à jour à chaque fois que des données sont envoyées à Google Analytics. " + cookie_matomo_basics_desc: "Cookies déposés par Matomo pour collecter les statistiques de trafic." + cookie_matomo_heatmap_desc: "Utilisé par Matomo pour enregistrer les sessions et \"cartes thermiques\" (représentations graphiques des données)" + cookie_matomo_ignore_desc: "Cookie utilisé pour se souvenir qu'un utilisateur a souhaité explicitement que sa navigation ne soit pas analysée par Matomo, et exclure cet utilisateur du suivi du site." + disabling_cookies_header: "Mises en garde sur la désactivation des cookies" + disabling_cookies_desc: "En tant qu'utilisateur, vous pouvez toujours autoriser, bloquer ou supprimer tous les cookies utilisés par Open Food France ou tout autre site web via les paramètres de votre navigateur. Chaque navigateur a un chemin spécifique pour effectuer cette désactivation:" + disabling_cookies_firefox_link: "https://support.mozilla.org/fr/kb/activer-desactiver-cookies-preferences" + disabling_cookies_chrome_link: "https://support.google.com/chrome/answer/95647?hl=fr" + disabling_cookies_ie_link: "https://support.microsoft.com/fr-fr/help/17442/windows-internet-explorer-delete-manage-cookies" + disabling_cookies_safari_link: "https://www.apple.com/legal/privacy/fr-ww/cookies/" + disabling_cookies_note: "Mais gardez bien en tête que si vous supprimez ou modifiez un des cookies essentiels utilisés par Open Food France, le site ne fonctionnera pas correctement, vous ne pourrez pas ajouter des produits à votre panier ni finaliser votre commande par exemple." + cookies_banner: + cookies_usage: "Ce site utilise des cookies pour rendre votre navigation fluide et sécurisée, et nous aider à comprendre l'usage que vous faites de la plateforme afin d'améliorer les fonctionnalités offertes." + cookies_definition: "Les cookies sont de tout petits fichiers texte qui sont stockés sur votre ordinateur quand vous naviguez sur certains sites web." + cookies_desc: "Nous n'utilisons que les cookies nécessaires pour vous offrir un service de vente/achat de produits alimentaires en ligne performant. Nous ne vendons aucune de vos données. Nous utilisons ces cookies principalement pour vous permettre de rester connecté(e) après votre première connexion, ou encore pour mémoriser les produits dans votre panier lorsque vous n'êtes pas connecté(e). Si vous naviguez sur le site sans cliquer sur \"Accepter les cookies\", cela vaut consentement à l'utilisation des cookies nécessaires au bon fonctionnement du site." + cookies_policy_link_desc: "Si vous voulez en savoir plus, consultez notre" + cookies_policy_link: "politique de cookies" + cookies_accept_button: "Accepter les cookies" + home_shop: Faire mes courses + brandstory_headline: "Des aliments porteurs de sens." + brandstory_intro: "Parfois, le meilleur moyen de réparer le système, c'est d'en inventer un autre..." + brandstory_part1: "Tout commence dans le sol. Avec ces paysans, agriculteurs, producteurs, engagés pour une agriculture durable et régénératrice, et désireux de partager leur histoire et leur passion avec fierté. Avec ces distributeurs souhaitant reconnecter les individus à leurs aliments et aux gens qui les produisent, soutenir les prises de conscience, dans une démarche de transparence, d'honnêteté, en assurant une juste rémunération des producteurs. Avec ces acheteurs qui croient que de meilleures décisions d'achats peuvent véritablement changer le monde." + brandstory_part2: "Nous avons besoin d'un outil pour rendre tout ça réel. Un moyen de redonner le pouvoir à ceux qui cultivent, vendent et achètent la nourriture. Un moyen de raconter les histoires, de gérer la logistique. Un moyen de transformer chaque jour les transactions en actions porteuses de changement." + brandstory_part3: "C'est pour cela que nous construisons cette plateforme, ce \"marché en ligne\", afin de rééquilibrer les échanges et redistribuer le pouvoir. Elle est transparente, pour assurer des relations équitables et favoriser les prises de conscience. Elle est open source, donc possédée par tout le monde. Elle se déploie aux échelles régionales et nationales, et des gens lancent de multiples versions à travers le monde." + brandstory_part4: "Elle fonctionne partout. Elle change tout." + brandstory_part5_strong: "Cette plateforme s'appelle Open Food Network." + brandstory_part6: "Nous aimons notre nourriture. Maintenant nous pouvons aussi aimer notre système alimentaire." + learn_body: "Explorer les modèles, les histoires et les ressources disponibles pour vous aider à développer votre propre initiative de commerce/organisation oeuvrant pour un système alimentaire équitable et juste. Trouver des outils pour vous former, des événements et autres opportunités d'apprendre de vos pairs." + learn_cta: "Découvrir " + connect_body: "Rechercher dans le répertoire des producteurs, hubs et groupes pour trouver des commerçants éthiques à côté de chez vous. Inscrivez votre commerce ou organisation sur OFFrance pour que les acheteurs puissent vous trouver. Rejoignez la communauté pour recevoir du soutien et résoudre ensemble les problèmes." + connect_cta: "Explorer" + system_headline: "Faire mes courses - comment ça marche?" + system_step1: "1. Recherche" + system_step1_text: "Recherchez des produits locaux, de saison, parmi nos multiples boutiques indépendantes. Filtrez par localisation ou catégorie de produits, livraison en point retrait ou à domicile." + system_step2: "2. Achat" + system_step2_text: "Transformez vos achats en choisissant des produits locaux et abordables, proposés par les divers producteurs et hubs. Découvrez les histoires et les personnes qui se cachent derrière les produits!" + system_step3: "3. Retrait / Livraison" + system_step3_text: "Réceptionnez vos produits à domicile, ou rendez vous chez votre producteur ou hub pour rencontrer les gens qui se cachent derrière les produits. Au delà de la bio-diversité, nous cultivons l'éco-diversité: vivez des expériences d'achat de nourriture uniques et humaines." + cta_headline: "Des achats qui rendent le monde un peu meilleur." + cta_label: "Je vote avec mes achats" + stats_headline: "Nous créons un nouveau système alimentaire." + stats_producers: "agriculteurs et producteurs" + stats_shops: "boutiques" + stats_shoppers: "acheteurs" + stats_orders: "commandes" + checkout_title: Finalisation commande + checkout_now: Passer la commande + checkout_order_ready: Commande prête pour + checkout_hide: Masquer + checkout_expand: Afficher + checkout_headline: "Ok, prêt à finaliser la commande?" + checkout_as_guest: "Passer commande en mode invité" + checkout_details: "Vos informations" + checkout_billing: "Informations de facturation" + checkout_default_bill_address: "Sauvegarder comme adresse de facturation par défaut" + checkout_shipping: Informations de livraison + checkout_default_ship_address: "Sauvegarder comme adresse de livraison par défaut" + checkout_method_free: Pas de frais supplémentaires + checkout_address_same: Adresse de livraison identique à l'adresse de facturation? + checkout_ready_for: "Prêt pour:" + checkout_instructions: "Commentaires ou demandes spécifiques?" + checkout_payment: Paiement + checkout_send: Passer la commande + checkout_your_order: Votre commande + checkout_cart_total: Panier total + checkout_shipping_price: Livraison + checkout_total_price: Total + checkout_back_to_cart: "Retour au Panier" + cost_currency: "Devise du Coût" + order_paid: RÉGLÉ + order_not_paid: NON RÉGLÉ + order_total: Total commande + order_payment: "Payer via:" + order_billing_address: Adresse de facturation + order_delivery_on: Livraison prévue + order_delivery_address: Adresse de livraison + order_delivery_time: Créneau de livraison/retrait + order_special_instructions: "Vos commentaires:" + order_pickup_time: Prêt à être retiré + order_pickup_instructions: Instructions de retrait + order_produce: Produit + order_total_price: Total + order_includes_tax: (dont TVA) + order_payment_paypal_successful: Votre paiement via PayPal a été réalisé avec succès. + order_hub_info: Hub Info + order_back_to_store: Retour à la boutique + order_back_to_cart: Retour au panier + bom_tip: "Utilisez cette page pour modifier les quantités sur plusieurs commandes à la fois. Les produits peuvent aussi être supprimés des commandes si nécessaire." + unsaved_changes_warning: "Des modifications n'ont pas été enregistrées et seront perdues si vous continuez." + unsaved_changes_error: "Les champs entourés en rouge contiennent des erreurs." + products: "Produits" + products_in: "dans %{oc}" + products_at: "à %{distributor}" + products_elsewhere: "Produits trouvés ailleurs" + email_welcome: "Bienvenue" + email_confirmed: "Veuillez confirmer votre adresse email." + email_registered: "fait maintenant partie de" + email_userguide_html: "Le Guide Utilisateur expliquant comment mettre en place son profil producteur ou son hub est accessible ici: %{link}" + email_admin_html: "Vous pouvez gérer votre compte en vous connectant ici %{link} ou en cliquant sur la roue en haut à droite de la page d'accueil et en sélectionnant Administration." + email_community_html: "Nous avons aussi un forum de discussion en ligne (en anglais) pour échanger avec la communauté sur des questions liées au logiciel OFN et aux défis de la gestion d'un food hub. Nous vous invitons à y participer. Nous sommes en constante évolution et vos contributions à ce forum vont façonner les prochaines étapes. %{link}" + join_community: "Rejoindre la communauté" + email_confirmation_activate_account: "Avant de pouvoir activer votre compte, nous devons nous assurer de la validité de votre adresse email." + email_confirmation_greeting: "Bonjour %{contact}!" + email_confirmation_profile_created: "Le profil pour %{name} a été créé avec succès! Pour activer votre Profil nous devons vérifier cette adresse email." + email_confirmation_click_link: "Veuillez cliquer sur le lien ci-dessous pour confirmer votre email et continuer la configuration de votre compte." + email_confirmation_link_label: "Confirmer cette adresse email »" + email_confirmation_help_html: "Après confirmation de votre email, vous pourrez accéder au compte d'administration de cette entreprise. Voir %{link} pour en savoir plus à propos de %{sitename} et commencer à utiliser votre profil et/ou boutique en ligne." + email_confirmation_notice_unexpected: "Vous recevez ce message car vous vous êtes inscrit sur %{sitename}, ou avez été invité à vous inscrire par l'un de vos contacts. Si vous ne comprenez pas pourquoi vous recevez ce message, écrivez à %{contact}." + email_social: "Nous suivre:" + email_contact: "Nous écrire:" + email_signoff: "Cordialement," + email_signature: "L'équipe %{sitename}" + email_confirm_customer_greeting: "Bonjour %{name}," + email_confirm_customer_intro_html: "Merci d'avoir passé commande chez %{distributor}!" + email_confirm_customer_number_html: "Confirmation de commande #%{number}" + email_confirm_customer_details_html: "Détails de votre commande chez %{distributor}:" + email_confirm_customer_signoff: "Cordialement," + email_confirm_shop_greeting: "Bonjour %{name}," + email_confirm_shop_order_html: "Bravo! Vous avez reçu une nouvelle commande automatique pour %{distributor}!" + email_confirm_shop_number_html: "Confirmation de commande #%{number}" + email_order_summary_item: "Produit" + email_order_summary_quantity: "Qté" + email_order_summary_price: "Prix" + email_order_summary_subtotal: "Sous-total:" + email_order_summary_total: "Total:" + email_order_summary_includes_tax: "(dont TVA)" + email_payment_paid: RÉGLÉ + email_payment_not_paid: NON RÉGLÉ + email_payment_summary: Résumé du paiement + email_payment_method: "Payer via :" + email_so_placement_intro_html: "Une nouvelle commande automatique a été passée pour vous chez %{distributor}" + email_so_placement_details_html: "Voici les détails de votre commande pour %{distributor}:" + email_so_placement_changes: "Malheureusement, certains produits demandés n'étaient pas disponibles. Les quantités d'origine demandées apparaissent comme barrées ci-dessous." + email_so_payment_success_intro_html: "Un paiement automatique a été effectué pour votre commande auprès de %{distributor}." + email_so_placement_explainer_html: "Cette commande a été créée automatiquement dans le cadre de votre abonnement." + email_so_edit_true_html: "Vous pouvez effectuer des modifications jusqu'à la fermeture de la période de commande le %{orders_close_at}." + email_so_edit_false_html: "Vous pouvez consulter les détails de cette commande à tout moment." + email_so_contact_distributor_html: "Pour toute question contactez %{distributor} via %{email}." + email_so_contact_distributor_to_change_order_html: "Cette commande a été automatiquement créée en votre nom. Vous pouvez effectuer des modifications sur cette commande jusqu'à fermeture de la période de commande le %{orders_close_at} en contactant %{distributor} à %{email}." + email_so_confirmation_intro_html: "Votre commande auprès de %{distributor} est maintenant confirmée" + email_so_confirmation_explainer_html: "Cette commande a été automatiquement passée pour vous dans le cadre de votre abonnement, et a maintenant été confirmée." + email_so_confirmation_details_html: "Voici les détails concernant cette commande auprès de %{distributor}:" + email_so_empty_intro_html: "Nous avons essayé de passer votre commande auprès de %{distributor}, mais une erreur est survenue..." + email_so_empty_explainer_html: "Malheureusement, aucun des produits demandés n'étaient disponibles, nous n'avons donc pas pu passer votre commande. Les quantités d'origine demandées apparaissent comme barrées ci-dessous." + email_so_empty_details_html: "Voici les détails concernant la commande qui n'a pas pu être passée auprès de %{distributor}:" + email_so_failed_payment_intro_html: "Nous avons essayé d'effectuer un paiement, mais une erreur est survenue..." + email_so_failed_payment_explainer_html: "Le paiement pour l'abonnement auprès de %{distributor} a échoué à cause d'un problème avec votre carte de crédit. %{distributor} a été notifié de ce problème de paiement." + email_so_failed_payment_details_html: "Voici les détails concernant l'erreur fournis par la passerelle de paiement:" + email_shipping_delivery_details: Détails de livraison + email_shipping_delivery_time: "Livré le:" + email_shipping_delivery_address: "Adresse de livraison:" + email_shipping_collection_details: Détails de retrait + email_shipping_collection_time: "Prêt pour retrait:" + email_shipping_collection_instructions: "Instructions de retrait:" + email_special_instructions: "Vos commentaires:" + email_signup_greeting: Bonjour! + email_signup_welcome: "Bienvenue sur %{sitename}!" + email_signup_confirmed_email: "Merci d'avoir confirmé votre email." + email_signup_shop_html: "Vous pouvez maintenant vous connecter sur %{link}." + email_signup_text: "Merci d'avoir rejoint le réseau. Si vous êtes un client, nous sommes impatients de vous faire découvrir de nombreux agriculteurs fantastiques, de merveilleux hubs de distribution et des plats délicieux! Si vous êtes un producteur ou autre entreprise alimentaire, nous sommes ravis de vous compter parmi les membres du réseau." + email_signup_help_html: "Vos questions et feedbacks sont les bienvenus! Cliquez sur le bouton Envoyer un commentaire sur le site ou envoyez-nous un email à %{email}" + invite_email: + greeting: "Bonjour!" + invited_to_manage: "Vous avez été invité(e) à gérer %{enterprise} sur %{instance}." + confirm_your_email: "Vous avez reçu ou allez recevoir prochainement un email avec un lien de validation. Vous n'aurez pas accès au profil de l'entreprise %{enterprise} avant d'avoir cliqué sur ce lien." + set_a_password: "Vous serez ensuite invité(e) à choisir un mot de passe avant de pouvoir accéder et gérer le profil de l'entreprise." + mistakenly_sent: "Vous ne savez pas pourquoi vous recevez cet email? Veuillez contacter %{owner_email} pour plus d'informations." + producer_mail_greeting: "Cher(ère)" + producer_mail_text_before: "Nous avons reçu toutes les commandes pour la prochaine livraison." + producer_mail_order_text: "Voilà la liste et les quantités des produits commandés vous concernant:" + producer_mail_delivery_instructions: "Modalités de livraison des produits:" + producer_mail_signoff: "Merci et belle fin de journée!" + shopping_oc_closed: La boutique est actuellement fermée + shopping_oc_closed_description: "Veuillez attendre l'ouverture du prochain cycle de vente (ou contactez-nous directement pour voir si nous pouvons accepter une commande tardive)" + shopping_oc_last_closed: "Le dernier cycle de vente s'est terminé il y a %{distance_of_time}" + shopping_oc_next_open: "Le prochain cycle de vente ouvrira dans %{distance_of_time}" + shopping_tabs_about: "A propos de %{distributor}" + shopping_tabs_contact: "Contact" + shopping_contact_address: "Adresse" + shopping_contact_web: "Contact" + shopping_contact_social: "Suivre" + 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_ready_for: "Prêt pour" + enterprises_choose: "Choisissez votre option:" + maps_open: "Ouvre" + maps_closed: "Fermée" + hubs_buy: "Acheter:" + hubs_shopping_here: "Achats en cours" + hubs_orders_closed: "Boutique fermée" + hubs_profile_only: "Fiche profil" + hubs_delivery_options: "Options de livraison" + hubs_pickup: "Retrait" + hubs_delivery: "Livraison" + hubs_producers: "Nos producteurs" + hubs_filter_by: "Filtrer par" + hubs_filter_type: "Catégorie" + hubs_filter_delivery: "Livraison" + hubs_filter_property: "Propriétés / labels" + hubs_matches: "Vous voulez dire?" + hubs_intro: Passez commande près de chez vous + hubs_distance: Le plus près de + hubs_distance_filter: "Afficher les boutiques près de %{location}" + shop_changeable_orders_alert_html: + one: Votre commande chez %{shop} / %{order} est ouverte pour vérification. Vous pouvez effectuer des modifications jusqu'au %{oc_close}. + other: Vous avez %{count} commandes avec %{shop} ouvertes à la vérification. Vous pouvez effectuer des modifications jusqu'au %{oc_close}. + orders_changeable_orders_alert_html: Cette commande a été confirmée, mais vous pouvez effectuer des modifications jusqu'à %{oc_close}. + products_clear_all: Vider + products_showing: "Afficher:" + products_with: avec + products_search: "Recherche par produit ou producteur" + products_loading: "Produits en cours de chargement..." + products_updating_cart: "Actualisation du panier..." + products_cart_empty: "Panier vide" + products_edit_cart: "Valider votre panier" + products_from: de + products_change: "Aucun changement à sauvegarder." + products_update_error: "Échec de l'enregistrement dû à:" + products_update_error_msg: "Échec de l'enregistrement." + products_update_error_data: "Échec de l'enregistrement dû à des données non valides." + products_changes_saved: "Modifications enregistrées." + search_no_results_html: "Désolé, aucun résultat pour %{query}. Autre recherche?" + components_profiles_popover: "Certaines entreprises ont juste créé leur profil sur Open Food France mais ne vendent pas via la plateforme. Elles ont peut-être une boutique physique, ou une boutique en ligne sur une autre plateforme." + components_profiles_show: "Afficher aussi les profils" + components_filters_nofilters: "Pas de filtre" + components_filters_clearfilters: "Vider les filtres" + groups_title: Groupes + groups_headline: Groupes / réseaux territoriaux + groups_text: "Chaque producteur est unique. Chaque entreprise peut offrir quelque chose de différent. Nos groupes sont des collectifs de producteurs, des plateformes et des distributeurs qui partagent une proximité géographique, un marché fermier ou des valeurs. C'est ce qui rend votre expérience d'achat plus simple. Explorez donc ces groupes sélectionnés." + groups_search: "Recherche par nom ou mot-clé" + groups_no_groups: "Aucun groupe trouvé" + groups_about: "A propos" + groups_producers: "Nos producteurs" + groups_hubs: "Nos hubs" + groups_contact_web: Contact + groups_contact_social: Suivre + groups_contact_address: Adresse + groups_contact_email: Nous écrire + groups_contact_website: Visiter notre site web + groups_contact_facebook: Nous suivre sur Facebook + groups_signup_title: S'inscrire en tant que groupe + groups_signup_headline: Inscription groupe + groups_signup_intro: "Nous sommes une plate-forme très efficace pour le marketing collaboratif, une excellente manière pour vos membres et parties prenantes d'atteindre de nouveaux marchés. Nous sommes à but non lucratif, abordable et simple." + groups_signup_email: Nous écrire + groups_signup_motivation1: Nous transformons les systèmes alimentaires pour remettre de l'équité dans les échanges. + groups_signup_motivation2: C'est pourquoi nous sortons du lit chaque matin. Nous sommes une organisation à but non lucratif, basée sur un code source ouvert. Nous opérons en toute transparence. + groups_signup_motivation3: Vous avez de belles idées, et nous voulons vous aider. Nous partageons nos connaissances, réseaux et ressources. Nous savons que l'isolement ne crée pas le changement, alors coopérons. + groups_signup_motivation4: Nous venons à votre rencontrer. + groups_signup_motivation5: Vous êtes un réseau de circuits de distribution alternatifs, de producteurs, de distributeurs, une administration liée à l'industrie alimentaire ou une autorité locale? + groups_signup_motivation6: Quel que soit votre rôle dans la relocalisation des systèmes alimentaires, nous sommes prêts à vous soutenir. Si vous vous demandez à quoi Open Food Network ressemble / pourrait ressembler dans votre coin du monde, contactez-nous. + groups_signup_motivation7: Nous contribuons à remettre du sens dans les systèmes alimentaires. + groups_signup_motivation8: Vous avez besoin de connecter et d'outiller vos réseaux, nous offrons une plate-forme pour la coopération et l'action. Vous souhaitez de l'engagement. Nous vous aidons à atteindre les acteurs, les parties-prenantes, les secteurs. + groups_signup_motivation9: Vous avez besoin de ressources. Nous mettons à votre service notre expérience. Vous avez besoin de coopération. Nous vous connectons à un large réseau d'acteurs et d'organisations soeurs partout dans le monde. + groups_signup_pricing: Compte groupe + groups_signup_studies: Etudes de cas + groups_signup_contact: Vous voulez discuter? + groups_signup_contact_text: "Prenez contact et découvrez ce qu'Open Food France peut faire pour vous:" + groups_signup_detail: "Plus de précisions." + login_invalid: "Email ou mot de passe erroné" + modal_hubs: "Food Hubs" + modal_hubs_abstract: Nos food hubs sont les points de contact entre vous et les personnes qui produisent votre nourriture! + modal_hubs_content1: Vous pouvez chercher le hub qui vous convient par localisation ou par nom. Certains hubs ont de multiples points de retrait de vos achats, et certains proposent également la livraison à domicile. Chaque food hub est un point de vente et gère de façon indépendante ses opérations et sa logistique - attendez-vous donc à des disparités de fonctionnement entre les hubs. + modal_hubs_content2: Vous pouvez uniquement faire vos courses dans un hub à la fois. + modal_groups: "Groupes / réseaux territoriaux" + modal_groups_content1: Voilà les organisations et les relations inter-hubs qui constituent l'Open Food Network. + modal_groups_content2: Certains groupes sont regroupés pas localisation ou région, d'autres sur des smilitudes non géographiques. + modal_how: "Comment ça marche" + modal_how_shop: Faire vos courses sur Open Food France + modal_how_shop_explained: Recherchez un food hub près de chez vous et commencez vos achats! Vous pouvez afficher plus d'infos sur chaque food hub pour voir le type de produits qu'il propose, et cliquer sur le hub pour commencer vos achats. (Vous ne pouvez faire vos courses que dans un food hub à la fois.) + modal_how_pickup: Frais de retrait, livraison et transport + modal_how_pickup_explained: Certains food hubs livrent à domicile, d'autres vous demandent de venir récupérer vos achats dans un point de retrait. Vous pouvez voir quelle options sont proposées sur la page d'accueil du hub, et sélectionner votre choix au moment de la validation de la commande. La livraison à domicile coûtera souvent plus cher, et les prix diffèrent selon le hub. Chaque food hub est un point de vente et gère de façon indépendante ses opérations et sa logistique - attendez-vous donc à des disparités de fonctionnement entre les hubs. + modal_how_more: En savoir plus + modal_how_more_explained: "Pour en savoir plus sur Open Food France, comment ça marche, et contribuer, allez voir:" + modal_producers: "Producteurs" + modal_producers_explained: "Nos producteurs font pousser et fabriquent tous les délicieux produits que vous pouvez acheter sur Open Food France." + producers_about: A propos + producers_buy: Acheter + producers_contact: Contact + producers_contact_phone: Appeler + producers_contact_social: Suivre + producers_buy_at_html: "Acheter les produits de %{enterprise} dans les boutiques suivantes:" + producers_filter: Filtrer par + producers_filter_type: Catégorie + producers_filter_property: Propriété + producers_title: Producteurs + producers_headline: Trouvez un producteur local + producers_signup_title: S'inscrire en tant que producteur + producers_signup_headline: Des producteurs, indépendants + producers_signup_motivation: Vendez vos produits et racontez vos histoires pour toucher de nouveaux marchés. Gagnez du temps et de l'argent sur la gestion des opérations courantes. Vous pouvez innover sans prendre de risque. Nous nivellons le terrain de jeu pour des échanges plus équitables. + producers_signup_send: Rejoindre le réseau + producers_signup_enterprise: Comptes entreprises + producers_signup_studies: Les histoires de nos producteurs. + producers_signup_cta_headline: Rejoindre le réseau! + producers_signup_cta_action: Rejoindre le réseau + producers_signup_detail: Comment ça marche. + products_item: Produit + products_description: Description + products_variant: Variante + products_quantity: Quantité + products_available: Disponible? + products_producer: "Producteur" + products_price: "Prix" + register_title: S'inscrire + sell_title: "S'inscrire" + sell_headline: "Aller sur Open Food France!" + sell_motivation: "Mettez en avant vos beaux aliments." + sell_producers: "Producteurs" + sell_hubs: "Hubs" + sell_groups: "Groupes" + sell_producers_detail: "Créer un profil pour votre entreprise sur OFFrance en quelques minutes. A tout moment vous pourrez créer une boutique en ligne pour vendre vos produits en direct aux acheteurs." + sell_hubs_detail: "Créer un profil pour votre entreprise de distribution ou organisation sur OFFrance. A tout moment vous pourrez créer une boutique multi-fournisseurs." + sell_groups_detail: "Créer un répertoire sur mesure (regroupant différents producteurs et hubs de distribution) pour votre région ou votre organisation." + sell_user_guide: "En savoir plus en explorant le guide utilisateur." + sell_listing_price: "La création d'un profil sur OFFrance est entièrement libre. Si vous ouvrez et gérez une boutique sur OFFrance, ou créez un groupe pour votre organisation ou réseau régional, nous vous invitons à contribuer au commun Open Food France que vous utilisez. En effet, faire tourner la plateforme Open Food France a un coût, et nous comptons sur VOUS pour contribuer à couvrir ces frais de fonctionnement (location et maintenance des serveurs, support utilisateur, nouveaux développements...). Par exemple, en reversant sous forme de don à l'association 2% de votre chiffre d'affaire, et/ou un montant fixe tous les mois. Vous pouvez aussi contribuer au commun \"en compétences\" (développement de fonctionnalités, recherche de financement, support utilisateur, etc.)" + sell_embed: "Nous pouvons aussi intégrer votre boutique OFFrance dans votre propre site web ou construire un site web d'alimentation locale sur mesure pour votre région." + sell_ask_services: "Nous consulter sur les services des partenaires OFFrance." + shops_title: Boutiques + shops_headline: Des achats qui transforment. + shops_text: Les aliments poussent selon des cycles naturels, les fermiers récoltent en cycles. Alors ici, nous achetons aussi en cycles. Si un cycle de vente est terminé, attendez le suivant ou demandez des infos au hub ! + shops_signup_title: S'inscrire en tant que hub + shops_signup_headline: Des hubs divers et variés + shops_signup_motivation: Quel que soit votre modèle, vous pouvez vous appuyer sur Open Food France. Si vous voulez le faire évoluer, nous sommes là pour vous aider. Nous agissons selon des principes de non-lucrativité, d'indépendance, et de transparence. Et nous faisons tout notre possible pour répondre à vos besoins et vous accompagner en toute circonstance. + shops_signup_action: Rejoindre le réseau + shops_signup_pricing: Comptes entreprises + shops_signup_stories: Histoires de hubs. + shops_signup_help: Nous sommes là pour vous aider. + shops_signup_help_text: Vous avez besoin de pouvoir travailler de manière efficace. Vous avez besoin de nouveaux acheteurs et de partenaires logistiques. Vous souhaitez que votre histoire soit racontée tout au long du circuit, que l'acheteur final sache qui se trouve derrière les produits. + shops_signup_detail: Comment ça marche. + orders: Commandes + orders_fees: Frais... + orders_edit_title: Panier + orders_edit_headline: Votre panier + orders_edit_time: Commande prête pour + orders_edit_continue: Retour à la boutique + orders_edit_checkout: Etape suivante (coordonnées) + orders_form_empty_cart: "Vider le panier" + orders_form_subtotal: Sous-total + orders_form_admin: Admin & traitements + orders_form_total: Total + orders_oc_expired_headline: Les commandes ne sont plus possibles pour ce cycle de vente. + orders_oc_expired_text: "Désolé, les commandes pour ce cycle de vente ont été clôturées il y a %{time}! Veuillez contacter directement le hub pour voir s'il accepte les commandes tardives." + orders_oc_expired_text_others_html: "Désolé, les commandes pour ce cycle de vente ont été clôturées il y a %{time}! Veuillez contacter directement le hub pour voir s'il accepte les commandes tardives %{link}." + orders_oc_expired_text_link: "ou voir si d'autres cycles de vente sont ouverts pour ce hub" + orders_oc_expired_email: "Email:" + orders_oc_expired_phone: "Téléphone:" + orders_show_title: Confirmation de commande + orders_show_time: Commande prête pour + orders_show_order_number: "Commande #%{number}" + orders_show_cancelled: Annulée + orders_show_confirmed: Confirmée + orders_your_order_has_been_cancelled: "Votre commande a été annulée" + orders_could_not_cancel: "Désolé, la commande n'a pas pu être annulée" + orders_cannot_remove_the_final_item: "Impossible de supprimer le dernier produit d'une commande, si vous souhaitez supprimer l'ensemble des produits, veuillez annuler la commande." + orders_bought_items_notice: + one: "Un produit ajouté a bien été confirmé pour ce cycle de vente" + other: "%{count} produits ajoutés ont été confirmés pour ce cycle de vente." + orders_bought_edit_button: Modifier les produits confirmés + orders_bought_already_confirmed: "* déjà confirmé" + orders_confirm_cancel: Voulez-vous vraiment annuler cette commande ? + products_cart_distributor_choice: "Distributeur pour votre commande:" + products_cart_distributor_change: "Vore distributeur pour cette commande sera dorénavant %{name} si vous ajoutez ce produit à votre panier." + products_cart_distributor_is: "Votre distributeur pour cette commande est %{name}." + products_distributor_error: "Terminez votre commande chez %{link} avant de faire vos courses chez un autre distributeur." + products_oc: "Cycle de vente pour votre commande:" + products_oc_change: "Votre cycle de vente pour cette commande sera dorénavant %{name} si vous ajoutez ce produit à votre panier." + products_oc_is: "Votre cycle de vente pour cette commande est %{name}." + products_oc_error: "Veuillez terminer votre commande pour %{link} avant de faire vos courses pour un autre cycle de vente." + products_oc_current: "votre cycle de vente actuel" + products_max_quantity: Quantité max + products_distributor: Distributeur + products_distributor_info: Quand vous choisissez un distributeur pour votre commande, les adresse et date de retrait seront affichées ici. + products_distribution_adjustment_label: "Distribution par %{distributor}du produit %{product}" + shop_trial_expires_in: "Votre période de test se termine dans" + shop_trial_expired_notice: "Vous pouvez continuer à utiliser la plateforme en contrepartie d'une contribution libre et volontaire. Merci de nous informer de la façon dont vous souhaitez contribuer :-)" + password: Mot de passe + remember_me: Se souvenir de moi + are_you_sure: "Confirmer?" + orders_open: Boutique ouverte + closing: "Fermeture " + going_back_to_home_page: "Retour à la page d'accueil" + creating: Création + updating: Mettre à jour + failed_to_create_enterprise: "Impossible de créer votre entreprise" + failed_to_create_enterprise_unknown: "Impossible de créer votre entreprise.\nVérifiez que tous les champs sont remplis." + failed_to_update_enterprise_unknown: "Impossible de mettre à jour votre entreprise.\nVérifiez que tous les champs sont remplis." + enterprise_confirm_delete_message: "Cette action supprimera également le produit %{product} que cette entreprise distibue. Voulez-vous vraiment continuer ?" + order_not_saved_yet: "Votre commande n'a pas encore été enregistrée. Attendez quelques secondes!" + filter_by: "Filtrer par" + hide_filters: "Masquer les filtres" + one_filter_applied: "1 filtre appliqué" + x_filters_applied: "filtres appliqués" + submitting_order: "Votre commande est en cours d'envoi : veuillez patienter" + confirm_hub_change: "Confirmer? Cette action modifiera la boutique sélectionnée et tous les articles de votre panier seront effacés." + confirm_oc_change: "Confirmer? Cette action modifiera le cycle de vente sélectionné et tous les articles de votre panier seront effacés." + location_placeholder: "Saisissez une localisation..." + error_required: "Champ obligatoire" + error_number: "saisir un nombre" + error_email: "saisir une adresse email" + error_not_found_in_database: "%{name} n'existe pas" + error_not_primary_producer: "%{name}n'est pas enregistré comme \"producteur\"" + error_no_permission_for_enterprise: "\"%{name}\" : vous n'avez pas les droits requis pour gérer les produits de cette entreprise" + item_handling_fees: "Frais logistiques (inclus dans le prix affiché)" + january: "Janvier" + february: "Février" + march: "Mars" + april: "Avril" + may: "Mai" + june: "Juin" + july: "Juillet" + august: "Août" + september: "Septembre" + october: "Octobre" + november: "Novembre" + december: "Décembre" + email_not_found: "Adresse email non trouvée" + email_unconfirmed: "Vous devez confirmer votre adresse email avant de pouvoir réinitiatliser votre mot de passe." + email_required: "Vous devez saisir une adresse email" + logging_in: "Veuillez patienter, connexion en cours" + signup_email: "Votre email" + choose_password: "Choisissez un mot de passe" + confirm_password: "Confirmez votre mot de passe" + action_signup: "S'inscrire" + welcome_to_ofn: "Bienvenue sur Open Food France" + signup_or_login: "Commencez par vous inscrire (ou connexion)" + have_an_account: "Déjà inscrit?" + action_login: "Se connecter." + forgot_password: "Mot de passe oublié?" + password_reset_sent: "Un email contenant les instructions pour changer votre mot de passe a été envoyé!" + reset_password: "Changer de mot de passe" + who_is_managing_enterprise: "Qui gère %{enterprise}?" + update_and_recalculate_fees: "Mettre à jour et recalculer les frais" + registration: + steps: + type: + headline: "Dernière étape pour ajouter %{enterprise} !" + question: "Etes-vous un producteur ?" + yes_producer: "Oui, je suis un producteur" + no_producer: "Non, je ne suis pas un producteur" + producer_field_error: "Veuillez faire un choix. Etes vous un producteur?" + yes_producer_help: "Un producteur fabrique de bonnes choses à boire et à manger. Vous êtes un producteur si vous les faites pousser, les élevez, les pétrissez, transformez, fermentez, les réduisez en grains, etc." + no_producer_help: "Si vous n'êtes pas un producteur, vous êtes probablement un revendeur ou distributeur alimentaire : un \"hub\", une coopérative, un groupement d'achat, un revendeur, un grossiste, ou autre." + create_profile: "Créer votre profil" + enterprise: + registration: + modal: + steps: + details: + title: 'Détails' + headline: "Commençons !" + enterprise: "Hey ! Nous avons d'abord besoin de quelques informations sur votre entreprise :" + producer: "Hey ! Nous avons d'abord besoin de quelques informations sur votre ferme :" + enterprise_name_field: "Nom de l'entreprise :" + producer_name_field: "Nom de la ferme :" + producer_name_field_placeholder: "ex: La Ferme du Marais" + producer_name_field_error: "Veuillez choisir le nom de votre entreprise" + address1_field: "Adresse ligne 1" + address1_field_placeholder: "ex : 35 rue du bac" + address1_field_error: "Veuillez saisir une adresse" + address2_field: "Adresse ligne 2" + suburb_field: "Ville :" + suburb_field_placeholder: "ex : Nantes" + suburb_field_error: "Veuillez saisir une ville" + postcode_field: "Code postal :" + postcode_field_placeholder: "ex : 44000" + postcode_field_error: "Veuillez saisir le code postal" + state_field: "Département :" + state_field_error: "Veuillez saisir un Département" + country_field: "Pays :" + country_field_error: "Veuillez saisir une Pays" + contact: + title: 'Contact' + contact_field: 'Personne référente' + contact_field_placeholder: 'Nom du contact principal' + contact_field_required: "Vous devez saisir une personne référente" + email_field: 'Adresse email' + email_field_placeholder: 'ex : robert@mabelleferme.fr' + phone_field: 'Numéro de téléphone' + phone_field_placeholder: 'ex : 06 24 53 26 53' + type: + title: 'Catégorie' + about: + title: 'A propos' + images: + title: 'Images' + social: + title: 'Réseaux sociaux' + enterprise_contact: "Personne référente" + enterprise_contact_placeholder: "Nom du contact principal" + enterprise_contact_required: "Vous devez saisir une personne référente" + enterprise_email_address: "Adresse email" + enterprise_email_placeholder: "ex : robert@mabelleferme.fr" + enterprise_phone: "Numéro de téléphone" + enterprise_phone_placeholder: "ex : 06 24 53 26 53" + back: "Retour" + continue: "Suivant" + limit_reached_headline: "Oh non!" + limit_reached_message: "Vous avez atteint la limite!" + limit_reached_text: "Vous avez atteint la limite du nombre d'entreprises que vous êtes autorisés à gérer sur" + limit_reached_action: "Retour sur la page d'accueil" + select_promo_image: "Etape 3. Sélectionnez une image promotionnelle" + promo_image_tip: "Conseil: affichée en format bannière, taille optimale 1200×260px" + promo_image_label: "Choisissez une image promotionnelle" + action_or: "OU" + promo_image_drag: "Glissez déplacez votre image promotionnelle ici" + review_promo_image: "Etape 4. Validez votre bannière promotionnelle" + review_promo_image_tip: "Conseil: pour un résultat optimal, votre image promotionnelle doit être adaptée à l'espace disponible" + promo_image_placeholder: "Votre logo apparaîtra ici pour validation une fois uploadé" + uploading: "Upload en cours..." + select_logo: "Etape 1. Insérez votre logo" + logo_tip: "Conseil: utilisez un format d'image carré de préférence, min 300×300px" + logo_label: "Insérez votre logo" + logo_drag: "Glissez déplacez votre logo ici" + review_logo: "Etape 2: Validez votre logo" + review_logo_tip: "Conseil: pour un résultat optimal, votre logo doit être adapté à l'espace disponible" + logo_placeholder: "Votre logo apparaîtra ici pour validation une fois uploadé" + enterprise_about_headline: "Bien joué!" + enterprise_about_message: "A présent, allons un peu plus dans les détails concernant" + enterprise_success: "Opération réussie ! %{enterprise} a été ajoutée à Open Food France" + enterprise_registration_exit_message: "Si vous quittez ce module, vous pourrez continuer la création de votre profil via l'interface d'administration." + enterprise_description: "Description courte" + enterprise_description_placeholder: "Une phrase pour décrire votre organisation" + enterprise_long_desc: "Description longue" + enterprise_long_desc_placeholder: "Vous pouvez ici raconter l'histoire de votre organisation - votre projet, les valeurs que vous défendez. Nous vous conseillons de ne pas dépasser 600 caractères ou 150 mots." + enterprise_long_desc_length: "%{num} caractères / inférieur à 600 recommandé" + enterprise_limit: Nombre max d'entreprises + enterprise_abn: "SIRET" + enterprise_abn_placeholder: "ex: 404 833 048 00022" + enterprise_acn: "n° TVA intracommunautaire" + enterprise_acn_placeholder: "ex: 404 833 048" + enterprise_tax_required: "Merci de choisir." + enterprise_final_step: "Dernière étape!" + enterprise_social_text: "Comment trouver la boutique en ligne %{enterprise} ?" + website: "Site internet" + website_placeholder: "ex: openfoodfrance.fr" + facebook: "Facebook" + facebook_placeholder: "ex: www.facebook.com/NomDeLaPage" + linkedin: "LinkedIn" + linkedin_placeholder: "ex: www.linkedin.com/VotreNom" + twitter: "Twitter" + twitter_placeholder: "ex: @twitter_pseudo" + instagram: "Instagram" + instagram_placeholder: "ex: @instagram_pseudo" + registration_greeting: "Bonjour!" + registration_intro: "Vous pouvez maintenant créer votre profil \"Producteur\" ou \"Hub\"" + registration_action: "Démarrons!" + registration_checklist: "Vous aurez besoin de" + registration_time: "5-10 minutes" + registration_enterprise_address: "L'adresse de l'entreprise" + registration_contact_details: "Les détails du contact référent" + registration_logo: "Votre logo" + registration_promo_image: "Une image bannière pour votre profil" + registration_about_us: "Un texte \"A propos\"" + registration_outcome_headline: "Qu'est-ce que ça m'apporte?" + registration_outcome1_html: "Votre profil permet aux gens de vous trouver et de vous contacter via Open Food France." + registration_outcome2: "Utilisez cet espace pour raconter l'histoire de votre entreprise, et stimuler les visites vers vos points de présence en ligne." + registration_outcome3: "C'est aussi le premier pas vers la vente via Open Food France, ou l'ouverture de votre boutique en ligne." + registration_finished_headline: "C'est terminé!" + registration_finished_thanks: "Merci d'avoir complété le profil de %{enterprise}" + registration_finished_login: "Vous pouvez modifier ou mettre à jour les détails de votre entreprise à tout moment en vous connectant sur Open Food France, rubrique Admin." + registration_finished_action: "Accueil Open Food France" + registration_contact_name: 'Nom du contact principal' + registration_type_headline: "Dernière étape pour ajouter %{enterprise}!" + registration_type_question: "Etes-vous un producteur?" + registration_type_producer: "Oui, je suis un producteur" + registration_type_no_producer: "Non, je ne suis pas un producteur" + registration_type_error: "Veuillez faire un choix. Etes vous un producteur?" + registration_type_producer_help: "Un producteur fabrique de bonnes choses à boire et à manger. Vous êtes un producteur si vous les faites pousser, les élevez, les pétrissez, transformez, fermentez, les réduisez en grains, etc." + registration_type_no_producer_help: "Si vous n'êtes pas un producteur, vous êtes probablement un revendeur ou distributeur alimentaire: un \"hub\", une coopérative, un groupement d'achat, un revendeur, un grossiste, ou autre." + registration_images_headline: "Merci!" + registration_images_description: "Ajoutez maintenant de jolies photos pour que votre profil soit attractif! :)" + registration_detail_headline: "Commençons" + registration_detail_enterprise: "Woohoo! Dites-nous déjà quelques mots à propos de votre entreprise:" + registration_detail_producer: "Woohoo! Dites-nous déjà quelques mots à propos de votre ferme:" + registration_detail_name_enterprise: "Nom de l'entreprise:" + registration_detail_name_producer: "Nom de la ferme:" + registration_detail_name_placeholder: "ex: La super ferme de Charlie" + registration_detail_name_error: "Veuillez choisir le nom de votre entreprise" + registration_detail_address1: "Adresse ligne 1" + registration_detail_address1_placeholder: "ex: 123 rue des étangs" + registration_detail_address1_error: "Veuillez saisir une adresse" + registration_detail_address2: "Adresse ligne 2" + registration_detail_suburb: "Ville:" + registration_detail_suburb_placeholder: "ex: Vendée" + registration_detail_suburb_error: "Veuillez saisir une ville" + registration_detail_postcode: "Code postal:" + registration_detail_postcode_placeholder: "ex: 44000" + registration_detail_postcode_error: "Veuillez saisir le code postal" + registration_detail_state: "Département:" + registration_detail_state_error: "Veuillez saisir un Département" + registration_detail_country: "Pays:" + registration_detail_country_error: "Veuillez saisir une Pays" + shipping_method_destroy_error: "Cette méthode de livraison ne peut pas être supprimée car elle est référencée dans une commande : %{number}." + accounts_and_billing_task_already_running_error: "Une autre tache est en cours, merci de patienter un instant..." + accounts_and_billing_start_task_notice: "Tache mise en file d'attente" + fees: "Frais" + item_cost: "Coût du produit" + bulk: "Vrac" + shop_variant_quantity_min: "min" + shop_variant_quantity_max: "max" + follow: "Suivre" + shop_for_products_html: "Acheter les produits de %{enterprise} dans les boutiques suivantes:" + change_shop: "Changer de boutique pour:" + shop_at: "Acheter maintenant :" + price_breakdown: "Détail du prix:" + admin_fee: "Frais de gestion admin" + sales_fee: "Frais de ventes/marketing" + packing_fee: "Frais de packaging" + transport_fee: "Frais logistiques" + fundraising_fee: "Frais recherche de financement" + price_graph: "Légende détail du prix" + included_tax: "Inclut TVA" + balance: "Solde" + transaction: "Transaction" + transaction_date: "Date" + payment_state: "Statut du paiement" + shipping_state: "Statut de la livraison" + value: "Nb unités" + balance_due: "Montant dû" + credit: "Crédit" + Paid: "Payé" + Ready: "Prêt" + ok: OK + not_visible: invisible + you_have_no_orders_yet: "Vous n'avez pas encore de commande" + running_balance: "Solde courant" + outstanding_balance: "Solde restant" + admin_enterprise_relationships: "Permissions Inter-entreprises" + admin_enterprise_relationships_everything: "Tout" + admin_enterprise_relationships_permits: "autorise" + admin_enterprise_relationships_seach_placeholder: "Rechercher" + admin_enterprise_relationships_button_create: "Créer" + admin_enterprise_groups: "Groupes d'entreprises" + admin_enterprise_groups_name: "Produit/Variante" + admin_enterprise_groups_owner: "Manager principal" + admin_enterprise_groups_on_front_page: "Sur la page d'accueil?" + admin_enterprise_groups_enterprise: "Entreprises" + admin_enterprise_groups_data_powertip: "Le manager principal en charge de ce groupe." + admin_enterprise_groups_data_powertip_logo: "Il s'agit du logo du groupe" + admin_enterprise_groups_data_powertip_promo_image: "Cette image est affichée en haut du profil Groupe." + admin_enterprise_groups_contact: "Contact" + admin_enterprise_groups_contact_phone_placeholder: "ex: 06 13 24 35 46" + admin_enterprise_groups_contact_address1_placeholder: "ex: 24 rue de la croix verte" + admin_enterprise_groups_contact_city: "Ville" + admin_enterprise_groups_contact_city_placeholder: "ex: Nantes" + admin_enterprise_groups_contact_zipcode: "Code postal" + admin_enterprise_groups_contact_zipcode_placeholder: "ex: 44000" + admin_enterprise_groups_contact_state_id: "Département" + admin_enterprise_groups_contact_country_id: "Pays" + admin_enterprise_groups_web: "Liens web" + admin_enterprise_groups_web_twitter: "ex: @OpenFoodNet_fr" + admin_enterprise_groups_web_website_placeholder: "ex: www.maferme.fr" + admin_order_cycles: "Gérer les cycles de vente" + open: "Ouvre" + close: "Ferme" + create: "Créer" + search: "Rechercher" + supplier: "Fournisseurs" + product_name: "Nom du Produit" + product_description: "Description du Produit" + units: "Unité de mesure" + coordinator: "Coordinateur" + distributor: "Distributeur" + enterprise_fees: "Marges et commissions" + process_my_order: "Valider ma Commande" + delivery_instructions: Instructions de Livraison + delivery_method: Méthode de Livraison + fee_type: "Type de marge" + tax_category: "TVA applicable" + calculator: "Calculateur" + calculator_values: "Valeurs applicables" + flat_percent_per_item: "Pourcentage net" + flat_rate_per_item: "Montant fixe par article (hors articles au poids/volume)" + flat_rate_per_order: "Montant fixe par commande" + flexible_rate: "Montant variable selon nb articles" + price_sack: "Montant variable selon total commande" + new_order_cycles: "Nouveau cycle de vente" + new_order_cycle: "Nouveau Cycle de Vente" + select_a_coordinator_for_your_order_cycle: "Choisissez un coordinateur pour votre cycle de vente" + notify_producers: 'Notifier les producteurs' + edit_order_cycle: "Modifier le cycle de vente" + roles: "Roles" + update: "Mettre à jour" + delete: Supprimer + add_producer_property: "Ajouter une propriété" + in_progress: "En cours" + started_at: "Commencé à" + queued: "En attente" + scheduled_for: "Prévu pour" + customers: "Acheteurs" + please_select_hub: "Veuillez sélectionner un Hub" + loading_customers: "Chargement de la liste des acheteurs" + no_customers_found: "Aucun acheteur trouvé" + go: "Lancer" + hub: "Hub" + producer: "Producteur" + product: "Produit" + price: "Prix" + on_hand: "En stock" + save_changes: "Sauvegarder les modifications" + order_saved: "Commande Sauvegardée" + no_products: Pas de Produits + spree_admin_overview_enterprises_header: "Mes entreprises" + spree_admin_overview_enterprises_footer: "GÉRER MES ENTREPRISES" + spree_admin_enterprises_hubs_name: "Nom" + spree_admin_enterprises_create_new: "CRÉER NOUVELLE" + spree_admin_enterprises_shipping_methods: "Méthodes de livraison" + spree_admin_enterprises_fees: "Marges et commissions" + spree_admin_enterprises_none_create_a_new_enterprise: "CRÉER UNE NOUVELLE ENTREPRISE" + spree_admin_enterprises_none_text: "Vous n'avez pas encore d'entreprise" + spree_admin_enterprises_tabs_hubs: "HUBS" + spree_admin_enterprises_producers_manage_products: "GÉRER LES PRODUITS" + spree_admin_enterprises_any_active_products_text: "Vous n'avez aucun produit actif." + spree_admin_enterprises_create_new_product: "CRÉER UN NOUVEAU PRODUIT" + spree_admin_single_enterprise_alert_mail_confirmation: "Veuillez confirmer l'adresse mail pour" + spree_admin_single_enterprise_alert_mail_sent: "Email envoyé à " + spree_admin_overview_action_required: "Action requise" + spree_admin_overview_check_your_inbox: "Veuillez vérifier votre boîte mail pour les prochaines étapes. Merci!" + spree_admin_unit_value: Nb Unités + spree_admin_unit_description: 'Description complémentaire (ex: "(vrac)")' + spree_admin_variant_unit: Unité + spree_admin_variant_unit_scale: Echelle unitaire (en g ou L) + spree_admin_supplier: Fournisseur + spree_admin_product_category: Catégorie Produit + spree_admin_variant_unit_name: Nom de la pièce (si vendu à la pièce) + change_package: "Changer de type de compte" + spree_admin_single_enterprise_hint: "Astuce: Pour permettre aux gens de vous trouver, activez votre visibilité " + spree_admin_eg_pickup_from_school: "ex : \"Retrait des produits à l'Ecole Marimati / Au Café du coin / chez Babette / ...\"" + spree_admin_eg_collect_your_order: "ex : \"Veuillez récupérer votre commande au 34 rue Victor Hugo, 75018 Paris\"" + spree_classification_primary_taxon_error: "La catégorie %{taxon}est utilisée par %{product} et ne peut être supprimée" + 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_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 )." + spree_variant_product_error: doit avoir au moins une variante + your_profil_live: "Votre profil en ligne" + on_ofn_map: "sur la carte Open Food France" + see: "Voir" + live: "en ligne" + manage: "Gérer" + resend: "Renvoyer" + trial: Découverte + add_and_manage_products: "Ajouter & gérer des produits" + add_and_manage_order_cycles: "Ajouter & gérer des cycles de vente" + manage_order_cycles: "Gérer les cycles de vente" + manage_products: "Gérer les produits" + edit_profile_details: "Modifier les informations du profil" + edit_profile_details_etc: "Modifier la description, les images, etc." + order_cycle: "Cycle de vente" + order_cycles: "Cycles de Vente" + enterprise_relationships: "Permissions inter-entreprises" + remove_tax: "Retirer TVA" + enterprise_tos_link: "Lien vers les Conditions Générales d'Utilisation" + enterprise_tos_message: "Nous soutenons la mise en place d'un système alimentaire résilient et durable, et souhaitons œuvrer avec des entreprises qui partagent nos valeurs et notre vision. Ainsi, nous demandons aux entreprises s'enregistrant sur Open Food France de valider nos " + enterprise_tos_link_text: "Conditions d'utilisation" + enterprise_tos_agree: "J'adhère aux valeurs d'Open Food France et valide les Conditions Générales d'Utilisation." + tax_settings: "Paramètres TVA" + products_require_tax_category: "vous devez choisir la TVA applicable" + admin_shared_address_1: "Adresse" + admin_shared_address_2: "Adresse (suite)" + admin_share_city: "Ville" + admin_share_zipcode: "Code postal" + admin_share_country: "Pays" + admin_share_state: "Département" + hub_sidebar_hubs: "Hubs" + hub_sidebar_none_available: "Aucun disponible" + hub_sidebar_manage: "Gérer" + hub_sidebar_at_least: "Sélectionnez un/des hubs" + hub_sidebar_blue: "bleu" + hub_sidebar_red: "rouge" + shop_trial_in_progress: "Votre période de test se termine dans %{days}." + report_customers_distributor: "Distributeur" + report_customers_supplier: "Fournisseurs" + report_customers_cycle: "Cycle de vente" + report_customers_type: "Type de rapport" + report_customers_csv: "Télécharger en csv" + report_producers: "Producteurs:" + report_type: "Type de rapport: " + report_hubs: "Hubs:" + report_payment: "Méthodes de paiement:" + report_distributor: "Distributeurs:" + report_payment_by: 'Paiements par type' + report_itemised_payment: 'Détail du paiement' + report_payment_totals: 'Total des paiements' + report_all: 'tous' + report_order_cycle: "Cycle de vente:" + report_enterprises: "Entreprises:" + report_users: "Managers:" + report_tax_rates: TVA par taux + report_tax_types: TVA par type de produit/service + report_header_order_cycle: Cycle de Vente + report_header_user: Utilisateur + report_header_email: Email + report_header_status: Statut + report_header_comments: Commentaire + report_header_first_name: Prénom + report_header_last_name: Nom + report_header_phone: n° tel + report_header_suburb: Ville + report_header_address: Adresse + report_header_billing_address: Adresse de facturation + report_header_relationship: Droits + report_header_hub: Hub + report_header_hub_address: Adresse du Hub + report_header_to_hub: Distributeur + report_header_hub_code: Code du Hub + report_header_code: Code + report_header_paid: Payé ? + report_header_delivery: Livré ? + report_header_shipping: Livraison + report_header_shipping_method: Méthode de Livraison + report_header_shipping_instructions: Instructions de Livraison + report_header_ship_street: Rue Livraison + report_header_ship_street_2: ' Rue (2) Livraison' + report_header_ship_city: Ville Livraison + report_header_ship_postcode: Code Postal Livraison + report_header_ship_state: Département Livraison + report_header_billing_street: Rue Facturation + report_header_billing_street_2: Rue (2) Facturation + report_header_billing_street_3: Rue (3) Facturation + report_header_billing_street_4: Rue (4) Facturation + report_header_billing_city: Ville Facturation + report_header_billing_postcode: Code Postal Facturation + report_header_billing_state: Département Facturation + report_header_incoming_transport: Transport réception + report_header_special_instructions: Note au producteur + report_header_order_number: N° commande + report_header_date: Date + report_header_confirmation_date: Date de confirmation + report_header_tags: Tags + report_header_items: Produits + report_header_items_total: "Montant des produits %{currency_symbol}" + report_header_taxable_items_total: "Montant produits soumis à TVA (%{currency_symbol})" + report_header_sales_tax: "TVA sur produits (%{currency_symbol})" + report_header_delivery_charge: "Frais de livraison (%{currency_symbol})" + report_header_tax_on_delivery: "TVA sur livraison (%{currency_symbol})" + report_header_tax_on_fees: "TVA sur commission hub (%{currency_symbol})" + report_header_total_tax: "Total TVA (%{currency_symbol})" + report_header_enterprise: Entreprise + report_header_customer: Client + report_header_customer_code: Code acheteur + report_header_product: Produit + report_header_product_properties: Propriétés / labels Produits + report_header_quantity: Nb commandé + report_header_max_quantity: Quantité Max + report_header_variant: Variante + report_header_variant_value: Nb Unités Variante + report_header_variant_unit: Unité + report_header_total_available: Total disponible + report_header_unallocated: Non alloué + report_header_max_quantity_excess: Dépassement Qté Max + report_header_taxons: Catégorie + report_header_supplier: Fournisseur + report_header_producer: Producteur + report_header_producer_suburb: Ville Producteur + report_header_unit: Unité + report_header_group_buy_unit_quantity: Nb d'unités achetées (vente par lots) + report_header_cost: Coût + report_header_shipping_cost: Coût de livraison + report_header_curr_cost_per_unit: Prix prod unitaire + report_header_total_shipping_cost: Prix prod total + report_header_payment_method: Méthode de paiement + report_header_sells: Vend + report_header_visible: Visible + report_header_price: Prix + report_header_unit_size: Unité de mesure + report_header_distributor: Distributeur + report_header_distributor_address: Adresse Hub Distributeur + report_header_distributor_city: Ville Distributeur + report_header_distributor_postcode: Code Postal Distributeur + report_header_delivery_address: Adresse Livraison + report_header_delivery_postcode: Code Postal Livraison + report_header_bulk_unit_size: Quantité totale du lot + report_header_weight: Poids + report_header_sum_total: Somme Totale + report_header_date_of_order: Date de Commande + report_header_amount_owing: Montant dû + report_header_amount_paid: Montant payé + report_header_units_required: Nb Unités Requises + report_header_remainder: Reste à payer + report_header_order_date: Date de commande + report_header_order_id: N° Commande + report_header_item_name: Nom de la pièce + report_header_temp_controlled_items: Article à température contrôlée ? + report_header_customer_name: Nom Acheteur + report_header_customer_email: E-mail Acheteur + report_header_customer_phone: Tel Acheteur + report_header_customer_city: Ville Acheteur + report_header_payment_state: Statut du Paiement + report_header_payment_type: Type de Paiement + report_header_item_price: "Coût produits (%{currency})" + report_header_item_fees_price: "Coût produits + Marge (%{currency})" + report_header_admin_handling_fees: "Frais si par commande (%{currency})" + report_header_ship_price: "Frais de livraison (%{currency})" + report_header_pay_fee_price: "Frais de Transaction (%{currency})" + report_header_total_price: "Total (%{currency})" + report_header_product_total_price: "Total Produit (%{currency})" + report_header_shipping_total_price: "Total Livaison (%{currency})" + report_header_outstanding_balance_price: "Solde (%{currency})" + report_header_eft_price: "TEF / Transfert Electronique (%{currency})" + report_header_paypal_price: "Paypal (%{currency})" + report_header_sku: Référence Produit + report_header_amount: Quantité + report_header_balance: Solde + report_header_total_cost: "Coût Total" + report_header_total_ordered: Total Commandé + report_header_total_max: Max Total + report_header_total_units: Vol. total + report_header_sum_max_total: "Somme Max Total" + report_header_total_excl_vat: "Total HT (%{currency_symbol})" + report_header_total_incl_vat: "Total TTC (%{currency_symbol})" + report_header_temp_controlled: Temp Contrôlée ? + report_header_is_producer: Producteur ? + report_header_not_confirmed: Non confirmé + report_header_gst_on_income: TVA + report_header_gst_free_income: Revenu TVA déduite + report_header_total_untaxable_produce: Total produits non taxable + report_header_total_taxable_produce: Total produits soumis à TVA (inclut TVA) + report_header_total_untaxable_fees: Total marges et frais annexes non taxables + report_header_total_taxable_fees: Total marges et frais annexes soumis à TVA (inclut TVA) + report_header_delivery_shipping_cost: Coût de Livraison (incl. TVA) + report_header_transaction_fee: Frais de Transaction (TVA non incluse) + report_header_total_untaxable_admin: Total ajustements non taxables + report_header_total_taxable_admin: Total ajustments soumis à TVA (inclut TVA) + initial_invoice_number: "N° de facture initial:" + invoice_date: "Date de facture:" + due_date: "Date d'échéance:" + account_code: "Code compte:" + equals: "Egal" + contains: "contient" + discount: "Réduction" + filter_products: "Filtrer les produits" + delete_product_variant: "La variante ne peut pas être supprimée!" + progress: "en cours" + saving: "Enregistrement..." + success: "succès" + failure: "échec" + unsaved_changes_confirmation: "Les changements non sauvegardés seront perdus. Continuer?" + one_product_unsaved: "Des changements sur un produit n'ont pas été sauvegardés." + products_unsaved: "Des changements sur %{n} produits n'ont pas été sauvegardés." + is_already_manager: "est déjà manager!" + no_change_to_save: "Pas de changement à sauvegarder" + user_invited: "%{email}a été invité à gérer cette entreprise" + add_manager: "Ajouter un utilisateur existant" + users: "Managers" + about: "A propos" + images: "Images" + web: "Web" + primary_details: "Informations de base" + adrdress: "Adresse" + contact: "Contact" + social: "Réseaux sociaux" + business_details: "Juridique" + properties: "Propriétés / labels" + shipping: "Expédition" + shipping_methods: "Méthodes de livraison" + payment_methods: "Méthodes de paiement" + payment_method_fee: "Frais de transaction" + inventory_settings: "Catalogue boutique" + tag_rules: "Règles de tag" + shop_preferences: "Préférences boutique" + enterprise_fee_whole_order: Commande totale + enterprise_fee_by: "%{type}marges/frais par %{role} %{enterprise_name}" + validation_msg_relationship_already_established: "^Un lien est déjà établi entre ces entreprises." + validation_msg_at_least_one_hub: "^Sélectionnez au moins un hub" + validation_msg_product_category_cant_be_blank: "^Veuillez sélectionner la catégorie produit" + validation_msg_tax_category_cant_be_blank: "^Veuillez sélectionner la TVA applicable" + validation_msg_is_associated_with_an_exising_customer: "est associé à un acheteur existant" + content_configuration_pricing_table: "(A FAIRE : Tableau des tarifs)" + content_configuration_case_studies: "(A FAIRE : Etudes de Cas)" + content_configuration_detail: "(A FAIRE : Détails)" + enterprise_name_error: "est déjà utilisé. Si vous êtes le gérant de cette entreprise et que vous souhaitez demander le transfert du compte, ou bien si vous souhaitez distribuer les produits de cette entreprise, merci de contacter le manager actuel du profil à %{email}." + enterprise_owner_error: "^ %{email} ne peut pas créer de nouvelles entreprises (limite actuelle : %{enterprise_limit} entreprises )." + enterprise_role_uniqueness_error: "^Ce rôle existe déjà." + inventory_item_visibility_error: doit être vrai ou faux + product_importer_file_error: "erreur : aucun document importé" + product_importer_spreadsheet_error: "impossible de traiter le fichier : type de fichier invalide" + product_importer_products_save_error: n'a pu sauvegarder aucun produit :-( + product_import_file_not_found_notice: 'Fichier non trouvé ou impossible à ouvrir' + product_import_no_data_in_spreadsheet_notice: 'Aucune donnée trouvée dans le tableau' + order_choosing_hub_notice: Votre hub a été sélectionné. + order_cycle_selecting_notice: Votre cycle de vente a été sélectionné. + adjustments_tax_rate_error: "^Veuillez vérifier la TVA applicable pour cet ajustement." + active_distributors_not_ready_for_checkout_message_singular: >- + Le hub %{distributor_names} est sélectionné comme distributeur dans un cycle + de vente actif, mais n'a pas paramétré de méthode de livraison et/ou de paiement. + La boutique de ce hub restera inaccessible jusqu'à ce qu'une méthode de livraison + et une méthode de paiement aient été paramétrées. + active_distributors_not_ready_for_checkout_message_plural: >- + Les hubs %{distributor_names} sont sélectionnés comme distributeurs dans un + cycle de vente actif, mais n'ont pas paramétré de méthode de livraison et/ou + de paiement. Les boutiques de ces hubs resteront inaccessibles jusqu'à ce qu'une + méthode de livraison et une méthode de paiement aient été paramétrées. + enterprise_fees_update_notice: Les marges et commissions de votre entreprise ont été mises à jour. + enterprise_fees_destroy_error: "Cette marge ou commission ne peut être supprimée car elle est utilisée par la vente suivante : %{id} - %{name}." + enterprise_register_package_error: "Veuillez choisir une option" + enterprise_register_error: "L'inscription a échoué pour %{enterprise}" + enterprise_register_success_notice: "Bravo ! L'entreprise %{enterprise} est maintenant inscrite sur Open Food France :-)" + enterprise_bulk_update_success_notice: "Entreprises mises à jour avec succès" + enterprise_bulk_update_error: 'Echec dans la mise à jour' + order_cycles_create_notice: 'Votre cycle de vente a été créé.' + order_cycles_update_notice: 'Votre cycle de vente a été mis à jour.' + order_cycles_bulk_update_notice: 'Des cycles de vente ont été mis à jour.' + order_cycles_clone_notice: "Votre cycle de vente %{name} a été dupliqué." + order_cycles_email_to_producers_notice: 'Les emails à destination des producteurs ont été mis en file d''attente.' + order_cycles_no_permission_to_coordinate_error: "Aucune de vos entreprises n'a les droits requis pour coordonner un cycle de vente" + order_cycles_no_permission_to_create_error: "Vous n'avez pas les droits requis pour créer un cycle de vente coordonné par cette entreprise" + back_to_orders_list: "Retour à la liste des commandes" + no_orders_found: "Aucune commande trouvée pour ces critères" + order_information: "Info commande" + date_completed: "Date d'opération" + amount: "Montant" + state_names: + ready: Prêt + pending: En attente + shipped: Expédié + js: + saving: 'Enregistrement en cours...' + changes_saved: 'Modifications sauvegardées.' + 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 + all_changes_saved_successfully: Toutes les modifications ont été sauvegardées avec succès + oh_no: "Oups ! Nous n'avons pas réussi à sauvegarder vos modification :-(" + unauthorized: "Vous n'avez pas les droits d'accès à cette page." + error: Erreur + unavailable: Non disponible + profile: Profil + hub: Hub + shop: Boutique + choose: Choisir + 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 + admin: + enterprise_limit_reached: "Vous avez atteint le nombre limite d'entreprises autorisées par défaut. Ecrivez à %{contact_email}si vous avez besoin d'augmenter cette limite." + modals: + got_it: J'ai compris + close: "Fermer" + invite: "Inviter" + invite_title: "Inviter un nouvel utilisateur" + tag_rule_help: + title: Règles de tag + overview: Aperçu + overview_text: > + Les règles de tag vous permettent de paramétrer ce qui est vu ou pas + par tel ou tel type d'acheteur. Par exemple des options de livraison, + des méthodes de paiement, des produits, ou des cycles de vente. + by_default_rules: "Règles à appliquer \"par défaut\"" + by_default_rules_text: > + Les règles de tag par défaut vous permettent de masquer des éléments + par défaut. Vous pouvez ensuite permettre à certains acheteurs, selon + les tags attribués, de voir ces éléments. + customer_tagged_rules: "Règles pour les acheteur avec un tag" + customer_tagged_rules_text: > + En créant une règle spécifique à un tag, vous pouvez modifier le contenu + vu par défaut (afficher ou masquer) par les acheteurs associés à ce + tag. + panels: + save: Enregistrer + saved: Enregistré + saving: En cours d'enregistrement + enterprise_package: + hub_profile: Profil Hub + hub_profile_cost: "COÛT: CONTRIBUTION LIBRE" + hub_profile_text1: > + Les visiteurs voient votre profil sur la carte, et peuvent vous contacter. + Vous augmentez ainsi votre visibilité. + hub_profile_text2: > + Créez votre profil et utilisez Open Food France pour vous connecter + à votre système alimentaire territorial. + hub_shop: Boutique Hub + hub_shop_text1: > + Vous proposez des produits de différents producteurs de votre région, + artisans, ou distributeurs afin de proposer une offre complète dans + votre boutique. Vous soutenez ainsi le développement de votre système + alimentaire territorial ! + hub_shop_text2: > + Un hub n'a pas de modèle figé, il peut s'agir d'un groupement d'achat, + d'une AMAP, d'une épicerie coopérative, d'une épicerie locale de quartier + ou épicerie en circuit court en ligne, etc. + hub_shop_text3: > + Si vous produisez et voulez également vendre vos propres produits, vous + devez modifier le statut de votre entreprise, elle doit apparaitre en + tant que "producteur". + choose_package: Choisir le type de compte souhaité + choose_package_text1: > + Votre entreprise ne sera activée et visible que lorsque vous aurez choisi + le type de compte souhaité parmi les options à gauche. + choose_package_text2: > + Cliquez sur une option pour voir le détail du compte proposé, puis une + fois votre choix fait, cliquez sur le bouton rouge ENREGISTRER ! + profile_only: Profil uniquement + profile_only_cost: "COÛT: CONTRIBUTION LIBRE" + profile_only_text1: > + Gagnez en visibilité, racontez votre histoire, et affichez vos coordonnées + pour pouvoir être contactés. + profile_only_text2: > + Si vous souhaitez vous concentrer sur votre activité de production, + et laisser à d'autre le soin de distribuer vos produits, vous n'avez + pas besoin d'une boutique sur Open Food France. + profile_only_text3: > + Saisissez votre catalogue produits sur Open Food France, ce qui permettra + aux hubs-distributeurs utilisant la plateforme de les proposer dans + leurs boutiques (sur votre autorisation). + producer_shop: Boutique Producteur + producer_shop_text1: > + Vendez vos produits en direct aux mangeurs/restaurateurs/etc. via votre + propre Boutique Producteur sur Open Food France. + producer_shop_text2: > + Une Boutique Producteur vous permet de vendre uniquement vos propres + produits. Si vous voulez vendre d'autres produits, sélectionnez "Hub + Producteur" + producer_hub: Hub Producteur + producer_hub_text1: > + Vous vendez non seulement vos produits, mais aussi des produits d'autres + producteurs de votre région, artisans, ou distributeurs afin de proposer + une offre complète dans votre boutique. Vous soutenez ainsi le développement + de votre système alimentaire territorial ! + producer_hub_text2: > + Un hub producteur peut prendre différentes formes, une boutique de vente + directe, un magasin de producteurs en ligne, un drive fermier, etc. + producer_hub_text3: > + Open Food France soutient tous les modèles de hubs alimentaires, nous + pensons que la résilience du système viendra de la diversité des modèles. + Donc quel que soit votre modèle, nous souhaitons vous apporter les outils + de gestion donc vous avez besoin pour opérer votre circuit court. + get_listing: Référencez votre entreprise + always_free: CONTRIBUTION LIBRE + sell_produce_others: Vendez des produits de multiples fournisseurs différents + sell_own_produce: Vendez vos propres produits + sell_both: Vendez vos produits et ceux d'autres fournisseurs + enterprise_producer: + producer: Producteur + producer_text1: > + Un producteur fabrique de bonnes choses à boire et à manger. Vous êtes + un producteur si vous les faites pousser, les élevez, les pétrissez, + transformez, fermentez, les réduisez en grains, etc. + producer_text2: > + Un producteur peut aussi avoir d'autres rôles, comme par exemple stocker + et distribuer des produits d'autres producteurs à travers une boutique + sur Open Food France. + non_producer: Non-producteur + non_producer_text1: > + Les entreprises qui ne produisent pas ne peuvent pas créer leur propre + catalogue produits pour les vendre sur Open Food France. + non_producer_text2: > + Ces entreprises vont plutôt faire le lien entre des producteurs et des + mangeurs/restaurateurs, en proposant un modèle opérationnel pour agréger, + préparer les commandes, ou encore livrer les produits. + producer_desc: Producteurs / transformateurs + producer_example: 'ex: maraichers, boulangers, brasseurs, artisans' + non_producer_desc: Autres entreprises de distribution alimentaire + non_producer_example: 'ex: épiceries, coopératives, groupements d''achats' + enterprise_status: + status_title: "%{name} est en place et prêt à démarrer!" + severity: Rigueur + description: Description + resolve: Résoudre + new_tag_rule_dialog: + select_rule_type: "Choisir le type de règle:" + orders: + index: + per_page: "%{results} par page" + resend_user_email_confirmation: + resend: "Renvoyer" + sending: "Renvoi...." + done: "Renvoi effectué ✓" + failed: "Renvoi échoué ✗" + out_of_stock: + reduced_stock_available: Stock disponible + out_of_stock_text: > + Pendant que vous faisiez vos achats, le niveau de stock disponible pour + un ou plusieurs produits dans votre panier est devenu insuffisant pour répondre + à votre demande. Voilà les modifications opérées: + now_out_of_stock: est maintenant en rupture de stock. + only_n_remainging: "plus que %{num} en stock." + variant_overrides: + inventory_products: "Produits du Catalogue Boutique" + hidden_products: "Produits Masqués" + new_products: "Nouveaux Produits" + reset_stock_levels: Réinitialiser les niveaux de stock (par défaut) + changes_to: Devient + one_override: une modification + overrides: modifications + remain_unsaved: n'a pas encore été sauvegardé. + no_changes_to_save: Aucune modification à sauvegarder.' + no_authorisation: "Nous n'avons pas pu sauvegarder ces modifications, elles ne sont donc pas enregistrées." + some_trouble: "Nous n'avons pas pu sauvegarder : %{errors}" + changing_on_hand_stock: Modification des niveaux de stock en cours... + stock_reset: Les niveaux de stock ont été réinitiatlisés (valeurs par défaut) + tag_rules: + show_hide_variants: 'Afficher ou Masquer les variantes dans ma boutique' + show_hide_shipping: 'Afficher ou Montrer les méthodes de livraison lors de la finalisation de commande' + show_hide_payment: 'Afficher ou Montrer les méthodes de paiement lors de la finalisation de commande' + show_hide_order_cycles: 'Afficher ou Masquer les cycles de vente de ma boutique' + visible: VISIBLE + not_visible: INVISIBLE + services: + unsaved_changes_message: Des modifications n'ont pas encore été sauvegardées, sauvegarder maintenant ou ignorer ? + save: SAUVEGARDER + ignore: IGNORER + add_to_order_cycle: "vendre les produits (ajouter au cycle de vente)" + manage_products: "modifier les produits" + edit_profile: "modifier le profil" + add_products_to_inventory: "ajouter les produits au catalogue boutique" + resources: + could_not_delete_customer: 'L''acheteur n''a pas pu être supprimé' + product_import: + confirmation: | + Cette action remettra tous les niveaux de stock à zero pour cette + entreprise pour les produits non présents dans ce fichier. + order_cycles: + create_failure: "La création du cycle de vente a échoué" + update_success: 'Votre cycle de vente a été mis à jour.' + update_failure: "La mise à jour du cycle de vente à échoué" + no_distributors: Il n'y a pas de distributeur pour ce cycle de vente. Il ne sera pas visible aux acheteurs tant qu'il n'y aura pas de distributeur. Voulez-vous tout de même sauvegarder ce cycle de vente ? + enterprises: + producer: "Producteur" + non_producer: "Non-producteur" + customers: + select_shop: 'Veuillez d''abord choisir une boutique' + could_not_create: Oups ! Création impossible... + subscriptions: + closes: fermer + closed: fermé + close_date_not_set: Date de fin non renseignée + producers: + signup: + start_free_profile: "Commencez par créer votre profil entreprise, et changez de formule quand vous êtes prêt !" + spree: + email: Email + account_updated: "Compte mis à jour!" + my_account: "Mon compte" + date: "Date" + time: "Heure" + layouts: + admin: + header: + store: Vue acheteur + admin: + product_properties: + index: + inherits_properties_checkbox_hint: "Hériter des propriétés de %{supplier} ? (non applicable si information de remplacement déjà saisie)" + orders: + index: + listing_orders: "Liste des commandes" + new_order: "Nouvelle commande" + capture: "Payée" + ship: "Expédier" + edit: "Modifier" + note: "Note" + first: "Début" + last: "Fin" + previous: "Précédent" + next: "Suivant" + loading: "Chargement en cours" + no_orders_found: "Aucune commande trouvée pour ces critères" + results_found: "%{number} résultats trouvés" + viewing: "Résultats %{start} à %{end} affichés." + invoice: + issued_on: Editée le + tax_invoice: FACTURE + code: Code + from: De + to: A + form: + distribution_fields: + title: Distribution + distributor: "Distributeur : " + order_cycle: "Cycle de vente : " + overview: + order_cycles: + order_cycles: "Cycles de vente" + order_cycles_tip: "Les cycles de vente définissent quand et où vos produits peuvent être commandés par vos acheteurs." + you_have_active: + zero: "Vous n'avez aucun cycle de vente actif." + one: "Vous avez un cycle de vente actif." + other: "Vous avez %{count} cycles de vente actifs." + manage_order_cycles: "GERER LES CYCLES DE VENTE" + payment_methods: + stripe_connect: + enterprise_select_placeholder: Choisir... + loading_account_information_msg: Informations de compte en cours de chargement depuis Stripe, veuillez patienter... + stripe_disabled_msg: Les paiements via Stripe ont été désactivés par l'administrateur système. + request_failed_msg: Désolé, une erreur est survenue lors de la vérification du compte par Stripe... + account_missing_msg: Aucun compte Stripe n'existe pour cette entreprise. + connect_one: En connecter un + access_revoked_msg: L'accès à ce compte Stripe a été révoqué, veuillez reconnecter votre compte. + status: Statut + connected: Connecté + account_id: Identifiant Compte + business_name: Nom de l'entreprise + charges_enabled: Frais activés + payments: + source_forms: + stripe: + error_saving_payment: Erreur à l'enregistrement du paiement + submitting_payment: Envoi du paiement... + products: + new: + title: 'Nouveau Produit' + unit_name_placeholder: 'ex: botte' + index: + header: + title: Gestion du catalogue produits + indicators: + title: CHARGEMENT DES PRODUITS + no_products: "Aucun produit trouvé. Ajouter un produit ?" + no_results: "Désolé, aucun résultat trouvé" + products_head: + name: Produit/Variante + unit: Unité + display_as: Unité affichéé + category: Catégorie + tax_category: TVA applicable + inherits_properties?: Hériter des propriétés? + available_on: Disponible via + av_on: "Disp. via" + import_date: "Date d'import (si import)" + products_variant: + variant_has_n_overrides: "Cette variante a été modifiée %{n} fois dans des catalogues boutiques" + new_variant: "Nouvelle variante" + product_name: Nom du Produit + primary_taxon_form: + product_category: Catégorie Produit + group_buy_form: + group_buy: "Achat groupé de lots fixes ?" + bulk_unit_size: Quantité totale du lot + display_as: + display_as: Unité affichéé + reports: + table: + select_and_search: "Sélectionnez les filtres et cliquez sur RECHERCHER pour accéder à vos données." + bulk_coop: + bulk_coop_supplier_report: 'Achats groupés - Totaux par Producteur' + bulk_coop_allocation: 'Achats groupés - Allocation' + bulk_coop_packing_sheets: 'Achats groupés - Feuilles de préparation des paniers' + bulk_coop_customer_payments: 'Achats groupés - Paiement des acheteurs' + users: + email_confirmation: + confirmation_pending: "L'email de confirmation n'a pas encore été validé. Il a été envoyé à %{address}." + variants: + autocomplete: + producer_name: Producteur + general_settings: + edit: + legal_settings: "Configuration légales" + cookies_consent_banner_toggle: "Afficher la bannière de consentement à l'utilisation des cookies" + privacy_policy_url: "URL de la politique de confidentialité" + enterprises_require_tos: "Les entreprises doivent accepter les Conditions Générales d'Utilisation" + cookies_policy_matomo_section: "Afficher la section Matomo sur la politique de cookies" + cookies_policy_ga_section: "Afficher la section Google Analytics sur la politique de cookies" + footer_tos_url: "Conditions d'utilisation URL" + checkout: + payment: + stripe: + choose_one: En choisir un + enter_new_card: Entrer les informations pour la nouvelle carte + used_saved_card: "Utiliser une carte sauvegardée :" + or_enter_new_card: "Ou entrez les informations pour utiliser une nouvelle carte :" + remember_this_card: Se souvenir de cette carte ? + date_picker: + format: '%Y-%m-%d' + js_format: 'yy-mm-dd' + inventory: Catalogue boutique + orders: + edit: + login_to_view_order: "Veuillez vous connecter pour voir votre commande." + bought: + item: "Déjà commandé dans ce cycle de vente" + order_mailer: + invoice_email: + hi: "Bonjour %{name}" + invoice_attached_text: 'Veuillez trouver ci-joint la facture pour votre récente commande auprès de ' + order_state: + address: adresse + adjustments: ajustements + awaiting_return: attente du retour + canceled: annulé + cart: panier + complete: terminer + confirm: confirmer + delivery: livraison + paused: mis en pause + payment: paiement + pending: en attente + resumed: recommencé + returned: retourné + skrill: cash + subscription_state: + active: actif + pending: en attente + ended: terminé + paused: mis en pause + canceled: annulé + payment_states: + balance_due: solde dû + completed: effectué + checkout: passer commande + credit_owed: crédit acheteur + failed: échec + paid: payé + pending: en attente + processing: en traitement + void: faire un avoir + invalid: invalide + shipment_states: + backorder: réapprovisionnement + partial: partiel + pending: en attente + ready: prêt + shipped: envoyé + user_mailer: + reset_password_instructions: + request_sent_text: | + Votre demande de nouveau mot de passe a bien été prise en compte. + Si vous n'avez pas demandé de nouveau mot de passe, veuillez ignorer cet e-mail. + link_text: > + Si vous êtes bien à l'origine de cette demande, veuillez cliquer sur le + lien ci-dessous : + issue_text: | + Si le lien ne fonctionne pas, essayez de le copier - coller dans la barre d'adresse de votre navigateur. + Si le problème persiste, n'hésitez pas à nous contacter. + confirmation_instructions: + subject: Veuillez confirmer votre compte + weight: Poids (au kg) + zipcode: Code postal + users: + form: + account_settings: Paramètres du Compte + show: + tabs: + orders: Commandes + cards: Cartes bancaires + transactions: Achats + settings: Paramètres du Compte + unconfirmed_email: "Attente de validation pour l'email: %{unconfirmed_email}. Votre adresse email sera mise à jour quand le nouvel email aura été confirmé." + orders: + open_orders: Commandes Ouvertes + past_orders: Commandes Passées + transactions: + transaction_history: Historique des Transactions + open_orders: + order: Commander + shop: Faire mes courses + changes_allowed_until: Modifications permises jusqu'à + items: Produits à commander + total: Total + edit: Modifier + cancel: Annuler + closed: Fermée + until: Jusqu'à + past_orders: + order: Commandes à venir + shop: Boutique + completed_at: Date + items: Produits + total: Total + paid?: Payé ? + view: Afficher + saved_cards: + default?: Carte utilisée par défaut? + delete?: Supprimer? + cards: + authorised_shops: Boutiques autorisées. + authorised_shops_popover: Voilà la liste des boutiques que vous avez autorisées à débiter votre carte de paiement par défaut dans le cadre de vos abonnements en cours (commandes récurrentes). Les informations concernant votre carte de paiement sont sécurisées et ne sont pas accessibles par le gérant de la boutique. Vous recevrez systématiquement une notification avant tout débit sur votre carte. + saved_cards_popover: Voilà la liste des cartes de paiement que vous avez enregistrées. Votre carte par défaut sera automatiquement sélectionnée au moment de la finalisation d'une commande, et pourra être débitée par les boutiques auxquelles vous avez donné cette autorisation (voir à droite). + authorised_shops: + shop_name: "Nom de la boutique" + allow_charges?: "Autoriser les prélèvements ?" + localized_number: + invalid_format: n'est pas un format valide. Veuillez entrer un nombre. From 26d95b725091c5770c71618205ebee9053831a4e Mon Sep 17 00:00:00 2001 From: Pau Perez Date: Tue, 6 Nov 2018 13:41:28 +0000 Subject: [PATCH 59/65] Fix long lines --- .../spree/api/products_controller_decorator.rb | 8 ++++++-- app/serializers/api/admin/product_serializer.rb | 12 ++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/controllers/spree/api/products_controller_decorator.rb b/app/controllers/spree/api/products_controller_decorator.rb index ee3cc9e70b..2101ca6f61 100644 --- a/app/controllers/spree/api/products_controller_decorator.rb +++ b/app/controllers/spree/api/products_controller_decorator.rb @@ -76,7 +76,11 @@ Spree::Api::ProductsController.class_eval do end def render_paged_products(products) - render text: { products: ActiveModel::ArraySerializer.new(products, each_serializer: Api::Admin::ProductSerializer), pages: products.num_pages }.to_json - end + serializer = ActiveModel::ArraySerializer.new( + products, + each_serializer: Api::Admin::ProductSerializer + ) + render text: { products: serializer, pages: products.num_pages }.to_json + end end diff --git a/app/serializers/api/admin/product_serializer.rb b/app/serializers/api/admin/product_serializer.rb index 8af5701b94..27b914de29 100644 --- a/app/serializers/api/admin/product_serializer.rb +++ b/app/serializers/api/admin/product_serializer.rb @@ -9,11 +9,19 @@ class Api::Admin::ProductSerializer < ActiveModel::Serializer has_one :master, serializer: Api::Admin::VariantSerializer def image_url - object.images.present? ? object.images.first.attachment.url(:product) : "/assets/noimage/product.png" + if object.images.present? + object.images.first.attachment.url(:product) + else + "/assets/noimage/product.png" + end end def thumb_url - object.images.present? ? object.images.first.attachment.url(:mini) : "/assets/noimage/mini.png" + if object.images.present? + object.images.first.attachment.url(:mini) + else + "/assets/noimage/mini.png" + end end def on_hand From 51155bb3682a6dba08cc9327dd97ef3385c4e8a0 Mon Sep 17 00:00:00 2001 From: Pau Perez Date: Fri, 19 Oct 2018 13:17:54 +0200 Subject: [PATCH 60/65] Remove old commented out code You know, git already keeps old code for us. --- app/assets/javascripts/admin/bulk_product_update.js.coffee | 7 ------- app/serializers/api/admin/product_serializer.rb | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 2a3497898b..4bf733da3e 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -65,13 +65,6 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout DirtyProducts.clear() StatusMessage.clear() - # $scope.matchProducer = (product) -> - # for producer in $scope.producers - # if angular.equals(producer.id, product.producer) - # product.producer = producer - # break - - $scope.updateOnHand = (product) -> on_demand_variants = [] if product.variants diff --git a/app/serializers/api/admin/product_serializer.rb b/app/serializers/api/admin/product_serializer.rb index 8af5701b94..ac0e472e79 100644 --- a/app/serializers/api/admin/product_serializer.rb +++ b/app/serializers/api/admin/product_serializer.rb @@ -5,7 +5,7 @@ class Api::Admin::ProductSerializer < ActiveModel::Serializer has_one :supplier, key: :producer_id, embed: :id has_one :primary_taxon, key: :category_id, embed: :id - has_many :variants, key: :variants, serializer: Api::Admin::VariantSerializer # embed: ids + has_many :variants, key: :variants, serializer: Api::Admin::VariantSerializer has_one :master, serializer: Api::Admin::VariantSerializer def image_url From 1ef04354fd107d0e3cdaeb46f875406d7f60dc13 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Thu, 25 Oct 2018 19:09:14 +0100 Subject: [PATCH 61/65] Add capacity to weight calculator to calculate based on any object with an order (needed in spree 2 to calculate based on package) --- app/models/open_food_network/calculator/weight.rb | 4 +++- spec/models/calculator/weight_spec.rb | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/models/open_food_network/calculator/weight.rb b/app/models/open_food_network/calculator/weight.rb index 7f0e8807ba..a7e0d8770b 100644 --- a/app/models/open_food_network/calculator/weight.rb +++ b/app/models/open_food_network/calculator/weight.rb @@ -17,7 +17,9 @@ module OpenFoodNetwork private def line_items_for(object) - if object.respond_to? :line_items + if object.respond_to? :order + object.order.line_items + elsif object.respond_to? :line_items object.line_items elsif object.respond_to?(:variant) && object.respond_to?(:quantity) [object] diff --git a/spec/models/calculator/weight_spec.rb b/spec/models/calculator/weight_spec.rb index 26ec3125a5..6af473ca54 100644 --- a/spec/models/calculator/weight_spec.rb +++ b/spec/models/calculator/weight_spec.rb @@ -24,4 +24,18 @@ describe OpenFoodNetwork::Calculator::Weight do subject.set_preference(:per_kg, 10) subject.compute(line_item).should == 10*2 * 10 end + + it "computes shipping cost for an object with an order" do + variant_1 = double(:variant, :weight => 10) + variant_2 = double(:variant, :weight => 5) + + line_item_1 = double(:line_item, :variant => variant_1, :quantity => 1) + line_item_2 = double(:line_item, :variant => variant_2, :quantity => 2) + + order = double(:order, :line_items => [line_item_1, line_item_2]) + object_with_order = double(:object_with_order, order: order) + + subject.set_preference(:per_kg, 10) + subject.compute(object_with_order).should == (10*1 + 5*2) * 10 + end end From 275a1922703b744a44df9516a6636fa1965a202a Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Thu, 25 Oct 2018 19:15:20 +0100 Subject: [PATCH 62/65] Convert specs to RSpec 3.7.1 syntax with Transpec This conversion is done by Transpec 3.3.0 with the following command: transpec spec/models/calculator/weight_spec.rb * 3 conversions from: == expected to: eq(expected) * 3 conversions from: obj.should to: expect(obj).to For more details: https://github.com/yujinakayama/transpec#supported-conversions --- spec/models/calculator/weight_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/models/calculator/weight_spec.rb b/spec/models/calculator/weight_spec.rb index 6af473ca54..fd1629ac0e 100644 --- a/spec/models/calculator/weight_spec.rb +++ b/spec/models/calculator/weight_spec.rb @@ -13,7 +13,7 @@ describe OpenFoodNetwork::Calculator::Weight do order = double(:order, :line_items => [line_item_1, line_item_2, line_item_3]) subject.set_preference(:per_kg, 10) - subject.compute(order).should == (10*1 + 20*3) * 10 + expect(subject.compute(order)).to eq((10*1 + 20*3) * 10) end it "computes shipping cost for a line item" do @@ -22,7 +22,7 @@ describe OpenFoodNetwork::Calculator::Weight do line_item = double(:line_item, :variant => variant, :quantity => 2) subject.set_preference(:per_kg, 10) - subject.compute(line_item).should == 10*2 * 10 + expect(subject.compute(line_item)).to eq(10*2 * 10) end it "computes shipping cost for an object with an order" do @@ -36,6 +36,6 @@ describe OpenFoodNetwork::Calculator::Weight do object_with_order = double(:object_with_order, order: order) subject.set_preference(:per_kg, 10) - subject.compute(object_with_order).should == (10*1 + 5*2) * 10 + expect(subject.compute(object_with_order)).to eq((10*1 + 5*2) * 10) end end From bf55f50a7eefff12f8a8f2537a5707753208ac5b Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Thu, 25 Oct 2018 19:28:55 +0100 Subject: [PATCH 63/65] Fix rubocop warnings on weight and weight_spec --- .rubocop_todo.yml | 7 --- .../open_food_network/calculator/weight.rb | 45 ++++++++++--------- spec/models/calculator/weight_spec.rb | 34 +++++++------- 3 files changed, 40 insertions(+), 46 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 39510bf5f5..42e79ea101 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -156,7 +156,6 @@ Layout/EmptyLines: - 'app/models/exchange.rb' - 'app/models/exchange_fee.rb' - 'app/models/inventory_item.rb' - - 'app/models/open_food_network/calculator/weight.rb' - 'app/models/order_cycle.rb' - 'app/models/producer_property.rb' - 'app/models/product_distribution.rb' @@ -679,7 +678,6 @@ Layout/SpaceAroundOperators: - 'spec/jobs/update_billable_periods_spec.rb' - 'spec/lib/open_food_network/order_grouper_spec.rb' - 'spec/lib/stripe/account_connector_spec.rb' - - 'spec/models/calculator/weight_spec.rb' - 'spec/spec_helper.rb' - 'spec/support/cancan_helper.rb' - 'spec/support/seeds.rb' @@ -1279,7 +1277,6 @@ Naming/VariableNumber: Exclude: - 'spec/archive/features/consumer/checkout_spec.rb' - 'spec/lib/open_food_network/products_and_inventory_report_spec.rb' - - 'spec/models/calculator/weight_spec.rb' # Offense count: 1 Performance/Caller: @@ -1682,7 +1679,6 @@ Style/ClassAndModuleChildren: - 'app/controllers/spree/store_controller_decorator.rb' - 'app/helpers/angular_form_helper.rb' - 'app/models/calculator/flat_percent_per_item.rb' - - 'app/models/open_food_network/calculator/weight.rb' - 'app/models/spree/gateway/migs.rb' - 'app/models/spree/gateway/pin.rb' - 'app/models/spree/preferences/file_configuration.rb' @@ -1932,7 +1928,6 @@ Style/HashSyntax: - 'app/models/enterprise_group.rb' - 'app/models/enterprise_role.rb' - 'app/models/exchange_variant.rb' - - 'app/models/open_food_network/calculator/weight.rb' - 'app/models/order_cycle.rb' - 'app/models/product_distribution.rb' - 'app/models/spree/adjustment_decorator.rb' @@ -2022,7 +2017,6 @@ Style/HashSyntax: - 'spec/lib/open_food_network/order_grouper_spec.rb' - 'spec/lib/open_food_network/tag_rule_applicator_spec.rb' - 'spec/mailers/order_mailer_spec.rb' - - 'spec/models/calculator/weight_spec.rb' - 'spec/models/enterprise_fee_spec.rb' - 'spec/models/enterprise_spec.rb' - 'spec/models/exchange_spec.rb' @@ -2288,7 +2282,6 @@ Style/RedundantSelf: - 'app/models/calculator/flat_percent_per_item.rb' - 'app/models/enterprise.rb' - 'app/models/exchange.rb' - - 'app/models/open_food_network/calculator/weight.rb' - 'app/models/order_cycle.rb' - 'app/models/producer_property.rb' - 'app/models/spree/calculator/flat_percent_item_total_decorator.rb' diff --git a/app/models/open_food_network/calculator/weight.rb b/app/models/open_food_network/calculator/weight.rb index a7e0d8770b..64f1ee9999 100644 --- a/app/models/open_food_network/calculator/weight.rb +++ b/app/models/open_food_network/calculator/weight.rb @@ -1,30 +1,31 @@ module OpenFoodNetwork - class Calculator::Weight < Spree::Calculator - preference :per_kg, :decimal, :default => 0.0 - attr_accessible :preferred_per_kg + module Calculator + class Weight < Spree::Calculator + preference :per_kg, :decimal, default: 0.0 + attr_accessible :preferred_per_kg - def self.description - I18n.t('spree.weight') - end + def self.description + I18n.t('spree.weight') + end - def compute(object) - line_items = line_items_for object - total_weight = line_items.sum { |li| ((li.variant.andand.weight || 0) * li.quantity) } - total_weight * self.preferred_per_kg - end + def compute(object) + line_items = line_items_for object + total_weight = line_items.sum { |li| ((li.variant.andand.weight || 0) * li.quantity) } + total_weight * preferred_per_kg + end + private - private - - def line_items_for(object) - if object.respond_to? :order - object.order.line_items - elsif object.respond_to? :line_items - object.line_items - elsif object.respond_to?(:variant) && object.respond_to?(:quantity) - [object] - else - raise "Unknown object type: #{object.inspect}" + def line_items_for(object) + if object.respond_to? :order + object.order.line_items + elsif object.respond_to? :line_items + object.line_items + elsif object.respond_to?(:variant) && object.respond_to?(:quantity) + [object] + else + raise "Unknown object type: #{object.inspect}" + end end end end diff --git a/spec/models/calculator/weight_spec.rb b/spec/models/calculator/weight_spec.rb index fd1629ac0e..eec06a5c46 100644 --- a/spec/models/calculator/weight_spec.rb +++ b/spec/models/calculator/weight_spec.rb @@ -2,40 +2,40 @@ require 'spec_helper' describe OpenFoodNetwork::Calculator::Weight do it "computes shipping cost for an order by total weight" do - variant_1 = double(:variant, :weight => 10) - variant_2 = double(:variant, :weight => 20) - variant_3 = double(:variant, :weight => nil) + variant1 = double(:variant, weight: 10) + variant2 = double(:variant, weight: 20) + variant3 = double(:variant, weight: nil) - line_item_1 = double(:line_item, :variant => variant_1, :quantity => 1) - line_item_2 = double(:line_item, :variant => variant_2, :quantity => 3) - line_item_3 = double(:line_item, :variant => variant_3, :quantity => 5) + line_item1 = double(:line_item, variant: variant1, quantity: 1) + line_item2 = double(:line_item, variant: variant2, quantity: 3) + line_item3 = double(:line_item, variant: variant3, quantity: 5) - order = double(:order, :line_items => [line_item_1, line_item_2, line_item_3]) + order = double(:order, line_items: [line_item1, line_item2, line_item3]) subject.set_preference(:per_kg, 10) - expect(subject.compute(order)).to eq((10*1 + 20*3) * 10) + expect(subject.compute(order)).to eq((10 * 1 + 20 * 3) * 10) end it "computes shipping cost for a line item" do - variant = double(:variant, :weight => 10) + variant = double(:variant, weight: 10) - line_item = double(:line_item, :variant => variant, :quantity => 2) + line_item = double(:line_item, variant: variant, quantity: 2) subject.set_preference(:per_kg, 10) - expect(subject.compute(line_item)).to eq(10*2 * 10) + expect(subject.compute(line_item)).to eq(10 * 2 * 10) end it "computes shipping cost for an object with an order" do - variant_1 = double(:variant, :weight => 10) - variant_2 = double(:variant, :weight => 5) + variant1 = double(:variant, weight: 10) + variant2 = double(:variant, weight: 5) - line_item_1 = double(:line_item, :variant => variant_1, :quantity => 1) - line_item_2 = double(:line_item, :variant => variant_2, :quantity => 2) + line_item1 = double(:line_item, variant: variant1, quantity: 1) + line_item2 = double(:line_item, variant: variant2, quantity: 2) - order = double(:order, :line_items => [line_item_1, line_item_2]) + order = double(:order, line_items: [line_item1, line_item2]) object_with_order = double(:object_with_order, order: order) subject.set_preference(:per_kg, 10) - expect(subject.compute(object_with_order)).to eq((10*1 + 5*2) * 10) + expect(subject.compute(object_with_order)).to eq((10 * 1 + 5 * 2) * 10) end end From 9490da329a1af5901f821087b6e06cb6340e78b2 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Thu, 25 Oct 2018 20:07:34 +0100 Subject: [PATCH 64/65] Move Calculator::Weight from models/open_food_network/calculator to models/calculator --- app/models/calculator/weight.rb | 30 +++++++++++++++++ .../open_food_network/calculator/weight.rb | 32 ------------------- config/application.rb | 4 +-- lib/tasks/dev.rake | 2 +- spec/factories.rb | 2 +- spec/models/calculator/weight_spec.rb | 2 +- 6 files changed, 35 insertions(+), 37 deletions(-) create mode 100644 app/models/calculator/weight.rb delete mode 100644 app/models/open_food_network/calculator/weight.rb diff --git a/app/models/calculator/weight.rb b/app/models/calculator/weight.rb new file mode 100644 index 0000000000..ae65026a46 --- /dev/null +++ b/app/models/calculator/weight.rb @@ -0,0 +1,30 @@ +module Calculator + class Weight < Spree::Calculator + preference :per_kg, :decimal, default: 0.0 + attr_accessible :preferred_per_kg + + def self.description + I18n.t('spree.weight') + end + + def compute(object) + line_items = line_items_for object + total_weight = line_items.sum { |li| ((li.variant.andand.weight || 0) * li.quantity) } + total_weight * preferred_per_kg + end + + private + + def line_items_for(object) + if object.respond_to? :order + object.order.line_items + elsif object.respond_to? :line_items + object.line_items + elsif object.respond_to?(:variant) && object.respond_to?(:quantity) + [object] + else + raise "Unknown object type: #{object.inspect}" + end + end + end +end diff --git a/app/models/open_food_network/calculator/weight.rb b/app/models/open_food_network/calculator/weight.rb deleted file mode 100644 index 64f1ee9999..0000000000 --- a/app/models/open_food_network/calculator/weight.rb +++ /dev/null @@ -1,32 +0,0 @@ -module OpenFoodNetwork - module Calculator - class Weight < Spree::Calculator - preference :per_kg, :decimal, default: 0.0 - attr_accessible :preferred_per_kg - - def self.description - I18n.t('spree.weight') - end - - def compute(object) - line_items = line_items_for object - total_weight = line_items.sum { |li| ((li.variant.andand.weight || 0) * li.quantity) } - total_weight * preferred_per_kg - end - - private - - def line_items_for(object) - if object.respond_to? :order - object.order.line_items - elsif object.respond_to? :line_items - object.line_items - elsif object.respond_to?(:variant) && object.respond_to?(:quantity) - [object] - else - raise "Unknown object type: #{object.inspect}" - end - end - end - end -end diff --git a/config/application.rb b/config/application.rb index a872fdfe40..14c0b82b86 100644 --- a/config/application.rb +++ b/config/application.rb @@ -55,7 +55,7 @@ module Openfoodnetwork # Register Spree calculators initializer 'spree.register.calculators' do |app| - app.config.spree.calculators.shipping_methods << OpenFoodNetwork::Calculator::Weight + app.config.spree.calculators.shipping_methods << Calculator::Weight app.config.spree.calculators.add_class('enterprise_fees') config.spree.calculators.enterprise_fees = [ Calculator::FlatPercentPerItem, @@ -63,7 +63,7 @@ module Openfoodnetwork Spree::Calculator::FlexiRate, Spree::Calculator::PerItem, Spree::Calculator::PriceSack, - OpenFoodNetwork::Calculator::Weight + Calculator::Weight ] app.config.spree.calculators.add_class('payment_methods') config.spree.calculators.payment_methods = [ diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 6fa9af6f3e..5f7f09481f 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -188,7 +188,7 @@ namespace :openfoodnetwork do name: 'Pickup', zone_id: 3, require_ship_address: true, - calculator_type: 'OpenFoodNetwork::Calculator::Weight', + calculator_type: 'Calculator::Weight', distributor_ids: [enterprise2.id] ) enterprise2.payment_methods << Spree::PaymentMethod.last diff --git a/spec/factories.rb b/spec/factories.rb index c440b54048..80bbc6182e 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -275,7 +275,7 @@ FactoryBot.define do enterprise_role 'distributor' end - factory :weight_calculator, :class => OpenFoodNetwork::Calculator::Weight do + factory :weight_calculator, :class => Calculator::Weight do after(:build) { |c| c.set_preference(:per_kg, 0.5) } after(:create) { |c| c.set_preference(:per_kg, 0.5); c.save! } end diff --git a/spec/models/calculator/weight_spec.rb b/spec/models/calculator/weight_spec.rb index eec06a5c46..dee2f3d067 100644 --- a/spec/models/calculator/weight_spec.rb +++ b/spec/models/calculator/weight_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe OpenFoodNetwork::Calculator::Weight do +describe Calculator::Weight do it "computes shipping cost for an order by total weight" do variant1 = double(:variant, weight: 10) variant2 = double(:variant, weight: 20) From b7ffde795a6c7b8acd5c7df85c6834d156e0ab86 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 6 Nov 2018 16:31:40 +0000 Subject: [PATCH 65/65] Add migration to update calculator class name of weight calculators --- ...106162211_update_weight_calculator_type_class_name.rb | 9 +++++++++ db/schema.rb | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20181106162211_update_weight_calculator_type_class_name.rb diff --git a/db/migrate/20181106162211_update_weight_calculator_type_class_name.rb b/db/migrate/20181106162211_update_weight_calculator_type_class_name.rb new file mode 100644 index 0000000000..f8e15b3414 --- /dev/null +++ b/db/migrate/20181106162211_update_weight_calculator_type_class_name.rb @@ -0,0 +1,9 @@ +class UpdateWeightCalculatorTypeClassName < ActiveRecord::Migration + def up + Spree::Calculator.connection.execute("UPDATE spree_calculators SET type = 'Calculator::Weight' WHERE type = 'OpenFoodNetwork::Calculator::Weight'") + end + + def down + Spree::Calculator.connection.execute("UPDATE spree_calculators SET type = 'OpenFoodNetwork::Calculator::Weight' WHERE type = 'Calculator::Weight'") + end +end diff --git a/db/schema.rb b/db/schema.rb index 18f7177b86..49dae1fa6e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20181031105158) do +ActiveRecord::Schema.define(:version => 20181106162211) do create_table "account_invoices", :force => true do |t| t.integer "user_id", :null => false