mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +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:
parent
189948be06
commit
1017ff0605
@ -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
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user