diff --git a/InvenTree/report/apps.py b/InvenTree/report/apps.py index bb1c5f0cb7..941133e481 100644 --- a/InvenTree/report/apps.py +++ b/InvenTree/report/apps.py @@ -18,6 +18,66 @@ class ReportConfig(AppConfig): """ 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): """ @@ -31,23 +91,6 @@ class ReportConfig(AppConfig): # Database is not ready yet 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 reports = [ { @@ -57,36 +100,27 @@ class ReportConfig(AppConfig): }, ] - for report in reports: + self.create_default_reports(TestReport, reports) - # Create destination file name - filename = os.path.join( - 'report', - 'inventree', - 'test', - report['file'] - ) + def create_default_build_reports(self): + """ + Create database entries for the default BuildReport templates + (if they do not already exist) + """ - src_file = os.path.join(src_dir, report['file']) - dst_file = os.path.join(settings.MEDIA_ROOT, filename) + try: + from .models import BuildReport + except: + # Database is not ready yet + return - if not os.path.exists(dst_file): - logger.info(f"Copying test report template '{dst_file}'") - shutil.copyfile(src_file, dst_file) + # List of Build reports to copy across + reports = [ + { + 'file': 'inventree_build_order.html', + 'name': 'InvenTree Build Order', + 'description': 'Build Order job sheet', + } + ] - try: - # 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 + self.create_default_reports(BuildReport, reports) diff --git a/InvenTree/report/models.py b/InvenTree/report/models.py index 906b65be0b..83810f7344 100644 --- a/InvenTree/report/models.py +++ b/InvenTree/report/models.py @@ -62,7 +62,6 @@ class ReportFileUpload(FileSystemStorage): def get_available_name(self, name, max_length=None): - print("Name:", name) return super().get_available_name(name, max_length) @@ -128,7 +127,8 @@ class ReportBase(models.Model): def __str__(self): return "{n} - {d}".format(n=self.name, d=self.description) - def getSubdir(self): + @classmethod + def getSubdir(cls): return '' def rename_file(self, filename): @@ -267,7 +267,8 @@ class TestReport(ReportTemplateBase): Render a TestReport against a StockItem object. """ - def getSubdir(self): + @classmethod + def getSubdir(cls): return 'test' filters = models.CharField( @@ -313,7 +314,8 @@ class BuildReport(ReportTemplateBase): Build order / work order report """ - def getSubdir(self): + @classmethod + def getSubdir(cls): return 'build' filters = models.CharField( @@ -349,7 +351,8 @@ class BillOfMaterialsReport(ReportTemplateBase): Render a Bill of Materials against a Part object """ - def getSubdir(self): + @classmethod + def getSubdir(cls): return 'bom' filters = models.CharField( diff --git a/InvenTree/report/templates/report/inventree_build_order.html b/InvenTree/report/templates/report/inventree_build_order.html new file mode 100644 index 0000000000..72e52a889a --- /dev/null +++ b/InvenTree/report/templates/report/inventree_build_order.html @@ -0,0 +1,3 @@ +{% extends "report/inventree_build_order_base.html" %} + + diff --git a/InvenTree/report/templates/report/inventree_build_order_base.html b/InvenTree/report/templates/report/inventree_build_order_base.html new file mode 100644 index 0000000000..6e8959ffbf --- /dev/null +++ b/InvenTree/report/templates/report/inventree_build_order_base.html @@ -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 %} + + +
{% trans "Build Order" %} | +{% internal_link build.get_absolute_url build %} | +
---|---|
{% trans "Part" %} | +{% internal_link part.get_absolute_url part.full_name %} | +
{% trans "Quantity" %} | +{{ build.quantity }} | +
{% trans "Description" %} | +{{ build.title }} | +
{% trans "Issued" %} | +{{ build.creation_date }} | +
{% trans "Target Date" %} | ++ {% if build.target_date %} + {{ build.target_date }} + {% else %} + Not specified + {% endif %} + | +
{% trans "Sales Order" %} | ++ {% if build.sales_order %} + {% internal_link build.sales_order.get_absolute_url build.sales_order %} + {% else %} + Not specified + {% endif %} + | +
{% trans "Required For" %} | +{% internal_link build.parent.get_absolute_url build.parent %} | +
{% trans "Issued By" %} | +{{ build.issued_by }} | +
{% trans "Responsible" %} | +{{ build.responsible }} | +
{% trans "Link" %} | +{{ build.link }} | +