-
-
-
+
+
+
+
@@ -102,6 +102,14 @@ function createNewModal(options={}) {
modalSetSubmitText(modal_name, options.submitText || '{% trans "Submit" %}');
modalSetCloseText(modal_name, options.cancelText || '{% trans "Cancel" %}');
+ if (options.hideSubmitButton) {
+ $(modal_name).find('#modal-form-submit').hide();
+ }
+
+ if (options.hideCloseButton) {
+ $(modal_name).find('#modal-form-cancel').hide();
+ }
+
// Return the "name" of the modal
return modal_name;
}
@@ -551,25 +559,18 @@ function showAlertDialog(title, content, options={}) {
*
* title - Title text
* content - HTML content of the dialog window
- * options:
- * modal - modal form to use (default = '#modal-alert-dialog')
*/
- var modal = options.modal || '#modal-alert-dialog';
- $(modal).on('shown.bs.modal', function() {
- $(modal + ' .modal-form-content').scrollTop(0);
+ var modal = createNewModal({
+ title: title,
+ cancelText: '{% trans "Close" %}',
+ hideSubmitButton: true,
});
- modalSetTitle(modal, title);
- modalSetContent(modal, content);
+ modalSetContent(modal, content);
- $(modal).modal({
- backdrop: 'static',
- keyboard: false,
- });
-
- $(modal).modal('show');
+ $(modal).modal('show');
}
@@ -586,22 +587,15 @@ function showQuestionDialog(title, content, options={}) {
* cancel - Functino to run if the user presses 'Cancel'
*/
- var modal = options.modal || '#modal-question-dialog';
-
- $(modal).on('shown.bs.modal', function() {
- $(modal + ' .modal-form-content').scrollTop(0);
+ var modal = createNewModal({
+ title: title,
+ submitText: options.accept_text || '{% trans "Accept" %}',
+ cancelText: options.cancel_text || '{% trans "Cancel" %}',
});
- modalSetTitle(modal, title);
modalSetContent(modal, content);
- var accept_text = options.accept_text || '{% trans "Accept" %}';
- var cancel_text = options.cancel_text || '{% trans "Cancel" %}';
-
- $(modal).find('#modal-form-cancel').html(cancel_text);
- $(modal).find('#modal-form-accept').html(accept_text);
-
- $(modal).on('click', '#modal-form-accept', function() {
+ $(modal).on('click', "#modal-form-submit", function() {
$(modal).modal('hide');
if (options.accept) {
@@ -609,14 +603,6 @@ function showQuestionDialog(title, content, options={}) {
}
});
- $(modal).on('click', 'modal-form-cancel', function() {
- $(modal).modal('hide');
-
- if (options.cancel) {
- options.cancel();
- }
- });
-
$(modal).modal('show');
}
diff --git a/InvenTree/templates/js/stock.js b/InvenTree/templates/js/stock.js
index 524b116743..f173f868f7 100644
--- a/InvenTree/templates/js/stock.js
+++ b/InvenTree/templates/js/stock.js
@@ -20,6 +20,315 @@ function stockStatusCodes() {
}
+/**
+ * Perform stock adjustments
+ */
+function adjustStock(action, items, options={}) {
+
+ var formTitle = 'Form Title Here';
+ var actionTitle = null;
+
+ // API url
+ var url = null;
+
+ var specifyLocation = false;
+ var allowSerializedStock = false;
+
+ switch (action) {
+ case 'move':
+ formTitle = '{% trans "Transfer Stock" %}';
+ actionTitle = '{% trans "Move" %}';
+ specifyLocation = true;
+ allowSerializedStock = true;
+ url = '{% url "api-stock-transfer" %}';
+ break;
+ case 'count':
+ formTitle = '{% trans "Count Stock" %}';
+ actionTitle = '{% trans "Count" %}';
+ url = '{% url "api-stock-count" %}';
+ break;
+ case 'take':
+ formTitle = '{% trans "Remove Stock" %}';
+ actionTitle = '{% trans "Take" %}';
+ url = '{% url "api-stock-remove" %}';
+ break;
+ case 'add':
+ formTitle = '{% trans "Add Stock" %}';
+ actionTitle = '{% trans "Add" %}';
+ url = '{% url "api-stock-add" %}';
+ break;
+ case 'delete':
+ formTitle = '{% trans "Delete Stock" %}';
+ allowSerializedStock = true;
+ break;
+ default:
+ break;
+ }
+
+ // Generate modal HTML content
+ var html = `
+
+
+
+ {% trans "Part" %} |
+ {% trans "Stock" %} |
+ {% trans "Location" %} |
+ ${actionTitle || ''} |
+ |
+
+
+
+ `;
+
+ var itemCount = 0;
+
+ for (var idx = 0; idx < items.length; idx++) {
+
+ var item = items[idx];
+
+ if ((item.serial != null) && !allowSerializedStock) {
+ continue;
+ }
+
+ var pk = item.pk;
+
+ var readonly = (item.serial != null);
+ var minValue = null;
+ var maxValue = null;
+ var value = null;
+
+ switch (action) {
+ case 'move':
+ minValue = 0;
+ maxValue = item.quantity;
+ value = item.quantity;
+ break;
+ case 'add':
+ minValue = 0;
+ value = 0;
+ break;
+ case 'take':
+ minValue = 0;
+ value = 0;
+ break;
+ case 'count':
+ minValue = 0;
+ value = item.quantity;
+ break;
+ default:
+ break;
+ }
+
+ var image = item.part_detail.thumbnail || item.part_detail.image || blankImage();
+
+ var status = stockStatusDisplay(item.status, {
+ classes: 'float-right'
+ });
+
+ var quantity = item.quantity;
+
+ var location = locationDetail(item, false);
+
+ if (item.location_detail) {
+ location = item.location_detail.pathstring;
+ }
+
+ if (item.serial != null) {
+ quantity = `#${item.serial}`;
+ }
+
+ var actionInput = '';
+
+ if (actionTitle != null) {
+ actionInput = constructNumberInput(
+ item.pk,
+ {
+ value: value,
+ min_value: minValue,
+ max_value: maxValue,
+ read_only: readonly,
+ title: readonly ? '{% trans "Quantity cannot be adjusted for serialized stock" %}' : '{% trans "Specify stock quantity" %}',
+ }
+ )
+ };
+
+ var buttons = ``;
+
+ buttons += makeIconButton(
+ 'fa-times icon-red',
+ 'button-stock-item-remove',
+ pk,
+ '{% trans "Remove stock item" %}',
+ );
+
+ buttons += `
`;
+
+ html += `
+
+ ${item.part_detail.full_name} |
+ ${quantity}${status} |
+ ${location} |
+
+
+ |
+ ${buttons} |
+
`;
+
+ itemCount += 1;
+ }
+
+ if (itemCount == 0) {
+ showAlertDialog(
+ '{% trans "Select Stock Items" %}',
+ '{% trans "You must select at least one available stock item" %}',
+ );
+
+ return;
+ }
+
+ html += `
`;
+
+ var modal = createNewModal({
+ title: formTitle,
+ });
+
+ // Extra fields
+ var extraFields = {
+ location: {
+ label: '{% trans "Location" %}',
+ help_text: '{% trans "Select destination stock location" %}',
+ type: 'related field',
+ required: true,
+ api_url: `/api/stock/location/`,
+ model: 'stocklocation',
+ },
+ notes: {
+ label: '{% trans "Notes" %}',
+ help_text: '{% trans "Stock transaction notes" %}',
+ type: 'string',
+ }
+ };
+
+ if (!specifyLocation) {
+ delete extraFields.location;
+ }
+
+ constructFormBody({}, {
+ preFormContent: html,
+ fields: extraFields,
+ confirm: true,
+ confirmMessage: '{% trans "Confirm stock adjustment" %}',
+ modal: modal,
+ onSubmit: function(fields, opts) {
+
+ // "Delete" action gets handled differently
+ if (action == 'delete') {
+
+ var requests = [];
+
+ items.forEach(function(item) {
+ requests.push(
+ inventreeDelete(
+ `/api/stock/${item.pk}/`,
+ )
+ )
+ });
+
+ // Wait for *all* the requests to complete
+ $.when.apply($, requests).then(function() {
+ // Destroy the modal window
+ $(modal).modal('hide');
+
+ if (options.onSuccess) {
+ options.onSuccess();
+ }
+ });
+
+ return;
+ }
+
+ // Data to transmit
+ var data = {
+ items: [],
+ };
+
+ // Add values for each selected stock item
+ items.forEach(function(item) {
+
+ var q = getFormFieldValue(item.pk, {}, {modal: modal});
+
+ if (q != null) {
+ data.items.push({pk: item.pk, quantity: q});
+ }
+ });
+
+ // Add in extra field data
+ for (field_name in extraFields) {
+ data[field_name] = getFormFieldValue(
+ field_name,
+ fields[field_name],
+ {
+ modal: modal,
+ }
+ );
+ }
+
+ inventreePut(
+ url,
+ data,
+ {
+ method: 'POST',
+ success: function(response, status) {
+
+ // Destroy the modal window
+ $(modal).modal('hide');
+
+ if (options.onSuccess) {
+ options.onSuccess();
+ }
+ },
+ error: function(xhr) {
+ switch (xhr.status) {
+ case 400:
+
+ // Handle errors for standard fields
+ handleFormErrors(
+ xhr.responseJSON,
+ extraFields,
+ {
+ modal: modal,
+ }
+ )
+
+ break;
+ default:
+ $(modal).modal('hide');
+ showApiError(xhr);
+ break;
+ }
+ }
+ }
+ );
+ }
+ });
+
+ // Attach callbacks for the action buttons
+ $(modal).find('.button-stock-item-remove').click(function() {
+ var pk = $(this).attr('pk');
+
+ $(modal).find(`#stock_item_${pk}`).remove();
+ });
+
+ attachToggle(modal);
+
+ $(modal + ' .select2-container').addClass('select-full-width');
+ $(modal + ' .select2-container').css('width', '100%');
+}
+
+
function removeStockRow(e) {
// Remove a selected row from a stock modal form
@@ -228,6 +537,58 @@ function loadStockTestResultsTable(table, options) {
}
+
+function locationDetail(row, showLink=true) {
+ /*
+ * Function to display a "location" of a StockItem.
+ *
+ * Complicating factors: A StockItem may not actually *be* in a location!
+ * - Could be at a customer
+ * - Could be installed in another stock item
+ * - Could be assigned to a sales order
+ * - Could be currently in production!
+ *
+ * So, instead of being naive, we'll check!
+ */
+
+ // Display text
+ var text = '';
+
+ // URL (optional)
+ var url = '';
+
+ if (row.is_building && row.build) {
+ // StockItem is currently being built!
+ text = '{% trans "In production" %}';
+ url = `/build/${row.build}/`;
+ } else if (row.belongs_to) {
+ // StockItem is installed inside a different StockItem
+ text = `{% trans "Installed in Stock Item" %} ${row.belongs_to}`;
+ url = `/stock/item/${row.belongs_to}/installed/`;
+ } else if (row.customer) {
+ // StockItem has been assigned to a customer
+ text = '{% trans "Shipped to customer" %}';
+ url = `/company/${row.customer}/assigned-stock/`;
+ } else if (row.sales_order) {
+ // StockItem has been assigned to a sales order
+ text = '{% trans "Assigned to Sales Order" %}';
+ url = `/order/sales-order/${row.sales_order}/`;
+ } else if (row.location) {
+ text = row.location_detail.pathstring;
+ url = `/stock/location/${row.location}/`;
+ } else {
+ text = '
{% trans "No stock location set" %}';
+ url = '';
+ }
+
+ if (showLink && url) {
+ return renderLink(text, url);
+ } else {
+ return text;
+ }
+}
+
+
function loadStockTable(table, options) {
/* Load data into a stock table with adjustable options.
* Fetches data (via AJAX) and loads into a bootstrap table.
@@ -271,56 +632,6 @@ function loadStockTable(table, options) {
filters[key] = params[key];
}
- function locationDetail(row) {
- /*
- * Function to display a "location" of a StockItem.
- *
- * Complicating factors: A StockItem may not actually *be* in a location!
- * - Could be at a customer
- * - Could be installed in another stock item
- * - Could be assigned to a sales order
- * - Could be currently in production!
- *
- * So, instead of being naive, we'll check!
- */
-
- // Display text
- var text = '';
-
- // URL (optional)
- var url = '';
-
- if (row.is_building && row.build) {
- // StockItem is currently being built!
- text = '{% trans "In production" %}';
- url = `/build/${row.build}/`;
- } else if (row.belongs_to) {
- // StockItem is installed inside a different StockItem
- text = `{% trans "Installed in Stock Item" %} ${row.belongs_to}`;
- url = `/stock/item/${row.belongs_to}/installed/`;
- } else if (row.customer) {
- // StockItem has been assigned to a customer
- text = '{% trans "Shipped to customer" %}';
- url = `/company/${row.customer}/assigned-stock/`;
- } else if (row.sales_order) {
- // StockItem has been assigned to a sales order
- text = '{% trans "Assigned to Sales Order" %}';
- url = `/order/sales-order/${row.sales_order}/`;
- } else if (row.location) {
- text = row.location_detail.pathstring;
- url = `/stock/location/${row.location}/`;
- } else {
- text = '
{% trans "No stock location set" %}';
- url = '';
- }
-
- if (url) {
- return renderLink(text, url);
- } else {
- return text;
- }
- }
-
var grouping = true;
if ('grouping' in options) {
@@ -741,39 +1052,15 @@ function loadStockTable(table, options) {
]
);
+
function stockAdjustment(action) {
var items = $("#stock-table").bootstrapTable("getSelections");
- var stock = [];
-
- items.forEach(function(item) {
- stock.push(item.pk);
- });
-
- // Buttons for launching secondary modals
- var secondary = [];
-
- if (action == 'move') {
- secondary.push({
- field: 'destination',
- label: '{% trans "New Location" %}',
- title: '{% trans "Create new location" %}',
- url: "/stock/location/new/",
- });
- }
-
- launchModalForm("/stock/adjust/",
- {
- data: {
- action: action,
- stock: stock,
- },
- success: function() {
- $("#stock-table").bootstrapTable('refresh');
- },
- secondary: secondary,
+ adjustStock(action, items, {
+ onSuccess: function() {
+ $('#stock-table').bootstrapTable('refresh');
}
- );
+ });
}
// Automatically link button callbacks
diff --git a/InvenTree/templates/modals.html b/InvenTree/templates/modals.html
index e2bd44554c..11ddc40938 100644
--- a/InvenTree/templates/modals.html
+++ b/InvenTree/templates/modals.html
@@ -56,46 +56,4 @@
-
-
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file