diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index ddcb78ac2a..9e3d82fe84 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -157,20 +157,6 @@ class BomMatchItemForm(MatchItemForm): return super().get_special_field(col_guess, row, file_manager) -class CreatePartRelatedForm(HelperForm): - """ Form for creating a PartRelated object """ - - class Meta: - model = PartRelated - fields = [ - 'part_1', - 'part_2', - ] - labels = { - 'part_2': _('Related Part'), - } - - class SetPartCategoryForm(forms.Form): """ Form for setting the category of multiple Part objects """ diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html index 0e356fa061..57d59a2942 100644 --- a/InvenTree/part/templates/part/detail.html +++ b/InvenTree/part/templates/part/detail.html @@ -759,11 +759,25 @@ ); $("#add-related-part").click(function() { - launchModalForm("{% url 'part-related-create' %}", { - data: { - part: {{ part.id }}, + + constructForm('{% url "api-part-related-list" %}', { + method: 'POST', + fields: { + part_1: { + hidden: true, + value: {{ part.pk }}, + }, + part_2: { + label: '{% trans "Related Part" %}', + filters: { + exclude_related: {{ part.pk }}, + } + } }, - reload: true, + title: '{% trans "Add Related Part" %}', + onSuccess: function() { + $('#related-parts-table').bootstrapTable('refresh'); + } }); }); diff --git a/InvenTree/part/test_views.py b/InvenTree/part/test_views.py index 2995d45811..f5392e1f75 100644 --- a/InvenTree/part/test_views.py +++ b/InvenTree/part/test_views.py @@ -145,36 +145,6 @@ class PartDetailTest(PartViewTestCase): self.assertIn('streaming_content', dir(response)) -class PartRelatedTests(PartViewTestCase): - - def test_valid_create(self): - """ test creation of a related part """ - - # Test GET view - response = self.client.get(reverse('part-related-create'), {'part': 1}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertEqual(response.status_code, 200) - - # Test POST view with valid form data - response = self.client.post(reverse('part-related-create'), {'part_1': 1, 'part_2': 2}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertContains(response, '"form_valid": true', status_code=200) - - # Try to create the same relationship with part_1 and part_2 pks reversed - response = self.client.post(reverse('part-related-create'), {'part_1': 2, 'part_2': 1}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertContains(response, '"form_valid": false', status_code=200) - - # Try to create part related to itself - response = self.client.post(reverse('part-related-create'), {'part_1': 1, 'part_2': 1}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertContains(response, '"form_valid": false', status_code=200) - - # Check final count - n = PartRelated.objects.all().count() - self.assertEqual(n, 1) - - class PartQRTest(PartViewTestCase): """ Tests for the Part QR Code AJAX view """ diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index 46f8094fff..e5907e15e2 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -12,10 +12,6 @@ from django.conf.urls import url, include from . import views -part_related_urls = [ - url(r'^new/?', views.PartRelatedCreate.as_view(), name='part-related-create'), - url(r'^(?P\d+)/delete/?', views.PartRelatedDelete.as_view(), name='part-related-delete'), -] sale_price_break_urls = [ url(r'^new/', views.PartSalePriceBreakCreate.as_view(), name='sale-price-break-create'), @@ -96,9 +92,6 @@ part_urls = [ # Part category url(r'^category/', include(category_urls)), - # Part related - url(r'^related-parts/', include(part_related_urls)), - # Part price breaks url(r'^sale-price/', include(sale_price_break_urls)), diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 62433084a6..cd9ea6b41a 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -30,7 +30,7 @@ import io from rapidfuzz import fuzz from decimal import Decimal, InvalidOperation -from .models import PartCategory, Part, PartRelated +from .models import PartCategory, Part from .models import PartParameterTemplate from .models import PartCategoryParameterTemplate from .models import BomItem @@ -85,75 +85,6 @@ class PartIndex(InvenTreeRoleMixin, ListView): return context -class PartRelatedCreate(AjaxCreateView): - """ View for creating a new PartRelated object - - - The view only makes sense if a Part object is passed to it - """ - model = PartRelated - form_class = part_forms.CreatePartRelatedForm - ajax_form_title = _("Add Related Part") - ajax_template_name = "modal_form.html" - - def get_initial(self): - """ Set parent part as part_1 field """ - - initials = {} - - part_id = self.request.GET.get('part', None) - - if part_id: - try: - initials['part_1'] = Part.objects.get(pk=part_id) - except (Part.DoesNotExist, ValueError): - pass - - return initials - - def get_form(self): - """ Create a form to upload a new PartRelated - - - Hide the 'part_1' field (parent part) - - Display parts which are not yet related - """ - - form = super(AjaxCreateView, self).get_form() - - form.fields['part_1'].widget = HiddenInput() - - try: - # Get parent part - parent_part = self.get_initial()['part_1'] - # Get existing related parts - related_parts = [related_part[1].pk for related_part in parent_part.get_related_parts()] - - # Build updated choice list excluding - # - parts already related to parent part - # - the parent part itself - updated_choices = [] - for choice in form.fields["part_2"].choices: - if (choice[0] not in related_parts) and (choice[0] != parent_part.pk): - updated_choices.append(choice) - - # Update choices for related part - form.fields['part_2'].choices = updated_choices - except KeyError: - pass - - return form - - -class PartRelatedDelete(AjaxDeleteView): - """ View for deleting a PartRelated object """ - - model = PartRelated - ajax_form_title = _("Delete Related Part") - context_object_name = "related" - - # Explicit role requirement - role_required = 'part.change' - - class PartSetCategory(AjaxUpdateView): """ View for settings the part category for multiple parts at once """