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:
@ -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'),
|
||||
])),
|
||||
|
||||
|
38
InvenTree/report/migrations/0017_auto_20230317_0816.py
Normal file
38
InvenTree/report/migrations/0017_auto_20230317_0816.py
Normal 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'),
|
||||
),
|
||||
]
|
@ -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
|
||||
|
@ -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()
|
||||
|
Reference in New Issue
Block a user