mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 04:55:44 +00:00
Table custom buttons (#5075)
* Add generic implementation for barcode actions - Commonize code against tables - Cleaner UI - Better code - Will make future react refactor easier * Add permissions.js - Separate .js file for dynamically checking permissions * Update stock table to use client-side actions * API endpoint for bulk category adjustment * Bug fix for purchase_order.js - Prevent some really strange API calls * Refactor actions for part table - Now done dynamically * Refactor actions for the attachment tables * Refactor actions for build output table * Increment API version * Cleanup janky button * Refactor supplier part table * Refactor manufacturer part table * Remove linkButtonsToSelection - no longer needed - Cleanup, yay! * Cleanup purchase order line table * Refactor BOM table buttons * JS linting * Further cleanup * Template cleanup - remove extra div elements * js linting * js fix
This commit is contained in:
@ -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'),
|
||||
]
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -23,20 +23,7 @@
|
||||
{% endif %}
|
||||
|
||||
<div id='bom-button-toolbar'>
|
||||
<div class="btn-group" role="group" aria-label="...">
|
||||
{% if roles.part.change %}
|
||||
<!-- Action menu -->
|
||||
<div class='btn-group'>
|
||||
<button id='bom-actions' title='{% trans "BOM actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
|
||||
<span class='fas fa-tools'></span> <span class='caret'></span>
|
||||
</button>
|
||||
<ul class='dropdown-menu' role='menu'>
|
||||
<li><a class='dropdown-item' href='#' id='bom-item-delete'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete Items" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "filter_list.html" with id="bom" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="bom" %}
|
||||
</div>
|
||||
|
||||
<table class='table table-bom table-condensed' data-toolbar="#bom-button-toolbar" id='bom-table'>
|
||||
|
@ -169,24 +169,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id='part-button-toolbar'>
|
||||
<div class='btn-group' role='group'>
|
||||
<div class='btn-group' role='group'>
|
||||
<button id='part-options' class='btn btn-primary dropdown-toggle' type='button' data-bs-toggle="dropdown">
|
||||
<span class='fas fa-tools' title='{% trans "Options" %}'></span>
|
||||
</button>
|
||||
<ul class='dropdown-menu'>
|
||||
{% if roles.part.change %}
|
||||
<li><a class='dropdown-item' href='#' id='multi-part-category' title='{% trans "Set category" %}'>
|
||||
<span class='fas fa-sitemap'></span> {% trans "Set Category" %}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
<li><a class='dropdown-item' href='#' id='multi-part-order' title='{% trans "Order parts" %}'>
|
||||
<span class='fas fa-shopping-cart'></span> {% trans "Order Parts" %}
|
||||
</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% include "filter_list.html" with id="parts" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="parts" %}
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<table class='table table-striped table-condensed' data-toolbar='#part-button-toolbar' id='part-table'>
|
||||
@ -209,9 +192,7 @@
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='param-button-toolbar'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="parameters" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="parameters" %}
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed' data-toolbar='#param-button-toolbar' id='parametric-part-table'>
|
||||
@ -235,9 +216,7 @@
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='subcategory-button-toolbar'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="category" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="category" %}
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed' id='subcategory-table' data-toolbar='#subcategory-button-toolbar'></table>
|
||||
|
@ -93,9 +93,7 @@
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='test-button-toolbar'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="parttests" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="parttests" %}
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed' data-toolbar='#test-button-toolbar' id='test-template-table'></table>
|
||||
@ -116,9 +114,7 @@
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='po-button-bar'>
|
||||
<div class='button-toolbar container-fluid' style='float: right;'>
|
||||
{% include "filter_list.html" with id="partpurchaseorders" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="partpurchaseorders" %}
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed po-table' id='purchase-order-table' data-toolbar='#po-button-bar'>
|
||||
@ -132,9 +128,7 @@
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='so-button-bar'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="salesorder" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="salesorder" %}
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed po-table' id='sales-order-table' data-toolbar='#so-button-bar'>
|
||||
@ -145,11 +139,8 @@
|
||||
<h4>{% trans "Sales Order Allocations" %}</h4>
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
|
||||
<div id='sales-order-allocation-button-toolbar'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="salesorderallocation" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="salesorderallocation" %}
|
||||
</div>
|
||||
<table class='table table-striped table-condensed' id='sales-order-allocation-table' data-toolbar='#sales-order-allocation-button-toolbar'></table>
|
||||
</div>
|
||||
@ -190,11 +181,7 @@
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='variant-button-toolbar'>
|
||||
<div class='button-toolbar container-fluid'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="variants" %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "filter_list.html" with id="variants" %}
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed' id='variants-table' data-toolbar='#variant-button-toolbar'>
|
||||
@ -218,11 +205,7 @@
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='param-button-toolbar'>
|
||||
<div class='button-toolbar container-fluid'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="parameters" %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "filter_list.html" with id="parameters" %}
|
||||
</div>
|
||||
<table id='parameter-table' class='table table-condensed table-striped' data-toolbar="#param-button-toolbar"></table>
|
||||
</div>
|
||||
@ -259,9 +242,7 @@
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='related-button-toolbar'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="related" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="related" %}
|
||||
</div>
|
||||
|
||||
<table id='related-parts-table' class='table table-striped table-condensed' data-toolbar='#related-button-toolbar'></table>
|
||||
@ -317,9 +298,7 @@
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='assembly-button-toolbar'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="usedin" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="usedin" %}
|
||||
</div>
|
||||
|
||||
<table class="table table-striped table-condensed" id='used-table' data-toolbar='#assembly-button-toolbar'>
|
||||
@ -346,9 +325,7 @@
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='build-button-toolbar'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="build" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="build" %}
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed' data-toolbar='#build-button-toolbar' id='build-table'>
|
||||
@ -362,9 +339,7 @@
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='build-allocation-button-toolbar'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="buildorderallocation" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="buildorderallocation" %}
|
||||
</div>
|
||||
<table class='table table-striped table-condensed' id='build-order-allocation-table' data-toolbar='#build-allocation-button-toolbar'></table>
|
||||
</div>
|
||||
@ -385,17 +360,7 @@
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='supplier-button-toolbar'>
|
||||
<div class='btn-group'>
|
||||
<div id='opt-dropdown' class="btn-group">
|
||||
<button id='supplier-part-options' class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown">{% trans "Options" %} <span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class='dropdown-item' href='#' id='supplier-part-delete' title='{% trans "Delete supplier parts" %}'>
|
||||
<span class='fas fa-trash-alt icon-red'></span> {% trans "Delete" %}
|
||||
</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% include "filter_list.html" with id="supplier-part" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="supplier-part" %}
|
||||
</div>
|
||||
|
||||
<table class="table table-striped table-condensed" id='supplier-part-table' data-toolbar='#supplier-button-toolbar'>
|
||||
@ -416,15 +381,7 @@
|
||||
<div class='panel-content'>
|
||||
<div class='panel-content'>
|
||||
<div id='manufacturer-button-toolbar'>
|
||||
<div class='btn-group'>
|
||||
<div id='opt-dropdown' class="btn-group">
|
||||
<button id='manufacturer-part-options' class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown">{% trans "Options" %} <span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class='dropdown-item' href='#' id='manufacturer-part-delete' title='{% trans "Delete manufacturer parts" %}'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete" %}</a></li>
|
||||
</ul>
|
||||
{% include "filter_list.html" with id="manufacturer-part" %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "filter_list.html" with id="manufacturer-part" %}
|
||||
</div>
|
||||
<table class='table table-condensed table-striped' id='manufacturer-part-table' data-toolbar='#manufacturer-button-toolbar'></table>
|
||||
</div>
|
||||
@ -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
|
||||
|
Reference in New Issue
Block a user