mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-04 07:05:41 +00:00 
			
		
		
		
	Merge pull request #2745 from matmair/coverage-changes
Coverage increase
This commit is contained in:
		@@ -28,7 +28,7 @@ class InvenTreeConfig(AppConfig):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            self.start_background_tasks()
 | 
					            self.start_background_tasks()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if not isInTestMode():
 | 
					            if not isInTestMode():  # pragma: no cover
 | 
				
			||||||
                self.update_exchange_rates()
 | 
					                self.update_exchange_rates()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if canAppAccessDatabase() or settings.TESTING_ENV:
 | 
					        if canAppAccessDatabase() or settings.TESTING_ENV:
 | 
				
			||||||
@@ -98,7 +98,7 @@ class InvenTreeConfig(AppConfig):
 | 
				
			|||||||
            schedule_type=Schedule.DAILY,
 | 
					            schedule_type=Schedule.DAILY,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update_exchange_rates(self):
 | 
					    def update_exchange_rates(self):  # pragma: no cover
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Update exchange rates each time the server is started, *if*:
 | 
					        Update exchange rates each time the server is started, *if*:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,14 +3,14 @@ Pull rendered copies of the templated
 | 
				
			|||||||
only used for testing the js files! - This file is omited from coverage
 | 
					only used for testing the js files! - This file is omited from coverage
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.test import TestCase
 | 
					from django.test import TestCase  # pragma: no cover
 | 
				
			||||||
from django.contrib.auth import get_user_model
 | 
					from django.contrib.auth import get_user_model  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import os
 | 
					import os  # pragma: no cover
 | 
				
			||||||
import pathlib
 | 
					import pathlib  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RenderJavascriptFiles(TestCase):
 | 
					class RenderJavascriptFiles(TestCase):  # pragma: no cover
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    A unit test to "render" javascript files.
 | 
					    A unit test to "render" javascript files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -95,7 +95,7 @@ def user_roles(request):
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if user.is_superuser:
 | 
					    if user.is_superuser:
 | 
				
			||||||
        for ruleset in RuleSet.RULESET_MODELS.keys():
 | 
					        for ruleset in RuleSet.RULESET_MODELS.keys():  # pragma: no cover
 | 
				
			||||||
            roles[ruleset] = {
 | 
					            roles[ruleset] = {
 | 
				
			||||||
                'view': True,
 | 
					                'view': True,
 | 
				
			||||||
                'add': True,
 | 
					                'add': True,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -68,7 +68,7 @@ def offload_task(taskname, *args, force_sync=False, **kwargs):
 | 
				
			|||||||
        import importlib
 | 
					        import importlib
 | 
				
			||||||
        from InvenTree.status import is_worker_running
 | 
					        from InvenTree.status import is_worker_running
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if is_worker_running() and not force_sync:
 | 
					        if is_worker_running() and not force_sync:  # pragma: no cover
 | 
				
			||||||
            # Running as asynchronous task
 | 
					            # Running as asynchronous task
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                task = AsyncTask(taskname, *args, **kwargs)
 | 
					                task = AsyncTask(taskname, *args, **kwargs)
 | 
				
			||||||
@@ -94,13 +94,13 @@ def offload_task(taskname, *args, force_sync=False, **kwargs):
 | 
				
			|||||||
            # Retrieve function
 | 
					            # Retrieve function
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                _func = getattr(_mod, func)
 | 
					                _func = getattr(_mod, func)
 | 
				
			||||||
            except AttributeError:
 | 
					            except AttributeError:  # pragma: no cover
 | 
				
			||||||
                # getattr does not work for local import
 | 
					                # getattr does not work for local import
 | 
				
			||||||
                _func = None
 | 
					                _func = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                if not _func:
 | 
					                if not _func:
 | 
				
			||||||
                    _func = eval(func)
 | 
					                    _func = eval(func)  # pragma: no cover
 | 
				
			||||||
            except NameError:
 | 
					            except NameError:
 | 
				
			||||||
                logger.warning(f"WARNING: '{taskname}' not started - No function named '{func}'")
 | 
					                logger.warning(f"WARNING: '{taskname}' not started - No function named '{func}'")
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
@@ -248,7 +248,7 @@ def update_exchange_rates():
 | 
				
			|||||||
        # Apps not yet loaded!
 | 
					        # Apps not yet loaded!
 | 
				
			||||||
        logger.info("Could not perform 'update_exchange_rates' - App registry not ready")
 | 
					        logger.info("Could not perform 'update_exchange_rates' - App registry not ready")
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    except:
 | 
					    except:  # pragma: no cover
 | 
				
			||||||
        # Other error?
 | 
					        # Other error?
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -257,7 +257,7 @@ def update_exchange_rates():
 | 
				
			|||||||
        backend = ExchangeBackend.objects.get(name='InvenTreeExchange')
 | 
					        backend = ExchangeBackend.objects.get(name='InvenTreeExchange')
 | 
				
			||||||
    except ExchangeBackend.DoesNotExist:
 | 
					    except ExchangeBackend.DoesNotExist:
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
    except:
 | 
					    except:  # pragma: no cover
 | 
				
			||||||
        # Some other error
 | 
					        # Some other error
 | 
				
			||||||
        logger.warning("update_exchange_rates: Database not ready")
 | 
					        logger.warning("update_exchange_rates: Database not ready")
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
@@ -274,7 +274,7 @@ def update_exchange_rates():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # Remove any exchange rates which are not in the provided currencies
 | 
					        # Remove any exchange rates which are not in the provided currencies
 | 
				
			||||||
        Rate.objects.filter(backend="InvenTreeExchange").exclude(currency__in=currency_codes()).delete()
 | 
					        Rate.objects.filter(backend="InvenTreeExchange").exclude(currency__in=currency_codes()).delete()
 | 
				
			||||||
    except Exception as e:
 | 
					    except Exception as e:  # pragma: no cover
 | 
				
			||||||
        logger.error(f"Error updating exchange rates: {e}")
 | 
					        logger.error(f"Error updating exchange rates: {e}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ from . import helpers
 | 
				
			|||||||
from . import version
 | 
					from . import version
 | 
				
			||||||
from . import status
 | 
					from . import status
 | 
				
			||||||
from . import ready
 | 
					from . import ready
 | 
				
			||||||
 | 
					from . import config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from decimal import Decimal
 | 
					from decimal import Decimal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -24,6 +25,7 @@ import InvenTree.tasks
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from stock.models import StockLocation
 | 
					from stock.models import StockLocation
 | 
				
			||||||
from common.settings import currency_codes
 | 
					from common.settings import currency_codes
 | 
				
			||||||
 | 
					from common.models import InvenTreeSetting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ValidatorTest(TestCase):
 | 
					class ValidatorTest(TestCase):
 | 
				
			||||||
@@ -453,3 +455,55 @@ class TestSettings(TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # make sure to clean up
 | 
					        # make sure to clean up
 | 
				
			||||||
        settings.TESTING_ENV = False
 | 
					        settings.TESTING_ENV = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_helpers_cfg_file(self):
 | 
				
			||||||
 | 
					        # normal run - not configured
 | 
				
			||||||
 | 
					        self.assertIn('InvenTree/InvenTree/config.yaml', config.get_config_file())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # with env set
 | 
				
			||||||
 | 
					        with self.env:
 | 
				
			||||||
 | 
					            self.env.set('INVENTREE_CONFIG_FILE', 'my_special_conf.yaml')
 | 
				
			||||||
 | 
					            self.assertIn('InvenTree/InvenTree/my_special_conf.yaml', config.get_config_file())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_helpers_plugin_file(self):
 | 
				
			||||||
 | 
					        # normal run - not configured
 | 
				
			||||||
 | 
					        self.assertIn('InvenTree/InvenTree/plugins.txt', config.get_plugin_file())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # with env set
 | 
				
			||||||
 | 
					        with self.env:
 | 
				
			||||||
 | 
					            self.env.set('INVENTREE_PLUGIN_FILE', 'my_special_plugins.txt')
 | 
				
			||||||
 | 
					            self.assertIn('my_special_plugins.txt', config.get_plugin_file())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_helpers_setting(self):
 | 
				
			||||||
 | 
					        TEST_ENV_NAME = '123TEST'
 | 
				
			||||||
 | 
					        # check that default gets returned if not present
 | 
				
			||||||
 | 
					        self.assertEqual(config.get_setting(TEST_ENV_NAME, None, '123!'), '123!')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # with env set
 | 
				
			||||||
 | 
					        with self.env:
 | 
				
			||||||
 | 
					            self.env.set(TEST_ENV_NAME, '321')
 | 
				
			||||||
 | 
					            self.assertEqual(config.get_setting(TEST_ENV_NAME, None), '321')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestInstanceName(TestCase):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Unit tests for instance name
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        # Create a user for auth
 | 
				
			||||||
 | 
					        user = get_user_model()
 | 
				
			||||||
 | 
					        self.user = user.objects.create_superuser('testuser', 'test@testing.com', 'password')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.client.login(username='testuser', password='password')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_instance_name(self):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # default setting
 | 
				
			||||||
 | 
					        self.assertEqual(version.inventreeInstanceTitle(), 'InvenTree')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set up required setting
 | 
				
			||||||
 | 
					        InvenTreeSetting.set_setting("INVENTREE_INSTANCE_TITLE", True, self.user)
 | 
				
			||||||
 | 
					        InvenTreeSetting.set_setting("INVENTREE_INSTANCE", "Testing title", self.user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(version.inventreeInstanceTitle(), 'Testing title')
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -171,7 +171,7 @@ def inventreeDocsVersion():
 | 
				
			|||||||
    if isInvenTreeDevelopmentVersion():
 | 
					    if isInvenTreeDevelopmentVersion():
 | 
				
			||||||
        return "latest"
 | 
					        return "latest"
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        return INVENTREE_SW_VERSION
 | 
					        return INVENTREE_SW_VERSION  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def isInvenTreeUpToDate():
 | 
					def isInvenTreeUpToDate():
 | 
				
			||||||
@@ -189,10 +189,10 @@ def isInvenTreeUpToDate():
 | 
				
			|||||||
        return True
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Extract "tuple" version (Python can directly compare version tuples)
 | 
					    # Extract "tuple" version (Python can directly compare version tuples)
 | 
				
			||||||
    latest_version = inventreeVersionTuple(latest)
 | 
					    latest_version = inventreeVersionTuple(latest)  # pragma: no cover
 | 
				
			||||||
    inventree_version = inventreeVersionTuple()
 | 
					    inventree_version = inventreeVersionTuple()  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return inventree_version >= latest_version
 | 
					    return inventree_version >= latest_version  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def inventreeApiVersion():
 | 
					def inventreeApiVersion():
 | 
				
			||||||
@@ -209,7 +209,7 @@ def inventreeCommitHash():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        return str(subprocess.check_output('git rev-parse --short HEAD'.split()), 'utf-8').strip()
 | 
					        return str(subprocess.check_output('git rev-parse --short HEAD'.split()), 'utf-8').strip()
 | 
				
			||||||
    except:
 | 
					    except:  # pragma: no cover
 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -219,5 +219,5 @@ def inventreeCommitDate():
 | 
				
			|||||||
    try:
 | 
					    try:
 | 
				
			||||||
        d = str(subprocess.check_output('git show -s --format=%ci'.split()), 'utf-8').strip()
 | 
					        d = str(subprocess.check_output('git show -s --format=%ci'.split()), 'utf-8').strip()
 | 
				
			||||||
        return d.split(' ')[0]
 | 
					        return d.split(' ')[0]
 | 
				
			||||||
    except:
 | 
					    except:  # pragma: no cover
 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,10 +7,10 @@ For more information on this file, see
 | 
				
			|||||||
https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
 | 
					https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import os
 | 
					import os  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.core.wsgi import get_wsgi_application
 | 
					from django.core.wsgi import get_wsgi_application  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "InvenTree.settings")
 | 
					os.environ.setdefault("DJANGO_SETTINGS_MODULE", "InvenTree.settings")  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
application = get_wsgi_application()
 | 
					application = get_wsgi_application()  # pragma: no cover
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,7 +47,7 @@ class InvenTreeBarcodePlugin(BarcodePlugin):
 | 
				
			|||||||
            except json.JSONDecodeError:
 | 
					            except json.JSONDecodeError:
 | 
				
			||||||
                return False
 | 
					                return False
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return False
 | 
					            return False  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # If any of the following keys are in the JSON data,
 | 
					        # If any of the following keys are in the JSON data,
 | 
				
			||||||
        # let's go ahead and assume that the code is a valid InvenTree one...
 | 
					        # let's go ahead and assume that the code is a valid InvenTree one...
 | 
				
			||||||
@@ -70,10 +70,10 @@ class InvenTreeBarcodePlugin(BarcodePlugin):
 | 
				
			|||||||
                # Initially try casting to an integer
 | 
					                # Initially try casting to an integer
 | 
				
			||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    pk = int(data)
 | 
					                    pk = int(data)
 | 
				
			||||||
                except (TypeError, ValueError):
 | 
					                except (TypeError, ValueError):  # pragma: no cover
 | 
				
			||||||
                    pk = None
 | 
					                    pk = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if pk is None:
 | 
					                if pk is None:  # pragma: no cover
 | 
				
			||||||
                    try:
 | 
					                    try:
 | 
				
			||||||
                        pk = self.data[k]['id']
 | 
					                        pk = self.data[k]['id']
 | 
				
			||||||
                    except (AttributeError, KeyError):
 | 
					                    except (AttributeError, KeyError):
 | 
				
			||||||
@@ -82,7 +82,7 @@ class InvenTreeBarcodePlugin(BarcodePlugin):
 | 
				
			|||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    item = StockItem.objects.get(pk=pk)
 | 
					                    item = StockItem.objects.get(pk=pk)
 | 
				
			||||||
                    return item
 | 
					                    return item
 | 
				
			||||||
                except (ValueError, StockItem.DoesNotExist):
 | 
					                except (ValueError, StockItem.DoesNotExist):  # pragma: no cover
 | 
				
			||||||
                    raise ValidationError({k, "Stock item does not exist"})
 | 
					                    raise ValidationError({k, "Stock item does not exist"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
@@ -97,10 +97,10 @@ class InvenTreeBarcodePlugin(BarcodePlugin):
 | 
				
			|||||||
                # First try simple integer lookup
 | 
					                # First try simple integer lookup
 | 
				
			||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    pk = int(self.data[k])
 | 
					                    pk = int(self.data[k])
 | 
				
			||||||
                except (TypeError, ValueError):
 | 
					                except (TypeError, ValueError):  # pragma: no cover
 | 
				
			||||||
                    pk = None
 | 
					                    pk = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if pk is None:
 | 
					                if pk is None:  # pragma: no cover
 | 
				
			||||||
                    # Lookup by 'id' field
 | 
					                    # Lookup by 'id' field
 | 
				
			||||||
                    try:
 | 
					                    try:
 | 
				
			||||||
                        pk = self.data[k]['id']
 | 
					                        pk = self.data[k]['id']
 | 
				
			||||||
@@ -110,7 +110,7 @@ class InvenTreeBarcodePlugin(BarcodePlugin):
 | 
				
			|||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    loc = StockLocation.objects.get(pk=pk)
 | 
					                    loc = StockLocation.objects.get(pk=pk)
 | 
				
			||||||
                    return loc
 | 
					                    return loc
 | 
				
			||||||
                except (ValueError, StockLocation.DoesNotExist):
 | 
					                except (ValueError, StockLocation.DoesNotExist):  # pragma: no cover
 | 
				
			||||||
                    raise ValidationError({k, "Stock location does not exist"})
 | 
					                    raise ValidationError({k, "Stock location does not exist"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
@@ -125,10 +125,10 @@ class InvenTreeBarcodePlugin(BarcodePlugin):
 | 
				
			|||||||
                # Try integer lookup first
 | 
					                # Try integer lookup first
 | 
				
			||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    pk = int(self.data[k])
 | 
					                    pk = int(self.data[k])
 | 
				
			||||||
                except (TypeError, ValueError):
 | 
					                except (TypeError, ValueError):  # pragma: no cover
 | 
				
			||||||
                    pk = None
 | 
					                    pk = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if pk is None:
 | 
					                if pk is None:  # pragma: no cover
 | 
				
			||||||
                    try:
 | 
					                    try:
 | 
				
			||||||
                        pk = self.data[k]['id']
 | 
					                        pk = self.data[k]['id']
 | 
				
			||||||
                    except (AttributeError, KeyError):
 | 
					                    except (AttributeError, KeyError):
 | 
				
			||||||
@@ -137,7 +137,7 @@ class InvenTreeBarcodePlugin(BarcodePlugin):
 | 
				
			|||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    part = Part.objects.get(pk=pk)
 | 
					                    part = Part.objects.get(pk=pk)
 | 
				
			||||||
                    return part
 | 
					                    return part
 | 
				
			||||||
                except (ValueError, Part.DoesNotExist):
 | 
					                except (ValueError, Part.DoesNotExist):  # pragma: no cover
 | 
				
			||||||
                    raise ValidationError({k, 'Part does not exist'})
 | 
					                    raise ValidationError({k, 'Part does not exist'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,8 +38,18 @@ class BarcodeAPITest(APITestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def test_invalid(self):
 | 
					    def test_invalid(self):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # test scan url
 | 
				
			||||||
        response = self.client.post(self.scan_url, format='json', data={})
 | 
					        response = self.client.post(self.scan_url, format='json', data={})
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # test wrong assign urls
 | 
				
			||||||
 | 
					        response = self.client.post(self.assign_url, format='json', data={})
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.post(self.assign_url, format='json', data={'barcode': '123'})
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.post(self.assign_url, format='json', data={'barcode': '123', 'stockitem': '123'})
 | 
				
			||||||
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
 | 
					        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_empty(self):
 | 
					    def test_empty(self):
 | 
				
			||||||
@@ -204,3 +214,57 @@ class BarcodeAPITest(APITestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.assertIn('error', data)
 | 
					        self.assertIn('error', data)
 | 
				
			||||||
        self.assertNotIn('success', data)
 | 
					        self.assertNotIn('success', data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestInvenTreeBarcode(APITestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fixtures = [
 | 
				
			||||||
 | 
					        'category',
 | 
				
			||||||
 | 
					        'part',
 | 
				
			||||||
 | 
					        'location',
 | 
				
			||||||
 | 
					        'stock'
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        # Create a user for auth
 | 
				
			||||||
 | 
					        user = get_user_model()
 | 
				
			||||||
 | 
					        user.objects.create_user('testuser', 'test@testing.com', 'password')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.client.login(username='testuser', password='password')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_errors(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test all possible error cases for assigment action
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def test_assert_error(barcode_data):
 | 
				
			||||||
 | 
					            response = self.client.post(
 | 
				
			||||||
 | 
					                reverse('api-barcode-link'), format='json',
 | 
				
			||||||
 | 
					                data={
 | 
				
			||||||
 | 
					                    'barcode': barcode_data,
 | 
				
			||||||
 | 
					                    'stockitem': 521
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
				
			||||||
 | 
					            self.assertIn('error', response.data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # test with already existing stock
 | 
				
			||||||
 | 
					        test_assert_error('{"stockitem": 521}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # test with already existing stock location
 | 
				
			||||||
 | 
					        test_assert_error('{"stocklocation": 7}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # test with already existing part location
 | 
				
			||||||
 | 
					        test_assert_error('{"part": 10004}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # test with hash
 | 
				
			||||||
 | 
					        test_assert_error('{"blbla": 10004}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_scan(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that a barcode can be scanned
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.post(reverse('api-barcode-scan'), format='json', data={'barcode': 'blbla=10004'})
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
				
			||||||
 | 
					        self.assertIn('success', response.data)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,7 @@ class SettingsAdmin(ImportExportModelAdmin):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    list_display = ('key', 'value')
 | 
					    list_display = ('key', 'value')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_readonly_fields(self, request, obj=None):
 | 
					    def get_readonly_fields(self, request, obj=None):  # pragma: no cover
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Prevent the 'key' field being edited once the setting is created
 | 
					        Prevent the 'key' field being edited once the setting is created
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@@ -27,7 +27,7 @@ class UserSettingsAdmin(ImportExportModelAdmin):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    list_display = ('key', 'value', 'user', )
 | 
					    list_display = ('key', 'value', 'user', )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_readonly_fields(self, request, obj=None):
 | 
					    def get_readonly_fields(self, request, obj=None):  # pragma: no cover
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Prevent the 'key' field being edited once the setting is created
 | 
					        Prevent the 'key' field being edited once the setting is created
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -570,7 +570,7 @@ class BaseInvenTreeSetting(models.Model):
 | 
				
			|||||||
        try:
 | 
					        try:
 | 
				
			||||||
            value = int(self.value)
 | 
					            value = int(self.value)
 | 
				
			||||||
        except (ValueError, TypeError):
 | 
					        except (ValueError, TypeError):
 | 
				
			||||||
            value = self.default_value()
 | 
					            value = self.default_value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return value
 | 
					        return value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,12 +18,12 @@ def currency_code_default():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        code = InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY')
 | 
					        code = InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY')
 | 
				
			||||||
    except ProgrammingError:
 | 
					    except ProgrammingError:  # pragma: no cover
 | 
				
			||||||
        # database is not initialized yet
 | 
					        # database is not initialized yet
 | 
				
			||||||
        code = ''
 | 
					        code = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if code not in CURRENCIES:
 | 
					    if code not in CURRENCIES:
 | 
				
			||||||
        code = 'USD'
 | 
					        code = 'USD'  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return code
 | 
					    return code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								InvenTree/common/test_tasks.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								InvenTree/common/test_tasks.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from common.models import NotificationEntry
 | 
				
			||||||
 | 
					from InvenTree.tasks import offload_task
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TaskTest(TestCase):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Tests for common tasks
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_delete(self):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check empty run
 | 
				
			||||||
 | 
					        self.assertEqual(NotificationEntry.objects.all().count(), 0)
 | 
				
			||||||
 | 
					        offload_task('common.tasks.delete_old_notifications',)
 | 
				
			||||||
@@ -6,7 +6,9 @@ from datetime import timedelta
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from django.test import TestCase, Client
 | 
					from django.test import TestCase, Client
 | 
				
			||||||
from django.contrib.auth import get_user_model
 | 
					from django.contrib.auth import get_user_model
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from InvenTree.api_tester import InvenTreeAPITestCase
 | 
				
			||||||
from .models import InvenTreeSetting, WebhookEndpoint, WebhookMessage, NotificationEntry
 | 
					from .models import InvenTreeSetting, WebhookEndpoint, WebhookMessage, NotificationEntry
 | 
				
			||||||
from .api import WebhookView
 | 
					from .api import WebhookView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -46,6 +48,67 @@ class SettingsTest(TestCase):
 | 
				
			|||||||
        # Check object lookup (case insensitive)
 | 
					        # Check object lookup (case insensitive)
 | 
				
			||||||
        self.assertEqual(InvenTreeSetting.get_setting_object('iNvEnTrEE_inSTanCE').pk, 1)
 | 
					        self.assertEqual(InvenTreeSetting.get_setting_object('iNvEnTrEE_inSTanCE').pk, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_settings_functions(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test settings functions and properties
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # define settings to check
 | 
				
			||||||
 | 
					        instance_ref = 'INVENTREE_INSTANCE'
 | 
				
			||||||
 | 
					        instance_obj = InvenTreeSetting.get_setting_object(instance_ref)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        stale_ref = 'STOCK_STALE_DAYS'
 | 
				
			||||||
 | 
					        stale_days = InvenTreeSetting.get_setting_object(stale_ref)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        report_size_obj = InvenTreeSetting.get_setting_object('REPORT_DEFAULT_PAGE_SIZE')
 | 
				
			||||||
 | 
					        report_test_obj = InvenTreeSetting.get_setting_object('REPORT_ENABLE_TEST_REPORT')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check settings base fields
 | 
				
			||||||
 | 
					        self.assertEqual(instance_obj.name, 'InvenTree Instance Name')
 | 
				
			||||||
 | 
					        self.assertEqual(instance_obj.get_setting_name(instance_ref), 'InvenTree Instance Name')
 | 
				
			||||||
 | 
					        self.assertEqual(instance_obj.description, 'String descriptor for the server instance')
 | 
				
			||||||
 | 
					        self.assertEqual(instance_obj.get_setting_description(instance_ref), 'String descriptor for the server instance')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check units
 | 
				
			||||||
 | 
					        self.assertEqual(instance_obj.units, '')
 | 
				
			||||||
 | 
					        self.assertEqual(instance_obj.get_setting_units(instance_ref), '')
 | 
				
			||||||
 | 
					        self.assertEqual(instance_obj.get_setting_units(stale_ref), 'days')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check as_choice
 | 
				
			||||||
 | 
					        self.assertEqual(instance_obj.as_choice(), 'My very first InvenTree Instance')
 | 
				
			||||||
 | 
					        self.assertEqual(report_size_obj.as_choice(), 'A4')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check is_choice
 | 
				
			||||||
 | 
					        self.assertEqual(instance_obj.is_choice(), False)
 | 
				
			||||||
 | 
					        self.assertEqual(report_size_obj.is_choice(), True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check setting_type
 | 
				
			||||||
 | 
					        self.assertEqual(instance_obj.setting_type(), 'string')
 | 
				
			||||||
 | 
					        self.assertEqual(report_test_obj.setting_type(), 'boolean')
 | 
				
			||||||
 | 
					        self.assertEqual(stale_days.setting_type(), 'integer')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check as_int
 | 
				
			||||||
 | 
					        self.assertEqual(stale_days.as_int(), 0)
 | 
				
			||||||
 | 
					        self.assertEqual(instance_obj.as_int(), 'InvenTree server')  # not an int -> return default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check as_bool
 | 
				
			||||||
 | 
					        self.assertEqual(report_test_obj.as_bool(), True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check to_native_value
 | 
				
			||||||
 | 
					        self.assertEqual(stale_days.to_native_value(), 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_allValues(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Make sure that the allValues functions returns correctly
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # define testing settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check a few keys
 | 
				
			||||||
 | 
					        result = InvenTreeSetting.allValues()
 | 
				
			||||||
 | 
					        self.assertIn('INVENTREE_INSTANCE', result)
 | 
				
			||||||
 | 
					        self.assertIn('PART_COPY_TESTS', result)
 | 
				
			||||||
 | 
					        self.assertIn('STOCK_OWNERSHIP_CONTROL', result)
 | 
				
			||||||
 | 
					        self.assertIn('SIGNUP_GROUP', result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_required_values(self):
 | 
					    def test_required_values(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        - Ensure that every global setting has a name.
 | 
					        - Ensure that every global setting has a name.
 | 
				
			||||||
@@ -93,6 +156,14 @@ class SettingsTest(TestCase):
 | 
				
			|||||||
                    raise ValueError(f'Non-boolean default value specified for {key}')  # pragma: no cover
 | 
					                    raise ValueError(f'Non-boolean default value specified for {key}')  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SettingsApiTest(InvenTreeAPITestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_settings_api(self):
 | 
				
			||||||
 | 
					        # test setting with choice
 | 
				
			||||||
 | 
					        url = reverse('api-user-setting-list')
 | 
				
			||||||
 | 
					        self.get(url, expected_code=200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class WebhookMessageTests(TestCase):
 | 
					class WebhookMessageTests(TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        self.endpoint_def = WebhookEndpoint.objects.create()
 | 
					        self.endpoint_def = WebhookEndpoint.objects.create()
 | 
				
			||||||
@@ -223,3 +294,26 @@ class NotificationTest(TestCase):
 | 
				
			|||||||
        self.assertFalse(NotificationEntry.check_recent('test.notification2', 1, delta))
 | 
					        self.assertFalse(NotificationEntry.check_recent('test.notification2', 1, delta))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertTrue(NotificationEntry.check_recent('test.notification', 1, delta))
 | 
					        self.assertTrue(NotificationEntry.check_recent('test.notification', 1, delta))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LoadingTest(TestCase):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Tests for the common config
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_restart_flag(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that the restart flag is reset on start
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        import common.models
 | 
				
			||||||
 | 
					        from plugin import registry
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set flag true
 | 
				
			||||||
 | 
					        common.models.InvenTreeSetting.set_setting('SERVER_RESTART_REQUIRED', False, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # reload the app
 | 
				
			||||||
 | 
					        registry.reload_plugins()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # now it should be false again
 | 
				
			||||||
 | 
					        self.assertFalse(common.models.InvenTreeSetting.get_setting('SERVER_RESTART_REQUIRED'))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,7 +53,7 @@ class InvenTreePluginBase():
 | 
				
			|||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return self.plugin_name()
 | 
					            return self.plugin_name()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def plugin_config(self, raise_error=False):
 | 
					    def plugin_config(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Return the PluginConfig object associated with this plugin
 | 
					        Return the PluginConfig object associated with this plugin
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@@ -65,12 +65,9 @@ class InvenTreePluginBase():
 | 
				
			|||||||
                key=self.plugin_slug(),
 | 
					                key=self.plugin_slug(),
 | 
				
			||||||
                name=self.plugin_name(),
 | 
					                name=self.plugin_name(),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        except (OperationalError, ProgrammingError) as error:
 | 
					        except (OperationalError, ProgrammingError):
 | 
				
			||||||
            cfg = None
 | 
					            cfg = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if raise_error:
 | 
					 | 
				
			||||||
                raise error
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return cfg
 | 
					        return cfg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_active(self):
 | 
					    def is_active(self):
 | 
				
			||||||
@@ -91,6 +88,6 @@ class InvenTreePlugin(InvenTreePluginBase):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    This is here for leagcy reasons and will be removed in the next major release
 | 
					    This is here for leagcy reasons and will be removed in the next major release
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):  # pragma: no cover
 | 
				
			||||||
        warnings.warn("Using the InvenTreePlugin is depreceated", DeprecationWarning)
 | 
					        warnings.warn("Using the InvenTreePlugin is depreceated", DeprecationWarning)
 | 
				
			||||||
        super().__init__()
 | 
					        super().__init__()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ from django.utils.text import slugify
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
    from importlib import metadata
 | 
					    from importlib import metadata
 | 
				
			||||||
except:
 | 
					except:  # pragma: no cover
 | 
				
			||||||
    import importlib_metadata as metadata
 | 
					    import importlib_metadata as metadata
 | 
				
			||||||
    # TODO remove when python minimum is 3.8
 | 
					    # TODO remove when python minimum is 3.8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -84,7 +84,7 @@ class PluginsRegistry:
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        if not settings.PLUGINS_ENABLED:
 | 
					        if not settings.PLUGINS_ENABLED:
 | 
				
			||||||
            # Plugins not enabled, do nothing
 | 
					            # Plugins not enabled, do nothing
 | 
				
			||||||
            return
 | 
					            return  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        logger.info('Start loading plugins')
 | 
					        logger.info('Start loading plugins')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -120,7 +120,7 @@ class PluginsRegistry:
 | 
				
			|||||||
                # We do not want to end in an endless loop
 | 
					                # We do not want to end in an endless loop
 | 
				
			||||||
                retry_counter -= 1
 | 
					                retry_counter -= 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if retry_counter <= 0:
 | 
					                if retry_counter <= 0:  # pragma: no cover
 | 
				
			||||||
                    if settings.PLUGIN_TESTING:
 | 
					                    if settings.PLUGIN_TESTING:
 | 
				
			||||||
                        print('[PLUGIN] Max retries, breaking loading')
 | 
					                        print('[PLUGIN] Max retries, breaking loading')
 | 
				
			||||||
                    # TODO error for server status
 | 
					                    # TODO error for server status
 | 
				
			||||||
@@ -143,14 +143,14 @@ class PluginsRegistry:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if not settings.PLUGINS_ENABLED:
 | 
					        if not settings.PLUGINS_ENABLED:
 | 
				
			||||||
            # Plugins not enabled, do nothing
 | 
					            # Plugins not enabled, do nothing
 | 
				
			||||||
            return
 | 
					            return  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        logger.info('Start unloading plugins')
 | 
					        logger.info('Start unloading plugins')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Set maintanace mode
 | 
					        # Set maintanace mode
 | 
				
			||||||
        _maintenance = bool(get_maintenance_mode())
 | 
					        _maintenance = bool(get_maintenance_mode())
 | 
				
			||||||
        if not _maintenance:
 | 
					        if not _maintenance:
 | 
				
			||||||
            set_maintenance_mode(True)
 | 
					            set_maintenance_mode(True)  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # remove all plugins from registry
 | 
					        # remove all plugins from registry
 | 
				
			||||||
        self._clean_registry()
 | 
					        self._clean_registry()
 | 
				
			||||||
@@ -160,7 +160,7 @@ class PluginsRegistry:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # remove maintenance
 | 
					        # remove maintenance
 | 
				
			||||||
        if not _maintenance:
 | 
					        if not _maintenance:
 | 
				
			||||||
            set_maintenance_mode(False)
 | 
					            set_maintenance_mode(False)  # pragma: no cover
 | 
				
			||||||
        logger.info('Finished unloading plugins')
 | 
					        logger.info('Finished unloading plugins')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def reload_plugins(self):
 | 
					    def reload_plugins(self):
 | 
				
			||||||
@@ -170,7 +170,7 @@ class PluginsRegistry:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # Do not reload whe currently loading
 | 
					        # Do not reload whe currently loading
 | 
				
			||||||
        if self.is_loading:
 | 
					        if self.is_loading:
 | 
				
			||||||
            return
 | 
					            return  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        logger.info('Start reloading plugins')
 | 
					        logger.info('Start reloading plugins')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -187,7 +187,7 @@ class PluginsRegistry:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if not settings.PLUGINS_ENABLED:
 | 
					        if not settings.PLUGINS_ENABLED:
 | 
				
			||||||
            # Plugins not enabled, do nothing
 | 
					            # Plugins not enabled, do nothing
 | 
				
			||||||
            return
 | 
					            return  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.plugin_modules = []  # clear
 | 
					        self.plugin_modules = []  # clear
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -200,7 +200,7 @@ class PluginsRegistry:
 | 
				
			|||||||
        # Check if not running in testing mode and apps should be loaded from hooks
 | 
					        # Check if not running in testing mode and apps should be loaded from hooks
 | 
				
			||||||
        if (not settings.PLUGIN_TESTING) or (settings.PLUGIN_TESTING and settings.PLUGIN_TESTING_SETUP):
 | 
					        if (not settings.PLUGIN_TESTING) or (settings.PLUGIN_TESTING and settings.PLUGIN_TESTING_SETUP):
 | 
				
			||||||
            # Collect plugins from setup entry points
 | 
					            # Collect plugins from setup entry points
 | 
				
			||||||
            for entry in metadata.entry_points().get('inventree_plugins', []):
 | 
					            for entry in metadata.entry_points().get('inventree_plugins', []):  # pragma: no cover
 | 
				
			||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    plugin = entry.load()
 | 
					                    plugin = entry.load()
 | 
				
			||||||
                    plugin.is_package = True
 | 
					                    plugin.is_package = True
 | 
				
			||||||
@@ -257,7 +257,7 @@ class PluginsRegistry:
 | 
				
			|||||||
            except (OperationalError, ProgrammingError) as error:
 | 
					            except (OperationalError, ProgrammingError) as error:
 | 
				
			||||||
                # Exception if the database has not been migrated yet - check if test are running - raise if not
 | 
					                # Exception if the database has not been migrated yet - check if test are running - raise if not
 | 
				
			||||||
                if not settings.PLUGIN_TESTING:
 | 
					                if not settings.PLUGIN_TESTING:
 | 
				
			||||||
                    raise error
 | 
					                    raise error  # pragma: no cover
 | 
				
			||||||
                plugin_db_setting = None
 | 
					                plugin_db_setting = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Always activate if testing
 | 
					            # Always activate if testing
 | 
				
			||||||
@@ -267,7 +267,7 @@ class PluginsRegistry:
 | 
				
			|||||||
                    # option1: package, option2: file-based
 | 
					                    # option1: package, option2: file-based
 | 
				
			||||||
                    if (plugin.__name__ == disabled) or (plugin.__module__ == disabled):
 | 
					                    if (plugin.__name__ == disabled) or (plugin.__module__ == disabled):
 | 
				
			||||||
                        # Errors are bad so disable the plugin in the database
 | 
					                        # Errors are bad so disable the plugin in the database
 | 
				
			||||||
                        if not settings.PLUGIN_TESTING:
 | 
					                        if not settings.PLUGIN_TESTING:  # pragma: no cover
 | 
				
			||||||
                            plugin_db_setting.active = False
 | 
					                            plugin_db_setting.active = False
 | 
				
			||||||
                            # TODO save the error to the plugin
 | 
					                            # TODO save the error to the plugin
 | 
				
			||||||
                            plugin_db_setting.save(no_reload=True)
 | 
					                            plugin_db_setting.save(no_reload=True)
 | 
				
			||||||
@@ -445,7 +445,7 @@ class PluginsRegistry:
 | 
				
			|||||||
            try:
 | 
					            try:
 | 
				
			||||||
                app_name = plugin_path.split('.')[-1]
 | 
					                app_name = plugin_path.split('.')[-1]
 | 
				
			||||||
                app_config = apps.get_app_config(app_name)
 | 
					                app_config = apps.get_app_config(app_name)
 | 
				
			||||||
            except LookupError:
 | 
					            except LookupError:  # pragma: no cover
 | 
				
			||||||
                # the plugin was never loaded correctly
 | 
					                # the plugin was never loaded correctly
 | 
				
			||||||
                logger.debug(f'{app_name} App was not found during deregistering')
 | 
					                logger.debug(f'{app_name} App was not found during deregistering')
 | 
				
			||||||
                break
 | 
					                break
 | 
				
			||||||
@@ -499,7 +499,7 @@ class PluginsRegistry:
 | 
				
			|||||||
                    # remove model from admin site
 | 
					                    # remove model from admin site
 | 
				
			||||||
                    admin.site.unregister(model)
 | 
					                    admin.site.unregister(model)
 | 
				
			||||||
                    models += [model._meta.model_name]
 | 
					                    models += [model._meta.model_name]
 | 
				
			||||||
            except LookupError:
 | 
					            except LookupError:  # pragma: no cover
 | 
				
			||||||
                # if an error occurs the app was never loaded right -> so nothing to do anymore
 | 
					                # if an error occurs the app was never loaded right -> so nothing to do anymore
 | 
				
			||||||
                logger.debug(f'{app_name} App was not found during deregistering')
 | 
					                logger.debug(f'{app_name} App was not found during deregistering')
 | 
				
			||||||
                break
 | 
					                break
 | 
				
			||||||
@@ -572,7 +572,7 @@ class PluginsRegistry:
 | 
				
			|||||||
        try:
 | 
					        try:
 | 
				
			||||||
            cmd(*args, **kwargs)
 | 
					            cmd(*args, **kwargs)
 | 
				
			||||||
            return True, []
 | 
					            return True, []
 | 
				
			||||||
        except Exception as error:
 | 
					        except Exception as error:  # pragma: no cover
 | 
				
			||||||
            handle_error(error)
 | 
					            handle_error(error)
 | 
				
			||||||
    # endregion
 | 
					    # endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					from __future__ import unicode_literals  # pragma: no cover
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -222,6 +222,7 @@
 | 
				
			|||||||
    lft: 0
 | 
					    lft: 0
 | 
				
			||||||
    rght: 0
 | 
					    rght: 0
 | 
				
			||||||
    expiry_date: "1990-10-10"
 | 
					    expiry_date: "1990-10-10"
 | 
				
			||||||
 | 
					    uid: 9e5ae7fc20568ed4814c10967bba8b65
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- model: stock.stockitem
 | 
					- model: stock.stockitem
 | 
				
			||||||
  pk: 521
 | 
					  pk: 521
 | 
				
			||||||
@@ -235,6 +236,7 @@
 | 
				
			|||||||
    lft: 0
 | 
					    lft: 0
 | 
				
			||||||
    rght: 0
 | 
					    rght: 0
 | 
				
			||||||
    status: 60
 | 
					    status: 60
 | 
				
			||||||
 | 
					    uid: 1be0dfa925825c5c6c79301449e50c2d
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- model: stock.stockitem
 | 
					- model: stock.stockitem
 | 
				
			||||||
  pk: 522
 | 
					  pk: 522
 | 
				
			||||||
@@ -248,4 +250,4 @@
 | 
				
			|||||||
    lft: 0
 | 
					    lft: 0
 | 
				
			||||||
    rght: 0
 | 
					    rght: 0
 | 
				
			||||||
    expiry_date: "1990-10-10"
 | 
					    expiry_date: "1990-10-10"
 | 
				
			||||||
    status: 70
 | 
					    status: 70
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,7 +49,7 @@ class InvenTreeGroupAdminForm(forms.ModelForm):
 | 
				
			|||||||
            'users',
 | 
					            'users',
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, *args, **kwargs):
 | 
					    def __init__(self, *args, **kwargs):  # pragma: no cover
 | 
				
			||||||
        super().__init__(*args, **kwargs)
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.instance.pk:
 | 
					        if self.instance.pk:
 | 
				
			||||||
@@ -65,12 +65,12 @@ class InvenTreeGroupAdminForm(forms.ModelForm):
 | 
				
			|||||||
        help_text=_('Select which users are assigned to this group')
 | 
					        help_text=_('Select which users are assigned to this group')
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def save_m2m(self):
 | 
					    def save_m2m(self):  # pragma: no cover
 | 
				
			||||||
        # Add the users to the Group.
 | 
					        # Add the users to the Group.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.instance.user_set.set(self.cleaned_data['users'])
 | 
					        self.instance.user_set.set(self.cleaned_data['users'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def save(self, *args, **kwargs):
 | 
					    def save(self, *args, **kwargs):  # pragma: no cover
 | 
				
			||||||
        # Default save
 | 
					        # Default save
 | 
				
			||||||
        instance = super().save()
 | 
					        instance = super().save()
 | 
				
			||||||
        # Save many-to-many data
 | 
					        # Save many-to-many data
 | 
				
			||||||
@@ -78,7 +78,7 @@ class InvenTreeGroupAdminForm(forms.ModelForm):
 | 
				
			|||||||
        return instance
 | 
					        return instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RoleGroupAdmin(admin.ModelAdmin):
 | 
					class RoleGroupAdmin(admin.ModelAdmin):  # pragma: no cover
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Custom admin interface for the Group model
 | 
					    Custom admin interface for the Group model
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -100,7 +100,7 @@ class RoleDetails(APIView):
 | 
				
			|||||||
            if len(permissions) > 0:
 | 
					            if len(permissions) > 0:
 | 
				
			||||||
                roles[role] = permissions
 | 
					                roles[role] = permissions
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                roles[role] = None
 | 
					                roles[role] = None  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        data = {
 | 
					        data = {
 | 
				
			||||||
            'user': user.pk,
 | 
					            'user': user.pk,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -265,9 +265,9 @@ class RuleSet(models.Model):
 | 
				
			|||||||
            model=model
 | 
					            model=model
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self, debug=False):
 | 
					    def __str__(self, debug=False):  # pragma: no cover
 | 
				
			||||||
        """ Ruleset string representation """
 | 
					        """ Ruleset string representation """
 | 
				
			||||||
        if debug:  # pragma: no cover
 | 
					        if debug:
 | 
				
			||||||
            # Makes debugging easier
 | 
					            # Makes debugging easier
 | 
				
			||||||
            return f'{str(self.group).ljust(15)}: {self.name.title().ljust(15)} | ' \
 | 
					            return f'{str(self.group).ljust(15)}: {self.name.title().ljust(15)} | ' \
 | 
				
			||||||
                   f'v: {str(self.can_view).ljust(5)} | a: {str(self.can_add).ljust(5)} | ' \
 | 
					                   f'v: {str(self.can_view).ljust(5)} | a: {str(self.can_add).ljust(5)} | ' \
 | 
				
			||||||
@@ -317,7 +317,7 @@ def split_permission(app, perm):
 | 
				
			|||||||
    """split permission string into permission and model"""
 | 
					    """split permission string into permission and model"""
 | 
				
			||||||
    permission_name, *model = perm.split('_')
 | 
					    permission_name, *model = perm.split('_')
 | 
				
			||||||
    # handle models that have underscores
 | 
					    # handle models that have underscores
 | 
				
			||||||
    if len(model) > 1:
 | 
					    if len(model) > 1:  # pragma: no cover
 | 
				
			||||||
        app += '_' + '_'.join(model[:-1])
 | 
					        app += '_' + '_'.join(model[:-1])
 | 
				
			||||||
        perm = permission_name + '_' + model[-1:][0]
 | 
					        perm = permission_name + '_' + model[-1:][0]
 | 
				
			||||||
    model = model[-1:][0]
 | 
					    model = model[-1:][0]
 | 
				
			||||||
@@ -373,7 +373,7 @@ def update_group_roles(group, debug=False):
 | 
				
			|||||||
            allowed - Whether or not the action is allowed
 | 
					            allowed - Whether or not the action is allowed
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if action not in ['view', 'add', 'change', 'delete']:
 | 
					        if action not in ['view', 'add', 'change', 'delete']:  # pragma: no cover
 | 
				
			||||||
            raise ValueError("Action {a} is invalid".format(a=action))
 | 
					            raise ValueError("Action {a} is invalid".format(a=action))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        permission_string = RuleSet.get_model_permission_string(model, action)
 | 
					        permission_string = RuleSet.get_model_permission_string(model, action)
 | 
				
			||||||
@@ -574,7 +574,7 @@ class Owner(models.Model):
 | 
				
			|||||||
        return owners
 | 
					        return owners
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def get_api_url():
 | 
					    def get_api_url():  # pragma: no cover
 | 
				
			||||||
        return reverse('api-owner-list')
 | 
					        return reverse('api-owner-list')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user