diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py
index 338e664252..71f87ffd19 100644
--- a/InvenTree/InvenTree/views.py
+++ b/InvenTree/InvenTree/views.py
@@ -445,9 +445,9 @@ class IndexView(TemplateView):
# TODO - Is there a less expensive way to get these from the database
context['to_order'] = [part for part in Part.objects.filter(purchaseable=True) if part.need_to_restock()]
- # Generate a list of buildable parts which have stock below their minimum values
+ # Generate a list of assembly parts which have stock below their minimum values
# TODO - Is there a less expensive way to get these from the database
- context['to_build'] = [part for part in Part.objects.filter(buildable=True) if part.need_to_restock()]
+ context['to_build'] = [part for part in Part.objects.filter(assembly=True) if part.need_to_restock()]
return context
diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py
index ad482b8dc6..f9ffc99953 100644
--- a/InvenTree/build/models.py
+++ b/InvenTree/build/models.py
@@ -51,7 +51,7 @@ class Build(models.Model):
related_name='builds',
limit_choices_to={
'is_template': False,
- 'buildable': True,
+ 'assembly': True,
'active': True
},
help_text='Select part to build',
diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py
index 500b4a2bdf..d0beee54ce 100644
--- a/InvenTree/part/api.py
+++ b/InvenTree/part/api.py
@@ -51,7 +51,7 @@ class CategoryList(generics.ListCreateAPIView):
filter_backends = [
DjangoFilterBackend,
- # filters.SearchFilter,
+ filters.SearchFilter,
filters.OrderingFilter,
]
@@ -129,8 +129,8 @@ class PartList(generics.ListCreateAPIView):
filter_fields = [
'is_template',
'variant_of',
- 'buildable',
- 'consumable',
+ 'assembly',
+ 'component',
'trackable',
'purchaseable',
'salable',
diff --git a/InvenTree/part/fixtures/part.yaml b/InvenTree/part/fixtures/part.yaml
index 632a265e23..7d669cf49d 100644
--- a/InvenTree/part/fixtures/part.yaml
+++ b/InvenTree/part/fixtures/part.yaml
@@ -57,5 +57,5 @@
fields:
name: 'Bob'
description: 'Can we build it?'
- buildable: true
+ assembly: true
\ No newline at end of file
diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py
index a2ec613429..2eb9600065 100644
--- a/InvenTree/part/forms.py
+++ b/InvenTree/part/forms.py
@@ -92,17 +92,17 @@ class EditPartForm(HelperForm):
'category',
'name',
'IPN',
- 'is_template',
- 'variant_of',
'description',
'keywords',
+ 'variant_of',
+ 'is_template',
'URL',
'default_location',
'default_supplier',
'units',
'minimum_stock',
- 'buildable',
- 'consumable',
+ 'assembly',
+ 'component',
'trackable',
'purchaseable',
'salable',
diff --git a/InvenTree/part/migrations/0007_auto_20190602_1944.py b/InvenTree/part/migrations/0007_auto_20190602_1944.py
new file mode 100644
index 0000000000..9bfeb7fb8d
--- /dev/null
+++ b/InvenTree/part/migrations/0007_auto_20190602_1944.py
@@ -0,0 +1,42 @@
+# Generated by Django 2.2 on 2019-06-02 09:44
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('part', '0006_auto_20190526_1215'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='part',
+ name='buildable',
+ ),
+ migrations.RemoveField(
+ model_name='part',
+ name='consumable',
+ ),
+ migrations.AddField(
+ model_name='part',
+ name='assembly',
+ field=models.BooleanField(default=False, help_text='Can this part be built from other parts?', verbose_name='Assembly'),
+ ),
+ migrations.AddField(
+ model_name='part',
+ name='component',
+ field=models.BooleanField(default=True, help_text='Can this part be used to build other parts?', verbose_name='Component'),
+ ),
+ migrations.AlterField(
+ model_name='bomitem',
+ name='part',
+ field=models.ForeignKey(help_text='Select parent part', limit_choices_to={'active': True, 'assembly': True}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part'),
+ ),
+ migrations.AlterField(
+ model_name='bomitem',
+ name='sub_part',
+ field=models.ForeignKey(help_text='Select part to be used in BOM', limit_choices_to={'active': True, 'component': True}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part'),
+ ),
+ ]
diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py
index 048330c2c6..f311aea1f1 100644
--- a/InvenTree/part/models.py
+++ b/InvenTree/part/models.py
@@ -201,8 +201,8 @@ class Part(models.Model):
minimum_stock: Minimum preferred quantity to keep in stock
units: Units of measure for this part (default='pcs')
salable: Can this part be sold to customers?
- buildable: Can this part be build from other parts?
- consumable: Can this part be used to make other parts?
+ assembly: Can this part be build from other parts?
+ component: Can this part be used to make other parts?
purchaseable: Can this part be purchased from suppliers?
trackable: Trackable parts can have unique serial numbers assigned, etc, etc
active: Is this part active? Parts are deactivated instead of being deleted
@@ -248,6 +248,18 @@ class Part(models.Model):
else:
return static('/img/blank_image.png')
+ def validate_unique(self, exclude=None):
+ super().validate_unique(exclude)
+
+ # Part name uniqueness should be case insensitive
+ try:
+ if Part.objects.filter(name__iexact=self.name).exclude(id=self.id).exists():
+ raise ValidationError({
+ "name": _("A part with this name already exists")
+ })
+ except Part.DoesNotExist:
+ pass
+
def clean(self):
""" Perform cleaning operations for the Part model """
@@ -343,9 +355,9 @@ class Part(models.Model):
units = models.CharField(max_length=20, default="pcs", blank=True, help_text='Stock keeping units for this part')
- buildable = models.BooleanField(default=False, help_text='Can this part be built from other parts?')
+ assembly = models.BooleanField(default=False, verbose_name='Assembly', help_text='Can this part be built from other parts?')
- consumable = models.BooleanField(default=True, help_text='Can this part be used to build other parts?')
+ component = models.BooleanField(default=True, verbose_name='Component', help_text='Can this part be used to build other parts?')
trackable = models.BooleanField(default=False, help_text='Does this part have tracking for unique items?')
@@ -858,7 +870,7 @@ class BomItem(models.Model):
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='bom_items',
help_text='Select parent part',
limit_choices_to={
- 'buildable': True,
+ 'assembly': True,
'active': True,
})
@@ -867,7 +879,7 @@ class BomItem(models.Model):
sub_part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='used_in',
help_text='Select part to be used in BOM',
limit_choices_to={
- 'consumable': True,
+ 'component': True,
'active': True
})
diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py
index fe858091c7..224e41cd97 100644
--- a/InvenTree/part/serializers.py
+++ b/InvenTree/part/serializers.py
@@ -94,8 +94,8 @@ class PartSerializer(serializers.ModelSerializer):
# 'available_stock',
'units',
'trackable',
- 'buildable',
- 'consumable',
+ 'assembly',
+ 'component',
'trackable',
'salable',
'active',
diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html
index fd8f20d2aa..2d76b08ac9 100644
--- a/InvenTree/part/templates/part/category.html
+++ b/InvenTree/part/templates/part/category.html
@@ -156,6 +156,7 @@
{% endif %}
},
buttons: ['#part-options'],
+ checkbox: true,
},
);
diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html
index 9765a2574e..b536a5542e 100644
--- a/InvenTree/part/templates/part/detail.html
+++ b/InvenTree/part/templates/part/detail.html
@@ -90,30 +90,6 @@
Units
{{ part.units }}
-
-
-
-
-
-
Buildable
-
{% include "yesnolabel.html" with value=part.buildable %}
-
-
-
Consumable
-
{% include "yesnolabel.html" with value=part.consumable %}
-
-
-
Trackable
-
{% include "yesnolabel.html" with value=part.trackable %}
-
-
-
Purchaseable
-
{% include "yesnolabel.html" with value=part.purchaseable %}
-
-
-
Salable
-
{% include "yesnolabel.html" with value=part.salable %}
-
{% if part.minimum_stock > 0 %}
Minimum Stock
@@ -122,6 +98,40 @@
{% endif %}
+
+
+ {% if part.assembly %}
+
+
Assembly
+
This part can be assembled from other parts
+
+ {% endif %}
+ {% if part.component %}
+
+
Component
+
This part can be used in assemblies
+
+ {% endif %}
+ {% if part.trackable %}
+
+
Trackable
+
Stock for this part will be tracked by (serial or batch)
+
+ {% endif %}
+ {% if part.purchaseable %}
+
+
Purchaseable
+
This part can be purchased from external suppliers
+
+ {% endif %}
+ {% if part.salable %}
+
+
Salable
+
This part can be sold to customers
+
+ {% endif %}
+
+
{% if part.notes %}
diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html
index d8af6d2ec9..1354a9a4a4 100644
--- a/InvenTree/part/templates/part/part_base.html
+++ b/InvenTree/part/templates/part/part_base.html
@@ -78,7 +78,7 @@
In Stock
{{ part.total_stock }}
- {% if part.buildable %}
+ {% if part.assembly %}
+{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/templates/InvenTree/search_stock_location.html b/InvenTree/templates/InvenTree/search_stock_location.html
new file mode 100644
index 0000000000..923635b13e
--- /dev/null
+++ b/InvenTree/templates/InvenTree/search_stock_location.html
@@ -0,0 +1,14 @@
+{% extends "collapse.html" %}
+
+{% block collapse_title %}
+
Stock Locations
+{% endblock %}
+
+{% block collapse_heading %}
+
{% include "InvenTree/searching.html" %}
+{% endblock %}
+
+{% block collapse_content %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/templates/InvenTree/searching.html b/InvenTree/templates/InvenTree/searching.html
index 9d111fe257..5821515ad7 100644
--- a/InvenTree/templates/InvenTree/searching.html
+++ b/InvenTree/templates/InvenTree/searching.html
@@ -1 +1 @@
- Searching...
\ No newline at end of file
+ Searching
\ No newline at end of file
diff --git a/InvenTree/templates/search_form.html b/InvenTree/templates/search_form.html
index 72d317d3ec..e9c2214731 100644
--- a/InvenTree/templates/search_form.html
+++ b/InvenTree/templates/search_form.html
@@ -3,5 +3,5 @@