From 4835ef067f06a20db8a27664e219cdc31e486e50 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 3 Aug 2016 17:23:02 +1000 Subject: [PATCH] Add feature to remove line items from open order cycle - Add JS controller to send delete requests. - Add resource controller to destroy items. - Add authorisation check to abilities. - Update fees after removing line item. --- .../edit_order_controller.js.coffee | 10 +++++ .../darkswarm/services/cart.js.coffee | 1 - app/controllers/line_items_controller.rb | 24 ++++++++++++ app/models/spree/ability_decorator.rb | 8 ++++ app/views/shared/menu/_cart.html.haml | 4 +- app/views/spree/orders/_bought.html.haml | 6 ++- app/views/spree/orders/_line_item.html.haml | 1 - config/routes.rb | 2 + .../controllers/line_items_controller_spec.rb | 37 +++++++++++++++++++ spec/features/consumer/shopping/cart_spec.rb | 29 +++++++++++++++ 10 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/controllers/edit_order_controller.js.coffee create mode 100644 app/controllers/line_items_controller.rb create mode 100644 spec/controllers/line_items_controller_spec.rb diff --git a/app/assets/javascripts/darkswarm/controllers/edit_order_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/edit_order_controller.js.coffee new file mode 100644 index 0000000000..e39b79c48b --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/edit_order_controller.js.coffee @@ -0,0 +1,10 @@ +Darkswarm.controller "EditOrderCtrl", ($scope, $resource, Cart) -> + + $scope.deleteLineItem = (id) -> + params = {id: id} + success = (response) -> + $(".line-item-" + id).remove() + fail = (error) -> + console.log error + + $resource("/line_items/:id").delete(params, success, fail) diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee index bc1c2d6504..9ad375f873 100644 --- a/app/assets/javascripts/darkswarm/services/cart.js.coffee +++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee @@ -102,7 +102,6 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, $modal, $roo for line_item in items line_item.variant.line_item = line_item Variants.extend line_item.variant - line_item.variant.extended_name = @extendedVariantName(line_item.variant) items total_item_count: => diff --git a/app/controllers/line_items_controller.rb b/app/controllers/line_items_controller.rb new file mode 100644 index 0000000000..5846538411 --- /dev/null +++ b/app/controllers/line_items_controller.rb @@ -0,0 +1,24 @@ +class LineItemsController < Spree::BaseController + respond_to :json + + def destroy + item = Spree::LineItem.find(params[:id]) + authorize! :destroy, item + destroy_with_lock item + respond_with(item) + end + + # Override default which just redirects + # See Spree::BaseController and Spree::Core::ControllerHelpers::Auth + def unauthorized + render text: '', status: 403 + end + + def destroy_with_lock(item) + order = item.order + order.with_lock do + item.destroy + order.update_distribution_charge! + end + end +end diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 3814365447..7642c42405 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -4,6 +4,7 @@ class AbilityDecorator # All abilites are allocated from this initialiser, currently in 5 chunks. # Spree also defines other abilities. def initialize(user) + add_shopping_abilities user add_base_abilities user if is_new_user? user add_enterprise_management_abilities user if can_manage_enterprises? user add_group_management_abilities user if can_manage_groups? user @@ -50,6 +51,12 @@ class AbilityDecorator can_manage_enterprises? user end + def add_shopping_abilities(user) + can [:destroy], Spree::LineItem do |item| + item.andand.order.andand.order_cycle.andand.open? && item.order.user_id == user.id + end + end + # New users can create an enterprise, and gain other permissions from doing this. def add_base_abilities(user) can [:create], Enterprise @@ -184,6 +191,7 @@ class AbilityDecorator can [:admin , :for_line_items], Enterprise can [:admin, :index, :create], Spree::LineItem can [:destroy, :update], Spree::LineItem do |item| + order = item.order user.admin? || user.enterprises.include?(order.distributor) || order.order_cycle.andand.coordinated_by?(user) end diff --git a/app/views/shared/menu/_cart.html.haml b/app/views/shared/menu/_cart.html.haml index 954246dd36..cfad66bb6a 100644 --- a/app/views/shared/menu/_cart.html.haml +++ b/app/views/shared/menu/_cart.html.haml @@ -48,11 +48,11 @@ = "{{ Cart.dirty ? '#{t(:cart_updating)}' : (Cart.empty() ? '#{t(:cart_empty)}' : '#{t(:cart_edit)}' ) }}" %a.button.primary.tiny{href: checkout_path, "ng-disabled" => "Cart.dirty || Cart.empty()"} = t 'checkout' - %h5{"ng-if" => "Cart.line_items_finalised().present()", style: 'margin-top: 1em'} + %h5{"ng-if" => "Cart.line_items_finalised().length", style: 'margin-top: 1em'} = t '.already_ordered_products' %table %tr.product-cart{"ng-repeat" => "line_item in Cart.line_items_finalised()", - "ng-controller" => "LineItemCtrl", "id" => "cart-variant-{{ line_item.variant.id }}"} + "id" => "cart-variant-{{ line_item.variant.id }}"} %td %small %strong diff --git a/app/views/spree/orders/_bought.html.haml b/app/views/spree/orders/_bought.html.haml index b3f32b1045..2cc33d52aa 100644 --- a/app/views/spree/orders/_bought.html.haml +++ b/app/views/spree/orders/_bought.html.haml @@ -1,4 +1,4 @@ -%div +%div{ng: {controller: "EditOrderCtrl"}} %div %div .row @@ -21,7 +21,7 @@ %tbody - @order.finalised_line_items.each do |line_item| - variant = line_item.variant - %tr.line-item{class: "variant-#{variant.id}"} + %tr.line-item{class: "line-item-#{line_item.id} variant-#{variant.id}"} %td.cart-item-description %div.item-thumb-image @@ -39,3 +39,5 @@ = line_item.display_amount_with_adjustments.to_html unless line_item.quantity.nil? %td.cart-item-delete.text-center + %a.delete{ng: {click: "deleteLineItem(#{line_item.id})"}} + %i.delete.ofn-i_026-trash diff --git a/app/views/spree/orders/_line_item.html.haml b/app/views/spree/orders/_line_item.html.haml index 5976332a18..14219c4e73 100644 --- a/app/views/spree/orders/_line_item.html.haml +++ b/app/views/spree/orders/_line_item.html.haml @@ -35,6 +35,5 @@ = line_item.display_amount_with_adjustments.to_html unless line_item.quantity.nil? %td.cart-item-delete.text-center{"data-hook" => "cart_item_delete"} - {{ quantity }} %a.delete{href: "#", id: "delete_#{dom_id(line_item)}"} %i.delete.ofn-i_026-trash diff --git a/config/routes.rb b/config/routes.rb index 90700d89b0..26a7b85df2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -42,6 +42,8 @@ Openfoodnetwork::Application.routes.draw do end end + resources :line_items, only: [:destroy] + resources :groups, only: [:index, :show] do collection do get :signup diff --git a/spec/controllers/line_items_controller_spec.rb b/spec/controllers/line_items_controller_spec.rb new file mode 100644 index 0000000000..4081e1fc86 --- /dev/null +++ b/spec/controllers/line_items_controller_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe LineItemsController do + let(:item) { create(:line_item) } + let(:item_with_oc) do + order_cycle = create(:simple_order_cycle) + item.order.order_cycle = order_cycle + item.order.save + item + end + + it "fails without line item id" do + expect { delete :destroy }.to raise_error + end + + it "denies deletion without order cycle" do + request = { format: :json, id: item } + delete :destroy, request + expect(response.status).to be 403 + expect { item.reload }.to_not raise_error + end + + it "denies deletion without user" do + request = { format: :json, id: item_with_oc } + delete :destroy, request + expect(response.status).to be 403 + expect { item.reload }.to_not raise_error + end + + it "deletes the line item" do + controller.stub spree_current_user: item.order.user + request = { format: :json, id: item_with_oc } + delete :destroy, request + expect(response.status).to be 204 + expect { item.reload }.to raise_error + end +end diff --git a/spec/features/consumer/shopping/cart_spec.rb b/spec/features/consumer/shopping/cart_spec.rb index b604769502..18d1166a72 100644 --- a/spec/features/consumer/shopping/cart_spec.rb +++ b/spec/features/consumer/shopping/cart_spec.rb @@ -84,5 +84,34 @@ feature "full-page cart", js: true do page.should have_content "Insufficient stock available, only 2 remaining" end end + + context "when ordered in the same order cycle" do + let(:address) { create(:address) } + let(:user) { create(:user, bill_address: address, ship_address: address) } + let!(:prev_order1) { create(:completed_order_with_totals, order_cycle: order_cycle, distributor: distributor, user: user) } + let!(:prev_order2) { create(:completed_order_with_totals, order_cycle: order_cycle, distributor: distributor, user: user) } + + before do + order.user = user + order.save + add_product_to_cart order, product_tax + quick_login_as user + visit spree.cart_path + end + + it "shows already ordered line items" do + item1 = prev_order1.line_items.first + item2 = prev_order2.line_items.first + expect(page).to have_content item1.variant.name + expect(page).to have_content item2.variant.name + page.find(".line-item-#{item1.id} a.delete").click + expect(page).to have_no_content item1.variant.name + expect(page).to have_content item2.variant.name + + visit spree.cart_path + expect(page).to have_no_content item1.variant.name + expect(page).to have_content item2.variant.name + end + end end end