diff --git a/InvenTree/InvenTree/api_version.py b/InvenTree/InvenTree/api_version.py index f4b8c7740f..92ee99fdde 100644 --- a/InvenTree/InvenTree/api_version.py +++ b/InvenTree/InvenTree/api_version.py @@ -2,11 +2,14 @@ # InvenTree API version -INVENTREE_API_VERSION = 125 +INVENTREE_API_VERSION = 126 """ Increment this API version number whenever there is a significant change to the API that any clients need to know about +v126 -> 2023-06-19 : https://github.com/inventree/InvenTree/pull/5075 + - Adds API endpoint for setting the "category" for multiple parts simultaneously + v125 -> 2023-06-17 : https://github.com/inventree/InvenTree/pull/5064 - Adds API endpoint for setting the "status" field for multiple stock items simultaneously diff --git a/InvenTree/InvenTree/static/css/inventree.css b/InvenTree/InvenTree/static/css/inventree.css index a118a58e22..6a080cba01 100644 --- a/InvenTree/InvenTree/static/css/inventree.css +++ b/InvenTree/InvenTree/static/css/inventree.css @@ -269,10 +269,6 @@ main { } /* Styles for table buttons and filtering */ -.button-toolbar .btn { - margin-left: 1px; - margin-right: 1px; -} .filter-list { display: inline-block; diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index d1932527c0..b7b49a51ae 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -95,6 +95,7 @@ notifications_urls = [ dynamic_javascript_urls = [ re_path(r'^calendar.js', DynamicJsView.as_view(template_name='js/dynamic/calendar.js'), name='calendar.js'), re_path(r'^nav.js', DynamicJsView.as_view(template_name='js/dynamic/nav.js'), name='nav.js'), + re_path(r'^permissions.js', DynamicJsView.as_view(template_name='js/dynamic/permissions.js'), name='permissions.js'), re_path(r'^settings.js', DynamicJsView.as_view(template_name='js/dynamic/settings.js'), name='settings.js'), ] diff --git a/InvenTree/build/templates/build/detail.html b/InvenTree/build/templates/build/detail.html index 14c75be201..2da2410fc8 100644 --- a/InvenTree/build/templates/build/detail.html +++ b/InvenTree/build/templates/build/detail.html @@ -165,9 +165,7 @@
-
- {% include "filter_list.html" with id='sub-build' %} -
+ {% include "filter_list.html" with id='sub-build' %}
@@ -199,26 +197,8 @@
- {% if build.active %} - {% if build.is_fully_allocated %} -
- {% trans "Untracked stock has been fully allocated for this Build Order" %} -
- {% else %} -
- {% trans "Untracked stock has not been fully allocated for this Build Order" %} -
- {% endif %} - {% endif %}
-
-
- - {% include "filter_list.html" with id='buildlines' %} -
-
+ {% include "filter_list.html" with id='buildlines' %}
@@ -240,37 +220,7 @@
-
- {% if build.active %} -
- - -
- - -
- {% include "filter_list.html" with id='incompletebuilditems' %} -
- {% endif %} -
+ {% include "filter_list.html" with id='incompletebuilditems' %}
@@ -501,10 +451,6 @@ $('#btn-unallocate').on('click', function() { }); }); -$('#allocate-selected-items').click(function() { - allocateSelectedLines(); -}); - $("#btn-allocate").on('click', function() { allocateSelectedLines(); }); diff --git a/InvenTree/build/templates/build/index.html b/InvenTree/build/templates/build/index.html index 5094c6a45a..f4a266ecca 100644 --- a/InvenTree/build/templates/build/index.html +++ b/InvenTree/build/templates/build/index.html @@ -24,11 +24,7 @@
-
-
- {% include "filter_list.html" with id="build" %} -
-
+ {% include "filter_list.html" with id="build" %}
diff --git a/InvenTree/company/templates/company/detail.html b/InvenTree/company/templates/company/detail.html index 9adfa255a0..6a653d2d0d 100644 --- a/InvenTree/company/templates/company/detail.html +++ b/InvenTree/company/templates/company/detail.html @@ -26,28 +26,7 @@
{% if roles.purchase_order.change %}
-
-
-
- - -
- {% include "filter_list.html" with id="supplier-part" %} -
-
+ {% include "filter_list.html" with id="supplier-part" %}
{% endif %} @@ -73,29 +52,7 @@
{% if roles.purchase_order.change %}
-
-
-
- - -
- {% include "filter_list.html" with id="manufacturer-part" %} -
-
+ {% include "filter_list.html" with id="manufacturer-part" %}
{% endif %}
@@ -128,9 +85,7 @@
-
- {% include "filter_list.html" with id="purchaseorder" %} -
+ {% include "filter_list.html" with id="purchaseorder" %}
@@ -156,9 +111,7 @@
-
- {% include "filter_list.html" with id="salesorder" %} -
+ {% include "filter_list.html" with id="salesorder" %}
@@ -174,9 +127,7 @@
-
- {% include "filter_list.html" with id="customerstock" %} -
+ {% include "filter_list.html" with id="customerstock" %}
@@ -201,11 +152,7 @@
-
-
- {% include "filter_list.html" with id="returnorder" %} -
-
+ {% include "filter_list.html" with id="returnorder" %}
@@ -246,9 +193,7 @@
-
- {% include "filter_list.html" with id="contacts" %} -
+ {% include "filter_list.html" with id="contacts" %}
@@ -271,9 +216,7 @@
-
- {% include "filter_list.html" with id="addresses" %} -
+ {% include "filter_list.html" with id="addresses" %}
@@ -498,32 +441,6 @@ } ); - $("#multi-manufacturer-part-delete").click(function() { - var selections = getTableData('#manufacturer-part-table'); - - deleteManufacturerParts(selections, { - success: function() { - $("#manufacturer-part-table").bootstrapTable('refresh'); - } - }); - }); - - $("#multi-manufacturer-part-order").click(function() { - var selections = getTableData('#manufacturer-part-table'); - - var parts = []; - - selections.forEach(function(item) { - var part = item.part_detail; - part.manufacturer_part = item.pk; - parts.push(part); - }); - - orderParts( - parts, - ); - }); - {% endif %} {% if company.is_supplier %} @@ -552,37 +469,6 @@ }, } ); - - $("#multi-supplier-part-delete").click(function() { - - var selections = getTableData("#supplier-part-table"); - - deleteSupplierParts(selections, { - success: function() { - $('#supplier-part-table').bootstrapTable('refresh'); - } - }); - }); - - $("#multi-supplier-part-order").click(function() { - - var selections = getTableData('#supplier-part-table'); - - var parts = []; - - selections.forEach(function(item) { - var part = item.part_detail; - parts.push(part); - }); - - orderParts( - parts, - { - supplier: {{ company.pk }}, - } - ); - }); - {% endif %} enableSidebar('company'); diff --git a/InvenTree/company/templates/company/index.html b/InvenTree/company/templates/company/index.html index b255137114..cb93522a6b 100644 --- a/InvenTree/company/templates/company/index.html +++ b/InvenTree/company/templates/company/index.html @@ -24,11 +24,11 @@
-
+
{% include "filter_list.html" with id='company' %}
- +
diff --git a/InvenTree/company/templates/company/manufacturer_part.html b/InvenTree/company/templates/company/manufacturer_part.html index c26e828558..0c158edd76 100644 --- a/InvenTree/company/templates/company/manufacturer_part.html +++ b/InvenTree/company/templates/company/manufacturer_part.html @@ -127,17 +127,7 @@ src="{% static 'img/blank_image.png' %}"
-
-
- - -
- {% include "filter_list.html" with id='supplier-part' %} -
+ {% include "filter_list.html" with id='supplier-part' %}
@@ -174,17 +164,7 @@ src="{% static 'img/blank_image.png' %}"
-
-
- - -
- {% include "filter_list.html" with id="manufacturer-part-parameters" %} -
+ {% include "filter_list.html" with id="manufacturer-part-parameters" %}
@@ -240,26 +220,6 @@ $('#supplier-create').click(function () { }); }); -$("#supplier-part-delete").click(function() { - - var selections = getTableData('#supplier-table'); - - deleteSupplierParts(selections, { - success: reloadSupplierPartTable, - }); -}); - -$("#multi-parameter-delete").click(function() { - - var selections = getTableData('#parameter-table'); - - deleteManufacturerPartParameters(selections, { - success: function() { - $('#parameter-table').bootstrapTable('refresh'); - } - }); -}); - loadSupplierPartTable( "#supplier-table", "{% url 'api-supplier-part-list' %}", diff --git a/InvenTree/company/templates/company/supplier_part.html b/InvenTree/company/templates/company/supplier_part.html index 4fc29abe38..9fc84ce123 100644 --- a/InvenTree/company/templates/company/supplier_part.html +++ b/InvenTree/company/templates/company/supplier_part.html @@ -234,9 +234,7 @@ src="{% static 'img/blank_image.png' %}"
-
- {% include "filter_list.html" with id='purchaseorder' %} -
+ {% include "filter_list.html" with id='purchaseorder' %}
@@ -258,10 +256,8 @@ src="{% static 'img/blank_image.png' %}"
-
-
- {% include "filter_list.html" with id='supplierpricebreak' %} -
+
+ {% include "filter_list.html" with id='supplierpricebreak' %}
diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html index 2918bd616f..d7491f753e 100644 --- a/InvenTree/order/templates/order/purchase_order_detail.html +++ b/InvenTree/order/templates/order/purchase_order_detail.html @@ -37,23 +37,7 @@
-
- {% if roles.purchase_order.change %} - {% if order.is_open or allow_extra_editing %} - - {% endif %} - {% endif %} - +
{% include "filter_list.html" with id="purchase-order-lines" %}
@@ -77,10 +61,8 @@
-
-
- {% include "filter_list.html" with id="purchase-order-extra-lines" %} -
+
+ {% include "filter_list.html" with id="purchase-order-extra-lines" %}
diff --git a/InvenTree/order/templates/order/purchase_orders.html b/InvenTree/order/templates/order/purchase_orders.html index 23b56baee1..d601b3fd17 100644 --- a/InvenTree/order/templates/order/purchase_orders.html +++ b/InvenTree/order/templates/order/purchase_orders.html @@ -24,11 +24,7 @@
-
-
- {% include "filter_list.html" with id="purchaseorder" %} -
-
+ {% include "filter_list.html" with id="purchaseorder" %}
diff --git a/InvenTree/order/templates/order/return_order_detail.html b/InvenTree/order/templates/order/return_order_detail.html index b0542ba188..9c11ff6270 100644 --- a/InvenTree/order/templates/order/return_order_detail.html +++ b/InvenTree/order/templates/order/return_order_detail.html @@ -34,10 +34,8 @@
-
-
- {% include "filter_list.html" with id="returnorderlines" %} -
+
+ {% include "filter_list.html" with id="returnorderlines" %}
@@ -58,10 +56,8 @@
-
-
- {% include "filter_list.html" with id="return-order-extra-lines" %} -
+
+ {% include "filter_list.html" with id="return-order-extra-lines" %}
diff --git a/InvenTree/order/templates/order/return_orders.html b/InvenTree/order/templates/order/return_orders.html index 698b682be0..834047eb46 100644 --- a/InvenTree/order/templates/order/return_orders.html +++ b/InvenTree/order/templates/order/return_orders.html @@ -26,15 +26,11 @@ {% block page_info %}
-
-
-
- {% include "filter_list.html" with id="returnorder" %} -
-
+
+ {% include "filter_list.html" with id="returnorder" %}
- +
diff --git a/InvenTree/order/templates/order/sales_order_detail.html b/InvenTree/order/templates/order/sales_order_detail.html index 8a43c3ee1b..24cb4e4b78 100644 --- a/InvenTree/order/templates/order/sales_order_detail.html +++ b/InvenTree/order/templates/order/sales_order_detail.html @@ -29,10 +29,8 @@
-
-
- {% include "filter_list.html" with id="sales-order-lines" %} -
+
+ {% include "filter_list.html" with id="sales-order-lines" %}
@@ -54,10 +52,8 @@
-
-
- {% include "filter_list.html" with id="sales-order-extra-lines" %} -
+
+ {% include "filter_list.html" with id="sales-order-extra-lines" %}
@@ -89,10 +85,8 @@
{% if roles.sales_order.change %} -
-
- {% include "filter_list.html" with id="pending-shipments" %} -
+
+ {% include "filter_list.html" with id="pending-shipments" %}
{% endif %}
@@ -105,10 +99,8 @@

{% trans "Completed Shipments" %}

-
-
- {% include "filter_list.html" with id="completed-shipments" %} -
+
+ {% include "filter_list.html" with id="completed-shipments" %}
@@ -119,10 +111,8 @@

{% trans "Build Orders" %}

-
-
- {% include "filter_list.html" with id='build' %} -
+
+ {% include "filter_list.html" with id='build' %}
diff --git a/InvenTree/order/templates/order/sales_orders.html b/InvenTree/order/templates/order/sales_orders.html index 86c64fbe54..c4cc86cad1 100644 --- a/InvenTree/order/templates/order/sales_orders.html +++ b/InvenTree/order/templates/order/sales_orders.html @@ -27,10 +27,8 @@
-
-
- {% include "filter_list.html" with id="salesorder" %} -
+
+ {% include "filter_list.html" with id="salesorder" %}
diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index b47175c3f0..801b0b5f8d 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -1271,6 +1271,13 @@ class PartList(PartMixin, APIDownloadMixin, ListCreateAPI): ] +class PartChangeCategory(CreateAPI): + """API endpoint to change the location of multiple parts in bulk""" + + serializer_class = part_serializers.PartSetCategorySerializer + queryset = Part.objects.none() + + class PartDetail(PartMixin, RetrieveUpdateDestroyAPI): """API endpoint for detail view of a single Part object.""" @@ -2020,6 +2027,8 @@ part_api_urls = [ re_path(r'^.*$', PartDetail.as_view(), name='api-part-detail'), ])), + re_path(r'^change_category/', PartChangeCategory.as_view(), name='api-part-change-category'), + re_path(r'^.*$', PartList.as_view(), name='api-part-list'), ] diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 512c5cd08b..e43cf46247 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -291,6 +291,56 @@ class PartBriefSerializer(InvenTree.serializers.InvenTreeModelSerializer): pricing_max = InvenTree.serializers.InvenTreeMoneySerializer(source='pricing_data.overall_max', allow_null=True, read_only=True) +class PartSetCategorySerializer(serializers.Serializer): + """Serializer for changing PartCategory for multiple Part objects""" + + class Meta: + """Metaclass options""" + fields = [ + 'parts', + 'category', + ] + + parts = serializers.PrimaryKeyRelatedField( + queryset=Part.objects.all(), + many=True, required=True, allow_null=False, + label=_('Parts'), + ) + + def validate_parts(self, parts): + """Validate the selected parts""" + if len(parts) == 0: + raise serializers.ValidationError(_("No parts selected")) + + return parts + + category = serializers.PrimaryKeyRelatedField( + queryset=PartCategory.objects.filter(structural=False), + many=False, required=True, allow_null=False, + label=_('Category'), + help_text=_('Select category',) + ) + + @transaction.atomic + def save(self): + """Save the serializer to change the location of the selected parts""" + + data = self.validated_data + parts = data['parts'] + category = data['category'] + + parts_to_save = [] + + for p in parts: + if p.category == category: + continue + + p.category = category + parts_to_save.append(p) + + Part.objects.bulk_update(parts_to_save, ['category']) + + class DuplicatePartSerializer(serializers.Serializer): """Serializer for specifying options when duplicating a Part. diff --git a/InvenTree/part/templates/part/bom.html b/InvenTree/part/templates/part/bom.html index f9bcfc483a..e917cbd288 100644 --- a/InvenTree/part/templates/part/bom.html +++ b/InvenTree/part/templates/part/bom.html @@ -23,20 +23,7 @@ {% endif %}
-
- {% if roles.part.change %} - - - {% endif %} - {% include "filter_list.html" with id="bom" %} -
+ {% include "filter_list.html" with id="bom" %}
diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html index c560eed392..1faac776f1 100644 --- a/InvenTree/part/templates/part/category.html +++ b/InvenTree/part/templates/part/category.html @@ -169,24 +169,7 @@
-
-
- - -
- {% include "filter_list.html" with id="parts" %} -
+ {% include "filter_list.html" with id="parts" %}
@@ -209,9 +192,7 @@
-
- {% include "filter_list.html" with id="parameters" %} -
+ {% include "filter_list.html" with id="parameters" %}
@@ -235,9 +216,7 @@
-
- {% include "filter_list.html" with id="category" %} -
+ {% include "filter_list.html" with id="category" %}
diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html index 447c83ee01..8ecac03b6c 100644 --- a/InvenTree/part/templates/part/detail.html +++ b/InvenTree/part/templates/part/detail.html @@ -93,9 +93,7 @@
-
- {% include "filter_list.html" with id="parttests" %} -
+ {% include "filter_list.html" with id="parttests" %}
@@ -116,9 +114,7 @@
-
- {% include "filter_list.html" with id="partpurchaseorders" %} -
+ {% include "filter_list.html" with id="partpurchaseorders" %}
@@ -132,9 +128,7 @@
-
- {% include "filter_list.html" with id="salesorder" %} -
+ {% include "filter_list.html" with id="salesorder" %}
@@ -145,11 +139,8 @@

{% trans "Sales Order Allocations" %}

-
-
- {% include "filter_list.html" with id="salesorderallocation" %} -
+ {% include "filter_list.html" with id="salesorderallocation" %}
@@ -190,11 +181,7 @@
-
-
- {% include "filter_list.html" with id="variants" %} -
-
+ {% include "filter_list.html" with id="variants" %}
@@ -218,11 +205,7 @@
-
-
- {% include "filter_list.html" with id="parameters" %} -
-
+ {% include "filter_list.html" with id="parameters" %}
@@ -259,9 +242,7 @@
@@ -317,9 +298,7 @@
-
- {% include "filter_list.html" with id="usedin" %} -
+ {% include "filter_list.html" with id="usedin" %}
@@ -346,9 +325,7 @@
-
- {% include "filter_list.html" with id="build" %} -
+ {% include "filter_list.html" with id="build" %}
@@ -362,9 +339,7 @@
-
- {% include "filter_list.html" with id="buildorderallocation" %} -
+ {% include "filter_list.html" with id="buildorderallocation" %}
@@ -385,17 +360,7 @@
-
-
- - -
- {% include "filter_list.html" with id="supplier-part" %} -
+ {% include "filter_list.html" with id="supplier-part" %}
@@ -416,15 +381,7 @@
-
-
- - - {% include "filter_list.html" with id="manufacturer-part" %} -
-
+ {% include "filter_list.html" with id="manufacturer-part" %}
@@ -526,8 +483,6 @@ } ); - linkButtonsToSelection($("#supplier-part-table"), ['#supplier-part-options']); - loadManufacturerPartTable( '#manufacturer-part-table', "{% url 'api-manufacturer-part-list' %}", @@ -540,8 +495,6 @@ } ); - linkButtonsToSelection($("#manufacturer-part-table"), ['#manufacturer-part-options']); - $("#manufacturer-part-delete").click(function() { var selectionss = getTableData('#manufacturer-part-table'); @@ -625,12 +578,6 @@ sub_part_detail: true, }); - linkButtonsToSelection($("#bom-table"), - [ - "#bom-item-delete", - ] - ); - $('#bom-item-delete').click(function() { // Get a list of the selected BOM items diff --git a/InvenTree/stock/templates/stock/item.html b/InvenTree/stock/templates/stock/item.html index f922e158ec..81ec9ef059 100644 --- a/InvenTree/stock/templates/stock/item.html +++ b/InvenTree/stock/templates/stock/item.html @@ -22,9 +22,7 @@
-
- {% include "filter_list.html" with id="stocktracking" %} -
+ {% include "filter_list.html" with id="stocktracking" %}
@@ -40,9 +38,7 @@
-
- {% include "filter_list.html" with id="buildorderallocation" %} -
+ {% include "filter_list.html" with id="buildorderallocation" %}
@@ -55,9 +51,7 @@
-
- {% include "filter_list.html" with id="salesorderallocation" %} -
+ {% include "filter_list.html" with id="salesorderallocation" %}
@@ -102,9 +96,7 @@
-
- {% include "filter_list.html" with id="stocktests" %} -
+ {% include "filter_list.html" with id="stocktests" %}
@@ -157,9 +149,7 @@
-
- {% include "filter_list.html" with id='installed-items' %} -
+ {% include "filter_list.html" with id='installed-items' %}
diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html index c65505a757..98a8283002 100644 --- a/InvenTree/stock/templates/stock/location.html +++ b/InvenTree/stock/templates/stock/location.html @@ -221,9 +221,7 @@
-
- {% include "filter_list.html" with id="location" %} -
+ {% include "filter_list.html" with id="location" %}
@@ -270,13 +268,6 @@ }); }); - linkButtonsToSelection( - $('#sublocation-table'), - [ - '#location-print-options', - ] - ); - {% if labels_enabled %} $('#print-label').click(function() { diff --git a/InvenTree/templates/InvenTree/notifications/history.html b/InvenTree/templates/InvenTree/notifications/history.html index adccdd88a1..ca8f24bee5 100644 --- a/InvenTree/templates/InvenTree/notifications/history.html +++ b/InvenTree/templates/InvenTree/notifications/history.html @@ -18,9 +18,7 @@ {% block content %}
-
- {% include "filter_list.html" with id="notifications-history" %} -
+ {% include "filter_list.html" with id="notifications-history" %}
diff --git a/InvenTree/templates/InvenTree/notifications/inbox.html b/InvenTree/templates/InvenTree/notifications/inbox.html index 832807ae1e..9ab28051f4 100644 --- a/InvenTree/templates/InvenTree/notifications/inbox.html +++ b/InvenTree/templates/InvenTree/notifications/inbox.html @@ -18,9 +18,7 @@ {% block content %}
-
- {% include "filter_list.html" with id="notifications-inbox" %} -
+ {% include "filter_list.html" with id="notifications-inbox" %}
diff --git a/InvenTree/templates/InvenTree/settings/part_stocktake.html b/InvenTree/templates/InvenTree/settings/part_stocktake.html index ad9e563a93..01bec1cd5f 100644 --- a/InvenTree/templates/InvenTree/settings/part_stocktake.html +++ b/InvenTree/templates/InvenTree/settings/part_stocktake.html @@ -35,9 +35,7 @@
-
- {% include "filter_list.html" with id="stocktakereport" %} -
+ {% include "filter_list.html" with id="stocktakereport" %}
diff --git a/InvenTree/templates/InvenTree/settings/plugin.html b/InvenTree/templates/InvenTree/settings/plugin.html index 3813264176..45ed034c0a 100644 --- a/InvenTree/templates/InvenTree/settings/plugin.html +++ b/InvenTree/templates/InvenTree/settings/plugin.html @@ -51,11 +51,7 @@ {% endif %}
-
-
- {% include "filter_list.html" with id="plugins" %} -
-
+ {% include "filter_list.html" with id="plugins" %}
diff --git a/InvenTree/templates/attachment_table.html b/InvenTree/templates/attachment_table.html index fbabb7e510..c023e0ff2f 100644 --- a/InvenTree/templates/attachment_table.html +++ b/InvenTree/templates/attachment_table.html @@ -1,21 +1,7 @@ {% load i18n %}
-
- - {% include "filter_list.html" with id="attachments" %} -
+ {% include "filter_list.html" with id="attachments" %}
diff --git a/InvenTree/templates/base.html b/InvenTree/templates/base.html index 0e395c3168..b33721693a 100644 --- a/InvenTree/templates/base.html +++ b/InvenTree/templates/base.html @@ -147,6 +147,7 @@ + diff --git a/InvenTree/templates/filter_list.html b/InvenTree/templates/filter_list.html index eebea0ed0d..55d2a4c39b 100644 --- a/InvenTree/templates/filter_list.html +++ b/InvenTree/templates/filter_list.html @@ -1 +1,3 @@ -
+
+ +
diff --git a/InvenTree/templates/js/dynamic/permissions.js b/InvenTree/templates/js/dynamic/permissions.js new file mode 100644 index 0000000000..34881a8dcf --- /dev/null +++ b/InvenTree/templates/js/dynamic/permissions.js @@ -0,0 +1,59 @@ +/* + * globals + inventreeGet, + +/* exported + checkPermission, +*/ + +// Keep track of the current user permissions +var user_roles = null; + + +/* + * Check if the user has the specified role and permission + */ +function checkPermission(role, permission='view') { + + // Allow permission to be specified in dotted notation, e.g. 'part.add' + if (role.indexOf('.') > 0) { + let parts = role.split('.'); + role = parts[0]; + permission = parts[1]; + } + + // Request user roles if we do not have them + if (user_roles == null) { + inventreeGet('{% url "api-user-roles" %}', {}, { + async: false, + success: function(response) { + user_roles = response.roles || {}; + } + }); + } + + if (user_roles == null) { + console.error("Failed to fetch user roles"); + return false; + } + + if (!(role in user_roles)) { + return false; + } + + let roles = user_roles[role]; + + if (!roles) { + return false; + } + + let found = false; + + user_roles[role].forEach(function(p) { + if (String(p).valueOf() == String(permission).valueOf()) { + found = true; + } + }); + + return found; +} diff --git a/InvenTree/templates/js/translated/attachment.js b/InvenTree/templates/js/translated/attachment.js index da7fb29037..6c925edf1f 100644 --- a/InvenTree/templates/js/translated/attachment.js +++ b/InvenTree/templates/js/translated/attachment.js @@ -187,7 +187,27 @@ function attachmentLink(filename) { let html = makeIcon(icon) + ` ${fn}`; return renderLink(html, filename, {download: true}); +} + +/* + * Construct a set of actions for an attachment table, + * with the provided permission set + */ +function makeAttachmentActions(permissions, options) { + + let actions = []; + + if (permissions.delete) { + actions.push({ + label: 'delete', + icon: 'fa-trash-alt icon-red', + title: '{% trans "Delete attachments" %}', + callback: options.callback, + }); + } + + return actions; } @@ -225,7 +245,20 @@ function loadAttachmentTable(url, options) { } }); - setupFilterList('attachments', $(table), '#filter-list-attachments'); + setupFilterList('attachments', $(table), '#filter-list-attachments', { + custom_actions: [ + { + label: 'attachments', + icon: 'fa-tools', + title: '{% trans "Attachment actions" %}', + actions: makeAttachmentActions(permissions, { + callback: function(attachments) { + deleteAttachments(attachments, url, options); + } + }), + } + ] + }); if (permissions.add) { addAttachmentButtonCallbacks(url, options.fields || {}); @@ -235,19 +268,6 @@ function loadAttachmentTable(url, options) { $('#new-attachment-link').hide(); } - if (permissions.delete) { - // Add callback for the 'multi delete' button - $('#multi-attachment-delete').click(function() { - var attachments = getTableData(table); - - if (attachments.length > 0) { - deleteAttachments(attachments, url, options); - } - }); - } else { - $('#multi-attachment-actions').hide(); - } - $(table).inventreeTable({ url: url, name: options.name || 'attachments', @@ -286,16 +306,6 @@ function loadAttachmentTable(url, options) { }); }); } - - if (permissions.delete) { - // Add callback for 'delete' button - $(table).find('.button-attachment-delete').click(function() { - var pk = $(this).attr('pk'); - - var attachment = $(table).bootstrapTable('getRowByUniqueId', pk); - deleteAttachments([attachment], url, options); - }); - } }, columns: [ { diff --git a/InvenTree/templates/js/translated/barcode.js b/InvenTree/templates/js/translated/barcode.js index dbe12c198d..6be685cdf2 100644 --- a/InvenTree/templates/js/translated/barcode.js +++ b/InvenTree/templates/js/translated/barcode.js @@ -763,7 +763,7 @@ function scanItemsIntoLocation(item_list, options={}) { // Extra form fields var extra = makeNotesField(); - // Header contentfor + // Header content var header = `
diff --git a/InvenTree/templates/js/translated/bom.js b/InvenTree/templates/js/translated/bom.js index e6841b9217..e1650e14fe 100644 --- a/InvenTree/templates/js/translated/bom.js +++ b/InvenTree/templates/js/translated/bom.js @@ -33,6 +33,7 @@ modalSetContent, partFields, partGroups, + reloadBootstrapTable, renderLink, setupFilterList, shortenString, @@ -817,7 +818,24 @@ function loadBomTable(table, options={}) { Object.assign(filters, params); - setupFilterList('bom', $(table)); + setupFilterList('bom', $(table), '#filter-list-bom', { + custom_actions: [{ + label: 'actions', + actions: [{ + label: 'delete', + title: '{% trans "Delete items" %}', + icon: 'fa-trash-alt icon-red', + permission: 'part.change', + callback: function(data) { + deleteBomItems(data, { + success: function() { + reloadBootstrapTable('#bom-table'); + } + }); + } + }] + }] + }); function availableQuantity(row) { diff --git a/InvenTree/templates/js/translated/build.js b/InvenTree/templates/js/translated/build.js index f99603aeba..422250e7f1 100644 --- a/InvenTree/templates/js/translated/build.js +++ b/InvenTree/templates/js/translated/build.js @@ -23,7 +23,6 @@ inventreeLoad, inventreePut, launchModalForm, - linkButtonsToSelection, loadTableFilters, locationDetail, makeDeleteButton, @@ -34,6 +33,7 @@ makePartIcons, makeProgressBar, orderParts, + reloadBootstrapTable, renderDate, renderLink, setupFilterList, @@ -387,7 +387,7 @@ function createBuildOutput(build_id, options) { fields: fields, preFormContent: html, onSuccess: function(response) { - location.reload(); + reloadBootstrapTable(options.table || '#build-output-table'); }, }); @@ -995,6 +995,70 @@ function loadBuildOrderAllocationTable(table, options={}) { } +/* + * Construct a set of actions for the build output table + */ +function makeBuildOutputActions(build_info) { + + return [ + { + label: 'complete', + title: '{% trans "Complete outputs" %}', + icon: 'fa-check-circle icon-green', + permission: 'build.add', + callback: function(data) { + completeBuildOutputs( + build_info.pk, + data, + { + success: function() { + $('#build-output-table').bootstrapTable('refresh'); // Reload the "in progress" table + $('#build-stock-table').bootstrapTable('refresh'); // Reload the "completed" table + } + } + ); + }, + }, + { + label: 'scrap', + title: '{% trans "Scrap outputs" %}', + icon: 'fa-times-circle icon-red', + permission: 'build.change', + callback: function(data) { + scrapBuildOutputs( + build_info.pk, + data, + { + success: function() { + $('#build-output-table').bootstrapTable('refresh'); // Reload the "in progress" table + $('#build-stock-table').bootstrapTable('refresh'); // Reload the "completed" table + } + } + ); + }, + }, + { + label: 'delete', + title: '{% trans "Delete outputs" %}', + icon: 'fa-trash-alt icon-red', + permission: 'build.delete', + callback: function(data) { + deleteBuildOutputs( + build_info.pk, + data, + { + success: function() { + $('#build-output-table').bootstrapTable('refresh'); // Reload the "in progress" table + $('#build-stock-table').bootstrapTable('refresh'); // Reload the "completed" table + } + } + ) + }, + } + ]; +} + + /* * Display a "build output" table for a particular build. * @@ -1035,6 +1099,12 @@ function loadBuildOutputTable(build_info, options={}) { }, singular_name: '{% trans "build output" %}', plural_name: '{% trans "build outputs" %}', + custom_actions: [{ + label: 'buildoutput', + icon: 'fa-tools', + title: '{% trans "Build output actions" %}', + actions: makeBuildOutputActions(build_info), + }] }); // Request list of required tests for the part being assembled @@ -1383,25 +1453,6 @@ function loadBuildOutputTable(build_info, options={}) { ); }); - // Complete multiple outputs - $('#multi-output-complete').click(function() { - var outputs = getTableData(table); - - completeBuildOutputs( - build_info.pk, - outputs, - { - success: function() { - // Reload the "in progress" table - $('#build-output-table').bootstrapTable('refresh'); - - // Reload the "completed" table - $('#build-stock-table').bootstrapTable('refresh'); - } - } - ); - }); - // Delete multiple build outputs $('#multi-output-delete').click(function() { var outputs = getTableData(table); @@ -1421,25 +1472,6 @@ function loadBuildOutputTable(build_info, options={}) { ); }); - // Scrap multiple outputs - $('#multi-output-scrap').click(function() { - var outputs = getTableData(table); - - scrapBuildOutputs( - build_info.pk, - outputs, - { - success: function() { - // Reload the "in progress" table - $('#build-output-table').bootstrapTable('refresh'); - - // Reload the "completed" table - $('#build-stock-table').bootstrapTable('refresh'); - } - } - ); - }); - $('#outputs-expand').click(function() { $(table).bootstrapTable('expandAllRows'); }); @@ -2180,13 +2212,6 @@ function loadBuildTable(table, options) { } } }); - - linkButtonsToSelection( - table, - [ - '#build-print-options', - ] - ); } diff --git a/InvenTree/templates/js/translated/company.js b/InvenTree/templates/js/translated/company.js index 833a822c50..d49ad7465e 100644 --- a/InvenTree/templates/js/translated/company.js +++ b/InvenTree/templates/js/translated/company.js @@ -18,6 +18,7 @@ makeDeleteButton, makeEditButton, makeIconBadge, + orderParts, renderClipboard, renderDate, renderLink, @@ -1213,6 +1214,43 @@ function deleteManufacturerPartParameters(selections, options={}) { } +// Construct a set of actions for the manufacturer part table +function makeManufacturerPartActions(options={}) { + return [ + { + label: 'order', + title: '{% trans "Order parts" %}', + icon: 'fa-shopping-cart', + permission: 'purchase_order.add', + callback: function(data) { + let parts = []; + + data.forEach(function(item) { + let part = item.part_detail; + part.manufacturer_part = item.pk; + parts.push(part); + }); + + orderParts(parts); + }, + }, + { + label: 'delete', + title: '{% trans "Delete manufacturer parts" %}', + icon: 'fa-trash-alt icon-red', + permission: 'purchase_order.delete', + callback: function(data) { + deleteManufacturerParts(data, { + success: function() { + $('#manufacturer-part-table').bootstrapTable('refresh'); + } + }); + }, + } + ]; +} + + /* * Load manufacturer part table */ @@ -1226,7 +1264,18 @@ function loadManufacturerPartTable(table, url, options) { var filterTarget = options.filterTarget || '#filter-list-manufacturer-part'; - setupFilterList('manufacturer-part', $(table), filterTarget); + setupFilterList('manufacturer-part', $(table), filterTarget, { + custom_actions: [ + { + label: 'manufacturer-part', + title: '{% trans "Manufacturer part actions" %}', + icon: 'fa-tools', + actions: makeManufacturerPartActions({ + manufacturer_id: options.params.manufacturer, + }) + } + ] + }); $(table).inventreeTable({ url: url, @@ -1453,6 +1502,43 @@ function loadManufacturerPartParameterTable(table, url, options) { } +// Construct a set of actions for the supplier part table +function makeSupplierPartActions(options={}) { + return [ + { + label: 'order', + title: '{% trans "Order parts" %}', + icon: 'fa-shopping-cart', + permission: 'purchase_order.add', + callback: function(data) { + let parts = [] + + data.forEach(function(entry) { + parts.push(entry.part_detail); + }); + + orderParts(parts, { + supplier: options.supplier_id, + }); + }, + }, + { + label: 'delete', + title: '{% trans "Delete supplier parts" %}', + icon: 'fa-trash-alt icon-red', + permission: 'purchase_order.delete', + callback: function(data) { + deleteSupplierParts(data, { + success: function() { + $('#supplier-part-table').bootstrapTable('refresh'); + } + }); + }, + } + ]; +} + + /* * Load supplier part table */ @@ -1464,7 +1550,18 @@ function loadSupplierPartTable(table, url, options) { // Load filters var filters = loadTableFilters('supplierpart', params); - setupFilterList('supplierpart', $(table)); + setupFilterList('supplierpart', $(table), '#filter-list-supplier-part', { + custom_actions: [ + { + label: 'supplier-part', + title: '{% trans "Supplier part actions" %}', + icon: 'fa-tools', + actions: makeSupplierPartActions({ + supplier_id: options.params.supplier, + }), + } + ] + }); $(table).inventreeTable({ url: url, diff --git a/InvenTree/templates/js/translated/filters.js b/InvenTree/templates/js/translated/filters.js index 767796a056..201591ac5e 100644 --- a/InvenTree/templates/js/translated/filters.js +++ b/InvenTree/templates/js/translated/filters.js @@ -1,6 +1,7 @@ {% load i18n %} /* globals + checkPermission, downloadTableData, getAvailableTableFilters, getTableData, @@ -266,6 +267,102 @@ function generateFilterInput(tableKey, filterKey) { } +/* + * Construct a single action button based on the provided definition + */ +function makeFilterActionButton(button, options={}) { + let prefix = options.prefix || 'action'; + + // Check for required permission (if specified) + if (button.permission) { + if (!checkPermission(button.permission)) { + return ''; + } + } + + return ` +
  • + ${button.title} +
  • `; +} + + +/* + * Construct a set of custom actions for a given table + */ +function makeCustomActionGroup(action_group, table) { + + let buttons = []; + let label = action_group.label || 'actions'; + let title = action_group.title || '{% trans "Actions" %}'; + let icon = action_group.icon || 'fa-tools'; + + // Construct the HTML for each button + action_group.actions.forEach(function(action) { + buttons.push(makeFilterActionButton(action, {prefix: label})); + }); + + if (buttons.length == 0) { + // Don't display anything if there are no buttons to show + return ''; + } + + let html = ` +
    + +
    `; + return html; +} + + +/* + * Construct a set of custom barcode actions for a given table + * + * To define barcode actions for a data table, use options.barcode_actions + */ +function makeBarcodeActions(barcode_actions, table) { + + let html = ` +
    + +
    `; + + return html; +} + + +/* + * Add callbacks for custom actions + */ +function addFilterActionCallbacks(element, label, table, actions) { + actions.forEach(function(action) { + let id = `action-${label}-${action.label}`; + element.find(`#${id}`).click(function() { + let data = getTableData(table); + action.callback(data); + }); + }); +} + + /* * Helper function to make a 'filter' style button */ @@ -315,6 +412,19 @@ function setupFilterList(tableKey, table, target, options={}) { let report_button = options.report && global_settings.REPORT_ENABLE; let labels_button = options.labels && global_settings.LABEL_ENABLE; + let barcode_actions = options.barcode_actions && global_settings.BARCODE_ENABLE; + + // Add in "custom" actions first (to the left of the table buttons) + if (options.custom_actions) { + options.custom_actions.forEach(function(action_group) { + buttons += makeCustomActionGroup(action_group, table); + }); + } + + // Add in button for custom barcode actions + if (barcode_actions) { + buttons += makeBarcodeActions(options.barcode_actions, table); + } if (report_button || labels_button) { let print_buttons = ` @@ -394,6 +504,19 @@ function setupFilterList(tableKey, table, target, options={}) { element.append(filter_tag); } + // Callback for custom actions + if (options.custom_actions) { + options.custom_actions.forEach(function(action_group) { + let label = action_group.label || 'actions'; + addFilterActionCallbacks(element, label, table, action_group.actions); + }); + } + + // Callback for barcode actions + if (barcode_actions) { + addFilterActionCallbacks(element, 'barcode', table, options.barcode_actions); + } + // Callback for printing reports if (options.report && global_settings.REPORT_ENABLE) { element.find(`#print-report-${tableKey}`).click(function() { diff --git a/InvenTree/templates/js/translated/part.js b/InvenTree/templates/js/translated/part.js index 20834d272d..8545f4e5f6 100644 --- a/InvenTree/templates/js/translated/part.js +++ b/InvenTree/templates/js/translated/part.js @@ -23,7 +23,6 @@ inventreeLoad, inventreePut, inventreeSave, - linkButtonsToSelection, loadTableFilters, makeDeleteButton, makeEditButton, @@ -2159,6 +2158,69 @@ function partGridTile(part) { } +/* + * Update the category for a set of parts + */ +function setPartCategory(data, options={}) { + + let parts = []; + + data.forEach(function(item) { + parts.push(item.pk); + }); + + var html = ` +
    + {% trans "Set the part category for the selected parts" %} +
    + `; + + constructForm('{% url "api-part-change-category" %}',{ + title: '{% trans "Set Part Category" %}', + method: 'POST', + preFormContent: html, + fields: { + category: {}, + }, + processBeforeUpload: function(data) { + data.parts = parts; + return data; + }, + onSuccess: function() { + $(options.table).bootstrapTable('refresh'); + } + }); +} + + +/* + * Construct a set of custom actions for the part table + */ +function makePartActions(table) { + + return [ + { + label: 'set-category', + title: '{% trans "Set category" %}', + icon: 'fa-sitemap', + permission: 'part.change', + callback: function(data) { + setPartCategory(data, {table: table}); + } + }, + { + label: 'order', + title: '{% trans "Order parts" %}', + icon: 'fa-shopping-cart', + permission: 'purchase_order.add', + callback: function(data) { + orderParts(data); + }, + } + ] +} + + /* Load part listing data into specified table. * * Args: @@ -2190,6 +2252,14 @@ function loadPartTable(table, url, options={}) { }, singular_name: '{% trans "part" %}', plural_name: '{% trans "parts" %}', + custom_actions: [ + { + label: 'parts', + icon: 'fa-tools', + title: '{% trans "Part actions" %}', + actions: makePartActions(table), + } + ] }); var columns = [ @@ -2438,97 +2508,6 @@ function loadPartTable(table, url, options={}) { return html; } }); - - if (options.buttons) { - linkButtonsToSelection($(table), options.buttons); - } - - /* Button callbacks for part table buttons */ - - // Callback function for the "order parts" button - $('#multi-part-order').click(function() { - var selections = getTableData(table); - - var parts = []; - - selections.forEach(function(part) { - parts.push(part); - }); - - orderParts(parts, {}); - }); - - // Callback function for the "set category" button - $('#multi-part-category').click(function() { - var selections = getTableData(table); - var parts = []; - - selections.forEach(function(item) { - parts.push(item.pk); - }); - - var html = ` -
    - {% trans "Set the part category for the selected parts" %} -
    - `; - - constructFormBody({}, { - title: '{% trans "Set Part Category" %}', - preFormContent: html, - fields: { - category: { - label: '{% trans "Category" %}', - help_text: '{% trans "Select Part Category" %}', - required: true, - type: 'related field', - model: 'partcategory', - api_url: '{% url "api-part-category-list" %}', - } - }, - onSubmit: function(fields, opts) { - var category = getFormFieldValue('category', fields['category'], opts); - - if (category == null) { - handleFormErrors( - { - 'category': ['{% trans "Category is required" %}'] - }, - opts.fields, - opts - ); - return; - } - - // Set the category for each part in sequence - function setCategory() { - if (parts.length > 0) { - var part = parts.shift(); - - inventreePut( - `{% url "api-part-list" %}${part}/`, - { - category: category, - }, - { - method: 'PATCH', - complete: setCategory, - } - ); - } else { - // We are done! - $(opts.modal).modal('hide'); - - $(table).bootstrapTable('refresh'); - } - } - - // Start the ball rolling - showModalSpinner(opts.modal); - setCategory(); - }, - }); - }); } diff --git a/InvenTree/templates/js/translated/purchase_order.js b/InvenTree/templates/js/translated/purchase_order.js index 25b46dbd9a..a27c8e12d1 100644 --- a/InvenTree/templates/js/translated/purchase_order.js +++ b/InvenTree/templates/js/translated/purchase_order.js @@ -31,7 +31,6 @@ inventreeLoad, inventreePut, launchModalForm, - linkButtonsToSelection, loadTableFilters, makeCopyButton, makeDeleteButton, @@ -606,7 +605,7 @@ function newSupplierPartFromOrderWizard(e) { /* * Create a new form to order parts based on the list of provided parts. */ -function orderParts(parts_list, options) { +function orderParts(parts_list, options={}) { var parts = []; @@ -786,7 +785,7 @@ function orderParts(parts_list, options) { supplier_part_filters.manufacturer_part = options.manufacturer_part; } - // Construct API filtres for the PurchaseOrder field + // Construct API filters for the PurchaseOrder field var order_filters = { status: {{ PurchaseOrderStatus.PENDING }}, supplier_detail: true, @@ -822,6 +821,10 @@ function orderParts(parts_list, options) { $(opts.modal).find(`#info-pack-size-${pk}`).remove(); + if (typeof value === 'object') { + value = value.pk; + } + if (value != null) { inventreeGet( `/api/company/part/${value}/`, @@ -1866,14 +1869,9 @@ function loadPurchaseOrderLineItemTable(table, options={}) { var filters = loadTableFilters('purchaseorderlineitem', options.params); - setupFilterList( - 'purchaseorderlineitem', - $(table), - options.filter_target || '#filter-list-purchase-order-lines', - { - download: true - } - ); + setupFilterList('purchaseorderlineitem', $(table), options.filter_target || '#filter-list-purchase-order-lines', { + download: true, + }); function setupCallbacks() { if (options.allow_edit) { @@ -2211,12 +2209,4 @@ function loadPurchaseOrderLineItemTable(table, options={}) { } ] }); - - linkButtonsToSelection( - table, - [ - '#multi-select-options', - ] - ); - } diff --git a/InvenTree/templates/js/translated/search.js b/InvenTree/templates/js/translated/search.js index 26d8caadc4..23882295cc 100644 --- a/InvenTree/templates/js/translated/search.js +++ b/InvenTree/templates/js/translated/search.js @@ -1,6 +1,7 @@ {% load i18n %} /* globals + checkPermission, getModelRenderer, inventreeGet, inventreePut, @@ -22,40 +23,6 @@ function closeSearchPanel() { } -// Keep track of the roles / permissions available to the current user -var search_user_roles = null; - - -/* - * Check if the user has the specified role and permission - */ -function checkPermission(role, permission='view') { - - if (!search_user_roles) { - return false; - } - - if (!(role in search_user_roles)) { - return false; - } - - var roles = search_user_roles[role]; - - if (!roles) { - return false; - } - - var found = false; - - search_user_roles[role].forEach(function(p) { - if (String(p).valueOf() == String(permission).valueOf()) { - found = true; - } - }); - - return found; -} - /* * Callback when the search panel is opened. @@ -71,15 +38,6 @@ function openSearchPanel() { clearSearchResults(); - // Request user roles if we do not have them - if (search_user_roles == null) { - inventreeGet('{% url "api-user-roles" %}', {}, { - success: function(response) { - search_user_roles = response.roles || {}; - } - }); - } - // Callback for text input changed search_input.on('keyup change', searchTextChanged); diff --git a/InvenTree/templates/js/translated/stock.js b/InvenTree/templates/js/translated/stock.js index d7df093097..a29bd7bcb3 100644 --- a/InvenTree/templates/js/translated/stock.js +++ b/InvenTree/templates/js/translated/stock.js @@ -30,7 +30,6 @@ inventreePut, inventreeSave, launchModalForm, - linkButtonsToSelection, loadTableFilters, makeDeleteButton, makeEditButton, @@ -1707,6 +1706,121 @@ function locationDetail(row, showLink=true) { } +/* + * Construct a set of custom actions for the stock table + */ +function makeStockActions(table) { + let actions = [ + { + label: 'add', + icon: 'fa-plus-circle icon-green', + title: '{% trans "Add stock" %}', + permission: 'stock.change', + callback: function(data) { + stockAdjustment('add', data, table); + } + }, + { + label: 'remove', + icon: 'fa-minus-circle icon-red', + title: '{% trans "Remove stock" %}', + permission: 'stock.change', + callback: function(data) { + stockAdjustment('take', data, table); + }, + }, + { + label: 'stocktake', + icon: 'fa-check-circle icon-blue', + title: '{% trans "Count stock" %}', + permission: 'stock.change', + callback: function(data) { + stockAdjustment('count', data, table); + }, + }, + { + label: 'move', + icon: 'fa-exchange-alt icon-blue', + title: '{% trans "Transfer stock" %}', + permission: 'stock.change', + callback: function(data) { + stockAdjustment('move', data, table); + } + }, + { + label: 'status', + icon: 'fa-info-circle icon-blue', + title: '{% trans "Change stock status" %}', + permission: 'stock.change', + callback: function(data) { + setStockStatus(data, {table: table}); + }, + }, + { + label: 'merge', + icon: 'fa-object-group', + title: '{% trans "Merge stock" %}', + permission: 'stock.change', + callback: function(data) { + mergeStockItems(data, { + success: function(response) { + $(table).bootstrapTable('refresh'); + + showMessage('{% trans "Merged stock items" %}', { + style: 'success', + }); + } + }); + }, + }, + { + label: 'order', + icon: 'fa-shopping-cart', + title: '{% trans "Order stock" %}', + permission: 'stock.change', + callback: function(data) { + let parts = []; + + data.forEach(function(item) { + var part = item.part_detail; + + if (part) { + parts.push(part); + } + }); + + orderParts(parts, {}); + }, + }, + { + label: 'assign', + icon: 'fa-user-tie', + title: '{% trans "Assign to customer" %}', + permission: 'stock.change', + callback: function(data) { + assignStockToCustomer(data, { + success: function() { + $(table).bootstrapTable('refresh'); + } + }); + }, + }, + { + label: 'delete', + icon: 'fa-trash-alt icon-red', + title: '{% trans "Delete stock" %}', + permission: 'stock.delete', + callback: function(data) { + stockAdjustment('delete', data, table); + }, + } + ]; + + return actions; + +} + + /* Load data into a stock table with adjustable options. * Fetches data (via AJAX) and loads into a bootstrap table. * Also links in default button callbacks. @@ -1746,7 +1860,26 @@ function loadStockTable(table, options) { key: 'item', }, singular_name: '{% trans "stock item" %}', - plural_name: '{% trans "stock items" %}' + plural_name: '{% trans "stock items" %}', + barcode_actions: [ + { + icon: 'fa-sitemap', + label: 'scantolocation', + title: '{% trans "Scan to location" %}', + permission: 'stock.change', + callback: function(items) { + scanItemsIntoLocation(items); + } + } + ], + custom_actions: [ + { + actions: makeStockActions(table), + icon: 'fa-boxes', + title: '{% trans "Stock Actions" %}', + label: 'stock', + } + ] }); // Override the default values, or add new ones @@ -2267,114 +2400,6 @@ function loadStockTable(table, options) { buttons.push('#stock-barcode-options'); } - linkButtonsToSelection( - table, - buttons, - ); - - function stockAdjustment(action) { - var items = getTableData(table); - - adjustStock(action, items, { - success: function() { - $(table).bootstrapTable('refresh'); - } - }); - } - - // Automatically link button callbacks - if (global_settings.BARCODE_ENABLE) { - $('#multi-item-barcode-scan-into-location').click(function() { - var selections = getTableData(table); - - var items = []; - - selections.forEach(function(item) { - items.push(item); - }); - - scanItemsIntoLocation(items); - }); - } - - // Callback for 'stocktake' button - $('#multi-item-stocktake').click(function() { - stockAdjustment('count'); - }); - - // Callback for 'remove stock' button - $('#multi-item-remove').click(function() { - stockAdjustment('take'); - }); - - // Callback for 'add stock' button - $('#multi-item-add').click(function() { - stockAdjustment('add'); - }); - - // Callback for 'move stock' button - $('#multi-item-move').click(function() { - stockAdjustment('move'); - }); - - // Callback for 'merge stock' button - $('#multi-item-merge').click(function() { - var items = getTableData(table); - - mergeStockItems(items, { - success: function(response) { - $(table).bootstrapTable('refresh'); - - showMessage('{% trans "Merged stock items" %}', { - style: 'success', - }); - } - }); - }); - - // Callback for 'assign stock' button - $('#multi-item-assign').click(function() { - - var items = getTableData(table); - - assignStockToCustomer(items, { - success: function() { - $(table).bootstrapTable('refresh'); - } - }); - }); - - // Callback for 'un-assign stock' button - $('#multi-item-order').click(function() { - - var selections = getTableData(table); - - var parts = []; - - selections.forEach(function(item) { - var part = item.part_detail; - - if (part) { - parts.push(part); - } - }); - - orderParts(parts, {}); - }); - - // Callback for 'delete stock' button - $('#multi-item-delete').click(function() { - var selections = getTableData(table); - - var stock = []; - - selections.forEach(function(item) { - stock.push(item.pk); - }); - - stockAdjustment('delete'); - }); - // Callback for 'change status' button $('#multi-item-status').click(function() { let selections = getTableData(table); @@ -2384,35 +2409,9 @@ function loadStockTable(table, options) { items.push(item.pk); }); - if (items.length == 0) { - showAlertDialog( - '{% trans "Select Stock Items" %}', - '{% trans "Select one or more stock items" %}' - ); - return; - } - let html = ` -
    0); - - // Add a callback - table.on('check.bs.table uncheck.bs.table check-some.bs.table uncheck-some.bs.table check-all.bs.table uncheck-all.bs.table', function() { - enableButtons(buttons, table.bootstrapTable('getSelections').length > 0); - }); -} - - /** * Returns true if the input looks like a valid number * @param {String} n @@ -474,11 +455,6 @@ $.fn.inventreeTable = function(options) { console.error(`Could not get list of visible columns for table '${tableName}'`); } } - - // Optionally, link buttons to the table selection - if (options.buttons) { - linkButtonsToSelection(table, options.buttons); - } }; diff --git a/InvenTree/templates/stock_table.html b/InvenTree/templates/stock_table.html index bb8e074efc..d8fa1aeac6 100644 --- a/InvenTree/templates/stock_table.html +++ b/InvenTree/templates/stock_table.html @@ -9,46 +9,7 @@ {% endif %}
    -
    -
    - {% if barcodes %} - - - {% endif %} - {% if not read_only %} - {% if roles.stock.change or roles.stock.delete %} - - {% endif %} - {% endif %} - {% include "filter_list.html" with prefix=prefix id="stock" %} -
    -
    + {% include "filter_list.html" with prefix=prefix id="stock" %}