diff --git a/InvenTree/order/templates/order/sales_order_base.html b/InvenTree/order/templates/order/sales_order_base.html index 5593918a38..af923ab097 100644 --- a/InvenTree/order/templates/order/sales_order_base.html +++ b/InvenTree/order/templates/order/sales_order_base.html @@ -57,6 +57,7 @@ src="{% static 'img/blank_image.png' %}" @@ -223,6 +224,16 @@ $("#edit-order").click(function() { }); }); +$("#complete-order-shipments").click(function() { + + completePendingShipments( + {{ order.pk }}, + { + reload: true, + } + ); +}); + $("#cancel-order").click(function() { cancelSalesOrder( diff --git a/InvenTree/templates/js/translated/forms.js b/InvenTree/templates/js/translated/forms.js index d45a2de78c..9e1a7d154d 100644 --- a/InvenTree/templates/js/translated/forms.js +++ b/InvenTree/templates/js/translated/forms.js @@ -561,6 +561,11 @@ function constructFormBody(fields, options) { insertPersistButton(options); } + // Insert secondary buttons (if required) + if (options.buttons) { + insertSecondaryButtons(options); + } + // Display the modal $(modal).modal('show'); @@ -650,6 +655,31 @@ function insertPersistButton(options) { $(options.modal).find('#modal-footer-buttons').append(html); } +/* + * Add secondary buttons to the left of the close and submit buttons + * with callback functions + */ +function insertSecondaryButtons(options) { + for (var idx = 0; idx < options.buttons.length; idx++) { + + var html = ` + + `; + + $(options.modal).find('#modal-footer-secondary-buttons').append(html); + + if (options.buttons[idx].onClick instanceof Function) { + // Copy callback reference to prevent errors if `idx` changes value before execution + var onclick_callback = options.buttons[idx].onClick; + + $(options.modal).find(`#modal-form-${options.buttons[idx].name}`).click(function() { + onclick_callback(options); + }); + } + } +} /* * Extract all specified form values as a single object diff --git a/InvenTree/templates/js/translated/modals.js b/InvenTree/templates/js/translated/modals.js index 464006ae12..cc1212494d 100644 --- a/InvenTree/templates/js/translated/modals.js +++ b/InvenTree/templates/js/translated/modals.js @@ -75,6 +75,9 @@ function createNewModal(options={}) {

+ @@ -99,7 +102,7 @@ function createNewModal(options={}) { $(modal_name).focus(); if (options.hideCloseButton) { - $(modal_name).find('#modal-form-cancel').hide(); + $(modal_name).find('#modal-form-close').hide(); } if (options.preventSubmit || options.hideSubmitButton) { diff --git a/InvenTree/templates/js/translated/order.js b/InvenTree/templates/js/translated/order.js index 53dead4b60..0c21e368f1 100644 --- a/InvenTree/templates/js/translated/order.js +++ b/InvenTree/templates/js/translated/order.js @@ -24,6 +24,7 @@ cancelSalesOrder, completePurchaseOrder, completeShipment, + completePendingShipments, createSalesOrder, createSalesOrderShipment, editPurchaseOrderLineItem, @@ -69,7 +70,7 @@ function salesOrderShipmentFields(options={}) { /* * Complete a shipment */ -function completeShipment(shipment_id) { +function completeShipment(shipment_id, options={}) { // Request the list of stock items which will be shipped inventreeGet(`/api/order/so/shipment/${shipment_id}/`, {}, { @@ -126,28 +127,128 @@ function completeShipment(shipment_id) { constructForm(`/api/order/so/shipment/${shipment_id}/ship/`, { method: 'POST', - title: '{% trans "Complete Shipment" %}', + title: `{% trans "Complete Shipment" %} ${shipment.reference}`, fields: { tracking_number: {}, }, preFormContent: html, confirm: true, confirmMessage: '{% trans "Confirm Shipment" %}', + buttons: options.buttons, onSuccess: function(data) { // Reload tables $('#so-lines-table').bootstrapTable('refresh'); $('#pending-shipments-table').bootstrapTable('refresh'); $('#completed-shipments-table').bootstrapTable('refresh'); + + if (options.onSuccess instanceof Function) { + options.onSuccess(data); + } }, - reload: true + reload: options.reload }); } }); } +/* + * Launches a modal to mark all allocated pending shipments as complete + */ +function completePendingShipments(order_id, options={}) { + var pending_shipments = null; + + // Request the list of stock items which will be shipped + inventreeGet(`/api/order/so/shipment/.*`, + { + order: order_id, + shipped: false + }, + { + async: false, + success: function(shipments) { + pending_shipments = shipments; + } + } + ); + + var allocated_shipments = []; + + for (var idx = 0; idx < pending_shipments.length; idx++) { + if (pending_shipments[idx].allocations.length > 0) { + allocated_shipments.push(pending_shipments[idx]); + } + } + + if (allocated_shipments.length > 0) { + completePendingShipmentsHelper(allocated_shipments, 0, options); + + } else { + html = ` +
+ `; + + if (!pending_shipments.length) { + html += ` + {% trans "No pending shipments found" %} + `; + } else { + html += ` + {% trans "No stock items have been allocated to pending shipments" %} + `; + } + + html += ` +
+ `; + + constructForm(`/api/order/so/shipment/0/ship/`, { + method: 'POST', + title: '{% trans "Complete Shipments" %}', + preFormContent: html, + onSubmit: function(fields, options) { + handleFormSuccess(fields, options); + }, + closeText: 'Close', + hideSubmitButton: true, + }); + } +} + + +/* + * Recursive helper for opening shipment completion modals + */ +function completePendingShipmentsHelper(shipments, shipment_idx, options={}) { + if (shipment_idx < shipments.length) { + completeShipment(shipments[shipment_idx].pk, + { + buttons: [ + { + name: 'skip', + title: `{% trans "Skip" %}`, + onClick: function(form_options) { + if (form_options.modal) { + $(form_options.modal).modal('hide'); + } + + completePendingShipmentsHelper(shipments, shipment_idx + 1, options); + } + } + ], + onSuccess: function(data) { + completePendingShipmentsHelper(shipments, shipment_idx + 1, options); + }, + } + ); + + } else if (options.reload) { + location.reload(); + } +} + /* * Launches a modal form to mark a PurchaseOrder as "complete" -*/ + */ function completePurchaseOrder(order_id, options={}) { constructForm(