Added related parts urls, views, form and templates

Adding related part relationships work but are still not shown in the part detail page
This commit is contained in:
eeintech 2020-10-15 16:58:39 -05:00
parent 3d9223c2ee
commit 8579abb9c2
7 changed files with 204 additions and 12 deletions

View File

@ -13,7 +13,7 @@ from mptt.fields import TreeNodeChoiceField
from django import forms
from django.utils.translation import ugettext as _
from .models import Part, PartCategory, PartAttachment
from .models import Part, PartCategory, PartAttachment, PartRelated
from .models import BomItem
from .models import PartParameterTemplate, PartParameter
from .models import PartTestTemplate
@ -104,6 +104,25 @@ class BomUploadSelectFile(HelperForm):
]
class CreatePartRelatedForm(HelperForm):
""" Form for creating a PartRelated object """
class Meta:
model = PartRelated
fields = [
'part_1',
'part_2',
]
labels = {
'part_2': _('Related Part'),
}
def save(self):
""" Disable model saving """
return super(CreatePartRelatedForm, self).save(commit=False)
class EditPartAttachmentForm(HelperForm):
""" Form for editing a PartAttachment object """

View File

@ -1264,11 +1264,11 @@ class Part(MPTTModel):
parts_2 = self.related_parts_2.filter(part_2__id=self.pk)
for part in parts_1:
# Append
# Append
related_parts.append(part.part_2)
for part in parts_2:
# Append
# Append
related_parts.append(part.part_1)
return related_parts
@ -1749,28 +1749,41 @@ class PartRelated(models.Model):
part_1 = models.ForeignKey(Part, related_name='related_parts_1', on_delete=models.DO_NOTHING)
part_2 = models.ForeignKey(Part, related_name='related_parts_2', on_delete=models.DO_NOTHING)
part_2 = models.ForeignKey(Part, related_name='related_parts_2', on_delete=models.DO_NOTHING,
help_text=_('Choose Related Part'))
def __str__(self):
return f'{self.part_1} <-> {self.part_2}'
def create_relationship(self, part_1, part_2):
''' Create relationship between two parts '''
validate = True
parts = Part.objects.all()
related_parts = PartRelated.objects.all()
# Check if part exist
# Check if part exist and there are not the same part
if (part_1 in parts and part_2 in parts) and (part_1 is not part_2):
# Check if relation exists already
for relation in related_parts:
if (part_1 == relation.part_1 and part_2 == relation.part_2) \
or (part_1 == relation.part_2 and part_2 == relation.part_1):
validate = False
else:
validate = False
if validate:
# Add relationship
self.part_1 = part_1
self.part_2 = part_2
self.save()
return True
else:
return False
return validate
@classmethod
def create(cls, part_1, part_2):
''' Create PartRelated object '''
related_part = cls()
related_part.create_relationship(part_1, part_2)
return related_part
def __str__(self):
return f'{part_1} <-> {part_2}'

View File

@ -0,0 +1,46 @@
{% extends "part/part_base.html" %}
{% load static %}
{% load i18n %}
{% block details %}
{% include 'part/tabs.html' with tab='related-parts' %}
<h4>{% trans "Related Parts" %}</h4>
<hr>
<div id='button-bar'>
<div class='button-toolbar container-fluid' style='float: left;'>
<button class='btn btn-primary' type='button' id='add-related-part' title='{% trans "Add Related" %}'>{% trans "Add Related" %}</button>
<div class='filter-list' id='filter-list-related'>
<!-- An empty div in which the filter list will be constructed -->
</div>
</div>
</div>
<table class='table table-striped table-condensed related-table' id='table-related-part' data-toolbar='#button-bar'>
</table>
{% endblock %}
{% 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,
});
});
{% endblock %}

View File

@ -63,6 +63,9 @@
</a>
</li>
{% endif %}
<li{% ifequal tab 'related-parts' %} class="active"{% endifequal %}>
<a href="{% url 'part-related' part.id %}">{% trans "Related Parts" %} {% if part.related_count > 0 %}<span class="badge">{{ part.related_count }}</span>{% endif %}</a>
</li>
<li{% ifequal tab 'attachments' %} class="active"{% endifequal %}>
<a href="{% url 'part-attachments' part.id %}">{% trans "Attachments" %} {% if part.attachment_count > 0 %}<span class="badge">{{ part.attachment_count }}</span>{% endif %}</a>
</li>

View File

@ -12,6 +12,11 @@ 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<pk>\d+)/delete/?', views.PartRelatedDelete.as_view(), name='part-related-delete'),
]
part_attachment_urls = [
url(r'^new/?', views.PartAttachmentCreate.as_view(), name='part-attachment-create'),
url(r'^(?P<pk>\d+)/edit/?', views.PartAttachmentEdit.as_view(), name='part-attachment-edit'),
@ -60,6 +65,7 @@ part_detail_urls = [
url(r'^sale-prices/', views.PartDetail.as_view(template_name='part/sale_prices.html'), name='part-sale-prices'),
url(r'^tests/', views.PartDetail.as_view(template_name='part/part_tests.html'), name='part-test-templates'),
url(r'^track/?', views.PartDetail.as_view(template_name='part/track.html'), name='part-track'),
url(r'^related-parts/?', views.PartDetail.as_view(template_name='part/related.html'), name='part-related'),
url(r'^attachments/?', views.PartDetail.as_view(template_name='part/attachments.html'), name='part-attachments'),
url(r'^notes/?', views.PartNotes.as_view(), name='part-notes'),
@ -112,6 +118,9 @@ part_urls = [
# Part category
url(r'^category/(?P<pk>\d+)/', include(part_category_urls)),
# Part related
url(r'^related-parts/', include(part_related_urls)),
# Part attachments
url(r'^attachment/', include(part_attachment_urls)),

View File

@ -21,7 +21,7 @@ import os
from rapidfuzz import fuzz
from decimal import Decimal, InvalidOperation
from .models import PartCategory, Part, PartAttachment
from .models import PartCategory, Part, PartAttachment, PartRelated
from .models import PartParameterTemplate, PartParameter
from .models import BomItem
from .models import match_part_names
@ -70,6 +70,73 @@ 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"
role_required = 'part.change'
# TODO: QuerySet should not show parts already related to object
def get_initial(self):
""" Point part_1 to parent part """
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)
"""
form = super(AjaxCreateView, self).get_form()
form.fields['part_1'].widget = HiddenInput()
return form
def post_save(self):
""" Save PartRelated model (POST method does not) """
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)
class PartRelatedDelete(AjaxDeleteView):
""" View for deleting a PartRelated object """
model = PartRelated
ajax_form_title = _("Delete Related Part")
ajax_template_name = "related_delete.html"
context_object_name = "related"
role_required = 'part.change'
class PartAttachmentCreate(AjaxCreateView):
""" View for creating a new PartAttachment object

View File

@ -163,6 +163,41 @@ 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
*