mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Add default build order report
Toot toot refactor tractor
This commit is contained in:
parent
f87b15e4ea
commit
46f20593c5
@ -18,6 +18,66 @@ class ReportConfig(AppConfig):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
self.create_default_test_reports()
|
self.create_default_test_reports()
|
||||||
|
self.create_default_build_reports()
|
||||||
|
|
||||||
|
def create_default_reports(self, model, reports):
|
||||||
|
"""
|
||||||
|
Copy defualt report files across to the media directory.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Source directory for report templates
|
||||||
|
src_dir = os.path.join(
|
||||||
|
os.path.dirname(os.path.realpath(__file__)),
|
||||||
|
'templates',
|
||||||
|
'report',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Destination directory
|
||||||
|
dst_dir = os.path.join(
|
||||||
|
settings.MEDIA_ROOT,
|
||||||
|
'report',
|
||||||
|
'inventree',
|
||||||
|
model.getSubdir(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if not os.path.exists(dst_dir):
|
||||||
|
logger.info(f"Creating missing directory: '{dst_dir}'")
|
||||||
|
os.makedirs(dst_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Copy each report template across (if required)
|
||||||
|
for report in reports:
|
||||||
|
|
||||||
|
# Destination filename
|
||||||
|
filename = os.path.join(
|
||||||
|
'report',
|
||||||
|
'inventree',
|
||||||
|
model.getSubdir(),
|
||||||
|
report['file'],
|
||||||
|
)
|
||||||
|
|
||||||
|
src_file = os.path.join(src_dir, report['file'])
|
||||||
|
dst_file = os.path.join(settings.MEDIA_ROOT, filename)
|
||||||
|
|
||||||
|
if not os.path.exists(dst_file):
|
||||||
|
logger.info(f"Copying test report template '{dst_file}'")
|
||||||
|
shutil.copyfile(src_file, dst_file)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Check if a report matching the template already exists
|
||||||
|
if model.objects.filter(template=filename).exists():
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.info(f"Creating new TestReport for '{report['name']}'")
|
||||||
|
|
||||||
|
model.objects.create(
|
||||||
|
name=report['name'],
|
||||||
|
description=report['description'],
|
||||||
|
template=filename,
|
||||||
|
enabled=True
|
||||||
|
)
|
||||||
|
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def create_default_test_reports(self):
|
def create_default_test_reports(self):
|
||||||
"""
|
"""
|
||||||
@ -31,23 +91,6 @@ class ReportConfig(AppConfig):
|
|||||||
# Database is not ready yet
|
# Database is not ready yet
|
||||||
return
|
return
|
||||||
|
|
||||||
src_dir = os.path.join(
|
|
||||||
os.path.dirname(os.path.realpath(__file__)),
|
|
||||||
'templates',
|
|
||||||
'report',
|
|
||||||
)
|
|
||||||
|
|
||||||
dst_dir = os.path.join(
|
|
||||||
settings.MEDIA_ROOT,
|
|
||||||
'report',
|
|
||||||
'inventree', # Stored in secret directory!
|
|
||||||
'test',
|
|
||||||
)
|
|
||||||
|
|
||||||
if not os.path.exists(dst_dir):
|
|
||||||
logger.info(f"Creating missing directory: '{dst_dir}'")
|
|
||||||
os.makedirs(dst_dir, exist_ok=True)
|
|
||||||
|
|
||||||
# List of test reports to copy across
|
# List of test reports to copy across
|
||||||
reports = [
|
reports = [
|
||||||
{
|
{
|
||||||
@ -57,36 +100,27 @@ class ReportConfig(AppConfig):
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
for report in reports:
|
self.create_default_reports(TestReport, reports)
|
||||||
|
|
||||||
# Create destination file name
|
def create_default_build_reports(self):
|
||||||
filename = os.path.join(
|
"""
|
||||||
'report',
|
Create database entries for the default BuildReport templates
|
||||||
'inventree',
|
(if they do not already exist)
|
||||||
'test',
|
"""
|
||||||
report['file']
|
|
||||||
)
|
|
||||||
|
|
||||||
src_file = os.path.join(src_dir, report['file'])
|
try:
|
||||||
dst_file = os.path.join(settings.MEDIA_ROOT, filename)
|
from .models import BuildReport
|
||||||
|
except:
|
||||||
|
# Database is not ready yet
|
||||||
|
return
|
||||||
|
|
||||||
if not os.path.exists(dst_file):
|
# List of Build reports to copy across
|
||||||
logger.info(f"Copying test report template '{dst_file}'")
|
reports = [
|
||||||
shutil.copyfile(src_file, dst_file)
|
{
|
||||||
|
'file': 'inventree_build_order.html',
|
||||||
|
'name': 'InvenTree Build Order',
|
||||||
|
'description': 'Build Order job sheet',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
try:
|
self.create_default_reports(BuildReport, reports)
|
||||||
# Check if a report matching the template already exists
|
|
||||||
if TestReport.objects.filter(template=filename).exists():
|
|
||||||
continue
|
|
||||||
|
|
||||||
logger.info(f"Creating new TestReport for '{report['name']}'")
|
|
||||||
|
|
||||||
TestReport.objects.create(
|
|
||||||
name=report['name'],
|
|
||||||
description=report['description'],
|
|
||||||
template=filename,
|
|
||||||
filters='',
|
|
||||||
enabled=True
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
@ -62,7 +62,6 @@ class ReportFileUpload(FileSystemStorage):
|
|||||||
|
|
||||||
def get_available_name(self, name, max_length=None):
|
def get_available_name(self, name, max_length=None):
|
||||||
|
|
||||||
print("Name:", name)
|
|
||||||
return super().get_available_name(name, max_length)
|
return super().get_available_name(name, max_length)
|
||||||
|
|
||||||
|
|
||||||
@ -128,7 +127,8 @@ class ReportBase(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{n} - {d}".format(n=self.name, d=self.description)
|
return "{n} - {d}".format(n=self.name, d=self.description)
|
||||||
|
|
||||||
def getSubdir(self):
|
@classmethod
|
||||||
|
def getSubdir(cls):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def rename_file(self, filename):
|
def rename_file(self, filename):
|
||||||
@ -267,7 +267,8 @@ class TestReport(ReportTemplateBase):
|
|||||||
Render a TestReport against a StockItem object.
|
Render a TestReport against a StockItem object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def getSubdir(self):
|
@classmethod
|
||||||
|
def getSubdir(cls):
|
||||||
return 'test'
|
return 'test'
|
||||||
|
|
||||||
filters = models.CharField(
|
filters = models.CharField(
|
||||||
@ -313,7 +314,8 @@ class BuildReport(ReportTemplateBase):
|
|||||||
Build order / work order report
|
Build order / work order report
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def getSubdir(self):
|
@classmethod
|
||||||
|
def getSubdir(cls):
|
||||||
return 'build'
|
return 'build'
|
||||||
|
|
||||||
filters = models.CharField(
|
filters = models.CharField(
|
||||||
@ -349,7 +351,8 @@ class BillOfMaterialsReport(ReportTemplateBase):
|
|||||||
Render a Bill of Materials against a Part object
|
Render a Bill of Materials against a Part object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def getSubdir(self):
|
@classmethod
|
||||||
|
def getSubdir(cls):
|
||||||
return 'bom'
|
return 'bom'
|
||||||
|
|
||||||
filters = models.CharField(
|
filters = models.CharField(
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
{% extends "report/inventree_build_order_base.html" %}
|
||||||
|
|
||||||
|
<!-- Refer to the inventree_build_order_base template -->
|
@ -0,0 +1,185 @@
|
|||||||
|
{% extends "report/inventree_report_base.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
{% load report %}
|
||||||
|
{% load inventree_extras %}
|
||||||
|
{% load markdownify %}
|
||||||
|
|
||||||
|
{% block page_margin %}
|
||||||
|
margin: 2cm;
|
||||||
|
margin-top: 4cm;
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
|
||||||
|
.header-right {
|
||||||
|
text-align: right;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
height: 20mm;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-image {
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 2px;
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 40mm;
|
||||||
|
display: inline-block;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-image {
|
||||||
|
max-width: 25%;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 5px;
|
||||||
|
min-height: 42mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details table {
|
||||||
|
overflow-x: scroll
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
width: 70%;
|
||||||
|
table-layout: fixed;
|
||||||
|
font-size: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details table td:not(:last-child){
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details table td:last-child{
|
||||||
|
width: 50%;
|
||||||
|
padding-left: 1cm;
|
||||||
|
padding-right: 1cm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-table td {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
border-bottom: 1px solid #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block bottom_left %}
|
||||||
|
content: "v{{report_revision}} - {{ date.isoformat }}";
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block bottom_center %}
|
||||||
|
content: "www.currawong.aero";
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block header_content %}
|
||||||
|
<img class='logo' src="{% asset 'logo_black_with_black_bird.png' %}" alt="hello" width="150">
|
||||||
|
|
||||||
|
<div class='header-right'>
|
||||||
|
<h3>
|
||||||
|
Build Order {{ build }}
|
||||||
|
</h3>
|
||||||
|
<small>{{ quantity }} x {{ part.full_name }}</small>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block page_content %}
|
||||||
|
|
||||||
|
<div class='details'>
|
||||||
|
<div class='details-image float-right'>
|
||||||
|
<img class='part-image' src="{% part_image part %}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='details-container'>
|
||||||
|
|
||||||
|
<table class='details-table'>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Build Order" %}</th>
|
||||||
|
<td>{% internal_link build.get_absolute_url build %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Part" %}</th>
|
||||||
|
<td>{% internal_link part.get_absolute_url part.full_name %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Quantity" %}</th>
|
||||||
|
<td>{{ build.quantity }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Description" %}</th>
|
||||||
|
<td>{{ build.title }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Issued" %}</th>
|
||||||
|
<td>{{ build.creation_date }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Target Date" %}</th>
|
||||||
|
<td>
|
||||||
|
{% if build.target_date %}
|
||||||
|
{{ build.target_date }}
|
||||||
|
{% else %}
|
||||||
|
<i>Not specified</i>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Sales Order" %}</th>
|
||||||
|
<td>
|
||||||
|
{% if build.sales_order %}
|
||||||
|
{% internal_link build.sales_order.get_absolute_url build.sales_order %}
|
||||||
|
{% else %}
|
||||||
|
<i>Not specified</i>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% if build.parent %}
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Required For" %}</th>
|
||||||
|
<td>{% internal_link build.parent.get_absolute_url build.parent %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% if build.issued_by %}
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Issued By" %}</th>
|
||||||
|
<td>{{ build.issued_by }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% if build.responsible %}
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Responsible" %}</th>
|
||||||
|
<td>{{ build.responsible }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% if build.link %}
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Link" %}</th>
|
||||||
|
<td><a href="{{ build.link }}">{{ build.link }}</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>{% trans "Notes" %}</h3>
|
||||||
|
|
||||||
|
{% if build.notes %}
|
||||||
|
{{ build.notes|markdownify }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user