diff --git a/.travis.yml b/.travis.yml index 2f8e9036d4..2b3ac04c3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,10 @@ language: python python: - - 2.7 - 3.3 - - 3.4 - - 3.5 before_install: - pip install pep8 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 diff --git a/InvenTree/InvenTree/models.py b/InvenTree/InvenTree/models.py index 6c2d10d866..efbddf5fc6 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 Company(models.Model): + """ Abstract model representing an external company + """ + + 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/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/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: 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..4442ea2525 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_ATTENTION = 20 + ITEM_COMPLETE = 50 + + status = models.IntegerField(default=ITEM_IN_PROGRESS, + choices=[ + (ITEM_IN_PROGRESS, "In progress"), + (ITEM_DAMAGED, "Damaged"), + (ITEM_ATTENTION, "Requires attention"), + (ITEM_COMPLETE, "Complete") + ]) + def __str__(self): return "{n} x {part} @ {loc}".format( n = self.quantity, 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..e3d4a7546e 100644 --- a/InvenTree/supplier/models.py +++ b/InvenTree/supplier/models.py @@ -2,66 +2,60 @@ from __future__ import unicode_literals from django.db import models +from InvenTree.models import Company from part.models import Part -class Supplier(models.Model): + +class Supplier(Company): """ 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) - - def __str__(self): - return self.name - + 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 145ada6021..9f6ffb3525 100644 --- a/InvenTree/track/models.py +++ b/InvenTree/track/models.py @@ -3,39 +3,60 @@ 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): """ 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 + 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 - + + 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