Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver Walters 2021-02-09 20:37:48 +11:00
commit fe6c4687d9
7 changed files with 214 additions and 145 deletions

View File

@ -174,6 +174,24 @@ class InvenTreeSetting(models.Model):
'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': {
'name': _('Test Reports'),
'description': _('Enable generation of test reports'),

View File

@ -3,12 +3,14 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext as _
from django.conf.urls import url, include
from django.http import HttpResponse
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics, filters
from rest_framework.response import Response
import common.models
import InvenTree.helpers
from stock.models import StockItem
@ -165,31 +167,53 @@ class StockItemTestReportPrint(generics.RetrieveAPIView, StockItemReportMixin):
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
for item in items:
report = self.get_object()
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:
# 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)
html = "\n".join(outputs)
return HttpResponse(html)
pdf = outputs[0].get_document().copy(pages).write_pdf()
else:
pdf = outputs[0].get_document().write_pdf()
"""
Concatenate all rendered pages into a single PDF object,
and return the resulting document!
"""
return InvenTree.helpers.DownloadFile(
pdf,
'test_report.pdf',
content_type='application/pdf'
)
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(
pdf,
'test_report.pdf',
content_type='application/pdf'
)
report_api_urls = [

View File

@ -14,10 +14,13 @@ import datetime
from django.db import models
from django.conf import settings
from django.template.loader import render_to_string
from django.core.files.storage import FileSystemStorage
from django.core.validators import FileExtensionValidator
import stock.models
import common.models
from InvenTree.helpers import validateFilterString
@ -174,6 +177,33 @@ class ReportTemplateBase(ReportBase):
return {}
def context(self, request):
"""
All context to be passed to the renderer.
"""
context = self.get_context_data(request)
context['date'] = datetime.datetime.now().date()
context['datetime'] = datetime.datetime.now()
context['default_page_size'] = common.models.InvenTreeSetting.get_setting('REPORT_DEFAULT_PAGE_SIZE')
context['report_description'] = self.description
context['report_name'] = self.name
context['report_revision'] = self.revision
context['request'] = request
context['user'] = request.user
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.
@ -184,18 +214,6 @@ class ReportTemplateBase(ReportBase):
# TODO: Support custom filename generation!
# filename = kwargs.get('filename', 'report.pdf')
context = self.get_context_data(request)
context['media'] = settings.MEDIA_ROOT
context['report_name'] = self.name
context['report_description'] = self.description
context['report_revision'] = self.revision
context['request'] = request
context['user'] = request.user
context['date'] = datetime.datetime.now().date()
context['datetime'] = datetime.datetime.now()
# Render HTML template to PDF
wp = WeasyprintReportMixin(
request,
@ -205,7 +223,7 @@ class ReportTemplateBase(ReportBase):
**kwargs)
return wp.render_to_response(
context,
self.context(request),
**kwargs)
enabled = models.BooleanField(

View File

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

View File

@ -1,112 +1,3 @@
{% extends "report/inventree_report_base.html" %}
{% extends "report/inventree_test_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%;
}
{% 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 %}
<!-- Refer to the source code for inventree_test_report.html -->

View File

@ -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 %}

View File

@ -15,6 +15,8 @@
<table class='table table-striped table-condensed'>
{% include "InvenTree/settings/header.html" %}
<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" %}
</tbody>
</table>