mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 20:16:44 +00:00
344 lines
9.5 KiB
Python
344 lines
9.5 KiB
Python
"""
|
|
Django views for interacting with Stock app
|
|
"""
|
|
|
|
# -*- coding: utf-8 -*-
|
|
from __future__ import unicode_literals
|
|
|
|
from datetime import datetime
|
|
|
|
from django.views.generic import DetailView, ListView
|
|
from django.urls import reverse
|
|
from django.http import HttpResponseRedirect
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from InvenTree.views import AjaxUpdateView, AjaxDeleteView, AjaxCreateView
|
|
from InvenTree.views import QRCodeView
|
|
from InvenTree.views import InvenTreeRoleMixin
|
|
from InvenTree.forms import ConfirmForm
|
|
|
|
from InvenTree.helpers import str2bool
|
|
|
|
from .models import StockItem, StockLocation, StockItemTracking
|
|
|
|
import common.settings
|
|
|
|
from . import forms as StockForms
|
|
|
|
from plugin.views import InvenTreePluginViewMixin
|
|
|
|
|
|
class StockIndex(InvenTreeRoleMixin, InvenTreePluginViewMixin, ListView):
|
|
""" StockIndex view loads all StockLocation and StockItem object
|
|
"""
|
|
model = StockItem
|
|
template_name = 'stock/location.html'
|
|
context_obect_name = 'locations'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs).copy()
|
|
|
|
# Return all top-level locations
|
|
locations = StockLocation.objects.filter(parent=None)
|
|
|
|
context['locations'] = locations
|
|
context['items'] = StockItem.objects.all()
|
|
|
|
context['loc_count'] = StockLocation.objects.count()
|
|
context['stock_count'] = StockItem.objects.count()
|
|
|
|
# No 'ownership' checks are necessary for the top-level StockLocation view
|
|
context['user_owns_location'] = True
|
|
context['location_owner'] = None
|
|
context['ownership_enabled'] = common.models.InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')
|
|
|
|
return context
|
|
|
|
|
|
class StockLocationDetail(InvenTreeRoleMixin, InvenTreePluginViewMixin, DetailView):
|
|
"""
|
|
Detailed view of a single StockLocation object
|
|
"""
|
|
|
|
context_object_name = 'location'
|
|
template_name = 'stock/location.html'
|
|
queryset = StockLocation.objects.all()
|
|
model = StockLocation
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super().get_context_data(**kwargs)
|
|
|
|
context['ownership_enabled'] = common.models.InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')
|
|
context['location_owner'] = context['location'].get_location_owner()
|
|
context['user_owns_location'] = context['location'].check_ownership(self.request.user)
|
|
|
|
return context
|
|
|
|
|
|
class StockItemDetail(InvenTreeRoleMixin, InvenTreePluginViewMixin, DetailView):
|
|
"""
|
|
Detailed view of a single StockItem object
|
|
"""
|
|
|
|
context_object_name = 'item'
|
|
template_name = 'stock/item.html'
|
|
queryset = StockItem.objects.all()
|
|
model = StockItem
|
|
|
|
def get_context_data(self, **kwargs):
|
|
"""
|
|
Add information on the "next" and "previous" StockItem objects,
|
|
based on the serial numbers.
|
|
"""
|
|
|
|
data = super().get_context_data(**kwargs)
|
|
|
|
if self.object.serialized:
|
|
data['previous'] = self.object.get_next_serialized_item(reverse=True)
|
|
data['next'] = self.object.get_next_serialized_item()
|
|
|
|
data['ownership_enabled'] = common.models.InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')
|
|
data['item_owner'] = self.object.get_item_owner()
|
|
data['user_owns_item'] = self.object.check_ownership(self.request.user)
|
|
|
|
return data
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
""" check if item exists else return to stock index """
|
|
|
|
stock_pk = kwargs.get('pk', None)
|
|
|
|
if stock_pk:
|
|
try:
|
|
stock_item = StockItem.objects.get(pk=stock_pk)
|
|
except StockItem.DoesNotExist:
|
|
stock_item = None
|
|
|
|
if not stock_item:
|
|
return HttpResponseRedirect(reverse('stock-index'))
|
|
|
|
return super().get(request, *args, **kwargs)
|
|
|
|
|
|
class StockLocationQRCode(QRCodeView):
|
|
""" View for displaying a QR code for a StockLocation object """
|
|
|
|
ajax_form_title = _("Stock Location QR code")
|
|
|
|
role_required = ['stock_location.view', 'stock.view']
|
|
|
|
def get_qr_data(self):
|
|
""" Generate QR code data for the StockLocation """
|
|
try:
|
|
loc = StockLocation.objects.get(id=self.pk)
|
|
return loc.format_barcode()
|
|
except StockLocation.DoesNotExist:
|
|
return None
|
|
|
|
|
|
class StockItemReturnToStock(AjaxUpdateView):
|
|
"""
|
|
View for returning a stock item (which is assigned to a customer) to stock.
|
|
"""
|
|
|
|
model = StockItem
|
|
ajax_form_title = _("Return to Stock")
|
|
context_object_name = "item"
|
|
form_class = StockForms.ReturnStockItemForm
|
|
|
|
def validate(self, item, form, **kwargs):
|
|
|
|
location = form.cleaned_data.get('location', None)
|
|
|
|
if not location:
|
|
form.add_error('location', _('Specify a valid location'))
|
|
|
|
def save(self, item, form, **kwargs):
|
|
|
|
location = form.cleaned_data.get('location', None)
|
|
|
|
if location:
|
|
item.returnFromCustomer(location, self.request.user)
|
|
|
|
def get_data(self):
|
|
return {
|
|
'success': _('Stock item returned from customer')
|
|
}
|
|
|
|
|
|
class StockItemDeleteTestData(AjaxUpdateView):
|
|
"""
|
|
View for deleting all test data
|
|
"""
|
|
|
|
model = StockItem
|
|
form_class = ConfirmForm
|
|
ajax_form_title = _("Delete All Test Data")
|
|
|
|
role_required = ['stock.change', 'stock.delete']
|
|
|
|
def get_form(self):
|
|
return ConfirmForm()
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
|
|
valid = False
|
|
|
|
stock_item = StockItem.objects.get(pk=self.kwargs['pk'])
|
|
form = self.get_form()
|
|
|
|
confirm = str2bool(request.POST.get('confirm', False))
|
|
|
|
if confirm is not True:
|
|
form.add_error('confirm', _('Confirm test data deletion'))
|
|
form.add_error(None, _('Check the confirmation box'))
|
|
else:
|
|
stock_item.test_results.all().delete()
|
|
valid = True
|
|
|
|
data = {
|
|
'form_valid': valid,
|
|
}
|
|
|
|
return self.renderJsonResponse(request, form, data)
|
|
|
|
|
|
class StockItemQRCode(QRCodeView):
|
|
""" View for displaying a QR code for a StockItem object """
|
|
|
|
ajax_form_title = _("Stock Item QR Code")
|
|
role_required = 'stock.view'
|
|
|
|
def get_qr_data(self):
|
|
""" Generate QR code data for the StockItem """
|
|
try:
|
|
item = StockItem.objects.get(id=self.pk)
|
|
return item.format_barcode()
|
|
except StockItem.DoesNotExist:
|
|
return None
|
|
|
|
|
|
class StockItemConvert(AjaxUpdateView):
|
|
"""
|
|
View for 'converting' a StockItem to a variant of its current part.
|
|
"""
|
|
|
|
model = StockItem
|
|
form_class = StockForms.ConvertStockItemForm
|
|
ajax_form_title = _('Convert Stock Item')
|
|
ajax_template_name = 'stock/stockitem_convert.html'
|
|
context_object_name = 'item'
|
|
|
|
def get_form(self):
|
|
"""
|
|
Filter the available parts.
|
|
"""
|
|
|
|
form = super().get_form()
|
|
item = self.get_object()
|
|
|
|
form.fields['part'].queryset = item.part.get_conversion_options()
|
|
|
|
return form
|
|
|
|
def save(self, obj, form):
|
|
|
|
stock_item = self.get_object()
|
|
|
|
variant = form.cleaned_data.get('part', None)
|
|
|
|
stock_item.convert_to_variant(variant, user=self.request.user)
|
|
|
|
return stock_item
|
|
|
|
|
|
class StockLocationDelete(AjaxDeleteView):
|
|
"""
|
|
View to delete a StockLocation
|
|
Presents a deletion confirmation form to the user
|
|
"""
|
|
|
|
model = StockLocation
|
|
success_url = '/stock'
|
|
ajax_template_name = 'stock/location_delete.html'
|
|
context_object_name = 'location'
|
|
ajax_form_title = _('Delete Stock Location')
|
|
|
|
|
|
class StockItemDelete(AjaxDeleteView):
|
|
"""
|
|
View to delete a StockItem
|
|
Presents a deletion confirmation form to the user
|
|
"""
|
|
|
|
model = StockItem
|
|
success_url = '/stock/'
|
|
ajax_template_name = 'stock/item_delete.html'
|
|
context_object_name = 'item'
|
|
ajax_form_title = _('Delete Stock Item')
|
|
|
|
|
|
class StockItemTrackingDelete(AjaxDeleteView):
|
|
"""
|
|
View to delete a StockItemTracking object
|
|
Presents a deletion confirmation form to the user
|
|
"""
|
|
|
|
model = StockItemTracking
|
|
ajax_template_name = 'stock/tracking_delete.html'
|
|
ajax_form_title = _('Delete Stock Tracking Entry')
|
|
|
|
|
|
class StockItemTrackingEdit(AjaxUpdateView):
|
|
""" View for editing a StockItemTracking object """
|
|
|
|
model = StockItemTracking
|
|
ajax_form_title = _('Edit Stock Tracking Entry')
|
|
form_class = StockForms.TrackingEntryForm
|
|
|
|
|
|
class StockItemTrackingCreate(AjaxCreateView):
|
|
""" View for creating a new StockItemTracking object.
|
|
"""
|
|
|
|
model = StockItemTracking
|
|
ajax_form_title = _("Add Stock Tracking Entry")
|
|
form_class = StockForms.TrackingEntryForm
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
|
|
self.request = request
|
|
self.form = self.get_form()
|
|
|
|
valid = False
|
|
|
|
if self.form.is_valid():
|
|
stock_id = self.kwargs['pk']
|
|
|
|
if stock_id:
|
|
try:
|
|
stock_item = StockItem.objects.get(id=stock_id)
|
|
|
|
# Save new tracking information
|
|
tracking = self.form.save(commit=False)
|
|
tracking.item = stock_item
|
|
tracking.user = self.request.user
|
|
tracking.quantity = stock_item.quantity
|
|
tracking.date = datetime.now().date()
|
|
tracking.system = False
|
|
|
|
tracking.save()
|
|
|
|
valid = True
|
|
|
|
except (StockItem.DoesNotExist, ValueError):
|
|
pass
|
|
|
|
data = {
|
|
'form_valid': valid
|
|
}
|
|
|
|
return self.renderJsonResponse(request, self.form, data=data)
|