From deda73a50f6bc5ced21a1a6124623ebf9521a19b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 28 Mar 2017 22:27:46 +1100 Subject: [PATCH 1/8] Added stock status field --- InvenTree/part/admin.py | 2 +- InvenTree/part/models.py | 2 +- InvenTree/stock/admin.py | 2 +- InvenTree/stock/models.py | 14 ++++++++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index 3cc2ad10bb..5c8211e2be 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -4,7 +4,7 @@ from .models import PartCategory, Part class PartAdmin(admin.ModelAdmin): - list_display = ('name', 'IPN', 'quantity', 'category') + list_display = ('name', 'IPN', 'stock', 'category') # Custom form for PartCategory class PartCategoryAdmin(admin.ModelAdmin): diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 80d4728c93..0af382ec64 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -45,7 +45,7 @@ class Part(models.Model): return self.stockitem_set.all() @property - def quantity(self): + def stock(self): """ Return the total stock quantity for this part. Part may be stored in multiple locations """ diff --git a/InvenTree/stock/admin.py b/InvenTree/stock/admin.py index 4a6b7be4c0..9e0dac771b 100644 --- a/InvenTree/stock/admin.py +++ b/InvenTree/stock/admin.py @@ -6,7 +6,7 @@ class WarehouseAdmin(admin.ModelAdmin): list_display = ('name', 'path', 'description') class StockItemAdmin(admin.ModelAdmin): - list_display = ('part', 'quantity', 'location', 'updated') + list_display = ('part', 'quantity', 'location', 'status', 'updated') admin.site.register(Warehouse, WarehouseAdmin) admin.site.register(StockItem, StockItemAdmin) \ No newline at end of file diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 2cc5e4c727..50116d6b36 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -15,6 +15,20 @@ class StockItem(models.Model): quantity = models.IntegerField() updated = models.DateField(auto_now=True) + # Stock status types + ITEM_IN_PROGRESS = 0 + ITEM_DAMAGED = 10 + ITEM_RETURNED = 20 + ITEM_COMPLETE = 50 + + status = models.IntegerField(default=ITEM_IN_PROGRESS, + choices=[ + (ITEM_IN_PROGRESS, "In progress"), + (ITEM_DAMAGED, "Damaged"), + (ITEM_RETURNED, "Returned"), + (ITEM_COMPLETE, "Complete") + ]) + def __str__(self): return "{n} x {part} @ {loc}".format( n = self.quantity, From 7eb00e8d4780a1129c0e6f2ce448ed4b9ea51f97 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 28 Mar 2017 22:45:43 +1100 Subject: [PATCH 2/8] More model updates --- InvenTree/InvenTree/models.py | 31 ++++++++++++++++++++++++++----- InvenTree/stock/models.py | 4 ++-- InvenTree/supplier/admin.py | 7 ++++--- InvenTree/supplier/models.py | 19 +++++-------------- InvenTree/track/models.py | 21 +++++++++++++++++++++ 5 files changed, 58 insertions(+), 24 deletions(-) diff --git a/InvenTree/InvenTree/models.py b/InvenTree/InvenTree/models.py index 6c2d10d866..f18ae41488 100644 --- a/InvenTree/InvenTree/models.py +++ b/InvenTree/InvenTree/models.py @@ -4,12 +4,37 @@ from django.db import models from django.core.exceptions import ObjectDoesNotExist from django.contrib.contenttypes.models import ContentType +class ExternalEntity(models.Model): + """ Abstract model representing an external person / supplier / etc + """ + + class Meta: + abstract = True + + name = models.CharField(max_length=100) + URL = models.URLField(blank=True) + address = models.CharField(max_length=200, + blank=True) + phone = models.CharField(max_length=50, + blank=True) + email = models.EmailField(blank=True) + contact = models.CharField(max_length=100, + blank=True) + notes = models.CharField(max_length=500, + blank=True) + + def __str__(self): + return self.name + class InvenTreeTree(models.Model): """ Provides an abstracted self-referencing tree model for data categories. - Each Category has one parent Category, which can be blank (for a top-level Category). - Each Category can have zero-or-more child Categor(y/ies) """ + class Meta: + abstract = True + name = models.CharField(max_length=100) description = models.CharField(max_length=250) parent = models.ForeignKey('self', @@ -123,8 +148,4 @@ class InvenTreeTree(models.Model): This is recursive - Make it not so. """ - return self.path - - - class Meta: - abstract = True \ No newline at end of file + return self.path \ No newline at end of file diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 50116d6b36..4442ea2525 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -18,14 +18,14 @@ class StockItem(models.Model): # Stock status types ITEM_IN_PROGRESS = 0 ITEM_DAMAGED = 10 - ITEM_RETURNED = 20 + ITEM_ATTENTION = 20 ITEM_COMPLETE = 50 status = models.IntegerField(default=ITEM_IN_PROGRESS, choices=[ (ITEM_IN_PROGRESS, "In progress"), (ITEM_DAMAGED, "Damaged"), - (ITEM_RETURNED, "Returned"), + (ITEM_ATTENTION, "Requires attention"), (ITEM_COMPLETE, "Complete") ]) diff --git a/InvenTree/supplier/admin.py b/InvenTree/supplier/admin.py index 4b76c26e0f..227c0a0d0a 100644 --- a/InvenTree/supplier/admin.py +++ b/InvenTree/supplier/admin.py @@ -1,9 +1,10 @@ from django.contrib import admin -from .models import Supplier, SupplierPart +from .models import Supplier, SupplierPart, Customer -class SupplierAdmin(admin.ModelAdmin): +class CompanyAdmin(admin.ModelAdmin): list_display=('name','URL','contact') -admin.site.register(Supplier, SupplierAdmin) +admin.site.register(Customer, CompanyAdmin) +admin.site.register(Supplier, CompanyAdmin) admin.site.register(SupplierPart) \ No newline at end of file diff --git a/InvenTree/supplier/models.py b/InvenTree/supplier/models.py index 9c560c9153..4be6f4315e 100644 --- a/InvenTree/supplier/models.py +++ b/InvenTree/supplier/models.py @@ -2,26 +2,17 @@ from __future__ import unicode_literals from django.db import models +from InvenTree.models import ExternalEntity from part.models import Part -class Supplier(models.Model): +class Supplier(ExternalEntity): """ Represents a manufacturer or supplier """ - name = models.CharField(max_length=100) - URL = models.URLField(blank=True) - address = models.CharField(max_length=200, - blank=True) - phone = models.CharField(max_length=50, - blank=True) - email = models.EmailField(blank=True) - contact = models.CharField(max_length=100, - blank=True) - notes = models.CharField(max_length=500, - blank=True) + pass - def __str__(self): - return self.name +class Customer(ExternalEntity): + pass class SupplierPart(models.Model): """ Represents a unique part as provided by a Supplier diff --git a/InvenTree/track/models.py b/InvenTree/track/models.py index 145ada6021..1060d3bf40 100644 --- a/InvenTree/track/models.py +++ b/InvenTree/track/models.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals from django.db import models from django.contrib.auth.models import User +from supplier.models import Customer from part.models import Part, PartRevision class UniquePart(models.Model): @@ -23,6 +24,26 @@ class UniquePart(models.Model): serial = models.IntegerField() createdBy = models.ForeignKey(User) + + customer = models.ForeignKey(Customer, blank=True, null=True) + + # Part status types + PART_IN_PROGRESS = 0 + PART_IN_STOCK = 10 + PART_SHIPPED = 20 + PART_RETURNED = 30 + PART_DAMAGED = 40 + PART_DESTROYED = 50 + + status = models.IntegerField(default=PART_IN_PROGRESS, + choices=[ + (PART_IN_PROGRESS, "In progress"), + (PART_IN_STOCK, "In stock"), + (PART_SHIPPED, "Shipped"), + (PART_RETURNED, "Returned"), + (PART_DAMAGED, "Damaged"), + (PART_DESTROYED, "Destroyed"), + ]) def __str__(self): return self.part.name From f3fbd8dc14e8c734466bdedb92088dee102e5b41 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 28 Mar 2017 23:00:40 +1100 Subject: [PATCH 3/8] Renamed Company --- InvenTree/InvenTree/models.py | 4 ++-- InvenTree/supplier/models.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/InvenTree/InvenTree/models.py b/InvenTree/InvenTree/models.py index f18ae41488..efbddf5fc6 100644 --- a/InvenTree/InvenTree/models.py +++ b/InvenTree/InvenTree/models.py @@ -4,8 +4,8 @@ from django.db import models from django.core.exceptions import ObjectDoesNotExist from django.contrib.contenttypes.models import ContentType -class ExternalEntity(models.Model): - """ Abstract model representing an external person / supplier / etc +class Company(models.Model): + """ Abstract model representing an external company """ class Meta: diff --git a/InvenTree/supplier/models.py b/InvenTree/supplier/models.py index 4be6f4315e..6af88f57a8 100644 --- a/InvenTree/supplier/models.py +++ b/InvenTree/supplier/models.py @@ -2,16 +2,16 @@ from __future__ import unicode_literals from django.db import models -from InvenTree.models import ExternalEntity +from InvenTree.models import Company from part.models import Part -class Supplier(ExternalEntity): +class Supplier(Company): """ Represents a manufacturer or supplier """ pass -class Customer(ExternalEntity): +class Customer(Company): pass class SupplierPart(models.Model): From b108c71eef9f30d145aa07e977ea89b0baae2d46 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 28 Mar 2017 23:03:06 +1100 Subject: [PATCH 4/8] limited to one python version --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2f8e9036d4..e480a71024 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,5 @@ language: python python: - - 2.7 - - 3.3 - - 3.4 - 3.5 before_install: From f4ef9d938e15937e88d603dcc466a88e42b91a0b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 28 Mar 2017 23:05:09 +1100 Subject: [PATCH 5/8] fixed python version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e480a71024..58ebe8deb1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: python python: - - 3.5 + - 3.3 before_install: - pip install pep8 From 09cb0676272a344e9430cedd411dc405391c5980 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 28 Mar 2017 23:09:51 +1100 Subject: [PATCH 6/8] PEP fixes --- InvenTree/supplier/models.py | 29 ++++++++++++++----------- InvenTree/track/models.py | 42 ++++++++++++++++++------------------ 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/InvenTree/supplier/models.py b/InvenTree/supplier/models.py index 6af88f57a8..e3d4a7546e 100644 --- a/InvenTree/supplier/models.py +++ b/InvenTree/supplier/models.py @@ -5,54 +5,57 @@ from django.db import models from InvenTree.models import Company from part.models import Part + class Supplier(Company): """ Represents a manufacturer or supplier """ pass - + + class Customer(Company): pass - + + class SupplierPart(models.Model): """ Represents a unique part as provided by a Supplier Each SupplierPart is identified by a MPN (Manufacturer Part Number) Each SupplierPart is also linked to a Part object - A Part may be available from multiple suppliers """ - + supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE) part = models.ForeignKey(Part, on_delete=models.CASCADE) - + MPN = models.CharField(max_length=100) URL = models.URLField(blank=True) description = models.CharField(max_length=250, blank=True) - + def __str__(self): return "{mpn} - {supplier}".format( mpn = self.MPN, supplier = self.supplier.name) - - + + class SupplierPriceBreak(models.Model): """ Represents a quantity price break for a SupplierPart - Suppliers can offer discounts at larger quantities - SupplierPart(s) may have zero-or-more associated SupplierPriceBreak(s) """ - + part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE) quantity = models.IntegerField() cost = models.DecimalField(max_digits=10, decimal_places=3) currency = models.CharField(max_length=10, blank=True) - + def __str__(self): return "{mpn} - {cost}{currency} @ {quan}".format( - mpn = part.MPN, - cost = self.cost, - currency = self.currency if self.currency else '', - quan = self.quantity) \ No newline at end of file + mpn=part.MPN, + cost=self.cost, + currency=self.currency if self.currency else '', + quan=self.quantity) \ No newline at end of file diff --git a/InvenTree/track/models.py b/InvenTree/track/models.py index 1060d3bf40..9f6ffb3525 100644 --- a/InvenTree/track/models.py +++ b/InvenTree/track/models.py @@ -6,27 +6,28 @@ from django.contrib.auth.models import User from supplier.models import Customer from part.models import Part, PartRevision + class UniquePart(models.Model): """ A unique instance of a Part object. Used for tracking parts based on serial numbers, and tracking all events in the life of a part """ - + part = models.ForeignKey(Part, on_delete=models.CASCADE) - + revision = models.ForeignKey(PartRevision, on_delete=models.CASCADE, blank=True, null=True) - + creation_date = models.DateField(auto_now_add=True, - editable=False) + editable=False) serial = models.IntegerField() - + createdBy = models.ForeignKey(User) - + customer = models.ForeignKey(Customer, blank=True, null=True) - + # Part status types PART_IN_PROGRESS = 0 PART_IN_STOCK = 10 @@ -34,29 +35,28 @@ class UniquePart(models.Model): PART_RETURNED = 30 PART_DAMAGED = 40 PART_DESTROYED = 50 - + status = models.IntegerField(default=PART_IN_PROGRESS, - choices=[ - (PART_IN_PROGRESS, "In progress"), - (PART_IN_STOCK, "In stock"), - (PART_SHIPPED, "Shipped"), - (PART_RETURNED, "Returned"), - (PART_DAMAGED, "Damaged"), - (PART_DESTROYED, "Destroyed"), - ]) + choices=[ + (PART_IN_PROGRESS, "In progress"), + (PART_IN_STOCK, "In stock"), + (PART_SHIPPED, "Shipped"), + (PART_RETURNED, "Returned"), + (PART_DAMAGED, "Damaged"), + (PART_DESTROYED, "Destroyed"), + ]) def __str__(self): return self.part.name - + + class PartTrackingInfo(models.Model): """ Single data-point in the life of a UniquePart Each time something happens to the UniquePart, a new PartTrackingInfo object should be created. """ - + part = models.ForeignKey(UniquePart, on_delete=models.CASCADE) date = models.DateField(auto_now_add=True, editable=False) - notes = models.CharField(max_length=500) - - \ No newline at end of file + notes = models.CharField(max_length=500) \ No newline at end of file From 8781b572c0e0f0ad753e8e3754686c28a0ac84aa Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 28 Mar 2017 23:10:43 +1100 Subject: [PATCH 7/8] bug fix for part serializer --- InvenTree/part/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 1174e6684b..57d544fa39 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -9,7 +9,7 @@ class PartSerializer(serializers.ModelSerializer): 'IPN', 'description', 'category', - 'quantity') + 'stock') class PartCategorySerializer(serializers.ModelSerializer): class Meta: From 9f34f9353ed55975d1bfc1ee2bc16843f1d420ae Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 28 Mar 2017 23:12:36 +1100 Subject: [PATCH 8/8] Ignore blank whitespace line --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 58ebe8deb1..2b3ac04c3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,4 @@ before_install: script: # TODO - Only perform PEP8 checks on files that have been changed in this push / PR - - find . -name \*.py -exec pep8 --ignore=E402 {} + \ No newline at end of file + - find . -name \*.py -exec pep8 --ignore=E402,W293 {} + \ No newline at end of file