From 1b4666fc6a33315d9f5c788a2f241e6400700522 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 29 Jan 2019 19:57:46 +0000 Subject: [PATCH 01/57] Add checkbox to cache settings to disable products cache --- .../admin/cache_settings_controller.rb | 9 +++- .../spree/app_configuration_decorator.rb | 3 ++ .../add_caching.html.haml.deface | 2 +- .../{show.html.haml => edit.html.haml} | 13 +++++ config/locales/en.yml | 15 +++--- .../cached_products_renderer.rb | 2 + .../cached_products_renderer_spec.rb | 54 ++++++++++++++++--- 7 files changed, 80 insertions(+), 18 deletions(-) rename app/views/admin/cache_settings/{show.html.haml => edit.html.haml} (52%) diff --git a/app/controllers/admin/cache_settings_controller.rb b/app/controllers/admin/cache_settings_controller.rb index 138bbc7b18..1074f13e9f 100644 --- a/app/controllers/admin/cache_settings_controller.rb +++ b/app/controllers/admin/cache_settings_controller.rb @@ -1,8 +1,7 @@ require 'open_food_network/products_cache_integrity_checker' class Admin::CacheSettingsController < Spree::Admin::BaseController - - def show + def edit @results = Exchange.cachable.map do |exchange| checker = OpenFoodNetwork::ProductsCacheIntegrityChecker.new(exchange.receiver, exchange.order_cycle) @@ -10,4 +9,10 @@ class Admin::CacheSettingsController < Spree::Admin::BaseController end end + def update + Spree::Config.set(params[:preferences]) + respond_to do |format| + format.html { redirect_to main_app.edit_admin_cache_settings_path } + end + end end diff --git a/app/models/spree/app_configuration_decorator.rb b/app/models/spree/app_configuration_decorator.rb index 992c39abe1..e909a0c69b 100644 --- a/app/models/spree/app_configuration_decorator.rb +++ b/app/models/spree/app_configuration_decorator.rb @@ -52,4 +52,7 @@ Spree::AppConfiguration.class_eval do # Number localization preference :enable_localized_number?, :boolean, default: false + + # Enable cache + preference :enable_products_cache?, :boolean, default: true end diff --git a/app/overrides/spree/admin/shared/_configuration_menu/add_caching.html.haml.deface b/app/overrides/spree/admin/shared/_configuration_menu/add_caching.html.haml.deface index c320e072c0..77c808315b 100644 --- a/app/overrides/spree/admin/shared/_configuration_menu/add_caching.html.haml.deface +++ b/app/overrides/spree/admin/shared/_configuration_menu/add_caching.html.haml.deface @@ -1,4 +1,4 @@ / insert_bottom "[data-hook='admin_configurations_sidebar_menu']" %li - = link_to t('admin.cache_settings.show.title'), main_app.admin_cache_settings_path + = link_to t('admin.cache_settings.edit.title'), main_app.edit_admin_cache_settings_path diff --git a/app/views/admin/cache_settings/show.html.haml b/app/views/admin/cache_settings/edit.html.haml similarity index 52% rename from app/views/admin/cache_settings/show.html.haml rename to app/views/admin/cache_settings/edit.html.haml index cecef216a3..222910be94 100644 --- a/app/views/admin/cache_settings/show.html.haml +++ b/app/views/admin/cache_settings/edit.html.haml @@ -1,6 +1,19 @@ - content_for :page_title do = t(:cache_settings) += form_tag main_app.admin_cache_settings_path, :method => :put do + .field + = hidden_field_tag 'preferences[enable_products_cache?]', '0' + = check_box_tag 'preferences[enable_products_cache?]', '1', Spree::Config[:enable_products_cache?] + = label_tag nil, t('.enable_products_cache') + .form-buttons + = button t(:update), 'icon-refresh' + +%br +%br + +%h4= t(:cache_state) +%br %table.index %thead %tr diff --git a/config/locales/en.yml b/config/locales/en.yml index f0c72f289a..ebebe97314 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -389,13 +389,14 @@ en: total_monthly_bill_incl_tax_tip: "The example total monthly bill with tax included, given the settings and the turnover provided." cache_settings: - show: - title: Caching - distributor: Distributor - order_cycle: Order Cycle - status: Status - diff: Diff - error: Error + edit: + title: "Caching" + distributor: "Distributor" + order_cycle: "Order Cycle" + status: "Status" + diff: "Diff" + error: "Error" + enable_products_cache: "Enable Products Cache?" invoice_settings: edit: diff --git a/lib/open_food_network/cached_products_renderer.rb b/lib/open_food_network/cached_products_renderer.rb index 733455ed73..2a3a690059 100644 --- a/lib/open_food_network/cached_products_renderer.rb +++ b/lib/open_food_network/cached_products_renderer.rb @@ -27,6 +27,8 @@ module OpenFoodNetwork private def cached_products_json + return uncached_products_json unless Spree::Config[:enable_products_cache?] + if Rails.env.production? || Rails.env.staging? Rails.cache.fetch("products-json-#{@distributor.id}-#{@order_cycle.id}") do begin diff --git a/spec/lib/open_food_network/cached_products_renderer_spec.rb b/spec/lib/open_food_network/cached_products_renderer_spec.rb index 8a3e72a775..57eead8832 100644 --- a/spec/lib/open_food_network/cached_products_renderer_spec.rb +++ b/spec/lib/open_food_network/cached_products_renderer_spec.rb @@ -7,19 +7,57 @@ module OpenFoodNetwork let(:order_cycle) { double(:order_cycle, id: 456) } let(:cpr) { CachedProductsRenderer.new(distributor, order_cycle) } + # keeps global state unchanged + around do |example| + original_config = Spree::Config[:enable_products_cache?] + example.run + Spree::Config[:enable_products_cache?] = original_config + end + describe "#products_json" do - context "when in testing / development" do - let(:products_renderer) do - double(ProductsRenderer, products_json: 'uncached products') - end + let(:products_renderer) do + double(ProductsRenderer, products_json: 'uncached products') + end + before do + allow(ProductsRenderer) + .to receive(:new) + .with(distributor, order_cycle) { products_renderer } + end + + context "products cache toggle" do before do - allow(ProductsRenderer) - .to receive(:new) - .with(distributor, order_cycle) { products_renderer } + allow(Rails.env).to receive(:production?) { true } + Rails.cache.write "products-json-#{distributor.id}-#{order_cycle.id}", 'products' end - it "returns uncaches products JSON" do + context "disabled" do + before do + Spree::Config[:enable_products_cache?] = false + end + + it "returns uncached products JSON" do + expect(cpr.products_json).to eq 'uncached products' + end + end + + context "enabled" do + before do + Spree::Config[:enable_products_cache?] = true + end + + it "returns the cached JSON" do + expect(cpr.products_json).to eq 'products' + end + end + end + + context "when in testing / development" do + before do + allow(Rails.env).to receive(:production?) { false } + end + + it "returns uncached products JSON" do expect(cpr.products_json).to eq 'uncached products' end end From acc99fc9bbe73c9715b5135b95830e027621a1b9 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Thu, 7 Feb 2019 20:27:51 +0000 Subject: [PATCH 02/57] Improve readability of cached_product_renderer and respective spec --- .../cached_products_renderer.rb | 20 ++++++++-------- .../cached_products_renderer_spec.rb | 24 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/open_food_network/cached_products_renderer.rb b/lib/open_food_network/cached_products_renderer.rb index 2a3a690059..abc1f45713 100644 --- a/lib/open_food_network/cached_products_renderer.rb +++ b/lib/open_food_network/cached_products_renderer.rb @@ -27,21 +27,21 @@ module OpenFoodNetwork private def cached_products_json - return uncached_products_json unless Spree::Config[:enable_products_cache?] + return uncached_products_json unless use_cached_products? - if Rails.env.production? || Rails.env.staging? - Rails.cache.fetch("products-json-#{@distributor.id}-#{@order_cycle.id}") do - begin - uncached_products_json - rescue ProductsRenderer::NoProducts - nil - end + Rails.cache.fetch("products-json-#{@distributor.id}-#{@order_cycle.id}") do + begin + uncached_products_json + rescue ProductsRenderer::NoProducts + nil end - else - uncached_products_json end end + def use_cached_products? + Spree::Config[:enable_products_cache?] && (Rails.env.production? || Rails.env.staging?) + end + def uncached_products_json ProductsRenderer.new(@distributor, @order_cycle).products_json end diff --git a/spec/lib/open_food_network/cached_products_renderer_spec.rb b/spec/lib/open_food_network/cached_products_renderer_spec.rb index 57eead8832..6bff04dad2 100644 --- a/spec/lib/open_food_network/cached_products_renderer_spec.rb +++ b/spec/lib/open_food_network/cached_products_renderer_spec.rb @@ -5,7 +5,7 @@ module OpenFoodNetwork describe CachedProductsRenderer do let(:distributor) { double(:distributor, id: 123) } let(:order_cycle) { double(:order_cycle, id: 456) } - let(:cpr) { CachedProductsRenderer.new(distributor, order_cycle) } + let(:cached_products_renderer) { CachedProductsRenderer.new(distributor, order_cycle) } # keeps global state unchanged around do |example| @@ -37,7 +37,7 @@ module OpenFoodNetwork end it "returns uncached products JSON" do - expect(cpr.products_json).to eq 'uncached products' + expect(cached_products_renderer.products_json).to eq 'uncached products' end end @@ -47,7 +47,7 @@ module OpenFoodNetwork end it "returns the cached JSON" do - expect(cpr.products_json).to eq 'products' + expect(cached_products_renderer.products_json).to eq 'products' end end end @@ -58,7 +58,7 @@ module OpenFoodNetwork end it "returns uncached products JSON" do - expect(cpr.products_json).to eq 'uncached products' + expect(cached_products_renderer.products_json).to eq 'uncached products' end end @@ -68,10 +68,10 @@ module OpenFoodNetwork end describe "when the distribution is not set" do - let(:cpr) { CachedProductsRenderer.new(nil, nil) } + let(:cached_products_renderer) { CachedProductsRenderer.new(nil, nil) } it "raises an exception and returns no products" do - expect { cpr.products_json }.to raise_error CachedProductsRenderer::NoProducts + expect { cached_products_renderer.products_json }.to raise_error CachedProductsRenderer::NoProducts end end @@ -81,12 +81,12 @@ module OpenFoodNetwork end it "returns the cached JSON" do - expect(cpr.products_json).to eq 'products' + expect(cached_products_renderer.products_json).to eq 'products' end it "raises an exception when there are no products" do Rails.cache.write "products-json-#{distributor.id}-#{order_cycle.id}", nil - expect { cpr.products_json }.to raise_error CachedProductsRenderer::NoProducts + expect { cached_products_renderer.products_json }.to raise_error CachedProductsRenderer::NoProducts end end @@ -108,11 +108,11 @@ module OpenFoodNetwork describe "when there are products" do it "returns products as JSON" do - expect(cpr.products_json).to eq 'fresh products' + expect(cached_products_renderer.products_json).to eq 'fresh products' end it "caches the JSON" do - cpr.products_json + cached_products_renderer.products_json expect(cached_json).to eq 'fresh products' end end @@ -129,11 +129,11 @@ module OpenFoodNetwork end it "raises an error" do - expect { cpr.products_json }.to raise_error CachedProductsRenderer::NoProducts + expect { cached_products_renderer.products_json }.to raise_error CachedProductsRenderer::NoProducts end it "caches the products as nil" do - expect { cpr.products_json }.to raise_error CachedProductsRenderer::NoProducts + expect { cached_products_renderer.products_json }.to raise_error CachedProductsRenderer::NoProducts expect(cache_present).to be expect(cached_json).to be_nil end From bfa0734cc684c2a73fcb225bd6d138010f6b15bf Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Thu, 14 Feb 2019 15:35:47 +0000 Subject: [PATCH 03/57] Break order_cycle_spec.js in specific files like create controller, edit controller and services Move bulk_product_update_spec.js to its apropriate place under /admin --- .../bulk_product_update_spec.js.coffee | 0 .../controllers/create_spec.js.coffee | 161 +++ .../controllers/edit_spec.js.coffee | 166 +++ .../services/enterprise_fee_spec.js.coffee | 37 + .../services/enterprise_spec.js.coffee | 75 ++ .../services/order_cycle_spec.js.coffee | 520 ++++++++++ .../unit/order_cycle_spec.js.coffee | 968 ------------------ 7 files changed, 959 insertions(+), 968 deletions(-) rename spec/javascripts/unit/{ => admin}/bulk_product_update_spec.js.coffee (100%) create mode 100644 spec/javascripts/unit/admin/order_cycles/controllers/create_spec.js.coffee create mode 100644 spec/javascripts/unit/admin/order_cycles/controllers/edit_spec.js.coffee create mode 100644 spec/javascripts/unit/admin/order_cycles/services/enterprise_fee_spec.js.coffee create mode 100644 spec/javascripts/unit/admin/order_cycles/services/enterprise_spec.js.coffee create mode 100644 spec/javascripts/unit/admin/order_cycles/services/order_cycle_spec.js.coffee delete mode 100644 spec/javascripts/unit/order_cycle_spec.js.coffee diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee similarity index 100% rename from spec/javascripts/unit/bulk_product_update_spec.js.coffee rename to spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee diff --git a/spec/javascripts/unit/admin/order_cycles/controllers/create_spec.js.coffee b/spec/javascripts/unit/admin/order_cycles/controllers/create_spec.js.coffee new file mode 100644 index 0000000000..d07c80f542 --- /dev/null +++ b/spec/javascripts/unit/admin/order_cycles/controllers/create_spec.js.coffee @@ -0,0 +1,161 @@ +describe 'AdminCreateOrderCycleCtrl', -> + ctrl = null + scope = null + event = null + OrderCycle = null + Enterprise = null + EnterpriseFee = null + + beforeEach -> + scope = + order_cycle_form: jasmine.createSpyObj('order_cycle_form', ['$dirty']) + $watch: jasmine.createSpy('$watch') + event = + preventDefault: jasmine.createSpy('preventDefault') + OrderCycle = + exchangeSelectedVariants: jasmine.createSpy('exchangeSelectedVariants').and.returnValue('variants selected') + productSuppliedToOrderCycle: jasmine.createSpy('productSuppliedToOrderCycle').and.returnValue('product supplied') + variantSuppliedToOrderCycle: jasmine.createSpy('variantSuppliedToOrderCycle').and.returnValue('variant supplied') + exchangeDirection: jasmine.createSpy('exchangeDirection').and.returnValue('exchange direction') + toggleProducts: jasmine.createSpy('toggleProducts') + setExchangeVariants: jasmine.createSpy('setExchangeVariants') + addSupplier: jasmine.createSpy('addSupplier') + addDistributor: jasmine.createSpy('addDistributor') + removeExchange: jasmine.createSpy('removeExchange') + addCoordinatorFee: jasmine.createSpy('addCoordinatorFee') + removeCoordinatorFee: jasmine.createSpy('removeCoordinatorFee') + addExchangeFee: jasmine.createSpy('addExchangeFee') + removeExchangeFee: jasmine.createSpy('removeExchangeFee') + removeDistributionOfVariant: jasmine.createSpy('removeDistributionOfVariant') + create: jasmine.createSpy('create') + new: jasmine.createSpy('new').and.returnValue "my order cycle" + Enterprise = + index: jasmine.createSpy('index').and.returnValue('enterprises list') + supplied_products: 'supplied products' + suppliedVariants: jasmine.createSpy('suppliedVariants').and.returnValue('supplied variants') + totalVariants: jasmine.createSpy('totalVariants').and.returnValue('variants total') + EnterpriseFee = + index: jasmine.createSpy('index').and.returnValue('enterprise fees list') + forEnterprise: jasmine.createSpy('forEnterprise').and.returnValue('enterprise fees for enterprise') + ocInstance = {} + + module('admin.orderCycles') + inject ($controller) -> + ctrl = $controller 'AdminCreateOrderCycleCtrl', {$scope: scope, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee, ocInstance: ocInstance} + + + it 'Loads enterprises and supplied products', -> + expect(Enterprise.index).toHaveBeenCalled() + expect(scope.enterprises).toEqual('enterprises list') + expect(scope.supplied_products).toEqual('supplied products') + + it 'Loads enterprise fees', -> + expect(EnterpriseFee.index).toHaveBeenCalled() + expect(scope.enterprise_fees).toEqual('enterprise fees list') + + it 'Loads order cycles', -> + expect(scope.order_cycle).toEqual('my order cycle') + + describe 'Reporting when all resources are loaded', -> + beforeEach inject (RequestMonitor) -> + RequestMonitor.loading = false + Enterprise.loaded = true + EnterpriseFee.loaded = true + OrderCycle.loaded = true + + it 'returns true when all resources are loaded', -> + expect(scope.loaded()).toBe(true) + + it 'returns false otherwise', -> + EnterpriseFee.loaded = false + expect(scope.loaded()).toBe(false) + + it "delegates suppliedVariants to Enterprise", -> + expect(scope.suppliedVariants('enterprise_id')).toEqual('supplied variants') + expect(Enterprise.suppliedVariants).toHaveBeenCalledWith('enterprise_id') + + it 'Delegates exchangeSelectedVariants to OrderCycle', -> + expect(scope.exchangeSelectedVariants('exchange')).toEqual('variants selected') + expect(OrderCycle.exchangeSelectedVariants).toHaveBeenCalledWith('exchange') + + it "delegates setExchangeVariants to OrderCycle", -> + scope.setExchangeVariants('exchange', 'variants', 'selected') + expect(OrderCycle.setExchangeVariants).toHaveBeenCalledWith('exchange', 'variants', 'selected') + + it 'Delegates enterpriseTotalVariants to Enterprise', -> + expect(scope.enterpriseTotalVariants('enterprise')).toEqual('variants total') + expect(Enterprise.totalVariants).toHaveBeenCalledWith('enterprise') + + it 'Delegates productSuppliedToOrderCycle to OrderCycle', -> + expect(scope.productSuppliedToOrderCycle('product')).toEqual('product supplied') + expect(OrderCycle.productSuppliedToOrderCycle).toHaveBeenCalledWith('product') + + it 'Delegates variantSuppliedToOrderCycle to OrderCycle', -> + expect(scope.variantSuppliedToOrderCycle('variant')).toEqual('variant supplied') + expect(OrderCycle.variantSuppliedToOrderCycle).toHaveBeenCalledWith('variant') + + it 'Delegates exchangeDirection to OrderCycle', -> + expect(scope.exchangeDirection('exchange')).toEqual('exchange direction') + expect(OrderCycle.exchangeDirection).toHaveBeenCalledWith('exchange') + + it 'Finds enterprises participating in the order cycle that have fees', -> + scope.enterprises = + 1: {id: 1, name: 'Eaterprises'} + 2: {id: 2, name: 'Pepper Tree Place'} + 3: {id: 3, name: 'South East'} + OrderCycle.participatingEnterpriseIds = jasmine.createSpy('participatingEnterpriseIds').and.returnValue([2]) + EnterpriseFee.enterprise_fees = [ {enterprise_id: 2} ] # Pepper Tree Place has a fee + expect(scope.enterprisesWithFees()).toEqual([ + {id: 2, name: 'Pepper Tree Place'} + ]) + + it 'Delegates enterpriseFeesForEnterprise to EnterpriseFee', -> + scope.enterpriseFeesForEnterprise('123') + expect(EnterpriseFee.forEnterprise).toHaveBeenCalledWith(123) + + it 'Adds order cycle suppliers', -> + scope.new_supplier_id = 'new supplier id' + scope.addSupplier(event) + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.addSupplier).toHaveBeenCalledWith('new supplier id') + + it 'Adds order cycle distributors', -> + scope.new_distributor_id = 'new distributor id' + scope.addDistributor(event) + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.addDistributor).toHaveBeenCalledWith('new distributor id') + + it 'Removes order cycle exchanges', -> + scope.removeExchange(event, 'exchange') + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.removeExchange).toHaveBeenCalledWith('exchange') + + it 'Adds coordinator fees', -> + scope.addCoordinatorFee(event) + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.addCoordinatorFee).toHaveBeenCalled() + + it 'Removes coordinator fees', -> + scope.removeCoordinatorFee(event, 0) + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.removeCoordinatorFee).toHaveBeenCalledWith(0) + + it 'Adds exchange fees', -> + scope.addExchangeFee(event) + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.addExchangeFee).toHaveBeenCalled() + + it 'Removes exchange fees', -> + scope.removeExchangeFee(event, 'exchange', 0) + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.removeExchangeFee).toHaveBeenCalledWith('exchange', 0) + + it 'Removes distribution of a variant', -> + scope.removeDistributionOfVariant('variant') + expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('variant') + + it 'Submits the order cycle via OrderCycle create', -> + eventMock = {preventDefault: jasmine.createSpy()} + scope.submit(eventMock,'/admin/order_cycles') + expect(eventMock.preventDefault).toHaveBeenCalled() + expect(OrderCycle.create).toHaveBeenCalledWith('/admin/order_cycles') diff --git a/spec/javascripts/unit/admin/order_cycles/controllers/edit_spec.js.coffee b/spec/javascripts/unit/admin/order_cycles/controllers/edit_spec.js.coffee new file mode 100644 index 0000000000..2f2016f5c7 --- /dev/null +++ b/spec/javascripts/unit/admin/order_cycles/controllers/edit_spec.js.coffee @@ -0,0 +1,166 @@ +describe 'AdminEditOrderCycleCtrl', -> + ctrl = null + scope = null + event = null + location = null + OrderCycle = null + Enterprise = null + EnterpriseFee = null + + beforeEach -> + scope = + order_cycle_form: jasmine.createSpyObj('order_cycle_form', ['$dirty', '$setPristine']) + $watch: jasmine.createSpy('$watch') + event = + preventDefault: jasmine.createSpy('preventDefault') + location = + absUrl: -> + 'example.com/admin/order_cycles/27/edit' + OrderCycle = + load: jasmine.createSpy('load') + exchangeSelectedVariants: jasmine.createSpy('exchangeSelectedVariants').and.returnValue('variants selected') + productSuppliedToOrderCycle: jasmine.createSpy('productSuppliedToOrderCycle').and.returnValue('product supplied') + variantSuppliedToOrderCycle: jasmine.createSpy('variantSuppliedToOrderCycle').and.returnValue('variant supplied') + exchangeDirection: jasmine.createSpy('exchangeDirection').and.returnValue('exchange direction') + toggleProducts: jasmine.createSpy('toggleProducts') + setExchangeVariants: jasmine.createSpy('setExchangeVariants') + addSupplier: jasmine.createSpy('addSupplier') + addDistributor: jasmine.createSpy('addDistributor') + removeExchange: jasmine.createSpy('removeExchange') + addCoordinatorFee: jasmine.createSpy('addCoordinatorFee') + removeCoordinatorFee: jasmine.createSpy('removeCoordinatorFee') + addExchangeFee: jasmine.createSpy('addExchangeFee') + removeExchangeFee: jasmine.createSpy('removeExchangeFee') + removeDistributionOfVariant: jasmine.createSpy('removeDistributionOfVariant') + update: jasmine.createSpy('update') + Enterprise = + index: jasmine.createSpy('index').and.returnValue('enterprises list') + supplied_products: 'supplied products' + suppliedVariants: jasmine.createSpy('suppliedVariants').and.returnValue('supplied variants') + totalVariants: jasmine.createSpy('totalVariants').and.returnValue('variants total') + EnterpriseFee = + index: jasmine.createSpy('index').and.returnValue('enterprise fees list') + forEnterprise: jasmine.createSpy('forEnterprise').and.returnValue('enterprise fees for enterprise') + + module('admin.orderCycles') + inject ($controller) -> + ctrl = $controller 'AdminEditOrderCycleCtrl', {$scope: scope, $location: location, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee} + + it 'Loads enterprises and supplied products', -> + expect(Enterprise.index).toHaveBeenCalled() + expect(scope.enterprises).toEqual('enterprises list') + expect(scope.supplied_products).toEqual('supplied products') + + it 'Loads enterprise fees', -> + expect(EnterpriseFee.index).toHaveBeenCalled() + expect(scope.enterprise_fees).toEqual('enterprise fees list') + + it 'Loads order cycles', -> + expect(OrderCycle.load).toHaveBeenCalledWith('27') + + describe 'Reporting when all resources are loaded', -> + beforeEach inject (RequestMonitor) -> + RequestMonitor.loading = false + Enterprise.loaded = true + EnterpriseFee.loaded = true + OrderCycle.loaded = true + + it 'returns true when all resources are loaded', -> + expect(scope.loaded()).toBe(true) + + it 'returns false otherwise', -> + EnterpriseFee.loaded = false + expect(scope.loaded()).toBe(false) + + it "delegates suppliedVariants to Enterprise", -> + expect(scope.suppliedVariants('enterprise_id')).toEqual('supplied variants') + expect(Enterprise.suppliedVariants).toHaveBeenCalledWith('enterprise_id') + + it 'Delegates exchangeSelectedVariants to OrderCycle', -> + expect(scope.exchangeSelectedVariants('exchange')).toEqual('variants selected') + expect(OrderCycle.exchangeSelectedVariants).toHaveBeenCalledWith('exchange') + + it "delegates setExchangeVariants to OrderCycle", -> + scope.setExchangeVariants('exchange', 'variants', 'selected') + expect(OrderCycle.setExchangeVariants).toHaveBeenCalledWith('exchange', 'variants', 'selected') + + it 'Delegates totalVariants to Enterprise', -> + expect(scope.enterpriseTotalVariants('enterprise')).toEqual('variants total') + expect(Enterprise.totalVariants).toHaveBeenCalledWith('enterprise') + + it 'Delegates productSuppliedToOrderCycle to OrderCycle', -> + expect(scope.productSuppliedToOrderCycle('product')).toEqual('product supplied') + expect(OrderCycle.productSuppliedToOrderCycle).toHaveBeenCalledWith('product') + + it 'Delegates variantSuppliedToOrderCycle to OrderCycle', -> + expect(scope.variantSuppliedToOrderCycle('variant')).toEqual('variant supplied') + expect(OrderCycle.variantSuppliedToOrderCycle).toHaveBeenCalledWith('variant') + + it 'Delegates exchangeDirection to OrderCycle', -> + expect(scope.exchangeDirection('exchange')).toEqual('exchange direction') + expect(OrderCycle.exchangeDirection).toHaveBeenCalledWith('exchange') + + it 'Finds enterprises participating in the order cycle that have fees', -> + scope.enterprises = + 1: {id: 1, name: 'Eaterprises'} + 2: {id: 2, name: 'Pepper Tree Place'} + 3: {id: 3, name: 'South East'} + OrderCycle.participatingEnterpriseIds = jasmine.createSpy('participatingEnterpriseIds').and.returnValue([2]) + EnterpriseFee.enterprise_fees = [ {enterprise_id: 2} ] # Pepper Tree Place has a fee + expect(scope.enterprisesWithFees()).toEqual([ + {id: 2, name: 'Pepper Tree Place'} + ]) + + it 'Delegates enterpriseFeesForEnterprise to EnterpriseFee', -> + scope.enterpriseFeesForEnterprise('123') + expect(EnterpriseFee.forEnterprise).toHaveBeenCalledWith(123) + + it 'Adds order cycle suppliers', -> + scope.new_supplier_id = 'new supplier id' + scope.addSupplier(event) + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.addSupplier).toHaveBeenCalledWith('new supplier id') + + it 'Adds order cycle distributors', -> + scope.new_distributor_id = 'new distributor id' + scope.addDistributor(event) + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.addDistributor).toHaveBeenCalledWith('new distributor id') + + it 'Removes order cycle exchanges', -> + scope.removeExchange(event, 'exchange') + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.removeExchange).toHaveBeenCalledWith('exchange') + expect(scope.order_cycle_form.$dirty).toEqual true + + it 'Adds coordinator fees', -> + scope.addCoordinatorFee(event) + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.addCoordinatorFee).toHaveBeenCalled() + + it 'Removes coordinator fees', -> + scope.removeCoordinatorFee(event, 0) + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.removeCoordinatorFee).toHaveBeenCalledWith(0) + expect(scope.order_cycle_form.$dirty).toEqual true + + it 'Adds exchange fees', -> + scope.addExchangeFee(event) + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.addExchangeFee).toHaveBeenCalled() + + it 'Removes exchange fees', -> + scope.removeExchangeFee(event, 'exchange', 0) + expect(event.preventDefault).toHaveBeenCalled() + expect(OrderCycle.removeExchangeFee).toHaveBeenCalledWith('exchange', 0) + expect(scope.order_cycle_form.$dirty).toEqual true + + it 'Removes distribution of a variant', -> + scope.removeDistributionOfVariant('variant') + expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('variant') + + it 'Submits the order cycle via OrderCycle update', -> + eventMock = {preventDefault: jasmine.createSpy()} + scope.submit(eventMock,'/admin/order_cycles') + expect(eventMock.preventDefault).toHaveBeenCalled() + expect(OrderCycle.update).toHaveBeenCalledWith('/admin/order_cycles', scope.order_cycle_form) diff --git a/spec/javascripts/unit/admin/order_cycles/services/enterprise_fee_spec.js.coffee b/spec/javascripts/unit/admin/order_cycles/services/enterprise_fee_spec.js.coffee new file mode 100644 index 0000000000..8e31008aab --- /dev/null +++ b/spec/javascripts/unit/admin/order_cycles/services/enterprise_fee_spec.js.coffee @@ -0,0 +1,37 @@ +describe 'EnterpriseFee service', -> + $httpBackend = null + EnterpriseFee = null + + beforeEach -> + module 'admin.orderCycles' + inject ($injector, _$httpBackend_)-> + EnterpriseFee = $injector.get('EnterpriseFee') + $httpBackend = _$httpBackend_ + $httpBackend.whenGET('/admin/enterprise_fees/for_order_cycle.json').respond [ + {id: 1, name: "Yayfee", enterprise_id: 1} + {id: 2, name: "FeeTwo", enterprise_id: 2} + ] + + it 'loads enterprise fees', -> + enterprise_fees = EnterpriseFee.index() + $httpBackend.flush() + expected_fees = [ + new EnterpriseFee.EnterpriseFee({id: 1, name: "Yayfee", enterprise_id: 1}) + new EnterpriseFee.EnterpriseFee({id: 2, name: "FeeTwo", enterprise_id: 2}) + ] + for fee, i in enterprise_fees + expect(fee.id).toEqual(expected_fees[i].id) + + it 'reports its loadedness', -> + expect(EnterpriseFee.loaded).toBe(false) + EnterpriseFee.index() + $httpBackend.flush() + expect(EnterpriseFee.loaded).toBe(true) + + it 'returns enterprise fees for an enterprise', -> + all_enterprise_fees = EnterpriseFee.index() + $httpBackend.flush() + enterprise_fees = EnterpriseFee.forEnterprise(1) + expect(enterprise_fees).toEqual [ + new EnterpriseFee.EnterpriseFee({id: 1, name: "Yayfee", enterprise_id: 1}) + ] diff --git a/spec/javascripts/unit/admin/order_cycles/services/enterprise_spec.js.coffee b/spec/javascripts/unit/admin/order_cycles/services/enterprise_spec.js.coffee new file mode 100644 index 0000000000..b3a7d122de --- /dev/null +++ b/spec/javascripts/unit/admin/order_cycles/services/enterprise_spec.js.coffee @@ -0,0 +1,75 @@ +describe 'Enterprise service', -> + $httpBackend = null + Enterprise = null + + beforeEach -> + module 'admin.orderCycles' + inject ($injector, _$httpBackend_)-> + Enterprise = $injector.get('Enterprise') + $httpBackend = _$httpBackend_ + $httpBackend.whenGET('/admin/enterprises/for_order_cycle.json').respond [ + {id: 1, name: 'One', supplied_products: [1, 2], is_primary_producer: true} + {id: 2, name: 'Two', supplied_products: [3, 4]} + {id: 3, name: 'Three', supplied_products: [5, 6], sells: 'any'} + ] + + it 'loads enterprises as a hash', -> + enterprises = Enterprise.index() + $httpBackend.flush() + expect(enterprises).toEqual + 1: new Enterprise.Enterprise({id: 1, name: 'One', supplied_products: [1, 2], is_primary_producer: true}) + 2: new Enterprise.Enterprise({id: 2, name: 'Two', supplied_products: [3, 4]}) + 3: new Enterprise.Enterprise({id: 3, name: 'Three', supplied_products: [5, 6], sells: 'any'}) + + it 'reports its loadedness', -> + expect(Enterprise.loaded).toBe(false) + Enterprise.index() + $httpBackend.flush() + expect(Enterprise.loaded).toBe(true) + + it 'loads producers as an array', -> + Enterprise.index() + $httpBackend.flush() + expect(Enterprise.producer_enterprises).toEqual [new Enterprise.Enterprise({id: 1, name: 'One', supplied_products: [1, 2], is_primary_producer: true})] + + it 'loads hubs as an array', -> + Enterprise.index() + $httpBackend.flush() + expect(Enterprise.hub_enterprises).toEqual [new Enterprise.Enterprise({id: 3, name: 'Three', supplied_products: [5, 6], sells: 'any'})] + + it 'collates all supplied products', -> + enterprises = Enterprise.index() + $httpBackend.flush() + expect(Enterprise.supplied_products).toEqual [1, 2, 3, 4, 5, 6] + + it "finds supplied variants for an enterprise", -> + spyOn(Enterprise, 'variantsOf').and.returnValue(10) + Enterprise.index() + $httpBackend.flush() + expect(Enterprise.suppliedVariants(1)).toEqual [10, 10] + + describe "finding the variants of a product", -> + it "returns the master for products without variants", -> + p = + master_id: 1 + variants: [] + expect(Enterprise.variantsOf(p)).toEqual [1] + + it "returns the variant ids for products with variants", -> + p = + master_id: 1 + variants: [{id: 2}, {id: 3}] + expect(Enterprise.variantsOf(p)).toEqual [2, 3] + + it 'counts total variants supplied by an enterprise', -> + enterprise = + supplied_products: [ + {variants: []}, + {variants: []}, + {variants: [{}, {}, {}]} + ] + + expect(Enterprise.totalVariants(enterprise)).toEqual(5) + + it 'returns zero when enterprise is null', -> + expect(Enterprise.totalVariants(null)).toEqual(0) diff --git a/spec/javascripts/unit/admin/order_cycles/services/order_cycle_spec.js.coffee b/spec/javascripts/unit/admin/order_cycles/services/order_cycle_spec.js.coffee new file mode 100644 index 0000000000..18f07406d8 --- /dev/null +++ b/spec/javascripts/unit/admin/order_cycles/services/order_cycle_spec.js.coffee @@ -0,0 +1,520 @@ +describe 'OrderCycle service', -> + OrderCycle = null + $httpBackend = null + $window = null + + beforeEach -> + $window = {navigator: {userAgent: 'foo'}} + + module 'admin.orderCycles', ($provide)-> + $provide.value('$window', $window) + null + + inject ($injector, _$httpBackend_)-> + OrderCycle = $injector.get('OrderCycle') + $httpBackend = _$httpBackend_ + $httpBackend.whenGET('/admin/order_cycles/123.json').respond + id: 123 + name: 'Test Order Cycle' + coordinator_id: 456 + coordinator_fees: [] + exchanges: [ + {sender_id: 1, receiver_id: 456, incoming: true} + {sender_id: 456, receiver_id: 2, incoming: false} + ] + $httpBackend.whenGET('/admin/order_cycles/new.json').respond + id: 123 + name: 'New Order Cycle' + coordinator_id: 456 + coordinator_fees: [] + exchanges: [] + + it 'initialises order cycle', -> + expect(OrderCycle.order_cycle).toEqual {incoming_exchanges: [], outgoing_exchanges: []} + + it 'counts selected variants in an exchange', -> + result = OrderCycle.exchangeSelectedVariants({variants: {1: true, 2: false, 3: true}}) + expect(result).toEqual(2) + + describe "fetching exchange ids", -> + it "gets enterprise ids as ints", -> + OrderCycle.order_cycle.incoming_exchanges = [ + {enterprise_id: 1} + {enterprise_id: '2'} + ] + OrderCycle.order_cycle.outgoing_exchanges = [ + {enterprise_id: 3} + {enterprise_id: '4'} + ] + expect(OrderCycle.exchangeIds('incoming')).toEqual [1, 2] + + describe "checking for novel enterprises", -> + e1 = {id: 1} + e2 = {id: 2} + + beforeEach -> + OrderCycle.order_cycle.incoming_exchanges = [{enterprise_id: 1}] + OrderCycle.order_cycle.outgoing_exchanges = [{enterprise_id: 1}] + + it "detects novel suppliers", -> + expect(OrderCycle.novelSupplier(e1)).toBe false + expect(OrderCycle.novelSupplier(e2)).toBe true + + it "detects novel suppliers with enterprise as string id", -> + expect(OrderCycle.novelSupplier('1')).toBe false + expect(OrderCycle.novelSupplier('2')).toBe true + + it "detects novel distributors", -> + expect(OrderCycle.novelDistributor(e1)).toBe false + expect(OrderCycle.novelDistributor(e2)).toBe true + + it "detects novel distributors with enterprise as string id", -> + expect(OrderCycle.novelDistributor('1')).toBe false + expect(OrderCycle.novelDistributor('2')).toBe true + + + describe 'fetching the direction for an exchange', -> + it 'returns "incoming" for incoming exchanges', -> + exchange = {id: 1} + OrderCycle.order_cycle.incoming_exchanges = [exchange] + OrderCycle.order_cycle.outgoing_exchanges = [] + expect(OrderCycle.exchangeDirection(exchange)).toEqual 'incoming' + + it 'returns "outgoing" for outgoing exchanges', -> + exchange = {id: 1} + OrderCycle.order_cycle.incoming_exchanges = [] + OrderCycle.order_cycle.outgoing_exchanges = [exchange] + expect(OrderCycle.exchangeDirection(exchange)).toEqual 'outgoing' + + describe "setting exchange variants", -> + describe "when I have permissions to edit the variants", -> + beforeEach -> + OrderCycle.order_cycle["editable_variants_for_outgoing_exchanges"] = { 1: [1, 2, 3] } + + it "sets all variants to the provided value", -> + exchange = { enterprise_id: 1, incoming: false, variants: {2: false}} + OrderCycle.setExchangeVariants(exchange, [1, 2, 3], true) + expect(exchange.variants).toEqual {1: true, 2: true, 3: true} + + describe "when I don't have permissions to edit the variants", -> + beforeEach -> + OrderCycle.order_cycle["editable_variants_for_outgoing_exchanges"] = { 1: [] } + + it "does not change variants to the provided value", -> + exchange = { enterprise_id: 1, incoming: false, variants: {2: false}} + OrderCycle.setExchangeVariants(exchange, [1, 2, 3], true) + expect(exchange.variants).toEqual {2: false} + + describe 'adding suppliers', -> + exchange = null + + beforeEach -> + # Initialise OC + OrderCycle.new() + $httpBackend.flush() + + it 'adds the supplier to incoming exchanges', -> + OrderCycle.addSupplier('123') + expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [ + {enterprise_id: '123', incoming: true, active: true, variants: {}, enterprise_fees: []} + ] + + describe 'adding distributors', -> + exchange = null + + beforeEach -> + # Initialise OC + OrderCycle.new() + $httpBackend.flush() + + it 'adds the distributor to outgoing exchanges', -> + OrderCycle.addDistributor('123') + expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [ + {enterprise_id: '123', incoming: false, active: true, variants: {}, enterprise_fees: []} + ] + + describe 'removing exchanges', -> + exchange = null + + beforeEach -> + spyOn(OrderCycle, 'removeDistributionOfVariant') + exchange = + enterprise_id: '123' + active: true + incoming: false + variants: {1: true, 2: false, 3: true} + enterprise_fees: [] + + describe "removing incoming exchanges", -> + beforeEach -> + exchange.incoming = true + OrderCycle.order_cycle.incoming_exchanges = [exchange] + + it 'removes the exchange', -> + OrderCycle.removeExchange(exchange) + expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [] + + it 'removes distribution of all exchange variants', -> + OrderCycle.removeExchange(exchange) + expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('1') + expect(OrderCycle.removeDistributionOfVariant).not.toHaveBeenCalledWith('2') + expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('3') + + describe "removing outgoing exchanges", -> + beforeEach -> + exchange.incoming = false + OrderCycle.order_cycle.outgoing_exchanges = [exchange] + + it 'removes the exchange', -> + OrderCycle.removeExchange(exchange) + expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [] + + it "does not remove distribution of any variants", -> + OrderCycle.removeExchange(exchange) + expect(OrderCycle.removeDistributionOfVariant).not.toHaveBeenCalled() + + it 'adds coordinator fees', -> + # Initialise OC + OrderCycle.new() + $httpBackend.flush() + OrderCycle.addCoordinatorFee() + expect(OrderCycle.order_cycle.coordinator_fees).toEqual [{}] + + describe 'removing coordinator fees', -> + it 'removes a coordinator fee by index', -> + OrderCycle.order_cycle.coordinator_fees = [ + {id: 1} + {id: 2} + {id: 3} + ] + OrderCycle.removeCoordinatorFee(1) + expect(OrderCycle.order_cycle.coordinator_fees).toEqual [ + {id: 1} + {id: 3} + ] + + it 'adds exchange fees', -> + exchange = {enterprise_fees: []} + OrderCycle.addExchangeFee(exchange) + expect(exchange.enterprise_fees).toEqual [{}] + + describe 'removing exchange fees', -> + it 'removes an exchange fee by index', -> + exchange = + enterprise_fees: [ + {id: 1} + {id: 2} + {id: 3} + ] + OrderCycle.removeExchangeFee(exchange, 1) + expect(exchange.enterprise_fees).toEqual [ + {id: 1} + {id: 3} + ] + + it 'finds participating enterprise ids', -> + OrderCycle.order_cycle.incoming_exchanges = [ + {enterprise_id: 1} + {enterprise_id: 2} + ] + OrderCycle.order_cycle.outgoing_exchanges = [ + {enterprise_id: 2} + {enterprise_id: 3} + ] + expect(OrderCycle.participatingEnterpriseIds()).toEqual [1, 2, 3] + + describe 'fetching all variants supplied on incoming exchanges', -> + it 'collects variants from incoming exchanges', -> + OrderCycle.order_cycle.incoming_exchanges = [ + {variants: {1: true, 2: false}} + {variants: {3: false, 4: true}} + {variants: {5: true, 6: false}} + ] + expect(OrderCycle.incomingExchangesVariants()).toEqual [1, 4, 5] + + describe 'checking whether a product is supplied to the order cycle', -> + product_master_present = product_variant_present = product_master_absent = product_variant_absent = null + + beforeEach -> + product_master_present = + name: "Linseed (500g)" + master_id: 1 + variants: [] + product_variant_present = + name: "Linseed (500g)" + master_id: 2 + variants: [{id: 3}, {id: 4}] + product_master_absent = + name: "Linseed (500g)" + master_id: 5 + variants: [] + product_variant_absent = + name: "Linseed (500g)" + master_id: 6 + variants: [{id: 7}, {id: 8}] + + spyOn(OrderCycle, 'incomingExchangesVariants').and.returnValue([1, 3]) + + it 'returns true for products whose master is supplied', -> + expect(OrderCycle.productSuppliedToOrderCycle(product_master_present)).toBeTruthy() + + it 'returns true for products for whom a variant is supplied', -> + expect(OrderCycle.productSuppliedToOrderCycle(product_variant_present)).toBeTruthy() + + it 'returns false for products whose master is not supplied', -> + expect(OrderCycle.productSuppliedToOrderCycle(product_master_absent)).toBeFalsy() + + it 'returns false for products whose variants are not supplied', -> + expect(OrderCycle.productSuppliedToOrderCycle(product_variant_absent)).toBeFalsy() + + + describe 'checking whether a variant is supplied to the order cycle', -> + beforeEach -> + spyOn(OrderCycle, 'incomingExchangesVariants').and.returnValue([1, 3]) + + it 'returns true for variants that are supplied', -> + expect(OrderCycle.variantSuppliedToOrderCycle({id: 1})).toBeTruthy() + + it 'returns false for variants that are not supplied', -> + expect(OrderCycle.variantSuppliedToOrderCycle({id: 999})).toBeFalsy() + + + describe 'remove all distribution of a variant', -> + it 'removes the variant from every outgoing exchange', -> + OrderCycle.order_cycle.outgoing_exchanges = [ + {variants: {123: true, 234: true}} + {variants: {123: true, 333: true}} + ] + OrderCycle.removeDistributionOfVariant('123') + expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [ + {variants: {123: false, 234: true}} + {variants: {123: false, 333: true}} + ] + + describe 'loading an order cycle, reporting loadedness', -> + it 'reports its loadedness', -> + expect(OrderCycle.loaded).toBe(false) + OrderCycle.load('123') + $httpBackend.flush() + expect(OrderCycle.loaded).toBe(true) + + describe 'loading a new order cycle', -> + beforeEach -> + OrderCycle.new() + $httpBackend.flush() + + + it 'loads basic fields', -> + expect(OrderCycle.order_cycle.id).toEqual(123) + expect(OrderCycle.order_cycle.name).toEqual('New Order Cycle') + expect(OrderCycle.order_cycle.coordinator_id).toEqual(456) + + it 'initialises the incoming and outgoing exchanges', -> + expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [] + expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [] + + it 'removes the original exchanges array', -> + expect(OrderCycle.order_cycle.exchanges).toBeUndefined() + + describe 'loading an existing order cycle', -> + beforeEach -> + OrderCycle.load('123') + $httpBackend.flush() + + it 'loads basic fields', -> + expect(OrderCycle.order_cycle.id).toEqual(123) + expect(OrderCycle.order_cycle.name).toEqual('Test Order Cycle') + expect(OrderCycle.order_cycle.coordinator_id).toEqual(456) + + it 'splits exchanges into incoming and outgoing', -> + expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [ + sender_id: 1 + enterprise_id: 1 + incoming: true + active: true + ] + + expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [ + receiver_id: 2 + enterprise_id: 2 + incoming: false + active: true + ] + + it 'removes the original exchanges array', -> + expect(OrderCycle.order_cycle.exchanges).toBeUndefined() + + describe 'creating an order cycle', -> + beforeEach -> + spyOn(OrderCycle, 'confirmNoDistributors').and.returnValue true + + it 'redirects to the destination page on success', -> + OrderCycle.order_cycle = 'this is the order cycle' + spyOn(OrderCycle, 'dataForSubmit').and.returnValue('this is the submit data') + $httpBackend.expectPOST('/admin/order_cycles.json', { + order_cycle: 'this is the submit data' + }).respond {success: true} + + OrderCycle.create('/destination/page') + $httpBackend.flush() + expect($window.location).toEqual('/destination/page') + + it 'does not redirect on error', -> + OrderCycle.order_cycle = 'this is the order cycle' + spyOn(OrderCycle, 'dataForSubmit').and.returnValue('this is the submit data') + $httpBackend.expectPOST('/admin/order_cycles.json', { + order_cycle: 'this is the submit data' + }).respond 400, { errors: [] } + + OrderCycle.create('/destination/page') + $httpBackend.flush() + expect($window.location).toEqual(undefined) + + describe 'updating an order cycle', -> + beforeEach -> + spyOn(OrderCycle, 'confirmNoDistributors').and.returnValue true + + it 'redirects to the destination page on success', -> + form = jasmine.createSpyObj('order_cycle_form', ['$dirty', '$setPristine']) + OrderCycle.order_cycle = 'this is the order cycle' + spyOn(OrderCycle, 'dataForSubmit').and.returnValue('this is the submit data') + $httpBackend.expectPUT('/admin/order_cycles.json?reloading=1', { + order_cycle: 'this is the submit data' + }).respond {success: true} + + OrderCycle.update('/destination/page', form) + $httpBackend.flush() + expect($window.location).toEqual('/destination/page') + expect(form.$setPristine.calls.count()).toBe 1 + + it 'does not redirect on error', -> + OrderCycle.order_cycle = 'this is the order cycle' + spyOn(OrderCycle, 'dataForSubmit').and.returnValue('this is the submit data') + $httpBackend.expectPUT('/admin/order_cycles.json?reloading=1', { + order_cycle: 'this is the submit data' + }).respond 400, { errors: [] } + + OrderCycle.update('/destination/page') + $httpBackend.flush() + expect($window.location).toEqual(undefined) + + describe 'preparing data for form submission', -> + it 'calls all the methods', -> + OrderCycle.order_cycle = {foo: 'bar'} + spyOn(OrderCycle, 'removeInactiveExchanges') + spyOn(OrderCycle, 'translateCoordinatorFees') + spyOn(OrderCycle, 'translateExchangeFees') + OrderCycle.dataForSubmit() + expect(OrderCycle.removeInactiveExchanges).toHaveBeenCalled() + expect(OrderCycle.translateCoordinatorFees).toHaveBeenCalled() + expect(OrderCycle.translateExchangeFees).toHaveBeenCalled() + + it 'removes inactive exchanges', -> + data = + incoming_exchanges: [ + {enterprise_id: "1", active: false} + {enterprise_id: "2", active: true} + {enterprise_id: "3", active: false} + ] + outgoing_exchanges: [ + {enterprise_id: "4", active: true} + {enterprise_id: "5", active: false} + {enterprise_id: "6", active: true} + ] + + data = OrderCycle.removeInactiveExchanges(data) + + expect(data.incoming_exchanges).toEqual [ + {enterprise_id: "2", active: true} + ] + expect(data.outgoing_exchanges).toEqual [ + {enterprise_id: "4", active: true} + {enterprise_id: "6", active: true} + ] + + it 'converts coordinator fees into a list of ids', -> + order_cycle = + coordinator_fees: [ + {id: 1} + {id: 2} + ] + + data = OrderCycle.translateCoordinatorFees(order_cycle) + + expect(data.coordinator_fees).toBeUndefined() + expect(data.coordinator_fee_ids).toEqual [1, 2] + + it "preserves original data when converting coordinator fees", -> + OrderCycle.order_cycle = + coordinator_fees: [ + {id: 1} + {id: 2} + ] + + data = OrderCycle.deepCopy() + data = OrderCycle.translateCoordinatorFees(data) + + expect(OrderCycle.order_cycle.coordinator_fees).toEqual [{id: 1}, {id: 2}] + expect(OrderCycle.order_cycle.coordinator_fee_ids).toBeUndefined() + + describe "converting exchange fees into a list of ids", -> + order_cycle = null + data = null + + beforeEach -> + order_cycle = + incoming_exchanges: [ + enterprise_fees: [ + {id: 1} + {id: 2} + ] + ] + outgoing_exchanges: [ + enterprise_fees: [ + {id: 3} + {id: 4} + ] + ] + OrderCycle.order_cycle = order_cycle + + data = OrderCycle.deepCopy() + data = OrderCycle.translateExchangeFees(data) + + it 'converts exchange fees into a list of ids', -> + expect(data.incoming_exchanges[0].enterprise_fees).toBeUndefined() + expect(data.outgoing_exchanges[0].enterprise_fees).toBeUndefined() + expect(data.incoming_exchanges[0].enterprise_fee_ids).toEqual [1, 2] + expect(data.outgoing_exchanges[0].enterprise_fee_ids).toEqual [3, 4] + + it "preserves original data when converting exchange fees", -> + expect(order_cycle.incoming_exchanges[0].enterprise_fees).toEqual [{id: 1}, {id: 2}] + expect(order_cycle.outgoing_exchanges[0].enterprise_fees).toEqual [{id: 3}, {id: 4}] + expect(order_cycle.incoming_exchanges[0].enterprise_fee_ids).toBeUndefined() + expect(order_cycle.outgoing_exchanges[0].enterprise_fee_ids).toBeUndefined() + + describe "confirming when there are no distributors", -> + order_cycle_with_exchanges = order_cycle_without_exchanges = null + + beforeEach -> + order_cycle_with_exchanges = + outgoing_exchanges: [{}] + order_cycle_without_exchanges = + outgoing_exchanges: [] + + it "returns true when there are distributors", -> + spyOn(window, 'confirm') + OrderCycle.order_cycle = order_cycle_with_exchanges + expect(OrderCycle.confirmNoDistributors()).toBe true + expect(window.confirm).not.toHaveBeenCalled() + + it "returns true when there are no distributors but the user confirms", -> + spyOn(window, 'confirm').and.returnValue(true) + OrderCycle.order_cycle = order_cycle_without_exchanges + expect(OrderCycle.confirmNoDistributors()).toBe true + expect(window.confirm).toHaveBeenCalled() + + it "returns false when there are no distributors and the user does not confirm", -> + spyOn(window, 'confirm').and.returnValue(false) + OrderCycle.order_cycle = order_cycle_without_exchanges + expect(OrderCycle.confirmNoDistributors()).toBe false + expect(window.confirm).toHaveBeenCalled() diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee deleted file mode 100644 index 6c6f3440e6..0000000000 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ /dev/null @@ -1,968 +0,0 @@ -describe 'OrderCycle controllers', -> - - describe 'AdminCreateOrderCycleCtrl', -> - ctrl = null - scope = null - event = null - OrderCycle = null - Enterprise = null - EnterpriseFee = null - - beforeEach -> - scope = - order_cycle_form: jasmine.createSpyObj('order_cycle_form', ['$dirty']) - $watch: jasmine.createSpy('$watch') - event = - preventDefault: jasmine.createSpy('preventDefault') - OrderCycle = - exchangeSelectedVariants: jasmine.createSpy('exchangeSelectedVariants').and.returnValue('variants selected') - productSuppliedToOrderCycle: jasmine.createSpy('productSuppliedToOrderCycle').and.returnValue('product supplied') - variantSuppliedToOrderCycle: jasmine.createSpy('variantSuppliedToOrderCycle').and.returnValue('variant supplied') - exchangeDirection: jasmine.createSpy('exchangeDirection').and.returnValue('exchange direction') - toggleProducts: jasmine.createSpy('toggleProducts') - setExchangeVariants: jasmine.createSpy('setExchangeVariants') - addSupplier: jasmine.createSpy('addSupplier') - addDistributor: jasmine.createSpy('addDistributor') - removeExchange: jasmine.createSpy('removeExchange') - addCoordinatorFee: jasmine.createSpy('addCoordinatorFee') - removeCoordinatorFee: jasmine.createSpy('removeCoordinatorFee') - addExchangeFee: jasmine.createSpy('addExchangeFee') - removeExchangeFee: jasmine.createSpy('removeExchangeFee') - removeDistributionOfVariant: jasmine.createSpy('removeDistributionOfVariant') - create: jasmine.createSpy('create') - new: jasmine.createSpy('new').and.returnValue "my order cycle" - Enterprise = - index: jasmine.createSpy('index').and.returnValue('enterprises list') - supplied_products: 'supplied products' - suppliedVariants: jasmine.createSpy('suppliedVariants').and.returnValue('supplied variants') - totalVariants: jasmine.createSpy('totalVariants').and.returnValue('variants total') - EnterpriseFee = - index: jasmine.createSpy('index').and.returnValue('enterprise fees list') - forEnterprise: jasmine.createSpy('forEnterprise').and.returnValue('enterprise fees for enterprise') - ocInstance = {} - - module('admin.orderCycles') - inject ($controller) -> - ctrl = $controller 'AdminCreateOrderCycleCtrl', {$scope: scope, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee, ocInstance: ocInstance} - - - it 'Loads enterprises and supplied products', -> - expect(Enterprise.index).toHaveBeenCalled() - expect(scope.enterprises).toEqual('enterprises list') - expect(scope.supplied_products).toEqual('supplied products') - - it 'Loads enterprise fees', -> - expect(EnterpriseFee.index).toHaveBeenCalled() - expect(scope.enterprise_fees).toEqual('enterprise fees list') - - it 'Loads order cycles', -> - expect(scope.order_cycle).toEqual('my order cycle') - - describe 'Reporting when all resources are loaded', -> - beforeEach inject (RequestMonitor) -> - RequestMonitor.loading = false - Enterprise.loaded = true - EnterpriseFee.loaded = true - OrderCycle.loaded = true - - it 'returns true when all resources are loaded', -> - expect(scope.loaded()).toBe(true) - - it 'returns false otherwise', -> - EnterpriseFee.loaded = false - expect(scope.loaded()).toBe(false) - - it "delegates suppliedVariants to Enterprise", -> - expect(scope.suppliedVariants('enterprise_id')).toEqual('supplied variants') - expect(Enterprise.suppliedVariants).toHaveBeenCalledWith('enterprise_id') - - it 'Delegates exchangeSelectedVariants to OrderCycle', -> - expect(scope.exchangeSelectedVariants('exchange')).toEqual('variants selected') - expect(OrderCycle.exchangeSelectedVariants).toHaveBeenCalledWith('exchange') - - it "delegates setExchangeVariants to OrderCycle", -> - scope.setExchangeVariants('exchange', 'variants', 'selected') - expect(OrderCycle.setExchangeVariants).toHaveBeenCalledWith('exchange', 'variants', 'selected') - - it 'Delegates enterpriseTotalVariants to Enterprise', -> - expect(scope.enterpriseTotalVariants('enterprise')).toEqual('variants total') - expect(Enterprise.totalVariants).toHaveBeenCalledWith('enterprise') - - it 'Delegates productSuppliedToOrderCycle to OrderCycle', -> - expect(scope.productSuppliedToOrderCycle('product')).toEqual('product supplied') - expect(OrderCycle.productSuppliedToOrderCycle).toHaveBeenCalledWith('product') - - it 'Delegates variantSuppliedToOrderCycle to OrderCycle', -> - expect(scope.variantSuppliedToOrderCycle('variant')).toEqual('variant supplied') - expect(OrderCycle.variantSuppliedToOrderCycle).toHaveBeenCalledWith('variant') - - it 'Delegates exchangeDirection to OrderCycle', -> - expect(scope.exchangeDirection('exchange')).toEqual('exchange direction') - expect(OrderCycle.exchangeDirection).toHaveBeenCalledWith('exchange') - - it 'Finds enterprises participating in the order cycle that have fees', -> - scope.enterprises = - 1: {id: 1, name: 'Eaterprises'} - 2: {id: 2, name: 'Pepper Tree Place'} - 3: {id: 3, name: 'South East'} - OrderCycle.participatingEnterpriseIds = jasmine.createSpy('participatingEnterpriseIds').and.returnValue([2]) - EnterpriseFee.enterprise_fees = [ {enterprise_id: 2} ] # Pepper Tree Place has a fee - expect(scope.enterprisesWithFees()).toEqual([ - {id: 2, name: 'Pepper Tree Place'} - ]) - - it 'Delegates enterpriseFeesForEnterprise to EnterpriseFee', -> - scope.enterpriseFeesForEnterprise('123') - expect(EnterpriseFee.forEnterprise).toHaveBeenCalledWith(123) - - it 'Adds order cycle suppliers', -> - scope.new_supplier_id = 'new supplier id' - scope.addSupplier(event) - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.addSupplier).toHaveBeenCalledWith('new supplier id') - - it 'Adds order cycle distributors', -> - scope.new_distributor_id = 'new distributor id' - scope.addDistributor(event) - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.addDistributor).toHaveBeenCalledWith('new distributor id') - - it 'Removes order cycle exchanges', -> - scope.removeExchange(event, 'exchange') - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.removeExchange).toHaveBeenCalledWith('exchange') - - it 'Adds coordinator fees', -> - scope.addCoordinatorFee(event) - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.addCoordinatorFee).toHaveBeenCalled() - - it 'Removes coordinator fees', -> - scope.removeCoordinatorFee(event, 0) - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.removeCoordinatorFee).toHaveBeenCalledWith(0) - - it 'Adds exchange fees', -> - scope.addExchangeFee(event) - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.addExchangeFee).toHaveBeenCalled() - - it 'Removes exchange fees', -> - scope.removeExchangeFee(event, 'exchange', 0) - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.removeExchangeFee).toHaveBeenCalledWith('exchange', 0) - - it 'Removes distribution of a variant', -> - scope.removeDistributionOfVariant('variant') - expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('variant') - - it 'Submits the order cycle via OrderCycle create', -> - eventMock = {preventDefault: jasmine.createSpy()} - scope.submit(eventMock,'/admin/order_cycles') - expect(eventMock.preventDefault).toHaveBeenCalled() - expect(OrderCycle.create).toHaveBeenCalledWith('/admin/order_cycles') - - describe 'AdminEditOrderCycleCtrl', -> - ctrl = null - scope = null - event = null - location = null - OrderCycle = null - Enterprise = null - EnterpriseFee = null - - beforeEach -> - scope = - order_cycle_form: jasmine.createSpyObj('order_cycle_form', ['$dirty', '$setPristine']) - $watch: jasmine.createSpy('$watch') - event = - preventDefault: jasmine.createSpy('preventDefault') - location = - absUrl: -> - 'example.com/admin/order_cycles/27/edit' - OrderCycle = - load: jasmine.createSpy('load') - exchangeSelectedVariants: jasmine.createSpy('exchangeSelectedVariants').and.returnValue('variants selected') - productSuppliedToOrderCycle: jasmine.createSpy('productSuppliedToOrderCycle').and.returnValue('product supplied') - variantSuppliedToOrderCycle: jasmine.createSpy('variantSuppliedToOrderCycle').and.returnValue('variant supplied') - exchangeDirection: jasmine.createSpy('exchangeDirection').and.returnValue('exchange direction') - toggleProducts: jasmine.createSpy('toggleProducts') - setExchangeVariants: jasmine.createSpy('setExchangeVariants') - addSupplier: jasmine.createSpy('addSupplier') - addDistributor: jasmine.createSpy('addDistributor') - removeExchange: jasmine.createSpy('removeExchange') - addCoordinatorFee: jasmine.createSpy('addCoordinatorFee') - removeCoordinatorFee: jasmine.createSpy('removeCoordinatorFee') - addExchangeFee: jasmine.createSpy('addExchangeFee') - removeExchangeFee: jasmine.createSpy('removeExchangeFee') - removeDistributionOfVariant: jasmine.createSpy('removeDistributionOfVariant') - update: jasmine.createSpy('update') - Enterprise = - index: jasmine.createSpy('index').and.returnValue('enterprises list') - supplied_products: 'supplied products' - suppliedVariants: jasmine.createSpy('suppliedVariants').and.returnValue('supplied variants') - totalVariants: jasmine.createSpy('totalVariants').and.returnValue('variants total') - EnterpriseFee = - index: jasmine.createSpy('index').and.returnValue('enterprise fees list') - forEnterprise: jasmine.createSpy('forEnterprise').and.returnValue('enterprise fees for enterprise') - - module('admin.orderCycles') - inject ($controller) -> - ctrl = $controller 'AdminEditOrderCycleCtrl', {$scope: scope, $location: location, OrderCycle: OrderCycle, Enterprise: Enterprise, EnterpriseFee: EnterpriseFee} - - it 'Loads enterprises and supplied products', -> - expect(Enterprise.index).toHaveBeenCalled() - expect(scope.enterprises).toEqual('enterprises list') - expect(scope.supplied_products).toEqual('supplied products') - - it 'Loads enterprise fees', -> - expect(EnterpriseFee.index).toHaveBeenCalled() - expect(scope.enterprise_fees).toEqual('enterprise fees list') - - it 'Loads order cycles', -> - expect(OrderCycle.load).toHaveBeenCalledWith('27') - - describe 'Reporting when all resources are loaded', -> - beforeEach inject (RequestMonitor) -> - RequestMonitor.loading = false - Enterprise.loaded = true - EnterpriseFee.loaded = true - OrderCycle.loaded = true - - it 'returns true when all resources are loaded', -> - expect(scope.loaded()).toBe(true) - - it 'returns false otherwise', -> - EnterpriseFee.loaded = false - expect(scope.loaded()).toBe(false) - - it "delegates suppliedVariants to Enterprise", -> - expect(scope.suppliedVariants('enterprise_id')).toEqual('supplied variants') - expect(Enterprise.suppliedVariants).toHaveBeenCalledWith('enterprise_id') - - it 'Delegates exchangeSelectedVariants to OrderCycle', -> - expect(scope.exchangeSelectedVariants('exchange')).toEqual('variants selected') - expect(OrderCycle.exchangeSelectedVariants).toHaveBeenCalledWith('exchange') - - it "delegates setExchangeVariants to OrderCycle", -> - scope.setExchangeVariants('exchange', 'variants', 'selected') - expect(OrderCycle.setExchangeVariants).toHaveBeenCalledWith('exchange', 'variants', 'selected') - - it 'Delegates totalVariants to Enterprise', -> - expect(scope.enterpriseTotalVariants('enterprise')).toEqual('variants total') - expect(Enterprise.totalVariants).toHaveBeenCalledWith('enterprise') - - it 'Delegates productSuppliedToOrderCycle to OrderCycle', -> - expect(scope.productSuppliedToOrderCycle('product')).toEqual('product supplied') - expect(OrderCycle.productSuppliedToOrderCycle).toHaveBeenCalledWith('product') - - it 'Delegates variantSuppliedToOrderCycle to OrderCycle', -> - expect(scope.variantSuppliedToOrderCycle('variant')).toEqual('variant supplied') - expect(OrderCycle.variantSuppliedToOrderCycle).toHaveBeenCalledWith('variant') - - it 'Delegates exchangeDirection to OrderCycle', -> - expect(scope.exchangeDirection('exchange')).toEqual('exchange direction') - expect(OrderCycle.exchangeDirection).toHaveBeenCalledWith('exchange') - - it 'Finds enterprises participating in the order cycle that have fees', -> - scope.enterprises = - 1: {id: 1, name: 'Eaterprises'} - 2: {id: 2, name: 'Pepper Tree Place'} - 3: {id: 3, name: 'South East'} - OrderCycle.participatingEnterpriseIds = jasmine.createSpy('participatingEnterpriseIds').and.returnValue([2]) - EnterpriseFee.enterprise_fees = [ {enterprise_id: 2} ] # Pepper Tree Place has a fee - expect(scope.enterprisesWithFees()).toEqual([ - {id: 2, name: 'Pepper Tree Place'} - ]) - - it 'Delegates enterpriseFeesForEnterprise to EnterpriseFee', -> - scope.enterpriseFeesForEnterprise('123') - expect(EnterpriseFee.forEnterprise).toHaveBeenCalledWith(123) - - it 'Adds order cycle suppliers', -> - scope.new_supplier_id = 'new supplier id' - scope.addSupplier(event) - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.addSupplier).toHaveBeenCalledWith('new supplier id') - - it 'Adds order cycle distributors', -> - scope.new_distributor_id = 'new distributor id' - scope.addDistributor(event) - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.addDistributor).toHaveBeenCalledWith('new distributor id') - - it 'Removes order cycle exchanges', -> - scope.removeExchange(event, 'exchange') - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.removeExchange).toHaveBeenCalledWith('exchange') - expect(scope.order_cycle_form.$dirty).toEqual true - - it 'Adds coordinator fees', -> - scope.addCoordinatorFee(event) - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.addCoordinatorFee).toHaveBeenCalled() - - it 'Removes coordinator fees', -> - scope.removeCoordinatorFee(event, 0) - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.removeCoordinatorFee).toHaveBeenCalledWith(0) - expect(scope.order_cycle_form.$dirty).toEqual true - - it 'Adds exchange fees', -> - scope.addExchangeFee(event) - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.addExchangeFee).toHaveBeenCalled() - - it 'Removes exchange fees', -> - scope.removeExchangeFee(event, 'exchange', 0) - expect(event.preventDefault).toHaveBeenCalled() - expect(OrderCycle.removeExchangeFee).toHaveBeenCalledWith('exchange', 0) - expect(scope.order_cycle_form.$dirty).toEqual true - - it 'Removes distribution of a variant', -> - scope.removeDistributionOfVariant('variant') - expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('variant') - - it 'Submits the order cycle via OrderCycle update', -> - eventMock = {preventDefault: jasmine.createSpy()} - scope.submit(eventMock,'/admin/order_cycles') - expect(eventMock.preventDefault).toHaveBeenCalled() - expect(OrderCycle.update).toHaveBeenCalledWith('/admin/order_cycles', scope.order_cycle_form) - - -describe 'OrderCycle services', -> - describe 'Enterprise service', -> - $httpBackend = null - Enterprise = null - - beforeEach -> - module 'admin.orderCycles' - inject ($injector, _$httpBackend_)-> - Enterprise = $injector.get('Enterprise') - $httpBackend = _$httpBackend_ - $httpBackend.whenGET('/admin/enterprises/for_order_cycle.json').respond [ - {id: 1, name: 'One', supplied_products: [1, 2], is_primary_producer: true} - {id: 2, name: 'Two', supplied_products: [3, 4]} - {id: 3, name: 'Three', supplied_products: [5, 6], sells: 'any'} - ] - - it 'loads enterprises as a hash', -> - enterprises = Enterprise.index() - $httpBackend.flush() - expect(enterprises).toEqual - 1: new Enterprise.Enterprise({id: 1, name: 'One', supplied_products: [1, 2], is_primary_producer: true}) - 2: new Enterprise.Enterprise({id: 2, name: 'Two', supplied_products: [3, 4]}) - 3: new Enterprise.Enterprise({id: 3, name: 'Three', supplied_products: [5, 6], sells: 'any'}) - - it 'reports its loadedness', -> - expect(Enterprise.loaded).toBe(false) - Enterprise.index() - $httpBackend.flush() - expect(Enterprise.loaded).toBe(true) - - it 'loads producers as an array', -> - Enterprise.index() - $httpBackend.flush() - expect(Enterprise.producer_enterprises).toEqual [new Enterprise.Enterprise({id: 1, name: 'One', supplied_products: [1, 2], is_primary_producer: true})] - - it 'loads hubs as an array', -> - Enterprise.index() - $httpBackend.flush() - expect(Enterprise.hub_enterprises).toEqual [new Enterprise.Enterprise({id: 3, name: 'Three', supplied_products: [5, 6], sells: 'any'})] - - it 'collates all supplied products', -> - enterprises = Enterprise.index() - $httpBackend.flush() - expect(Enterprise.supplied_products).toEqual [1, 2, 3, 4, 5, 6] - - it "finds supplied variants for an enterprise", -> - spyOn(Enterprise, 'variantsOf').and.returnValue(10) - Enterprise.index() - $httpBackend.flush() - expect(Enterprise.suppliedVariants(1)).toEqual [10, 10] - - describe "finding the variants of a product", -> - it "returns the master for products without variants", -> - p = - master_id: 1 - variants: [] - expect(Enterprise.variantsOf(p)).toEqual [1] - - it "returns the variant ids for products with variants", -> - p = - master_id: 1 - variants: [{id: 2}, {id: 3}] - expect(Enterprise.variantsOf(p)).toEqual [2, 3] - - it 'counts total variants supplied by an enterprise', -> - enterprise = - supplied_products: [ - {variants: []}, - {variants: []}, - {variants: [{}, {}, {}]} - ] - - expect(Enterprise.totalVariants(enterprise)).toEqual(5) - - it 'returns zero when enterprise is null', -> - expect(Enterprise.totalVariants(null)).toEqual(0) - - describe 'EnterpriseFee service', -> - $httpBackend = null - EnterpriseFee = null - - beforeEach -> - module 'admin.orderCycles' - inject ($injector, _$httpBackend_)-> - EnterpriseFee = $injector.get('EnterpriseFee') - $httpBackend = _$httpBackend_ - $httpBackend.whenGET('/admin/enterprise_fees/for_order_cycle.json').respond [ - {id: 1, name: "Yayfee", enterprise_id: 1} - {id: 2, name: "FeeTwo", enterprise_id: 2} - ] - - it 'loads enterprise fees', -> - enterprise_fees = EnterpriseFee.index() - $httpBackend.flush() - expected_fees = [ - new EnterpriseFee.EnterpriseFee({id: 1, name: "Yayfee", enterprise_id: 1}) - new EnterpriseFee.EnterpriseFee({id: 2, name: "FeeTwo", enterprise_id: 2}) - ] - for fee, i in enterprise_fees - expect(fee.id).toEqual(expected_fees[i].id) - - it 'reports its loadedness', -> - expect(EnterpriseFee.loaded).toBe(false) - EnterpriseFee.index() - $httpBackend.flush() - expect(EnterpriseFee.loaded).toBe(true) - - it 'returns enterprise fees for an enterprise', -> - all_enterprise_fees = EnterpriseFee.index() - $httpBackend.flush() - enterprise_fees = EnterpriseFee.forEnterprise(1) - expect(enterprise_fees).toEqual [ - new EnterpriseFee.EnterpriseFee({id: 1, name: "Yayfee", enterprise_id: 1}) - ] - - - describe 'OrderCycle service', -> - OrderCycle = null - $httpBackend = null - $window = null - - beforeEach -> - $window = {navigator: {userAgent: 'foo'}} - - module 'admin.orderCycles', ($provide)-> - $provide.value('$window', $window) - null - - inject ($injector, _$httpBackend_)-> - OrderCycle = $injector.get('OrderCycle') - $httpBackend = _$httpBackend_ - $httpBackend.whenGET('/admin/order_cycles/123.json').respond - id: 123 - name: 'Test Order Cycle' - coordinator_id: 456 - coordinator_fees: [] - exchanges: [ - {sender_id: 1, receiver_id: 456, incoming: true} - {sender_id: 456, receiver_id: 2, incoming: false} - ] - $httpBackend.whenGET('/admin/order_cycles/new.json').respond - id: 123 - name: 'New Order Cycle' - coordinator_id: 456 - coordinator_fees: [] - exchanges: [] - - it 'initialises order cycle', -> - expect(OrderCycle.order_cycle).toEqual {incoming_exchanges: [], outgoing_exchanges: []} - - it 'counts selected variants in an exchange', -> - result = OrderCycle.exchangeSelectedVariants({variants: {1: true, 2: false, 3: true}}) - expect(result).toEqual(2) - - describe "fetching exchange ids", -> - it "gets enterprise ids as ints", -> - OrderCycle.order_cycle.incoming_exchanges = [ - {enterprise_id: 1} - {enterprise_id: '2'} - ] - OrderCycle.order_cycle.outgoing_exchanges = [ - {enterprise_id: 3} - {enterprise_id: '4'} - ] - expect(OrderCycle.exchangeIds('incoming')).toEqual [1, 2] - - describe "checking for novel enterprises", -> - e1 = {id: 1} - e2 = {id: 2} - - beforeEach -> - OrderCycle.order_cycle.incoming_exchanges = [{enterprise_id: 1}] - OrderCycle.order_cycle.outgoing_exchanges = [{enterprise_id: 1}] - - it "detects novel suppliers", -> - expect(OrderCycle.novelSupplier(e1)).toBe false - expect(OrderCycle.novelSupplier(e2)).toBe true - - it "detects novel suppliers with enterprise as string id", -> - expect(OrderCycle.novelSupplier('1')).toBe false - expect(OrderCycle.novelSupplier('2')).toBe true - - it "detects novel distributors", -> - expect(OrderCycle.novelDistributor(e1)).toBe false - expect(OrderCycle.novelDistributor(e2)).toBe true - - it "detects novel distributors with enterprise as string id", -> - expect(OrderCycle.novelDistributor('1')).toBe false - expect(OrderCycle.novelDistributor('2')).toBe true - - - describe 'fetching the direction for an exchange', -> - it 'returns "incoming" for incoming exchanges', -> - exchange = {id: 1} - OrderCycle.order_cycle.incoming_exchanges = [exchange] - OrderCycle.order_cycle.outgoing_exchanges = [] - expect(OrderCycle.exchangeDirection(exchange)).toEqual 'incoming' - - it 'returns "outgoing" for outgoing exchanges', -> - exchange = {id: 1} - OrderCycle.order_cycle.incoming_exchanges = [] - OrderCycle.order_cycle.outgoing_exchanges = [exchange] - expect(OrderCycle.exchangeDirection(exchange)).toEqual 'outgoing' - - describe "setting exchange variants", -> - describe "when I have permissions to edit the variants", -> - beforeEach -> - OrderCycle.order_cycle["editable_variants_for_outgoing_exchanges"] = { 1: [1, 2, 3] } - - it "sets all variants to the provided value", -> - exchange = { enterprise_id: 1, incoming: false, variants: {2: false}} - OrderCycle.setExchangeVariants(exchange, [1, 2, 3], true) - expect(exchange.variants).toEqual {1: true, 2: true, 3: true} - - describe "when I don't have permissions to edit the variants", -> - beforeEach -> - OrderCycle.order_cycle["editable_variants_for_outgoing_exchanges"] = { 1: [] } - - it "does not change variants to the provided value", -> - exchange = { enterprise_id: 1, incoming: false, variants: {2: false}} - OrderCycle.setExchangeVariants(exchange, [1, 2, 3], true) - expect(exchange.variants).toEqual {2: false} - - describe 'adding suppliers', -> - exchange = null - - beforeEach -> - # Initialise OC - OrderCycle.new() - $httpBackend.flush() - - it 'adds the supplier to incoming exchanges', -> - OrderCycle.addSupplier('123') - expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [ - {enterprise_id: '123', incoming: true, active: true, variants: {}, enterprise_fees: []} - ] - - describe 'adding distributors', -> - exchange = null - - beforeEach -> - # Initialise OC - OrderCycle.new() - $httpBackend.flush() - - it 'adds the distributor to outgoing exchanges', -> - OrderCycle.addDistributor('123') - expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [ - {enterprise_id: '123', incoming: false, active: true, variants: {}, enterprise_fees: []} - ] - - describe 'removing exchanges', -> - exchange = null - - beforeEach -> - spyOn(OrderCycle, 'removeDistributionOfVariant') - exchange = - enterprise_id: '123' - active: true - incoming: false - variants: {1: true, 2: false, 3: true} - enterprise_fees: [] - - describe "removing incoming exchanges", -> - beforeEach -> - exchange.incoming = true - OrderCycle.order_cycle.incoming_exchanges = [exchange] - - it 'removes the exchange', -> - OrderCycle.removeExchange(exchange) - expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [] - - it 'removes distribution of all exchange variants', -> - OrderCycle.removeExchange(exchange) - expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('1') - expect(OrderCycle.removeDistributionOfVariant).not.toHaveBeenCalledWith('2') - expect(OrderCycle.removeDistributionOfVariant).toHaveBeenCalledWith('3') - - describe "removing outgoing exchanges", -> - beforeEach -> - exchange.incoming = false - OrderCycle.order_cycle.outgoing_exchanges = [exchange] - - it 'removes the exchange', -> - OrderCycle.removeExchange(exchange) - expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [] - - it "does not remove distribution of any variants", -> - OrderCycle.removeExchange(exchange) - expect(OrderCycle.removeDistributionOfVariant).not.toHaveBeenCalled() - - it 'adds coordinator fees', -> - # Initialise OC - OrderCycle.new() - $httpBackend.flush() - OrderCycle.addCoordinatorFee() - expect(OrderCycle.order_cycle.coordinator_fees).toEqual [{}] - - describe 'removing coordinator fees', -> - it 'removes a coordinator fee by index', -> - OrderCycle.order_cycle.coordinator_fees = [ - {id: 1} - {id: 2} - {id: 3} - ] - OrderCycle.removeCoordinatorFee(1) - expect(OrderCycle.order_cycle.coordinator_fees).toEqual [ - {id: 1} - {id: 3} - ] - - it 'adds exchange fees', -> - exchange = {enterprise_fees: []} - OrderCycle.addExchangeFee(exchange) - expect(exchange.enterprise_fees).toEqual [{}] - - describe 'removing exchange fees', -> - it 'removes an exchange fee by index', -> - exchange = - enterprise_fees: [ - {id: 1} - {id: 2} - {id: 3} - ] - OrderCycle.removeExchangeFee(exchange, 1) - expect(exchange.enterprise_fees).toEqual [ - {id: 1} - {id: 3} - ] - - it 'finds participating enterprise ids', -> - OrderCycle.order_cycle.incoming_exchanges = [ - {enterprise_id: 1} - {enterprise_id: 2} - ] - OrderCycle.order_cycle.outgoing_exchanges = [ - {enterprise_id: 2} - {enterprise_id: 3} - ] - expect(OrderCycle.participatingEnterpriseIds()).toEqual [1, 2, 3] - - describe 'fetching all variants supplied on incoming exchanges', -> - it 'collects variants from incoming exchanges', -> - OrderCycle.order_cycle.incoming_exchanges = [ - {variants: {1: true, 2: false}} - {variants: {3: false, 4: true}} - {variants: {5: true, 6: false}} - ] - expect(OrderCycle.incomingExchangesVariants()).toEqual [1, 4, 5] - - describe 'checking whether a product is supplied to the order cycle', -> - product_master_present = product_variant_present = product_master_absent = product_variant_absent = null - - beforeEach -> - product_master_present = - name: "Linseed (500g)" - master_id: 1 - variants: [] - product_variant_present = - name: "Linseed (500g)" - master_id: 2 - variants: [{id: 3}, {id: 4}] - product_master_absent = - name: "Linseed (500g)" - master_id: 5 - variants: [] - product_variant_absent = - name: "Linseed (500g)" - master_id: 6 - variants: [{id: 7}, {id: 8}] - - spyOn(OrderCycle, 'incomingExchangesVariants').and.returnValue([1, 3]) - - it 'returns true for products whose master is supplied', -> - expect(OrderCycle.productSuppliedToOrderCycle(product_master_present)).toBeTruthy() - - it 'returns true for products for whom a variant is supplied', -> - expect(OrderCycle.productSuppliedToOrderCycle(product_variant_present)).toBeTruthy() - - it 'returns false for products whose master is not supplied', -> - expect(OrderCycle.productSuppliedToOrderCycle(product_master_absent)).toBeFalsy() - - it 'returns false for products whose variants are not supplied', -> - expect(OrderCycle.productSuppliedToOrderCycle(product_variant_absent)).toBeFalsy() - - - describe 'checking whether a variant is supplied to the order cycle', -> - beforeEach -> - spyOn(OrderCycle, 'incomingExchangesVariants').and.returnValue([1, 3]) - - it 'returns true for variants that are supplied', -> - expect(OrderCycle.variantSuppliedToOrderCycle({id: 1})).toBeTruthy() - - it 'returns false for variants that are not supplied', -> - expect(OrderCycle.variantSuppliedToOrderCycle({id: 999})).toBeFalsy() - - - describe 'remove all distribution of a variant', -> - it 'removes the variant from every outgoing exchange', -> - OrderCycle.order_cycle.outgoing_exchanges = [ - {variants: {123: true, 234: true}} - {variants: {123: true, 333: true}} - ] - OrderCycle.removeDistributionOfVariant('123') - expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [ - {variants: {123: false, 234: true}} - {variants: {123: false, 333: true}} - ] - - describe 'loading an order cycle, reporting loadedness', -> - it 'reports its loadedness', -> - expect(OrderCycle.loaded).toBe(false) - OrderCycle.load('123') - $httpBackend.flush() - expect(OrderCycle.loaded).toBe(true) - - describe 'loading a new order cycle', -> - beforeEach -> - OrderCycle.new() - $httpBackend.flush() - - - it 'loads basic fields', -> - expect(OrderCycle.order_cycle.id).toEqual(123) - expect(OrderCycle.order_cycle.name).toEqual('New Order Cycle') - expect(OrderCycle.order_cycle.coordinator_id).toEqual(456) - - it 'initialises the incoming and outgoing exchanges', -> - expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [] - expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [] - - it 'removes the original exchanges array', -> - expect(OrderCycle.order_cycle.exchanges).toBeUndefined() - - describe 'loading an existing order cycle', -> - beforeEach -> - OrderCycle.load('123') - $httpBackend.flush() - - it 'loads basic fields', -> - expect(OrderCycle.order_cycle.id).toEqual(123) - expect(OrderCycle.order_cycle.name).toEqual('Test Order Cycle') - expect(OrderCycle.order_cycle.coordinator_id).toEqual(456) - - it 'splits exchanges into incoming and outgoing', -> - expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [ - sender_id: 1 - enterprise_id: 1 - incoming: true - active: true - ] - - expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [ - receiver_id: 2 - enterprise_id: 2 - incoming: false - active: true - ] - - it 'removes the original exchanges array', -> - expect(OrderCycle.order_cycle.exchanges).toBeUndefined() - - describe 'creating an order cycle', -> - beforeEach -> - spyOn(OrderCycle, 'confirmNoDistributors').and.returnValue true - - it 'redirects to the destination page on success', -> - OrderCycle.order_cycle = 'this is the order cycle' - spyOn(OrderCycle, 'dataForSubmit').and.returnValue('this is the submit data') - $httpBackend.expectPOST('/admin/order_cycles.json', { - order_cycle: 'this is the submit data' - }).respond {success: true} - - OrderCycle.create('/destination/page') - $httpBackend.flush() - expect($window.location).toEqual('/destination/page') - - it 'does not redirect on error', -> - OrderCycle.order_cycle = 'this is the order cycle' - spyOn(OrderCycle, 'dataForSubmit').and.returnValue('this is the submit data') - $httpBackend.expectPOST('/admin/order_cycles.json', { - order_cycle: 'this is the submit data' - }).respond 400, { errors: [] } - - OrderCycle.create('/destination/page') - $httpBackend.flush() - expect($window.location).toEqual(undefined) - - describe 'updating an order cycle', -> - beforeEach -> - spyOn(OrderCycle, 'confirmNoDistributors').and.returnValue true - - it 'redirects to the destination page on success', -> - form = jasmine.createSpyObj('order_cycle_form', ['$dirty', '$setPristine']) - OrderCycle.order_cycle = 'this is the order cycle' - spyOn(OrderCycle, 'dataForSubmit').and.returnValue('this is the submit data') - $httpBackend.expectPUT('/admin/order_cycles.json?reloading=1', { - order_cycle: 'this is the submit data' - }).respond {success: true} - - OrderCycle.update('/destination/page', form) - $httpBackend.flush() - expect($window.location).toEqual('/destination/page') - expect(form.$setPristine.calls.count()).toBe 1 - - it 'does not redirect on error', -> - OrderCycle.order_cycle = 'this is the order cycle' - spyOn(OrderCycle, 'dataForSubmit').and.returnValue('this is the submit data') - $httpBackend.expectPUT('/admin/order_cycles.json?reloading=1', { - order_cycle: 'this is the submit data' - }).respond 400, { errors: [] } - - OrderCycle.update('/destination/page') - $httpBackend.flush() - expect($window.location).toEqual(undefined) - - describe 'preparing data for form submission', -> - it 'calls all the methods', -> - OrderCycle.order_cycle = {foo: 'bar'} - spyOn(OrderCycle, 'removeInactiveExchanges') - spyOn(OrderCycle, 'translateCoordinatorFees') - spyOn(OrderCycle, 'translateExchangeFees') - OrderCycle.dataForSubmit() - expect(OrderCycle.removeInactiveExchanges).toHaveBeenCalled() - expect(OrderCycle.translateCoordinatorFees).toHaveBeenCalled() - expect(OrderCycle.translateExchangeFees).toHaveBeenCalled() - - it 'removes inactive exchanges', -> - data = - incoming_exchanges: [ - {enterprise_id: "1", active: false} - {enterprise_id: "2", active: true} - {enterprise_id: "3", active: false} - ] - outgoing_exchanges: [ - {enterprise_id: "4", active: true} - {enterprise_id: "5", active: false} - {enterprise_id: "6", active: true} - ] - - data = OrderCycle.removeInactiveExchanges(data) - - expect(data.incoming_exchanges).toEqual [ - {enterprise_id: "2", active: true} - ] - expect(data.outgoing_exchanges).toEqual [ - {enterprise_id: "4", active: true} - {enterprise_id: "6", active: true} - ] - - it 'converts coordinator fees into a list of ids', -> - order_cycle = - coordinator_fees: [ - {id: 1} - {id: 2} - ] - - data = OrderCycle.translateCoordinatorFees(order_cycle) - - expect(data.coordinator_fees).toBeUndefined() - expect(data.coordinator_fee_ids).toEqual [1, 2] - - it "preserves original data when converting coordinator fees", -> - OrderCycle.order_cycle = - coordinator_fees: [ - {id: 1} - {id: 2} - ] - - data = OrderCycle.deepCopy() - data = OrderCycle.translateCoordinatorFees(data) - - expect(OrderCycle.order_cycle.coordinator_fees).toEqual [{id: 1}, {id: 2}] - expect(OrderCycle.order_cycle.coordinator_fee_ids).toBeUndefined() - - describe "converting exchange fees into a list of ids", -> - order_cycle = null - data = null - - beforeEach -> - order_cycle = - incoming_exchanges: [ - enterprise_fees: [ - {id: 1} - {id: 2} - ] - ] - outgoing_exchanges: [ - enterprise_fees: [ - {id: 3} - {id: 4} - ] - ] - OrderCycle.order_cycle = order_cycle - - data = OrderCycle.deepCopy() - data = OrderCycle.translateExchangeFees(data) - - it 'converts exchange fees into a list of ids', -> - expect(data.incoming_exchanges[0].enterprise_fees).toBeUndefined() - expect(data.outgoing_exchanges[0].enterprise_fees).toBeUndefined() - expect(data.incoming_exchanges[0].enterprise_fee_ids).toEqual [1, 2] - expect(data.outgoing_exchanges[0].enterprise_fee_ids).toEqual [3, 4] - - it "preserves original data when converting exchange fees", -> - expect(order_cycle.incoming_exchanges[0].enterprise_fees).toEqual [{id: 1}, {id: 2}] - expect(order_cycle.outgoing_exchanges[0].enterprise_fees).toEqual [{id: 3}, {id: 4}] - expect(order_cycle.incoming_exchanges[0].enterprise_fee_ids).toBeUndefined() - expect(order_cycle.outgoing_exchanges[0].enterprise_fee_ids).toBeUndefined() - - describe "confirming when there are no distributors", -> - order_cycle_with_exchanges = order_cycle_without_exchanges = null - - beforeEach -> - order_cycle_with_exchanges = - outgoing_exchanges: [{}] - order_cycle_without_exchanges = - outgoing_exchanges: [] - - it "returns true when there are distributors", -> - spyOn(window, 'confirm') - OrderCycle.order_cycle = order_cycle_with_exchanges - expect(OrderCycle.confirmNoDistributors()).toBe true - expect(window.confirm).not.toHaveBeenCalled() - - it "returns true when there are no distributors but the user confirms", -> - spyOn(window, 'confirm').and.returnValue(true) - OrderCycle.order_cycle = order_cycle_without_exchanges - expect(OrderCycle.confirmNoDistributors()).toBe true - expect(window.confirm).toHaveBeenCalled() - - it "returns false when there are no distributors and the user does not confirm", -> - spyOn(window, 'confirm').and.returnValue(false) - OrderCycle.order_cycle = order_cycle_without_exchanges - expect(OrderCycle.confirmNoDistributors()).toBe false - expect(window.confirm).toHaveBeenCalled() From bf4ce90f988381ca1c293cb61e7dfa9469349fcd Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Thu, 14 Feb 2019 15:56:44 +0000 Subject: [PATCH 04/57] Add product.on_hand and product.on_demand positive test, these should be returned if no variants are provided in the input Also, removed product.count_on_hand negative test (there's no such attriubte in v2) and added product.on_hand, this should not be returned because a variant is provided --- .../admin/bulk_product_update_spec.js.coffee | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee index c1c34cbe6b..a26fdd523e 100644 --- a/spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee @@ -166,7 +166,22 @@ describe "filtering products for submission to database", -> variant_unit_scale: 1 ] - # TODO Not an exhaustive test, is there a better way to do this? + it "returns stock properties of a product if no variant is provided", -> + available_on = new Date() + + testProduct = + id: 1 + name: "TestProduct" + on_hand: 0 + on_demand: false + + expect(filterSubmitProducts([testProduct])).toEqual [ + id: 1 + name: "TestProduct" + on_hand: 0 + on_demand: false + ] + it "only returns the properties of products which ought to be updated", -> available_on = new Date() @@ -183,12 +198,11 @@ describe "filtering products for submission to database", -> shipping_category_id: null created_at: null updated_at: null - count_on_hand: 0 + on_hand: 0 + on_demand: false producer_id: 5 - group_buy: null group_buy_unit_size: null - on_demand: false master: id: 2 unit_value: 250 From 4a35b53b4366663d2cab58c43aa31bb60c7ff777 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Thu, 14 Feb 2019 17:02:52 +0000 Subject: [PATCH 05/57] Remove dead overrides --- ...hange_distributor_form_to_checkout_address.rb | 6 ------ .../add_distributor_details_js_to_product.rb | 5 ----- .../add_distributor_details_to_product.rb | 5 ----- .../add_distributor_to_add_to_cart_form.rb | 5 ----- app/overrides/add_source_to_product.rb | 5 ----- app/overrides/replace_checkout_payment_button.rb | 5 ----- ...ping_address_form_with_distributor_details.rb | 5 ----- ...ons_to_delivery_instructions.html.haml.deface | 5 ----- ...ng_method_to_delivery_method.html.haml.deface | 2 -- .../add_new_save_checkout_button.html.erb.deface | 16 ---------------- ...urrent_distribution_variants.html.haml.deface | 16 ---------------- .../_taxons/hide_taxon_crumbs.html.haml.deface | 1 - 12 files changed, 76 deletions(-) delete mode 100644 app/overrides/add_change_distributor_form_to_checkout_address.rb delete mode 100644 app/overrides/add_distributor_details_js_to_product.rb delete mode 100644 app/overrides/add_distributor_details_to_product.rb delete mode 100644 app/overrides/add_distributor_to_add_to_cart_form.rb delete mode 100644 app/overrides/add_source_to_product.rb delete mode 100644 app/overrides/replace_checkout_payment_button.rb delete mode 100644 app/overrides/replace_shipping_address_form_with_distributor_details.rb delete mode 100644 app/overrides/spree/checkout/_delivery/shipping_instructions_to_delivery_instructions.html.haml.deface delete mode 100644 app/overrides/spree/checkout/_delivery/shipping_method_to_delivery_method.html.haml.deface delete mode 100644 app/overrides/spree/checkout/edit/add_new_save_checkout_button.html.erb.deface delete mode 100644 app/overrides/spree/products/_cart_form/only_show_current_distribution_variants.html.haml.deface delete mode 100644 app/overrides/spree/products/_taxons/hide_taxon_crumbs.html.haml.deface diff --git a/app/overrides/add_change_distributor_form_to_checkout_address.rb b/app/overrides/add_change_distributor_form_to_checkout_address.rb deleted file mode 100644 index 19b91d2b1f..0000000000 --- a/app/overrides/add_change_distributor_form_to_checkout_address.rb +++ /dev/null @@ -1,6 +0,0 @@ -# Disabled until this form takes order cycles into account -# Deface::Override.new(:virtual_path => "spree/checkout/edit", -# :insert_after => "[data-hook='checkout_summary_box']", -# :partial => "spree/checkout/other_available_distributors", -# :name => "add_change_distributor_form_to_checkout_address", -# :original => '60b9b1a39079f4efa85ff0425108edf671349d4f') diff --git a/app/overrides/add_distributor_details_js_to_product.rb b/app/overrides/add_distributor_details_js_to_product.rb deleted file mode 100644 index 607735b992..0000000000 --- a/app/overrides/add_distributor_details_js_to_product.rb +++ /dev/null @@ -1,5 +0,0 @@ -Deface::Override.new(:virtual_path => "spree/products/show", - :insert_after => "[data-hook='product_show']", - :text => "<%= javascript_include_tag main_app.distributors_enterprises_path(:format => :js) %>", - :name => "add_distributor_details_js_to_product", - :original => 'b05ac497efeeebd4464f29891fd2c4a0f60c24d9') diff --git a/app/overrides/add_distributor_details_to_product.rb b/app/overrides/add_distributor_details_to_product.rb deleted file mode 100644 index dbe657bf96..0000000000 --- a/app/overrides/add_distributor_details_to_product.rb +++ /dev/null @@ -1,5 +0,0 @@ -Deface::Override.new(:virtual_path => "spree/products/show", - :insert_before => "[data-hook='cart_form']", - :partial => "spree/products/distributor_details", - :name => "add_distributor_details_to_product", - :original => '789e3f5f6f36a8cd4115d7342752a37735659298') diff --git a/app/overrides/add_distributor_to_add_to_cart_form.rb b/app/overrides/add_distributor_to_add_to_cart_form.rb deleted file mode 100644 index ad47700473..0000000000 --- a/app/overrides/add_distributor_to_add_to_cart_form.rb +++ /dev/null @@ -1,5 +0,0 @@ -Deface::Override.new(:virtual_path => "spree/products/_cart_form", - :replace => "[data-hook='product_price'] .add-to-cart", - :partial => "spree/products/add_to_cart", - :name => "add_distributor_to_add_to_cart_form", - :original => '294014222132c795048fc6c7866971a332672c87') diff --git a/app/overrides/add_source_to_product.rb b/app/overrides/add_source_to_product.rb deleted file mode 100644 index ded798e840..0000000000 --- a/app/overrides/add_source_to_product.rb +++ /dev/null @@ -1,5 +0,0 @@ -Deface::Override.new(:virtual_path => "spree/products/show", - :insert_bottom => "[data-hook='product_left_part_wrap']", - :partial => "spree/products/source", - :name => "add_source_to_product", - :original => 'bce3ba4847b3eac8ae061774a664ac4951d3d9db') diff --git a/app/overrides/replace_checkout_payment_button.rb b/app/overrides/replace_checkout_payment_button.rb deleted file mode 100644 index 60a198817c..0000000000 --- a/app/overrides/replace_checkout_payment_button.rb +++ /dev/null @@ -1,5 +0,0 @@ -Deface::Override.new(:virtual_path => "spree/checkout/_payment", - :replace => "code[erb-loud]:contains('submit_tag t(:save_and_continue)')", - :text => "<%= submit_tag I18n.t(:process_my_order), :class => 'continue button primary' %>", - :name => "replace_checkout_payment_button", - :original => 'ce2043a01931b3bc16b045302ebb0e0bb9150b67') diff --git a/app/overrides/replace_shipping_address_form_with_distributor_details.rb b/app/overrides/replace_shipping_address_form_with_distributor_details.rb deleted file mode 100644 index c408dc8ed4..0000000000 --- a/app/overrides/replace_shipping_address_form_with_distributor_details.rb +++ /dev/null @@ -1,5 +0,0 @@ -Deface::Override.new(:virtual_path => "spree/checkout/_address", - :replace => "[data-hook='shipping_fieldset_wrapper']", - :partial => "spree/checkout/distributor", - :name => "replace_shipping_address_form_with_distributor_details", - :original => '53e219f90a2e1ba702a767261d0c2afe100ac751') diff --git a/app/overrides/spree/checkout/_delivery/shipping_instructions_to_delivery_instructions.html.haml.deface b/app/overrides/spree/checkout/_delivery/shipping_instructions_to_delivery_instructions.html.haml.deface deleted file mode 100644 index badf49339c..0000000000 --- a/app/overrides/spree/checkout/_delivery/shipping_instructions_to_delivery_instructions.html.haml.deface +++ /dev/null @@ -1,5 +0,0 @@ -/ replace_contents '#shipping_method p#minstrs' - -= form.label :special_instructions, t(:delivery_instructions) -%br/ -= form.text_area :special_instructions, :cols => 40, :rows => 7 diff --git a/app/overrides/spree/checkout/_delivery/shipping_method_to_delivery_method.html.haml.deface b/app/overrides/spree/checkout/_delivery/shipping_method_to_delivery_method.html.haml.deface deleted file mode 100644 index 817a27b0ca..0000000000 --- a/app/overrides/spree/checkout/_delivery/shipping_method_to_delivery_method.html.haml.deface +++ /dev/null @@ -1,2 +0,0 @@ -/ replace_contents '#shipping_method legend' -= t(:delivery_method) diff --git a/app/overrides/spree/checkout/edit/add_new_save_checkout_button.html.erb.deface b/app/overrides/spree/checkout/edit/add_new_save_checkout_button.html.erb.deface deleted file mode 100644 index f70c064f72..0000000000 --- a/app/overrides/spree/checkout/edit/add_new_save_checkout_button.html.erb.deface +++ /dev/null @@ -1,16 +0,0 @@ - - -<% # Add a new 'Save and Continue/Process My Order' button under Order Summary on the checkout pages %> - -
- <%= submit_tag @order.state == "payment" ? t(:process_my_order) : t(:save_and_continue), - :class => "continue button primary large", - :form=> "checkout_form_#{@order.state}" %> - -
diff --git a/app/overrides/spree/products/_cart_form/only_show_current_distribution_variants.html.haml.deface b/app/overrides/spree/products/_cart_form/only_show_current_distribution_variants.html.haml.deface deleted file mode 100644 index adfe5af84f..0000000000 --- a/app/overrides/spree/products/_cart_form/only_show_current_distribution_variants.html.haml.deface +++ /dev/null @@ -1,16 +0,0 @@ -/ replace_contents '#product-variants' - -%h6.product-section-title= t(:variants) -%ul - - has_checked = false - - @product.variants.active(current_currency).each_with_index do |v,index| - - next if v.option_values.empty? || (!v.in_stock && !Spree::Config[:show_zero_stock_products]) - - next if current_order_cycle.present? && !current_order_cycle.has_variant?(v) # All copied from spree apart from this line - - checked = !has_checked && (v.in_stock || Spree::Config[:allow_backorders]) - - has_checked = true if checked - %li - = radio_button_tag "products[#{@product.id}]", v.id, checked, :disabled => !v.in_stock && !Spree::Config[:allow_backorders], 'data-price' => v.price_in(current_currency).display_price - %label{:for => ['products', @product.id, v.id].join('_')} - %span.variant-description= variant_options v - - if variant_price v - %span.price.diff= variant_price v diff --git a/app/overrides/spree/products/_taxons/hide_taxon_crumbs.html.haml.deface b/app/overrides/spree/products/_taxons/hide_taxon_crumbs.html.haml.deface deleted file mode 100644 index 524433516c..0000000000 --- a/app/overrides/spree/products/_taxons/hide_taxon_crumbs.html.haml.deface +++ /dev/null @@ -1 +0,0 @@ -/ remove '#taxon-crumbs' From a369b48bf49cb80ab321a29ab0d09797a464a76a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 14 Feb 2019 19:30:48 +0000 Subject: [PATCH 06/57] Bump stripe from 4.5.0 to 4.9.0 Bumps [stripe](https://github.com/stripe/stripe-ruby) from 4.5.0 to 4.9.0. - [Release notes](https://github.com/stripe/stripe-ruby/releases) - [Changelog](https://github.com/stripe/stripe-ruby/blob/master/CHANGELOG.md) - [Commits](https://github.com/stripe/stripe-ruby/compare/v4.5.0...v4.9.0) Signed-off-by: dependabot[bot] --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 93aa430690..5fa4a1fa6d 100644 --- a/Gemfile +++ b/Gemfile @@ -23,7 +23,7 @@ gem 'spree_auth_devise', github: 'openfoodfoundation/spree_auth_devise', branch: # - Change type of password from string to password to hide it in the form gem 'spree_paypal_express', github: "openfoodfoundation/better_spree_paypal_express", branch: "spree-upgrade-intermediate" #gem 'spree_paypal_express', github: "spree-contrib/better_spree_paypal_express", branch: "1-3-stable" -gem 'stripe', '~> 4.5.0' +gem 'stripe', '~> 4.9.0' # We need at least this version to have Digicert's root certificate # which is needed for Pin Payments (and possibly others). gem 'activemerchant', '~> 1.78' diff --git a/Gemfile.lock b/Gemfile.lock index 2790f7ae51..e9451981a3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -724,7 +724,7 @@ GEM tilt (~> 1.1, != 1.3.0) state_machine (1.2.0) stringex (1.3.3) - stripe (4.5.0) + stripe (4.9.0) faraday (~> 0.13) net-http-persistent (~> 3.0) therubyracer (0.12.0) @@ -863,7 +863,7 @@ DEPENDENCIES spree_paypal_express! spring (= 1.7.2) spring-commands-rspec - stripe (~> 4.5.0) + stripe (~> 4.9.0) therubyracer (= 0.12.0) timecop truncate_html From 75a79717cf0edbeb64145227996273b549431364 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Fri, 15 Feb 2019 12:41:52 +0000 Subject: [PATCH 07/57] Adapt scope_variant_to_hub to new VO rules where on_demand nil is seen as use_producer_settings --- app/models/variant_override.rb | 2 +- lib/open_food_network/scope_variant_to_hub.rb | 18 ++++++------------ .../scope_variant_to_hub_spec.rb | 5 +++-- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/app/models/variant_override.rb b/app/models/variant_override.rb index dd3b22a5e0..cea630719b 100644 --- a/app/models/variant_override.rb +++ b/app/models/variant_override.rb @@ -57,7 +57,7 @@ class VariantOverride < ActiveRecord::Base end def stock_overridden? - count_on_hand.present? + on_demand == false && count_on_hand.present? end def decrement_stock!(quantity) diff --git a/lib/open_food_network/scope_variant_to_hub.rb b/lib/open_food_network/scope_variant_to_hub.rb index bc2afa63da..55d8dd92b1 100644 --- a/lib/open_food_network/scope_variant_to_hub.rb +++ b/lib/open_food_network/scope_variant_to_hub.rb @@ -29,21 +29,15 @@ module OpenFoodNetwork end def count_on_hand - @variant_override.andand.count_on_hand || super + return super unless @variant_override.andand.stock_overridden? + + @variant_override.count_on_hand end def on_demand - if @variant_override.andand.on_demand.nil? - if @variant_override.andand.count_on_hand.present? - # If we're overriding the stock level of an on_demand variant, show it as not - # on_demand, so our stock control can take effect. - false - else - super - end - else - @variant_override.andand.on_demand - end + return super if @variant_override.andand.on_demand.nil? + + @variant_override.andand.on_demand end def decrement!(attribute, by = 1) diff --git a/spec/lib/open_food_network/scope_variant_to_hub_spec.rb b/spec/lib/open_food_network/scope_variant_to_hub_spec.rb index aa5ce4b317..03f19c62eb 100644 --- a/spec/lib/open_food_network/scope_variant_to_hub_spec.rb +++ b/spec/lib/open_food_network/scope_variant_to_hub_spec.rb @@ -1,3 +1,4 @@ +require 'spec_helper' require 'open_food_network/scope_variant_to_hub' module OpenFoodNetwork @@ -82,9 +83,9 @@ module OpenFoodNetwork before { vo.update_column(:on_demand, nil) } context "when count_on_hand is set" do - it "returns false" do + it "returns variant's on_demand" do scoper.scope v - expect(v.on_demand).to be false + expect(v.on_demand).to be true end end From ca2c8e6ff2ca4145f0ed59e739fdfb7fc1dd68ac Mon Sep 17 00:00:00 2001 From: Kristina Lim Date: Thu, 14 Feb 2019 22:18:01 +1100 Subject: [PATCH 08/57] Add test showing non-"eligible" entries included --- .../report_service_spec.rb | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb b/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb index 2161bbb4c6..b4c8162afc 100644 --- a/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb +++ b/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb @@ -151,6 +151,36 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do end end + describe "data exclusions" do + describe "invalid adjustments (through 'eligible') like failed payments" do + let!(:customer_order) { prepare_order(customer: customer) } + + before do + # Make the payment fail. See Spree::Payment#revoke_adjustment_eligibility. + payment = customer_order.payments.first + payment.state = "failed" + payment.save! + end + + it "is included" do + totals = service.list + + expect(totals.length).to eq(2) + + expected_result = [ + ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", + nil, nil, nil, "2.00"], + ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", + nil, nil, "Platform Rate", "1.00"] + ] + + expected_result.each_with_index do |expected_attributes, row_index| + expect_total_attributes(totals[row_index], expected_attributes) + end + end + end + end + describe "handling of more complex cases" do context "with non-sender fee for incoming exchange and non-receiver fee for outgoing" do let!(:variant) do From 8ce7337c9d4d5c63d5ea192962f2954860eb6e3c Mon Sep 17 00:00:00 2001 From: Kristina Lim Date: Mon, 18 Feb 2019 12:59:19 +1100 Subject: [PATCH 09/57] Add test showing $0 non-mandatory adjustments included --- .../report_service_spec.rb | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb b/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb index b4c8162afc..751f06187e 100644 --- a/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb +++ b/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb @@ -179,6 +179,45 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do end end end + + describe "non-mandatory $0 adjustments (through 'eligible')" do + let!(:variant) { prepare_variant(outgoing_exchange_fees: [enterprise_fee]) } + + let!(:enterprise_fee) do + create(:enterprise_fee, :per_item, name: "Sample Enterprise Fee", enterprise: distributor, + fee_type: "admin", amount: 0) + end + + let!(:customer_order) { prepare_order(customer: customer) } + + before do + # Change "eligible" in enterprise fee adjustment to false. $0 adjustments that are not + # mandatory are set to be ineligible, but there are no non-mandatory adjustments supported + # by the report yet. + adjustment = Spree::Adjustment.where(originator_type: "EnterpriseFee").first + adjustment.eligible = false + adjustment.save! + end + + it "is included" do + totals = service.list + + expect(totals.length).to eq(3) + + expected_result = [ + ["Admin", "Sample Distributor", "Sample Enterprise Fee", "Sample Customer", + "Outgoing", "Sample Distributor", nil, "0.00"], + ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", + nil, nil, nil, "2.00"], + ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", + nil, nil, "Platform Rate", "1.00"] + ] + + expected_result.each_with_index do |expected_attributes, row_index| + expect_total_attributes(totals[row_index], expected_attributes) + end + end + end end describe "handling of more complex cases" do From 19f99255fa64d6063734c3a4b0953d8b30ff5d2d Mon Sep 17 00:00:00 2001 From: Kristina Lim Date: Mon, 18 Feb 2019 09:30:03 +0800 Subject: [PATCH 10/57] Exclude non-eligible entries from report --- .../reports/enterprise_fee_summary/scope.rb | 2 +- .../reports/enterprise_fee_summary/report_service_spec.rb | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb index 9ebec61417..9760e80933 100644 --- a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb +++ b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb @@ -46,7 +46,7 @@ module OrderManagement def find_adjustments chain_to_scope do - Spree::Adjustment + Spree::Adjustment.eligible end end diff --git a/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb b/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb index 751f06187e..2fccd7bce0 100644 --- a/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb +++ b/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb @@ -165,11 +165,9 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do it "is included" do totals = service.list - expect(totals.length).to eq(2) + expect(totals.length).to eq(1) expected_result = [ - ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", - nil, nil, nil, "2.00"], ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", nil, nil, "Platform Rate", "1.00"] ] @@ -202,11 +200,9 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do it "is included" do totals = service.list - expect(totals.length).to eq(3) + expect(totals.length).to eq(2) expected_result = [ - ["Admin", "Sample Distributor", "Sample Enterprise Fee", "Sample Customer", - "Outgoing", "Sample Distributor", nil, "0.00"], ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", nil, nil, nil, "2.00"], ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", From 36fd385f7d4db12e3cbde259fdd6e0d89e967469 Mon Sep 17 00:00:00 2001 From: Kristina Lim Date: Mon, 18 Feb 2019 16:07:10 +0800 Subject: [PATCH 11/57] Add test showing $0 mandatory adjustments included --- .../report_service_spec.rb | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb b/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb index 2fccd7bce0..e9fbc62f89 100644 --- a/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb +++ b/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb @@ -214,6 +214,31 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do end end end + + describe "$0 mandatory adjustments" do + let!(:payment_method) do + create(:payment_method, :per_item, amount: 0, name: "Sample Payment Method") + end + + let!(:customer_order) { prepare_order(customer: customer) } + + it "is included" do + totals = service.list + + expect(totals.length).to eq(2) + + expected_result = [ + ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", + nil, nil, nil, "0.00"], + ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", + nil, nil, "Platform Rate", "1.00"] + ] + + expected_result.each_with_index do |expected_attributes, row_index| + expect_total_attributes(totals[row_index], expected_attributes) + end + end + end end describe "handling of more complex cases" do From b0a82b0e214686899aedc3967e59eebb0d9e4c1b Mon Sep 17 00:00:00 2001 From: Kristina Lim Date: Mon, 18 Feb 2019 16:52:40 +0800 Subject: [PATCH 12/57] Exclude $0 adjustments from report --- .../reports/enterprise_fee_summary/scope.rb | 6 +++++- .../reports/enterprise_fee_summary/report_service_spec.rb | 4 +--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb index 9760e80933..d6bcb8554a 100644 --- a/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb +++ b/engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb @@ -17,7 +17,7 @@ module OrderManagement end def result - group_data.select_attributes + group_data.exclude_groups_with_zero_total.select_attributes @scope.all end @@ -336,6 +336,10 @@ module OrderManagement if params.payment_method_ids.present? end + def exclude_groups_with_zero_total + filter_scope("spree_adjustments.amount != 0") + end + def group_data chain_to_scope do group("enterprise_fees.id", "enterprises.id", "customers.id", "hubs.id", diff --git a/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb b/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb index e9fbc62f89..38a760dc96 100644 --- a/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb +++ b/engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/report_service_spec.rb @@ -225,11 +225,9 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do it "is included" do totals = service.list - expect(totals.length).to eq(2) + expect(totals.length).to eq(1) expected_result = [ - ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", - nil, nil, nil, "0.00"], ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", nil, nil, "Platform Rate", "1.00"] ] From f0842fcbe5603d8a76b222c9eec6e49caea2cb57 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Mon, 18 Feb 2019 21:54:07 +0000 Subject: [PATCH 13/57] Convert all calls to variant.count_on_hand to variant.on_hand --- .../darkswarm/services/cart.js.coffee | 10 ++-- .../templates/out_of_stock.html.haml | 6 +-- .../shop_variant_no_group_buy.html.haml | 4 +- .../shop_variant_with_group_buy.html.haml | 2 +- app/models/product_import/entry_processor.rb | 2 +- app/models/product_import/entry_validator.rb | 1 - .../product_import/products_reset_strategy.rb | 12 ++--- .../product_import/spreadsheet_entry.rb | 2 +- app/serializers/api/variant_serializer.rb | 2 +- app/services/order_factory.rb | 2 +- app/views/shop/products/_form.html.haml | 2 +- .../spree/admin/variants/_autocomplete.js.erb | 6 +-- app/views/spree/admin/variants/search.rabl | 2 +- spec/controllers/cart_controller_spec.rb | 4 +- .../spree/admin/variants_controller_spec.rb | 2 +- spec/factories.rb | 2 +- spec/features/admin/reports_spec.rb | 6 +-- .../shopping/variant_overrides_spec.rb | 8 ++-- ...ariant_overrides_controller_spec.js.coffee | 6 +-- .../unit/bulk_product_update_spec.js.coffee | 2 +- .../darkswarm/services/cart_spec.js.coffee | 6 +-- spec/jobs/subscription_placement_job_spec.rb | 12 ++--- .../lettuce_share_report_spec.rb | 2 +- .../products_renderer_spec.rb | 2 +- .../scope_variant_to_hub_spec.rb | 8 ++-- .../products_reset_strategy_spec.rb | 46 +++++++++---------- spec/models/spree/product_spec.rb | 10 ++-- spec/requests/shop_spec.rb | 16 +++---- spec/serializers/variant_serializer_spec.rb | 2 +- spec/services/order_factory_spec.rb | 2 +- spec/services/order_syncer_spec.rb | 4 +- spec/services/variants_stock_levels_spec.rb | 6 +-- 32 files changed, 99 insertions(+), 100 deletions(-) diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee index 849af562b8..b0311cb402 100644 --- a/app/assets/javascripts/darkswarm/services/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee @@ -62,12 +62,12 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, $modal, $roo for li in @line_items when li.quantity > 0 if stockLevels[li.variant.id]? - li.variant.count_on_hand = stockLevels[li.variant.id].on_hand - if li.quantity > li.variant.count_on_hand - li.quantity = li.variant.count_on_hand + li.variant.on_hand = stockLevels[li.variant.id].on_hand + if li.quantity > li.variant.on_hand + li.quantity = li.variant.on_hand scope.variants.push li.variant - if li.variant.count_on_hand == 0 && li.max_quantity > li.variant.count_on_hand - li.max_quantity = li.variant.count_on_hand + if li.variant.on_hand == 0 && li.max_quantity > li.variant.on_hand + li.max_quantity = li.variant.on_hand scope.variants.push(li.variant) unless li.variant in scope.variants if scope.variants.length > 0 diff --git a/app/assets/javascripts/templates/out_of_stock.html.haml b/app/assets/javascripts/templates/out_of_stock.html.haml index a92c721c25..4ee27e7aa1 100644 --- a/app/assets/javascripts/templates/out_of_stock.html.haml +++ b/app/assets/javascripts/templates/out_of_stock.html.haml @@ -9,7 +9,7 @@ %p{'ng-repeat' => "v in variants"} %em {{ v.name_to_display }} - {{ v.unit_to_display }} - %span{'ng-if' => "v.count_on_hand == 0"} + %span{'ng-if' => "v.on_hand == 0"} {{ 'js.out_of_stock.now_out_of_stock' | t }} - %span{'ng-if' => "v.count_on_hand > 0"} - {{ 'js.out_of_stock.only_n_remainging' | t:{ num: v.count_on_hand } }} + %span{'ng-if' => "v.on_hand > 0"} + {{ 'js.out_of_stock.only_n_remainging' | t:{ num: v.on_hand } }} diff --git a/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml b/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml index 0441156c2c..2e7e49a0ca 100644 --- a/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml +++ b/app/assets/javascripts/templates/partials/shop_variant_no_group_buy.html.haml @@ -7,6 +7,6 @@ placeholder: "0", "ofn-disable-scroll" => true, "ng-model" => "variant.line_item.quantity", - "ofn-on-hand" => "{{variant.on_demand && 9999 || variant.count_on_hand }}", - "ng-disabled" => "!variant.on_demand && variant.count_on_hand == 0", + "ofn-on-hand" => "{{variant.on_demand && 9999 || variant.on_hand }}", + "ng-disabled" => "!variant.on_demand && variant.on_hand == 0", name: "variants[{{::variant.id}}]", id: "variants_{{::variant.id}}"} diff --git a/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml b/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml index 05bdf468b4..169b33391c 100644 --- a/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml +++ b/app/assets/javascripts/templates/partials/shop_variant_with_group_buy.html.haml @@ -9,7 +9,7 @@ "ng-model" => "variant.line_item.quantity", placeholder: "{{::'shop_variant_quantity_min' | t}}", "ofn-disable-scroll" => true, - "ofn-on-hand" => "{{variant.on_demand && 9999 || variant.count_on_hand }}", + "ofn-on-hand" => "{{variant.on_demand && 9999 || variant.on_hand }}", name: "variants[{{::variant.id}}]", id: "variants_{{::variant.id}}"} %span.bulk-input %input.bulk.second{type: :number, diff --git a/app/models/product_import/entry_processor.rb b/app/models/product_import/entry_processor.rb index bd9ef94d45..b7aecee078 100644 --- a/app/models/product_import/entry_processor.rb +++ b/app/models/product_import/entry_processor.rb @@ -214,7 +214,7 @@ module ProductImport object.assign_attributes(attribute => setting['value']) when 'overwrite_empty' if object.public_send(attribute).blank? || - ((attribute == 'on_hand' || attribute == 'count_on_hand') && + ((attribute == 'on_hand') && entry.on_hand_nil) object.assign_attributes(attribute => setting['value']) diff --git a/app/models/product_import/entry_validator.rb b/app/models/product_import/entry_validator.rb index 090c55e01d..39e3d761f9 100644 --- a/app/models/product_import/entry_validator.rb +++ b/app/models/product_import/entry_validator.rb @@ -372,7 +372,6 @@ module ProductImport return if entry.on_hand.present? object.on_hand = 0 if object.respond_to?(:on_hand) - object.count_on_hand = 0 if object.respond_to?(:count_on_hand) entry.on_hand_nil = true end diff --git a/app/models/product_import/products_reset_strategy.rb b/app/models/product_import/products_reset_strategy.rb index 0dc005af82..989f5a275c 100644 --- a/app/models/product_import/products_reset_strategy.rb +++ b/app/models/product_import/products_reset_strategy.rb @@ -9,7 +9,7 @@ module ProductImport return 0 if enterprise_ids.blank? - reset_variants_count_on_hand(enterprise_variants_relation) + reset_variants_on_hand(enterprise_variants_relation) end private @@ -29,17 +29,17 @@ module ProductImport relation.where('spree_variants.id NOT IN (?)', excluded_items_ids) end - def reset_variants_count_on_hand(variants) + def reset_variants_on_hand(variants) updated_records_count = 0 variants.each do |variant| - updated_records_count += 1 if reset_variant_count_on_hand(variant) + updated_records_count += 1 if reset_variant_on_hand(variant) end updated_records_count end - def reset_variant_count_on_hand(variant) - variant.count_on_hand = 0 - variant.count_on_hand.zero? + def reset_variant_on_hand(variant) + variant.on_hand = 0 + variant.on_hand.zero? end end end diff --git a/app/models/product_import/spreadsheet_entry.rb b/app/models/product_import/spreadsheet_entry.rb index c87f46ec0e..990124e24b 100644 --- a/app/models/product_import/spreadsheet_entry.rb +++ b/app/models/product_import/spreadsheet_entry.rb @@ -16,7 +16,7 @@ module ProductImport :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, + :price, :on_hand, :on_demand, :tax_category_id, :shipping_category_id, :description, :import_date, :enterprise, :enterprise_id diff --git a/app/serializers/api/variant_serializer.rb b/app/serializers/api/variant_serializer.rb index 59600b6ab3..f4845dc6af 100644 --- a/app/serializers/api/variant_serializer.rb +++ b/app/serializers/api/variant_serializer.rb @@ -1,5 +1,5 @@ class Api::VariantSerializer < ActiveModel::Serializer - attributes :id, :is_master, :count_on_hand, :name_to_display, :unit_to_display, :unit_value + attributes :id, :is_master, :on_hand, :name_to_display, :unit_to_display, :unit_value attributes :options_text, :on_demand, :price, :fees, :price_with_fees, :product_name attributes :tag_list diff --git a/app/services/order_factory.rb b/app/services/order_factory.rb index e920719384..85c887411a 100644 --- a/app/services/order_factory.rb +++ b/app/services/order_factory.rb @@ -45,7 +45,7 @@ class OrderFactory attrs[:line_items].each do |li| next unless variant = Spree::Variant.find_by_id(li[:variant_id]) scoper.scope(variant) - li[:quantity] = stock_limited_quantity(variant.count_on_hand, li[:quantity]) + li[:quantity] = stock_limited_quantity(variant.on_hand, li[:quantity]) li[:price] = variant.price build_item_from(li) end diff --git a/app/views/shop/products/_form.html.haml b/app/views/shop/products/_form.html.haml index 8bf8f8dcbb..98fb49501d 100644 --- a/app/views/shop/products/_form.html.haml +++ b/app/views/shop/products/_form.html.haml @@ -31,7 +31,7 @@ %div.pad-top{ "infinite-scroll" => "incrementLimit()", "infinite-scroll-distance" => "1", "infinite-scroll-disabled" => 'filteredProducts.length <= limit' } %product.animate-repeat{"ng-controller" => "ProductNodeCtrl", "ng-repeat" => "product in visibleProducts track by product.id", "id" => "product-{{ product.id }}"} = render "shop/products/summary" - %shop-variant{variant: 'variant', "ng-repeat" => "variant in product.variants | orderBy: ['name_to_display','unit_value'] track by variant.id", "id" => "variant-{{ variant.id }}", "ng-class" => "{'out-of-stock': !variant.on_demand && variant.count_on_hand == 0}"} + %shop-variant{variant: 'variant', "ng-repeat" => "variant in product.variants | orderBy: ['name_to_display','unit_value'] track by variant.id", "id" => "variant-{{ variant.id }}", "ng-class" => "{'out-of-stock': !variant.on_demand && variant.on_hand == 0}"} %product{"ng-show" => "Products.loading"} .row.summary diff --git a/app/views/spree/admin/variants/_autocomplete.js.erb b/app/views/spree/admin/variants/_autocomplete.js.erb index 4cecb1f61d..c4c6d101bf 100644 --- a/app/views/spree/admin/variants/_autocomplete.js.erb +++ b/app/views/spree/admin/variants/_autocomplete.js.erb @@ -16,7 +16,7 @@
  • <%= t('admin.sku') %>: {{variant.sku}}
  • -
  • <%= t('on_hand') %>: {{variant.count_on_hand}}
  • +
  • <%= t('on_hand') %>: {{variant.on_hand}}
{{#if variant.option_values}} @@ -41,7 +41,7 @@ <%= Spree.t(:location) %> - <%= Spree.t(:count_on_hand) %> + <%= Spree.t(:on_hand) %> <%= Spree.t(:quantity) %> @@ -53,7 +53,7 @@ Spree.t(:on_demand) {{else}} - {{variant.count_on_hand}} + {{variant.on_hand}} {{/if}} diff --git a/app/views/spree/admin/variants/search.rabl b/app/views/spree/admin/variants/search.rabl index 8206932c5f..b65ef4d768 100644 --- a/app/views/spree/admin/variants/search.rabl +++ b/app/views/spree/admin/variants/search.rabl @@ -2,7 +2,7 @@ # overriding spree/core/app/views/spree/admin/variants/search.rabl # collection @variants -attributes :sku, :options_text, :in_stock?, :on_demand, :count_on_hand, :id, :cost_price +attributes :sku, :options_text, :in_stock?, :on_demand, :on_hand, :id, :cost_price node(:name) do |v| # TODO: when products must have a unit, full_name will always be present diff --git a/spec/controllers/cart_controller_spec.rb b/spec/controllers/cart_controller_spec.rb index 8a7ad16dd8..5630429d6d 100644 --- a/spec/controllers/cart_controller_spec.rb +++ b/spec/controllers/cart_controller_spec.rb @@ -63,8 +63,8 @@ describe CartController, type: :controller do let!(:line_item) { create(:line_item, order: order, variant: variant_in_the_order, quantity: 2, max_quantity: 3) } before do - variant_in_the_order.count_on_hand = 4 - variant_not_in_the_order.count_on_hand = 2 + variant_in_the_order.on_hand = 4 + variant_not_in_the_order.on_hand = 2 order_cycle.exchanges.outgoing.first.variants = [variant_in_the_order, variant_not_in_the_order] order.order_cycle = order_cycle order.distributor = hub diff --git a/spec/controllers/spree/admin/variants_controller_spec.rb b/spec/controllers/spree/admin/variants_controller_spec.rb index 36fd0463a2..d8178fa3a1 100644 --- a/spec/controllers/spree/admin/variants_controller_spec.rb +++ b/spec/controllers/spree/admin/variants_controller_spec.rb @@ -22,7 +22,7 @@ module Spree it "applies variant overrides" do spree_get :search, q: 'Prod', distributor_id: d.id.to_s assigns(:variants).should == [v1] - assigns(:variants).first.count_on_hand.should == 44 + assigns(:variants).first.on_hand.should == 44 end it "filters by order cycle" do diff --git a/spec/factories.rb b/spec/factories.rb index d21d1143fd..2582de3fb6 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -602,7 +602,7 @@ FactoryBot.modify do after(:create) do |variant, evaluator| variant.on_demand = evaluator.on_demand - variant.count_on_hand = evaluator.on_hand + variant.on_hand = evaluator.on_hand variant.save end end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index f34d2ef28e..08e01a4159 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -308,11 +308,11 @@ xfeature %q{ product2.set_property 'Organic', 'NASAA 12345' product1.taxons = [taxon] product2.taxons = [taxon] - variant1.count_on_hand = 10 + variant1.on_hand = 10 variant1.update_column(:sku, "sku1") - variant2.count_on_hand = 20 + variant2.on_hand = 20 variant2.update_column(:sku, "sku2") - variant3.count_on_hand = 9 + variant3.on_hand = 9 variant3.update_column(:sku, "") variant1.option_values = [create(:option_value, :presentation => "Test")] variant2.option_values = [create(:option_value, :presentation => "Something")] diff --git a/spec/features/consumer/shopping/variant_overrides_spec.rb b/spec/features/consumer/shopping/variant_overrides_spec.rb index 8638616872..5aaa0d1ac9 100644 --- a/spec/features/consumer/shopping/variant_overrides_spec.rb +++ b/spec/features/consumer/shopping/variant_overrides_spec.rb @@ -123,7 +123,7 @@ feature "shopping with variant overrides defined", js: true, retry: 3 do expect do expect do complete_checkout - end.to change { product1_variant3.reload.count_on_hand }.by(0) + end.to change { product1_variant3.reload.on_hand }.by(0) end.to change { product1_variant3_override.reload.count_on_hand }.by(-2) end @@ -134,7 +134,7 @@ feature "shopping with variant overrides defined", js: true, retry: 3 do expect do expect do complete_checkout - end.to change { product3_variant2.reload.count_on_hand }.by(0) + end.to change { product3_variant2.reload.on_hand }.by(0) end.to change { product3_variant2_override.reload.count_on_hand }.by(-2) end @@ -143,12 +143,12 @@ feature "shopping with variant overrides defined", js: true, retry: 3 do click_checkout expect do complete_checkout - end.to change { product1_variant1.reload.count_on_hand }.by(-2) + end.to change { product1_variant1.reload.on_hand }.by(-2) product1_variant1_override.reload.count_on_hand.should be_nil end it "does not show out of stock flags on order confirmation page" do - product1_variant3.count_on_hand = 0 + product1_variant3.on_hand = 0 fill_in "variants[#{product1_variant3.id}]", with: "2" click_checkout diff --git a/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee b/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee index 3ff397eb18..5724fe1353 100644 --- a/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee @@ -98,7 +98,7 @@ describe "VariantOverridesCtrl", -> beforeEach -> # Ideally, count_on_hand is blank when the variant is on demand. However, this rule is not # enforced. - variant = {id: 2, on_demand: true, count_on_hand: 20, on_hand: "On demand"} + variant = {id: 2, on_demand: true, on_hand: 20, on_hand: "On demand"} it "clears count_on_hand when variant override uses producer stock settings", -> scope.variantOverrides[123][2] = {on_demand: null, count_on_hand: 1} @@ -162,7 +162,7 @@ describe "VariantOverridesCtrl", -> beforeEach -> # Ideally, count_on_hand is blank when the variant is on demand. However, this rule is not # enforced. - variant = {id: 2, on_demand: true, count_on_hand: 20, on_hand: t("on_demand")} + variant = {id: 2, on_demand: true, on_hand: 20, on_hand: t("on_demand")} it "is 'On demand' when variant override uses producer stock settings", -> scope.variantOverrides[123][2] = {on_demand: null, count_on_hand: 1} @@ -183,7 +183,7 @@ describe "VariantOverridesCtrl", -> variant = null beforeEach -> - variant = {id: 2, on_demand: false, count_on_hand: 20, on_hand: 20} + variant = {id: 2, on_demand: false, on_hand: 20, on_hand: 20} it "is variant count on hand when variant override uses producer stock settings", -> scope.variantOverrides[123][2] = {on_demand: null, count_on_hand: 1} diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index c1c34cbe6b..466253885f 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -183,7 +183,7 @@ describe "filtering products for submission to database", -> shipping_category_id: null created_at: null updated_at: null - count_on_hand: 0 + on_hand: 0 producer_id: 5 group_buy: null diff --git a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee index 131880ddc7..f8d124c110 100644 --- a/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/cart_spec.js.coffee @@ -145,11 +145,11 @@ describe 'Cart service', -> expect(li.max_quantity).toEqual 0 it "resets the count on hand available", -> - li = {variant: {id: 1, count_on_hand: 10}, quantity: 5} + li = {variant: {id: 1, on_hand: 10}, quantity: 5} Cart.line_items = [li] stockLevels = {1: {quantity: 0, max_quantity: 0, on_hand: 0}} Cart.compareAndNotifyStockLevels stockLevels - expect(li.variant.count_on_hand).toEqual 0 + expect(li.variant.on_hand).toEqual 0 describe "when the quantity available is less than that requested", -> it "reduces the quantity in the cart", -> @@ -172,7 +172,7 @@ describe 'Cart service', -> Cart.line_items = [li] stockLevels = {1: {quantity: 5, on_hand: 6}} Cart.compareAndNotifyStockLevels stockLevels - expect(li.variant.count_on_hand).toEqual 6 + expect(li.variant.on_hand).toEqual 6 describe "when the client-side quantity has been increased during the request", -> it "does not reset the quantity", -> diff --git a/spec/jobs/subscription_placement_job_spec.rb b/spec/jobs/subscription_placement_job_spec.rb index 685d6f4ded..a55bce0d00 100644 --- a/spec/jobs/subscription_placement_job_spec.rb +++ b/spec/jobs/subscription_placement_job_spec.rb @@ -76,9 +76,9 @@ describe SubscriptionPlacementJob do context "and insufficient stock exists to fulfil the order for some items" do before do - variant1.update_attribute(:count_on_hand, 5) - variant2.update_attribute(:count_on_hand, 2) - variant3.update_attribute(:count_on_hand, 0) + variant1.update_attribute(:on_hand, 5) + variant2.update_attribute(:on_hand, 2) + variant3.update_attribute(:on_hand, 0) end xit "caps quantity at the stock level for stock-limited items, and reports the change" do @@ -98,9 +98,9 @@ describe SubscriptionPlacementJob do context "and insufficient stock exists to fulfil the order for some items" do before do - variant1.update_attribute(:count_on_hand, 5) - variant2.update_attribute(:count_on_hand, 2) - variant3.update_attribute(:count_on_hand, 0) + variant1.update_attribute(:on_hand, 5) + variant2.update_attribute(:on_hand, 2) + variant3.update_attribute(:on_hand, 0) end xit "sets quantity to 0 for unavailable items, and reports the change" do diff --git a/spec/lib/open_food_network/lettuce_share_report_spec.rb b/spec/lib/open_food_network/lettuce_share_report_spec.rb index eb912f58c2..0ca330251c 100644 --- a/spec/lib/open_food_network/lettuce_share_report_spec.rb +++ b/spec/lib/open_food_network/lettuce_share_report_spec.rb @@ -50,7 +50,7 @@ module OpenFoodNetwork end it "only available items" do - variant.count_on_hand = 0 + variant.on_hand = 0 report.stub(:child_variants) { Spree::Variant.where(id: [variant, variant2, variant3, variant4]) } report.table.count.should eq 3 end diff --git a/spec/lib/open_food_network/products_renderer_spec.rb b/spec/lib/open_food_network/products_renderer_spec.rb index a845dfecf5..330ad57062 100644 --- a/spec/lib/open_food_network/products_renderer_spec.rb +++ b/spec/lib/open_food_network/products_renderer_spec.rb @@ -50,7 +50,7 @@ module OpenFoodNetwork it "doesn't return products not in stock" do variant.update_attribute(:on_demand, false) - variant.update_attribute(:count_on_hand, 0) + variant.update_attribute(:on_hand, 0) pr.products_json.should_not include product.name end diff --git a/spec/lib/open_food_network/scope_variant_to_hub_spec.rb b/spec/lib/open_food_network/scope_variant_to_hub_spec.rb index 11bf218a1d..4daaa05c6c 100644 --- a/spec/lib/open_food_network/scope_variant_to_hub_spec.rb +++ b/spec/lib/open_food_network/scope_variant_to_hub_spec.rb @@ -39,12 +39,12 @@ module OpenFoodNetwork it "returns the overridden stock level when one is present" do vo scoper.scope v - v.count_on_hand.should == 2 + v.on_hand.should == 2 end it "returns the variant's stock level otherwise" do scoper.scope v - v.count_on_hand.should == 1 + v.on_hand.should == 1 end describe "overriding stock on an on_demand variant" do @@ -131,7 +131,7 @@ module OpenFoodNetwork end context "when variant out of stock" do - before { v.count_on_hand = 0 } + before { v.on_hand = 0 } it "returns true if VO in stock" do scoper.scope v @@ -153,7 +153,7 @@ module OpenFoodNetwork end it "returns false if variant out of stock" do - v.count_on_hand = 0 + v.on_hand = 0 scoper.scope v expect(v.in_stock?).to eq(false) end diff --git a/spec/models/product_import/products_reset_strategy_spec.rb b/spec/models/product_import/products_reset_strategy_spec.rb index c3aa64a00b..e44249e8a3 100644 --- a/spec/models/product_import/products_reset_strategy_spec.rb +++ b/spec/models/product_import/products_reset_strategy_spec.rb @@ -9,7 +9,7 @@ describe ProductImport::ProductsResetStrategy do let(:enterprise) { product.supplier } let(:variant) { product.variants.first } - before { variant.count_on_hand = 2 } + before { variant.on_hand = 2 } context 'when there are excluded_items_ids' do let(:excluded_items_ids) { [variant.id] } @@ -17,35 +17,35 @@ describe ProductImport::ProductsResetStrategy do context 'and supplier_ids is []' do let(:supplier_ids) { [] } - it 'does not reset the variant.count_on_hand' do + it 'does not reset the variant.on_hand' do products_reset.reset(supplier_ids) - expect(variant.reload.count_on_hand).to eq(2) + expect(variant.reload.on_hand).to eq(2) end end context 'and supplier_ids is nil' do let(:supplier_ids) { nil } - it 'does not reset the variant.count_on_hand' do + it 'does not reset the variant.on_hand' do products_reset.reset(supplier_ids) - expect(variant.reload.count_on_hand).to eq(2) + expect(variant.reload.on_hand).to eq(2) end end context 'and supplier_ids is set' do - it 'does not update the count_on_hand of the excluded items' do + it 'does not update the on_hand of the excluded items' do products_reset.reset(supplier_ids) - expect(variant.reload.count_on_hand).to eq(2) + expect(variant.reload.on_hand).to eq(2) end - it 'updates the count_on_hand of the non-excluded items' do + it 'updates the on_hand of the non-excluded items' do non_excluded_variant = create( :variant, product: variant.product ) - non_excluded_variant.count_on_hand = 3 + non_excluded_variant.on_hand = 3 products_reset.reset(supplier_ids) - expect(non_excluded_variant.reload.count_on_hand).to eq(0) + expect(non_excluded_variant.reload.on_hand).to eq(0) end end end @@ -56,31 +56,31 @@ describe ProductImport::ProductsResetStrategy do context 'and supplier_ids is []' do let(:supplier_ids) { [] } - it 'does not reset the variant.count_on_hand' do + it 'does not reset the variant.on_hand' do products_reset.reset(supplier_ids) - expect(variant.reload.count_on_hand).to eq(2) + expect(variant.reload.on_hand).to eq(2) end end context 'and supplier_ids is nil' do let(:supplier_ids) { nil } - it 'does not reset the variant.count_on_hand' do + it 'does not reset the variant.on_hand' do products_reset.reset(supplier_ids) - expect(variant.reload.count_on_hand).to eq(2) + expect(variant.reload.on_hand).to eq(2) end end context 'and supplier_ids is not nil' do - it 'sets all count_on_hand to 0' do + it 'sets all on_hand to 0' do updated_records_count = products_reset.reset(supplier_ids) - expect(variant.reload.count_on_hand).to eq(0) + expect(variant.reload.on_hand).to eq(0) expect(updated_records_count).to eq(1) end context 'and there is an unresetable variant' do before do - variant.stock_items = [] # this makes variant.count_on_hand raise an error + variant.stock_items = [] # this makes variant.on_hand raise an error end it 'returns correct number of resetted variants' do @@ -96,24 +96,24 @@ describe ProductImport::ProductsResetStrategy do context 'and supplier_ids is []' do let(:supplier_ids) { [] } - it 'does not reset the variant.count_on_hand' do + it 'does not reset the variant.on_hand' do products_reset.reset(supplier_ids) - expect(variant.reload.count_on_hand).to eq(2) + expect(variant.reload.on_hand).to eq(2) end end context 'and supplier_ids is nil' do let(:supplier_ids) { nil } - it 'does not reset the variant.count_on_hand' do + it 'does not reset the variant.on_hand' do products_reset.reset(supplier_ids) - expect(variant.reload.count_on_hand).to eq(2) + expect(variant.reload.on_hand).to eq(2) end end context 'and supplier_ids is nil' do - it 'sets all count_on_hand to 0' do + it 'sets all on_hand to 0' do products_reset.reset(supplier_ids) - expect(variant.reload.count_on_hand).to eq(0) + expect(variant.reload.on_hand).to eq(0) end end end diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 6f04f97a0c..0499b0a38c 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -634,14 +634,14 @@ module Spree describe "stock filtering" do it "considers products that are on_demand as being in stock" do product = create(:simple_product, on_demand: true) - product.master.update_attribute(:count_on_hand, 0) + product.master.update_attribute(:on_hand, 0) product.has_stock?.should == true end describe "finding products in stock for a particular distribution" do it "returns on-demand products" do p = create(:simple_product, on_demand: true) - p.variants.first.update_attributes!(count_on_hand: 0, on_demand: true) + p.variants.first.update_attributes!(on_hand: 0, on_demand: true) d = create(:distributor_enterprise) oc = create(:simple_order_cycle, distributors: [d]) oc.exchanges.outgoing.first.variants << p.variants.first @@ -652,7 +652,7 @@ module Spree it "returns products with in-stock variants" do p = create(:simple_product) v = create(:variant, product: p) - v.update_attribute(:count_on_hand, 1) + v.update_attribute(:on_hand, 1) d = create(:distributor_enterprise) oc = create(:simple_order_cycle, distributors: [d]) oc.exchanges.outgoing.first.variants << v @@ -663,7 +663,7 @@ module Spree it "returns products with on-demand variants" do p = create(:simple_product) v = create(:variant, product: p, on_demand: true) - v.update_attribute(:count_on_hand, 0) + v.update_attribute(:on_hand, 0) d = create(:distributor_enterprise) oc = create(:simple_order_cycle, distributors: [d]) oc.exchanges.outgoing.first.variants << v @@ -673,7 +673,7 @@ module Spree it "does not return products that have stock not in the distribution" do p = create(:simple_product) - p.master.update_attribute(:count_on_hand, 1) + p.master.update_attribute(:on_hand, 1) d = create(:distributor_enterprise) oc = create(:simple_order_cycle, distributors: [d]) diff --git a/spec/requests/shop_spec.rb b/spec/requests/shop_spec.rb index 1b15c08a7f..3058a47c89 100644 --- a/spec/requests/shop_spec.rb +++ b/spec/requests/shop_spec.rb @@ -23,15 +23,15 @@ describe "Shop API", type: :request do before do set_order order - v61.update_attribute(:count_on_hand, 1) + v61.update_attribute(:on_hand, 1) p6.delete - v71.update_attribute(:count_on_hand, 1) - v41.update_attribute(:count_on_hand, 1) - v42.update_attribute(:count_on_hand, 0) - v43.update_attribute(:count_on_hand, 0) - v51.update_attribute(:count_on_hand, 1) - v52.update_attribute(:count_on_hand, 0) - v71.update_attribute(:count_on_hand, 1) + v71.update_attribute(:on_hand, 1) + v41.update_attribute(:on_hand, 1) + v42.update_attribute(:on_hand, 0) + v43.update_attribute(:on_hand, 0) + v51.update_attribute(:on_hand, 1) + v52.update_attribute(:on_hand, 0) + v71.update_attribute(:on_hand, 1) v71.update_attribute(:deleted_at, Time.zone.now) exchange = Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) exchange.update_attribute :pickup_time, "frogs" diff --git a/spec/serializers/variant_serializer_spec.rb b/spec/serializers/variant_serializer_spec.rb index 3c6aebf6a9..583412ff56 100644 --- a/spec/serializers/variant_serializer_spec.rb +++ b/spec/serializers/variant_serializer_spec.rb @@ -10,7 +10,7 @@ describe Api::VariantSerializer do :id, :name_to_display, :is_master, - :count_on_hand, + :on_hand, :name_to_display, :unit_to_display, :unit_value, diff --git a/spec/services/order_factory_spec.rb b/spec/services/order_factory_spec.rb index 3755f69e8e..672ac0a780 100644 --- a/spec/services/order_factory_spec.rb +++ b/spec/services/order_factory_spec.rb @@ -55,7 +55,7 @@ describe OrderFactory do context "when requested quantity is greater than available stock" do context "when no override is present" do before do - variant1.update_attribute(:count_on_hand, 2) + variant1.update_attribute(:on_hand, 2) attrs[:line_items].first[:quantity] = 5 end diff --git a/spec/services/order_syncer_spec.rb b/spec/services/order_syncer_spec.rb index e1ee400fc8..8645eb674e 100644 --- a/spec/services/order_syncer_spec.rb +++ b/spec/services/order_syncer_spec.rb @@ -292,7 +292,7 @@ describe OrderSyncer do let(:sli) { subscription.subscription_line_items.first } let(:variant) { sli.variant } - before { variant.update_attribute(:count_on_hand, 2) } + before { variant.update_attribute(:on_hand, 2) } context "when quantity is within available stock" do let(:params) { { subscription_line_items_attributes: [{ id: sli.id, quantity: 2}] } } @@ -327,7 +327,7 @@ describe OrderSyncer do let(:syncer) { OrderSyncer.new(subscription) } let(:changed_line_item) { order.line_items.find_by_variant_id(sli.variant_id) } - before { variant.update_attribute(:count_on_hand, 3) } + before { variant.update_attribute(:on_hand, 3) } context "when the changed line_item quantity matches the new quantity on the subscription line item" do before { changed_line_item.update_attributes(quantity: 3) } diff --git a/spec/services/variants_stock_levels_spec.rb b/spec/services/variants_stock_levels_spec.rb index a7e788d657..48088a6fce 100644 --- a/spec/services/variants_stock_levels_spec.rb +++ b/spec/services/variants_stock_levels_spec.rb @@ -12,8 +12,8 @@ describe VariantsStockLevels do let(:variant_stock_levels) { VariantsStockLevels.new } before do - variant_in_the_order.count_on_hand = 4 - variant_not_in_the_order.count_on_hand = 2 + variant_in_the_order.on_hand = 4 + variant_not_in_the_order.on_hand = 2 order.reload end @@ -40,7 +40,7 @@ describe VariantsStockLevels do describe "encoding Infinity" do let!(:variant_in_the_order) { create(:variant, on_demand: true) } - before { variant_in_the_order.count_on_hand = 0 } + before { variant_in_the_order.on_hand = 0 } it "encodes Infinity as a large, finite integer" do expect(variant_stock_levels.call(order, [variant_in_the_order.id])).to eq( From 12eab1bfa9b8e6ece85c1e5dd6e3691beda1d687 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Mon, 18 Feb 2019 22:00:16 +0000 Subject: [PATCH 14/57] Merge variant_stock.count_on_hand into variant_stock.on_hand variant.on_hand will not return infinity any longer if variant.on_demand is true. Clients of these methods will have to handle the combinations between on_hand and on_demand values --- app/models/concerns/variant_stock.rb | 49 ++++---------------- spec/models/concerns/variant_stock_spec.rb | 54 +++++++--------------- 2 files changed, 25 insertions(+), 78 deletions(-) diff --git a/app/models/concerns/variant_stock.rb b/app/models/concerns/variant_stock.rb index f7553b107f..8eac3eb32e 100644 --- a/app/models/concerns/variant_stock.rb +++ b/app/models/concerns/variant_stock.rb @@ -20,61 +20,30 @@ module VariantStock after_update :save_stock end - # Returns the number of items of the variant available in the stock. When - # allowing on demand, it returns infinite. - # - # Spree computes it as the sum of the count_on_hand of all its stock_items. + # Returns the number of items of the variant available. + # Spree computes total_on_hand as the sum of the count_on_hand of all its stock_items. # # @return [Float|Integer] def on_hand warn_deprecation(__method__, '#total_on_hand') - if on_demand - Float::INFINITY - else - total_on_hand - end - end - - # Returns the number of items available in the stock for this variant - # - # @return [Float|Integer] - def count_on_hand - warn_deprecation(__method__, '#total_on_hand') total_on_hand end - # Sets the stock level when `track_inventory_levels` config is - # set. It raises otherwise. + # Sets the stock level of the variant. + # This will only work if `track_inventory_levels` config is set + # and if there is a stock item for the variant. # - # @raise [StandardError] when the track_inventory_levels config - # key is not set. + # @raise [StandardError] when the track_inventory_levels config key is not set + # and when the variant has no stock item def on_hand=(new_level) warn_deprecation(__method__, '#total_on_hand') error = 'Cannot set on_hand value when Spree::Config[:track_inventory_levels] is false' raise error unless Spree::Config.track_inventory_levels - self.count_on_hand = new_level - end - - # Sets the stock level. As opposed to #on_hand= it does not check - # `track_inventory_levels`'s value as it was previously an ActiveModel - # setter of the database column of the `spree_variants` table. That is why - # #on_hand= is more widely used in Spree's codebase using #count_on_hand= - # underneath. - # - # So, if #count_on_hand= is used, `track_inventory_levels` won't be taken - # into account thus dismissing instance's configuration. - # - # It does ensure there's a stock item for the variant however. See - # #raise_error_if_no_stock_item_available for details. - # - # @raise [StandardError] when the variant has no stock item yet - def count_on_hand=(new_level) - warn_deprecation(__method__, '#total_on_hand') - raise_error_if_no_stock_item_available + overwrite_stock_levels(new_level) end @@ -131,7 +100,7 @@ module VariantStock # Here we depend only on variant.total_on_hand and variant.on_demand. # This way, variant_overrides only need to override variant.total_on_hand and variant.on_demand. def fill_status(quantity) - if count_on_hand >= quantity + if on_hand >= quantity on_hand = quantity backordered = 0 else diff --git a/spec/models/concerns/variant_stock_spec.rb b/spec/models/concerns/variant_stock_spec.rb index 50c89671e4..0c521ac3e6 100644 --- a/spec/models/concerns/variant_stock_spec.rb +++ b/spec/models/concerns/variant_stock_spec.rb @@ -18,15 +18,15 @@ describe VariantStock do end describe '#on_hand' do - context 'when the variant is ordered on demand' do + context 'when the variant is on demand' do before do variant.stock_items.first.update_attribute( :backorderable, true ) end - it 'returns infinite' do - expect(variant.on_hand).to eq(Float::INFINITY) + it 'returns the total items in stock anyway' do + expect(variant.on_hand).to eq(variant.stock_items.sum(&:count_on_hand)) end end @@ -44,28 +44,27 @@ describe VariantStock do end end - describe '#count_on_hand' do - before { allow(variant).to receive(:total_on_hand) } - - it 'delegates to #total_on_hand' do - variant.count_on_hand - expect(variant).to have_received(:total_on_hand) - end - end - describe '#on_hand=' do context 'when track_inventory_levels is set' do before do - allow(variant).to receive(:count_on_hand=) allow(Spree::Config) .to receive(:track_inventory_levels) { true } end - it 'delegates to #count_on_hand=' do + it 'sets the new level as the stock item\'s count_on_hand' do variant.on_hand = 3 - expect(variant) - .to have_received(:count_on_hand=).with(3) + unique_stock_item = variant.stock_items.first + expect(unique_stock_item.count_on_hand).to eq(3) end + + context 'when the variant has no stock item' do + let(:variant) { build(:variant) } + + it 'raises' do + expect { variant.on_hand = 3 } + .to raise_error(StandardError) + end + end end context 'when track_inventory_levels is not set' do @@ -81,27 +80,6 @@ describe VariantStock do end end - describe '#count_on_hand=' do - context 'when the variant has a stock item' do - let(:variant) { create(:variant) } - - it 'sets the new level as the stock item\'s count_on_hand' do - variant.count_on_hand = 3 - unique_stock_item = variant.stock_items.first - expect(unique_stock_item.count_on_hand).to eq(3) - end - end - - context 'when the variant has no stock item' do - let(:variant) { build(:variant) } - - it 'raises' do - expect { variant.count_on_hand = 3 } - .to raise_error(StandardError) - end - end - end - describe '#on_demand' do context 'when the variant has a stock item' do let(:variant) { create(:variant) } @@ -187,7 +165,7 @@ describe VariantStock do end context 'when variant out of stock' do - before { variant.count_on_hand = 0 } + before { variant.on_hand = 0 } it "returns true for zero" do expect(variant.can_supply?(0)).to eq(true) From 30967a5ffae2f33ee4a75791be1eb581fe7dfe24 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Mon, 18 Feb 2019 15:46:40 +0000 Subject: [PATCH 15/57] Fix bulk product edit page. on_hand and on_demand should never be mixed on the server side, the UI can make the decision to show on_demand on the on_hand column if on_demand is true --- app/serializers/api/admin/product_serializer.rb | 3 ++- app/serializers/api/admin/variant_serializer.rb | 3 ++- .../spree/admin/products/index/_products_variant.html.haml | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/serializers/api/admin/product_serializer.rb b/app/serializers/api/admin/product_serializer.rb index e84588128f..5ef3a27d4e 100644 --- a/app/serializers/api/admin/product_serializer.rb +++ b/app/serializers/api/admin/product_serializer.rb @@ -25,7 +25,8 @@ class Api::Admin::ProductSerializer < ActiveModel::Serializer end def on_hand - object.on_hand.nil? ? 0 : object.on_hand.to_f.finite? ? object.on_hand : I18n.t(:on_demand) + return 0 if object.on_hand.nil? + object.on_hand end def price diff --git a/app/serializers/api/admin/variant_serializer.rb b/app/serializers/api/admin/variant_serializer.rb index 3fc22b98d4..9be1b3662b 100644 --- a/app/serializers/api/admin/variant_serializer.rb +++ b/app/serializers/api/admin/variant_serializer.rb @@ -4,7 +4,8 @@ class Api::Admin::VariantSerializer < ActiveModel::Serializer has_many :variant_overrides def on_hand - object.on_hand.nil? ? 0 : ( object.on_hand.to_f.finite? ? object.on_hand : I18n.t(:on_demand) ) + return 0 if object.on_hand.nil? + object.on_hand end def price diff --git a/app/views/spree/admin/products/index/_products_variant.html.haml b/app/views/spree/admin/products/index/_products_variant.html.haml index c4c0412eef..584e9f3610 100644 --- a/app/views/spree/admin/products/index/_products_variant.html.haml +++ b/app/views/spree/admin/products/index/_products_variant.html.haml @@ -16,7 +16,8 @@ %input{ 'ng-model' => 'variant.price', 'ofn-decimal' => :true, :name => 'variant_price', 'ofn-track-variant' => 'price', :type => 'text' } %td{ 'ng-show' => 'columns.on_hand.visible' } %input.field{ 'ng-model' => 'variant.on_hand', 'ng-change' => 'updateOnHand(product)', :name => 'variant_on_hand', 'ng-if' => '!variant.on_demand', 'ofn-track-variant' => 'on_hand', :type => 'number' } - %span{ 'ng-bind' => 'variant.on_hand', :name => 'variant_on_hand', 'ng-if' => 'variant.on_demand' } + %span{ :name => 'variant_on_hand', 'ng-if' => 'variant.on_demand' } + = t(:on_demand) %td{ 'ng-show' => 'columns.on_demand.visible' } %input.field{ 'ng-model' => 'variant.on_demand', :name => 'variant_on_demand', 'ofn-track-variant' => 'on_demand', :type => 'checkbox' } %td{ 'ng-show' => 'columns.category.visible' } From 8ee14ccb4b1b7cb2f4ff102264930c26926418e8 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Mon, 18 Feb 2019 15:13:51 +0000 Subject: [PATCH 16/57] Make variants_stock_levels return variants on_demand values --- .../darkswarm/services/cart.js.coffee | 24 ++++++++++--------- app/models/spree/line_item_decorator.rb | 1 + app/services/variants_stock_levels.rb | 13 ++++------ .../consumer/shopping/shopping_spec.rb | 21 ++++++++++++++++ spec/services/variants_stock_levels_spec.rb | 16 ++++++------- 5 files changed, 47 insertions(+), 28 deletions(-) diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee index b0311cb402..bdd0aa3676 100644 --- a/app/assets/javascripts/darkswarm/services/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee @@ -57,18 +57,20 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, $modal, $roo scope = $rootScope.$new(true) scope.variants = [] - # TODO: These changes to quantity/max_quantity trigger another cart update, which - # is unnecessary. - + # TODO: These changes to quantity/max_quantity trigger another cart update, which is unnecessary. for li in @line_items when li.quantity > 0 - if stockLevels[li.variant.id]? - li.variant.on_hand = stockLevels[li.variant.id].on_hand - if li.quantity > li.variant.on_hand - li.quantity = li.variant.on_hand - scope.variants.push li.variant - if li.variant.on_hand == 0 && li.max_quantity > li.variant.on_hand - li.max_quantity = li.variant.on_hand - scope.variants.push(li.variant) unless li.variant in scope.variants + continue unless stockLevels[li.variant.id]? + + li.variant.on_hand = stockLevels[li.variant.id].on_hand + li.variant.on_demand = stockLevels[li.variant.id].on_demand + continue if li.variant.on_demand + + if li.quantity > li.variant.on_hand + li.quantity = li.variant.on_hand + scope.variants.push li.variant + if li.variant.on_hand == 0 && li.max_quantity > li.variant.on_hand + li.max_quantity = li.variant.on_hand + scope.variants.push(li.variant) unless li.variant in scope.variants if scope.variants.length > 0 $modal.open(templateUrl: "out_of_stock.html", scope: scope, windowClass: 'out-of-stock-modal') diff --git a/app/models/spree/line_item_decorator.rb b/app/models/spree/line_item_decorator.rb index 31b4945c51..d64c02155d 100644 --- a/app/models/spree/line_item_decorator.rb +++ b/app/models/spree/line_item_decorator.rb @@ -64,6 +64,7 @@ Spree::LineItem.class_eval do def cap_quantity_at_stock! scoper.scope(variant) + return if variant.on_demand update_attributes!(quantity: variant.on_hand) if quantity > variant.on_hand end diff --git a/app/services/variants_stock_levels.rb b/app/services/variants_stock_levels.rb index 43d9190800..69dbc49dee 100644 --- a/app/services/variants_stock_levels.rb +++ b/app/services/variants_stock_levels.rb @@ -10,8 +10,8 @@ class VariantsStockLevels order_variant_ids = variant_stock_levels.keys missing_variant_ids = requested_variant_ids - order_variant_ids missing_variant_ids.each do |variant_id| - variant_on_hand = Spree::Variant.find(variant_id).on_hand - variant_stock_levels[variant_id] = { quantity: 0, max_quantity: 0, on_hand: variant_on_hand } + variant = Spree::Variant.find(variant_id) + variant_stock_levels[variant_id] = { quantity: 0, max_quantity: 0, on_hand: variant.on_hand, on_demand: variant.on_demand } end # The code above is most probably dead code, this bugsnag notification will confirm it @@ -37,14 +37,9 @@ class VariantsStockLevels [line_item.variant.id, { quantity: line_item.quantity, max_quantity: line_item.max_quantity, - on_hand: wrap_json_infinity(line_item.variant.on_hand) }] + on_hand: line_item.variant.on_hand, + on_demand: line_item.variant.on_demand }] end ] end - - # Rails to_json encodes Float::INFINITY as Infinity, which is not valid JSON - # Return it as a large integer (max 32 bit signed int) - def wrap_json_infinity(number) - number == Float::INFINITY ? 2_147_483_647 : number - end end diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 4b18a62434..abf1cc7d51 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -302,6 +302,15 @@ feature "As a consumer I want to shop with a distributor", js: true do Spree::LineItem.where(id: li).should be_empty end + it "lets us add a quantity greater than on_hand value if product is on_demand" do + variant.update_attributes on_hand: 5, on_demand: true + visit shop_path + + fill_in "variants[#{variant.id}]", with: '10' + + page.should have_field "variants[#{variant.id}]", with: '10' + end + it "alerts us when we enter a quantity greater than the stock available" do variant.update_attributes on_hand: 5 visit shop_path @@ -341,6 +350,18 @@ feature "As a consumer I want to shop with a distributor", js: true do page.should have_selector "#variants_#{variant.id}[disabled='disabled']" end + it 'does not show out of stock modal if product is on_demand' do + expect(page).to have_content "Product" + + variant.update_attributes! on_hand: 0, on_demand: true + + expect(page).to have_input "variants[#{variant.id}]" + fill_in "variants[#{variant.id}]", with: '1' + wait_until { !cart_dirty } + + expect(page).to_not have_selector '.out-of-stock-modal' + end + context "group buy products" do let(:product) { create(:simple_product, group_buy: true) } diff --git a/spec/services/variants_stock_levels_spec.rb b/spec/services/variants_stock_levels_spec.rb index 48088a6fce..f4a77d7018 100644 --- a/spec/services/variants_stock_levels_spec.rb +++ b/spec/services/variants_stock_levels_spec.rb @@ -17,34 +17,34 @@ describe VariantsStockLevels do order.reload end - it "returns a hash with variant id, quantity, max_quantity and stock on hand" do + it "returns a hash with variant id, quantity, max_quantity, on hand and on demand" do expect(variant_stock_levels.call(order, [variant_in_the_order.id])).to eq( - variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 4 } + variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 4, on_demand: false } ) end it "includes all line items, even when the variant_id is not specified" do expect(variant_stock_levels.call(order, [])).to eq( - variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 4 } + variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 4, on_demand: false } ) end it "includes an empty quantity entry for variants that aren't in the order" do variant_ids = [variant_in_the_order.id, variant_not_in_the_order.id] expect(variant_stock_levels.call(order, variant_ids)).to eq( - variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 4 }, - variant_not_in_the_order.id => { quantity: 0, max_quantity: 0, on_hand: 2 } + variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 4, on_demand: false }, + variant_not_in_the_order.id => { quantity: 0, max_quantity: 0, on_hand: 2, on_demand: false } ) end - describe "encoding Infinity" do + describe "when variant is on_demand" do let!(:variant_in_the_order) { create(:variant, on_demand: true) } before { variant_in_the_order.on_hand = 0 } - it "encodes Infinity as a large, finite integer" do + it "includes the actual on_hand value and on_demand: true" do expect(variant_stock_levels.call(order, [variant_in_the_order.id])).to eq( - variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 2147483647 } + variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 0, on_demand: true } ) end end From 47e154f2ac837d0e5088e25f59d9f2b64a6b593f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 19 Feb 2019 19:21:37 +0000 Subject: [PATCH 17/57] Bump oj from 3.7.8 to 3.7.9 Bumps [oj](https://github.com/ohler55/oj) from 3.7.8 to 3.7.9. - [Release notes](https://github.com/ohler55/oj/releases) - [Changelog](https://github.com/ohler55/oj/blob/master/CHANGELOG.md) - [Commits](https://github.com/ohler55/oj/compare/v3.7.8...v3.7.9) Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 87b0c1f49d..f88886708e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -559,7 +559,7 @@ GEM multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) - oj (3.7.8) + oj (3.7.9) orm_adapter (0.5.0) paper_trail (5.2.3) activerecord (>= 3.0, < 6.0) From 5b4c98673300e81d60c7a720d19d16e13d9b94b9 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Wed, 20 Feb 2019 00:05:28 +0000 Subject: [PATCH 18/57] Add spec to cover variant scoping in order_cycle.items_bought_by_user --- spec/models/order_cycle_spec.rb | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index aec7f61cd8..16a024f1a3 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -523,20 +523,31 @@ describe OrderCycle do let(:shop) { create(:enterprise) } let(:user) { create(:user) } let(:oc) { create(:order_cycle) } - let!(:order1) { create(:completed_order_with_totals, distributor: shop, user: user, order_cycle: oc) } - let!(:order2) { create(:completed_order_with_totals, distributor: create(:enterprise), user: user, order_cycle: oc) } - let!(:order3) { create(:completed_order_with_totals, distributor: shop, user: create(:user), order_cycle: oc) } - let!(:order4) { create(:completed_order_with_totals, distributor: shop, user: user, order_cycle: create(:order_cycle)) } - let!(:order5) { create(:completed_order_with_totals, distributor: shop, user: user, order_cycle: oc) } + let!(:order) { create(:completed_order_with_totals, distributor: shop, user: user, order_cycle: oc) } + let!(:order_from_other_hub) { create(:completed_order_with_totals, distributor: create(:enterprise), user: user, order_cycle: oc) } + let!(:order_from_other_user) { create(:completed_order_with_totals, distributor: shop, user: create(:user), order_cycle: oc) } + let!(:order_from_other_oc) { create(:completed_order_with_totals, distributor: shop, user: user, order_cycle: create(:order_cycle)) } + let!(:order_cancelled) { create(:completed_order_with_totals, distributor: shop, user: user, order_cycle: oc) } before do setup_email end - before { order5.cancel } + before { order_cancelled.cancel } it "only returns items from non-cancelled orders in the OC, placed by the user at the shop" do items = oc.items_bought_by_user(user, shop) - expect(items).to match_array order1.reload.line_items + expect(items).to match_array order.reload.line_items + end + + it "returns items with scoped variants" do + overridden_variant = order.line_items.first.variant + create(:variant_override, hub: shop, variant: overridden_variant, count_on_hand: 1000) + + items = oc.items_bought_by_user(user, shop) + + expect(items).to match_array order.reload.line_items + item_with_overridden_variant = items.find { |item| item.variant_id == overridden_variant.id } + expect(item_with_overridden_variant.variant.on_hand).to eq(1000) end end end From ef786adcfce6bb37adf6777bf1bb3e4111413105 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Wed, 20 Feb 2019 13:07:21 +0000 Subject: [PATCH 19/57] Make variant_override.stock_overridden? simpler and add comment to explain relationship between count_on_hand and on_demand in a VO --- app/models/variant_override.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/variant_override.rb b/app/models/variant_override.rb index cea630719b..a00caef367 100644 --- a/app/models/variant_override.rb +++ b/app/models/variant_override.rb @@ -57,7 +57,9 @@ class VariantOverride < ActiveRecord::Base end def stock_overridden? - on_demand == false && count_on_hand.present? + # If count_on_hand is present, it means on_demand is false + # See StockSettingsOverrideValidation for details + count_on_hand.present? end def decrement_stock!(quantity) From f73221ed85db77b547590bed43f26d4d837a7d51 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sun, 3 Feb 2019 16:35:23 +0000 Subject: [PATCH 20/57] Use Dir::Tmpname.make_tmpname for threadsafe file naming --- app/controllers/admin/product_import_controller.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/controllers/admin/product_import_controller.rb b/app/controllers/admin/product_import_controller.rb index 72a5a63f30..fd700d633f 100644 --- a/app/controllers/admin/product_import_controller.rb +++ b/app/controllers/admin/product_import_controller.rb @@ -79,11 +79,9 @@ module Admin end def save_uploaded_file(upload) - filename = 'import' + Time.zone.now.strftime('%d-%m-%Y-%H-%M-%S') - extension = '.' + upload.original_filename.split('.').last - directory = 'tmp/product_import' - Dir.mkdir(directory) unless File.exist?(directory) - File.open(Rails.root.join(directory, filename + extension), 'wb') do |f| + extension = File.extname(upload.original_filename) + directory = Dir.mktmpdir 'product_import' + File.open(File.join(directory, "import#{extension}"), 'wb') do |f| data = UploadSanitizer.new(upload.read).call f.write(data) f.path From 4b21c9cb7090eb7ec7a0019d0017a72dc7f6f8f2 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Thu, 21 Feb 2019 07:32:50 +1100 Subject: [PATCH 21/57] Updating translations for config/locales/en_CA.yml --- config/locales/en_CA.yml | 2860 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 2860 insertions(+) create mode 100644 config/locales/en_CA.yml diff --git a/config/locales/en_CA.yml b/config/locales/en_CA.yml new file mode 100644 index 0000000000..2055b84403 --- /dev/null +++ b/config/locales/en_CA.yml @@ -0,0 +1,2860 @@ +en_CA: + language_name: "English" + activerecord: + attributes: + spree/order: + payment_state: Payment State + shipment_state: Shipment State + completed_at: Completed At + number: Number + state: State + email: Customer E-Mail + spree/payment: + amount: Amount + order_cycle: + orders_close_at: Close date + errors: + models: + spree/user: + attributes: + email: + taken: "There's already an account for this email. Please login or reset your password." + spree/order: + no_card: There are no authorised credit cards available to charge + order_cycle: + attributes: + orders_close_at: + after_orders_open_at: must be after open date + variant_override: + count_on_hand: + using_producer_stock_settings_but_count_on_hand_set: "must be blank because you are using producer stock settings" + on_demand_but_count_on_hand_set: "must be blank if 'on demand' is used" + limited_stock_but_no_count_on_hand: "must be specified because you are forcing limited stock" + activemodel: + attributes: + order_management/reports/enterprise_fee_summary/parameters: + start_at: "Start" + end_at: "End" + distributor_ids: "Hubs" + producer_ids: "Producers" + order_cycle_ids: "Order Cycles" + enterprise_fee_ids: "Names for Fees" + shipping_method_ids: "Shipping Methods" + payment_method_ids: "Payment Methods" + errors: + models: + subscription_validator: + attributes: + subscription_line_items: + at_least_one_product: "^Please add at least one product" + not_available: "^%{name} is not available from the selected schedule" + ends_at: + after_begins_at: "must be after begins at" + customer: + does_not_belong_to_shop: "does not belong to %{shop}" + schedule: + not_coordinated_by_shop: "is not coordinated by %{shop}" + payment_method: + not_available_to_shop: "is not available to %{shop}" + invalid_type: "must be a Cash or Stripe method" + charges_not_allowed: "^Credit card charges are not allowed by this customer" + no_default_card: "^No default card available for this customer" + shipping_method: + not_available_to_shop: "is not available to %{shop}" + devise: + confirmations: + send_instructions: "You will receive an email with instructions about how to confirm your account in a few minutes." + failed_to_send: "An error occurred whilst sending your confirmation email." + resend_confirmation_email: "Resend confirmation email." + confirmed: "Thanks for confirming your email! You can now log in." + not_confirmed: "Your email address could not be confirmed. Perhaps you have already completed this step?" + user_registrations: + spree_user: + signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please open the link to activate your account." + unknown_error: "Something went wrong while creating your account. Check your email address and try again." + failure: + invalid: | + Invalid email or password. + Were you a guest last time? Perhaps you need to create an account or reset your password. + unconfirmed: "You have to confirm your account before continuing." + already_registered: "This email address is already registered. Please log in to continue, or go back and use another email address." + user_passwords: + spree_user: + updated_not_active: "Your password has been reset, but your email has not been confirmed yet." + models: + order_cycle: + cloned_order_cycle_name: "COPY OF%{order_cycle}" + validators: + date_time_string_validator: + not_string_error: "must be a string" + invalid_format_error: "must be valid" + integer_array_validator: + not_array_error: "must be an array" + invalid_element_error: "must contain only valid integers" + enterprise_mailer: + confirmation_instructions: + subject: "Please confirm the email address for %{enterprise}" + welcome: + subject: "%{enterprise} is now on %{sitename}" + invite_manager: + subject: "%{enterprise} has invited you to be a manager" + order_mailer: + cancel_email: + dear_customer: "Dear Customer, " + instructions: "Your order has been CANCELED. Please retain this cancellation information for your records. " + order_summary_canceled: "Order Summary [CANCELED]" + subject: "Cancellation of Order" + subtotal: "Subtotal: %{subtotal}" + total: "Order Total: %{total}" + producer_mailer: + order_cycle: + subject: "Order cycle report for %{producer}" + shipment_mailer: + shipped_email: + dear_customer: "Dear Customer, " + instructions: "Your order has been shipped" + shipment_summary: "Shipment Summary" + subject: "Shipment Notification" + thanks: "Thank you for your business." + track_information: "Tracking Information: %{tracking}" + track_link: "Tracking Link: %{url}" + subscription_mailer: + placement_summary_email: + subject: A summary of recently placed subscription orders + greeting: "Hi %{name}," + intro: "Below is a summary of the subscription orders that have just been placed for %{shop}." + confirmation_summary_email: + subject: A summary of recently confirmed subscription orders + greeting: "Hi %{name}," + intro: "Below is a summary of the subscription orders that have just been finalised for %{shop}." + summary_overview: + total: A total of %{count} subscriptions were marked for automatic processing. + success_zero: Of these, none were processed successfully. + success_some: Of these, %{count} were processed successfully. + success_all: All were processed successfully. + issues: Details of the issues encountered are provided below. + summary_detail: + no_message_provided: No error message provided + changes: + title: Insufficient Stock (%{count} orders) + explainer: These orders were processed but insufficient stock was available for some requested items + empty: + title: No Stock (%{count} orders) + explainer: These orders were unable to be processed because no stock was available for any requested items + complete: + title: Already Processed (%{count} orders) + explainer: These orders were already marked as complete, and were therefore left untouched + processing: + title: Error Encountered (%{count} orders) + explainer: Automatic processing of these orders failed due to an error. The error has been listed where possible. + failed_payment: + title: Failed Payment (%{count} orders) + explainer: Automatic processing of payment for these orders failed due to an error. The error has been listed where possible. + other: + title: Other Failure (%{count} orders) + explainer: Automatic processing of these orders failed for an unknown reason. This should not occur, please contact us if you are seeing this. + home: "OFN" + title: Open Food Network + welcome_to: 'Welcome to ' + site_meta_description: "We begin from the ground up. With farmers and growers ready to tell their stories proudly and truly. With distributors ready to connect people with products fairly and honestly. With buyers who believe that better weekly shopping decisions can…" + search_by_name: Search by name or suburb... + producers_join: Australian producers are now welcome to join the Open Food Network. + charges_sales_tax: Charges GST? + print_invoice: "Print Invoice" + print_ticket: "Print Ticket" + select_ticket_printer: "Select printer for tickets" + send_invoice: "Send Invoice" + resend_confirmation: "Resend Confirmation" + view_order: "View Order" + edit_order: "Edit Order" + ship_order: "Ship Order" + cancel_order: "Cancel Order" + confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" + confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" + must_have_valid_business_number: "%{enterprise_name} must have a valid ABN before invoices can be sent." + invoice: "Invoice" + percentage_of_sales: "%{percentage} of sales" + capped_at_cap: "capped at %{cap}" + per_month: "per month" + free: "free" + free_trial: "free trial" + plus_tax: "plus GST" + min_bill_turnover_desc: "once turnover exceeds %{mbt_amount}" + more: "More" + say_no: "No" + say_yes: "Yes" + then: then + ongoing: Ongoing + bill_address: Billing Address + ship_address: Shipping Address + sort_order_cycles_on_shopfront_by: "Sort Order Cycles On Shopfront By" + required_fields: Required fields are denoted with an asterisk + select_continue: Select and Continue + remove: Remove + or: or + collapse_all: Collapse all + expand_all: Expand all + loading: Loading... + show_more: Show more + show_all: Show all + show_all_with_more: "Show All (%{num} More)" + cancel: Cancel + edit: Edit + clone: Clone + distributors: Distributors + distribution: Distribution + bulk_order_management: Bulk Order Management + enterprises: Enterprises + enterprise_groups: Groups + reports: Reports + variant_overrides: Inventory + spree_products: Spree Products + all: All + current: Current + available: Available + dashboard: Dashboard + undefined: undefined + unused: unused + admin_and_handling: Admin & Handling + profile: Profile + supplier_only: Supplier Only + has_shopfront: Has Shopfront + weight: Weight + volume: Volume + items: Items + summary: Summary + detailed: Detailed + updated: Updated + 'yes': "Yes" + 'no': "No" + y: 'Y' + n: 'N' + powered_by: Powered by + blocked_cookies_alert: "Your browser may be blocking cookies needed to use this shopfront. Click below to allow cookies and reload the page." + allow_cookies: "Allow Cookies" + notes: Notes + error: Error + processing_payment: Processing payment... + show_only_unfulfilled_orders: Show only unfulfilled orders + filter_results: Filter Results + quantity: Quantity + pick_up: Pick up + copy: Copy + password_confirmation: Password Confirmation + reset_password_token: Reset password token + expired: has expired, please request a new one + back_to_payments_list: "Back to Payments List" + actions: + create_and_add_another: "Create and Add Another" + admin: + begins_at: Begins At + begins_on: Begins On + customer: Customer + date: Date + email: Email + ends_at: Ends At + ends_on: Ends On + name: Name + on_hand: On Hand + on_demand: On Demand + on_demand?: On Demand? + order_cycle: Order Cycle + payment: Payment + payment_method: Payment Method + phone: Phone + price: Price + producer: Producer + image: Image + product: Product + quantity: Quantity + schedule: Schedule + shipping: Shipping + shipping_method: Shipping Method + shop: Shop + sku: SKU + status_state: State + tags: Tags + variant: Variant + weight: Weight + volume: Volume + items: Items + select_all: Select all + obsolete_master: Obsolete master + quick_search: Quick Search + clear_all: Clear All + start_date: "Start Date" + end_date: "End Date" + form_invalid: "Form contains missing or invalid fields" + clear_filters: Clear Filters + clear: Clear + save: Save + cancel: Cancel + back: Back + show_more: Show more + show_n_more: Show %{num} more + choose: "Choose..." + please_select: Please select... + columns: Columns + actions: Actions + viewing: "Viewing: %{current_view_name}" + description: Description + whats_this: What's this? + tag_has_rules: "Existing rules for this tag: %{num}" + has_one_rule: "has one rule" + has_n_rules: "has %{num} rules" + unsaved_confirm_leave: "There are unsaved changed on this page. Continue without saving?" + unsaved_changes: "You have unsaved changes" + accounts_and_billing_settings: + method_settings: + default_accounts_payment_method: "Default Accounts Payment Method" + default_accounts_shipping_method: "Default Accounts Shipping Method" + edit: + accounts_and_billing: "Accounts & Billing" + accounts_administration_distributor: "Accounts Administration Distributor" + admin_settings: "Settings" + update_invoice: "Update Invoices" + auto_update_invoices: "Auto-update invoices nightly at 1:00am" + finalise_invoice: "Finalise Invoices" + auto_finalise_invoices: "Auto-finalise invoices monthly on the 2nd at 1:30am" + manually_run_task: "Manually Run Task " + update_user_invoice_explained: "Use this button to immediately update invoices for the month to date for each enterprise user in the system. This task can be set up to run automatically every night." + finalise_user_invoices: "Finalise User Invoices" + finalise_user_invoice_explained: "Use this button to finalize all invoices in the system for the previous calendar month. This task can be set up to run automatically once a month." + update_user_invoices: "Update User Invoices" + errors: + accounts_distributor: must be set if you wish to create invoices for enterprise users. + default_payment_method: must be set if you wish to create invoices for enterprise users. + default_shipping_method: must be set if you wish to create invoices for enterprise users. + shopfront_settings: + embedded_shopfront_settings: "Embedded Shopfront Settings" + enable_embedded_shopfronts: "Enable Embedded Shopfronts" + embedded_shopfronts_whitelist: "External Domains Whitelist" + number_localization: + number_localization_settings: "Number Localization Settings" + enable_localized_number: "Use the international thousand/decimal separator logic" + business_model_configuration: + edit: + business_model_configuration: "Business Model" + business_model_configuration_tip: "Configure the rate at which shops will be charged each month for use of the Open Food Network." + bill_calculation_settings: "Bill Calculation Settings" + bill_calculation_settings_tip: "Adjust the amount that enterprises will be billed each month for use of the OFN." + shop_trial_length: "Shop Trial Length (Days)" + shop_trial_length_tip: "The length of time (in days) that enterprises who are set up as shops can run as a trial period." + fixed_monthly_charge: "Fixed Monthly Charge" + fixed_monthly_charge_tip: "A fixed monthly charge for all enterprises who are set up as a shop and have exceeded the minimum billable turnover (if set)." + percentage_of_turnover: "Percentage of turnover" + percentage_of_turnover_tip: "When greater than zero, this rate (0.0 - 1.0) will be applied to the total turnover of each shop and added to any fixed charges (to the left) to calculate the monthly bill." + monthly_cap_excl_tax: "monthly cap (excl. GST)" + monthly_cap_excl_tax_tip: "When greater than zero, this value will be used as a cap on the amount that shops will be charged each month." + tax_rate: "Tax Rate" + tax_rate_tip: "Tax rate that applies to the the monthly bill that enterprises are charged for using the system." + minimum_monthly_billable_turnover: "Minimum Monthly Billable Turnover" + minimum_monthly_billable_turnover_tip: "Minimum monthly turnover before a shopfront will be charged for using OFN. Enterprises turning over less than this amount in a month will not be charged, either as a percentage or fixed rate." + example_bill_calculator: "Example Bill Calculator" + example_bill_calculator_legend: "Alter the example turnover to visualise the effect of the settings to the left." + example_monthly_turnover: "Example Monthly Turnover" + example_monthly_turnover_tip: "An example monthly turnover for an enterprise which will be used to generate calculate an example monthly bill below." + cap_reached?: "Cap Reached ?" + cap_reached?_tip: "Whether the cap (specified to the left) has been reached, given the settings and the turnover provided." + included_tax: "Included tax" + included_tax_tip: "The total tax included in the example monthly bill, given the settings and the turnover provided." + total_monthly_bill_incl_tax: "Total Monthly Bill (Incl. Tax)" + total_monthly_bill_incl_tax_tip: "The example total monthly bill with tax included, given the settings and the turnover provided." + cache_settings: + show: + title: Caching + distributor: Distributor + order_cycle: Order Cycle + status: Status + diff: Diff + error: Error + invoice_settings: + edit: + title: Invoice Settings + invoice_style2?: Use the alternative invoice model that includes total tax breakdown per rate and tax rate info per item (not yet suitable for countries displaying prices excluding tax) + enable_receipt_printing?: Show options for printing receipts using thermal printers in order dropdown? + stripe_connect_settings: + edit: + title: "Stripe Connect" + settings: "Settings" + stripe_connect_enabled: Enable shops to accept payments using Stripe Connect? + no_api_key_msg: No Stripe account exists for this enterprise. + configuration_explanation_html: For detailed instructions on configuring the Stripe Connect integration, please consult this guide. + status: Status + ok: Ok + instance_secret_key: Instance Secret Key + account_id: Account ID + business_name: Business Name + charges_enabled: Charges Enabled + charges_enabled_warning: "Warning: Charges are not enabled for your account" + auth_fail_error: The API key you provided is invalid + empty_api_key_error_html: No Stripe API key has been provided. To set your API key, please follow these instructions + matomo_settings: + edit: + title: "Matomo Settings" + matomo_url: "Matomo URL" + matomo_site_id: "Matomo Site ID" + info_html: "Matomo is a Web and Mobile Analytics. You can either host Matomo on-premises or use a cloud-hosted service. See matomo.org for more information." + config_instructions_html: "Here you can configure the OFN Matomo integration. The Matomo URL below should point to the Matomo instance where the user tracking information will be sent to; if it is left empty, Matomo user tracking will be disabled. The Site ID field is not mandatory but useful if you are tracking more than one website on a single Matomo instance; it can be found on the Matomo instance console." + customers: + index: + add_customer: "Add Customer" + new_customer: "New Customer" + customer_placeholder: "customer@example.org" + valid_email_error: Please enter a valid email address + add_a_new_customer_for: Add a new customer for %{shop_name} + code: Code + duplicate_code: "This code is used already." + bill_address: "Billing Address" + ship_address: "Shipping Address" + update_address_success: 'Address updated successfully.' + update_address_error: 'Sorry! Please input all of the required fields!' + edit_bill_address: 'Edit Billing Address' + edit_ship_address: 'Edit Shipping Address' + required_fileds: 'Required fields are denoted with an asterisk ' + select_country: 'Select Country' + select_state: 'Select State' + edit: 'Edit' + update_address: 'Update Address' + confirm_delete: 'Sure to delete?' + search_by_email: "Search by email/code..." + guest_label: 'Guest checkout' + destroy: + has_associated_orders: 'Delete failed: customer has associated orders with his shop' + contents: + edit: + title: Content + header: Header + home_page: Home page + producer_signup_page: Producer signup page + hub_signup_page: Hub signup page + group_signup_page: Group signup page + main_links: Main Menu Links + footer_and_external_links: Footer and External Links + your_content: Your content + user_guide: User Guide + enterprise_fees: + index: + title: Enterprise Fees + enterprise: Enterprise + fee_type: Fee Type + name: Name + tax_category: Tax Category + calculator: Calculator + calculator_values: Calculator Values + enterprise_groups: + index: + new_button: New Enterprise Group + enterprise_roles: + form: + manages: manages + enterprise_role: + manages: manages + products: + unit_name_placeholder: 'eg. bunches' + index: + unit: Unit + display_as: Display As + category: Category + tax_category: Tax Category + inherits_properties?: Inherits Properties? + available_on: Available On + av_on: "Av. On" + import_date: Imported + upload_an_image: Upload an image + product_search_keywords: Product Search Keywords + product_search_tip: Type words to help search your products in the shops. Use space to separate each keyword. + SEO_keywords: SEO Keywords + seo_tip: Type words to help search your products in the web. Use space to separate each keyword. + Search: Search + properties: + property_name: Property Name + inherited_property: Inherited Property + variants: + to_order_tip: "Items made to order do not have a set stock level, such as loaves of bread made fresh to order." + product_distributions: "Product Distributions" + group_buy_options: "Group Buy Options" + back_to_products_list: "Back to products list" + product_import: + title: Product Import + file_not_found: File not found or could not be opened + no_data: No data found in spreadsheet + confirm_reset: "This will set stock level to zero on all products for this \n enterprise that are not present in the uploaded file" + model: + no_file: "error: no file uploaded" + could_not_process: "could not process file: invalid filetype" + incorrect_value: incorrect value + conditional_blank: can't be blank if unit_type is blank + no_product: did not match any products in the database + not_found: not found in database + not_updatable: cannot be updated on existing products via product import + blank: can't be blank + products_no_permission: you do not have permission to manage products for this enterprise + inventory_no_permission: you do not have permission to create inventory for this producer + none_saved: did not save any products successfully + line_number: "Line %{number}:" + encoding_error: "Please check the language setting of your source file and ensure it is saved with UTF-8 encoding" + unexpected_error: "Product Import encountered an unexpected error whilst opening the file: %{error_message}" + index: + select_file: Select a spreadsheet to upload + spreadsheet: Spreadsheet + choose_import_type: Select import type + import_into: Import type + product_list: Product list + inventories: Inventories + import: Import + upload: Upload + csv_templates: CSV Templates + product_list_template: Download Product List template + inventory_template: Download Inventory template + category_values: Available Category Values + product_categories: Product Categories + tax_categories: Tax Categories + shipping_categories: Shipping Categories + import: + review: Review + import: Import + save: Save + results: Results + save_imported: Save imported products + no_valid_entries: No valid entries found + none_to_save: There are no entries that can be saved + some_invalid_entries: Imported file contains invalid entries + fix_before_import: Please fix these errors and try importing the file again + save_valid?: Save valid entries for now and discard the others? + no_errors: No errors detected! + save_all_imported?: Save all imported products? + options_and_defaults: Import options and defaults + 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_enterprise: some products do not have an enterprise associated + 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 + overwrite_empty: Overwrite if empty + default_stock: Set stock level + default_tax_cat: Set tax category + default_shipping_cat: Set shipping category + default_available_date: Set available date + validation_overview: Import validation overview + entries_found: Entries found in imported file + entries_with_errors: Items contain errors and will not be imported + products_to_create: Products will be created + products_to_update: Products will be updated + inventory_to_create: Inventory items will be created + inventory_to_update: Inventory items will be updated + products_to_reset: Existing products will have their stock reset to zero + inventory_to_reset: Existing inventory items will have their stock reset to zero + line: Line + item_line: Item line + 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 for existing products + save_results: + final_results: Import final results + products_created: Products created + products_updated: Products updated + inventory_created: Inventory items created + inventory_updated: Inventory items updated + products_reset: Products had stock level reset to zero + inventory_reset: Inventory items had stock level reset to zero + all_saved: "All items saved successfully" + some_saved: "items saved successfully" + save_errors: Save errors + import_again: Upload Another File + view_products: Go To Products Page + view_inventory: Go To Inventory Page + variant_overrides: + loading_flash: + loading_inventory: LOADING INVENTORY + index: + title: Inventory + description: Use this page to manage inventories for your enterprises. Any product details set here will override those set on the 'Products' page + enable_reset?: Enable Stock Reset? + inherit?: Inherit? + add: Add + hide: Hide + import_date: Imported + select_a_shop: Select A Shop + review_now: Review Now + new_products_alert_message: There are %{new_product_count} new products available to add to your inventory. + currently_empty: Your inventory is currently empty + no_matching_products: No matching products found in your inventory + no_hidden_products: No products have been hidden from this inventory + no_matching_hidden_products: No hidden products match your search criteria + no_new_products: No new products are available to add to this inventory + no_matching_new_products: No new products match your search criteria + inventory_powertip: This is your inventory of products. To add products to your inventory, select 'New Products' from the Viewing dropdown. + hidden_powertip: These products have been hidden from your inventory and will not be available to add to your shop. You can click 'Add' to add a product to you inventory. + new_powertip: These products are available to be added to your inventory. Click 'Add' to add a product to your inventory, or 'Hide' to hide it from view. You can always change your mind later! + controls: + back_to_my_inventory: Back to my inventory + orders: + invoice_email_sent: 'Invoice email has been sent' + order_email_resent: 'Order email has been resent' + bulk_management: + tip: "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." + shared: "Shared Resource?" + order_no: "Order No." + order_date: "Completed at" + max: "Max" + product_unit: "Product: Unit" + weight_volume: "Weight/Volume" + ask: "Ask?" + page_title: "Bulk Order Management" + actions_delete: "Delete Selected" + loading: "Loading orders" + no_results: "No orders found." + group_buy_unit_size: "Group Buy Unit Size" + total_qtt_ordered: "Total Quantity Ordered" + max_qtt_ordered: "Max Quantity Ordered" + current_fulfilled_units: "Current Fulfilled Units" + max_fulfilled_units: "Max Fulfilled Units" + order_error: "Some errors must be resolved before you can update orders.\nAny fields with red borders contain errors." + variants_without_unit_value: "WARNING: Some variants do not have a unit value" + select_variant: "Select a variant" + enterprise: + select_outgoing_oc_products_from: Select outgoing OC products from + enterprises: + index: + title: Enterprises + new_enterprise: New Enterprise + producer?: "Producer?" + package: Package + status: Status + manage: Manage + form: + about_us: + desc_short: Short Description + desc_short_placeholder: Tell us about your enterprise in one or two sentences + desc_long: About Us + desc_long_placeholder: Tell customers about yourself. This information appears on your public profile. + business_details: + abn: Business number if available + abn_placeholder: Number will show on invoices + acn: Business Number if available + acn_placeholder: eg. 123 456 789 + display_invoice_logo: Display logo in invoices + invoice_text: Add customized text at the end of invoices + contact: + name: Name + name_placeholder: eg. Gustav Plum + email_address: Public Email Address + email_address_placeholder: eg. inquiries@fresh-food.com + email_address_tip: "This email address will be displayed in your public profile" + phone: Phone + phone_placeholder: eg. 98 7654 3210 + website: Website + website_placeholder: eg. www.truffles.com + enterprise_fees: + name: Name + fee_type: Fee Type + manage_fees: Manage Enterprise Fees + no_fees_yet: You don't have any enterprise fees yet. + create_button: Create One Now + images: + logo: Logo + promo_image_placeholder: 'This image is displayed in "About Us"' + promo_image_note1: 'PLEASE NOTE:' + promo_image_note2: Any promo image uploaded here will be cropped to 1200 x 260. + promo_image_note3: The promo image is displayed at the top of an enterprise's profile page and pop-ups. + inventory_settings: + text1: You may opt to manage stock levels and prices in via your + inventory: inventory + text2: > + If you are using the inventory tool, you can select whether new products + added by your suppliers need to be added to your inventory before they + can be stocked. If you are not using your inventory to manage your products + you should select the 'recommended' option below: + preferred_product_selection_from_inventory_only_yes: New products can be put into my shopfront (recommended) + preferred_product_selection_from_inventory_only_no: New products must be added to my inventory before they can be put into my shopfront + payment_methods: + name: Name + applies: Applies? + manage: Manage Payment Methods + not_method_yet: You don't have any payment methods yet. + create_button: Create New Payment Method + create_one_button: Create One Now + primary_details: + name: Name + name_placeholder: eg. Professor Plum's Biodynamic Truffles + groups: Groups + groups_tip: Select any groups or regions that you are a member of. This will help customers find your enterprise. + groups_placeholder: Start typing to search available groups... + primary_producer: Primary Producer? + primary_producer_tip: Select 'Producer' if you are a primary producer of food. + producer: Producer + any: Any + none: None + own: Own + sells: Sells + sells_tip: "None - enterprise does not sell to customers directly.
Own - Enterprise sells own products to customers.
Any - Enterprise can sell own or other enterprises products.
" + visible_in_search: Visible in search? + visible_in_search_tip: Determines whether this enterprise will be visible to customers when searching the site. + visible: Visible + not_visible: Not visible + permalink: Permalink (no spaces) + permalink_tip: "This permalink is used to create the url to your shop: %{link}your-shop-name/shop" + link_to_front: Link to shop front + link_to_front_tip: A direct link to your shopfront on the Open Food Network. + shipping_methods: + name: Name + applies: Applies? + manage: Manage Shipping Methods + create_button: Create New Shipping Method + create_one_button: Create One Now + no_method_yet: You don't have any shipping methods yet. + shop_preferences: + shopfront_requires_login: "Publicly visible shopfront?" + shopfront_requires_login_tip: "Choose whether customers must login to view the shopfront or if it's visible to everybody." + shopfront_requires_login_false: "Public" + shopfront_requires_login_true: "Visible to registered customers only" + recommend_require_login: "We recommend to require users to login when orders can be changed." + allow_guest_orders: "Guest orders" + allow_guest_orders_tip: "Allow checkout as guest or require a registered user." + allow_guest_orders_false: "Require login to order" + allow_guest_orders_true: "Allow guest checkout" + allow_order_changes: "Change orders" + allow_order_changes_tip: "Allow customers to change their order as long the order cycle is open." + allow_order_changes_false: "Placed orders cannot be changed / cancelled" + allow_order_changes_true: "Customers can change / cancel orders while order cycle is open" + enable_subscriptions: "Subscriptions" + enable_subscriptions_tip: "Enable subscriptions functionality?" + enable_subscriptions_false: "Disabled" + enable_subscriptions_true: "Enabled" + shopfront_message: Shopfront Message + shopfront_message_placeholder: > + An optional explanation for customers detailing how your shopfront works, + to be displayed above the product list on your shop page. + shopfront_closed_message: Shopfront Closed Message + shopfront_closed_message_placeholder: > + A message which provides a more detailed explanation about why your + shop is closed and/or when customers can expect it to open again. This + is displayed on your shop only when you have no active order cycles + (ie. shop is closed). + shopfront_category_ordering: Shopfront Category Ordering + open_date: Open Date + close_date: Close Date + social: + twitter_placeholder: eg. @the_prof + instagram_placeholder: eg. www.instagram.com/YourAccountHere + facebook_placeholder: eg. www.facebook.com/PageNameHere + linkedin_placeholder: eg. www.linkedin.com/in/YourNameHere + stripe_connect: + connect_with_stripe: "Connect with Stripe" + stripe_connect_intro: "To accept payments using credit card, you will need to connect your stripe account to the Open Food Network. Use the button to the right to get started." + stripe_account_connected: "Stripe account connected." + disconnect: "Disconnect account" + confirm_modal: + title: Connect with Stripe + part1: Stripe is a payment processing service that allows shops on the OFN to accept credit card payments from customers. + part2: To use this feature, you must connect your Stripe account to the OFN. Clicking 'I Agree' below will redirect to you the Stripe website where you can connect an existing Stripe account, or create a new one if you don't already have one. + part3: This will allow the Open Food Network to accept credit card payments from customers on your behalf. Please note that you will need to maintain your own Stripe account, pay the fees Stripe charges and handle any chargebacks and customer service yourself. + i_agree: I Agree + cancel: Cancel + tag_rules: + default_rules: + by_default: By Default + no_rules_yet: No default rules apply yet + add_new_button: '+ Add A New Default Rule' + no_tags_yet: No tags apply to this enterprise yet + no_rules_yet: No rules apply to this tag yet + for_customers_tagged: 'For customers tagged:' + add_new_rule: '+ Add A New Rule' + add_new_tag: '+ Add A New Tag' + users: + email_confirmation_notice_html: "Email confirmation is pending. We've sent a confirmation email to %{email}." + resend: Resend + owner: 'Owner' + contact: "Contact" + contact_tip: "The manager who will receive enterprise emails for orders and notifications. Must have a confirmed email adress." + owner_tip: The primary user responsible for this enterprise. + notifications: Notifications + notifications_tip: Notifications about orders will be send to this email address. + notifications_placeholder: eg. gustav@truffles.com + notifications_note: 'Note: A new email address may need to be confirmed prior to use' + managers: Managers + managers_tip: The other users with permission to manage this enterprise. + invite_manager: "Invite Manager" + invite_manager_tip: "Invite an unregistered user to sign up and become a manager of this enterprise." + add_unregistered_user: "Add an unregistered user" + email_confirmed: "Email confirmed" + email_not_confirmed: "Email not confirmed" + actions: + edit_profile: Settings + properties: Properties + payment_methods: Payment Methods + payment_methods_tip: This enterprise has no payment methods + shipping_methods: Shipping Methods + shipping_methods_tip: This enterprise has shipping methods + enterprise_fees: Enterprise Fees + enterprise_fees_tip: This enterprise has no fees + admin_index: + name: Name + role: Role + sells: Sells + visible: Visible? + owner: Owner + producer: Producer + change_type_form: + producer_profile: Producer Profile + connect_ofn: Connect through OFN + always_free: ALWAYS FREE + producer_description_text: Add your products to Open Food Network, allowing hubs to stock your products in their stores. + producer_shop: Producer Shop + sell_your_produce: Sell your own produce + producer_shop_description_text: Sell your products directly to customers through your very own Open Food Network shopfront. + producer_shop_description_text2: A Producer Shop is for your produce only, if you want to sell produce grown/produced off site, select 'Producer Hub'. + producer_hub: Producer Hub + producer_hub_text: Sell produce from self and others + producer_hub_description_text: Your enterprise is the backbone of your local food system. You can sell your own produce as well as produce aggregated from other enterprises through your shopfront on the Open Food Network. + profile: Profile Only + get_listing: Get a listing + profile_description_text: People can find and contact you on the Open Food Network. Your enterprise will be visible on the map, and will be searchable in listings. + hub_shop: Hub Shop + hub_shop_text: Sell produce from others + hub_shop_description_text: Your enterprise is the backbone of your local food system. You aggregate produce from other enterprises and can sell it through your shop on the Open Food Network. + choose_option: Please choose one of the options above. + change_now: Change now + enterprise_user_index: + loading_enterprises: LOADING ENTERPRISES + no_enterprises_found: No enterprises found. + search_placeholder: Search By Name + manage: Manage + manage_link: Settings + producer?: "Producer?" + package: "Package" + status: "Status" + new_form: + owner: Owner + owner_tip: The primary user responsible for this enterprise. + i_am_producer: I am a Producer + contact_name: Contact Name + edit: + editing: 'Settings:' + back_link: Back to enterprises list + new: + title: New Enterprise + back_link: Back to enterprises list + remove_logo: + remove: "Remove Image" + removed_successfully: "Logo removed successfully" + immediate_removal_warning: "The logo will be removed immediately after you confirm." + remove_promo_image: + remove: "Remove Image" + removed_successfully: "Promo image removed successfully" + immediate_removal_warning: "The promo image will be removed immediately after you confirm." + welcome: + welcome_title: Welcome to the Open Food Network! + welcome_text: You have successfully created a + next_step: Next step + choose_starting_point: 'Choose your package:' + invite_manager: + user_already_exists: "User already exists" + error: "Something went wrong" + order_cycles: + edit: + advanced_settings: Advanced Settings + update_and_close: Update and Close + choose_products_from: 'Choose Products From:' + exchange_form: + pickup_time_tip: When orders from this OC will be ready for the customer + pickup_instructions_placeholder: "Pick-up instructions" + pickup_instructions_tip: These instructions are shown to customers after they complete an order + pickup_time_placeholder: "Ready for (ie. Date / Time)" + receival_instructions_placeholder: "Receival instructions" + add_fee: 'Add fee' + selected: 'selected' + add_exchange_form: + add_supplier: 'Add supplier' + add_distributor: 'Add distributor' + advanced_settings: + title: Advanced Settings + choose_product_tip: You can opt to restrict all available products (both incoming and outgoing), to only those in %{inventory}'s inventory. + preferred_product_selection_from_coordinator_inventory_only_here: Coordinator's Inventory Only + preferred_product_selection_from_coordinator_inventory_only_all: All Available Products + save_reload: Save and Reload Page + coordinator_fees: + add: Add coordinator fee + filters: + search_by_order_cycle_name: "Search by Order Cycle name..." + involving: "Involving" + any_enterprise: "Any Enterprise" + any_schedule: "Any Schedule" + form: + incoming: Incoming + supplier: Supplier + receival_details: Receival details + fees: Fees + outgoing: Outgoing + distributor: Distributor + products: Products + tags: Tags + add_a_tag: Add a tag + delivery_details: Pickup / Delivery details + debug_info: Debug information + index: + schedule: Schedule + schedules: Schedules + adding_a_new_schedule: Adding A New Schedule + updating_a_schedule: Updating A Schedule + new_schedule: New Schedule + create_schedule: Create Schedule + update_schedule: Update Schedule + delete_schedule: Delete Schedule + created_schedule: Created schedule + updated_schedule: Updated schedule + deleted_schedule: Deleted schedule + schedule_name_placeholder: Schedule Name + name_required_error: Please enter a name for this schedule + no_order_cycles_error: Please select at least one order cycle (drag and drop) + name_and_timing_form: + name: Name + orders_open: Orders open at + coordinator: Coordinator + orders_close: Orders close + row: + suppliers: suppliers + distributors: distributors + variants: variants + simple_form: + ready_for: Ready for + ready_for_placeholder: Date / time + customer_instructions: Customer instructions + customer_instructions_placeholder: Pick-up or delivery notes + products: Products + fees: Fees + destroy_errors: + orders_present: That order cycle has been selected by a customer and cannot be deleted. To prevent customers from accessing it, please close it instead. + schedule_present: That order cycle is linked to a schedule and cannot be deleted. Please unlink or delete the schedule first. + bulk_update: + no_data: Hm, something went wrong. No order cycle data found. + date_warning: + msg: This order cycle is linked to %{n} open subscription orders. Changing this date now will not affect any orders which have already been placed, but should be avoided if possible. Are you sure you want to proceed? + cancel: Cancel + proceed: Proceed + producer_properties: + index: + title: Producer Properties + proxy_orders: + cancel: + could_not_cancel_the_order: Could not cancel the order + resume: + could_not_resume_the_order: Could not resume the order + shared: + user_guide_link: + user_guide: User Guide + overview: + enterprises_header: + ofn_with_tip: Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network. + enterprises_hubs_tabs: + has_no_payment_methods: "%{enterprise} has no payment methods" + has_no_shipping_methods: "%{enterprise} has no shipping methods" + has_no_enterprise_fees: "%{enterprise} has no enterprise fees" + enterprise_issues: + create_new: Create New + resend_email: Resend Email + has_no_payment_methods: "%{enterprise} currently has no payment methods" + has_no_shipping_methods: "%{enterprise} currently has no shipping methods" + email_confirmation: "Email confirmation is pending. We've sent a confirmation email to %{email}." + not_visible: "%{enterprise} is not visible and so cannot be found on the map or in searches" + reports: + hidden: HIDDEN + unitsize: UNITSIZE + total: TOTAL + total_items: TOTAL ITEMS + supplier_totals: Order Cycle Supplier Totals + supplier_totals_by_distributor: Order Cycle Supplier Totals by Distributor + totals_by_supplier: Order Cycle Distributor Totals by Supplier + customer_totals: Order Cycle Customer Totals + all_products: All products + inventory: Inventory (on hand) + lettuce_share: LettuceShare + mailing_list: Mailing List + addresses: Addresses + payment_methods: Payment Methods Report + delivery: Delivery Report + tax_types: Tax Types + tax_rates: Tax Rates + pack_by_customer: Pack By Customer + pack_by_supplier: Pack By Supplier + orders_and_distributors: + name: Orders And Distributors + description: Orders with distributor details + bulk_coop: + name: Bulk Co-Op + description: Reports for Bulk Co-Op orders + payments: + name: Payment Reports + description: Reports for Payments + orders_and_fulfillment: + name: Orders & Fulfillment Reports + customers: + name: Customers + products_and_inventory: + name: Products & Inventory + sales_total: + name: Sales Total + description: Sales Total For All Orders + users_and_enterprises: + name: Users & Enterprises + description: Enterprise Ownership & Status + order_cycle_management: + name: Order Cycle Management + sales_tax: + name: Sales Tax + xero_invoices: + name: Xero Invoices + description: Invoices for import into Xero + packing: + name: Packing Reports + enterprise_fee_summary: + name: "Enterprise Fee Summary" + description: "Summary of Enterprise Fees collected" + subscriptions: + subscriptions: Subscriptions + new: New Subscription + create: Create Subscription + index: + please_select_a_shop: Please select a shop + edit_subscription: Edit Subscription + pause_subscription: Pause Subscription + unpause_subscription: Unpause Subscription + cancel_subscription: Cancel Subscription + setup_explanation: + just_a_few_more_steps: 'Just a few more steps before you can begin:' + enable_subscriptions: "Enable subscriptions for at least one of your shops" + enable_subscriptions_step_1_html: 1. Go to the %{enterprises_link} page, find your shop, and click "Manage" + enable_subscriptions_step_2: 2. Under "Shop Preferences", enable the Subscriptions option + set_up_shipping_and_payment_methods_html: Set up %{shipping_link} and %{payment_link} methods + set_up_shipping_and_payment_methods_note_html: Note that only Cash and Stripe payment methods may
be used with subscriptions + ensure_at_least_one_customer_html: Ensure that at least one %{customer_link} exists + create_at_least_one_schedule: Create at least one Schedule + create_at_least_one_schedule_step_1_html: 1. Go to the on the %{order_cycles_link} page + create_at_least_one_schedule_step_2: 2. Create an order cycle if you have not already done so + create_at_least_one_schedule_step_3: 3. Click '+ New Schedule', and fill out the form + once_you_are_done_you_can_html: Once you are done, you can %{reload_this_page_link} + reload_this_page: reload this page + steps: + details: 1. Basic Details + address: 2. Address + products: 3. Add Products + review: 4. Review & Save + subscription_line_items: + this_is_an_estimate: | + The displayed prices are only an estimate and calculated at the time the subscription is changed. + If you change prices or fees, orders will be updated, but the subscription will still display the old values. + not_in_open_and_upcoming_order_cycles_warning: "There are no open or upcoming order cycles for this product." + details: + details: Details + invalid_error: Oops! Please fill in all of the required fields... + allowed_payment_method_types_tip: Only Cash and Stripe payment methods may be used at the moment + credit_card: Credit Card + charges_not_allowed: Charges are not allowed by this customer + no_default_card: Customer has no cards available to charge + card_ok: Customer has a card available to charge + loading_flash: + loading: LOADING SUBSCRIPTIONS + review: + details: Details + address: Address + products: Products + no_open_or_upcoming_order_cycle: "No Upcoming Order Cycle" + product_already_in_order: This product has already been added to the order. Please edit the quantity directly. + orders: + number: Number + confirm_edit: Are you sure you want to edit this order? Doing so may make it more difficult to automatically sync changes to the subscription in the future. + confirm_cancel_msg: Are you sure you want to cancel this subscription? This action cannot be undone. + cancel_failure_msg: 'Sorry, cancellation failed!' + confirm_pause_msg: Are you sure you want to pause this subscription? + pause_failure_msg: 'Sorry, pausing failed!' + confirm_unpause_msg: Are you sure you want to unpause this subscription? + unpause_failure_msg: 'Sorry, unpausing failed!' + confirm_cancel_open_orders_msg: "Some orders for this subscription are currently open. The customer has already been notified that the order will be placed. Would you like to cancel these order(s) or keep them?" + resume_canceled_orders_msg: "Some orders for this subscription can be resumed right now. You can resume them from the orders dropdown." + yes_cancel_them: Cancel them + no_keep_them: Keep them + yes_i_am_sure: Yes, I'm sure + order_update_issues_msg: Some orders could not be automatically updated, this is most likely because they have been manually edited. Please review the issues listed below and make any adjustments to individual orders if required. + no_results: + no_subscriptions: No subscriptions yet... + why_dont_you_add_one: Why don't you add one? :) + no_matching_subscriptions: No matching subscriptions found + schedules: + destroy: + associated_subscriptions_error: This schedule cannot be deleted because it has associated subscriptions + controllers: + enterprises: + stripe_connect_cancelled: "Connection to Stripe has been cancelled" + stripe_connect_success: "Stripe account connected successfully" + stripe_connect_fail: Sorry, the connection of your Stripe account failed + stripe_connect_settings: + resource: Stripe Connect configuration + api: + enterprise_logo: + destroy_attachment_does_not_exist: "Logo does not exist" + enterprise_promo_image: + destroy_attachment_does_not_exist: "Promo image does not exist" + checkout: + already_ordered: + cart: "cart" + message_html: "You have an order for this order cycle already. Check the %{cart} to see the items you ordered before. You can also cancel items as long as the order cycle is open." + shops: + hubs: + show_closed_shops: "Show closed shops" + hide_closed_shops: "Hide closed shops" + show_on_map: "Show all on the map" + shared: + menu: + cart: + checkout: "Checkout now" + already_ordered_products: "Already ordered in this order cycle" + register_call: + selling_on_ofn: "Interested in getting on the Open Food Network?" + register: "Register here" + footer: + footer_global_headline: "OFN Global" + footer_global_home: "Home" + footer_global_news: "News" + footer_global_about: "About" + footer_global_contact: "Contact" + footer_sites_headline: "OFN Sites" + footer_sites_developer: "Developer" + footer_sites_community: "Community" + footer_sites_userguide: "User Guide" + footer_secure: "Secure and trusted." + footer_secure_text: "Open Food Network uses SSL encryption (2048 bit RSA) everywhere to keep your shopping and payment information private. Our servers do not store your credit card details and payments are processed by PCI-compliant services." + footer_contact_headline: "Keep in touch" + footer_contact_email: "Email us" + footer_nav_headline: "Navigate" + footer_join_headline: "Join us" + footer_join_body: "Create a listing, shop or group directory on the Open Food Network." + footer_join_cta: "Tell me more!" + footer_legal_call: "Read our" + footer_legal_tos: "Terms and conditions" + footer_legal_visit: "Find us on" + footer_legal_text_html: "Open Food Network is a free and open source software platform. Our content is licensed with %{content_license} and our code with %{code_license}." + footer_data_text_with_privacy_policy_html: "We take good care of your data. See our %{privacy_policy} and %{cookies_policy}" + footer_data_text_without_privacy_policy_html: "We take good care of your data. See our %{cookies_policy}" + footer_data_privacy_policy: "privacy policy" + footer_data_cookies_policy: "cookies policy" + footer_skylight_dashboard_html: Performance data is available on %{dashboard}. + shop: + messages: + login: "login" + register: "register" + contact: "contact" + require_customer_login: "This shop is for customers only." + require_login_html: "Please %{login} if you have an account already. Otherwise, %{register} to become a customer." + require_customer_html: "Please %{contact} %{enterprise} to become a customer." + card_could_not_be_updated: Card could not be updated + card_could_not_be_saved: card could not be saved + spree_gateway_error_flash_for_checkout: "There was a problem with your payment information: %{error}" + invoice_billing_address: "Billing address:" + invoice_column_tax: "GST" + invoice_column_price: "Price" + invoice_column_item: "Item" + invoice_column_qty: "Qty" + invoice_column_unit_price_with_taxes: "Unit price (Incl. tax)" + invoice_column_unit_price_without_taxes: "Unit price (Excl. tax)" + invoice_column_price_with_taxes: "Total price (Incl. tax)" + invoice_column_price_without_taxes: "Total price (Excl. tax)" + invoice_column_tax_rate: "Tax rate" + invoice_tax_total: "GST Total:" + tax_invoice: "TAX INVOICE" + tax_total: "Total tax (%{rate}):" + total_excl_tax: "Total (Excl. tax):" + total_incl_tax: "Total (Incl. tax):" + abn: "ABN:" + acn: "ACN:" + invoice_issued_on: "Invoice issued on:" + order_number: "Invoice number:" + date_of_transaction: "Date of transaction:" + ticket_column_qty: "Qty" + ticket_column_item: "Item" + ticket_column_unit_price: "Unit Price" + ticket_column_total_price: "Total Price" + menu_1_title: "Shops" + menu_1_url: "/shops" + menu_2_title: "Map" + menu_2_url: "/map" + menu_3_title: "Producers" + menu_3_url: "/producers" + menu_4_title: "Groups" + menu_4_url: "/groups" + menu_5_title: "About" + menu_5_url: "http://www.openfoodnetwork.org/" + menu_6_title: "Connect" + menu_6_url: "https://openfoodnetwork.org/au/connect/" + menu_7_title: "Learn" + menu_7_url: "https://openfoodnetwork.org/au/learn/" + logo: "Logo (640x130)" + logo_mobile: "Mobile logo (75x26)" + logo_mobile_svg: "Mobile logo (SVG)" + home_hero: "Hero image" + home_show_stats: "Show statistics" + 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: "Links" + footer_about_url: "About URL" + user_guide_link: "User Guide Link" + name: Name + first_name: First Name + last_name: Last Name + email: Email + phone: Phone + next: Next + address: Address + address_placeholder: eg. 123 High Street + address2: Address (contd.) + city: City + city_placeholder: eg. Northcote + postcode: Postcode + postcode_placeholder: eg. 3070 + state: State + country: Country + unauthorized: Unauthorized + terms_of_service: "Terms of service" + on_demand: On demand + none: None + not_allowed: Not allowed + no_shipping: no shipping methods + no_payment: no payment methods + no_shipping_or_payment: no shipping or payment methods + unconfirmed: unconfirmed + days: days + label_shop: "Shop" + label_shops: "Shops" + label_map: "Map" + label_producer: "Producer" + label_producers: "Producers" + label_groups: "Groups" + label_about: "About" + label_connect: "Connect" + label_learn: "Learn" + label_blog: "Blog" + label_support: "Support" + label_shopping: "Shopping" + label_login: "Login" + label_logout: "Logout" + label_signup: "Sign up" + label_administration: "Administration" + label_admin: "Admin" + label_account: "Account" + label_more: "Show more" + label_less: "Show less" + label_notices: "Notices" + cart_items: "items" + cart_headline: "Your shopping cart" + total: "Total" + cart_updating: "Updating cart..." + cart_empty: "Cart empty" + cart_edit: "Edit your cart" + card_number: Card Number + card_securitycode: "Security Code" + card_expiry_date: Expiry Date + card_masked_digit: "X" + card_expiry_abbreviation: "Exp" + new_credit_card: "New credit card" + my_credit_cards: My credit cards + add_new_credit_card: Add new credit card + saved_cards: Saved cards + add_a_card: Add a Card + add_card: Add Card + you_have_no_saved_cards: You haven't saved any cards yet + saving_credit_card: Saving credit card... + card_has_been_removed: "Your card has been removed (number: %{number})" + card_could_not_be_removed: Sorry, the card could not be removed + ie_warning_headline: "Your browser is out of date :-(" + ie_warning_text: "For the best Open Food Network experience, we strongly recommend upgrading your browser:" + ie_warning_chrome: Download Chrome + ie_warning_firefox: Download Firefox + ie_warning_ie: Upgrade Internet Explorer + ie_warning_other: "Can't upgrade your browser? Try Open Food Network on your smartphone :-)" + legal: + cookies_policy: + header: "How We Use Cookies" + desc_part_1: "Cookies are very small text files that are stored on your computer when you visit some websites." + desc_part_2: "In OFN we are fully respectful of your privacy. We use only the cookies that are necessary for delivering you the service of selling/buying food online. We don’t sell any of your data. We might in the future propose you to share some of your data to build new commons services that could be useful for the ecosystem (like logistics services for short food systems) but we are not yet there, and we won’t do it without your authorization :-)" + desc_part_3: "We use cookies mainly to remember who you are if you 'log in' to the service, or to be able to remember the items you put in your cart even if you are not logged in. If you keep navigating on the website without clicking on “Accept cookies”, we assume you are giving us consent to store the cookies that are essential for the functioning of the website. Here is the list of cookies we use!" + essential_cookies: "Essential Cookies" + essential_cookies_desc: "The following cookies are strictly necessary for the operation of our website." + essential_cookies_note: "Most cookies only contain a unique identifier, but no other data, so your email address and password for instance are never contained in them and never exposed." + cookie_domain: "Set By:" + cookie_session_desc: "Used to allow the website to remember users between page visits, for example, remember items in your cart." + cookie_consent_desc: "Used to maintain status of user consent to store cookies" + cookie_remember_me_desc: "Used if the user has requested the website to remember him. This cookie is automatically deleted after 12 days. If as a user you want that cookie to be deleted, you only need to logout. If you don’t want that cookie to be installed on your computer you shouldn’t check the “remember me” checkbox when logging in." + cookie_openstreemap_desc: "Used by our friendly open source mapping provider (OpenStreetMap) to ensure that it does not receive too many requests during a given time period, to prevent abuse of their services." + cookie_stripe_desc: "Data collected by our payment processor Stripe for fraud detection https://stripe.com/cookies-policy/legal. Not all shops use Stripe as a payment method but it is a good practice to prevent fraud to apply it to all pages. Stripe probably build a picture of which of our pages usually interact with their API and then flag anything unusual. So setting the Stripe cookie has a broader function than simply the provision of a payment method to a user. Removing it could affect the security of the service itself. You can learn more about Stripe and read its privacy policy at https://stripe.com/privacy." + statistics_cookies: "Statistics Cookies" + statistics_cookies_desc: "The following are not strictly necessary, but help to provide you with the best user experience by allowing us to analyse user behaviour, identify which features you use most, or don’t use, understand user experience issues, etc." + statistics_cookies_analytics_desc_html: "To collect and analyse platform usage data, we use Google Analytics, as it was the default service connected with Spree (the e-commerce open source software that we built on) but our vision is to switch to Matomo (ex Piwik, open source analytics tool that is GDPR compliant and protects your privacy) as soon as we can." + statistics_cookies_matomo_desc_html: "To collect and analyse platform usage data, we use Matomo (ex Piwik), an open source analytics tool that is GDPR compliant and protects your privacy." + statistics_cookies_matomo_optout: "Do you want to opt-out of Matomo analytics? We don’t collect any personal data, and Matomo helps us to improve our service, but we respect your choice :-)" + cookie_analytics_utma_desc: "Used to distinguish users and sessions. The cookie is created when the javascript library executes and no existing __utma cookies exists. The cookie is updated every time data is sent to Google Analytics." + cookie_analytics_utmt_desc: "Used to throttle request rate." + cookie_analytics_utmb_desc: "Used to determine new sessions/visits. The cookie is created when the javascript library executes and no existing __utmb cookies exists. The cookie is updated every time data is sent to Google Analytics." + cookie_analytics_utmc_desc: "Not used in ga.js. Set for interoperability with urchin.js. Historically, this cookie operated in conjunction with the __utmb cookie to determine whether the user was in a new session/visit." + cookie_analytics_utmz_desc: "Stores the traffic source or campaign that explains how the user reached your site. The cookie is created when the javascript library executes and is updated every time data is sent to Google Analytics." + cookie_matomo_basics_desc: "Matomo first party cookies to collect statistics." + cookie_matomo_heatmap_desc: "Matomo Heatmap & Session Recording cookie." + cookie_matomo_ignore_desc: "Cookie used to exclude user from being tracked." + disabling_cookies_header: "Warning on disabling cookies" + disabling_cookies_desc: "As a user you can always allow, block or delete Open Food Network’s or any other website cookies whenever you want to through your browser’s setting control. Each browser has a different operative. Here are the links:" + disabling_cookies_firefox_link: "https://support.mozilla.org/en-US/kb/enable-and-disable-cookies-website-preferences" + disabling_cookies_chrome_link: "https://support.google.com/chrome/answer/95647" + disabling_cookies_ie_link: "https://support.microsoft.com/en-us/help/17442/windows-internet-explorer-delete-manage-cookies" + disabling_cookies_safari_link: "https://www.apple.com/legal/privacy/en-ww/cookies/" + disabling_cookies_note: "But be aware that if you delete or modify the essential cookies used by Open Food Network, the website won’t work, you will not be able to add anything to your cart neither to checkout for instance." + cookies_banner: + cookies_usage: "This site uses cookies in order to make your navigation frictionless and secure, and to help us understand how you use it in order to improve the features we offer." + cookies_definition: "Cookies are very small text files that are stored on your computer when you visit some websites." + cookies_desc: "We use only the cookies that are necessary for delivering you the service of selling/buying food online. We don’t sell any of your data. We use cookies mainly to remember who you are if you ‘log in’ to the service, or to be able to remember the items you put in your cart even if you are not logged in. If you keep navigating on the website without clicking on “Accept cookies”, we assume you are giving us consent to store the cookies that are essential for the functioning of the website." + cookies_policy_link_desc: "If you want to learn more, check our" + cookies_policy_link: "cookies policy" + cookies_accept_button: "Accept Cookies" + home_shop: Shop Now + brandstory_headline: "Food, unincorporated." + brandstory_intro: "Sometimes the best way to fix the system is to start a new one…" + brandstory_part1: "We begin from the ground up. With farmers and growers ready to tell their stories proudly and truly. With distributors ready to connect people with products fairly and honestly. With buyers who believe that better weekly shopping decisions can seriously change the world." + brandstory_part2: "Then we need a way to make it real. A way to empower everyone who grows, sells and buys food. A way to tell all the stories, to handle all the logistics. A way to turn transaction into transformation every day." + brandstory_part3: "So we build an online marketplace that levels the playing field. It’s transparent, so it creates real relationships. It’s open source, so it’s owned by everyone. It scales to regions and nations, so people start versions across the world." + brandstory_part4: "It works everywhere. It changes everything." + brandstory_part5_strong: "We call it Open Food Network." + brandstory_part6: "We all love food. Now we can love our food system too." + learn_body: "Explore models, stories and resources to support you to develop your fair food business or organisation. Find training, events and other opportunities to learn from peers." + learn_cta: "Get Inspired" + connect_body: "Search our full directories of producers, hubs and groups to find fair food traders near you. List your business or organisation on the OFN so buyers can find you. Join the community to get advice and solve problems together." + connect_cta: "Go Exploring" + system_headline: "Shopping - here's how it works." + system_step1: "1. Search" + system_step1_text: "Search our diverse, independent shops for seasonal local food. Search by neighbourhood and food category, or whether you prefer delivery or pickup." + system_step2: "2. Shop" + system_step2_text: "Transform your transactions with affordable local food from diverse producers and hubs. Know the stories behind your food and the people who make it!" + system_step3: "3. Pick-up / Delivery" + system_step3_text: "Hang on for your delivery, or visit your producer or hub for a more personal connection with your food. Food shopping as diverse as nature intended it." + cta_headline: "Shopping that makes the world a better place." + cta_label: "I'm Ready" + stats_headline: "We're creating a new food system." + stats_producers: "food producers" + stats_shops: "food shops" + stats_shoppers: "food shoppers" + stats_orders: "food orders" + checkout_title: Checkout + checkout_now: Checkout now + checkout_order_ready: Order ready for + checkout_hide: Hide + checkout_expand: Expand + checkout_headline: "Ok, ready to checkout?" + checkout_as_guest: "Checkout as guest" + checkout_details: "Your details" + checkout_billing: "Billing info" + checkout_default_bill_address: "Save as default billing address" + checkout_shipping: Shipping info + checkout_default_ship_address: "Save as default shipping address" + checkout_method_free: Free + checkout_address_same: Shipping address same as billing address? + checkout_ready_for: "Ready for:" + checkout_instructions: "Any comments or special instructions?" + checkout_payment: Payment + checkout_send: Place order now + checkout_your_order: Your order + checkout_cart_total: Cart total + checkout_shipping_price: Shipping + checkout_total_price: Total + checkout_back_to_cart: "Back to Cart" + cost_currency: "Cost Currency" + order_paid: PAID + order_not_paid: NOT PAID + order_total: Total order + order_payment: "Paying via:" + order_billing_address: Billing address + order_delivery_on: Delivery on + order_delivery_address: Delivery address + order_delivery_time: Delivery time + order_special_instructions: "Your notes:" + order_pickup_time: Ready for collection + order_pickup_instructions: Collection Instructions + order_produce: Produce + order_total_price: Total + order_includes_tax: (includes tax) + order_payment_paypal_successful: Your payment via PayPal has been processed successfully. + order_hub_info: Hub Info + order_back_to_store: Back To Store + order_back_to_cart: Back To Cart + bom_tip: "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." + unsaved_changes_warning: "Unsaved changes exist and will be lost if you continue." + unsaved_changes_error: "Fields with red borders contain errors." + products: "Products" + products_in: "in %{oc}" + products_at: "at %{distributor}" + products_elsewhere: "Products found elsewhere" + email_welcome: "Welcome" + email_confirmed: "Thank you for confirming your email address." + email_registered: "is now part of" + email_userguide_html: "The User Guide with detailed support for setting up your Producer or Hub is here: %{link}" + email_admin_html: "You can manage your account by logging into the %{link} or by clicking on the cog in the top right hand side of the homepage, and selecting Administration." + email_community_html: "We also have an online forum for community discussion related to OFN software and the unique challenges of running a food enterprise. You are encouraged to join in. We are constantly evolving and your input into this forum will shape what happens next. %{link}" + join_community: "Join the community" + email_confirmation_activate_account: "Before we can activate your new account, we need to confirm your email address." + email_confirmation_greeting: "Hi, %{contact}!" + email_confirmation_profile_created: "A profile for %{name} has been successfully created! To activate your Profile we need to confirm this email address." + email_confirmation_click_link: "Please click the link below to confirm your email and to continue setting up your profile." + 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_social: "Connect with Us:" + email_contact: "Email us:" + email_signoff: "Cheers," + email_signature: "%{sitename} Team" + email_confirm_customer_greeting: "Hi %{name}," + email_confirm_customer_intro_html: "Thanks for shopping at %{distributor}!" + email_confirm_customer_number_html: "Order confirmation #%{number}" + email_confirm_customer_details_html: "Here are your order details from %{distributor}:" + email_confirm_customer_signoff: "Kind regards," + email_confirm_shop_greeting: "Hi %{name}," + email_confirm_shop_order_html: "Well done! You have a new order for %{distributor}!" + email_confirm_shop_number_html: "Order confirmation #%{number}" + email_order_summary_item: "Item" + email_order_summary_quantity: "Qty" + email_order_summary_price: "Price" + email_order_summary_subtotal: "Subtotal:" + email_order_summary_total: "Total:" + email_order_summary_includes_tax: "(includes tax):" + email_payment_paid: PAID + email_payment_not_paid: NOT PAID + email_payment_summary: Payment summary + email_payment_method: "Paying via:" + email_so_placement_intro_html: "You have a new order with %{distributor}" + email_so_placement_details_html: "Here are the details of your order for %{distributor}:" + email_so_placement_changes: "Unfortunately, not all products that you requested were available. The original quantities that you requested appear crossed-out below." + email_so_payment_success_intro_html: "An automatic payment has been processed for your order from %{distributor}." + email_so_placement_explainer_html: "This order was automatically created for you." + email_so_edit_true_html: "You can make changes until orders close on %{orders_close_at}." + email_so_edit_false_html: "You can view details of this order at any time." + email_so_contact_distributor_html: "If you have any questions you can contact %{distributor} via %{email}." + email_so_contact_distributor_to_change_order_html: "This order was automatically created for you. You can make changes until orders close on %{orders_close_at}by contacting %{distributor}via %{email}." + email_so_confirmation_intro_html: "Your order with %{distributor} is now confirmed" + email_so_confirmation_explainer_html: "This order was automatically placed for you, and it has now been finalised." + email_so_confirmation_details_html: "Here's everything you need to know about your order from %{distributor}:" + email_so_empty_intro_html: "We tried to place a new order with %{distributor}, but had some problems..." + email_so_empty_explainer_html: "Unfortunately, none of products that you ordered were available, so no order has been placed. The original quantities that you requested appear crossed-out below." + email_so_empty_details_html: "Here are the details of the unplaced order for %{distributor}:" + email_so_failed_payment_intro_html: "We tried to process a payment, but had some problems..." + email_so_failed_payment_explainer_html: "The payment for your subscription with %{distributor} failed because of a problem with your credit card. %{distributor} has been notified of this failed payment." + email_so_failed_payment_details_html: "Here are the details of the failure provided by the payment gateway:" + email_shipping_delivery_details: Delivery details + email_shipping_delivery_time: "Delivery on:" + email_shipping_delivery_address: "Delivery address:" + email_shipping_collection_details: Collection details + email_shipping_collection_time: "Ready for collection:" + email_shipping_collection_instructions: "Collection instructions:" + email_special_instructions: "Your notes:" + email_signup_greeting: Hello! + email_signup_welcome: "Welcome to %{sitename}!" + email_signup_confirmed_email: "Thanks for confirming your email." + email_signup_shop_html: "You can now log in at %{link}." + email_signup_text: "Thanks for joining the network. If you are a customer, we look forward to introducing you to many fantastic farmers, wonderful food hubs and delicious food! If you are a producer or food enterprise, we are excited to have you as a part of the network." + email_signup_help_html: "We welcome all your questions and feedback; you can use the Send Feedback button on the site or email us at %{email}" + invite_email: + greeting: "Hello!" + invited_to_manage: "You have been invited to manage %{enterprise} on %{instance}." + confirm_your_email: "You should have received or will soon receive an email with a confirmation link. You won’t be able to access %{enterprise}'s profile until you have confirmed your email." + set_a_password: "You will then be prompted to set a password before you are able to administer the enterprise." + mistakenly_sent: "Not sure why you have received this email? Please contact %{owner_email} for more information." + producer_mail_greeting: "Dear" + producer_mail_text_before: "We now have all the consumer orders for the next food drop." + producer_mail_order_text: "Here is a summary of the orders for your products:" + producer_mail_delivery_instructions: "Stock pickup/delivery instructions:" + producer_mail_signoff: "Thanks and best wishes" + shopping_oc_closed: Orders are closed + shopping_oc_closed_description: "Please wait until the next cycle opens (or contact us directly to see if we can accept any late orders)" + shopping_oc_last_closed: "The last cycle closed %{distance_of_time} ago" + shopping_oc_next_open: "The next cycle opens in %{distance_of_time}" + shopping_tabs_about: "About %{distributor}" + shopping_tabs_contact: "Contact" + shopping_contact_address: "Address" + shopping_contact_web: "Contact" + shopping_contact_social: "Follow" + shopping_groups_part_of: "is part of:" + shopping_producers_of_hub: "%{hub}'s producers:" + enterprises_next_closing: "Next order closing" + enterprises_ready_for: "Ready for" + enterprises_choose: "Choose when you want your order:" + maps_open: "Open" + maps_closed: "Closed" + hubs_buy: "Shop for:" + hubs_shopping_here: "Shopping here" + hubs_orders_closed: "Orders closed" + hubs_profile_only: "Profile only" + hubs_delivery_options: "Delivery options" + hubs_pickup: "Pickup" + hubs_delivery: "Delivery" + hubs_producers: "Our producers" + hubs_filter_by: "Filter by" + hubs_filter_type: "Type" + hubs_filter_delivery: "Delivery" + hubs_filter_property: "Property" + hubs_matches: "Did you mean?" + hubs_intro: Shop in your local area + hubs_distance: Closest to + hubs_distance_filter: "Show me shops near %{location}" + shop_changeable_orders_alert_html: + one: Your order with %{shop} / %{order} is open for review. You can make changes until %{oc_close}. + other: You have %{count} orders with %{shop} currently open for review. You can make changes until %{oc_close}. + orders_changeable_orders_alert_html: This order has been confirmed, but you can make changes until %{oc_close}. + products_clear_all: Clear all + products_showing: "Showing:" + products_with: with + products_search: "Search by product or producer" + products_loading: "Loading products..." + products_updating_cart: "Updating cart..." + products_cart_empty: "Cart empty" + products_edit_cart: "Edit your cart" + products_from: from + products_change: "No changes to save." + products_update_error: "Saving failed with the following error(s):" + products_update_error_msg: "Saving failed." + products_update_error_data: "Save failed due to invalid data:" + products_changes_saved: "Changes saved." + search_no_results_html: "Sorry, no results found for %{query}. Try another search?" + components_profiles_popover: "Profiles do not have a shopfront on the Open Food Network, but may have their own physical or online shop elsewhere" + components_profiles_show: "Show profiles" + components_filters_nofilters: "No filters" + components_filters_clearfilters: "Clear all filters" + groups_title: Groups + groups_headline: Groups / regions + groups_text: "Every producer is unique. Every business has something different to offer. Our groups are collectives of producers, hubs and distributors who share something in common like location, farmers market or philosophy. This makes your shopping experience easier. So explore our groups and have the curating done for you." + groups_search: "Search name or keyword" + groups_no_groups: "No groups found" + groups_about: "About Us" + groups_producers: "Our producers" + groups_hubs: "Our hubs" + groups_contact_web: Contact + groups_contact_social: Follow + groups_contact_address: Address + groups_contact_email: Email us + groups_contact_website: Visit our website + groups_contact_facebook: Follow us on Facebook + groups_signup_title: Sign up as a group + groups_signup_headline: Groups sign up + groups_signup_intro: "We're an amazing platform for collaborative marketing, the easiest way for your members and stakeholders to reach new markets. We're non-profit, affordable, and simple." + groups_signup_email: Email us + groups_signup_motivation1: We transform food systems fairly. + groups_signup_motivation2: It's why we get out of bed every day. We're a global non-profit, based on open source code. We play fair. You can always trust us. + groups_signup_motivation3: We know you have big ideas, and we want to help. We'll share our knowledge, networks and resources. We know that isolation doesn't create change, so we'll partner with you. + groups_signup_motivation4: We meet you where you are. + groups_signup_motivation5: You might be an alliance of food hubs, producers, or distributors, and an industry body, or a local government. + groups_signup_motivation6: Whatever your role in your local food movement, we're ready to help. However you come to wonder what Open Food Network would look like or is doing in your part of the world, let's start the conversation. + groups_signup_motivation7: We make food movements make more sense. + groups_signup_motivation8: You need to activate and enable your networks, we offer a platform for conversation and action. You need real engagement. We’ll help reach all the players, all the stakeholders, all the sectors. + groups_signup_motivation9: You need resourcing. We’ll bring all our experience to bear. You need cooperation. We’ll better connect you to a global network of peers. + groups_signup_pricing: Group Account + groups_signup_studies: Case Studies + groups_signup_contact: Ready to discuss? + groups_signup_contact_text: "Get in touch to discover what OFN can do for you:" + groups_signup_detail: "Here's the detail." + login_invalid: "Invalid email or password" + modal_hubs: "Food Hubs" + modal_hubs_abstract: Our food hubs are the point of contact between you and the people who make your food! + modal_hubs_content1: You can search for a convenient hub by location or name. Some hubs have multiple points where you can pick-up your purchases, and some will also provide delivery options. Each food hub is a sales point with independent business operations and logistics - so variations between hubs are to be expected. + modal_hubs_content2: You can only shop at one food hub at a time. + modal_groups: "Groups / Regions" + modal_groups_content1: These are the organisations and relationships between hubs which make up the Open Food Network. + modal_groups_content2: Some groups are clustered by location or council, others by non-geographic similarities. + modal_how: "How it works" + modal_how_shop: Shop the Open Food Network + modal_how_shop_explained: Search for a food hub near you to start shopping! You can expand each food hub to see what kinds of goodies are available, and click through to start shopping. (You can only shop one food hub at a time.) + modal_how_pickup: Pick-ups, delivery and shipping costs + modal_how_pickup_explained: Some food hubs deliver to your door, while others require you to pick-up your purchases. You can see which options are available on the homepage, and select which you'd like at the shopping and check-out pages. Delivery will cost more, and pricing differs from hub-to-hub. Each food hub is a sales point with independent business operations and logisitics - so variations between hubs are to be expected. + modal_how_more: Learn more + modal_how_more_explained: "If you want to learn more about the Open Food Network, how it works, and get involved, check out:" + modal_producers: "Producers" + modal_producers_explained: "Our producers make all the delicious food you can shop for on the Open Food Network." + producers_about: About us + producers_buy: Shop for + producers_contact: Contact + producers_contact_phone: Call + producers_contact_social: Follow + producers_buy_at_html: "Shop for %{enterprise} products at:" + producers_filter: Filter by + producers_filter_type: Type + producers_filter_property: Property + producers_title: Producers + producers_headline: Find local producers + producers_signup_title: Sign up as a producer + producers_signup_headline: Food producers, empowered. + producers_signup_motivation: Sell your food and tell your stories to diverse new markets. Save time and money on every overhead. We support innovation without the risk. We've levelled the playing field. + producers_signup_send: Join now + producers_signup_enterprise: Enterprise Accounts + producers_signup_studies: Stories from our producers. + producers_signup_cta_headline: Join now! + producers_signup_cta_action: Join now + producers_signup_detail: Here's the detail. + products_item: Item + products_description: Description + products_variant: Variant + products_quantity: Quantity + products_available: Available? + products_producer: "Producer" + products_price: "Price" + register_title: Register + sell_title: "Register" + sell_headline: "Get on the Open Food Network!" + sell_motivation: "Showcase your beautiful food." + sell_producers: "Producers" + sell_hubs: "Hubs" + sell_groups: "Groups" + sell_producers_detail: "Set up a profile for your business on the OFN in just minutes. At any time you can upgrade your profile to an online store and sell your products direct to customers." + sell_hubs_detail: "Set up a profile for your food enterprise or organisation on the OFN. At any time you can upgrade your profile to a multi-producer shop." + sell_groups_detail: "Set up a tailored directory of enterprises (producers and other food enterprises) for your region or for your organisation." + sell_user_guide: "Find out more in our user guide." + sell_listing_price: "Listing on the OFN is free. Opening and running a shop on OFN is free. Setting up a group directory on OFN for your organisation or regional network is free." + sell_embed: "We can also embed an OFN shop in your own customised website or build a customised local food network website for your region." + sell_ask_services: "Ask us about OFN services." + shops_title: Shops + shops_headline: Shopping, transformed. + shops_text: Food grows in cycles, farmers harvest in cycles, and we order food in cycles. If you find an order cycle closed, check back soon. + shops_signup_title: Sign up as a hub + shops_signup_headline: Food hubs, unlimited. + shops_signup_motivation: Whatever your model, we support you. However you change, we're with you. We're non-profit, independent, and open-sourced. We're the software partners you've dreamed of. + shops_signup_action: Join now + shops_signup_pricing: Enterprise Accounts + shops_signup_stories: Stories from our hubs. + shops_signup_help: We're ready to help. + shops_signup_help_text: You need a better return. You need new buyers and logistics partners. You need your story told across wholesale, retail, and the kitchen table. + shops_signup_detail: Here's the detail. + orders: Orders + orders_fees: Fees... + orders_edit_title: Shopping Cart + orders_edit_headline: Your shopping cart + orders_edit_time: Order ready for + orders_edit_continue: Continue shopping + orders_edit_checkout: Checkout + orders_form_empty_cart: "Empty cart" + orders_form_subtotal: Produce subtotal + orders_form_admin: Admin & Handling + orders_form_total: Total + orders_oc_expired_headline: Orders have closed for this order cycle + orders_oc_expired_text: "Sorry, orders for this order cycle closed %{time} ago! Please contact your hub directly to see if they can accept late orders." + orders_oc_expired_text_others_html: "Sorry, orders for this order cycle closed %{time} ago! Please contact your hub directly to see if they can accept late orders %{link}." + orders_oc_expired_text_link: "or see the other order cycles available at this hub" + orders_oc_expired_email: "Email:" + orders_oc_expired_phone: "Phone:" + orders_show_title: Order Confirmation + orders_show_time: Order ready on + orders_show_order_number: "Order #%{number}" + orders_show_cancelled: Cancelled + orders_show_confirmed: Confirmed + orders_your_order_has_been_cancelled: "Your order has been cancelled" + orders_could_not_cancel: "Sorry, the order could not be cancelled" + orders_cannot_remove_the_final_item: "Cannot remove the final item from an order, please cancel the order instead." + orders_bought_items_notice: + one: "An additional item is already confirmed for this order cycle" + other: "%{count} additional items already confirmed for this order cycle" + orders_bought_edit_button: Edit confirmed items + orders_bought_already_confirmed: "* already confirmed" + orders_confirm_cancel: Are you sure you want to cancel this order? + products_cart_distributor_choice: "Distributor for your order:" + products_cart_distributor_change: "Your distributor for this order will be changed to %{name} if you add this product to your cart." + products_cart_distributor_is: "Your distributor for this order is %{name}." + products_distributor_error: "Please complete your order at %{link} before shopping with another distributor." + products_oc: "Order cycle for your order:" + products_oc_change: "Your order cycle for this order will be changed to %{name} if you add this product to your cart." + products_oc_is: "Your order cycle for this order is %{name}." + products_oc_error: "Please complete your order from %{link} before shopping in a different order cycle." + products_oc_current: "your current order cycle" + products_max_quantity: Max quantity + products_distributor: Distributor + products_distributor_info: When you select a distributor for your order, their address and pickup times will be displayed here. + products_distribution_adjustment_label: "Product distribution by %{distributor} for %{product}" + shop_trial_expires_in: "Your shopfront trial expires in" + shop_trial_expired_notice: "Good news! We have decided to extend shopfront trials until further notice." + password: Password + remember_me: Remember Me + are_you_sure: "Are you sure?" + orders_open: Orders open + closing: "Closing " + going_back_to_home_page: "Taking you back to the home page" + creating: Creating + updating: Updating + failed_to_create_enterprise: "Failed to create your enterprise." + failed_to_create_enterprise_unknown: "Failed to create your enterprise.\nPlease ensure all fields are completely filled out." + failed_to_update_enterprise_unknown: "Failed to update your enterprise.\nPlease ensure all fields are completely filled out." + enterprise_confirm_delete_message: "This will also delete the %{product} that this enterprise supplies. Are you sure you want to continue?" + order_not_saved_yet: "Your order hasn't been saved yet. Give us a few seconds to finish!" + filter_by: "Filter by" + hide_filters: "Hide filters" + one_filter_applied: "1 filter applied" + x_filters_applied: " filters applied" + submitting_order: "Submitting your order: please wait" + confirm_hub_change: "Are you sure? This will change your selected hub and remove any items in your shopping cart." + confirm_oc_change: "Are you sure? This will change your selected order cycle and remove any items in your shopping cart." + location_placeholder: "Type in a location..." + error_required: "can't be blank" + error_number: "must be number" + error_email: "must be email address" + error_not_found_in_database: "%{name} not found in database" + error_not_primary_producer: "%{name} is not enabled as a producer" + error_no_permission_for_enterprise: "\"%{name}\": you do not have permission to manage products for this enterprise" + item_handling_fees: "Item Handling Fees (included in item totals)" + january: "January" + february: "February" + march: "March" + april: "April" + may: "May" + june: "June" + july: "July" + august: "August" + september: "September" + october: "October" + november: "November" + december: "December" + email_not_found: "Email address not found" + email_unconfirmed: "You must confirm your email address before you can reset your password." + email_required: "You must provide an email address" + logging_in: "Hold on a moment, we're logging you in" + signup_email: "Your email" + choose_password: "Choose a password" + confirm_password: "Confirm password" + action_signup: "Sign up now" + welcome_to_ofn: "Welcome to the Open Food Network!" + signup_or_login: "Start By Signing Up (or logging in)" + have_an_account: "Already have an account?" + action_login: "Log in now." + forgot_password: "Forgot Password?" + password_reset_sent: "An email with instructions on resetting your password has been sent!" + reset_password: "Reset password" + who_is_managing_enterprise: "Who is responsible for managing %{enterprise}?" + update_and_recalculate_fees: "Update And Recalculate Fees" + registration: + steps: + images: + continue: "Continue" + back: "Back" + headline: "Thanks!" + description: "Let's upload some pretty pictures so your profile looks great! :)" + type: + headline: "Last step to add %{enterprise}!" + question: "Are you a producer?" + yes_producer: "Yes, I'm a producer" + no_producer: "No, I'm not a producer" + producer_field_error: "Please choose one. Are you are producer?" + yes_producer_help: "Producers make yummy things to eat and/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it." + no_producer_help: "If you’re not a producer, you’re probably someone who sells and distributes food. You might be a hub, coop, buying group, retailer, wholesaler or other." + create_profile: "Create Profile" + enterprise: + registration: + modal: + steps: + details: + title: 'Details' + headline: "Let's Get Started" + enterprise: "Woot! First we need to know a little bit about your enterprise:" + producer: "Woot! First we need to know a little bit about your farm:" + enterprise_name_field: "Enterprise Name:" + producer_name_field: "Farm Name:" + producer_name_field_placeholder: "e.g. Charlie's Awesome Farm" + producer_name_field_error: "Please choose a unique name for your enterprise" + address1_field: "Address line 1:" + address1_field_placeholder: "e.g. 123 Cranberry Drive" + address1_field_error: "Please enter an address" + address2_field: "Address line 2:" + suburb_field: "Suburb:" + suburb_field_placeholder: "e.g. Northcote" + suburb_field_error: "Please enter a suburb" + postcode_field: "Postcode:" + postcode_field_placeholder: "e.g. 3070" + postcode_field_error: "Postcode required" + state_field: "State:" + state_field_error: "State required" + country_field: "Country:" + country_field_error: "Please select a country" + contact: + title: 'Contact' + contact_field: 'Primary Contact' + contact_field_placeholder: 'Contact Name' + contact_field_required: "You need to enter a primary contact." + email_field: 'Email address' + email_field_placeholder: 'eg. charlie@thefarm.com' + phone_field: 'Phone number' + phone_field_placeholder: 'eg. (03) 1234 5678' + type: + title: 'Type' + about: + title: 'About' + images: + title: 'Images' + social: + title: 'Social' + enterprise_contact: "Primary Contact" + enterprise_contact_placeholder: "Contact Name" + enterprise_contact_required: "You need to enter a primary contact." + enterprise_email_address: "Email address" + enterprise_email_placeholder: "eg. charlie@thefarm.com" + enterprise_phone: "Phone number" + enterprise_phone_placeholder: "eg. (03) 1234 5678" + back: "Back" + continue: "Continue" + limit_reached_headline: "Oh no!" + limit_reached_message: "You have reached the limit!" + limit_reached_text: "You have reached the limit for the number of enterprises you are allowed to own on the" + limit_reached_action: "Return to the homepage" + select_promo_image: "Step 3. Select Promo Image" + promo_image_tip: "Tip: Shown as a banner, preferred size is 1200×260px" + promo_image_label: "Choose a promo image" + action_or: "OR" + promo_image_drag: "Drag and drop your promo here" + review_promo_image: "Step 4. Review Your Promo Banner" + review_promo_image_tip: "Tip: for best results, your promo image should fill the available space" + promo_image_placeholder: "Your logo will appear here for review once uploaded" + uploading: "Uploading..." + select_logo: "Step 1. Select Logo Image" + logo_tip: "Tip: Square images will work best, preferably at least 300×300px" + logo_label: "Choose a logo image" + logo_drag: "Drag and drop your logo here" + review_logo: "Step 2. Review Your Logo" + review_logo_tip: "Tip: for best results, your logo should fill the available space" + logo_placeholder: "Your logo will appear here for review once uploaded" + enterprise_about_headline: "Nice one!" + enterprise_about_message: "Now let's flesh out the details about" + enterprise_success: "Success! %{enterprise} added to the Open Food Network " + enterprise_registration_exit_message: "If you exit this wizard at any stage, you can continue to create your profile by going to the admin interface." + enterprise_description: "Short Description" + enterprise_description_placeholder: "A short sentence describing your enterprise" + enterprise_long_desc: "Long Description" + enterprise_long_desc_placeholder: "This is your opportunity to tell the story of your enterprise - what makes you different and wonderful? We'd suggest keeping your description to under 600 characters or 150 words." + enterprise_long_desc_length: "%{num} characters / up to 600 recommended" + enterprise_abn: "ABN" + enterprise_abn_placeholder: "eg. 99 123 456 789" + enterprise_acn: "ACN" + enterprise_acn_placeholder: "eg. 123 456 789" + enterprise_tax_required: "You need to make a selection." + enterprise_final_step: "Final step!" + enterprise_social_text: "How can people find %{enterprise} online?" + website: "Website" + website_placeholder: "eg. openfoodnetwork.org.au" + facebook: "Facebook" + facebook_placeholder: "eg. www.facebook.com/PageNameHere" + linkedin: "LinkedIn" + linkedin_placeholder: "eg. www.linkedin.com/YourNameHere" + twitter: "Twitter" + twitter_placeholder: "eg. @twitter_handle" + instagram: "Instagram" + instagram_placeholder: "eg. @instagram_handle" + registration_greeting: "Hi there!" + registration_intro: "You can now create a profile for your Producer or Hub" + registration_action: "Let's get started!" + registration_checklist: "You'll need" + registration_time: "5-10 minutes" + registration_enterprise_address: "Enterprise address" + registration_contact_details: "Primary contact details" + registration_logo: "Your logo image" + registration_promo_image: "Landscape image for your profile" + registration_about_us: "'About Us' text" + registration_outcome_headline: "What do I get?" + registration_outcome1_html: "Your profile helps people find and contact you on the Open Food Network." + registration_outcome2: "Use this space to tell the story of your enterprise, to help drive connections to your social and online presence. " + registration_outcome3: "It's also the first step towards trading on the Open Food Network, or opening an online store." + registration_finished_headline: "Finished!" + registration_finished_thanks: "Thanks for filling out the details for %{enterprise}." + registration_finished_login: "You can change or update your enterprise at any stage by logging into Open Food Network and going to Admin." + registration_finished_action: "Open Food Network home" + registration_contact_name: 'Contact Name' + registration_type_headline: "Last step to add %{enterprise}!" + registration_type_question: "Are you a producer?" + registration_type_producer: "Yes, I'm a producer" + registration_type_no_producer: "No, I'm not a producer" + registration_type_error: "Please choose one. Are you are producer?" + registration_type_producer_help: "Producers make yummy things to eat and/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it." + registration_type_no_producer_help: "If you’re not a producer, you’re probably someone who sells and distributes food. You might be a hub, coop, buying group, retailer, wholesaler or other." + registration_detail_headline: "Let's Get Started" + registration_detail_enterprise: "Woot! First we need to know a little bit about your enterprise:" + registration_detail_producer: "Woot! First we need to know a little bit about your farm:" + registration_detail_name_enterprise: "Enterprise Name:" + registration_detail_name_producer: "Farm Name:" + registration_detail_name_placeholder: "e.g. Charlie's Awesome Farm" + registration_detail_name_error: "Please choose a unique name for your enterprise" + registration_detail_address1: "Address line 1:" + registration_detail_address1_placeholder: "e.g. 123 Cranberry Drive" + registration_detail_address1_error: "Please enter an address" + registration_detail_address2: "Address line 2:" + registration_detail_suburb: "Suburb:" + registration_detail_suburb_placeholder: "e.g. Northcote" + registration_detail_suburb_error: "Please enter a suburb" + registration_detail_postcode: "Postcode:" + registration_detail_postcode_placeholder: "e.g. 3070" + registration_detail_postcode_error: "Postcode required" + registration_detail_state: "State:" + registration_detail_state_error: "State required" + registration_detail_country: "Country:" + registration_detail_country_error: "Please select a country" + shipping_method_destroy_error: "That shipping method cannot be deleted as it is referenced by an order: %{number}." + accounts_and_billing_task_already_running_error: "A task is already running, please wait until it has finished" + accounts_and_billing_start_task_notice: "Task Queued" + fees: "Fees" + item_cost: "Item cost" + bulk: "Bulk" + shop_variant_quantity_min: "min" + shop_variant_quantity_max: "max" + follow: "Follow" + shop_for_products_html: "Shop for %{enterprise} products at:" + change_shop: "Change shop to:" + shop_at: "Shop now at:" + price_breakdown: "Full price breakdown" + admin_fee: "Admin fee" + sales_fee: "Sales fee" + packing_fee: "Packing fee" + transport_fee: "Transport fee" + fundraising_fee: "Fundraising fee" + price_graph: "Price graph" + included_tax: "Included tax" + balance: "Balance" + transaction: "Transaction" + transaction_date: "Date" + payment_state: "Payment status" + shipping_state: "Shipping status" + value: "Value" + balance_due: "Balance due" + credit: "Credit" + Paid: "Paid" + Ready: "Ready" + ok: OK + not_visible: not visible + you_have_no_orders_yet: "You have no orders yet" + running_balance: "Running balance" + outstanding_balance: "Outstanding balance" + admin_enterprise_relationships: "Enterprise Permissions" + admin_enterprise_relationships_everything: "Everything" + admin_enterprise_relationships_permits: "permits" + admin_enterprise_relationships_seach_placeholder: "Search" + admin_enterprise_relationships_button_create: "Create" + admin_enterprise_groups: "Enterprise Groups" + admin_enterprise_groups_name: "Name" + admin_enterprise_groups_owner: "Owner" + admin_enterprise_groups_on_front_page: "On front page ?" + admin_enterprise_groups_enterprise: "Enterprises" + admin_enterprise_groups_data_powertip: "The primary user responsible for this group." + admin_enterprise_groups_data_powertip_logo: "This is the logo for the group" + admin_enterprise_groups_data_powertip_promo_image: "This image is displayed at the top of the Group profile" + admin_enterprise_groups_contact: "Contact" + admin_enterprise_groups_contact_phone_placeholder: "eg. 98 7654 3210" + admin_enterprise_groups_contact_address1_placeholder: "eg. 123 High Street" + admin_enterprise_groups_contact_city: "Suburb" + admin_enterprise_groups_contact_city_placeholder: "eg. Northcote" + admin_enterprise_groups_contact_zipcode: "Postcode" + admin_enterprise_groups_contact_zipcode_placeholder: "eg. 3070" + admin_enterprise_groups_contact_state_id: "State" + admin_enterprise_groups_contact_country_id: "Country" + admin_enterprise_groups_web: "Web Resources" + admin_enterprise_groups_web_twitter: "eg. @the_prof" + admin_enterprise_groups_web_website_placeholder: "eg. www.truffles.com" + admin_order_cycles: "Admin Order Cycles" + open: "Open" + close: "Close" + create: "Create" + search: "Search" + supplier: "Supplier" + product_name: "Product Name" + product_description: "Product Description" + units: "Unit Size" + coordinator: "Coordinator" + distributor: "Distributor" + enterprise_fees: "Enterprise Fees" + process_my_order: "Process My Order" + delivery_instructions: Delivery Instructions + delivery_method: Delivery Method + fee_type: "Fee Type" + tax_category: "Tax Category" + calculator: "Calculator" + calculator_values: "Calculator values" + flat_percent_per_item: "Flat Percent (per item)" + flat_rate_per_item: "Flat Rate (per item)" + flat_rate_per_order: "Flat Rate (per order)" + flexible_rate: "Flexible Rate" + price_sack: "Price Sack" + new_order_cycles: "New Order Cycles" + new_order_cycle: "New Order Cycle" + select_a_coordinator_for_your_order_cycle: "Select a coordinator for your order cycle" + notify_producers: 'Notify producers' + edit_order_cycle: "Edit Order Cycle" + roles: "Roles" + update: "Update" + delete: Delete + add_producer_property: "Add producer property" + in_progress: "In Progress" + started_at: "Started at" + queued: "Queued" + scheduled_for: "Scheduled for" + customers: "Customers" + please_select_hub: "Please select a Hub" + loading_customers: "Loading Customers" + no_customers_found: "No customers found" + go: "Go" + hub: "Hub" + producer: "Producer" + product: "Product" + price: "Price" + on_hand: "On hand" + save_changes: "Save Changes" + order_saved: "Order Saved" + no_products: No Products + spree_admin_overview_enterprises_header: "My Enterprises" + spree_admin_overview_enterprises_footer: "MANAGE MY ENTERPRISES" + spree_admin_enterprises_hubs_name: "Name" + spree_admin_enterprises_create_new: "CREATE NEW" + spree_admin_enterprises_shipping_methods: "Shipping Methods" + spree_admin_enterprises_fees: "Enterprise Fees" + spree_admin_enterprises_none_create_a_new_enterprise: "CREATE A NEW ENTERPRISE" + spree_admin_enterprises_none_text: "You don't have any enterprises yet" + spree_admin_enterprises_tabs_hubs: "HUBS" + spree_admin_enterprises_producers_manage_products: "MANAGE PRODUCTS" + spree_admin_enterprises_create_new_product: "CREATE A NEW PRODUCT" + spree_admin_single_enterprise_alert_mail_confirmation: "Please confirm the email address for" + spree_admin_single_enterprise_alert_mail_sent: "We've sent an email to" + spree_admin_overview_action_required: "Action Required" + spree_admin_overview_check_your_inbox: "Please check your inbox for further instructions. Thanks!" + spree_admin_unit_value: Unit Value + spree_admin_unit_description: Unit Description + spree_admin_variant_unit: Variant unit + spree_admin_variant_unit_scale: Variant unit scale + spree_admin_supplier: Supplier + spree_admin_product_category: Product Category + spree_admin_variant_unit_name: Variant unit name + change_package: "Change Package" + spree_admin_single_enterprise_hint: "Hint: To allow people to find you, turn on your visibility under" + spree_admin_eg_pickup_from_school: "eg. 'Pick-up from Primary School'" + spree_admin_eg_collect_your_order: "eg. 'Please collect your order from 123 Imaginary St, Northcote, 3070'" + spree_classification_primary_taxon_error: "Taxon %{taxon} is the primary taxon of %{product} and cannot be deleted" + spree_order_availability_error: "Distributor or order cycle cannot supply the products in your cart" + spree_order_populator_error: "That distributor or order cycle can't supply all the products in your cart. Please choose another." + spree_order_populator_availability_error: "That product is not available from the chosen distributor or order cycle." + spree_distributors_error: "At least one hub must be selected" + spree_user_enterprise_limit_error: "^%{email} is not permitted to own any more enterprises (limit is %{enterprise_limit})." + spree_variant_product_error: must have at least one variant + your_profil_live: "Your profile live" + on_ofn_map: "on the Open Food Network map" + see: "See" + live: "live" + manage: "Manage" + resend: "Resend" + trial: Trial + add_and_manage_products: "Add & manage products" + add_and_manage_order_cycles: "Add & manage order cycles" + manage_order_cycles: "Manage order cycles" + manage_products: "Manage products" + edit_profile_details: "Edit profile details" + edit_profile_details_etc: "Change your profile description, images, etc." + order_cycle: "Order Cycle" + order_cycles: "Order Cycles" + enterprise_relationships: "Enterprise permissions" + remove_tax: "Remove tax" + first_name_begins_with: "First name begins with" + last_name_begins_with: "Last name begins with" + enterprise_tos_link: "Enterprise Terms of Service link" + enterprise_tos_message: "We want to work with people that share our aims and values. As such we ask new enterprises to agree to our " + enterprise_tos_link_text: "Terms of Service." + enterprise_tos_agree: "I agree to the above Terms of Service" + tax_settings: "Tax Settings" + products_require_tax_category: "products require tax category" + admin_shared_address_1: "Address" + admin_shared_address_2: "Address (cont.)" + admin_share_city: "City" + admin_share_zipcode: "Postcode" + admin_share_country: "Country" + admin_share_state: "State" + hub_sidebar_hubs: "Hubs" + hub_sidebar_none_available: "None Available" + hub_sidebar_manage: "Manage" + hub_sidebar_at_least: "At least one hub must be selected" + hub_sidebar_blue: "blue" + hub_sidebar_red: "red" + shop_trial_in_progress: "Your shopfront trial expires in %{days}." + report_customers_distributor: "Distributor" + report_customers_supplier: "Supplier" + report_customers_cycle: "Order Cycle" + report_customers_type: "Report Type" + report_customers_csv: "Download as csv" + report_producers: "Producers: " + report_type: "Report Type: " + report_hubs: "Hubs: " + report_payment: "Payment Methods: " + report_distributor: "Distributor: " + report_payment_by: 'Payments By Type' + report_itemised_payment: 'Itemised Payment Totals' + report_payment_totals: 'Payment Totals' + report_all: 'all' + report_order_cycle: "Order Cycle: " + report_enterprises: "Enterprises: " + report_users: "Users: " + report_tax_rates: Tax rates + report_tax_types: Tax types + report_header_order_cycle: Order Cycle + report_header_user: User + report_header_email: Email + report_header_status: Status + report_header_comments: Comments + report_header_first_name: First Name + report_header_last_name: Last Name + report_header_phone: Phone + report_header_suburb: Suburb + report_header_address: Address + report_header_billing_address: Billing Address + report_header_relationship: Relationship + report_header_hub: Hub + report_header_hub_address: Hub Address + report_header_to_hub: To Hub + report_header_hub_code: Hub Code + report_header_code: Code + report_header_paid: Paid? + report_header_delivery: Delivery? + report_header_shipping: Shipping + report_header_shipping_method: Shipping Method + report_header_shipping_instructions: Shipping instructions + report_header_ship_street: Ship Street + report_header_ship_street_2: Ship Street 2 + report_header_ship_city: Ship City + report_header_ship_postcode: Ship Postcode + report_header_ship_state: Ship State + report_header_billing_street: Billing Street + report_header_billing_street_2: Billing Street 2 + report_header_billing_street_3: Billing Street 3 + report_header_billing_street_4: Billing Street 4 + report_header_billing_city: Billing City + report_header_billing_postcode: Billing Postcode + report_header_billing_state: Billing State + report_header_incoming_transport: Incoming Transport + report_header_special_instructions: Special Instructions + report_header_order_number: Order number + report_header_date: Date + report_header_confirmation_date: Confirmation Date + report_header_tags: Tags + report_header_items: Items + report_header_items_total: "Items total %{currency_symbol}" + report_header_taxable_items_total: "Taxable Items Total (%{currency_symbol})" + report_header_sales_tax: "Sales Tax (%{currency_symbol})" + report_header_delivery_charge: "Delivery Charge (%{currency_symbol})" + report_header_tax_on_delivery: "Tax on Delivery (%{currency_symbol})" + report_header_tax_on_fees: "Tax on Fees (%{currency_symbol})" + report_header_total_tax: "Total Tax (%{currency_symbol})" + report_header_enterprise: Enterprise + report_header_customer: Customer + report_header_customer_code: Customer Code + report_header_product: Product + report_header_product_properties: Product Properties + report_header_quantity: Quantity + report_header_max_quantity: Max Quantity + report_header_variant: Variant + report_header_variant_value: Variant Value + report_header_variant_unit: Variant Unit + report_header_total_available: Total available + report_header_unallocated: Unallocated + report_header_max_quantity_excess: Max Quantity Excess + report_header_taxons: Taxons + report_header_supplier: Supplier + report_header_producer: Producer + report_header_producer_suburb: Producer Suburb + report_header_unit: Unit + report_header_group_buy_unit_quantity: Group Buy Unit Quantity + report_header_cost: Cost + report_header_shipping_cost: Shipping Cost + report_header_curr_cost_per_unit: Curr. Cost per Unit + report_header_total_shipping_cost: Total Shipping Cost + report_header_payment_method: Payment Method + report_header_sells: Sells + report_header_visible: Visible + report_header_price: Price + report_header_unit_size: Unit Size + report_header_distributor: Distributor + report_header_distributor_address: Distributor address + report_header_distributor_city: Distributor city + report_header_distributor_postcode: Distributor postcode + report_header_delivery_address: Delivery Address + report_header_delivery_postcode: Delivery Postcode + report_header_bulk_unit_size: Bulk Unit Size + report_header_weight: Weight + report_header_sum_total: Sum Total + report_header_date_of_order: Date of Order + report_header_amount_owing: Amount Owing + report_header_amount_paid: Amount Paid + report_header_units_required: Units Required + report_header_remainder: Remainder + report_header_order_date: Order date + report_header_order_id: Order Id + report_header_item_name: Item name + report_header_temp_controlled_items: Temp Controlled Items? + report_header_customer_name: Customer Name + report_header_customer_email: Customer Email + report_header_customer_phone: Customer Phone + report_header_customer_city: Customer City + report_header_payment_state: Payment State + report_header_payment_type: Payment Type + report_header_item_price: "Item (%{currency})" + report_header_item_fees_price: "Item + Fees (%{currency})" + report_header_admin_handling_fees: "Admin & Handling (%{currency})" + report_header_ship_price: "Ship (%{currency})" + report_header_pay_fee_price: "Pay fee (%{currency})" + report_header_total_price: "Total (%{currency})" + report_header_product_total_price: "Product Total (%{currency})" + report_header_shipping_total_price: "Shipping Total (%{currency})" + report_header_outstanding_balance_price: "Outstanding Balance (%{currency})" + report_header_eft_price: "EFT (%{currency})" + report_header_paypal_price: "PayPal (%{currency})" + report_header_sku: SKU + report_header_amount: Amount + report_header_balance: Balance + report_header_total_cost: "Total Cost" + report_header_total_ordered: Total Ordered + report_header_total_max: Total Max + report_header_total_units: Total Units + report_header_sum_max_total: "Sum Max Total" + report_header_total_excl_vat: "Total excl. tax (%{currency_symbol})" + report_header_total_incl_vat: "Total incl. tax (%{currency_symbol})" + report_header_temp_controlled: TempControlled? + report_header_is_producer: Producer? + report_header_not_confirmed: Not Confirmed + report_header_gst_on_income: GST on Income + report_header_gst_free_income: GST Free Income + report_header_total_untaxable_produce: Total untaxable produce (no tax) + report_header_total_taxable_produce: Total taxable produce (tax inclusive) + report_header_total_untaxable_fees: Total untaxable fees (no tax) + report_header_total_taxable_fees: Total taxable fees (tax inclusive) + report_header_delivery_shipping_cost: Delivery Shipping Cost (tax inclusive) + report_header_transaction_fee: Transaction Fee (no tax) + report_header_total_untaxable_admin: Total untaxable admin adjustments (no tax) + report_header_total_taxable_admin: Total taxable admin adjustments (tax inclusive) + initial_invoice_number: "Initial invoice number:" + invoice_date: "Invoice date:" + due_date: "Due date:" + account_code: "Account code:" + equals: "Equals" + contains: "contains" + discount: "Discount" + filter_products: "Filter Products" + delete_product_variant: "The last variant cannot be deleted!" + progress: "progress" + saving: "Saving.." + success: "success" + failure: "failure" + unsaved_changes_confirmation: "Unsaved changes will be lost. Continue anyway?" + one_product_unsaved: "Changes to one product remain unsaved." + products_unsaved: "Changes to %{n} products remain unsaved." + is_already_manager: "is already a manager!" + no_change_to_save: " No change to save" + user_invited: "%{email} has been invited to manage this enterprise" + add_manager: "Add an existing user" + users: "Users" + about: "About" + images: "Images" + web: "Web" + primary_details: "Primary Details" + adrdress: "Address" + contact: "Contact" + social: "Social" + business_details: "Business Details" + properties: "Properties" + shipping: "Shipping" + shipping_methods: "Shipping Methods" + payment_methods: "Payment Methods" + payment_method_fee: "Transaction fee" + inventory_settings: "Inventory Settings" + tag_rules: "Tag Rules" + shop_preferences: "Shop Preferences" + enterprise_fee_whole_order: Whole order + enterprise_fee_by: "%{type} fee by %{role} %{enterprise_name}" + validation_msg_relationship_already_established: "^That relationship is already established." + validation_msg_at_least_one_hub: "^At least one hub must be selected" + validation_msg_product_category_cant_be_blank: "^Product Category cant be blank" + validation_msg_tax: "^Tax Category is required" + validation_msg_tax_category_cant_be_blank: "^Tax Category can't be blank" + validation_msg_is_associated_with_an_exising_customer: "is associated with an existing customer" + content_configuration_pricing_table: "(TODO: Pricing table)" + content_configuration_case_studies: "(TODO: Case studies)" + content_configuration_detail: "(TODO: Detail)" + enterprise_name_error: "has already been taken. If this is your enterprise and you would like to claim ownership, or if you would like to trade with this enterprise please contact the current manager of this profile at %{email}." + enterprise_owner_error: "^%{email} is not permitted to own any more enterprises (limit is %{enterprise_limit})." + enterprise_role_uniqueness_error: "^That role is already present." + inventory_item_visibility_error: must be true or false + product_importer_file_error: "error: no file uploaded" + product_importer_spreadsheet_error: "could not process file: invalid filetype" + product_importer_products_save_error: did not save any products successfully + product_import_file_not_found_notice: 'File not found or could not be opened' + product_import_no_data_in_spreadsheet_notice: 'No data found in spreadsheet' + order_choosing_hub_notice: Your hub has been selected. + order_cycle_selecting_notice: Your order cycle has been selected. + adjustments_tax_rate_error: "^Please check that the tax rate for this adjustment is correct." + active_distributors_not_ready_for_checkout_message_singular: >- + The hub %{distributor_names} is listed in an active order cycle, but does not + have valid shipping and payment methods. Until you set these up, customers will + not be able to shop at this hub. + active_distributors_not_ready_for_checkout_message_plural: >- + The hubs %{distributor_names} are listed in an active order cycle, but do not + have valid shipping and payment methods. Until you set these up, customers will + not be able to shop at these hubs. + enterprise_fees_update_notice: Your enterprise fees have been updated. + enterprise_fees_destroy_error: "That enterprise fee cannot be deleted as it is referenced by a product distribution: %{id} - %{name}." + enterprise_register_package_error: "Please select a package" + enterprise_register_error: "Could not complete registration for %{enterprise}" + enterprise_register_success_notice: "Congratulations! Registration for %{enterprise} is complete!" + enterprise_bulk_update_success_notice: "Enterprises updated successfully" + enterprise_bulk_update_error: 'Update failed' + order_cycles_create_notice: 'Your order cycle has been created.' + order_cycles_update_notice: 'Your order cycle has been updated.' + order_cycles_bulk_update_notice: 'Order cycles have been updated.' + order_cycles_clone_notice: "Your order cycle %{name} has been cloned." + order_cycles_email_to_producers_notice: 'Emails to be sent to producers have been queued for sending.' + order_cycles_no_permission_to_coordinate_error: "None of your enterprises have permission to coordinate an order cycle" + order_cycles_no_permission_to_create_error: "You don't have permission to create an order cycle coordinated by that enterprise" + back_to_orders_list: "Back to order list" + no_orders_found: "No Orders Found" + order_information: "Order Information" + date_completed: "Date Completed" + amount: "Amount" + state_names: + ready: Ready + pending: Pending + shipped: Shipped + js: + saving: 'Saving...' + changes_saved: 'Changes saved.' + save_changes_first: Save changes first. + all_changes_saved: All changes saved + unsaved_changes: You have unsaved changes + all_changes_saved_successfully: All changes saved successfully + oh_no: "Oh no! I was unable to save your changes." + unauthorized: "You are unauthorised to access this page." + error: Error + unavailable: Unavailable + profile: Profile + hub: Hub + shop: Shop + choose: Choose + resolve_errors: Please resolve the following errors + more_items: "+ %{count} More" + default_card_updated: Default Card Updated + admin: + enterprise_limit_reached: "You have reached the standard limit of enterprises per account. Write to %{contact_email} if you need to increase it." + modals: + got_it: Got it + close: "Close" + invite: "Invite" + invite_title: "Invite an unregistered user" + tag_rule_help: + title: Tag Rules + overview: Overview + overview_text: > + Tag rules provide a way to describe which items are visible or otherwise + to which customers. Items can be Shipping Methods, Payment Methods, + Products and Order Cycles. + by_default_rules: "'By Default...' Rules" + by_default_rules_text: > + Default rules allow you to hide items so that they are not visible by + default. This behaviour can then be overriden by non-default rules for + customers with particular tags. + customer_tagged_rules: "'Customers Tagged...' Rules" + customer_tagged_rules_text: > + By creating rules related to a specific customer tag, you can override + the default behaviour (whether it be to show or to hide items) for customers + with the specified tag. + panels: + save: SAVE + saved: SAVED + saving: SAVING + enterprise_package: + hub_profile: Hub Profile + hub_profile_cost: "COST: ALWAYS FREE" + hub_profile_text1: > + People can find and contact you on the Open Food Network. Your enterprise + will be visible on the map, and will be searchable in listings. + hub_profile_text2: > + Having a profile, and making connections within your local food system + through the Open Food Network will always be free. + hub_shop: Hub Shop + hub_shop_text1: > + Your enterprise is the backbone of your local food system. You aggregate + produce from other enterprises and can sell it through your shop on + the Open Food Network. + hub_shop_text2: > + Hubs can take many forms, whether they be a food co-op, a buying group, + a veggie-box program, or a local grocery store. + hub_shop_text3: > + If you also want to sell your own products, you will need to switch + this enterprise to be a producer. + choose_package: Please Choose a Package + choose_package_text1: > + Your enterprise will not be fully activated until a package is selected + from the options on the left. + choose_package_text2: > + Click on an option to see more detailed information about each package, + and hit the red SAVE button when you are done! + profile_only: Profile Only + profile_only_cost: "COST: ALWAYS FREE" + profile_only_text1: > + A profile makes you visible and contactable to others and is a way to + share your story. + profile_only_text2: > + If you prefer to focus on producing food, and want to leave the work + of selling it to someone else, you won't require a shop on the Open + Food Network. + profile_only_text3: > + Add your products to Open Food Network, allowing hubs to stock your + products in their stores. + producer_shop: Producer Shop + producer_shop_text1: > + Sell your products directly to customers through your very own Open + Food Network shopfront. + producer_shop_text2: > + A Producer Shop is for your produce only, if you want to sell produce + grown/produced off site, please select 'Producer Hub'. + producer_hub: Producer Hub + producer_hub_text1: > + Your enterprise is the backbone of your local food system. You can sell + your own produce as well as produce aggregated from other enterprises + through your shopfront on the Open Food Network. + producer_hub_text2: > + Producer Hubs can take many forms, whether they be a CSA, a veggie-box + program, or a food co-op with a rooftop garden. + producer_hub_text3: > + The Open Food Network aims to support as many hub models as possible, + so no matter your situation, we want to provide the tools you need to + run your organisation or local food business. + get_listing: Get a listing + always_free: ALWAYS FREE + sell_produce_others: Sell produce from others + sell_own_produce: Sell your own produce + sell_both: Sell produce from self and others + enterprise_producer: + producer: Producer + producer_text1: > + Producers make yummy things to eat or drink. You're a producer if you + grow it, raise it, brew it, bake it, ferment it, milk it or mould it. + producer_text2: > + Producers can also perform other functions, such as aggregating food + from other enterprises and selling it through a shop on the Open Food + Network. + non_producer: Non-producer + non_producer_text1: > + Non-producers do not produce any food themselves, meaning that they + cannot create their own products for sale through the Open Food Network. + non_producer_text2: > + Instead, non-producers specialise in linking producers to the end eater, + whether it be by aggregating, grading, packing, selling or delivering + food. + producer_desc: Producers of food + producer_example: eg. GROWERS, BAKERS, BREWERS, MAKERS + non_producer_desc: All other food enterprises + non_producer_example: eg. Grocery stores, Food co-ops, Buying groups + enterprise_status: + status_title: "%{name} is set up and ready to go!" + severity: Severity + description: Description + resolve: Resolve + new_tag_rule_dialog: + select_rule_type: "Select a rule type:" + orders: + index: + per_page: "%{results} per page" + view_file: View File + compiling_invoices: Compiling Invoices + bulk_invoice_created: Bulk Invoice created + bulk_invoice_failed: Failed to create Bulk Invoice + please_wait: Please wait until the PDF is ready before closing this modal. + resend_user_email_confirmation: + resend: "Resend" + sending: "Resend..." + done: "Resend done ✓" + failed: "Resend failed ✗" + out_of_stock: + reduced_stock_available: Reduced stock available + out_of_stock_text: > + While you've been shopping, the stock levels for one or more of the products + in your cart have reduced. Here's what's changed: + now_out_of_stock: is now out of stock. + only_n_remainging: "now only has %{num} remaining." + variants: + on_demand: + 'yes': "On demand" + variant_overrides: + on_demand: + use_producer_settings: "Use producer stock settings" + 'yes': "Yes" + 'no': "No" + inventory_products: "Inventory Products" + hidden_products: "Hidden Products" + new_products: "New Products" + reset_stock_levels: Reset Stock Levels To Defaults + changes_to: Changes to + one_override: one override + overrides: overrides + remain_unsaved: remain unsaved. + no_changes_to_save: No changes to save.' + no_authorisation: "I couldn't get authorisation to save those changes, so they remain unsaved." + some_trouble: "I had some trouble saving: %{errors}" + changing_on_hand_stock: Changing on hand stock levels... + stock_reset: Stocks reset to defaults. + tag_rules: + show_hide_variants: 'Show or Hide variants in my shopfront' + show_hide_shipping: 'Show or Hide shipping methods at checkout' + show_hide_payment: 'Show or Hide payment methods at checkout' + show_hide_order_cycles: 'Show or Hide order cycles in my shopfront' + visible: VISIBLE + not_visible: NOT VISIBLE + services: + unsaved_changes_message: Unsaved changes currently exist, save now or ignore? + save: SAVE + ignore: IGNORE + add_to_order_cycle: "add to order cycle" + manage_products: "manage products" + edit_profile: "edit profile" + add_products_to_inventory: "add products to inventory" + resources: + could_not_delete_customer: 'Could not delete customer' + product_import: + confirmation: | + This will set stock level to zero on all products for this + enterprise that are not present in the uploaded file. + order_cycles: + create_failure: "Failed to create order cycle" + update_success: 'Your order cycle has been updated.' + update_failure: "Failed to update order cycle" + no_distributors: There are no distributors in this order cycle. This order cycle will not be visible to customers until you add one. Would you like to continue saving this order cycle?' + enterprises: + producer: "Producer" + non_producer: "Non-Producer" + customers: + select_shop: 'Please select a shop first' + could_not_create: Sorry! Could not create + subscriptions: + closes: closes + closed: closed + close_date_not_set: Close date not set + producers: + signup: + start_free_profile: "Start with a free profile, and expand when you're ready!" + order_management: + reports: + enterprise_fee_summary: + date_end_before_start_error: "must be after start" + parameter_not_allowed_error: "You are not authorized to use one or more selected filters for this report." + fee_calculated_on_transfer_through_all: "All" + fee_calculated_on_transfer_through_entire_orders: "Entire Orders through %{distributor}" + tax_category_various: "Various" + fee_type: + payment_method: "Payment Transaction" + shipping_method: "Shipment" + fee_placements: + supplier: "Incoming" + distributor: "Outgoing" + coordinator: "Coordinator" + tax_category_name: + shipping_instance_rate: "Platform Rate" + formats: + csv: + header: + fee_type: "Fee Type" + enterprise_name: "Enterprise Owner" + fee_name: "Fee Name" + customer_name: "Customer" + fee_placement: "Fee Placement" + fee_calculated_on_transfer_through_name: "Fee Calc on Transfer Through" + tax_category_name: "Tax Category" + total_amount: "$$ SUM" + html: + header: + fee_type: "Fee Type" + enterprise_name: "Enterprise Owner" + fee_name: "Fee Name" + customer_name: "Customer" + fee_placement: "Fee Placement" + fee_calculated_on_transfer_through_name: "Fee Calc on Transfer Through" + tax_category_name: "Tax Category" + total_amount: "$$ SUM" + invalid_filter_parameters: "The filters you selected for this report are invalid." + spree: + email: Email + account_updated: "Account updated!" + my_account: "My account" + date: "Date" + time: "Time" + layouts: + admin: + header: + store: Store + admin: + product_properties: + index: + 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}." + print_invoices: "Print Invoices" + invoice: + issued_on: Issued on + tax_invoice: TAX INVOICE + code: Code + from: From + to: To + form: + distribution_fields: + title: Distribution + distributor: "Distributor:" + order_cycle: "Order cycle:" + overview: + products: + active_products: + zero: "You don't have any active products." + one: "You have one active product" + other: "You have %{count} active products" + order_cycles: + order_cycles: "Order Cycles" + order_cycles_tip: "Order cycles determine when and where your products are available to customers." + you_have_active: + zero: "You don't have any active order cycles." + one: "You have one active order cycle." + other: "You have %{count} active order cycles." + manage_order_cycles: "MANAGE ORDER CYCLES" + payment_methods: + new: + new_payment_method: "New Payment Method" + back_to_payment_methods_list: "Back To Payment Methods List" + edit: + editing_payment_method: "Editing Payment Method" + back_to_payment_methods_list: "Back To Payment Methods List" + stripe_connect: + enterprise_select_placeholder: Choose... + loading_account_information_msg: Loading account information from stripe, please wait... + stripe_disabled_msg: Stripe payments have been disabled by the system administrator. + request_failed_msg: Sorry. Something went wrong when trying to verify account details with Stripe... + account_missing_msg: No Stripe account exists for this enterprise. + connect_one: Connect One + access_revoked_msg: Access to this Stripe account has been revoked, please reconnect your account. + status: Status + connected: Connected + account_id: Account ID + business_name: Business Name + charges_enabled: Charges Enabled + payments: + source_forms: + stripe: + error_saving_payment: Error saving payment + submitting_payment: Submitting payment... + products: + new: + title: 'New Product' + unit_name_placeholder: 'eg. bunches' + index: + header: + title: Bulk Edit Products + indicators: + title: LOADING PRODUCTS + no_products: "No products yet. Why don't you add some?" + no_results: "Sorry, no results match" + products_head: + name: Name + unit: Unit + display_as: Display As + category: Category + tax_category: Tax Category + inherits_properties?: Inherits Properties? + available_on: Available On + av_on: "Av. On" + import_date: "Import Date" + products_variant: + variant_has_n_overrides: "This variant has %{n} override(s)" + new_variant: "New variant" + product_name: Product Name + primary_taxon_form: + product_category: Product Category + group_buy_form: + group_buy: "Group Buy?" + bulk_unit_size: Bulk unit size + display_as: + display_as: Display As + reports: + table: + select_and_search: "Select filters and click on %{option} to access your data." + bulk_coop: + bulk_coop_supplier_report: 'Bulk Co-op - Totals by Supplier' + bulk_coop_allocation: 'Bulk Co-op - Allocation' + bulk_coop_packing_sheets: 'Bulk Co-op - Packing Sheets' + bulk_coop_customer_payments: 'Bulk Co-op - Customer Payments' + enterprise_fee_summaries: + filters: + date_range: "Date Range" + report_format_csv: "Download as CSV" + generate_report: "Generate Report" + report: + none: "None" + select_and_search: "Select filters and click on GENERATE REPORT to access your data." + users: + index: + listing_users: "Listing Users" + new_user: "New User" + user: "User" + enterprise_limit: "Enterprise Limit" + search: "Search" + email: "Email" + edit: + editing_user: "Editing User" + back_to_users_list: "Back To Users List" + general_settings: "General Settings" + form: + email: "Email" + roles: "Roles" + enterprise_limit: "Enterprise Limit" + confirm_password: "Confirm Password" + password: "Password" + email_confirmation: + confirmation_pending: "Email confirmation is pending. We've sent a confirmation email to %{address}." + variants: + autocomplete: + producer_name: Producer + general_settings: + edit: + legal_settings: "Legal Settings" + cookies_consent_banner_toggle: "Display cookies consent banner" + privacy_policy_url: "Privacy Policy URL" + enterprises_require_tos: "Enterprises must accept Terms of Service" + cookies_policy_matomo_section: "Display Matomo section on cookies policy page" + cookies_policy_ga_section: "Display Google Analytics section on cookies policy page" + footer_tos_url: "Terms of Service URL" + checkout: + payment: + stripe: + choose_one: Choose one + enter_new_card: Enter details for a new card + used_saved_card: "Use a saved card:" + or_enter_new_card: "Or, enter details for a new card:" + remember_this_card: Remember this card? + date_picker: + format: '%Y-%m-%d' + 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: + invoice_email: + hi: "Hi %{name}" + invoice_attached_text: Please find attached an invoice for your recent order from + order_state: + address: address + adjustments: adjustments + awaiting_return: awaiting return + canceled: cancelled + cart: cart + complete: complete + confirm: confirm + delivery: delivery + paused: paused + payment: payment + pending: pending + resumed: resumed + returned: returned + skrill: skrill + subscription_state: + active: active + pending: pending + ended: ended + paused: paused + canceled: cancelled + payment_states: + balance_due: balance due + completed: completed + checkout: checkout + credit_owed: credit owed + failed: failed + paid: paid + pending: pending + processing: processing + void: void + invalid: invalid + shipment_states: + backorder: backorder + partial: partial + pending: pending + ready: ready + shipped: shipped + user_mailer: + reset_password_instructions: + request_sent_text: | + A request to reset your password has been made. + If you did not make this request, simply ignore this email. + link_text: > + If you did make this request just click the link below: + issue_text: | + If the above URL does not work try copying and pasting it into your browser. + If you continue to have problems please feel free to contact us. + confirmation_instructions: + subject: Please confirm your OFN account + weight: Weight (per kg) + zipcode: Postcode + users: + form: + account_settings: Account Settings + show: + tabs: + orders: Orders + cards: Credit Cards + transactions: Transactions + settings: Account Settings + unconfirmed_email: "Pending email confirmation for: %{unconfirmed_email}. Your email address will be updated once the new email is confirmed." + orders: + open_orders: Open Orders + past_orders: Past Orders + transactions: + transaction_history: Transaction History + open_orders: + order: Order + shop: Shop + changes_allowed_until: Changes Allowed Until + items: Items + total: Total + edit: Edit + cancel: Cancel + closed: Closed + until: Until + past_orders: + order: Order + shop: Shop + completed_at: Completed At + items: Items + total: Total + paid?: Paid? + view: View + saved_cards: + default?: Default? + delete?: Delete? + cards: + 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. From 45e5fed609b11776f162eba9382ab5f65a4bc411 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Thu, 21 Feb 2019 11:25:01 +0000 Subject: [PATCH 22/57] Improve scope_variant_to_hub code and spec by testing an invalid data state --- app/models/variant_override.rb | 4 ++++ lib/open_food_network/scope_variant_to_hub.rb | 16 ++++++++++------ .../scope_variant_to_hub_spec.rb | 14 +++++++------- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app/models/variant_override.rb b/app/models/variant_override.rb index a00caef367..6b43822e6c 100644 --- a/app/models/variant_override.rb +++ b/app/models/variant_override.rb @@ -62,6 +62,10 @@ class VariantOverride < ActiveRecord::Base count_on_hand.present? end + def use_producer_settings? + on_demand.nil? + end + def decrement_stock!(quantity) if stock_overridden? decrement! :count_on_hand, quantity diff --git a/lib/open_food_network/scope_variant_to_hub.rb b/lib/open_food_network/scope_variant_to_hub.rb index 55d8dd92b1..d838e253cb 100644 --- a/lib/open_food_network/scope_variant_to_hub.rb +++ b/lib/open_food_network/scope_variant_to_hub.rb @@ -29,15 +29,19 @@ module OpenFoodNetwork end def count_on_hand - return super unless @variant_override.andand.stock_overridden? - - @variant_override.count_on_hand + if @variant_override.present? && @variant_override.stock_overridden? + @variant_override.count_on_hand + else + super + end end def on_demand - return super if @variant_override.andand.on_demand.nil? - - @variant_override.andand.on_demand + if @variant_override.present? && !@variant_override.use_producer_settings? + @variant_override.on_demand + else + super + end end def decrement!(attribute, by = 1) diff --git a/spec/lib/open_food_network/scope_variant_to_hub_spec.rb b/spec/lib/open_food_network/scope_variant_to_hub_spec.rb index 03f19c62eb..4f0ec5f13e 100644 --- a/spec/lib/open_food_network/scope_variant_to_hub_spec.rb +++ b/spec/lib/open_food_network/scope_variant_to_hub_spec.rb @@ -82,13 +82,6 @@ module OpenFoodNetwork context "without an on_demand set" do before { vo.update_column(:on_demand, nil) } - context "when count_on_hand is set" do - it "returns variant's on_demand" do - scoper.scope v - expect(v.on_demand).to be true - end - end - context "when count_on_hand is not set" do before { vo.update_column(:count_on_hand, nil) } @@ -97,6 +90,13 @@ module OpenFoodNetwork expect(v.on_demand).to be true end end + + context "when count_on_hand is set" do + it "should return validation error on save" do + scoper.scope v + expect{ vo.save! }.to raise_error ActiveRecord::RecordInvalid + end + end end end From 0b1ea1beda14016ddca2d97ad07336989044c819 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Fri, 22 Feb 2019 09:41:25 +0000 Subject: [PATCH 23/57] Rename VariantOverride.use_producer_settings to VariantOverride.use_producer_stock_settings --- app/models/variant_override.rb | 2 +- lib/open_food_network/scope_variant_to_hub.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/variant_override.rb b/app/models/variant_override.rb index 6b43822e6c..355a46d1db 100644 --- a/app/models/variant_override.rb +++ b/app/models/variant_override.rb @@ -62,7 +62,7 @@ class VariantOverride < ActiveRecord::Base count_on_hand.present? end - def use_producer_settings? + def use_producer_stock_settings? on_demand.nil? end diff --git a/lib/open_food_network/scope_variant_to_hub.rb b/lib/open_food_network/scope_variant_to_hub.rb index d838e253cb..a721505f7f 100644 --- a/lib/open_food_network/scope_variant_to_hub.rb +++ b/lib/open_food_network/scope_variant_to_hub.rb @@ -37,7 +37,7 @@ module OpenFoodNetwork end def on_demand - if @variant_override.present? && !@variant_override.use_producer_settings? + if @variant_override.present? && !@variant_override.use_producer_stock_settings? @variant_override.on_demand else super From ee8fe4b6b407356a9647692a0733c263bda2ccaf Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Thu, 14 Jun 2018 16:21:11 +0100 Subject: [PATCH 24/57] Move registration process translations to lazy lookups to better organize the en.yml file --- .../registration_authentication.html.haml | 8 +- app/views/registration/steps/_about.html.haml | 28 +- .../registration/steps/_contact.html.haml | 16 +- .../registration/steps/_details.html.haml | 43 ++- .../registration/steps/_finished.html.haml | 8 +- .../steps/_introduction.html.haml | 28 +- .../steps/_limit_reached.html.haml | 8 +- app/views/registration/steps/_logo.html.haml | 18 +- app/views/registration/steps/_promo.html.haml | 18 +- .../registration/steps/_social.html.haml | 24 +- app/views/registration/steps/_steps.html.haml | 2 +- config/locales/en.yml | 280 ++++++++---------- spec/features/consumer/registration_spec.rb | 2 +- 13 files changed, 219 insertions(+), 264 deletions(-) diff --git a/app/assets/javascripts/templates/registration_authentication.html.haml b/app/assets/javascripts/templates/registration_authentication.html.haml index f277c50abc..b5a525c3f2 100644 --- a/app/assets/javascripts/templates/registration_authentication.html.haml +++ b/app/assets/javascripts/templates/registration_authentication.html.haml @@ -1,7 +1,7 @@ .container .row.modal-centered - %h2 {{'welcome_to_ofn' | t}} - %h5 {{'signup_or_login' | t}}: + %h2 {{'js.registration.welcome_to_ofn' | t}} + %h5 {{'js.registration.signup_or_login' | t}}: %div{"ng-controller" => "AuthenticationCtrl"} %tabset %ng-include{src: "'signup.html'"} @@ -9,9 +9,9 @@ %ng-include{src: "'forgot.html'"} %div{ ng: { show: "active('/signup')"} } %hr - {{'have_an_account' | t}} + {{'js.registration.have_an_account' | t}} %a{ href: "", ng: { click: "select('/login')"}} - {{'action_login' | t}} + {{'js.registration.action_login' | t}} %a.close-reveal-modal{"ng-click" => "$close()"} %i.ofn-i_009-close diff --git a/app/views/registration/steps/_about.html.haml b/app/views/registration/steps/_about.html.haml index 529fdebea3..5ec5dbb2c7 100644 --- a/app/views/registration/steps/_about.html.haml +++ b/app/views/registration/steps/_about.html.haml @@ -4,9 +4,9 @@ .row .small-12.columns %header - %h2 {{'enterprise_about_headline' | t}} + %h2= t(".headline") %h5 - {{'enterprise_about_message' | t}} + = t(".message") %span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } } {{ enterprise.name }} @@ -14,33 +14,33 @@ .row .small-12.columns .alert-box.info{ "ofn-inline-alert" => true, ng: { show: "visible" } } - %h6{ "ng-bind" => "'enterprise_success' | t:{enterprise: enterprise.name}" } - %span {{'enterprise_registration_exit_message' | t}} + %h6{ "ng-bind" => "'registration.steps.about.success' | t:{enterprise: enterprise.name}" } + %span= t(".registration_exit_message") %a.close{ ng: { click: "close()" } } × .small-12.large-8.columns .row .small-12.columns .field - %label{ for: 'enterprise_description' } {{'enterprise_description' | t}}: - %input.chunky{ id: 'enterprise_description', placeholder: "{{'enterprise_description_placeholder' | t}}", ng: { model: 'enterprise.description' } } + %label{ for: 'enterprise_description' }= t(".enterprise_description") + %input.chunky{ id: 'enterprise_description', placeholder: "{{'registration.steps.about.enterprise_description_placeholder' | t}}", ng: { model: 'enterprise.description' } } .row .small-12.columns .field - %label{ for: 'enterprise_long_desc' } {{'enterprise_long_desc' | t}}: - %textarea.chunky{ id: 'enterprise_long_desc', rows: 6, placeholder: "{{'enterprise_long_desc_placeholder' | t}}", ng: { model: 'enterprise.long_description' } } - %small{ "ng-bind" => "'enterprise_long_desc_length' | t:{num: enterprise.long_description.length}" } + %label{ for: 'enterprise_long_desc' }= t(".enterprise_long_desc") + %textarea.chunky{ id: 'enterprise_long_desc', rows: 6, placeholder: "{{'registration.steps.about.enterprise_long_desc_placeholder' | t}}", ng: { model: 'enterprise.long_description' } } + %small{ "ng-bind" => "'registration.steps.about.enterprise_long_desc_length' | t:{num: enterprise.long_description.length}" } .small-12.large-4.columns .row .small-12.columns .field - %label{ for: 'enterprise_abn' } {{'enterprise_abn' | t}}: - %input.chunky{ id: 'enterprise_abn', placeholder: "{{'enterprise_abn_placeholder' | t}}", ng: { model: 'enterprise.abn' } } + %label{ for: 'enterprise_abn' }= t(".enterprise_abn")+":" + %input.chunky{ id: 'enterprise_abn', placeholder: "{{'registration.steps.about.enterprise_abn_placeholder' | t}}", ng: { model: 'enterprise.abn' } } .row .small-12.columns .field - %label{ for: 'enterprise_acn' } {{'enterprise_acn' | t}}: - %input.chunky{ id: 'enterprise_acn', placeholder: "{{'enterprise_acn_placeholder' | t}}", ng: { model: 'enterprise.acn' } } + %label{ for: 'enterprise_acn' }= t(".enterprise_acn")+":" + %input.chunky{ id: 'enterprise_acn', placeholder: "{{'registration.steps.about.enterprise_acn_placeholder' | t}}", ng: { model: 'enterprise.acn' } } .row .small-12.columns .field @@ -50,7 +50,7 @@ %input{ id: 'enterprise_charges_sales_tax_false', type: 'radio', name: 'charges_sales_tax', value: 'false', required: true, ng: { model: 'enterprise.charges_sales_tax' } } %label{ for: 'enterprise_charges_sales_tax_false' } {{'say_no' | t}} %span.error.small-12.columns{ ng: { show: "about.charges_sales_tax.$error.required && submitted" } } - {{'enterprise_tax_required' | t}} + = t(".enterprise_tax_required") .row.buttons.pad-top .small-12.columns diff --git a/app/views/registration/steps/_contact.html.haml b/app/views/registration/steps/_contact.html.haml index 746503282d..eed9c25c73 100644 --- a/app/views/registration/steps/_contact.html.haml +++ b/app/views/registration/steps/_contact.html.haml @@ -4,26 +4,26 @@ .row .small-12.columns %header - %h2 {{'registration_greeting' | t}} - %h5{ "ng-bind" => "'who_is_managing_enterprise' | t:{enterprise: enterprise.name}" } + %h2= t('registration.steps.introduction.registration_greeting') + %h5{ "ng-bind" => "'registration.steps.contact.who_is_managing_enterprise' | t:{enterprise: enterprise.name}" } %form{ name: 'contact', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('type',contact)" } } .row.content .small-12.medium-12.large-7.columns .row .small-12.columns.field - %label{ for: 'enterprise_contact' } {{'enterprise.registration.modal.steps.contact.contact_field' | t}}: - %input.chunky.small-12.columns{ id: 'enterprise_contact', name: 'contact_name', required: true, placeholder: "{{'enterprise.registration.modal.steps.contact.contact_field_placeholder' | t}}", ng: { model: 'enterprise.contact_name' } } + %label{ for: 'enterprise_contact' }= t(".contact_field") + %input.chunky.small-12.columns{ id: 'enterprise_contact', name: 'contact_name', required: true, placeholder: "{{'registration.steps.contact.contact_field_placeholder' | t}}", ng: { model: 'enterprise.contact_name' } } %span.error.small-12.columns{ ng: { show: "contact.contact_name.$error.required && submitted" } } - {{'enterprise.registration.modal.steps.contact.contact_field_required' | t}} + = t(".contact_field_required") .row .small-12.columns.field - %label{ for: 'enterprise_email_address' } {{'admin.enterprises.form.contact.email_address' | t}}: + %label{ for: 'enterprise_email_address' }= t("admin.enterprises.form.contact.email_address")+":" %input.chunky.small-12.columns{ id: 'enterprise_email_address', name: 'email_address', type: 'email', placeholder: t('admin.enterprises.form.contact.email_address_placeholder'), ng: { model: 'enterprise.email_address' } } .row .small-12.columns.field - %label{ for: 'enterprise_phone' } {{'enterprise.registration.modal.steps.contact.phone_field' | t}}: - %input.chunky.small-12.columns{ id: 'enterprise_phone', name: 'phone', placeholder: "{{'enterprise.registration.modal.steps.contact.phone_field_placeholder' | t}}", ng: { model: 'enterprise.phone' } } + %label{ for: 'enterprise_phone' }= t(".phone_field")+":" + %input.chunky.small-12.columns{ id: 'enterprise_phone', name: 'phone', placeholder: "{{'registration.steps.contact.phone_field_placeholder' | t}}", ng: { model: 'enterprise.phone' } } .small-12.medium-12.large-5.hide-for-small-only .row.buttons diff --git a/app/views/registration/steps/_details.html.haml b/app/views/registration/steps/_details.html.haml index 4fce6a4319..6639919830 100644 --- a/app/views/registration/steps/_details.html.haml +++ b/app/views/registration/steps/_details.html.haml @@ -4,60 +4,59 @@ .row .small-12.columns %header - %h2 {{'enterprise.registration.modal.steps.details.headline' | t}} - %h5{ ng: { if: "::enterprise.type != 'own'" } } {{'enterprise.registration.modal.steps.details.enterprise' | t}} - %h5{ ng: { if: "::enterprise.type == 'own'" } } {{'enterprise.registration.modal.steps.details.producer' | t}} + %h2= t('.headline') + %h5{ ng: { if: "::enterprise.type != 'own'" } }= t(".enterprise") + %h5{ ng: { if: "::enterprise.type == 'own'" } }= t(".producer") %form{ name: 'details', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('contact',details)" } } .row .small-12.medium-9.large-12.columns.end .field - %label{ for: 'enterprise_name', ng: { if: "::enterprise.type != 'own'" } } {{'enterprise.registration.modal.steps.details.enterprise_name_field' | t}} - %label{ for: 'enterprise_name', ng: { if: "::enterprise.type == 'own'" } } {{'enterprise.registration.modal.steps.details.producer_name_field' | t}} - %input.chunky{ id: 'enterprise_name', name: 'name', placeholder: "{{'enterprise.registration.modal.steps.details.producer_name_field_placeholder' | t}}", required: true, ng: { model: 'enterprise.name' } } + %label{ for: 'enterprise_name', ng: { if: "::enterprise.type != 'own'" } }= t(".enterprise_name_field") + %label{ for: 'enterprise_name', ng: { if: "::enterprise.type == 'own'" } }= t(".producer_name_field") + %input.chunky{ id: 'enterprise_name', name: 'name', placeholder: "{{'registration.steps.details.producer_name_field_placeholder' | t}}", required: true, ng: { model: 'enterprise.name' } } %span.error{ ng: { show: "details.name.$error.required && submitted" } } - {{'enterprise.registration.modal.steps.details.producer_name_field_error' | t}} + = t(".producer_name_field_error") .row .small-12.medium-9.large-6.columns .field - %label{ for: 'enterprise_address' } {{'enterprise.registration.modal.steps.details.address1_field' | t}} - %input.chunky{ id: 'enterprise_address', name: 'address1', required: true, placeholder: "{{'enterprise.registration.modal.steps.details.address1_field_placeholder' | t}}", required: true, ng: { model: 'enterprise.address.address1' } } + %label{ for: 'enterprise_address' }= t(".address1_field") + %input.chunky{ id: 'enterprise_address', name: 'address1', required: true, placeholder: "{{'registration.steps.details.address1_field_placeholder' | t}}", required: true, ng: { model: 'enterprise.address.address1' } } %span.error{ ng: { show: "details.address1.$error.required && submitted" } } - {{'enterprise.registration.modal.steps.details.address1_field_error' | t}} + = t(".address1_field_error") .field - %label{ for: 'enterprise_address2' } {{'enterprise.registration.modal.steps.details.address2_field' | t}} + %label{ for: 'enterprise_address2' }= t(".address2_field") %input.chunky{ id: 'enterprise_address2', name: 'address2', required: false, placeholder: "", required: false, ng: { model: 'enterprise.address.address2' } } .small-12.medium-9.large-6.columns.end .row .small-12.medium-8.large-8.columns .field - %label{ for: 'enterprise_city' } {{'enterprise.registration.modal.steps.details.suburb_field' | t}} - %input.chunky{ id: 'enterprise_city', name: 'city', required: true, placeholder: "{{'enterprise.registration.modal.steps.details.suburb_field_placeholder' | t}}", ng: { model: 'enterprise.address.city' } } + %label{ for: 'enterprise_city' }= t('.suburb_field') + %input.chunky{ id: 'enterprise_city', name: 'city', required: true, placeholder: "{{'registration.steps.details.suburb_field_placeholder' | t}}", ng: { model: 'enterprise.address.city' } } %span.error{ ng: { show: "details.city.$error.required && submitted" } } - {{'enterprise.registration.modal.steps.details.suburb_field_error' | t}} + = t(".suburb_field_error") .small-12.medium-4.large-4.columns .field - %label{ for: 'enterprise_zipcode' } {{'enterprise.registration.modal.steps.details.postcode_field' | t}} - %input.chunky{ id: 'enterprise_zipcode', name: 'zipcode', required: true, placeholder: "{{'enterprise.registration.modal.steps.details.postcode_field_placeholder' | t}}", ng: { model: 'enterprise.address.zipcode' } } + %label{ for: 'enterprise_zipcode' }= t(".postcode_field") + %input.chunky{ id: 'enterprise_zipcode', name: 'zipcode', required: true, placeholder: "{{'registration.steps.details.postcode_field_placeholder' | t}}", ng: { model: 'enterprise.address.zipcode' } } %span.error{ ng: { show: "details.zipcode.$error.required && submitted" } } - {{'enterprise.registration.modal.steps.details.postcode_field_error' | t}} + = t(".postcode_field_error") .row .small-12.medium-4.large-4.columns .field - %label{ for: 'enterprise_state' } {{'enterprise.registration.modal.steps.details.state_field' | t}} + %label{ for: 'enterprise_state' }= t(".state_field") %select.chunky{ id: 'enterprise_state', name: 'state', ng: { model: 'enterprise.address.state_id', options: 's.id as s.abbr for s in enterprise.country.states', show: 'countryHasStates()', required: 'countryHasStates()' } } %span.error{ ng: { show: "details.state.$error.required && submitted" } } - {{'enterprise.registration.modal.steps.details.state_field_error' | t}} + = t(".state_field_error") .small-12.medium-8.large-8.columns .field - %label{ for: 'enterprise_country' } {{'enterprise.registration.modal.steps.details.country_field' | t}} + %label{ for: 'enterprise_country' }= t(".country_field") %select.chunky{ id: 'enterprise_country', name: 'country', required: true, ng: { init: "setDefaultCountry(#{Spree::Config[:default_country_id]})", model: 'enterprise.country', options: 'c as c.name for c in countries' } } %span.error{ ng: { show: "details.country.$error.required && submitted" } } - {{'enterprise.registration.modal.steps.details.country_field_error' | t}} - + = t(".country_field_error") .row.buttons .small-12.columns diff --git a/app/views/registration/steps/_finished.html.haml b/app/views/registration/steps/_finished.html.haml index 96ed600147..d22384dbdb 100644 --- a/app/views/registration/steps/_finished.html.haml +++ b/app/views/registration/steps/_finished.html.haml @@ -3,10 +3,10 @@ .row .small-12.columns.pad-top %header - %h2 {{'registration_finished_headline' | t}} + %h2= t(".headline") .panel.callout - %p{ "ng-bind" => "'registration_finished_thanks' | t:{enterprise: enterprise.name}" } - %p {{'registration_finished_login' | t}} + %p{ "ng-bind" => "'registration.steps.finished.thanks' | t:{enterprise: enterprise.name}" } + %p= t(".login") .row .small-12.columns.text-center - %a.button.primary{ type: "button", href: "/" } {{'registration_finished_action' | t}} > + %a.button.primary{ type: "button", href: "/" }= "#{t(".action")} >" diff --git a/app/views/registration/steps/_introduction.html.haml b/app/views/registration/steps/_introduction.html.haml index f973ca055a..84bbb9840e 100644 --- a/app/views/registration/steps/_introduction.html.haml +++ b/app/views/registration/steps/_introduction.html.haml @@ -2,35 +2,35 @@ .row .small-12.columns %header - %h2 {{'registration_greeting' | t}} + %h2= t(".registration_greeting") %h4 %small %i.ofn-i_040-hub - {{'registration_intro' | t}} + = t(".registration_intro") .row{ 'data-equalizer' => true } .small-12.medium-12.large-6.columns.pad-top{ 'data-equalizer-watch' => true } - %h5 {{'registration_checklist' | t}}: + %h5= t(".registration_checklist")+":" %ul.check-list %li - {{'registration_time' | t}} + = t(".registration_time") %li - {{'registration_enterprise_address' | t}} + = t(".registration_enterprise_address") %li - {{'registration_contact_details' | t}} + = t(".registration_contact_details") %li - {{'registration_logo' | t}} + = t(".registration_logo") %li - {{'registration_promo_image' | t}} + = t(".registration_promo_image") %li - {{'registration_about_us' | t}} + = t(".registration_about_us") .small-9.medium-8.large-5.columns.pad-top.end{ 'data-equalizer-watch' => true} %h5 - {{'registration_outcome_headline' | t}} - %p{ "ng-bind-html" => "t('registration_outcome1_html')" } - %p {{'registration_outcome2' | t}} - %p {{'registration_outcome3' | t}} + = t(".registration_outcome_headline") + %p= t(".registration_outcome1_html") + %p= t(".registration_outcome2") + %p= t(".registration_outcome3") .row{'ng-init' => "tos_required=#{Spree::Config.enterprises_require_tos}" } %hr @@ -43,4 +43,4 @@ %label{for: "accept_terms"} #{t(:enterprise_tos_agree)} .small-12.medium-6.columns - %input.button.primary.left{ type: "button", value: "{{'registration_action' | t}}", ng: { click: "select('details')", disabled: "tos_required && !tos_accepted", model: "tos_accepted"} } + %input.button.primary.left{ type: "button", value: "{{'registration.steps.introduction.registration_action' | t}}", ng: { click: "select('details')", disabled: "tos_required && !tos_accepted", model: "tos_accepted"} } diff --git a/app/views/registration/steps/_limit_reached.html.haml b/app/views/registration/steps/_limit_reached.html.haml index 44f3b53f22..c8fa25a43c 100644 --- a/app/views/registration/steps/_limit_reached.html.haml +++ b/app/views/registration/steps/_limit_reached.html.haml @@ -2,16 +2,16 @@ .row .small-12.columns %header - %h2 {{'limit_reached_headline' | t}} - %h4 {{'limit_reached_message' | t}} + %h2= t(".headline") + %h4= t(".message") .row .small-12.medium-3.large-2.columns.text-right.hide-for-small-only %img{:src => "/assets/potatoes.png"} .small-12.medium-9.large-10.columns %p - {{'limit_reached_text' | t}} + = t(".text") %strong Open Food Network. .row .small-12.columns %hr - %input.button.primary{ type: "button", value: "{{'limit_reached_action' | t}}", ng: { click: "close()" } } + %input.button.primary{ type: "button", value: "{{'registration.steps.limit_reached.action' | t}}", ng: { click: "close()" } } diff --git a/app/views/registration/steps/_logo.html.haml b/app/views/registration/steps/_logo.html.haml index 3d1effdf93..677653b4bb 100644 --- a/app/views/registration/steps/_logo.html.haml +++ b/app/views/registration/steps/_logo.html.haml @@ -5,42 +5,42 @@ .row .small-12.columns.center %h4 - {{'select_logo' | t}} + = t(".select_logo") .row .small-12.columns.center %span.small - {{'logo_tip' | t}} + = t(".logo_tip") .row.pad-top .small-12.columns .image-select.small-12.columns - %label.small-12.columns.button{ for: 'image-select' } {{'logo_label' | t}} + %label.small-12.columns.button{ for: 'image-select' }= t(".logo_label") %input#image-select{ type: 'file', hidden: true, 'nv-file-select' => true, uploader: "imageUploader", options: '{ alias: imageStep }' } .row.show-for-large-up .large-12.columns %span#or.large-12.columns - {{'action_or' | t}} + = t("action_or") .row.show-for-large-up .large-12.columns #image-over{ 'nv-file-over' => true, uploader: "imageUploader" } - {{'logo_drag' | t}} + = t(".logo_drag") .small-12.medium-12.large-6.columns .row .small-12.columns.center .row .small-12.columns.center %h4 - {{'review_logo' | t}} + = t(".review_logo") .row .small-12.columns.center %span.small - {{'review_logo_tip' | t}} + = t(".review_logo_tip") .row.pad-top .small-12.columns.center #image-placeholder.logo %img{ ng: { show: "imageSrc() && !imageUploader.isUploading", src: '{{ imageSrc() }}' } } .message{ ng: { hide: "imageSrc() || imageUploader.isUploading" } } - {{'logo_placeholder' | t}} + = t(".logo_placeholder") .loading{ ng: { hide: "!imageUploader.isUploading" } } %img.spinner{ src: "/assets/spinning-circles.svg" } %br/ - {{'uploading' | t}} + = t("registration.steps.images.uploading") diff --git a/app/views/registration/steps/_promo.html.haml b/app/views/registration/steps/_promo.html.haml index 0b224e1f64..91a8bc9a78 100644 --- a/app/views/registration/steps/_promo.html.haml +++ b/app/views/registration/steps/_promo.html.haml @@ -3,42 +3,42 @@ .row .small-12.columns.center %h4 - {{'select_promo_image' | t}} + = t(".select_promo_image") .row .small-12.medium-12.large-5.columns.center .row .small-12.columns.center %span.small - {{'promo_image_tip' | t}} + = t(".promo_image_tip") .row.pad-top .small-12.columns .image-select.small-12.columns - %label.small-12.columns.button{ for: 'image-select' } {{'promo_image_label' | t}} + %label.small-12.columns.button{ for: 'image-select' }= t(".promo_image_label") %input#image-select{ type: 'file', hidden: true, 'nv-file-select' => true, uploader: "imageUploader", options: '{ alias: imageStep }' } .large-2.columns %span#or.horizontal.large-12.columns - {{'action_or' | t}} + = t("action_or") .large-5.columns #image-over{ 'nv-file-over' => true, uploader: "imageUploader" } - {{'promo_image_drag' | t}} + = t(".promo_image_drag") .small-12.medium-12.large-12.columns.pad-top .row .small-12.columns.center %h4 - {{'review_promo_image' | t}} + = t(".review_promo_image") .row .small-12.columns.center .row .small-12.columns.center %span.small - {{'review_promo_image_tip' | t}} + = t(".review_promo_image_tip") .row.pad-top .small-12.columns.center #image-placeholder.promo %img{ ng: { show: "imageSrc() && !imageUploader.isUploading", src: '{{ imageSrc() }}' } } .message{ ng: { hide: "imageSrc() || imageUploader.isUploading" } } - {{'promo_image_placeholder' | t}} + = t(".promo_image_placeholder") .loading{ ng: { hide: "!imageUploader.isUploading" } } %img.spinner{ src: "/assets/spinning-circles.svg" } %br/ - {{'uploading' | t}} + = t("registration.steps.images.uploading") diff --git a/app/views/registration/steps/_social.html.haml b/app/views/registration/steps/_social.html.haml index edbdd94dd9..8552b927b7 100644 --- a/app/views/registration/steps/_social.html.haml +++ b/app/views/registration/steps/_social.html.haml @@ -5,8 +5,8 @@ .row .small-12.columns %header - %h2 {{'enterprise_final_step' | t}} - %h5{ "ng-bind" => "'enterprise_social_text' | t:{enterprise: enterprise.name}" } + %h2= t(".enterprise_final_step") + %h5{ "ng-bind" => "'registration.steps.social.enterprise_social_text' | t:{enterprise: enterprise.name}" } %form{ name: 'social', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "update('finished',social)" } } .row.content @@ -14,29 +14,29 @@ .row .small-12.columns .field - %label{ for: 'enterprise_website' } {{'website' | t}}: - %input.chunky{ id: 'enterprise_website', placeholder: "{{'website_placeholder' | t}}", ng: { model: 'enterprise.website' } } + %label{ for: 'enterprise_website' }= t(".website")+":" + %input.chunky{ id: 'enterprise_website', placeholder: "{{'registration.steps.social.website_placeholder' | t}}", ng: { model: 'enterprise.website' } } .row .small-12.columns .field - %label{ for: 'enterprise_facebook' } {{'facebook' | t}}: - %input.chunky{ id: 'enterprise_facebook', placeholder: "{{'facebook_placeholder' | t}}", ng: { model: 'enterprise.facebook' } } + %label{ for: 'enterprise_facebook' }= t(".facebook")+":" + %input.chunky{ id: 'enterprise_facebook', placeholder: "{{'registration.steps.social.facebook_placeholder' | t}}", ng: { model: 'enterprise.facebook' } } .row .small-12.columns .field - %label{ for: 'enterprise_linkedin' } {{'linkedin' | t}}: - %input.chunky{ id: 'enterprise_linkedin', placeholder: "{{'linkedin_placeholder' | t}}", ng: { model: 'enterprise.linkedin' } } + %label{ for: 'enterprise_linkedin' }= t(".linkedin")+":" + %input.chunky{ id: 'enterprise_linkedin', placeholder: "{{'registration.steps.social.linkedin_placeholder' | t}}", ng: { model: 'enterprise.linkedin' } } .small-12.large-5.columns .row .small-12.columns .field - %label{ for: 'enterprise_twitter' } {{'twitter' | t}}: - %input.chunky{ id: 'enterprise_twitter', placeholder: "{{'twitter_placeholder' | t}}", ng: { model: 'enterprise.twitter' } } + %label{ for: 'enterprise_twitter' }= t(".twitter")+":" + %input.chunky{ id: 'enterprise_twitter', placeholder: "{{'registration.steps.social.twitter_placeholder' | t}}", ng: { model: 'enterprise.twitter' } } .row .small-12.columns .field - %label{ for: 'enterprise_instagram' } {{'instagram' | t}}: - %input.chunky{ id: 'enterprise_instagram', placeholder: "{{'instagram_placeholder' | t}}", ng: { model: 'enterprise.instagram' } } + %label{ for: 'enterprise_instagram' }= t(".instagram")+":" + %input.chunky{ id: 'enterprise_instagram', placeholder: "{{'registration.steps.social.instagram_placeholder' | t}}", ng: { model: 'enterprise.instagram' } } .row.buttons .small-12.columns diff --git a/app/views/registration/steps/_steps.html.haml b/app/views/registration/steps/_steps.html.haml index 3538d5ff48..081cd97d98 100644 --- a/app/views/registration/steps/_steps.html.haml +++ b/app/views/registration/steps/_steps.html.haml @@ -1,5 +1,5 @@ %script{ type: "text/ng-template", id: "registration/steps.html" } .row#progress-bar .small-12.medium-2.columns.item{ ng: { repeat: 'step in steps', class: "{active: (currentStep() == step),'show-for-medium-up': (currentStep() != step)}" } } - {{ $index+1 + ". " + ('enterprise.registration.modal.steps.' + step + '.title' | t) }} + {{ $index+1 + ". " + ('registration.steps.' + step + '.title' | t) }} diff --git a/config/locales/en.yml b/config/locales/en.yml index fc1595d709..486e5cb6e3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1867,24 +1867,60 @@ See the %{link} to find out more about %{sitename}'s features and to start using choose_password: "Choose a password" confirm_password: "Confirm password" action_signup: "Sign up now" - welcome_to_ofn: "Welcome to the Open Food Network!" - signup_or_login: "Start By Signing Up (or logging in)" - have_an_account: "Already have an account?" - action_login: "Log in now." forgot_password: "Forgot Password?" password_reset_sent: "An email with instructions on resetting your password has been sent!" reset_password: "Reset password" - registration_greeting: "Greetings!" - who_is_managing_enterprise: "Who is responsible for managing %{enterprise}?" update_and_recalculate_fees: "Update And Recalculate Fees" registration: steps: - images: - continue: "Continue" - back: "Back" - headline: "Thanks!" - description: "Let's upload some pretty pictures so your profile looks great! :)" + introduction: + registration_greeting: "Hi there!" + registration_intro: "You can now create a profile for your Producer or Hub" + registration_checklist: "You'll need" + registration_time: "5-10 minutes" + registration_enterprise_address: "Enterprise address" + registration_contact_details: "Primary contact details" + registration_logo: "Your logo image" + registration_promo_image: "Landscape image for your profile" + registration_about_us: "'About Us' text" + registration_outcome_headline: "What do I get?" + registration_outcome1_html: "Your profile helps people find and contact you on the Open Food Network." + registration_outcome2: "Use this space to tell the story of your enterprise, to help drive connections to your social and online presence." + registration_outcome3: "It's also the first step towards trading on the Open Food Network, or opening an online store." + registration_action: "Let's get started!" + details: + title: "Details" + headline: "Let's Get Started" + enterprise: "Woot! First need to know a little bit about your enterprise:" + producer: "Woot! First we need to know a little bit about your farm:" + enterprise_name_field: "Enterprise Name:" + producer_name_field: "Farm Name:" + producer_name_field_placeholder: "e.g. Charlie's Awesome Farm" + producer_name_field_error: "Please choose a unique name for your enterprise" + address1_field: "Address line 1:" + address1_field_placeholder: "e.g. 123 Cranberry Drive" + address1_field_error: "Please enter an address" + address2_field: "Address line 2:" + suburb_field: "Suburb:" + suburb_field_placeholder: "e.g. Northcote" + suburb_field_error: "Please enter a suburb" + postcode_field: "Postcode:" + postcode_field_placeholder: "e.g. 3070" + postcode_field_error: "Postcode required" + state_field: "State:" + state_field_error: "State required" + country_field: "Country:" + country_field_error: "Please select a country" + contact: + title: "Contact" + who_is_managing_enterprise: "Who is responsible for managing %{enterprise}?" + contact_field: "Primary Contact" + contact_field_placeholder: "Contact Name" + contact_field_required: "You need to enter a primary contact." + phone_field: "Phone number" + phone_field_placeholder: "eg. (03) 1234 5678" type: + title: "Type" headline: "Last step to add %{enterprise}!" question: "Are you a producer?" yes_producer: "Yes, I'm a producer" @@ -1893,161 +1929,75 @@ See the %{link} to find out more about %{sitename}'s features and to start using yes_producer_help: "Producers make yummy things to eat and/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it." no_producer_help: "If you’re not a producer, you’re probably someone who sells and distributes food. You might be a hub, coop, buying group, retailer, wholesaler or other." create_profile: "Create Profile" - enterprise: - registration: - modal: - steps: - details: - title: 'Details' - headline: "Let's Get Started" - enterprise: "Woot! First we need to know a little bit about your enterprise:" - producer: "Woot! First we need to know a little bit about your farm:" - enterprise_name_field: "Enterprise Name:" - producer_name_field: "Farm Name:" - producer_name_field_placeholder: "e.g. Charlie's Awesome Farm" - producer_name_field_error: "Please choose a unique name for your enterprise" - address1_field: "Address line 1:" - address1_field_placeholder: "e.g. 123 Cranberry Drive" - address1_field_error: "Please enter an address" - address2_field: "Address line 2:" - suburb_field: "Suburb:" - suburb_field_placeholder: "e.g. Northcote" - suburb_field_error: "Please enter a suburb" - postcode_field: "Postcode:" - postcode_field_placeholder: "e.g. 3070" - postcode_field_error: "Postcode required" - state_field: "State:" - state_field_error: "State required" - country_field: "Country:" - country_field_error: "Please select a country" - contact: - title: 'Contact' - contact_field: 'Primary Contact' - contact_field_placeholder: 'Contact Name' - contact_field_required: "You need to enter a primary contact." - email_field: 'Email address' - email_field_placeholder: 'eg. charlie@thefarm.com' - phone_field: 'Phone number' - phone_field_placeholder: 'eg. (03) 1234 5678' - type: - title: 'Type' - about: - title: 'About' - images: - title: 'Images' - social: - title: 'Social' - # TODO: Remove these once the enterprise.registration.modal keys are translated - enterprise_contact: "Primary Contact" - enterprise_contact_placeholder: "Contact Name" - enterprise_contact_required: "You need to enter a primary contact." - enterprise_email_address: "Email address" - enterprise_email_placeholder: "eg. charlie@thefarm.com" - enterprise_phone: "Phone number" - enterprise_phone_placeholder: "eg. (03) 1234 5678" - # END + about: + title: "About" + headline: "Nice one!" + message: "Now let's flesh out the details about" + success: "Success! %{enterprise} added to the Open Food Network" + registration_exit_message: "If you exit this wizard at any stage, you can continue to create your profile by going to the admin interface." + enterprise_description: "Short Description" + enterprise_description_placeholder: "A short sentence describing your enterprise" + enterprise_long_desc: "Long Description" + enterprise_long_desc_placeholder: "This is your opportunity to tell the story of your enterprise - what makes you different and wonderful? We'd suggest keeping your description to under 600 characters or 150 words." + enterprise_long_desc_length: "%{num} characters / up to 600 recommended" + enterprise_abn: "ABN" + enterprise_abn_placeholder: "eg. 99 123 456 789" + enterprise_acn: "ACN" + enterprise_acn_placeholder: "eg. 123 456 789" + enterprise_tax_required: "You need to make a selection." + images: + title: "Images" + headline: "Thanks!" + description: "Let's upload some pretty pictures so your profile looks great! :)" + uploading: "Uploading..." + continue: "Continue" + back: "Back" + logo: + select_logo: "Step 1. Select Logo Image" + logo_tip: "Tip: Square images will work best, preferably at least 300×300px" + logo_label: "Choose a logo image" + logo_drag: "Drag and drop your logo here" + review_logo: "Step 2. Review Your Logo" + review_logo_tip: "Tip: for best results, your logo should fill the available space" + logo_placeholder: "Your logo will appear here for review once uploaded" + promo: + select_promo_image: "Step 3. Select Promo Image" + promo_image_tip: "Tip: Shown as a banner, preferred size is 1200×260px" + promo_image_label: "Choose a promo image" + promo_image_drag: "Drag and drop your promo here" + review_promo_image: "Step 4. Review Your Promo Banner" + review_promo_image_tip: "Tip: for best results, your promo image should fill the available space" + promo_image_placeholder: "Your logo will appear here for review once uploaded" + social: + title: "Social" + enterprise_final_step: "Final step!" + enterprise_social_text: "How can people find %{enterprise} online?" + website: "Website" + website_placeholder: "eg. openfoodnetwork.org.au" + facebook: "Facebook" + facebook_placeholder: "eg. www.facebook.com/PageNameHere" + linkedin: "LinkedIn" + linkedin_placeholder: "eg. www.linkedin.com/YourNameHere" + twitter: "Twitter" + twitter_placeholder: "eg. @twitter_handle" + instagram: "Instagram" + instagram_placeholder: "eg. @instagram_handle" + limit_reached: + headline: "Oh no!" + message: "You have reached the limit!" + text: "You have reached the limit for the number of enterprises you are allowed to own on the" + action: "Return to the homepage" + finished: + headline: "Finished!" + thanks: "Thanks for filling out the details for %{enterprise}." + login: "You can change or update your enterprise at any stage by logging into Open Food Network and going to Admin." + action: "Open Food Network home" + back: "Back" continue: "Continue" - limit_reached_headline: "Oh no!" - limit_reached_message: "You have reached the limit!" - limit_reached_text: "You have reached the limit for the number of enterprises you are allowed to own on the" - limit_reached_action: "Return to the homepage" - select_promo_image: "Step 3. Select Promo Image" - promo_image_tip: "Tip: Shown as a banner, preferred size is 1200×260px" - promo_image_label: "Choose a promo image" action_or: "OR" - promo_image_drag: "Drag and drop your promo here" - review_promo_image: "Step 4. Review Your Promo Banner" - review_promo_image_tip: "Tip: for best results, your promo image should fill the available space" - promo_image_placeholder: "Your logo will appear here for review once uploaded" - uploading: "Uploading..." - select_logo: "Step 1. Select Logo Image" - logo_tip: "Tip: Square images will work best, preferably at least 300×300px" - logo_label: "Choose a logo image" - logo_drag: "Drag and drop your logo here" - review_logo: "Step 2. Review Your Logo" - review_logo_tip: "Tip: for best results, your logo should fill the available space" - logo_placeholder: "Your logo will appear here for review once uploaded" - enterprise_about_headline: "Nice one!" - enterprise_about_message: "Now let's flesh out the details about" - enterprise_success: "Success! %{enterprise} added to the Open Food Network " - enterprise_registration_exit_message: "If you exit this wizard at any stage, you can continue to create your profile by going to the admin interface." - enterprise_description: "Short Description" - enterprise_description_placeholder: "A short sentence describing your enterprise" - enterprise_long_desc: "Long Description" - enterprise_long_desc_placeholder: "This is your opportunity to tell the story of your enterprise - what makes you different and wonderful? We'd suggest keeping your description to under 600 characters or 150 words." - enterprise_long_desc_length: "%{num} characters / up to 600 recommended" - enterprise_abn: "ABN" - enterprise_abn_placeholder: "eg. 99 123 456 789" - enterprise_acn: "ACN" - enterprise_acn_placeholder: "eg. 123 456 789" - enterprise_tax_required: "You need to make a selection." - enterprise_final_step: "Final step!" - enterprise_social_text: "How can people find %{enterprise} online?" - website: "Website" - website_placeholder: "eg. openfoodnetwork.org.au" - facebook: "Facebook" - facebook_placeholder: "eg. www.facebook.com/PageNameHere" - linkedin: "LinkedIn" - linkedin_placeholder: "eg. www.linkedin.com/YourNameHere" - twitter: "Twitter" - twitter_placeholder: "eg. @twitter_handle" - instagram: "Instagram" - instagram_placeholder: "eg. @instagram_handle" - registration_greeting: "Hi there!" - registration_intro: "You can now create a profile for your Producer or Hub" - registration_action: "Let's get started!" - registration_checklist: "You'll need" - registration_time: "5-10 minutes" - registration_enterprise_address: "Enterprise address" - registration_contact_details: "Primary contact details" - registration_logo: "Your logo image" - registration_promo_image: "Landscape image for your profile" - registration_about_us: "'About Us' text" - registration_outcome_headline: "What do I get?" - registration_outcome1_html: "Your profile helps people find and contact you on the Open Food Network." - registration_outcome2: "Use this space to tell the story of your enterprise, to help drive connections to your social and online presence. " - registration_outcome3: "It's also the first step towards trading on the Open Food Network, or opening an online store." - registration_finished_headline: "Finished!" - registration_finished_thanks: "Thanks for filling out the details for %{enterprise}." - registration_finished_login: "You can change or update your enterprise at any stage by logging into Open Food Network and going to Admin." - registration_finished_action: "Open Food Network home" - registration_contact_name: 'Contact Name' + enterprise_limit: Enterprise Limit - # TODO: Remove these once the enterprise.registration.modal keys are translated - registration_type_headline: "Last step to add %{enterprise}!" - registration_type_question: "Are you a producer?" - registration_type_producer: "Yes, I'm a producer" - registration_type_no_producer: "No, I'm not a producer" - registration_type_error: "Please choose one. Are you are producer?" - registration_type_producer_help: "Producers make yummy things to eat and/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it." - registration_type_no_producer_help: "If you’re not a producer, you’re probably someone who sells and distributes food. You might be a hub, coop, buying group, retailer, wholesaler or other." - # END - - - # TODO: Remove these once the enterprise.registration.modal keys are translated - registration_detail_headline: "Let's Get Started" - registration_detail_enterprise: "Woot! First we need to know a little bit about your enterprise:" - registration_detail_producer: "Woot! First we need to know a little bit about your farm:" - registration_detail_name_enterprise: "Enterprise Name:" - registration_detail_name_producer: "Farm Name:" - registration_detail_name_placeholder: "e.g. Charlie's Awesome Farm" - registration_detail_name_error: "Please choose a unique name for your enterprise" - registration_detail_address1: "Address line 1:" - registration_detail_address1_placeholder: "e.g. 123 Cranberry Drive" - registration_detail_address1_error: "Please enter an address" - registration_detail_address2: "Address line 2:" - registration_detail_suburb: "Suburb:" - registration_detail_suburb_placeholder: "e.g. Northcote" - registration_detail_suburb_error: "Please enter a suburb" - registration_detail_postcode: "Postcode:" - registration_detail_postcode_placeholder: "e.g. 3070" - registration_detail_postcode_error: "Postcode required" - registration_detail_state: "State:" - registration_detail_state_error: "State required" - registration_detail_country: "Country:" - registration_detail_country_error: "Please select a country" - # END shipping_method_destroy_error: "That shipping method cannot be deleted as it is referenced by an order: %{number}." accounts_and_billing_task_already_running_error: "A task is already running, please wait until it has finished" accounts_and_billing_start_task_notice: "Task Queued" @@ -2687,6 +2637,12 @@ See the %{link} to find out more about %{sitename}'s features and to start using closes: closes closed: closed close_date_not_set: Close date not set + registration: + welcome_to_ofn: "Welcome to the Open Food Network!" + signup_or_login: "Start By Signing Up (or logging in)" + have_an_account: "Already have an account?" + action_login: "Log in now." + producers: signup: start_free_profile: "Start with a free profile, and expand when you're ready!" diff --git a/spec/features/consumer/registration_spec.rb b/spec/features/consumer/registration_spec.rb index 0a7756d104..efbb9deca0 100644 --- a/spec/features/consumer/registration_spec.rb +++ b/spec/features/consumer/registration_spec.rb @@ -139,7 +139,7 @@ feature "Registration", js: true do fill_in "Email", with: user.email fill_in "Password", with: user.password click_button 'Login' - expect(page).to have_content I18n.t('limit_reached_headline') + expect(page).to have_content I18n.t('registration.steps.limit_reached.headline') end end end From c4b646c5839bef241477bc153b154d06b18064c7 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 22 Feb 2019 23:01:01 +0000 Subject: [PATCH 25/57] Wait for angular to load in BOM spec --- spec/features/admin/bulk_order_management_spec.rb | 2 ++ spec/support/request/web_helper.rb | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index 26dbc2aab0..7e11b329c2 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -45,6 +45,7 @@ feature %q{ before :each do visit '/admin/orders/bulk_management' + wait_for_angular_requests end it "displays a column for user's full name" do @@ -456,6 +457,7 @@ feature %q{ before :each do visit '/admin/orders/bulk_management' + wait_for_angular_requests end it "displays date fields for filtering orders, with default values set" do diff --git a/spec/support/request/web_helper.rb b/spec/support/request/web_helper.rb index 912c19e445..19f503d837 100644 --- a/spec/support/request/web_helper.rb +++ b/spec/support/request/web_helper.rb @@ -152,8 +152,16 @@ module WebHelper page.driver.browser.switch_to.alert.accept end + def wait_for_angular_requests + wait_until { angular_requests_finished } + end + private + def angular_requests_finished + page.evaluate_script('angular.element(".ng-scope").injector().get("$http").pendingRequests.length === 0') + end + def wait_for_ajax wait_until { page.evaluate_script("$.active") == 0 } end From ea8be0ab950e46bef0b3877313ae54dedf0cb12d Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 23 Feb 2019 16:13:23 +0000 Subject: [PATCH 26/57] Fine-tune angular helper --- spec/features/admin/bulk_order_management_spec.rb | 13 +++++++++++-- spec/support/request/web_helper.rb | 14 ++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index 7e11b329c2..aaa2429d5a 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -45,7 +45,7 @@ feature %q{ before :each do visit '/admin/orders/bulk_management' - wait_for_angular_requests + wait_until { request_monitor_finished 'LineItemsCtrl' } end it "displays a column for user's full name" do @@ -93,6 +93,7 @@ feature %q{ before do visit spree.admin_bulk_order_management_path + wait_until { request_monitor_finished 'LineItemsCtrl' } end it "sorts by customer name when the customer name header is clicked" do @@ -261,6 +262,7 @@ feature %q{ before :each do visit '/admin/orders/bulk_management' + wait_until { request_monitor_finished 'LineItemsCtrl' } end it "displays a select box for producers, which filters line items by the selected supplier" do @@ -299,6 +301,7 @@ feature %q{ before :each do visit '/admin/orders/bulk_management' + wait_until { request_monitor_finished 'LineItemsCtrl' } end it "displays a select box for distributors, which filters line items by the selected distributor" do @@ -338,6 +341,7 @@ feature %q{ before do visit '/admin/orders/bulk_management' + wait_until { request_monitor_finished 'LineItemsCtrl' } end it "displays a select box for order cycles, which filters line items by the selected order cycle" do @@ -378,6 +382,7 @@ feature %q{ before :each do visit '/admin/orders/bulk_management' + wait_until { request_monitor_finished 'LineItemsCtrl' } end it "allows filters to be used in combination" do @@ -428,6 +433,7 @@ feature %q{ before :each do visit '/admin/orders/bulk_management' + wait_until { request_monitor_finished 'LineItemsCtrl' } end it "displays a quick search input" do @@ -457,7 +463,7 @@ feature %q{ before :each do visit '/admin/orders/bulk_management' - wait_for_angular_requests + wait_until { request_monitor_finished 'LineItemsCtrl' } end it "displays date fields for filtering orders, with default values set" do @@ -530,6 +536,7 @@ feature %q{ before :each do visit '/admin/orders/bulk_management' + wait_until { request_monitor_finished 'LineItemsCtrl' } end it "displays a checkbox for each line item in the list" do @@ -572,6 +579,7 @@ feature %q{ expect(page).to have_no_selector "tr#li_#{li2.id}" check "toggle_bulk" fill_in "quick_search", with: '' + wait_until { request_monitor_finished 'LineItemsCtrl' } expect(find("tr#li_#{li1.id} input[type='checkbox'][name='bulk']").checked?).to be true expect(find("tr#li_#{li2.id} input[type='checkbox'][name='bulk']").checked?).to be false expect(find("input[type='checkbox'][name='toggle_bulk']").checked?).to be false @@ -585,6 +593,7 @@ feature %q{ find("div#bulk-actions-dropdown div.menu_item", :text => "Delete Selected" ).click expect(page).to have_no_selector "tr#li_#{li1.id}" fill_in "quick_search", with: '' + wait_until { request_monitor_finished 'LineItemsCtrl' } expect(page).to have_selector "tr#li_#{li2.id}" expect(page).to have_no_selector "tr#li_#{li1.id}" end diff --git a/spec/support/request/web_helper.rb b/spec/support/request/web_helper.rb index 19f503d837..4cf788ae8e 100644 --- a/spec/support/request/web_helper.rb +++ b/spec/support/request/web_helper.rb @@ -152,16 +152,18 @@ module WebHelper page.driver.browser.switch_to.alert.accept end - def wait_for_angular_requests - wait_until { angular_requests_finished } + def angular_http_requests_finished(angular_controller=nil) + scope_element = angular_controller ? "[ng-controller=#{angular_controller}]" : '.ng-scope' + page.evaluate_script("angular.element(document.querySelector('#{scope_element}')).injector().get('$http').pendingRequests.length == 0") + end + + def request_monitor_finished(angular_controller=nil) + scope_element = angular_controller ? "[ng-controller=#{angular_controller}]" : '.ng-scope' + page.evaluate_script("angular.element(document.querySelector('#{scope_element}')).scope().RequestMonitor.loading == false") end private - def angular_requests_finished - page.evaluate_script('angular.element(".ng-scope").injector().get("$http").pendingRequests.length === 0') - end - def wait_for_ajax wait_until { page.evaluate_script("$.active") == 0 } end From 627b101be9c20986d1aa6e9a6e205908818b7917 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sun, 24 Feb 2019 12:06:58 +0000 Subject: [PATCH 27/57] Don't process proxy_order if the order has been cancelled --- app/jobs/subscription_confirm_job.rb | 2 +- spec/jobs/subscription_confirm_job_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/jobs/subscription_confirm_job.rb b/app/jobs/subscription_confirm_job.rb index f0c8e8d07f..898eb4382c 100644 --- a/app/jobs/subscription_confirm_job.rb +++ b/app/jobs/subscription_confirm_job.rb @@ -25,7 +25,7 @@ class SubscriptionConfirmJob def proxy_orders ProxyOrder.not_canceled.where('confirmed_at IS NULL AND placed_at IS NOT NULL') .joins(:order_cycle).merge(recently_closed_order_cycles) - .joins(:order).merge(Spree::Order.complete) + .joins(:order).merge(Spree::Order.complete.not_state('canceled')) end def recently_closed_order_cycles diff --git a/spec/jobs/subscription_confirm_job_spec.rb b/spec/jobs/subscription_confirm_job_spec.rb index 3c6383f196..0d86ab0a18 100644 --- a/spec/jobs/subscription_confirm_job_spec.rb +++ b/spec/jobs/subscription_confirm_job_spec.rb @@ -58,6 +58,12 @@ describe SubscriptionConfirmJob do proxy_order.update_attributes!(confirmed_at: 1.second.ago) expect(proxy_orders).to_not include proxy_order end + + it "ignores orders that have been cancelled" do + setup_email + proxy_order.order.cancel! + expect(proxy_orders).to_not include proxy_order + end end describe "performing the job" do From f40ff61f97b7619defe6c91e623f4aad4fdaa834 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Fri, 22 Feb 2019 22:41:07 +0000 Subject: [PATCH 28/57] Move all en.yml entries added so far to v2 to master so that we can start the translation process now --- config/locales/en.yml | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index fc1595d709..e857ae4f03 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -272,6 +272,8 @@ en: pick_up: Pick up ok: Ok copy: Copy + change_my_password: "Change my password" + update_password: "Update password" password_confirmation: Password Confirmation reset_password_token: Reset password token expired: has expired, please request a new one @@ -279,6 +281,7 @@ en: actions: create_and_add_another: "Create and Add Another" + create: "Create" admin: # Common properties / models begins_at: Begins At @@ -1316,7 +1319,7 @@ en: no_shipping_or_payment: no shipping or payment methods unconfirmed: unconfirmed days: days - + authorization_failure: "Authorization Failure" label_shop: "Shop" label_shops: "Shops" @@ -1738,6 +1741,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using products_available: Available? products_producer: "Producer" products_price: "Price" + name_or_sku: "NAME OR SKU" register_title: Register @@ -1799,6 +1803,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using orders_bought_edit_button: Edit confirmed items orders_bought_already_confirmed: "* already confirmed" orders_confirm_cancel: Are you sure you want to cancel this order? + order_processed_successfully: "Your order has been processed successfully" products_cart_distributor_choice: "Distributor for your order:" products_cart_distributor_change: "Your distributor for this order will be changed to %{name} if you add this product to your cart." @@ -2083,6 +2088,10 @@ See the %{link} to find out more about %{sitename}'s features and to start using ok: OK not_visible: not visible you_have_no_orders_yet: "You have no orders yet" + show_only_complete_orders: "Only show complete orders" + successfully_created: ! '%{resource} has been successfully created!' + successfully_removed: ! '%{resource} has been successfully removed!' + successfully_updated: ! '%{resource} has been successfully updated!' running_balance: "Running balance" outstanding_balance: "Outstanding balance" admin_enterprise_relationships: "Enterprise Permissions" @@ -2422,6 +2431,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using shipping_methods: "Shipping Methods" payment_methods: "Payment Methods" payment_method_fee: "Transaction fee" + payment_processing_failed: "Payment could not be processed, please check the details you entered" + payment_updated: "Payment Updated" inventory_settings: "Inventory Settings" tag_rules: "Tag Rules" shop_preferences: "Shop Preferences" @@ -2623,6 +2634,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using sending: "Resend..." done: "Resend done ✓" failed: "Resend failed ✗" + insufficient_stock: "Insufficient stock available, only %{on_hand} remaining" out_of_stock: reduced_stock_available: Reduced stock available out_of_stock_text: > @@ -2687,6 +2699,9 @@ See the %{link} to find out more about %{sitename}'s features and to start using closes: closes closed: closed close_date_not_set: Close date not set + spree: + users: + order: "Order" producers: signup: start_free_profile: "Start with a free profile, and expand when you're ready!" @@ -2739,11 +2754,34 @@ See the %{link} to find out more about %{sitename}'s features and to start using my_account: "My account" date: "Date" time: "Time" + inventory_error_flash_for_insufficient_quantity: "An item in your cart has become unavailable." + errors: + messages: + blank: "can't be blank" layouts: admin: header: store: Store admin: + tab: + dashboard: "Dashboard" + orders: "Orders" + bulk_order_management: "Bulk Order Management" + subscriptions: "Subscriptions" + products: "Products" + option_types: "Option Types" + properties: "Properties" + prototypes: "Prototypes" + variant_overrides: "Inventory" + reports: "Reports" + configuration: "Configuration" + users: "Users" + roles: "Roles" + order_cycles: "Order Cycles" + enterprises: "Enterprises" + enterprise_relationships: "Permissions" + customers: "Customers" + groups: "Groups" product_properties: index: inherits_properties_checkbox_hint: "Inherit properties from %{supplier}? (unless overridden above)" @@ -2912,6 +2950,9 @@ See the %{link} to find out more about %{sitename}'s features and to start using login_to_view_order: "Please log in to view your order." bought: item: "Already ordered in this order cycle" + line_item: + insufficient_stock: "Insufficient stock available, only %{on_hand} remaining" + out_of_stock: "Out of Stock" shipment_states: backorder: backorder partial: partial From 963fddf1593ec52cee5750910838dd748206c5db Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Sun, 24 Feb 2019 22:12:16 +0000 Subject: [PATCH 29/57] Add missing translations These translations are missing in v2 but some will also make v1 better --- config/locales/en.yml | 212 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 208 insertions(+), 4 deletions(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index e857ae4f03..734728e14e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2746,7 +2746,209 @@ See the %{link} to find out more about %{sitename}'s features and to start using total_amount: "$$ SUM" invalid_filter_parameters: "The filters you selected for this report are invalid." + order: "Order" + order_state: + address: "address" + adjustments: "adjustments" + awaiting_return: "awaiting return" + canceled: "cancelled" + cart: "cart" + complete: "complete" + confirm: "confirm" + delivery: "delivery" + paused: "paused" + payment: "payment" + pending: "pending" + resumed: "resumed" + returned: "returned" + skrill: "skrill" + distribution: "Distribution" + order_details: "Order Details" + customer_details: "Customer Details" + adjustments: "Adjustments" + payments: "Payments" + + payment: "Payment" + payment_method: "Payment Method" + shipment: "Shipment" + shipment_inc_vat: "Shipment including VAT" + shipping_tax_rate: "Shipping Tax Rate" + category: "Category" + delivery: "Delivery" + temperature_controlled: "Temperature Controlled" + new_product: "New Product" + + administration: "Administration" + logged_in_as: "Logged in as" + account: "Account" + logout: "Logout" + + date_range: "Date Range" + status: "status" + new: "New" + start: "Start" + stop: "Stop" + first: "First" + previous: "Previous" + last: "Last" + spree: + your_order_is_empty_add_product: "Your order is empty, please search for and add a product above" + add_product: "Add Product" + name_or_sku: "Name or SKU (enter at least first 4 characters of product name)" + resend: Resend + back_to_orders_list: Back To Orders List + select_stock: "Select stock" + location: "Location" + count_on_hand: "Count On Hand" + quantity: "Quantity" + package_from: "package from" + item_description: "Item Description" + price: "Price" + total: "Total" + edit: "Edit" + split: "Split" + delete: "Delete" + cannot_set_shipping_method_without_address: "Cannot set shipping method until customer details are provided." + no_tracking_present: "No tracking details provided." + order_total: "Order Total" + customer_details: "Customer Details" + customer_search: "Customer Search" + choose_a_customer: "Choose a customer" + account: "Account" + billing_address: "Billing Address" + shipping_address: "Shipping Address" + first_name: "First name" + last_name: "Last name" + street_address: "Street Address" + street_address_2: "Street Address (cont'd)" + city: "City" + zip: "Zip" + country: "Country" + state: "State" + phone: "Phone" + update: "Update" + use_billing_address: "Use Billing Address" + adjustments: "Adjustments" + continue: "Continue" + fill_in_customer_info: "Please fill in customer info" + new_payment: "New Payment" + + configurations: "Configurations" + general_settings: "General Settings" + site_name: "Site Name" + site_url: "Site URL" + default_seo_title: "Default Seo Title" + default_meta_description: "Default Meta Description" + default_meta_keywords: "Default Meta Keywords" + security_settings: "Security Settings" + allow_ssl_in_development_and_test: "Allow SSL to be used when in development and test modes" + allow_ssl_in_production: "Allow SSL to be used in production mode" + allow_ssl_in_staging: "Allow SSL to be used in staging mode" + check_for_spree_alerts: "Check for Spree alerts" + currency_decimal_mark: "Currency decimal mark" + currency_settings: "Currency Settings" + currency_symbol_position: Put "currency symbol before or after dollar amount?" + currency_thousands_separator: "Currency thousands separator" + hide_cents: "Hide cents" + display_currency: "Display currency" + choose_currency: "Choose Currency" + + mail_method_settings: "Mail Method Settings" + general: "General" + enable_mail_delivery: "Enable Mail Delivery" + send_mails_as: "Send Mails As" + smtp_send_all_emails_as_from_following_address: "Send all mails as from the following address." + send_copy_of_all_mails_to: "Send Copy of All Mails To" + smtp_send_copy_to_this_addresses: "Sends a copy of all outgoing mails to this address. For multiple addresses, separate with commas." + intercept_email_address: "Intercept Email Address" + intercept_email_instructions: "Override email recipient and replace with this address." + smtp: "SMTP" + smtp_domain: "SMTP Domain" + smtp_mail_host: "SMTP Mail Host" + smtp_port: "SMTP Port" + secure_connection_type: "Secure Connection Type" + smtp_authentication_type: "SMTP Authentication Type" + smtp_username: "SMTP Username" + smtp_password: "SMTP Password" + + image_settings: "Image Settings" + image_settings_warning: "You will need to regenerate thumbnails if you update the paperclip styles. Use rake paperclip:refresh:thumbnails CLASS=Spree::Image to do this." + attachment_default_style: Attachments Style + attachment_default_url: "Attachments Default URL" + attachment_path: "Attachments Path" + attachment_styles: "Paperclip Styles" + attachment_url: "Attachments URL" + add_new_style: "Add New Style" + image_settings_updated: "Image Settings successfully updated." + + tax_categories: "Tax Categories" + listing_tax_categories: "Listing Tax Categories" + back_to_tax_categories_list: "Back To Tax Categories List" + + tax rate: "Tax Rates" + new_tax_rate: "New Tax Rate" + tax_category: "Tax Category" + rate: "Rate" + tax_rate_amount_explanation: "Tax rates are a decimal amount to aid in calculations, (i.e. if the tax rate is 5% then enter 0.05)" + included_in_price: "Included in Price" + show_rate_in_label: "Show rate in label" + back_to_tax_rates_list: "Back to Tax Rates List" + + tax_settings: "Tax Settings" + zones: "Zones" + new_zone: "New Zone" + default_tax: "Default Tax" + default_tax_zone: "Default Tax Zone" + country_based: "Country Based" + state_based: "State Based" + + countries: "Countries" + listing_countries: "Listing Countries" + iso_name: "ISO Name" + states_required: "States Required" + editing_country: "Editing Country" + back_to_countries_list: "Back to Countries List" + + states: "States" + abbreviation: "Abbreviation" + new_state: "New State" + + payment_methods: "Payment Methods" + new_payment_method: "New Payment Method" + provider: "Provider" + + taxonomies: "Taxonomies" + new_taxonomy: "New Taxonomy" + back_to_taxonomies_list: "Back to Taxonomies List" + + shipping_methods: "Shipping Methods" + + shipping_categories: "Shipping Categories" + new_shipping_category: "NEWEW Shipping Categories" + back_to_shipping_categories: "Back To Shipping Categories" + + analytics_trackers: "Analytics Trackers" + no_trackers_found: "No Trackers Found" + new_tracker: "New Tracker" + add_one: "Add One" + google_analytics_id: "Analytics ID" + back_to_trackers_list: "Back to Trackers List" + + name: "Name" + description: "Description" + type: "Type" + default: "default" + calculator: "Calculator" + zone: "Zone" + display: "Display" + environment: "Environment" + active: "Active" + nore: "More" + no_results: "No results" + create: "Create" + loading: "Loading" + # TODO: remove `email` key once we get to Spree 2.0 email: Email # TODO: remove 'account_updated' key once we get to Spree 2.0 @@ -2755,6 +2957,12 @@ See the %{link} to find out more about %{sitename}'s features and to start using date: "Date" time: "Time" inventory_error_flash_for_insufficient_quantity: "An item in your cart has become unavailable." + inventory: Inventory + zipcode: Postcode + weight: Weight (per kg) + + actions: + update: "Update" errors: messages: blank: "can't be blank" @@ -2943,8 +3151,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using date_picker: format: ! '%Y-%m-%d' js_format: 'yy-mm-dd' - inventory: Inventory - zipcode: Postcode orders: edit: login_to_view_order: "Please log in to view your order." @@ -3024,8 +3230,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using If you continue to have problems please feel free to contact us. confirmation_instructions: subject: Please confirm your OFN account - weight: Weight (per kg) - zipcode: Postcode users: form: account_settings: Account Settings From 021220514ecf33d7452b42f32de440450f14e2e7 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Mon, 25 Feb 2019 21:11:12 +1100 Subject: [PATCH 30/57] Updating translations for config/locales/ca.yml --- config/locales/ca.yml | 99 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 14 deletions(-) diff --git a/config/locales/ca.yml b/config/locales/ca.yml index f340592af3..b47a8890bb 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -31,6 +31,16 @@ ca: on_demand_but_count_on_hand_set: "ha d'estar en blanc si és sota demanda" limited_stock_but_no_count_on_hand: "cal especificar-se perquè força existències limitades" activemodel: + attributes: + order_management/reports/enterprise_fee_summary/parameters: + start_at: "Començar" + end_at: "Acabar" + distributor_ids: "Grups" + producer_ids: "Productors" + order_cycle_ids: "Cicles de comanda" + enterprise_fee_ids: "Noms de les comissions" + shipping_method_ids: "Mètodes d'enviament" + payment_method_ids: "Mètodes de Pagament" errors: models: subscription_validator: @@ -74,6 +84,13 @@ ca: models: order_cycle: cloned_order_cycle_name: "CÒPIA DE %{order_cycle}" + validators: + date_time_string_validator: + not_string_error: "ha de ser una seqüència" + invalid_format_error: "ha de ser vàlid" + integer_array_validator: + not_array_error: "ha de ser una matriu" + invalid_element_error: "ha de contenir només nombres enters vàlids" enterprise_mailer: confirmation_instructions: subject: "Sisplau, confirma l'adreça electrònica d'%{enterprise}" @@ -318,7 +335,7 @@ ca: business_model_configuration: edit: business_model_configuration: "Model de sostenibilitat" - business_model_configuration_tip: "Configureu la tarifa què es cobrarà a les botigues cada mes per utilitzar la Xarxa Open Food." + business_model_configuration_tip: "Configureu la tarifa què es cobrarà a les botigues cada mes per utilitzar Open Food Network." bill_calculation_settings: "Configuració del càlcul de ticket" bill_calculation_settings_tip: "Ajusteu la quantitat que es facturarà a les organitzacions cada mes per utilitzar l'OFN." shop_trial_length: "Durada de la prova de la botiga (dies)" @@ -418,9 +435,9 @@ ca: user_guide: Guia de l'usuari enterprise_fees: index: - title: Tarifes de l'organització + title: Comissions de l'organització enterprise: Organització - fee_type: Tipus de tarifa + fee_type: Tipus de comissió name: Nom tax_category: Categoria d'impostos calculator: Calculadora @@ -634,8 +651,8 @@ ca: website_placeholder: 'p. ex.: www.hortajosepribes.com' enterprise_fees: name: Nom - fee_type: Tipus de tarifa - manage_fees: Gestioneu les tarifes de l'organització + fee_type: Tipus de comissió + manage_fees: Gestioneu les comissions de l'organització no_fees_yet: Encara no tens cap tipus de comissió de l'organització create_button: Crea'n una ara images: @@ -737,7 +754,7 @@ ca: title: Connectar amb Stripe part1: Stripe és un servei de processament de pagaments que permet que les botigues de l'OFN acceptin els pagaments amb targeta de crèdit de les consumidores. part2: Per utilitzar aquesta funció heu de connectar el vostre compte Stripe a l'OFN. Si feu clic a "Accepto", us redirigirem al lloc web de Stripe on podeu connectar un compte Stripe existent o bé crear-ne un si encara no en teniu cap. - part3: Això permetrà que Open Food Network accepti pagaments amb targeta de crèdit de consumidores en nom vostre. Tingueu en compte que haureu de mantenir el vostre propi compte de Stripe, pagar-ne les tarifes i mantenir el servei a les consumidores pel teu compte. + part3: Això permetrà que Open Food Network accepti pagaments amb targeta de crèdit de consumidores en nom vostre. Tingueu en compte que haureu de mantenir el vostre propi compte de Stripe, pagar-ne les comissions i mantenir el servei a les consumidores pel teu compte. i_agree: Accepto cancel: Cancel·lar tag_rules: @@ -775,7 +792,7 @@ ca: payment_methods_tip: Aquesta organització no té mètodes de pagament shipping_methods: Mètodes d'enviament shipping_methods_tip: 'Aquesta organització té mètodes d''enviament ' - enterprise_fees: Honoraris de l'organització + enterprise_fees: Comissions de l'organització enterprise_fees_tip: Aquesta organització no té comissions admin_index: name: Nom @@ -873,7 +890,7 @@ ca: incoming: Entrant supplier: Proveïdora receival_details: Detalls de recepció - fees: Tarifes + fees: Comissions outgoing: Sortint distributor: Distribuïdora products: Productes @@ -911,7 +928,7 @@ ca: customer_instructions: Instruccions de la consumidora customer_instructions_placeholder: Notes de recollida o de lliurament products: Productes - fees: Tarifes + fees: Comissions destroy_errors: orders_present: Una consumidora ha seleccionat aquest Cicle de Comanda i no es pot esborrar. Per evitar que les consumidores hi accedeixin, tanqueu-lo. schedule_present: Aquest cicle de comanda està vinculat a una programació i no es pot esborrar. Desenllaça o suprimeix primer la programació. @@ -996,6 +1013,9 @@ ca: description: Factures per a la importació a Xero packing: name: Informes d'embalatge + enterprise_fee_summary: + name: "Resum de les comissions de l'organització" + description: "Resum de les comissions de l'organització recollides" subscriptions: subscriptions: Subscripcions new: Nova subscripció @@ -1883,7 +1903,7 @@ ca: shipping_method_destroy_error: "Aquest mètode d'enviament no es pot esborrar perquè s'hi fa referència en una comanda: %{number}." accounts_and_billing_task_already_running_error: "Ja s'està executant una tasca, espera fins que hagi acabat" accounts_and_billing_start_task_notice: "Tasca en cua" - fees: "Tarifes" + fees: "Comissions" item_cost: "Cost de l'article" bulk: "A granel" shop_variant_quantity_min: "min" @@ -1955,7 +1975,7 @@ ca: process_my_order: "Processa la meva comanda" delivery_instructions: Instruccions de lliurament delivery_method: Mètode de lliurament - fee_type: "Tipus de tarifa" + fee_type: "Tipus de comissió" tax_category: "Categoria d'impostos" calculator: "Calculadora" calculator_values: "Valors de la calculadora" @@ -2000,7 +2020,6 @@ ca: spree_admin_enterprises_none_text: "Encara no tens cap organització" spree_admin_enterprises_tabs_hubs: "GRUPS" spree_admin_enterprises_producers_manage_products: "GESTIONA ELS PRODUCTES" - spree_admin_enterprises_any_active_products_text: "No tens cap producte actiu." spree_admin_enterprises_create_new_product: "CREA UN NOU PRODUCTE" spree_admin_single_enterprise_alert_mail_confirmation: "Si us plau confirma l'adreça de correu electrònic de" spree_admin_single_enterprise_alert_mail_sent: "Hem enviat un correu electrònic a" @@ -2127,7 +2146,7 @@ ca: report_header_sales_tax: "Impost sobre vendes (%{currency_symbol})" report_header_delivery_charge: "Càrrec de lliurament (%{currency_symbol})" report_header_tax_on_delivery: "Impost sobre el lliurament (%{currency_symbol})" - report_header_tax_on_fees: "Impost sobre les tarifes (%{currency_symbol})" + report_header_tax_on_fees: "Impost sobre les comissions (%{currency_symbol})" report_header_total_tax: "Impost total (%{currency_symbol})" report_header_enterprise: Organització report_header_customer: Consumidora @@ -2248,7 +2267,7 @@ ca: shipping: "Enviament" shipping_methods: "Mètodes d'enviament" payment_methods: "Mètodes de Pagament" - payment_method_fee: "Tarifa de transacció" + payment_method_fee: "Comissió de transacció" inventory_settings: "Configuració de l'inventari" tag_rules: "Regles d'etiqueta" shop_preferences: "Preferències de la botiga" @@ -2526,6 +2545,45 @@ ca: producers: signup: start_free_profile: "Comença amb un perfil gratuït i amplia'l quan estiguis preparada." + order_management: + reports: + enterprise_fee_summary: + date_end_before_start_error: "ha de ser després de l'inici" + parameter_not_allowed_error: "No esteu autoritzats a utilitzar un o més filtres seleccionats per a aquest informe." + fee_calculated_on_transfer_through_all: "Tots" + fee_calculated_on_transfer_through_entire_orders: "Comandes completades a través de" + tax_category_various: "Varis" + fee_type: + payment_method: "Transacció de pagament" + shipping_method: "Enviament" + fee_placements: + supplier: "Entrant" + distributor: "Sortint" + coordinator: "Coordinador" + tax_category_name: + shipping_instance_rate: "Tarifa de la plataforma" + formats: + csv: + header: + fee_type: "Tipus de comissió" + enterprise_name: "Propietària de l'organització" + fee_name: "Nom de la comissió" + customer_name: "Consumidora" + fee_placement: "Col·locació de comissions" + fee_calculated_on_transfer_through_name: "Càlcul de comissions a través de transferència" + tax_category_name: "Categoria d'impostos" + total_amount: "€€ SUM" + html: + header: + fee_type: "Tipus de comissió" + enterprise_name: "Propietària de l'organització" + fee_name: "Nom de la comissió" + customer_name: "Consumidora" + fee_placement: "Col·locació de comissions" + fee_calculated_on_transfer_through_name: "Càlcul de comissions a través de transferència" + tax_category_name: "Categoria d'impostos" + total_amount: "€€ SUM" + invalid_filter_parameters: "Els filtres que heu seleccionat per a aquest informe no són vàlids." spree: email: Correu electrònic account_updated: "Compte actualitzat!" @@ -2569,6 +2627,11 @@ ca: distributor: "Distribuïdora:" order_cycle: "Cicle de comanda:" overview: + products: + active_products: + zero: "No tens cap producte actiu." + one: "Teniu un producte actiu" + other: "Teniu %{count} productes actius" order_cycles: order_cycles: "Cicles de comanda" order_cycles_tip: "Els cicles de comanda determinen quan i on els teus productes estan disponibles per a les consumidores." @@ -2642,6 +2705,14 @@ ca: bulk_coop_allocation: 'Compra grupal - Assignació' bulk_coop_packing_sheets: 'Compra grupal - Fulls de preparació de cistelles' bulk_coop_customer_payments: 'Compra grupal - Pagaments de les consumidores' + enterprise_fee_summaries: + filters: + date_range: "Interval de dates" + report_format_csv: "Descarrega com a CSV" + generate_report: "Generar informe" + report: + none: "No productora" + select_and_search: "Seleccioneu els filtres i feu clic a GENERAR INFORME per accedir a les dades." users: index: listing_users: "Llistat d'usuàries" From bbfe71fd2763343e16a23790d7a92b8c78502111 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 25 Feb 2019 10:25:23 +0000 Subject: [PATCH 31/57] Improve readability and add code comment --- spec/support/request/web_helper.rb | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/spec/support/request/web_helper.rb b/spec/support/request/web_helper.rb index 4cf788ae8e..52ffc306ac 100644 --- a/spec/support/request/web_helper.rb +++ b/spec/support/request/web_helper.rb @@ -152,18 +152,23 @@ module WebHelper page.driver.browser.switch_to.alert.accept end - def angular_http_requests_finished(angular_controller=nil) - scope_element = angular_controller ? "[ng-controller=#{angular_controller}]" : '.ng-scope' - page.evaluate_script("angular.element(document.querySelector('#{scope_element}')).injector().get('$http').pendingRequests.length == 0") + def angular_http_requests_finished(controller=nil) + page.evaluate_script("#{angular_scope(controller)}.injector().get('$http').pendingRequests.length == 0") end - def request_monitor_finished(angular_controller=nil) - scope_element = angular_controller ? "[ng-controller=#{angular_controller}]" : '.ng-scope' - page.evaluate_script("angular.element(document.querySelector('#{scope_element}')).scope().RequestMonitor.loading == false") + def request_monitor_finished(controller=nil) + page.evaluate_script("#{angular_scope(controller)}.scope().RequestMonitor.loading == false") end private + # Takes an optional angular controller name eg: "LineItemsCtrl", + # otherwise finds the first object in the DOM with an angular scope + def angular_scope(controller=nil) + element = controller ? "[ng-controller=#{controller}]" : '.ng-scope' + "angular.element(document.querySelector('#{element}'))" + end + def wait_for_ajax wait_until { page.evaluate_script("$.active") == 0 } end From 6e77afbf35644dc24519d18be6ae7e66287d36e7 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 23 Feb 2019 20:33:05 +0000 Subject: [PATCH 32/57] Adjust expectations in flaky spec --- spec/features/admin/shipping_methods_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/features/admin/shipping_methods_spec.rb b/spec/features/admin/shipping_methods_spec.rb index bb65e70255..03f1bcec8c 100644 --- a/spec/features/admin/shipping_methods_spec.rb +++ b/spec/features/admin/shipping_methods_spec.rb @@ -103,9 +103,8 @@ feature 'shipping methods' do click_button I18n.t("actions.create") - expect(page).to have_no_button I18n.t("actions.create") - message = "Shipping method \"Teleport\" has been successfully created!" - expect(page).to have_flash_message message + expect(page).to have_content I18n.t('editing_shipping_method') + expect(flash_message).to eq I18n.t('successfully_created', resource: 'Shipping method "Teleport"') expect(first('tags-input .tag-list ti-tag-item')).to have_content "local" From 36e2786a1e0ca241821706f3ae410a30f0e6bea8 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Mon, 14 Jan 2019 11:28:25 +0000 Subject: [PATCH 33/57] Delete Accounts and Billing dead feature code --- .rubocop_manual_todo.yml | 33 - .rubocop_todo.yml | 72 -- .../accounts_and_billing_settings.js.coffee | 1 - .../directives/method_settings.js.coffee | 14 - app/assets/javascripts/admin/all.js | 2 - .../business_model_configuration.js.coffee | 1 - ...s_model_configuration_controller.js.coffee | 25 - app/assets/stylesheets/admin/account.css.scss | 19 - app/controllers/admin/account_controller.rb | 6 - ...ccounts_and_billing_settings_controller.rb | 74 -- ...business_model_configuration_controller.rb | 35 - .../admin/invoice_settings_controller.rb | 13 - app/controllers/api/customers_controller.rb | 2 +- .../spree/users_controller_decorator.rb | 4 - app/helpers/admin/account_helper.rb | 14 - .../business_model_configuration_helper.rb | 71 -- app/helpers/admin/injection_helper.rb | 6 - app/helpers/injection_helper.rb | 2 +- app/jobs/finalize_account_invoices.rb | 110 --- app/jobs/update_account_invoices.rb | 108 --- app/jobs/update_billable_periods.rb | 130 ---- app/models/account_invoice.rb | 6 - app/models/billable_period.rb | 77 -- app/models/customer.rb | 5 - app/models/enterprise.rb | 1 - app/models/spree/adjustment_decorator.rb | 1 - .../spree/app_configuration_decorator.rb | 15 - app/models/spree/order_decorator.rb | 7 +- app/models/spree/user_decorator.rb | 2 - .../add_accounts_and_billing.html.haml.deface | 4 - ...iness_model_configuration.html.haml.deface | 4 - app/views/admin/account/show.html.haml | 47 -- .../_method_settings.html.haml | 10 - .../edit.html.haml | 94 --- .../edit.html.haml | 104 --- .../enterprises/_change_type_form.html.haml | 1 - app/views/admin/enterprises/index.html.haml | 1 - config/initializers/user_class_extensions.rb | 11 - config/locales/en.yml | 53 -- config/routes/admin.rb | 11 - config/schedule.rb | 9 - knapsack_rspec_report.json | 8 - ...accounts_and_billing_settings_validator.rb | 41 - lib/open_food_network/bill_calculator.rb | 21 - .../business_model_configuration_validator.rb | 22 - lib/open_food_network/xero_invoices_report.rb | 13 +- lib/tasks/billing.rake | 13 - ...ts_and_billing_settings_controller_spec.rb | 241 ------ ...ess_model_configuration_controller_spec.rb | 98 --- .../api/customers_controller_spec.rb | 12 - .../spree/users_controller_spec.rb | 7 - spec/factories.rb | 20 - spec/features/admin/account_spec.rb | 21 - .../accounts_and_billing_settings_spec.rb | 60 -- .../business_model_configuration_spec.rb | 53 -- spec/features/admin/reports_spec.rb | 35 - spec/features/consumer/account_spec.rb | 8 - ...usiness_model_configuration_helper_spec.rb | 574 -------------- spec/jobs/finalize_account_invoices_spec.rb | 216 ------ spec/jobs/update_account_invoices_spec.rb | 448 ----------- spec/jobs/update_billable_periods_spec.rb | 725 ------------------ .../xero_invoices_report_spec.rb | 29 +- spec/models/billable_period_spec.rb | 558 -------------- spec/models/spree/order_spec.rb | 26 - 64 files changed, 7 insertions(+), 4447 deletions(-) delete mode 100644 app/assets/javascripts/admin/accounts_and_billing_settings/accounts_and_billing_settings.js.coffee delete mode 100644 app/assets/javascripts/admin/accounts_and_billing_settings/directives/method_settings.js.coffee delete mode 100644 app/assets/javascripts/admin/business_model_configuration/business_model_configuration.js.coffee delete mode 100644 app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee delete mode 100644 app/assets/stylesheets/admin/account.css.scss delete mode 100644 app/controllers/admin/account_controller.rb delete mode 100644 app/controllers/admin/accounts_and_billing_settings_controller.rb delete mode 100644 app/controllers/admin/business_model_configuration_controller.rb delete mode 100644 app/controllers/admin/invoice_settings_controller.rb delete mode 100644 app/helpers/admin/account_helper.rb delete mode 100644 app/helpers/admin/business_model_configuration_helper.rb delete mode 100644 app/jobs/finalize_account_invoices.rb delete mode 100644 app/jobs/update_account_invoices.rb delete mode 100644 app/jobs/update_billable_periods.rb delete mode 100644 app/models/account_invoice.rb delete mode 100644 app/models/billable_period.rb delete mode 100644 app/overrides/spree/admin/shared/_configuration_menu/add_accounts_and_billing.html.haml.deface delete mode 100644 app/overrides/spree/admin/shared/_configuration_menu/add_business_model_configuration.html.haml.deface delete mode 100644 app/views/admin/account/show.html.haml delete mode 100644 app/views/admin/accounts_and_billing_settings/_method_settings.html.haml delete mode 100644 app/views/admin/accounts_and_billing_settings/edit.html.haml delete mode 100644 app/views/admin/business_model_configuration/edit.html.haml delete mode 100644 config/initializers/user_class_extensions.rb delete mode 100644 lib/open_food_network/accounts_and_billing_settings_validator.rb delete mode 100644 lib/open_food_network/bill_calculator.rb delete mode 100644 lib/open_food_network/business_model_configuration_validator.rb delete mode 100644 lib/tasks/billing.rake delete mode 100644 spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb delete mode 100644 spec/controllers/admin/business_model_configuration_controller_spec.rb delete mode 100644 spec/features/admin/account_spec.rb delete mode 100644 spec/features/admin/accounts_and_billing_settings_spec.rb delete mode 100644 spec/features/admin/business_model_configuration_spec.rb delete mode 100644 spec/helpers/admin/business_model_configuration_helper_spec.rb delete mode 100644 spec/jobs/finalize_account_invoices_spec.rb delete mode 100644 spec/jobs/update_account_invoices_spec.rb delete mode 100644 spec/jobs/update_billable_periods_spec.rb delete mode 100644 spec/models/billable_period_spec.rb diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index 71812eb60a..fecfd46adc 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -22,9 +22,7 @@ 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 @@ -63,7 +61,6 @@ Metrics/LineLength: - 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 @@ -79,16 +76,12 @@ Metrics/LineLength: - 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 @@ -160,11 +153,8 @@ Metrics/LineLength: - 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 @@ -201,7 +191,6 @@ Metrics/LineLength: - 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 @@ -245,7 +234,6 @@ Metrics/LineLength: - 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 @@ -289,7 +277,6 @@ Metrics/LineLength: - 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 @@ -298,12 +285,9 @@ Metrics/LineLength: - 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 @@ -337,7 +321,6 @@ Metrics/LineLength: - 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 @@ -452,18 +435,13 @@ Metrics/AbcSize: - 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 @@ -492,7 +470,6 @@ Metrics/AbcSize: - 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 @@ -534,13 +511,10 @@ Metrics/CyclomaticComplexity: - 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 @@ -553,7 +527,6 @@ Metrics/CyclomaticComplexity: - 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 @@ -569,12 +542,10 @@ Metrics/PerceivedComplexity: - 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 @@ -624,12 +595,8 @@ Metrics/MethodLength: - 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 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index f0d9000e11..d8a109f6e3 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -143,7 +143,6 @@ Layout/EmptyLines: - 'app/helpers/angular_form_helper.rb' - 'app/helpers/injection_helper.rb' - 'app/helpers/spree/admin/base_helper_decorator.rb' - - 'app/jobs/finalize_account_invoices.rb' - 'app/jobs/products_cache_integrity_checker_job.rb' - 'app/jobs/refresh_products_cache_job.rb' - 'app/models/coordinator_fee.rb' @@ -218,7 +217,6 @@ Layout/EmptyLines: - 'spec/features/consumer/shopping/variant_overrides_spec.rb' - 'spec/features/consumer/shops_spec.rb' - 'spec/helpers/checkout_helper_spec.rb' - - 'spec/jobs/finalize_account_invoices_spec.rb' - 'spec/jobs/heartbeat_job_spec.rb' - 'spec/lib/open_food_network/enterprise_fee_calculator_spec.rb' - 'spec/lib/open_food_network/option_value_namer_spec.rb' @@ -278,17 +276,14 @@ Layout/EmptyLinesAroundBlockBody: - 'spec/features/consumer/shopping/embedded_groups_spec.rb' - 'spec/features/consumer/shopping/embedded_shopfronts_spec.rb' - 'spec/features/consumer/shopping/shopping_spec.rb' - - 'spec/helpers/admin/business_model_configuration_helper_spec.rb' - 'spec/helpers/shared_helper_spec.rb' - 'spec/helpers/shop_helper_spec.rb' - 'spec/helpers/spree/orders_helper_spec.rb' - - 'spec/jobs/update_billable_periods_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/order_grouper_spec.rb' - 'spec/lib/open_food_network/referer_parser_spec.rb' - 'spec/lib/open_food_network/user_balance_calculator_spec.rb' - - 'spec/models/billable_period_spec.rb' - 'spec/models/product_distribution_spec.rb' - 'spec/models/spree/ability_spec.rb' - 'spec/models/spree/product_spec.rb' @@ -307,7 +302,6 @@ Layout/EmptyLinesAroundBlockBody: # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only Layout/EmptyLinesAroundClassBody: Exclude: - - 'app/controllers/admin/account_controller.rb' - 'app/controllers/admin/cache_settings_controller.rb' - 'app/controllers/admin/enterprise_fees_controller.rb' - 'app/controllers/admin/inventory_items_controller.rb' @@ -358,7 +352,6 @@ Layout/ExtraSpacing: - 'lib/open_food_network/variant_and_line_item_naming.rb' - 'lib/spree/product_filters.rb' - 'lib/tasks/karma.rake' - - 'spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb' - 'spec/features/admin/bulk_order_management_spec.rb' - 'spec/features/admin/bulk_product_update_spec.rb' - 'spec/features/admin/orders_spec.rb' @@ -409,7 +402,6 @@ Layout/IndentationConsistency: - 'lib/open_food_network/permissions.rb' - 'spec/controllers/admin/tag_rules_controller_spec.rb' - 'spec/features/consumer/shopping/checkout_spec.rb' - - 'spec/helpers/admin/business_model_configuration_helper_spec.rb' - 'spec/models/spree/line_item_spec.rb' - 'spec/models/spree/product_spec.rb' @@ -425,7 +417,6 @@ Layout/IndentationWidth: - 'app/serializers/api/admin/for_order_cycle/supplied_product_serializer.rb' - 'app/serializers/api/admin/order_cycle_serializer.rb' - 'spec/features/consumer/shopping/variant_overrides_spec.rb' - - 'spec/helpers/admin/business_model_configuration_helper_spec.rb' - 'spec/helpers/groups_helper_spec.rb' - 'spec/lib/open_food_network/enterprise_fee_calculator_spec.rb' - 'spec/lib/open_food_network/order_grouper_spec.rb' @@ -439,7 +430,6 @@ Layout/IndentationWidth: Layout/LeadingCommentSpace: Exclude: - 'Gemfile' - - 'app/models/billable_period.rb' - 'app/models/content_configuration.rb' - 'app/models/spree/inventory_unit_decorator.rb' - 'app/models/spree/taxon_decorator.rb' @@ -449,7 +439,6 @@ Layout/LeadingCommentSpace: - 'spec/controllers/spree/api/line_items_controller_spec.rb' - 'spec/features/admin/products_spec.rb' - 'spec/features/admin/reports_spec.rb' - - 'spec/jobs/finalize_account_invoices_spec.rb' - 'spec/lib/open_food_network/order_grouper_spec.rb' - 'spec/lib/open_food_network/user_balance_calculator_spec.rb' - 'spec/models/enterprise_spec.rb' @@ -471,7 +460,6 @@ Layout/MultilineBlockLayout: - 'spec/features/admin/variant_overrides_spec.rb' - 'spec/features/consumer/shopping/cart_spec.rb' - 'spec/helpers/enterprises_helper_spec.rb' - - 'spec/jobs/update_billable_periods_spec.rb' - 'spec/lib/open_food_network/enterprise_fee_calculator_spec.rb' - 'spec/lib/open_food_network/permissions_spec.rb' - 'spec/lib/open_food_network/user_balance_calculator_spec.rb' @@ -489,7 +477,6 @@ Layout/MultilineBlockLayout: Layout/MultilineHashBraceLayout: Exclude: - 'app/controllers/spree/admin/products_controller_decorator.rb' - - 'app/models/billable_period.rb' - 'lib/spree/product_filters.rb' - 'spec/support/request/authentication_workflow.rb' @@ -528,7 +515,6 @@ Layout/MultilineOperationIndentation: - 'app/models/producer_property.rb' - 'app/models/spree/ability_decorator.rb' - 'app/models/variant_override_set.rb' - - 'lib/open_food_network/accounts_and_billing_settings_validator.rb' - 'lib/open_food_network/order_cycle_permissions.rb' - 'lib/open_food_network/sales_tax_report.rb' - 'lib/open_food_network/users_and_enterprises_report.rb' @@ -551,8 +537,6 @@ Layout/SpaceAfterComma: - 'app/controllers/spree/orders_controller_decorator.rb' - 'app/models/column_preference.rb' - 'lib/discourse/single_sign_on.rb' - - 'lib/open_food_network/accounts_and_billing_settings_validator.rb' - - 'lib/open_food_network/business_model_configuration_validator.rb' - 'lib/open_food_network/order_cycle_form_applicator.rb' - 'lib/open_food_network/users_and_enterprises_report.rb' - 'spec/controllers/admin/enterprises_controller_spec.rb' @@ -561,7 +545,6 @@ Layout/SpaceAfterComma: - 'spec/features/admin/bulk_product_update_spec.rb' - 'spec/features/admin/customers_spec.rb' - 'spec/features/admin/variant_overrides_spec.rb' - - 'spec/jobs/update_account_invoices_spec.rb' - 'spec/lib/open_food_network/group_buy_report_spec.rb' - 'spec/lib/open_food_network/subscription_summary_spec.rb' - 'spec/models/content_configuration_spec.rb' @@ -608,9 +591,6 @@ Layout/SpaceAroundEqualsInParameterDefault: - 'app/models/spree/taxon_decorator.rb' - 'app/models/variant_override_set.rb' - 'lib/discourse/single_sign_on.rb' - - 'lib/open_food_network/accounts_and_billing_settings_validator.rb' - - 'lib/open_food_network/bill_calculator.rb' - - 'lib/open_food_network/business_model_configuration_validator.rb' - 'lib/open_food_network/enterprise_fee_calculator.rb' - 'lib/open_food_network/enterprise_issue_validator.rb' - 'lib/open_food_network/order_cycle_form_applicator.rb' @@ -631,8 +611,6 @@ Layout/SpaceAroundEqualsInParameterDefault: Layout/SpaceAroundOperators: Exclude: - 'app/controllers/checkout_controller.rb' - - 'app/helpers/admin/business_model_configuration_helper.rb' - - 'app/jobs/update_billable_periods.rb' - 'app/overrides/remove_search_bar.rb' - 'app/overrides/remove_side_bar.rb' - 'app/overrides/replace_shipping_address_form_with_distributor_details.rb' @@ -644,7 +622,6 @@ Layout/SpaceAroundOperators: - 'spec/features/consumer/shopping/checkout_spec.rb' - 'spec/helpers/checkout_helper_spec.rb' - 'spec/helpers/order_cycles_helper_spec.rb' - - 'spec/jobs/update_billable_periods_spec.rb' - 'spec/lib/open_food_network/order_grouper_spec.rb' - 'spec/lib/stripe/account_connector_spec.rb' - 'spec/spec_helper.rb' @@ -694,7 +671,6 @@ Layout/SpaceInsideArrayLiteralBrackets: - 'lib/open_food_network/users_and_enterprises_report.rb' - 'spec/controllers/admin/variant_overrides_controller_spec.rb' - 'spec/features/admin/reports_spec.rb' - - 'spec/jobs/update_billable_periods_spec.rb' - 'spec/lib/open_food_network/order_grouper_spec.rb' - 'spec/lib/open_food_network/users_and_enterprises_report_spec.rb' - 'spec/models/spree/order_spec.rb' @@ -715,8 +691,6 @@ Layout/SpaceInsideBlockBraces: - 'lib/spree/product_filters.rb' - 'lib/tasks/karma.rake' - 'spec/archive/features/consumer/checkout_spec.rb' - - 'spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb' - - 'spec/controllers/admin/business_model_configuration_controller_spec.rb' - 'spec/controllers/admin/inventory_items_controller_spec.rb' - 'spec/controllers/admin/tag_rules_controller_spec.rb' - 'spec/controllers/admin/variant_overrides_controller_spec.rb' @@ -732,9 +706,6 @@ Layout/SpaceInsideBlockBraces: - 'spec/helpers/enterprises_helper_spec.rb' - 'spec/helpers/injection_helper_spec.rb' - 'spec/helpers/spree/orders_helper_spec.rb' - - 'spec/jobs/finalize_account_invoices_spec.rb' - - 'spec/jobs/update_account_invoices_spec.rb' - - 'spec/jobs/update_billable_periods_spec.rb' - 'spec/lib/open_food_network/order_cycle_form_applicator_spec.rb' - 'spec/lib/open_food_network/order_grouper_spec.rb' - 'spec/lib/open_food_network/orders_and_fulfillments_report_spec.rb' @@ -767,7 +738,6 @@ Layout/SpaceInsideHashLiteralBraces: - 'app/controllers/spree/admin/line_items_controller_decorator.rb' - 'app/controllers/spree/admin/products_controller_decorator.rb' - 'app/controllers/spree/admin/search_controller_decorator.rb' - - 'app/helpers/admin/business_model_configuration_helper.rb' - 'app/helpers/admin/injection_helper.rb' - 'app/helpers/angular_form_builder.rb' - 'app/helpers/injection_helper.rb' @@ -775,9 +745,7 @@ Layout/SpaceInsideHashLiteralBraces: - 'app/helpers/serializer_helper.rb' - 'app/helpers/spree/admin/base_helper_decorator.rb' - 'app/helpers/spree/admin/navigation_helper_decorator.rb' - - 'app/jobs/update_billable_periods.rb' - 'app/mailers/spree/base_mailer_decorator.rb' - - 'app/models/billable_period.rb' - 'app/models/enterprise.rb' - 'app/models/enterprise_group.rb' - 'app/models/enterprise_relationship.rb' @@ -796,8 +764,6 @@ Layout/SpaceInsideHashLiteralBraces: - 'lib/open_food_network/sales_tax_report.rb' - 'lib/open_food_network/variant_and_line_item_naming.rb' - 'lib/open_food_network/xero_invoices_report.rb' - - 'spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb' - - 'spec/controllers/admin/business_model_configuration_controller_spec.rb' - 'spec/controllers/admin/enterprises_controller_spec.rb' - 'spec/controllers/admin/manager_invitations_controller_spec.rb' - 'spec/controllers/admin/order_cycles_controller_spec.rb' @@ -816,7 +782,6 @@ Layout/SpaceInsideHashLiteralBraces: - 'spec/controllers/spree/user_sessions_controller_spec.rb' - 'spec/controllers/user_passwords_controller_spec.rb' - 'spec/controllers/user_registrations_controller_spec.rb' - - 'spec/features/admin/accounts_and_billing_settings_spec.rb' - 'spec/features/admin/image_settings_spec.rb' - 'spec/features/admin/products_spec.rb' - 'spec/features/admin/reports_spec.rb' @@ -972,7 +937,6 @@ Lint/ShadowingOuterLocalVariable: # Cop supports --auto-correct. Lint/StringConversionInInterpolation: Exclude: - - 'app/controllers/admin/accounts_and_billing_settings_controller.rb' - 'app/controllers/enterprises_controller.rb' - 'app/helpers/admin/injection_helper.rb' - 'app/helpers/injection_helper.rb' @@ -1025,8 +989,6 @@ Lint/UnusedMethodArgument: - 'app/helpers/angular_form_helper.rb' - 'app/helpers/order_cycles_helper.rb' - 'app/helpers/spree/base_helper_decorator.rb' - - 'app/jobs/finalize_account_invoices.rb' - - 'app/jobs/update_account_invoices.rb' - 'app/models/spree/ability_decorator.rb' - 'app/models/spree/product_decorator.rb' - 'lib/open_food_network/enterprise_fee_applicator.rb' @@ -1337,8 +1299,6 @@ Rails/HasAndBelongsToMany: # Include: app/models/**/*.rb Rails/HasManyOrHasOneDependent: Exclude: - - 'app/models/account_invoice.rb' - - 'app/models/billable_period.rb' - 'app/models/customer.rb' - 'app/models/enterprise.rb' - 'app/models/order_cycle.rb' @@ -1394,8 +1354,6 @@ Rails/OutputSafety: Rails/PluralizationGrammar: Exclude: - 'spec/features/admin/order_cycles_spec.rb' - - 'spec/jobs/update_account_invoices_spec.rb' - - 'spec/jobs/update_billable_periods_spec.rb' - 'spec/models/order_cycle_spec.rb' - 'spec/models/spree/product_spec.rb' @@ -1515,15 +1473,9 @@ Style/BracesAroundHashParameters: - 'app/controllers/checkout_controller.rb' - 'app/controllers/spree/admin/products_controller_decorator.rb' - 'app/controllers/spree/admin/search_controller_decorator.rb' - - 'app/helpers/admin/account_helper.rb' - - 'app/helpers/admin/business_model_configuration_helper.rb' - 'app/helpers/angular_form_builder.rb' - 'app/helpers/checkout_helper.rb' - 'app/helpers/spree/admin/orders_helper_decorator.rb' - - 'app/jobs/finalize_account_invoices.rb' - - 'app/jobs/update_account_invoices.rb' - - 'app/jobs/update_billable_periods.rb' - - 'app/models/billable_period.rb' - 'app/models/exchange.rb' - 'app/models/spree/adjustment_decorator.rb' - 'app/models/spree/line_item_decorator.rb' @@ -1533,8 +1485,6 @@ Style/BracesAroundHashParameters: - 'lib/open_food_network/reports/rule.rb' - 'lib/open_food_network/variant_and_line_item_naming.rb' - 'lib/open_food_network/xero_invoices_report.rb' - - 'spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb' - - 'spec/controllers/admin/business_model_configuration_controller_spec.rb' - 'spec/controllers/admin/enterprises_controller_spec.rb' - 'spec/controllers/admin/manager_invitations_controller_spec.rb' - 'spec/controllers/admin/order_cycles_controller_spec.rb' @@ -1552,19 +1502,14 @@ Style/BracesAroundHashParameters: - 'spec/controllers/spree/orders_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/business_model_configuration_spec.rb' - 'spec/features/admin/order_cycles_spec.rb' - 'spec/features/admin/products_spec.rb' - 'spec/features/admin/reports_spec.rb' - 'spec/features/admin/tax_settings_spec.rb' - 'spec/helpers/order_cycles_helper_spec.rb' - - 'spec/jobs/finalize_account_invoices_spec.rb' - - 'spec/jobs/update_account_invoices_spec.rb' - 'spec/lib/open_food_network/feature_toggle_spec.rb' - 'spec/lib/open_food_network/order_cycle_form_applicator_spec.rb' - 'spec/lib/open_food_network/subscription_summarizer_spec.rb' - - 'spec/models/billable_period_spec.rb' - 'spec/models/product_distribution_spec.rb' - 'spec/models/spree/ability_spec.rb' - 'spec/models/spree/order_spec.rb' @@ -1588,9 +1533,6 @@ Style/CaseEquality: # SupportedStyles: nested, compact Style/ClassAndModuleChildren: Exclude: - - 'app/controllers/admin/account_controller.rb' - - 'app/controllers/admin/accounts_and_billing_settings_controller.rb' - - 'app/controllers/admin/business_model_configuration_controller.rb' - 'app/controllers/admin/cache_settings_controller.rb' - 'app/controllers/spree/store_controller_decorator.rb' - 'app/helpers/angular_form_helper.rb' @@ -1758,7 +1700,6 @@ Style/FormatStringToken: # Configuration parameters: MinBodyLength. Style/GuardClause: Exclude: - - 'app/controllers/admin/accounts_and_billing_settings_controller.rb' - 'app/controllers/admin/enterprises_controller.rb' - 'app/controllers/admin/order_cycles_controller.rb' - 'app/controllers/admin/product_import_controller.rb' @@ -1775,8 +1716,6 @@ Style/GuardClause: - 'app/controllers/spree/orders_controller_decorator.rb' - 'app/controllers/spree/paypal_controller_decorator.rb' - 'app/jobs/products_cache_integrity_checker_job.rb' - - 'app/jobs/update_account_invoices.rb' - - 'app/jobs/update_billable_periods.rb' - 'app/models/enterprise.rb' - 'app/models/enterprise_group.rb' - 'app/models/producer_property.rb' @@ -1786,7 +1725,6 @@ Style/GuardClause: - 'app/models/spree/product_decorator.rb' - 'app/models/spree/user_decorator.rb' - 'lib/discourse/single_sign_on.rb' - - 'lib/open_food_network/accounts_and_billing_settings_validator.rb' - 'lib/open_food_network/order_cycle_form_applicator.rb' - 'lib/open_food_network/products_cache.rb' - 'lib/open_food_network/products_renderer.rb' @@ -1805,7 +1743,6 @@ Style/GuardClause: # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys Style/HashSyntax: Exclude: - - 'app/controllers/admin/accounts_and_billing_settings_controller.rb' - 'app/controllers/admin/contents_controller.rb' - 'app/controllers/admin/enterprise_fees_controller.rb' - 'app/controllers/admin/enterprises_controller.rb' @@ -1823,7 +1760,6 @@ Style/HashSyntax: - 'app/controllers/spree/paypal_controller_decorator.rb' - 'app/controllers/spree/store_controller_decorator.rb' - 'app/controllers/user_passwords_controller.rb' - - 'app/helpers/admin/account_helper.rb' - 'app/helpers/angular_form_builder.rb' - 'app/helpers/application_helper.rb' - 'app/helpers/checkout_helper.rb' @@ -1832,7 +1768,6 @@ Style/HashSyntax: - 'app/helpers/spree/admin/orders_helper_decorator.rb' - 'app/mailers/enterprise_mailer.rb' - 'app/mailers/spree/user_mailer_decorator.rb' - - 'app/models/billable_period.rb' - 'app/models/calculator/flat_percent_per_item.rb' - 'app/models/enterprise.rb' - 'app/models/enterprise_fee.rb' @@ -2090,9 +2025,7 @@ Style/NumericLiterals: Style/NumericPredicate: Exclude: - 'spec/**/*' - - 'app/helpers/admin/business_model_configuration_helper.rb' - 'app/helpers/checkout_helper.rb' - - 'app/jobs/update_account_invoices.rb' - 'app/models/spree/calculator/flexi_rate_decorator.rb' - 'app/models/spree/line_item_decorator.rb' - 'app/models/spree/order_decorator.rb' @@ -2181,7 +2114,6 @@ Style/RedundantReturn: # Cop supports --auto-correct. Style/RedundantSelf: Exclude: - - 'app/models/billable_period.rb' - 'app/models/calculator/flat_percent_per_item.rb' - 'app/models/enterprise.rb' - 'app/models/exchange.rb' @@ -2295,7 +2227,6 @@ Style/StabbyLambdaParentheses: Style/StringLiteralsInInterpolation: Exclude: - 'app/controllers/application_controller.rb' - - 'app/jobs/update_billable_periods.rb' - 'lib/discourse/single_sign_on.rb' - 'lib/open_food_network/users_and_enterprises_report.rb' - 'spec/features/admin/bulk_order_management_spec.rb' @@ -2373,12 +2304,9 @@ Style/UnneededCondition: # Cop supports --auto-correct. Style/UnneededInterpolation: Exclude: - - 'app/controllers/admin/accounts_and_billing_settings_controller.rb' - 'app/controllers/admin/resource_controller.rb' - - 'app/helpers/admin/business_model_configuration_helper.rb' - 'app/helpers/angular_form_builder.rb' - 'app/helpers/injection_helper.rb' - - 'app/jobs/update_billable_periods.rb' - 'lib/discourse/single_sign_on.rb' - 'spec/features/admin/bulk_order_management_spec.rb' - 'spec/features/admin/bulk_product_update_spec.rb' diff --git a/app/assets/javascripts/admin/accounts_and_billing_settings/accounts_and_billing_settings.js.coffee b/app/assets/javascripts/admin/accounts_and_billing_settings/accounts_and_billing_settings.js.coffee deleted file mode 100644 index 06ee4fa4ef..0000000000 --- a/app/assets/javascripts/admin/accounts_and_billing_settings/accounts_and_billing_settings.js.coffee +++ /dev/null @@ -1 +0,0 @@ -angular.module("admin.accounts_and_billing_settings", ["admin.utils"]) diff --git a/app/assets/javascripts/admin/accounts_and_billing_settings/directives/method_settings.js.coffee b/app/assets/javascripts/admin/accounts_and_billing_settings/directives/method_settings.js.coffee deleted file mode 100644 index 32ef50bb64..0000000000 --- a/app/assets/javascripts/admin/accounts_and_billing_settings/directives/method_settings.js.coffee +++ /dev/null @@ -1,14 +0,0 @@ -angular.module("admin.accounts_and_billing_settings").directive "methodSettingsFor", -> - template: "
" - restrict: 'A' - scope: { - enterprise_id: '=methodSettingsFor' - } - link: (scope, element, attrs) -> - scope.include_html = "" - - scope.$watch "enterprise_id", (newVal, oldVal)-> - if !newVal? || newVal == "" - scope.include_html = "" - else - scope.include_html = "/admin/accounts_and_billing_settings/show_methods?enterprise_id=#{newVal};" diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js index 61f809ed25..5ed758af28 100644 --- a/app/assets/javascripts/admin/all.js +++ b/app/assets/javascripts/admin/all.js @@ -23,8 +23,6 @@ //= require angular-rails-templates //= require_tree ../templates/admin //= require ./admin_ofn -//= require ./accounts_and_billing_settings/accounts_and_billing_settings -//= require ./business_model_configuration/business_model_configuration //= require ./customers/customers //= require ./dropdown/dropdown //= require ./enterprises/enterprises diff --git a/app/assets/javascripts/admin/business_model_configuration/business_model_configuration.js.coffee b/app/assets/javascripts/admin/business_model_configuration/business_model_configuration.js.coffee deleted file mode 100644 index cecb7c397e..0000000000 --- a/app/assets/javascripts/admin/business_model_configuration/business_model_configuration.js.coffee +++ /dev/null @@ -1 +0,0 @@ -angular.module("admin.businessModelConfiguration", ["admin.utils"]) diff --git a/app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee b/app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee deleted file mode 100644 index d5b7fa4b63..0000000000 --- a/app/assets/javascripts/admin/business_model_configuration/controllers/business_model_configuration_controller.js.coffee +++ /dev/null @@ -1,25 +0,0 @@ -angular.module("admin.businessModelConfiguration").controller "BusinessModelConfigCtrl", ($scope, $filter) -> - $scope.turnover = 1000 - - $scope.bill = -> - return $filter('currency')(0) unless $scope.fixed || $scope.rate - Number($scope.fixed) + Number($scope.turnover) * Number($scope.rate) - - $scope.cappedBill = -> - return $scope.bill() if !$scope.cap? || Number($scope.cap) == 0 - Math.min($scope.bill(), Number($scope.cap)) - - $scope.finalBill = -> - return 0 if Number($scope.turnover) < Number($scope.minBillableTurnover) - $scope.cappedBill() - - $scope.capReached = -> - return t('no') if !$scope.cap? || Number($scope.cap) == 0 - if $scope.bill() >= Number($scope.cap) then t('yes') else t('no') - - $scope.includedTax = -> - return 0 if !$scope.taxRate? || Number($scope.taxRate) == 0 - ($scope.cappedBill() * Number($scope.taxRate)) - - $scope.total = -> - $scope.finalBill() + $scope.includedTax() diff --git a/app/assets/stylesheets/admin/account.css.scss b/app/assets/stylesheets/admin/account.css.scss deleted file mode 100644 index 9cba996673..0000000000 --- a/app/assets/stylesheets/admin/account.css.scss +++ /dev/null @@ -1,19 +0,0 @@ -@import "variables"; - -.row.invoice_title { - margin-bottom: 0px; -} - -table.invoice_summary { - margin-bottom: 70px; - - tr.total { - font-weight: bold; - } -} - -.invoice_title { - .balance { - color: $spree-green; - } -} diff --git a/app/controllers/admin/account_controller.rb b/app/controllers/admin/account_controller.rb deleted file mode 100644 index 00531cd426..0000000000 --- a/app/controllers/admin/account_controller.rb +++ /dev/null @@ -1,6 +0,0 @@ -class Admin::AccountController < Spree::Admin::BaseController - - def show - @invoices = spree_current_user.account_invoices - end -end diff --git a/app/controllers/admin/accounts_and_billing_settings_controller.rb b/app/controllers/admin/accounts_and_billing_settings_controller.rb deleted file mode 100644 index 20a6f047c3..0000000000 --- a/app/controllers/admin/accounts_and_billing_settings_controller.rb +++ /dev/null @@ -1,74 +0,0 @@ -require 'open_food_network/accounts_and_billing_settings_validator' - -class Admin::AccountsAndBillingSettingsController < Spree::Admin::BaseController - before_filter :load_distributors, only: [:edit, :update, :start_job] - before_filter :load_jobs, only: [:edit, :update, :start_job] - before_filter :load_settings, only: [:edit, :update, :start_job] - before_filter :require_valid_settings, only: [:update, :start_job] - before_filter :require_known_job, only: [:start_job] - - def update - Spree::Config.set(params[:settings]) - flash[:success] = t(:successfully_updated, :resource => t(:billing_and_account_settings)) - redirect_to_edit - end - - def start_job - if @update_account_invoices_job || @finalize_account_invoices_job - flash[:error] = I18n.t(:accounts_and_billing_task_already_running_error) - else - new_job = "#{params[:job][:name]}".camelize.constantize.new - Delayed::Job.enqueue new_job - flash[:success] = I18n.t(:accounts_and_billing_start_task_notice) - end - - redirect_to_edit - end - - def show_methods - @enterprise = Enterprise.find_by_id(params[:enterprise_id]) - @shipping_methods = @enterprise.shipping_methods - @payment_methods = @enterprise.payment_methods - render partial: 'method_settings' - end - - private - - def redirect_to_edit - redirect_to main_app.edit_admin_accounts_and_billing_settings_path - end - - def require_valid_settings - render :edit unless @settings.valid? - end - - def known_jobs - ['update_account_invoices', 'finalize_account_invoices'] - end - - def require_known_job - unless known_jobs.include?(params[:job][:name]) - flash[:error] = "Unknown Task: #{params[:job][:name].to_s}" - redirect_to_edit - end - end - - def load_settings - @settings = OpenFoodNetwork::AccountsAndBillingSettingsValidator.new(params[:settings] || { - accounts_distributor_id: Spree::Config[:accounts_distributor_id], - default_accounts_payment_method_id: Spree::Config[:default_accounts_payment_method_id], - default_accounts_shipping_method_id: Spree::Config[:default_accounts_shipping_method_id], - auto_update_invoices: Spree::Config[:auto_update_invoices], - auto_finalize_invoices: Spree::Config[:auto_finalize_invoices] - }) - end - - def load_distributors - @distributors = Enterprise.is_distributor.select([:id, :name]) - end - - def load_jobs - @update_account_invoices_job = Delayed::Job.where("handler LIKE (?)", "%UpdateAccountInvoices%").last - @finalize_account_invoices_job = Delayed::Job.where("handler LIKE (?)", "%FinalizeAccountInvoices%").last - end -end diff --git a/app/controllers/admin/business_model_configuration_controller.rb b/app/controllers/admin/business_model_configuration_controller.rb deleted file mode 100644 index 1cf2ad088d..0000000000 --- a/app/controllers/admin/business_model_configuration_controller.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'open_food_network/business_model_configuration_validator' - -class Admin::BusinessModelConfigurationController < Spree::Admin::BaseController - before_filter :load_settings, only: [:edit, :update] - before_filter :require_valid_settings, only: [:update] - - def update - Spree::Config.set(params[:settings]) - flash[:success] = t(:successfully_updated, - resource: t('admin.business_model_configuration.edit.business_model_configuration')) - redirect_to_edit - end - - private - - def redirect_to_edit - redirect_to main_app.edit_admin_business_model_configuration_path - end - - def load_settings - @settings = OpenFoodNetwork::BusinessModelConfigurationValidator.new(params[:settings] || { - shop_trial_length_days: Spree::Config[:shop_trial_length_days], - account_invoices_monthly_fixed: Spree::Config[:account_invoices_monthly_fixed], - account_invoices_monthly_rate: Spree::Config[:account_invoices_monthly_rate], - account_invoices_monthly_cap: Spree::Config[:account_invoices_monthly_cap], - account_invoices_tax_rate: Spree::Config[:account_invoices_tax_rate], - minimum_billable_turnover: Spree::Config[:minimum_billable_turnover] - - }) - end - - def require_valid_settings - render :edit unless @settings.valid? - end -end diff --git a/app/controllers/admin/invoice_settings_controller.rb b/app/controllers/admin/invoice_settings_controller.rb deleted file mode 100644 index bfa22205ec..0000000000 --- a/app/controllers/admin/invoice_settings_controller.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Admin - class InvoiceSettingsController < Spree::Admin::BaseController - def update - Spree::Config.set(params[:preferences]) - - respond_to do |format| - format.html { - redirect_to main_app.edit_admin_invoice_settings_path - } - end - end - end -end diff --git a/app/controllers/api/customers_controller.rb b/app/controllers/api/customers_controller.rb index e983b372c9..cbbe4ce35f 100644 --- a/app/controllers/api/customers_controller.rb +++ b/app/controllers/api/customers_controller.rb @@ -1,7 +1,7 @@ module Api class CustomersController < BaseController def index - @customers = current_api_user.customers.of_regular_shops + @customers = current_api_user.customers render json: @customers, each_serializer: CustomerSerializer end diff --git a/app/controllers/spree/users_controller_decorator.rb b/app/controllers/spree/users_controller_decorator.rb index 53cfd09324..e8073e7bbb 100644 --- a/app/controllers/spree/users_controller_decorator.rb +++ b/app/controllers/spree/users_controller_decorator.rb @@ -10,10 +10,6 @@ Spree::UsersController.class_eval do def show @orders = @user.orders.where(state: 'complete').order('completed_at desc') @unconfirmed_email = spree_current_user.unconfirmed_email - - return unless Spree::Config.accounts_distributor_id - - @orders = @orders.where('distributor_id != ?', Spree::Config.accounts_distributor_id) end # Endpoint for queries to check if a user is already registered diff --git a/app/helpers/admin/account_helper.rb b/app/helpers/admin/account_helper.rb deleted file mode 100644 index 0292522b74..0000000000 --- a/app/helpers/admin/account_helper.rb +++ /dev/null @@ -1,14 +0,0 @@ -module Admin - module AccountHelper - def invoice_description_for(invoice) - month = t(:abbr_month_names, :scope => :date)[invoice.month] - year = invoice.year - star = invoice.order.nil? || invoice.order.completed? ? "" : "*" - "#{month} #{year}#{star}" - end - - def invoice_total_for(invoice) - invoice.order.andand.display_total || Spree::Money.new(0, { :currency => Spree::Config[:currency] }) - end - end -end diff --git a/app/helpers/admin/business_model_configuration_helper.rb b/app/helpers/admin/business_model_configuration_helper.rb deleted file mode 100644 index de17761924..0000000000 --- a/app/helpers/admin/business_model_configuration_helper.rb +++ /dev/null @@ -1,71 +0,0 @@ -module Admin - module BusinessModelConfigurationHelper - def monthly_bill_description - plus = monthly_bill_includes_fixed? && monthly_bill_includes_rate? ? " + " : "" - ts = trial_description.empty? ? "": " " - ms = minimum_description.empty? ? "": " " - - if fixed_description.empty? && rate_description.empty? - t(:free).upcase - elsif monthly_bill_includes_rate_limits? && monthly_bill_includes_rate? # only care about cap/min-bill-to if there is a rate too - "#{trial_description}#{ts}#{fixed_description}#{plus}#{rate_description}#{ms}#{minimum_description}#{cap_description} #{t(:per_month).upcase}#{tax_description.upcase}" - else - "#{trial_description}#{ts}#{fixed_description}#{plus}#{rate_description} #{t(:per_month).upcase}#{tax_description.upcase}" - end - end - - private - - def free_use? - Spree::Config[:account_invoices_monthly_fixed] == 0 && Spree::Config[:account_invoices_monthly_rate] == 0 - end - - def fixed_description - fixed_amount = Spree::Money.new(Spree::Config[:account_invoices_monthly_fixed], {currency: Spree::Config[:currency]} ).rounded - monthly_bill_includes_fixed? ? "#{fixed_amount}" : "" - end - - def rate_description - percentage = (Spree::Config[:account_invoices_monthly_rate]*100).round(2) - monthly_bill_includes_rate? ? t(:percentage_of_sales, percentage: "#{percentage}%").upcase : "" - end - - def cap_description - cap_amount = Spree::Money.new(Spree::Config[:account_invoices_monthly_cap], { currency: Spree::Config[:currency] }).rounded - monthly_bill_includes_cap? ? ", #{t(:capped_at_cap, cap: cap_amount).upcase}" : "" - end - - def tax_description - Spree::Config[:account_invoices_tax_rate] > 0 ? ", #{t(:plus_tax).upcase}" : "" - end - - def trial_description - Spree::Config[:shop_trial_length_days] > 0 ? "#{t(:free_trial).upcase} #{t(:then).upcase}" : "" - end - - def minimum_description - mbt_amount = Spree::Money.new(Spree::Config[:minimum_billable_turnover], { currency: Spree::Config[:currency] }).rounded - monthly_bill_includes_min_turnover? ? "#{t(:min_bill_turnover_desc, mbt_amount: mbt_amount).upcase}" : "" - end - - def monthly_bill_includes_fixed? - Spree::Config[:account_invoices_monthly_fixed] > 0 - end - - def monthly_bill_includes_rate? - Spree::Config[:account_invoices_monthly_rate] > 0 - end - - def monthly_bill_includes_cap? - Spree::Config[:account_invoices_monthly_cap] > 0 - end - - def monthly_bill_includes_min_turnover? - Spree::Config[:minimum_billable_turnover] > 1 - end - - def monthly_bill_includes_rate_limits? - monthly_bill_includes_min_turnover? || monthly_bill_includes_cap? - end - end -end diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb index 022a27c7a2..a8fcac65a6 100644 --- a/app/helpers/admin/injection_helper.rb +++ b/app/helpers/admin/injection_helper.rb @@ -1,7 +1,5 @@ module Admin module InjectionHelper - include BusinessModelConfigurationHelper - def admin_inject_enterprise admin_inject_json_ams "admin.enterprises", "enterprise", @enterprise, Api::Admin::EnterpriseSerializer end @@ -110,10 +108,6 @@ module Admin admin_inject_json_ams_array "admin.orders", "orderCycles", @order_cycles, Api::Admin::BasicOrderCycleSerializer, current_user: spree_current_user end - def admin_inject_monthly_bill_description - render partial: "admin/json/injection_ams", locals: {ngModule: "admin.enterprises", name: "monthlyBillDescription", json: monthly_bill_description.to_json} - end - def admin_inject_spree_api_key render partial: "admin/json/injection_ams", locals: {ngModule: 'admin.indexUtils', name: 'SpreeApiKey', json: "'#{@spree_api_key.to_s}'"} end diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 52d178cb74..aaec90f0b8 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -65,7 +65,7 @@ module InjectionHelper end def inject_shops - customers = spree_current_user.customers.of_regular_shops + customers = spree_current_user.customers shops = Enterprise.where(id: @orders.pluck(:distributor_id).uniq | customers.pluck(:enterprise_id)) inject_json_ams "shops", shops.all, Api::ShopForOrdersSerializer end diff --git a/app/jobs/finalize_account_invoices.rb b/app/jobs/finalize_account_invoices.rb deleted file mode 100644 index 99b2790810..0000000000 --- a/app/jobs/finalize_account_invoices.rb +++ /dev/null @@ -1,110 +0,0 @@ -# This class is part of the system that charges hubs for using OFN. It does so -# by creating orders. These are not orders for food that customers place, but -# instead are orders for OFN usage, so to speak. Thus, they're technically not -# "shipped" anywhere, so they're just given a default shipping method. -# -# The "orders" used by this class are not real orders, they are basically -# "invoices" that enterprise owners need to pay for use of an open food network -# instance. The amount that the enterprise owner is charged is configurable by -# the instance, and can be based on a combination of a percentage of their -# turnover, fixed fees, caps and floors and trial periods. This "orders" hold -# the billing information for a particular enterprise owner for a given month. -# -# We assign them also a default shipping method because there is a validation -# on Spree::Shipment that requires it. -class FinalizeAccountInvoices - attr_reader :year, :month, :start_date, :end_date - - def initialize(year = nil, month = nil) - ref_point = Time.zone.now - 1.month - @year = year || ref_point.year - @month = month || ref_point.month - @start_date = Time.zone.local(@year, @month) - @end_date = Time.zone.local(@year, @month) + 1.month - end - - def before(job) - UpdateBillablePeriods.new(year, month).perform - UpdateAccountInvoices.new(year, month).perform - end - - def perform - return unless settings_are_valid? - - - invoice_orders = AccountInvoice.where(year: year, month: month).map(&:order) - invoice_orders.select{ |order| order.present? && order.completed_at.nil? }.each{ |order| finalize(order) } - end - - def finalize(invoice_order) - # TODO: When we implement per-customer and/or per-user preferences around shipping and payment methods - # we can update these to read from those preferences - invoice_order.payments.create(payment_method_id: Spree::Config.default_accounts_payment_method_id, amount: invoice_order.total) - invoice_order.update_attribute(:shipping_method_id, Spree::Config.default_accounts_shipping_method_id) - while invoice_order.state != "complete" - if invoice_order.errors.any? - Bugsnag.notify(RuntimeError.new("FinalizeInvoiceError"), { - job: "FinalizeAccountInvoices", - error: "Cannot finalize invoice due to errors", - data: { - errors: invoice_order.errors.full_messages - } - }) - break - else - invoice_order.next - end - end - end - - private - - def settings_are_valid? - unless end_date <= Time.zone.now - Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { - job: "FinalizeAccountInvoices", - error: "end_date is in the future", - data: { - end_date: end_date.in_time_zone.strftime("%F %T"), - now: Time.zone.now.strftime("%F %T") - } - }) - return false - end - - unless @accounts_distributor = Enterprise.find_by_id(Spree::Config.accounts_distributor_id) - Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { - job: "FinalizeAccountInvoices", - error: "accounts_distributor_id is invalid", - data: { - accounts_distributor_id: Spree::Config.accounts_distributor_id - } - }) - return false - end - - unless @accounts_distributor.payment_methods.find_by_id(Spree::Config.default_accounts_payment_method_id) - Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { - job: "FinalizeAccountInvoices", - error: "default_accounts_payment_method_id is invalid", - data: { - default_accounts_payment_method_id: Spree::Config.default_accounts_payment_method_id - } - }) - return false - end - - unless @accounts_distributor.shipping_methods.find_by_id(Spree::Config.default_accounts_shipping_method_id) - Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { - job: "FinalizeAccountInvoices", - error: "default_accounts_shipping_method_id is invalid", - data: { - default_accounts_shipping_method_id: Spree::Config.default_accounts_shipping_method_id - } - }) - return false - end - - true - end -end diff --git a/app/jobs/update_account_invoices.rb b/app/jobs/update_account_invoices.rb deleted file mode 100644 index a2a726df3e..0000000000 --- a/app/jobs/update_account_invoices.rb +++ /dev/null @@ -1,108 +0,0 @@ -class UpdateAccountInvoices - attr_reader :year, :month, :start_date, :end_date - - def initialize(year = nil, month = nil) - ref_point = Time.zone.now - 1.day - @year = year || ref_point.year - @month = month || ref_point.month - @start_date = Time.zone.local(@year, @month) - @end_date = Time.zone.local(@year, @month) + 1.month - @end_date = Time.zone.now.beginning_of_day if start_date == Time.zone.now.beginning_of_month - end - - def before(job) - UpdateBillablePeriods.new(year, month).perform - end - - def perform - return unless settings_are_valid? - - account_invoices = AccountInvoice.where(year: year, month: month) - account_invoices.each { |account_invoice| update(account_invoice) } - end - - def update(account_invoice) - current_adjustments = [] - unless account_invoice.order - account_invoice.order = account_invoice.user.orders.new(distributor_id: Spree::Config[:accounts_distributor_id]) - end - - if account_invoice.order.complete? - Bugsnag.notify(RuntimeError.new("InvoiceAlreadyFinalized"), { - invoice_order: account_invoice.order.as_json - }) - else - billable_periods = account_invoice.billable_periods.order(:enterprise_id, :begins_at).reject{ |bp| bp.bill == 0 } - - if billable_periods.any? - oldest_enterprise = billable_periods.first.enterprise - address = oldest_enterprise.address.dup - first, _space, last = (oldest_enterprise.contact_name || "").partition(' ') - address.update_attributes(phone: oldest_enterprise.phone) if oldest_enterprise.phone.present? - address.update_attributes(firstname: first, lastname: last) if first.present? && last.present? - account_invoice.order.update_attributes(bill_address: address, ship_address: address) - end - - billable_periods.each do |billable_period| - current_adjustments << billable_period.ensure_correct_adjustment_for(account_invoice.order) - end - - account_invoice.save if current_adjustments.any? - - clean_up(account_invoice.order, current_adjustments) - end - end - - def clean_up(invoice_order, current_adjustments) - # Snag and then delete any obsolete adjustments - obsolete_adjustments = invoice_order.adjustments.where('source_type = (?) AND id NOT IN (?)', "BillablePeriod", current_adjustments) - - if obsolete_adjustments.any? - Bugsnag.notify(RuntimeError.new("Obsolete Adjustments"), { - current: current_adjustments.map(&:as_json), - obsolete: obsolete_adjustments.map(&:as_json) - }) - - obsolete_adjustments.destroy_all - end - - if current_adjustments.empty? - if invoice_order.persisted? - Bugsnag.notify(RuntimeError.new("Empty Persisted Invoice"), { - invoice_order: invoice_order.as_json - }) - else - invoice_order.destroy - end - end - end - - private - - def settings_are_valid? - unless end_date <= Time.zone.now - Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { - job: "UpdateAccountInvoices", - error: "end_date is in the future", - data: { - end_date: end_date.in_time_zone.strftime("%F %T"), - now: Time.zone.now.strftime("%F %T") - } - }) - return false - end - - unless Enterprise.find_by_id(Spree::Config.accounts_distributor_id) - Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { - job: "UpdateAccountInvoices", - error: "accounts_distributor_id is invalid", - data: { - accounts_distributor_id: Spree::Config.accounts_distributor_id - } - }) - return false - end - - true - end -end diff --git a/app/jobs/update_billable_periods.rb b/app/jobs/update_billable_periods.rb deleted file mode 100644 index 46e64b81ca..0000000000 --- a/app/jobs/update_billable_periods.rb +++ /dev/null @@ -1,130 +0,0 @@ -class UpdateBillablePeriods - attr_reader :year, :month, :start_date, :end_date - - def initialize(year = nil, month = nil) - ref_point = Time.zone.now - 1.day - @year = year || ref_point.year - @month = month || ref_point.month - @start_date = Time.zone.local(@year, @month) - @end_date = Time.zone.local(@year, @month) + 1.month - @end_date = Time.zone.now.beginning_of_day if start_date == Time.zone.now.beginning_of_month - end - - def perform - return unless settings_are_valid? - - job_start_time = Time.zone.now - - enterprises = Enterprise.where('created_at < (?)', end_date).select([:id, :name, :owner_id, :sells, :shop_trial_start_date, :created_at]) - - # Cycle through enterprises - enterprises.each do |enterprise| - start_for_enterprise = [start_date, enterprise.created_at].max - end_for_enterprise = [end_date].min # [end_date, enterprise.deleted_at].min - - # Cycle through previous versions of this enterprise - versions = enterprise.versions.where('created_at >= (?) AND created_at < (?)', start_for_enterprise, end_for_enterprise).order(:created_at) - - trial_start = enterprise.shop_trial_start_date - trial_expiry = enterprise.shop_trial_expiry - - versions.each do |version| - begins_at = version.previous.andand.created_at || start_for_enterprise - ends_at = version.created_at - - split_for_trial(version.reify, begins_at, ends_at, trial_start, trial_expiry) - end - - # Update / create billable_period for current start - begins_at = versions.last.andand.created_at || start_for_enterprise - ends_at = end_date - - split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) - - clean_up_untouched_billable_periods_for(enterprise, job_start_time) - end - end - - def split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) - trial_start = trial_expiry = begins_at-1.day if trial_start.nil? || trial_expiry.nil? - - # If the trial begins after ends_at, create a bill for the entire period - # Otherwise, create a normal billable_period from the begins_at until the start of the trial - if trial_start > begins_at - update_billable_period(enterprise, begins_at, [ends_at, trial_start].min, false) - end - - # If all or some of the trial occurs between begins_at and ends_at - # Create a trial billable_period from the from begins_at or trial_start, whichever occurs last, until ends_at, or trial_expiry whichever occurs first - if trial_expiry >= begins_at && trial_start <= ends_at - update_billable_period(enterprise, [trial_start, begins_at].max, [ends_at, trial_expiry].min, true) - end - - # If the trial finishes before begins_at, or trial has not been set, create a bill for the entire period - # Otherwise, create a normal billable_period from the end of the trial until ends_at - if trial_expiry < ends_at - update_billable_period(enterprise, [trial_expiry, begins_at].max, ends_at, false) - end - end - - def update_billable_period(enterprise, begins_at, ends_at, trial) - owner_id = enterprise.owner_id - sells = enterprise.sells - orders = Spree::Order.where('distributor_id = (?) AND completed_at >= (?) AND completed_at < (?)', enterprise.id, begins_at, ends_at) - account_invoice = AccountInvoice.find_or_create_by_user_id_and_year_and_month(owner_id, begins_at.year, begins_at.month) - - billable_period = BillablePeriod.where(account_invoice_id: account_invoice.id, begins_at: begins_at, enterprise_id: enterprise.id).first - - unless account_invoice.order.andand.complete? - billable_period ||= BillablePeriod.new(account_invoice_id: account_invoice.id, begins_at: begins_at, enterprise_id: enterprise.id) - billable_period.update_attributes({ - ends_at: ends_at, - sells: sells, - trial: trial, - owner_id: owner_id, - turnover: orders.sum(&:total) - }) - end - - billable_period.touch - end - - def clean_up_untouched_billable_periods_for(enterprise, job_start_time) - # Snag and then delete any BillablePeriods which overlap - obsolete_billable_periods = enterprise.billable_periods.where('ends_at > (?) AND begins_at < (?) AND billable_periods.updated_at < (?)', start_date, end_date, job_start_time) - - if obsolete_billable_periods.any? - current_billable_periods = enterprise.billable_periods.where('ends_at >= (?) AND begins_at <= (?) AND billable_periods.updated_at > (?)', start_date, end_date, job_start_time) - - Delayed::Worker.logger.info "#{enterprise.name} #{start_date.strftime("%F %T")} #{job_start_time.strftime("%F %T")}" - Delayed::Worker.logger.info "#{obsolete_billable_periods.first.updated_at.strftime("%F %T")}" - - Bugsnag.notify(RuntimeError.new("Obsolete BillablePeriods"), { - current: current_billable_periods.map(&:as_json), - obsolete: obsolete_billable_periods.map(&:as_json) - }) - end - - obsolete_billable_periods.includes({ account_invoice: :order}). - where('spree_orders.state <> \'complete\' OR account_invoices.order_id IS NULL'). - each(&:delete) - end - - private - - def settings_are_valid? - unless end_date <= Time.zone.now - Bugsnag.notify(RuntimeError.new("InvalidJobSettings"), { - job: "UpdateBillablePeriods", - error: "end_date is in the future", - data: { - end_date: end_date.in_time_zone.strftime("%F %T"), - now: Time.zone.now.strftime("%F %T") - } - }) - return false - end - - true - end -end diff --git a/app/models/account_invoice.rb b/app/models/account_invoice.rb deleted file mode 100644 index 6d381565bb..0000000000 --- a/app/models/account_invoice.rb +++ /dev/null @@ -1,6 +0,0 @@ -class AccountInvoice < ActiveRecord::Base - belongs_to :user, class_name: "Spree::User" - belongs_to :order, class_name: "Spree::Order" - attr_accessible :user_id, :order_id, :issued_at, :month, :year - has_many :billable_periods -end diff --git a/app/models/billable_period.rb b/app/models/billable_period.rb deleted file mode 100644 index d2ca41b0a9..0000000000 --- a/app/models/billable_period.rb +++ /dev/null @@ -1,77 +0,0 @@ -require 'open_food_network/bill_calculator' - -class BillablePeriod < ActiveRecord::Base - belongs_to :enterprise - belongs_to :owner, class_name: 'Spree::User' - belongs_to :account_invoice - has_one :adjustment, :as => :source, class_name: "Spree::Adjustment" #, :dependent => :destroy - - default_scope where(deleted_at: nil) - - def display_turnover - Spree::Money.new(turnover, {currency: Spree::Config[:currency]}) - end - - def display_bill - Spree::Money.new(bill, {currency: Spree::Config[:currency]}) - end - - def bill - return 0 if trial? - return 0 unless ['own', 'any'].include?(sells) - OpenFoodNetwork::BillCalculator.new(turnover: turnover).bill - end - - def label - enterprise_version = enterprise.version_at(begins_at) - category = enterprise_version.category.to_s.titleize - category += (trial ? " #{I18n.t(:trial)}" : "") - - "#{enterprise_version.name} (#{category})" - end - - def adjustment_label - begins = begins_at.in_time_zone.strftime("%d/%m/%y") - ends = ends_at.in_time_zone.strftime("%d/%m/%y") - - "#{label} [#{begins} - #{ends}]" - end - - def delete - self.update_column(:deleted_at, Time.zone.now) - end - - def ensure_correct_adjustment_for(invoice) - if adjustment - # adjustment.originator = enterprise.package - adjustment.adjustable = invoice - adjustment.update_attributes( label: adjustment_label, amount: bill ) - else - self.adjustment = invoice.adjustments.new( adjustment_attrs, :without_protection => true ) - end - - if Spree::Config.account_invoices_tax_rate > 0 - adjustment.set_included_tax! Spree::Config.account_invoices_tax_rate - else - adjustment.set_included_tax! 0 - end - - adjustment - end - - private - - def adjustment_attrs - # We should ultimately have an EnterprisePackage model, which holds all info about shop type, producer, trials, etc. - # It should also implement a calculator that we can use here by specifying the package as the originator of the - # adjustment, meaning that adjustments are created and updated using Spree's existing architecture. - - { label: adjustment_label, - amount: bill, - source: self, - originator: nil, # enterprise.package - mandatory: true, - state: 'closed' - } - end -end diff --git a/app/models/customer.rb b/app/models/customer.rb index 0ec6f52262..dfddd90c80 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -23,11 +23,6 @@ class Customer < ActiveRecord::Base scope :of, ->(enterprise) { where(enterprise_id: enterprise) } - scope :of_regular_shops, lambda { - next scoped unless Spree::Config.accounts_distributor_id - where('enterprise_id <> ?', Spree::Config.accounts_distributor_id) - } - before_create :associate_user private diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index ea1724f20a..26f45ad503 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -37,7 +37,6 @@ class Enterprise < ActiveRecord::Base has_many :distributor_shipping_methods, foreign_key: :distributor_id has_many :shipping_methods, through: :distributor_shipping_methods has_many :customers - has_many :billable_periods has_many :inventory_items has_many :tag_rules has_one :stripe_account, dependent: :destroy diff --git a/app/models/spree/adjustment_decorator.rb b/app/models/spree/adjustment_decorator.rb index e7ffcf41f0..13cf2827da 100644 --- a/app/models/spree/adjustment_decorator.rb +++ b/app/models/spree/adjustment_decorator.rb @@ -11,7 +11,6 @@ module Spree belongs_to :tax_rate, foreign_key: 'originator_id', conditions: "spree_adjustments.originator_type = 'Spree::TaxRate'" scope :enterprise_fee, where(originator_type: 'EnterpriseFee') - scope :billable_period, where(source_type: 'BillablePeriod') scope :admin, where(source_type: nil, originator_type: nil) scope :included_tax, where(originator_type: 'Spree::TaxRate', adjustable_type: 'Spree::LineItem') diff --git a/app/models/spree/app_configuration_decorator.rb b/app/models/spree/app_configuration_decorator.rb index 992c39abe1..af74d91ae7 100644 --- a/app/models/spree/app_configuration_decorator.rb +++ b/app/models/spree/app_configuration_decorator.rb @@ -20,21 +20,6 @@ Spree::AppConfiguration.class_eval do preference :products_require_tax_category, :boolean, default: false preference :shipping_tax_rate, :decimal, default: 0 - # Accounts & Billing Preferences - preference :accounts_distributor_id, :integer, default: nil - preference :default_accounts_payment_method_id, :integer, default: nil - preference :default_accounts_shipping_method_id, :integer, default: nil - preference :auto_update_invoices, :boolean, default: false - preference :auto_finalize_invoices, :boolean, default: false - - # Business Model Configuration - preference :account_invoices_monthly_fixed, :decimal, default: 0 - preference :account_invoices_monthly_rate, :decimal, default: 0 - preference :account_invoices_monthly_cap, :decimal, default: 0 - preference :account_invoices_tax_rate, :decimal, default: 0 - preference :shop_trial_length_days, :integer, default: 30 - preference :minimum_billable_turnover, :integer, default: 0 - # Monitoring preference :last_job_queue_heartbeat_at, :string, default: nil diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 76b295e794..16c71cf036 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -309,14 +309,9 @@ Spree::Order.class_eval do not line_items.with_tax.empty? end - def account_invoice? - distributor_id == Spree::Config.accounts_distributor_id - end - # Overrride of Spree method, that allows us to send separate confirmation emails to user and shop owners - # And separately, to skip sending confirmation email completely for user invoice orders def deliver_order_confirmation_email - unless account_invoice? || subscription.present? + unless subscription.present? Delayed::Job.enqueue ConfirmOrderJob.new(id) end end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index dec0309d2b..6c6fff3965 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -7,8 +7,6 @@ Spree.user_class.class_eval do has_many :enterprises, through: :enterprise_roles has_many :owned_enterprises, class_name: 'Enterprise', foreign_key: :owner_id, inverse_of: :owner has_many :owned_groups, class_name: 'EnterpriseGroup', foreign_key: :owner_id, inverse_of: :owner - has_many :account_invoices - has_many :billable_periods, foreign_key: :owner_id, inverse_of: :owner has_many :customers has_many :credit_cards diff --git a/app/overrides/spree/admin/shared/_configuration_menu/add_accounts_and_billing.html.haml.deface b/app/overrides/spree/admin/shared/_configuration_menu/add_accounts_and_billing.html.haml.deface deleted file mode 100644 index eb6ab7c01b..0000000000 --- a/app/overrides/spree/admin/shared/_configuration_menu/add_accounts_and_billing.html.haml.deface +++ /dev/null @@ -1,4 +0,0 @@ -// insert_bottom "[data-hook='admin_configurations_sidebar_menu']" - -%li - = link_to t('admin.accounts_and_billing_settings.edit.accounts_and_billing'), main_app.edit_admin_accounts_and_billing_settings_path diff --git a/app/overrides/spree/admin/shared/_configuration_menu/add_business_model_configuration.html.haml.deface b/app/overrides/spree/admin/shared/_configuration_menu/add_business_model_configuration.html.haml.deface deleted file mode 100644 index 32d0edfda1..0000000000 --- a/app/overrides/spree/admin/shared/_configuration_menu/add_business_model_configuration.html.haml.deface +++ /dev/null @@ -1,4 +0,0 @@ -// insert_bottom "[data-hook='admin_configurations_sidebar_menu']" - -%li - = link_to t('admin.business_model_configuration.edit.business_model_configuration'), main_app.edit_admin_business_model_configuration_path diff --git a/app/views/admin/account/show.html.haml b/app/views/admin/account/show.html.haml deleted file mode 100644 index e46fb9623f..0000000000 --- a/app/views/admin/account/show.html.haml +++ /dev/null @@ -1,47 +0,0 @@ -- content_for :page_title do - = t(:account) - -- if @invoices.empty? - %h4= t(:no_invoices_to_display) - -- @invoices.order('year DESC, month DESC').each do |invoice| - .row.invoice_title - .two.columns.alpha - %h4= invoice_description_for(invoice) - .two.columns.text-right - %h5 - - if invoice.order.andand.complete? - %a{ href: print_admin_order_url(invoice.order), :target => "_blank"} - %i.icon-print - = t(:print) - - else -   - .ten.columns -   - .two.columns.omega.text-right - %h4.balance= invoice_total_for(invoice) - %table.invoice_summary - %col{ width: '25%' } - %col{ width: '62.5%' } - %col{ width: '12.5%' } - %thead - %th= t('admin.date') - %th= t(:description) - %th= t(:charge) - - if order = invoice.order - - invoice.billable_periods.select{ |bp| bp.adjustment.andand.amount.andand > 0}.each do |billable_period| - %tr - %td.text-center= "#{billable_period.begins_at.strftime("%d/%m/%Y")}" - %td= billable_period.label - -# Using amount from the actual adjustment on the order here so that we avoid recalculating the bill - -# at a future date with different settings to those used at the time the invoice was finalized - %td.text-right= billable_period.adjustment.display_amount - - order.adjustments.where('source_type <> (?)', "BillablePeriod").reject{ |a| a.amount == 0 }.each do |adjustment| - %tr - %td.text-center   - %td= adjustment.label - %td.text-right= adjustment.display_amount - %tr.total - %td.text-center   - %td= t(:total).upcase - %td.text-right= invoice_total_for(invoice) diff --git a/app/views/admin/accounts_and_billing_settings/_method_settings.html.haml b/app/views/admin/accounts_and_billing_settings/_method_settings.html.haml deleted file mode 100644 index 32b1c0b9a5..0000000000 --- a/app/views/admin/accounts_and_billing_settings/_method_settings.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -.row - .six.columns.alpha - .field - = label :settings, :default_accounts_payment_method_id, t('.default_accounts_payment_method') - = collection_select(:settings, :default_accounts_payment_method_id, @payment_methods, :id, :name, { include_blank: true, selected: Spree::Config.default_accounts_payment_method_id}, { class: "select2 fullwidth" }) - - .six.columns.omega - .field - = label :settings, :default_accounts_shipping_method_id, t('.default_accounts_shipping_method') - = collection_select(:settings, :default_accounts_shipping_method_id, @shipping_methods, :id, :name, { include_blank: true, selected: Spree::Config.default_accounts_shipping_method_id}, { class: "select2 fullwidth" }) diff --git a/app/views/admin/accounts_and_billing_settings/edit.html.haml b/app/views/admin/accounts_and_billing_settings/edit.html.haml deleted file mode 100644 index e17ceec637..0000000000 --- a/app/views/admin/accounts_and_billing_settings/edit.html.haml +++ /dev/null @@ -1,94 +0,0 @@ -= render :partial => 'spree/admin/shared/configuration_menu' - -- content_for :page_title do - = t('.accounts_and_billing') - -= render 'spree/shared/error_messages', target: @settings - --# - month_options = (0...12).map { |i| Time.zone.now.beginning_of_month - i.months }.map{ |t| [t.strftime("%b %Y"), t.strftime("%b %Y %z")]} - -%fieldset.no-border-bottom - %legend - = t('.admin_settings') - = form_for @settings, as: :settings, url: main_app.admin_accounts_and_billing_settings_path, :method => :put do |f| - .row{ ng: { app: 'admin.accounts_and_billing_settings' } } - .twelve.columns.alpha.omega - .field - = f.label :accounts_distributor_id, t('.accounts_administration_distributor') - = f.collection_select(:accounts_distributor_id, @distributors, :id, :name, { include_blank: true }, { class: "select2 fullwidth", 'watch-value-as' => "enterprise_id"}) - - = f.hidden_field :default_accounts_payment_method_id, value: '' - = f.hidden_field :default_accounts_shipping_method_id, value: '' - %div{ 'method-settings-for' => 'enterprise_id' } - - .row - .six.columns.alpha - %fieldset.no-border-bottom - %legend - = t('.update_invoice') - = f.check_box :auto_update_invoices - = f.label :auto_update_invoices, t('.auto_update_invoices') - - .six.columns.omega - %fieldset.no-border-bottom - %legend - = t('.finalise_invoice') - = f.check_box :auto_finalize_invoices - = f.label :auto_finalize_invoices, t('.auto_finalise_invoices') - - .row - .twelve.columns.alpha.omega.form-buttons{"data-hook" => "buttons"} - = button t(:update), 'icon-refresh', value: "update" - -%fieldset.no-border-bottom - %legend - =t('.manually_run_task') - .row - .six.columns.alpha.step.text-center - .form-buttons{"data-hook" => "buttons"} - = link_to_with_icon "icon-undo", t('.update_user_invoices'), - main_app.start_job_admin_accounts_and_billing_settings_path(job: { name: "update_account_invoices" }), - class: "button fullwidth" - - %br - - - if @update_account_invoices_job - %p.text-center - - if @update_account_invoices_job.run_at < Time.zone.now - = t(:in_progress) - %br - = t(:started_at) - - else - %strong - = t(:queued) - %br - = t(:scheduled_for) - = @update_account_invoices_job.run_at - - else - %p.explanation - = t('.update_user_invoice_explained') - - .six.columns.omega.step.text-center - .form-buttons{"data-hook" => "buttons"} - =link_to_with_icon "icon-ok-sign", t('.finalise_user_invoices'), - main_app.start_job_admin_accounts_and_billing_settings_path(job: { name: "finalize_account_invoices" }), - class: "button fullwidth" - - %br - - - if @finalize_account_invoices_job - %p.text-center - - if @finalize_account_invoices_job.run_at < Time.zone.now - %strong - = t(:in_progress) - %br - = t(:started_at) - - else - %strong - = t(:queued) - %br - = t(:scheduled_for) - = @finalize_account_invoices_job.run_at - - else - %p.explanation - = t('.finalise_user_invoice_explained') diff --git a/app/views/admin/business_model_configuration/edit.html.haml b/app/views/admin/business_model_configuration/edit.html.haml deleted file mode 100644 index 29139d78bf..0000000000 --- a/app/views/admin/business_model_configuration/edit.html.haml +++ /dev/null @@ -1,104 +0,0 @@ -= render :partial => 'spree/admin/shared/configuration_menu' - -- content_for :app_wrapper_attrs do - = "ng-app='admin.businessModelConfiguration'" - -- content_for :page_title do - %h1.page-title= t('.business_model_configuration') - %a{ 'ofn-with-tip' => t('.business_model_configuration_tip') } - = t('admin.whats_this') - -= render 'spree/shared/error_messages', target: @settings - -.row{ ng: { controller: "BusinessModelConfigCtrl" } } - .five.columns.omega - %fieldset.no-border-bottom - %legend=t('.bill_calculation_settings') - %p - = t('.bill_calculation_settings_tip') - %br - = form_for @settings, as: :settings, url: main_app.admin_business_model_configuration_path, :method => :put do |f| - .row - .three.columns.alpha - = f.label :shop_trial_length_days, t('.shop_trial_length') - %span.icon-question-sign{'ofn-with-tip' => t('.shop_trial_length_tip')} - .two.columns.omega - = f.number_field :shop_trial_length_days, min: 0.0, step: 1.0, class: "fullwidth" - .row - .three.columns.alpha - = f.label :account_invoices_monthly_fixed, t('.fixed_monthly_charge') - %span.icon-question-sign{'ofn-with-tip' => t('.fixed_monthly_charge_tip')} - .two.columns.omega - .input-symbol.before - %span= Spree::Money.currency_symbol - = f.number_field :account_invoices_monthly_fixed, min: 0.0, class: "fullwidth", 'watch-value-as' => 'fixed' - .row - .three.columns.alpha - = f.label :account_invoices_monthly_rate, t('.percentage_of_turnover') - %span.icon-question-sign{'ofn-with-tip' => t('.percentage_of_turnover_tip')} - .two.columns.omega - = f.number_field :account_invoices_monthly_rate, min: 0.0, max: 1.0, step: 0.01, class: "fullwidth", 'watch-value-as' => 'rate' - .row - .three.columns.alpha - = f.label :account_invoices_monthly_cap, t('.monthly_cap_excl_tax') - %span.icon-question-sign{'ofn-with-tip' => t('.monthly_cap_excl_tax_tip')} - .two.columns.omega - .input-symbol.before - %span= Spree::Money.currency_symbol - = f.number_field :account_invoices_monthly_cap, min: 0.0, class: "fullwidth", 'watch-value-as' => 'cap' - .row - .three.columns.alpha - = f.label :account_invoices_tax_rate, t('.tax_rate') - %span.icon-question-sign{'ofn-with-tip' => t('.tax_rate_tip')} - .two.columns.omega - = f.number_field :account_invoices_tax_rate, min: 0.0, max: 1.0, step: 0.01, class: "fullwidth", 'watch-value-as' => 'taxRate' - .row - .three.columns.alpha - = f.label :minimum_billable_turnover, t('.minimum_monthly_billable_turnover') - %span.icon-question-sign{'ofn-with-tip' => t('.minimum_monthly_billable_turnover_tip')} - .two.columns.omega - .input-symbol.before - %span= Spree::Money.currency_symbol - = f.number_field :minimum_billable_turnover, min: 0, class: "fullwidth", 'watch-value-as' => 'minBillableTurnover' - - .row - .five.columns.alpha.omega.form-buttons{"data-hook" => "buttons"} - = button t(:update), 'icon-refresh', value: "update" - - .two.columns -   - - .five.columns.alpha - %fieldset.no-border-bottom - %legend= t('.example_bill_calculator') - %p - = t('.example_bill_calculator_legend') - %br - .row - .three.columns.alpha - = label_tag :turnover, t('.example_monthly_turnover') - %span.icon-question-sign{'ofn-with-tip' => t('.example_monthly_turnover_tip')} - .two.columns.omega - .input-symbol.before - %span= Spree::Money.currency_symbol - %input.fullwidth{ id: 'turnover', type: "number", ng: { model: 'turnover' } } - .row - .three.columns.alpha - = label_tag :cap_reached, t('.cap_reached?') - %span.icon-question-sign{'ofn-with-tip' => t('.cap_reached?_tip')} - .two.columns.omega - %input.fullwidth{ id: 'cap_reached', type: "text", readonly: true, ng: { value: 'capReached()' } } - .row - .three.columns.alpha - = label_tag :included_tax, t('.included_tax') - %span.icon-question-sign{'ofn-with-tip' => t('.included_tax_tip')} - .two.columns.omega - %span= Spree::Money.currency_symbol - %input.fullwidth{ id: 'included_tax', type: "text", readonly: true, ng: { value: 'includedTax()' } } - .row - .three.columns.alpha - = label_tag :total_incl_tax, t('.total_monthly_bill_incl_tax') - %span.icon-question-sign{'ofn-with-tip' => t('.total_monthly_bill_incl_tax_tip')} - .two.columns.omega - %span= Spree::Money.currency_symbol - %input.fullwidth{ id: 'total_incl_tax', type: "text", readonly: true, ng: { value: 'total()' } } diff --git a/app/views/admin/enterprises/_change_type_form.html.haml b/app/views/admin/enterprises/_change_type_form.html.haml index 28dac2e7b8..dfaebabf0d 100644 --- a/app/views/admin/enterprises/_change_type_form.html.haml +++ b/app/views/admin/enterprises/_change_type_form.html.haml @@ -1,5 +1,4 @@ = admin_inject_enterprise -= admin_inject_monthly_bill_description = form_for @enterprise, url: main_app.register_admin_enterprise_path(@enterprise), html: { name: "change_type", id: "change_type", novalidate: true, "ng-app" => "admin.enterprises", "ng-controller"=> 'changeTypeFormCtrl' } do |change_type_form| diff --git a/app/views/admin/enterprises/index.html.haml b/app/views/admin/enterprises/index.html.haml index d6e6d0fbb2..8ced7bd769 100644 --- a/app/views/admin/enterprises/index.html.haml +++ b/app/views/admin/enterprises/index.html.haml @@ -12,7 +12,6 @@ - modal_message = t('js.admin.enterprise_limit_reached', contact_email: ContentConfig.footer_email) = button_link_to t('.new_enterprise'), button_href, icon: 'icon-plus', id: 'admin_new_enterprise_link', 'enterprise-limit' => !spree_current_user.can_own_more_enterprises?, 'modal-message' => modal_message -= admin_inject_monthly_bill_description = admin_inject_column_preferences module: 'admin.enterprises', action: "enterprises_index" = render 'admin/shared/enterprises_sub_menu' diff --git a/config/initializers/user_class_extensions.rb b/config/initializers/user_class_extensions.rb deleted file mode 100644 index cd9ff60a0c..0000000000 --- a/config/initializers/user_class_extensions.rb +++ /dev/null @@ -1,11 +0,0 @@ -Spree::Core::Engine.config.to_prepare do - if Spree.user_class - Spree.user_class.class_eval do - - # Override of spree method to ignore orders associated with account_invoices - def last_incomplete_spree_order - spree_orders.incomplete.where("id NOT IN (?)", account_invoices.map(&:order_id)).order('created_at DESC').first - end - end - end -end diff --git a/config/locales/en.yml b/config/locales/en.yml index d7afd039af..b2be034d58 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -348,28 +348,6 @@ en: unsaved_confirm_leave: "There are unsaved changed on this page. Continue without saving?" unsaved_changes: "You have unsaved changes" - accounts_and_billing_settings: - method_settings: - default_accounts_payment_method: "Default Accounts Payment Method" - default_accounts_shipping_method: "Default Accounts Shipping Method" - edit: - accounts_and_billing: "Accounts & Billing" - accounts_administration_distributor: "Accounts Administration Distributor" - admin_settings: "Settings" - update_invoice: "Update Invoices" - auto_update_invoices: "Auto-update invoices nightly at 1:00am" - finalise_invoice: "Finalise Invoices" - auto_finalise_invoices: "Auto-finalise invoices monthly on the 2nd at 1:30am" - manually_run_task: "Manually Run Task " - update_user_invoice_explained: "Use this button to immediately update invoices for the month to date for each enterprise user in the system. This task can be set up to run automatically every night." - finalise_user_invoices: "Finalise User Invoices" - finalise_user_invoice_explained: "Use this button to finalize all invoices in the system for the previous calendar month. This task can be set up to run automatically once a month." - update_user_invoices: "Update User Invoices" - errors: - accounts_distributor: must be set if you wish to create invoices for enterprise users. - default_payment_method: must be set if you wish to create invoices for enterprise users. - default_shipping_method: must be set if you wish to create invoices for enterprise users. - shopfront_settings: embedded_shopfront_settings: "Embedded Shopfront Settings" enable_embedded_shopfronts: "Enable Embedded Shopfronts" @@ -379,35 +357,6 @@ en: number_localization_settings: "Number Localization Settings" enable_localized_number: "Use the international thousand/decimal separator logic" - business_model_configuration: - edit: - business_model_configuration: "Business Model" - business_model_configuration_tip: "Configure the rate at which shops will be charged each month for use of the Open Food Network." - bill_calculation_settings: "Bill Calculation Settings" - bill_calculation_settings_tip: "Adjust the amount that enterprises will be billed each month for use of the OFN." - shop_trial_length: "Shop Trial Length (Days)" - shop_trial_length_tip: "The length of time (in days) that enterprises who are set up as shops can run as a trial period." - fixed_monthly_charge: "Fixed Monthly Charge" - fixed_monthly_charge_tip: "A fixed monthly charge for all enterprises who are set up as a shop and have exceeded the minimum billable turnover (if set)." - percentage_of_turnover: "Percentage of turnover" - percentage_of_turnover_tip: "When greater than zero, this rate (0.0 - 1.0) will be applied to the total turnover of each shop and added to any fixed charges (to the left) to calculate the monthly bill." - monthly_cap_excl_tax: "monthly cap (excl. GST)" - monthly_cap_excl_tax_tip: "When greater than zero, this value will be used as a cap on the amount that shops will be charged each month." - tax_rate: "Tax Rate" - tax_rate_tip: "Tax rate that applies to the the monthly bill that enterprises are charged for using the system." - minimum_monthly_billable_turnover: "Minimum Monthly Billable Turnover" - minimum_monthly_billable_turnover_tip: "Minimum monthly turnover before a shopfront will be charged for using OFN. Enterprises turning over less than this amount in a month will not be charged, either as a percentage or fixed rate." - example_bill_calculator: "Example Bill Calculator" - example_bill_calculator_legend: "Alter the example turnover to visualise the effect of the settings to the left." - example_monthly_turnover: "Example Monthly Turnover" - example_monthly_turnover_tip: "An example monthly turnover for an enterprise which will be used to generate calculate an example monthly bill below." - cap_reached?: "Cap Reached ?" - cap_reached?_tip: "Whether the cap (specified to the left) has been reached, given the settings and the turnover provided." - included_tax: "Included tax" - included_tax_tip: "The total tax included in the example monthly bill, given the settings and the turnover provided." - total_monthly_bill_incl_tax: "Total Monthly Bill (Incl. Tax)" - total_monthly_bill_incl_tax_tip: "The example total monthly bill with tax included, given the settings and the turnover provided." - cache_settings: show: title: Caching @@ -2053,8 +2002,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using registration_detail_country_error: "Please select a country" # END shipping_method_destroy_error: "That shipping method cannot be deleted as it is referenced by an order: %{number}." - accounts_and_billing_task_already_running_error: "A task is already running, please wait until it has finished" - accounts_and_billing_start_task_notice: "Task Queued" fees: "Fees" item_cost: "Item cost" bulk: "Bulk" diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 1f55f8d154..1bd72d4fdd 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -70,19 +70,8 @@ Openfoodnetwork::Application.routes.draw do resource :content - resource :accounts_and_billing_settings, only: [:edit, :update] do - collection do - get :show_methods - get :start_job - end - end - - resource :business_model_configuration, only: [:edit, :update], controller: 'business_model_configuration' - resource :cache_settings - resource :account, only: [:show], controller: 'account' - resources :column_preferences, only: [], format: :json do put :bulk_update, on: :collection end diff --git a/config/schedule.rb b/config/schedule.rb index 4bb20df545..0e0ee31289 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -33,12 +33,3 @@ every 5.minutes do enqueue_job 'SubscriptionPlacementJob', priority: 0 enqueue_job 'SubscriptionConfirmJob', priority: 0 end - -every 1.day, at: '1:00am' do - rake 'ofn:billing:update_account_invoices' -end - -# On the 2nd of every month at 1:30am -every '30 1 2 * *' do - rake 'ofn:billing:finalize_account_invoices' -end diff --git a/knapsack_rspec_report.json b/knapsack_rspec_report.json index 12f72f882c..45e5e437a5 100644 --- a/knapsack_rspec_report.json +++ b/knapsack_rspec_report.json @@ -68,14 +68,12 @@ "spec/controllers/admin/enterprises_controller_spec.rb": 4.12069845199585, "spec/lib/open_food_network/scope_variant_to_hub_spec.rb": 3.415233612060547, "spec/controllers/shop_controller_spec.rb": 2.8152332305908203, - "spec/features/admin/accounts_and_billing_settings_spec.rb": 4.564471960067749, "spec/lib/open_food_network/user_balance_calculator_spec.rb": 5.701348066329956, "spec/features/consumer/groups_spec.rb": 4.695758104324341, "spec/lib/open_food_network/order_cycle_form_applicator_spec.rb": 5.4417150020599365, "spec/controllers/spree/api/products_controller_spec.rb": 9.90657091140747, "spec/serializers/admin/exchange_serializer_spec.rb": 2.6061851978302, "spec/controllers/api/promo_images_controller_spec.rb": 3.7358314990997314, - "spec/jobs/update_billable_periods_spec.rb": 7.687055826187134, "spec/controllers/enterprises_controller_spec.rb": 4.6496901512146, "spec/features/consumer/registration_spec.rb": 4.664679765701294, "spec/serializers/order_serializer_spec.rb": 6.241122722625732, @@ -97,14 +95,12 @@ "spec/models/enterprise_fee_spec.rb": 2.6969127655029297, "spec/features/consumer/shopping/embedded_groups_spec.rb": 3.5200917720794678, "spec/lib/open_food_network/scope_variants_to_search_spec.rb": 2.9324328899383545, - "spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb": 3.5419158935546875, "spec/models/spree/payment_spec.rb": 3.644035816192627, "spec/controllers/admin/schedules_controller_spec.rb": 4.086155414581299, "spec/controllers/spree/admin/reports_controller_spec.rb": 4.6371729373931885, "spec/helpers/enterprises_helper_spec.rb": 4.092283725738525, "spec/features/admin/schedules_spec.rb": 4.011422872543335, "spec/controllers/admin/order_cycles_controller_spec.rb": 3.8047308921813965, - "spec/models/billable_period_spec.rb": 4.33881139755249, "spec/features/admin/authentication_spec.rb": 2.6899383068084717, "spec/controllers/spree/admin/orders/customer_details_controller_spec.rb": 3.8118021488189697, "spec/models/enterprise_caching_spec.rb": 3.222259998321533, @@ -118,8 +114,6 @@ "spec/lib/open_food_network/subscription_payment_updater_spec.rb": 3.8399856090545654, "spec/features/consumer/footer_links_spec.rb": 1.9081592559814453, "spec/jobs/subscription_placement_job_spec.rb": 3.095796823501587, - "spec/helpers/admin/business_model_configuration_helper_spec.rb": 1.4220046997070312, - "spec/features/admin/business_model_configuration_spec.rb": 2.1453568935394287, "spec/controllers/admin/subscription_line_items_controller_spec.rb": 2.2608282566070557, "spec/controllers/cart_controller_spec.rb": 2.738463878631592, "spec/models/producer_property_spec.rb": 2.198972702026367, @@ -206,7 +200,6 @@ "spec/models/concerns/order_shipping_method_spec.rb": 0.29420924186706543, "spec/models/stripe_account_spec.rb": 0.23795485496520996, "spec/controllers/admin/stripe_connect_settings_controller_spec.rb": 0.2342853546142578, - "spec/controllers/admin/business_model_configuration_controller_spec.rb": 0.18909120559692383, "spec/serializers/api/admin/product_serializer_spec.rb": 0.2630436420440674, "spec/serializers/variant_serializer_spec.rb": 0.29114508628845215, "spec/controllers/stripe/callbacks_controller_spec.rb": 0.2801051139831543, @@ -230,7 +223,6 @@ "spec/controllers/stripe/webhooks_controller_spec.rb": 0.15674114227294922, "spec/models/model_set_spec.rb": 0.17897748947143555, "spec/models/spree/credit_card_spec.rb": 0.11442422866821289, - "spec/features/admin/account_spec.rb": 0.15611958503723145, "spec/features/consumer/confirm_invitation_spec.rb": 0.1410982608795166, "spec/lib/open_food_network/sales_tax_report_spec.rb": 0.10101437568664551, "spec/models/coordinator_fee_spec.rb": 0.11246252059936523, diff --git a/lib/open_food_network/accounts_and_billing_settings_validator.rb b/lib/open_food_network/accounts_and_billing_settings_validator.rb deleted file mode 100644 index ec1de0b6b7..0000000000 --- a/lib/open_food_network/accounts_and_billing_settings_validator.rb +++ /dev/null @@ -1,41 +0,0 @@ -# This class is a lightweight model used to validate preferences for accounts and billing settings -# when they are submitted to the AccountsAndBillingSettingsController - -module OpenFoodNetwork - class AccountsAndBillingSettingsValidator - include ActiveModel::Validations - - attr_accessor :accounts_distributor_id, :default_accounts_payment_method_id, :default_accounts_shipping_method_id - attr_accessor :auto_update_invoices, :auto_finalize_invoices - - validate :ensure_accounts_distributor_set - validate :ensure_default_payment_method_set - validate :ensure_default_shipping_method_set - # validate :ensure_billing_info_collected, unless: lambda { create_invoices_for_enterprise_users == '0' } - - def initialize(attr, button=nil) - attr.each { |k,v| instance_variable_set("@#{k}", v) } - @button = button - end - - def ensure_accounts_distributor_set - unless Enterprise.find_by_id(accounts_distributor_id) - errors.add(:accounts_distributor, I18n.t('admin.accounts_and_billing_settings.errors.accounts_distributor')) - end - end - - def ensure_default_payment_method_set - unless Enterprise.find_by_id(accounts_distributor_id) && - Enterprise.find_by_id(accounts_distributor_id).payment_methods.find_by_id(default_accounts_payment_method_id) - errors.add(:default_payment_method, I18n.t('admin.accounts_and_billing_settings.errors.default_payment_method')) - end - end - - def ensure_default_shipping_method_set - unless Enterprise.find_by_id(accounts_distributor_id) && - Enterprise.find_by_id(accounts_distributor_id).shipping_methods.find_by_id(default_accounts_shipping_method_id) - errors.add(:default_shipping_method, I18n.t('admin.accounts_and_billing_settings.errors.default_shipping_method')) - end - end - end -end diff --git a/lib/open_food_network/bill_calculator.rb b/lib/open_food_network/bill_calculator.rb deleted file mode 100644 index 90f5077a14..0000000000 --- a/lib/open_food_network/bill_calculator.rb +++ /dev/null @@ -1,21 +0,0 @@ -module OpenFoodNetwork - class BillCalculator - attr_accessor :turnover, :fixed, :rate, :cap, :tax_rate, :minimum_billable_turnover - - def initialize(opts={}) - @turnover = opts[:turnover] || 0 - @fixed = opts[:fixed] || Spree::Config[:account_invoices_monthly_fixed] - @rate = opts[:rate] || Spree::Config[:account_invoices_monthly_rate] - @cap = opts[:cap] || Spree::Config[:account_invoices_monthly_cap] - @tax_rate = opts[:tax_rate] || Spree::Config[:account_invoices_tax_rate] - @minimum_billable_turnover = opts[:minimum_billable_turnover] || Spree::Config[:minimum_billable_turnover] - end - - def bill - return 0 if turnover < minimum_billable_turnover - bill = fixed + (turnover * rate) - bill = [bill, cap].min if cap > 0 - bill * (1 + tax_rate) - end - end -end diff --git a/lib/open_food_network/business_model_configuration_validator.rb b/lib/open_food_network/business_model_configuration_validator.rb deleted file mode 100644 index 93534937db..0000000000 --- a/lib/open_food_network/business_model_configuration_validator.rb +++ /dev/null @@ -1,22 +0,0 @@ -# This class is a lightweight model used to validate preferences for business model configuration -# when they are submitted to the BusinessModelConfigurationController - -module OpenFoodNetwork - class BusinessModelConfigurationValidator - include ActiveModel::Validations - - attr_accessor :shop_trial_length_days, :account_invoices_monthly_fixed, :account_invoices_monthly_rate, :account_invoices_monthly_cap, :account_invoices_tax_rate, :minimum_billable_turnover - - validates :shop_trial_length_days, presence: true, numericality: { greater_than_or_equal_to: 0 } - validates :account_invoices_monthly_fixed, presence: true, numericality: { greater_than_or_equal_to: 0 } - validates :account_invoices_monthly_rate, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 } - validates :account_invoices_monthly_cap, presence: true, numericality: { greater_than_or_equal_to: 0 } - validates :account_invoices_tax_rate, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 } - validates :minimum_billable_turnover, presence: true, numericality: { greater_than_or_equal_to: 0 } - - def initialize(attr, button=nil) - attr.each { |k,v| instance_variable_set("@#{k}", v) } - @button = button - end - end -end diff --git a/lib/open_food_network/xero_invoices_report.rb b/lib/open_food_network/xero_invoices_report.rb index 5e351871e8..b27291f535 100644 --- a/lib/open_food_network/xero_invoices_report.rb +++ b/lib/open_food_network/xero_invoices_report.rb @@ -89,7 +89,7 @@ module OpenFoodNetwork rows = [] rows += produce_summary_rows(order, invoice_number, opts) unless detail? - rows += fee_summary_rows(order, invoice_number, opts) unless detail? && order.account_invoice? + rows += fee_summary_rows(order, invoice_number, opts) rows += shipping_summary_rows(order, invoice_number, opts) rows += payment_summary_rows(order, invoice_number, opts) rows += admin_adjustment_summary_rows(order, invoice_number, opts) unless detail? @@ -158,18 +158,11 @@ module OpenFoodNetwork end def adjustments(order) - account_invoice_adjustments(order) + order.adjustments.admin - end - - def account_invoice_adjustments(order) - order.adjustments. - billable_period. - select { |a| a.source.present? } + order.adjustments.admin end def adjustment_order(adjustment) - adjustment.source.andand.account_invoice.andand.order || - (adjustment.adjustable.is_a?(Spree::Order) ? adjustment.adjustable : nil) + adjustment.adjustable.is_a?(Spree::Order) ? adjustment.adjustable : nil end def invoice_number_for(order, i) diff --git a/lib/tasks/billing.rake b/lib/tasks/billing.rake deleted file mode 100644 index 4c279bc41e..0000000000 --- a/lib/tasks/billing.rake +++ /dev/null @@ -1,13 +0,0 @@ -namespace :ofn do - namespace :billing do - desc 'Update enterprise user invoices' - task update_account_invoices: :environment do - Delayed::Job.enqueue(UpdateAccountInvoices.new) if Spree::Config[:auto_update_invoices] - end - - desc 'Finalize enterprise user invoices' - task finalize_account_invoices: :environment do - Delayed::Job.enqueue(FinalizeAccountInvoices.new) if Spree::Config[:auto_finalize_invoices] - end - end -end diff --git a/spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb b/spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb deleted file mode 100644 index 7ec9eb1ca0..0000000000 --- a/spec/controllers/admin/accounts_and_billing_settings_controller_spec.rb +++ /dev/null @@ -1,241 +0,0 @@ -require 'spec_helper' - -describe Admin::AccountsAndBillingSettingsController, type: :controller do - let!(:pm1) { create(:payment_method) } - let!(:sm1) { create(:shipping_method) } - let!(:pm2) { create(:payment_method) } - let!(:sm2) { create(:shipping_method) } - let!(:accounts_distributor) { create(:distributor_enterprise, payment_methods: [pm1], shipping_methods: [sm1]) } - let!(:new_distributor) { create(:distributor_enterprise, payment_methods: [pm2], shipping_methods: [sm2]) } - let(:user) { create(:user) } - let(:admin) { create(:admin_user) } - - before do - Spree::Config.set({ - accounts_distributor_id: accounts_distributor.id, - default_accounts_payment_method_id: pm1.id, - default_accounts_shipping_method_id: sm1.id, - auto_update_invoices: true, - auto_finalize_invoices: false - }) - end - - describe "edit" do - context "as an enterprise user" do - before { allow(controller).to receive(:spree_current_user) { user } } - - it "does not allow access" do - spree_get :edit - expect(response).to redirect_to spree.unauthorized_path - end - end - - context "as super admin" do - before { allow(controller).to receive(:spree_current_user) { admin } } - - it "loads relevant global settings into a locally dummy class" do - spree_get :edit - settings = assigns(:settings) - - expect(settings.accounts_distributor_id).to eq accounts_distributor.id - expect(settings.default_accounts_payment_method_id).to eq pm1.id - expect(settings.default_accounts_shipping_method_id).to eq sm1.id - expect(settings.auto_update_invoices).to eq true - expect(settings.auto_finalize_invoices).to eq false - end - end - end - - describe "update" do - context "as an enterprise user" do - before { allow(controller).to receive(:spree_current_user) { user } } - - it "does not allow access" do - spree_get :update - expect(response).to redirect_to spree.unauthorized_path - end - end - - context "as super admin" do - before {allow(controller).to receive(:spree_current_user) { admin } } - let(:params) { { settings: { } } } - - context "when required settings have no values" do - before do - params[:settings][:accounts_distributor_id] = '' - params[:settings][:default_accounts_payment_method_id] = '0' - params[:settings][:default_accounts_shipping_method_id] = '0' - params[:settings][:auto_update_invoices] = '0' - params[:settings][:auto_finalize_invoices] = '0' - spree_get :update, params - end - - it "does not allow them to be empty/false" do - expect(response).to render_template :edit - expect(assigns(:settings).errors.count).to be 3 - expect(Spree::Config.accounts_distributor_id).to eq accounts_distributor.id - expect(Spree::Config.default_accounts_payment_method_id).to eq pm1.id - expect(Spree::Config.default_accounts_shipping_method_id).to eq sm1.id - expect(Spree::Config.auto_update_invoices).to be true - expect(Spree::Config.auto_finalize_invoices).to be false - end - end - - context "when required settings have values" do - before do - params[:settings][:accounts_distributor_id] = new_distributor.id - params[:settings][:default_accounts_payment_method_id] = pm2.id - params[:settings][:default_accounts_shipping_method_id] = sm2.id - params[:settings][:auto_update_invoices] = '0' - params[:settings][:auto_finalize_invoices] = '0' - end - - it "sets global config to the specified values" do - spree_get :update, params - expect(Spree::Config.accounts_distributor_id).to eq new_distributor.id - expect(Spree::Config.default_accounts_payment_method_id).to eq pm2.id - expect(Spree::Config.default_accounts_shipping_method_id).to eq sm2.id - expect(Spree::Config.auto_update_invoices).to be false - expect(Spree::Config.auto_finalize_invoices).to be false - end - end - end - end - - describe "start_job" do - context "as an enterprise user" do - before do - allow(controller).to receive(:spree_current_user) { user } - spree_post :start_job, enterprise_id: accounts_distributor.id - end - - it "does not allow access" do - expect(response).to redirect_to spree.unauthorized_path - end - end - - context "as super admin" do - before do - allow(controller).to receive(:spree_current_user) { admin } - end - - context "when settings are not valid" do - before do - Spree::Config.set({ accounts_distributor_id: "" }) - Spree::Config.set({ default_accounts_payment_method_id: "" }) - Spree::Config.set({ default_accounts_shipping_method_id: "" }) - spree_post :start_job, job: { name: "" } - end - - it "returns immediately and renders :edit" do - expect(assigns(:settings).errors.count).to eq 3 - expect(response).to render_template :edit - end - end - - context "when settings are valid" do - before do - Spree::Config.set({ accounts_distributor_id: accounts_distributor.id }) - Spree::Config.set({ default_accounts_payment_method_id: pm1.id }) - Spree::Config.set({ default_accounts_shipping_method_id: sm1.id }) - end - - context "and job_name is not on the known_jobs list" do - before do - spree_post :start_job, job: { name: "" } - end - - it "returns immediately with an error" do - expect(flash[:error]).to eq "Unknown Task: " - expect(response).to redirect_to edit_admin_accounts_and_billing_settings_path - end - end - - context "and job_name is update_account_invoices" do - let!(:params) { { job: { name: "update_account_invoices" } } } - - context "and no jobs are currently running" do - before do - allow(controller).to receive(:load_jobs) - end - - it "runs the job" do - expect{spree_post :start_job, params}.to enqueue_job UpdateAccountInvoices - expect(flash[:success]).to eq "Task Queued" - expect(response).to redirect_to edit_admin_accounts_and_billing_settings_path - end - end - - context "and there are jobs currently running" do - before do - allow(controller).to receive(:load_jobs) - controller.instance_variable_set("@update_account_invoices_job", double(:update_account_invoices_job)) - end - - it "does not run the job" do - expect{spree_post :start_job, params}.to_not enqueue_job UpdateAccountInvoices - expect(flash[:error]).to eq "A task is already running, please wait until it has finished" - expect(response).to redirect_to edit_admin_accounts_and_billing_settings_path - end - end - end - - context "and job_name is finalize_account_invoices" do - let!(:params) { { job: { name: "finalize_account_invoices" } } } - - context "and no jobs are currently running" do - before do - allow(controller).to receive(:load_jobs) - end - - it "runs the job" do - expect{spree_post :start_job, params}.to enqueue_job FinalizeAccountInvoices - expect(flash[:success]).to eq "Task Queued" - expect(response).to redirect_to edit_admin_accounts_and_billing_settings_path - end - end - - context "and there are jobs currently running" do - before do - allow(controller).to receive(:load_jobs) - controller.instance_variable_set("@finalize_account_invoices_job", double(:finalize_account_invoices_job)) - end - - it "does not run the job" do - expect{spree_post :start_job, params}.to_not enqueue_job FinalizeAccountInvoices - expect(flash[:error]).to eq "A task is already running, please wait until it has finished" - expect(response).to redirect_to edit_admin_accounts_and_billing_settings_path - end - end - end - end - end - end - - describe "show_methods" do - context "as an enterprise user" do - before do - allow(controller).to receive(:spree_current_user) { user } - spree_get :show_methods, enterprise_id: accounts_distributor.id - end - - it "does not allow access" do - expect(response).to redirect_to spree.unauthorized_path - end - end - - context "as super admin" do - before do - allow(controller).to receive(:spree_current_user) { admin } - spree_get :show_methods, enterprise_id: accounts_distributor.id - end - - it "renders the method_settings template" do - expect(assigns(:payment_methods)).to eq [pm1] - expect(assigns(:shipping_methods)).to eq [sm1] - expect(assigns(:enterprise)).to eq accounts_distributor - expect(response).to render_template :method_settings - end - end - end -end diff --git a/spec/controllers/admin/business_model_configuration_controller_spec.rb b/spec/controllers/admin/business_model_configuration_controller_spec.rb deleted file mode 100644 index 3836773b08..0000000000 --- a/spec/controllers/admin/business_model_configuration_controller_spec.rb +++ /dev/null @@ -1,98 +0,0 @@ -require 'spec_helper' - -describe Admin::BusinessModelConfigurationController, type: :controller do - let(:user) { create(:user) } - let(:admin) { create(:admin_user) } - - before do - Spree::Config.set({ - account_invoices_monthly_fixed: 5, - account_invoices_monthly_rate: 0.02, - account_invoices_monthly_cap: 50, - account_invoices_tax_rate: 0.1, - shop_trial_length_days: 30, - minimum_billable_turnover: 0 - }) - end - - describe "edit" do - context "as an enterprise user" do - before { allow(controller).to receive(:spree_current_user) { user } } - - it "does not allow access" do - spree_get :edit - expect(response).to redirect_to spree.unauthorized_path - end - end - - context "as super admin" do - before { allow(controller).to receive(:spree_current_user) { admin } } - - it "allows access" do - spree_get :edit - expect(response).to_not redirect_to spree.unauthorized_path - end - end - end - - describe "update" do - context "as an enterprise user" do - before { allow(controller).to receive(:spree_current_user) { user } } - - it "does not allow access" do - spree_get :update - expect(response).to redirect_to spree.unauthorized_path - end - end - - context "as super admin" do - before {allow(controller).to receive(:spree_current_user) { admin } } - let(:params) { { settings: { } } } - - context "when settings are invalid" do - before do - params[:settings][:account_invoices_monthly_fixed] = '' - params[:settings][:account_invoices_monthly_rate] = '2' - params[:settings][:account_invoices_monthly_cap] = '-1' - params[:settings][:account_invoices_tax_rate] = '4' - params[:settings][:shop_trial_length_days] = '-30' - params[:settings][:minimum_billable_turnover] = '-1' - spree_get :update, params - end - - it "does not allow them to be set" do - expect(response).to render_template :edit - expect(assigns(:settings).errors.count).to be 7 - expect(Spree::Config.account_invoices_monthly_fixed).to eq 5 - expect(Spree::Config.account_invoices_monthly_rate).to eq 0.02 - expect(Spree::Config.account_invoices_monthly_cap).to eq 50 - expect(Spree::Config.account_invoices_tax_rate).to eq 0.1 - expect(Spree::Config.shop_trial_length_days).to eq 30 - expect(Spree::Config.minimum_billable_turnover).to eq 0 - end - end - - context "when required settings are valid" do - before do - params[:settings][:account_invoices_monthly_fixed] = '10' - params[:settings][:account_invoices_monthly_rate] = '0.05' - params[:settings][:account_invoices_monthly_cap] = '30' - params[:settings][:account_invoices_tax_rate] = '0.15' - params[:settings][:shop_trial_length_days] = '20' - params[:settings][:minimum_billable_turnover] = '10' - end - - it "sets global config to the specified values" do - spree_get :update, params - expect(assigns(:settings).errors.count).to be 0 - expect(Spree::Config.account_invoices_monthly_fixed).to eq 10 - expect(Spree::Config.account_invoices_monthly_rate).to eq 0.05 - expect(Spree::Config.account_invoices_monthly_cap).to eq 30 - expect(Spree::Config.account_invoices_tax_rate).to eq 0.15 - expect(Spree::Config.shop_trial_length_days).to eq 20 - expect(Spree::Config.minimum_billable_turnover).to eq 10 - end - end - end - end -end diff --git a/spec/controllers/api/customers_controller_spec.rb b/spec/controllers/api/customers_controller_spec.rb index 360037cc90..f6c8f4e0a5 100644 --- a/spec/controllers/api/customers_controller_spec.rb +++ b/spec/controllers/api/customers_controller_spec.rb @@ -23,18 +23,6 @@ module Api expect(json_response.length).to eq 1 expect(json_response.first[:id]).to eq customer1.id end - - context "when the accounts distributor id has been set" do - before do - Spree::Config.set(accounts_distributor_id: customer1.enterprise.id) - end - - it "ignores the customer for that enterprise (if it exists)" do - spree_get :index - expect(response.status).to eq 200 - expect(json_response.length).to eq 0 - end - end end describe "#update" do diff --git a/spec/controllers/spree/users_controller_spec.rb b/spec/controllers/spree/users_controller_spec.rb index 35e0e0f6a3..95001084dd 100644 --- a/spec/controllers/spree/users_controller_spec.rb +++ b/spec/controllers/spree/users_controller_spec.rb @@ -14,14 +14,11 @@ describe Spree::UsersController, type: :controller do let!(:d1_order_for_u2) { create(:completed_order_with_totals, distributor: distributor1, user_id: u2.id) } let!(:d1o3) { create(:order, state: 'cart', distributor: distributor1, user_id: u1.id) } let!(:d2o1) { create(:completed_order_with_totals, distributor: distributor2, user_id: u2.id) } - let!(:accounts_distributor) { create :distributor_enterprise } - let!(:order_account_invoice) { create(:order, distributor: accounts_distributor, state: 'complete', user: u1) } let(:orders) { assigns(:orders) } let(:shops) { Enterprise.where(id: orders.pluck(:distributor_id)) } before do - Spree::Config.set(accounts_distributor_id: accounts_distributor.id) allow(controller).to receive(:spree_current_user) { u1 } end @@ -32,10 +29,6 @@ describe Spree::UsersController, type: :controller do expect(orders).to_not include d1_order_for_u2, d1o3, d2o1 expect(shops).to include distributor1 - # Doesn't return orders belonging to the accounts distributor" do - expect(orders).to_not include order_account_invoice - expect(shops).to_not include accounts_distributor - # Doesn't return orders for irrelevant distributors" do expect(orders).not_to include d2o1 expect(shops).not_to include distributor2 diff --git a/spec/factories.rb b/spec/factories.rb index cbdbdab3a8..4741696930 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -485,26 +485,6 @@ FactoryBot.define do bill_address { create(:address) } end - factory :billable_period do - begins_at { Time.zone.now.beginning_of_month } - ends_at { Time.zone.now.beginning_of_month + 1.month } - sells { 'any' } - trial { false } - enterprise - owner { enterprise.owner } - turnover { rand(100000).to_f/100 } - account_invoice do - AccountInvoice.where(user_id: owner_id, year: begins_at.year, month: begins_at.month).first || - FactoryBot.create(:account_invoice, user: owner, year: begins_at.year, month: begins_at.month) - end - end - - factory :account_invoice do - user { FactoryBot.create :user } - year { 2000 + rand(100) } - month { 1 + rand(12) } - end - factory :filter_order_cycles_tag_rule, class: TagRule::FilterOrderCycles do enterprise { FactoryBot.create :distributor_enterprise } end diff --git a/spec/features/admin/account_spec.rb b/spec/features/admin/account_spec.rb deleted file mode 100644 index f2be99c3a3..0000000000 --- a/spec/features/admin/account_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'spec_helper' - -feature 'Account Page' do - include AuthenticationWorkflow - - describe "updating" do - let!(:user) { create(:user) } - let!(:enterprise) { create(:distributor_enterprise, owner: user) } - - before do - quick_login_as user - end - - context "as an enterprise user" do - it "loads the page" do - visit admin_account_path - expect(page).to have_content "Account" - end - end - end -end diff --git a/spec/features/admin/accounts_and_billing_settings_spec.rb b/spec/features/admin/accounts_and_billing_settings_spec.rb deleted file mode 100644 index b5316f6b21..0000000000 --- a/spec/features/admin/accounts_and_billing_settings_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -require 'spec_helper' - -feature 'Account and Billing Settings' do - include AuthenticationWorkflow - include WebHelper - - describe "updating" do - let!(:admin) { create(:admin_user) } - let!(:pm1) { create(:payment_method) } - let!(:sm1) { create(:shipping_method) } - let!(:accounts_distributor) { create(:distributor_enterprise, payment_methods: [pm1], shipping_methods: [sm1]) } - - before do - Spree::Config.set({ - accounts_distributor_id: 0, - default_accounts_payment_method_id: 0, - default_accounts_shipping_method_id: 0, - auto_update_invoices: false, - auto_finalize_invoices: false - }) - end - - before do - quick_login_as_admin - end - - context "as an admin user", js: true do - it "loads the page" do - visit spree.admin_path - click_link "Configuration" - click_link "Accounts & Billing" - - expect(page).to have_select2 "settings_accounts_distributor_id" - select2_select accounts_distributor.name, from: "settings_accounts_distributor_id" - expect(page).to have_select "settings_default_accounts_payment_method_id" - expect(page).to have_select "settings_default_accounts_shipping_method_id" - expect(page).to have_link "Update User Invoices", href: start_job_admin_accounts_and_billing_settings_path(job: { name: 'update_account_invoices'}) - expect(page).to have_link "Finalise User Invoices", href: start_job_admin_accounts_and_billing_settings_path(job: { name: 'finalize_account_invoices'}) - end - - it "attributes can be changed", js: true do - visit edit_admin_accounts_and_billing_settings_path - - select2_select accounts_distributor.name, from: "settings_accounts_distributor_id" - select pm1.name, from: "settings_default_accounts_payment_method_id" - select sm1.name, from: "settings_default_accounts_shipping_method_id" - check "settings_auto_update_invoices" - check "settings_auto_finalize_invoices" - - click_button "Update" - - expect(Spree::Config.accounts_distributor_id).to eq accounts_distributor.id - expect(Spree::Config.default_accounts_payment_method_id).to eq pm1.id - expect(Spree::Config.default_accounts_shipping_method_id).to eq sm1.id - expect(Spree::Config.auto_update_invoices).to be true - expect(Spree::Config.auto_finalize_invoices).to be true - end - end - end -end diff --git a/spec/features/admin/business_model_configuration_spec.rb b/spec/features/admin/business_model_configuration_spec.rb deleted file mode 100644 index a16a23976e..0000000000 --- a/spec/features/admin/business_model_configuration_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -require 'spec_helper' - -feature 'Business Model Configuration' do - include AuthenticationWorkflow - include WebHelper - - describe "updating" do - let!(:admin) { create(:admin_user) } - - before do - Spree::Config.set({ - account_invoices_monthly_fixed: 5, - account_invoices_monthly_rate: 0.02, - account_invoices_monthly_cap: 50, - account_invoices_tax_rate: 0.1 - }) - end - - before do - quick_login_as_admin - end - - context "as an admin user", js: true do - it "loads the page" do - visit spree.admin_path - click_link "Configuration" - click_link "Business Model" - - expect(page).to have_field "settings_account_invoices_monthly_fixed", with: 5.0 - expect(page).to have_field "settings_account_invoices_monthly_rate", with: 0.02 - expect(page).to have_field "settings_account_invoices_monthly_cap", with: 50.0 - expect(page).to have_field "settings_account_invoices_tax_rate", with: 0.1 - end - - it "attributes can be changed", js: true do - visit edit_admin_business_model_configuration_path - - fill_in "settings_account_invoices_monthly_fixed", with: 10 - fill_in "settings_account_invoices_monthly_rate", with: 0.05 - fill_in "settings_account_invoices_monthly_cap", with: 30 - fill_in "settings_account_invoices_tax_rate", with: 0.15 - - click_button "Update" - - expect(page).to have_content "Business Model has been successfully updated!" - expect(Spree::Config.account_invoices_monthly_fixed).to eq 10 - expect(Spree::Config.account_invoices_monthly_rate).to eq 0.05 - expect(Spree::Config.account_invoices_monthly_cap).to eq 30 - expect(Spree::Config.account_invoices_tax_rate).to eq 0.15 - end - end - end -end diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index f34d2ef28e..be6a4135ed 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -493,36 +493,6 @@ xfeature %q{ xero_invoice_summary_row('Delivery Shipping Cost (tax inclusive)', 100.55, 'GST on Income', opts) ] end - - describe "account invoices" do - let(:accounts_distributor) { create(:distributor_enterprise) } - let(:billable_period) { create(:billable_period, account_invoice: account_invoice) } - let(:account_invoice) { create(:account_invoice, order: account_invoice_order) } - let!(:account_invoice_order) { create(:order, order_cycle: order_cycle, distributor: accounts_distributor) } - let!(:adjustment) { create(:adjustment, adjustable: account_invoice_order, source: billable_period, label: 'Account invoice item', amount: 12.34) } # Tax? - - before do - Spree::Config.accounts_distributor_id = accounts_distributor.id - - account_invoice_order.update_attribute :email, 'customer@email.com' - Timecop.travel(Time.zone.local(2015, 4, 25, 14, 0, 0)) { account_invoice_order.finalize! } - - visit current_path - end - - it "generates a detailed report for account invoices" do - select 'Detailed', from: 'report_type' - select accounts_distributor.name, from: 'q_distributor_id_eq' - click_button 'Search' - - opts = {} - - expect(xero_invoice_table).to match_table [ - xero_invoice_header, - xero_invoice_account_invoice_row(adjustment) - ] - end - end end private @@ -549,11 +519,6 @@ xfeature %q{ xero_invoice_row('', adjustment.label, adjustment.amount, '1', tax_type, opts) end - def xero_invoice_account_invoice_row(adjustment, opts={}) - opts.reverse_merge!({customer_name: '', address1: '', city: '', state: '', zipcode: '', country: '', invoice_number: account_invoice_order.number, order_number: account_invoice_order.number}) - xero_invoice_adjustment_row(adjustment, opts) - end - def xero_invoice_row(sku, description, amount, quantity, tax_type, opts={}) opts.reverse_merge!({customer_name: 'Customer Name', address1: 'customer l1', city: 'customer city', state: 'Victoria', zipcode: '1234', country: country.name, invoice_number: order1.number, order_number: order1.number, invoice_date: '2015-04-26', due_date: '2015-05-26', account_code: 'food sales'}) diff --git a/spec/features/consumer/account_spec.rb b/spec/features/consumer/account_spec.rb index 19175348f3..1ea86078b5 100644 --- a/spec/features/consumer/account_spec.rb +++ b/spec/features/consumer/account_spec.rb @@ -13,12 +13,9 @@ feature %q{ let!(:distributor2) { create(:distributor_enterprise) } let!(:distributor_credit) { create(:distributor_enterprise) } let!(:distributor_without_orders) { create(:distributor_enterprise) } - let!(:accounts_distributor) {create :distributor_enterprise} - let!(:order_account_invoice) { create(:order, distributor: accounts_distributor, state: 'complete', user: user) } context "as a logged in user" do before do - Spree::Config.accounts_distributor_id = accounts_distributor.id login_as user end @@ -42,9 +39,6 @@ feature %q{ expect(page).to have_content I18n.t('spree.users.orders.past_orders') - # Doesn't show orders from the special Accounts & Billing distributor - expect(page).not_to have_content accounts_distributor.name - # Lists all other orders expect(page).to have_content d1o1.number.to_s expect(page).to have_content d1o2.number.to_s @@ -59,8 +53,6 @@ feature %q{ expect(page).to have_content distributor2.name expect(page).not_to have_content distributor_without_orders.name - # Exclude the special Accounts & Billing distributor - expect(page).not_to have_content accounts_distributor.name expect(page).to have_content distributor1.name + " " + "Balance due" expect(page).to have_content distributor_credit.name + " Credit" diff --git a/spec/helpers/admin/business_model_configuration_helper_spec.rb b/spec/helpers/admin/business_model_configuration_helper_spec.rb deleted file mode 100644 index 3490efb8be..0000000000 --- a/spec/helpers/admin/business_model_configuration_helper_spec.rb +++ /dev/null @@ -1,574 +0,0 @@ -require 'spec_helper' - -describe Admin::BusinessModelConfigurationHelper, type: :helper do - describe "describing monthly bills for enterprises" do - - context "when there is no free trial" do - before { Spree::Config.set(:shop_trial_length_days, 0) } - - context "when tax is applied to the service change" do - before { Spree::Config.set(:account_invoices_tax_rate, 0.1) } - - context "when minimum billable turnover is zero" do - before { Spree::Config.set(:minimum_billable_turnover, 0) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} + 5.0% OF SALES, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH, PLUS GST" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} + 5.0% OF SALES PER MONTH, PLUS GST" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} PER MONTH, PLUS GST" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} PER MONTH, PLUS GST" } - end - end - end - - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH, PLUS GST" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES PER MONTH, PLUS GST" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - end - end - end - - context "when minimum billable turnover is 100" do - before { Spree::Config.set(:minimum_billable_turnover, 100) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} + 5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)}, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH, PLUS GST" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} + 5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)} PER MONTH, PLUS GST" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} PER MONTH, PLUS GST" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} PER MONTH, PLUS GST" } - end - end - end - - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)}, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH, PLUS GST" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)} PER MONTH, PLUS GST" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - end - end - end - end - - context "when tax is not applied to the service change" do - before { Spree::Config.set(:account_invoices_tax_rate, 0.0) } - - context "when minimum billable turnover is zero" do - before { Spree::Config.set(:minimum_billable_turnover, 0) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} + 5.0% OF SALES, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} + 5.0% OF SALES PER MONTH" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} PER MONTH" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} PER MONTH" } - end - end - end - - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES PER MONTH" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - end - end - end - - context "when minimum billable turnover is 100" do - before { Spree::Config.set(:minimum_billable_turnover, 100) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} + 5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)}, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} + 5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)} PER MONTH" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} PER MONTH" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "#{with_currency(10, no_cents: true)} PER MONTH" } - end - end - end - - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)}, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)} PER MONTH" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - end - end - end - end - end - - context "when there is a 30 day free trial" do - before { Spree::Config.set(:shop_trial_length_days, 30) } - - context "when tax is applied to the service change" do - before { Spree::Config.set(:account_invoices_tax_rate, 0.1) } - - context "when minimum billable turnover is zero" do - before { Spree::Config.set(:minimum_billable_turnover, 0) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} + 5.0% OF SALES, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH, PLUS GST" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} + 5.0% OF SALES PER MONTH, PLUS GST" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} PER MONTH, PLUS GST" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} PER MONTH, PLUS GST" } - end - end - end - - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN 5.0% OF SALES, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH, PLUS GST" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN 5.0% OF SALES PER MONTH, PLUS GST" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - end - end - end - - context "when minimum billable turnover is 100" do - before { Spree::Config.set(:minimum_billable_turnover, 100) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} + 5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)}, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH, PLUS GST" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} + 5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)} PER MONTH, PLUS GST" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} PER MONTH, PLUS GST" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} PER MONTH, PLUS GST" } - end - end - end - - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN 5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)}, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH, PLUS GST" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN 5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)} PER MONTH, PLUS GST" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - end - end - end - end - - context "when tax is not applied to the service change" do - before { Spree::Config.set(:account_invoices_tax_rate, 0.0) } - - context "when minimum billable turnover is zero" do - before { Spree::Config.set(:minimum_billable_turnover, 0) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} + 5.0% OF SALES, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} + 5.0% OF SALES PER MONTH" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} PER MONTH" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} PER MONTH" } - end - end - end - - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN 5.0% OF SALES, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN 5.0% OF SALES PER MONTH" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - end - end - end - - context "when minimum billable turnover is 100" do - before { Spree::Config.set(:minimum_billable_turnover, 100) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} + 5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)}, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} + 5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)} PER MONTH" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} PER MONTH" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN #{with_currency(10, no_cents: true)} PER MONTH" } - end - end - end - - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.05) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN 5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)}, CAPPED AT #{with_currency(20, no_cents: true)} PER MONTH" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE TRIAL THEN 5.0% OF SALES ONCE TURNOVER EXCEEDS #{with_currency(100, no_cents: true)} PER MONTH" } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(helper.monthly_bill_description).to eq "FREE" } - end - end - end - end - end - end - end -end diff --git a/spec/jobs/finalize_account_invoices_spec.rb b/spec/jobs/finalize_account_invoices_spec.rb deleted file mode 100644 index a8b591395d..0000000000 --- a/spec/jobs/finalize_account_invoices_spec.rb +++ /dev/null @@ -1,216 +0,0 @@ -require 'spec_helper' - -def travel_to(time) - around { |example| Timecop.travel(start_of_july + time) { example.run } } -end - - -xdescribe FinalizeAccountInvoices do - let!(:year) { Time.zone.now.year } - - describe "unit specs" do - let!(:finalizer) { FinalizeAccountInvoices.new } - let!(:start_of_july) { Time.zone.local(year, 7) } - - describe "perform" do - let!(:accounts_distributor) { create(:distributor_enterprise) } - - #Invoice from June - let!(:account_invoice1) { create(:account_invoice, year: year, month: 6, order: create(:order, completed_at: nil))} - - # We don't care when it was completed, in the future or past - let!(:account_invoice2) { create(:account_invoice, year: year, month: 6, order: create(:order, completed_at: start_of_july - 10.days))} - let!(:account_invoice3) { create(:account_invoice, year: year, month: 6, order: create(:order, completed_at: start_of_july + 10.days))} - - # Invoices from July - let!(:account_invoice4) { create(:account_invoice, year: year, month: 7, order: create(:order, completed_at: nil))} - let!(:account_invoice5) { create(:account_invoice, year: year, month: 7, order: create(:order, completed_at: start_of_july + 10.days))} - - before do - allow(Enterprise).to receive(:find_by_id) { accounts_distributor } - allow(accounts_distributor).to receive(:payment_methods) { double(:payment_methods, find_by_id: true) } - allow(accounts_distributor).to receive(:shipping_methods) { double(:shipping_methods, find_by_id: true) } - allow(finalizer).to receive(:finalize) - allow(Bugsnag).to receive(:notify) - end - - context "when necessary global config setting have not been set" do - travel_to(20.days) - - context "when accounts_distributor has been set" do - before do - allow(Enterprise).to receive(:find_by_id) { false } - finalizer.perform - end - - it "snags errors and doesn't run" do - expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("InvalidJobSettings"), anything) - expect(finalizer).to_not have_received(:finalize) - end - end - - context "when default payment method has been set" do - before do - allow(accounts_distributor).to receive(:payment_methods) { double(:payment_methods, find_by_id: false) } - finalizer.perform - end - - it "snags errors and doesn't run" do - expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("InvalidJobSettings"), anything) - expect(finalizer).to_not have_received(:finalize) - end - end - - context "when default shipping method has been set" do - before do - allow(accounts_distributor).to receive(:shipping_methods) { double(:shipping_methods, find_by_id: false) } - finalizer.perform - end - - it "snags errors and doesn't run" do - expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("InvalidJobSettings"), anything) - expect(finalizer).to_not have_received(:finalize) - end - end - end - - context "when necessary global config setting have been set" do - context "and no date arguments are passed to the job" do - travel_to(3.days) - - it "finalizes the uncompleted orders from account_invoices for the previous calendar month" do - finalizer.perform - expect(finalizer).to have_received(:finalize).with(account_invoice1.order) - expect(finalizer).to_not have_received(:finalize).with(account_invoice2.order) - expect(finalizer).to_not have_received(:finalize).with(account_invoice3.order) - expect(finalizer).to_not have_received(:finalize).with(account_invoice4.order) - expect(finalizer).to_not have_received(:finalize).with(account_invoice5.order) - end - end - - context "and a specific year and month are passed as arguments" do - let!(:finalizer) { FinalizeAccountInvoices.new(year, 7) } - - before do - allow(finalizer).to receive(:finalizer) - end - - context "that ends in the past" do - travel_to(1.month + 3.hours) - - it "finalizes the uncompleted orders from account_invoices for the specified calendar month" do - finalizer.perform - expect(finalizer).to_not have_received(:finalize).with(account_invoice1.order) - expect(finalizer).to_not have_received(:finalize).with(account_invoice2.order) - expect(finalizer).to_not have_received(:finalize).with(account_invoice3.order) - expect(finalizer).to have_received(:finalize).with(account_invoice4.order) - expect(finalizer).to_not have_received(:finalize).with(account_invoice5.order) - end - end - - context "that ends in the future" do - travel_to 3.days - - it "does not finalize any orders" do - finalizer.perform - expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("InvalidJobSettings"), anything) - expect(finalizer).to_not have_received(:finalize) - end - end - end - end - end - - describe "finalize" do - let!(:pm) { create(:payment_method, name: "PM1") } - let!(:sm) { create(:shipping_method, name: "ship1") } - let!(:accounts_distributor) { create(:distributor_enterprise, payment_methods: [pm], shipping_methods: [sm]) } - let!(:invoice_order) { create(:order, distributor: accounts_distributor) } - - before do - Spree::Config.set({ accounts_distributor_id: accounts_distributor.id }) - Spree::Config.set({ default_accounts_payment_method_id: pm.id }) - Spree::Config.set({ default_accounts_shipping_method_id: sm.id }) - invoice_order.line_items.clear - end - - it "creates payment, assigns shipping method and finalizes the order" do - expect(invoice_order.completed_at).to be nil - finalizer.finalize(invoice_order) - expect(invoice_order.completed_at).to_not be nil - expect(invoice_order.payments.count).to eq 1 - expect(invoice_order.payments.first.payment_method).to eq pm - expect(invoice_order.shipping_method).to eq sm - end - - it "does not send a confirmation email" do - expect(invoice_order).to receive(:deliver_order_confirmation_email).and_call_original - expect{finalizer.finalize(invoice_order)}.to_not enqueue_job ConfirmOrderJob - end - - context "when errors exist on the order" do - before do - allow(invoice_order).to receive(:errors) { double(:errors, any?: true, full_messages: ["Error message 1", "Error message 2"]) } - allow(Bugsnag).to receive(:notify) - end - - it "Snags a bug and does not finalize the order" do - finalizer.finalize(invoice_order) - expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("FinalizeInvoiceError"), anything) - expect(invoice_order).to_not be_completed - end - end - end - end - - describe "validation spec" do - let!(:start_of_july) { Time.zone.local(year, 7) } - - let!(:updater) { UpdateAccountInvoices.new } - let!(:finalizer) { FinalizeAccountInvoices.new } - - let!(:pm) { create(:payment_method, name: "Default Payment Method") } - let!(:sm) { create(:shipping_method, name: "Default Shipping Method") } - let!(:accounts_distributor) { create(:distributor_enterprise, payment_methods: [pm], shipping_methods: [sm]) } - - let!(:user) { create(:user) } - let!(:billable_period1) { create(:billable_period, sells: 'any', owner: user, begins_at: start_of_july - 1.month, ends_at: start_of_july) } - let!(:billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 10.days) } - let!(:billable_period3) { create(:billable_period, owner: user, begins_at: start_of_july + 12.days, ends_at: start_of_july + 20.days) } - - before do - sm.calculator.set_preference(:amount, 0); sm.calculator.save! - - Spree::Config.set({ accounts_distributor_id: accounts_distributor.id }) - Spree::Config.set({ default_accounts_payment_method_id: pm.id }) - Spree::Config.set({ default_accounts_shipping_method_id: sm.id }) - - # Make sure that bills are > 0 - Spree::Config.set(:account_invoices_monthly_fixed, 5) - Spree::Config.set(:account_invoices_monthly_rate, 0.02) - Spree::Config.set(:account_invoices_monthly_cap, 50) - Spree::Config.set(:minimum_billable_turnover, 0) - end - - context "finalizing an invoice" do - travel_to(3.hours) - - it "finalizes it" do - # Create an invoice using the updater, to make sure we are using - # an order as it would be when generated this way - expect{updater.perform}.to change{Spree::Order.count}.from(0).to(1) - invoice = user.orders.first - - # Finalize invoices - finalizer.perform - invoice.reload - - expect(invoice.completed_at).to_not be_nil - expect(invoice.total).to eq billable_period1.bill.round(2) - expect(invoice.payments.count).to eq 1 - expect(invoice.payments.first.amount).to eq billable_period1.bill.round(2) - expect(invoice.state).to eq 'complete' - end - end - end -end diff --git a/spec/jobs/update_account_invoices_spec.rb b/spec/jobs/update_account_invoices_spec.rb deleted file mode 100644 index abe65ec853..0000000000 --- a/spec/jobs/update_account_invoices_spec.rb +++ /dev/null @@ -1,448 +0,0 @@ -require 'spec_helper' - -def travel_to(time) - around { |example| Timecop.travel(start_of_july + time) { example.run } } -end - -xdescribe UpdateAccountInvoices do - let(:year) { Time.zone.now.year } - - before do - # Make sure that bills are > 0 - Spree::Config.set(:account_invoices_monthly_fixed, 5) - Spree::Config.set(:account_invoices_monthly_rate, 0.02) - Spree::Config.set(:account_invoices_monthly_cap, 50) - Spree::Config.set(:minimum_billable_turnover, 0) - end - - describe "units specs" do - let!(:start_of_july) { Time.zone.local(year, 7) } - - let!(:updater) { UpdateAccountInvoices.new } - - let!(:user) { create(:user) } - let!(:june_billable_period1) { create(:billable_period, owner: user, begins_at: start_of_july - 1.month, ends_at: start_of_july - 20.days) } - let!(:june_billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july - 20.days, ends_at: start_of_july - 10.days, turnover: 45, sells: "none" ) } - let!(:june_billable_period3) { create(:billable_period, owner: user, begins_at: start_of_july - 10.days, ends_at: start_of_july - 1.days, turnover: 0, sells: "any" ) } - let!(:july_billable_period1) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 12.days) } - let!(:july_billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july + 12.days, ends_at: start_of_july + 20.days) } - let!(:july_billable_period3) { create(:billable_period, owner: user, begins_at: start_of_july + 20.days, ends_at: start_of_july + 25.days, turnover: 45, sells: 'none') } - let!(:july_billable_period4) { create(:billable_period, owner: user, begins_at: start_of_july + 25.days, ends_at: start_of_july + 28.days, turnover: 0, sells: 'any') } - let(:june_account_invoice) { june_billable_period1.account_invoice } - let(:july_account_invoice) { july_billable_period1.account_invoice } - - describe "perform" do - let(:accounts_distributor) { double(:accounts_distributor) } - before do - allow(Enterprise).to receive(:find_by_id) { accounts_distributor } - allow(updater).to receive(:update) - allow(Bugsnag).to receive(:notify) - end - - context "when necessary global config setting have not been set" do - travel_to(20.days) - - context "when accounts_distributor has been set" do - before do - allow(Enterprise).to receive(:find_by_id) { false } - updater.perform - end - - it "snags errors and doesn't run" do - expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("InvalidJobSettings"), anything) - expect(updater).to_not have_received(:update) - end - end - end - - context "when necessary global config setting have been set" do - context "on the first of the month" do - travel_to(3.hours) - - it "updates invoices from the previous month" do - updater.perform - expect(updater).to have_received(:update).once - .with(june_account_invoice) - expect(updater).to_not have_received(:update) - .with(july_account_invoice) - end - end - - context "on other days" do - travel_to(20.days) - - it "updates invoices from the current month" do - updater.perform - expect(updater).to have_received(:update).once - .with(july_account_invoice) - end - end - - context "when specfic a specific month (and year) are passed as arguments" do - let!(:updater) { UpdateAccountInvoices.new(year, 7) } - - before do - allow(updater).to receive(:update) - end - - context "that just ended (in the past)" do - travel_to(1.month) - - it "updates invoices from the previous month" do - updater.perform - expect(updater).to have_received(:update).once - .with(july_account_invoice) - end - end - - context "that starts in the past and ends in the future (ie. current_month)" do - travel_to 30.days - - it "updates invoices from that current month" do - updater.perform - expect(updater).to have_received(:update).once - .with(july_account_invoice) - end - end - - context "that starts in the future" do - travel_to(-1.days) - - it "snags an error and does not update invoices" do - updater.perform - expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("InvalidJobSettings"), anything) - expect(updater).to_not have_received(:update) - end - end - end - end - end - - describe "update" do - before do - allow(june_account_invoice).to receive(:save).and_call_original - allow(july_account_invoice).to receive(:save).and_call_original - allow(updater).to receive(:clean_up) - allow(updater).to receive(:finalize) - allow(Bugsnag).to receive(:notify) - end - - context "where an order for the invoice already exists" do - let!(:invoice_order) { create(:order, user: user) } - - before do - expect(Spree::Order).to_not receive(:new) - allow(june_account_invoice).to receive(:order) { invoice_order } - end - - context "where the order is already complete" do - before do - allow(invoice_order).to receive(:complete?) { true } - updater.update(june_account_invoice) - end - - it "snags a bug" do - expect(Bugsnag).to have_received(:notify) - end - - it "does not save the order" do - expect(june_account_invoice).to_not have_received(:save) - end - - it "does not clean up the order" do - expect(updater).to_not have_received(:clean_up).with(invoice_order, anything) - end - end - - context "where the order is not complete" do - before do - allow(invoice_order).to receive(:complete?) { false } - june_billable_period1.enterprise.update_attributes(contact_name: "Firstname Lastname Something Else", phone: '12345') - updater.update(june_account_invoice) - end - - it "creates adjustments for each billing item where bill is not 0" do - adjustments = invoice_order.adjustments - expect(adjustments.map(&:source_id)).to eq [june_billable_period1.id, june_billable_period3.id] - expect(adjustments.map(&:amount)).to eq [june_billable_period1.bill.round(2), june_billable_period3.bill.round(2)] - expect(adjustments.map(&:label)).to eq [june_billable_period1.adjustment_label, june_billable_period3.adjustment_label] - end - - it "assigns a addresses to the order" do - expect(invoice_order.billing_address).to be_a Spree::Address - expect(invoice_order.shipping_address).to be_a Spree::Address - expect(invoice_order.shipping_address).to eq invoice_order.billing_address - [:address1, :address2, :city, :zipcode, :state_id, :country_id].each do |attr| - expect(invoice_order.billing_address[attr]).to eq june_billable_period1.enterprise.address[attr] - end - expect(invoice_order.billing_address.firstname).to eq "Firstname" - expect(invoice_order.billing_address.lastname).to eq "Lastname Something Else" - expect(invoice_order.billing_address.phone).to eq "12345" - end - - it "saves the order" do - expect(june_account_invoice).to have_received(:save) - expect(june_account_invoice.order).to be_persisted - end - - it "cleans up the order" do - expect(updater).to have_received(:clean_up).with(invoice_order, anything).once - end - end - end - - context "where an order for the invoice does not already exist" do - let!(:accounts_distributor) { create(:distributor_enterprise) } - before do - Spree::Config.set({ accounts_distributor_id: accounts_distributor.id }) - updater.update(july_account_invoice) - end - - it "creates adjustments for each billing item where bill is not 0" do - adjustments = july_account_invoice.order.adjustments - expect(adjustments.map(&:source_id)).to eq [july_billable_period1.id, july_billable_period2.id,july_billable_period4.id] - expect(adjustments.map(&:amount)).to eq [july_billable_period1.bill.round(2), july_billable_period2.bill.round(2), july_billable_period4.bill.round(2)] - expect(adjustments.map(&:label)).to eq [july_billable_period1.adjustment_label, july_billable_period2.adjustment_label, july_billable_period4.adjustment_label] - end - - it "saves the order" do - expect(july_account_invoice).to have_received(:save) - expect(july_account_invoice.order).to be_persisted - end - - it "cleans up order" do - expect(updater).to have_received(:clean_up).with(july_account_invoice.order, anything).once - end - end - end - - describe "clean_up" do - let!(:invoice_order) { create(:order) } - let!(:obsolete1) { create(:adjustment, adjustable: invoice_order) } - let!(:obsolete2) { create(:adjustment, adjustable: invoice_order) } - let!(:current1) { create(:adjustment, adjustable: invoice_order) } - let!(:current2) { create(:adjustment, adjustable: invoice_order) } - - before do - allow(invoice_order).to receive(:save) - allow(invoice_order).to receive(:destroy) - allow(Bugsnag).to receive(:notify) - end - - context "when current adjustments are present" do - let!(:current_adjustments) { [current1, current2] } - - context "and obsolete adjustments are present" do - let!(:obsolete_adjustments) { [obsolete1, obsolete2] } - - before do - allow(obsolete_adjustments).to receive(:destroy_all) - allow(invoice_order).to receive(:adjustments) { double(:adjustments, where: obsolete_adjustments) } - updater.clean_up(invoice_order, current_adjustments) - end - - it "destroys obsolete adjustments and snags a bug" do - expect(obsolete_adjustments).to have_received(:destroy_all) - expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("Obsolete Adjustments"), anything) - end - end - - context "and obsolete adjustments are not present" do - let!(:obsolete_adjustments) { [] } - - before do - allow(invoice_order).to receive(:adjustments) { double(:adjustments, where: obsolete_adjustments) } - updater.clean_up(invoice_order, current_adjustments) - end - - it "has no bugs to snag" do - expect(Bugsnag).to_not have_received(:notify) - end - end - end - - context "when current adjustments are not present" do - let!(:current_adjustments) { [] } - - context "and obsolete adjustments are present" do - let!(:obsolete_adjustments) { [obsolete1, obsolete2] } - - before do - allow(obsolete_adjustments).to receive(:destroy_all) - allow(invoice_order).to receive(:adjustments) { double(:adjustments, where: obsolete_adjustments) } - end - - it "destroys obsolete adjustments and snags a bug" do - updater.clean_up(invoice_order, current_adjustments) - expect(obsolete_adjustments).to have_received(:destroy_all) - expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("Obsolete Adjustments"), anything) - end - - context "when the order is not persisted" do - before do - allow(invoice_order).to receive(:persisted?) { false } - end - - it "destroys the order" do - updater.clean_up(invoice_order, current_adjustments) - expect(invoice_order).to have_received(:destroy) - end - end - - context "when the order is persisted" do - before do - allow(invoice_order).to receive(:persisted?) { true } - end - - it "snags a bug" do - updater.clean_up(invoice_order, current_adjustments) - expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("Empty Persisted Invoice"), anything) - end - end - end - - context "and obsolete adjustments are not present" do - let!(:obsolete_adjustments) { [] } - - before do - allow(invoice_order).to receive(:adjustments) { double(:adjustments, where: obsolete_adjustments) } - end - - it "has no bugs to snag" do - expect(Bugsnag).to_not have_received(:notify).with(RuntimeError.new("Obsolete Adjustments"), anything) - end - - context "when the order is not persisted" do - before do - allow(invoice_order).to receive(:persisted?) { false } - end - - it "destroys the order" do - updater.clean_up(invoice_order, current_adjustments) - expect(invoice_order).to have_received(:destroy) - end - end - - context "when the order is persisted" do - before do - allow(invoice_order).to receive(:persisted?) { true } - end - - it "snags a bug" do - updater.clean_up(invoice_order, current_adjustments) - expect(Bugsnag).to have_received(:notify).with(RuntimeError.new("Empty Persisted Invoice"), anything) - end - end - end - end - end - end - - describe "validation spec" do - let!(:start_of_july) { Time.zone.local(year, 7) } - - let!(:updater) { UpdateAccountInvoices.new } - - let!(:accounts_distributor) { create(:distributor_enterprise) } - - let!(:user) { create(:user) } - let!(:july_billable_period1) { create(:billable_period, sells: 'any', owner: user, begins_at: start_of_july - 1.month, ends_at: start_of_july) } - let!(:july_billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 10.days) } - let!(:july_billable_period3) { create(:billable_period, owner: user, begins_at: start_of_july + 12.days, ends_at: start_of_july + 20.days) } - let!(:july_account_invoice) { july_billable_period2.account_invoice } - let!(:august_account_invoice) { create(:account_invoice, user: user, year: july_account_invoice.year, month: 8)} - - before do - Spree::Config.set({ accounts_distributor_id: accounts_distributor.id }) - july_billable_period2.enterprise.update_attributes(contact_name: 'Anna Karenina', phone: '3433523') - end - - context "when no invoice_order currently exists" do - context "when relevant billable periods exist" do - travel_to(20.days) - - it "creates an invoice_order" do - expect{updater.perform}.to change{Spree::Order.count}.from(0).to(1) - invoice_order = july_account_invoice.reload.order - expect(user.orders.first).to eq invoice_order - expect(invoice_order.completed_at).to be_nil - billable_adjustments = invoice_order.adjustments.where('source_type = (?)', 'BillablePeriod') - expect(billable_adjustments.map(&:amount)).to eq [july_billable_period2.bill.round(2), july_billable_period3.bill.round(2)] - expect(invoice_order.total).to eq july_billable_period2.bill.round(2) + july_billable_period3.bill.round(2) - expect(invoice_order.payments.count).to eq 0 - expect(invoice_order.state).to eq 'cart' - expect(invoice_order.bill_address).to be_a Spree::Address - expect(invoice_order.ship_address).to be_a Spree::Address - expect(invoice_order.shipping_address).to eq invoice_order.billing_address - [:address1, :address2, :city, :zipcode, :state_id, :country_id].each do |attr| - expect(invoice_order.billing_address[attr]).to eq july_billable_period2.enterprise.address[attr] - end - expect(invoice_order.billing_address.firstname).to eq "Anna" - expect(invoice_order.billing_address.lastname).to eq "Karenina" - expect(invoice_order.billing_address.phone).to eq "3433523" - end - end - - context "when no relevant billable periods exist" do - travel_to(1.month + 5.days) - - it "does not create an order" do - expect(updater).to receive(:update).with(august_account_invoice).and_call_original - expect{updater.perform}.to_not change{Spree::Order.count}.from(0) - end - end - end - - context "when an order already exists" do - context "when relevant billable periods exist" do - let!(:invoice_order) { create(:order, user: user, distributor: accounts_distributor, created_at: start_of_july) } - let!(:billable_adjustment) { create(:adjustment, adjustable: invoice_order, source_type: 'BillablePeriod') } - - before do - invoice_order.line_items.clear - july_account_invoice.update_attribute(:order, invoice_order) - end - - travel_to(20.days) - - it "updates the order, and clears any obsolete invoices" do - expect{updater.perform}.to_not change{Spree::Order.count} - invoice_order = user.orders.first - expect(invoice_order.completed_at).to be_nil - billable_adjustments = invoice_order.adjustments.where('source_type = (?)', 'BillablePeriod') - expect(billable_adjustments).to_not include billable_adjustment - expect(billable_adjustments.map(&:amount)).to eq [july_billable_period2.bill.round(2), july_billable_period3.bill.round(2)] - expect(invoice_order.total).to eq july_billable_period2.bill.round(2) + july_billable_period3.bill.round(2) - expect(invoice_order.payments.count).to eq 0 - expect(invoice_order.state).to eq 'cart' - expect(invoice_order.bill_address).to be_a Spree::Address - expect(invoice_order.ship_address).to be_a Spree::Address - expect(invoice_order.shipping_address).to eq invoice_order.billing_address - [:address1, :address2, :city, :zipcode, :state_id, :country_id].each do |attr| - expect(invoice_order.billing_address[attr]).to eq july_billable_period2.enterprise.address[attr] - end - expect(invoice_order.billing_address.firstname).to eq "Anna" - expect(invoice_order.billing_address.lastname).to eq "Karenina" - expect(invoice_order.billing_address.phone).to eq "3433523" - end - end - - context "when no relevant billable periods exist" do - let!(:invoice_order) { create(:order, user: user, distributor: accounts_distributor) } - - before do - invoice_order.line_items.clear - august_account_invoice.update_attribute(:order, invoice_order) - end - - travel_to(1.month + 5.days) - - it "snags a bug" do - expect(updater).to receive(:update).with(august_account_invoice).and_call_original - expect(Bugsnag).to receive(:notify).with(RuntimeError.new("Empty Persisted Invoice"), anything) - expect{updater.perform}.to_not change{Spree::Order.count} - end - end - end - end -end diff --git a/spec/jobs/update_billable_periods_spec.rb b/spec/jobs/update_billable_periods_spec.rb deleted file mode 100644 index 1b461e7f2a..0000000000 --- a/spec/jobs/update_billable_periods_spec.rb +++ /dev/null @@ -1,725 +0,0 @@ -require 'spec_helper' - -def travel_to(time) - around { |example| Timecop.travel(start_of_july + time) { example.run } } -end - -describe UpdateBillablePeriods do - let!(:year) { Time.zone.now.year } - - describe "unit specs" do - let!(:start_of_july) { Time.zone.local(year, 7) } - - let!(:updater) { UpdateBillablePeriods.new } - - describe "perform", versioning: true do - let!(:enterprise) { create(:supplier_enterprise, created_at: start_of_july - 1.month, sells: 'any') } - - context "when no date arguments are passed to the job" do - before do - expect(updater).to receive(:clean_up_untouched_billable_periods_for).once - end - - context "on the first of the month" do - travel_to(3.hours) - - it "processes the previous month" do - expect(updater).to receive(:split_for_trial) - .with(enterprise, start_of_july - 1.month, start_of_july, nil, nil) - updater.perform - end - end - - context "on all other days" do - travel_to(1.day + 3.hours) - - it "processes the current month up until previous midnight" do - expect(updater).to receive(:split_for_trial) - .with(enterprise, start_of_july, start_of_july + 1.day, nil, nil) - updater.perform - end - end - end - - context "when a specfic year and month are passed as arguments" do - let!(:updater) { UpdateBillablePeriods.new(year, 6) } - - before do - allow(updater).to receive(:split_for_trial) - end - - context "that ends in the past" do - travel_to(3.hours) - - it "processes the month" do - expect(updater).to receive(:split_for_trial) - .with(enterprise, start_of_july - 1.month, start_of_july, nil, nil) - updater.perform - end - end - - context "that starts in the past and ends in the future (ie. current month)" do - travel_to(-3.days) - - it "processes the current month up to the previous midnight" do - expect(updater).to receive(:split_for_trial) - .with(enterprise, start_of_july - 1.month, start_of_july-3.days, nil, nil) - updater.perform - end - end - - context "that starts in the future" do - travel_to(-31.days) - - it "does not run" do - expect(updater).to_not receive(:split_for_trial) - updater.perform - end - end - end - - context "when an enterprise is created before the beginning of the current month" do - before do - expect(updater).to receive(:clean_up_untouched_billable_periods_for).once - end - - travel_to(28.days) - - context "when no alterations to sells or owner have been made during the current month" do - - it "begins at the start of the month" do - expect(updater).to receive(:split_for_trial) - .with(enterprise, start_of_july, start_of_july + 28.days, nil, nil) - updater.perform - end - end - - context "when sells has been changed within the current month" do - before do - Timecop.freeze(start_of_july + 10.days) do - # NOTE: Sells is changed between when order1 and order2 are placed - enterprise.update_attribute(:sells, 'own') - end - end - - travel_to(28.days) - - it "processes each sells period separately" do - allow(updater).to receive(:split_for_trial).twice - updater.perform - - expect(updater).to have_received(:split_for_trial) - .with(enterprise.versions.first.reify, start_of_july, start_of_july + 10.days, nil, nil) - - expect(updater).to have_received(:split_for_trial) - .with(enterprise, start_of_july + 10.days, start_of_july + 28.days, nil, nil) - end - end - - context "when owner has been changed within the current month" do - let!(:new_owner) { create(:user) } - - before do - Timecop.freeze(start_of_july + 10.days) do - # NOTE: Sells is changed between when order1 and order2 are placed - enterprise.update_attribute(:owner, new_owner) - end - end - - travel_to(28.days) - - it "processes each ownership period separately" do - allow(updater).to receive(:split_for_trial).twice - updater.perform - - expect(updater).to have_received(:split_for_trial) - .with(enterprise.versions.first.reify, start_of_july, start_of_july + 10.days, nil, nil) - - expect(updater).to have_received(:split_for_trial) - .with(enterprise, start_of_july + 10.days, start_of_july + 28.days, nil, nil) - end - end - - context "when some other attribute has been changed within the current month" do - before do - Timecop.freeze(start_of_july + 10.days) do - # NOTE: Sells is changed between when order1 and order2 are placed - enterprise.update_attribute(:name, 'Some New Name') - end - end - - travel_to(28.days) - - it "does not create a version, and so does not split the period" do - expect(enterprise.versions).to eq [] - allow(updater).to receive(:split_for_trial).once - updater.perform - expect(updater).to have_received(:split_for_trial) - .with(enterprise, start_of_july, start_of_july + 28.days, nil, nil) - end - end - - context "where sells or owner_id were altered during the previous month (ie. June)" do - let!(:new_owner) { create(:user) } - - before do - Timecop.freeze(start_of_july - 20.days) do - # NOTE: Sells is changed between when order1 and order2 are placed - enterprise.update_attribute(:sells, 'own') - end - Timecop.freeze(start_of_july - 10.days) do - # NOTE: Sells is changed between when order1 and order2 are placed - enterprise.update_attribute(:owner, new_owner) - end - end - - travel_to(28.days) - - it "ignores those verions" do - allow(updater).to receive(:split_for_trial).once - updater.perform - expect(updater).to have_received(:split_for_trial) - .with(enterprise, start_of_july, start_of_july + 28.days, nil, nil) - end - end - - context "where sells or owner_id were altered in the future" do - let!(:new_owner) { create(:user) } - - before do - Timecop.freeze(start_of_july + 17.days) do - enterprise.update_attribute(:sells, 'own') - end - Timecop.freeze(start_of_july + 35.days) do - enterprise.update_attribute(:owner, new_owner) - end - end - - travel_to(15.days) - - it "ignores those verions" do - allow(updater).to receive(:split_for_trial).once - updater.perform - expect(updater).to have_received(:split_for_trial) - .with(enterprise, start_of_july, start_of_july + 15.days, nil, nil) - end - end - end - - context "when an enterprise is created during the current month" do - before do - expect(updater).to receive(:clean_up_untouched_billable_periods_for).once - enterprise.update_attribute(:created_at, start_of_july + 10.days) - end - - travel_to(28.days) - - it "begins at the date the enterprise was created" do - allow(updater).to receive(:split_for_trial).once - updater.perform - expect(updater).to have_received(:split_for_trial) - .with(enterprise, start_of_july + 10.days, start_of_july + 28.days, nil, nil) - end - end - - context "when an enterprise is created after the previous midnight" do - before do - expect(updater).to_not receive(:clean_up_untouched_billable_periods_for) - enterprise.update_attribute(:created_at, start_of_july + 29.days) - end - - travel_to(28.days) - - it "ignores the enterprise" do - allow(updater).to receive(:split_for_trial) - updater.perform - expect(updater).to_not have_received(:split_for_trial) - end - end - - pending "when an enterprise is deleted during the current month" do - before do - expect(updater).to receive(:clean_up_untouched_billable_periods_for).once - enterprise.update_attribute(:deleted_at, start_of_july + 20.days) - end - - travel_to(28.days) - - it "ends at the date the enterprise was deleted" do - allow(updater).to receive(:split_for_trial) - updater.perform - expect(updater).to have_received(:split_for_trial) - .with(enterprise, start_of_july, start_of_july + 20.days, nil, nil) - end - end - end - - describe "split_for_trial" do - let!(:enterprise) { double(:enterprise) } - let(:begins_at) { start_of_july } - let(:ends_at) { begins_at + 30.days } - - context "when trial_start is nil" do - let(:trial_start) { nil } - let(:trial_expiry) { begins_at + 3.days } - - before do - allow(updater).to receive(:update_billable_period).once - updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) - end - - it "calls update_billable_period once for the entire period" do - expect(updater).to have_received(:update_billable_period) - .with(enterprise, begins_at, ends_at, false) - end - end - - context "when trial_expiry is nil" do - let(:trial_start) { begins_at + 3.days } - let(:trial_expiry) { nil } - - before do - allow(updater).to receive(:update_billable_period).once - updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) - end - - it "calls update_billable_period once for the entire period" do - expect(updater).to have_received(:update_billable_period) - .with(enterprise, begins_at, ends_at, false) - end - end - - context "when the trial begins before begins_at" do - let(:trial_start) { begins_at - 10.days } - - context "and the trial ends before begins_at" do - let(:trial_expiry) { begins_at - 5.days } - - before do - allow(updater).to receive(:update_billable_period).once - updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) - end - - it "calls update_billable_period once for the entire period" do - expect(updater).to have_received(:update_billable_period) - .with(enterprise, begins_at, ends_at, false) - end - end - - context "and the trial ends after begins_at" do - let(:trial_expiry) { begins_at + 5.days } - - before do - allow(updater).to receive(:update_billable_period).twice - updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) - end - - it "calls update_billable_period once for the trial period" do - expect(updater).to have_received(:update_billable_period) - .with(enterprise, begins_at, trial_expiry, true) - end - - it "calls update_billable_period once for the non-trial period" do - expect(updater).to have_received(:update_billable_period) - .with(enterprise, trial_expiry, ends_at, false) - end - end - - context "and the trial ends after ends_at" do - let(:trial_expiry) { ends_at + 5.days } - - before do - allow(updater).to receive(:update_billable_period).once - updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) - end - - it "calls update_billable_period once for the entire (trial) period" do - expect(updater).to have_received(:update_billable_period) - .with(enterprise, begins_at, ends_at, true) - end - end - end - - context "when the trial begins after begins_at" do - let(:trial_start) { begins_at + 5.days } - - context "and the trial begins after ends_at" do - let(:trial_start) { ends_at + 5.days } - let(:trial_expiry) { ends_at + 10.days } - - before do - allow(updater).to receive(:update_billable_period).once - updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) - end - - it "calls update_billable_period once for the entire period" do - expect(updater).to have_received(:update_billable_period) - .with(enterprise, begins_at, ends_at, false) - end - end - - context "and the trial ends before ends_at" do - let(:trial_expiry) { ends_at - 2.days } - - before do - allow(updater).to receive(:update_billable_period).exactly(3).times - updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) - end - - it "calls update_billable_period once for the non-trial period before the trial" do - expect(updater).to have_received(:update_billable_period) - .with(enterprise, begins_at, trial_start, false) - end - - it "calls update_billable_period once for the trial period" do - expect(updater).to have_received(:update_billable_period) - .with(enterprise, trial_start, trial_expiry, true) - end - - it "calls update_billable_period once for the non-trial period after the trial" do - expect(updater).to have_received(:update_billable_period) - .with(enterprise, trial_expiry, ends_at, false) - end - end - - context "and the trial ends after ends_at" do - let(:trial_expiry) { ends_at + 5.days } - - before do - allow(updater).to receive(:update_billable_period).twice - updater.split_for_trial(enterprise, begins_at, ends_at, trial_start, trial_expiry) - end - - it "calls update_billable_period once for the non-trial period" do - expect(updater).to have_received(:update_billable_period) - .with(enterprise, begins_at, trial_start, false) - end - - it "calls update_billable_period once for the trial period" do - expect(updater).to have_received(:update_billable_period) - .with(enterprise, trial_start, ends_at, true) - end - end - end - end - - describe "update_billable_period" do - let!(:enterprise) { create(:enterprise, sells: 'any') } - - let!(:existing) { create(:billable_period, enterprise: enterprise, begins_at: start_of_july) } - - before do - allow(Spree::Order).to receive(:where) { [ - double(:order, total: 10), - double(:order, total: 20), - double(:order, total: 30) - ] - } - end - - context "when the account invoice is already_complete" do - before do - allow(BillablePeriod).to receive(:where) { [existing] } - allow(existing.account_invoice).to receive(:order) { double(:order, complete?: true ) } - allow(AccountInvoice).to receive(:find_or_create_by_user_id_and_year_and_month) { existing.account_invoice } - end - - it "does not update the billing period, but changes updated_at by touching the billable period " do - expect(existing).to_not receive(:update_attributes) - expect(existing).to receive(:touch) - expect(Bugsnag).to_not receive(:notify) - expect{ - updater.update_billable_period(enterprise, start_of_july, start_of_july + 20.days, false) - }.to_not change{ BillablePeriod.count } - end - end - - context "when arguments match both 'begins_at' and 'enterprise_id' of an existing billable period" do - it "updates the existing billable period" do - expect{ - updater.update_billable_period(enterprise, start_of_july, start_of_july + 20.days, false) - }.to_not change{ BillablePeriod.count } - existing.reload - expect(existing.owner_id).to eq enterprise.owner_id - expect(existing.ends_at).to eq start_of_july + 20.days - expect(existing.sells).to eq enterprise.sells - expect(existing.trial).to eq false - expect(existing.turnover).to eq 60 - end - - context "when there is nothing to update" do - before do - Timecop.freeze(start_of_july + 3.days) { - existing.update_attributes( - begins_at: start_of_july, - ends_at: start_of_july + 20.days, - trial: false, - sells: enterprise.sells, - turnover: 60 - ) - } - end - - it "changes updated_at anyway by touching the billable period" do - Timecop.freeze(start_of_july + 10.days) { - expect{ - updater.update_billable_period(enterprise, start_of_july, start_of_july + 20.days, false) - }.to change{ existing.reload.updated_at } - .from(start_of_july + 3.days) - .to(start_of_july + 10.days) - } - end - end - end - - context "when 'begins_at' does not match an existing billable period" do - before do - expect{ - updater.update_billable_period(enterprise, start_of_july + 20.days, start_of_july + 30.days, false) - }.to change{ BillablePeriod.count }.from(1).to(2) - end - - it "creates a new existing billable period" do - billable_period = BillablePeriod.last - expect(billable_period.owner_id).to eq enterprise.owner_id - expect(billable_period.ends_at).to eq start_of_july + 30.days - expect(billable_period.sells).to eq enterprise.sells - expect(billable_period.trial).to eq false - expect(billable_period.turnover).to eq 60 - end - end - - context "when 'enterprise_id' does not match an existing billable period" do - let!(:new_enterprise) { create(:enterprise, sells: 'own') } - - before do - expect{ - updater.update_billable_period(new_enterprise, start_of_july, start_of_july + 20.days, false) - }.to change{ BillablePeriod.count }.from(1).to(2) - end - - it "creates a new existing billable period" do - billable_period = BillablePeriod.last - expect(billable_period.owner_id).to eq new_enterprise.owner_id - expect(billable_period.ends_at).to eq start_of_july + 20.days - expect(billable_period.sells).to eq new_enterprise.sells - expect(billable_period.trial).to eq false - expect(billable_period.turnover).to eq 60 - end - end - end - - context "cleaning up untouched billable periods" do - let(:job_start_time) { Time.zone.now } - let(:enterprise) { create(:enterprise) } - # Updated after start - let!(:bp1) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time + 2.seconds, begins_at: start_of_july, ends_at: start_of_july + 5.days ) } - let!(:bp2) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time + 2.seconds, begins_at: start_of_july + 5.days, ends_at: start_of_july + 10.days ) } - # Updated before start - let!(:bp3) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july, ends_at: start_of_july + 10.days ) } - # Updated before start but begins after end_date - let!(:bp4) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july + 10.days, ends_at: start_of_july + 15.days ) } - # Updated before start but begins at end_date (ie. not before end_date, so should be ignored) EDGE CASE - let!(:bp5) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july + 8.days, ends_at: start_of_july + 10.days ) } - # Updated before start but ends before start_date - let!(:bp6) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july - 10.days, ends_at: start_of_july - 5.days ) } - # Updated before start but ends at start_date (ie. not after start_date, so should be ignored) EDGE CASE - let!(:bp7) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july - 5.days, ends_at: start_of_july ) } - # Updated before start, but order is already complete, so should not be deleted - let!(:bp8) { create(:billable_period, enterprise: enterprise, updated_at: job_start_time - 5.seconds, begins_at: start_of_july, ends_at: start_of_july + 10.days, account_invoice: create(:account_invoice, order: create(:order, state: 'complete', completed_at: 5.minutes.ago))) } - - before do - allow(Bugsnag).to receive(:notify) - allow(updater).to receive(:start_date) { start_of_july } - allow(updater).to receive(:end_date) { start_of_july + 8.days } - updater.clean_up_untouched_billable_periods_for(enterprise, job_start_time) - end - - it "soft deletes untouched billable_periods" do - expect(bp1.reload.deleted_at).to be_nil - expect(bp2.reload.deleted_at).to be_nil - expect(bp3.reload.deleted_at).to_not be_nil - expect(bp4.reload.deleted_at).to be_nil - expect(bp5.reload.deleted_at).to be_nil - expect(bp6.reload.deleted_at).to be_nil - expect(bp7.reload.deleted_at).to be_nil - expect(bp8.reload.deleted_at).to be_nil - end - - it "notifies bugsnag" do - expect(Bugsnag).to have_received(:notify).once - end - end - end - - describe "validation spec" do - # Chose july to test with because June has 30 days and so is easy to calculate end date for shop trial - let!(:year) { Time.zone.now.year } - let!(:start_of_july) { Time.zone.local(year, 7) } - - let!(:enterprise) { create(:supplier_enterprise, sells: 'any') } - - let!(:original_owner) { enterprise.owner } - - let!(:new_owner) { create(:user) } - - let!(:account_invoice1) { create(:account_invoice, user: original_owner, year: year, month: 7)} - let!(:account_invoice2) { create(:account_invoice, user: new_owner, year: year, month: 7)} - - # This BP was updated before the current run and so should be marked for deletion at the end of the run - let!(:obsolete_bp) { create(:billable_period, enterprise: enterprise, updated_at: start_of_july + 10.days, begins_at: start_of_july + 6.5.days, ends_at: start_of_july + 10.days ) } - - # This one has an updated_at in the future (so that it doesn't get deleted) - # It also has a begins_at date which matches a period that would otherwise be created, - # and so it should be picked up and overwritten - let!(:bp_to_overwrite) { create(:billable_period, enterprise: enterprise, updated_at: start_of_july + 21.days, begins_at: start_of_july + 10.days, ends_at: start_of_july + 15.days ) } - - let!(:order1) { create(:order, completed_at: start_of_july + 1.days, distributor: enterprise) } - let!(:order2) { create(:order, completed_at: start_of_july + 3.days, distributor: enterprise) } - let!(:order3) { create(:order, completed_at: start_of_july + 5.days, distributor: enterprise) } - let!(:order4) { create(:order, completed_at: start_of_july + 7.days, distributor: enterprise) } - let!(:order5) { create(:order, completed_at: start_of_july + 9.days, distributor: enterprise) } - let!(:order6) { create(:order, completed_at: start_of_july + 11.days, distributor: enterprise) } - let!(:order7) { create(:order, completed_at: start_of_july + 13.days, distributor: enterprise) } - let!(:order8) { create(:order, completed_at: start_of_july + 15.days, distributor: enterprise) } - let!(:order9) { create(:order, completed_at: start_of_july + 17.days, distributor: enterprise) } - let!(:order10) { create(:order, completed_at: start_of_july + 19.days, distributor: enterprise) } - - before do - order1.line_items = [ create(:line_item_with_shipment, price: 12.56, order: order1) ] - order2.line_items = [ create(:line_item_with_shipment, price: 87.44, order: order2) ] - order3.line_items = [ create(:line_item_with_shipment, price: 50.00, order: order3) ] - order4.line_items = [ create(:line_item_with_shipment, price: 73.37, order: order4) ] - order5.line_items = [ create(:line_item_with_shipment, price: 22.46, order: order5) ] - order6.line_items = [ create(:line_item_with_shipment, price: 44.85, order: order6) ] - order7.line_items = [ create(:line_item_with_shipment, price: 93.45, order: order7) ] - order8.line_items = [ create(:line_item_with_shipment, price: 59.38, order: order8) ] - order9.line_items = [ create(:line_item_with_shipment, price: 47.23, order: order9) ] - order10.line_items = [ create(:line_item_with_shipment, price: 2.35, order: order10) ] - [order1, order2, order3, order4, order5, order6, order7, order8, order9, order10].each(&:update!) - - allow(Enterprise).to receive(:where) { double(:enterprises, select: [enterprise]) } - end - - context "super complex example", versioning: true do - before do - enterprise.update_attribute(:created_at, start_of_july + 2.days) - - Timecop.freeze(start_of_july + 4.days) { enterprise.update_attribute(:sells, 'own') } - - Timecop.freeze(start_of_july + 6.days) { enterprise.update_attribute(:owner, new_owner) } - - enterprise.update_attribute(:shop_trial_start_date, start_of_july + 8.days) - - Timecop.freeze(start_of_july + 10.days) { enterprise.update_attribute(:owner, original_owner) } - - Timecop.freeze(start_of_july + 12.days) { enterprise.update_attribute(:sells, 'any') } - - allow(enterprise).to receive(:shop_trial_expiry) { start_of_july + 14.days } - - Timecop.freeze(start_of_july + 16.days) { enterprise.update_attribute(:sells, 'own') } - - Timecop.freeze(start_of_july + 18.days) { enterprise.update_attribute(:owner, new_owner) } - end - - travel_to(20.days) - - before do - UpdateBillablePeriods.new.perform - end - - let(:billable_periods) { BillablePeriod.order(:updated_at) } - - it "creates the correct billable periods and deleted obsolete ones" do - expect(obsolete_bp.reload.deleted_at).to_not be_nil - - bp_to_overwrite.reload - - expect(bp_to_overwrite.sells).to eq 'own' - expect(bp_to_overwrite.trial).to be true - expect(bp_to_overwrite.owner).to eq original_owner - expect(bp_to_overwrite.begins_at).to eq start_of_july + 10.days - expect(bp_to_overwrite.ends_at).to eq start_of_july + 12.days - expect(bp_to_overwrite.turnover).to eq order6.total - expect(bp_to_overwrite.account_invoice).to eq account_invoice1 - - expect(billable_periods.count).to eq 9 - - expect(account_invoice1.billable_periods.sort).to eq billable_periods.sort.select{ |bp| bp.owner == original_owner } - expect(account_invoice2.billable_periods.sort).to eq billable_periods.sort.select{ |bp| bp.owner == new_owner } - - expect(billable_periods.map(&:begins_at)).to eq [ - start_of_july + 2.days, - start_of_july + 4.days, - start_of_july + 6.days, - start_of_july + 8.days, - start_of_july + 10.days, - start_of_july + 12.days, - start_of_july + 14.days, - start_of_july + 16.days, - start_of_july + 18.days - ] - - expect(billable_periods.map(&:ends_at)).to eq [ - start_of_july + 4.days, - start_of_july + 6.days, - start_of_july + 8.days, - start_of_july + 10.days, - start_of_july + 12.days, - start_of_july + 14.days, - start_of_july + 16.days, - start_of_july + 18.days, - start_of_july + 20.days - ] - - expect(billable_periods.map(&:owner)).to eq [ - original_owner, - original_owner, - new_owner, - new_owner, - original_owner, - original_owner, - original_owner, - original_owner, - new_owner - ] - - expect(billable_periods.map(&:sells)).to eq [ - 'any', - 'own', - 'own', - 'own', - 'own', - 'any', - 'any', - 'own', - 'own' - ] - - expect(billable_periods.map(&:trial)).to eq [ - false, - false, - false, - true, - true, - true, - false, - false, - false - ] - - expect(billable_periods.map(&:turnover)).to eq [ - order2.total, - order3.total, - order4.total, - order5.total, - order6.total, - order7.total, - order8.total, - order9.total, - order10.total - ] - end - end - end -end diff --git a/spec/lib/open_food_network/xero_invoices_report_spec.rb b/spec/lib/open_food_network/xero_invoices_report_spec.rb index e5ae74d8d0..d2a437d57f 100644 --- a/spec/lib/open_food_network/xero_invoices_report_spec.rb +++ b/spec/lib/open_food_network/xero_invoices_report_spec.rb @@ -31,7 +31,6 @@ module OpenFoodNetwork allow(report).to receive(:shipping_summary_rows) { ['shipping'] } allow(report).to receive(:payment_summary_rows) { ['payment'] } allow(report).to receive(:admin_adjustment_summary_rows) { ['admin'] } - allow(order).to receive(:account_invoice?) { false } end it "displays produce summary rows when summary report" do @@ -46,22 +45,14 @@ module OpenFoodNetwork it "displays fee summary rows when summary report" do allow(report).to receive(:detail?) { false } - allow(order).to receive(:account_invoice?) { true } expect(summary_rows).to include 'fee' end - it "displays fee summary rows when this is not an account invoice" do + it "displays fee summary rows when detail report" do allow(report).to receive(:detail?) { true } - allow(order).to receive(:account_invoice?) { false } expect(summary_rows).to include 'fee' end - it "does not display fee summary rows when this is a detail report for an account invoice" do - allow(report).to receive(:detail?) { true } - allow(order).to receive(:account_invoice?) { true } - expect(summary_rows).not_to include 'fee' - end - it "always displays shipping summary rows" do expect(summary_rows).to include 'shipping' end @@ -76,24 +67,6 @@ module OpenFoodNetwork end end - describe "finding account invoice adjustments" do - let(:report) { XeroInvoicesReport.new user, initial_invoice_number: '', invoice_date: '', due_date: '', account_code: '' } - let!(:order) { create(:order) } - let(:billable_period) { create(:billable_period) } - let(:shipping_method) { create(:shipping_method) } - let!(:adj_invoice) { create(:adjustment, adjustable: order, label: 'Account invoice item', source: billable_period) } - let!(:adj_shipping) { create(:adjustment, adjustable: order, label: "Shipping", originator: shipping_method) } - - it "returns BillablePeriod adjustments only" do - expect(report.send(:account_invoice_adjustments, order)).to eq([adj_invoice]) - end - - it "excludes adjustments where the source is missing" do - billable_period.destroy - expect(report.send(:account_invoice_adjustments, order)).to be_empty - end - end - describe "generating invoice numbers" do let(:order) { double(:order, number: 'R731032860') } diff --git a/spec/models/billable_period_spec.rb b/spec/models/billable_period_spec.rb deleted file mode 100644 index 7095080920..0000000000 --- a/spec/models/billable_period_spec.rb +++ /dev/null @@ -1,558 +0,0 @@ -require 'spec_helper' - -describe BillablePeriod, type: :model do - - require 'spec_helper' - - describe 'ensure_correct_adjustment' do - let!(:start_of_july) { Time.zone.now.beginning_of_year + 6.months } - let!(:user) { create(:user) } - let!(:invoice) { create(:order, user: user) } - let!(:subject) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 12.days) } - - before do - allow(subject).to receive(:bill) { 99 } - allow(subject).to receive(:adjustment_label) { "Label for adjustment" } - Spree::Config.set({ account_invoices_tax_rate: 0.1 }) - end - - context "when no adjustment currently exists" do - it "creates an adjustment on the given order" do - expect(invoice.total_tax).to eq 0.0 - expect(subject.adjustment).to be nil - subject.ensure_correct_adjustment_for(invoice) - expect(subject.adjustment).to be_a Spree::Adjustment - expect(invoice.total_tax).to eq 9.0 - end - end - end - - describe "calculating monthly bills for enterprises with no turnover" do - let!(:subject) { create(:billable_period, turnover: 0) } - - context "when no tax is charged" do - before { Spree::Config.set(:account_invoices_tax_rate, 0) } - - context "when minimum billable turnover is zero" do - before { Spree::Config.set(:minimum_billable_turnover, 0) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - it { expect(subject.bill).to eq 10 } - end - - context "when no fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - it { expect(subject.bill).to eq 0 } - end - end - - context "when minimum billable turnover is > zero" do - before { Spree::Config.set(:minimum_billable_turnover, 1) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - it { expect(subject.bill).to eq 0 } - end - - context "when no fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - it { expect(subject.bill).to eq 0 } - end - end - end - - context "when tax is charged" do - before { Spree::Config.set(:account_invoices_tax_rate, 0.1) } - - context "when minimum billable turnover is zero" do - before { Spree::Config.set(:minimum_billable_turnover, 0) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - it { expect(subject.bill).to eq 11 } - end - - context "when no fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - it { expect(subject.bill).to eq 0 } - end - end - - context "when minimum billable turnover is > zero" do - before { Spree::Config.set(:minimum_billable_turnover, 1) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - it { expect(subject.bill).to eq 0 } - end - - context "when no fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - it { expect(subject.bill).to eq 0 } - end - end - end - end - - describe "calculating monthly bills for enterprises" do - let!(:subject) { create(:billable_period, turnover: 100) } - - context "when no tax is charged" do - before { Spree::Config.set(:account_invoices_tax_rate, 0) } - - context "when no minimum billable turnover" do - before { Spree::Config.set(:minimum_billable_turnover, 0) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } - - context "when the bill is capped" do - context "at a level higher than the fixed charge plus the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 65) } - it { expect(subject.bill).to eq 60 } - end - - context "at a level lower than the fixed charge plus the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 55) } - it { expect(subject.bill).to eq 55 } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 60 } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - context "at a level higher than the fixed charge" do - before { Spree::Config.set(:account_invoices_monthly_cap, 15) } - it { expect(subject.bill).to eq 10 } - end - - context "at a level lower than the fixed charge" do - before { Spree::Config.set(:account_invoices_monthly_cap, 5) } - it { expect(subject.bill).to eq 5 } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 10 } - end - end - end - - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } - - context "when the bill is capped" do - context "at a level higher than the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 55) } - it { expect(subject.bill).to eq 50 } - end - - context "at a level lower than the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 45) } - it { expect(subject.bill).to eq 45 } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 50 } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(subject.bill).to eq 0 } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 0 } - end - end - end - end - - context "when turnover is above minimum billable turnover" do - before { Spree::Config.set(:minimum_billable_turnover, 99) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } - - context "when the bill is capped" do - context "at a level higher than the fixed charge plus the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 65) } - it { expect(subject.bill).to eq 60 } - end - - context "at a level lower than the fixed charge plus the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 55) } - it { expect(subject.bill).to eq 55 } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 60 } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - context "at a level higher than the fixed charge" do - before { Spree::Config.set(:account_invoices_monthly_cap, 15) } - it { expect(subject.bill).to eq 10 } - end - - context "at a level lower than the fixed charge" do - before { Spree::Config.set(:account_invoices_monthly_cap, 5) } - it { expect(subject.bill).to eq 5 } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 10 } - end - end - end - - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } - - context "when the bill is capped" do - context "at a level higher than the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 55) } - it { expect(subject.bill).to eq 50 } - end - - context "at a level lower than the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 45) } - it { expect(subject.bill).to eq 45 } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 50 } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(subject.bill).to eq 0 } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 0 } - end - end - end - end - - context "when turnover is below minimum billable turnover" do - before { Spree::Config.set(:minimum_billable_turnover, 101) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } - - context "when the bill is capped" do - context "at a level higher than the fixed charge plus the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 65) } - it { expect(subject.bill).to eq 0 } - end - - context "at a level lower than the fixed charge plus the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 55) } - it { expect(subject.bill).to eq 0 } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 0 } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - context "at a level higher than the fixed charge" do - before { Spree::Config.set(:account_invoices_monthly_cap, 15) } - it { expect(subject.bill).to eq 0 } - end - - context "at a level lower than the fixed charge" do - before { Spree::Config.set(:account_invoices_monthly_cap, 5) } - it { expect(subject.bill).to eq 0 } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 0 } - end - end - end - - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } - - context "when the bill is capped" do - context "at a level higher than the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 55) } - it { expect(subject.bill).to eq 0 } - end - - context "at a level lower than the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 45) } - it { expect(subject.bill).to eq 0 } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 0 } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(subject.bill).to eq 0 } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 0 } - end - end - end - end - - context "when tax is charged" do - before { Spree::Config.set(:account_invoices_tax_rate, 0.1) } - - context "when turnover is above minimum billable turnover" do - before { Spree::Config.set(:minimum_billable_turnover, 99) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } - - context "when the bill is capped" do - context "at a level higher than the fixed charge plus the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 61) } - it { expect(subject.bill).to eq 66 } - end - - context "at a level lower than the fixed charge plus the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 59) } - it { - expect(subject.bill.to_f).to eq 64.9 - } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 66 } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - context "at a level higher than the fixed charge" do - before { Spree::Config.set(:account_invoices_monthly_cap, 11) } - it { expect(subject.bill).to eq 11 } - end - - context "at a level lower than the fixed charge" do - before { Spree::Config.set(:account_invoices_monthly_cap, 9) } - it { expect(subject.bill.to_f).to eq 9.9 } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 11 } - end - end - end - - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } - - context "when the bill is capped" do - context "at a level higher than the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 51) } - it { expect(subject.bill).to eq 55 } - end - - context "at a level lower than the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 49) } - it { expect(subject.bill.to_f).to eq 53.9 } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 55 } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(subject.bill).to eq 0 } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 0 } - end - end - end - end - end - end - - context "when turnover is below minimum billable turnover" do - before { Spree::Config.set(:minimum_billable_turnover, 101) } - - context "when a fixed cost is included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 10) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } - - context "when the bill is capped" do - context "at a level higher than the fixed charge plus the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 61) } - it { expect(subject.bill).to eq 0 } - end - - context "at a level lower than the fixed charge plus the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 59) } - it { - expect(subject.bill.to_f).to eq 0 - } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 0 } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - context "at a level higher than the fixed charge" do - before { Spree::Config.set(:account_invoices_monthly_cap, 11) } - it { expect(subject.bill).to eq 0 } - end - - context "at a level lower than the fixed charge" do - before { Spree::Config.set(:account_invoices_monthly_cap, 9) } - it { expect(subject.bill.to_f).to eq 0 } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 0 } - end - end - end - - context "when a fixed cost is not included" do - before { Spree::Config.set(:account_invoices_monthly_fixed, 0) } - - context "when a percentage of turnover is included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0.5) } - - context "when the bill is capped" do - context "at a level higher than the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 51) } - it { expect(subject.bill).to eq 0 } - end - - context "at a level lower than the product of the rate and turnover" do - before { Spree::Config.set(:account_invoices_monthly_cap, 49) } - it { expect(subject.bill.to_f).to eq 0 } - end - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 0 } - end - end - - context "when a percentage of turnover is not included" do - before { Spree::Config.set(:account_invoices_monthly_rate, 0) } - - context "when the bill is capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 20) } - it { expect(subject.bill).to eq 0 } - end - - context "when the bill is not capped" do - before { Spree::Config.set(:account_invoices_monthly_cap, 0) } - it { expect(subject.bill).to eq 0 } - end - end - end - end - end -end diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 305ba29138..952ca743b7 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -533,24 +533,6 @@ describe Spree::Order do end end - describe "checking if an order is an account invoice" do - let(:accounts_distributor) { create(:distributor_enterprise) } - let(:order_account_invoice) { create(:order, distributor: accounts_distributor) } - let(:order_general) { create(:order, distributor: create(:distributor_enterprise)) } - - before do - Spree::Config.accounts_distributor_id = accounts_distributor.id - end - - it "returns true when the order is distributed by the accounts distributor" do - order_account_invoice.should be_account_invoice - end - - it "returns false otherwise" do - order_general.should_not be_account_invoice - end - end - describe "sending confirmation emails" do let!(:distributor) { create(:distributor_enterprise) } let!(:order) { create(:order, distributor: distributor) } @@ -561,14 +543,6 @@ describe Spree::Order do end.to enqueue_job ConfirmOrderJob end - it "does not send confirmation emails when distributor is the accounts_distributor" do - Spree::Config.set({ accounts_distributor_id: distributor.id }) - - expect do - order.deliver_order_confirmation_email - end.to_not enqueue_job ConfirmOrderJob - end - it "does not send confirmation emails when the order belongs to a subscription" do create(:proxy_order, order: order) From a870299cedc4c314bf10ae93f11b54b1ffa8bcd6 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Thu, 21 Feb 2019 13:26:46 +0000 Subject: [PATCH 34/57] Delete preferences and adjustments related to account invoices --- ...622_delete_account_invoices_preferences.rb | 22 +++++++++++++++++++ ...741_delete_account_invoices_adjustments.rb | 11 ++++++++++ db/schema.rb | 8 +++---- 3 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20190221131622_delete_account_invoices_preferences.rb create mode 100644 db/migrate/20190221131741_delete_account_invoices_adjustments.rb diff --git a/db/migrate/20190221131622_delete_account_invoices_preferences.rb b/db/migrate/20190221131622_delete_account_invoices_preferences.rb new file mode 100644 index 0000000000..e55a61f721 --- /dev/null +++ b/db/migrate/20190221131622_delete_account_invoices_preferences.rb @@ -0,0 +1,22 @@ +class DeleteAccountInvoicesPreferences < ActiveRecord::Migration + def up + Spree::Preference + .where( key: ['spree/app_configuration/accounts_distributor_id', + 'spree/app_configuration/default_accounts_payment_method_id', + 'spree/app_configuration/default_accounts_shipping_method_id', + 'spree/app_configuration/auto_update_invoices', + 'spree/app_configuration/auto_finalize_invoices', + 'spree/app_configuration/account_invoices_monthly_fixed', + 'spree/app_configuration/account_invoices_monthly_rate', + 'spree/app_configuration/account_invoices_monthly_cap', + 'spree/app_configuration/account_invoices_tax_rate', + 'spree/app_configuration/shop_trial_length_days', + 'spree/app_configuration/minimum_billable_turnover']) + .destroy_all + end + + def down + # If these preferences are re-added to app/models/spree/app_configuration_decorator.rb + # these DB entries will be regenerated + end +end diff --git a/db/migrate/20190221131741_delete_account_invoices_adjustments.rb b/db/migrate/20190221131741_delete_account_invoices_adjustments.rb new file mode 100644 index 0000000000..f8baa0e5bf --- /dev/null +++ b/db/migrate/20190221131741_delete_account_invoices_adjustments.rb @@ -0,0 +1,11 @@ +class DeleteAccountInvoicesAdjustments < ActiveRecord::Migration + def up + Spree::Adjustment + .where(:source_type => 'BillablePeriod') + .destroy_all + end + + def down + # This data does not need to be recovered + end +end diff --git a/db/schema.rb b/db/schema.rb index 696182139d..0f3f29f08c 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 => 20181128054803) do +ActiveRecord::Schema.define(:version => 20190221131741) do create_table "account_invoices", :force => true do |t| t.integer "user_id", :null => false @@ -533,9 +533,9 @@ ActiveRecord::Schema.define(:version => 20181128054803) do t.datetime "updated_at", :null => false t.integer "max_quantity" t.string "currency" - t.decimal "distribution_fee", :precision => 10, :scale => 2 - t.decimal "final_weight_volume", :precision => 10, :scale => 2 - t.decimal "cost_price", :precision => 8, :scale => 2 + t.decimal "distribution_fee", :precision => 10, :scale => 2 + t.decimal "final_weight_volume", :precision => 10, :scale => 2 + t.decimal "cost_price", :precision => 8, :scale => 2 t.integer "tax_category_id" end From 0501db1782934d8c48db04cbd7cee6e713199cb2 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Thu, 21 Feb 2019 22:40:23 +0000 Subject: [PATCH 35/57] Remove changes related to enterprise trials as they dont make sense without business models and account invoices --- .../admin/components/trial_progess_bar.scss | 12 --- .../admin/enterprises_controller.rb | 4 - app/helpers/enterprises_helper.rb | 16 ---- app/models/enterprise.rb | 4 - .../enterprises/_change_type_form.html.haml | 15 +--- app/views/shops/signup.html.haml | 3 - .../single_enterprise_dashboard.html.haml | 2 - .../shared/_trial_progress_bar.html.haml | 7 -- config/locales/en.yml | 14 --- ...2_drop_enterprise_shop_trial_start_date.rb | 9 ++ db/schema.rb | 3 +- .../admin/enterprises_controller_spec.rb | 90 +++---------------- 12 files changed, 21 insertions(+), 158 deletions(-) delete mode 100644 app/assets/stylesheets/admin/components/trial_progess_bar.scss delete mode 100644 app/views/spree/admin/shared/_trial_progress_bar.html.haml create mode 100644 db/migrate/20190221214542_drop_enterprise_shop_trial_start_date.rb diff --git a/app/assets/stylesheets/admin/components/trial_progess_bar.scss b/app/assets/stylesheets/admin/components/trial_progess_bar.scss deleted file mode 100644 index bfcc743c58..0000000000 --- a/app/assets/stylesheets/admin/components/trial_progess_bar.scss +++ /dev/null @@ -1,12 +0,0 @@ -@import "../variables"; - -#trial_progress_bar { - position: fixed; - left: 0px; - bottom: 0px; - width: 100vw; - padding: 8px 10px; - font-weight: bold; - background-color: $spree-blue; - color: white; -} diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 11dd547042..c59f610436 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -64,10 +64,6 @@ module Admin attributes = { sells: params[:sells], visible: true } - if ['own', 'any'].include? params[:sells] - attributes[:shop_trial_start_date] = @enterprise.shop_trial_start_date || Time.zone.now - end - if @enterprise.update_attributes(attributes) flash[:success] = I18n.t(:enterprise_register_success_notice, enterprise: @enterprise.name) redirect_to admin_path diff --git a/app/helpers/enterprises_helper.rb b/app/helpers/enterprises_helper.rb index e1b85fc14b..899d6156bb 100644 --- a/app/helpers/enterprises_helper.rb +++ b/app/helpers/enterprises_helper.rb @@ -78,22 +78,6 @@ module EnterprisesHelper link_to_with_icon 'icon-trash', name, url, options end - def shop_trial_in_progress?(enterprise) - !!enterprise.shop_trial_start_date && - (enterprise.shop_trial_start_date + Spree::Config[:shop_trial_length_days].days > Time.zone.now) && - %w(own any).include?(enterprise.sells) - end - - def shop_trial_expired?(enterprise) - !!enterprise.shop_trial_start_date && - (enterprise.shop_trial_start_date + Spree::Config[:shop_trial_length_days].days <= Time.zone.now) && - %w(own any).include?(enterprise.sells) - end - - def remaining_trial_days(enterprise) - distance_of_time_in_words(Time.zone.now, enterprise.shop_trial_start_date + Spree::Config[:shop_trial_length_days].days) - end - def order_changes_allowed? current_order.andand.distributor.andand.allow_order_changes? end diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 26f45ad503..2e4bc66fcd 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -321,10 +321,6 @@ class Enterprise < ActiveRecord::Base end end - def shop_trial_expiry - shop_trial_start_date.andand + Spree::Config[:shop_trial_length_days].days - end - def can_invoice? abn.present? end diff --git a/app/views/admin/enterprises/_change_type_form.html.haml b/app/views/admin/enterprises/_change_type_form.html.haml index dfaebabf0d..687ee3af3b 100644 --- a/app/views/admin/enterprises/_change_type_form.html.haml +++ b/app/views/admin/enterprises/_change_type_form.html.haml @@ -42,12 +42,6 @@ %p.description = t('.producer_hub_description_text') - -# %p.description - -# Test out having your own shopfront with full access to all Shopfront features for 30 days. - -# %br - -# %br - -# At the end of your trial, there is a one-off $200 fee to fully activate your account. Then you will be billed for 2% of your actual transactions, capped at $50 a month (so if you don’t sell anything you don’t pay anything, but you never pay more than $50 a month). - - else .two.columns.alpha   @@ -76,14 +70,7 @@ .sixteen.columns.alpha %span.error{ ng: { show: "(change_type.sells.$error.required || change_type.sells.$error.pattern) && submitted" } } = t('.choose_option') - - if @enterprise.sells == 'unspecified' && @enterprise.shop_trial_start_date.nil? - -if free_use? - %input.button.big{ type: 'submit', value: t(:select_continue), ng: { click: "submit(change_type)" } } - - else - - trial_length = Spree::Config[:shop_trial_length_days] - %input.button.big{ type: 'submit', value: "Start #{trial_length}-Day Shop Trial", ng: { click: "submit(change_type)", show: "sells=='own' || sells=='any'" } } - %input.button.big{ type: 'submit', value: t(:select_continue), ng: { click: "submit(change_type)", hide: "sells=='own' || sells=='any'" } } - - elsif @enterprise.sells == 'unspecified' + - if @enterprise.sells == 'unspecified' %input.button.big{ type: 'submit', value: t(:select_continue), ng: { click: "submit(change_type)" } } - else %input.button.big{ type: 'submit', value: t('.change_now'), ng: { click: "submit(change_type)" } } diff --git a/app/views/shops/signup.html.haml b/app/views/shops/signup.html.haml index fd8dc3a0da..72f63a0dca 100644 --- a/app/views/shops/signup.html.haml +++ b/app/views/shops/signup.html.haml @@ -19,9 +19,6 @@ .small-12.medium-10.medium-offset-1.columns.text-center %h2 = t :shops_signup_pricing - -# %p.text-big - -# If there is a time-sensitive offer you can write it here, e.g. - -# Sign up before 30th June for an extra month free! %br = ContentConfig.hub_signup_pricing_table_html.html_safe diff --git a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml index dac3df1b8f..caa8e925c4 100644 --- a/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml +++ b/app/views/spree/admin/overview/single_enterprise_dashboard.html.haml @@ -89,5 +89,3 @@ %a.button.bottom{href: main_app.admin_order_cycles_path} = t "manage_order_cycles" %span.icon-arrow-right - -= render 'spree/admin/shared/trial_progress_bar', enterprise: @enterprise diff --git a/app/views/spree/admin/shared/_trial_progress_bar.html.haml b/app/views/spree/admin/shared/_trial_progress_bar.html.haml deleted file mode 100644 index 4c76e40705..0000000000 --- a/app/views/spree/admin/shared/_trial_progress_bar.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -- if enterprise - -if shop_trial_in_progress?(enterprise) - #trial_progress_bar - = "#{t(:shop_trial_expires_in)} #{remaining_trial_days(enterprise)}." - -elsif shop_trial_expired?(enterprise) - #trial_progress_bar - = t(:shop_trial_expired_notice) diff --git a/config/locales/en.yml b/config/locales/en.yml index b2be034d58..d9c3f67b48 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -202,17 +202,9 @@ en: confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" must_have_valid_business_number: "%{enterprise_name} must have a valid ABN before invoices can be sent." invoice: "Invoice" - percentage_of_sales: "%{percentage} of sales" - capped_at_cap: "capped at %{cap}" - per_month: "per month" - free: "free" - free_trial: "free trial" - plus_tax: "plus GST" - min_bill_turnover_desc: "once turnover exceeds %{mbt_amount}" more: "More" say_no: "No" say_yes: "Yes" - then: then ongoing: Ongoing bill_address: Billing Address ship_address: Shipping Address @@ -1767,10 +1759,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using products_distributor_info: When you select a distributor for your order, their address and pickup times will be displayed here. products_distribution_adjustment_label: "Product distribution by %{distributor} for %{product}" - shop_trial_expires_in: "Your shopfront trial expires in" - shop_trial_expired_notice: "Good news! We have decided to extend shopfront trials until further notice." - - # keys used in javascript password: Password remember_me: Remember Me @@ -2154,7 +2142,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using live: "live" manage: "Manage" resend: "Resend" - trial: Trial add_and_manage_products: "Add & manage products" add_and_manage_order_cycles: "Add & manage order cycles" manage_order_cycles: "Manage order cycles" @@ -2187,7 +2174,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using hub_sidebar_at_least: "At least one hub must be selected" hub_sidebar_blue: "blue" hub_sidebar_red: "red" - shop_trial_in_progress: "Your shopfront trial expires in %{days}." report_customers_distributor: "Distributor" report_customers_supplier: "Supplier" report_customers_cycle: "Order Cycle" diff --git a/db/migrate/20190221214542_drop_enterprise_shop_trial_start_date.rb b/db/migrate/20190221214542_drop_enterprise_shop_trial_start_date.rb new file mode 100644 index 0000000000..69dd360e2c --- /dev/null +++ b/db/migrate/20190221214542_drop_enterprise_shop_trial_start_date.rb @@ -0,0 +1,9 @@ +class DropEnterpriseShopTrialStartDate < ActiveRecord::Migration + def up + remove_column :enterprises, :shop_trial_start_date + end + + def down + add_column :enterprises, :shop_trial_start_date, :datetime, default: nil + end +end diff --git a/db/schema.rb b/db/schema.rb index 0f3f29f08c..6e4426e2bb 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 => 20190221131741) do +ActiveRecord::Schema.define(:version => 20190221214542) do create_table "account_invoices", :force => true do |t| t.integer "user_id", :null => false @@ -233,7 +233,6 @@ ActiveRecord::Schema.define(:version => 20190221131741) do t.string "linkedin" t.integer "owner_id", :null => false t.string "sells", :default => "none", :null => false - t.datetime "shop_trial_start_date" t.boolean "producer_profile_only", :default => false t.string "permalink", :null => false t.boolean "charges_sales_tax", :default => false, :null => false diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index f95bc6e8da..5c9fbea31a 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -344,90 +344,20 @@ module Admin enterprise.save! end - context "if the trial has finished" do - let(:trial_start) { 30.days.ago.beginning_of_day } - - before do - enterprise.update_attribute(:shop_trial_start_date, trial_start) - end - - it "is allowed" do - Timecop.freeze(Time.zone.local(2015, 4, 16, 14, 0, 0)) do - spree_post :register, { id: enterprise, sells: 'own' } - expect(response).to redirect_to spree.admin_path - expect(enterprise.reload.sells).to eq 'own' - expect(enterprise.shop_trial_start_date).to eq trial_start - end - end - end - - context "if the trial has not finished" do - let(:trial_start) { Date.current.to_time } - - before do - enterprise.update_attribute(:shop_trial_start_date, trial_start) - end - - it "is allowed, but trial start date is not reset" do - spree_post :register, { id: enterprise, sells: 'own' } - expect(response).to redirect_to spree.admin_path - expect(enterprise.reload.sells).to eq 'own' - expect(enterprise.shop_trial_start_date).to eq trial_start - end - end - - context "if a trial has not started" do - it "is allowed" do - spree_post :register, { id: enterprise, sells: 'own' } - expect(response).to redirect_to spree.admin_path - expect(flash[:success]).to eq "Congratulations! Registration for #{enterprise.name} is complete!" - expect(enterprise.reload.sells).to eq 'own' - expect(enterprise.reload.shop_trial_start_date).to be > Time.zone.now-(1.minute) - end + it "is allowed" do + spree_post :register, { id: enterprise, sells: 'own' } + expect(response).to redirect_to spree.admin_path + expect(flash[:success]).to eq "Congratulations! Registration for #{enterprise.name} is complete!" + expect(enterprise.reload.sells).to eq 'own' end end context "setting 'sells' to any" do - context "if the trial has finished" do - let(:trial_start) { 30.days.ago.beginning_of_day } - - before do - enterprise.update_attribute(:shop_trial_start_date, trial_start) - end - - it "is allowed" do - Timecop.freeze(Time.zone.local(2015, 4, 16, 14, 0, 0)) do - spree_post :register, { id: enterprise, sells: 'any' } - expect(response).to redirect_to spree.admin_path - expect(enterprise.reload.sells).to eq 'any' - expect(enterprise.shop_trial_start_date).to eq trial_start - end - end - end - - context "if the trial has not finished" do - let(:trial_start) { Date.current.to_time } - - before do - enterprise.update_attribute(:shop_trial_start_date, trial_start) - end - - it "is allowed, but trial start date is not reset" do - spree_post :register, { id: enterprise, sells: 'any' } - expect(response).to redirect_to spree.admin_path - expect(enterprise.reload.sells).to eq 'any' - expect(enterprise.shop_trial_start_date).to eq trial_start - end - end - - context "if a trial has not started" do - it "is allowed" do - spree_post :register, { id: enterprise, sells: 'any' } - expect(response).to redirect_to spree.admin_path - expect(flash[:success]).to eq "Congratulations! Registration for #{enterprise.name} is complete!" - expect(enterprise.reload.sells).to eq 'any' - expect(enterprise.reload.shop_trial_start_date).to be > Time.zone.now-(1.minute) - end + it "is allowed" do + spree_post :register, { id: enterprise, sells: 'any' } + expect(response).to redirect_to spree.admin_path + expect(flash[:success]).to eq "Congratulations! Registration for #{enterprise.name} is complete!" + expect(enterprise.reload.sells).to eq 'any' end end From a1647053cbf1f4a0d87509c0f44fbb58b8311fe7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 25 Feb 2019 19:26:41 +0000 Subject: [PATCH 36/57] Bump combine_pdf from 1.0.15 to 1.0.16 Bumps [combine_pdf](https://github.com/boazsegev/combine_pdf) from 1.0.15 to 1.0.16. - [Release notes](https://github.com/boazsegev/combine_pdf/releases) - [Changelog](https://github.com/boazsegev/combine_pdf/blob/master/CHANGELOG.md) - [Commits](https://github.com/boazsegev/combine_pdf/compare/v1.0.15...v1.0.16) Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index f88886708e..d586e1f7f4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -239,7 +239,7 @@ GEM execjs coffee-script-source (1.10.0) colorize (0.8.1) - combine_pdf (1.0.15) + combine_pdf (1.0.16) ruby-rc4 (>= 0.1.5) compass (1.0.3) chunky_png (~> 1.2) From 68fd6de39b074bea49ace01b59b1e665bda4f4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Turbelin?= Date: Mon, 11 Feb 2019 18:21:59 +0100 Subject: [PATCH 37/57] Update Jquery datetimepicker lib to v1.6.3 --- .../shared/jquery-ui-timepicker-addon.js | 1637 ++++++++++------- .../shared/jquery-ui-timepicker-addon.css | 28 +- 2 files changed, 1047 insertions(+), 618 deletions(-) diff --git a/app/assets/javascripts/shared/jquery-ui-timepicker-addon.js b/app/assets/javascripts/shared/jquery-ui-timepicker-addon.js index b8616da378..d37bc02b48 100644 --- a/app/assets/javascripts/shared/jquery-ui-timepicker-addon.js +++ b/app/assets/javascripts/shared/jquery-ui-timepicker-addon.js @@ -1,18 +1,13 @@ -/* - * jQuery timepicker addon - * By: Trent Richardson [http://trentrichardson.com] - * Version 1.1.1 - * Last Modified: 11/07/2012 - * - * Copyright 2012 Trent Richardson - * You may use this project under MIT or GPL licenses. - * http://trentrichardson.com/Impromptu/GPL-LICENSE.txt - * http://trentrichardson.com/Impromptu/MIT-LICENSE.txt - */ - -/*jslint evil: true, white: false, undef: false, nomen: false */ - -(function($) { +/*! jQuery Timepicker Addon - v1.6.3 - 2016-04-20 +* http://trentrichardson.com/examples/timepicker +* Copyright (c) 2016 Trent Richardson; Licensed MIT */ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery', 'jquery-ui'], factory); + } else { + factory(jQuery); + } +}(function ($) { /* * Lets not redefine timepicker, Prevent "Uncaught RangeError: Maximum call stack size exceeded" @@ -27,17 +22,17 @@ */ $.extend($.ui, { timepicker: { - version: "1.1.1" + version: "1.6.3" } }); - /* + /* * Timepicker manager. * Use the singleton instance of this class, $.timepicker, to interact with the time picker. * Settings for (groups of) time pickers are maintained in an instance object, * allowing multiple different settings on the same page. */ - function Timepicker() { + var Timepicker = function () { this.regional = []; // Available regional settings, indexed by language code this.regional[''] = { // Default regional settings currentText: 'Now', @@ -52,63 +47,73 @@ minuteText: 'Minute', secondText: 'Second', millisecText: 'Millisecond', + microsecText: 'Microsecond', timezoneText: 'Time Zone', isRTL: false }; this._defaults = { // Global defaults for all the datetime picker instances showButtonPanel: true, timeOnly: false, - showHour: true, - showMinute: true, - showSecond: false, - showMillisec: false, - showTimezone: false, + timeOnlyShowDate: false, + showHour: null, + showMinute: null, + showSecond: null, + showMillisec: null, + showMicrosec: null, + showTimezone: null, showTime: true, stepHour: 1, stepMinute: 1, stepSecond: 1, stepMillisec: 1, + stepMicrosec: 1, hour: 0, minute: 0, second: 0, millisec: 0, + microsec: 0, timezone: null, - useLocalTimezone: false, - defaultTimezone: "+0000", hourMin: 0, minuteMin: 0, secondMin: 0, millisecMin: 0, + microsecMin: 0, hourMax: 23, minuteMax: 59, secondMax: 59, millisecMax: 999, + microsecMax: 999, minDateTime: null, maxDateTime: null, + maxTime: null, + minTime: null, onSelect: null, hourGrid: 0, minuteGrid: 0, secondGrid: 0, millisecGrid: 0, + microsecGrid: 0, alwaysSetTime: true, separator: ' ', altFieldTimeOnly: true, altTimeFormat: null, altSeparator: null, altTimeSuffix: null, + altRedirectFocus: true, pickerTimeFormat: null, pickerTimeSuffix: null, showTimepicker: true, - timezoneIso8601: false, timezoneList: null, addSliderAccess: false, sliderAccessArgs: null, controlType: 'slider', + oneLine: false, defaultValue: null, - parse: 'strict' + parse: 'strict', + afterInject: null }; $.extend(this._defaults, this.regional['']); - } + }; $.extend(Timepicker.prototype, { $input: null, @@ -119,35 +124,41 @@ minute_slider: null, second_slider: null, millisec_slider: null, + microsec_slider: null, timezone_select: null, + maxTime: null, + minTime: null, hour: 0, minute: 0, second: 0, millisec: 0, + microsec: 0, timezone: null, - defaultTimezone: "+0000", hourMinOriginal: null, minuteMinOriginal: null, secondMinOriginal: null, millisecMinOriginal: null, + microsecMinOriginal: null, hourMaxOriginal: null, minuteMaxOriginal: null, secondMaxOriginal: null, millisecMaxOriginal: null, + microsecMaxOriginal: null, ampm: '', formattedDate: '', formattedTime: '', formattedDateTime: '', timezoneList: null, - units: ['hour','minute','second','millisec'], + units: ['hour', 'minute', 'second', 'millisec', 'microsec'], + support: {}, control: null, - /* + /* * Override the default settings for all instances of the time picker. - * @param settings object - the new settings to use as defaults (anonymous object) - * @return the manager object + * @param {Object} settings object - the new settings to use as defaults (anonymous object) + * @return {Object} the manager object */ - setDefaults: function(settings) { + setDefaults: function (settings) { extendRemove(this._defaults, settings || {}); return this; }, @@ -155,14 +166,14 @@ /* * Create a new Timepicker instance */ - _newInst: function($input, o) { + _newInst: function ($input, opts) { var tp_inst = new Timepicker(), inlineSettings = {}, - fns = {}, - overrides, i; + fns = {}, + overrides, i; for (var attrName in this._defaults) { - if(this._defaults.hasOwnProperty(attrName)){ + if (this._defaults.hasOwnProperty(attrName)) { var attrValue = $input.attr('time:' + attrName); if (attrValue) { try { @@ -173,83 +184,104 @@ } } } - overrides = { - beforeShow: function (input, dp_inst) { - if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) { - return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst); - } - }, - onChangeMonthYear: function (year, month, dp_inst) { - // Update the time as well : this prevents the time from disappearing from the $input field. - tp_inst._updateDateTime(dp_inst); - if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) { - tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst); - } - }, - onClose: function (dateText, dp_inst) { - if (tp_inst.timeDefined === true && $input.val() !== '') { - tp_inst._updateDateTime(dp_inst); - } - if ($.isFunction(tp_inst._defaults.evnts.onClose)) { - tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst); - } - } - }; - for (i in overrides) { - if (overrides.hasOwnProperty(i)) { - fns[i] = o[i] || null; - } - } - tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, o, overrides, { - evnts:fns, - timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker'); - }); - tp_inst.amNames = $.map(tp_inst._defaults.amNames, function(val) { + + overrides = { + beforeShow: function (input, dp_inst) { + if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) { + return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst); + } + }, + onChangeMonthYear: function (year, month, dp_inst) { + // Update the time as well : this prevents the time from disappearing from the $input field. + // tp_inst._updateDateTime(dp_inst); + if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) { + tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst); + } + }, + onClose: function (dateText, dp_inst) { + if (tp_inst.timeDefined === true && $input.val() !== '') { + tp_inst._updateDateTime(dp_inst); + } + if ($.isFunction(tp_inst._defaults.evnts.onClose)) { + tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst); + } + } + }; + for (i in overrides) { + if (overrides.hasOwnProperty(i)) { + fns[i] = opts[i] || this._defaults[i] || null; + } + } + + tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, opts, overrides, { + evnts: fns, + timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker'); + }); + tp_inst.amNames = $.map(tp_inst._defaults.amNames, function (val) { return val.toUpperCase(); }); - tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function(val) { + tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function (val) { return val.toUpperCase(); }); + // detect which units are supported + tp_inst.support = detectSupport( + tp_inst._defaults.timeFormat + + (tp_inst._defaults.pickerTimeFormat ? tp_inst._defaults.pickerTimeFormat : '') + + (tp_inst._defaults.altTimeFormat ? tp_inst._defaults.altTimeFormat : '')); + // controlType is string - key to our this._controls - if(typeof(tp_inst._defaults.controlType) === 'string'){ - if($.fn[tp_inst._defaults.controlType] === undefined){ + if (typeof(tp_inst._defaults.controlType) === 'string') { + if (tp_inst._defaults.controlType === 'slider' && typeof($.ui.slider) === 'undefined') { tp_inst._defaults.controlType = 'select'; } tp_inst.control = tp_inst._controls[tp_inst._defaults.controlType]; } // controlType is an object and must implement create, options, value methods - else{ + else { tp_inst.control = tp_inst._defaults.controlType; } - if (tp_inst._defaults.timezoneList === null) { - var timezoneList = ['-1200', '-1100', '-1000', '-0930', '-0900', '-0800', '-0700', '-0600', '-0500', '-0430', '-0400', '-0330', '-0300', '-0200', '-0100', '+0000', - '+0100', '+0200', '+0300', '+0330', '+0400', '+0430', '+0500', '+0530', '+0545', '+0600', '+0630', '+0700', '+0800', '+0845', '+0900', '+0930', - '+1000', '+1030', '+1100', '+1130', '+1200', '+1245', '+1300', '+1400']; - - if (tp_inst._defaults.timezoneIso8601) { - timezoneList = $.map(timezoneList, function(val) { - return val == '+0000' ? 'Z' : (val.substring(0, 3) + ':' + val.substring(3)); - }); - } - tp_inst._defaults.timezoneList = timezoneList; + // prep the timezone options + var timezoneList = [-720, -660, -600, -570, -540, -480, -420, -360, -300, -270, -240, -210, -180, -120, -60, + 0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 525, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840]; + if (tp_inst._defaults.timezoneList !== null) { + timezoneList = tp_inst._defaults.timezoneList; } + var tzl = timezoneList.length, tzi = 0, tzv = null; + if (tzl > 0 && typeof timezoneList[0] !== 'object') { + for (; tzi < tzl; tzi++) { + tzv = timezoneList[tzi]; + timezoneList[tzi] = { value: tzv, label: $.timepicker.timezoneOffsetString(tzv, tp_inst.support.iso8601) }; + } + } + tp_inst._defaults.timezoneList = timezoneList; - tp_inst.timezone = tp_inst._defaults.timezone; - tp_inst.hour = tp_inst._defaults.hour; - tp_inst.minute = tp_inst._defaults.minute; - tp_inst.second = tp_inst._defaults.second; - tp_inst.millisec = tp_inst._defaults.millisec; + // set the default units + tp_inst.timezone = tp_inst._defaults.timezone !== null ? $.timepicker.timezoneOffsetNumber(tp_inst._defaults.timezone) : + ((new Date()).getTimezoneOffset() * -1); + tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin ? tp_inst._defaults.hourMin : + tp_inst._defaults.hour > tp_inst._defaults.hourMax ? tp_inst._defaults.hourMax : tp_inst._defaults.hour; + tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin ? tp_inst._defaults.minuteMin : + tp_inst._defaults.minute > tp_inst._defaults.minuteMax ? tp_inst._defaults.minuteMax : tp_inst._defaults.minute; + tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin ? tp_inst._defaults.secondMin : + tp_inst._defaults.second > tp_inst._defaults.secondMax ? tp_inst._defaults.secondMax : tp_inst._defaults.second; + tp_inst.millisec = tp_inst._defaults.millisec < tp_inst._defaults.millisecMin ? tp_inst._defaults.millisecMin : + tp_inst._defaults.millisec > tp_inst._defaults.millisecMax ? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec; + tp_inst.microsec = tp_inst._defaults.microsec < tp_inst._defaults.microsecMin ? tp_inst._defaults.microsecMin : + tp_inst._defaults.microsec > tp_inst._defaults.microsecMax ? tp_inst._defaults.microsecMax : tp_inst._defaults.microsec; tp_inst.ampm = ''; tp_inst.$input = $input; - if (o.altField) { - tp_inst.$altInput = $(o.altField).css({ - cursor: 'pointer' - }).focus(function() { - $input.trigger("focus"); - }); + if (tp_inst._defaults.altField) { + tp_inst.$altInput = $(tp_inst._defaults.altField); + if (tp_inst._defaults.altRedirectFocus === true) { + tp_inst.$altInput.css({ + cursor: 'pointer' + }).focus(function () { + $input.trigger("focus"); + }); + } } if (tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0) { @@ -272,7 +304,7 @@ if (tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) { tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime()); } - tp_inst.$input.bind('focus', function() { + tp_inst.$input.bind('focus', function () { tp_inst._onFocus(); }); @@ -282,18 +314,19 @@ /* * add our sliders to the calendar */ - _addTimePicker: function(dp_inst) { - var currDT = (this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val(); + _addTimePicker: function (dp_inst) { + var currDT = $.trim((this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val()); this.timeDefined = this._parseTime(currDT); this._limitMinMaxDateTime(dp_inst, false); this._injectTimePicker(); + this._afterInject(); }, /* * parse the time string from input value or _setTime */ - _parseTime: function(timeString, withDate) { + _parseTime: function (timeString, withDate) { if (!this.inst) { this.inst = $.datepicker._getInst(this.$input[0]); } @@ -307,7 +340,7 @@ } $.extend(this, parseRes.timeObj); } catch (err) { - $.datepicker.log("Error parsing the date/time string: " + err + + $.timepicker.log("Error parsing the date/time string: " + err + "\ndate/time string = " + timeString + "\ntimeFormat = " + this._defaults.timeFormat + "\ndateFormat = " + dp_dateFormat); @@ -324,52 +357,67 @@ } }, + /* + * Handle callback option after injecting timepicker + */ + _afterInject: function() { + var o = this.inst.settings; + if ($.isFunction(o.afterInject)) { + o.afterInject.call(this); + } + }, + /* * generate and inject html for timepicker into ui datepicker */ - _injectTimePicker: function() { + _injectTimePicker: function () { var $dp = this.inst.dpDiv, o = this.inst.settings, tp_inst = this, litem = '', uitem = '', + show = null, max = {}, gridSize = {}, - size = null; + size = null, + i = 0, + l = 0; // Prevent displaying twice if ($dp.find("div.ui-timepicker-div").length === 0 && o.showTimepicker) { - var noDisplay = ' style="display:none;"', - html = '
' + '
' + o.timeText + '
' + - '
'; + var noDisplay = ' ui_tpicker_unit_hide', + html = '
' + '
' + o.timeText + '
' + + '
'; // Create the markup - for(var i=0,l=this.units.length; i' + o[litem +'Text'] + '' + - '
'; + html += '
' + o[litem + 'Text'] + '
' + + '
'; - if (o['show'+uitem] && o[litem+'Grid'] > 0) { + if (show && o[litem + 'Grid'] > 0) { html += '
'; - if(litem == 'hour'){ - for (var h = o[litem+'Min']; h <= max[litem]; h += parseInt(o[litem+'Grid'], 10)) { + if (litem === 'hour') { + for (var h = o[litem + 'Min']; h <= max[litem]; h += parseInt(o[litem + 'Grid'], 10)) { gridSize[litem]++; - var tmph = $.datepicker.formatTime(useAmpm(o.pickerTimeFormat || o.timeFormat)? 'hht':'HH', {hour:h}, o); - html += ''; + var tmph = $.datepicker.formatTime(this.support.ampm ? 'hht' : 'HH', {hour: h}, o); + html += ''; } } - else{ - for (var m = o[litem+'Min']; m <= max[litem]; m += parseInt(o[litem+'Grid'], 10)) { + else { + for (var m = o[litem + 'Min']; m <= max[litem]; m += parseInt(o[litem + 'Grid'], 10)) { gridSize[litem]++; - html += ''; + html += ''; } } @@ -377,10 +425,11 @@ } html += ''; } - + // Timezone - html += '
' + o.timezoneText + '
'; - html += '
'; + var showTz = o.showTimezone !== null ? o.showTimezone : this.support.timezone; + html += '
' + o.timezoneText + '
'; + html += '
'; // Create the elements from string html += ''; @@ -391,47 +440,47 @@ $tp.prepend('
' + '
' + o.timeOnlyTitle + '
' + '
'); $dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide(); } - + // add sliders, adjust grids, add events - for(var i=0,l=tp_inst.units.length; i 0) { - size = 100 * gridSize[litem] * o[litem+'Grid'] / (max[litem] - o[litem+'Min']); - $tp.find('.ui_tpicker_'+litem+' table').css({ + if (show && o[litem + 'Grid'] > 0) { + size = 100 * gridSize[litem] * o[litem + 'Grid'] / (max[litem] - o[litem + 'Min']); + $tp.find('.ui_tpicker_' + litem + ' table').css({ width: size + "%", - marginLeft: o.isRTL? '0' : ((size / (-2 * gridSize[litem])) + "%"), - marginRight: o.isRTL? ((size / (-2 * gridSize[litem])) + "%") : '0', + marginLeft: o.isRTL ? '0' : ((size / (-2 * gridSize[litem])) + "%"), + marginRight: o.isRTL ? ((size / (-2 * gridSize[litem])) + "%") : '0', borderCollapse: 'collapse' - }).find("td").click(function(e){ + }).find("td").click(function (e) { var $t = $(this), h = $t.html(), - n = parseInt(h.replace(/[^0-9]/g),10), + n = parseInt(h.replace(/[^0-9]/g), 10), ap = h.replace(/[^apm]/ig), f = $t.data('for'); // loses scope, so we use data-for - if(f == 'hour'){ - if(ap.indexOf('p') !== -1 && n < 12){ + if (f === 'hour') { + if (ap.indexOf('p') !== -1 && n < 12) { n += 12; } - else{ - if(ap.indexOf('a') !== -1 && n === 12){ + else { + if (ap.indexOf('a') !== -1 && n === 12) { n = 0; } } } - - tp_inst.control.value(tp_inst, tp_inst[f+'_slider'], litem, n); + + tp_inst.control.value(tp_inst, tp_inst[f + '_slider'], litem, n); tp_inst._onTimeChange(); tp_inst._onSelectHandler(); - }) - .css({ + }).css({ cursor: 'pointer', width: (100 / gridSize[litem]) + '%', textAlign: 'center', @@ -443,30 +492,30 @@ // Add timezone options this.timezone_select = $tp.find('.ui_tpicker_timezone').append('').find("select"); $.fn.append.apply(this.timezone_select, - $.map(o.timezoneList, function(val, idx) { - return $("
' + tmph + '' + tmph + '' + ((m < 10) ? '0' : '') + m + '' + ((m < 10) ? '0' : '') + m + '