mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #118 from SchrodingersGat/travis-fix
Updated travis environment
This commit is contained in:
commit
a465e990b9
@ -1,11 +1,18 @@
|
|||||||
|
dist: xenial
|
||||||
|
|
||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
- 3.5
|
- 3.5
|
||||||
|
- 3.6
|
||||||
|
|
||||||
|
addons:
|
||||||
|
apt-packages:
|
||||||
|
-sqlite3
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- make setup
|
- make setup
|
||||||
- make setup_ci
|
- make setup_ci
|
||||||
|
|
||||||
script:
|
script:
|
||||||
# - make style
|
|
||||||
- make test
|
- make test
|
||||||
|
- make style
|
||||||
|
@ -11,4 +11,4 @@ class HelperForm(forms.ModelForm):
|
|||||||
super(forms.ModelForm, self).__init__(*args, **kwargs)
|
super(forms.ModelForm, self).__init__(*args, **kwargs)
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
|
|
||||||
self.helper.form_tag = False
|
self.helper.form_tag = False
|
||||||
|
@ -23,4 +23,4 @@ class AuthRequiredMiddleware(object):
|
|||||||
# Code to be executed for each request/response after
|
# Code to be executed for each request/response after
|
||||||
# the view is called.
|
# the view is called.
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
@ -3,10 +3,10 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
from rest_framework import mixins
|
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(serializers.ModelSerializer):
|
class UserSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -33,4 +33,4 @@ class DraftRUDView(generics.RetrieveAPIView, generics.UpdateAPIView, generics.De
|
|||||||
if ctx_data.get('_is_final', False) in [True, u'true', u'True', 1]:
|
if ctx_data.get('_is_final', False) in [True, u'true', u'True', 1]:
|
||||||
super(generics.UpdateAPIView, self).perform_update(serializer)
|
super(generics.UpdateAPIView, self).perform_update(serializer)
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
@ -9,7 +9,6 @@ from django.views.generic import UpdateView, CreateView, DeleteView
|
|||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
|
|
||||||
from rest_framework import views
|
from rest_framework import views
|
||||||
from django.http import JsonResponse
|
|
||||||
|
|
||||||
|
|
||||||
class TreeSerializer(views.APIView):
|
class TreeSerializer(views.APIView):
|
||||||
@ -159,9 +158,6 @@ class AjaxUpdateView(AjaxMixin, UpdateView):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
|
||||||
response = super(UpdateView, self).get(request, *args, **kwargs)
|
|
||||||
|
|
||||||
if request.is_ajax():
|
if request.is_ajax():
|
||||||
form = self.form_class(instance=self.get_object())
|
form = self.form_class(instance=self.get_object())
|
||||||
|
|
||||||
|
@ -16,6 +16,6 @@ class EditBuildForm(HelperForm):
|
|||||||
'quantity',
|
'quantity',
|
||||||
'batch',
|
'batch',
|
||||||
'notes',
|
'notes',
|
||||||
# 'status',
|
# 'status',
|
||||||
# 'completion_date',
|
# 'completion_date',
|
||||||
]
|
]
|
||||||
|
@ -7,11 +7,9 @@ from django.views.generic import DetailView, ListView
|
|||||||
|
|
||||||
from part.models import Part
|
from part.models import Part
|
||||||
from .models import Build
|
from .models import Build
|
||||||
|
|
||||||
from .forms import EditBuildForm
|
from .forms import EditBuildForm
|
||||||
|
|
||||||
from InvenTree.views import AjaxView, AjaxUpdateView, AjaxCreateView
|
from InvenTree.views import AjaxView, AjaxUpdateView, AjaxCreateView
|
||||||
from django.http import JsonResponse
|
|
||||||
|
|
||||||
|
|
||||||
class BuildIndex(ListView):
|
class BuildIndex(ListView):
|
||||||
|
@ -31,4 +31,4 @@ class CompanyImageForm(HelperForm):
|
|||||||
model = Company
|
model = Company
|
||||||
fields = [
|
fields = [
|
||||||
'image'
|
'image'
|
||||||
]
|
]
|
||||||
|
@ -78,4 +78,4 @@ class Contact(models.Model):
|
|||||||
role = models.CharField(max_length=100, blank=True)
|
role = models.CharField(max_length=100, blank=True)
|
||||||
|
|
||||||
company = models.ForeignKey(Company, related_name='contacts',
|
company = models.ForeignKey(Company, related_name='contacts',
|
||||||
on_delete = models.CASCADE)
|
on_delete=models.CASCADE)
|
||||||
|
@ -15,6 +15,7 @@ class CompanyBriefSerializer(serializers.ModelSerializer):
|
|||||||
'name'
|
'name'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CompanySerializer(serializers.ModelSerializer):
|
class CompanySerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
|
@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import filters
|
from rest_framework import filters
|
||||||
from rest_framework import generics, permissions, mixins
|
from rest_framework import generics, permissions
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url, include
|
||||||
|
|
||||||
@ -17,6 +17,7 @@ from .serializers import CategorySerializer
|
|||||||
from InvenTree.views import TreeSerializer
|
from InvenTree.views import TreeSerializer
|
||||||
from InvenTree.serializers import DraftRUDView
|
from InvenTree.serializers import DraftRUDView
|
||||||
|
|
||||||
|
|
||||||
class PartCategoryTree(TreeSerializer):
|
class PartCategoryTree(TreeSerializer):
|
||||||
|
|
||||||
title = "Parts"
|
title = "Parts"
|
||||||
@ -33,7 +34,7 @@ class CategoryList(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
filter_backends = [
|
filter_backends = [
|
||||||
DjangoFilterBackend,
|
DjangoFilterBackend,
|
||||||
#filters.SearchFilter,
|
# filters.SearchFilter,
|
||||||
filters.OrderingFilter,
|
filters.OrderingFilter,
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -61,6 +62,7 @@ class PartDetail(DraftRUDView):
|
|||||||
permissions.IsAuthenticatedOrReadOnly,
|
permissions.IsAuthenticatedOrReadOnly,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartList(generics.ListCreateAPIView):
|
class PartList(generics.ListCreateAPIView):
|
||||||
|
|
||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
@ -133,13 +135,12 @@ class SupplierPartList(generics.ListAPIView):
|
|||||||
'supplier'
|
'supplier'
|
||||||
]
|
]
|
||||||
|
|
||||||
cat_api_urls = [
|
|
||||||
|
|
||||||
|
cat_api_urls = [
|
||||||
url(r'^$', CategoryList.as_view(), name='api-part-category-list'),
|
url(r'^$', CategoryList.as_view(), name='api-part-category-list'),
|
||||||
]
|
]
|
||||||
|
|
||||||
part_api_urls = [
|
part_api_urls = [
|
||||||
|
|
||||||
url(r'^tree/?', PartCategoryTree.as_view(), name='api-part-tree'),
|
url(r'^tree/?', PartCategoryTree.as_view(), name='api-part-tree'),
|
||||||
|
|
||||||
url(r'^category/', include(cat_api_urls)),
|
url(r'^category/', include(cat_api_urls)),
|
||||||
|
@ -7,7 +7,6 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Sum
|
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
|
|
||||||
from django.db.models.signals import pre_delete
|
from django.db.models.signals import pre_delete
|
||||||
@ -250,7 +249,6 @@ class Part(models.Model):
|
|||||||
self.allocated_build_count,
|
self.allocated_build_count,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def stock_entries(self):
|
def stock_entries(self):
|
||||||
return [loc for loc in self.locations.all() if loc.in_stock]
|
return [loc for loc in self.locations.all() if loc.in_stock]
|
||||||
@ -263,7 +261,6 @@ class Part(models.Model):
|
|||||||
|
|
||||||
return sum([loc.quantity for loc in self.stock_entries])
|
return sum([loc.quantity for loc in self.stock_entries])
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_bom(self):
|
def has_bom(self):
|
||||||
return self.bom_count > 0
|
return self.bom_count > 0
|
||||||
|
@ -5,6 +5,7 @@ from .models import SupplierPart
|
|||||||
|
|
||||||
from company.serializers import CompanyBriefSerializer
|
from company.serializers import CompanyBriefSerializer
|
||||||
|
|
||||||
|
|
||||||
class CategorySerializer(serializers.ModelSerializer):
|
class CategorySerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
@ -99,4 +100,4 @@ class SupplierPartSerializer(serializers.ModelSerializer):
|
|||||||
'SKU',
|
'SKU',
|
||||||
'manufacturer',
|
'manufacturer',
|
||||||
'MPN',
|
'MPN',
|
||||||
]
|
]
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
from django_filters.rest_framework import FilterSet, DjangoFilterBackend
|
from django_filters.rest_framework import FilterSet, DjangoFilterBackend
|
||||||
from django_filters import NumberFilter
|
from django_filters import NumberFilter
|
||||||
|
|
||||||
from rest_framework import generics, permissions, response, filters
|
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url, include
|
||||||
|
|
||||||
# from InvenTree.models import FilterChildren
|
|
||||||
from .models import StockLocation, StockItem
|
from .models import StockLocation, StockItem
|
||||||
from .models import StockItemTracking
|
from .models import StockItemTracking
|
||||||
|
|
||||||
@ -19,8 +16,8 @@ from InvenTree.serializers import DraftRUDView
|
|||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework import authentication, permissions
|
from rest_framework import generics, response, filters, permissions
|
||||||
from django.contrib.auth.models import User
|
|
||||||
|
|
||||||
class StockCategoryTree(TreeSerializer):
|
class StockCategoryTree(TreeSerializer):
|
||||||
title = 'Stock'
|
title = 'Stock'
|
||||||
@ -69,26 +66,26 @@ class StockStocktake(APIView):
|
|||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
||||||
if not 'action' in request.data:
|
if 'action' not in request.data:
|
||||||
raise ValidationError({'action': 'Stocktake action must be provided'})
|
raise ValidationError({'action': 'Stocktake action must be provided'})
|
||||||
|
|
||||||
action = request.data['action']
|
action = request.data['action']
|
||||||
|
|
||||||
ACTIONS = ['stocktake', 'remove', 'add']
|
ACTIONS = ['stocktake', 'remove', 'add']
|
||||||
|
|
||||||
if not action in ACTIONS:
|
if action not in ACTIONS:
|
||||||
raise ValidationError({'action': 'Action must be one of ' + ','.join(ACTIONS)})
|
raise ValidationError({'action': 'Action must be one of ' + ','.join(ACTIONS)})
|
||||||
|
|
||||||
if not 'items[]' in request.data:
|
elif 'items[]' not in request.data:
|
||||||
raise ValidationError({'items[]:' 'Request must contain list of items'})
|
raise ValidationError({'items[]:' 'Request must contain list of items'})
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
|
|
||||||
# Ensure each entry is valid
|
# Ensure each entry is valid
|
||||||
for entry in request.data['items[]']:
|
for entry in request.data['items[]']:
|
||||||
if not 'pk' in entry:
|
if 'pk' not in entry:
|
||||||
raise ValidationError({'pk': 'Each entry must contain pk field'})
|
raise ValidationError({'pk': 'Each entry must contain pk field'})
|
||||||
if not 'quantity' in entry:
|
elif 'quantity' not in entry:
|
||||||
raise ValidationError({'quantity': 'Each entry must contain quantity field'})
|
raise ValidationError({'quantity': 'Each entry must contain quantity field'})
|
||||||
|
|
||||||
item = {}
|
item = {}
|
||||||
@ -112,7 +109,6 @@ class StockStocktake(APIView):
|
|||||||
if 'notes' in request.data:
|
if 'notes' in request.data:
|
||||||
notes = request.data['notes']
|
notes = request.data['notes']
|
||||||
|
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
quantity = int(item['quantity'])
|
quantity = int(item['quantity'])
|
||||||
|
|
||||||
@ -136,7 +132,7 @@ class StockMove(APIView):
|
|||||||
|
|
||||||
data = request.data
|
data = request.data
|
||||||
|
|
||||||
if not u'location' in data:
|
if u'location' not in data:
|
||||||
raise ValidationError({'location': 'Destination must be specified'})
|
raise ValidationError({'location': 'Destination must be specified'})
|
||||||
|
|
||||||
loc_id = data.get(u'location')
|
loc_id = data.get(u'location')
|
||||||
@ -146,7 +142,7 @@ class StockMove(APIView):
|
|||||||
except StockLocation.DoesNotExist:
|
except StockLocation.DoesNotExist:
|
||||||
raise ValidationError({'location': 'Location does not exist'})
|
raise ValidationError({'location': 'Location does not exist'})
|
||||||
|
|
||||||
if not u'parts[]' in data:
|
if u'parts[]' not in data:
|
||||||
raise ValidationError({'parts[]': 'Parts list must be specified'})
|
raise ValidationError({'parts[]': 'Parts list must be specified'})
|
||||||
|
|
||||||
part_list = data.get(u'parts[]')
|
part_list = data.get(u'parts[]')
|
||||||
@ -160,7 +156,7 @@ class StockMove(APIView):
|
|||||||
part = StockItem.objects.get(pk=pid)
|
part = StockItem.objects.get(pk=pid)
|
||||||
parts.append(part)
|
parts.append(part)
|
||||||
except StockItem.DoesNotExist:
|
except StockItem.DoesNotExist:
|
||||||
errors.append({'part': 'Part {id} does not exist'.format(id=part_id)})
|
errors.append({'part': 'Part {id} does not exist'.format(id=pid)})
|
||||||
|
|
||||||
if len(errors) > 0:
|
if len(errors) > 0:
|
||||||
raise ValidationError(errors)
|
raise ValidationError(errors)
|
||||||
@ -227,7 +223,7 @@ class StockList(generics.ListCreateAPIView):
|
|||||||
'supplier_part',
|
'supplier_part',
|
||||||
'customer',
|
'customer',
|
||||||
'belongs_to',
|
'belongs_to',
|
||||||
#'status',
|
# 'status' TODO - There are some issues filtering based on an enumeration field
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -250,7 +246,7 @@ class StockTrackingList(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
queryset = StockItemTracking.objects.all()
|
queryset = StockItemTracking.objects.all()
|
||||||
serializer_class = StockTrackingSerializer
|
serializer_class = StockTrackingSerializer
|
||||||
permission_classes = [permissions.IsAuthenticatedOrReadOnly,]
|
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
|
||||||
|
|
||||||
filter_backends = [
|
filter_backends = [
|
||||||
DjangoFilterBackend,
|
DjangoFilterBackend,
|
||||||
@ -275,7 +271,6 @@ class StockTrackingList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
|
class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -303,9 +298,7 @@ location_endpoints = [
|
|||||||
url(r'^$', LocationDetail.as_view(), name='stocklocation-detail'),
|
url(r'^$', LocationDetail.as_view(), name='stocklocation-detail'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
stock_api_urls = [
|
stock_api_urls = [
|
||||||
|
|
||||||
url(r'location/?', StockLocationList.as_view(), name='api-location-list'),
|
url(r'location/?', StockLocationList.as_view(), name='api-location-list'),
|
||||||
|
|
||||||
url(r'location/(?P<pk>\d+)/', include(location_endpoints)),
|
url(r'location/(?P<pk>\d+)/', include(location_endpoints)),
|
||||||
@ -322,4 +315,4 @@ stock_api_urls = [
|
|||||||
url(r'^(?P<pk>\d+)/', include(stock_endpoints)),
|
url(r'^(?P<pk>\d+)/', include(stock_endpoints)),
|
||||||
|
|
||||||
url(r'^.*$', StockList.as_view(), name='api-stock-list'),
|
url(r'^.*$', StockList.as_view(), name='api-stock-list'),
|
||||||
]
|
]
|
||||||
|
@ -67,4 +67,4 @@ class EditStockItemForm(HelperForm):
|
|||||||
'batch',
|
'batch',
|
||||||
'status',
|
'status',
|
||||||
'notes'
|
'notes'
|
||||||
]
|
]
|
||||||
|
@ -69,12 +69,11 @@ class StockItem(models.Model):
|
|||||||
if add_note:
|
if add_note:
|
||||||
# This StockItem is being saved for the first time
|
# This StockItem is being saved for the first time
|
||||||
self.add_transaction_note(
|
self.add_transaction_note(
|
||||||
'Created stock item',
|
'Created stock item',
|
||||||
None,
|
None,
|
||||||
system=True
|
system=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
|
||||||
# The 'supplier_part' field must point to the same part!
|
# The 'supplier_part' field must point to the same part!
|
||||||
@ -227,8 +226,10 @@ class StockItem(models.Model):
|
|||||||
if location.pk == self.location.pk:
|
if location.pk == self.location.pk:
|
||||||
return False # raise forms.ValidationError("Cannot move item to its current location")
|
return False # raise forms.ValidationError("Cannot move item to its current location")
|
||||||
|
|
||||||
msg = "Moved to {loc} (from {src})".format(loc=location.name,
|
msg = "Moved to {loc} (from {src})".format(
|
||||||
src=self.location.name)
|
loc=location.name,
|
||||||
|
src=self.location.name
|
||||||
|
)
|
||||||
|
|
||||||
self.location = location
|
self.location = location
|
||||||
self.save()
|
self.save()
|
||||||
@ -240,7 +241,6 @@ class StockItem(models.Model):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def stocktake(self, count, user, notes=''):
|
def stocktake(self, count, user, notes=''):
|
||||||
""" Perform item stocktake.
|
""" Perform item stocktake.
|
||||||
|
@ -4,9 +4,13 @@ from .models import StockItem, StockLocation
|
|||||||
from .models import StockItemTracking
|
from .models import StockItemTracking
|
||||||
|
|
||||||
from part.serializers import PartBriefSerializer
|
from part.serializers import PartBriefSerializer
|
||||||
from InvenTree.serializers import UserSerializer, UserSerializerBrief
|
from InvenTree.serializers import UserSerializerBrief
|
||||||
|
|
||||||
|
|
||||||
class LocationBriefSerializer(serializers.ModelSerializer):
|
class LocationBriefSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
Provides a brief serializer for a StockLocation object
|
||||||
|
"""
|
||||||
|
|
||||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
|
|
||||||
@ -49,8 +53,12 @@ class StockTrackingSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class StockItemSerializer(serializers.ModelSerializer):
|
class StockItemSerializer(serializers.ModelSerializer):
|
||||||
""" Serializer for a StockItem
|
|
||||||
"""
|
"""
|
||||||
|
Serializer for a StockItem
|
||||||
|
- Includes serialization for the linked part
|
||||||
|
- Includes serialization for the item location
|
||||||
|
"""
|
||||||
|
|
||||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
|
|
||||||
part = PartBriefSerializer(many=False, read_only=True)
|
part = PartBriefSerializer(many=False, read_only=True)
|
||||||
@ -66,17 +74,11 @@ class StockItemSerializer(serializers.ModelSerializer):
|
|||||||
'supplier_part',
|
'supplier_part',
|
||||||
'location',
|
'location',
|
||||||
'in_stock',
|
'in_stock',
|
||||||
#'belongs_to',
|
|
||||||
#'customer',
|
|
||||||
'quantity',
|
'quantity',
|
||||||
'serial',
|
'serial',
|
||||||
'batch',
|
'batch',
|
||||||
'status',
|
'status',
|
||||||
'notes',
|
'notes',
|
||||||
#'updated',
|
|
||||||
#'stocktake_date',
|
|
||||||
#'stocktake_user',
|
|
||||||
#'review_needed',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
""" These fields are read-only in this context.
|
""" These fields are read-only in this context.
|
||||||
@ -86,7 +88,8 @@ class StockItemSerializer(serializers.ModelSerializer):
|
|||||||
'stocktake_date',
|
'stocktake_date',
|
||||||
'stocktake_user',
|
'stocktake_user',
|
||||||
'updated',
|
'updated',
|
||||||
#'quantity',
|
'quantity',
|
||||||
|
'in_stock'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -106,10 +109,10 @@ class LocationSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = StockLocation
|
model = StockLocation
|
||||||
fields = [
|
fields = [
|
||||||
'pk',
|
'pk',
|
||||||
'url',
|
'url',
|
||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
'parent',
|
'parent',
|
||||||
'pathstring'
|
'pathstring'
|
||||||
]
|
]
|
||||||
|
@ -10,15 +10,17 @@ from InvenTree.views import AjaxUpdateView, AjaxDeleteView, AjaxCreateView
|
|||||||
from part.models import Part
|
from part.models import Part
|
||||||
from .models import StockItem, StockLocation
|
from .models import StockItem, StockLocation
|
||||||
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from .forms import EditStockLocationForm
|
from .forms import EditStockLocationForm
|
||||||
from .forms import CreateStockItemForm
|
from .forms import CreateStockItemForm
|
||||||
from .forms import EditStockItemForm
|
from .forms import EditStockItemForm
|
||||||
from .forms import MoveStockItemForm
|
from .forms import MoveStockItemForm
|
||||||
from .forms import StocktakeForm
|
from .forms import StocktakeForm
|
||||||
|
|
||||||
|
|
||||||
class StockIndex(ListView):
|
class StockIndex(ListView):
|
||||||
|
"""
|
||||||
|
StockIndex view loads all StockLocation and StockItem object
|
||||||
|
"""
|
||||||
model = StockItem
|
model = StockItem
|
||||||
template_name = 'stock/location.html'
|
template_name = 'stock/location.html'
|
||||||
context_obect_name = 'locations'
|
context_obect_name = 'locations'
|
||||||
@ -36,6 +38,10 @@ class StockIndex(ListView):
|
|||||||
|
|
||||||
|
|
||||||
class StockLocationDetail(DetailView):
|
class StockLocationDetail(DetailView):
|
||||||
|
"""
|
||||||
|
Detailed view of a single StockLocation object
|
||||||
|
"""
|
||||||
|
|
||||||
context_object_name = 'location'
|
context_object_name = 'location'
|
||||||
template_name = 'stock/location.html'
|
template_name = 'stock/location.html'
|
||||||
queryset = StockLocation.objects.all()
|
queryset = StockLocation.objects.all()
|
||||||
@ -43,6 +49,10 @@ class StockLocationDetail(DetailView):
|
|||||||
|
|
||||||
|
|
||||||
class StockItemDetail(DetailView):
|
class StockItemDetail(DetailView):
|
||||||
|
"""
|
||||||
|
Detailed view of a single StockItem object
|
||||||
|
"""
|
||||||
|
|
||||||
context_object_name = 'item'
|
context_object_name = 'item'
|
||||||
template_name = 'stock/item.html'
|
template_name = 'stock/item.html'
|
||||||
queryset = StockItem.objects.all()
|
queryset = StockItem.objects.all()
|
||||||
@ -50,6 +60,11 @@ class StockItemDetail(DetailView):
|
|||||||
|
|
||||||
|
|
||||||
class StockLocationEdit(AjaxUpdateView):
|
class StockLocationEdit(AjaxUpdateView):
|
||||||
|
"""
|
||||||
|
View for editing details of a StockLocation.
|
||||||
|
This view is used with the EditStockLocationForm to deliver a modal form to the web view
|
||||||
|
"""
|
||||||
|
|
||||||
model = StockLocation
|
model = StockLocation
|
||||||
form_class = EditStockLocationForm
|
form_class = EditStockLocationForm
|
||||||
template_name = 'stock/location_edit.html'
|
template_name = 'stock/location_edit.html'
|
||||||
@ -59,6 +74,10 @@ class StockLocationEdit(AjaxUpdateView):
|
|||||||
|
|
||||||
|
|
||||||
class StockItemEdit(AjaxUpdateView):
|
class StockItemEdit(AjaxUpdateView):
|
||||||
|
"""
|
||||||
|
View for editing details of a single StockItem
|
||||||
|
"""
|
||||||
|
|
||||||
model = StockItem
|
model = StockItem
|
||||||
form_class = EditStockItemForm
|
form_class = EditStockItemForm
|
||||||
template_name = 'stock/item_edit.html'
|
template_name = 'stock/item_edit.html'
|
||||||
@ -68,6 +87,11 @@ class StockItemEdit(AjaxUpdateView):
|
|||||||
|
|
||||||
|
|
||||||
class StockLocationCreate(AjaxCreateView):
|
class StockLocationCreate(AjaxCreateView):
|
||||||
|
"""
|
||||||
|
View for creating a new StockLocation
|
||||||
|
A parent location (another StockLocation object) can be passed as a query parameter
|
||||||
|
"""
|
||||||
|
|
||||||
model = StockLocation
|
model = StockLocation
|
||||||
form_class = EditStockLocationForm
|
form_class = EditStockLocationForm
|
||||||
template_name = 'stock/location_create.html'
|
template_name = 'stock/location_create.html'
|
||||||
@ -87,6 +111,13 @@ class StockLocationCreate(AjaxCreateView):
|
|||||||
|
|
||||||
|
|
||||||
class StockItemCreate(AjaxCreateView):
|
class StockItemCreate(AjaxCreateView):
|
||||||
|
"""
|
||||||
|
View for creating a new StockItem
|
||||||
|
Parameters can be pre-filled by passing query items:
|
||||||
|
- part: The part of which the new StockItem is an instance
|
||||||
|
- location: The location of the new StockItem
|
||||||
|
"""
|
||||||
|
|
||||||
model = StockItem
|
model = StockItem
|
||||||
form_class = CreateStockItemForm
|
form_class = CreateStockItemForm
|
||||||
template_name = 'stock/item_create.html'
|
template_name = 'stock/item_create.html'
|
||||||
@ -114,6 +145,11 @@ class StockItemCreate(AjaxCreateView):
|
|||||||
|
|
||||||
|
|
||||||
class StockLocationDelete(AjaxDeleteView):
|
class StockLocationDelete(AjaxDeleteView):
|
||||||
|
"""
|
||||||
|
View to delete a StockLocation
|
||||||
|
Presents a deletion confirmation form to the user
|
||||||
|
"""
|
||||||
|
|
||||||
model = StockLocation
|
model = StockLocation
|
||||||
success_url = '/stock'
|
success_url = '/stock'
|
||||||
template_name = 'stock/location_delete.html'
|
template_name = 'stock/location_delete.html'
|
||||||
@ -122,6 +158,11 @@ class StockLocationDelete(AjaxDeleteView):
|
|||||||
|
|
||||||
|
|
||||||
class StockItemDelete(AjaxDeleteView):
|
class StockItemDelete(AjaxDeleteView):
|
||||||
|
"""
|
||||||
|
View to delete a StockItem
|
||||||
|
Presents a deletion confirmation form to the user
|
||||||
|
"""
|
||||||
|
|
||||||
model = StockItem
|
model = StockItem
|
||||||
success_url = '/stock/'
|
success_url = '/stock/'
|
||||||
template_name = 'stock/item_delete.html'
|
template_name = 'stock/item_delete.html'
|
||||||
@ -130,6 +171,11 @@ class StockItemDelete(AjaxDeleteView):
|
|||||||
|
|
||||||
|
|
||||||
class StockItemMove(AjaxUpdateView):
|
class StockItemMove(AjaxUpdateView):
|
||||||
|
"""
|
||||||
|
View to move a StockItem from one location to another
|
||||||
|
Performs some data validation to prevent illogical stock moves
|
||||||
|
"""
|
||||||
|
|
||||||
model = StockItem
|
model = StockItem
|
||||||
template_name = 'modal_form.html'
|
template_name = 'modal_form.html'
|
||||||
context_object_name = 'item'
|
context_object_name = 'item'
|
||||||
@ -156,7 +202,6 @@ class StockItemMove(AjaxUpdateView):
|
|||||||
form.errors['location'] = ['Cannot move to an empty location']
|
form.errors['location'] = ['Cannot move to an empty location']
|
||||||
|
|
||||||
except StockLocation.DoesNotExist:
|
except StockLocation.DoesNotExist:
|
||||||
loc_path = ''
|
|
||||||
form.errors['location'] = ['Location does not exist']
|
form.errors['location'] = ['Location does not exist']
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
@ -167,6 +212,11 @@ class StockItemMove(AjaxUpdateView):
|
|||||||
|
|
||||||
|
|
||||||
class StockItemStocktake(AjaxUpdateView):
|
class StockItemStocktake(AjaxUpdateView):
|
||||||
|
"""
|
||||||
|
View to perform stocktake on a single StockItem
|
||||||
|
Updates the quantity, which will also create a new StockItemTracking item
|
||||||
|
"""
|
||||||
|
|
||||||
model = StockItem
|
model = StockItem
|
||||||
template_name = 'modal_form.html'
|
template_name = 'modal_form.html'
|
||||||
context_object_name = 'item'
|
context_object_name = 'item'
|
||||||
@ -188,5 +238,3 @@ class StockItemStocktake(AjaxUpdateView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
return self.renderJsonResponse(request, form, data)
|
return self.renderJsonResponse(request, form, data)
|
||||||
|
|
||||||
|
|
||||||
|
2
Makefile
2
Makefile
@ -8,7 +8,7 @@ clean:
|
|||||||
rm -f .coverage
|
rm -f .coverage
|
||||||
|
|
||||||
style:
|
style:
|
||||||
flake8 InvenTree
|
flake8 InvenTree --ignore=C901,E501
|
||||||
|
|
||||||
test:
|
test:
|
||||||
python InvenTree/manage.py check
|
python InvenTree/manage.py check
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
Django==2.2
|
Django==2.2
|
||||||
|
psycopg2>=2.8.1
|
||||||
pillow>=5.0.0
|
pillow>=5.0.0
|
||||||
djangorestframework>=3.6.2
|
djangorestframework>=3.6.2
|
||||||
django_filter>=1.0.2
|
django_filter>=1.0.2
|
||||||
|
Loading…
Reference in New Issue
Block a user