mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
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:
parent
3d9223c2ee
commit
8579abb9c2
@ -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 """
|
||||
|
||||
|
@ -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}'
|
||||
|
46
InvenTree/part/templates/part/related.html
Normal file
46
InvenTree/part/templates/part/related.html
Normal 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 %}
|
@ -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>
|
||||
|
@ -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)),
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user