2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-11 23:44:14 +00:00

Merge pull request from SchrodingersGat/default-page-size

Default page size
This commit is contained in:
Oliver
2021-02-06 18:42:38 +11:00
committed by GitHub
7 changed files with 214 additions and 145 deletions

@ -174,6 +174,24 @@ class InvenTreeSetting(models.Model):
'validator': bool, 'validator': bool,
}, },
'REPORT_DEBUG_MODE': {
'name': _('Debug Mode'),
'description': _('Generate reports in debug mode (HTML output)'),
'default': False,
'validator': bool,
},
'REPORT_DEFAULT_PAGE_SIZE': {
'name': _('Page Size'),
'description': _('Default page size for PDF reports'),
'default': 'A4',
'choices': [
('A4', 'A4'),
('Legal', 'Legal'),
('Letter', 'Letter')
],
},
'REPORT_ENABLE_TEST_REPORT': { 'REPORT_ENABLE_TEST_REPORT': {
'name': _('Test Reports'), 'name': _('Test Reports'),
'description': _('Enable generation of test reports'), 'description': _('Enable generation of test reports'),

@ -3,12 +3,14 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.conf.urls import url, include from django.conf.urls import url, include
from django.http import HttpResponse
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics, filters from rest_framework import generics, filters
from rest_framework.response import Response from rest_framework.response import Response
import common.models
import InvenTree.helpers import InvenTree.helpers
from stock.models import StockItem from stock.models import StockItem
@ -165,31 +167,53 @@ class StockItemTestReportPrint(generics.RetrieveAPIView, StockItemReportMixin):
outputs = [] outputs = []
# In debug mode, generate single HTML output, rather than PDF
debug_mode = common.models.InvenTreeSetting.get_setting('REPORT_DEBUG_MODE')
# Merge one or more PDF files into a single download # Merge one or more PDF files into a single download
for item in items: for item in items:
report = self.get_object() report = self.get_object()
report.stock_item = item report.stock_item = item
outputs.append(report.render(request)) if debug_mode:
outputs.append(report.render_to_string(request))
else:
outputs.append(report.render(request))
pages = [] if debug_mode:
"""
Contatenate all rendered templates into a single HTML string,
and return the string as a HTML response.
"""
if len(outputs) > 1: html = "\n".join(outputs)
# If more than one output is generated, merge them into a single file
for output in outputs: return HttpResponse(html)
doc = output.get_document()
for page in doc.pages:
pages.append(page)
pdf = outputs[0].get_document().copy(pages).write_pdf()
else: else:
pdf = outputs[0].get_document().write_pdf() """
Concatenate all rendered pages into a single PDF object,
and return the resulting document!
"""
pages = []
if len(outputs) > 1:
# If more than one output is generated, merge them into a single file
for output in outputs:
doc = output.get_document()
for page in doc.pages:
pages.append(page)
pdf = outputs[0].get_document().copy(pages).write_pdf()
else:
pdf = outputs[0].get_document().write_pdf()
return InvenTree.helpers.DownloadFile( return InvenTree.helpers.DownloadFile(
pdf, pdf,
'test_report.pdf', 'test_report.pdf',
content_type='application/pdf' content_type='application/pdf'
) )
report_api_urls = [ report_api_urls = [

@ -14,10 +14,13 @@ import datetime
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
from django.template.loader import render_to_string
from django.core.files.storage import FileSystemStorage from django.core.files.storage import FileSystemStorage
from django.core.validators import FileExtensionValidator from django.core.validators import FileExtensionValidator
import stock.models import stock.models
import common.models
from InvenTree.helpers import validateFilterString from InvenTree.helpers import validateFilterString
@ -174,27 +177,42 @@ class ReportTemplateBase(ReportBase):
return {} return {}
def render(self, request, **kwargs): def context(self, request):
""" """
Render the template to a PDF file. All context to be passed to the renderer.
Uses django-weasyprint plugin to render HTML template against Weasyprint
""" """
# TODO: Support custom filename generation!
# filename = kwargs.get('filename', 'report.pdf')
context = self.get_context_data(request) context = self.get_context_data(request)
context['media'] = settings.MEDIA_ROOT context['date'] = datetime.datetime.now().date()
context['datetime'] = datetime.datetime.now()
context['report_name'] = self.name context['default_page_size'] = common.models.InvenTreeSetting.get_setting('REPORT_DEFAULT_PAGE_SIZE')
context['report_description'] = self.description context['report_description'] = self.description
context['report_name'] = self.name
context['report_revision'] = self.revision context['report_revision'] = self.revision
context['request'] = request context['request'] = request
context['user'] = request.user context['user'] = request.user
context['date'] = datetime.datetime.now().date()
context['datetime'] = datetime.datetime.now() return context
def render_to_string(self, request, **kwargs):
"""
Render the report to a HTML stiring.
Useful for debug mode (viewing generated code)
"""
return render_to_string(self.template_name, self.context(request), request)
def render(self, request, **kwargs):
"""
Render the template to a PDF file.
Uses django-weasyprint plugin to render HTML template against Weasyprint
"""
# TODO: Support custom filename generation!
# filename = kwargs.get('filename', 'report.pdf')
# Render HTML template to PDF # Render HTML template to PDF
wp = WeasyprintReportMixin( wp = WeasyprintReportMixin(
@ -205,7 +223,7 @@ class ReportTemplateBase(ReportBase):
**kwargs) **kwargs)
return wp.render_to_response( return wp.render_to_response(
context, self.context(request),
**kwargs) **kwargs)
enabled = models.BooleanField( enabled = models.BooleanField(

@ -3,14 +3,12 @@
<head> <head>
<style> <style>
@page { @page {
{% block page_size %} {% block page_style %}
size: A4; size: {% block page_size %}{{ default_page_size }}{% endblock %};
{% endblock %} margin: {% block page_margin %}2cm{% endblock %};
{% block page_margin %}
margin: 2cm;
{% endblock %}
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
font-size: 75%; font-size: 75%;
{% endblock %}
@top-left { @top-left {
{% block top_left %} {% block top_left %}
@ -45,7 +43,9 @@
} }
body { body {
{% block body_style %}
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
{% endblock %}
} }
.header { .header {

@ -1,112 +1,3 @@
{% extends "report/inventree_report_base.html" %} {% extends "report/inventree_test_report_base.html" %}
{% load i18n %} <!-- Refer to the source code for inventree_test_report.html -->
{% load report %}
{% load inventree_extras %}
{% block style %}
.test-table {
width: 100%;
}
{% block bottom_left %}
content: "{{ date.isoformat }}";
{% endblock %}
{% block bottom_center %}
content: "InvenTree v{% inventree_version %}";
{% endblock %}
{% block top_center %}
content: "{% trans 'Stock Item Test Report' %}";
{% endblock %}
.test-row {
padding: 3px;
}
.test-pass {
color: #5f5;
}
.test-fail {
color: #F55;
}
.container {
padding: 5px;
border: 1px solid;
}
.text-left {
display: inline-block;
width: 50%;
}
.img-right {
display: inline;
align-content: right;
align-items: right;
width: 50%;
}
{% endblock %}
{% block page_content %}
<div class='container'>
<div class='text-left'>
<h2>
{{ part.full_name }}
</h2>
<p>{{ part.description }}</p>
<p><i>{{ stock_item.location }}</i></p>
<p><i>Stock Item ID: {{ stock_item.pk }}</i></p>
</div>
<div class='img-right'>
<img src="{% part_image part %}">
<hr>
<h4>
{% if stock_item.is_serialized %}
{% trans "Serial Number" %}: {{ stock_item.serial }}
{% else %}
{% trans "Quantity" %}: {% decimal stock_item.quantity %}
{% endif %}
</h4>
</div>
</div>
<h3>{% trans "Test Results" %}</h3>
<table class='table test-table'>
<thead>
<tr>
<th>{% trans "Test" %}</th>
<th>{% trans "Result" %}</th>
<th>{% trans "Value" %}</th>
<th>{% trans "User" %}</th>
<th>{% trans "Date" %}</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan='5'><hr></td>
</tr>
{% for test in result_list %}
<tr class='test-row'>
<td>{{ test.test }}</td>
{% if test.result %}
<td class='test-pass'>{% trans "Pass" %}</td>
{% else %}
<td class='test-fail'>{% trans "Fail" %}</td>
{% endif %}
<td>{{ test.value }}</td>
<td>{{ test.user.username }}</td>
<td>{{ test.date.date.isoformat }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

@ -0,0 +1,116 @@
{% extends "report/inventree_report_base.html" %}
{% load i18n %}
{% load report %}
{% load inventree_extras %}
{% block style %}
.test-table {
width: 100%;
}
{% block bottom_left %}
content: "{{ date.isoformat }}";
{% endblock %}
{% block bottom_center %}
content: "InvenTree v{% inventree_version %}";
{% endblock %}
{% block top_center %}
content: "{% trans 'Stock Item Test Report' %}";
{% endblock %}
.test-row {
padding: 3px;
}
.test-pass {
color: #5f5;
}
.test-fail {
color: #F55;
}
.container {
padding: 5px;
border: 1px solid;
}
.text-left {
display: inline-block;
width: 50%;
}
.img-right {
display: inline;
align-content: right;
align-items: right;
width: 50%;
}
.part-img {
height: 4cm;
}
{% endblock %}
{% block page_content %}
<div class='container'>
<div class='text-left'>
<h2>
{{ part.full_name }}
</h2>
<p>{{ part.description }}</p>
<p><i>{{ stock_item.location }}</i></p>
<p><i>Stock Item ID: {{ stock_item.pk }}</i></p>
</div>
<div class='img-right'>
<img class='part-img' src="{% part_image part %}">
<hr>
<h4>
{% if stock_item.is_serialized %}
{% trans "Serial Number" %}: {{ stock_item.serial }}
{% else %}
{% trans "Quantity" %}: {% decimal stock_item.quantity %}
{% endif %}
</h4>
</div>
</div>
<h3>{% trans "Test Results" %}</h3>
<table class='table test-table'>
<thead>
<tr>
<th>{% trans "Test" %}</th>
<th>{% trans "Result" %}</th>
<th>{% trans "Value" %}</th>
<th>{% trans "User" %}</th>
<th>{% trans "Date" %}</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan='5'><hr></td>
</tr>
{% for test in result_list %}
<tr class='test-row'>
<td>{{ test.test }}</td>
{% if test.result %}
<td class='test-pass'>{% trans "Pass" %}</td>
{% else %}
<td class='test-fail'>{% trans "Fail" %}</td>
{% endif %}
<td>{{ test.value }}</td>
<td>{{ test.user.username }}</td>
<td>{{ test.date.date.isoformat }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

@ -15,6 +15,8 @@
<table class='table table-striped table-condensed'> <table class='table table-striped table-condensed'>
{% include "InvenTree/settings/header.html" %} {% include "InvenTree/settings/header.html" %}
<tbody> <tbody>
{% include "InvenTree/settings/setting.html" with key="REPORT_DEFAULT_PAGE_SIZE" %}
{% include "InvenTree/settings/setting.html" with key="REPORT_DEBUG_MODE" %}
{% include "InvenTree/settings/setting.html" with key="REPORT_ENABLE_TEST_REPORT" %} {% include "InvenTree/settings/setting.html" with key="REPORT_ENABLE_TEST_REPORT" %}
</tbody> </tbody>
</table> </table>