From 4fa09639cb7337d2bc16f6ae263fc06c41a44625 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 6 Jun 2018 16:27:23 +1000 Subject: [PATCH 01/18] Rewrite user stat query for improved performance of homepage --- app/controllers/home_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 55b6b827e1f7..94dcd7d49c6e 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -7,7 +7,7 @@ def index if ContentConfig.home_show_stats @num_distributors = Enterprise.is_distributor.activated.visible.count @num_producers = Enterprise.is_primary_producer.activated.visible.count - @num_users = Spree::User.joins(:orders).merge(Spree::Order.complete).count('DISTINCT spree_users.*') + @num_users = Spree::Order.complete.count('DISTINCT user_id') @num_orders = Spree::Order.complete.count end end From f2e1caabff4e30fbb08c77684f9b54f0184be78e Mon Sep 17 00:00:00 2001 From: Frank West Date: Fri, 15 Jun 2018 08:27:26 -0700 Subject: [PATCH 02/18] Fix NoMethodError in order cycles index When a user's session has timed out and they try to load new data on the order cycles page by changing filters, the application throws a `NoMethodError` because we are prepending the load data method before checking the user's session. We can fix this by removing the prepend on this action. --- app/controllers/admin/order_cycles_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index 445e26808504..bb2e2da2a724 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -6,7 +6,7 @@ module Admin class OrderCyclesController < ResourceController include OrderCyclesHelper - prepend_before_filter :load_data_for_index, :only => :index + before_filter :load_data_for_index, only: :index before_filter :require_coordinator, only: :new before_filter :remove_protected_attrs, only: [:update] before_filter :check_editable_schedule_ids, only: [:create, :update] From e73b37820166901e1bd9ebc2641696a5b6ef61a9 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Fri, 15 Jun 2018 21:50:44 +0100 Subject: [PATCH 03/18] Adjust embedded response headers --- app/controllers/application_controller.rb | 6 ++++-- spec/requests/embedded_shopfronts_headers_spec.rb | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d22e8a6ba447..e3ea8b375f95 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -60,7 +60,7 @@ def enable_embedded_shopfront return if embedding_without_https? response.headers.delete 'X-Frame-Options' - response.headers['Content-Security-Policy'] = "frame-ancestors #{URI(request.referer).host.downcase}" + response.headers['Content-Security-Policy'] = "frame-ancestors 'self' #{URI(request.referer).host.downcase}" check_embedded_request set_embedded_layout @@ -73,8 +73,10 @@ def embedded_shopfront_referer end def embeddable? - whitelist = Spree::Config[:embedded_shopfronts_whitelist] domain = embedded_shopfront_referer + return true if domain == request.host + + whitelist = Spree::Config[:embedded_shopfronts_whitelist] Spree::Config[:enable_embedded_shopfronts] && whitelist.present? && domain.present? && whitelist.include?(domain) end diff --git a/spec/requests/embedded_shopfronts_headers_spec.rb b/spec/requests/embedded_shopfronts_headers_spec.rb index 9d2c1c523e5c..6d6ea4b9f6b4 100644 --- a/spec/requests/embedded_shopfronts_headers_spec.rb +++ b/spec/requests/embedded_shopfronts_headers_spec.rb @@ -52,7 +52,7 @@ expect(response.status).to be 200 expect(response.headers['X-Frame-Options']).to be_nil - expect(response.headers['Content-Security-Policy']).to eq "frame-ancestors external-site.com" + expect(response.headers['Content-Security-Policy']).to eq "frame-ancestors 'self' external-site.com" get spree.admin_path @@ -73,7 +73,7 @@ expect(response.status).to be 200 expect(response.headers['X-Frame-Options']).to be_nil - expect(response.headers['Content-Security-Policy']).to eq "frame-ancestors www.external-site.com" + expect(response.headers['Content-Security-Policy']).to eq "frame-ancestors 'self' www.external-site.com" end end end From ff0e0d9f3d6461caf8f749942f1e6de607779619 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Sat, 16 Jun 2018 02:49:22 +0100 Subject: [PATCH 04/18] Move logic from ApplicationController to service and improve clarity --- app/controllers/application_controller.rb | 47 +--------- app/services/embedded_page_service.rb | 90 +++++++++++++++++++ .../embedded_shopfronts_headers_spec.rb | 4 +- 3 files changed, 95 insertions(+), 46 deletions(-) create mode 100644 app/services/embedded_page_service.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e3ea8b375f95..08a552cb6ba6 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -56,50 +56,9 @@ def restrict_iframes end def enable_embedded_shopfront - return unless embeddable? - return if embedding_without_https? - - response.headers.delete 'X-Frame-Options' - response.headers['Content-Security-Policy'] = "frame-ancestors 'self' #{URI(request.referer).host.downcase}" - - check_embedded_request - set_embedded_layout - end - - def embedded_shopfront_referer - return if request.referer.blank? - domain = URI(request.referer).host.downcase - domain.start_with?('www.') ? domain[4..-1] : domain - end - - def embeddable? - domain = embedded_shopfront_referer - return true if domain == request.host - - whitelist = Spree::Config[:embedded_shopfronts_whitelist] - Spree::Config[:enable_embedded_shopfronts] && whitelist.present? && domain.present? && whitelist.include?(domain) - end - - def embedding_without_https? - request.referer && URI(request.referer).scheme != 'https' && !Rails.env.test? && !Rails.env.development? - end - - def check_embedded_request - return unless params[:embedded_shopfront] - - # Show embedded shopfront CSS - session[:embedded_shopfront] = true - - # Get shopfront slug and set redirect path - if params[:controller] == 'enterprises' && params[:action] == 'shop' && params[:id] - slug = params[:id] - session[:shopfront_redirect] = '/' + slug + '/shop?embedded_shopfront=true' - end - end - - def set_embedded_layout - return unless session[:embedded_shopfront] - @shopfront_layout = 'embedded' + embed_service = EmbeddedPageService.new(params, session, request, response) + embed_service.embed! + @shopfront_layout = 'embedded' if embed_service.use_embedded_layout end def action diff --git a/app/services/embedded_page_service.rb b/app/services/embedded_page_service.rb new file mode 100644 index 000000000000..b4ab2372cc96 --- /dev/null +++ b/app/services/embedded_page_service.rb @@ -0,0 +1,90 @@ +# Processes requests for pages embedded in iframes + +class EmbeddedPageService + attr_reader :use_embedded_layout + + def initialize(params, session, request, response) + @params = params + @session = session + @request = request + @response = response + + @embedding_domain = @session[:embedding_domain] + @use_embedded_layout = false + end + + def embed! + return unless embeddable? + return if embedding_without_https? + + process_embedded_request + set_response_headers + set_embedded_layout + end + + private + + def embeddable? + return true if current_referer == @request.host + + domain = current_referer_without_www + whitelist = Spree::Config[:embedded_shopfronts_whitelist] + + embedding_enabled? && whitelist.present? && domain.present? && whitelist.include?(domain) + end + + def embedding_without_https? + @request.referer && URI(@request.referer).scheme != 'https' && !Rails.env.test? && !Rails.env.development? + end + + def process_embedded_request + return unless @params[:embedded_shopfront] + + set_embedding_domain + + @session[:embedded_shopfront] = true + set_logout_redirect + end + + def set_response_headers + @response.headers.delete 'X-Frame-Options' + @response.headers['Content-Security-Policy'] = "frame-ancestors 'self' #{@embedding_domain}" + end + + def set_embedding_domain + return unless @params[:embedded_shopfront] + return if current_referer == @request.host + + @embedding_domain = current_referer + @session[:embedding_domain] = current_referer + end + + def set_logout_redirect + return unless enterprise_slug + @session[:shopfront_redirect] = '/' + enterprise_slug + '/shop?embedded_shopfront=true' + end + + def enterprise_slug + return false unless @params[:controller] == 'enterprises' && @params[:action] == 'shop' && @params[:id] + @params[:id] + end + + def current_referer + return if @request.referer.blank? + URI(@request.referer).host.downcase + end + + def current_referer_without_www + return unless current_referer + current_referer.start_with?('www.') ? current_referer[4..-1] : current_referer + end + + def set_embedded_layout + return unless @session[:embedded_shopfront] + @use_embedded_layout = true + end + + def embedding_enabled? + Spree::Config[:enable_embedded_shopfronts] + end +end diff --git a/spec/requests/embedded_shopfronts_headers_spec.rb b/spec/requests/embedded_shopfronts_headers_spec.rb index 6d6ea4b9f6b4..6428b009be64 100644 --- a/spec/requests/embedded_shopfronts_headers_spec.rb +++ b/spec/requests/embedded_shopfronts_headers_spec.rb @@ -48,7 +48,7 @@ end it "allows iframes on certain pages when enabled in configuration" do - get shops_path + get enterprise_shop_path(enterprise) + '?embedded_shopfront=true' expect(response.status).to be 200 expect(response.headers['X-Frame-Options']).to be_nil @@ -69,7 +69,7 @@ end it "matches the URL structure in the header" do - get shops_path + get enterprise_shop_path(enterprise) + '?embedded_shopfront=true' expect(response.status).to be 200 expect(response.headers['X-Frame-Options']).to be_nil From 6e8187145995cdd88725ad74c57dea9585c91018 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Wed, 20 Jun 2018 15:14:56 +0100 Subject: [PATCH 05/18] Specs for new EmbeddedPageService --- spec/services/embedded_page_service_spec.rb | 59 +++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 spec/services/embedded_page_service_spec.rb diff --git a/spec/services/embedded_page_service_spec.rb b/spec/services/embedded_page_service_spec.rb new file mode 100644 index 000000000000..f1ea8a971e3e --- /dev/null +++ b/spec/services/embedded_page_service_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe EmbeddedPageService do + let(:enterprise_slug) { 'test-enterprise' } + let(:params) { { controller: 'enterprises', action: 'shop', id: enterprise_slug, embedded_shopfront: true } } + let(:session) { {} } + let(:request) { ActionController::TestRequest.new('HTTP_HOST' => 'ofn-instance.com', 'HTTP_REFERER' => 'https://embedding-enterprise.com') } + let(:response) { ActionController::TestResponse.new(200, 'X-Frame-Options' => 'DENY', 'Content-Security-Policy' => "frame-ancestors 'none'") } + let(:service) { EmbeddedPageService.new(params, session, request, response) } + + before do + Spree::Config.set( + enable_embedded_shopfronts: true, + embedded_shopfronts_whitelist: 'embedding-enterprise.com example.com' + ) + end + + describe "#embed!" do + context "when the request's referer is in the whitelist" do + before { service.embed! } + + it "sets the response headers to enables embedding requests from the embedding site" do + expect(response.headers).to_not include 'X-Frame-Options' => 'DENY' + expect(response.headers).to include 'Content-Security-Policy' => "frame-ancestors 'self' embedding-enterprise.com" + end + + it "sets session variables" do + expect(session[:embedded_shopfront]).to eq true + expect(session[:embedding_domain]).to eq 'embedding-enterprise.com' + expect(session[:shopfront_redirect]).to eq '/' + enterprise_slug + '/shop?embedded_shopfront=true' + end + end + + context "when embedding is enabled for a different site in the current session" do + before do + session[:embedding_domain] = 'another-enterprise.com' + session[:shopfront_redirect] = '/another-enterprise/shop?embedded_shopfront=true' + service.embed! + end + + it "resets the session variables for the new request" do + expect(session[:embedded_shopfront]).to eq true + expect(session[:embedding_domain]).to eq 'embedding-enterprise.com' + expect(session[:shopfront_redirect]).to eq '/' + enterprise_slug + '/shop?embedded_shopfront=true' + end + end + + context "when the request's referer is not in the whitelist" do + before do + Spree::Config.set(embedded_shopfronts_whitelist: 'example.com') + service.embed! + end + + it "does not enable embedding" do + expect(response.headers['X-Frame-Options']).to eq 'DENY' + end + end + end +end From aaba6da1629a4f0782a10341c6c85e3aa13dc5b3 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Sat, 16 Jun 2018 14:30:17 +0100 Subject: [PATCH 06/18] Add available_on notes to PI guide --- app/views/admin/product_import/guide/_columns.html.haml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/views/admin/product_import/guide/_columns.html.haml b/app/views/admin/product_import/guide/_columns.html.haml index d5aa4546704d..c79a88116235 100644 --- a/app/views/admin/product_import/guide/_columns.html.haml +++ b/app/views/admin/product_import/guide/_columns.html.haml @@ -91,3 +91,10 @@ %td (Various, see notes) %td Sets the product shipping category %td See below for a list of available categories + %tr + %td + %strong available_on + %td No + %td 2018-05-21 + %td Sets the date from which the product will be available + %td Date format is: YYYY-MM-DD From 172fa168ead0a01be6e623045b46f401c3cf4377 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley Date: Thu, 21 Jun 2018 14:18:32 +0100 Subject: [PATCH 07/18] Change layout attribute to method with question mark --- app/controllers/application_controller.rb | 2 +- app/services/embedded_page_service.rb | 6 ++++-- spec/services/embedded_page_service_spec.rb | 6 +++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 08a552cb6ba6..65d1fb1f33fc 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -58,7 +58,7 @@ def restrict_iframes def enable_embedded_shopfront embed_service = EmbeddedPageService.new(params, session, request, response) embed_service.embed! - @shopfront_layout = 'embedded' if embed_service.use_embedded_layout + @shopfront_layout = 'embedded' if embed_service.use_embedded_layout? end def action diff --git a/app/services/embedded_page_service.rb b/app/services/embedded_page_service.rb index b4ab2372cc96..8d6e27df26e2 100644 --- a/app/services/embedded_page_service.rb +++ b/app/services/embedded_page_service.rb @@ -1,8 +1,6 @@ # Processes requests for pages embedded in iframes class EmbeddedPageService - attr_reader :use_embedded_layout - def initialize(params, session, request, response) @params = params @session = session @@ -22,6 +20,10 @@ def embed! set_embedded_layout end + def use_embedded_layout? + @use_embedded_layout + end + private def embeddable? diff --git a/spec/services/embedded_page_service_spec.rb b/spec/services/embedded_page_service_spec.rb index f1ea8a971e3e..eb44b014ab97 100644 --- a/spec/services/embedded_page_service_spec.rb +++ b/spec/services/embedded_page_service_spec.rb @@ -15,7 +15,7 @@ ) end - describe "#embed!" do + describe "processing embedded page requests" do context "when the request's referer is in the whitelist" do before { service.embed! } @@ -29,6 +29,10 @@ expect(session[:embedding_domain]).to eq 'embedding-enterprise.com' expect(session[:shopfront_redirect]).to eq '/' + enterprise_slug + '/shop?embedded_shopfront=true' end + + it "publicly reports that embedded layout should be used" do + expect(service.use_embedded_layout?).to be true + end end context "when embedding is enabled for a different site in the current session" do From e25574790b32de7ec9d47c3c3abe068444bf6e10 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 6 Apr 2018 14:55:21 +1000 Subject: [PATCH 08/18] Split out float: right css from .help-btn.tiny selector --- app/assets/stylesheets/darkswarm/ui.css.scss | 3 +++ app/views/shared/components/_show_profiles.html.haml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/darkswarm/ui.css.scss b/app/assets/stylesheets/darkswarm/ui.css.scss index 1d1b9e166a35..529fb6e48bb2 100644 --- a/app/assets/stylesheets/darkswarm/ui.css.scss +++ b/app/assets/stylesheets/darkswarm/ui.css.scss @@ -87,6 +87,9 @@ button.success, .button.success { &.tiny { padding: 0rem; margin: 0; + } + + &.right { float: right; } diff --git a/app/views/shared/components/_show_profiles.html.haml b/app/views/shared/components/_show_profiles.html.haml index 84a55ae40599..5a1b5ec1db7f 100644 --- a/app/views/shared/components/_show_profiles.html.haml +++ b/app/views/shared/components/_show_profiles.html.haml @@ -1,6 +1,6 @@ .small-12.medium-6.columns.text-right .profile-checkbox - %button.button.secondary.tiny.help-btn.ng-scope{:popover => t(:components_profiles_popover, sitename: Spree::Config[:site_name]), "popover-placement" => "left"}>< + %button.button.secondary.tiny.right.help-btn.ng-scope{:popover => t(:components_profiles_popover, sitename: Spree::Config[:site_name]), "popover-placement" => "left"}>< %i.ofn-i_013-help %label %input{"ng-model" => "show_profiles", type: "checkbox", name: "profile"} From 6e76fd816474a85add770d10509c08e639634309 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 20 Apr 2018 11:27:33 +1000 Subject: [PATCH 09/18] Add Api::CustomersController with update action --- app/controllers/api/customers_controller.rb | 16 ++++++ app/models/spree/ability_decorator.rb | 4 ++ config/routes.rb | 2 + .../api/customers_controller_spec.rb | 49 +++++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 app/controllers/api/customers_controller.rb create mode 100644 spec/controllers/api/customers_controller_spec.rb diff --git a/app/controllers/api/customers_controller.rb b/app/controllers/api/customers_controller.rb new file mode 100644 index 000000000000..9df753425953 --- /dev/null +++ b/app/controllers/api/customers_controller.rb @@ -0,0 +1,16 @@ +module Api + class CustomersController < Spree::Api::BaseController + respond_to :json + + def update + @customer = Customer.find(params[:id]) + authorize! :update, @customer + + if @customer.update_attributes(params[:customer]) + render text: @customer.id, :status => 200 + else + invalid_resource!(@customer) + end + end + end +end diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 385b9be0321a..daf816114232 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -64,6 +64,10 @@ def add_shopping_abilities(user) can [:update, :destroy], Spree::CreditCard do |credit_card| credit_card.user == user end + + can [:update], Customer do |customer| + customer.user == user + end end # New users can create an enterprise, and gain other permissions from doing this. diff --git a/config/routes.rb b/config/routes.rb index 53356e2f521e..f0f6bd4488a2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -217,6 +217,8 @@ get :job_queue end + resources :customers, only: [:update] + post '/product_images/:product_id', to: 'product_images#update_product_image' end diff --git a/spec/controllers/api/customers_controller_spec.rb b/spec/controllers/api/customers_controller_spec.rb new file mode 100644 index 000000000000..764f76754919 --- /dev/null +++ b/spec/controllers/api/customers_controller_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +module Api + describe CustomersController, type: :controller do + include AuthenticationWorkflow + render_views + + let(:user) { create(:user) } + let(:customer) { create(:customer, user: user) } + let(:params) { { format: :json, id: customer.id, customer: { code: '123' } } } + + describe "#update" do + context "as a user who is not associated with the customer" do + before do + allow(controller).to receive(:spree_current_user) { create(:user) } + end + + it "returns unauthorized" do + spree_post :update, params + assert_unauthorized! + end + end + + context "as the user associated with the customer" do + before do + allow(controller).to receive(:spree_current_user) { user } + end + + context "when the update request is successful" do + it "returns the id of the updated customer" do + spree_post :update, params + expect(response.status).to eq 200 + expect(response.body).to eq customer.id.to_s + end + end + + context "when the update request fails" do + before { params[:customer][:email] = '' } + + it "returns a 422, with an error message" do + spree_post :update, params + expect(response.status).to be 422 + expect(JSON.parse(response.body)['error']).to be + end + end + end + end + end +end From 29922d4be992854318c8bfe62434d8275e3575de Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 11 Apr 2018 09:54:43 +1000 Subject: [PATCH 10/18] Add allow_charges field to Customer model --- .../20180406045821_add_charges_allowed_to_customers.rb | 5 +++++ db/schema.rb | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20180406045821_add_charges_allowed_to_customers.rb diff --git a/db/migrate/20180406045821_add_charges_allowed_to_customers.rb b/db/migrate/20180406045821_add_charges_allowed_to_customers.rb new file mode 100644 index 000000000000..4503dc87b6c4 --- /dev/null +++ b/db/migrate/20180406045821_add_charges_allowed_to_customers.rb @@ -0,0 +1,5 @@ +class AddChargesAllowedToCustomers < ActiveRecord::Migration + def change + add_column :customers, :allow_charges, :boolean, default: false, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 787c37c227d0..caad9f27a52d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -79,15 +79,16 @@ add_index "coordinator_fees", ["order_cycle_id"], :name => "index_coordinator_fees_on_order_cycle_id" create_table "customers", :force => true do |t| - t.string "email", :null => false - t.integer "enterprise_id", :null => false + t.string "email", :null => false + t.integer "enterprise_id", :null => false t.string "code" t.integer "user_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.integer "bill_address_id" t.integer "ship_address_id" t.string "name" + t.boolean "allow_charges", :default => false, :null => false end add_index "customers", ["bill_address_id"], :name => "index_customers_on_bill_address_id" From ffa8a8c7d685e04e39b445d1abc840ae8628123f Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 27 Apr 2018 16:04:58 +1000 Subject: [PATCH 11/18] Create Api::BaseController to allow use of ActiveModelSerializers Also add index action to Api::CustomersController --- app/controllers/api/base_controller.rb | 13 +++++++++ app/controllers/api/customers_controller.rb | 9 ++++--- app/controllers/api/statuses_controller.rb | 2 +- app/serializers/api/customer_serializer.rb | 5 ++++ config/routes.rb | 2 +- .../api/customers_controller_spec.rb | 27 ++++++++++++++++--- spec/support/api_helper.rb | 15 +++++++++++ 7 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 app/controllers/api/base_controller.rb create mode 100644 app/serializers/api/customer_serializer.rb create mode 100644 spec/support/api_helper.rb diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb new file mode 100644 index 000000000000..f25c47417d25 --- /dev/null +++ b/app/controllers/api/base_controller.rb @@ -0,0 +1,13 @@ +# Base controller for OFN's API +# Includes the minimum machinery required by ActiveModelSerializers +module Api + class BaseController < Spree::Api::BaseController + # Need to include these because Spree::Api::BaseContoller inherits + # from ActionController::Metal rather than ActionController::Base + # and they are required by ActiveModelSerializers + include ActionController::Serialization + include ActionController::UrlFor + include Rails.application.routes.url_helpers + use_renderers :json + end +end diff --git a/app/controllers/api/customers_controller.rb b/app/controllers/api/customers_controller.rb index 9df753425953..cbbe4ce35f33 100644 --- a/app/controllers/api/customers_controller.rb +++ b/app/controllers/api/customers_controller.rb @@ -1,13 +1,16 @@ module Api - class CustomersController < Spree::Api::BaseController - respond_to :json + class CustomersController < BaseController + def index + @customers = current_api_user.customers + render json: @customers, each_serializer: CustomerSerializer + end def update @customer = Customer.find(params[:id]) authorize! :update, @customer if @customer.update_attributes(params[:customer]) - render text: @customer.id, :status => 200 + render json: @customer, serializer: CustomerSerializer, status: 200 else invalid_resource!(@customer) end diff --git a/app/controllers/api/statuses_controller.rb b/app/controllers/api/statuses_controller.rb index c8844b868b18..49a6f991ffc8 100644 --- a/app/controllers/api/statuses_controller.rb +++ b/app/controllers/api/statuses_controller.rb @@ -1,5 +1,5 @@ module Api - class StatusesController < BaseController + class StatusesController < ::BaseController respond_to :json def job_queue diff --git a/app/serializers/api/customer_serializer.rb b/app/serializers/api/customer_serializer.rb new file mode 100644 index 000000000000..44914e0a4922 --- /dev/null +++ b/app/serializers/api/customer_serializer.rb @@ -0,0 +1,5 @@ +module Api + class CustomerSerializer < ActiveModel::Serializer + attributes :id, :enterprise_id, :name, :code, :email, :allow_charges + end +end diff --git a/config/routes.rb b/config/routes.rb index f0f6bd4488a2..ba4da8ae2e9b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -217,7 +217,7 @@ get :job_queue end - resources :customers, only: [:update] + resources :customers, only: [:index, :update] post '/product_images/:product_id', to: 'product_images#update_product_image' end diff --git a/spec/controllers/api/customers_controller_spec.rb b/spec/controllers/api/customers_controller_spec.rb index 764f76754919..f6c8f4e0a5d3 100644 --- a/spec/controllers/api/customers_controller_spec.rb +++ b/spec/controllers/api/customers_controller_spec.rb @@ -3,13 +3,32 @@ module Api describe CustomersController, type: :controller do include AuthenticationWorkflow + include OpenFoodNetwork::ApiHelper render_views let(:user) { create(:user) } - let(:customer) { create(:customer, user: user) } - let(:params) { { format: :json, id: customer.id, customer: { code: '123' } } } + + describe "index" do + let!(:customer1) { create(:customer) } + let!(:customer2) { create(:customer) } + + before do + user.customers << customer1 + allow(controller).to receive(:spree_current_user) { user } + end + + it "lists customers associated with the current user" do + spree_get :index + expect(response.status).to eq 200 + expect(json_response.length).to eq 1 + expect(json_response.first[:id]).to eq customer1.id + end + end describe "#update" do + let(:customer) { create(:customer, user: user) } + let(:params) { { format: :json, id: customer.id, customer: { code: '123' } } } + context "as a user who is not associated with the customer" do before do allow(controller).to receive(:spree_current_user) { create(:user) } @@ -30,7 +49,7 @@ module Api it "returns the id of the updated customer" do spree_post :update, params expect(response.status).to eq 200 - expect(response.body).to eq customer.id.to_s + expect(json_response[:id]).to eq customer.id end end @@ -40,7 +59,7 @@ module Api it "returns a 422, with an error message" do spree_post :update, params expect(response.status).to be 422 - expect(JSON.parse(response.body)['error']).to be + expect(json_response[:error]).to be end end end diff --git a/spec/support/api_helper.rb b/spec/support/api_helper.rb new file mode 100644 index 000000000000..08918fb76177 --- /dev/null +++ b/spec/support/api_helper.rb @@ -0,0 +1,15 @@ +module OpenFoodNetwork + module ApiHelper + def json_response + json_response = JSON.parse(response.body) + case json_response + when Hash + json_response.with_indifferent_access + when Array + json_response.map(&:with_indifferent_access) + else + json_response + end + end + end +end From 6457a17fde359f00ddae254e67a0dc4a9d2799d0 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 27 Apr 2018 17:42:34 +1000 Subject: [PATCH 12/18] Add basic view allowing customers to authorise shop use of their credit cards --- .../authorised_shops_controller.js.coffee | 3 ++ .../darkswarm/services/customer.js.coffee | 20 ++++++++++ .../darkswarm/services/customers.js.coffee | 13 +++++++ .../darkswarm/services/shops.js.coffee | 13 +++++++ .../stylesheets/darkswarm/account.css.scss | 6 +++ .../spree/users/_authorised_shops.html.haml | 13 +++++++ app/views/spree/users/_cards.html.haml | 13 +++++-- config/locales/en.yml | 3 ++ spec/features/consumer/account/cards_spec.rb | 11 +++++- .../services/customer_spec.js.coffee | 39 +++++++++++++++++++ .../services/customers_spec.js.coffee | 24 ++++++++++++ .../darkswarm/services/shops_spec.js.coffee | 27 +++++++++++++ 12 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/controllers/authorised_shops_controller.js.coffee create mode 100644 app/assets/javascripts/darkswarm/services/customer.js.coffee create mode 100644 app/assets/javascripts/darkswarm/services/customers.js.coffee create mode 100644 app/assets/javascripts/darkswarm/services/shops.js.coffee create mode 100644 app/views/spree/users/_authorised_shops.html.haml create mode 100644 spec/javascripts/unit/darkswarm/services/customer_spec.js.coffee create mode 100644 spec/javascripts/unit/darkswarm/services/customers_spec.js.coffee create mode 100644 spec/javascripts/unit/darkswarm/services/shops_spec.js.coffee diff --git a/app/assets/javascripts/darkswarm/controllers/authorised_shops_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/authorised_shops_controller.js.coffee new file mode 100644 index 000000000000..f100a0a7c32f --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/authorised_shops_controller.js.coffee @@ -0,0 +1,3 @@ +angular.module("Darkswarm").controller "AuthorisedShopsCtrl", ($scope, Customers, Shops) -> + $scope.customers = Customers.index() + $scope.shopsByID = Shops.byID diff --git a/app/assets/javascripts/darkswarm/services/customer.js.coffee b/app/assets/javascripts/darkswarm/services/customer.js.coffee new file mode 100644 index 000000000000..ac27945c545b --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/customer.js.coffee @@ -0,0 +1,20 @@ +angular.module("Darkswarm").factory 'Customer', ($resource, RailsFlashLoader) -> + Customer = $resource('/api/customers/:id/:action.json', {}, { + 'index': + method: 'GET' + isArray: true + 'update': + method: 'PUT' + params: + id: '@id' + transformRequest: (data, headersGetter) -> + angular.toJson(customer: data) + }) + + Customer.prototype.update = -> + @$update().then (response) => + RailsFlashLoader.loadFlash({success: t('js.changes_saved')}) + , (response) => + RailsFlashLoader.loadFlash({error: response.data.error}) + + Customer diff --git a/app/assets/javascripts/darkswarm/services/customers.js.coffee b/app/assets/javascripts/darkswarm/services/customers.js.coffee new file mode 100644 index 000000000000..cf5c56563a6e --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/customers.js.coffee @@ -0,0 +1,13 @@ +angular.module("Darkswarm").factory 'Customers', (Customer) -> + new class Customers + all: [] + byID: {} + + index: (params={}) -> + Customer.index params, (data) => @load(data) + @all + + load: (customers) -> + for customer in customers + @all.push customer + @byID[customer.id] = customer diff --git a/app/assets/javascripts/darkswarm/services/shops.js.coffee b/app/assets/javascripts/darkswarm/services/shops.js.coffee new file mode 100644 index 000000000000..0af415250882 --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/shops.js.coffee @@ -0,0 +1,13 @@ +angular.module("Darkswarm").factory 'Shops', ($injector) -> + new class Shops + all: [] + byID: {} + + constructor: -> + if $injector.has('shops') + @load($injector.get('shops')) + + load: (shops) -> + for shop in shops + @all.push shop + @byID[shop.id] = shop diff --git a/app/assets/stylesheets/darkswarm/account.css.scss b/app/assets/stylesheets/darkswarm/account.css.scss index c41cd2ef98fb..44d93b4cd1f4 100644 --- a/app/assets/stylesheets/darkswarm/account.css.scss +++ b/app/assets/stylesheets/darkswarm/account.css.scss @@ -28,6 +28,12 @@ margin-bottom: 0px; } } + + .authorised_shops{ + table { + width: 100%; + } + } } .orders { diff --git a/app/views/spree/users/_authorised_shops.html.haml b/app/views/spree/users/_authorised_shops.html.haml new file mode 100644 index 000000000000..692c953f12ab --- /dev/null +++ b/app/views/spree/users/_authorised_shops.html.haml @@ -0,0 +1,13 @@ +%table + %tr + %th= t(:shop_title) + %th= t(:allow_charges?) + %tr.customer{ id: "customer{{ customer.id }}", ng: { repeat: "customer in customers" } } + %td.shop{ ng: { bind: 'shopsByID[customer.enterprise_id].name' } } + %td.allow_charges + %input{ type: 'checkbox', + name: 'allow_charges', + ng: { model: 'customer.allow_charges', + change: 'customer.update()', + "true-value" => "true", + "false-value" => "false" } } diff --git a/app/views/spree/users/_cards.html.haml b/app/views/spree/users/_cards.html.haml index 80069c282f81..8929aac79189 100644 --- a/app/views/spree/users/_cards.html.haml +++ b/app/views/spree/users/_cards.html.haml @@ -10,6 +10,13 @@ %button.button.primary{ ng: { click: 'showForm()', hide: 'CreditCard.visible' } } = t(:add_a_card) - .small-12.medium-6.columns.new_card{ ng: { show: 'CreditCard.visible', class: '{visible: CreditCard.visible}' } } - %h3= t(:add_a_new_card) - = render 'new_card_form' + .small-12.medium-6.columns + .new_card{ ng: { show: 'CreditCard.visible', class: '{visible: CreditCard.visible}' } } + %h3= t(:add_a_new_card) + = render 'new_card_form' + .authorised_shops{ ng: { controller: 'AuthorisedShopsCtrl', hide: 'CreditCard.visible' } } + %h3 + = t('.authorised_shops') + %button.button.secondary.tiny.help-btn.ng-scope{ :popover => t('.authorised_shops_popover'), "popover-placement" => 'right' } + %i.ofn-i_013-help + = render 'authorised_shops' diff --git a/config/locales/en.yml b/config/locales/en.yml index 35754e0bc2d3..597dd60c3605 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2740,5 +2740,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using saved_cards: default?: Default? delete?: Delete? + cards: + authorised_shops: Authorised Shops + authorised_shops_popover: This is a list of shops which are permitted to charge your default credit card for OFN services (eg. subscriptions). Your card details will be kept secure and will not be shared with shop owners. localized_number: invalid_format: has an invalid format. Please enter a number. diff --git a/spec/features/consumer/account/cards_spec.rb b/spec/features/consumer/account/cards_spec.rb index 2f7851113535..25c6410ade65 100644 --- a/spec/features/consumer/account/cards_spec.rb +++ b/spec/features/consumer/account/cards_spec.rb @@ -4,6 +4,7 @@ include AuthenticationWorkflow describe "as a logged in user" do let(:user) { create(:user) } + let!(:customer) { create(:customer, user: user) } let!(:default_card) { create(:credit_card, user_id: user.id, gateway_customer_profile_id: 'cus_AZNMJ', is_default: true) } let!(:non_default_card) { create(:credit_card, user_id: user.id, gateway_customer_profile_id: 'cus_FDTG') } @@ -49,10 +50,10 @@ expect(page).to have_content I18n.t('js.default_card_updated') + expect(default_card.reload.is_default).to be false within(".card#card#{default_card.id}") do expect(find_field('default_card')).to_not be_checked end - expect(default_card.reload.is_default).to be false expect(non_default_card.reload.is_default).to be true # Shows the interface for adding a card @@ -67,6 +68,14 @@ expect(page).to have_content I18n.t(:card_has_been_removed, number: "x-#{default_card.last_digits}") expect(page).to_not have_selector ".card#card#{default_card.id}" + + # Allows authorisation of card use by shops + within "tr#customer#{customer.id}" do + expect(find_field('allow_charges')).to_not be_checked + find_field('allow_charges').click + end + expect(page).to have_content I18n.t('js.changes_saved') + expect(customer.reload.allow_charges).to be true end end end diff --git a/spec/javascripts/unit/darkswarm/services/customer_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/customer_spec.js.coffee new file mode 100644 index 000000000000..33a1d72cdc5d --- /dev/null +++ b/spec/javascripts/unit/darkswarm/services/customer_spec.js.coffee @@ -0,0 +1,39 @@ +describe 'Customer', -> + describe "update", -> + $httpBackend = null + customer = null + response = { id: 3, code: '1234' } + RailsFlashLoaderMock = jasmine.createSpyObj('RailsFlashLoader', ['loadFlash']) + + beforeEach -> + module 'Darkswarm' + module ($provide) -> + $provide.value 'RailsFlashLoader', RailsFlashLoaderMock + null + + inject (_$httpBackend_, Customer)-> + customer = new Customer(id: 3) + $httpBackend = _$httpBackend_ + + it "nests the params inside 'customer'", -> + $httpBackend + .expectPUT('/api/customers/3.json', { customer: { id: 3 } }) + .respond 200, response + customer.update() + $httpBackend.flush() + + describe "when the request succeeds", -> + it "shows a success flash", -> + $httpBackend.expectPUT('/api/customers/3.json').respond 200, response + customer.update() + $httpBackend.flush() + expect(RailsFlashLoaderMock.loadFlash) + .toHaveBeenCalledWith({success: jasmine.any(String)}) + + describe "when the request fails", -> + it "shows a error flash", -> + $httpBackend.expectPUT('/api/customers/3.json').respond 400, { error: 'Some error' } + customer.update() + $httpBackend.flush() + expect(RailsFlashLoaderMock.loadFlash) + .toHaveBeenCalledWith({error: 'Some error'}) diff --git a/spec/javascripts/unit/darkswarm/services/customers_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/customers_spec.js.coffee new file mode 100644 index 000000000000..9680b8934111 --- /dev/null +++ b/spec/javascripts/unit/darkswarm/services/customers_spec.js.coffee @@ -0,0 +1,24 @@ +describe 'Customers', -> + describe "index", -> + $httpBackend = null + Customers = null + customerList = ['somecustomer'] + + beforeEach -> + module 'Darkswarm' + module ($provide) -> + $provide.value 'RailsFlashLoader', null + null + + inject (_$httpBackend_, _Customers_)-> + Customers = _Customers_ + $httpBackend = _$httpBackend_ + + it "asks for customers and returns @all, promises to populate via @load", -> + spyOn(Customers,'load').and.callThrough() + $httpBackend.expectGET('/api/customers.json').respond 200, customerList + result = Customers.index() + $httpBackend.flush() + expect(Customers.load).toHaveBeenCalled() + expect(result).toEqual customerList + expect(Customers.all).toEqual customerList diff --git a/spec/javascripts/unit/darkswarm/services/shops_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/shops_spec.js.coffee new file mode 100644 index 000000000000..ddc9e14af5d8 --- /dev/null +++ b/spec/javascripts/unit/darkswarm/services/shops_spec.js.coffee @@ -0,0 +1,27 @@ +describe 'Shops', -> + describe "initialisation", -> + Shops = null + shops = ['some shop'] + + beforeEach -> + module 'Darkswarm' + + describe "when the injector does not have a value for 'shops'", -> + beforeEach -> + inject (_Shops_) -> + Shops = _Shops_ + + it "does nothing, leaves @all empty", -> + expect(Shops.all).toEqual [] + + describe "when the injector has a value for 'shops'", -> + beforeEach -> + module ($provide) -> + $provide.value 'shops', shops + null + + inject (_Shops_) -> + Shops = _Shops_ + + it "loads injected shops array into @all", -> + expect(Shops.all).toEqual shops From 32622c77bceae8cca405e6224ad0edffba9d88df Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 22 Jun 2018 13:57:21 +1000 Subject: [PATCH 13/18] Add basic help modal directive Useful for showing help text that is too long for a tool tip --- .../darkswarm/directives/help_modal.js.coffee | 10 ++++++++++ app/assets/javascripts/templates/help-modal.html.haml | 9 +++++++++ app/assets/stylesheets/darkswarm/help-modal.css.scss | 9 +++++++++ 3 files changed, 28 insertions(+) create mode 100644 app/assets/javascripts/darkswarm/directives/help_modal.js.coffee create mode 100644 app/assets/javascripts/templates/help-modal.html.haml create mode 100644 app/assets/stylesheets/darkswarm/help-modal.css.scss diff --git a/app/assets/javascripts/darkswarm/directives/help_modal.js.coffee b/app/assets/javascripts/darkswarm/directives/help_modal.js.coffee new file mode 100644 index 000000000000..6aef4f481b0b --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/help_modal.js.coffee @@ -0,0 +1,10 @@ +Darkswarm.directive "helpModal", ($modal, $compile, $templateCache)-> + restrict: 'A' + scope: + helpText: "@helpModal" + + link: (scope, elem, attrs, ctrl)-> + compiled = $compile($templateCache.get('help-modal.html'))(scope) + + elem.on "click", => + $modal.open(controller: ctrl, template: compiled, scope: scope, windowClass: 'help-modal small') diff --git a/app/assets/javascripts/templates/help-modal.html.haml b/app/assets/javascripts/templates/help-modal.html.haml new file mode 100644 index 000000000000..f87abd8fd454 --- /dev/null +++ b/app/assets/javascripts/templates/help-modal.html.haml @@ -0,0 +1,9 @@ +.row.help-icon + .small-12.text-center + %i.ofn-i_013-help +.row.help-text + .small-12.columns.text-center + {{ helpText }} +.row.text-center + %button.primary.small{ ng: { click: '$close()' } } + = t(:ok) diff --git a/app/assets/stylesheets/darkswarm/help-modal.css.scss b/app/assets/stylesheets/darkswarm/help-modal.css.scss new file mode 100644 index 000000000000..c26b17edc2d5 --- /dev/null +++ b/app/assets/stylesheets/darkswarm/help-modal.css.scss @@ -0,0 +1,9 @@ +.help-modal { + .help-text { + font-size: 1rem; + margin: 20px 0px; + } + .help-icon { + font-size: 4rem; + } +} From 7db7084008106dd3aa80c4c786d0495107b992d2 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 22 Jun 2018 14:05:05 +1000 Subject: [PATCH 14/18] Use help-modal to display help text for authorised shops Also updated the text slightly to make it more clear when the purpose of authorised shops are --- app/views/spree/users/_cards.html.haml | 2 +- config/locales/en.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/spree/users/_cards.html.haml b/app/views/spree/users/_cards.html.haml index 8929aac79189..180dc7d47809 100644 --- a/app/views/spree/users/_cards.html.haml +++ b/app/views/spree/users/_cards.html.haml @@ -17,6 +17,6 @@ .authorised_shops{ ng: { controller: 'AuthorisedShopsCtrl', hide: 'CreditCard.visible' } } %h3 = t('.authorised_shops') - %button.button.secondary.tiny.help-btn.ng-scope{ :popover => t('.authorised_shops_popover'), "popover-placement" => 'right' } + %button.button.secondary.tiny.help-btn.ng-scope{ "help-modal" => t('.authorised_shops_popover') } %i.ofn-i_013-help = render 'authorised_shops' diff --git a/config/locales/en.yml b/config/locales/en.yml index 597dd60c3605..aaec61c99a21 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2742,6 +2742,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using delete?: Delete? cards: authorised_shops: Authorised Shops - authorised_shops_popover: This is a list of shops which are permitted to charge your default credit card for OFN services (eg. subscriptions). Your card details will be kept secure and will not be shared with shop owners. + 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. localized_number: invalid_format: has an invalid format. Please enter a number. From d1d9c5a092d91b84e2f64dc7e6c1b5a237993f17 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 22 Jun 2018 14:12:41 +1000 Subject: [PATCH 15/18] Add help button to saved cards list on account page Describes the purpose of the default card --- app/views/spree/users/_cards.html.haml | 6 +++++- config/locales/en.yml | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/views/spree/users/_cards.html.haml b/app/views/spree/users/_cards.html.haml index 180dc7d47809..6f7d13a06a99 100644 --- a/app/views/spree/users/_cards.html.haml +++ b/app/views/spree/users/_cards.html.haml @@ -2,7 +2,11 @@ .credit_cards{"ng-controller" => "CreditCardsCtrl"} .row .small-12.medium-6.columns - %h3= t(:saved_cards) + %h3 + = t(:saved_cards) + %button.button.secondary.tiny.help-btn.ng-scope{ "help-modal" => t('.saved_cards_popover') } + %i.ofn-i_013-help + .saved_cards{ ng: { show: 'savedCreditCards.length > 0' } } = render 'saved_cards' .no_cards{ ng: { hide: 'savedCreditCards.length > 0' } } diff --git a/config/locales/en.yml b/config/locales/en.yml index aaec61c99a21..8a34f8b9ffaa 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2743,5 +2743,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using 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). localized_number: invalid_format: has an invalid format. Please enter a number. From 5e6291bce36de8315a8fb6ee60e3e9dadf796944 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 22 Jun 2018 15:31:32 +1000 Subject: [PATCH 16/18] Don't request customers if list is already populated --- app/assets/javascripts/darkswarm/services/customers.js.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/darkswarm/services/customers.js.coffee b/app/assets/javascripts/darkswarm/services/customers.js.coffee index cf5c56563a6e..fe2c862c37fd 100644 --- a/app/assets/javascripts/darkswarm/services/customers.js.coffee +++ b/app/assets/javascripts/darkswarm/services/customers.js.coffee @@ -4,6 +4,7 @@ angular.module("Darkswarm").factory 'Customers', (Customer) -> byID: {} index: (params={}) -> + return @all if @all.length Customer.index params, (data) => @load(data) @all From 0d9b03b0660ad32dcdd3dda217be483e56468bd1 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Fri, 22 Jun 2018 11:42:50 +0100 Subject: [PATCH 17/18] Hide postcode - not necessary as passed from billing address and wrecks mobile UX --- .../javascripts/darkswarm/directives/stripe_elements.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/darkswarm/directives/stripe_elements.js.coffee b/app/assets/javascripts/darkswarm/directives/stripe_elements.js.coffee index d325b6f9623f..d88e0868a60e 100644 --- a/app/assets/javascripts/darkswarm/directives/stripe_elements.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/stripe_elements.js.coffee @@ -10,7 +10,7 @@ Darkswarm.directive "stripeElements", ($injector, StripeElements) -> stripe = $injector.get('stripeObject') card = stripe.elements().create 'card', - hidePostalCode: false + hidePostalCode: true style: base: fontFamily: "Roboto, Arial, sans-serif" From ddb9ae1140a9df5d4e4f2a204595c81ff9d77060 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 22 Jun 2018 15:34:17 +1000 Subject: [PATCH 18/18] Load all shops that a user is associated with as a customer Regardless of the presence of an order --- app/controllers/api/customers_controller.rb | 2 +- app/helpers/injection_helper.rb | 3 ++- app/models/customer.rb | 5 +++++ spec/controllers/api/customers_controller_spec.rb | 12 ++++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/customers_controller.rb b/app/controllers/api/customers_controller.rb index cbbe4ce35f33..e983b372c914 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 + @customers = current_api_user.customers.of_regular_shops render json: @customers, each_serializer: CustomerSerializer end diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index ea5589abe6ae..f4e857607e86 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -69,7 +69,8 @@ def inject_orders end def inject_shops - shops = Enterprise.where(id: @orders.pluck(:distributor_id).uniq) + customers = spree_current_user.customers.of_regular_shops + 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/models/customer.rb b/app/models/customer.rb index dfddd90c80d7..0ec6f5226287 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -23,6 +23,11 @@ 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/spec/controllers/api/customers_controller_spec.rb b/spec/controllers/api/customers_controller_spec.rb index f6c8f4e0a5d3..360037cc90a1 100644 --- a/spec/controllers/api/customers_controller_spec.rb +++ b/spec/controllers/api/customers_controller_spec.rb @@ -23,6 +23,18 @@ 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