diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 5cfdbcf924..4488233196 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -34,6 +34,7 @@ ALLOWED_HOSTS = [] INSTALLED_APPS = [ 'django_filters', 'rest_framework', + 'simple_history', # Core django modules 'django.contrib.admin', @@ -59,6 +60,7 @@ MIDDLEWARE = [ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'simple_history.middleware.HistoryRequestMiddleware', ] ROOT_URLCONF = 'InvenTree.urls' diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index a81134e48f..2e808e3945 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, stock_track_urls +from stock.urls import stock_urls, stock_loc_urls from project.urls import prj_urls, prj_part_urls, prj_cat_urls, prj_run_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 @@ -17,7 +17,6 @@ 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/admin.py b/InvenTree/stock/admin.py index 6ce4df95c5..c5f560f7f2 100644 --- a/InvenTree/stock/admin.py +++ b/InvenTree/stock/admin.py @@ -1,4 +1,5 @@ from django.contrib import admin +from simple_history.admin import SimpleHistoryAdmin from .models import StockLocation, StockItem @@ -7,7 +8,7 @@ class LocationAdmin(admin.ModelAdmin): list_display = ('name', 'path', 'description') -class StockItemAdmin(admin.ModelAdmin): +class StockItemAdmin(SimpleHistoryAdmin): list_display = ('part', 'quantity', 'location', 'status', 'updated') diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index c4be02eb63..ea7cc09c71 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -1,8 +1,9 @@ from __future__ import unicode_literals from django.utils.translation import ugettext as _ -from django.db import models +from django.db import models, transaction from django.core.validators import MinValueValidator from django.contrib.auth.models import User +from simple_history.models import HistoricalRecords from supplier.models import SupplierPart from part.models import Part @@ -65,6 +66,10 @@ class StockItem(models.Model): infinite = models.BooleanField(default=False) + # History of this item + history = HistoricalRecords() + + @transaction.atomic def stocktake(self, count, user): """ Perform item stocktake. When the quantity of an item is counted, @@ -81,6 +86,7 @@ class StockItem(models.Model): self.stocktake_user = user self.save() + @transaction.atomic def add_stock(self, amount): """ Add items to stock This function can be called by initiating a ProjectRun, @@ -101,6 +107,7 @@ class StockItem(models.Model): self.quantity = q self.save() + @transaction.atomic def take_stock(self, amount): self.add_stock(-amount) @@ -109,15 +116,3 @@ 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() - when = models.DateTimeField(auto_now=True) diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py index fc0c6979fe..3502348298 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, StockTracking +from .models import StockItem, StockLocation class StockItemSerializer(serializers.HyperlinkedModelSerializer): @@ -25,7 +25,10 @@ class StockItemSerializer(serializers.HyperlinkedModelSerializer): """ These fields are read-only in this context. They can be updated by accessing the appropriate API endpoints """ - read_only_fields = ('stocktake_date', 'quantity',) + read_only_fields = ('stocktake_date', + 'stocktake_user', + 'updated', + 'quantity',) class StockQuantitySerializer(serializers.ModelSerializer): @@ -46,13 +49,3 @@ class LocationSerializer(serializers.HyperlinkedModelSerializer): 'description', 'parent', 'path') - - -class StockTrackingSerializer(serializers.HyperlinkedModelSerializer): - - class Meta: - model = StockTracking - fields = ('url', - 'item', - 'quantity', - 'when') diff --git a/InvenTree/stock/urls.py b/InvenTree/stock/urls.py index f8086c7eed..412bf23279 100644 --- a/InvenTree/stock/urls.py +++ b/InvenTree/stock/urls.py @@ -26,11 +26,3 @@ 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 4dab2f4546..99c2a246da 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -4,9 +4,9 @@ from django_filters import NumberFilter from rest_framework import generics, permissions, response # from InvenTree.models import FilterChildren -from .models import StockLocation, StockItem, StockTracking +from .models import StockLocation, StockItem from .serializers import StockItemSerializer, StockQuantitySerializer -from .serializers import LocationSerializer, StockTrackingSerializer +from .serializers import LocationSerializer class StockDetail(generics.RetrieveUpdateDestroyAPIView): @@ -127,47 +127,3 @@ 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): - - 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 diff --git a/requirements/base.txt b/requirements/base.txt index d80fe55e91..d9c16d5ecc 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,5 +1,6 @@ Django==1.11 djangorestframework==3.6.2 django_filter==1.0.2 +django-simple-history==1.8.2 coreapi==2.3.0 pygments==2.2.0