diff --git a/InvenTree/company/tests.py b/InvenTree/company/tests.py index 5229fdb045..f7bcd4e0b6 100644 --- a/InvenTree/company/tests.py +++ b/InvenTree/company/tests.py @@ -1,3 +1,6 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + from django.test import TestCase from django.core.exceptions import ValidationError diff --git a/InvenTree/report/admin.py b/InvenTree/report/admin.py index 390964b590..610b32dd4c 100644 --- a/InvenTree/report/admin.py +++ b/InvenTree/report/admin.py @@ -8,7 +8,7 @@ from .models import ReportSnippet, TestReport, ReportAsset class ReportTemplateAdmin(admin.ModelAdmin): - list_display = ('name', 'description', 'template', 'filters', 'enabled') + list_display = ('name', 'description', 'template', 'filters', 'enabled', 'revision') class ReportSnippetAdmin(admin.ModelAdmin): diff --git a/InvenTree/report/migrations/0009_testreport_revision.py b/InvenTree/report/migrations/0009_testreport_revision.py new file mode 100644 index 0000000000..d78e866968 --- /dev/null +++ b/InvenTree/report/migrations/0009_testreport_revision.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.7 on 2021-02-05 00:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('report', '0008_auto_20210204_2100'), + ] + + operations = [ + migrations.AddField( + model_name='testreport', + name='revision', + field=models.PositiveIntegerField(default=1, help_text='Report revision number (auto-increments)', verbose_name='Revision'), + ), + ] diff --git a/InvenTree/report/migrations/0010_auto_20210205_1201.py b/InvenTree/report/migrations/0010_auto_20210205_1201.py new file mode 100644 index 0000000000..af8821032e --- /dev/null +++ b/InvenTree/report/migrations/0010_auto_20210205_1201.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.7 on 2021-02-05 01:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('report', '0009_testreport_revision'), + ] + + operations = [ + migrations.AlterField( + model_name='testreport', + name='revision', + field=models.PositiveIntegerField(default=1, editable=False, help_text='Report revision number (auto-increments)', verbose_name='Revision'), + ), + ] diff --git a/InvenTree/report/models.py b/InvenTree/report/models.py index 422216f9a9..0c77f4d19a 100644 --- a/InvenTree/report/models.py +++ b/InvenTree/report/models.py @@ -7,12 +7,14 @@ from __future__ import unicode_literals import os import sys +import logging import datetime from django.db import models from django.conf import settings +from django.core.files.storage import FileSystemStorage from django.core.validators import FileExtensionValidator import stock.models @@ -29,6 +31,35 @@ except OSError as err: sys.exit(1) +logger = logging.getLogger(__name__) + + +class ReportFileUpload(FileSystemStorage): + """ + Custom implementation of FileSystemStorage class. + + When uploading a report (or a snippet / asset / etc), + it is often important to ensure the filename is not arbitrarily *changed*, + if the name of the uploaded file is identical to the currently stored file. + + For example, a snippet or asset file is referenced in a template by filename, + and we do not want that filename to change when we upload a new *version* + of the snippet or asset file. + + This uploader class performs the following pseudo-code function: + + - If the model is *new*, proceed as normal + - If the model is being updated: + a) If the new filename is *different* from the existing filename, proceed as normal + b) If the new filename is *identical* to the existing filename, we want to overwrite the existing file + """ + + def get_available_name(self, name, max_length=None): + + print("Name:", name) + return super().get_available_name(name, max_length) + + def rename_template(instance, filename): return instance.rename_file(filename) @@ -62,6 +93,13 @@ class ReportBase(models.Model): class Meta: abstract = True + def save(self, *args, **kwargs): + + # Increment revision number + self.revision += 1 + + super().save() + def __str__(self): return "{n} - {d}".format(n=self.name, d=self.description) @@ -113,6 +151,13 @@ class ReportBase(models.Model): help_text=_("Report template description") ) + revision = models.PositiveIntegerField( + default=1, + verbose_name=_("Revision"), + help_text=_("Report revision number (auto-increments)"), + editable=False, + ) + class ReportTemplateBase(ReportBase): """ @@ -145,6 +190,7 @@ class ReportTemplateBase(ReportBase): 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() @@ -220,7 +266,19 @@ def rename_snippet(instance, filename): filename = os.path.basename(filename) - return os.path.join('report', 'snippets', filename) + path = os.path.join('report', 'snippets', filename) + + # If the snippet file is the *same* filename as the one being uploaded, + # delete the original one from the media directory + if str(filename) == str(instance.snippet): + fullpath = os.path.join(settings.MEDIA_ROOT, path) + fullpath = os.path.abspath(fullpath) + + if os.path.exists(fullpath): + logger.info(f"Deleting existing snippet file: '{filename}'") + os.remove(fullpath) + + return path class ReportSnippet(models.Model): @@ -244,7 +302,19 @@ def rename_asset(instance, filename): filename = os.path.basename(filename) - return os.path.join('report', 'assets', filename) + path = os.path.join('report', 'assets', filename) + + # If the asset file is the *same* filename as the one being uploaded, + # delete the original one from the media directory + if str(filename) == str(instance.asset): + fullpath = os.path.join(settings.MEDIA_ROOT, path) + fullpath = os.path.abspath(fullpath) + + if os.path.exists(fullpath): + logger.info(f"Deleting existing asset file: '{filename}'") + os.remove(fullpath) + + return path class ReportAsset(models.Model): diff --git a/InvenTree/templates/js/report.js b/InvenTree/templates/js/report.js index 6e47706917..26907b52df 100644 --- a/InvenTree/templates/js/report.js +++ b/InvenTree/templates/js/report.js @@ -52,7 +52,7 @@ function selectTestReport(reports, items, options={}) {