diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index 9e71cb3e86..bab9e71793 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -4,7 +4,7 @@ from django.contrib import admin from rest_framework.documentation import include_docs_urls from part.urls import part_urls, part_cat_urls, part_param_urls, part_param_template_urls -from stock.urls import stock_urls, stock_loc_urls +from stock.urls import stock_urls, stock_loc_urls, stock_track_urls from project.urls import prj_urls, prj_part_urls, prj_cat_urls from supplier.urls import cust_urls, manu_urls, supplier_part_urls, price_break_urls, supplier_urls from track.urls import unique_urls, part_track_urls @@ -16,6 +16,7 @@ apipatterns = [ # Stock URLs url(r'^stock/', include(stock_urls)), url(r'^stock-location/', include(stock_loc_urls)), + url(r'^stock-track/', include(stock_track_urls)), # Part URLs url(r'^part/', include(part_urls)), diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index b7f3932c12..9984d1d74a 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from django.utils.translation import ugettext as _ from django.db import models +from supplier.models import SupplierPart from part.models import Part from InvenTree.models import InvenTreeTree @@ -18,6 +19,7 @@ class StockLocation(InvenTreeTree): 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() updated = models.DateField(auto_now=True) @@ -50,6 +52,8 @@ class StockItem(models.Model): default=ITEM_IN_STOCK, choices=ITEM_STATUS_CODES.items()) + notes = models.CharField(max_length=100, blank=True) + # If stock item is incoming, an (optional) ETA field expected_arrival = models.DateField(null=True, blank=True) @@ -58,3 +62,16 @@ class StockItem(models.Model): n=self.quantity, part=self.part.name, loc=self.location.name) + + +class StockTracking(models.Model): + """ Tracks a single movement of stock + - Used to track stock being taken from a location + - Used to track stock being added to a location + - "Pending" flag shows that stock WILL be taken / added + """ + + 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 5c69329f82..c7e958e4e9 100644 --- a/InvenTree/stock/serializers.py +++ b/InvenTree/stock/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from .models import StockItem, StockLocation +from .models import StockItem, StockLocation, StockTracking class StockItemSerializer(serializers.HyperlinkedModelSerializer): @@ -14,6 +14,7 @@ class StockItemSerializer(serializers.HyperlinkedModelSerializer): 'location', 'quantity', 'status', + 'notes', 'updated', 'last_checked', 'review_needed', @@ -31,3 +32,14 @@ class LocationSerializer(serializers.HyperlinkedModelSerializer): 'description', 'parent', 'path') + + +class StockTrackingSerializer(serializers.HyperlinkedModelSerializer): + + class Meta: + model = StockTracking + fields = ('url', + 'item', + 'quantity', + 'pending', + 'when') diff --git a/InvenTree/stock/urls.py b/InvenTree/stock/urls.py index bf58ddbb89..068f4f4d65 100644 --- a/InvenTree/stock/urls.py +++ b/InvenTree/stock/urls.py @@ -18,3 +18,11 @@ stock_loc_urls = [ url(r'^$', views.LocationList.as_view()) ] + +stock_track_urls = [ + url(r'^(?P[0-9]+)/?$', views.StockTrackingDetail.as_view(), name='stocktracking-detail'), + + url(r'^\?.*/?$', views.StockTrackingList.as_view()), + + url(r'^$', views.StockTrackingList.as_view()) +] diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 6c19410fc4..026c9084ab 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -4,8 +4,8 @@ from django_filters import NumberFilter from rest_framework import generics, permissions # from InvenTree.models import FilterChildren -from .models import StockLocation, StockItem -from .serializers import StockItemSerializer, LocationSerializer +from .models import StockLocation, StockItem, StockTracking +from .serializers import StockItemSerializer, LocationSerializer, StockTrackingSerializer class StockDetail(generics.RetrieveUpdateDestroyAPIView): @@ -100,3 +100,49 @@ class LocationList(generics.ListCreateAPIView): permission_classes = (permissions.IsAuthenticatedOrReadOnly,) filter_backends = (DjangoFilterBackend,) filter_class = StockLocationFilter + + +class StockTrackingDetail(generics.RetrieveUpdateDestroyAPIView): + """ + + get: + Return a single StockTracking object + + post: + Update a StockTracking object + + delete: + Remove a StockTracking object + + """ + + queryset = StockTracking.objects.all() + serializer_class = StockTrackingSerializer + permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + + +class StockTrackingFilter(FilterSet): + + item = NumberFilter(name='item', lookup_expr='exact') + + class Meta: + model = StockTracking + fields = ['item'] + + +class StockTrackingList(generics.ListCreateAPIView): + """ + + get: + Return a list of all StockTracking items + + post: + Create a new StockTracking item + + """ + + queryset = StockTracking.objects.all() + serializer_class = StockTrackingSerializer + permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + filter_backends = (DjangoFilterBackend,) + filter_class = StockTrackingFilter