From 59bb5d15c88728d698b348a9249e2619b891a8cd Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 8 Jan 2021 08:43:00 +1100 Subject: [PATCH 01/12] Filter PartAttachment API list by Part reference --- InvenTree/part/api.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index b479575a35..3c848620d5 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -182,6 +182,10 @@ class PartAttachmentList(generics.ListCreateAPIView, AttachmentMixin): queryset = PartAttachment.objects.all() serializer_class = part_serializers.PartAttachmentSerializer + filter_backends = [ + DjangoFilterBackend, + ] + filter_fields = [ 'part', ] From ba1862478c7c53a3fd766eb07aa41b630516a347 Mon Sep 17 00:00:00 2001 From: eeintech Date: Tue, 12 Jan 2021 17:33:42 -0500 Subject: [PATCH 02/12] Allow user with part.change permission to delete BOM items --- InvenTree/part/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 1d76860ac1..e7fd281dbb 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -2554,7 +2554,7 @@ class BomItemDelete(AjaxDeleteView): context_object_name = 'item' ajax_form_title = _('Confim BOM item deletion') - role_required = 'part.delete' + role_required = 'part.change' class PartSalePriceBreakCreate(AjaxCreateView): From a1b2347784c03737d84df405c3ce7fe02f77d4e9 Mon Sep 17 00:00:00 2001 From: eeintech Date: Tue, 12 Jan 2021 17:43:12 -0500 Subject: [PATCH 03/12] Also allow part attachements and parameters to be deleted --- InvenTree/part/templates/part/params.html | 2 +- InvenTree/part/views.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/InvenTree/part/templates/part/params.html b/InvenTree/part/templates/part/params.html index af1c4cdab6..d07a19f79f 100644 --- a/InvenTree/part/templates/part/params.html +++ b/InvenTree/part/templates/part/params.html @@ -37,7 +37,7 @@ {% if roles.part.change %} {% endif %} - {% if roles.part.delete %} + {% if roles.part.change %} {% endif %} diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index e7fd281dbb..d1a612b7f4 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -231,7 +231,7 @@ class PartAttachmentDelete(AjaxDeleteView): ajax_template_name = "attachment_delete.html" context_object_name = "attachment" - role_required = 'part.delete' + role_required = 'part.change' def get_data(self): return { @@ -2073,7 +2073,7 @@ class PartParameterEdit(AjaxUpdateView): class PartParameterDelete(AjaxDeleteView): """ View for deleting a PartParameter """ - role_required = 'part.delete' + role_required = 'part.change' model = PartParameter ajax_template_name = 'part/param_delete.html' From 59c0a502892ac81f939847f7a10db5e9d682187f Mon Sep 17 00:00:00 2001 From: eeintech Date: Wed, 13 Jan 2021 13:35:49 -0500 Subject: [PATCH 04/12] Separated category from part permissions and location from stock item permissions --- InvenTree/part/templates/part/category.html | 8 ++++---- InvenTree/part/views.py | 14 +++++++------- InvenTree/stock/templates/stock/location.html | 8 +++++--- InvenTree/stock/templates/stock/location_list.html | 2 +- InvenTree/stock/views.py | 10 +++++----- InvenTree/users/admin.py | 13 +++++++++++-- InvenTree/users/models.py | 14 ++++++++++---- 7 files changed, 43 insertions(+), 26 deletions(-) diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html index 6992d9bac4..47ef866e2e 100644 --- a/InvenTree/part/templates/part/category.html +++ b/InvenTree/part/templates/part/category.html @@ -9,7 +9,7 @@ {% if category %}

{{ category.name }} - {% if user.is_staff and roles.part.change %} + {% if user.is_staff and roles.part_category.change %} {% endif %}

@@ -20,18 +20,18 @@ {% endif %}

- {% if roles.part.add %} + {% if roles.part_category.add %} {% endif %} {% if category %} - {% if roles.part.change %} + {% if roles.part_category.change %} {% endif %} - {% if roles.part.delete %} + {% if roles.part_category.delete %} diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index d1a612b7f4..0e16bfb498 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -2088,7 +2088,7 @@ class CategoryDetail(InvenTreeRoleMixin, DetailView): queryset = PartCategory.objects.all().prefetch_related('children') template_name = 'part/category_partlist.html' - role_required = 'part.view' + role_required = ['part_category.view', 'part.view'] def get_context_data(self, **kwargs): @@ -2138,7 +2138,7 @@ class CategoryEdit(AjaxUpdateView): ajax_template_name = 'modal_form.html' ajax_form_title = _('Edit Part Category') - role_required = 'part.change' + role_required = 'part_category.change' def get_context_data(self, **kwargs): context = super(CategoryEdit, self).get_context_data(**kwargs).copy() @@ -2177,7 +2177,7 @@ class CategoryDelete(AjaxDeleteView): context_object_name = 'category' success_url = '/part/' - role_required = 'part.delete' + role_required = 'part_category.delete' def get_data(self): return { @@ -2193,7 +2193,7 @@ class CategoryCreate(AjaxCreateView): ajax_template_name = 'modal_form.html' form_class = part_forms.EditCategoryForm - role_required = 'part.add' + role_required = 'part_category.add' def get_context_data(self, **kwargs): """ Add extra context data to template. @@ -2233,7 +2233,7 @@ class CategoryCreate(AjaxCreateView): class CategoryParameterTemplateCreate(AjaxCreateView): """ View for creating a new PartCategoryParameterTemplate """ - role_required = 'part.add' + role_required = 'part_category.change' model = PartCategoryParameterTemplate form_class = part_forms.EditCategoryParameterTemplateForm @@ -2336,7 +2336,7 @@ class CategoryParameterTemplateCreate(AjaxCreateView): class CategoryParameterTemplateEdit(AjaxUpdateView): """ View for editing a PartCategoryParameterTemplate """ - role_required = 'part.change' + role_required = 'part_category.change' model = PartCategoryParameterTemplate form_class = part_forms.EditCategoryParameterTemplateForm @@ -2395,7 +2395,7 @@ class CategoryParameterTemplateEdit(AjaxUpdateView): class CategoryParameterTemplateDelete(AjaxDeleteView): """ View for deleting an existing PartCategoryParameterTemplate """ - role_required = 'part.delete' + role_required = 'part_category.change' model = PartCategoryParameterTemplate ajax_form_title = _("Delete Category Parameter Template") diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html index fef3428373..765a4bd903 100644 --- a/InvenTree/stock/templates/stock/location.html +++ b/InvenTree/stock/templates/stock/location.html @@ -8,7 +8,7 @@ {% if location %}

{{ location.name }} - {% if user.is_staff and roles.stock.change %} + {% if user.is_staff and roles.stock_location.change %} {% endif %}

@@ -18,7 +18,7 @@

{% trans "All stock items" %}

{% endif %}
- {% if roles.stock.add %} + {% if roles.stock_location.add %} @@ -41,11 +41,13 @@ {% trans "Count stock" %}
+ {% endif %} + {% if roles.stock_location.change %}
diff --git a/InvenTree/stock/templates/stock/location_list.html b/InvenTree/stock/templates/stock/location_list.html index 4ad30e1310..a2ea4a361a 100644 --- a/InvenTree/stock/templates/stock/location_list.html +++ b/InvenTree/stock/templates/stock/location_list.html @@ -1,6 +1,6 @@ {% extends "collapse.html" %} -{% if roles.stock.view %} +{% if roles.stock_location.view or roles.stock.view %} {% block collapse_title %} Sub-Locations{{ children|length }} {% endblock %} diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index ab6f64fb44..830f2c66f7 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -73,7 +73,7 @@ class StockLocationDetail(InvenTreeRoleMixin, DetailView): template_name = 'stock/location.html' queryset = StockLocation.objects.all() model = StockLocation - role_required = 'stock.view' + role_required = ['stock_location.view', 'stock.view'] class StockItemDetail(InvenTreeRoleMixin, DetailView): @@ -121,7 +121,7 @@ class StockLocationEdit(AjaxUpdateView): context_object_name = 'location' ajax_template_name = 'modal_form.html' ajax_form_title = _('Edit Stock Location') - role_required = 'stock.change' + role_required = 'stock_location.change' def get_form(self): """ Customize form data for StockLocation editing. @@ -146,7 +146,7 @@ class StockLocationQRCode(QRCodeView): """ View for displaying a QR code for a StockLocation object """ ajax_form_title = _("Stock Location QR code") - role_required = 'stock.view' + role_required = ['stock_location.view', 'stock.view'] def get_qr_data(self): """ Generate QR code data for the StockLocation """ @@ -1361,7 +1361,7 @@ class StockLocationCreate(AjaxCreateView): context_object_name = 'location' ajax_template_name = 'modal_form.html' ajax_form_title = _('Create new Stock Location') - role_required = 'stock.add' + role_required = 'stock_location.add' def get_initial(self): initials = super(StockLocationCreate, self).get_initial().copy() @@ -1748,7 +1748,7 @@ class StockLocationDelete(AjaxDeleteView): ajax_template_name = 'stock/location_delete.html' context_object_name = 'location' ajax_form_title = _('Delete Stock Location') - role_required = 'stock.delete' + role_required = 'stock_location.delete' class StockItemDelete(AjaxDeleteView): diff --git a/InvenTree/users/admin.py b/InvenTree/users/admin.py index 29496d02a7..c84f1310ce 100644 --- a/InvenTree/users/admin.py +++ b/InvenTree/users/admin.py @@ -30,6 +30,8 @@ class RuleSetInline(admin.TabularInline): max_num = len(RuleSet.RULESET_CHOICES) min_num = 1 extra = 0 + # TODO: find better way to order inlines + ordering = ['name'] class InvenTreeGroupAdminForm(forms.ModelForm): @@ -87,7 +89,8 @@ class RoleGroupAdmin(admin.ModelAdmin): RuleSetInline, ] - list_display = ('name', 'admin', 'part', 'stock', 'build', 'purchase_order', 'sales_order') + list_display = ('name', 'admin', 'part_category', 'part', 'stock_location', + 'stock_item', 'build', 'purchase_order', 'sales_order') def get_rule_set(self, obj, rule_set_type): ''' Return list of permissions for the given ruleset ''' @@ -130,10 +133,16 @@ class RoleGroupAdmin(admin.ModelAdmin): def admin(self, obj): return self.get_rule_set(obj, 'admin') + def part_category(self, obj): + return self.get_rule_set(obj, 'part_category') + def part(self, obj): return self.get_rule_set(obj, 'part') - def stock(self, obj): + def stock_location(self, obj): + return self.get_rule_set(obj, 'stock_location') + + def stock_item(self, obj): return self.get_rule_set(obj, 'stock') def build(self, obj): diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index b54cddf7c4..776514fc9f 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -25,8 +25,10 @@ class RuleSet(models.Model): RULESET_CHOICES = [ ('admin', _('Admin')), + ('part_category', _('Part Categories')), ('part', _('Parts')), - ('stock', _('Stock')), + ('stock_location', _('Stock Locations')), + ('stock', _('Stock Items')), ('build', _('Build Orders')), ('purchase_order', _('Purchase Orders')), ('sales_order', _('Sales Orders')), @@ -48,21 +50,25 @@ class RuleSet(models.Model): 'authtoken_token', 'users_ruleset', ], + 'part_category': [ + 'part_partcategory', + 'part_partcategoryparametertemplate', + ], 'part': [ 'part_part', 'part_bomitem', - 'part_partcategory', 'part_partattachment', 'part_partsellpricebreak', 'part_parttesttemplate', 'part_partparametertemplate', 'part_partparameter', 'part_partrelated', - 'part_partcategoryparametertemplate', + ], + 'stock_location': [ + 'stock_stocklocation', ], 'stock': [ 'stock_stockitem', - 'stock_stocklocation', 'stock_stockitemattachment', 'stock_stockitemtracking', 'stock_stockitemtestresult', From af1abb7129b04763d344cb535a9f437a5aeb28bc Mon Sep 17 00:00:00 2001 From: eeintech Date: Wed, 13 Jan 2021 14:57:16 -0500 Subject: [PATCH 05/12] Added missing migration file --- .../migrations/0004_auto_20210113_1909.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 InvenTree/users/migrations/0004_auto_20210113_1909.py diff --git a/InvenTree/users/migrations/0004_auto_20210113_1909.py b/InvenTree/users/migrations/0004_auto_20210113_1909.py new file mode 100644 index 0000000000..d762dc4c08 --- /dev/null +++ b/InvenTree/users/migrations/0004_auto_20210113_1909.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.7 on 2021-01-13 19:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0003_auto_20201005_2227'), + ] + + operations = [ + migrations.AlterField( + model_name='ruleset', + name='name', + field=models.CharField(choices=[('admin', 'Admin'), ('part_category', 'Part Categories'), ('part', 'Parts'), ('stock_location', 'Stock Locations'), ('stock', 'Stock Items'), ('build', 'Build Orders'), ('purchase_order', 'Purchase Orders'), ('sales_order', 'Sales Orders')], help_text='Permission set', max_length=50), + ), + ] From 890ce9ef956fa132eceab737fbf24d66f94e2133 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 14 Jan 2021 10:58:29 +1100 Subject: [PATCH 06/12] Fix IPN comparison against null value --- InvenTree/part/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 0e16bfb498..26cf93e56d 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -1320,7 +1320,7 @@ class BomUpload(InvenTreeRoleMixin, FormView): # Otherwise, check to see if there is a matching IPN try: if row['part_ipn']: - part_matches = [part for part in self.allowed_parts if row['part_ipn'].lower() == part.IPN.lower()] + part_matches = [part for part in self.allowed_parts if part.IPN and row['part_ipn'].lower() == str(part.IPN.lower())] # Check for single match if len(part_matches) == 1: From 1316e6bf5b8134d4586f9ed8643bfe2e28c99854 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 14 Jan 2021 11:24:52 +1100 Subject: [PATCH 07/12] Properly save user data when creating a new StockItem --- InvenTree/stock/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 830f2c66f7..5d505b5280 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -1721,7 +1721,7 @@ class StockItemCreate(AjaxCreateView): item = form.save(commit=False) item.user = self.request.user - item.save() + item.save(user=self.request.user) return item @@ -1732,7 +1732,7 @@ class StockItemCreate(AjaxCreateView): item = form.save(commit=False) item.user = self.request.user - item.save() + item.save(user=self.request.user) return item From df327d4e6418810f898a3556ba55d59321c3f63c Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 14 Jan 2021 11:29:35 +1100 Subject: [PATCH 08/12] Add stocktake_date field to stock API, and to stock table --- InvenTree/InvenTree/static/script/inventree/tables.js | 3 +-- InvenTree/stock/serializers.py | 1 + InvenTree/templates/js/stock.js | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/static/script/inventree/tables.js b/InvenTree/InvenTree/static/script/inventree/tables.js index 1a50780393..4d868f94b4 100644 --- a/InvenTree/InvenTree/static/script/inventree/tables.js +++ b/InvenTree/InvenTree/static/script/inventree/tables.js @@ -131,8 +131,7 @@ $.fn.inventreeTable = function(options) { // Callback when a column is changed options.onColumnSwitch = function(field, checked) { - console.log(`${field} -> ${checked}`); - + var columns = table.bootstrapTable('getVisibleColumns'); var text = visibleColumnString(columns); diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py index 70ad1abc18..6048d5e248 100644 --- a/InvenTree/stock/serializers.py +++ b/InvenTree/stock/serializers.py @@ -208,6 +208,7 @@ class StockItemSerializer(InvenTreeModelSerializer): 'stale', 'status', 'status_text', + 'stocktake_date', 'supplier_part', 'supplier_part_detail', 'tracking_items', diff --git a/InvenTree/templates/js/stock.js b/InvenTree/templates/js/stock.js index e3f124d252..9ce395db57 100644 --- a/InvenTree/templates/js/stock.js +++ b/InvenTree/templates/js/stock.js @@ -590,6 +590,11 @@ function loadStockTable(table, options) { return locationDetail(row); } }, + { + field: 'stocktake_date', + title: '{% trans "Stocktake" %}', + sortable: true, + }, {% settings_value "STOCK_ENABLE_EXPIRY" as expiry %} {% if expiry %} { From 1cb951bd0b682030255292eba236a042c42d99dc Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 14 Jan 2021 12:08:54 +1100 Subject: [PATCH 09/12] Fix for font-awesome icon --- InvenTree/part/templates/part/detail.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html index f723193abb..59c33ea7b6 100644 --- a/InvenTree/part/templates/part/detail.html +++ b/InvenTree/part/templates/part/detail.html @@ -214,9 +214,9 @@ {% if part.active %} - + {% else %} - + {% endif %} {% trans "Active" %} From d45994794981682064a1c6d5f7a05965f36c7733 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 14 Jan 2021 13:34:51 +1100 Subject: [PATCH 10/12] Add "Can Build" column in BOM view --- InvenTree/templates/js/bom.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/InvenTree/templates/js/bom.js b/InvenTree/templates/js/bom.js index 299045cfa5..e1b3b46cc0 100644 --- a/InvenTree/templates/js/bom.js +++ b/InvenTree/templates/js/bom.js @@ -255,6 +255,38 @@ function loadBomTable(table, options) { }); */ } + + cols.push( + { + 'field': 'can_build', + 'title': '{% trans "Can Build" %}', + formatter: function(value, row, index, field) { + var can_build = 0; + + if (row.quantity > 0) { + can_build = row.sub_part_detail.stock / row.quantity; + } + + return +can_build.toFixed(2); + }, + sorter: function(valA, valB, rowA, rowB) { + // Function to sort the "can build" quantity + var cb_a = 0; + var cb_b = 0; + + if (rowA.quantity > 0) { + cb_a = rowA.sub_part_detail.stock / rowA.quantity; + } + + if (rowB.quantity > 0) { + cb_b = rowB.sub_part_detail.stock / rowB.quantity; + } + + return (cb_a > cb_b) ? 1 : -1; + }, + sortable: true, + } + ) // Part notes cols.push( From aac835f634e8427c7c9689523a5d9e8ed8bef867 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 14 Jan 2021 13:41:38 +1100 Subject: [PATCH 11/12] Add menu item to set stock status for multiple items --- InvenTree/templates/js/stock.js | 8 ++++++++ InvenTree/templates/stock_table.html | 1 + 2 files changed, 9 insertions(+) diff --git a/InvenTree/templates/js/stock.js b/InvenTree/templates/js/stock.js index 9ce395db57..bc2db4098a 100644 --- a/InvenTree/templates/js/stock.js +++ b/InvenTree/templates/js/stock.js @@ -682,6 +682,14 @@ function loadStockTable(table, options) { }); }); + $("#multi-item-set-status").click(function() { + var selections = $("#stock-table").bootstrapTable('getSelections'); + + selections.forEach(function(item) { + // TODO + }); + }); + $("#multi-item-delete").click(function() { var selections = $("#stock-table").bootstrapTable("getSelections"); diff --git a/InvenTree/templates/stock_table.html b/InvenTree/templates/stock_table.html index 51f7c277db..f39f9c733a 100644 --- a/InvenTree/templates/stock_table.html +++ b/InvenTree/templates/stock_table.html @@ -23,6 +23,7 @@
  • {% trans "Count stock" %}
  • {% trans "Move stock" %}
  • {% trans "Order stock" %}
  • +
  • {% trans "Change stock status" %}
  • {% endif %} {% if roles.stock.delete %}
  • {% trans "Delete Stock" %}
  • From bb9fe98a7edd50d07b41e02730549f6e358f1b92 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 14 Jan 2021 14:04:24 +1100 Subject: [PATCH 12/12] Set status for multiple stock items at once --- InvenTree/templates/js/stock.js | 97 +++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 4 deletions(-) diff --git a/InvenTree/templates/js/stock.js b/InvenTree/templates/js/stock.js index bc2db4098a..e9cb5e2696 100644 --- a/InvenTree/templates/js/stock.js +++ b/InvenTree/templates/js/stock.js @@ -6,8 +6,18 @@ * Requires api.js to be loaded first */ -/* Functions for interacting with stock management forms - */ + +function stockStatusCodes() { + return [ + {% for code in StockStatus.list %} + { + key: {{ code.key }}, + text: "{{ code.value }}", + }, + {% endfor %} + ]; +} + function removeStockRow(e) { // Remove a selected row from a stock modal form @@ -683,11 +693,90 @@ function loadStockTable(table, options) { }); $("#multi-item-set-status").click(function() { + // Select and set the STATUS field for selected stock items var selections = $("#stock-table").bootstrapTable('getSelections'); - selections.forEach(function(item) { - // TODO + // Select stock status + var modal = '#modal-form'; + + var status_list = makeOptionsList( + stockStatusCodes(), + function(item) { + return item.text; + }, + function (item) { + return item.key; + } + ); + + // Add an empty option at the start of the list + status_list.unshift(''); + + // Construct form + var html = ` +
    +
    + +
    + +
    +
    +
    `; + + openModal({ + modal: modal, }); + + modalEnable(modal, true); + modalSetTitle(modal, '{% trans "Set Stock Status" %}'); + modalSetContent(modal, html); + + attachSelect(modal); + + modalSubmit(modal, function() { + var label = $(modal).find('#id_status'); + + var status_code = label.val(); + + closeModal(modal); + + if (!status_code) { + showAlertDialog( + '{% trans "Select Status Code" %}', + '{% trans "Status code must be selected" %}' + ); + + return; + } + + var requests = []; + + selections.forEach(function(item) { + var url = `/api/stock/${item.pk}/`; + + requests.push( + inventreePut( + url, + { + status: status_code, + }, + { + method: 'PATCH', + success: function() { + } + } + ) + ); + }); + + $.when.apply($, requests).then(function() { + $("#stock-table").bootstrapTable('refresh'); + }); + }) }); $("#multi-item-delete").click(function() {