From 883efd0945fa8082deb29ff4a293981c0d97ced8 Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@gmail.com>
Date: Sun, 16 Apr 2017 14:11:20 +1000
Subject: [PATCH 1/3] Removed 'pending' tag

---
 InvenTree/stock/models.py      | 1 -
 InvenTree/stock/serializers.py | 1 -
 2 files changed, 2 deletions(-)

diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py
index 9984d1d74a..703bd3b9fe 100644
--- a/InvenTree/stock/models.py
+++ b/InvenTree/stock/models.py
@@ -73,5 +73,4 @@ class StockTracking(models.Model):
 
     item = models.ForeignKey(StockItem, on_delete=models.CASCADE, related_name='tracking')
     quantity = models.IntegerField()
-    pending = models.BooleanField(default=False)
     when = models.DateTimeField(auto_now=True)
diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py
index c7e958e4e9..99db85e2ec 100644
--- a/InvenTree/stock/serializers.py
+++ b/InvenTree/stock/serializers.py
@@ -41,5 +41,4 @@ class StockTrackingSerializer(serializers.HyperlinkedModelSerializer):
         fields = ('url',
                   'item',
                   'quantity',
-                  'pending',
                   'when')

From 057fd1dd20eb3cb2000d3fd270bcff9f2e4dc538 Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@gmail.com>
Date: Sun, 16 Apr 2017 14:24:15 +1000
Subject: [PATCH 2/3] Filtering improvements

---
 InvenTree/part/views.py        |  3 ---
 InvenTree/project/views.py     | 26 +++++++++++++-------------
 InvenTree/stock/serializers.py |  1 +
 InvenTree/stock/views.py       |  6 ------
 InvenTree/supplier/views.py    |  8 --------
 InvenTree/track/serializers.py |  5 +----
 InvenTree/track/views.py       |  5 -----
 7 files changed, 15 insertions(+), 39 deletions(-)

diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py
index 40c7134860..a77638c753 100644
--- a/InvenTree/part/views.py
+++ b/InvenTree/part/views.py
@@ -31,8 +31,6 @@ class PartDetail(generics.RetrieveUpdateDestroyAPIView):
 
 class PartParamFilter(FilterSet):
 
-    part = NumberFilter(name='part', lookup_expr='exact')
-
     class Meta:
         model = PartParameter
         fields = ['part']
@@ -75,7 +73,6 @@ class PartParamDetail(generics.RetrieveUpdateDestroyAPIView):
 
 
 class PartFilter(FilterSet):
-    category = NumberFilter(name='category', lookup_expr='exact')
 
     class Meta:
         model = Part
diff --git a/InvenTree/project/views.py b/InvenTree/project/views.py
index 2a293a0cca..593407ae63 100644
--- a/InvenTree/project/views.py
+++ b/InvenTree/project/views.py
@@ -1,5 +1,6 @@
-from rest_framework import generics, permissions
+from django_filters.rest_framework import FilterSet, DjangoFilterBackend
 
+from rest_framework import generics, permissions
 from InvenTree.models import FilterChildren
 from .models import ProjectCategory, Project, ProjectPart
 from .serializers import ProjectSerializer
@@ -26,6 +27,13 @@ class ProjectDetail(generics.RetrieveUpdateDestroyAPIView):
     permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
 
 
+class ProjectFilter(FilterSet):
+
+    class Meta:
+        model = Project
+        fields = ['category']
+
+
 class ProjectList(generics.ListCreateAPIView):
     """
 
@@ -38,19 +46,11 @@ class ProjectList(generics.ListCreateAPIView):
 
     """
 
-    def get_queryset(self):
-        projects = Project.objects.all()
-        params = self.request.query_params
-
-        cat_id = params.get('category', None)
-
-        if cat_id:
-            projects = projects.filter(category=cat_id)
-
-        return projects
-
+    queryset = Project.objects.all()
     serializer_class = ProjectSerializer
     permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
+    filter_backends = (DjangoFilterBackend,)
+    filter_class = ProjectFilter
 
 
 class ProjectCategoryDetail(generics.RetrieveUpdateAPIView):
@@ -136,7 +136,7 @@ class ProjectPartDetail(generics.RetrieveUpdateDestroyAPIView):
 
     delete:
     Remove a ProjectPart
-    
+
     """
 
     queryset = ProjectPart.objects.all()
diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py
index 99db85e2ec..f0ba1c805d 100644
--- a/InvenTree/stock/serializers.py
+++ b/InvenTree/stock/serializers.py
@@ -11,6 +11,7 @@ class StockItemSerializer(serializers.HyperlinkedModelSerializer):
         model = StockItem
         fields = ('url',
                   'part',
+                  'supplier_part',
                   'location',
                   'quantity',
                   'status',
diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py
index 026c9084ab..a0a9cf4beb 100644
--- a/InvenTree/stock/views.py
+++ b/InvenTree/stock/views.py
@@ -29,8 +29,6 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView):
 class StockFilter(FilterSet):
     min_stock = NumberFilter(name='quantity', lookup_expr='gte')
     max_stock = NumberFilter(name='quantity', lookup_expr='lte')
-    part = NumberFilter(name='part', lookup_expr='exact')
-    location = NumberFilter(name='location', lookup_expr='exact')
 
     class Meta:
         model = StockItem
@@ -76,8 +74,6 @@ class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
 
 class StockLocationFilter(FilterSet):
 
-    parent = NumberFilter(name='parent', lookup_expr='exact')
-
     class Meta:
         model = StockLocation
         fields = ['parent']
@@ -123,8 +119,6 @@ class StockTrackingDetail(generics.RetrieveUpdateDestroyAPIView):
 
 class StockTrackingFilter(FilterSet):
 
-    item = NumberFilter(name='item', lookup_expr='exact')
-
     class Meta:
         model = StockTracking
         fields = ['item']
diff --git a/InvenTree/supplier/views.py b/InvenTree/supplier/views.py
index ac8995de48..aed416f56b 100644
--- a/InvenTree/supplier/views.py
+++ b/InvenTree/supplier/views.py
@@ -138,12 +138,6 @@ class SupplierPartDetail(generics.RetrieveUpdateDestroyAPIView):
 
 class SupplierPartFilter(FilterSet):
 
-    supplier = NumberFilter(name='supplier', lookup_expr='exact')
-
-    part = NumberFilter(name='part', lookup_expr='exact')
-
-    manufacturer = NumberFilter(name='manufacturer', lookup_expr='exact')
-
     class Meta:
         model = SupplierPart
         fields = ['supplier', 'part', 'manufacturer']
@@ -190,8 +184,6 @@ class SupplierPriceBreakDetail(generics.RetrieveUpdateDestroyAPIView):
 
 class PriceBreakFilter(FilterSet):
 
-    part = NumberFilter(name='part', lookup_expr='exact')
-
     class Meta:
         model = SupplierPriceBreak
         fields = ['part']
diff --git a/InvenTree/track/serializers.py b/InvenTree/track/serializers.py
index c2c097c28d..34f15e227d 100644
--- a/InvenTree/track/serializers.py
+++ b/InvenTree/track/serializers.py
@@ -5,8 +5,6 @@ from .models import UniquePart, PartTrackingInfo
 
 class UniquePartSerializer(serializers.HyperlinkedModelSerializer):
 
-    tracking_info = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
-
     class Meta:
         model = UniquePart
         fields = ['url',
@@ -15,8 +13,7 @@ class UniquePartSerializer(serializers.HyperlinkedModelSerializer):
                   'serial',
                   # 'createdBy',
                   'customer',
-                  'status',
-                  'tracking_info']
+                  'status']
 
 
 class PartTrackingInfoSerializer(serializers.HyperlinkedModelSerializer):
diff --git a/InvenTree/track/views.py b/InvenTree/track/views.py
index f45205993d..c36f710e0d 100644
--- a/InvenTree/track/views.py
+++ b/InvenTree/track/views.py
@@ -31,10 +31,6 @@ class UniquePartFilter(FilterSet):
     min_sn = NumberFilter(name='serial', lookup_expr='gte')
     max_sn = NumberFilter(name='serial', lookup_expr='lte')
 
-    sn = NumberFilter(name='serial', lookup_expr='exact')
-    part = NumberFilter(name='part', lookup_expr='exact')
-    customer = NumberFilter(name='customer', lookup_expr='exact')
-
     class Meta:
         model = UniquePart
         fields = ['serial', 'part', 'customer']
@@ -77,7 +73,6 @@ class PartTrackingDetail(generics.RetrieveUpdateDestroyAPIView):
 
 
 class PartTrackingFilter(FilterSet):
-    part = NumberFilter(name='part', lookup_expr='exact')
 
     class Meta:
         model = PartTrackingInfo

From 6c7f5fdaf3dce22e17be1da7b123d4e4de223e80 Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@gmail.com>
Date: Sun, 16 Apr 2017 17:05:02 +1000
Subject: [PATCH 3/3] Added MinValue validator(s)

---
 InvenTree/part/models.py     | 6 ++++--
 InvenTree/part/views.py      | 1 -
 InvenTree/project/models.py  | 8 +++++---
 InvenTree/stock/models.py    | 6 ++++--
 InvenTree/supplier/models.py | 7 ++++---
 InvenTree/supplier/views.py  | 1 -
 6 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py
index b9d7e71c86..099d5ab6cd 100644
--- a/InvenTree/part/models.py
+++ b/InvenTree/part/models.py
@@ -2,6 +2,7 @@ from __future__ import unicode_literals
 from django.utils.translation import ugettext as _
 from django.db import models
 from django.db.models import Sum
+from django.core.validators import MinValueValidator
 
 from InvenTree.models import InvenTreeTree
 
@@ -35,7 +36,7 @@ class Part(models.Model):
     category = models.ForeignKey(PartCategory, on_delete=models.CASCADE)
 
     # Minimum "allowed" stock level
-    minimum_stock = models.PositiveIntegerField(default=0)
+    minimum_stock = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0)])
 
     # Units of quantity for this part. Default is "pcs"
     units = models.CharField(max_length=20, default="pcs", blank=True)
@@ -112,7 +113,8 @@ class PartParameterTemplate(models.Model):
 
     format = models.PositiveIntegerField(
         default=PARAM_NUMERIC,
-        choices=PARAM_TYPE_CODES.items())
+        choices=PARAM_TYPE_CODES.items(),
+        validators=[MinValueValidator(0)])
 
     def __str__(self):
         return "{name} ({units})".format(
diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py
index a77638c753..d2a1e9f55d 100644
--- a/InvenTree/part/views.py
+++ b/InvenTree/part/views.py
@@ -1,5 +1,4 @@
 from django_filters.rest_framework import FilterSet, DjangoFilterBackend
-from django_filters import NumberFilter
 
 from rest_framework import generics, permissions
 
diff --git a/InvenTree/project/models.py b/InvenTree/project/models.py
index 8038184215..99228deb22 100644
--- a/InvenTree/project/models.py
+++ b/InvenTree/project/models.py
@@ -5,6 +5,7 @@ from django.db import models
 
 from InvenTree.models import InvenTreeTree
 from part.models import Part
+from django.core.validators import MinValueValidator
 
 
 class ProjectCategory(InvenTreeTree):
@@ -46,7 +47,7 @@ class ProjectPart(models.Model):
 
     part = models.ForeignKey(Part, on_delete=models.CASCADE)
     project = models.ForeignKey(Project, on_delete=models.CASCADE)
-    quantity = models.PositiveIntegerField(default=1)
+    quantity = models.PositiveIntegerField(default=1, validators=[MinValueValidator(0)])
 
     class Meta:
         unique_together = ('part', 'project')
@@ -66,7 +67,8 @@ class ProjectPart(models.Model):
     overage = models.FloatField(default=0)
     overage_type = models.PositiveIntegerField(
         default=OVERAGE_ABSOLUTE,
-        choices=OVARAGE_CODES.items())
+        choices=OVARAGE_CODES.items(),
+        validators=[MinValueValidator(0)])
     """
 
     # Set if the part is generated by the project,
@@ -89,6 +91,6 @@ class ProjectRun(models.Model):
     """
 
     project = models.ForeignKey(Project, on_delete=models.CASCADE)
-    quantity = models.PositiveIntegerField(default=1)
+    quantity = models.PositiveIntegerField(default=1, validators=[MinValueValidator(0)])
 
     run_date = models.DateField(auto_now_add=True)
diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py
index 703bd3b9fe..0c847e80d5 100644
--- a/InvenTree/stock/models.py
+++ b/InvenTree/stock/models.py
@@ -1,6 +1,7 @@
 from __future__ import unicode_literals
 from django.utils.translation import ugettext as _
 from django.db import models
+from django.core.validators import MinValueValidator
 
 from supplier.models import SupplierPart
 from part.models import Part
@@ -21,7 +22,7 @@ class StockItem(models.Model):
     part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='locations')
     supplier_part = models.ForeignKey(SupplierPart, blank=True, null=True, on_delete=models.SET_NULL)
     location = models.ForeignKey(StockLocation, on_delete=models.CASCADE)
-    quantity = models.PositiveIntegerField()
+    quantity = models.PositiveIntegerField(validators=[MinValueValidator(0)])
     updated = models.DateField(auto_now=True)
 
     # last time the stock was checked / counted
@@ -50,7 +51,8 @@ class StockItem(models.Model):
 
     status = models.PositiveIntegerField(
         default=ITEM_IN_STOCK,
-        choices=ITEM_STATUS_CODES.items())
+        choices=ITEM_STATUS_CODES.items(),
+        validators=[MinValueValidator(0)])
 
     notes = models.CharField(max_length=100, blank=True)
 
diff --git a/InvenTree/supplier/models.py b/InvenTree/supplier/models.py
index 9f5067caf8..13393bb4ad 100644
--- a/InvenTree/supplier/models.py
+++ b/InvenTree/supplier/models.py
@@ -1,6 +1,7 @@
 from __future__ import unicode_literals
 
 from django.db import models
+from django.core.validators import MinValueValidator
 
 from InvenTree.models import Company
 from part.models import Part
@@ -54,10 +55,10 @@ class SupplierPart(models.Model):
     packaging = models.CharField(max_length=50, blank=True)
 
     # multiple that the part is provided in
-    multiple = models.PositiveIntegerField(default=1)
+    multiple = models.PositiveIntegerField(default=1, validators=[MinValueValidator(0)])
 
     # Mimumum number required to order
-    minimum = models.PositiveIntegerField(default=1)
+    minimum = models.PositiveIntegerField(default=1, validators=[MinValueValidator(0)])
 
     # lead time for parts that cannot be delivered immediately
     lead_time = models.DurationField(blank=True, null=True)
@@ -75,7 +76,7 @@ class SupplierPriceBreak(models.Model):
     """
 
     part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='price_breaks')
-    quantity = models.PositiveIntegerField()
+    quantity = models.PositiveIntegerField(validators=[MinValueValidator(0)])
     cost = models.DecimalField(max_digits=10, decimal_places=3)
 
     class Meta:
diff --git a/InvenTree/supplier/views.py b/InvenTree/supplier/views.py
index aed416f56b..8f9b43e03a 100644
--- a/InvenTree/supplier/views.py
+++ b/InvenTree/supplier/views.py
@@ -1,5 +1,4 @@
 from django_filters.rest_framework import FilterSet, DjangoFilterBackend
-from django_filters import NumberFilter
 
 from rest_framework import generics, permissions