diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index f0c9e3f233..61097d0952 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -12,6 +12,7 @@ from .models import PartCategory, Part from .models import PartAttachment, PartStar from .models import BomItem from .models import PartParameterTemplate, PartParameter +from .models import PartCategoryParameterTemplate from .models import PartTestTemplate from .models import PartSellPriceBreak @@ -269,6 +270,18 @@ class ParameterAdmin(ImportExportModelAdmin): list_display = ('part', 'template', 'data') +class PartCategoryParameterAdmin(admin.ModelAdmin): + + def get_form(self, request, obj=None, **kwargs): + """ Display only parent categories as choices for category field """ + + form = super().get_form(request, obj, **kwargs) + + form.base_fields['category'].choices = PartCategory.get_parent_categories() + + return form + + class PartSellPriceBreakAdmin(admin.ModelAdmin): class Meta: @@ -284,5 +297,6 @@ admin.site.register(PartStar, PartStarAdmin) admin.site.register(BomItem, BomItemAdmin) admin.site.register(PartParameterTemplate, ParameterTemplateAdmin) admin.site.register(PartParameter, ParameterAdmin) +admin.site.register(PartCategoryParameterTemplate, PartCategoryParameterAdmin) admin.site.register(PartTestTemplate, PartTestTemplateAdmin) admin.site.register(PartSellPriceBreak, PartSellPriceBreakAdmin) diff --git a/InvenTree/part/migrations/0053_partcategoryparametertemplate.py b/InvenTree/part/migrations/0053_partcategoryparametertemplate.py new file mode 100644 index 0000000000..e9a1e90af7 --- /dev/null +++ b/InvenTree/part/migrations/0053_partcategoryparametertemplate.py @@ -0,0 +1,27 @@ +# Generated by Django 3.0.7 on 2020-10-30 18:04 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0052_auto_20201027_1557'), + ] + + operations = [ + migrations.CreateModel( + name='PartCategoryParameterTemplate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('default_value', models.CharField(blank=True, help_text='Default Parameter Value', max_length=500)), + ('category', models.ForeignKey(help_text='Part Category', on_delete=django.db.models.deletion.CASCADE, related_name='parameter_templates', to='part.PartCategory')), + ('parameter_template', models.ForeignKey(help_text='Parameter Template', on_delete=django.db.models.deletion.CASCADE, related_name='part_categories', to='part.PartParameterTemplate')), + ], + ), + migrations.AddConstraint( + model_name='partcategoryparametertemplate', + constraint=models.UniqueConstraint(fields=('category', 'parameter_template'), name='unique_category_parameter_template_pair'), + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 715d1423eb..2278993bf7 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -12,7 +12,7 @@ from django.core.exceptions import ValidationError from django.urls import reverse from django.db import models, transaction -from django.db.models import Sum +from django.db.models import Sum, UniqueConstraint from django.db.models.functions import Coalesce from django.core.validators import MinValueValidator @@ -163,6 +163,22 @@ class PartCategory(InvenTreeTree): return category_parameters + @classmethod + def get_parent_categories(cls): + """ Return tuple list of parent (root) categories """ + + # Store parent categories (add empty label) + parent_categories = [ + ('', '-' * 10) + ] + # Get root nodes + root_categories = cls.objects.filter(level=0) + + for category in root_categories: + parent_categories.append((category.id, category.name)) + + return parent_categories + @receiver(pre_delete, sender=PartCategory, dispatch_uid='partcategory_delete_log') def before_delete_part_category(sender, instance, using, **kwargs): @@ -1550,6 +1566,48 @@ class PartParameter(models.Model): return part_parameter +class PartCategoryParameterTemplate(models.Model): + """ + A PartCategoryParameterTemplate creates a unique relationship between a PartCategory + and a PartParameterTemplate. + Multiple PartParameterTemplate instances can be associated to a PartCategory to drive + a default list of parameter templates attached to a Part instance upon creation. + + Attributes: + category: Reference to a single PartCategory object + parameter_template: Reference to a single PartParameterTemplate object + default_value: The default value for the parameter in the context of the selected + category + """ + + class Meta: + constraints = [ + UniqueConstraint(fields=['category', 'parameter_template'], + name='unique_category_parameter_template_pair') + ] + + def __str__(self): + """ String representation of a PartCategoryParameterTemplate (admin interface) """ + if self.default_value: + return f'{self.category.name} | {self.parameter_template.name} | {self.default_value}' + else: + return f'{self.category.name} | {self.parameter_template.name}' + + category = models.ForeignKey(PartCategory, + on_delete=models.CASCADE, + related_name='parameter_templates', + help_text=_('Part Category')) + + parameter_template = models.ForeignKey(PartParameterTemplate, + on_delete=models.CASCADE, + related_name='part_categories', + help_text=_('Parameter Template')) + + default_value = models.CharField(max_length=500, + blank=True, + help_text=_('Default Parameter Value')) + + class BomItem(models.Model): """ A BomItem links a part to its component items. A part can have a BOM (bill of materials) which defines