mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	Docstring checks in QC checks (#3089)
* Add pre-commit to the stack * exclude static * Add locales to excludes * fix style errors * rename pipeline steps * also wait on precommit * make template matching simpler * Use the same code for python setup everywhere * use step and cache for python setup * move regular settings up into general envs * just use full update * Use invoke instead of static references * make setup actions more similar * use python3 * refactor names to be similar * fix runner version * fix references * remove incidential change * use matrix for os * Github can't do this right now * ignore docstyle errors * Add seperate docstring test * update flake call * do not fail on docstring * refactor setup into workflow * update reference * switch to action * resturcture * add bash statements * remove os from cache * update input checks * make code cleaner * fix boolean * no relative paths * install wheel by python * switch to install * revert back to simple wheel * refactor import export tests * move setup keys back to not disturbe tests * remove docstyle till that is fixed * update references * continue on error * add docstring test * use relativ action references * Change step / job docstrings * update to merge * reformat comments 1 * fix docstrings 2 * fix docstrings 3 * fix docstrings 4 * fix docstrings 5 * fix docstrings 6 * fix docstrings 7 * fix docstrings 8 * fix docstirns 9 * fix docstrings 10 * docstring adjustments * update the remaining docstrings * small docstring changes * fix function name * update support files for docstrings * Add missing args to docstrings * Remove outdated function * Add docstrings for the 'build' app * Make API code cleaner * add more docstrings for plugin app * Remove dead code for plugin settings No idea what that was even intended for * ignore __init__ files for docstrings * More docstrings * Update docstrings for the 'part' directory * Fixes for related_part functionality * Fix removed stuff from merge99676ee* make more consistent * Show statistics for docstrings * add more docstrings * move specific register statements to make them clearer to understant * More docstrings for common * and more docstrings * and more * simpler call * docstrings for notifications * docstrings for common/tests * Add docs for common/models * Revert "move specific register statements to make them clearer to understant" This reverts commitca96654622. * use typing here * Revert "Make API code cleaner" This reverts commit24fb68bd3e. * docstring updates for the 'users' app * Add generic Meta info to simple Meta classes * remove unneeded unique_together statements * More simple metas * Remove unnecessary format specifier * Remove extra json format specifiers * Add docstrings for the 'plugin' app * Docstrings for the 'label' app * Add missing docstrings for the 'report' app * Fix build test regression * Fix top-level files * docstrings for InvenTree/InvenTree * reduce unneeded code * add docstrings * and more docstrings * more docstrings * more docstrings for stock * more docstrings * docstrings for order/views * Docstrings for various files in the 'order' app * Docstrings for order/test_api.py * Docstrings for order/serializers.py * Docstrings for order/admin.py * More docstrings for the order app * Add docstrings for the 'company' app * Add unit tests for rebuilding the reference fields * Prune out some more dead code * remove more dead code Co-authored-by: Oliver Walters <oliver.henry.walters@gmail.com>
This commit is contained in:
		| @@ -1,3 +1,5 @@ | ||||
| """Admin functionality for the 'report' app""" | ||||
|  | ||||
| from django.contrib import admin | ||||
|  | ||||
| from .models import (BillOfMaterialsReport, BuildReport, PurchaseOrderReport, | ||||
| @@ -5,17 +7,17 @@ from .models import (BillOfMaterialsReport, BuildReport, PurchaseOrderReport, | ||||
|  | ||||
|  | ||||
| class ReportTemplateAdmin(admin.ModelAdmin): | ||||
|  | ||||
|     """Admin class for the various reporting models""" | ||||
|     list_display = ('name', 'description', 'template', 'filters', 'enabled', 'revision') | ||||
|  | ||||
|  | ||||
| class ReportSnippetAdmin(admin.ModelAdmin): | ||||
|  | ||||
|     """Admin class for the ReportSnippet model""" | ||||
|     list_display = ('id', 'snippet', 'description') | ||||
|  | ||||
|  | ||||
| class ReportAssetAdmin(admin.ModelAdmin): | ||||
|  | ||||
|     """Admin class for the ReportAsset model""" | ||||
|     list_display = ('id', 'asset', 'description') | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| """API functionality for the 'report' app""" | ||||
|  | ||||
| from django.core.exceptions import FieldError, ValidationError | ||||
| from django.http import HttpResponse | ||||
| @@ -24,9 +25,7 @@ from .serializers import (BOMReportSerializer, BuildReportSerializer, | ||||
|  | ||||
|  | ||||
| class ReportListView(generics.ListAPIView): | ||||
|     """ | ||||
|     Generic API class for report templates | ||||
|     """ | ||||
|     """Generic API class for report templates.""" | ||||
|  | ||||
|     filter_backends = [ | ||||
|         DjangoFilterBackend, | ||||
| @@ -44,15 +43,10 @@ class ReportListView(generics.ListAPIView): | ||||
|  | ||||
|  | ||||
| class StockItemReportMixin: | ||||
|     """ | ||||
|     Mixin for extracting stock items from query params | ||||
|     """ | ||||
|     """Mixin for extracting stock items from query params.""" | ||||
|  | ||||
|     def get_items(self): | ||||
|         """ | ||||
|         Return a list of requested stock items | ||||
|         """ | ||||
|  | ||||
|         """Return a list of requested stock items.""" | ||||
|         items = [] | ||||
|  | ||||
|         params = self.request.query_params | ||||
| @@ -77,15 +71,10 @@ class StockItemReportMixin: | ||||
|  | ||||
|  | ||||
| class BuildReportMixin: | ||||
|     """ | ||||
|     Mixin for extracting Build items from query params | ||||
|     """ | ||||
|     """Mixin for extracting Build items from query params.""" | ||||
|  | ||||
|     def get_builds(self): | ||||
|         """ | ||||
|         Return a list of requested Build objects | ||||
|         """ | ||||
|  | ||||
|         """Return a list of requested Build objects.""" | ||||
|         builds = [] | ||||
|  | ||||
|         params = self.request.query_params | ||||
| @@ -109,17 +98,13 @@ class BuildReportMixin: | ||||
|  | ||||
|  | ||||
| class OrderReportMixin: | ||||
|     """ | ||||
|     Mixin for extracting order items from query params | ||||
|     """Mixin for extracting order items from query params. | ||||
|  | ||||
|     requires the OrderModel class attribute to be set! | ||||
|     """ | ||||
|  | ||||
|     def get_orders(self): | ||||
|         """ | ||||
|         Return a list of order objects | ||||
|         """ | ||||
|  | ||||
|         """Return a list of order objects.""" | ||||
|         orders = [] | ||||
|  | ||||
|         params = self.request.query_params | ||||
| @@ -143,15 +128,10 @@ class OrderReportMixin: | ||||
|  | ||||
|  | ||||
| class PartReportMixin: | ||||
|     """ | ||||
|     Mixin for extracting part items from query params | ||||
|     """ | ||||
|     """Mixin for extracting part items from query params.""" | ||||
|  | ||||
|     def get_parts(self): | ||||
|         """ | ||||
|         Return a list of requested part objects | ||||
|         """ | ||||
|  | ||||
|         """Return a list of requested part objects.""" | ||||
|         parts = [] | ||||
|  | ||||
|         params = self.request.query_params | ||||
| @@ -176,15 +156,10 @@ class PartReportMixin: | ||||
|  | ||||
|  | ||||
| class ReportPrintMixin: | ||||
|     """ | ||||
|     Mixin for printing reports | ||||
|     """ | ||||
|     """Mixin for printing reports.""" | ||||
|  | ||||
|     def print(self, request, items_to_print): | ||||
|         """ | ||||
|         Print this report template against a number of pre-validated items. | ||||
|         """ | ||||
|  | ||||
|         """Print this report template against a number of pre-validated items.""" | ||||
|         if len(items_to_print) == 0: | ||||
|             # No valid items provided, return an error message | ||||
|             data = { | ||||
| @@ -229,19 +204,13 @@ class ReportPrintMixin: | ||||
|             report_name += '.pdf' | ||||
|  | ||||
|         if debug_mode: | ||||
|             """ | ||||
|             Contatenate all rendered templates into a single HTML string, | ||||
|             and return the string as a HTML response. | ||||
|             """ | ||||
|             """Contatenate all rendered templates into a single HTML string, and return the string as a HTML response.""" | ||||
|  | ||||
|             html = "\n".join(outputs) | ||||
|  | ||||
|             return HttpResponse(html) | ||||
|         else: | ||||
|             """ | ||||
|             Concatenate all rendered pages into a single PDF object, | ||||
|             and return the resulting document! | ||||
|             """ | ||||
|             """Concatenate all rendered pages into a single PDF object, and return the resulting document!""" | ||||
|  | ||||
|             pages = [] | ||||
|  | ||||
| @@ -283,21 +252,19 @@ class ReportPrintMixin: | ||||
|  | ||||
|  | ||||
| class StockItemTestReportList(ReportListView, StockItemReportMixin): | ||||
|     """ | ||||
|     API endpoint for viewing list of TestReport objects. | ||||
|     """API endpoint for viewing list of TestReport objects. | ||||
|  | ||||
|     Filterable by: | ||||
|  | ||||
|     - enabled: Filter by enabled / disabled status | ||||
|     - item: Filter by stock item(s) | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     queryset = TestReport.objects.all() | ||||
|     serializer_class = TestReportSerializer | ||||
|  | ||||
|     def filter_queryset(self, queryset): | ||||
|  | ||||
|         """Custom queryset filtering""" | ||||
|         queryset = super().filter_queryset(queryset) | ||||
|  | ||||
|         # List of StockItem objects to match against | ||||
| @@ -347,35 +314,27 @@ class StockItemTestReportList(ReportListView, StockItemReportMixin): | ||||
|  | ||||
|  | ||||
| class StockItemTestReportDetail(generics.RetrieveUpdateDestroyAPIView): | ||||
|     """ | ||||
|     API endpoint for a single TestReport object | ||||
|     """ | ||||
|     """API endpoint for a single TestReport object.""" | ||||
|  | ||||
|     queryset = TestReport.objects.all() | ||||
|     serializer_class = TestReportSerializer | ||||
|  | ||||
|  | ||||
| class StockItemTestReportPrint(generics.RetrieveAPIView, StockItemReportMixin, ReportPrintMixin): | ||||
|     """ | ||||
|     API endpoint for printing a TestReport object | ||||
|     """ | ||||
|     """API endpoint for printing a TestReport object.""" | ||||
|  | ||||
|     queryset = TestReport.objects.all() | ||||
|     serializer_class = TestReportSerializer | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         """ | ||||
|         Check if valid stock item(s) have been provided. | ||||
|         """ | ||||
|  | ||||
|         """Check if valid stock item(s) have been provided.""" | ||||
|         items = self.get_items() | ||||
|  | ||||
|         return self.print(request, items) | ||||
|  | ||||
|  | ||||
| class BOMReportList(ReportListView, PartReportMixin): | ||||
|     """ | ||||
|     API endpoint for viewing a list of BillOfMaterialReport objects. | ||||
|     """API endpoint for viewing a list of BillOfMaterialReport objects. | ||||
|  | ||||
|     Filterably by: | ||||
|  | ||||
| @@ -387,7 +346,7 @@ class BOMReportList(ReportListView, PartReportMixin): | ||||
|     serializer_class = BOMReportSerializer | ||||
|  | ||||
|     def filter_queryset(self, queryset): | ||||
|  | ||||
|         """Custom queryset filtering""" | ||||
|         queryset = super().filter_queryset(queryset) | ||||
|  | ||||
|         # List of Part objects to match against | ||||
| @@ -436,35 +395,27 @@ class BOMReportList(ReportListView, PartReportMixin): | ||||
|  | ||||
|  | ||||
| class BOMReportDetail(generics.RetrieveUpdateDestroyAPIView): | ||||
|     """ | ||||
|     API endpoint for a single BillOfMaterialReport object | ||||
|     """ | ||||
|     """API endpoint for a single BillOfMaterialReport object.""" | ||||
|  | ||||
|     queryset = BillOfMaterialsReport.objects.all() | ||||
|     serializer_class = BOMReportSerializer | ||||
|  | ||||
|  | ||||
| class BOMReportPrint(generics.RetrieveAPIView, PartReportMixin, ReportPrintMixin): | ||||
|     """ | ||||
|     API endpoint for printing a BillOfMaterialReport object | ||||
|     """ | ||||
|     """API endpoint for printing a BillOfMaterialReport object.""" | ||||
|  | ||||
|     queryset = BillOfMaterialsReport.objects.all() | ||||
|     serializer_class = BOMReportSerializer | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         """ | ||||
|         Check if valid part item(s) have been provided | ||||
|         """ | ||||
|  | ||||
|         """Check if valid part item(s) have been provided.""" | ||||
|         parts = self.get_parts() | ||||
|  | ||||
|         return self.print(request, parts) | ||||
|  | ||||
|  | ||||
| class BuildReportList(ReportListView, BuildReportMixin): | ||||
|     """ | ||||
|     API endpoint for viewing a list of BuildReport objects. | ||||
|     """API endpoint for viewing a list of BuildReport objects. | ||||
|  | ||||
|     Can be filtered by: | ||||
|  | ||||
| @@ -476,7 +427,7 @@ class BuildReportList(ReportListView, BuildReportMixin): | ||||
|     serializer_class = BuildReportSerializer | ||||
|  | ||||
|     def filter_queryset(self, queryset): | ||||
|  | ||||
|         """Custom queryset filtering""" | ||||
|         queryset = super().filter_queryset(queryset) | ||||
|  | ||||
|         # List of Build objects to match against | ||||
| @@ -526,45 +477,41 @@ class BuildReportList(ReportListView, BuildReportMixin): | ||||
|  | ||||
|  | ||||
| class BuildReportDetail(generics.RetrieveUpdateDestroyAPIView): | ||||
|     """ | ||||
|     API endpoint for a single BuildReport object | ||||
|     """ | ||||
|     """API endpoint for a single BuildReport object.""" | ||||
|  | ||||
|     queryset = BuildReport.objects.all() | ||||
|     serializer_class = BuildReportSerializer | ||||
|  | ||||
|  | ||||
| class BuildReportPrint(generics.RetrieveAPIView, BuildReportMixin, ReportPrintMixin): | ||||
|     """ | ||||
|     API endpoint for printing a BuildReport | ||||
|     """ | ||||
|     """API endpoint for printing a BuildReport.""" | ||||
|  | ||||
|     queryset = BuildReport.objects.all() | ||||
|     serializer_class = BuildReportSerializer | ||||
|  | ||||
|     def get(self, request, *ars, **kwargs): | ||||
|  | ||||
|         """Perform a GET action to print the report""" | ||||
|         builds = self.get_builds() | ||||
|  | ||||
|         return self.print(request, builds) | ||||
|  | ||||
|  | ||||
| class PurchaseOrderReportList(ReportListView, OrderReportMixin): | ||||
|  | ||||
|     """API list endpoint for the PurchaseOrderReport model""" | ||||
|     OrderModel = order.models.PurchaseOrder | ||||
|  | ||||
|     queryset = PurchaseOrderReport.objects.all() | ||||
|     serializer_class = PurchaseOrderReportSerializer | ||||
|  | ||||
|     def filter_queryset(self, queryset): | ||||
|  | ||||
|         """Custom queryset filter for the PurchaseOrderReport list""" | ||||
|         queryset = super().filter_queryset(queryset) | ||||
|  | ||||
|         orders = self.get_orders() | ||||
|  | ||||
|         if len(orders) > 0: | ||||
|             """ | ||||
|             We wish to filter by purchase orders | ||||
|             We wish to filter by purchase orders. | ||||
|  | ||||
|             We need to compare the 'filters' string of each report, | ||||
|             and see if it matches against each of the specified orders. | ||||
| @@ -607,18 +554,14 @@ class PurchaseOrderReportList(ReportListView, OrderReportMixin): | ||||
|  | ||||
|  | ||||
| class PurchaseOrderReportDetail(generics.RetrieveUpdateDestroyAPIView): | ||||
|     """ | ||||
|     API endpoint for a single PurchaseOrderReport object | ||||
|     """ | ||||
|     """API endpoint for a single PurchaseOrderReport object.""" | ||||
|  | ||||
|     queryset = PurchaseOrderReport.objects.all() | ||||
|     serializer_class = PurchaseOrderReportSerializer | ||||
|  | ||||
|  | ||||
| class PurchaseOrderReportPrint(generics.RetrieveAPIView, OrderReportMixin, ReportPrintMixin): | ||||
|     """ | ||||
|     API endpoint for printing a PurchaseOrderReport object | ||||
|     """ | ||||
|     """API endpoint for printing a PurchaseOrderReport object.""" | ||||
|  | ||||
|     OrderModel = order.models.PurchaseOrder | ||||
|  | ||||
| @@ -626,28 +569,28 @@ class PurchaseOrderReportPrint(generics.RetrieveAPIView, OrderReportMixin, Repor | ||||
|     serializer_class = PurchaseOrderReportSerializer | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|  | ||||
|         """Perform GET request to print the report""" | ||||
|         orders = self.get_orders() | ||||
|  | ||||
|         return self.print(request, orders) | ||||
|  | ||||
|  | ||||
| class SalesOrderReportList(ReportListView, OrderReportMixin): | ||||
|  | ||||
|     """API list endpoint for the SalesOrderReport model""" | ||||
|     OrderModel = order.models.SalesOrder | ||||
|  | ||||
|     queryset = SalesOrderReport.objects.all() | ||||
|     serializer_class = SalesOrderReportSerializer | ||||
|  | ||||
|     def filter_queryset(self, queryset): | ||||
|  | ||||
|         """Custom queryset filtering for the SalesOrderReport API list""" | ||||
|         queryset = super().filter_queryset(queryset) | ||||
|  | ||||
|         orders = self.get_orders() | ||||
|  | ||||
|         if len(orders) > 0: | ||||
|             """ | ||||
|             We wish to filter by purchase orders | ||||
|             We wish to filter by purchase orders. | ||||
|  | ||||
|             We need to compare the 'filters' string of each report, | ||||
|             and see if it matches against each of the specified orders. | ||||
| @@ -690,18 +633,14 @@ class SalesOrderReportList(ReportListView, OrderReportMixin): | ||||
|  | ||||
|  | ||||
| class SalesOrderReportDetail(generics.RetrieveUpdateDestroyAPIView): | ||||
|     """ | ||||
|     API endpoint for a single SalesOrderReport object | ||||
|     """ | ||||
|     """API endpoint for a single SalesOrderReport object.""" | ||||
|  | ||||
|     queryset = SalesOrderReport.objects.all() | ||||
|     serializer_class = SalesOrderReportSerializer | ||||
|  | ||||
|  | ||||
| class SalesOrderReportPrint(generics.RetrieveAPIView, OrderReportMixin, ReportPrintMixin): | ||||
|     """ | ||||
|     API endpoint for printing a PurchaseOrderReport object | ||||
|     """ | ||||
|     """API endpoint for printing a PurchaseOrderReport object.""" | ||||
|  | ||||
|     OrderModel = order.models.SalesOrder | ||||
|  | ||||
| @@ -709,7 +648,7 @@ class SalesOrderReportPrint(generics.RetrieveAPIView, OrderReportMixin, ReportPr | ||||
|     serializer_class = SalesOrderReportSerializer | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|  | ||||
|         """Perform a GET request to print the report""" | ||||
|         orders = self.get_orders() | ||||
|  | ||||
|         return self.print(request, orders) | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| """Config options for the 'report' app""" | ||||
|  | ||||
| import logging | ||||
| import os | ||||
| import shutil | ||||
| @@ -11,22 +13,17 @@ logger = logging.getLogger("inventree") | ||||
|  | ||||
|  | ||||
| class ReportConfig(AppConfig): | ||||
|     """Configuration class for the 'report' app""" | ||||
|     name = 'report' | ||||
|  | ||||
|     def ready(self): | ||||
|         """ | ||||
|         This function is called whenever the report app is loaded | ||||
|         """ | ||||
|  | ||||
|         """This function is called whenever the report app is loaded.""" | ||||
|         if canAppAccessDatabase(allow_test=True): | ||||
|             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. | ||||
|         """ | ||||
|  | ||||
|         """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__)), | ||||
| @@ -82,11 +79,7 @@ class ReportConfig(AppConfig): | ||||
|                 pass | ||||
|  | ||||
|     def create_default_test_reports(self): | ||||
|         """ | ||||
|         Create database entries for the default TestReport templates, | ||||
|         if they do not already exist | ||||
|         """ | ||||
|  | ||||
|         """Create database entries for the default TestReport templates, if they do not already exist.""" | ||||
|         try: | ||||
|             from .models import TestReport | ||||
|         except:  # pragma: no cover | ||||
| @@ -105,11 +98,7 @@ class ReportConfig(AppConfig): | ||||
|         self.create_default_reports(TestReport, reports) | ||||
|  | ||||
|     def create_default_build_reports(self): | ||||
|         """ | ||||
|         Create database entries for the default BuildReport templates | ||||
|         (if they do not already exist) | ||||
|         """ | ||||
|  | ||||
|         """Create database entries for the default BuildReport templates (if they do not already exist)""" | ||||
|         try: | ||||
|             from .models import BuildReport | ||||
|         except:  # pragma: no cover | ||||
|   | ||||
| @@ -1,6 +1,4 @@ | ||||
| """ | ||||
| Report template model definitions | ||||
| """ | ||||
| """Report template model definitions.""" | ||||
|  | ||||
| import datetime | ||||
| import logging | ||||
| @@ -36,94 +34,78 @@ logger = logging.getLogger("inventree") | ||||
|  | ||||
|  | ||||
| def rename_template(instance, filename): | ||||
|     """ | ||||
|     Helper function for 'renaming' uploaded report files. | ||||
|     """Helper function for 'renaming' uploaded report files. | ||||
|  | ||||
|     Pass responsibility back to the calling class, | ||||
|     to ensure that files are uploaded to the correct directory. | ||||
|     """ | ||||
|  | ||||
|     return instance.rename_file(filename) | ||||
|  | ||||
|  | ||||
| def validate_stock_item_report_filters(filters): | ||||
|     """ | ||||
|     Validate filter string against StockItem model | ||||
|     """ | ||||
|  | ||||
|     """Validate filter string against StockItem model.""" | ||||
|     return validateFilterString(filters, model=stock.models.StockItem) | ||||
|  | ||||
|  | ||||
| def validate_part_report_filters(filters): | ||||
|     """ | ||||
|     Validate filter string against Part model | ||||
|     """ | ||||
|  | ||||
|     """Validate filter string against Part model.""" | ||||
|     return validateFilterString(filters, model=part.models.Part) | ||||
|  | ||||
|  | ||||
| def validate_build_report_filters(filters): | ||||
|     """ | ||||
|     Validate filter string against Build model | ||||
|     """ | ||||
|  | ||||
|     """Validate filter string against Build model.""" | ||||
|     return validateFilterString(filters, model=build.models.Build) | ||||
|  | ||||
|  | ||||
| def validate_purchase_order_filters(filters): | ||||
|     """ | ||||
|     Validate filter string against PurchaseOrder model | ||||
|     """ | ||||
|  | ||||
|     """Validate filter string against PurchaseOrder model.""" | ||||
|     return validateFilterString(filters, model=order.models.PurchaseOrder) | ||||
|  | ||||
|  | ||||
| def validate_sales_order_filters(filters): | ||||
|     """ | ||||
|     Validate filter string against SalesOrder model | ||||
|     """ | ||||
|  | ||||
|     """Validate filter string against SalesOrder model.""" | ||||
|     return validateFilterString(filters, model=order.models.SalesOrder) | ||||
|  | ||||
|  | ||||
| class WeasyprintReportMixin(WeasyTemplateResponseMixin): | ||||
|     """ | ||||
|     Class for rendering a HTML template to a PDF. | ||||
|     """ | ||||
|     """Class for rendering a HTML template to a PDF.""" | ||||
|  | ||||
|     pdf_filename = 'report.pdf' | ||||
|     pdf_attachment = True | ||||
|  | ||||
|     def __init__(self, request, template, **kwargs): | ||||
|  | ||||
|         """Initialize the report mixin with some standard attributes""" | ||||
|         self.request = request | ||||
|         self.template_name = template | ||||
|         self.pdf_filename = kwargs.get('filename', 'report.pdf') | ||||
|  | ||||
|  | ||||
| class ReportBase(models.Model): | ||||
|     """ | ||||
|     Base class for uploading html templates | ||||
|     """ | ||||
|     """Base class for uploading html templates.""" | ||||
|  | ||||
|     class Meta: | ||||
|         """Metaclass options. Abstract ensures no database table is created.""" | ||||
|  | ||||
|         abstract = True | ||||
|  | ||||
|     def save(self, *args, **kwargs): | ||||
|  | ||||
|         """Perform additional actions when the report is saved""" | ||||
|         # Increment revision number | ||||
|         self.revision += 1 | ||||
|  | ||||
|         super().save() | ||||
|  | ||||
|     def __str__(self): | ||||
|         """Format a string representation of a report instance""" | ||||
|         return "{n} - {d}".format(n=self.name, d=self.description) | ||||
|  | ||||
|     @classmethod | ||||
|     def getSubdir(cls): | ||||
|         """Return the subdirectory where template files for this report model will be located.""" | ||||
|         return '' | ||||
|  | ||||
|     def rename_file(self, filename): | ||||
|         # Function for renaming uploaded file | ||||
|         """Function for renaming uploaded file""" | ||||
|  | ||||
|         filename = os.path.basename(filename) | ||||
|  | ||||
| @@ -147,15 +129,15 @@ class ReportBase(models.Model): | ||||
|  | ||||
|     @property | ||||
|     def extension(self): | ||||
|         """Return the filename extension of the associated template file""" | ||||
|         return os.path.splitext(self.template.name)[1].lower() | ||||
|  | ||||
|     @property | ||||
|     def template_name(self): | ||||
|         """ | ||||
|         Returns the file system path to the template file. | ||||
|         """Returns the file system path to the template file. | ||||
|  | ||||
|         Required for passing the file to an external process | ||||
|         """ | ||||
|  | ||||
|         template = self.template.name | ||||
|         template = template.replace('/', os.path.sep) | ||||
|         template = template.replace('\\', os.path.sep) | ||||
| @@ -192,28 +174,20 @@ class ReportBase(models.Model): | ||||
|  | ||||
|  | ||||
| class ReportTemplateBase(ReportBase): | ||||
|     """ | ||||
|     Reporting template model. | ||||
|     """Reporting template model. | ||||
|  | ||||
|     Able to be passed context data | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     # Pass a single top-level object to the report template | ||||
|     object_to_print = None | ||||
|  | ||||
|     def get_context_data(self, request): | ||||
|         """ | ||||
|         Supply context data to the template for rendering | ||||
|         """ | ||||
|  | ||||
|         """Supply context data to the template for rendering.""" | ||||
|         return {} | ||||
|  | ||||
|     def context(self, request): | ||||
|         """ | ||||
|         All context to be passed to the renderer. | ||||
|         """ | ||||
|  | ||||
|         """All context to be passed to the renderer.""" | ||||
|         # Generate custom context data based on the particular report subclass | ||||
|         context = self.get_context_data(request) | ||||
|  | ||||
| @@ -230,10 +204,7 @@ class ReportTemplateBase(ReportBase): | ||||
|         return context | ||||
|  | ||||
|     def generate_filename(self, request, **kwargs): | ||||
|         """ | ||||
|         Generate a filename for this report | ||||
|         """ | ||||
|  | ||||
|         """Generate a filename for this report.""" | ||||
|         template_string = Template(self.filename_pattern) | ||||
|  | ||||
|         ctx = self.context(request) | ||||
| @@ -243,21 +214,17 @@ class ReportTemplateBase(ReportBase): | ||||
|         return template_string.render(context) | ||||
|  | ||||
|     def render_as_string(self, request, **kwargs): | ||||
|         """ | ||||
|         Render the report to a HTML string. | ||||
|         """Render the report to a HTML string. | ||||
|  | ||||
|         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. | ||||
|         """Render the template to a PDF file. | ||||
|  | ||||
|         Uses django-weasyprint plugin to render HTML template against Weasyprint | ||||
|         """ | ||||
|  | ||||
|         # TODO: Support custom filename generation! | ||||
|         # filename = kwargs.get('filename', 'report.pdf') | ||||
|  | ||||
| @@ -288,20 +255,22 @@ class ReportTemplateBase(ReportBase): | ||||
|     ) | ||||
|  | ||||
|     class Meta: | ||||
|         """Metaclass options. Abstract ensures no database table is created.""" | ||||
|  | ||||
|         abstract = True | ||||
|  | ||||
|  | ||||
| class TestReport(ReportTemplateBase): | ||||
|     """ | ||||
|     Render a TestReport against a StockItem object. | ||||
|     """ | ||||
|     """Render a TestReport against a StockItem object.""" | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_api_url(): | ||||
|         """Return the API URL associated with the TestReport model""" | ||||
|         return reverse('api-stockitem-testreport-list') | ||||
|  | ||||
|     @classmethod | ||||
|     def getSubdir(cls): | ||||
|         """Return the subdirectory where TestReport templates are located""" | ||||
|         return 'test' | ||||
|  | ||||
|     filters = models.CharField( | ||||
| @@ -321,10 +290,7 @@ class TestReport(ReportTemplateBase): | ||||
|     ) | ||||
|  | ||||
|     def matches_stock_item(self, item): | ||||
|         """ | ||||
|         Test if this report template matches a given StockItem objects | ||||
|         """ | ||||
|  | ||||
|         """Test if this report template matches a given StockItem objects.""" | ||||
|         try: | ||||
|             filters = validateFilterString(self.filters) | ||||
|             items = stock.models.StockItem.objects.filter(**filters) | ||||
| @@ -337,7 +303,7 @@ class TestReport(ReportTemplateBase): | ||||
|         return items.exists() | ||||
|  | ||||
|     def get_context_data(self, request): | ||||
|  | ||||
|         """Return custom context data for the TestReport template""" | ||||
|         stock_item = self.object_to_print | ||||
|  | ||||
|         return { | ||||
| @@ -352,16 +318,16 @@ class TestReport(ReportTemplateBase): | ||||
|  | ||||
|  | ||||
| class BuildReport(ReportTemplateBase): | ||||
|     """ | ||||
|     Build order / work order report | ||||
|     """ | ||||
|     """Build order / work order report.""" | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_api_url(): | ||||
|         """Return the API URL associated with the BuildReport model""" | ||||
|         return reverse('api-build-report-list') | ||||
|  | ||||
|     @classmethod | ||||
|     def getSubdir(cls): | ||||
|         """Return the subdirectory where BuildReport templates are located""" | ||||
|         return 'build' | ||||
|  | ||||
|     filters = models.CharField( | ||||
| @@ -375,10 +341,7 @@ class BuildReport(ReportTemplateBase): | ||||
|     ) | ||||
|  | ||||
|     def get_context_data(self, request): | ||||
|         """ | ||||
|         Custom context data for the build report | ||||
|         """ | ||||
|  | ||||
|         """Custom context data for the build report.""" | ||||
|         my_build = self.object_to_print | ||||
|  | ||||
|         if type(my_build) != build.models.Build: | ||||
| @@ -395,16 +358,16 @@ class BuildReport(ReportTemplateBase): | ||||
|  | ||||
|  | ||||
| class BillOfMaterialsReport(ReportTemplateBase): | ||||
|     """ | ||||
|     Render a Bill of Materials against a Part object | ||||
|     """ | ||||
|     """Render a Bill of Materials against a Part object.""" | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_api_url(): | ||||
|         """Return the API URL associated with the BillOfMaterialsReport model""" | ||||
|         return reverse('api-bom-report-list') | ||||
|  | ||||
|     @classmethod | ||||
|     def getSubdir(cls): | ||||
|         """Retun the directory where BillOfMaterialsReport templates are located""" | ||||
|         return 'bom' | ||||
|  | ||||
|     filters = models.CharField( | ||||
| @@ -418,7 +381,7 @@ class BillOfMaterialsReport(ReportTemplateBase): | ||||
|     ) | ||||
|  | ||||
|     def get_context_data(self, request): | ||||
|  | ||||
|         """Return custom context data for the BillOfMaterialsReport template""" | ||||
|         part = self.object_to_print | ||||
|  | ||||
|         return { | ||||
| @@ -429,16 +392,16 @@ class BillOfMaterialsReport(ReportTemplateBase): | ||||
|  | ||||
|  | ||||
| class PurchaseOrderReport(ReportTemplateBase): | ||||
|     """ | ||||
|     Render a report against a PurchaseOrder object | ||||
|     """ | ||||
|     """Render a report against a PurchaseOrder object.""" | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_api_url(): | ||||
|         """Return the API URL associated with the PurchaseOrderReport model""" | ||||
|         return reverse('api-po-report-list') | ||||
|  | ||||
|     @classmethod | ||||
|     def getSubdir(cls): | ||||
|         """Return the directory where PurchaseOrderReport templates are stored""" | ||||
|         return 'purchaseorder' | ||||
|  | ||||
|     filters = models.CharField( | ||||
| @@ -452,7 +415,7 @@ class PurchaseOrderReport(ReportTemplateBase): | ||||
|     ) | ||||
|  | ||||
|     def get_context_data(self, request): | ||||
|  | ||||
|         """Return custom context data for the PurchaseOrderReport template""" | ||||
|         order = self.object_to_print | ||||
|  | ||||
|         return { | ||||
| @@ -468,16 +431,16 @@ class PurchaseOrderReport(ReportTemplateBase): | ||||
|  | ||||
|  | ||||
| class SalesOrderReport(ReportTemplateBase): | ||||
|     """ | ||||
|     Render a report against a SalesOrder object | ||||
|     """ | ||||
|     """Render a report against a SalesOrder object.""" | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_api_url(): | ||||
|         """Return the API URL associated with the SalesOrderReport model""" | ||||
|         return reverse('api-so-report-list') | ||||
|  | ||||
|     @classmethod | ||||
|     def getSubdir(cls): | ||||
|         """Retun the subdirectory where SalesOrderReport templates are located""" | ||||
|         return 'salesorder' | ||||
|  | ||||
|     filters = models.CharField( | ||||
| @@ -491,7 +454,7 @@ class SalesOrderReport(ReportTemplateBase): | ||||
|     ) | ||||
|  | ||||
|     def get_context_data(self, request): | ||||
|  | ||||
|         """Return custom context data for a SalesOrderReport template""" | ||||
|         order = self.object_to_print | ||||
|  | ||||
|         return { | ||||
| @@ -507,6 +470,7 @@ class SalesOrderReport(ReportTemplateBase): | ||||
|  | ||||
|  | ||||
| def rename_snippet(instance, filename): | ||||
|     """Function to rename a report snippet once uploaded""" | ||||
|  | ||||
|     filename = os.path.basename(filename) | ||||
|  | ||||
| @@ -530,9 +494,7 @@ def rename_snippet(instance, filename): | ||||
|  | ||||
|  | ||||
| class ReportSnippet(models.Model): | ||||
|     """ | ||||
|     Report template 'snippet' which can be used to make templates | ||||
|     that can then be included in other reports. | ||||
|     """Report template 'snippet' which can be used to make templates that can then be included in other reports. | ||||
|  | ||||
|     Useful for 'common' template actions, sub-templates, etc | ||||
|     """ | ||||
| @@ -548,6 +510,7 @@ class ReportSnippet(models.Model): | ||||
|  | ||||
|  | ||||
| def rename_asset(instance, filename): | ||||
|     """Function to rename an asset file when uploaded""" | ||||
|  | ||||
|     filename = os.path.basename(filename) | ||||
|  | ||||
| @@ -567,14 +530,15 @@ def rename_asset(instance, filename): | ||||
|  | ||||
|  | ||||
| class ReportAsset(models.Model): | ||||
|     """ | ||||
|     Asset file for use in report templates. | ||||
|     """Asset file for use in report templates. | ||||
|  | ||||
|     For example, an image to use in a header file. | ||||
|     Uploaded asset files appear in MEDIA_ROOT/report/assets, | ||||
|     and can be loaded in a template using the {% report_asset <filename> %} tag. | ||||
|     """ | ||||
|  | ||||
|     def __str__(self): | ||||
|         """String representation of a ReportAsset instance""" | ||||
|         return os.path.basename(self.asset.name) | ||||
|  | ||||
|     asset = models.FileField( | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| """API serializers for the reporting models""" | ||||
|  | ||||
| from InvenTree.serializers import (InvenTreeAttachmentSerializerField, | ||||
|                                    InvenTreeModelSerializer) | ||||
| @@ -7,10 +8,13 @@ from .models import (BillOfMaterialsReport, BuildReport, PurchaseOrderReport, | ||||
|  | ||||
|  | ||||
| class TestReportSerializer(InvenTreeModelSerializer): | ||||
|     """Serializer class for the TestReport model""" | ||||
|  | ||||
|     template = InvenTreeAttachmentSerializerField(required=True) | ||||
|  | ||||
|     class Meta: | ||||
|         """Metaclass options.""" | ||||
|  | ||||
|         model = TestReport | ||||
|         fields = [ | ||||
|             'pk', | ||||
| @@ -23,10 +27,13 @@ class TestReportSerializer(InvenTreeModelSerializer): | ||||
|  | ||||
|  | ||||
| class BuildReportSerializer(InvenTreeModelSerializer): | ||||
|     """Serializer class for the BuildReport model""" | ||||
|  | ||||
|     template = InvenTreeAttachmentSerializerField(required=True) | ||||
|  | ||||
|     class Meta: | ||||
|         """Metaclass options.""" | ||||
|  | ||||
|         model = BuildReport | ||||
|         fields = [ | ||||
|             'pk', | ||||
| @@ -39,10 +46,12 @@ class BuildReportSerializer(InvenTreeModelSerializer): | ||||
|  | ||||
|  | ||||
| class BOMReportSerializer(InvenTreeModelSerializer): | ||||
|  | ||||
|     """Serializer class for the BillOfMaterialsReport model""" | ||||
|     template = InvenTreeAttachmentSerializerField(required=True) | ||||
|  | ||||
|     class Meta: | ||||
|         """Metaclass options.""" | ||||
|  | ||||
|         model = BillOfMaterialsReport | ||||
|         fields = [ | ||||
|             'pk', | ||||
| @@ -55,10 +64,12 @@ class BOMReportSerializer(InvenTreeModelSerializer): | ||||
|  | ||||
|  | ||||
| class PurchaseOrderReportSerializer(InvenTreeModelSerializer): | ||||
|  | ||||
|     """Serializer class for the PurchaseOrdeReport model""" | ||||
|     template = InvenTreeAttachmentSerializerField(required=True) | ||||
|  | ||||
|     class Meta: | ||||
|         """Metaclass options.""" | ||||
|  | ||||
|         model = PurchaseOrderReport | ||||
|         fields = [ | ||||
|             'pk', | ||||
| @@ -71,10 +82,12 @@ class PurchaseOrderReportSerializer(InvenTreeModelSerializer): | ||||
|  | ||||
|  | ||||
| class SalesOrderReportSerializer(InvenTreeModelSerializer): | ||||
|  | ||||
|     """Serializer class for the SalesOrderReport model""" | ||||
|     template = InvenTreeAttachmentSerializerField(required=True) | ||||
|  | ||||
|     class Meta: | ||||
|         """Metaclass options.""" | ||||
|  | ||||
|         model = SalesOrderReport | ||||
|         fields = [ | ||||
|             'pk', | ||||
|   | ||||
| @@ -1,6 +1,4 @@ | ||||
| """ | ||||
| Template tags for rendering various barcodes | ||||
| """ | ||||
| """Template tags for rendering various barcodes.""" | ||||
|  | ||||
| import base64 | ||||
| from io import BytesIO | ||||
| @@ -14,12 +12,10 @@ register = template.Library() | ||||
|  | ||||
|  | ||||
| def image_data(img, fmt='PNG'): | ||||
|     """ | ||||
|     Convert an image into HTML renderable data | ||||
|     """Convert an image into HTML renderable data. | ||||
|  | ||||
|     Returns a string ``data:image/FMT;base64,xxxxxxxxx`` which can be rendered to an <img> tag | ||||
|     """ | ||||
|  | ||||
|     buffered = BytesIO() | ||||
|     img.save(buffered, format=fmt) | ||||
|  | ||||
| @@ -30,8 +26,7 @@ def image_data(img, fmt='PNG'): | ||||
|  | ||||
| @register.simple_tag() | ||||
| def qrcode(data, **kwargs): | ||||
|     """ | ||||
|     Return a byte-encoded QR code image | ||||
|     """Return a byte-encoded QR code image. | ||||
|  | ||||
|     Optional kwargs | ||||
|     --------------- | ||||
| @@ -39,7 +34,6 @@ def qrcode(data, **kwargs): | ||||
|     fill_color: Fill color (default = black) | ||||
|     back_color: Background color (default = white) | ||||
|     """ | ||||
|  | ||||
|     # Construct "default" values | ||||
|     params = dict( | ||||
|         box_size=20, | ||||
| @@ -63,10 +57,7 @@ def qrcode(data, **kwargs): | ||||
|  | ||||
| @register.simple_tag() | ||||
| def barcode(data, barcode_class='code128', **kwargs): | ||||
|     """ | ||||
|     Render a barcode | ||||
|     """ | ||||
|  | ||||
|     """Render a barcode.""" | ||||
|     constructor = python_barcode.get_barcode_class(barcode_class) | ||||
|  | ||||
|     data = str(data).zfill(constructor.digits) | ||||
|   | ||||
| @@ -1,6 +1,4 @@ | ||||
| """ | ||||
| Custom template tags for report generation | ||||
| """ | ||||
| """Custom template tags for report generation.""" | ||||
|  | ||||
| import os | ||||
|  | ||||
| @@ -19,10 +17,7 @@ register = template.Library() | ||||
|  | ||||
| @register.simple_tag() | ||||
| def asset(filename): | ||||
|     """ | ||||
|     Return fully-qualified path for an upload report asset file. | ||||
|     """ | ||||
|  | ||||
|     """Return fully-qualified path for an upload report asset file.""" | ||||
|     # If in debug mode, return URL to the image, not a local file | ||||
|     debug_mode = InvenTreeSetting.get_setting('REPORT_DEBUG_MODE') | ||||
|  | ||||
| @@ -38,10 +33,7 @@ def asset(filename): | ||||
|  | ||||
| @register.simple_tag() | ||||
| def part_image(part): | ||||
|     """ | ||||
|     Return a fully-qualified path for a part image | ||||
|     """ | ||||
|  | ||||
|     """Return a fully-qualified path for a part image.""" | ||||
|     # If in debug mode, return URL to the image, not a local file | ||||
|     debug_mode = InvenTreeSetting.get_setting('REPORT_DEBUG_MODE') | ||||
|  | ||||
| @@ -75,10 +67,7 @@ def part_image(part): | ||||
|  | ||||
| @register.simple_tag() | ||||
| def company_image(company): | ||||
|     """ | ||||
|     Return a fully-qualified path for a company image | ||||
|     """ | ||||
|  | ||||
|     """Return a fully-qualified path for a company image.""" | ||||
|     # If in debug mode, return the URL to the image, not a local file | ||||
|     debug_mode = InvenTreeSetting.get_setting('REPORT_DEBUG_MODE') | ||||
|  | ||||
| @@ -108,15 +97,13 @@ def company_image(company): | ||||
|  | ||||
| @register.simple_tag() | ||||
| def internal_link(link, text): | ||||
|     """ | ||||
|     Make a <a></a> href which points to an InvenTree URL. | ||||
|     """Make a <a></a> href which points to an InvenTree URL. | ||||
|  | ||||
|     Important Note: This only works if the INVENTREE_BASE_URL parameter is set! | ||||
|  | ||||
|     If the INVENTREE_BASE_URL parameter is not configured, | ||||
|     the text will be returned (unlinked) | ||||
|     """ | ||||
|  | ||||
|     text = str(text) | ||||
|  | ||||
|     url = InvenTree.helpers.construct_absolute_url(link) | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| """Unit testing for the various report models""" | ||||
|  | ||||
| import os | ||||
| import shutil | ||||
| @@ -14,7 +15,7 @@ from stock.models import StockItem | ||||
|  | ||||
|  | ||||
| class ReportTest(InvenTreeAPITestCase): | ||||
|  | ||||
|     """Base class for unit testing reporting models""" | ||||
|     fixtures = [ | ||||
|         'category', | ||||
|         'part', | ||||
| @@ -32,14 +33,8 @@ class ReportTest(InvenTreeAPITestCase): | ||||
|     detail_url = None | ||||
|     print_url = None | ||||
|  | ||||
|     def setUp(self): | ||||
|         super().setUp() | ||||
|  | ||||
|     def copyReportTemplate(self, filename, description): | ||||
|         """ | ||||
|         Copy the provided report template into the required media directory | ||||
|         """ | ||||
|  | ||||
|         """Copy the provided report template into the required media directory.""" | ||||
|         src_dir = os.path.join( | ||||
|             os.path.dirname(os.path.realpath(__file__)), | ||||
|             'templates', | ||||
| @@ -81,10 +76,7 @@ class ReportTest(InvenTreeAPITestCase): | ||||
|         ) | ||||
|  | ||||
|     def test_list_endpoint(self): | ||||
|         """ | ||||
|         Test that the LIST endpoint works for each report | ||||
|         """ | ||||
|  | ||||
|         """Test that the LIST endpoint works for each report.""" | ||||
|         if not self.list_url: | ||||
|             return | ||||
|  | ||||
| @@ -121,7 +113,7 @@ class ReportTest(InvenTreeAPITestCase): | ||||
|  | ||||
|  | ||||
| class TestReportTest(ReportTest): | ||||
|  | ||||
|     """Unit testing class for the stock item TestReport model""" | ||||
|     model = report_models.TestReport | ||||
|  | ||||
|     list_url = 'api-stockitem-testreport-list' | ||||
| @@ -129,16 +121,13 @@ class TestReportTest(ReportTest): | ||||
|     print_url = 'api-stockitem-testreport-print' | ||||
|  | ||||
|     def setUp(self): | ||||
|  | ||||
|         """Setup function for the stock item TestReport""" | ||||
|         self.copyReportTemplate('inventree_test_report.html', 'stock item test report') | ||||
|  | ||||
|         return super().setUp() | ||||
|  | ||||
|     def test_print(self): | ||||
|         """ | ||||
|         Printing tests for the TestReport | ||||
|         """ | ||||
|  | ||||
|         """Printing tests for the TestReport.""" | ||||
|         report = self.model.objects.first() | ||||
|  | ||||
|         url = reverse(self.print_url, kwargs={'pk': report.pk}) | ||||
| @@ -163,7 +152,7 @@ class TestReportTest(ReportTest): | ||||
|  | ||||
|  | ||||
| class BuildReportTest(ReportTest): | ||||
|  | ||||
|     """Unit test class for the BuildReport model""" | ||||
|     model = report_models.BuildReport | ||||
|  | ||||
|     list_url = 'api-build-report-list' | ||||
| @@ -171,16 +160,13 @@ class BuildReportTest(ReportTest): | ||||
|     print_url = 'api-build-report-print' | ||||
|  | ||||
|     def setUp(self): | ||||
|  | ||||
|         """Setup unit testing functions""" | ||||
|         self.copyReportTemplate('inventree_build_order.html', 'build order template') | ||||
|  | ||||
|         return super().setUp() | ||||
|  | ||||
|     def test_print(self): | ||||
|         """ | ||||
|         Printing tests for the BuildReport | ||||
|         """ | ||||
|  | ||||
|         """Printing tests for the BuildReport.""" | ||||
|         report = self.model.objects.first() | ||||
|  | ||||
|         url = reverse(self.print_url, kwargs={'pk': report.pk}) | ||||
| @@ -216,7 +202,7 @@ class BuildReportTest(ReportTest): | ||||
|  | ||||
|  | ||||
| class BOMReportTest(ReportTest): | ||||
|  | ||||
|     """Unit test class fot the BillOfMaterialsReport model""" | ||||
|     model = report_models.BillOfMaterialsReport | ||||
|  | ||||
|     list_url = 'api-bom-report-list' | ||||
| @@ -225,7 +211,7 @@ class BOMReportTest(ReportTest): | ||||
|  | ||||
|  | ||||
| class PurchaseOrderReportTest(ReportTest): | ||||
|  | ||||
|     """Unit test class fort he PurchaseOrderReport model""" | ||||
|     model = report_models.PurchaseOrderReport | ||||
|  | ||||
|     list_url = 'api-po-report-list' | ||||
| @@ -234,7 +220,7 @@ class PurchaseOrderReportTest(ReportTest): | ||||
|  | ||||
|  | ||||
| class SalesOrderReportTest(ReportTest): | ||||
|  | ||||
|     """Unit test class for the SalesOrderReport model""" | ||||
|     model = report_models.SalesOrderReport | ||||
|  | ||||
|     list_url = 'api-so-report-list' | ||||
|   | ||||
		Reference in New Issue
	
	Block a user