mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
commit
141d9b779d
@ -127,6 +127,8 @@ class PartList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
filter_fields = [
|
filter_fields = [
|
||||||
|
'is_template',
|
||||||
|
'variant_of',
|
||||||
'buildable',
|
'buildable',
|
||||||
'consumable',
|
'consumable',
|
||||||
'trackable',
|
'trackable',
|
||||||
|
@ -92,7 +92,6 @@ class EditPartForm(HelperForm):
|
|||||||
'category',
|
'category',
|
||||||
'name',
|
'name',
|
||||||
'IPN',
|
'IPN',
|
||||||
'variant',
|
|
||||||
'is_template',
|
'is_template',
|
||||||
'variant_of',
|
'variant_of',
|
||||||
'description',
|
'description',
|
||||||
|
26
InvenTree/part/migrations/0005_auto_20190526_1119.py
Normal file
26
InvenTree/part/migrations/0005_auto_20190526_1119.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Generated by Django 2.2 on 2019-05-26 01:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('part', '0004_auto_20190525_2356'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='part',
|
||||||
|
name='revision',
|
||||||
|
field=models.CharField(blank=True, help_text='Part rerevision code', max_length=32),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='part',
|
||||||
|
unique_together={('name', 'revision')},
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='part',
|
||||||
|
name='variant',
|
||||||
|
),
|
||||||
|
]
|
27
InvenTree/part/migrations/0006_auto_20190526_1215.py
Normal file
27
InvenTree/part/migrations/0006_auto_20190526_1215.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Generated by Django 2.2 on 2019-05-26 02:15
|
||||||
|
|
||||||
|
import InvenTree.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('part', '0005_auto_20190526_1119'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='part',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(help_text='Part name (must be unique)', max_length=100, unique=True, validators=[InvenTree.validators.validate_part_name]),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='part',
|
||||||
|
unique_together=set(),
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='part',
|
||||||
|
name='revision',
|
||||||
|
),
|
||||||
|
]
|
@ -162,6 +162,7 @@ def match_part_names(match, threshold=80, reverse=True, compare_length=False):
|
|||||||
|
|
||||||
if compare_length:
|
if compare_length:
|
||||||
# Also employ primitive length comparison
|
# Also employ primitive length comparison
|
||||||
|
# TODO - Improve this somewhat...
|
||||||
l_min = min(len(match), len(compare))
|
l_min = min(len(match), len(compare))
|
||||||
l_max = max(len(match), len(compare))
|
l_max = max(len(match), len(compare))
|
||||||
|
|
||||||
@ -211,9 +212,6 @@ class Part(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Part"
|
verbose_name = "Part"
|
||||||
verbose_name_plural = "Parts"
|
verbose_name_plural = "Parts"
|
||||||
unique_together = [
|
|
||||||
('name', 'variant')
|
|
||||||
]
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{n} - {d}".format(n=self.full_name, d=self.description)
|
return "{n} - {d}".format(n=self.full_name, d=self.description)
|
||||||
@ -236,9 +234,6 @@ class Part(models.Model):
|
|||||||
|
|
||||||
elements.append(self.name)
|
elements.append(self.name)
|
||||||
|
|
||||||
if self.variant:
|
|
||||||
elements.append(self.variant)
|
|
||||||
|
|
||||||
return ' | '.join(elements)
|
return ' | '.join(elements)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
@ -262,12 +257,11 @@ class Part(models.Model):
|
|||||||
'variant_of': _("Part cannot be a variant of another part if it is already a template"),
|
'variant_of': _("Part cannot be a variant of another part if it is already a template"),
|
||||||
})
|
})
|
||||||
|
|
||||||
name = models.CharField(max_length=100, blank=False, help_text='Part name',
|
name = models.CharField(max_length=100, blank=False, unique=True,
|
||||||
|
help_text='Part name (must be unique)',
|
||||||
validators=[validators.validate_part_name]
|
validators=[validators.validate_part_name]
|
||||||
)
|
)
|
||||||
|
|
||||||
variant = models.CharField(max_length=32, blank=True, help_text='Part variant or revision code')
|
|
||||||
|
|
||||||
is_template = models.BooleanField(default=False, help_text='Is this part a template part?')
|
is_template = models.BooleanField(default=False, help_text='Is this part a template part?')
|
||||||
|
|
||||||
variant_of = models.ForeignKey('part.Part', related_name='variants',
|
variant_of = models.ForeignKey('part.Part', related_name='variants',
|
||||||
|
@ -87,7 +87,6 @@ class PartSerializer(serializers.ModelSerializer):
|
|||||||
'IPN',
|
'IPN',
|
||||||
'is_template',
|
'is_template',
|
||||||
'variant_of',
|
'variant_of',
|
||||||
'variant',
|
|
||||||
'description',
|
'description',
|
||||||
'keywords',
|
'keywords',
|
||||||
'URL',
|
'URL',
|
||||||
|
@ -42,12 +42,6 @@
|
|||||||
<td>{{ part.IPN }}</td>
|
<td>{{ part.IPN }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if part.variant %}
|
|
||||||
<tr>
|
|
||||||
<td><b>Variant</b></td>
|
|
||||||
<td>{{ part.variant }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><b>Description</b></td>
|
<td><b>Description</b></td>
|
||||||
<td>{{ part.description }}</td>
|
<td>{{ part.description }}</td>
|
||||||
|
@ -38,9 +38,6 @@
|
|||||||
<h4>
|
<h4>
|
||||||
{{ part.full_name }}
|
{{ part.full_name }}
|
||||||
</h4>
|
</h4>
|
||||||
{% if part.variant %}
|
|
||||||
<p>Variant: {{ part.variant }}</p>
|
|
||||||
{% endif %}
|
|
||||||
<p><i>{{ part.description }}</i></p>
|
<p><i>{{ part.description }}</i></p>
|
||||||
<p>
|
<p>
|
||||||
<div class='btn-group'>
|
<div class='btn-group'>
|
||||||
|
12
InvenTree/part/templates/part/variant_part.html
Normal file
12
InvenTree/part/templates/part/variant_part.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{% extends "modal_form.html" %}
|
||||||
|
|
||||||
|
{% block pre_form_content %}
|
||||||
|
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
<div class='alert alert-info alert-block'>
|
||||||
|
<b>Create new part variant</b><br>
|
||||||
|
Create a new variant of template <i>'{{ part.full_name }}'</i>.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -60,4 +60,13 @@
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#new-variant').click(function() {
|
||||||
|
launchModalForm(
|
||||||
|
"{% url 'make-part-variant' part.id %}",
|
||||||
|
{
|
||||||
|
follow: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -24,6 +24,7 @@ part_detail_urls = [
|
|||||||
url(r'^bom-export/?', views.BomDownload.as_view(), name='bom-export'),
|
url(r'^bom-export/?', views.BomDownload.as_view(), name='bom-export'),
|
||||||
url(r'^validate-bom/', views.BomValidate.as_view(), name='bom-validate'),
|
url(r'^validate-bom/', views.BomValidate.as_view(), name='bom-validate'),
|
||||||
url(r'^duplicate/', views.PartDuplicate.as_view(), name='part-duplicate'),
|
url(r'^duplicate/', views.PartDuplicate.as_view(), name='part-duplicate'),
|
||||||
|
url(r'^make-variant/', views.MakePartVariant.as_view(), name='make-part-variant'),
|
||||||
url(r'^pricing/', views.PartPricing.as_view(), name='part-pricing'),
|
url(r'^pricing/', views.PartPricing.as_view(), name='part-pricing'),
|
||||||
|
|
||||||
url(r'^variants/?', views.PartDetail.as_view(template_name='part/variants.html'), name='part-variants'),
|
url(r'^variants/?', views.PartDetail.as_view(template_name='part/variants.html'), name='part-variants'),
|
||||||
|
@ -124,6 +124,77 @@ class PartAttachmentDelete(AjaxDeleteView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MakePartVariant(AjaxCreateView):
|
||||||
|
""" View for creating a new variant based on an existing template Part
|
||||||
|
|
||||||
|
- Part <pk> is provided in the URL '/part/<pk>/make_variant/'
|
||||||
|
- Automatically copy relevent data (BOM, etc, etc)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
model = Part
|
||||||
|
form_class = part_forms.EditPartForm
|
||||||
|
|
||||||
|
ajax_form_title = 'Create Variant'
|
||||||
|
ajax_template_name = 'part/variant_part.html'
|
||||||
|
|
||||||
|
def get_part_template(self):
|
||||||
|
return get_object_or_404(Part, id=self.kwargs['pk'])
|
||||||
|
|
||||||
|
def get_context_data(self):
|
||||||
|
return {
|
||||||
|
'part': self.get_part_template(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_form(self):
|
||||||
|
form = super(AjaxCreateView, self).get_form()
|
||||||
|
|
||||||
|
# Hide some variant-related fields
|
||||||
|
form.fields['is_template'].widget = HiddenInput()
|
||||||
|
form.fields['variant_of'].widget = HiddenInput()
|
||||||
|
|
||||||
|
return form
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
form = self.get_form()
|
||||||
|
context = self.get_context_data()
|
||||||
|
part_template = self.get_part_template()
|
||||||
|
|
||||||
|
valid = form.is_valid()
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'form_valid': valid,
|
||||||
|
}
|
||||||
|
|
||||||
|
if valid:
|
||||||
|
# Create the new part variant
|
||||||
|
part = form.save(commit=False)
|
||||||
|
part.variant_of = part_template
|
||||||
|
part.is_template = False
|
||||||
|
|
||||||
|
part.save()
|
||||||
|
|
||||||
|
data['pk'] = part.pk
|
||||||
|
data['text'] = str(part)
|
||||||
|
data['url'] = part.get_absolute_url()
|
||||||
|
|
||||||
|
# Copy relevent information from the template part
|
||||||
|
part.deepCopy(part_template, bom=True)
|
||||||
|
|
||||||
|
return self.renderJsonResponse(request, form, data, context=context)
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
|
||||||
|
part_template = self.get_part_template()
|
||||||
|
|
||||||
|
initials = model_to_dict(part_template)
|
||||||
|
initials['is_template'] = False
|
||||||
|
initials['variant_of'] = part_template
|
||||||
|
|
||||||
|
return initials
|
||||||
|
|
||||||
|
|
||||||
class PartDuplicate(AjaxCreateView):
|
class PartDuplicate(AjaxCreateView):
|
||||||
""" View for duplicating an existing Part object.
|
""" View for duplicating an existing Part object.
|
||||||
|
|
||||||
@ -209,7 +280,7 @@ class PartDuplicate(AjaxCreateView):
|
|||||||
original = self.get_part_to_copy()
|
original = self.get_part_to_copy()
|
||||||
|
|
||||||
if original:
|
if original:
|
||||||
part.deepCopy(original, bom=deep_copy)
|
part.deepCopy(original, bom=deep_copy)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data['url'] = part.get_absolute_url()
|
data['url'] = part.get_absolute_url()
|
||||||
|
Loading…
Reference in New Issue
Block a user