From db45d7f4eba71d50b14f92ccc85383cc05ecd590 Mon Sep 17 00:00:00 2001 From: Guido Oliveira Date: Wed, 6 Oct 2021 14:32:55 -0300 Subject: [PATCH] Cancel empty orders on BOM page --- .../line_items_controller.js.coffee | 37 ++++-- app/serializers/api/admin/order_serializer.rb | 6 +- config/locales/en.yml | 2 + .../line_items_controller_spec.js.coffee | 106 ++++++++++++++++-- .../admin/bulk_order_management_spec.rb | 12 +- 5 files changed, 139 insertions(+), 24 deletions(-) diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index ccd4902eac..4c8e47f63f 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -104,15 +104,38 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, else StatusMessage.display 'failure', t "unsaved_changes_error" + $scope.cancelOrder = (order) -> + return $http( + method: 'GET' + url: "/admin/orders/#{order.number}/fire?e=cancel") + $scope.deleteLineItem = (lineItem) -> - if ($scope.confirmDelete && confirm(t "are_you_sure")) || !$scope.confirmDelete - LineItems.delete lineItem + if lineItem.order.item_count == 1 + if confirm(t('js.admin.deleting_item_will_cancel_order')) + $scope.cancelOrder(lineItem.order).then(-> $scope.refreshData()) + else if ($scope.confirmDelete && confirm(t "are_you_sure")) || !$scope.confirmDelete + LineItems.delete(lineItem, () -> $scope.refreshData()) - $scope.deleteLineItems = (lineItemsToDelete) -> - existingState = $scope.confirmDelete - $scope.confirmDelete = false - $scope.deleteLineItem lineItem for lineItem in lineItemsToDelete when lineItem.checked - $scope.confirmDelete = existingState + $scope.deleteLineItems = (lineItems) -> + lineItemsToDelete = lineItems.filter (item) -> item.checked + willCancelOrders = false + itemsPerOrder = new Map() + for item in lineItemsToDelete + { order } = item + if itemsPerOrder.has(order) + itemsPerOrder.get(order).push(item) + else + itemsPerOrder.set(order, [item]) + willCancelOrders = true if (order.item_count == itemsPerOrder.get(order).length) + + if willCancelOrders + return unless confirm(t("js.admin.deleting_item_will_cancel_order")) + + itemsPerOrder.forEach (items, order) => + if order.item_count == items.length + $scope.cancelOrder(order).then(-> $scope.refreshData()) + else + Promise.all(LineItems.delete(item) for item in items).then(-> $scope.refreshData()) $scope.allBoxesChecked = -> checkedCount = $scope.filteredLineItems.reduce (count,lineItem) -> diff --git a/app/serializers/api/admin/order_serializer.rb b/app/serializers/api/admin/order_serializer.rb index 4a35d593fa..c13d22240b 100644 --- a/app/serializers/api/admin/order_serializer.rb +++ b/app/serializers/api/admin/order_serializer.rb @@ -7,7 +7,7 @@ module Api :edit_path, :state, :payment_state, :shipment_state, :payments_path, :ready_to_ship, :ready_to_capture, :created_at, :distributor_name, :special_instructions, :display_outstanding_balance, - :item_total, :adjustment_total, :payment_total, :total + :item_total, :adjustment_total, :payment_total, :total, :item_count has_one :distributor, serializer: Api::Admin::IdSerializer has_one :order_cycle, serializer: Api::Admin::IdSerializer @@ -69,6 +69,10 @@ module Api object.completed_at.blank? ? "" : I18n.l(object.completed_at, format: '%B %d, %Y') end + def item_count + object.line_items.count + end + private def spree_routes_helper diff --git a/config/locales/en.yml b/config/locales/en.yml index a2f2bddb1a..7f0b27292b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2748,6 +2748,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using admin: unit_price_tooltip: "The unit price increases transparency by allowing your customers to easily compare prices between different products and packaging sizes. Note, that the final unit price displayed in the shopfront might differ as it is includes taxes & fees." enterprise_limit_reached: "You have reached the standard limit of enterprises per account. Write to %{contact_email} if you need to increase it." + deleting_item_will_cancel_order: "This operation will result in one or more empty orders, which will be cancelled. Do you wish to proceed?" modals: got_it: "Got it" close: "Close" @@ -3245,6 +3246,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using last: "Last" spree: + order_updated: "Order Updated" add_country: "Add country" add_state: "Add state" adjustment: "Adjustment" diff --git a/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee b/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee index f0808722cf..0399f77604 100644 --- a/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee @@ -44,6 +44,9 @@ describe "LineItemsCtrl", -> LineItems = index: jasmine.createSpy('index').and.returnValue(lineItem) all: [lineItem] + delete: (lineItem, callback=null) -> + callback() if callback + return Promise.resolve() httpBackend.expectGET("/api/v0/orders.json?q%5Bcompleted_at_gteq%5D=SomeDate&q%5Bcompleted_at_lt%5D=SomeDate&q%5Bcompleted_at_not_null%5D=true&q%5Bdistributor_id_eq%5D=&q%5Border_cycle_id_eq%5D=&q%5Bshipment_state_not_eq%5D=shipped&q%5Bstate_not_eq%5D=canceled").respond {orders: [order], pagination: {page: 1, pages: 1, results: 1}} httpBackend.expectGET("/admin/enterprises/visible.json?ams_prefix=basic&q%5Bsells_in%5D%5B%5D=own&q%5Bsells_in%5D%5B%5D=any").respond [distributor] @@ -98,23 +101,102 @@ describe "LineItemsCtrl", -> it "resets the form state to pristine", -> expect(scope.bulk_order_form.$setPristine.calls.count()).toBe 1 - describe "deleting 'checked' line items", -> - line_item1 = line_item2 = line_item3 = line_item4 = null + describe "deleting a line item", -> + line_item1 = line_item2 = null + order1 = order2 = null beforeEach -> - line_item1 = { name: "line item 1", checked: false } - line_item2 = { name: "line item 2", checked: true } - line_item3 = { name: "line item 3", checked: false } - line_item4 = { name: "line item 4", checked: true } + order1 = { id: 1, item_count: 1 } + order2 = { id: 2, item_count: 2 } + + line_item1 = { + name: "line item 1", + order: order1 + } + line_item2 = { + name: "line item 2", + order: order2 + } + scope.line_items = [ line_item1, line_item2 ] + + it "shows a different message if order will be canceled", -> + spyOn(window, "confirm") + scope.deleteLineItem(line_item2) + expect(confirm).not.toHaveBeenCalledWith("This operation will result in one or more empty orders, which will be cancelled. Do you wish to proceed?") + scope.deleteLineItem(line_item1) + expect(confirm).toHaveBeenCalledWith("This operation will result in one or more empty orders, which will be cancelled. Do you wish to proceed?") + + it "deletes the line item", -> + spyOn(window, "confirm").and.callFake(-> return true) + spyOn(LineItems, "delete") + scope.deleteLineItem(line_item2) + expect(LineItems.delete).toHaveBeenCalledWith(line_item2, jasmine.anything()) + + it "cancels empty order", -> + spyOn(window, "confirm").and.callFake(-> return true) + spyOn(scope, "cancelOrder").and.callFake(-> return Promise.resolve()) + scope.deleteLineItem(line_item1) + expect(scope.cancelOrder).toHaveBeenCalledWith(order1) + + describe "deleting 'checked' line items", -> + line_item1 = line_item2 = line_item3 = line_item4 = null + order1 = order2 = order3 = null + + beforeEach -> + order1 = { id: 1, item_count: 1 } + order2 = { id: 2, item_count: 1 } + order3 = { id: 3, item_count: 2 } + line_item1 = { + name: "line item 1", + order: order1 + checked: false + } + line_item2 = { + name: "line item 2", + order: order2, + checked: false + } + line_item3 = { + name: "line item 3", + order: order3, + checked: false + } + line_item4 = { + name: "line item 4", + order: order3, + checked: false + } scope.line_items = [ line_item1, line_item2, line_item3, line_item4 ] - it "calls deletedLineItem for each 'checked' line item", -> - spyOn(scope, "deleteLineItem") + it "asks for confirmation only if orders will be canceled", -> + spyOn(window, "confirm") + line_item3.checked = true scope.deleteLineItems(scope.line_items) - expect(scope.deleteLineItem).toHaveBeenCalledWith(line_item2) - expect(scope.deleteLineItem).toHaveBeenCalledWith(line_item4) - expect(scope.deleteLineItem).not.toHaveBeenCalledWith(line_item1) - expect(scope.deleteLineItem).not.toHaveBeenCalledWith(line_item3) + expect(confirm).not.toHaveBeenCalled() + line_item1.checked = true + scope.deleteLineItems(scope.line_items) + expect(confirm).toHaveBeenCalledWith("This operation will result in one or more empty orders, which will be cancelled. Do you wish to proceed?") + + + it "deletes checked line items for non-empty orders", -> + line_item1.checked = true + line_item3.checked = true + spyOn(window, "confirm").and.callFake(-> return true) + spyOn(LineItems, "delete") + scope.deleteLineItems(scope.line_items) + expect(LineItems.delete).toHaveBeenCalledWith(line_item3) + expect(LineItems.delete).not.toHaveBeenCalledWith(line_item1) + expect(LineItems.delete).not.toHaveBeenCalledWith(line_item2) + expect(LineItems.delete).not.toHaveBeenCalledWith(line_item4) + + it "cancels all empty orders", -> + line_item1.checked = true + line_item3.checked = true + spyOn(window, "confirm").and.callFake(-> return true) + spyOn(scope, "cancelOrder").and.callFake(-> return Promise.resolve()) + scope.deleteLineItems(scope.line_items) + expect(scope.cancelOrder).toHaveBeenCalledWith(order1) + expect(scope.cancelOrder).not.toHaveBeenCalledWith(order3) describe "check boxes for line items", -> line_item1 = line_item2 = null diff --git a/spec/system/admin/bulk_order_management_spec.rb b/spec/system/admin/bulk_order_management_spec.rb index 4575db79c2..417cb61b67 100644 --- a/spec/system/admin/bulk_order_management_spec.rb +++ b/spec/system/admin/bulk_order_management_spec.rb @@ -670,8 +670,10 @@ describe ' within("tr#li_#{li2.id} td.bulk") do check "bulk" end - find("div#bulk-actions-dropdown").click - find("div#bulk-actions-dropdown div.menu_item", text: "Delete Selected" ).click + page.driver.accept_modal :confirm do + find("div#bulk-actions-dropdown").click + find("div#bulk-actions-dropdown div.menu_item", text: "Delete Selected" ).click + end expect(page).to have_selector "tr#li_#{li1.id}" expect(page).to have_no_selector "tr#li_#{li2.id}" end @@ -693,8 +695,10 @@ describe ' check "toggle_bulk" fill_in "quick_search", with: o1.number expect(page).to have_no_selector "tr#li_#{li2.id}" - find("div#bulk-actions-dropdown").click - find("div#bulk-actions-dropdown div.menu_item", text: "Delete Selected" ).click + page.driver.accept_modal :confirm do + find("div#bulk-actions-dropdown").click + find("div#bulk-actions-dropdown div.menu_item", text: "Delete Selected" ).click + end expect(page).to have_no_selector "tr#li_#{li1.id}" expect(page).to have_selector "#quick_search" fill_in "quick_search", with: ''