2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-03 04:00:57 +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:
miggland
2023-03-23 11:51:08 +01:00
committed by GitHub
parent 4e72ac303f
commit 807784d9e4
32 changed files with 642 additions and 40 deletions

View File

@ -18,7 +18,9 @@ import common.models
import InvenTree.helpers
import order.models
import part.models
from InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateDestroyAPI
from InvenTree.mixins import (ListAPI, RetrieveAPI, RetrieveUpdateAPI,
RetrieveUpdateDestroyAPI)
from plugin.serializers import MetadataSerializer
from stock.models import StockItem, StockItemAttachment
from .models import (BillOfMaterialsReport, BuildReport, PurchaseOrderReport,
@ -421,6 +423,31 @@ class SalesOrderReportPrint(SalesOrderReportMixin, ReportPrintMixin, RetrieveAPI
pass
class ReportMetadata(RetrieveUpdateAPI):
"""API endpoint for viewing / updating Report metadata."""
MODEL_REF = 'reportmodel'
def _get_model(self, *args, **kwargs):
"""Return model depending on which report type is requested in get_view constructor."""
reportmodel = self.kwargs.get(self.MODEL_REF, PurchaseOrderReport)
if reportmodel not in [PurchaseOrderReport, SalesOrderReport, BuildReport, BillOfMaterialsReport, TestReport]:
raise ValidationError("Invalid report model")
return reportmodel
# Return corresponding Serializer
def get_serializer(self, *args, **kwargs):
"""Return correct MetadataSerializer instance depending on which model is requested"""
# Get type of report, make sure its one of the allowed values
UseModel = self._get_model(*args, **kwargs)
return MetadataSerializer(UseModel, *args, **kwargs)
def get_queryset(self, *args, **kwargs):
"""Return correct queryset depending on which model is requested"""
UseModel = self._get_model(*args, **kwargs)
return UseModel.objects.all()
report_api_urls = [
# Purchase order reports
@ -428,6 +455,7 @@ report_api_urls = [
# Detail views
re_path(r'^(?P<pk>\d+)/', include([
re_path(r'print/', PurchaseOrderReportPrint.as_view(), name='api-po-report-print'),
re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: PurchaseOrderReport}, name='api-po-report-metadata'),
path('', PurchaseOrderReportDetail.as_view(), name='api-po-report-detail'),
])),
@ -440,6 +468,7 @@ report_api_urls = [
# Detail views
re_path(r'^(?P<pk>\d+)/', include([
re_path(r'print/', SalesOrderReportPrint.as_view(), name='api-so-report-print'),
re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: SalesOrderReport}, name='api-so-report-metadata'),
path('', SalesOrderReportDetail.as_view(), name='api-so-report-detail'),
])),
@ -451,6 +480,7 @@ report_api_urls = [
# Detail views
re_path(r'^(?P<pk>\d+)/', include([
re_path(r'print/?', BuildReportPrint.as_view(), name='api-build-report-print'),
re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: BuildReport}, name='api-build-report-metadata'),
re_path(r'^.$', BuildReportDetail.as_view(), name='api-build-report-detail'),
])),
@ -464,6 +494,7 @@ report_api_urls = [
# Detail views
re_path(r'^(?P<pk>\d+)/', include([
re_path(r'print/?', BOMReportPrint.as_view(), name='api-bom-report-print'),
re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: BillOfMaterialsReport}, name='api-bom-report-metadata'),
re_path(r'^.*$', BOMReportDetail.as_view(), name='api-bom-report-detail'),
])),
@ -476,6 +507,7 @@ report_api_urls = [
# Detail views
re_path(r'^(?P<pk>\d+)/', include([
re_path(r'print/?', StockItemTestReportPrint.as_view(), name='api-stockitem-testreport-print'),
re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: TestReport}, name='api-stockitem-testreport-metadata'),
re_path(r'^.*$', StockItemTestReportDetail.as_view(), name='api-stockitem-testreport-detail'),
])),

View File

@ -0,0 +1,38 @@
# Generated by Django 3.2.18 on 2023-03-17 08:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('report', '0016_auto_20210513_1303'),
]
operations = [
migrations.AddField(
model_name='billofmaterialsreport',
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='buildreport',
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='purchaseorderreport',
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='salesorderreport',
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='testreport',
name='metadata',
field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'),
),
]

View File

@ -21,6 +21,7 @@ import order.models
import part.models
import stock.models
from InvenTree.helpers import validateFilterString
from plugin.models import MetadataMixin
try:
from django_weasyprint import WeasyTemplateResponseMixin
@ -174,7 +175,7 @@ class ReportBase(models.Model):
)
class ReportTemplateBase(ReportBase):
class ReportTemplateBase(MetadataMixin, ReportBase):
"""Reporting template model.
Able to be passed context data

View File

@ -258,7 +258,6 @@ class ReportTest(InvenTreeAPITestCase):
reports = self.model.objects.all()
n = len(reports)
# API endpoint must return correct number of reports
self.assertEqual(len(response.data), n)
@ -281,6 +280,25 @@ class ReportTest(InvenTreeAPITestCase):
response = self.get(url, {'enabled': False})
self.assertEqual(len(response.data), n)
def test_metadata(self):
"""Unit tests for the metadata field."""
if self.model is not None:
p = self.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 TestReportTest(ReportTest):
"""Unit testing class for the stock item TestReport model"""
@ -393,6 +411,12 @@ class BOMReportTest(ReportTest):
detail_url = 'api-bom-report-detail'
print_url = 'api-bom-report-print'
def setUp(self):
"""Setup function for the bill of materials Report"""
self.copyReportTemplate('inventree_bill_of_materials_report.html', 'bill of materials report')
return super().setUp()
class PurchaseOrderReportTest(ReportTest):
"""Unit test class fort he PurchaseOrderReport model"""
@ -402,6 +426,12 @@ class PurchaseOrderReportTest(ReportTest):
detail_url = 'api-po-report-detail'
print_url = 'api-po-report-print'
def setUp(self):
"""Setup function for the purchase order Report"""
self.copyReportTemplate('inventree_po_report.html', 'purchase order report')
return super().setUp()
class SalesOrderReportTest(ReportTest):
"""Unit test class for the SalesOrderReport model"""
@ -410,3 +440,9 @@ class SalesOrderReportTest(ReportTest):
list_url = 'api-so-report-list'
detail_url = 'api-so-report-detail'
print_url = 'api-so-report-print'
def setUp(self):
"""Setup function for the sales order Report"""
self.copyReportTemplate('inventree_so_report.html', 'sales order report')
return super().setUp()