diff --git a/app/assets/javascripts/admin/spree/orders/variant_autocomplete.js.erb b/app/assets/javascripts/admin/spree/orders/variant_autocomplete.js.erb
index 23ba80d3fb..ea46231ece 100644
--- a/app/assets/javascripts/admin/spree/orders/variant_autocomplete.js.erb
+++ b/app/assets/javascripts/admin/spree/orders/variant_autocomplete.js.erb
@@ -4,6 +4,7 @@ $(document).ready(function() {
initAlert()
initConfirm()
+ initCancelOrder()
if ($('#variant_autocomplete_template').length > 0) {
window.variantTemplate = Handlebars.compile($('#variant_autocomplete_template').text());
@@ -52,12 +53,12 @@ $(document).ready(function() {
}
toggleItemEdit();
- adjustItems(shipment_number, variant_id, quantity);
+ adjustItems(shipment_number, variant_id, quantity, true);
return false;
}
$('a.save-item').click(handle_save_click);
- handle_delete_click = function(elementSelector){
+ handle_delete_click = function(elementSelector, restock_item){
var del = $(elementSelector);
del.hide()
var shipment_number = del.data('shipment-number');
@@ -65,26 +66,37 @@ $(document).ready(function() {
toggleItemEdit();
- adjustItems(shipment_number, variant_id, 0);
+ adjustItems(shipment_number, variant_id, 0, restock_item);
}
$('a.delete-item').click((event) => {
- ofnConfirm(() => {
- handle_delete_click('#custom-confirm');
- });
+ try {
+ var del = $('a.delete-item');
+ var shipment_number = del.data('shipment-number');
+ var variant_id = del.data('variant-id');
+ var shipment = _.findWhere(shipments, {number: shipment_number + ''});
+ var inventory_units = _.where(shipment.inventory_units, {variant_id: variant_id});
+ if (inventory_units.length !== shipment.inventory_units.length) {
+ ofnConfirm((reStockItem) => {
+ handle_delete_click('#custom-confirm', reStockItem);
+ });
+ } else {
+ adjustItems(shipment_number, variant_id, 0);
+ }
+ } catch (e) {
+ }
});
-
}
});
-adjustItems = function(shipment_number, variant_id, quantity){
+adjustItems = function(shipment_number, variant_id, quantity, restock_item){
var shipment = _.findWhere(shipments, {number: shipment_number + ''});
var inventory_units = _.where(shipment.inventory_units, {variant_id: variant_id});
- if (quantity == 0 && inventory_units.length == shipment.inventory_units.length) {
- ofnCancelOrderAlert((confirm, sendEmailCancellation) => {
+ if (quantity === 0 && inventory_units.length === shipment.inventory_units.length) {
+ ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_item) => {
if (confirm) {
- doAdjustItems(shipment_number, variant_id, quantity, inventory_units, () => {
+ doAdjustItems(shipment_number, variant_id, quantity, inventory_units, restock_item, () => {
var redirectTo = new URL(Spree.routes.cancel_order.toString());
redirectTo.searchParams.append("send_cancellation_email", sendEmailCancellation);
window.location.href = redirectTo.toString();
@@ -93,23 +105,26 @@ adjustItems = function(shipment_number, variant_id, quantity){
});
return;
}
- doAdjustItems(shipment_number, variant_id, quantity, inventory_units, () => {
+ doAdjustItems(shipment_number, variant_id, quantity, inventory_units, restock_item, () => {
window.location.reload();
});
}
-doAdjustItems = function(shipment_number, variant_id, quantity, inventory_units, callback) {
+doAdjustItems = function(shipment_number, variant_id, quantity, inventory_units, restock_item, callback) {
var url = Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number;
var new_quantity = 0;
+ var data = { variant_id: variant_id };
if (inventory_units.length < quantity) {
url += "/add";
new_quantity = (quantity - inventory_units.length);
} else if (inventory_units.length > quantity) {
url += "/remove"
new_quantity = (inventory_units.length - quantity);
+ data.restock_item = restock_item;
}
url += '.json';
+ data.quantity = new_quantity;
if (new_quantity == 0) {
ofnAlert(t("js.admin.orders.quantity_unchanged"));
@@ -117,7 +132,7 @@ doAdjustItems = function(shipment_number, variant_id, quantity, inventory_units,
$.ajax({
type: "PUT",
url: Spree.url(url),
- data: { variant_id: variant_id, quantity: new_quantity }
+ data: data
}).done(function( msg ) {
callback();
});
@@ -171,7 +186,7 @@ addVariantFromStockLocation = function() {
});
}else{
//add to existing shipment
- adjustItems(shipment.number, variant_id, quantity);
+ adjustItems(shipment.number, variant_id, quantity, true);
}
return 1
}
@@ -202,11 +217,13 @@ ofnCancelOrderAlert = function(callback, i18nKey) {
` ${t(i18nKey)}
-
+
+
+
`);
$('#custom-confirm button.confirm').unbind( "click" ).click(() => {
$('#custom-confirm').hide();
- callback(true, $('#send_cancellation_email').is(':checked'));
+ callback(true, $('#send_cancellation_email').is(':checked'), $('#restock_items').is(':checked'));
});
$('#custom-confirm button.cancel').click(() => {
$('#custom-confirm').hide();
@@ -216,7 +233,30 @@ ofnCancelOrderAlert = function(callback, i18nKey) {
}
ofnConfirm = function(callback) {
+ $('#custom-confirm .message').html(
+ ` ${t("are_you_sure")}
+
+
+
+
`);
$('#custom-confirm').data($(event.target).data());
- $('#custom-confirm button.confirm').click(callback);
+ $('#custom-confirm button.confirm').click(() => {
+ callback($('#restock_items').is(':checked'));
+ });
$('#custom-confirm').show();
}
+
+initCancelOrder = function() {
+ $('#cancel_order_form').submit(function(e){
+ ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) => {
+ if (confirm) {
+ var redirectTo = new URL(Spree.routes.cancel_order.toString());
+ redirectTo.searchParams.append("send_cancellation_email", sendEmailCancellation);
+ redirectTo.searchParams.append("restock_items", restock_items);
+ window.location.href = redirectTo.toString();
+ }
+ });
+ e.preventDefault();
+ return false;
+ });
+}
diff --git a/app/controllers/api/v0/shipments_controller.rb b/app/controllers/api/v0/shipments_controller.rb
index cd0f31dcd3..af59069627 100644
--- a/app/controllers/api/v0/shipments_controller.rb
+++ b/app/controllers/api/v0/shipments_controller.rb
@@ -79,8 +79,9 @@ module Api
def remove
variant = scoped_variant(params[:variant_id])
quantity = params[:quantity].to_i
+ restock_item = params.fetch(:restock_item, "true") == "true"
- @order.contents.remove(variant, quantity, @shipment)
+ @order.contents.remove(variant, quantity, @shipment, restock_item)
@shipment.reload if @shipment.persisted?
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
diff --git a/app/controllers/spree/admin/orders_controller.rb b/app/controllers/spree/admin/orders_controller.rb
index baa7b169b4..ccce49e27b 100644
--- a/app/controllers/spree/admin/orders_controller.rb
+++ b/app/controllers/spree/admin/orders_controller.rb
@@ -66,6 +66,8 @@ module Spree
def fire
event = params[:e]
@order.send_cancellation_email = params[:send_cancellation_email] != "false"
+ @order.restock_items = params.fetch(:restock_items, "true") == "true"
+
if @order.public_send(event.to_s)
flash[:success] = Spree.t(:order_updated)
else
diff --git a/app/helpers/spree/admin/navigation_helper.rb b/app/helpers/spree/admin/navigation_helper.rb
index 24c33e4996..324e056330 100644
--- a/app/helpers/spree/admin/navigation_helper.rb
+++ b/app/helpers/spree/admin/navigation_helper.rb
@@ -115,7 +115,7 @@ module Spree
if html_options[:method] &&
html_options[:method].to_s.downcase != 'get' &&
!html_options[:remote]
- form_tag(url, method: html_options.delete(:method)) do
+ form_tag(url, method: html_options.delete(:method), id: html_options.delete(:form_id)) do
button(text, html_options.delete(:icon), nil, html_options)
end
else
diff --git a/app/helpers/spree/admin/orders_helper.rb b/app/helpers/spree/admin/orders_helper.rb
index 02384b3ecd..d70560abad 100644
--- a/app/helpers/spree/admin/orders_helper.rb
+++ b/app/helpers/spree/admin/orders_helper.rb
@@ -5,8 +5,8 @@ module Spree
module OrdersHelper
def event_links
links = []
- links << event_link("cancel") if @order.can_cancel?
- links << event_link("resume") if @order.can_resume?
+ links << cancel_event_link if @order.can_cancel?
+ links << resume_event_link if @order.can_resume?
links.join(' ').html_safe
end
@@ -114,12 +114,19 @@ module Spree
confirm: t(:are_you_sure) }
end
- def event_link(event)
- event_label = I18n.t(event, scope: "actions")
+ def cancel_event_link
+ event_label = I18n.t("cancel", scope: "actions")
+ button_link_to(event_label,
+ fire_admin_order_url(@order, e: "cancel"),
+ method: :put, icon: "icon-cancel", form_id: "cancel_order_form")
+ end
+
+ def resume_event_link
+ event_label = I18n.t("resume", scope: "actions")
confirm_message = I18n.t("admin.orders.edit.order_sure_want_to", event: event_label)
button_link_to(event_label,
- fire_admin_order_url(@order, e: event),
- method: :put, icon: "icon-#{event}",
+ fire_admin_order_url(@order, e: "resume"),
+ method: :put, icon: "icon-resume",
data: { confirm: confirm_message })
end
diff --git a/app/models/spree/line_item.rb b/app/models/spree/line_item.rb
index 4b698f3dbd..b1eb094558 100644
--- a/app/models/spree/line_item.rb
+++ b/app/models/spree/line_item.rb
@@ -50,6 +50,8 @@ module Spree
attr_accessor :skip_stock_check, :target_shipment # Allows manual skipping of Stock::AvailabilityValidator
+ attribute :restock_item, type: :boolean, default: true
+
# -- Scopes
scope :managed_by, lambda { |user|
if user.has_spree_role?('admin')
diff --git a/app/models/spree/order.rb b/app/models/spree/order.rb
index e401dda2f6..b4b2c3bf75 100644
--- a/app/models/spree/order.rb
+++ b/app/models/spree/order.rb
@@ -106,6 +106,7 @@ module Spree
after_save_commit DefaultAddressUpdater
attribute :send_cancellation_email, type: :boolean, default: true
+ attribute :restock_items, type: :boolean, default: true
# -- Scopes
scope :not_empty, -> {
left_outer_joins(:line_items).where.not(spree_line_items: { id: nil })
diff --git a/app/models/spree/order_contents.rb b/app/models/spree/order_contents.rb
index 62978b2488..848906e82c 100644
--- a/app/models/spree/order_contents.rb
+++ b/app/models/spree/order_contents.rb
@@ -19,8 +19,8 @@ module Spree
# Get current line item for variant
# Remove variant qty from line_item
- def remove(variant, quantity = nil, shipment = nil)
- line_item = remove_from_line_item(variant, quantity, shipment)
+ def remove(variant, quantity = nil, shipment = nil, restock_item = true)
+ line_item = remove_from_line_item(variant, quantity, shipment, restock_item)
update_shipment(shipment)
order.update_order_fees! if order.completed?
update_order
@@ -97,9 +97,9 @@ module Spree
line_item
end
- def remove_from_line_item(variant, quantity, shipment = nil)
+ def remove_from_line_item(variant, quantity, shipment = nil, restock_item = true)
line_item = find_line_item_by_variant(variant, true)
-
+ line_item.restock_item = restock_item
quantity.present? ? line_item.quantity += -quantity : line_item.quantity = 0
line_item.target_shipment = shipment
diff --git a/app/models/spree/order_inventory.rb b/app/models/spree/order_inventory.rb
index 44ff226609..f1b468f128 100644
--- a/app/models/spree/order_inventory.rb
+++ b/app/models/spree/order_inventory.rb
@@ -44,12 +44,15 @@ module Spree
quantity = variant_units.size - line_item.quantity
if shipment.present?
- remove_from_shipment(shipment, line_item.variant, quantity)
+ remove_from_shipment(shipment, line_item.variant, quantity, line_item.restock_item)
else
order.shipments.each do |each_shipment|
break if quantity == 0
- quantity -= remove_from_shipment(each_shipment, line_item.variant, quantity)
+ quantity -= remove_from_shipment(each_shipment,
+ line_item.variant,
+ quantity,
+ line_item.restock_item)
end
end
end
@@ -83,7 +86,7 @@ module Spree
quantity
end
- def remove_from_shipment(shipment, variant, quantity)
+ def remove_from_shipment(shipment, variant, quantity, restock_item)
return 0 if quantity == 0 || shipment.shipped?
shipment_units = shipment.inventory_units_for(variant).reject do |variant_unit|
@@ -101,7 +104,7 @@ module Spree
shipment.destroy if shipment.inventory_units.reload.count == 0
# removing this from shipment, and adding to stock_location
- if order.completed?
+ if order.completed? && restock_item
shipment.stock_location.restock variant, removed_quantity, shipment
end
diff --git a/app/models/spree/shipment.rb b/app/models/spree/shipment.rb
index d03f63b20d..8ab08a51ac 100644
--- a/app/models/spree/shipment.rb
+++ b/app/models/spree/shipment.rb
@@ -192,7 +192,7 @@ module Spree
end
def after_cancel
- manifest.each { |item| manifest_restock(item) }
+ manifest.each { |item| manifest_restock(item) } if order.restock_items
end
def after_resume
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 854406c54d..1c555d3b09 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -3083,6 +3083,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using
quantity_unchanged: "Quantity unchanged from previous amount."
cancel_the_order_html: "This will cancel the current order.
Are you sure you want to proceed?"
cancel_the_order_send_cancelation_email: "Send a cancellation email to the customer"
+ restock_item: "Restock Items: return this item to stock"
+ restock_items: "Restock Items: return all items to stock"
resend_user_email_confirmation:
resend: "Resend"
sending: "Resend..."
diff --git a/spec/controllers/api/v0/shipments_controller_spec.rb b/spec/controllers/api/v0/shipments_controller_spec.rb
index 77b6775cd4..233a13560e 100644
--- a/spec/controllers/api/v0/shipments_controller_spec.rb
+++ b/spec/controllers/api/v0/shipments_controller_spec.rb
@@ -153,6 +153,13 @@ describe Api::V0::ShipmentsController, type: :controller do
api_put :remove, params.merge(variant_id: existing_variant.to_param)
}.to change { existing_variant.reload.on_hand }.by(2)
end
+
+ it 'does not adjust stock when removing a variant' do
+ expect {
+ api_put :remove, params.merge(variant_id: existing_variant.to_param,
+ restock_item: 'false')
+ }.to change { existing_variant.reload.on_hand }.by(0)
+ end
end
context "for canceled orders" do
diff --git a/spec/models/spree/order_inventory_spec.rb b/spec/models/spree/order_inventory_spec.rb
index 09de81a866..bc8a035d53 100644
--- a/spec/models/spree/order_inventory_spec.rb
+++ b/spec/models/spree/order_inventory_spec.rb
@@ -89,12 +89,21 @@ describe Spree::OrderInventory do
it "doesn't restock items" do
expect(shipment.stock_location).not_to receive(:restock)
- expect(subject.send(:remove_from_shipment, shipment, variant, 1)).to eq 1
+ expect(subject.send(:remove_from_shipment, shipment, variant, 1, true)).to eq 1
+ end
+ end
+
+ context "order is completed" do
+ before { allow(order).to receive_messages completed?: true }
+
+ it "doesn't restock items" do
+ expect(shipment.stock_location).not_to receive(:restock)
+ expect(subject.send(:remove_from_shipment, shipment, variant, 1, false)).to eq 1
end
end
it 'should create stock_movement' do
- expect(subject.send(:remove_from_shipment, shipment, variant, 1)).to eq 1
+ expect(subject.send(:remove_from_shipment, shipment, variant, 1, true)).to eq 1
stock_item = shipment.stock_location.stock_item(variant)
movement = stock_item.stock_movements.last
@@ -112,7 +121,7 @@ describe Spree::OrderInventory do
expect(shipment.inventory_units_for[1]).not_to receive(:destroy)
expect(shipment.inventory_units_for[2]).to receive(:destroy)
- expect(subject.send(:remove_from_shipment, shipment, variant, 2)).to eq 2
+ expect(subject.send(:remove_from_shipment, shipment, variant, 2, true)).to eq 2
end
it 'should destroy unshipped units first' do
@@ -123,7 +132,7 @@ describe Spree::OrderInventory do
expect(shipment.inventory_units_for[0]).not_to receive(:destroy)
expect(shipment.inventory_units_for[1]).to receive(:destroy)
- expect(subject.send(:remove_from_shipment, shipment, variant, 1)).to eq 1
+ expect(subject.send(:remove_from_shipment, shipment, variant, 1, true)).to eq 1
end
it 'only attempts to destroy as many units as are eligible, and return amount destroyed' do
@@ -134,14 +143,14 @@ describe Spree::OrderInventory do
expect(shipment.inventory_units_for[0]).not_to receive(:destroy)
expect(shipment.inventory_units_for[1]).to receive(:destroy)
- expect(subject.send(:remove_from_shipment, shipment, variant, 1)).to eq 1
+ expect(subject.send(:remove_from_shipment, shipment, variant, 1, true)).to eq 1
end
it 'should destroy self if not inventory units remain' do
allow(shipment.inventory_units).to receive_messages(count: 0)
expect(shipment).to receive(:destroy)
- expect(subject.send(:remove_from_shipment, shipment, variant, 1)).to eq 1
+ expect(subject.send(:remove_from_shipment, shipment, variant, 1, true)).to eq 1
end
end
end
diff --git a/spec/system/admin/order_spec.rb b/spec/system/admin/order_spec.rb
index 35b4c26f1b..c4cbfc125e 100644
--- a/spec/system/admin/order_spec.rb
+++ b/spec/system/admin/order_spec.rb
@@ -163,13 +163,9 @@ describe '
order.line_items = [order.line_items.first]
login_as_admin_and_visit spree.edit_admin_order_path(order)
find("a.delete-item").click
- within(".modal", visible: true) do
- # ignore first modal by confirming it
- click_on("OK")
- end
end
- context "it shows a second modal about last item deletion and therefore about order cancellation" do
+ context "it shows a modal about last item deletion and therefore about order cancellation" do
it "that the user can close and then nothing change" do
expect(page).to have_content "This will cancel the current order."
expect(page).to have_checked_field "Send a cancellation email to the customer"
@@ -205,23 +201,18 @@ describe '
end.to have_enqueued_mail(Spree::OrderMailer, :cancel_email)
end
end
- end
- end
- end
- context "user can cancel an order" do
- before do
- login_as_admin_and_visit spree.edit_admin_order_path(order)
- end
-
- it "by clicking on the cancel button" do
- expect do
- accept_alert do
- click_button "Cancel"
+ context "that the user can choose to restock item" do
+ let(:shipment) { order.shipments.first }
+ it "uncheck the checkbox to not restock item" do
+ within(".modal", visible: true) do
+ check("restock_items")
+ click_on("OK")
+ end
+ expect(shipment.stock_location).not_to receive(:restock)
+ end
end
- expect(page).to have_content "Order updated"
- expect(order.reload.state).to eq("canceled")
- end.to have_enqueued_mail(Spree::OrderMailer, :cancel_email)
+ end
end
end