Merge pull request #9044 from georgethoppil/optional-restock-items

Optional restock items
This commit is contained in:
Maikel
2022-05-09 09:08:26 +10:00
committed by GitHub
14 changed files with 126 additions and 61 deletions

View File

@@ -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)}
<div class="form">
<input type="checkbox" name="send_cancellation_email" value="1" id="send_cancellation_email" checked="true" />
<label for="send_cancellation_email">${t("js.admin.orders.cancel_the_order_send_cancelation_email")}</label>
<label for="send_cancellation_email">${t("js.admin.orders.cancel_the_order_send_cancelation_email")}</label><br />
<input type="checkbox" name="restock_items" id="restock_items" checked="checked"/>
<label for="restock_items">${t("js.admin.orders.restock_items")}</label>
</div>`);
$('#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")}
<div class="form">
<input type="checkbox" name="restock_items" id="restock_items" checked="checked"/>
<label for="restock_items">${t("js.admin.orders.restock_item")}</label>
</div>`);
$('#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;
});
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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('&nbsp;').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

View File

@@ -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')

View File

@@ -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 })

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.<br />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..."

View File

@@ -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

View File

@@ -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

View File

@@ -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