mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
commit
146ad5e9ae
@ -17,13 +17,13 @@ def Inventree404(self):
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^stock/', include('stock.urls')),
|
||||
url(r'^part/', include('part.urls')),
|
||||
url(r'^supplier/', include('supplier.urls')),
|
||||
url(r'^track/', include('track.urls')),
|
||||
url(r'^project/', include('project.urls')),
|
||||
url(r'^admin/', admin.site.urls),
|
||||
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
|
||||
url(r'^stock/?', include('stock.urls')),
|
||||
url(r'^part/?', include('part.urls')),
|
||||
url(r'^supplier/?', include('supplier.urls')),
|
||||
url(r'^track/?', include('track.urls')),
|
||||
url(r'^project/?', include('project.urls')),
|
||||
url(r'^admin/?', admin.site.urls),
|
||||
url(r'^auth/?', include('rest_framework.urls', namespace='rest_framework')),
|
||||
|
||||
# Any other URL
|
||||
url(r'', Inventree404)
|
||||
|
@ -149,11 +149,9 @@ class PartParameterManager(models.Manager):
|
||||
part_id = kwargs['part']
|
||||
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]
|
||||
except:
|
||||
pass
|
||||
|
||||
return super(PartParameterManager, self).create(*args, **kwargs)
|
||||
|
||||
|
@ -32,16 +32,11 @@ class PartSerializer(serializers.ModelSerializer):
|
||||
'stock')
|
||||
|
||||
|
||||
class PartCategoryBriefSerializer(serializers.ModelSerializer):
|
||||
class PartCategorySerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = PartCategory
|
||||
fields = ('pk',
|
||||
'name',
|
||||
'description')
|
||||
children = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
|
||||
|
||||
class PartCategoryDetailSerializer(serializers.ModelSerializer):
|
||||
parts = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = PartCategory
|
||||
@ -49,7 +44,9 @@ class PartCategoryDetailSerializer(serializers.ModelSerializer):
|
||||
'name',
|
||||
'description',
|
||||
'parent',
|
||||
'path')
|
||||
'path',
|
||||
'children',
|
||||
'parts')
|
||||
|
||||
|
||||
class PartTemplateSerializer(serializers.ModelSerializer):
|
||||
|
@ -5,7 +5,7 @@ from rest_framework import generics, permissions
|
||||
from InvenTree.models import FilterChildren
|
||||
from .models import PartCategory, Part, PartParameter, PartParameterTemplate
|
||||
from .serializers import PartSerializer
|
||||
from .serializers import PartCategoryDetailSerializer
|
||||
from .serializers import PartCategorySerializer
|
||||
from .serializers import PartParameterSerializer
|
||||
from .serializers import PartTemplateSerializer
|
||||
|
||||
@ -32,13 +32,6 @@ class PartParamList(generics.ListCreateAPIView):
|
||||
serializer_class = PartParameterSerializer
|
||||
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):
|
||||
""" Detail view of a single PartParameter
|
||||
@ -83,7 +76,7 @@ class PartCategoryDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
""" Return information on a single PartCategory
|
||||
"""
|
||||
queryset = PartCategory.objects.all()
|
||||
serializer_class = PartCategoryDetailSerializer
|
||||
serializer_class = PartCategorySerializer
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||
|
||||
|
||||
@ -102,7 +95,7 @@ class PartCategoryList(generics.ListCreateAPIView):
|
||||
return categories
|
||||
|
||||
queryset = PartCategory.objects.filter(parent=None)
|
||||
serializer_class = PartCategoryDetailSerializer
|
||||
serializer_class = PartCategorySerializer
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||
|
||||
|
||||
|
@ -29,17 +29,11 @@ class Project(models.Model):
|
||||
|
||||
name = models.CharField(max_length=100)
|
||||
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):
|
||||
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):
|
||||
""" Manager for handling ProjectParts
|
||||
@ -56,11 +50,9 @@ class ProjectPartManager(models.Manager):
|
||||
project_id = kwargs['project']
|
||||
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]
|
||||
except:
|
||||
pass
|
||||
|
||||
return super(ProjectPartManager, self).create(*args, **kwargs)
|
||||
|
||||
|
@ -26,14 +26,11 @@ class ProjectSerializer(serializers.ModelSerializer):
|
||||
'category')
|
||||
|
||||
|
||||
class ProjectCategoryBriefSerializer(serializers.ModelSerializer):
|
||||
class ProjectCategorySerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = ProjectCategory
|
||||
fields = ('pk', 'name', 'description')
|
||||
children = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
|
||||
|
||||
class ProjectCategoryDetailSerializer(serializers.ModelSerializer):
|
||||
projects = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = ProjectCategory
|
||||
@ -41,4 +38,6 @@ class ProjectCategoryDetailSerializer(serializers.ModelSerializer):
|
||||
'name',
|
||||
'description',
|
||||
'parent',
|
||||
'path')
|
||||
'path',
|
||||
'children',
|
||||
'projects')
|
||||
|
@ -3,7 +3,7 @@ from rest_framework import generics, permissions
|
||||
from InvenTree.models import FilterChildren
|
||||
from .models import ProjectCategory, Project, ProjectPart
|
||||
from .serializers import ProjectSerializer
|
||||
from .serializers import ProjectCategoryDetailSerializer
|
||||
from .serializers import ProjectCategorySerializer
|
||||
from .serializers import ProjectPartSerializer
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ class ProjectCategoryDetail(generics.RetrieveUpdateAPIView):
|
||||
"""
|
||||
|
||||
queryset = ProjectCategory.objects.all()
|
||||
serializer_class = ProjectCategoryDetailSerializer
|
||||
serializer_class = ProjectCategorySerializer
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||
|
||||
|
||||
@ -57,7 +57,7 @@ class ProjectCategoryList(generics.ListCreateAPIView):
|
||||
|
||||
return categories
|
||||
|
||||
serializer_class = ProjectCategoryDetailSerializer
|
||||
serializer_class = ProjectCategorySerializer
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||
|
||||
|
||||
@ -82,13 +82,6 @@ class ProjectPartsList(generics.ListCreateAPIView):
|
||||
|
||||
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):
|
||||
""" Detail for a single project part
|
||||
|
@ -45,13 +45,6 @@ class StockList(generics.ListCreateAPIView):
|
||||
|
||||
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):
|
||||
""" Return information on a specific stock location
|
||||
|
@ -1,4 +1,5 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
@ -7,12 +8,37 @@ from supplier.models import Customer
|
||||
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):
|
||||
""" 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
|
||||
"""
|
||||
|
||||
objects = UniquePartManager()
|
||||
|
||||
part = models.ForeignKey(Part, on_delete=models.CASCADE)
|
||||
|
||||
revision = models.ForeignKey(PartRevision,
|
||||
@ -50,6 +76,17 @@ class UniquePart(models.Model):
|
||||
def __str__(self):
|
||||
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):
|
||||
""" Single data-point in the life of a UniquePart
|
||||
@ -57,7 +94,6 @@ class PartTrackingInfo(models.Model):
|
||||
a new PartTrackingInfo object should be created.
|
||||
"""
|
||||
|
||||
part = models.ForeignKey(UniquePart, on_delete=models.CASCADE)
|
||||
date = models.DateField(auto_now_add=True,
|
||||
editable=False)
|
||||
part = models.ForeignKey(UniquePart, on_delete=models.CASCADE, related_name='tracking_info')
|
||||
date = models.DateField(auto_now_add=True, editable=False)
|
||||
notes = models.CharField(max_length=500)
|
||||
|
27
InvenTree/track/serializers.py
Normal file
27
InvenTree/track/serializers.py
Normal 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__'
|
@ -1,7 +1,19 @@
|
||||
from django.conf.urls import url
|
||||
from django.conf.urls import url, include
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.index, name='index')
|
||||
infopatterns = [
|
||||
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()),
|
||||
]
|
||||
|
@ -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):
|
||||
return HttpResponse("This is the Tracking page")
|
||||
class UniquePartDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user