mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 13:05:42 +00:00
Add Metadata to further models (#4410)
* Add metadata for ManufacturerPart * Add Metadata for SupplierPart * Add metadata to label models * Add metadata to order line items * Add metadata to shipment * Add metadata to Build and BuildItem * Add metadata to BomItem * Add metadata to PartParameterTemplate * Syntax, lint * Fix isort style * Lint * Correction of model name * Metadata for Reports * Fix silly error * Fix silly error * Correct model name * Correct model name * Correction * Correct company urls * Apply generic model to Report metadat * Rename/remove redundant import * Remove shadowing of report in loop variable * Update import ordering * More corrections * better docstrings * Correct names for API endpoints * Default to PO, required for api-doc to work * Changes by @matmair * Suppress metadata from Bom export * Add migration files * Increment API version * Add tests for all Metadata models, even previously existing ones * Update tests * Fix * Delay tests * Fix imports * Fix tests * API Version number * Remove unused import * isort * Revent unintended change of cache
This commit is contained in:
@ -318,7 +318,7 @@ class BomItemResource(InvenTreeResource):
|
||||
is_importing = getattr(self, 'is_importing', False)
|
||||
include_pricing = getattr(self, 'include_pricing', False)
|
||||
|
||||
to_remove = []
|
||||
to_remove = ['metadata']
|
||||
|
||||
if is_importing or not include_pricing:
|
||||
# Remove pricing fields in this instance
|
||||
|
@ -1459,6 +1459,16 @@ class PartParameterTemplateDetail(RetrieveUpdateDestroyAPI):
|
||||
serializer_class = part_serializers.PartParameterTemplateSerializer
|
||||
|
||||
|
||||
class PartParameterTemplateMetadata(RetrieveUpdateAPI):
|
||||
"""API endpoint for viewing / updating PartParameterTemplate metadata."""
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
"""Return a MetadataSerializer pointing to the referenced PartParameterTemplate instance"""
|
||||
return MetadataSerializer(PartParameterTemplate, *args, **kwargs)
|
||||
|
||||
queryset = PartParameterTemplate.objects.all()
|
||||
|
||||
|
||||
class PartParameterList(ListCreateAPI):
|
||||
"""API endpoint for accessing a list of PartParameter objects.
|
||||
|
||||
@ -1926,6 +1936,16 @@ class BomItemSubstituteDetail(RetrieveUpdateDestroyAPI):
|
||||
serializer_class = part_serializers.BomItemSubstituteSerializer
|
||||
|
||||
|
||||
class BomItemMetadata(RetrieveUpdateAPI):
|
||||
"""API endpoint for viewing / updating PartBOM metadata."""
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
"""Return a MetadataSerializer pointing to the referenced PartCategory instance"""
|
||||
return MetadataSerializer(BomItem, *args, **kwargs)
|
||||
|
||||
queryset = BomItem.objects.all()
|
||||
|
||||
|
||||
part_api_urls = [
|
||||
|
||||
# Base URL for PartCategory API endpoints
|
||||
@ -1933,8 +1953,8 @@ part_api_urls = [
|
||||
re_path(r'^tree/', CategoryTree.as_view(), name='api-part-category-tree'),
|
||||
|
||||
re_path(r'^parameters/', include([
|
||||
re_path('^(?P<pk>\d+)/', CategoryParameterDetail.as_view(), name='api-part-category-parameter-detail'),
|
||||
re_path('^.*$', CategoryParameterList.as_view(), name='api-part-category-parameter-list'),
|
||||
re_path(r'^(?P<pk>\d+)/', CategoryParameterDetail.as_view(), name='api-part-category-parameter-detail'),
|
||||
re_path(r'^.*$', CategoryParameterList.as_view(), name='api-part-category-parameter-list'),
|
||||
])),
|
||||
|
||||
# Category detail endpoints
|
||||
@ -1982,7 +2002,10 @@ part_api_urls = [
|
||||
# Base URL for PartParameter API endpoints
|
||||
re_path(r'^parameter/', include([
|
||||
path('template/', include([
|
||||
re_path(r'^(?P<pk>\d+)/', PartParameterTemplateDetail.as_view(), name='api-part-parameter-template-detail'),
|
||||
re_path(r'^(?P<pk>\d+)/', include([
|
||||
re_path(r'^metadata/?', PartParameterTemplateMetadata.as_view(), name='api-part-parameter-template-metadata'),
|
||||
re_path(r'^.*$', PartParameterTemplateDetail.as_view(), name='api-part-parameter-template-detail'),
|
||||
])),
|
||||
re_path(r'^.*$', PartParameterTemplateList.as_view(), name='api-part-parameter-template-list'),
|
||||
])),
|
||||
|
||||
@ -2059,6 +2082,7 @@ bom_api_urls = [
|
||||
# BOM Item Detail
|
||||
re_path(r'^(?P<pk>\d+)/', include([
|
||||
re_path(r'^validate/?', BomItemValidate.as_view(), name='api-bom-item-validate'),
|
||||
re_path(r'^metadata/?', BomItemMetadata.as_view(), name='api-bom-item-metadata'),
|
||||
re_path(r'^.*$', BomDetail.as_view(), name='api-bom-item-detail'),
|
||||
])),
|
||||
|
||||
|
23
InvenTree/part/migrations/0103_auto_20230317_0816.py
Normal file
23
InvenTree/part/migrations/0103_auto_20230317_0816.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Generated by Django 3.2.18 on 2023-03-17 08:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('part', '0102_auto_20230314_0112'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='bomitem',
|
||||
name='metadata',
|
||||
field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='partparametertemplate',
|
||||
name='metadata',
|
||||
field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'),
|
||||
),
|
||||
]
|
@ -3277,7 +3277,7 @@ def validate_template_name(name):
|
||||
"""Placeholder for legacy function used in migrations."""
|
||||
|
||||
|
||||
class PartParameterTemplate(models.Model):
|
||||
class PartParameterTemplate(MetadataMixin, models.Model):
|
||||
"""A PartParameterTemplate provides a template for key:value pairs for extra parameters fields/values to be added to a Part.
|
||||
|
||||
This allows users to arbitrarily assign data fields to a Part beyond the built-in attributes.
|
||||
@ -3419,7 +3419,7 @@ class PartCategoryParameterTemplate(models.Model):
|
||||
help_text=_('Default Parameter Value'))
|
||||
|
||||
|
||||
class BomItem(DataImportMixin, models.Model):
|
||||
class BomItem(DataImportMixin, MetadataMixin, models.Model):
|
||||
"""A BomItem links a part to its component items.
|
||||
|
||||
A part can have a BOM (bill of materials) which defines
|
||||
|
@ -245,3 +245,21 @@ class BomItemTest(TestCase):
|
||||
)
|
||||
|
||||
self.assertEqual(assembly.can_build, 20)
|
||||
|
||||
def test_metadata(self):
|
||||
"""Unit tests for the metadata field."""
|
||||
for model in [BomItem]:
|
||||
p = model.objects.first()
|
||||
self.assertIsNone(p.metadata)
|
||||
|
||||
self.assertIsNone(p.get_metadata('test'))
|
||||
self.assertEqual(p.get_metadata('test', backup_value=123), 123)
|
||||
|
||||
# Test update via the set_metadata() method
|
||||
p.set_metadata('test', 3)
|
||||
self.assertEqual(p.get_metadata('test'), 3)
|
||||
|
||||
for k in ['apple', 'banana', 'carrot', 'carrot', 'banana']:
|
||||
p.set_metadata(k, k)
|
||||
|
||||
self.assertEqual(len(p.metadata.keys()), 4)
|
||||
|
@ -43,6 +43,24 @@ class TestParams(TestCase):
|
||||
t3.full_clean()
|
||||
t3.save() # pragma: no cover
|
||||
|
||||
def test_metadata(self):
|
||||
"""Unit tests for the metadata field."""
|
||||
for model in [PartParameterTemplate]:
|
||||
p = model.objects.first()
|
||||
self.assertIsNone(p.metadata)
|
||||
|
||||
self.assertIsNone(p.get_metadata('test'))
|
||||
self.assertEqual(p.get_metadata('test', backup_value=123), 123)
|
||||
|
||||
# Test update via the set_metadata() method
|
||||
p.set_metadata('test', 3)
|
||||
self.assertEqual(p.get_metadata('test'), 3)
|
||||
|
||||
for k in ['apple', 'banana', 'carrot', 'carrot', 'banana']:
|
||||
p.set_metadata(k, k)
|
||||
|
||||
self.assertEqual(len(p.metadata.keys()), 4)
|
||||
|
||||
|
||||
class TestCategoryTemplates(TransactionTestCase):
|
||||
"""Test class for PartCategoryParameterTemplate model"""
|
||||
|
@ -274,21 +274,22 @@ class PartTest(TestCase):
|
||||
self.assertEqual(float(self.r1.get_internal_price(10)), 0.5)
|
||||
|
||||
def test_metadata(self):
|
||||
"""Unit tests for the Part metadata field."""
|
||||
p = Part.objects.get(pk=1)
|
||||
self.assertIsNone(p.metadata)
|
||||
"""Unit tests for the metadata field."""
|
||||
for model in [Part]:
|
||||
p = model.objects.first()
|
||||
self.assertIsNone(p.metadata)
|
||||
|
||||
self.assertIsNone(p.get_metadata('test'))
|
||||
self.assertEqual(p.get_metadata('test', backup_value=123), 123)
|
||||
self.assertIsNone(p.get_metadata('test'))
|
||||
self.assertEqual(p.get_metadata('test', backup_value=123), 123)
|
||||
|
||||
# Test update via the set_metadata() method
|
||||
p.set_metadata('test', 3)
|
||||
self.assertEqual(p.get_metadata('test'), 3)
|
||||
# Test update via the set_metadata() method
|
||||
p.set_metadata('test', 3)
|
||||
self.assertEqual(p.get_metadata('test'), 3)
|
||||
|
||||
for k in ['apple', 'banana', 'carrot', 'carrot', 'banana']:
|
||||
p.set_metadata(k, k)
|
||||
for k in ['apple', 'banana', 'carrot', 'carrot', 'banana']:
|
||||
p.set_metadata(k, k)
|
||||
|
||||
self.assertEqual(len(p.metadata.keys()), 4)
|
||||
self.assertEqual(len(p.metadata.keys()), 4)
|
||||
|
||||
def test_related(self):
|
||||
"""Unit tests for the PartRelated model"""
|
||||
|
Reference in New Issue
Block a user