mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-04 07:05:41 +00:00 
			
		
		
		
	Merge remote-tracking branch 'inventree/master'
This commit is contained in:
		
							
								
								
									
										60
									
								
								InvenTree/InvenTree/management/commands/rebuild_models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								InvenTree/InvenTree/management/commands/rebuild_models.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Custom management command to rebuild all MPTT models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- This is crucial after importing any fixtures, etc
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.core.management.base import BaseCommand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Command(BaseCommand):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Rebuild all database models which leverage the MPTT structure.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle(self, *args, **kwargs):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Part model
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            print("Rebuilding Part objects")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            from part.models import Part
 | 
				
			||||||
 | 
					            Part.objects.rebuild()
 | 
				
			||||||
 | 
					        except:
 | 
				
			||||||
 | 
					            print("Error rebuilding Part objects")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Part category
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            print("Rebuilding PartCategory objects")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            from part.models import PartCategory
 | 
				
			||||||
 | 
					            PartCategory.objects.rebuild()
 | 
				
			||||||
 | 
					        except:
 | 
				
			||||||
 | 
					            print("Error rebuilding PartCategory objects")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # StockItem model
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            print("Rebuilding StockItem objects")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            from stock.models import StockItem
 | 
				
			||||||
 | 
					            StockItem.objects.rebuild()
 | 
				
			||||||
 | 
					        except:
 | 
				
			||||||
 | 
					            print("Error rebuilding StockItem objects")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # StockLocation model
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            print("Rebuilding StockLocation objects")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            from stock.models import StockLocation
 | 
				
			||||||
 | 
					            StockLocation.objects.rebuild()
 | 
				
			||||||
 | 
					        except:
 | 
				
			||||||
 | 
					            print("Error rebuilding StockLocation objects")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Build model
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            print("Rebuilding Build objects")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            from build.models import Build
 | 
				
			||||||
 | 
					            Build.objects.rebuild()
 | 
				
			||||||
 | 
					        except:
 | 
				
			||||||
 | 
					            print("Error rebuilding Build objects")
 | 
				
			||||||
@@ -165,6 +165,19 @@ class BuildItemList(generics.ListCreateAPIView):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    serializer_class = BuildItemSerializer
 | 
					    serializer_class = BuildItemSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_serializer(self, *args, **kwargs):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            params = self.request.query_params
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            kwargs['part_detail'] = str2bool(params.get('part_detail', False))
 | 
				
			||||||
 | 
					            kwargs['build_detail'] = str2bool(params.get('build_detail', False))
 | 
				
			||||||
 | 
					            kwargs['location_detail'] = str2bool(params.get('location_detail', False))
 | 
				
			||||||
 | 
					        except AttributeError:
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return self.serializer_class(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_queryset(self):
 | 
					    def get_queryset(self):
 | 
				
			||||||
        """ Override the queryset method,
 | 
					        """ Override the queryset method,
 | 
				
			||||||
        to allow filtering by stock_item.part
 | 
					        to allow filtering by stock_item.part
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,8 @@ from rest_framework import serializers
 | 
				
			|||||||
from InvenTree.serializers import InvenTreeModelSerializer
 | 
					from InvenTree.serializers import InvenTreeModelSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from stock.serializers import StockItemSerializerBrief
 | 
					from stock.serializers import StockItemSerializerBrief
 | 
				
			||||||
from part.serializers import PartBriefSerializer
 | 
					from stock.serializers import LocationSerializer
 | 
				
			||||||
 | 
					from part.serializers import PartSerializer, PartBriefSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .models import Build, BuildItem
 | 
					from .models import Build, BuildItem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -99,22 +100,45 @@ class BuildItemSerializer(InvenTreeModelSerializer):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    bom_part = serializers.IntegerField(source='bom_item.sub_part.pk', read_only=True)
 | 
					    bom_part = serializers.IntegerField(source='bom_item.sub_part.pk', read_only=True)
 | 
				
			||||||
    part = serializers.IntegerField(source='stock_item.part.pk', read_only=True)
 | 
					    part = serializers.IntegerField(source='stock_item.part.pk', read_only=True)
 | 
				
			||||||
    part_name = serializers.CharField(source='stock_item.part.full_name', read_only=True)
 | 
					    location = serializers.IntegerField(source='stock_item.location.pk', read_only=True)
 | 
				
			||||||
    part_thumb = serializers.CharField(source='getStockItemThumbnail', read_only=True)
 | 
					
 | 
				
			||||||
 | 
					    # Extra (optional) detail fields
 | 
				
			||||||
 | 
					    part_detail = PartSerializer(source='stock_item.part', many=False, read_only=True)
 | 
				
			||||||
 | 
					    build_detail = BuildSerializer(source='build', many=False, read_only=True)
 | 
				
			||||||
    stock_item_detail = StockItemSerializerBrief(source='stock_item', read_only=True)
 | 
					    stock_item_detail = StockItemSerializerBrief(source='stock_item', read_only=True)
 | 
				
			||||||
 | 
					    location_detail = LocationSerializer(source='stock_item.location', read_only=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quantity = serializers.FloatField()
 | 
					    quantity = serializers.FloatField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        build_detail = kwargs.pop('build_detail', False)
 | 
				
			||||||
 | 
					        part_detail = kwargs.pop('part_detail', False)
 | 
				
			||||||
 | 
					        location_detail = kwargs.pop('location_detail', False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not build_detail:
 | 
				
			||||||
 | 
					            self.fields.pop('build_detail')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not part_detail:
 | 
				
			||||||
 | 
					            self.fields.pop('part_detail')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not location_detail:
 | 
				
			||||||
 | 
					            self.fields.pop('location_detail')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = BuildItem
 | 
					        model = BuildItem
 | 
				
			||||||
        fields = [
 | 
					        fields = [
 | 
				
			||||||
            'pk',
 | 
					            'pk',
 | 
				
			||||||
            'bom_part',
 | 
					            'bom_part',
 | 
				
			||||||
            'build',
 | 
					            'build',
 | 
				
			||||||
 | 
					            'build_detail',
 | 
				
			||||||
            'install_into',
 | 
					            'install_into',
 | 
				
			||||||
 | 
					            'location',
 | 
				
			||||||
 | 
					            'location_detail',
 | 
				
			||||||
            'part',
 | 
					            'part',
 | 
				
			||||||
            'part_name',
 | 
					            'part_detail',
 | 
				
			||||||
            'part_thumb',
 | 
					 | 
				
			||||||
            'stock_item',
 | 
					            'stock_item',
 | 
				
			||||||
            'stock_item_detail',
 | 
					            'stock_item_detail',
 | 
				
			||||||
            'quantity'
 | 
					            'quantity'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -103,17 +103,11 @@ class ManufacturerPartList(generics.ListCreateAPIView):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # Do we wish to include extra detail?
 | 
					        # Do we wish to include extra detail?
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            kwargs['part_detail'] = str2bool(self.request.query_params.get('part_detail', None))
 | 
					            params = self.request.query_params
 | 
				
			||||||
        except AttributeError:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					            kwargs['part_detail'] = str2bool(params.get('part_detail', None))
 | 
				
			||||||
            kwargs['manufacturer_detail'] = str2bool(self.request.query_params.get('manufacturer_detail', None))
 | 
					            kwargs['manufacturer_detail'] = str2bool(params.get('manufacturer_detail', None))
 | 
				
			||||||
        except AttributeError:
 | 
					            kwargs['pretty'] = str2bool(params.get('pretty', None))
 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            kwargs['pretty'] = str2bool(self.request.query_params.get('pretty', None))
 | 
					 | 
				
			||||||
        except AttributeError:
 | 
					        except AttributeError:
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -252,22 +246,11 @@ class SupplierPartList(generics.ListCreateAPIView):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # Do we wish to include extra detail?
 | 
					        # Do we wish to include extra detail?
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            kwargs['part_detail'] = str2bool(self.request.query_params.get('part_detail', None))
 | 
					            params = self.request.query_params
 | 
				
			||||||
        except AttributeError:
 | 
					            kwargs['part_detail'] = str2bool(params.get('part_detail', None))
 | 
				
			||||||
            pass
 | 
					            kwargs['supplier_detail'] = str2bool(params.get('supplier_detail', None))
 | 
				
			||||||
 | 
					            kwargs['manufacturer_detail'] = str2bool(self.params.get('manufacturer_detail', None))
 | 
				
			||||||
        try:
 | 
					            kwargs['pretty'] = str2bool(params.get('pretty', None))
 | 
				
			||||||
            kwargs['supplier_detail'] = str2bool(self.request.query_params.get('supplier_detail', None))
 | 
					 | 
				
			||||||
        except AttributeError:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            kwargs['manufacturer_detail'] = str2bool(self.request.query_params.get('manufacturer_detail', None))
 | 
					 | 
				
			||||||
        except AttributeError:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            kwargs['pretty'] = str2bool(self.request.query_params.get('pretty', None))
 | 
					 | 
				
			||||||
        except AttributeError:
 | 
					        except AttributeError:
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,9 +22,10 @@ from .models import PurchaseOrder, PurchaseOrderLineItem
 | 
				
			|||||||
from .models import PurchaseOrderAttachment
 | 
					from .models import PurchaseOrderAttachment
 | 
				
			||||||
from .serializers import POSerializer, POLineItemSerializer, POAttachmentSerializer
 | 
					from .serializers import POSerializer, POLineItemSerializer, POAttachmentSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .models import SalesOrder, SalesOrderLineItem
 | 
					from .models import SalesOrder, SalesOrderLineItem, SalesOrderAllocation
 | 
				
			||||||
from .models import SalesOrderAttachment
 | 
					from .models import SalesOrderAttachment
 | 
				
			||||||
from .serializers import SalesOrderSerializer, SOLineItemSerializer, SOAttachmentSerializer
 | 
					from .serializers import SalesOrderSerializer, SOLineItemSerializer, SOAttachmentSerializer
 | 
				
			||||||
 | 
					from .serializers import SalesOrderAllocationSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class POList(generics.ListCreateAPIView):
 | 
					class POList(generics.ListCreateAPIView):
 | 
				
			||||||
@@ -422,17 +423,11 @@ class SOLineItemList(generics.ListCreateAPIView):
 | 
				
			|||||||
    def get_serializer(self, *args, **kwargs):
 | 
					    def get_serializer(self, *args, **kwargs):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            kwargs['part_detail'] = str2bool(self.request.query_params.get('part_detail', False))
 | 
					            params = self.request.query_params
 | 
				
			||||||
        except AttributeError:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					            kwargs['part_detail'] = str2bool(params.get('part_detail', False))
 | 
				
			||||||
            kwargs['order_detail'] = str2bool(self.request.query_params.get('order_detail', False))
 | 
					            kwargs['order_detail'] = str2bool(params.get('order_detail', False))
 | 
				
			||||||
        except AttributeError:
 | 
					            kwargs['allocations'] = str2bool(params.get('allocations', False))
 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            kwargs['allocations'] = str2bool(self.request.query_params.get('allocations', False))
 | 
					 | 
				
			||||||
        except AttributeError:
 | 
					        except AttributeError:
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -486,6 +481,70 @@ class SOLineItemDetail(generics.RetrieveUpdateAPIView):
 | 
				
			|||||||
    serializer_class = SOLineItemSerializer
 | 
					    serializer_class = SOLineItemSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SOAllocationList(generics.ListCreateAPIView):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    API endpoint for listing SalesOrderAllocation objects
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    queryset = SalesOrderAllocation.objects.all()
 | 
				
			||||||
 | 
					    serializer_class = SalesOrderAllocationSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_serializer(self, *args, **kwargs):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            params = self.request.query_params
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            kwargs['part_detail'] = str2bool(params.get('part_detail', False))
 | 
				
			||||||
 | 
					            kwargs['item_detail'] = str2bool(params.get('item_detail', False))
 | 
				
			||||||
 | 
					            kwargs['order_detail'] = str2bool(params.get('order_detail', False))
 | 
				
			||||||
 | 
					            kwargs['location_detail'] = str2bool(params.get('location_detail', False))
 | 
				
			||||||
 | 
					        except AttributeError:
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self.serializer_class(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def filter_queryset(self, queryset):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        queryset = super().filter_queryset(queryset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Filter by order
 | 
				
			||||||
 | 
					        params = self.request.query_params
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Filter by "part" reference
 | 
				
			||||||
 | 
					        part = params.get('part', None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if part is not None:
 | 
				
			||||||
 | 
					            queryset = queryset.filter(item__part=part)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Filter by "order" reference
 | 
				
			||||||
 | 
					        order = params.get('order', None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if order is not None:
 | 
				
			||||||
 | 
					            queryset = queryset.filter(line__order=order)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Filter by "outstanding" order status
 | 
				
			||||||
 | 
					        outstanding = params.get('outstanding', None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if outstanding is not None:
 | 
				
			||||||
 | 
					            outstanding = str2bool(outstanding)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if outstanding:
 | 
				
			||||||
 | 
					                queryset = queryset.filter(line__order__status__in=SalesOrderStatus.OPEN)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                queryset = queryset.exclude(line__order__status__in=SalesOrderStatus.OPEN)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return queryset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    filter_backends = [
 | 
				
			||||||
 | 
					        DjangoFilterBackend,
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Default filterable fields
 | 
				
			||||||
 | 
					    filter_fields = [
 | 
				
			||||||
 | 
					        'item',
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class POAttachmentList(generics.ListCreateAPIView, AttachmentMixin):
 | 
					class POAttachmentList(generics.ListCreateAPIView, AttachmentMixin):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    API endpoint for listing (and creating) a PurchaseOrderAttachment (file upload)
 | 
					    API endpoint for listing (and creating) a PurchaseOrderAttachment (file upload)
 | 
				
			||||||
@@ -494,10 +553,6 @@ class POAttachmentList(generics.ListCreateAPIView, AttachmentMixin):
 | 
				
			|||||||
    queryset = PurchaseOrderAttachment.objects.all()
 | 
					    queryset = PurchaseOrderAttachment.objects.all()
 | 
				
			||||||
    serializer_class = POAttachmentSerializer
 | 
					    serializer_class = POAttachmentSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    filter_fields = [
 | 
					 | 
				
			||||||
        'order',
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
order_api_urls = [
 | 
					order_api_urls = [
 | 
				
			||||||
    # API endpoints for purchase orders
 | 
					    # API endpoints for purchase orders
 | 
				
			||||||
@@ -512,14 +567,26 @@ order_api_urls = [
 | 
				
			|||||||
    url(r'^po-line/$', POLineItemList.as_view(), name='api-po-line-list'),
 | 
					    url(r'^po-line/$', POLineItemList.as_view(), name='api-po-line-list'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # API endpoints for sales ordesr
 | 
					    # API endpoints for sales ordesr
 | 
				
			||||||
    url(r'^so/(?P<pk>\d+)/$', SODetail.as_view(), name='api-so-detail'),
 | 
					    url(r'^so/', include([
 | 
				
			||||||
    url(r'so/attachment/', include([
 | 
					        url(r'^(?P<pk>\d+)/$', SODetail.as_view(), name='api-so-detail'),
 | 
				
			||||||
        url(r'^.*$', SOAttachmentList.as_view(), name='api-so-attachment-list'),
 | 
					        url(r'attachment/', include([
 | 
				
			||||||
 | 
					            url(r'^.*$', SOAttachmentList.as_view(), name='api-so-attachment-list'),
 | 
				
			||||||
 | 
					        ])),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # List all sales orders
 | 
				
			||||||
 | 
					        url(r'^.*$', SOList.as_view(), name='api-so-list'),
 | 
				
			||||||
    ])),
 | 
					    ])),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    url(r'^so/.*$', SOList.as_view(), name='api-so-list'),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # API endpoints for sales order line items
 | 
					    # API endpoints for sales order line items
 | 
				
			||||||
    url(r'^so-line/(?P<pk>\d+)/$', SOLineItemDetail.as_view(), name='api-so-line-detail'),
 | 
					    url(r'^so-line/', include([
 | 
				
			||||||
    url(r'^so-line/$', SOLineItemList.as_view(), name='api-so-line-list'),
 | 
					        url(r'^(?P<pk>\d+)/$', SOLineItemDetail.as_view(), name='api-so-line-detail'),
 | 
				
			||||||
 | 
					        url(r'^$', SOLineItemList.as_view(), name='api-so-line-list'),
 | 
				
			||||||
 | 
					    ])),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # API endpoints for sales order allocations
 | 
				
			||||||
 | 
					    url(r'^so-allocation', include([
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # List all sales order allocations
 | 
				
			||||||
 | 
					        url(r'^.*$', SOAllocationList.as_view(), name='api-so-allocation-list'),
 | 
				
			||||||
 | 
					    ])),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ from InvenTree.serializers import InvenTreeAttachmentSerializerField
 | 
				
			|||||||
from company.serializers import CompanyBriefSerializer, SupplierPartSerializer
 | 
					from company.serializers import CompanyBriefSerializer, SupplierPartSerializer
 | 
				
			||||||
from part.serializers import PartBriefSerializer
 | 
					from part.serializers import PartBriefSerializer
 | 
				
			||||||
from stock.serializers import LocationBriefSerializer
 | 
					from stock.serializers import LocationBriefSerializer
 | 
				
			||||||
 | 
					from stock.serializers import StockItemSerializer, LocationSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .models import PurchaseOrder, PurchaseOrderLineItem
 | 
					from .models import PurchaseOrder, PurchaseOrderLineItem
 | 
				
			||||||
from .models import PurchaseOrderAttachment, SalesOrderAttachment
 | 
					from .models import PurchaseOrderAttachment, SalesOrderAttachment
 | 
				
			||||||
@@ -42,7 +43,7 @@ class POSerializer(InvenTreeModelSerializer):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        Add extra information to the queryset
 | 
					        Add extra information to the queryset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        - Number of liens in the PurchaseOrder
 | 
					        - Number of lines in the PurchaseOrder
 | 
				
			||||||
        - Overdue status of the PurchaseOrder
 | 
					        - Overdue status of the PurchaseOrder
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -236,11 +237,38 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
 | 
				
			|||||||
    This includes some fields from the related model objects.
 | 
					    This includes some fields from the related model objects.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    location_path = serializers.CharField(source='get_location_path')
 | 
					    part = serializers.PrimaryKeyRelatedField(source='item.part', read_only=True)
 | 
				
			||||||
    location_id = serializers.IntegerField(source='get_location')
 | 
					    order = serializers.PrimaryKeyRelatedField(source='line.order', many=False, read_only=True)
 | 
				
			||||||
    serial = serializers.CharField(source='get_serial')
 | 
					    serial = serializers.CharField(source='get_serial', read_only=True)
 | 
				
			||||||
    po = serializers.CharField(source='get_po')
 | 
					    quantity = serializers.FloatField(read_only=True)
 | 
				
			||||||
    quantity = serializers.FloatField()
 | 
					    location = serializers.PrimaryKeyRelatedField(source='item.location', many=False, read_only=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Extra detail fields
 | 
				
			||||||
 | 
					    order_detail = SalesOrderSerializer(source='line.order', many=False, read_only=True)
 | 
				
			||||||
 | 
					    part_detail = PartBriefSerializer(source='item.part', many=False, read_only=True)
 | 
				
			||||||
 | 
					    item_detail = StockItemSerializer(source='item', many=False, read_only=True)
 | 
				
			||||||
 | 
					    location_detail = LocationSerializer(source='item.location', many=False, read_only=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        order_detail = kwargs.pop('order_detail', False)
 | 
				
			||||||
 | 
					        part_detail = kwargs.pop('part_detail', False)
 | 
				
			||||||
 | 
					        item_detail = kwargs.pop('item_detail', False)
 | 
				
			||||||
 | 
					        location_detail = kwargs.pop('location_detail', False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not order_detail:
 | 
				
			||||||
 | 
					            self.fields.pop('order_detail')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not part_detail:
 | 
				
			||||||
 | 
					            self.fields.pop('part_detail')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not item_detail:
 | 
				
			||||||
 | 
					            self.fields.pop('item_detail')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not location_detail:
 | 
				
			||||||
 | 
					            self.fields.pop('location_detail')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = SalesOrderAllocation
 | 
					        model = SalesOrderAllocation
 | 
				
			||||||
@@ -250,10 +278,14 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
 | 
				
			|||||||
            'line',
 | 
					            'line',
 | 
				
			||||||
            'serial',
 | 
					            'serial',
 | 
				
			||||||
            'quantity',
 | 
					            'quantity',
 | 
				
			||||||
            'location_id',
 | 
					            'location',
 | 
				
			||||||
            'location_path',
 | 
					            'location_detail',
 | 
				
			||||||
            'po',
 | 
					 | 
				
			||||||
            'item',
 | 
					            'item',
 | 
				
			||||||
 | 
					            'item_detail',
 | 
				
			||||||
 | 
					            'order',
 | 
				
			||||||
 | 
					            'order_detail',
 | 
				
			||||||
 | 
					            'part',
 | 
				
			||||||
 | 
					            'part_detail',
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,10 +81,10 @@ function showAllocationSubTable(index, row, element) {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            field: 'location_id',
 | 
					            field: 'location',
 | 
				
			||||||
            title: 'Location',
 | 
					            title: 'Location',
 | 
				
			||||||
            formatter: function(value, row, index, field) {
 | 
					            formatter: function(value, row, index, field) {
 | 
				
			||||||
                return renderLink(row.location_path, `/stock/location/${row.location_id}/`);
 | 
					                return renderLink(row.location_path, `/stock/location/${row.location}/`);
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,52 +8,43 @@
 | 
				
			|||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block heading %}
 | 
					{% block heading %}
 | 
				
			||||||
{% trans "Part Stock Allocations" %}
 | 
					{% trans "Build Order Allocations" %}
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block details %}
 | 
					{% block details %}
 | 
				
			||||||
<table class='table table-striped table-condensed' id='build-table'>
 | 
					
 | 
				
			||||||
<tr>
 | 
					<table class='table table-striped table-condensed' id='build-order-table'></table>
 | 
				
			||||||
    <th>{% trans "Order" %}</th>
 | 
					 | 
				
			||||||
    <th>{% trans "Stock Item" %}</th>
 | 
					 | 
				
			||||||
    <th>{% trans "Quantity" %}</th>
 | 
					 | 
				
			||||||
</tr>
 | 
					 | 
				
			||||||
{% for allocation in part.build_order_allocations %}
 | 
					 | 
				
			||||||
<tr>
 | 
					 | 
				
			||||||
    <td><a href="{% url 'build-detail' allocation.build.id %}">{% trans "Build Order" %}: {{ allocation.build }}</a></td>
 | 
					 | 
				
			||||||
    <td><a href="{% url 'stock-item-detail' allocation.stock_item.id %}">{% trans "Stock Item" %}: {{ allocation.stock_item }}</a></td>
 | 
					 | 
				
			||||||
    <td>{% decimal allocation.quantity %}</td>
 | 
					 | 
				
			||||||
</tr>
 | 
					 | 
				
			||||||
{% endfor %}
 | 
					 | 
				
			||||||
{% for allocation in part.sales_order_allocations %}
 | 
					 | 
				
			||||||
<tr>
 | 
					 | 
				
			||||||
    <td><a href="{% url 'so-detail' allocation.line.order.id %}">{% trans "Sales Order" %}: {{ allocation.line.order }}</a></td>
 | 
					 | 
				
			||||||
    <td><a href="{% url 'stock-item-detail' allocation.item.id %}">{% trans "Stock Item" %}: {{ allocation.item }}</a></td>
 | 
					 | 
				
			||||||
    <td>{% decimal allocation.quantity %}</td>
 | 
					 | 
				
			||||||
</tr>
 | 
					 | 
				
			||||||
{% endfor %}
 | 
					 | 
				
			||||||
</table>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block pre_content_panel %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class='panel panel-default panel-inventree'>
 | 
				
			||||||
 | 
					    <div class='panel-heading'>
 | 
				
			||||||
 | 
					        <h4>{% trans "Sales  Order Allocations" %}</h4>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    <div class='panel-content'>
 | 
				
			||||||
 | 
					        <table class='table table-striped table-condensed' id='sales-order-table'></table>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block js_ready %}
 | 
					{% block js_ready %}
 | 
				
			||||||
{{ block.super }}
 | 
					{{ block.super }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $("#build-table").inventreeTable({
 | 
					    loadSalesOrderAllocationTable("#sales-order-table", {
 | 
				
			||||||
        columns: [
 | 
					        params: {
 | 
				
			||||||
            {
 | 
					            part: {{ part.id }},
 | 
				
			||||||
                title: '{% trans "Order" %}',
 | 
					        }
 | 
				
			||||||
                sortable: true,
 | 
					    });
 | 
				
			||||||
            },
 | 
					
 | 
				
			||||||
            {
 | 
					    loadBuildOrderAllocationTable("#build-order-table", {
 | 
				
			||||||
                title: '{% trans "Stock Item" %}',
 | 
					        params: {
 | 
				
			||||||
                sortable: true,
 | 
					            part: {{ part.id }},
 | 
				
			||||||
            },
 | 
					        }
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                title: '{% trans "Quantity" %}',
 | 
					 | 
				
			||||||
                sortable: true,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -195,8 +195,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block pre_content_panel %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class='panel panel-default panel-inventree'>
 | 
					<div class='panel panel-default panel-inventree'>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div class='panel-heading'>
 | 
					    <div class='panel-heading'>
 | 
				
			||||||
        <h4>
 | 
					        <h4>
 | 
				
			||||||
            {% block heading %}
 | 
					            {% block heading %}
 | 
				
			||||||
@@ -210,7 +215,11 @@
 | 
				
			|||||||
        <!-- Specific part details go here... -->
 | 
					        <!-- Specific part details go here... -->
 | 
				
			||||||
        {% endblock %}
 | 
					        {% endblock %}
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					{% block post_content_panel %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -155,6 +155,88 @@ function makeBuildOutputActionButtons(output, buildInfo, lines) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function loadBuildOrderAllocationTable(table, options={}) {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Load a table showing all the BuildOrder allocations for a given part
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    options.params['part_detail'] = true;
 | 
				
			||||||
 | 
					    options.params['build_detail'] = true;
 | 
				
			||||||
 | 
					    options.params['location_detail'] = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var filters = loadTableFilters("buildorderallocation");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var key in options.params) {
 | 
				
			||||||
 | 
					        filters[key] = options.params[key];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setupFilterList("buildorderallocation", $(table));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $(table).inventreeTable({
 | 
				
			||||||
 | 
					        url: '{% url "api-build-item-list" %}',
 | 
				
			||||||
 | 
					        queryParams: filters,
 | 
				
			||||||
 | 
					        name: 'buildorderallocation',
 | 
				
			||||||
 | 
					        groupBy: false,
 | 
				
			||||||
 | 
					        search: false,
 | 
				
			||||||
 | 
					        paginationVAlign: 'bottom',
 | 
				
			||||||
 | 
					        original: options.params,
 | 
				
			||||||
 | 
					        formatNoMatches: function() {
 | 
				
			||||||
 | 
					            return '{% trans "No build order allocations found" %}'
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        columns: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                field: 'pk',
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                switchable: false,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                field: 'build',
 | 
				
			||||||
 | 
					                switchable: false,
 | 
				
			||||||
 | 
					                title: '{% trans "Build Order" %}',
 | 
				
			||||||
 | 
					                formatter: function(value, row) {
 | 
				
			||||||
 | 
					                    var prefix = "{% settings_value 'BUILDORDER_REFERENCE_PREFIX' %}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var ref = `${prefix}${row.build_detail.reference}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return renderLink(ref, `/build/${row.build}/`);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                field: 'item',
 | 
				
			||||||
 | 
					                title: '{% trans "Stock Item" %}',
 | 
				
			||||||
 | 
					                formatter: function(value, row) {
 | 
				
			||||||
 | 
					                    // Render a link to the particular stock item
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var link = `/stock/item/${row.stock_item}/`;
 | 
				
			||||||
 | 
					                    var text = `{% trans "Stock Item" %} ${row.stock_item}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return renderLink(text, link);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                field: 'location',
 | 
				
			||||||
 | 
					                title: '{% trans "Location" %}',
 | 
				
			||||||
 | 
					                formatter: function(value, row) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (!value) {
 | 
				
			||||||
 | 
					                        return '{% trans "Location not specified" %}';
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    var link = `/stock/location/${value}`;
 | 
				
			||||||
 | 
					                    var text = row.location_detail.description;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return renderLink(text, link);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                field: 'quantity',
 | 
				
			||||||
 | 
					                title: '{% trans "Quantity" %}',
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
 | 
					function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
     * Load the "allocation table" for a particular build output.
 | 
					     * Load the "allocation table" for a particular build output.
 | 
				
			||||||
@@ -347,6 +429,8 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var params = {
 | 
					            var params = {
 | 
				
			||||||
                build: buildId,
 | 
					                build: buildId,
 | 
				
			||||||
 | 
					                part_detail: true,
 | 
				
			||||||
 | 
					                location_detail: true,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (output) {
 | 
					            if (output) {
 | 
				
			||||||
@@ -466,8 +550,8 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
 | 
				
			|||||||
                        title: '{% trans "Part" %}',
 | 
					                        title: '{% trans "Part" %}',
 | 
				
			||||||
                        formatter: function(value, row) {
 | 
					                        formatter: function(value, row) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            var html = imageHoverIcon(row.part_thumb);
 | 
					                            var html = imageHoverIcon(row.part_detail.thumbnail);
 | 
				
			||||||
                            html += renderLink(row.part_name, `/part/${value}/`);
 | 
					                            html += renderLink(row.part_detail.full_name, `/part/${value}/`);
 | 
				
			||||||
                            return html;
 | 
					                            return html;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -310,3 +310,88 @@ function loadSalesOrderTable(table, options) {
 | 
				
			|||||||
        ],
 | 
					        ],
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function loadSalesOrderAllocationTable(table, options={}) {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Load a table with SalesOrderAllocation items
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    options.params = options.params || {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    options.params['location_detail'] = true;
 | 
				
			||||||
 | 
					    options.params['part_detail'] = true;
 | 
				
			||||||
 | 
					    options.params['item_detail'] = true;
 | 
				
			||||||
 | 
					    options.params['order_detail'] = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var filters = loadTableFilters("salesorderallocation");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var key in options.params) {
 | 
				
			||||||
 | 
					        filters[key] = options.params[key];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setupFilterList("salesorderallocation", $(table));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $(table).inventreeTable({
 | 
				
			||||||
 | 
					        url: '{% url "api-so-allocation-list" %}',
 | 
				
			||||||
 | 
					        queryParams: filters,
 | 
				
			||||||
 | 
					        name: 'salesorderallocation',
 | 
				
			||||||
 | 
					        groupBy: false,
 | 
				
			||||||
 | 
					        search: false,
 | 
				
			||||||
 | 
					        paginationVAlign: 'bottom',
 | 
				
			||||||
 | 
					        original: options.params,
 | 
				
			||||||
 | 
					        formatNoMatches: function() { return '{% trans "No sales order allocations found" %}'; },
 | 
				
			||||||
 | 
					        columns: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                field: 'pk',
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                switchable: false,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                field: 'order',
 | 
				
			||||||
 | 
					                switchable: false,
 | 
				
			||||||
 | 
					                title: '{% trans "Order" %}',
 | 
				
			||||||
 | 
					                switchable: false,
 | 
				
			||||||
 | 
					                formatter: function(value, row) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var prefix = "{% settings_value 'SALESORDER_REFERENCE_PREFIX' %}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var ref = `${prefix}${row.order_detail.reference}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return renderLink(ref, `/order/sales-order/${row.order}/`);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                field: 'item',
 | 
				
			||||||
 | 
					                title: '{% trans "Stock Item" %}',
 | 
				
			||||||
 | 
					                formatter: function(value, row) {
 | 
				
			||||||
 | 
					                    // Render a link to the particular stock item
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var link = `/stock/item/${row.item}/`;
 | 
				
			||||||
 | 
					                    var text = `{% trans "Stock Item" %} ${row.item}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return renderLink(text, link);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                field: 'location',
 | 
				
			||||||
 | 
					                title: '{% trans "Location" %}',
 | 
				
			||||||
 | 
					                formatter: function(value, row) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (!value) {
 | 
				
			||||||
 | 
					                        return '{% trans "Location not specified" %}';
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    var link = `/stock/location/${value}`;
 | 
				
			||||||
 | 
					                    var text = row.location_detail.description;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return renderLink(text, link);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                field: 'quantity',
 | 
				
			||||||
 | 
					                title: '{% trans "Quantity" %}',
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -135,7 +135,7 @@ $.fn.inventreeTable = function(options) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Pagingation options (can be server-side or client-side as specified by the caller)
 | 
					    // Pagingation options (can be server-side or client-side as specified by the caller)
 | 
				
			||||||
    options.pagination = true;
 | 
					    options.pagination = true;
 | 
				
			||||||
    options.paginationVAlign = 'both';
 | 
					    options.paginationVAlign = options.paginationVAlign || 'both';
 | 
				
			||||||
    options.pageSize = inventreeLoad(varName, 25);
 | 
					    options.pageSize = inventreeLoad(varName, 25);
 | 
				
			||||||
    options.pageList = [25, 50, 100, 250, 'all'];
 | 
					    options.pageList = [25, 50, 100, 250, 'all'];
 | 
				
			||||||
    options.totalField = 'count';
 | 
					    options.totalField = 'count';
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										15
									
								
								tasks.py
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								tasks.py
									
									
									
									
									
								
							@@ -129,6 +129,14 @@ def wait(c):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    manage(c, "wait_for_db")
 | 
					    manage(c, "wait_for_db")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@task
 | 
				
			||||||
 | 
					def rebuild(c):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Rebuild database models with MPTT structures
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    manage(c, "rebuild_models")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@task
 | 
					@task
 | 
				
			||||||
def migrate(c):
 | 
					def migrate(c):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
@@ -243,12 +251,15 @@ def content_excludes():
 | 
				
			|||||||
        "contenttypes",
 | 
					        "contenttypes",
 | 
				
			||||||
        "sessions.session",
 | 
					        "sessions.session",
 | 
				
			||||||
        "auth.permission",
 | 
					        "auth.permission",
 | 
				
			||||||
 | 
					        "authtoken.token",
 | 
				
			||||||
        "error_report.error",
 | 
					        "error_report.error",
 | 
				
			||||||
        "admin.logentry",
 | 
					        "admin.logentry",
 | 
				
			||||||
        "django_q.schedule",
 | 
					        "django_q.schedule",
 | 
				
			||||||
        "django_q.task",
 | 
					        "django_q.task",
 | 
				
			||||||
        "django_q.ormq",
 | 
					        "django_q.ormq",
 | 
				
			||||||
        "users.owner",
 | 
					        "users.owner",
 | 
				
			||||||
 | 
					        "exchange.rate",
 | 
				
			||||||
 | 
					        "exchange.exchangebackend",
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    output = ""
 | 
					    output = ""
 | 
				
			||||||
@@ -311,7 +322,7 @@ def export_records(c, filename='data.json'):
 | 
				
			|||||||
    print("Data export completed")
 | 
					    print("Data export completed")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@task(help={'filename': 'Input filename'})
 | 
					@task(help={'filename': 'Input filename'}, post=[rebuild])
 | 
				
			||||||
def import_records(c, filename='data.json'):
 | 
					def import_records(c, filename='data.json'):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Import database records from a file
 | 
					    Import database records from a file
 | 
				
			||||||
@@ -354,7 +365,7 @@ def import_records(c, filename='data.json'):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    print("Data import completed")
 | 
					    print("Data import completed")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@task
 | 
					@task(post=[rebuild])
 | 
				
			||||||
def import_fixtures(c):
 | 
					def import_fixtures(c):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Import fixture data into the database.
 | 
					    Import fixture data into the database.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user