diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index aeef72da70..bf979bfbb5 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -1254,25 +1254,32 @@ class Part(MPTTModel): return self.get_descendants(include_self=False) - def fetch_related_parts(self): - """ Return all related parts """ + def get_related_parts(self): + """ Return list of tuples for all related parts: + - first value is PartRelated object + - second value is matching Part object + """ related_parts = [] - parts_1 = self.related_parts_1.filter(part_1__id=self.pk) + related_parts_1 = self.related_parts_1.filter(part_1__id=self.pk) - parts_2 = self.related_parts_2.filter(part_2__id=self.pk) + related_parts_2 = self.related_parts_2.filter(part_2__id=self.pk) - for part in parts_1: - # Append - related_parts.append(part.part_2) + for related_part in related_parts_1: + # Add to related parts list + related_parts.append((related_part, related_part.part_2)) - for part in parts_2: - # Append - related_parts.append(part.part_1) + for related_part in related_parts_2: + # Add to related parts list + related_parts.append((related_part, related_part.part_1)) return related_parts + @property + def related_count(self): + return len(self.get_related_parts()) + def attach_file(instance, filename): """ Function for storing a file for a PartAttachment @@ -1783,7 +1790,8 @@ class PartRelated(models.Model): @classmethod def create(cls, part_1, part_2): - ''' Create PartRelated object ''' + ''' Create PartRelated object and relationship between two parts ''' + related_part = cls() related_part.create_relationship(part_1, part_2) return related_part diff --git a/InvenTree/part/templates/part/related.html b/InvenTree/part/templates/part/related.html index f9175ed590..8fd167b592 100644 --- a/InvenTree/part/templates/part/related.html +++ b/InvenTree/part/templates/part/related.html @@ -18,7 +18,32 @@ - + + + + + + + + {% for item in part.get_related_parts %} + {% with part_related=item.0 part=item.1 %} + + + + {% endwith %} + {% endfor %} + @@ -27,20 +52,24 @@ {% block js_ready %} {{ block.super }} -loadPartRelatedTable($("#table-related-part"), { - url: "{% url 'api-part-list' %}", - params: { - part: {{ part.id }}, - }, -}); - -$("#add-related-part").click(function() { - launchModalForm("{% url 'part-related-create' %}", { - data: { - part: {{ part.id }}, - }, - reload: true, + $('#table-related-part').inventreeTable({ + }); + + $("#add-related-part").click(function() { + launchModalForm("{% url 'part-related-create' %}", { + data: { + part: {{ part.id }}, + }, + reload: true, + }); + }); + + $('.delete-related-part').click(function() { + var button = $(this); + + launchModalForm(button.attr('url'), { + reload: true, + }); }); -}); {% endblock %} \ No newline at end of file diff --git a/InvenTree/part/templates/part/tabs.html b/InvenTree/part/templates/part/tabs.html index c96de9b825..8bfaba4d89 100644 --- a/InvenTree/part/templates/part/tabs.html +++ b/InvenTree/part/templates/part/tabs.html @@ -64,7 +64,7 @@ {% endif %} - {% trans "Related Parts" %} {% if part.related_count > 0 %}{{ part.related_count }}{% endif %} + {% trans "Related" %} {% if part.related_count > 0 %}{{ part.related_count }}{% endif %} {% trans "Attachments" %} {% if part.attachment_count > 0 %}{{ part.attachment_count }}{% endif %} diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index f49d57bbe3..6ae782ab2c 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -81,10 +81,8 @@ class PartRelatedCreate(AjaxCreateView): ajax_template_name = "modal_form.html" role_required = 'part.change' - # TODO: QuerySet should not show parts already related to object - def get_initial(self): - """ Point part_1 to parent part """ + """ Set parent part as part_1 field """ initials = {} @@ -102,12 +100,30 @@ class PartRelatedCreate(AjaxCreateView): """ 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 + updated_choices = [] + for choice in form.fields["part_2"].choices: + if choice[0] not in related_parts: + updated_choices.append(choice) + + # Update choices for related part + form.fields['part_2'].choices = updated_choices + except KeyError: + pass + return form def post_save(self): @@ -116,14 +132,9 @@ class PartRelatedCreate(AjaxCreateView): form = self.get_form() if form.is_valid(): - print('form is valid!') - part_1 = form.cleaned_data['part_1'] part_2 = form.cleaned_data['part_2'] - print(f'{part_1=}') - print(f'{part_2=}') - PartRelated.create(part_1, part_2) @@ -132,7 +143,6 @@ class PartRelatedDelete(AjaxDeleteView): model = PartRelated ajax_form_title = _("Delete Related Part") - ajax_template_name = "related_delete.html" context_object_name = "related" role_required = 'part.change' diff --git a/InvenTree/templates/js/part.html b/InvenTree/templates/js/part.html index fbe1c945e0..e5fafef070 100644 --- a/InvenTree/templates/js/part.html +++ b/InvenTree/templates/js/part.html @@ -163,41 +163,6 @@ function loadSimplePartTable(table, url, options={}) { } -function loadPartRelatedTable(table, options={}) { - /* Load related parts table */ - - var columns = [ - { - field: options.params['part'], - title: '{% trans 'Part' %}', - sortable: true, - sortName: 'name', - formatter: function(value, row, index, field) { - - var name = ''; - - if (row.IPN) { - name += row.IPN + ' | ' + row.name; - } else { - name += row.name; - } - - return renderLink(name, '/part/' + row.pk + '/'); - } - } - ]; - - $(table).inventreeTable({ - sortName: 'part', - groupBy: false, - name: options.name || 'related_parts', - formatNoMatches: function() { return "{% trans "No parts found" %}"; }, - columns: columns, - showColumns: true, - }); -} - - function loadParametricPartTable(table, options={}) { /* Load parametric table for part parameters *