mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 21:25:42 +00:00 
			
		
		
		
	Merge remote-tracking branch 'inventree/master'
This commit is contained in:
		| @@ -1,3 +1,6 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  |  | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from django.core.exceptions import ValidationError | from django.core.exceptions import ValidationError | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ from .models import ReportSnippet, TestReport, ReportAsset | |||||||
|  |  | ||||||
| class ReportTemplateAdmin(admin.ModelAdmin): | class ReportTemplateAdmin(admin.ModelAdmin): | ||||||
|  |  | ||||||
|     list_display = ('name', 'description', 'template', 'filters', 'enabled') |     list_display = ('name', 'description', 'template', 'filters', 'enabled', 'revision') | ||||||
|  |  | ||||||
|  |  | ||||||
| class ReportSnippetAdmin(admin.ModelAdmin): | class ReportSnippetAdmin(admin.ModelAdmin): | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								InvenTree/report/migrations/0009_testreport_revision.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								InvenTree/report/migrations/0009_testreport_revision.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										18
									
								
								InvenTree/report/migrations/0010_auto_20210205_1201.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								InvenTree/report/migrations/0010_auto_20210205_1201.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
| @@ -7,12 +7,14 @@ from __future__ import unicode_literals | |||||||
|  |  | ||||||
| import os | import os | ||||||
| import sys | import sys | ||||||
|  | import logging | ||||||
|  |  | ||||||
| import datetime | import datetime | ||||||
|  |  | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
|  |  | ||||||
|  | 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 | ||||||
| @@ -29,6 +31,35 @@ except OSError as err: | |||||||
|     sys.exit(1) |     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): | def rename_template(instance, filename): | ||||||
|  |  | ||||||
|     return instance.rename_file(filename) |     return instance.rename_file(filename) | ||||||
| @@ -62,6 +93,13 @@ class ReportBase(models.Model): | |||||||
|     class Meta: |     class Meta: | ||||||
|         abstract = True |         abstract = True | ||||||
|  |  | ||||||
|  |     def save(self, *args, **kwargs): | ||||||
|  |  | ||||||
|  |         # Increment revision number | ||||||
|  |         self.revision += 1 | ||||||
|  |  | ||||||
|  |         super().save() | ||||||
|  |  | ||||||
|     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) | ||||||
|  |  | ||||||
| @@ -113,6 +151,13 @@ class ReportBase(models.Model): | |||||||
|         help_text=_("Report template description") |         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): | class ReportTemplateBase(ReportBase): | ||||||
|     """ |     """ | ||||||
| @@ -145,6 +190,7 @@ class ReportTemplateBase(ReportBase): | |||||||
|  |  | ||||||
|         context['report_name'] = self.name |         context['report_name'] = self.name | ||||||
|         context['report_description'] = self.description |         context['report_description'] = self.description | ||||||
|  |         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['date'] = datetime.datetime.now().date() | ||||||
| @@ -220,7 +266,19 @@ def rename_snippet(instance, filename): | |||||||
|  |  | ||||||
|     filename = os.path.basename(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): | class ReportSnippet(models.Model): | ||||||
| @@ -244,7 +302,19 @@ def rename_asset(instance, filename): | |||||||
|  |  | ||||||
|     filename = os.path.basename(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): | class ReportAsset(models.Model): | ||||||
|   | |||||||
| @@ -52,7 +52,7 @@ function selectTestReport(reports, items, options={}) { | |||||||
|     <form method='post' action='' class='js-modal-form' enctype='multipart/form-data'> |     <form method='post' action='' class='js-modal-form' enctype='multipart/form-data'> | ||||||
|         <div class='form-group'> |         <div class='form-group'> | ||||||
|             <label class='control-label requiredField' for='id_report'> |             <label class='control-label requiredField' for='id_report'> | ||||||
|             {% trans "Select Label" %} |             {% trans "Select Report Template" %} | ||||||
|             </label> |             </label> | ||||||
|             <div class='controls'> |             <div class='controls'> | ||||||
|                 <select id='id_report' class='select form-control name='report'> |                 <select id='id_report' class='select form-control name='report'> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user