mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-03 22:55:43 +00:00 
			
		
		
		
	Default location column (#7587)
* Add "default_location_detail" serializer to part API * Add column to CUI table * Implement in PUI part table * Update API version
This commit is contained in:
		@@ -1,12 +1,15 @@
 | 
			
		||||
"""InvenTree API version information."""
 | 
			
		||||
 | 
			
		||||
# InvenTree API version
 | 
			
		||||
INVENTREE_API_VERSION = 213
 | 
			
		||||
INVENTREE_API_VERSION = 214
 | 
			
		||||
 | 
			
		||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
INVENTREE_API_TEXT = """
 | 
			
		||||
v214 - 2024-07-08 : https://github.com/inventree/InvenTree/pull/7587
 | 
			
		||||
    - Adds "default_location_detail" field to the Part API
 | 
			
		||||
 | 
			
		||||
v213 - 2024-07-06 : https://github.com/inventree/InvenTree/pull/7527
 | 
			
		||||
    - Adds 'locked' field to Part API
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ from sql_util.utils import SubqueryCount
 | 
			
		||||
from taggit.serializers import TagListSerializerField
 | 
			
		||||
 | 
			
		||||
import part.filters
 | 
			
		||||
import part.serializers as part_serializers
 | 
			
		||||
from importer.mixins import DataImportExportSerializerMixin
 | 
			
		||||
from importer.registry import register_importer
 | 
			
		||||
from InvenTree.serializers import (
 | 
			
		||||
@@ -22,7 +23,6 @@ from InvenTree.serializers import (
 | 
			
		||||
    NotesFieldMixin,
 | 
			
		||||
    RemoteImageMixin,
 | 
			
		||||
)
 | 
			
		||||
from part.serializers import PartBriefSerializer
 | 
			
		||||
 | 
			
		||||
from .models import (
 | 
			
		||||
    Address,
 | 
			
		||||
@@ -254,7 +254,9 @@ class ManufacturerPartSerializer(
 | 
			
		||||
        if prettify is not True:
 | 
			
		||||
            self.fields.pop('pretty_name', None)
 | 
			
		||||
 | 
			
		||||
    part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
 | 
			
		||||
    part_detail = part_serializers.PartBriefSerializer(
 | 
			
		||||
        source='part', many=False, read_only=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    manufacturer_detail = CompanyBriefSerializer(
 | 
			
		||||
        source='manufacturer', many=False, read_only=True
 | 
			
		||||
@@ -387,7 +389,9 @@ class SupplierPartSerializer(
 | 
			
		||||
 | 
			
		||||
    pack_quantity_native = serializers.FloatField(read_only=True)
 | 
			
		||||
 | 
			
		||||
    part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
 | 
			
		||||
    part_detail = part_serializers.PartBriefSerializer(
 | 
			
		||||
        source='part', many=False, read_only=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    supplier_detail = CompanyBriefSerializer(
 | 
			
		||||
        source='supplier', many=False, read_only=True
 | 
			
		||||
 
 | 
			
		||||
@@ -1204,6 +1204,7 @@ class PartMixin:
 | 
			
		||||
 | 
			
		||||
            kwargs['parameters'] = str2bool(params.get('parameters', None))
 | 
			
		||||
            kwargs['category_detail'] = str2bool(params.get('category_detail', False))
 | 
			
		||||
            kwargs['location_detail'] = str2bool(params.get('location_detail', False))
 | 
			
		||||
            kwargs['path_detail'] = str2bool(params.get('path_detail', False))
 | 
			
		||||
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
@@ -1354,6 +1355,7 @@ class PartList(PartMixin, DataExportViewMixin, ListCreateAPI):
 | 
			
		||||
        'total_in_stock',
 | 
			
		||||
        'unallocated_stock',
 | 
			
		||||
        'category',
 | 
			
		||||
        'default_location',
 | 
			
		||||
        'last_stocktake',
 | 
			
		||||
        'units',
 | 
			
		||||
        'pricing_min',
 | 
			
		||||
 
 | 
			
		||||
@@ -591,6 +591,21 @@ class InitialSupplierSerializer(serializers.Serializer):
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DefaultLocationSerializer(InvenTree.serializers.InvenTreeModelSerializer):
 | 
			
		||||
    """Brief serializer for a StockLocation object.
 | 
			
		||||
 | 
			
		||||
    Defined here, rather than stock.serializers, to negotiate circular imports.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        """Metaclass options."""
 | 
			
		||||
 | 
			
		||||
        import stock.models as stock_models
 | 
			
		||||
 | 
			
		||||
        model = stock_models.StockLocation
 | 
			
		||||
        fields = ['pk', 'name', 'pathstring']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_importer()
 | 
			
		||||
class PartSerializer(
 | 
			
		||||
    DataImportExportSerializerMixin,
 | 
			
		||||
@@ -623,6 +638,7 @@ class PartSerializer(
 | 
			
		||||
            'creation_user',
 | 
			
		||||
            'default_expiry',
 | 
			
		||||
            'default_location',
 | 
			
		||||
            'default_location_detail',
 | 
			
		||||
            'default_supplier',
 | 
			
		||||
            'description',
 | 
			
		||||
            'full_name',
 | 
			
		||||
@@ -687,6 +703,7 @@ class PartSerializer(
 | 
			
		||||
        """
 | 
			
		||||
        self.starred_parts = kwargs.pop('starred_parts', [])
 | 
			
		||||
        category_detail = kwargs.pop('category_detail', False)
 | 
			
		||||
        location_detail = kwargs.pop('location_detail', False)
 | 
			
		||||
        parameters = kwargs.pop('parameters', False)
 | 
			
		||||
        create = kwargs.pop('create', False)
 | 
			
		||||
        pricing = kwargs.pop('pricing', True)
 | 
			
		||||
@@ -697,6 +714,9 @@ class PartSerializer(
 | 
			
		||||
        if not category_detail:
 | 
			
		||||
            self.fields.pop('category_detail', None)
 | 
			
		||||
 | 
			
		||||
        if not location_detail:
 | 
			
		||||
            self.fields.pop('default_location_detail', None)
 | 
			
		||||
 | 
			
		||||
        if not parameters:
 | 
			
		||||
            self.fields.pop('parameters', None)
 | 
			
		||||
 | 
			
		||||
@@ -740,6 +760,8 @@ class PartSerializer(
 | 
			
		||||
 | 
			
		||||
        Performing database queries as efficiently as possible, to reduce database trips.
 | 
			
		||||
        """
 | 
			
		||||
        queryset = queryset.prefetch_related('category', 'default_location')
 | 
			
		||||
 | 
			
		||||
        # Annotate with the total number of stock items
 | 
			
		||||
        queryset = queryset.annotate(stock_item_count=SubqueryCount('stock_items'))
 | 
			
		||||
 | 
			
		||||
@@ -833,6 +855,10 @@ class PartSerializer(
 | 
			
		||||
        child=serializers.DictField(), source='category.get_path', read_only=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    default_location_detail = DefaultLocationSerializer(
 | 
			
		||||
        source='default_location', many=False, read_only=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    category_name = serializers.CharField(
 | 
			
		||||
        source='category.name', read_only=True, label=_('Category Name')
 | 
			
		||||
    )
 | 
			
		||||
 
 | 
			
		||||
@@ -17,19 +17,19 @@ from taggit.serializers import TagListSerializerField
 | 
			
		||||
 | 
			
		||||
import build.models
 | 
			
		||||
import company.models
 | 
			
		||||
import company.serializers as company_serializers
 | 
			
		||||
import InvenTree.helpers
 | 
			
		||||
import InvenTree.serializers
 | 
			
		||||
import order.models
 | 
			
		||||
import part.filters as part_filters
 | 
			
		||||
import part.models as part_models
 | 
			
		||||
import part.serializers as part_serializers
 | 
			
		||||
import stock.filters
 | 
			
		||||
import stock.status_codes
 | 
			
		||||
from common.settings import get_global_setting
 | 
			
		||||
from company.serializers import SupplierPartSerializer
 | 
			
		||||
from importer.mixins import DataImportExportSerializerMixin
 | 
			
		||||
from importer.registry import register_importer
 | 
			
		||||
from InvenTree.serializers import InvenTreeCurrencySerializer, InvenTreeDecimalField
 | 
			
		||||
from part.serializers import PartBriefSerializer, PartTestTemplateSerializer
 | 
			
		||||
 | 
			
		||||
from .models import (
 | 
			
		||||
    StockItem,
 | 
			
		||||
@@ -233,7 +233,9 @@ class StockItemTestResultSerializer(
 | 
			
		||||
        label=_('Test template for this result'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    template_detail = PartTestTemplateSerializer(source='template', read_only=True)
 | 
			
		||||
    template_detail = part_serializers.PartTestTemplateSerializer(
 | 
			
		||||
        source='template', read_only=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    attachment = InvenTree.serializers.InvenTreeAttachmentSerializerField(
 | 
			
		||||
        required=False
 | 
			
		||||
@@ -560,7 +562,7 @@ class StockItemSerializer(
 | 
			
		||||
    sku = serializers.CharField(source='supplier_part.SKU', read_only=True)
 | 
			
		||||
 | 
			
		||||
    # Optional detail fields, which can be appended via query parameters
 | 
			
		||||
    supplier_part_detail = SupplierPartSerializer(
 | 
			
		||||
    supplier_part_detail = company_serializers.SupplierPartSerializer(
 | 
			
		||||
        source='supplier_part',
 | 
			
		||||
        supplier_detail=False,
 | 
			
		||||
        manufacturer_detail=False,
 | 
			
		||||
@@ -568,7 +570,9 @@ class StockItemSerializer(
 | 
			
		||||
        many=False,
 | 
			
		||||
        read_only=True,
 | 
			
		||||
    )
 | 
			
		||||
    part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
 | 
			
		||||
    part_detail = part_serializers.PartBriefSerializer(
 | 
			
		||||
        source='part', many=False, read_only=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    location_detail = LocationBriefSerializer(
 | 
			
		||||
        source='location', many=False, read_only=True
 | 
			
		||||
 
 | 
			
		||||
@@ -2276,6 +2276,7 @@ function loadPartTable(table, url, options={}) {
 | 
			
		||||
 | 
			
		||||
    // Ensure category detail is included
 | 
			
		||||
    options.params['category_detail'] = true;
 | 
			
		||||
    options.params['location_detail'] = true;
 | 
			
		||||
 | 
			
		||||
    let filters = {};
 | 
			
		||||
 | 
			
		||||
@@ -2389,6 +2390,19 @@ function loadPartTable(table, url, options={}) {
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    columns.push({
 | 
			
		||||
        field: 'default_location',
 | 
			
		||||
        title: '{% trans "Default Location" %}',
 | 
			
		||||
        sortable: true,
 | 
			
		||||
        formatter: function(value, row) {
 | 
			
		||||
            if (row.default_location && row.default_location_detail) {
 | 
			
		||||
                let text = shortenString(row.default_location_detail.pathstring);
 | 
			
		||||
                return withTitle(renderLink(text, `/stock/location/${row.default_location}/`), row.default_location_detail.pathstring);
 | 
			
		||||
            } else {
 | 
			
		||||
                return '-';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    columns.push({
 | 
			
		||||
        field: 'total_in_stock',
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,11 @@ function partTableColumns(): TableColumn[] {
 | 
			
		||||
      sortable: true,
 | 
			
		||||
      render: (record: any) => record.category_detail?.pathstring
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      accessor: 'default_location',
 | 
			
		||||
      sortable: true,
 | 
			
		||||
      render: (record: any) => record.default_location_detail?.pathstring
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      accessor: 'total_in_stock',
 | 
			
		||||
      sortable: true,
 | 
			
		||||
@@ -327,7 +332,8 @@ export function PartListTable({ props }: { props: InvenTreeTableProps }) {
 | 
			
		||||
          tableActions: tableActions,
 | 
			
		||||
          params: {
 | 
			
		||||
            ...props.params,
 | 
			
		||||
            category_detail: true
 | 
			
		||||
            category_detail: true,
 | 
			
		||||
            location_detail: true
 | 
			
		||||
          }
 | 
			
		||||
        }}
 | 
			
		||||
      />
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user