From 95e7cc7a5d2d32164ff4d18a467ee003c112e618 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 6 Oct 2021 08:56:24 +1100 Subject: [PATCH] Fixes for unit tests --- InvenTree/part/templates/part/part_base.html | 2 +- .../stock/templates/stock/item_base.html | 2 +- InvenTree/stock/templates/stock/location.html | 2 +- InvenTree/stock/test_api.py | 51 +++-- InvenTree/templates/js/translated/stock.js | 189 +++++++++--------- 5 files changed, 120 insertions(+), 126 deletions(-) diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index 847baf8ab5..a81f918013 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -372,7 +372,7 @@ { success: function(items) { adjustStock(action, items, { - onSuccess: function() { + success: function() { location.reload(); } }); diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html index 759732fe6e..3addeacde2 100644 --- a/InvenTree/stock/templates/stock/item_base.html +++ b/InvenTree/stock/templates/stock/item_base.html @@ -561,7 +561,7 @@ function itemAdjust(action) { { success: function(item) { adjustStock(action, [item], { - onSuccess: function() { + success: function() { location.reload(); } }); diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html index 9a5aeb6a7e..3afaf45635 100644 --- a/InvenTree/stock/templates/stock/location.html +++ b/InvenTree/stock/templates/stock/location.html @@ -287,7 +287,7 @@ { success: function(items) { adjustStock(action, items, { - onSuccess: function() { + success: function() { location.reload(); } }); diff --git a/InvenTree/stock/test_api.py b/InvenTree/stock/test_api.py index 21c355fae2..d07c35aaf7 100644 --- a/InvenTree/stock/test_api.py +++ b/InvenTree/stock/test_api.py @@ -513,31 +513,34 @@ class StocktakeTest(StockAPITestCase): # POST with a valid action response = self.post(url, data) - self.assertContains(response, "must contain list", status_code=status.HTTP_400_BAD_REQUEST) + + self.assertIn("This field is required", str(response.data["items"])) data['items'] = [{ 'no': 'aa' }] # POST without a PK - response = self.post(url, data) - self.assertContains(response, 'must contain a valid integer primary-key', status_code=status.HTTP_400_BAD_REQUEST) + response = self.post(url, data, expected_code=400) + + self.assertIn('This field is required', str(response.data)) # POST with an invalid PK data['items'] = [{ 'pk': 10 }] - response = self.post(url, data) - self.assertContains(response, 'does not match valid stock item', status_code=status.HTTP_400_BAD_REQUEST) + response = self.post(url, data, expected_code=400) + + self.assertContains(response, 'object does not exist', status_code=status.HTTP_400_BAD_REQUEST) # POST with missing quantity value data['items'] = [{ 'pk': 1234 }] - response = self.post(url, data) - self.assertContains(response, 'Invalid quantity value', status_code=status.HTTP_400_BAD_REQUEST) + response = self.post(url, data, expected_code=400) + self.assertContains(response, 'This field is required', status_code=status.HTTP_400_BAD_REQUEST) # POST with an invalid quantity value data['items'] = [{ @@ -546,7 +549,7 @@ class StocktakeTest(StockAPITestCase): }] response = self.post(url, data) - self.assertContains(response, 'Invalid quantity value', status_code=status.HTTP_400_BAD_REQUEST) + self.assertContains(response, 'A valid number is required', status_code=status.HTTP_400_BAD_REQUEST) data['items'] = [{ 'pk': 1234, @@ -554,18 +557,7 @@ class StocktakeTest(StockAPITestCase): }] response = self.post(url, data) - self.assertContains(response, 'must not be less than zero', status_code=status.HTTP_400_BAD_REQUEST) - - # Test with a single item - data = { - 'item': { - 'pk': 1234, - 'quantity': '10', - } - } - - response = self.post(url, data) - self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertContains(response, 'Ensure this value is greater than or equal to 0', status_code=status.HTTP_400_BAD_REQUEST) def test_transfer(self): """ @@ -573,24 +565,27 @@ class StocktakeTest(StockAPITestCase): """ data = { - 'item': { - 'pk': 1234, - 'quantity': 10, - }, + 'items': [ + { + 'pk': 1234, + 'quantity': 10, + } + ], 'location': 1, 'notes': "Moving to a new location" } url = reverse('api-stock-transfer') - response = self.post(url, data) - self.assertContains(response, "Moved 1 parts to", status_code=status.HTTP_200_OK) + # This should succeed + response = self.post(url, data, expected_code=201) # Now try one which will fail due to a bad location data['location'] = 'not a location' - response = self.post(url, data) - self.assertContains(response, 'Valid location must be specified', status_code=status.HTTP_400_BAD_REQUEST) + response = self.post(url, data, expected_code=400) + + self.assertContains(response, 'Incorrect type. Expected pk value', status_code=status.HTTP_400_BAD_REQUEST) class StockItemDeletionTest(StockAPITestCase): diff --git a/InvenTree/templates/js/translated/stock.js b/InvenTree/templates/js/translated/stock.js index 17c2598d1b..c6efd88f5f 100644 --- a/InvenTree/templates/js/translated/stock.js +++ b/InvenTree/templates/js/translated/stock.js @@ -247,7 +247,7 @@ function adjustStock(action, items, options={}) { break; } - var image = item.part_detail.thumbnail || item.part_detail.image || blankImage(); + var thumb = thumbnailImage(item.part_detail.thumbnail || item.part_detail.image); var status = stockStatusDisplay(item.status, { classes: 'float-right' @@ -268,14 +268,18 @@ function adjustStock(action, items, options={}) { var actionInput = ''; if (actionTitle != null) { - actionInput = constructNumberInput( - item.pk, + actionInput = constructField( + `items_quantity_${pk}`, { - value: value, + type: 'decimal', min_value: minValue, max_value: maxValue, - read_only: readonly, + value: value, title: readonly ? '{% trans "Quantity cannot be adjusted for serialized stock" %}' : '{% trans "Specify stock quantity" %}', + required: true, + }, + { + hideLabels: true, } ); } @@ -293,7 +297,7 @@ function adjustStock(action, items, options={}) { html += ` - ${item.part_detail.full_name} + ${thumb} ${item.part_detail.full_name} ${quantity}${status} ${location} @@ -319,50 +323,89 @@ function adjustStock(action, items, options={}) { html += ``; - var modal = createNewModal({ - title: formTitle, - }); + var extraFields = {}; - // 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', - name: 'location', - }, - notes: { - label: '{% trans "Notes" %}', - help_text: '{% trans "Stock transaction notes" %}', - type: 'string', - name: 'notes', - } - }; - - if (!specifyLocation) { - delete extraFields.location; + if (specifyLocation) { + extraFields.location = {}; } - constructFormBody({}, { - preFormContent: html, + if (action != 'delete') { + extraFields.notes = {}; + } + + constructForm(url, { + method: 'POST', fields: extraFields, + preFormContent: html, confirm: true, confirmMessage: '{% trans "Confirm stock adjustment" %}', - modal: modal, - onSubmit: function(fields) { + title: formTitle, + afterRender: function(fields, opts) { + // Add button callbacks to remove rows + $(opts.modal).find('.button-stock-item-remove').click(function() { + var pk = $(this).attr('pk'); - // "Delete" action gets handled differently + $(opts.modal).find(`#stock_item_${pk}`).remove(); + }); + + // Initialize "location" field + if (specifyLocation) { + initializeRelatedField( + { + name: 'location', + type: 'related field', + model: 'stocklocation', + required: true, + }, + null, + opts + ); + } + }, + onSubmit: function(fields, opts) { + + // Extract data elements from the form + var data = { + items: [], + }; + + if (action != 'delete') { + data.notes = getFormFieldValue('notes', {}, opts); + } + + if (specifyLocation) { + data.location = getFormFieldValue('location', {}, opts); + } + + var item_pk_values = []; + + items.forEach(function(item) { + var pk = item.pk; + + // Does the row exist in the form? + var row = $(opts.modal).find(`#stock_item_${pk}`); + + if (row) { + + item_pk_values.push(pk); + + var quantity = getFormFieldValue(`items_quantity_${pk}`, {}, opts); + + data.items.push({ + pk: pk, + quantity: quantity, + }); + } + }); + + // Delete action is handled differently if (action == 'delete') { - var requests = []; - items.forEach(function(item) { + item_pk_values.forEach(function(pk) { requests.push( inventreeDelete( - `/api/stock/${item.pk}/`, + `/api/stock/${pk}/`, ) ); }); @@ -370,72 +413,40 @@ function adjustStock(action, items, options={}) { // Wait for *all* the requests to complete $.when.apply($, requests).done(function() { // Destroy the modal window - $(modal).modal('hide'); + $(opts.modal).modal('hide'); - if (options.onSuccess) { - options.onSuccess(); + if (options.success) { + options.success(); } }); return; } - // Data to transmit - var data = { - items: [], + opts.nested = { + 'items': item_pk_values, }; - // 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 (var field_name in extraFields) { - data[field_name] = getFormFieldValue( - field_name, - fields[field_name], - { - modal: modal, - } - ); - } - inventreePut( url, data, { method: 'POST', - success: function() { + success: function(response) { + // Hide the modal + $(opts.modal).modal('hide'); - // Destroy the modal window - $(modal).modal('hide'); - - if (options.onSuccess) { - options.onSuccess(); + if (options.success) { + options.success(response); } }, error: function(xhr) { switch (xhr.status) { case 400: - - // Handle errors for standard fields - handleFormErrors( - xhr.responseJSON, - extraFields, - { - modal: modal, - } - ); - + handleFormErrors(xhr.responseJSON, fields, opts); break; default: - $(modal).modal('hide'); + $(opts.modal).modal('hide'); showApiError(xhr); break; } @@ -444,18 +455,6 @@ function adjustStock(action, items, options={}) { ); } }); - - // 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%'); } @@ -1258,7 +1257,7 @@ function loadStockTable(table, options) { var items = $(table).bootstrapTable('getSelections'); adjustStock(action, items, { - onSuccess: function() { + success: function() { $(table).bootstrapTable('refresh'); } });