diff --git a/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee b/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee index 8ebe07d456..d2e6d58562 100644 --- a/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee +++ b/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.customers").controller "customersCtrl", ($scope, Customers, Columns, pendingChanges, shops) -> +angular.module("admin.customers").controller "customersCtrl", ($scope, CustomerResource, Columns, pendingChanges, shops) -> $scope.shop = {} $scope.shops = shops $scope.submitAll = pendingChanges.submitAll @@ -10,8 +10,24 @@ angular.module("admin.customers").controller "customersCtrl", ($scope, Customers $scope.$watch "shop.id", -> if $scope.shop.id? - Customers.loaded = false - $scope.customers = Customers.index(enterprise_id: $scope.shop.id) + $scope.customers = index {enterprise_id: $scope.shop.id} - $scope.loaded = -> - Customers.loaded + $scope.add = (email) -> + params = + enterprise_id: $scope.shop.id + email: email + CustomerResource.create params, (customer) => + if customer.id + $scope.customers.push customer + $scope.quickSearch = customer.email + + $scope.deleteCustomer = (customer) -> + params = id: customer.id + CustomerResource.destroy params, -> + i = $scope.customers.indexOf customer + $scope.customers.splice i, 1 unless i < 0 + + index = (params) -> + $scope.loaded = false + CustomerResource.index params, => + $scope.loaded = true diff --git a/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee b/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee index 523e0c1495..5b6c1ab205 100644 --- a/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee +++ b/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee @@ -1,8 +1,17 @@ angular.module("admin.customers").factory 'CustomerResource', ($resource) -> - $resource('/admin/customers.json', {}, { + $resource('/admin/customers/:id.json', {}, { 'index': method: 'GET' isArray: true params: enterprise_id: '@enterprise_id' + 'create': + method: 'POST' + params: + enterprise_id: '@enterprise_id' + email: '@email' + 'destroy': + method: 'DELETE' + params: + id: '@id' }) diff --git a/app/assets/javascripts/admin/customers/services/customers.js.coffee b/app/assets/javascripts/admin/customers/services/customers.js.coffee deleted file mode 100644 index 9acfa317d2..0000000000 --- a/app/assets/javascripts/admin/customers/services/customers.js.coffee +++ /dev/null @@ -1,16 +0,0 @@ -angular.module("admin.customers").factory 'Customers', (CustomerResource) -> - new class Customers - customers: [] - customers_by_id: {} - loaded: false - - index: (params={}, callback=null) -> - CustomerResource.index params, (data) => - for customer in data - @customers.push customer - @customers_by_id[customer.id] = customer - - @loaded = true - (callback || angular.noop)(@customers) - - @customers diff --git a/app/controllers/admin/customers_controller.rb b/app/controllers/admin/customers_controller.rb index b1ceb88c2f..14712662b6 100644 --- a/app/controllers/admin/customers_controller.rb +++ b/app/controllers/admin/customers_controller.rb @@ -14,6 +14,16 @@ module Admin end end + def create + @customer = Customer.new(params[:customer]) + if spree_current_user.enterprises.include? @customer.enterprise + @customer.save + render json: Api::Admin::CustomerSerializer.new(@customer).to_json + else + redirect_to '/unauthorized' + end + end + private def collection diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index c234cf1358..867d3c9e2b 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -101,6 +101,11 @@ class AbilityDecorator can [:print], Spree::Order do |order| order.user == user end + + can [:create], Customer + can [:destroy], Customer do |customer| + user.enterprises.include? customer.enterprise + end end def add_product_management_abilities(user) diff --git a/app/views/admin/customers/index.html.haml b/app/views/admin/customers/index.html.haml index 1b2dc11547..e33b871df4 100644 --- a/app/views/admin/customers/index.html.haml +++ b/app/views/admin/customers/index.html.haml @@ -5,7 +5,7 @@ = admin_inject_shops %div{ ng: { app: 'admin.customers', controller: 'customersCtrl' } } - .row{ ng: { hide: "loaded() && customers.length > 0" } } + .row{ ng: { hide: "loaded && customers.length > 0" } } .five.columns.alpha %h3 =t :please_select_hub @@ -13,24 +13,23 @@ %select.select2.fullwidth#shop_id{ 'ng-model' => 'shop.id', name: 'shop_id', 'ng-options' => 'shop.id as shop.name for shop in shops' } .seven.columns.omega   - .row{ 'ng-hide' => '!loaded() || customers.length == 0' } + .row{ 'ng-hide' => '!loaded || customers.length == 0' } .controls.sixteen.columns.alpha.omega .five.columns.alpha %input.fullwidth{ :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' } - .five.columns   - .three.columns   + .eight.columns   = render 'admin/shared/columns_dropdown' - .row{ 'ng-if' => 'shop.id && !loaded()' } + .row{ 'ng-if' => 'shop.id && !loaded' } .sixteen.columns.alpha#loading %img.spinner{ src: "/assets/spinning-circles.svg" } %h1 =t :loading_customers - .row{ :class => "sixteen columns alpha", 'ng-show' => 'loaded() && filteredCustomers.length == 0'} + .row{ :class => "sixteen columns alpha", 'ng-show' => 'loaded && filteredCustomers.length == 0'} %h1#no_results =t :no_customers_found - .row{ ng: { show: "loaded() && filteredCustomers.length > 0" } } + .row{ ng: { show: "loaded && filteredCustomers.length > 0" } } %form{ name: "customers" } %table.index#customers %col.email{ width: "20%"} @@ -61,3 +60,11 @@ %td.actions %a{ 'ng-click' => "deleteCustomer(customer)", :class => "delete-customer icon-trash no-text" } %input{ :type => "button", 'value' => 'Update', 'ng-click' => 'submitAll()' } + + %form{ng: {show: "loaded", submit: 'add(newCustomerEmail)'}} + %h2= t '.add_new_customer' + .row + .five.columns.alpha + %input.fullwidth{type: "text", placeholder: t('.customer_placeholder'), ng: {model: 'newCustomerEmail'}} + .eleven.columns.omega + %input{type: "submit", value: t('.add_customer')} diff --git a/config/locales/en.yml b/config/locales/en.yml index 40926259f7..7b5f9b168f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -80,6 +80,10 @@ en: whats_this: What's this? + customers: + index: + add_customer: "Add customer" + customer_placeholder: "customer@example.org" inventory: 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 diff --git a/config/routes.rb b/config/routes.rb index 6293117a3f..8d8d6d27c3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -115,7 +115,7 @@ Openfoodnetwork::Application.routes.draw do resources :inventory_items, only: [:create, :update] - resources :customers, only: [:index, :update] + resources :customers, only: [:index, :create, :update, :destroy] resource :content diff --git a/spec/controllers/admin/customers_controller_spec.rb b/spec/controllers/admin/customers_controller_spec.rb index bb2e4888c2..2f9b2024e9 100644 --- a/spec/controllers/admin/customers_controller_spec.rb +++ b/spec/controllers/admin/customers_controller_spec.rb @@ -94,4 +94,37 @@ describe Admin::CustomersController, type: :controller do end end end + + describe "create" do + let(:enterprise) { create(:distributor_enterprise) } + let(:another_enterprise) { create(:distributor_enterprise) } + + def create_customer(enterprise) + spree_put :create, format: :json, customer: { email: 'new@example.com', enterprise_id: enterprise.id } + end + + context "json" do + let!(:customer) { create(:customer, enterprise: enterprise) } + + context "where I manage the customer's enterprise" do + before do + controller.stub spree_current_user: enterprise.owner + end + + it "allows me to create the customer" do + expect { create_customer enterprise }.to change(Customer, :count).by(1) + end + end + + context "where I don't manage the customer's enterprise" do + before do + controller.stub spree_current_user: another_enterprise.owner + end + + it "prevents me from creating the customer" do + expect { create_customer enterprise }.to change(Customer, :count).by(0) + end + end + end + end end diff --git a/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee b/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee index 22777a6528..215f2834ed 100644 --- a/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee @@ -1,25 +1,49 @@ describe "CustomersCtrl", -> - ctrl = null scope = null - Customers = null + http = null beforeEach -> - shops = "list of shops" - module('admin.customers') - inject ($controller, $rootScope, _Customers_) -> + inject ($controller, $rootScope, _CustomerResource_, $httpBackend) -> scope = $rootScope - Customers = _Customers_ - ctrl = $controller 'customersCtrl', {$scope: scope, Customers: Customers, shops: shops} + http = $httpBackend + $controller 'customersCtrl', {$scope: scope, CustomerResource: _CustomerResource_, shops: {}} + this.addMatchers + toAngularEqual: (expected) -> + return angular.equals(this.actual, expected) + + it "has no shop pre-selected", -> + expect(scope.shop).toEqual {} describe "setting the shop on scope", -> + customer = { id: 5, email: 'someone@email.com'} + customers = [customer] + beforeEach -> - spyOn(Customers, "index").andReturn "list of customers" + http.expectGET('/admin/customers.json?enterprise_id=1').respond 200, customers scope.$apply -> scope.shop = {id: 1} + http.flush() - it "calls Customers#index with the correct params", -> - expect(Customers.index).toHaveBeenCalledWith({enterprise_id: 1}) + it "retrievs the list of customers", -> + expect(scope.customers).toAngularEqual customers - it "resets $scope.customers with the result of Customers#index", -> - expect(scope.customers).toEqual "list of customers" + describe "scope.add", -> + it "creates a new customer", -> + email = "customer@example.org" + newCustomer = {id: 6, email: email} + customers.push(newCustomer) + http.expectPOST('/admin/customers.json?email=' + email + '&enterprise_id=1').respond 200, newCustomer + scope.add(email) + http.flush() + expect(scope.customers).toAngularEqual customers + + describe "scope.deleteCustomer", -> + it "deletes a customer", -> + expect(scope.customers.length).toBe 2 + customer = scope.customers[0] + http.expectDELETE('/admin/customers/' + customer.id + '.json').respond 200 + scope.deleteCustomer(customer) + http.flush() + expect(scope.customers.length).toBe 1 + expect(scope.customers[0]).not.toAngularEqual customer diff --git a/spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee b/spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee deleted file mode 100644 index 7123055d63..0000000000 --- a/spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee +++ /dev/null @@ -1,31 +0,0 @@ -describe "Customers service", -> - Customers = CustomerResource = customers = $httpBackend = null - - beforeEach -> - module 'admin.customers' - - inject ($q, _$httpBackend_, _Customers_, _CustomerResource_) -> - Customers = _Customers_ - CustomerResource = _CustomerResource_ - $httpBackend = _$httpBackend_ - $httpBackend.expectGET('/admin/customers.json?enterprise_id=2').respond 200, [{ id: 5, email: 'someone@email.com'}] - - describe "#index", -> - result = null - - beforeEach -> - expect(Customers.loaded).toBe false - result = Customers.index(enterprise_id: 2) - $httpBackend.flush() - - it "stores returned data in @customers, with ids as keys", -> - # This is super weird and freaking annoying. I think resource results have extra - # properties ($then, $promise) that cause them to not be equal to the reponse object - # provided to the expectGET clause above. - expect(Customers.customers).toEqual [ new CustomerResource({ id: 5, email: 'someone@email.com'}) ] - - it "returns @customers", -> - expect(result).toEqual Customers.customers - - it "sets @loaded to true", -> - expect(Customers.loaded).toBe true