2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-07-08 16:11:23 +00:00

Report Generation Updates (#12187)

* Fix for TemplateEditor

- Allow dragging of split section

* Cleaner report template code

* Pass correct error message through

* Prevent multiple retries if running in worker thread

* Handle report merge error

* Add playwright tests for broken report printing

* Reduce scope for exception messages

* Reduce comment deltas

* Adjust unit test

* Raise ValidaitonError

* Handle message parsing

* Additional comment

* Fix unit tests
This commit is contained in:
Oliver
2026-06-18 13:41:44 +10:00
committed by GitHub
parent 4b29032c6e
commit fc15f30f8f
7 changed files with 96 additions and 32 deletions
+1 -1
View File
@@ -568,7 +568,7 @@ class InvenTreeParameterMixin(InvenTreePermissionCheckMixin, models.Model):
if 'parameters_list' in cache:
return cache['parameters_list']
return self.parameters_list.all()
return self.parameters_list.all().prefetch_related('template')
def delete(self, *args, **kwargs):
"""Handle the deletion of a model instance.
+18 -4
View File
@@ -25,6 +25,7 @@ from pypdf import PdfWriter
import InvenTree.exceptions
import InvenTree.helpers
import InvenTree.models
import InvenTree.ready
import report.helpers
import report.validators
from common.models import DataOutput, RenderChoices, UpdatedUserMixin
@@ -578,10 +579,10 @@ class ReportTemplate(TemplateUploadMixin, ReportTemplateBase):
raise ValidationError(msg)
except TemplateSyntaxError as e:
msg = _('Template syntax error')
output.mark_failure(msg)
output.mark_failure(str(e) or msg)
raise ValidationError(f'{msg}: {e!s}')
except ValidationError as e:
output.mark_failure(str(e))
output.mark_failure(','.join(e.messages))
raise e
except Exception as e:
msg = _('Error rendering report')
@@ -617,10 +618,10 @@ class ReportTemplate(TemplateUploadMixin, ReportTemplateBase):
raise ValidationError(msg)
except TemplateSyntaxError as e:
msg = _('Template syntax error')
output.mark_failure(error=_('Template syntax error'))
output.mark_failure(error=str(e) or msg)
raise ValidationError(f'{msg}: {e!s}')
except ValidationError as e:
output.mark_failure(str(e))
output.mark_failure(', '.join(e.messages))
raise e
except Exception as e:
msg = _('Error rendering report')
@@ -643,6 +644,13 @@ class ReportTemplate(TemplateUploadMixin, ReportTemplateBase):
# Something went wrong during the report generation process
log_report_error('ReportTemplate.print')
# If the error occurred in a worker thread, we do not want to raise an error,
# as this would cause the worker to retry the task indefinitely
if InvenTree.ready.isInWorkerThread():
return
# Raise a ValidationError with the error message
# This will be caught by the caller and displayed to the user
raise ValidationError({
'error': _('Error generating report'),
'detail': str(exc),
@@ -677,6 +685,12 @@ class ReportTemplate(TemplateUploadMixin, ReportTemplateBase):
log_report_error('ReportTemplate.print')
msg = _('Error merging report outputs')
output.mark_failure(error=msg)
# If the error occurred in a worker thread, we do not want to raise an error,
# as this would cause the worker to retry the task indefinitely
if InvenTree.ready.isInWorkerThread():
return
raise ValidationError(msg)
# Save the generated report to the database
@@ -461,7 +461,7 @@ def part_image(part: Part, preview: bool = False, thumbnail: bool = False, **kwa
TypeError: If provided part is not a Part instance
"""
if not part or not isinstance(part, Part):
raise TypeError(_('part_image tag requires a Part instance'))
raise ValidationError(_('part_image tag requires a Part instance'))
image_filename = InvenTree.helpers.image2name(part.image, preview, thumbnail)
@@ -487,28 +487,22 @@ def parameter(
Returns:
A Parameter object, or the provided default value if not found
"""
if instance is None:
raise ValueError('parameter tag requires a valid Model instance')
if instance is None or not isinstance(instance, Model):
raise ValidationError('parameter tag requires a valid Model instance')
if not isinstance(instance, Model) or not hasattr(instance, 'parameters'):
raise TypeError("parameter tag requires a Model with 'parameters' attribute")
if not hasattr(instance, 'parameters'):
raise ValidationError(
"parameter tag requires a Model with 'parameters' attribute"
)
parameters = instance.parameters_list.all().prefetch_related('template')
# First try with exact match
if (
parameter := instance.parameters
.prefetch_related('template')
.filter(template__name=parameter_name)
.first()
):
if parameter := parameters.filter(template__name=parameter_name).first():
return parameter
# Next, try with case-insensitive match
if (
parameter := instance.parameters
.prefetch_related('template')
.filter(template__name__iexact=parameter_name)
.first()
):
if parameter := parameters.filter(template__name__iexact=parameter_name).first():
return parameter
return None
+4 -4
View File
@@ -15,7 +15,7 @@ from PIL import Image
from common.models import InvenTreeSetting, Parameter, ParameterTemplate
from InvenTree.unit_test import InvenTreeTestCase
from part.models import Part # TODO fix import: PartParameter, PartParameterTemplate
from part.models import Part
from part.test_api import PartImageTestMixin
from report.templatetags import barcode as barcode_tags
from report.templatetags import report as report_tags
@@ -184,7 +184,7 @@ class ReportTagTest(PartImageTestMixin, InvenTreeTestCase):
def test_part_image(self):
"""Unit tests for the 'part_image' tag."""
with self.assertRaises(TypeError):
with self.assertRaises(ValidationError):
report_tags.part_image(None)
obj = Part.objects.create(name='test', description='test')
@@ -502,11 +502,11 @@ class ReportTagTest(PartImageTestMixin, InvenTreeTestCase):
self.assertEqual(report_tags.parameter(part, 'Template 1'), parameter)
# Test with a null part
with self.assertRaises(ValueError):
with self.assertRaises(ValidationError):
report_tags.parameter(None, 'name')
# Test with an invalid model type
with self.assertRaises(TypeError):
with self.assertRaises(ValidationError):
report_tags.parameter(parameter, 'name')
def test_render_currency(self):