From db468afaa5cc35acdc526dc868a9188c6a6a3961 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 30 Nov 2025 02:37:38 +0000 Subject: [PATCH] Refactor part parameter exporter plugin - Any model which supports parameters can use this now - Update documentation --- docs/docs/plugins/builtin/index.md | 2 +- .../plugins/builtin/parameter_exporter.md | 27 ++++ .../builtin/part_parameter_exporter.md | 25 ---- docs/docs/report/helpers.md | 8 +- docs/mkdocs.yml | 2 +- .../builtin/exporter/parameter_exporter.py | 98 +++++++++++++ .../exporter/part_parameter_exporter.py | 132 ------------------ 7 files changed, 131 insertions(+), 163 deletions(-) create mode 100644 docs/docs/plugins/builtin/parameter_exporter.md delete mode 100644 docs/docs/plugins/builtin/part_parameter_exporter.md create mode 100644 src/backend/InvenTree/plugin/builtin/exporter/parameter_exporter.py delete mode 100644 src/backend/InvenTree/plugin/builtin/exporter/part_parameter_exporter.py diff --git a/docs/docs/plugins/builtin/index.md b/docs/docs/plugins/builtin/index.md index 0d4d1db859..c229f02ca7 100644 --- a/docs/docs/plugins/builtin/index.md +++ b/docs/docs/plugins/builtin/index.md @@ -21,7 +21,7 @@ The following builtin plugins are available in InvenTree: | Barcodes | [TME](./barcode_tme.md) | TME barcode support | No | | Data Export | [BOM Exporter](./bom_exporter.md) | Custom [exporter](../mixins/export.md) for BOM data | Yes | | Data Export | [InvenTree Exporter](./inventree_exporter.md) | Custom [exporter](../mixins/export.md) for InvenTree data | Yes | -| Data Export | [Parameter Exporter](./part_parameter_exporter.md) | Custom [exporter](../mixins/export.md) for part parameter data | Yes | +| Data Export | [Parameter Exporter](./parameter_exporter.md) | Custom [exporter](../mixins/export.md) for parameter data | Yes | | Data Export | [Stocktake Exporter](./stocktake_exporter.md) | Custom [exporter](../mixins/export.md) for stocktake data | No | | Events | [Auto Create Child Builds](./auto_create_builds.md) | Automatically create child build orders for sub-assemblies | No | | Events | [Auto Issue Orders](./auto_issue.md) | Automatically issue pending orders when target date is reached | No | diff --git a/docs/docs/plugins/builtin/parameter_exporter.md b/docs/docs/plugins/builtin/parameter_exporter.md new file mode 100644 index 0000000000..7bc104d663 --- /dev/null +++ b/docs/docs/plugins/builtin/parameter_exporter.md @@ -0,0 +1,27 @@ +--- +title: Parameter Exporter +--- + +## Parameter Exporter + +The **Parameter Exporter** plugin provides custom export functionality for models which support custom [Parameter](../../concepts/parameters.md) data. + +It utilizes the [ExporterMixin](../mixins/export.md) mixin to provide a custom export format for part parameter data. + +In addition to the standard exported fields, this plugin also exports all associated parameter data for each row of the export. + +### Activation + +This plugin is a *mandatory* plugin, and is always enabled. + +### Plugin Settings + +This plugin has no configurable settings. + +## Usage + +This plugin is used in the same way as the [InvenTree Exporter Plugin](./inventree_exporter.md), but provides a custom export format for part parameter data. + +When exporting parameter data, the *Parameter Exporter* plugin is available for selection in the export dialog. When selected, the plugin provides some additional export options to control the data export process. + +{{ image("parameter_export_options.png", base="plugin/builtin", title="Parameter Export Options") }} diff --git a/docs/docs/plugins/builtin/part_parameter_exporter.md b/docs/docs/plugins/builtin/part_parameter_exporter.md deleted file mode 100644 index c516dbcd29..0000000000 --- a/docs/docs/plugins/builtin/part_parameter_exporter.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: Part Parameter Exporter ---- - -## Part Parameter Exporter - -The **Part Parameter Exporter** plugin provides custom export functionality for [Parameter](../../concepts/parameters.md) data. - -It utilizes the [ExporterMixin](../mixins/export.md) mixin to provide a custom export format for part parameter data. - -### Activation - -This plugin is a *mandatory* plugin, and is always enabled. - -### Plugin Settings - -This plugin has no configurable settings. - -## Usage - -This plugin is used in the same way as the [InvenTree Exporter Plugin](./inventree_exporter.md), but provides a custom export format for part parameter data. - -When exporting part parameter data, the *Part Parameter Exporter* plugin is available for selection in the export dialog. When selected, the plugin provides some additional export options to control the data export process. - -{{ image("parameter_export_options.png", base="plugin/builtin", title="Part Parameter Export Options") }} diff --git a/docs/docs/report/helpers.md b/docs/docs/report/helpers.md index 2caf969fe4..79fde22b2f 100644 --- a/docs/docs/report/helpers.md +++ b/docs/docs/report/helpers.md @@ -545,11 +545,11 @@ You can add asset images to the reports and labels by using the `{% raw %}{% ass {% endraw %} ``` -## Part Parameters +## Parameters -If you need to load a part parameter for a particular Part, within the context of your template, you can use the `part_parameter` template tag: +If you need to load a parameter value for a particular model instance, within the context of your template, you can use the `parameter` template tag: -::: report.templatetags.report.part_parameter +::: report.templatetags.report.parameter options: show_docstring_description: false show_source: False @@ -562,7 +562,7 @@ The following example assumes that you have a report or label which contains a v {% raw %} {% load report %} -{% part_parameter part "length" as length %} +{% parameter part "length" as length %} Part: {{ part.name }}
Length: {{ length.data }} [{{ length.units }}] diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 64252fc042..9134d6ad04 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -239,7 +239,7 @@ nav: - Export Plugins: - BOM Exporter: plugins/builtin/bom_exporter.md - InvenTree Exporter: plugins/builtin/inventree_exporter.md - - Parameter Exporter: plugins/builtin/part_parameter_exporter.md + - Parameter Exporter: plugins/builtin/parameter_exporter.md - Stocktake Exporter: plugins/builtin/stocktake_exporter.md - Label Printing: - Label Printer: plugins/builtin/inventree_label.md diff --git a/src/backend/InvenTree/plugin/builtin/exporter/parameter_exporter.py b/src/backend/InvenTree/plugin/builtin/exporter/parameter_exporter.py new file mode 100644 index 0000000000..0c890d8a8b --- /dev/null +++ b/src/backend/InvenTree/plugin/builtin/exporter/parameter_exporter.py @@ -0,0 +1,98 @@ +"""Custom exporter for Parameter data.""" + +from django.utils.translation import gettext_lazy as _ + +from rest_framework import serializers + +from plugin import InvenTreePlugin +from plugin.mixins import DataExportMixin + + +class ParameterExportOptionsSerializer(serializers.Serializer): + """Custom export options for the ParameterExporter plugin.""" + + export_exclude_inactive_parameters = serializers.BooleanField( + default=True, + label=_('Exclude Inactive'), + help_text=_('Exclude parameters which are inactive'), + ) + + +class ParameterExporter(DataExportMixin, InvenTreePlugin): + """Builtin plugin for exporting Parameter data. + + Extends the export process, to include all associated Parameter data. + """ + + NAME = 'Parameter Exporter' + SLUG = 'parameter-exporter' + TITLE = _('Parameter Exporter') + DESCRIPTION = _('Exporter for model parameter data') + VERSION = '2.0.0' + AUTHOR = _('InvenTree contributors') + + ExportOptionsSerializer = ParameterExportOptionsSerializer + + def supports_export( + self, + model_class: type, + user=None, + serializer_class=None, + view_class=None, + *args, + **kwargs, + ) -> bool: + """Supported if the base model implements the InvenTreeParameterMixin.""" + from InvenTree.models import InvenTreeParameterMixin + + return issubclass(model_class, InvenTreeParameterMixin) + + def update_headers(self, headers, context, **kwargs): + """Update headers for the export.""" + # Add in a header for each parameter + for pk, name in self.parameters.items(): + headers[f'parameter_{pk}'] = str(name) + + return headers + + def prefetch_queryset(self, queryset): + """Ensure that the associated parameters are prefetched.""" + from InvenTree.models import InvenTreeParameterMixin + + queryset = InvenTreeParameterMixin.annotate_parameters(queryset) + return queryset + + def export_data( + self, queryset, serializer_class, headers, context, output, **kwargs + ): + """Export parameter data.""" + # Extract custom serializer options and cache + queryset = self.prefetch_queryset(queryset) + self.serializer_class = serializer_class + + self.exclude_inactive = context.get('export_exclude_inactive_parameters', True) + + # Keep a dict of observed parameters against their primary key + self.parameters = {} + + # Serialize the queryset using DRF first + rows = self.serializer_class( + queryset, parameters=True, exporting=True, many=True + ).data + + for row in rows: + # Extract the associated parameters from the serialized data + for parameter in row.get('parameters', []): + template_detail = parameter['template_detail'] + template_id = template_detail['pk'] + + active = template_detail.get('enabled', True) + + if not active and self.exclude_inactive: + continue + + self.parameters[template_id] = template_detail['name'] + + row[f'parameter_{template_id}'] = parameter['data'] + + return rows diff --git a/src/backend/InvenTree/plugin/builtin/exporter/part_parameter_exporter.py b/src/backend/InvenTree/plugin/builtin/exporter/part_parameter_exporter.py deleted file mode 100644 index 7872c2e59d..0000000000 --- a/src/backend/InvenTree/plugin/builtin/exporter/part_parameter_exporter.py +++ /dev/null @@ -1,132 +0,0 @@ -"""Custom exporter for PartParameters.""" - -from django.utils.translation import gettext_lazy as _ - -from rest_framework import serializers - -from part.models import Part -from part.serializers import PartSerializer -from plugin import InvenTreePlugin -from plugin.mixins import DataExportMixin - - -class PartParameterExportOptionsSerializer(serializers.Serializer): - """Custom export options for the PartParameterExporter plugin.""" - - export_stock_data = serializers.BooleanField( - default=True, label=_('Stock Data'), help_text=_('Include part stock data') - ) - - export_pricing_data = serializers.BooleanField( - default=True, label=_('Pricing Data'), help_text=_('Include part pricing data') - ) - - -class PartParameterExporter(DataExportMixin, InvenTreePlugin): - """Builtin plugin for exporting Part Parameter data. - - Extends the "part" export process, to include all associated PartParameter data. - """ - - NAME = 'Part Parameter Exporter' - SLUG = 'parameter-exporter' - TITLE = _('Part Parameter Exporter') - DESCRIPTION = _('Exporter for part parameter data') - VERSION = '1.0.0' - AUTHOR = _('InvenTree contributors') - - ExportOptionsSerializer = PartParameterExportOptionsSerializer - - def supports_export( - self, - model_class: type, - user=None, - serializer_class=None, - view_class=None, - *args, - **kwargs, - ) -> bool: - """Supported if the base model is Part.""" - return model_class == Part and serializer_class == PartSerializer - - def update_headers(self, headers, context, **kwargs): - """Update headers for the export.""" - if not self.export_stock_data: - # Remove stock data from the headers - for field in [ - 'allocated_to_build_orders', - 'allocated_to_sales_orders', - 'available_stock', - 'available_substitute_stock', - 'available_variant_stock', - 'building', - 'can_build', - 'external_stock', - 'in_stock', - 'on_order', - 'ordering', - 'required_for_build_orders', - 'required_for_sales_orders', - 'stock_item_count', - 'total_in_stock', - 'unallocated_stock', - 'variant_stock', - ]: - headers.pop(field, None) - - if not self.export_pricing_data: - # Remove pricing data from the headers - for field in [ - 'pricing_min', - 'pricing_max', - 'pricing_min_total', - 'pricing_max_total', - 'pricing_updated', - ]: - headers.pop(field, None) - - # Add in a header for each part parameter - for pk, name in self.parameters.items(): - headers[f'parameter_{pk}'] = str(name) - - return headers - - def prefetch_queryset(self, queryset): - """Ensure that the part parameters are prefetched.""" - queryset = queryset.prefetch_related( - 'parameters_list', 'parameters_list__template' - ) - - return queryset - - def export_data( - self, queryset, serializer_class, headers, context, output, **kwargs - ): - """Export part and parameter data.""" - # Extract custom serializer options and cache - self.export_stock_data = context.get('export_stock_data', True) - self.export_pricing_data = context.get('export_pricing_data', True) - - queryset = self.prefetch_queryset(queryset) - self.serializer_class = serializer_class - - # Keep a dict of observed part parameters against their primary key - self.parameters = {} - - # Serialize the queryset using DRF first - parts = self.serializer_class( - queryset, parameters=True, exporting=True, many=True - ).data - - for part in parts: - # Extract the part parameters from the serialized data - for parameter in part.get('parameters_list', []): - if template := parameter.get('template_detail', None): - template_id = template['pk'] - - if template_id not in self.parameters: - self.parameters[template_id] = template['name'] - - part[f'parameter_{template_id}'] = parameter['data'] - - return parts