2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-29 12:06:44 +00:00

Merge pull request #38 from SchrodingersGat/master

Added /track/ api
This commit is contained in:
Oliver 2017-04-14 16:15:10 +10:00 committed by GitHub
commit 146ad5e9ae
12 changed files with 184 additions and 74 deletions

View File

@ -17,13 +17,13 @@ def Inventree404(self):
urlpatterns = [ urlpatterns = [
url(r'^stock/', include('stock.urls')), url(r'^stock/?', include('stock.urls')),
url(r'^part/', include('part.urls')), url(r'^part/?', include('part.urls')),
url(r'^supplier/', include('supplier.urls')), url(r'^supplier/?', include('supplier.urls')),
url(r'^track/', include('track.urls')), url(r'^track/?', include('track.urls')),
url(r'^project/', include('project.urls')), url(r'^project/?', include('project.urls')),
url(r'^admin/', admin.site.urls), url(r'^admin/?', admin.site.urls),
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')), url(r'^auth/?', include('rest_framework.urls', namespace='rest_framework')),
# Any other URL # Any other URL
url(r'', Inventree404) url(r'', Inventree404)

View File

@ -149,11 +149,9 @@ class PartParameterManager(models.Manager):
part_id = kwargs['part'] part_id = kwargs['part']
template_id = kwargs['template'] template_id = kwargs['template']
try: params = self.filter(part=part_id, template=template_id)
params = self.filter(part=part_id, template=template_id) if len(params) > 0:
return params[0] return params[0]
except:
pass
return super(PartParameterManager, self).create(*args, **kwargs) return super(PartParameterManager, self).create(*args, **kwargs)

View File

@ -32,16 +32,11 @@ class PartSerializer(serializers.ModelSerializer):
'stock') 'stock')
class PartCategoryBriefSerializer(serializers.ModelSerializer): class PartCategorySerializer(serializers.ModelSerializer):
class Meta: children = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
model = PartCategory
fields = ('pk',
'name',
'description')
parts = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class PartCategoryDetailSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = PartCategory model = PartCategory
@ -49,7 +44,9 @@ class PartCategoryDetailSerializer(serializers.ModelSerializer):
'name', 'name',
'description', 'description',
'parent', 'parent',
'path') 'path',
'children',
'parts')
class PartTemplateSerializer(serializers.ModelSerializer): class PartTemplateSerializer(serializers.ModelSerializer):

View File

@ -5,7 +5,7 @@ from rest_framework import generics, permissions
from InvenTree.models import FilterChildren from InvenTree.models import FilterChildren
from .models import PartCategory, Part, PartParameter, PartParameterTemplate from .models import PartCategory, Part, PartParameter, PartParameterTemplate
from .serializers import PartSerializer from .serializers import PartSerializer
from .serializers import PartCategoryDetailSerializer from .serializers import PartCategorySerializer
from .serializers import PartParameterSerializer from .serializers import PartParameterSerializer
from .serializers import PartTemplateSerializer from .serializers import PartTemplateSerializer
@ -32,13 +32,6 @@ class PartParamList(generics.ListCreateAPIView):
serializer_class = PartParameterSerializer serializer_class = PartParameterSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,) permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def create(self, request, *args, **kwargs):
# Ensure part link is set correctly
part_id = self.request.query_params.get('part', None)
if part_id:
request.data['part'] = part_id
return super(PartParamList, self).create(request, *args, **kwargs)
class PartParamDetail(generics.RetrieveUpdateDestroyAPIView): class PartParamDetail(generics.RetrieveUpdateDestroyAPIView):
""" Detail view of a single PartParameter """ Detail view of a single PartParameter
@ -83,7 +76,7 @@ class PartCategoryDetail(generics.RetrieveUpdateDestroyAPIView):
""" Return information on a single PartCategory """ Return information on a single PartCategory
""" """
queryset = PartCategory.objects.all() queryset = PartCategory.objects.all()
serializer_class = PartCategoryDetailSerializer serializer_class = PartCategorySerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,) permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
@ -102,7 +95,7 @@ class PartCategoryList(generics.ListCreateAPIView):
return categories return categories
queryset = PartCategory.objects.filter(parent=None) queryset = PartCategory.objects.filter(parent=None)
serializer_class = PartCategoryDetailSerializer serializer_class = PartCategorySerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,) permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

View File

@ -29,17 +29,11 @@ class Project(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
description = models.CharField(max_length=500, blank=True) description = models.CharField(max_length=500, blank=True)
category = models.ForeignKey(ProjectCategory, on_delete=models.CASCADE) category = models.ForeignKey(ProjectCategory, on_delete=models.CASCADE, related_name='projects')
def __str__(self): def __str__(self):
return self.name return self.name
@property
def projectParts(self):
""" Return a list of all project parts associated with this project
"""
return self.projectpart_set.all()
class ProjectPartManager(models.Manager): class ProjectPartManager(models.Manager):
""" Manager for handling ProjectParts """ Manager for handling ProjectParts
@ -56,11 +50,9 @@ class ProjectPartManager(models.Manager):
project_id = kwargs['project'] project_id = kwargs['project']
part_id = kwargs['part'] part_id = kwargs['part']
try: project_parts = self.filter(project=project_id, part=part_id)
project_parts = self.filter(project=project_id, part=part_id) if len(project_parts) > 0:
return project_parts[0] return project_parts[0]
except:
pass
return super(ProjectPartManager, self).create(*args, **kwargs) return super(ProjectPartManager, self).create(*args, **kwargs)

View File

@ -26,14 +26,11 @@ class ProjectSerializer(serializers.ModelSerializer):
'category') 'category')
class ProjectCategoryBriefSerializer(serializers.ModelSerializer): class ProjectCategorySerializer(serializers.ModelSerializer):
class Meta: children = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
model = ProjectCategory
fields = ('pk', 'name', 'description')
projects = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class ProjectCategoryDetailSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = ProjectCategory model = ProjectCategory
@ -41,4 +38,6 @@ class ProjectCategoryDetailSerializer(serializers.ModelSerializer):
'name', 'name',
'description', 'description',
'parent', 'parent',
'path') 'path',
'children',
'projects')

View File

@ -3,7 +3,7 @@ from rest_framework import generics, permissions
from InvenTree.models import FilterChildren from InvenTree.models import FilterChildren
from .models import ProjectCategory, Project, ProjectPart from .models import ProjectCategory, Project, ProjectPart
from .serializers import ProjectSerializer from .serializers import ProjectSerializer
from .serializers import ProjectCategoryDetailSerializer from .serializers import ProjectCategorySerializer
from .serializers import ProjectPartSerializer from .serializers import ProjectPartSerializer
@ -40,7 +40,7 @@ class ProjectCategoryDetail(generics.RetrieveUpdateAPIView):
""" """
queryset = ProjectCategory.objects.all() queryset = ProjectCategory.objects.all()
serializer_class = ProjectCategoryDetailSerializer serializer_class = ProjectCategorySerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,) permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
@ -57,7 +57,7 @@ class ProjectCategoryList(generics.ListCreateAPIView):
return categories return categories
serializer_class = ProjectCategoryDetailSerializer serializer_class = ProjectCategorySerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,) permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
@ -82,13 +82,6 @@ class ProjectPartsList(generics.ListCreateAPIView):
return parts return parts
def create(self, request, *args, **kwargs):
# Ensure project link is set correctly
prj_id = self.request.query_params.get('project', None)
if prj_id:
request.data['project'] = prj_id
return super(ProjectPartsList, self).create(request, *args, **kwargs)
class ProjectPartDetail(generics.RetrieveUpdateDestroyAPIView): class ProjectPartDetail(generics.RetrieveUpdateDestroyAPIView):
""" Detail for a single project part """ Detail for a single project part

View File

@ -45,13 +45,6 @@ class StockList(generics.ListCreateAPIView):
return items return items
def create(self, request, *args, **kwargs):
# If the PART parameter is passed in the URL, use that
part_id = self.request.query_params.get('part', None)
if part_id:
request.data['part'] = part_id
return super(StockList, self).create(request, *args, **kwargs)
class LocationDetail(generics.RetrieveUpdateDestroyAPIView): class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
""" Return information on a specific stock location """ Return information on a specific stock location

View File

@ -1,4 +1,5 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.db import models from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -7,12 +8,37 @@ from supplier.models import Customer
from part.models import Part, PartRevision from part.models import Part, PartRevision
class UniquePartManager(models.Manager):
""" Ensures UniqueParts are correctly handled
"""
def create(self, *args, **kwargs):
part_id = kwargs['part']
sn = kwargs.get('serial', None)
if not sn:
raise ValidationError(_("Serial number must be supplied"))
if not isinstance(sn, int):
raise ValidationError(_("Serial number must be integer"))
# Does a part already exists with this serial number?
parts = self.filter(part=part_id, serial=sn)
if len(parts) > 0:
raise ValidationError(_("Matching part and serial number found!"))
return super(UniquePartManager, self).create(*args, **kwargs)
class UniquePart(models.Model): class UniquePart(models.Model):
""" A unique instance of a Part object. """ A unique instance of a Part object.
Used for tracking parts based on serial numbers, Used for tracking parts based on serial numbers,
and tracking all events in the life of a part and tracking all events in the life of a part
""" """
objects = UniquePartManager()
part = models.ForeignKey(Part, on_delete=models.CASCADE) part = models.ForeignKey(Part, on_delete=models.CASCADE)
revision = models.ForeignKey(PartRevision, revision = models.ForeignKey(PartRevision,
@ -50,6 +76,17 @@ class UniquePart(models.Model):
def __str__(self): def __str__(self):
return self.part.name return self.part.name
def save(self, *args, **kwargs):
# Disallow saving a serial number that already exists
matches = UniquePart.objects.filter(serial=self.serial, part=self.part)
matches = matches.filter(~models.Q(id=self.id))
if len(matches) > 0:
raise ValidationError(_("Matching serial number already exists"))
super(UniquePart, self).save(*args, **kwargs)
class PartTrackingInfo(models.Model): class PartTrackingInfo(models.Model):
""" Single data-point in the life of a UniquePart """ Single data-point in the life of a UniquePart
@ -57,7 +94,6 @@ class PartTrackingInfo(models.Model):
a new PartTrackingInfo object should be created. a new PartTrackingInfo object should be created.
""" """
part = models.ForeignKey(UniquePart, on_delete=models.CASCADE) part = models.ForeignKey(UniquePart, on_delete=models.CASCADE, related_name='tracking_info')
date = models.DateField(auto_now_add=True, date = models.DateField(auto_now_add=True, editable=False)
editable=False)
notes = models.CharField(max_length=500) notes = models.CharField(max_length=500)

View File

@ -0,0 +1,27 @@
from rest_framework import serializers
from .models import UniquePart, PartTrackingInfo
class UniquePartSerializer(serializers.ModelSerializer):
tracking_info = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = UniquePart
fields = ['pk',
'part',
'revision',
'creation_date',
'serial',
'createdBy',
'customer',
'status',
'tracking_info']
class PartTrackingInfoSerializer(serializers.ModelSerializer):
class Meta:
model = PartTrackingInfo
fields = '__all__'

View File

@ -1,7 +1,19 @@
from django.conf.urls import url from django.conf.urls import url, include
from . import views from . import views
urlpatterns = [ infopatterns = [
url(r'^$', views.index, name='index') url(r'^(?P<pk>[0-9]+)/?$', views.PartTrackingDetail.as_view()),
url(r'^\?*[^/]*/?$', views.PartTrackingList.as_view())
]
urlpatterns = [
url(r'info/?', include(infopatterns)),
# Detail for a single unique part
url(r'^(?P<pk>[0-9]+)$', views.UniquePartDetail.as_view()),
# List all unique parts, with optional filters
url(r'^\?*[^/]*/?$', views.UniquePartList.as_view()),
] ]

View File

@ -1,5 +1,75 @@
from django.http import HttpResponse import django_filters
from rest_framework import generics, permissions
from .models import UniquePart, PartTrackingInfo
from .serializers import UniquePartSerializer, PartTrackingInfoSerializer
def index(request): class UniquePartDetail(generics.RetrieveUpdateDestroyAPIView):
return HttpResponse("This is the Tracking page")
queryset = UniquePart.objects.all()
serializer_class = UniquePartSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
class UniquePartFilter(django_filters.rest_framework.FilterSet):
# Filter based on serial number
min_sn = django_filters.NumberFilter(name='serial', lookup_expr='gte')
max_sn = django_filters.NumberFilter(name='serial', lookup_expr='lte')
class Meta:
model = UniquePart
fields = ['serial', ]
class UniquePartList(generics.ListCreateAPIView):
serializer_class = UniquePartSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
filter_backends = (django_filters.rest_framework.DjangoFilterBackend,)
filter_class = UniquePartFilter
def get_queryset(self):
parts = UniquePart.objects.all()
query = self.request.query_params
# Filter by associated part
part_id = query.get('part', None)
if part_id:
parts = parts.filter(part=part_id)
# Filter by serial number
sn = query.get('sn', None)
if sn:
parts = parts.filter(serial=sn)
# Filter by customer
customer = query.get('customer', None)
if customer:
parts = parts.filter(customer=customer)
return parts
class PartTrackingDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = PartTrackingInfo.objects.all()
serializer_class = PartTrackingInfoSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
class PartTrackingList(generics.ListCreateAPIView):
serializer_class = PartTrackingInfoSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def get_queryset(self):
tracking = PartTrackingInfo.objects.all()
query = self.request.query_params
part_id = query.get('part', None)
if part_id:
tracking = tracking.filter(part=part_id)
return tracking