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:
Oliver 2024-07-09 08:13:21 +10:00 committed by GitHub
parent 189948be06
commit 1017ff0605
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 69 additions and 10 deletions

View File

@ -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

View File

@ -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

View File

@ -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',

View File

@ -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')
)

View File

@ -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

View File

@ -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',

View File

@ -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
}
}}
/>