mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-01 19:20:55 +00:00
Report merge
This commit is contained in:
@ -17,7 +17,7 @@ repos:
|
||||
- id: check-yaml
|
||||
- id: mixed-line-ending
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.11.0
|
||||
rev: v0.11.6
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
args: [--preview]
|
||||
@ -28,7 +28,7 @@ repos:
|
||||
--preview
|
||||
]
|
||||
- repo: https://github.com/astral-sh/uv-pre-commit
|
||||
rev: 0.6.6
|
||||
rev: 0.6.14
|
||||
hooks:
|
||||
- id: pip-compile
|
||||
name: pip-compile requirements-dev.in
|
||||
@ -70,13 +70,13 @@ repos:
|
||||
src/frontend/vite.config.ts |
|
||||
)$
|
||||
- repo: https://github.com/biomejs/pre-commit
|
||||
rev: v1.9.4
|
||||
rev: v2.0.0-beta.1
|
||||
hooks:
|
||||
- id: biome-check
|
||||
additional_dependencies: ["@biomejs/biome@1.9.4"]
|
||||
files: ^src/frontend/.*\.(js|ts|tsx)$
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
rev: v8.24.0
|
||||
rev: v8.24.3
|
||||
hooks:
|
||||
- id: gitleaks
|
||||
language_version: 1.23.6
|
||||
|
@ -0,0 +1,22 @@
|
||||
# Generated by Django 4.2.20 on 2025-04-03 01:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("report", "0029_remove_reportoutput_template_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="reporttemplate",
|
||||
name="merge",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="Render a single report against selected items",
|
||||
verbose_name="Merge",
|
||||
),
|
||||
),
|
||||
]
|
@ -170,10 +170,12 @@ class ReportContextExtension(TypedDict):
|
||||
Attributes:
|
||||
page_size: The page size of the report
|
||||
landscape: Boolean value, True if the report is in landscape mode
|
||||
merge: Boolean value, True if the a single report is generated against multiple items
|
||||
"""
|
||||
|
||||
page_size: str
|
||||
landscape: bool
|
||||
merge: bool
|
||||
|
||||
|
||||
class ReportTemplateBase(MetadataMixin, InvenTree.models.InvenTreeModel):
|
||||
@ -366,6 +368,12 @@ class ReportTemplate(TemplateUploadMixin, ReportTemplateBase):
|
||||
help_text=_('Render report in landscape orientation'),
|
||||
)
|
||||
|
||||
merge = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=_('Merge'),
|
||||
help_text=_('Render a single report against selected items'),
|
||||
)
|
||||
|
||||
def get_report_size(self) -> str:
|
||||
"""Return the printable page size for this report."""
|
||||
try:
|
||||
@ -382,14 +390,21 @@ class ReportTemplate(TemplateUploadMixin, ReportTemplateBase):
|
||||
|
||||
return page_size
|
||||
|
||||
def get_context(self, instance, request=None, **kwargs):
|
||||
"""Supply context data to the report template for rendering."""
|
||||
base_context = super().get_context(instance, request)
|
||||
def get_report_context(self):
|
||||
"""Return report template context."""
|
||||
report_context: ReportContextExtension = {
|
||||
'page_size': self.get_report_size(),
|
||||
'landscape': self.landscape,
|
||||
'merge': self.merge,
|
||||
}
|
||||
|
||||
return report_context
|
||||
|
||||
def get_context(self, instance, request=None, **kwargs):
|
||||
"""Supply context data to the report template for rendering."""
|
||||
base_context = super().get_context(instance, request)
|
||||
report_context: ReportContextExtension = self.get_report_context()
|
||||
|
||||
context = {**base_context, **report_context}
|
||||
|
||||
# Pass the context through to the plugin registry for any additional information
|
||||
@ -455,21 +470,23 @@ class ReportTemplate(TemplateUploadMixin, ReportTemplateBase):
|
||||
output.save()
|
||||
|
||||
try:
|
||||
for instance in items:
|
||||
context = self.get_context(instance, request)
|
||||
if self.merge:
|
||||
base_context = super().base_context(request)
|
||||
report_context = self.get_report_context()
|
||||
item_contexts = []
|
||||
for instance in items:
|
||||
item_contexts.append(instance.report_context())
|
||||
contexts = {
|
||||
**base_context,
|
||||
**report_context,
|
||||
'instances': item_contexts,
|
||||
}
|
||||
|
||||
if report_name is None:
|
||||
report_name = self.generate_filename(context)
|
||||
report_name = self.generate_filename(contexts)
|
||||
|
||||
# Render the report output
|
||||
try:
|
||||
if debug_mode:
|
||||
report = self.render_as_string(instance, request)
|
||||
else:
|
||||
report = self.render(instance, request)
|
||||
except TemplateDoesNotExist as e:
|
||||
t_name = str(e) or self.template
|
||||
raise ValidationError(f'Template file {t_name} does not exist')
|
||||
html = render_to_string(self.template_name, contexts, request)
|
||||
report = HTML(string=html).write_pdf()
|
||||
|
||||
outputs.append(report)
|
||||
|
||||
@ -495,6 +512,47 @@ class ReportTemplate(TemplateUploadMixin, ReportTemplateBase):
|
||||
# Update the progress of the report generation
|
||||
output.progress += 1
|
||||
output.save()
|
||||
else:
|
||||
for instance in items:
|
||||
context = self.get_context(instance, request)
|
||||
|
||||
if report_name is None:
|
||||
report_name = self.generate_filename(context)
|
||||
|
||||
# Render the report output
|
||||
try:
|
||||
if debug_mode:
|
||||
report = self.render_as_string(instance, request)
|
||||
else:
|
||||
report = self.render(instance, request)
|
||||
except TemplateDoesNotExist as e:
|
||||
t_name = str(e) or self.template
|
||||
raise ValidationError(f'Template file {t_name} does not exist')
|
||||
|
||||
outputs.append(report)
|
||||
|
||||
# Attach the generated report to the model instance (if required)
|
||||
if self.attach_to_model and not debug_mode:
|
||||
instance.create_attachment(
|
||||
attachment=ContentFile(report, report_name),
|
||||
comment=_(f'Report generated from template {self.name}'),
|
||||
upload_user=request.user
|
||||
if request and request.user.is_authenticated
|
||||
else None,
|
||||
)
|
||||
|
||||
# Provide generated report to any interested plugins
|
||||
for plugin in report_plugins:
|
||||
try:
|
||||
plugin.report_callback(self, instance, report, request)
|
||||
except Exception:
|
||||
InvenTree.exceptions.log_error(
|
||||
f'plugins.{plugin.slug}.report_callback'
|
||||
)
|
||||
|
||||
# Update the progress of the report generation
|
||||
output.progress += 1
|
||||
output.save()
|
||||
|
||||
except Exception as exc:
|
||||
# Something went wrong during the report generation process
|
||||
|
@ -65,7 +65,12 @@ class ReportTemplateSerializer(ReportSerializerBase):
|
||||
"""Metaclass options."""
|
||||
|
||||
model = report.models.ReportTemplate
|
||||
fields = [*ReportSerializerBase.base_fields(), 'page_size', 'landscape']
|
||||
fields = [
|
||||
*ReportSerializerBase.base_fields(),
|
||||
'page_size',
|
||||
'landscape',
|
||||
'merge',
|
||||
]
|
||||
|
||||
page_size = serializers.ChoiceField(
|
||||
required=False,
|
||||
|
@ -21,6 +21,12 @@ function ReportTemplateTable() {
|
||||
<YesNoButton value={instance.landscape} />
|
||||
)
|
||||
},
|
||||
merge: {
|
||||
label: t`Merge`,
|
||||
modelRenderer: (instance: any) => (
|
||||
<YesNoButton value={instance.merge} />
|
||||
)
|
||||
},
|
||||
attach_to_model: {
|
||||
label: t`Attach to Model`,
|
||||
modelRenderer: (instance: any) => (
|
||||
|
Reference in New Issue
Block a user