mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-01 03:00:54 +00:00
Add 'Tag' management (#4367)
* 'Tag' management Fixes #83 * Add for ManufacturerPart, SupplierPart * Add tags for StockLocation, StockItem * fix serializer definition * add migrations * update pre-commit * bump dependencies * revert updates * set version for bugbear * remove bugbear * readd bugbear remove isort * and remove bugbear again * remove bugbear * make tag fields not required * add ruleset * Merge migrations * fix migrations * add unittest for detail * test tag add * order api * reduce database access * add tag modification test * use overriden serializer to ensuer the manager is always available * fix typo * fix serializer * increae query thershold by 1 * move tag serializer * fix migrations * content_types are changing between tests - removing them * remove unneeded fixture * Add basic docs * bump API version * add api access to the docs * add python code * Add tags to search and filters for all models
This commit is contained in:
@ -214,7 +214,9 @@ class StockLocationList(APIDownloadMixin, ListCreateAPI):
|
||||
- POST: Create a new StockLocation
|
||||
"""
|
||||
|
||||
queryset = StockLocation.objects.all()
|
||||
queryset = StockLocation.objects.all().prefetch_related(
|
||||
'tags',
|
||||
)
|
||||
serializer_class = StockSerializers.LocationSerializer
|
||||
|
||||
def download_queryset(self, queryset, export_format):
|
||||
@ -300,11 +302,15 @@ class StockLocationList(APIDownloadMixin, ListCreateAPI):
|
||||
'name',
|
||||
'structural',
|
||||
'external',
|
||||
'tags__name',
|
||||
'tags__slug',
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
'name',
|
||||
'description',
|
||||
'tags__name',
|
||||
'tags__slug',
|
||||
]
|
||||
|
||||
ordering_fields = [
|
||||
@ -351,6 +357,8 @@ class StockFilter(rest_filters.FilterSet):
|
||||
'customer',
|
||||
'sales_order',
|
||||
'purchase_order',
|
||||
'tags__name',
|
||||
'tags__slug',
|
||||
]
|
||||
|
||||
# Relationship filters
|
||||
@ -811,7 +819,8 @@ class StockList(APIDownloadMixin, ListCreateDestroyAPIView):
|
||||
queryset = queryset.prefetch_related(
|
||||
'part',
|
||||
'part__category',
|
||||
'location'
|
||||
'location',
|
||||
'tags',
|
||||
)
|
||||
|
||||
return queryset
|
||||
@ -1035,6 +1044,8 @@ class StockList(APIDownloadMixin, ListCreateDestroyAPIView):
|
||||
'part__IPN',
|
||||
'part__description',
|
||||
'location__name',
|
||||
'tags__name',
|
||||
'tags__slug',
|
||||
]
|
||||
|
||||
|
||||
|
25
InvenTree/stock/migrations/0098_auto_20230427_2033.py
Normal file
25
InvenTree/stock/migrations/0098_auto_20230427_2033.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 3.2.18 on 2023-04-27 20:33
|
||||
|
||||
from django.db import migrations
|
||||
import taggit.managers
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('taggit', '0005_auto_20220424_2025'),
|
||||
('stock', '0097_alter_stockitem_notes'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='stockitem',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='stocklocation',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
|
||||
),
|
||||
]
|
@ -21,6 +21,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from jinja2 import Template
|
||||
from mptt.managers import TreeManager
|
||||
from mptt.models import MPTTModel, TreeForeignKey
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
import common.models
|
||||
import InvenTree.helpers
|
||||
@ -53,6 +54,8 @@ class StockLocation(InvenTreeBarcodeMixin, MetadataMixin, InvenTreeTree):
|
||||
verbose_name = _('Stock Location')
|
||||
verbose_name_plural = _('Stock Locations')
|
||||
|
||||
tags = TaggableManager()
|
||||
|
||||
def delete_recursive(self, *args, **kwargs):
|
||||
"""This function handles the recursive deletion of sub-locations depending on kwargs contents"""
|
||||
delete_stock_items = kwargs.get('delete_stock_items', False)
|
||||
@ -321,6 +324,8 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo
|
||||
}
|
||||
}
|
||||
|
||||
tags = TaggableManager()
|
||||
|
||||
# A Query filter which will be re-used in multiple places to determine if a StockItem is actually "in stock"
|
||||
IN_STOCK_FILTER = Q(
|
||||
quantity__gt=0,
|
||||
|
@ -12,6 +12,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
from rest_framework.serializers import ValidationError
|
||||
from sql_util.utils import SubqueryCount, SubquerySum
|
||||
from taggit.serializers import TagListSerializerField
|
||||
|
||||
import common.models
|
||||
import company.models
|
||||
@ -76,7 +77,7 @@ class StockItemSerializerBrief(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
return value
|
||||
|
||||
|
||||
class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
class StockItemSerializer(InvenTree.serializers.InvenTreeTagModelSerializer):
|
||||
"""Serializer for a StockItem.
|
||||
|
||||
- Includes serialization for the linked part
|
||||
@ -123,6 +124,8 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
'updated',
|
||||
'purchase_price',
|
||||
'purchase_price_currency',
|
||||
|
||||
'tags',
|
||||
]
|
||||
|
||||
"""
|
||||
@ -236,6 +239,8 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
purchase_order_reference = serializers.CharField(source='purchase_order.reference', read_only=True)
|
||||
sales_order_reference = serializers.CharField(source='sales_order.reference', read_only=True)
|
||||
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Add detail fields."""
|
||||
part_detail = kwargs.pop('part_detail', False)
|
||||
@ -566,7 +571,7 @@ class LocationTreeSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class LocationSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
class LocationSerializer(InvenTree.serializers.InvenTreeTagModelSerializer):
|
||||
"""Detailed information about a stock location."""
|
||||
|
||||
class Meta:
|
||||
@ -587,6 +592,8 @@ class LocationSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
'icon',
|
||||
'structural',
|
||||
'external',
|
||||
|
||||
'tags',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
@ -610,6 +617,8 @@ class LocationSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
|
||||
level = serializers.IntegerField(read_only=True)
|
||||
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
|
||||
class StockItemAttachmentSerializer(InvenTree.serializers.InvenTreeAttachmentSerializer):
|
||||
"""Serializer for StockItemAttachment model."""
|
||||
|
Reference in New Issue
Block a user