diff --git a/InvenTree/InvenTree/admin.py b/InvenTree/InvenTree/admin.py index c69e9a7c00..a9b68ece4f 100644 --- a/InvenTree/InvenTree/admin.py +++ b/InvenTree/InvenTree/admin.py @@ -30,7 +30,6 @@ class InvenTreeResource(ModelResource): **kwargs ): """Override the default import_data_inner function to provide better error handling""" - if len(dataset) > self.MAX_IMPORT_ROWS: raise ImportExportError(f"Dataset contains too many rows (max {self.MAX_IMPORT_ROWS})") @@ -71,7 +70,6 @@ class InvenTreeResource(ModelResource): def get_fields(self, **kwargs): """Return fields, with some common exclusions""" - fields = super().get_fields(**kwargs) fields_to_exclude = [ diff --git a/InvenTree/InvenTree/api.py b/InvenTree/InvenTree/api.py index 35792444e9..c269cd5eb1 100644 --- a/InvenTree/InvenTree/api.py +++ b/InvenTree/InvenTree/api.py @@ -35,7 +35,6 @@ class InfoView(AjaxView): def worker_pending_tasks(self): """Return the current number of outstanding background tasks""" - return OrmQ.objects.count() def get(self, request, *args, **kwargs): @@ -256,7 +255,6 @@ class APISearchView(APIView): def get_result_types(self): """Construct a list of search types we can return""" - import build.api import company.api import order.api @@ -279,7 +277,6 @@ class APISearchView(APIView): def post(self, request, *args, **kwargs): """Perform search query against available models""" - data = request.data results = {} diff --git a/InvenTree/InvenTree/apps.py b/InvenTree/InvenTree/apps.py index dbc45b85d4..609602796e 100644 --- a/InvenTree/InvenTree/apps.py +++ b/InvenTree/InvenTree/apps.py @@ -79,7 +79,6 @@ class InvenTreeConfig(AppConfig): def start_background_tasks(self): """Start all background tests for InvenTree.""" - logger.info("Starting background tasks...") from django_q.models import Schedule @@ -140,7 +139,6 @@ class InvenTreeConfig(AppConfig): def collect_tasks(self): """Collect all background tasks.""" - for app_name, app in apps.app_configs.items(): if app_name == 'InvenTree': continue diff --git a/InvenTree/InvenTree/config.py b/InvenTree/InvenTree/config.py index b9df8cbe3f..310309a890 100644 --- a/InvenTree/InvenTree/config.py +++ b/InvenTree/InvenTree/config.py @@ -23,7 +23,6 @@ def to_list(value, delimiter=','): However, the same setting may be specified via an environment variable, using a comma delimited string! """ - if type(value) in [list, tuple]: return value @@ -70,7 +69,6 @@ def ensure_dir(path: Path) -> None: If it does not exist, create it. """ - if not path.exists(): path.mkdir(parents=True, exist_ok=True) @@ -143,7 +141,6 @@ def get_setting(env_var=None, config_key=None, default_value=None, typecast=None """ def try_typecasting(value, source: str): """Attempt to typecast the value""" - # Force 'list' of strings if typecast is list: value = to_list(value) @@ -201,13 +198,11 @@ def get_setting(env_var=None, config_key=None, default_value=None, typecast=None def get_boolean_setting(env_var=None, config_key=None, default_value=False): """Helper function for retrieving a boolean configuration setting""" - return is_true(get_setting(env_var, config_key, default_value)) def get_media_dir(create=True): """Return the absolute path for the 'media' directory (where uploaded files are stored)""" - md = get_setting('INVENTREE_MEDIA_ROOT', 'media_root') if not md: @@ -223,7 +218,6 @@ def get_media_dir(create=True): def get_static_dir(create=True): """Return the absolute path for the 'static' directory (where static files are stored)""" - sd = get_setting('INVENTREE_STATIC_ROOT', 'static_root') if not sd: @@ -239,7 +233,6 @@ def get_static_dir(create=True): def get_backup_dir(create=True): """Return the absolute path for the backup directory""" - bd = get_setting('INVENTREE_BACKUP_DIR', 'backup_dir') if not bd: @@ -258,7 +251,6 @@ def get_plugin_file(): Note: It will be created if it does not already exist! """ - # Check if the plugin.txt file (specifying required plugins) is specified plugin_file = get_setting('INVENTREE_PLUGIN_FILE', 'plugin_file') @@ -283,7 +275,6 @@ def get_plugin_file(): def get_plugin_dir(): """Returns the path of the custom plugins directory""" - return get_setting('INVENTREE_PLUGIN_DIR', 'plugin_dir') @@ -297,7 +288,6 @@ def get_secret_key(): C) Look for default key file "secret_key.txt" D) Create "secret_key.txt" if it does not exist """ - # Look for environment variable if secret_key := get_setting('INVENTREE_SECRET_KEY', 'secret_key'): logger.info("SECRET_KEY loaded by INVENTREE_SECRET_KEY") # pragma: no cover diff --git a/InvenTree/InvenTree/conversion.py b/InvenTree/InvenTree/conversion.py index dc7c4a3224..6275b00c15 100644 --- a/InvenTree/InvenTree/conversion.py +++ b/InvenTree/InvenTree/conversion.py @@ -15,7 +15,6 @@ logger = logging.getLogger('inventree') def get_unit_registry(): """Return a custom instance of the Pint UnitRegistry.""" - global _unit_registry # Cache the unit registry for speedier access @@ -30,7 +29,6 @@ def reload_unit_registry(): This function is called at startup, and whenever the database is updated. """ - import time t_start = time.time() @@ -84,7 +82,6 @@ def convert_physical_value(value: str, unit: str = None, strip_units=True): Returns: The converted quantity, in the specified units """ - original = str(value).strip() # Ensure that the value is a string diff --git a/InvenTree/InvenTree/email.py b/InvenTree/InvenTree/email.py index dda090f69a..9552262dd1 100644 --- a/InvenTree/InvenTree/email.py +++ b/InvenTree/InvenTree/email.py @@ -52,7 +52,6 @@ def is_email_configured(): def send_email(subject, body, recipients, from_email=None, html_message=None): """Send an email with the specified subject and body, to the specified recipients list.""" - if isinstance(recipients, str): recipients = [recipients] diff --git a/InvenTree/InvenTree/exceptions.py b/InvenTree/InvenTree/exceptions.py index cef6925586..75cbaff125 100644 --- a/InvenTree/InvenTree/exceptions.py +++ b/InvenTree/InvenTree/exceptions.py @@ -31,7 +31,6 @@ def log_error(path): Arguments: path: The 'path' (most likely a URL) associated with this error (optional) """ - kind, info, data = sys.exc_info() # Check if the error is on the ignore list diff --git a/InvenTree/InvenTree/exchange.py b/InvenTree/InvenTree/exchange.py index f447af07f7..b3c6262d84 100644 --- a/InvenTree/InvenTree/exchange.py +++ b/InvenTree/InvenTree/exchange.py @@ -22,7 +22,6 @@ class InvenTreeExchange(SimpleExchangeBackend): def get_rates(self, **kwargs) -> None: """Set the requested currency codes and get rates.""" - from common.models import InvenTreeSetting from plugin import registry @@ -74,7 +73,6 @@ class InvenTreeExchange(SimpleExchangeBackend): @atomic def update_rates(self, base_currency=None, **kwargs): """Call to update all exchange rates""" - backend, _ = ExchangeBackend.objects.update_or_create(name=self.name, defaults={"base_currency": base_currency}) if base_currency is None: diff --git a/InvenTree/InvenTree/fields.py b/InvenTree/InvenTree/fields.py index 4e2e552ed4..6c6691ec1f 100644 --- a/InvenTree/InvenTree/fields.py +++ b/InvenTree/InvenTree/fields.py @@ -22,7 +22,6 @@ class InvenTreeRestURLField(RestURLField): def __init__(self, **kwargs): """Update schemes.""" - # Enforce 'max length' parameter in form validation if 'max_length' not in kwargs: kwargs['max_length'] = 200 @@ -38,7 +37,6 @@ class InvenTreeURLField(models.URLField): def __init__(self, **kwargs): """Initialization method for InvenTreeURLField""" - # Max length for InvenTreeURLField is set to 200 kwargs['max_length'] = 200 super().__init__(**kwargs) @@ -117,7 +115,6 @@ class InvenTreeMoneyField(MoneyField): def __init__(self, *args, **kwargs): """Override initial values with the real info from database.""" - kwargs = money_kwargs(**kwargs) super().__init__(*args, **kwargs) @@ -150,7 +147,6 @@ class DatePickerFormField(forms.DateField): def round_decimal(value, places, normalize=False): """Round value to the specified number of places.""" - if type(value) in [Decimal, float]: value = round(value, places) @@ -187,7 +183,6 @@ class RoundingDecimalField(models.DecimalField): def formfield(self, **kwargs): """Return a Field instance for this field.""" - kwargs['form_class'] = RoundingDecimalFormField return super().formfield(**kwargs) diff --git a/InvenTree/InvenTree/filters.py b/InvenTree/InvenTree/filters.py index e68df53524..560e859a57 100644 --- a/InvenTree/InvenTree/filters.py +++ b/InvenTree/InvenTree/filters.py @@ -15,7 +15,6 @@ class InvenTreeSearchFilter(filters.SearchFilter): The following query params are available to 'augment' the search (in decreasing order of priority) - search_regex: If True, search is performed on 'regex' comparison """ - regex = InvenTree.helpers.str2bool(request.query_params.get('search_regex', False)) search_fields = super().get_search_fields(view, request) @@ -36,7 +35,6 @@ class InvenTreeSearchFilter(filters.SearchFilter): Depending on the request parameters, we may "augment" these somewhat """ - whole = InvenTree.helpers.str2bool(request.query_params.get('search_whole', False)) terms = [] diff --git a/InvenTree/InvenTree/format.py b/InvenTree/InvenTree/format.py index 307bbf07de..afb16bc146 100644 --- a/InvenTree/InvenTree/format.py +++ b/InvenTree/InvenTree/format.py @@ -11,7 +11,6 @@ def parse_format_string(fmt_string: str) -> dict: Returns a dict object which contains structured information about the format groups """ - groups = string.Formatter().parse(fmt_string) info = {} @@ -62,7 +61,6 @@ def construct_format_regex(fmt_string: str) -> str: Raises: ValueError: Format string is invalid """ - pattern = "^" for group in string.Formatter().parse(fmt_string): @@ -121,7 +119,6 @@ def validate_string(value: str, fmt_string: str) -> str: Raises: ValueError: The provided format string is invalid """ - pattern = construct_format_regex(fmt_string) result = re.match(pattern, value) @@ -145,7 +142,6 @@ def extract_named_group(name: str, value: str, fmt_string: str) -> str: NameError: named value does not exist in the format string IndexError: named value could not be found in the provided entry """ - info = parse_format_string(fmt_string) if name not in info.keys(): diff --git a/InvenTree/InvenTree/forms.py b/InvenTree/InvenTree/forms.py index 4a7743a45b..f48e683074 100644 --- a/InvenTree/InvenTree/forms.py +++ b/InvenTree/InvenTree/forms.py @@ -176,7 +176,6 @@ class CustomLoginForm(LoginForm): First check that: - A valid user has been supplied """ - if not self.user: # No user supplied - redirect to the login page return HttpResponseRedirect(reverse('account_login')) @@ -313,7 +312,6 @@ class CustomAccountAdapter(CustomUrlMixin, RegistratonMixin, OTPAdapter, Default def get_email_confirmation_url(self, request, emailconfirmation): """Construct the email confirmation url""" - from InvenTree.helpers_model import construct_absolute_url url = super().get_email_confirmation_url(request, emailconfirmation) diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index 5c8cad601c..b48ff1f6b1 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -51,7 +51,6 @@ def constructPathString(path, max_chars=250): path: A list of strings e.g. ['path', 'to', 'location'] max_chars: Maximum number of characters """ - pathstring = '/'.join(path) # Replace middle elements to limit the pathstring @@ -93,7 +92,6 @@ def getBlankThumbnail(): def getLogoImage(as_file=False, custom=True): """Return the InvenTree logo image, or a custom logo if available.""" - """Return the path to the logo-file.""" if custom and settings.CUSTOM_LOGO: @@ -122,7 +120,6 @@ def getLogoImage(as_file=False, custom=True): def getSplashScreen(custom=True): """Return the InvenTree splash screen, or a custom splash if available""" - static_storage = StaticFilesStorage() if custom and settings.CUSTOM_SPLASH: @@ -338,7 +335,6 @@ def MakeBarcode(cls_name, object_pk: int, object_data=None, **kwargs): Returns: json string of the supplied data plus some other data """ - if object_data is None: object_data = {} @@ -415,7 +411,6 @@ def increment_serial_number(serial: str): Returns: incremented value, or None if incrementing could not be performed. """ - from plugin.registry import registry # Ensure we start with a string value @@ -452,7 +447,6 @@ def extract_serial_numbers(input_string, expected_quantity: int, starting_value= expected_quantity: The number of (unique) serial numbers we expect starting_value: Provide a starting value for the sequence (or None) """ - if starting_value is None: starting_value = increment_serial_number(None) @@ -724,7 +718,6 @@ def strip_html_tags(value: str, raise_error=True, field_name=None): If raise_error is True, a ValidationError will be thrown if HTML tags are detected """ - cleaned = clean( value, strip=True, @@ -756,7 +749,6 @@ def strip_html_tags(value: str, raise_error=True, field_name=None): def remove_non_printable_characters(value: str, remove_newline=True, remove_ascii=True, remove_unicode=True): """Remove non-printable / control characters from the provided string""" - cleaned = value if remove_ascii: @@ -787,7 +779,6 @@ def hash_barcode(barcode_data): We first remove any non-printable characters from the barcode data, as some browsers have issues scanning characters in. """ - barcode_data = str(barcode_data).strip() barcode_data = remove_non_printable_characters(barcode_data) @@ -813,7 +804,6 @@ def get_objectreference(obj, type_ref: str = 'content_type', object_ref: str = ' The method name must always be the name of the field prefixed by 'get_' """ - model_cls = getattr(obj, type_ref) obj_id = getattr(obj, object_ref) diff --git a/InvenTree/InvenTree/helpers_model.py b/InvenTree/InvenTree/helpers_model.py index f0d797b2c2..2f9272655e 100644 --- a/InvenTree/InvenTree/helpers_model.py +++ b/InvenTree/InvenTree/helpers_model.py @@ -41,7 +41,6 @@ def construct_absolute_url(*arg, **kwargs): 2. If the InvenTree setting INVENTREE_BASE_URL is set, use that 3. Otherwise, use the current request URL (if available) """ - relative_url = '/'.join(arg) # If a site URL is provided, use that @@ -96,7 +95,6 @@ def download_image_from_url(remote_url, timeout=2.5): ValueError: Server responded with invalid 'Content-Length' value TypeError: Response is not a valid image """ - # Check that the provided URL at least looks valid validator = URLValidator() validator(remote_url) @@ -180,7 +178,6 @@ def render_currency(money, decimal_places=None, currency=None, include_symbol=Tr min_decimal_places: The minimum number of decimal places to render to. If unspecified, uses the PRICING_DECIMAL_PLACES_MIN setting. max_decimal_places: The maximum number of decimal places to render to. If unspecified, uses the PRICING_DECIMAL_PLACES setting. """ - if money in [None, '']: return '-' @@ -234,7 +231,6 @@ def getModelsWithMixin(mixin_class) -> list: Returns: List of models that inherit from the given mixin class """ - from django.contrib.contenttypes.models import ContentType db_models = [x.model_class() for x in ContentType.objects.all() if x is not None] diff --git a/InvenTree/InvenTree/management/commands/prerender.py b/InvenTree/InvenTree/management/commands/prerender.py index 4a5eb02549..5bba15109b 100644 --- a/InvenTree/InvenTree/management/commands/prerender.py +++ b/InvenTree/InvenTree/management/commands/prerender.py @@ -12,7 +12,6 @@ from django.utils.translation import override as lang_over def render_file(file_name, source, target, locales, ctx): """Renders a file into all provided locales.""" - for locale in locales: # Enforce lower-case for locale names diff --git a/InvenTree/InvenTree/middleware.py b/InvenTree/InvenTree/middleware.py index 887551dc59..a6c2a0c435 100644 --- a/InvenTree/InvenTree/middleware.py +++ b/InvenTree/InvenTree/middleware.py @@ -125,7 +125,6 @@ class Check2FAMiddleware(BaseRequire2FAMiddleware): """Check if user is required to have MFA enabled.""" def require_2fa(self, request): """Use setting to check if MFA should be enforced for frontend page.""" - from common.models import InvenTreeSetting try: diff --git a/InvenTree/InvenTree/mixins.py b/InvenTree/InvenTree/mixins.py index 36cc812ecd..e77027734b 100644 --- a/InvenTree/InvenTree/mixins.py +++ b/InvenTree/InvenTree/mixins.py @@ -49,7 +49,6 @@ class CleanMixin(): Ref: https://github.com/mozilla/bleach/issues/192 """ - cleaned = strip_html_tags(data, field_name=field) # By default, newline characters are removed @@ -93,7 +92,6 @@ class CleanMixin(): Returns: dict: Provided data Sanitized; still in the same order. """ - clean_data = {} for k, v in data.items(): diff --git a/InvenTree/InvenTree/models.py b/InvenTree/InvenTree/models.py index 8ef5eb03d5..3bc9d58f31 100644 --- a/InvenTree/InvenTree/models.py +++ b/InvenTree/InvenTree/models.py @@ -73,7 +73,6 @@ class MetadataMixin(models.Model): def validate_metadata(self): """Validate the metadata field.""" - # Ensure that the 'metadata' field is a valid dict object if self.metadata is None: self.metadata = {} @@ -202,7 +201,6 @@ class ReferenceIndexingMixin(models.Model): This is defined by a global setting object, specified by the REFERENCE_PATTERN_SETTING attribute """ - # By default, we return an empty string if cls.REFERENCE_PATTERN_SETTING is None: return '' @@ -218,7 +216,6 @@ class ReferenceIndexingMixin(models.Model): - Returns a python dict object which contains the context data for formatting the reference string. - The default implementation provides some default context information """ - return { 'ref': cls.get_next_reference(), 'date': datetime.now(), @@ -230,7 +227,6 @@ class ReferenceIndexingMixin(models.Model): In practice, this means the item with the highest reference value """ - query = cls.objects.all().order_by('-reference_int', '-pk') if query.exists(): @@ -241,7 +237,6 @@ class ReferenceIndexingMixin(models.Model): @classmethod def get_next_reference(cls): """Return the next available reference value for this particular class.""" - # Find the "most recent" item latest = cls.get_most_recent_item() @@ -270,7 +265,6 @@ class ReferenceIndexingMixin(models.Model): @classmethod def generate_reference(cls): """Generate the next 'reference' field based on specified pattern""" - fmt = cls.get_reference_pattern() ctx = cls.get_reference_context() @@ -310,7 +304,6 @@ class ReferenceIndexingMixin(models.Model): @classmethod def validate_reference_pattern(cls, pattern): """Ensure that the provided pattern is valid""" - ctx = cls.get_reference_context() try: @@ -336,7 +329,6 @@ class ReferenceIndexingMixin(models.Model): @classmethod def validate_reference_field(cls, value): """Check that the provided 'reference' value matches the requisite pattern""" - pattern = cls.get_reference_pattern() value = str(value).strip() @@ -368,7 +360,6 @@ class ReferenceIndexingMixin(models.Model): If we cannot extract using the pattern for some reason, fallback to the entire reference """ - try: # Extract named group based on provided pattern reference = InvenTree.format.extract_named_group('ref', reference, cls.get_reference_pattern()) @@ -390,7 +381,6 @@ class ReferenceIndexingMixin(models.Model): def extract_int(reference, clip=0x7fffffff, allow_negative=False): """Extract an integer out of reference.""" - # Default value if we cannot convert to an integer ref_int = 0 @@ -571,7 +561,6 @@ class InvenTreeAttachment(models.Model): - If the attachment is a link to an external resource, return the link - If the attachment is an uploaded file, return the fully qualified media URL """ - if self.link: return self.link @@ -608,7 +597,6 @@ class InvenTreeTree(MPTTModel): Note that a 'unique_together' requirement for ('name', 'parent') is insufficient, as it ignores cases where parent=None (i.e. top-level items) """ - super().validate_unique(exclude) results = self.__class__.objects.filter( @@ -631,7 +619,6 @@ class InvenTreeTree(MPTTModel): def save(self, *args, **kwargs): """Custom save method for InvenTreeTree abstract model""" - try: super().save(*args, **kwargs) except InvalidMove: @@ -769,7 +756,6 @@ class InvenTreeTree(MPTTModel): name: , } """ - return [ { 'pk': item.pk, @@ -839,13 +825,11 @@ class InvenTreeBarcodeMixin(models.Model): @classmethod def barcode_model_type(cls): """Return the model 'type' for creating a custom QR code.""" - # By default, use the name of the class return cls.__name__.lower() def format_barcode(self, **kwargs): """Return a JSON string for formatting a QR code for this model instance.""" - return InvenTree.helpers.MakeBarcode( self.__class__.barcode_model_type(), self.pk, @@ -855,18 +839,15 @@ class InvenTreeBarcodeMixin(models.Model): @property def barcode(self): """Format a minimal barcode string (e.g. for label printing)""" - return self.format_barcode(brief=True) @classmethod def lookup_barcode(cls, barcode_hash): """Check if a model instance exists with the specified third-party barcode hash.""" - return cls.objects.filter(barcode_hash=barcode_hash).first() def assign_barcode(self, barcode_hash=None, barcode_data=None, raise_error=True, save=True): """Assign an external (third-party) barcode to this object.""" - # Must provide either barcode_hash or barcode_data if barcode_hash is None and barcode_data is None: raise ValueError("Provide either 'barcode_hash' or 'barcode_data'") @@ -894,7 +875,6 @@ class InvenTreeBarcodeMixin(models.Model): def unassign_barcode(self): """Unassign custom barcode from this model""" - self.barcode_data = '' self.barcode_hash = '' @@ -919,7 +899,6 @@ def after_error_logged(sender, instance: Error, created: bool, **kwargs): - Send a UI notification to all users with staff status """ - if created: try: import common.models diff --git a/InvenTree/InvenTree/permissions.py b/InvenTree/InvenTree/permissions.py index e46367400b..3029e4c35f 100644 --- a/InvenTree/InvenTree/permissions.py +++ b/InvenTree/InvenTree/permissions.py @@ -9,7 +9,6 @@ import users.models def get_model_for_view(view, raise_error=True): """Attempt to introspect the 'model' type for an API view""" - if hasattr(view, 'get_permission_model'): return view.get_permission_model() diff --git a/InvenTree/InvenTree/sanitizer.py b/InvenTree/InvenTree/sanitizer.py index 8536048874..0565c1b7f2 100644 --- a/InvenTree/InvenTree/sanitizer.py +++ b/InvenTree/InvenTree/sanitizer.py @@ -55,7 +55,6 @@ def sanitize_svg(file_data, strip: bool = True, elements: str = ALLOWED_ELEMENTS Returns: str: Sanitzied SVG file. """ - # Handle byte-encoded data if isinstance(file_data, bytes): file_data = file_data.decode('utf-8') diff --git a/InvenTree/InvenTree/sentry.py b/InvenTree/InvenTree/sentry.py index 49957adc0a..b1ead7a5cb 100644 --- a/InvenTree/InvenTree/sentry.py +++ b/InvenTree/InvenTree/sentry.py @@ -17,7 +17,6 @@ logger = logging.getLogger('inventree') def default_sentry_dsn(): """Return the default Sentry.io DSN for InvenTree""" - return 'https://3928ccdba1d34895abde28031fd00100@o378676.ingest.sentry.io/6494600' @@ -26,7 +25,6 @@ def sentry_ignore_errors(): These error types will *not* be reported to sentry.io. """ - return [ Http404, ValidationError, @@ -39,7 +37,6 @@ def sentry_ignore_errors(): def init_sentry(dsn, sample_rate, tags): """Initialize sentry.io error reporting""" - logger.info("Initializing sentry.io integration") sentry_sdk.init( @@ -64,7 +61,6 @@ def init_sentry(dsn, sample_rate, tags): def report_exception(exc): """Report an exception to sentry.io""" - if settings.SENTRY_ENABLED and settings.SENTRY_DSN: if not any(isinstance(exc, e) for e in sentry_ignore_errors()): diff --git a/InvenTree/InvenTree/serializers.py b/InvenTree/InvenTree/serializers.py index a209f5c329..221463df06 100644 --- a/InvenTree/InvenTree/serializers.py +++ b/InvenTree/InvenTree/serializers.py @@ -43,7 +43,6 @@ class InvenTreeMoneySerializer(MoneyField): def get_value(self, data): """Test that the returned amount is a valid Decimal.""" - amount = super(DecimalField, self).get_value(data) # Convert an empty string to None @@ -73,7 +72,6 @@ class InvenTreeCurrencySerializer(serializers.ChoiceField): def __init__(self, *args, **kwargs): """Initialize the currency serializer""" - choices = currency_code_mappings() allow_blank = kwargs.get('allow_blank', False) or kwargs.get('allow_null', False) @@ -197,7 +195,6 @@ class InvenTreeModelSerializer(serializers.ModelSerializer): def create(self, validated_data): """Custom create method which supports field adjustment""" - initial_data = validated_data.copy() # Remove any fields which do not exist on the model @@ -221,7 +218,6 @@ class InvenTreeModelSerializer(serializers.ModelSerializer): In addition to running validators on the serializer fields, this class ensures that the underlying model is also validated. """ - # Run any native validation checks first (may raise a ValidationError) data = super().run_validation(data) @@ -705,7 +701,6 @@ class RemoteImageMixin(metaclass=serializers.SerializerMetaclass): def skip_create_fields(self): """Ensure the 'remote_image' field is skipped when creating a new instance""" - return [ 'remote_image', ] @@ -724,7 +719,6 @@ class RemoteImageMixin(metaclass=serializers.SerializerMetaclass): - Attempt to download the image and store it against this object instance - Catches and re-throws any errors """ - if not url: return diff --git a/InvenTree/InvenTree/social_auth_urls.py b/InvenTree/InvenTree/social_auth_urls.py index b9a55109bd..304232ea9a 100644 --- a/InvenTree/InvenTree/social_auth_urls.py +++ b/InvenTree/InvenTree/social_auth_urls.py @@ -31,7 +31,6 @@ class GenericOAuth2ApiConnectView(GenericOAuth2ApiLoginView): def dispatch(self, request, *args, **kwargs): """Dispatch the connect request directly.""" - # Override the request method be in connection mode request.GET = request.GET.copy() request.GET['process'] = 'connect' diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index c49312cd2a..ebd68d12d5 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -89,7 +89,6 @@ def check_daily_holdoff(task_name: str, n_days: int = 1) -> bool: Note that this function creates some *hidden* global settings (designated with the _ prefix), which are used to keep a running track of when the particular task was was last run. """ - from common.models import InvenTreeSetting from InvenTree.ready import isInTestMode @@ -146,7 +145,6 @@ def check_daily_holdoff(task_name: str, n_days: int = 1) -> bool: def record_task_attempt(task_name: str): """Record that a multi-day task has been attempted *now*""" - from common.models import InvenTreeSetting logger.info("Logging task attempt for '%s'", task_name) @@ -156,7 +154,6 @@ def record_task_attempt(task_name: str): def record_task_success(task_name: str): """Record that a multi-day task was successful *now*""" - from common.models import InvenTreeSetting InvenTreeSetting.set_setting(f'_{task_name}_SUCCESS', datetime.now().isoformat(), None) @@ -168,7 +165,6 @@ def offload_task(taskname, *args, force_async=False, force_sync=False, **kwargs) If workers are not running or force_sync flag is set then the task is ran synchronously. """ - try: import importlib @@ -353,7 +349,6 @@ def delete_successful_tasks(): @scheduled_task(ScheduledTask.DAILY) def delete_failed_tasks(): """Delete failed task logs which are older than a specified period""" - try: from django_q.models import Failure @@ -402,7 +397,6 @@ def delete_old_error_logs(): @scheduled_task(ScheduledTask.DAILY) def delete_old_notifications(): """Delete old notification logs""" - try: from common.models import (InvenTreeSetting, NotificationEntry, NotificationMessage) @@ -503,7 +497,6 @@ def update_exchange_rates(force: bool = False): Arguments: force: If True, force the update to run regardless of the last update time """ - try: from djmoney.contrib.exchange.models import Rate @@ -547,7 +540,6 @@ def update_exchange_rates(force: bool = False): @scheduled_task(ScheduledTask.DAILY) def run_backup(): """Run the backup command.""" - from common.models import InvenTreeSetting if not InvenTreeSetting.get_setting('INVENTREE_BACKUP_ENABLE', False, cache=False): @@ -582,7 +574,6 @@ def check_for_migrations(): If the setting auto_update is enabled we will start updating. """ - from common.models import InvenTreeSetting from plugin import registry diff --git a/InvenTree/InvenTree/template.py b/InvenTree/InvenTree/template.py index a07cbca988..f9c11dd173 100644 --- a/InvenTree/InvenTree/template.py +++ b/InvenTree/InvenTree/template.py @@ -16,7 +16,6 @@ class InvenTreeTemplateLoader(CachedLoader): Any custom report or label templates will be forced to reload (without cache). This ensures that generated PDF reports / labels are always up-to-date. """ - # List of template patterns to skip cache for skip_cache_dirs = [ os.path.abspath(os.path.join(settings.MEDIA_ROOT, 'report')), diff --git a/InvenTree/InvenTree/test_api.py b/InvenTree/InvenTree/test_api.py index 162b1bc8b7..aa7907d926 100644 --- a/InvenTree/InvenTree/test_api.py +++ b/InvenTree/InvenTree/test_api.py @@ -267,7 +267,6 @@ class BulkDeleteTests(InvenTreeAPITestCase): def test_errors(self): """Test that the correct errors are thrown""" - url = reverse('api-stock-test-result-list') # DELETE without any of the required fields @@ -318,7 +317,6 @@ class SearchTests(InvenTreeAPITestCase): def test_empty(self): """Test empty request""" - data = [ '', None, @@ -331,7 +329,6 @@ class SearchTests(InvenTreeAPITestCase): def test_results(self): """Test individual result types""" - response = self.post( reverse('api-search'), { @@ -374,7 +371,6 @@ class SearchTests(InvenTreeAPITestCase): def test_permissions(self): """Test that users with insufficient permissions are handled correctly""" - # First, remove all roles for ruleset in self.group.rule_sets.all(): ruleset.can_view = False diff --git a/InvenTree/InvenTree/test_views.py b/InvenTree/InvenTree/test_views.py index f90cae791e..708304c68d 100644 --- a/InvenTree/InvenTree/test_views.py +++ b/InvenTree/InvenTree/test_views.py @@ -45,7 +45,6 @@ class ViewTests(InvenTreeTestCase): def test_settings_page(self): """Test that the 'settings' page loads correctly""" - # Settings page loads url = reverse('settings') @@ -122,7 +121,6 @@ class ViewTests(InvenTreeTestCase): def test_url_login(self): """Test logging in via arguments""" - # Log out self.client.logout() response = self.client.get("/index/") diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index 02d8b9c408..5687d5f7a6 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -44,7 +44,6 @@ class ConversionTest(TestCase): def test_prefixes(self): """Test inputs where prefixes are used""" - tests = { "3": 3, "3m": 3, @@ -78,7 +77,6 @@ class ConversionTest(TestCase): def test_dimensionless_units(self): """Tests for 'dimensionless' unit quantities""" - # Test some dimensionless units tests = { 'ea': 1, @@ -106,7 +104,6 @@ class ConversionTest(TestCase): def test_invalid_units(self): """Test conversion with bad units""" - tests = { '3': '10', '13': '-?-', @@ -121,7 +118,6 @@ class ConversionTest(TestCase): def test_invalid_values(self): """Test conversion of invalid inputs""" - inputs = [ '-x', '1/0', @@ -140,7 +136,6 @@ class ConversionTest(TestCase): def test_custom_units(self): """Tests for custom unit conversion""" - # Start with an empty set of units CustomUnit.objects.all().delete() InvenTree.conversion.reload_unit_registry() @@ -214,7 +209,6 @@ class FormatTest(TestCase): def test_parse(self): """Tests for the 'parse_format_string' function""" - # Extract data from a valid format string fmt = "PO-{abc:02f}-{ref:04d}-{date}-???" @@ -236,7 +230,6 @@ class FormatTest(TestCase): def test_create_regex(self): """Test function for creating a regex from a format string""" - tests = { "PO-123-{ref:04f}": r"^PO\-123\-(?P.+)$", "{PO}-???-{ref}-{date}-22": r"^(?P.+)\-...\-(?P.+)\-(?P.+)\-22$", @@ -249,7 +242,6 @@ class FormatTest(TestCase): def test_validate_format(self): """Test that string validation works as expected""" - # These tests should pass for value, pattern in { "ABC-hello-123": "???-{q}-###", @@ -270,7 +262,6 @@ class FormatTest(TestCase): def test_extract_value(self): """Test that we can extract named values based on a format string""" - # Simple tests based on a straight-forward format string fmt = "PO-###-{ref:04d}" @@ -345,7 +336,6 @@ class TestHelpers(TestCase): def test_absolute_url(self): """Test helper function for generating an absolute URL""" - base = "https://demo.inventree.org:12345" InvenTreeSetting.set_setting('INVENTREE_BASE_URL', base, change_user=None) @@ -418,9 +408,7 @@ class TestHelpers(TestCase): def test_logo_image(self): """Test for retrieving logo image""" - # By default, there is no custom logo provided - logo = helpers.getLogoImage() self.assertEqual(logo, '/static/img/inventree.png') @@ -429,7 +417,6 @@ class TestHelpers(TestCase): def test_download_image(self): """Test function for downloading image from remote URL""" - # Run check with a sequence of bad URLs for url in [ "blog", @@ -489,7 +476,6 @@ class TestHelpers(TestCase): def test_model_mixin(self): """Test the getModelsWithMixin function""" - from InvenTree.models import InvenTreeBarcodeMixin models = InvenTree.helpers_model.getModelsWithMixin(InvenTreeBarcodeMixin) @@ -829,7 +815,6 @@ class CurrencyTests(TestCase): def test_rates(self): """Test exchange rate update.""" - # Initially, there will not be any exchange rate information rates = Rate.objects.all() @@ -1083,7 +1068,6 @@ class TestOffloadTask(InvenTreeTestCase): Ref: https://github.com/inventree/InvenTree/pull/3273 """ - offload_task( 'dummy_tasks.parts', part=Part.objects.get(pk=1), @@ -1106,7 +1090,6 @@ class TestOffloadTask(InvenTreeTestCase): def test_daily_holdoff(self): """Tests for daily task holdoff helper functions""" - import InvenTree.tasks with self.assertLogs(logger='inventree', level='INFO') as cm: @@ -1162,7 +1145,6 @@ class BarcodeMixinTest(InvenTreeTestCase): def test_barcode_model_type(self): """Test that the barcode_model_type property works for each class""" - from part.models import Part from stock.models import StockItem, StockLocation @@ -1172,7 +1154,6 @@ class BarcodeMixinTest(InvenTreeTestCase): def test_barcode_hash(self): """Test that the barcode hashing function provides correct results""" - # Test multiple values for the hashing function # This is to ensure that the hash function is always "backwards compatible" hashing_tests = { @@ -1208,7 +1189,6 @@ class MagicLoginTest(InvenTreeTestCase): def test_generation(self): """Test that magic login tokens are generated correctly""" - # User does not exists resp = self.client.post(reverse('sesame-generate'), {'email': 1}) self.assertEqual(resp.status_code, 200) diff --git a/InvenTree/InvenTree/translation.py b/InvenTree/InvenTree/translation.py index d965f72df2..d07f5c8ed7 100644 --- a/InvenTree/InvenTree/translation.py +++ b/InvenTree/InvenTree/translation.py @@ -40,7 +40,6 @@ def reload_translation_stats(): def get_translation_percent(lang_code): """Return the translation percentage for the given language code""" - if _translation_stats is None: reload_translation_stats() diff --git a/InvenTree/InvenTree/unit_test.py b/InvenTree/InvenTree/unit_test.py index 3590a6f56b..43a2d66cbf 100644 --- a/InvenTree/InvenTree/unit_test.py +++ b/InvenTree/InvenTree/unit_test.py @@ -143,7 +143,6 @@ class UserMixin: def setUp(self): """Run setup for individual test methods""" - if self.auto_login: self.client.login(username=self.username, password=self.password) @@ -156,7 +155,6 @@ class UserMixin: assign_all: Set to True to assign *all* roles group: The group to assign roles to (or leave None to use the group assigned to this class) """ - if group is None: group = cls.group @@ -207,7 +205,6 @@ class ExchangeRateMixin: def generate_exchange_rates(self): """Helper function which generates some exchange rates to work with""" - rates = { 'AUD': 1.5, 'CAD': 1.7, @@ -271,7 +268,6 @@ class InvenTreeAPITestCase(ExchangeRateMixin, UserMixin, APITestCase): def checkResponse(self, url, method, expected_code, response): """Debug output for an unexpected response""" - # No expected code, return if expected_code is None: return @@ -318,7 +314,6 @@ class InvenTreeAPITestCase(ExchangeRateMixin, UserMixin, APITestCase): def post(self, url, data=None, expected_code=None, format='json'): """Issue a POST request.""" - # Set default value - see B006 if data is None: data = {} @@ -331,7 +326,6 @@ class InvenTreeAPITestCase(ExchangeRateMixin, UserMixin, APITestCase): def delete(self, url, data=None, expected_code=None, format='json'): """Issue a DELETE request.""" - if data is None: data = {} diff --git a/InvenTree/InvenTree/validators.py b/InvenTree/InvenTree/validators.py index 1d38891da4..9a4b5f32e5 100644 --- a/InvenTree/InvenTree/validators.py +++ b/InvenTree/InvenTree/validators.py @@ -17,7 +17,6 @@ import InvenTree.conversion def validate_physical_units(unit): """Ensure that a given unit is a valid physical unit.""" - unit = unit.strip() # Ignore blank units @@ -69,7 +68,6 @@ class AllowedURLValidator(validators.URLValidator): def validate_purchase_order_reference(value): """Validate the 'reference' field of a PurchaseOrder.""" - from order.models import PurchaseOrder # If we get to here, run the "default" validation routine @@ -78,7 +76,6 @@ def validate_purchase_order_reference(value): def validate_sales_order_reference(value): """Validate the 'reference' field of a SalesOrder.""" - from order.models import SalesOrder # If we get to here, run the "default" validation routine @@ -140,7 +137,6 @@ def validate_part_name_format(value): Make sure that each template container has a field of Part Model """ - # Make sure that the field_name exists in Part model from part.models import Part diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py index 72e2551b66..839e6ea4a0 100644 --- a/InvenTree/InvenTree/version.py +++ b/InvenTree/InvenTree/version.py @@ -178,5 +178,4 @@ def inventreeTarget(): def inventreePlatform(): """Returns the platform for the instance.""" - return platform.platform(aliased=True) diff --git a/InvenTree/build/api.py b/InvenTree/build/api.py index acadb88a16..21d6ac666e 100644 --- a/InvenTree/build/api.py +++ b/InvenTree/build/api.py @@ -100,7 +100,6 @@ class BuildFilter(rest_filters.FilterSet): def filter_has_project_code(self, queryset, name, value): """Filter by whether or not the order has a project code""" - if str2bool(value): return queryset.exclude(project_code=None) else: @@ -235,7 +234,6 @@ class BuildDetail(RetrieveUpdateDestroyAPI): def destroy(self, request, *args, **kwargs): """Only allow deletion of a BuildOrder if the build status is CANCELLED""" - build = self.get_object() if build.status != BuildStatus.CANCELLED: @@ -292,7 +290,6 @@ class BuildLineFilter(rest_filters.FilterSet): def filter_allocated(self, queryset, name, value): """Filter by whether each BuildLine is fully allocated""" - if str2bool(value): return queryset.filter(allocated__gte=F('quantity')) else: @@ -309,7 +306,6 @@ class BuildLineFilter(rest_filters.FilterSet): - The quantity available for each BuildLine - The quantity allocated for each BuildLine """ - flt = Q(quantity__lte=F('total_available_stock') + F('allocated')) if str2bool(value): diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index 33a9b8f9cd..5be28f6775 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -348,7 +348,6 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models. @property def tracked_line_items(self): """Returns the "trackable" BOM lines for this BuildOrder.""" - return self.build_lines.filter(bom_item__sub_part__trackable=True) def has_tracked_line_items(self): @@ -358,7 +357,6 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models. @property def untracked_line_items(self): """Returns the "non trackable" BOM items for this BuildOrder.""" - return self.build_lines.filter(bom_item__sub_part__trackable=False) @property @@ -432,7 +430,6 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models. def is_partially_allocated(self): """Test is this build order has any stock allocated against it""" - return self.allocated_stock.count() > 0 @property @@ -497,7 +494,6 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models. - Completed count must meet the required quantity - Untracked parts must be allocated """ - if self.incomplete_count > 0: return False @@ -780,7 +776,6 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models. @transaction.atomic def trim_allocated_stock(self): """Called after save to reduce allocated stock if the build order is now overallocated.""" - # Only need to worry about untracked stock here for build_line in self.untracked_line_items: @@ -817,7 +812,6 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models. @transaction.atomic def subtract_allocated_stock(self, user): """Called when the Build is marked as "complete", this function removes the allocated untracked items from stock.""" - # Find all BuildItem objects which point to this build items = self.allocated_stock.filter( build_line__bom_item__sub_part__trackable=False @@ -839,7 +833,6 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models. - Set the item status to "scrapped" - Add a transaction entry to the stock item history """ - if not output: raise ValidationError(_("No build output specified")) @@ -1069,7 +1062,6 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models. def unallocated_lines(self, tracked=None): """Returns a list of BuildLine objects which have not been fully allocated.""" - lines = self.build_lines.all() if tracked is True: @@ -1096,7 +1088,6 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models. Returns: True if the BuildOrder has been fully allocated, otherwise False """ - lines = self.unallocated_lines(tracked=tracked) return len(lines) == 0 @@ -1109,7 +1100,6 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models. To determine if the output has been fully allocated, we need to test all "trackable" BuildLine objects """ - for line in self.build_lines.filter(bom_item__sub_part__trackable=True): # Grab all BuildItem objects which point to this output allocations = BuildItem.objects.filter( @@ -1134,7 +1124,6 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models. Returns: True if any BuildLine has been over-allocated. """ - for line in self.build_lines.all(): if line.is_overallocated(): return True @@ -1159,7 +1148,6 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models. @transaction.atomic def create_build_line_items(self, prevent_duplicates=True): """Create BuildLine objects for each BOM line in this BuildOrder.""" - lines = [] bom_items = self.part.get_bom_items() @@ -1192,7 +1180,6 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models. @transaction.atomic def update_build_line_items(self): """Rebuild required quantity field for each BuildLine object""" - lines_to_update = [] for line in self.build_lines.all(): @@ -1296,7 +1283,6 @@ class BuildLine(models.Model): def allocated_quantity(self): """Calculate the total allocated quantity for this BuildLine""" - # Queryset containing all BuildItem objects allocated against this BuildLine allocations = self.allocations.all() @@ -1312,7 +1298,6 @@ class BuildLine(models.Model): def is_fully_allocated(self): """Return True if this BuildLine is fully allocated""" - if self.bom_item.consumable: return True diff --git a/InvenTree/build/serializers.py b/InvenTree/build/serializers.py index df6fa1b213..337c00452d 100644 --- a/InvenTree/build/serializers.py +++ b/InvenTree/build/serializers.py @@ -129,7 +129,6 @@ class BuildSerializer(InvenTreeModelSerializer): def validate_reference(self, reference): """Custom validation for the Build reference field""" - # Ensure the reference matches the required pattern Build.validate_reference_field(reference) @@ -209,7 +208,6 @@ class BuildOutputQuantitySerializer(BuildOutputSerializer): def validate(self, data): """Validate the serializer data""" - data = super().validate(data) output = data.get('output') @@ -450,7 +448,6 @@ class BuildOutputScrapSerializer(serializers.Serializer): def save(self): """Save the serializer to scrap the build outputs""" - build = self.context['build'] request = self.context['request'] data = self.validated_data @@ -625,7 +622,6 @@ class BuildCompleteSerializer(serializers.Serializer): This is so we can determine (at run time) whether the build is ready to be completed. """ - build = self.context['build'] return { @@ -1095,7 +1091,6 @@ class BuildLineSerializer(InvenTreeModelSerializer): - available: Total stock available for allocation against this build line - on_order: Total stock on order for this build line """ - queryset = queryset.select_related( 'build', 'bom_item', ) diff --git a/InvenTree/build/tasks.py b/InvenTree/build/tasks.py index 763e26af1c..46fd97cab4 100644 --- a/InvenTree/build/tasks.py +++ b/InvenTree/build/tasks.py @@ -29,7 +29,6 @@ def update_build_order_lines(bom_item_pk: int): This task is triggered when a BomItem is created or updated. """ - logger.info("Updating build order lines for BomItem %s", bom_item_pk) bom_item = part_models.BomItem.objects.filter(pk=bom_item_pk).first() @@ -156,7 +155,6 @@ def check_build_stock(build: build.models.Build): def notify_overdue_build_order(bo: build.models.Build): """Notify appropriate users that a Build has just become 'overdue'""" - targets = [] if bo.issued_by: @@ -202,7 +200,6 @@ def check_overdue_build_orders(): - Look at the 'target_date' of any outstanding BuildOrder objects - If the 'target_date' expired *yesterday* then the order is just out of date """ - yesterday = datetime.now().date() - timedelta(days=1) overdue_orders = build.models.Build.objects.filter( diff --git a/InvenTree/build/test_api.py b/InvenTree/build/test_api.py index fd082dd44b..0d5e834cbd 100644 --- a/InvenTree/build/test_api.py +++ b/InvenTree/build/test_api.py @@ -279,7 +279,6 @@ class BuildTest(BuildAPITest): def test_delete(self): """Test that we can delete a BuildOrder via the API""" - bo = Build.objects.get(pk=1) url = reverse('api-build-detail', kwargs={'pk': bo.pk}) @@ -684,9 +683,7 @@ class BuildAllocationTest(BuildAPITest): def test_invalid_bom_item(self): """Test by passing an invalid BOM item.""" - # Find the right (in this case, wrong) BuildLine instance - si = StockItem.objects.get(pk=11) lines = self.build.build_lines.all() @@ -718,7 +715,6 @@ class BuildAllocationTest(BuildAPITest): This should result in creation of a new BuildItem object """ - # Find the correct BuildLine si = StockItem.objects.get(pk=2) @@ -758,7 +754,6 @@ class BuildAllocationTest(BuildAPITest): This should increment the quantity of the existing BuildItem object """ - # Find the correct BuildLine si = StockItem.objects.get(pk=2) @@ -875,7 +870,6 @@ class BuildOverallocationTest(BuildAPITest): def test_setup(self): """Validate expected state after set-up.""" - self.assertEqual(self.build.incomplete_outputs.count(), 0) self.assertEqual(self.build.complete_outputs.count(), 1) self.assertEqual(self.build.completed, self.build.quantity) @@ -1040,7 +1034,6 @@ class BuildOutputScrapTest(BuildAPITest): def scrap(self, build_id, data, expected_code=None): """Helper method to POST to the scrap API""" - url = reverse('api-build-output-scrap', kwargs={'pk': build_id}) response = self.post(url, data, expected_code=expected_code) @@ -1049,7 +1042,6 @@ class BuildOutputScrapTest(BuildAPITest): def test_invalid_scraps(self): """Test that invalid scrap attempts are rejected""" - # Test with missing required fields response = self.scrap(1, {}, expected_code=400) @@ -1113,7 +1105,6 @@ class BuildOutputScrapTest(BuildAPITest): def test_valid_scraps(self): """Test that valid scrap attempts succeed""" - # Create a build output build = Build.objects.get(pk=1) diff --git a/InvenTree/build/test_build.py b/InvenTree/build/test_build.py index 89babf0bdf..9569aa0870 100644 --- a/InvenTree/build/test_build.py +++ b/InvenTree/build/test_build.py @@ -45,7 +45,6 @@ class BuildTestBase(TestCase): - 7 x output_2 """ - super().setUpTestData() # Create a base "Part" @@ -145,7 +144,6 @@ class BuildTest(BuildTestBase): def test_ref_int(self): """Test the "integer reference" field used for natural sorting""" - # Set build reference to new value common.models.InvenTreeSetting.set_setting('BUILDORDER_REFERENCE_PATTERN', 'BO-{ref}-???', change_user=None) @@ -174,9 +172,7 @@ class BuildTest(BuildTestBase): def test_ref_validation(self): """Test that the reference field validation works as expected""" - # Default reference pattern = 'BO-{ref:04d} - # These patterns should fail for ref in [ 'BO-1234x', @@ -223,7 +219,6 @@ class BuildTest(BuildTestBase): def test_next_ref(self): """Test that the next reference is automatically generated""" - common.models.InvenTreeSetting.set_setting('BUILDORDER_REFERENCE_PATTERN', 'XYZ-{ref:06d}', change_user=None) build = Build.objects.create( @@ -250,7 +245,6 @@ class BuildTest(BuildTestBase): def test_init(self): """Perform some basic tests before we start the ball rolling""" - self.assertEqual(StockItem.objects.count(), 10) # Build is PENDING @@ -272,7 +266,6 @@ class BuildTest(BuildTestBase): def test_build_item_clean(self): """Ensure that dodgy BuildItem objects cannot be created""" - stock = StockItem.objects.create(part=self.assembly, quantity=99) # Create a BuiltItem which points to an invalid StockItem @@ -299,7 +292,6 @@ class BuildTest(BuildTestBase): def test_duplicate_bom_line(self): """Try to add a duplicate BOM item - it should be allowed""" - BomItem.objects.create( part=self.assembly, sub_part=self.sub_part_1, @@ -313,7 +305,6 @@ class BuildTest(BuildTestBase): output: StockItem object (or None) allocations: Map of {StockItem: quantity} """ - items_to_create = [] for item, quantity in allocations.items(): @@ -335,7 +326,6 @@ class BuildTest(BuildTestBase): def test_partial_allocation(self): """Test partial allocation of stock""" - # Fully allocate tracked stock against build output 1 self.allocate_stock( self.output_1, @@ -409,7 +399,6 @@ class BuildTest(BuildTestBase): def test_overallocation_and_trim(self): """Test overallocation of stock and trim function""" - # Fully allocate tracked stock (not eligible for trimming) self.allocate_stock( self.output_1, @@ -484,9 +473,7 @@ class BuildTest(BuildTestBase): def test_cancel(self): """Test cancellation of the build""" - # TODO - """ self.allocate_stock(50, 50, 200, self.output_1) self.build.cancel_build(None) @@ -497,7 +484,6 @@ class BuildTest(BuildTestBase): def test_complete(self): """Test completion of a build output""" - self.stock_1_1.quantity = 1000 self.stock_1_1.save() @@ -567,7 +553,6 @@ class BuildTest(BuildTestBase): def test_overdue_notification(self): """Test sending of notifications when a build order is overdue.""" - self.build.target_date = datetime.now().date() - timedelta(days=1) self.build.save() @@ -583,7 +568,6 @@ class BuildTest(BuildTestBase): def test_new_build_notification(self): """Test that a notification is sent when a new build is created""" - Build.objects.create( reference='BO-9999', title='Some new build', @@ -609,7 +593,6 @@ class BuildTest(BuildTestBase): def test_metadata(self): """Unit tests for the metadata field.""" - # Make sure a BuildItem exists before trying to run this test b = BuildItem(stock_item=self.stock_1_2, build_line=self.line_1, install_into=self.output_1, quantity=10) b.save() @@ -664,7 +647,6 @@ class AutoAllocationTests(BuildTestBase): A "fully auto" allocation should allocate *all* of these stock items to the build """ - # No build item allocations have been made against the build self.assertEqual(self.build.allocated_stock.count(), 0) @@ -717,7 +699,6 @@ class AutoAllocationTests(BuildTestBase): def test_fully_auto(self): """We should be able to auto-allocate against a build in a single go""" - self.build.auto_allocate_stock( interchangeable=True, substitutes=True, diff --git a/InvenTree/build/test_migrations.py b/InvenTree/build/test_migrations.py index 440074d4d7..dba739764a 100644 --- a/InvenTree/build/test_migrations.py +++ b/InvenTree/build/test_migrations.py @@ -111,7 +111,6 @@ class TestReferencePatternMigration(MigratorTestCase): def prepare(self): """Create some initial data prior to migration""" - Setting = self.old_state.apps.get_model('common', 'inventreesetting') # Create a custom existing prefix so we can confirm the operation is working @@ -141,7 +140,6 @@ class TestReferencePatternMigration(MigratorTestCase): def test_reference_migration(self): """Test that the reference fields have been correctly updated""" - Build = self.new_state.apps.get_model('build', 'build') for build in Build.objects.all(): @@ -170,7 +168,6 @@ class TestBuildLineCreation(MigratorTestCase): def prepare(self): """Create data to work with""" - # Model references Part = self.old_state.apps.get_model('part', 'part') BomItem = self.old_state.apps.get_model('part', 'bomitem') @@ -235,7 +232,6 @@ class TestBuildLineCreation(MigratorTestCase): def test_build_line_creation(self): """Test that the BuildLine objects have been created correctly""" - Build = self.new_state.apps.get_model('build', 'build') BomItem = self.new_state.apps.get_model('part', 'bomitem') BuildLine = self.new_state.apps.get_model('build', 'buildline') diff --git a/InvenTree/build/validators.py b/InvenTree/build/validators.py index 5d529218a1..87ebfc25ba 100644 --- a/InvenTree/build/validators.py +++ b/InvenTree/build/validators.py @@ -3,7 +3,6 @@ def generate_next_build_reference(): """Generate the next available BuildOrder reference""" - from build.models import Build return Build.generate_reference() @@ -11,7 +10,6 @@ def generate_next_build_reference(): def validate_build_order_reference_pattern(pattern): """Validate the BuildOrder reference 'pattern' setting""" - from build.models import Build Build.validate_reference_pattern(pattern) @@ -19,7 +17,6 @@ def validate_build_order_reference_pattern(pattern): def validate_build_order_reference(value): """Validate that the BuildOrder reference field matches the required pattern.""" - from build.models import Build # If we get to here, run the "default" validation routine diff --git a/InvenTree/common/api.py b/InvenTree/common/api.py index c13ae456dd..63bd11d900 100644 --- a/InvenTree/common/api.py +++ b/InvenTree/common/api.py @@ -113,7 +113,6 @@ class CurrencyExchangeView(APIView): def get(self, request, format=None): """Return information on available currency conversions""" - # Extract a list of all available rates try: rates = Rate.objects.all() @@ -157,7 +156,6 @@ class CurrencyRefreshView(APIView): def post(self, request, *args, **kwargs): """Performing a POST request will update currency exchange rates""" - from InvenTree.tasks import update_exchange_rates update_exchange_rates(force=True) @@ -194,7 +192,6 @@ class GlobalSettingsList(SettingsList): def list(self, request, *args, **kwargs): """Ensure all global settings are created""" - common.models.InvenTreeSetting.build_default_values() return super().list(request, *args, **kwargs) @@ -253,7 +250,6 @@ class UserSettingsList(SettingsList): def list(self, request, *args, **kwargs): """Ensure all user settings are created""" - common.models.InvenTreeUserSetting.build_default_values(user=request.user) return super().list(request, *args, **kwargs) @@ -385,7 +381,6 @@ class NotificationList(NotificationMessageMixin, BulkDeleteMixin, ListAPI): def filter_delete_queryset(self, queryset, request): """Ensure that the user can only delete their *own* notifications""" - queryset = queryset.filter(user=request.user) return queryset diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index a198e29e82..c62577bc09 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -84,7 +84,6 @@ class BaseURLValidator(URLValidator): def __init__(self, schemes=None, **kwargs): """Custom init routine""" - super().__init__(schemes, **kwargs) # Override default host_re value - allow optional tld regex @@ -204,7 +203,6 @@ class BaseInvenTreeSetting(models.Model): If a particular setting is not present, create it with the default value """ - cache_key = f"BUILD_DEFAULT_VALUES:{str(cls.__name__)}" if InvenTree.helpers.str2bool(cache.get(cache_key, False)): @@ -255,7 +253,6 @@ class BaseInvenTreeSetting(models.Model): def save_to_cache(self): """Save this setting object to cache""" - ckey = self.cache_key # skip saving to cache if no pk is set @@ -283,7 +280,6 @@ class BaseInvenTreeSetting(models.Model): - The unique KEY string - Any key:value kwargs associated with the particular setting type (e.g. user-id) """ - key = f"{str(cls.__name__)}:{setting_key}" for k, v in kwargs.items(): @@ -992,7 +988,6 @@ def validate_email_domains(setting): def currency_exchange_plugins(): """Return a set of plugin choices which can be used for currency exchange""" - try: from plugin import registry plugs = registry.with_mixin('currencyexchange', active=True) @@ -1006,7 +1001,6 @@ def currency_exchange_plugins(): def update_exchange_rates(setting): """Update exchange rates when base currency is changed""" - if InvenTree.ready.isImportingData(): return @@ -1018,7 +1012,6 @@ def update_exchange_rates(setting): def reload_plugin_registry(setting): """When a core plugin setting is changed, reload the plugin registry""" - from plugin import registry logger.info("Reloading plugin registry due to change in setting '%s'", setting.key) @@ -2891,7 +2884,6 @@ class NewsFeedEntry(models.Model): def rename_notes_image(instance, filename): """Function for renaming uploading image file. Will store in the 'notes' directory.""" - fname = os.path.basename(filename) return os.path.join('notes', fname) @@ -2936,7 +2928,6 @@ class CustomUnit(models.Model): def clean(self): """Validate that the provided custom unit is indeed valid""" - super().clean() from InvenTree.conversion import get_unit_registry @@ -2994,7 +2985,6 @@ class CustomUnit(models.Model): @receiver(post_delete, sender=CustomUnit, dispatch_uid='custom_unit_deleted') def after_custom_unit_updated(sender, instance, **kwargs): """Callback when a custom unit is updated or deleted""" - # Force reload of the unit registry from InvenTree.conversion import reload_unit_registry reload_unit_registry() diff --git a/InvenTree/common/notifications.py b/InvenTree/common/notifications.py index 8477f18c3c..5c2aa1434a 100644 --- a/InvenTree/common/notifications.py +++ b/InvenTree/common/notifications.py @@ -242,7 +242,6 @@ class UIMessageNotification(SingleNotificationMethod): def get_targets(self): """Only send notifications for active users""" - return [target for target in self.targets if target.is_active] def send(self, target): diff --git a/InvenTree/common/serializers.py b/InvenTree/common/serializers.py index 94109eb831..c791f39f83 100644 --- a/InvenTree/common/serializers.py +++ b/InvenTree/common/serializers.py @@ -192,7 +192,6 @@ class NotificationMessageSerializer(InvenTreeModelSerializer): def get_target(self, obj): """Function to resolve generic object reference to target.""" - target = get_objectreference(obj, 'target_content_type', 'target_object_id') if target and 'link' not in target: diff --git a/InvenTree/common/settings.py b/InvenTree/common/settings.py index 485b75aaca..b62f081a28 100644 --- a/InvenTree/common/settings.py +++ b/InvenTree/common/settings.py @@ -12,7 +12,6 @@ logger = logging.getLogger('inventree') def currency_code_default(): """Returns the default currency code (or USD if not specified)""" - from common.models import InvenTreeSetting cached_value = cache.get('currency_code_default', '') diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index 641e079673..61590411f8 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -268,7 +268,6 @@ class SettingsTest(InvenTreeTestCase): def test_global_setting_caching(self): """Test caching operations for the global settings class""" - key = 'PART_NAME_FORMAT' cache_key = InvenTreeSetting.create_cache_key(key) @@ -290,7 +289,6 @@ class SettingsTest(InvenTreeTestCase): def test_user_setting_caching(self): """Test caching operation for the user settings class""" - cache.clear() # Generate a number of new users @@ -610,7 +608,6 @@ class NotificationUserSettingsApiTest(InvenTreeAPITestCase): def test_setting(self): """Test the string name for NotificationUserSetting.""" - NotificationUserSetting.set_setting('NOTIFICATION_METHOD_MAIL', True, change_user=self.user, user=self.user) test_setting = NotificationUserSetting.get_setting_object('NOTIFICATION_METHOD_MAIL', user=self.user) self.assertEqual(str(test_setting), 'NOTIFICATION_METHOD_MAIL (for testuser): True') @@ -823,7 +820,6 @@ class NotificationTest(InvenTreeAPITestCase): def test_api_list(self): """Test list URL.""" - url = reverse('api-notifications-list') self.get(url, expected_code=200) @@ -843,7 +839,6 @@ class NotificationTest(InvenTreeAPITestCase): def test_bulk_delete(self): """Tests for bulk deletion of user notifications""" - from error_report.models import Error # Create some notification messages by throwing errors @@ -1019,7 +1014,6 @@ class CurrencyAPITests(InvenTreeAPITestCase): def test_exchange_endpoint(self): """Test that the currency exchange endpoint works as expected""" - response = self.get(reverse('api-currency-exchange'), expected_code=200) self.assertIn('base_currency', response.data) @@ -1027,7 +1021,6 @@ class CurrencyAPITests(InvenTreeAPITestCase): def test_refresh_endpoint(self): """Call the 'refresh currencies' endpoint""" - from djmoney.contrib.exchange.models import Rate # Delete any existing exchange rate data @@ -1053,7 +1046,6 @@ class NotesImageTest(InvenTreeAPITestCase): def test_invalid_files(self): """Test that invalid files are rejected.""" - n = NotesImage.objects.count() # Test upload of a simple text file @@ -1085,7 +1077,6 @@ class NotesImageTest(InvenTreeAPITestCase): def test_valid_image(self): """Test upload of a valid image file""" - n = NotesImage.objects.count() # Construct a simple image file @@ -1132,13 +1123,11 @@ class ProjectCodesTest(InvenTreeAPITestCase): def test_list(self): """Test that the list endpoint works as expected""" - response = self.get(self.url, expected_code=200) self.assertEqual(len(response.data), ProjectCode.objects.count()) def test_delete(self): """Test we can delete a project code via the API""" - n = ProjectCode.objects.count() # Get the first project code @@ -1155,7 +1144,6 @@ class ProjectCodesTest(InvenTreeAPITestCase): def test_duplicate_code(self): """Test that we cannot create two project codes with the same code""" - # Create a new project code response = self.post( self.url, @@ -1170,7 +1158,6 @@ class ProjectCodesTest(InvenTreeAPITestCase): def test_write_access(self): """Test that non-staff users have read-only access""" - # By default user has staff access, can create a new project code response = self.post( self.url, @@ -1240,13 +1227,11 @@ class CustomUnitAPITest(InvenTreeAPITestCase): def test_list(self): """Test API list functionality""" - response = self.get(self.url, expected_code=200) self.assertEqual(len(response.data), CustomUnit.objects.count()) def test_edit(self): """Test edit permissions for CustomUnit model""" - unit = CustomUnit.objects.first() # Try to edit without permission @@ -1278,7 +1263,6 @@ class CustomUnitAPITest(InvenTreeAPITestCase): def test_validation(self): """Test that validation works as expected""" - unit = CustomUnit.objects.first() self.user.is_staff = True diff --git a/InvenTree/company/api.py b/InvenTree/company/api.py index d2268cb961..88f8a875ac 100644 --- a/InvenTree/company/api.py +++ b/InvenTree/company/api.py @@ -382,7 +382,6 @@ class SupplierPartList(ListCreateDestroyAPIView): def get_serializer(self, *args, **kwargs): """Return serializer instance for this endpoint""" - # Do we wish to include extra detail? try: params = self.request.query_params @@ -489,7 +488,6 @@ class SupplierPriceBreakList(ListCreateAPI): def get_serializer(self, *args, **kwargs): """Return serializer instance for this endpoint""" - try: params = self.request.query_params diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index b48f58f3fa..0b0bfba960 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -160,7 +160,6 @@ class Company(InvenTreeNotesMixin, MetadataMixin, models.Model): This property exists for backwards compatibility """ - addr = self.primary_address return str(addr) if addr is not None else None @@ -287,7 +286,6 @@ class Address(models.Model): def __init__(self, *args, **kwargs): """Custom init function""" - super().__init__(*args, **kwargs) def __str__(self): @@ -312,7 +310,6 @@ class Address(models.Model): - If this address is marked as "primary", ensure that all other addresses for this company are marked as non-primary """ - others = list(Address.objects.filter(company=self.company).exclude(pk=self.pk).all()) # If this is the *only* address for this company, make it the primary one @@ -755,7 +752,6 @@ class SupplierPart(MetadataMixin, InvenTreeBarcodeMixin, common.models.MetaMixin def base_quantity(self, quantity=1) -> Decimal: """Calculate the base unit quantiy for a given quantity.""" - q = Decimal(quantity) * Decimal(self.pack_quantity_native) q = round(q, 10).normalize() @@ -780,7 +776,6 @@ class SupplierPart(MetadataMixin, InvenTreeBarcodeMixin, common.models.MetaMixin def update_available_quantity(self, quantity): """Update the available quantity for this SupplierPart""" - self.available = quantity self.availability_updated = datetime.now() self.save() @@ -918,7 +913,6 @@ class SupplierPriceBreak(common.models.PriceBreak): @receiver(post_save, sender=SupplierPriceBreak, dispatch_uid='post_save_supplier_price_break') def after_save_supplier_price(sender, instance, created, **kwargs): """Callback function when a SupplierPriceBreak is created or updated""" - if InvenTree.ready.canAppAccessDatabase() and not InvenTree.ready.isImportingData(): if instance.part and instance.part.part: @@ -928,7 +922,6 @@ def after_save_supplier_price(sender, instance, created, **kwargs): @receiver(post_delete, sender=SupplierPriceBreak, dispatch_uid='post_delete_supplier_price_break') def after_delete_supplier_price(sender, instance, **kwargs): """Callback function when a SupplierPriceBreak is deleted""" - if InvenTree.ready.canAppAccessDatabase() and not InvenTree.ready.isImportingData(): if instance.part and instance.part.part: diff --git a/InvenTree/company/serializers.py b/InvenTree/company/serializers.py index 06db55e6bc..a91176d862 100644 --- a/InvenTree/company/serializers.py +++ b/InvenTree/company/serializers.py @@ -342,7 +342,6 @@ class SupplierPartSerializer(InvenTreeTagModelSerializer): def __init__(self, *args, **kwargs): """Initialize this serializer with extra detail fields as required""" - # Check if 'available' quantity was supplied self.has_available_quantity = 'available' in kwargs.get('data', {}) @@ -402,7 +401,6 @@ class SupplierPartSerializer(InvenTreeTagModelSerializer): Fields: in_stock: Current stock quantity for each SupplierPart """ - queryset = queryset.annotate( in_stock=part.filters.annotate_total_stock() ) @@ -411,7 +409,6 @@ class SupplierPartSerializer(InvenTreeTagModelSerializer): def update(self, supplier_part, data): """Custom update functionality for the serializer""" - available = data.pop('available', None) response = super().update(supplier_part, data) @@ -423,7 +420,6 @@ class SupplierPartSerializer(InvenTreeTagModelSerializer): def create(self, validated_data): """Extract manufacturer data and process ManufacturerPart.""" - # Extract 'available' quantity from the serializer available = validated_data.pop('available', None) @@ -468,7 +464,6 @@ class SupplierPriceBreakSerializer(InvenTreeModelSerializer): def __init__(self, *args, **kwargs): """Initialize this serializer with extra fields as required""" - supplier_detail = kwargs.pop('supplier_detail', False) part_detail = kwargs.pop('part_detail', False) diff --git a/InvenTree/company/test_api.py b/InvenTree/company/test_api.py index 05fbfe8eb2..33720e30c2 100644 --- a/InvenTree/company/test_api.py +++ b/InvenTree/company/test_api.py @@ -20,7 +20,6 @@ class CompanyTest(InvenTreeAPITestCase): @classmethod def setUpTestData(cls): """Perform initialization for the unit test class""" - super().setUpTestData() # Create some company objects to work with @@ -148,7 +147,6 @@ class ContactTest(InvenTreeAPITestCase): @classmethod def setUpTestData(cls): """Perform init for this test class""" - super().setUpTestData() # Create some companies @@ -178,7 +176,6 @@ class ContactTest(InvenTreeAPITestCase): def test_list(self): """Test company list API endpoint""" - # List all results response = self.get(self.url, {}, expected_code=200) @@ -202,7 +199,6 @@ class ContactTest(InvenTreeAPITestCase): def test_create(self): """Test that we can create a new Contact object via the API""" - n = Contact.objects.count() company = Company.objects.first() @@ -232,7 +228,6 @@ class ContactTest(InvenTreeAPITestCase): def test_edit(self): """Test that we can edit a Contact via the API""" - # Get the first contact contact = Contact.objects.first() # Use this contact in the tests @@ -268,7 +263,6 @@ class ContactTest(InvenTreeAPITestCase): def test_delete(self): """Tests that we can delete a Contact via the API""" - # Get the last contact contact = Contact.objects.first() url = reverse('api-contact-detail', kwargs={'pk': contact.pk}) @@ -292,7 +286,6 @@ class AddressTest(InvenTreeAPITestCase): @classmethod def setUpTestData(cls): """Perform initialization for this test class""" - super().setUpTestData() cls.num_companies = 3 cls.num_addr = 3 @@ -323,14 +316,12 @@ class AddressTest(InvenTreeAPITestCase): def test_list(self): """Test listing all addresses without filtering""" - response = self.get(self.url, expected_code=200) self.assertEqual(len(response.data), self.num_companies * self.num_addr) def test_filter_list(self): """Test listing addresses filtered on company""" - company = Company.objects.first() response = self.get(self.url, {'company': company.pk}, expected_code=200) @@ -339,7 +330,6 @@ class AddressTest(InvenTreeAPITestCase): def test_create(self): """Test creating a new address""" - company = Company.objects.first() self.post(self.url, @@ -360,7 +350,6 @@ class AddressTest(InvenTreeAPITestCase): def test_get(self): """Test that objects are properly returned from a get""" - addr = Address.objects.first() url = reverse('api-address-detail', kwargs={'pk': addr.pk}) @@ -373,7 +362,6 @@ class AddressTest(InvenTreeAPITestCase): def test_edit(self): """Test editing an object""" - addr = Address.objects.first() url = reverse('api-address-detail', kwargs={'pk': addr.pk}) @@ -402,7 +390,6 @@ class AddressTest(InvenTreeAPITestCase): def test_delete(self): """Test deleting an object""" - addr = Address.objects.first() url = reverse('api-address-detail', kwargs={'pk': addr.pk}) @@ -567,7 +554,6 @@ class SupplierPartTest(InvenTreeAPITestCase): def test_available(self): """Tests for updating the 'available' field""" - url = reverse('api-supplier-part-list') # Should fail when sending an invalid 'available' field @@ -651,7 +637,6 @@ class CompanyMetadataAPITest(InvenTreeAPITestCase): def metatester(self, apikey, model): """Generic tester""" - modeldata = model.objects.first() # Useless test unless a model object is found @@ -680,7 +665,6 @@ class CompanyMetadataAPITest(InvenTreeAPITestCase): def test_metadata(self): """Test all endpoints""" - for apikey, model in { 'api-manufacturer-part-metadata': ManufacturerPart, 'api-supplier-part-metadata': SupplierPart, diff --git a/InvenTree/company/test_migrations.py b/InvenTree/company/test_migrations.py index 2dcfe5ee0c..d8285ee5a1 100644 --- a/InvenTree/company/test_migrations.py +++ b/InvenTree/company/test_migrations.py @@ -293,7 +293,6 @@ class TestAddressMigration(MigratorTestCase): def prepare(self): """Set up some companies with addresses""" - Company = self.old_state.apps.get_model('company', 'company') Company.objects.create(name='Company 1', address=self.short_l1) @@ -301,7 +300,6 @@ class TestAddressMigration(MigratorTestCase): def test_address_migration(self): """Test database state after applying the migration""" - Address = self.new_state.apps.get_model('company', 'address') Company = self.new_state.apps.get_model('company', 'company') @@ -329,7 +327,6 @@ class TestSupplierPartQuantity(MigratorTestCase): def prepare(self): """Prepare a number of SupplierPart objects""" - Part = self.old_state.apps.get_model('part', 'part') Company = self.old_state.apps.get_model('company', 'company') SupplierPart = self.old_state.apps.get_model('company', 'supplierpart') @@ -356,7 +353,6 @@ class TestSupplierPartQuantity(MigratorTestCase): def test_supplier_part_quantity(self): """Test that the supplier part quantity is correctly migrated.""" - SupplierPart = self.new_state.apps.get_model('company', 'supplierpart') for i, sp in enumerate(SupplierPart.objects.all()): diff --git a/InvenTree/company/test_supplier_parts.py b/InvenTree/company/test_supplier_parts.py index 5d49d2e0b2..21c34d9bbf 100644 --- a/InvenTree/company/test_supplier_parts.py +++ b/InvenTree/company/test_supplier_parts.py @@ -14,7 +14,6 @@ class SupplierPartPackUnitsTests(InvenTreeTestCase): def test_pack_quantity_dimensionless(self): """Test valid values for the 'pack_quantity' field""" - # Create a part without units (dimensionless) part = Part.objects.create(name='Test Part', description='Test part description', component=True) @@ -59,7 +58,6 @@ class SupplierPartPackUnitsTests(InvenTreeTestCase): def test_pack_quantity(self): """Test pack_quantity for a part with a specified dimension""" - # Create a part with units 'm' part = Part.objects.create(name='Test Part', description='Test part description', component=True, units='m') diff --git a/InvenTree/company/tests.py b/InvenTree/company/tests.py index c0a68d53f2..1f6e7ab56d 100644 --- a/InvenTree/company/tests.py +++ b/InvenTree/company/tests.py @@ -29,7 +29,6 @@ class CompanySimpleTest(TestCase): @classmethod def setUpTestData(cls): """Perform initialization for the tests in this class""" - super().setUpTestData() Company.objects.create(name='ABC Co.', @@ -194,7 +193,6 @@ class AddressTest(TestCase): def test_primary_constraint(self): """Test that there can only be one company-'primary=true' pair""" - Address.objects.create(company=self.c, primary=True) Address.objects.create(company=self.c, primary=False) @@ -211,7 +209,6 @@ class AddressTest(TestCase): def test_first_address_is_primary(self): """Test that first address related to company is always set to primary""" - addr = Address.objects.create(company=self.c) self.assertTrue(addr.primary) @@ -255,7 +252,6 @@ class ManufacturerPartSimpleTest(TestCase): def setUp(self): """Initialization for the unit tests in this class""" - # Create a manufacturer part self.part = Part.objects.get(pk=1) manufacturer = Company.objects.get(pk=1) diff --git a/InvenTree/company/views.py b/InvenTree/company/views.py index 147e5e407d..c8b2b31dc0 100644 --- a/InvenTree/company/views.py +++ b/InvenTree/company/views.py @@ -21,7 +21,6 @@ class CompanyIndex(InvenTreeRoleMixin, ListView): def get_context_data(self, **kwargs): """Add extra context data to the company index page""" - ctx = super().get_context_data(**kwargs) # Provide custom context data to the template, diff --git a/InvenTree/generic/states/api.py b/InvenTree/generic/states/api.py index 70e6256232..e549d237d1 100644 --- a/InvenTree/generic/states/api.py +++ b/InvenTree/generic/states/api.py @@ -27,7 +27,6 @@ class StatusView(APIView): def get_status_model(self, *args, **kwargs): """Return the StatusCode moedl based on extra parameters passed to the view""" - status_model = self.kwargs.get(self.MODEL_REF, None) if status_model is None: @@ -37,7 +36,6 @@ class StatusView(APIView): def get(self, request, *args, **kwargs): """Perform a GET request to learn information about status codes""" - status_class = self.get_status_model() if not inspect.isclass(status_class): diff --git a/InvenTree/generic/states/states.py b/InvenTree/generic/states/states.py index 06aa45ba22..7d23ca359d 100644 --- a/InvenTree/generic/states/states.py +++ b/InvenTree/generic/states/states.py @@ -163,7 +163,6 @@ class StatusCode(BaseEnum): @classmethod def template_context(cls): """Return a dict representation containing all required information for templates.""" - ret = {x.name: x.value for x in cls.values()} ret['list'] = cls.list() diff --git a/InvenTree/label/api.py b/InvenTree/label/api.py index aa5b4ce223..65fb1d38a3 100644 --- a/InvenTree/label/api.py +++ b/InvenTree/label/api.py @@ -41,7 +41,6 @@ class LabelFilterMixin: def get_items(self): """Return a list of database objects from query parameter""" - ids = [] # Construct a list of possible query parameter value options @@ -73,7 +72,6 @@ class LabelListView(LabelFilterMixin, ListAPI): As each 'label' instance may optionally define its own filters, the resulting queryset is the 'union' of the two. """ - queryset = super().filter_queryset(queryset) items = self.get_items() diff --git a/InvenTree/label/models.py b/InvenTree/label/models.py index fef3395ed9..18cc2e843f 100644 --- a/InvenTree/label/models.py +++ b/InvenTree/label/models.py @@ -399,7 +399,6 @@ class BuildLineLabel(LabelTemplate): def get_context_data(self, request): """Generate context data for each provided BuildLine object.""" - build_line = self.object_to_print return { diff --git a/InvenTree/label/serializers.py b/InvenTree/label/serializers.py index 576d4af8fa..ecceb8ed69 100644 --- a/InvenTree/label/serializers.py +++ b/InvenTree/label/serializers.py @@ -13,7 +13,6 @@ class LabelSerializerBase(InvenTreeModelSerializer): @staticmethod def label_fields(): """Generic serializer fields for a label template""" - return [ 'pk', 'name', diff --git a/InvenTree/label/tasks.py b/InvenTree/label/tasks.py index fc486cafab..0a0d2ac791 100644 --- a/InvenTree/label/tasks.py +++ b/InvenTree/label/tasks.py @@ -11,6 +11,5 @@ from label.models import LabelOutput @scheduled_task(ScheduledTask.DAILY) def cleanup_old_label_outputs(): """Remove old label outputs from the database""" - # Remove any label outputs which are older than 30 days LabelOutput.objects.filter(created__lte=timezone.now() - timedelta(days=5)).delete() diff --git a/InvenTree/label/tests.py b/InvenTree/label/tests.py index 5d88ccef60..5823cc9175 100644 --- a/InvenTree/label/tests.py +++ b/InvenTree/label/tests.py @@ -94,7 +94,6 @@ class LabelTest(InvenTreeAPITestCase): def test_print_part_label(self): """Actually 'print' a label, and ensure that the correct information is contained.""" - label_data = """ {% load barcode %} {% load report %} diff --git a/InvenTree/order/admin.py b/InvenTree/order/admin.py index b411b31699..838bb6bf53 100644 --- a/InvenTree/order/admin.py +++ b/InvenTree/order/admin.py @@ -173,7 +173,6 @@ class PurchaseOrderLineItemResource(PriceResourceMixin, InvenTreeResource): def dehydrate_purchase_price(self, line): """Return a string value of the 'purchase_price' field, rather than the 'Money' object""" - if line.purchase_price: return line.purchase_price.amount else: diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index f11414f202..2f9202d625 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -92,7 +92,6 @@ class OrderFilter(rest_filters.FilterSet): def filter_status(self, queryset, name, value): """Filter by integer status code""" - return queryset.filter(status=value) # Exact match for reference @@ -106,7 +105,6 @@ class OrderFilter(rest_filters.FilterSet): def filter_assigned_to_me(self, queryset, name, value): """Filter by orders which are assigned to the current user.""" - # Work out who "me" is! owners = Owner.get_owners_matching_user(self.request.user) @@ -122,7 +120,6 @@ class OrderFilter(rest_filters.FilterSet): Note that the overdue_filter() classmethod must be defined for the model """ - if str2bool(value): return queryset.filter(self.Meta.model.overdue_filter()) else: @@ -132,7 +129,6 @@ class OrderFilter(rest_filters.FilterSet): def filter_outstanding(self, queryset, name, value): """Generic filter for determining if an order is 'outstanding'""" - if str2bool(value): return queryset.filter(status__in=self.Meta.model.get_status_class().OPEN) else: @@ -147,7 +143,6 @@ class OrderFilter(rest_filters.FilterSet): def filter_has_project_code(self, queryset, name, value): """Filter by whether or not the order has a project code""" - if str2bool(value): return queryset.exclude(project_code=None) else: @@ -227,7 +222,6 @@ class PurchaseOrderList(PurchaseOrderMixin, APIDownloadMixin, ListCreateAPI): def create(self, request, *args, **kwargs): """Save user information on create.""" - data = self.clean_data(request.data) duplicate_order = data.pop('duplicate_order', None) @@ -275,7 +269,6 @@ class PurchaseOrderList(PurchaseOrderMixin, APIDownloadMixin, ListCreateAPI): def download_queryset(self, queryset, export_format): """Download the filtered queryset as a file""" - dataset = PurchaseOrderResource().export(queryset=queryset) filedata = dataset.export(export_format) @@ -432,7 +425,6 @@ class PurchaseOrderLineItemFilter(LineItemFilter): def filter_pending(self, queryset, name, value): """Filter by "pending" status (order status = pending)""" - if str2bool(value): return queryset.filter(order__status__in=PurchaseOrderStatusGroups.OPEN) else: @@ -511,7 +503,6 @@ class PurchaseOrderLineItemList(PurchaseOrderLineItemMixin, APIDownloadMixin, Li def download_queryset(self, queryset, export_format): """Download the requested queryset as a file""" - dataset = PurchaseOrderLineItemResource().export(queryset=queryset) filedata = dataset.export(export_format) @@ -562,7 +553,6 @@ class PurchaseOrderExtraLineList(GeneralExtraLineList, ListCreateAPI): def download_queryset(self, queryset, export_format): """Download this queryset as a file""" - dataset = PurchaseOrderExtraLineResource().export(queryset=queryset) filedata = dataset.export(export_format) filename = f"InvenTree_ExtraPurchaseOrderLines.{export_format}" @@ -662,7 +652,6 @@ class SalesOrderList(SalesOrderMixin, APIDownloadMixin, ListCreateAPI): def download_queryset(self, queryset, export_format): """Download this queryset as a file""" - dataset = SalesOrderResource().export(queryset=queryset) filedata = dataset.export(export_format) @@ -812,7 +801,6 @@ class SalesOrderLineItemList(SalesOrderLineItemMixin, APIDownloadMixin, ListCrea def download_queryset(self, queryset, export_format): """Download the requested queryset as a file""" - dataset = SalesOrderLineItemResource().export(queryset=queryset) filedata = dataset.export(export_format) @@ -849,7 +837,6 @@ class SalesOrderExtraLineList(GeneralExtraLineList, ListCreateAPI): def download_queryset(self, queryset, export_format): """Download this queryset as a file""" - dataset = SalesOrderExtraLineResource().export(queryset=queryset) filedata = dataset.export(export_format) filename = f"InvenTree_ExtraSalesOrderLines.{export_format}" @@ -1151,7 +1138,6 @@ class ReturnOrderList(ReturnOrderMixin, APIDownloadMixin, ListCreateAPI): def download_queryset(self, queryset, export_format): """Download this queryset as a file""" - dataset = ReturnOrderResource().export(queryset=queryset) filedata = dataset.export(export_format) filename = f"InvenTree_ReturnOrders.{export_format}" @@ -1252,7 +1238,6 @@ class ReturnOrderLineItemFilter(LineItemFilter): def filter_received(self, queryset, name, value): """Filter by 'received' field""" - if str2bool(value): return queryset.exclude(received_date=None) else: @@ -1267,7 +1252,6 @@ class ReturnOrderLineItemMixin: def get_serializer(self, *args, **kwargs): """Return serializer for this endpoint with extra data as requested""" - try: params = self.request.query_params @@ -1283,7 +1267,6 @@ class ReturnOrderLineItemMixin: def get_queryset(self, *args, **kwargs): """Return annotated queryset for this endpoint""" - queryset = super().get_queryset(*args, **kwargs) queryset = queryset.prefetch_related( @@ -1302,7 +1285,6 @@ class ReturnOrderLineItemList(ReturnOrderLineItemMixin, APIDownloadMixin, ListCr def download_queryset(self, queryset, export_format): """Download the requested queryset as a file""" - raise NotImplementedError("download_queryset not yet implemented for this endpoint") filter_backends = SEARCH_ORDER_FILTER @@ -1334,7 +1316,6 @@ class ReturnOrderExtraLineList(GeneralExtraLineList, ListCreateAPI): def download_queryset(self, queryset, export_format): """Download this queryset as a file""" - raise NotImplementedError("download_queryset not yet implemented") @@ -1389,7 +1370,6 @@ class OrderCalendarExport(ICalFeed): https://stackoverflow.com/questions/152248/can-i-use-http-basic-authentication-with-django https://www.djangosnippets.org/snippets/243/ """ - import base64 if request.user.is_authenticated: @@ -1435,7 +1415,6 @@ class OrderCalendarExport(ICalFeed): def title(self, obj): """Return calendar title.""" - if obj["ordertype"] == 'purchase-order': ordertype_title = _('Purchase Order') elif obj["ordertype"] == 'sales-order': @@ -1514,7 +1493,6 @@ class OrderCalendarExport(ICalFeed): def item_link(self, item): """Set the item link.""" - return construct_absolute_url(item.get_absolute_url()) diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 563b0b3696..297982d7c8 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -62,7 +62,6 @@ class TotalPriceMixin(models.Model): def save(self, *args, **kwargs): """Update the total_price field when saved""" - # Recalculate total_price for this order self.update_total_price(commit=False) super().save(*args, **kwargs) @@ -90,7 +89,6 @@ class TotalPriceMixin(models.Model): - Otherwise, return the currency associated with the company - Finally, return the default currency code """ - if self.order_currency: return self.order_currency @@ -102,7 +100,6 @@ class TotalPriceMixin(models.Model): def update_total_price(self, commit=True): """Recalculate and save the total_price for this order""" - self.total_price = self.calculate_total_price(target_currency=self.currency) if commit: @@ -200,7 +197,6 @@ class Order(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, Reference def clean(self): """Custom clean method for the generic order class""" - super().clean() # Check that the referenced 'contact' matches the correct 'company' @@ -216,7 +212,6 @@ class Order(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, Reference It requires any subclasses to implement the get_status_class() class method """ - today = datetime.now().date() return Q(status__in=cls.get_status_class().OPEN) & ~Q(target_date=None) & Q(target_date__lt=today) @@ -226,7 +221,6 @@ class Order(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, Reference Makes use of the overdue_filter() method to avoid code duplication """ - return self.__class__.objects.filter(pk=self.pk).filter(self.__class__.overdue_filter()).exists() description = models.CharField(max_length=250, blank=True, verbose_name=_('Description'), help_text=_('Order description (optional)')) @@ -284,7 +278,6 @@ class Order(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, Reference @classmethod def get_status_class(cls): """Return the enumeration class which represents the 'status' field for this model""" - raise NotImplementedError(f"get_status_class() not implemented for {__class__}") @@ -315,7 +308,6 @@ class PurchaseOrder(TotalPriceMixin, Order): @classmethod def api_defaults(cls, request): """Return default values for this model when issuing an API OPTIONS request""" - defaults = { 'reference': order.validators.generate_next_purchase_order_reference(), } @@ -362,7 +354,6 @@ class PurchaseOrder(TotalPriceMixin, Order): def __str__(self): """Render a string representation of this PurchaseOrder""" - return f"{self.reference} - {self.supplier.name if self.supplier else _('deleted')}" reference = models.CharField( @@ -768,7 +759,6 @@ class SalesOrder(TotalPriceMixin, Order): def __str__(self): """Render a string representation of this SalesOrder""" - return f"{self.reference} - {self.customer.name if self.customer else _('deleted')}" reference = models.CharField( @@ -895,7 +885,6 @@ class SalesOrder(TotalPriceMixin, Order): @transaction.atomic def issue_order(self): """Change this order from 'PENDING' to 'IN_PROGRESS'""" - if self.status == SalesOrderStatus.PENDING: self.status = SalesOrderStatus.IN_PROGRESS.value self.issue_date = datetime.now().date() @@ -1069,7 +1058,6 @@ class OrderLineItem(MetadataMixin, models.Model): Calls save method on the linked order """ - super().save(*args, **kwargs) self.order.save() @@ -1078,7 +1066,6 @@ class OrderLineItem(MetadataMixin, models.Model): Calls save method on the linked order """ - super().delete(*args, **kwargs) self.order.save() @@ -1093,7 +1080,6 @@ class OrderLineItem(MetadataMixin, models.Model): @property def total_line_price(self): """Return the total price for this line item""" - if self.price: return self.quantity * self.price @@ -1295,7 +1281,6 @@ class SalesOrderLineItem(OrderLineItem): def clean(self): """Perform extra validation steps for this SalesOrderLineItem instance""" - super().clean() if self.part: @@ -1729,7 +1714,6 @@ class ReturnOrder(TotalPriceMixin, Order): def __str__(self): """Render a string representation of this ReturnOrder""" - return f"{self.reference} - {self.customer.name if self.customer else _('no customer')}" reference = models.CharField( @@ -1810,7 +1794,6 @@ class ReturnOrder(TotalPriceMixin, Order): @transaction.atomic def complete_order(self): """Complete this ReturnOrder (if not already completed)""" - if self.status == ReturnOrderStatus.IN_PROGRESS: self.status = ReturnOrderStatus.COMPLETE.value self.complete_date = datetime.now().date() @@ -1825,7 +1808,6 @@ class ReturnOrder(TotalPriceMixin, Order): @transaction.atomic def issue_order(self): """Issue this ReturnOrder (if currently pending)""" - if self.status == ReturnOrderStatus.PENDING: self.status = ReturnOrderStatus.IN_PROGRESS.value self.issue_date = datetime.now().date() @@ -1842,7 +1824,6 @@ class ReturnOrder(TotalPriceMixin, Order): - Adds a tracking entry to the StockItem - Removes the 'customer' reference from the StockItem """ - # Prevent an item from being "received" multiple times if line.received_date is not None: logger.warning("receive_line_item called with item already returned") @@ -1908,7 +1889,6 @@ class ReturnOrderLineItem(OrderLineItem): def clean(self): """Perform extra validation steps for the ReturnOrderLineItem model""" - super().clean() if self.item and not self.item.serialized: @@ -1977,7 +1957,6 @@ class ReturnOrderAttachment(InvenTreeAttachment): @staticmethod def get_api_url(): """Return the API URL associated with the ReturnOrderAttachment class""" - return reverse('api-return-order-attachment-list') def getSubdir(self): diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py index 0153d9c5aa..b4825ce1a9 100644 --- a/InvenTree/order/serializers.py +++ b/InvenTree/order/serializers.py @@ -86,14 +86,12 @@ class AbstractOrderSerializer(serializers.Serializer): def validate_reference(self, reference): """Custom validation for the reference field""" - self.Meta.model.validate_reference_field(reference) return reference @staticmethod def annotate_queryset(queryset): """Add extra information to the queryset""" - queryset = queryset.annotate( line_items=SubqueryCount('lines') ) @@ -103,7 +101,6 @@ class AbstractOrderSerializer(serializers.Serializer): @staticmethod def order_fields(extra_fields): """Construct a set of fields for this serializer""" - return [ 'pk', 'creation_date', @@ -272,7 +269,6 @@ class PurchaseOrderCompleteSerializer(serializers.Serializer): def validate_accept_incomplete(self, value): """Check if the 'accept_incomplete' field is required""" - order = self.context['order'] if not value and not order.is_complete: @@ -910,7 +906,6 @@ class SalesOrderLineItemSerializer(InvenTreeModelSerializer): - "overdue" status (boolean field) - "available_quantity" """ - queryset = queryset.annotate( overdue=Case( When( @@ -1160,7 +1155,6 @@ class SalesOrderCompleteSerializer(serializers.Serializer): def validate_accept_incomplete(self, value): """Check if the 'accept_incomplete' field is required""" - order = self.context['order'] if not value and not order.is_completed(): @@ -1170,7 +1164,6 @@ class SalesOrderCompleteSerializer(serializers.Serializer): def get_context_data(self): """Custom context data for this serializer""" - order = self.context['order'] return { @@ -1498,7 +1491,6 @@ class ReturnOrderSerializer(AbstractOrderSerializer, TotalPriceMixin, InvenTreeM def __init__(self, *args, **kwargs): """Initialization routine for the serializer""" - customer_detail = kwargs.pop('customer_detail', False) super().__init__(*args, **kwargs) @@ -1509,7 +1501,6 @@ class ReturnOrderSerializer(AbstractOrderSerializer, TotalPriceMixin, InvenTreeM @staticmethod def annotate_queryset(queryset): """Custom annotation for the serializer queryset""" - queryset = AbstractOrderSerializer.annotate_queryset(queryset) queryset = queryset.annotate( @@ -1585,7 +1576,6 @@ class ReturnOrderLineItemReceiveSerializer(serializers.Serializer): def validate_line_item(self, item): """Validation for a single line item""" - if item.order != self.context['order']: raise ValidationError(_("Line item does not match return order")) @@ -1619,7 +1609,6 @@ class ReturnOrderReceiveSerializer(serializers.Serializer): def validate(self, data): """Perform data validation for this serializer""" - order = self.context['order'] if order.status != ReturnOrderStatus.IN_PROGRESS: raise ValidationError(_("Items can only be received against orders which are in progress")) @@ -1636,7 +1625,6 @@ class ReturnOrderReceiveSerializer(serializers.Serializer): @transaction.atomic def save(self): """Saving this serializer marks the returned items as received""" - order = self.context['order'] request = self.context['request'] @@ -1682,7 +1670,6 @@ class ReturnOrderLineItemSerializer(InvenTreeModelSerializer): def __init__(self, *args, **kwargs): """Initialization routine for the serializer""" - order_detail = kwargs.pop('order_detail', False) item_detail = kwargs.pop('item_detail', False) part_detail = kwargs.pop('part_detail', False) diff --git a/InvenTree/order/tasks.py b/InvenTree/order/tasks.py index 225c3a7976..6d6a2eb7d6 100644 --- a/InvenTree/order/tasks.py +++ b/InvenTree/order/tasks.py @@ -15,7 +15,6 @@ from plugin.events import trigger_event def notify_overdue_purchase_order(po: order.models.PurchaseOrder): """Notify users that a PurchaseOrder has just become 'overdue'""" - targets = [] if po.created_by: @@ -64,7 +63,6 @@ def check_overdue_purchase_orders(): - Look at the 'target_date' of any outstanding PurchaseOrder objects - If the 'target_date' expired *yesterday* then the order is just out of date """ - yesterday = datetime.now().date() - timedelta(days=1) overdue_orders = order.models.PurchaseOrder.objects.filter( @@ -78,7 +76,6 @@ def check_overdue_purchase_orders(): def notify_overdue_sales_order(so: order.models.SalesOrder): """Notify appropriate users that a SalesOrder has just become 'overdue'""" - targets = [] if so.created_by: @@ -127,7 +124,6 @@ def check_overdue_sales_orders(): - Look at the 'target_date' of any outstanding SalesOrder objects - If the 'target_date' expired *yesterday* then the order is just out of date """ - yesterday = datetime.now().date() - timedelta(days=1) overdue_orders = order.models.SalesOrder.objects.filter( diff --git a/InvenTree/order/test_api.py b/InvenTree/order/test_api.py index 9b3645c523..2b7aa76782 100644 --- a/InvenTree/order/test_api.py +++ b/InvenTree/order/test_api.py @@ -62,7 +62,6 @@ class PurchaseOrderTest(OrderTest): def test_options(self): """Test the PurchaseOrder OPTIONS endpoint.""" - self.assignRole('purchase_order.add') response = self.options(self.LIST_URL, expected_code=200) @@ -144,7 +143,6 @@ class PurchaseOrderTest(OrderTest): def test_total_price(self): """Unit tests for the 'total_price' field""" - # Ensure we have exchange rate data self.generate_exchange_rates() @@ -360,7 +358,6 @@ class PurchaseOrderTest(OrderTest): def test_po_duplicate(self): """Test that we can duplicate a PurchaseOrder via the API""" - self.assignRole('purchase_order.add') po = models.PurchaseOrder.objects.get(pk=1) @@ -511,7 +508,6 @@ class PurchaseOrderTest(OrderTest): def test_po_calendar(self): """Test the calendar export endpoint""" - # Create required purchase orders self.assignRole('purchase_order.add') @@ -1120,7 +1116,6 @@ class SalesOrderTest(OrderTest): def test_total_price(self): """Unit tests for the 'total_price' field""" - # Ensure we have exchange rate data self.generate_exchange_rates() @@ -1359,7 +1354,6 @@ class SalesOrderTest(OrderTest): def test_so_calendar(self): """Test the calendar export endpoint""" - # Create required sales orders self.assignRole('sales_order.add') @@ -1420,7 +1414,6 @@ class SalesOrderTest(OrderTest): def test_export(self): """Test we can export the SalesOrder list""" - n = models.SalesOrder.objects.count() # Check there are some sales orders @@ -1940,7 +1933,6 @@ class ReturnOrderTests(InvenTreeAPITestCase): def test_options(self): """Test the OPTIONS endpoint""" - self.assignRole('return_order.add') data = self.options(reverse('api-return-order-list'), expected_code=200).data @@ -1958,7 +1950,6 @@ class ReturnOrderTests(InvenTreeAPITestCase): def test_list(self): """Tests for the list endpoint""" - url = reverse('api-return-order-list') response = self.get(url, expected_code=200) @@ -2024,7 +2015,6 @@ class ReturnOrderTests(InvenTreeAPITestCase): def test_create(self): """Test creation of ReturnOrder via the API""" - url = reverse('api-return-order-list') # Do not have required permissions yet @@ -2055,7 +2045,6 @@ class ReturnOrderTests(InvenTreeAPITestCase): def test_update(self): """Test that we can update a ReturnOrder via the API""" - url = reverse('api-return-order-detail', kwargs={'pk': 1}) # Test detail endpoint @@ -2087,7 +2076,6 @@ class ReturnOrderTests(InvenTreeAPITestCase): def test_ro_issue(self): """Test the 'issue' order for a ReturnOrder""" - order = models.ReturnOrder.objects.get(pk=1) self.assertEqual(order.status, ReturnOrderStatus.PENDING) self.assertIsNone(order.issue_date) @@ -2106,7 +2094,6 @@ class ReturnOrderTests(InvenTreeAPITestCase): def test_receive(self): """Test that we can receive items against a ReturnOrder""" - customer = Company.objects.get(pk=4) # Create an order @@ -2209,7 +2196,6 @@ class ReturnOrderTests(InvenTreeAPITestCase): def test_ro_calendar(self): """Test the calendar export endpoint""" - # Full test is in test_po_calendar. Since these use the same backend, test only # that the endpoint is available url = reverse('api-po-so-calendar', kwargs={'ordertype': 'return-order'}) @@ -2243,7 +2229,6 @@ class OrderMetadataAPITest(InvenTreeAPITestCase): def metatester(self, apikey, model): """Generic tester""" - modeldata = model.objects.first() # Useless test unless a model object is found @@ -2272,7 +2257,6 @@ class OrderMetadataAPITest(InvenTreeAPITestCase): def test_metadata(self): """Test all endpoints""" - for apikey, model in { 'api-po-metadata': models.PurchaseOrder, 'api-po-line-metadata': models.PurchaseOrderLineItem, diff --git a/InvenTree/order/test_sales_order.py b/InvenTree/order/test_sales_order.py index 9ac410be05..86eaaf37e0 100644 --- a/InvenTree/order/test_sales_order.py +++ b/InvenTree/order/test_sales_order.py @@ -72,7 +72,6 @@ class SalesOrderTest(TestCase): def test_so_reference(self): """Unit tests for sales order generation""" - # Test that a good reference is created when we have no existing orders SalesOrder.objects.all().delete() @@ -80,7 +79,6 @@ class SalesOrderTest(TestCase): def test_rebuild_reference(self): """Test that the 'reference_int' field gets rebuilt when the model is saved""" - self.assertEqual(self.order.reference_int, 1234) self.order.reference = '999' @@ -121,7 +119,6 @@ class SalesOrderTest(TestCase): def test_add_duplicate_line_item(self): """Adding a duplicate line item to a SalesOrder is accepted""" - for ii in range(1, 5): SalesOrderLineItem.objects.create(order=self.order, part=self.part, quantity=ii) @@ -283,14 +280,12 @@ class SalesOrderTest(TestCase): def test_shipment_delivery(self): """Test the shipment delivery settings""" - # Shipment delivery date should be empty before setting date self.assertIsNone(self.shipment.delivery_date) self.assertFalse(self.shipment.is_delivered()) def test_overdue_notification(self): """Test overdue sales order notification""" - self.order.created_by = get_user_model().objects.get(pk=3) self.order.responsible = Owner.create(obj=Group.objects.get(pk=2)) self.order.target_date = datetime.now().date() - timedelta(days=1) @@ -311,7 +306,6 @@ class SalesOrderTest(TestCase): - The responsible user should receive a notification - The creating user should *not* receive a notification """ - SalesOrder.objects.create( customer=self.customer, reference='1234567', diff --git a/InvenTree/order/tests.py b/InvenTree/order/tests.py index 151a2ba631..5b9ee89b0d 100644 --- a/InvenTree/order/tests.py +++ b/InvenTree/order/tests.py @@ -39,7 +39,6 @@ class OrderTest(TestCase): def test_basics(self): """Basic tests e.g. repr functions etc.""" - for pk in range(1, 8): order = PurchaseOrder.objects.get(pk=pk) @@ -53,7 +52,6 @@ class OrderTest(TestCase): def test_rebuild_reference(self): """Test that the reference_int field is correctly updated when the model is saved""" - order = PurchaseOrder.objects.get(pk=1) order.save() self.assertEqual(order.reference_int, 1) @@ -219,7 +217,6 @@ class OrderTest(TestCase): def test_receive_pack_size(self): """Test receiving orders from suppliers with different pack_size values""" - prt = Part.objects.get(pk=1) sup = Company.objects.get(pk=1) @@ -366,7 +363,6 @@ class OrderTest(TestCase): - The responsible user(s) should receive a notification - The creating user should *not* receive a notification """ - po = PurchaseOrder.objects.create( supplier=Company.objects.get(pk=1), reference='XYZABC', diff --git a/InvenTree/order/validators.py b/InvenTree/order/validators.py index ee9c832e05..a340002c9d 100644 --- a/InvenTree/order/validators.py +++ b/InvenTree/order/validators.py @@ -3,7 +3,6 @@ def generate_next_sales_order_reference(): """Generate the next available SalesOrder reference""" - from order.models import SalesOrder return SalesOrder.generate_reference() @@ -11,7 +10,6 @@ def generate_next_sales_order_reference(): def generate_next_purchase_order_reference(): """Generate the next available PurchasesOrder reference""" - from order.models import PurchaseOrder return PurchaseOrder.generate_reference() @@ -19,7 +17,6 @@ def generate_next_purchase_order_reference(): def generate_next_return_order_reference(): """Generate the next available ReturnOrder reference""" - from order.models import ReturnOrder return ReturnOrder.generate_reference() @@ -27,7 +24,6 @@ def generate_next_return_order_reference(): def validate_sales_order_reference_pattern(pattern): """Validate the SalesOrder reference 'pattern' setting""" - from order.models import SalesOrder SalesOrder.validate_reference_pattern(pattern) @@ -35,7 +31,6 @@ def validate_sales_order_reference_pattern(pattern): def validate_purchase_order_reference_pattern(pattern): """Validate the PurchaseOrder reference 'pattern' setting""" - from order.models import PurchaseOrder PurchaseOrder.validate_reference_pattern(pattern) @@ -43,7 +38,6 @@ def validate_purchase_order_reference_pattern(pattern): def validate_return_order_reference_pattern(pattern): """Validate the ReturnOrder reference 'pattern' setting""" - from order.models import ReturnOrder ReturnOrder.validate_reference_pattern(pattern) @@ -51,7 +45,6 @@ def validate_return_order_reference_pattern(pattern): def validate_sales_order_reference(value): """Validate that the SalesOrder reference field matches the required pattern""" - from order.models import SalesOrder SalesOrder.validate_reference_field(value) @@ -59,7 +52,6 @@ def validate_sales_order_reference(value): def validate_purchase_order_reference(value): """Validate that the PurchaseOrder reference field matches the required pattern""" - from order.models import PurchaseOrder PurchaseOrder.validate_reference_field(value) @@ -67,7 +59,6 @@ def validate_purchase_order_reference(value): def validate_return_order_reference(value): """Validate that the ReturnOrder reference field matches the required pattern""" - from order.models import ReturnOrder ReturnOrder.validate_reference_field(value) diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index fb221b0724..ca2ecf1ea4 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -68,7 +68,6 @@ class PartResource(InvenTreeResource): def dehydrate_min_cost(self, part): """Render minimum cost value for this Part""" - min_cost = part.pricing.overall_min if part.pricing else None if min_cost is not None: @@ -76,7 +75,6 @@ class PartResource(InvenTreeResource): def dehydrate_max_cost(self, part): """Render maximum cost value for this Part""" - max_cost = part.pricing.overall_max if part.pricing else None if max_cost is not None: @@ -97,7 +95,6 @@ class PartResource(InvenTreeResource): def after_import(self, dataset, result, using_transactions, dry_run, **kwargs): """Rebuild MPTT tree structure after importing Part data""" - super().after_import(dataset, result, using_transactions, dry_run, **kwargs) # Rebuild the Part tree(s) @@ -203,7 +200,6 @@ class PartCategoryResource(InvenTreeResource): def after_import(self, dataset, result, using_transactions, dry_run, **kwargs): """Rebuild MPTT tree structure after importing PartCategory data""" - super().after_import(dataset, result, using_transactions, dry_run, **kwargs) # Rebuild the PartCategory tree(s) @@ -284,7 +280,6 @@ class BomItemResource(InvenTreeResource): def dehydrate_min_cost(self, item): """Render minimum cost value for the BOM line item""" - min_price = item.sub_part.pricing.overall_min if item.sub_part.pricing else None if min_price is not None: @@ -292,7 +287,6 @@ class BomItemResource(InvenTreeResource): def dehydrate_max_cost(self, item): """Render maximum cost value for the BOM line item""" - max_price = item.sub_part.pricing.overall_max if item.sub_part.pricing else None if max_price is not None: @@ -307,7 +301,6 @@ class BomItemResource(InvenTreeResource): def before_export(self, queryset, *args, **kwargs): """Perform before exporting data""" - self.is_importing = kwargs.get('importing', False) self.include_pricing = kwargs.pop('include_pricing', False) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 9251573ac5..5670b2c82e 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -50,7 +50,6 @@ class CategoryMixin: def get_queryset(self, *args, **kwargs): """Return an annotated queryset for the CategoryDetail endpoint""" - queryset = super().get_queryset(*args, **kwargs) queryset = part_serializers.CategorySerializer.annotate_queryset(queryset) return queryset @@ -77,7 +76,6 @@ class CategoryList(CategoryMixin, APIDownloadMixin, ListCreateAPI): def download_queryset(self, queryset, export_format): """Download the filtered queryset as a data file""" - dataset = PartCategoryResource().export(queryset=queryset) filedata = dataset.export(export_format) filename = f"InvenTree_Categories.{export_format}" @@ -192,7 +190,6 @@ class CategoryDetail(CategoryMixin, CustomRetrieveUpdateDestroyAPI): def get_serializer(self, *args, **kwargs): """Add additional context based on query parameters""" - try: params = self.request.query_params @@ -466,7 +463,6 @@ class PartScheduling(RetrieveAPI): def retrieve(self, request, *args, **kwargs): """Return scheduling information for the referenced Part instance""" - part = self.get_object() schedule = [] @@ -674,7 +670,6 @@ class PartRequirements(RetrieveAPI): def retrieve(self, request, *args, **kwargs): """Construct a response detailing Part requirements""" - part = self.get_object() data = { @@ -700,13 +695,11 @@ class PartPricingDetail(RetrieveUpdateAPI): def get_object(self): """Return the PartPricing object associated with the linked Part""" - part = super().get_object() return part.pricing def _get_serializer(self, *args, **kwargs): """Return a part pricing serializer object""" - part = self.get_object() kwargs['instance'] = part.pricing @@ -825,7 +818,6 @@ class PartFilter(rest_filters.FilterSet): def filter_has_units(self, queryset, name, value): """Filter by whether the Part has units or not""" - if str2bool(value): return queryset.exclude(units='') else: @@ -836,7 +828,6 @@ class PartFilter(rest_filters.FilterSet): def filter_has_ipn(self, queryset, name, value): """Filter by whether the Part has an IPN (internal part number) or not""" - if str2bool(value): return queryset.exclude(IPN='') else: @@ -860,7 +851,6 @@ class PartFilter(rest_filters.FilterSet): def filter_low_stock(self, queryset, name, value): """Filter by "low stock" status.""" - if str2bool(value): # Ignore any parts which do not have a specified 'minimum_stock' level # Filter items which have an 'in_stock' level lower than 'minimum_stock' @@ -874,7 +864,6 @@ class PartFilter(rest_filters.FilterSet): def filter_has_stock(self, queryset, name, value): """Filter by whether the Part has any stock""" - if str2bool(value): return queryset.filter(Q(in_stock__gt=0)) else: @@ -885,7 +874,6 @@ class PartFilter(rest_filters.FilterSet): def filter_unallocated_stock(self, queryset, name, value): """Filter by whether the Part has unallocated stock""" - if str2bool(value): return queryset.filter(Q(unallocated_stock__gt=0)) else: @@ -905,7 +893,6 @@ class PartFilter(rest_filters.FilterSet): def filter_exclude_tree(self, queryset, name, part): """Exclude all parts and variants 'down' from the specified part from the queryset""" - children = part.get_descendants(include_self=True) return queryset.exclude(id__in=children) @@ -914,7 +901,6 @@ class PartFilter(rest_filters.FilterSet): def filter_ancestor(self, queryset, name, part): """Limit queryset to descendants of the specified ancestor part""" - descendants = part.get_descendants(include_self=False) return queryset.filter(id__in=descendants) @@ -922,14 +908,12 @@ class PartFilter(rest_filters.FilterSet): def filter_variant_of(self, queryset, name, part): """Limit queryset to direct children (variants) of the specified part""" - return queryset.filter(id__in=part.get_children()) in_bom_for = rest_filters.ModelChoiceFilter(label='In BOM Of', queryset=Part.objects.all(), method='filter_in_bom') def filter_in_bom(self, queryset, name, part): """Limit queryset to parts in the BOM for the specified part""" - bom_parts = part.get_parts_in_bom() return queryset.filter(id__in=[p.pk for p in bom_parts]) @@ -937,7 +921,6 @@ class PartFilter(rest_filters.FilterSet): def filter_has_pricing(self, queryset, name, value): """Filter the queryset based on whether pricing information is available for the sub_part""" - q_a = Q(pricing_data=None) q_b = Q(pricing_data__overall_min=None, pricing_data__overall_max=None) @@ -950,7 +933,6 @@ class PartFilter(rest_filters.FilterSet): def filter_has_stocktake(self, queryset, name, value): """Filter the queryset based on whether stocktake data is available""" - if str2bool(value): return queryset.exclude(last_stocktake=None) else: @@ -960,7 +942,6 @@ class PartFilter(rest_filters.FilterSet): def filter_stock_to_build(self, queryset, name, value): """Filter the queryset based on whether part stock is required for a pending BuildOrder""" - if str2bool(value): # Return parts which are required for a build order, but have not yet been allocated return queryset.filter(required_for_build_orders__gt=F('allocated_to_build_orders')) @@ -972,7 +953,6 @@ class PartFilter(rest_filters.FilterSet): def filter_depleted_stock(self, queryset, name, value): """Filter the queryset based on whether the part is fully depleted of stock""" - if str2bool(value): return queryset.filter(Q(in_stock=0) & ~Q(stock_item_count=0)) else: @@ -1234,7 +1214,6 @@ class PartList(PartMixin, APIDownloadMixin, ListCreateAPI): - Only parts which have a matching parameter are returned - Queryset is ordered based on parameter value """ - # Extract "ordering" parameter from query args ordering = self.request.query_params.get('ordering', None) @@ -1379,7 +1358,6 @@ class PartParameterTemplateFilter(rest_filters.FilterSet): def filter_has_choices(self, queryset, name, value): """Filter queryset to include only PartParameterTemplates with choices.""" - if str2bool(value): return queryset.exclude(Q(choices=None) | Q(choices='')) else: @@ -1392,7 +1370,6 @@ class PartParameterTemplateFilter(rest_filters.FilterSet): def filter_has_units(self, queryset, name, value): """Filter queryset to include only PartParameterTemplates with units.""" - if str2bool(value): return queryset.exclude(Q(units=None) | Q(units='')) else: @@ -1488,7 +1465,6 @@ class PartParameterAPIMixin: - part_detail - template_detail """ - try: kwargs['part_detail'] = str2bool(self.request.GET.get('part_detail', False)) kwargs['template_detail'] = str2bool(self.request.GET.get('template_detail', True)) @@ -1515,7 +1491,6 @@ class PartParameterFilter(rest_filters.FilterSet): If 'include_variants' query parameter is provided, filter against variant parts also """ - try: include_variants = str2bool(self.request.GET.get('include_variants', False)) except AttributeError: @@ -1679,7 +1654,6 @@ class BomFilter(rest_filters.FilterSet): def filter_available_stock(self, queryset, name, value): """Filter the queryset based on whether each line item has any available stock""" - if str2bool(value): return queryset.filter(available_stock__gt=0) else: @@ -1689,7 +1663,6 @@ class BomFilter(rest_filters.FilterSet): def filter_on_order(self, queryset, name, value): """Filter the queryset based on whether each line item has any stock on order""" - if str2bool(value): return queryset.filter(on_order__gt=0) else: @@ -1699,7 +1672,6 @@ class BomFilter(rest_filters.FilterSet): def filter_has_pricing(self, queryset, name, value): """Filter the queryset based on whether pricing information is available for the sub_part""" - q_a = Q(sub_part__pricing_data=None) q_b = Q(sub_part__pricing_data__overall_min=None, sub_part__pricing_data__overall_max=None) @@ -1722,7 +1694,6 @@ class BomMixin: - part_detail - sub_part_detail """ - # Do we wish to include extra detail? try: kwargs['part_detail'] = str2bool(self.request.GET.get('part_detail', None)) @@ -1760,7 +1731,6 @@ class BomList(BomMixin, ListCreateDestroyAPIView): def list(self, request, *args, **kwargs): """Return serialized list response for this endpoint""" - queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) diff --git a/InvenTree/part/apps.py b/InvenTree/part/apps.py index f6e54797af..9e59236cea 100644 --- a/InvenTree/part/apps.py +++ b/InvenTree/part/apps.py @@ -49,7 +49,6 @@ class PartConfig(AppConfig): Prevents issues with state machine if the server is restarted mid-update """ - from .models import PartPricing if isImportingData(): diff --git a/InvenTree/part/bom.py b/InvenTree/part/bom.py index bc12e40d4f..525e60987b 100644 --- a/InvenTree/part/bom.py +++ b/InvenTree/part/bom.py @@ -63,7 +63,6 @@ def ExportBom(part: Part, fmt='csv', cascade: bool = False, max_levels: int = No Returns: StreamingHttpResponse: Response that can be passed to the endpoint """ - parameter_data = str2bool(kwargs.get('parameter_data', False)) stock_data = str2bool(kwargs.get('stock_data', False)) supplier_data = str2bool(kwargs.get('supplier_data', False)) diff --git a/InvenTree/part/filters.py b/InvenTree/part/filters.py index dbdc907bfd..e48909dd87 100644 --- a/InvenTree/part/filters.py +++ b/InvenTree/part/filters.py @@ -43,7 +43,6 @@ def annotate_on_order_quantity(reference: str = ''): Note that in addition to the 'quantity' on order, we must also take into account 'pack_quantity'. """ - # Filter only 'active' purhase orders # Filter only line with outstanding quantity order_filter = Q( @@ -85,7 +84,6 @@ def annotate_total_stock(reference: str = ''): reference: The relationship reference of the part from the current model e.g. 'part' stock_filter: Q object which defines how to filter the stock items """ - # Stock filter only returns 'in stock' items stock_filter = stock.models.StockItem.IN_STOCK_FILTER @@ -107,7 +105,6 @@ def annotate_build_order_requirements(reference: str = ''): - We are interested in the 'quantity' of each BuildLine item """ - # Active build orders only build_filter = Q(build__status__in=BuildStatusGroups.ACTIVE_CODES) @@ -132,7 +129,6 @@ def annotate_build_order_allocations(reference: str = ''): reference: The relationship reference of the part from the current model build_filter: Q object which defines how to filter the allocation items """ - # Build filter only returns 'active' build orders build_filter = Q(build_line__build__status__in=BuildStatusGroups.ACTIVE_CODES) @@ -157,7 +153,6 @@ def annotate_sales_order_allocations(reference: str = ''): reference: The relationship reference of the part from the current model order_filter: Q object which defines how to filter the allocation items """ - # Order filter only returns incomplete shipments for open orders order_filter = Q( line__order__status__in=SalesOrderStatusGroups.OPEN, @@ -183,7 +178,6 @@ def variant_stock_query(reference: str = '', filter: Q = stock.models.StockItem. reference: The relationship reference of the part from the current model filter: Q object which defines how to filter the returned StockItem instances """ - return stock.models.StockItem.objects.filter( part__tree_id=OuterRef(f'{reference}tree_id'), part__lft__gt=OuterRef(f'{reference}lft'), @@ -198,7 +192,6 @@ def annotate_variant_quantity(subquery: Q, reference: str = 'quantity'): subquery: A 'variant_stock_query' Q object reference: The relationship reference of the variant stock items from the current queryset """ - return Coalesce( Subquery( subquery.annotate( @@ -216,7 +209,6 @@ def annotate_category_parts(): - Includes parts in subcategories also - Requires subquery to perform annotation """ - # Construct a subquery to provide all parts in this category and any subcategories: subquery = part.models.Part.objects.exclude(category=None).filter( category__tree_id=OuterRef('tree_id'), @@ -250,9 +242,7 @@ def filter_by_parameter(queryset, template_id: int, value: str, func: str = ''): Returns: A queryset of Part objects filtered by the given parameter """ - # TODO - return queryset @@ -268,7 +258,6 @@ def order_by_parameter(queryset, template_id: int, ascending=True): Returns: A queryset of Part objects ordered by the given parameter """ - template_filter = part.models.PartParameter.objects.filter( template__id=template_id, part_id=OuterRef('id'), diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 4527d0c078..ac6b8c123e 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -453,7 +453,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) If the part image has been updated, then check if the "old" (previous) image is still used by another part. If not, it is considered "orphaned" and will be deleted. """ - if self.pk: try: previous = Part.objects.get(pk=self.pk) @@ -556,7 +555,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) This function is exposed to any Validation plugins, and thus can be customized. """ - from plugin.registry import registry for plugin in registry.with_mixin('validation'): @@ -579,7 +577,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) - Validation is handled by custom plugins - By default, no validation checks are performed """ - from plugin.registry import registry for plugin in registry.with_mixin('validation'): @@ -626,7 +623,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) Raises: ValidationError if serial number is invalid and raise_error = True """ - serial = str(serial).strip() # First, throw the serial number against each of the loaded validation plugins @@ -682,7 +678,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) def find_conflicting_serial_numbers(self, serials: list): """For a provided list of serials, return a list of those which are conflicting.""" - conflicts = [] for serial in serials: @@ -704,7 +699,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) Returns: The latest serial number specified for this part, or None """ - stock = StockModels.StockItem.objects.all().exclude(serial=None).exclude(serial='') # Generate a query for any stock items for this part variant tree with non-empty serial numbers @@ -1237,7 +1231,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) @property def can_build(self): """Return the number of units that can be build with available stock.""" - import part.filters # If this part does NOT have a BOM, result is simply the currently available stock @@ -1436,7 +1429,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) def allocation_count(self, **kwargs): """Return the total quantity of stock allocated for this part, against both build orders and sales orders.""" - if self.id is None: # If this instance has not been saved, foreign-key lookups will fail return 0 @@ -1562,7 +1554,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) So we construct a query for each case, and combine them... """ - # Cache all *parent* parts try: parents = self.get_ancestors(include_self=False) @@ -1599,7 +1590,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) Includes consideration of inherited BOMs """ - # Grab a queryset of all BomItem objects which "require" this part bom_items = BomItem.objects.filter( self.get_used_in_bom_item_filter( @@ -1738,7 +1728,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) def update_pricing(self): """Recalculate cached pricing for this Part instance""" - self.pricing.update_pricing() @property @@ -1748,7 +1737,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) If there is no PartPricing database entry defined for this Part, it will first be created, and then returned. """ - try: pricing = PartPricing.objects.get(part=self) except PartPricing.DoesNotExist: @@ -1768,7 +1756,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) create: Whether or not a new PartPricing object should be created if it does not already exist test: Whether or not the pricing update is allowed during unit tests """ - try: self.refresh_from_db() except Part.DoesNotExist: @@ -2102,7 +2089,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) def getTestTemplateMap(self, **kwargs): """Return a map of all test templates associated with this Part""" - templates = {} for template in self.getTestTemplates(**kwargs): @@ -2160,7 +2146,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) Note that some supplier parts may have a different pack_quantity attribute, and this needs to be taken into account! """ - quantity = 0 # Iterate through all supplier parts @@ -2213,7 +2198,6 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel) @property def latest_stocktake(self): """Return the latest PartStocktake object associated with this part (if one exists)""" - return self.stocktakes.order_by('-pk').first() @property @@ -2356,7 +2340,6 @@ class PartPricing(common.models.MetaMixin): @property def is_valid(self): """Return True if the cached pricing is valid""" - return self.updated is not None def convert(self, money): @@ -2364,7 +2347,6 @@ class PartPricing(common.models.MetaMixin): If a MissingRate error is raised, ignore it and return None """ - if money is None: return None @@ -2380,7 +2362,6 @@ class PartPricing(common.models.MetaMixin): def schedule_for_update(self, counter: int = 0, test: bool = False): """Schedule this pricing to be updated""" - import InvenTree.ready # If we are running within CI, only schedule the update if the test flag is set @@ -2446,7 +2427,6 @@ class PartPricing(common.models.MetaMixin): def update_pricing(self, counter: int = 0, cascade: bool = True): """Recalculate all cost data for the referenced Part instance""" - # If importing data, skip pricing update if InvenTree.ready.isImportingData(): return @@ -2485,7 +2465,6 @@ class PartPricing(common.models.MetaMixin): def update_assemblies(self, counter: int = 0): """Schedule updates for any assemblies which use this part""" - # If the linked Part is used in any assemblies, schedule a pricing update for those assemblies used_in_parts = self.part.get_used_in() @@ -2494,7 +2473,6 @@ class PartPricing(common.models.MetaMixin): def update_templates(self, counter: int = 0): """Schedule updates for any template parts above this part""" - templates = self.part.get_ancestors(include_self=False) for p in templates: @@ -2502,7 +2480,6 @@ class PartPricing(common.models.MetaMixin): def save(self, *args, **kwargs): """Whenever pricing model is saved, automatically update overall prices""" - # Update the currency which was used to perform the calculation self.currency = currency_code_default() @@ -2524,7 +2501,6 @@ class PartPricing(common.models.MetaMixin): Note: The cumulative costs are calculated based on the specified default currency """ - if not self.part.assembly: # Not an assembly - no BOM pricing self.bom_cost_min = None @@ -2603,7 +2579,6 @@ class PartPricing(common.models.MetaMixin): Purchase history only takes into account "completed" purchase orders. """ - # Find all line items for completed orders which reference this part line_items = OrderModels.PurchaseOrderLineItem.objects.filter( order__status=PurchaseOrderStatus.COMPLETE.value, @@ -2670,7 +2645,6 @@ class PartPricing(common.models.MetaMixin): def update_internal_cost(self, save=True): """Recalculate internal cost for the referenced Part instance""" - min_int_cost = None max_int_cost = None @@ -2704,7 +2678,6 @@ class PartPricing(common.models.MetaMixin): - The limits are simply the lower and upper bounds of available SupplierPriceBreaks - We do not take "quantity" into account here """ - min_sup_cost = None max_sup_cost = None @@ -2745,7 +2718,6 @@ class PartPricing(common.models.MetaMixin): Here we track the min/max costs of any variant parts. """ - variant_min = None variant_max = None @@ -2785,7 +2757,6 @@ class PartPricing(common.models.MetaMixin): Here we simply take the minimum / maximum values of the other calculated fields. """ - overall_min = None overall_max = None @@ -2851,7 +2822,6 @@ class PartPricing(common.models.MetaMixin): def update_sale_cost(self, save=True): """Recalculate sale cost data""" - # Iterate through the sell price breaks min_sell_price = None max_sell_price = None @@ -3091,7 +3061,6 @@ class PartStocktake(models.Model): @receiver(post_save, sender=PartStocktake, dispatch_uid='post_save_stocktake') def update_last_stocktake(sender, instance, created, **kwargs): """Callback function when a PartStocktake instance is created / edited""" - # When a new PartStocktake instance is create, update the last_stocktake date for the Part if created: try: @@ -3104,7 +3073,6 @@ def update_last_stocktake(sender, instance, created, **kwargs): def save_stocktake_report(instance, filename): """Save stocktake reports to the correct subdirectory""" - filename = os.path.basename(filename) return os.path.join('stocktake', 'report', filename) @@ -3397,7 +3365,6 @@ class PartParameterTemplate(MetadataMixin, models.Model): - A 'checkbox' field cannot have 'choices' set - A 'checkbox' field cannot have 'units' set """ - super().clean() # Check that checkbox parameters do not have units or choices @@ -3450,7 +3417,6 @@ class PartParameterTemplate(MetadataMixin, models.Model): def get_choices(self): """Return a list of choices for this parameter template""" - if not self.choices: return [] @@ -3496,7 +3462,6 @@ class PartParameterTemplate(MetadataMixin, models.Model): @receiver(post_save, sender=PartParameterTemplate, dispatch_uid='post_save_part_parameter_template') def post_save_part_parameter_template(sender, instance, created, **kwargs): """Callback function when a PartParameterTemplate is created or saved""" - import part.tasks as part_tasks if InvenTree.ready.canAppAccessDatabase() and not InvenTree.ready.isImportingData(): @@ -3540,7 +3505,6 @@ class PartParameter(MetadataMixin, models.Model): def save(self, *args, **kwargs): """Custom save method for the PartParameter model.""" - # Validate the PartParameter before saving self.calculate_numeric_value() @@ -3553,7 +3517,6 @@ class PartParameter(MetadataMixin, models.Model): def clean(self): """Validate the PartParameter before saving to the database.""" - super().clean() # Validate the parameter data against the template units @@ -3597,7 +3560,6 @@ class PartParameter(MetadataMixin, models.Model): - If a 'units' field is provided, then the data will be converted to the base SI unit. - Otherwise, we'll try to do a simple float cast """ - if self.template.units: try: self.data_numeric = InvenTree.conversion.convert_physical_value(self.data, self.template.units) @@ -3775,7 +3737,6 @@ class BomItem(DataImportMixin, MetadataMixin, models.Model): def get_assemblies(self): """Return a list of assemblies which use this BomItem""" - assemblies = [self.part] if self.inherited: @@ -4087,7 +4048,6 @@ class BomItem(DataImportMixin, MetadataMixin, models.Model): @receiver(post_save, sender=BomItem, dispatch_uid='update_bom_build_lines') def update_bom_build_lines(sender, instance, created, **kwargs): """Update existing build orders when a BomItem is created or edited""" - if InvenTree.ready.canAppAccessDatabase() and not InvenTree.ready.isImportingData(): import build.tasks InvenTree.tasks.offload_task( @@ -4101,7 +4061,6 @@ def update_bom_build_lines(sender, instance, created, **kwargs): @receiver(post_save, sender=PartInternalPriceBreak, dispatch_uid='post_save_internal_price_break') def update_pricing_after_edit(sender, instance, created, **kwargs): """Callback function when a part price break is created or updated""" - # Update part pricing *unless* we are importing data if InvenTree.ready.canAppAccessDatabase() and not InvenTree.ready.isImportingData(): instance.part.schedule_pricing_update(create=True) @@ -4112,7 +4071,6 @@ def update_pricing_after_edit(sender, instance, created, **kwargs): @receiver(post_delete, sender=PartInternalPriceBreak, dispatch_uid='post_delete_internal_price_break') def update_pricing_after_delete(sender, instance, **kwargs): """Callback function when a part price break is deleted""" - # Update part pricing *unless* we are importing data if InvenTree.ready.canAppAccessDatabase() and not InvenTree.ready.isImportingData(): instance.part.schedule_pricing_update(create=False) @@ -4203,7 +4161,6 @@ class PartRelated(MetadataMixin, models.Model): def clean(self): """Overwrite clean method to check that relation is unique.""" - super().clean() if self.part_1 == self.part_2: diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 2e65df205f..ab6881c1b0 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -64,7 +64,6 @@ class CategorySerializer(InvenTree.serializers.InvenTreeModelSerializer): def __init__(self, *args, **kwargs): """Optionally add or remove extra fields""" - path_detail = kwargs.pop('path_detail', False) super().__init__(*args, **kwargs) @@ -79,7 +78,6 @@ class CategorySerializer(InvenTree.serializers.InvenTreeModelSerializer): @staticmethod def annotate_queryset(queryset): """Annotate extra information to the queryset""" - # Annotate the number of 'parts' which exist in each category (including subcategories!) queryset = queryset.annotate( part_count=part.filters.annotate_category_parts() @@ -274,7 +272,6 @@ class PartBriefSerializer(InvenTree.serializers.InvenTreeModelSerializer): def __init__(self, *args, **kwargs): """Custom initialization routine for the PartBrief serializer""" - pricing = kwargs.pop('pricing', True) super().__init__(*args, **kwargs) @@ -311,7 +308,6 @@ class PartParameterSerializer(InvenTree.serializers.InvenTreeModelSerializer): Allows us to optionally include or exclude particular information """ - template_detail = kwargs.pop('template_detail', True) part_detail = kwargs.pop('part_detail', False) @@ -360,7 +356,6 @@ class PartSetCategorySerializer(serializers.Serializer): @transaction.atomic def save(self): """Save the serializer to change the location of the selected parts""" - data = self.validated_data parts = data['parts'] category = data['category'] @@ -453,7 +448,6 @@ class InitialSupplierSerializer(serializers.Serializer): def validate_supplier(self, company): """Validation for the provided Supplier""" - if company and not company.is_supplier: raise serializers.ValidationError(_('Selected company is not a valid supplier')) @@ -461,7 +455,6 @@ class InitialSupplierSerializer(serializers.Serializer): def validate_manufacturer(self, company): """Validation for the provided Manufacturer""" - if company and not company.is_manufacturer: raise serializers.ValidationError(_('Selected company is not a valid manufacturer')) @@ -469,7 +462,6 @@ class InitialSupplierSerializer(serializers.Serializer): def validate(self, data): """Extra validation for this serializer""" - if company.models.ManufacturerPart.objects.filter( manufacturer=data.get('manufacturer', None), MPN=data.get('mpn', '') @@ -603,7 +595,6 @@ class PartSerializer(InvenTree.serializers.RemoteImageMixin, InvenTree.serialize def skip_create_fields(self): """Skip these fields when instantiating a new Part instance""" - fields = super().skip_create_fields() fields += [ @@ -621,7 +612,6 @@ class PartSerializer(InvenTree.serializers.RemoteImageMixin, InvenTree.serialize Performing database queries as efficiently as possible, to reduce database trips. """ - # Annotate with the total number of stock items queryset = queryset.annotate( stock_item_count=SubqueryCount('stock_items') @@ -759,7 +749,6 @@ class PartSerializer(InvenTree.serializers.RemoteImageMixin, InvenTree.serialize @transaction.atomic def create(self, validated_data): """Custom method for creating a new Part instance using this serializer""" - duplicate = validated_data.pop('duplicate', None) initial_stock = validated_data.pop('initial_stock', None) initial_supplier = validated_data.pop('initial_supplier', None) @@ -862,7 +851,6 @@ class PartSerializer(InvenTree.serializers.RemoteImageMixin, InvenTree.serialize def save(self): """Save the Part instance""" - super().save() part = self.instance @@ -925,7 +913,6 @@ class PartStocktakeSerializer(InvenTree.serializers.InvenTreeModelSerializer): def save(self): """Called when this serializer is saved""" - data = self.validated_data # Add in user information automatically @@ -997,7 +984,6 @@ class PartStocktakeReportGenerateSerializer(serializers.Serializer): def validate(self, data): """Custom validation for this serializer""" - # Stocktake functionality must be enabled if not common.models.InvenTreeSetting.get_setting('STOCKTAKE_ENABLE', False): raise serializers.ValidationError(_("Stocktake functionality is not enabled")) @@ -1010,7 +996,6 @@ class PartStocktakeReportGenerateSerializer(serializers.Serializer): def save(self): """Saving this serializer instance requests generation of a new stocktake report""" - data = self.validated_data user = self.context['request'].user diff --git a/InvenTree/part/stocktake.py b/InvenTree/part/stocktake.py index f803248c88..b889b11474 100644 --- a/InvenTree/part/stocktake.py +++ b/InvenTree/part/stocktake.py @@ -41,7 +41,6 @@ def perform_stocktake(target: part.models.Part, user: User, note: str = '', comm In this case, the stocktake *report* will be limited to the specified location. """ - # Determine which locations are "valid" for the generated report location = kwargs.get('location', None) locations = location.get_descendants(include_self=True) if location else [] @@ -158,7 +157,6 @@ def generate_stocktake_report(**kwargs): generate_report: If True, generate a stocktake report from the calculated data (default=True) update_parts: If True, save stocktake information against each filtered Part (default = True) """ - # Determine if external locations should be excluded exclude_external = kwargs.get( 'exclude_exernal', diff --git a/InvenTree/part/tasks.py b/InvenTree/part/tasks.py index a4ea07ee72..a305adeba8 100644 --- a/InvenTree/part/tasks.py +++ b/InvenTree/part/tasks.py @@ -74,7 +74,6 @@ def update_part_pricing(pricing: part.models.PartPricing, counter: int = 0): pricing: The target PartPricing instance to be updated counter: How many times this function has been called in sequence """ - logger.info("Updating part pricing for %s", pricing.part) pricing.update_pricing(counter=counter) @@ -91,7 +90,6 @@ def check_missing_pricing(limit=250): Arguments: limit: Maximum number of parts to process at once """ - # Find parts for which pricing information has never been updated results = part.models.PartPricing.objects.filter(updated=None)[:limit] @@ -144,7 +142,6 @@ def scheduled_stocktake_reports(): - Delete 'old' stocktake report files after the specified period - Generate new reports at the specified period """ - # Sleep a random number of seconds to prevent worker conflict time.sleep(random.randint(1, 5)) @@ -185,7 +182,6 @@ def rebuild_parameters(template_id): This function is called when a base template is changed, which may cause the base unit to be adjusted. """ - try: template = part.models.PartParameterTemplate.objects.get(pk=template_id) except part.models.PartParameterTemplate.DoesNotExist: @@ -215,7 +211,6 @@ def rebuild_supplier_parts(part_id): This function is called when a bart part is changed, which may cause the native units of any supplier parts to be updated """ - try: prt = part.models.Part.objects.get(pk=part_id) except part.models.Part.DoesNotExist: diff --git a/InvenTree/part/templatetags/i18n.py b/InvenTree/part/templatetags/i18n.py index ac74a07713..54af6040f6 100644 --- a/InvenTree/part/templatetags/i18n.py +++ b/InvenTree/part/templatetags/i18n.py @@ -18,7 +18,6 @@ register = template.Library() @register.simple_tag() def translation_stats(lang_code): """Return the translation percentage for the given language code""" - if lang_code is None: return None @@ -30,7 +29,6 @@ class CustomTranslateNode(TranslateNode): def render(self, context): """Custom render function overrides / extends default behaviour""" - result = super().render(context) result = bleach.clean(result) @@ -58,7 +56,6 @@ def do_translate(parser, token): The only difference is that we pass this to our custom rendering node class """ - bits = token.split_contents() if len(bits) < 2: raise TemplateSyntaxError("'%s' takes at least one argument" % bits[0]) diff --git a/InvenTree/part/templatetags/inventree_extras.py b/InvenTree/part/templatetags/inventree_extras.py index d002e332b3..35ed4336c6 100644 --- a/InvenTree/part/templatetags/inventree_extras.py +++ b/InvenTree/part/templatetags/inventree_extras.py @@ -105,7 +105,6 @@ def render_date(context, date_object): @register.simple_tag def render_currency(money, **kwargs): """Render a currency / Money object""" - return InvenTree.helpers_model.render_currency(money, **kwargs) @@ -211,14 +210,12 @@ def inventree_logo(**kwargs): Returns a path to an image file, which can be rendered in the web interface """ - return InvenTree.helpers.getLogoImage(**kwargs) @register.simple_tag() def inventree_splash(**kwargs): """Return the URL for the InvenTree splash screen, *or* a custom screen if the user has provided one.""" - return InvenTree.helpers.getSplashScreen(**kwargs) @@ -344,7 +341,6 @@ def setting_object(key, *args, **kwargs): (Or return None if the setting does not exist) if a user-setting was requested return that """ - cache = kwargs.get('cache', True) if 'plugin' in kwargs: @@ -499,7 +495,6 @@ def primitive_to_javascript(primitive): @register.simple_tag() def js_bool(val): """Return a javascript boolean value (true or false)""" - if val: return 'true' else: diff --git a/InvenTree/part/templatetags/sso.py b/InvenTree/part/templatetags/sso.py index 250a42ea21..ecd6d19ef6 100644 --- a/InvenTree/part/templatetags/sso.py +++ b/InvenTree/part/templatetags/sso.py @@ -14,7 +14,6 @@ logger = logging.getLogger('inventree') @register.simple_tag() def sso_login_enabled(): """Return True if single-sign-on is enabled""" - return str2bool(InvenTreeSetting.get_setting('LOGIN_ENABLE_SSO')) @@ -33,7 +32,6 @@ def sso_auto_enabled(): @register.simple_tag() def sso_check_provider(provider): """Return True if the given provider is correctly configured""" - import allauth.app_settings from allauth.socialaccount.models import SocialApp diff --git a/InvenTree/part/test_api.py b/InvenTree/part/test_api.py index 57af3632cb..45a0dc46b5 100644 --- a/InvenTree/part/test_api.py +++ b/InvenTree/part/test_api.py @@ -109,7 +109,6 @@ class PartCategoryAPITest(InvenTreeAPITestCase): def test_part_count(self): """Test that the 'part_count' field is annotated correctly""" - url = reverse('api-part-category-list') # Create a parent category @@ -162,7 +161,6 @@ class PartCategoryAPITest(InvenTreeAPITestCase): def test_category_parameters(self): """Test that the PartCategoryParameterTemplate API function work""" - url = reverse('api-part-category-parameter-list') response = self.get(url, {}, expected_code=200) @@ -216,7 +214,6 @@ class PartCategoryAPITest(InvenTreeAPITestCase): This helps to protect against XSS injection """ - url = reverse('api-part-category-detail', kwargs={'pk': 1}) # Invalid values containing tags @@ -258,7 +255,6 @@ class PartCategoryAPITest(InvenTreeAPITestCase): def test_invisible_chars(self): """Test that invisible characters are removed from the input data""" - url = reverse('api-part-category-detail', kwargs={'pk': 1}) values = [ @@ -395,7 +391,6 @@ class PartCategoryAPITest(InvenTreeAPITestCase): - Parts cannot be created in structural categories - Parts cannot be assigned to structural categories """ - # Create our structural part category structural_category = PartCategory.objects.create( name='Structural category', @@ -443,7 +438,6 @@ class PartCategoryAPITest(InvenTreeAPITestCase): def test_path_detail(self): """Test path_detail information""" - url = reverse('api-part-category-detail', kwargs={'pk': 5}) # First, request without path detail @@ -718,7 +712,6 @@ class PartAPITest(PartAPITestBase): def test_filter_by_in_bom(self): """Test that we can filter part list by the 'in_bom_for' parameter""" - url = reverse('api-part-list') response = self.get( @@ -755,7 +748,6 @@ class PartAPITest(PartAPITestBase): def test_filter_by_convert(self): """Test that we can correctly filter the Part list by conversion options""" - category = PartCategory.objects.get(pk=3) # First, construct a set of template / variant parts @@ -1207,7 +1199,6 @@ class PartCreationTests(PartAPITestBase): def submit(stock_data, expected_code=None): """Helper function for submitting with initial stock data""" - data = { 'category': 1, 'name': "My lil' test part", @@ -1252,7 +1243,6 @@ class PartCreationTests(PartAPITestBase): def submit(supplier_data, expected_code=400): """Helper function for submitting with supplier data""" - data = { 'name': 'My test part', 'description': 'A test part thingy', @@ -1355,7 +1345,6 @@ class PartCreationTests(PartAPITestBase): def test_duplication(self): """Test part duplication options""" - # Run a matrix of tests for bom in [True, False]: for img in [True, False]: @@ -1384,7 +1373,6 @@ class PartCreationTests(PartAPITestBase): def test_category_parameters(self): """Test that category parameters are correctly applied""" - cat = PartCategory.objects.get(pk=1) # Add some parameter template to the parent category @@ -1684,7 +1672,6 @@ class PartDetailTests(PartAPITestBase): def test_path_detail(self): """Check that path_detail can be requested against the serializer""" - response = self.get( reverse('api-part-detail', kwargs={'pk': 1}), { @@ -1702,7 +1689,6 @@ class PartListTests(PartAPITestBase): def test_query_count(self): """Test that the query count is unchanged, independent of query results""" - queries = [ {'limit': 1}, {'limit': 10}, @@ -1768,7 +1754,6 @@ class PartNotesTests(InvenTreeAPITestCase): def test_long_notes(self): """Test that very long notes field is rejected""" - # Ensure that we cannot upload a very long piece of text url = reverse('api-part-detail', kwargs={'pk': 1}) @@ -1784,7 +1769,6 @@ class PartNotesTests(InvenTreeAPITestCase): def test_multiline_formatting(self): """Ensure that markdown formatting is retained""" - url = reverse('api-part-detail', kwargs={'pk': 1}) notes = """ @@ -1828,12 +1812,10 @@ class PartPricingDetailTests(InvenTreeAPITestCase): def url(self, pk): """Construct a pricing URL""" - return reverse('api-part-pricing', kwargs={'pk': pk}) def test_pricing_detail(self): """Test an empty pricing detail""" - response = self.get( self.url(1), expected_code=200 @@ -2100,7 +2082,6 @@ class PartAPIAggregationTest(InvenTreeAPITestCase): This queryset annotation takes into account any outstanding line items for active orders, and should also use the 'pack_size' of the supplier part objects. """ - supplier = Company.objects.create( name='Paint Supplies', description='A supplier of paints', @@ -2284,7 +2265,6 @@ class BomItemTest(InvenTreeAPITestCase): def test_bom_list_search(self): """Test that we can search the BOM list API endpoint""" - url = reverse('api-bom-list') response = self.get(url, expected_code=200) @@ -2328,7 +2308,6 @@ class BomItemTest(InvenTreeAPITestCase): def test_bom_list_ordering(self): """Test that the BOM list results can be ordered""" - url = reverse('api-bom-list') # Order by increasing quantity @@ -2698,7 +2677,6 @@ class PartAttachmentTest(InvenTreeAPITestCase): def test_add_attachment(self): """Test that we can create a new PartAttachment via the API""" - url = reverse('api-part-attachment-list') # Upload without permission @@ -2795,7 +2773,6 @@ class PartInternalPriceBreakTest(InvenTreeAPITestCase): def test_create_price_breaks(self): """Test we can create price breaks at various quantities""" - url = reverse('api-part-internal-price-list') breaks = [ @@ -2859,7 +2836,6 @@ class PartStocktakeTest(InvenTreeAPITestCase): def test_list_endpoint(self): """Test the list endpoint for the stocktake data""" - url = reverse('api-part-stocktake-list') self.assignRole('part.view') @@ -2911,7 +2887,6 @@ class PartStocktakeTest(InvenTreeAPITestCase): def test_create_stocktake(self): """Test that stocktake entries can be created via the API""" - url = reverse('api-part-stocktake-list') self.assignRole('stocktake.add') @@ -2948,7 +2923,6 @@ class PartStocktakeTest(InvenTreeAPITestCase): Note that only 'staff' users can perform these actions. """ - p = Part.objects.all().first() st = PartStocktake.objects.create(part=p, quantity=10) @@ -2989,7 +2963,6 @@ class PartStocktakeTest(InvenTreeAPITestCase): def test_report_list(self): """Test for PartStocktakeReport list endpoint""" - from part.stocktake import generate_stocktake_report # Initially, no stocktake records are available @@ -3021,7 +2994,6 @@ class PartStocktakeTest(InvenTreeAPITestCase): def test_report_generate(self): """Test API functionality for generating a new stocktake report""" - url = reverse('api-part-stocktake-report-generate') # Permission denied, initially @@ -3064,7 +3036,6 @@ class PartMetadataAPITest(InvenTreeAPITestCase): def metatester(self, apikey, model): """Generic tester""" - modeldata = model.objects.first() # Useless test unless a model object is found @@ -3093,7 +3064,6 @@ class PartMetadataAPITest(InvenTreeAPITestCase): def test_metadata(self): """Test all endpoints""" - for apikey, model in { 'api-part-category-parameter-metadata': PartCategoryParameterTemplate, 'api-part-category-metadata': PartCategory, @@ -3113,7 +3083,6 @@ class PartSchedulingTest(PartAPITestBase): def test_get_schedule(self): """Test that the scheduling endpoint returns OK""" - part_ids = [ 1, 3, 100, 101, ] diff --git a/InvenTree/part/test_bom_item.py b/InvenTree/part/test_bom_item.py index a2a7d0b99b..1925d00b42 100644 --- a/InvenTree/part/test_bom_item.py +++ b/InvenTree/part/test_bom_item.py @@ -202,7 +202,6 @@ class BomItemTest(TestCase): def test_consumable(self): """Tests for the 'consumable' BomItem field""" - # Create an assembly part assembly = Part.objects.create(name="An assembly", description="Made with parts", assembly=True) diff --git a/InvenTree/part/test_category.py b/InvenTree/part/test_category.py index 2d404b5ef9..0762f40cf3 100644 --- a/InvenTree/part/test_category.py +++ b/InvenTree/part/test_category.py @@ -22,7 +22,6 @@ class CategoryTest(TestCase): @classmethod def setUpTestData(cls): """Extract some interesting categories for time-saving""" - super().setUpTestData() cls.electronics = PartCategory.objects.get(name='Electronics') @@ -68,7 +67,6 @@ class CategoryTest(TestCase): def test_path_string(self): """Test that the category path string works correctly.""" - # Note that due to data migrations, these fields need to be saved first self.resistors.save() self.transceivers.save() @@ -137,7 +135,6 @@ class CategoryTest(TestCase): def test_part_count(self): """Test that the Category part count works.""" - self.assertEqual(self.fasteners.partcount(), 2) self.assertEqual(self.capacitors.partcount(), 1) @@ -203,7 +200,6 @@ class CategoryTest(TestCase): def test_default_locations(self): """Test traversal for default locations.""" - self.assertIsNotNone(self.fasteners.default_location) self.fasteners.default_location.save() self.assertEqual(str(self.fasteners.default_location), 'Office/Drawer_1 - In my desk') diff --git a/InvenTree/part/test_migrations.py b/InvenTree/part/test_migrations.py index bd0e3aa173..59c1a650c4 100644 --- a/InvenTree/part/test_migrations.py +++ b/InvenTree/part/test_migrations.py @@ -56,7 +56,6 @@ class TestBomItemMigrations(MigratorTestCase): def prepare(self): """Create initial dataset""" - Part = self.old_state.apps.get_model('part', 'part') BomItem = self.old_state.apps.get_model('part', 'bomitem') @@ -75,7 +74,6 @@ class TestBomItemMigrations(MigratorTestCase): def test_validated_field(self): """Test that the 'validated' field is added to the BomItem objects""" - BomItem = self.new_state.apps.get_model('part', 'bomitem') self.assertEqual(BomItem.objects.count(), 2) @@ -92,7 +90,6 @@ class TestParameterMigrations(MigratorTestCase): def prepare(self): """Create some parts, and templates with parameters""" - Part = self.old_state.apps.get_model('part', 'part') PartParameter = self.old_state.apps.get_model('part', 'partparameter') PartParameterTemlate = self.old_state.apps.get_model('part', 'partparametertemplate') @@ -121,7 +118,6 @@ class TestParameterMigrations(MigratorTestCase): def test_data_migration(self): """Test that the template units and values have been updated correctly""" - Part = self.new_state.apps.get_model('part', 'part') PartParameter = self.new_state.apps.get_model('part', 'partparameter') PartParameterTemlate = self.new_state.apps.get_model('part', 'partparametertemplate') @@ -164,7 +160,6 @@ class PartUnitsMigrationTest(MigratorTestCase): def prepare(self): """Prepare some parts with units""" - Part = self.old_state.apps.get_model('part', 'part') units = ['mm', 'INCH', '', '%'] @@ -177,7 +172,6 @@ class PartUnitsMigrationTest(MigratorTestCase): def test_units_migration(self): """Test that the units have migrated OK""" - Part = self.new_state.apps.get_model('part', 'part') part_1 = Part.objects.get(name='Part 1') @@ -202,7 +196,6 @@ class TestPartParameterTemplateMigration(MigratorTestCase): def prepare(self): """Prepare some parts with units""" - PartParameterTemplate = self.old_state.apps.get_model('part', 'partparametertemplate') # Create a test template @@ -217,7 +210,6 @@ class TestPartParameterTemplateMigration(MigratorTestCase): def test_units_migration(self): """Test that the new fields have been added correctly""" - PartParameterTemplate = self.new_state.apps.get_model('part', 'partparametertemplate') template = PartParameterTemplate.objects.get(name='Template 1') diff --git a/InvenTree/part/test_param.py b/InvenTree/part/test_param.py index 072ab306f6..827b8a369b 100644 --- a/InvenTree/part/test_param.py +++ b/InvenTree/part/test_param.py @@ -66,7 +66,6 @@ class TestParams(TestCase): def test_get_parameter(self): """Test the Part.get_parameter method""" - prt = Part.objects.get(pk=3) # Check that we can get a parameter by name @@ -119,7 +118,6 @@ class ParameterTests(TestCase): def test_choice_validation(self): """Test that parameter choices are correctly validated""" - template = PartParameterTemplate.objects.create( name='My Template', description='A template with choices', @@ -142,7 +140,6 @@ class ParameterTests(TestCase): def test_unit_validation(self): """Test validation of 'units' field for PartParameterTemplate""" - # Test that valid units pass for unit in [None, '', '%', 'mm', 'A', 'm^2', 'Pa', 'V', 'C', 'F', 'uF', 'mF', 'millifarad']: tmp = PartParameterTemplate(name='test', units=unit) @@ -156,7 +153,6 @@ class ParameterTests(TestCase): def test_param_unit_validation(self): """Test that parameters are correctly validated against template units""" - template = PartParameterTemplate.objects.create( name='My Template', units='m', @@ -198,7 +194,6 @@ class ParameterTests(TestCase): def test_param_unit_conversion(self): """Test that parameters are correctly converted to template units""" - template = PartParameterTemplate.objects.create( name='My Template', units='m', @@ -263,7 +258,6 @@ class PartParameterTest(InvenTreeAPITestCase): def test_param_template_validation(self): """Test that part parameter template validation routines work correctly.""" - # Checkbox parameter cannot have "units" specified with self.assertRaises(django_exceptions.ValidationError): template = PartParameterTemplate( diff --git a/InvenTree/part/test_part.py b/InvenTree/part/test_part.py index 14de4a1ae0..bc9857c614 100644 --- a/InvenTree/part/test_part.py +++ b/InvenTree/part/test_part.py @@ -137,7 +137,6 @@ class PartTest(TestCase): @classmethod def setUpTestData(cls): """Create some Part instances as part of init routine""" - super().setUpTestData() cls.r1 = Part.objects.get(name='R_2K2_0805') @@ -149,7 +148,6 @@ class PartTest(TestCase): def test_barcode_mixin(self): """Test the barcode mixin functionality""" - self.assertEqual(Part.barcode_model_type(), 'part') p = Part.objects.get(pk=1) @@ -292,7 +290,6 @@ class PartTest(TestCase): def test_related(self): """Unit tests for the PartRelated model""" - # Create a part relationship # Count before creation countbefore = PartRelated.objects.count() @@ -341,7 +338,6 @@ class PartTest(TestCase): def test_stocktake(self): """Test for adding stocktake data""" - # Grab a part p = Part.objects.all().first() @@ -419,7 +415,6 @@ class PartSettingsTest(InvenTreeTestCase): def make_part(self): """Helper function to create a simple part.""" - cache.clear() part = Part.objects.create( @@ -432,7 +427,6 @@ class PartSettingsTest(InvenTreeTestCase): def test_defaults(self): """Test that the default values for the part settings are correct.""" - cache.clear() self.assertTrue(part.settings.part_component_default()) @@ -442,7 +436,6 @@ class PartSettingsTest(InvenTreeTestCase): def test_initial(self): """Test the 'initial' default values (no default values have been set)""" - cache.clear() part = self.make_part() diff --git a/InvenTree/part/test_pricing.py b/InvenTree/part/test_pricing.py index e0f975dfb3..e6a348f413 100644 --- a/InvenTree/part/test_pricing.py +++ b/InvenTree/part/test_pricing.py @@ -20,7 +20,6 @@ class PartPricingTests(InvenTreeTestCase): def setUp(self): """Setup routines""" - super().setUp() self.generate_exchange_rates() @@ -37,7 +36,6 @@ class PartPricingTests(InvenTreeTestCase): def create_price_breaks(self): """Create some price breaks for the part, in various currencies""" - # First supplier part (CAD) self.supplier_1 = company.models.Company.objects.create( name='Supplier 1', @@ -104,7 +102,6 @@ class PartPricingTests(InvenTreeTestCase): def test_pricing_data(self): """Test link between Part and PartPricing model""" - # Initially there is no associated Pricing data with self.assertRaises(ObjectDoesNotExist): pricing = self.part.pricing_data @@ -130,7 +127,6 @@ class PartPricingTests(InvenTreeTestCase): def test_simple(self): """Tests for hard-coded values""" - pricing = self.part.pricing # Add internal pricing @@ -162,7 +158,6 @@ class PartPricingTests(InvenTreeTestCase): def test_supplier_part_pricing(self): """Test for supplier part pricing""" - pricing = self.part.pricing # Initially, no information (not yet calculated) @@ -189,7 +184,6 @@ class PartPricingTests(InvenTreeTestCase): def test_internal_pricing(self): """Tests for internal price breaks""" - # Ensure internal pricing is enabled common.models.InvenTreeSetting.set_setting('PART_INTERNAL_PRICE', True, None) @@ -225,7 +219,6 @@ class PartPricingTests(InvenTreeTestCase): def test_stock_item_pricing(self): """Test for stock item pricing data""" - # Create a part p = part.models.Part.objects.create( name='Test part for pricing', @@ -273,7 +266,6 @@ class PartPricingTests(InvenTreeTestCase): def test_bom_pricing(self): """Unit test for BOM pricing calculations""" - pricing = self.part.pricing self.assertIsNone(pricing.bom_cost_min) @@ -315,7 +307,6 @@ class PartPricingTests(InvenTreeTestCase): def test_purchase_pricing(self): """Unit tests for historical purchase pricing""" - self.create_price_breaks() pricing = self.part.pricing @@ -380,7 +371,6 @@ class PartPricingTests(InvenTreeTestCase): def test_delete_with_pricing(self): """Test for deleting a part which has pricing information""" - # Create some pricing data self.create_price_breaks() @@ -405,7 +395,6 @@ class PartPricingTests(InvenTreeTestCase): def test_delete_without_pricing(self): """Test that we can delete a part which does not have pricing information""" - pricing = self.part.pricing self.assertIsNone(pricing.pk) @@ -426,7 +415,6 @@ class PartPricingTests(InvenTreeTestCase): - Create PartPricing objects where there are none - Schedule pricing calculations for the newly created PartPricing objects """ - from part.tasks import check_missing_pricing # Create some parts @@ -453,7 +441,6 @@ class PartPricingTests(InvenTreeTestCase): Essentially a series of on_delete listeners caused a new PartPricing object to be created, but it pointed to a Part instance which was slated to be deleted inside an atomic transaction. """ - p = part.models.Part.objects.create( name="my part", description="my part description", diff --git a/InvenTree/plugin/api.py b/InvenTree/plugin/api.py index dcacdb3022..ebfb501924 100644 --- a/InvenTree/plugin/api.py +++ b/InvenTree/plugin/api.py @@ -183,7 +183,6 @@ class PluginActivate(UpdateAPI): def perform_update(self, serializer): """Activate the plugin.""" - serializer.save() diff --git a/InvenTree/plugin/base/barcodes/api.py b/InvenTree/plugin/base/barcodes/api.py index bc6937a154..d41113ba67 100644 --- a/InvenTree/plugin/base/barcodes/api.py +++ b/InvenTree/plugin/base/barcodes/api.py @@ -101,7 +101,6 @@ class BarcodeAssign(APIView): Checks inputs and assign barcode (hash) to StockItem. """ - data = request.data barcode_data = data.get('barcode', None) @@ -180,7 +179,6 @@ class BarcodeUnassign(APIView): def post(self, request, *args, **kwargs): """Respond to a barcode unassign POST request""" - # The following database models support assignment of third-party barcodes supported_models = InvenTreeInternalBarcodePlugin.get_supported_barcode_models() diff --git a/InvenTree/plugin/base/barcodes/mixins.py b/InvenTree/plugin/base/barcodes/mixins.py index 5ad4794ebd..03a0345501 100644 --- a/InvenTree/plugin/base/barcodes/mixins.py +++ b/InvenTree/plugin/base/barcodes/mixins.py @@ -35,5 +35,4 @@ class BarcodeMixin: Default return value is None """ - return None diff --git a/InvenTree/plugin/base/event/events.py b/InvenTree/plugin/base/event/events.py index ef3fa69a91..01502b528c 100644 --- a/InvenTree/plugin/base/event/events.py +++ b/InvenTree/plugin/base/event/events.py @@ -99,7 +99,6 @@ def process_event(plugin_slug, event, *args, **kwargs): This function is run by the background worker process. This function may queue multiple functions to be handled by the background worker. """ - plugin = registry.plugins.get(plugin_slug, None) if plugin is None: # pragma: no cover diff --git a/InvenTree/plugin/base/event/mixins.py b/InvenTree/plugin/base/event/mixins.py index 9030eb2d22..8f17199c5f 100644 --- a/InvenTree/plugin/base/event/mixins.py +++ b/InvenTree/plugin/base/event/mixins.py @@ -14,7 +14,6 @@ class EventMixin: Return true if you're interested in the given event, false if not. """ - # Default implementation always returns true (backwards compatibility) return True diff --git a/InvenTree/plugin/base/integration/mixins.py b/InvenTree/plugin/base/integration/mixins.py index e4250a2da6..cc6276a5a0 100644 --- a/InvenTree/plugin/base/integration/mixins.py +++ b/InvenTree/plugin/base/integration/mixins.py @@ -144,7 +144,6 @@ class PanelMixin: Returns: Array of panels """ - panels = [] # Construct an updated context object for template rendering diff --git a/InvenTree/plugin/base/label/mixins.py b/InvenTree/plugin/base/label/mixins.py index a9fc619f72..80e77e7ebe 100644 --- a/InvenTree/plugin/base/label/mixins.py +++ b/InvenTree/plugin/base/label/mixins.py @@ -55,7 +55,6 @@ class LabelPrintingMixin: def render_to_png(self, label: LabelTemplate, request=None, **kwargs): """Render this label to PNG format""" - # Check if pdf data is provided pdf_data = kwargs.get('pdf_data', None) @@ -85,7 +84,6 @@ class LabelPrintingMixin: The default implementation simply calls print_label() for each label, producing multiple single label output "jobs" but this can be overridden by the particular plugin. """ - try: user = request.user except AttributeError: @@ -152,7 +150,6 @@ class LabelPrintingMixin: Offloads a call to the 'print_label' method (of this plugin) to a background worker. """ - # Exclude the 'pdf_file' object - cannot be pickled kwargs.pop('pdf_file', None) diff --git a/InvenTree/plugin/builtin/barcodes/inventree_barcode.py b/InvenTree/plugin/builtin/barcodes/inventree_barcode.py index cdfa756779..8dcd3ca38c 100644 --- a/InvenTree/plugin/builtin/barcodes/inventree_barcode.py +++ b/InvenTree/plugin/builtin/barcodes/inventree_barcode.py @@ -30,12 +30,10 @@ class InvenTreeInternalBarcodePlugin(BarcodeMixin, InvenTreePlugin): @staticmethod def get_supported_barcode_models(): """Returns a list of database models which support barcode functionality""" - return getModelsWithMixin(InvenTreeBarcodeMixin) def format_matched_response(self, label, model, instance): """Format a response for the scanned data""" - data = { 'pk': instance.pk } @@ -65,7 +63,6 @@ class InvenTreeInternalBarcodePlugin(BarcodeMixin, InvenTreePlugin): Here we are looking for a dict object which contains a reference to a particular InvenTree database object """ - # Create hash from raw barcode data barcode_hash = hash_barcode(barcode_data) diff --git a/InvenTree/plugin/builtin/barcodes/test_inventree_barcode.py b/InvenTree/plugin/builtin/barcodes/test_inventree_barcode.py index fb5485473f..82e76089c1 100644 --- a/InvenTree/plugin/builtin/barcodes/test_inventree_barcode.py +++ b/InvenTree/plugin/builtin/barcodes/test_inventree_barcode.py @@ -45,7 +45,6 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase): def assign(self, data, expected_code=None): """Perform a 'barcode assign' request""" - return self.post( reverse('api-barcode-link'), data=data, @@ -54,7 +53,6 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase): def unassign(self, data, expected_code=None): """Perform a 'barcode unassign' request""" - return self.post( reverse('api-barcode-unlink'), data=data, @@ -63,7 +61,6 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase): def scan(self, data, expected_code=None): """Perform a 'scan' operation""" - return self.post( reverse('api-barcode-scan'), data=data, @@ -72,7 +69,6 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase): def test_unassign_errors(self): """Test various error conditions for the barcode unassign endpoint""" - # Fail without any fields provided response = self.unassign( {}, @@ -114,7 +110,6 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase): def test_assign_to_stock_item(self): """Test that we can assign a unique barcode to a StockItem object""" - # Test without providing any fields response = self.assign( { @@ -198,7 +193,6 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase): def test_assign_to_part(self): """Test that we can assign a unique barcode to a Part instance""" - barcode = 'xyz-123' self.assignRole('part.change') @@ -281,7 +275,6 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase): def test_assign_to_location(self): """Test that we can assign a unique barcode to a StockLocation instance""" - barcode = '555555555555555555555555' # Assign random barcode data to a StockLocation instance @@ -338,7 +331,6 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase): def test_scan_third_party(self): """Test scanning of third-party barcodes""" - # First scanned barcode is for a 'third-party' barcode (which does not exist) response = self.scan({'barcode': 'blbla=10008'}, expected_code=400) self.assertEqual(response.data['error'], 'No match found for barcode data') @@ -367,7 +359,6 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase): def test_scan_inventree(self): """Test scanning of first-party barcodes""" - # Scan a StockItem object (which does not exist) response = self.scan( { diff --git a/InvenTree/plugin/builtin/integration/core_notifications.py b/InvenTree/plugin/builtin/integration/core_notifications.py index 7385b27d47..a988cf8287 100644 --- a/InvenTree/plugin/builtin/integration/core_notifications.py +++ b/InvenTree/plugin/builtin/integration/core_notifications.py @@ -133,7 +133,6 @@ class InvenTreeCoreNotificationsPlugin(SettingsContentMixin, SettingsMixin, Inve def send_bulk(self): """Send the notifications out via slack.""" - instance = registry.plugins.get(self.get_plugin().NAME.lower()) url = instance.get_setting('NOTIFICATION_SLACK_URL') diff --git a/InvenTree/plugin/builtin/integration/currency_exchange.py b/InvenTree/plugin/builtin/integration/currency_exchange.py index 79c08a0695..f853b472ee 100644 --- a/InvenTree/plugin/builtin/integration/currency_exchange.py +++ b/InvenTree/plugin/builtin/integration/currency_exchange.py @@ -26,7 +26,6 @@ class InvenTreeCurrencyExchange(APICallMixin, CurrencyExchangeMixin, InvenTreePl def update_exchange_rates(self, base_currency: str, symbols: list[str]) -> dict: """Request exchange rate data from external API""" - response = self.api_call( 'latest', url_args={ diff --git a/InvenTree/plugin/builtin/labels/inventree_label.py b/InvenTree/plugin/builtin/labels/inventree_label.py index e90f0a5a67..cd052aee14 100644 --- a/InvenTree/plugin/builtin/labels/inventree_label.py +++ b/InvenTree/plugin/builtin/labels/inventree_label.py @@ -39,7 +39,6 @@ class InvenTreeLabelPlugin(LabelPrintingMixin, SettingsMixin, InvenTreePlugin): - Label outputs are concatenated together, and we return a single PDF file. - If DEBUG mode is enabled, we return a single HTML file. """ - debug = self.get_setting('DEBUG') outputs = [] @@ -87,7 +86,6 @@ class InvenTreeLabelPlugin(LabelPrintingMixin, SettingsMixin, InvenTreePlugin): Returns either a PDF or HTML output, depending on the DEBUG setting. """ - debug = kwargs.get('debug', self.get_setting('DEBUG')) if debug: diff --git a/InvenTree/plugin/helpers.py b/InvenTree/plugin/helpers.py index d85231d72a..d896ce10c2 100644 --- a/InvenTree/plugin/helpers.py +++ b/InvenTree/plugin/helpers.py @@ -109,7 +109,6 @@ def get_entrypoints(): # region git-helpers def get_git_log(path): """Get dict with info of the last commit to file named in path.""" - import datetime from dulwich.repo import NotGitRepository, Repo diff --git a/InvenTree/plugin/installer.py b/InvenTree/plugin/installer.py index 1bc2d15932..885114ab83 100644 --- a/InvenTree/plugin/installer.py +++ b/InvenTree/plugin/installer.py @@ -18,7 +18,6 @@ def pip_command(*args): returns: subprocess.check_output throws: subprocess.CalledProcessError """ - python = sys.executable command = [python, '-m', 'pip'] @@ -41,7 +40,6 @@ def check_package_path(packagename: str): - If installed, return the installation path - If not installed, return False """ - logger.debug("check_package_path: %s", packagename) # Remove version information @@ -71,7 +69,6 @@ def check_package_path(packagename: str): def install_plugins_file(): """Install plugins from the plugins file""" - logger.info("Installing plugins from plugins file") pf = settings.PLUGIN_FILE @@ -96,7 +93,6 @@ def install_plugins_file(): def add_plugin_to_file(install_name): """Add a plugin to the plugins file""" - logger.info("Adding plugin to plugins file: %s", install_name) pf = settings.PLUGIN_FILE @@ -140,7 +136,6 @@ def install_plugin(url=None, packagename=None, user=None): - A staff user account is required - We must detect that we are running within a virtual environment """ - if user and not user.is_staff: raise ValidationError(_("Permission denied: only staff users can install plugins")) diff --git a/InvenTree/plugin/models.py b/InvenTree/plugin/models.py index ce4cf577c6..44f281cf02 100644 --- a/InvenTree/plugin/models.py +++ b/InvenTree/plugin/models.py @@ -78,7 +78,6 @@ class PluginConfig(InvenTree.models.MetadataMixin, models.Model): def get_plugin_meta(name): """Return a meta-value associated with this plugin""" - # Ignore if the plugin config is not defined if not plugin: return None @@ -134,13 +133,11 @@ class PluginConfig(InvenTree.models.MetadataMixin, models.Model): A plugin might not be installed if it has been removed from the system, but the PluginConfig associated with it still exists. """ - return self.plugin is not None @admin.display(boolean=True, description=_('Sample plugin')) def is_sample(self) -> bool: """Is this plugin a sample app?""" - if not self.plugin: return False @@ -149,7 +146,6 @@ class PluginConfig(InvenTree.models.MetadataMixin, models.Model): @admin.display(boolean=True, description=_('Builtin Plugin')) def is_builtin(self) -> bool: """Return True if this is a 'builtin' plugin""" - if not self.plugin: return False diff --git a/InvenTree/plugin/plugin.py b/InvenTree/plugin/plugin.py index 91c879caf4..16ac0ff03d 100644 --- a/InvenTree/plugin/plugin.py +++ b/InvenTree/plugin/plugin.py @@ -106,7 +106,6 @@ class MetaBase: def is_active(self): """Return True if this plugin is currently active.""" - # Builtin plugins are always considered "active" if self.is_builtin: return True @@ -348,7 +347,6 @@ class InvenTreePlugin(VersionMixin, MixinBase, MetaBase): # region package info def _get_package_commit(self): """Get last git commit for the plugin.""" - return get_git_log(str(self.file())) @classmethod @@ -361,7 +359,6 @@ class InvenTreePlugin(VersionMixin, MixinBase, MetaBase): @classmethod def _get_package_metadata(cls): """Get package metadata for plugin.""" - # Try simple metadata lookup try: meta = metadata(cls.__name__) @@ -389,7 +386,6 @@ class InvenTreePlugin(VersionMixin, MixinBase, MetaBase): def define_package(self): """Add package info of the plugin into plugins context.""" - try: package = self._get_package_metadata() if self._is_package else self._get_package_commit() except TypeError: diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index f723a0db50..4cb8053a7b 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -77,7 +77,6 @@ class PluginsRegistry: def get_plugin(self, slug): """Lookup plugin by slug (unique key).""" - # Check if the registry needs to be reloaded self.check_reload() @@ -94,7 +93,6 @@ class PluginsRegistry: slug (str): Plugin slug state (bool): Plugin state - true = active, false = inactive """ - # Check if the registry needs to be reloaded self.check_reload() @@ -114,7 +112,6 @@ class PluginsRegistry: Instead, any error messages are returned to the worker. """ - # Check if the registry needs to be reloaded self.check_reload() @@ -130,7 +127,6 @@ class PluginsRegistry: # region registry functions def with_mixin(self, mixin: str, active=None, builtin=None): """Returns reference to all plugins that have a specified mixin enabled.""" - # Check if the registry needs to be loaded self.check_reload() @@ -161,7 +157,6 @@ class PluginsRegistry: Args: full_reload (bool, optional): Reload everything - including plugin mechanism. Defaults to False. """ - logger.info('Loading plugins') # Set maintenance mode @@ -229,7 +224,6 @@ class PluginsRegistry: Args: force_reload (bool, optional): Also reload base apps. Defaults to False. """ - logger.info('Start unloading plugins') # Set maintenance mode @@ -285,7 +279,6 @@ class PluginsRegistry: def plugin_dirs(self): """Construct a list of directories from where plugins can be loaded""" - # Builtin plugins are *always* loaded dirs = ['plugin.builtin', ] @@ -343,7 +336,6 @@ class PluginsRegistry: def collect_plugins(self): """Collect plugins from all possible ways of loading. Returned as list.""" - collected_plugins = [] # Collect plugins from paths @@ -591,7 +583,6 @@ class PluginsRegistry: force_reload (bool, optional): Also reload base apps. Defaults to False. full_reload (bool, optional): Reload everything - including plugin mechanism. Defaults to False. """ - if force_reload: # we can not use the built in functions as we need to brute force the registry apps.app_configs = OrderedDict() @@ -623,7 +614,6 @@ class PluginsRegistry: Note that we also have to refresh the admin site URLS, as any custom AppMixin plugins require admin integration """ - from InvenTree.urls import urlpatterns from plugin.urls import get_plugin_urls @@ -643,7 +633,6 @@ class PluginsRegistry: # region plugin registry hash calculations def update_plugin_hash(self): """When the state of the plugin registry changes, update the hash""" - from common.models import InvenTreeSetting self.registry_hash = self.calculate_plugin_hash() @@ -666,7 +655,6 @@ class PluginsRegistry: This is used to detect changes in the plugin registry, and to inform other processes that the plugin registry has changed """ - from hashlib import md5 from common.models import InvenTreeSetting @@ -698,7 +686,6 @@ class PluginsRegistry: def check_reload(self): """Determine if the registry needs to be reloaded""" - from common.models import InvenTreeSetting if settings.TESTING: diff --git a/InvenTree/plugin/samples/integration/label_sample.py b/InvenTree/plugin/samples/integration/label_sample.py index 517d68b58f..f0b5629cc5 100644 --- a/InvenTree/plugin/samples/integration/label_sample.py +++ b/InvenTree/plugin/samples/integration/label_sample.py @@ -22,7 +22,6 @@ class SampleLabelPrinter(LabelPrintingMixin, InvenTreePlugin): Normally here the connection to the printer and transfer of the label would take place. """ - # Test that the expected kwargs are present print(f"Printing Label: {kwargs['filename']} (User: {kwargs['user']})") diff --git a/InvenTree/plugin/samples/integration/report_plugin_sample.py b/InvenTree/plugin/samples/integration/report_plugin_sample.py index 779311432f..5b143ab307 100644 --- a/InvenTree/plugin/samples/integration/report_plugin_sample.py +++ b/InvenTree/plugin/samples/integration/report_plugin_sample.py @@ -22,9 +22,7 @@ class SampleReportPlugin(ReportMixin, InvenTreePlugin): def add_report_context(self, report_instance, model_instance, request, context): """Add example content to the report instance""" - # We can add any extra context data we want to the report - # Generate a random string of data context['random_text'] = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=20)) diff --git a/InvenTree/plugin/samples/integration/sample_currency_exchange.py b/InvenTree/plugin/samples/integration/sample_currency_exchange.py index 271b98dae9..7a490543da 100644 --- a/InvenTree/plugin/samples/integration/sample_currency_exchange.py +++ b/InvenTree/plugin/samples/integration/sample_currency_exchange.py @@ -19,7 +19,6 @@ class SampleCurrencyExchangePlugin(CurrencyExchangeMixin, InvenTreePlugin): def update_exchange_rates(self, base_currency: str, symbols: list[str]) -> dict: """Return dummy data for some currencies""" - rates = { base_currency: 1.00, } diff --git a/InvenTree/plugin/samples/integration/test_sample.py b/InvenTree/plugin/samples/integration/test_sample.py index d787d8f9c3..66a6106f76 100644 --- a/InvenTree/plugin/samples/integration/test_sample.py +++ b/InvenTree/plugin/samples/integration/test_sample.py @@ -11,7 +11,6 @@ class SampleIntegrationPluginTests(InvenTreeTestCase): def test_view(self): """Check the function of the custom sample plugin.""" - from common.models import InvenTreeSetting url = '/plugin/sample/ho/he/' @@ -53,7 +52,6 @@ class SampleIntegrationPluginTests(InvenTreeTestCase): # validator def test_settings_validator(self): """Test settings validator for plugins.""" - plugin = registry.get_plugin('sample') valid_json = '{"ts": 13}' not_valid_json = '{"ts""13"}' diff --git a/InvenTree/plugin/samples/integration/validation_sample.py b/InvenTree/plugin/samples/integration/validation_sample.py index 5b15026fc4..63e8d0eb9b 100644 --- a/InvenTree/plugin/samples/integration/validation_sample.py +++ b/InvenTree/plugin/samples/integration/validation_sample.py @@ -59,7 +59,6 @@ class CustomValidationMixin(SettingsMixin, ValidationMixin, InvenTreePlugin): These examples are silly, but serve to demonstrate how the feature could be used """ - if len(part.description) < len(name): raise ValidationError("Part description cannot be shorter than the name") @@ -74,7 +73,6 @@ class CustomValidationMixin(SettingsMixin, ValidationMixin, InvenTreePlugin): These examples are silly, but serve to demonstrate how the feature could be used """ - if self.get_setting('IPN_MUST_CONTAIN_Q') and 'Q' not in ipn: raise ValidationError("IPN must contain 'Q'") @@ -83,7 +81,6 @@ class CustomValidationMixin(SettingsMixin, ValidationMixin, InvenTreePlugin): These examples are silly, but serve to demonstrate how the feature could be used """ - if parameter.template.name.lower() in ['length', 'width']: d = int(data) if d >= 100: @@ -94,7 +91,6 @@ class CustomValidationMixin(SettingsMixin, ValidationMixin, InvenTreePlugin): These examples are silly, but serve to demonstrate how the feature could be used """ - if self.get_setting('SERIAL_MUST_BE_PALINDROME'): if serial != serial[::-1]: raise ValidationError("Serial must be a palindrome") @@ -109,7 +105,6 @@ class CustomValidationMixin(SettingsMixin, ValidationMixin, InvenTreePlugin): These examples are silly, but serve to demonstrate how the feature could be used """ - prefix = self.get_setting('BATCH_CODE_PREFIX') if len(batch_code) > 0: @@ -118,6 +113,5 @@ class CustomValidationMixin(SettingsMixin, ValidationMixin, InvenTreePlugin): def generate_batch_code(self): """Generate a new batch code.""" - now = datetime.now() return f"BATCH-{now.year}:{now.month}:{now.day}" diff --git a/InvenTree/plugin/serializers.py b/InvenTree/plugin/serializers.py index 9ae55fd2f3..49b9a6996e 100644 --- a/InvenTree/plugin/serializers.py +++ b/InvenTree/plugin/serializers.py @@ -116,7 +116,6 @@ class PluginConfigInstallSerializer(serializers.Serializer): def save(self): """Install a plugin from a package registry and set operational results as instance data.""" - from plugin.installer import install_plugin data = self.validated_data @@ -145,7 +144,6 @@ class PluginActivateSerializer(serializers.Serializer): def update(self, instance, validated_data): """Apply the new 'active' value to the plugin instance""" - from InvenTree.tasks import check_for_migrations, offload_task instance.active = validated_data.get('active', True) diff --git a/InvenTree/plugin/test_api.py b/InvenTree/plugin/test_api.py index ae0b4bb175..a62b1c6d64 100644 --- a/InvenTree/plugin/test_api.py +++ b/InvenTree/plugin/test_api.py @@ -103,7 +103,6 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase): def test_plugin_activate(self): """Test the plugin activate.""" - test_plg = self.plugin_confs.first() def assert_plugin_active(self, active): @@ -189,7 +188,6 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase): def test_check_plugin(self): """Test check_plugin function.""" - # No argument with self.assertRaises(NotFound) as exc: check_plugin(plugin_slug=None, plugin_pk=None) @@ -207,7 +205,6 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase): def test_plugin_settings(self): """Test plugin settings access via the API""" - # Ensure we have superuser permissions self.user.is_superuser = True self.user.save() diff --git a/InvenTree/plugin/test_plugin.py b/InvenTree/plugin/test_plugin.py index 51c6984c09..b0b91ed2bf 100644 --- a/InvenTree/plugin/test_plugin.py +++ b/InvenTree/plugin/test_plugin.py @@ -69,7 +69,6 @@ class InvenTreePluginTests(TestCase): @classmethod def setUpTestData(cls): """Setup for all tests.""" - super().setUpTestData() cls.plugin = InvenTreePlugin() @@ -184,7 +183,6 @@ class InvenTreePluginTests(TestCase): def test_version(self): """Test Version checks""" - self.assertFalse(self.plugin_version.check_version([0, 0, 3])) self.assertTrue(self.plugin_version.check_version([0, 1, 0])) self.assertFalse(self.plugin_version.check_version([0, 1, 4])) @@ -202,7 +200,6 @@ class RegistryTests(TestCase): def run_package_test(self, directory): """General runner for testing package based installs.""" - # Patch environment variable to add dir envs = {'INVENTREE_PLUGIN_TEST_DIR': directory} with mock.patch.dict(os.environ, envs): @@ -236,7 +233,6 @@ class RegistryTests(TestCase): def test_folder_loading(self): """Test that plugins in folders outside of BASE_DIR get loaded.""" - # Run in temporary directory -> always a new random name with tempfile.TemporaryDirectory() as tmp: # Fill directory with sample data @@ -262,7 +258,6 @@ class RegistryTests(TestCase): def test_broken_samples(self): """Test that the broken samples trigger reloads.""" - # In the base setup there are no errors self.assertEqual(len(registry.errors), 0) diff --git a/InvenTree/report/api.py b/InvenTree/report/api.py index a0482b3d64..c2546367e6 100644 --- a/InvenTree/report/api.py +++ b/InvenTree/report/api.py @@ -69,7 +69,6 @@ class ReportFilterMixin: def get_items(self): """Return a list of database objects from query parameters""" - if not self.ITEM_MODEL: raise NotImplementedError(f"ITEM_MODEL attribute not defined for {__class__}") @@ -100,7 +99,6 @@ class ReportFilterMixin: As each 'report' instance may optionally define its own filters, the resulting queryset is the 'union' of the two """ - queryset = super().filter_queryset(queryset) items = self.get_items() @@ -308,7 +306,6 @@ class StockItemTestReportPrint(StockItemTestReportMixin, ReportPrintMixin, Retri def report_callback(self, item, report, request): """Callback to (optionally) save a copy of the generated report""" - if common.models.InvenTreeSetting.get_setting('REPORT_ATTACH_TEST_REPORT', cache=False): # Construct a PDF file object diff --git a/InvenTree/report/apps.py b/InvenTree/report/apps.py index df948fcc47..ac3f413e23 100644 --- a/InvenTree/report/apps.py +++ b/InvenTree/report/apps.py @@ -17,7 +17,6 @@ class ReportConfig(AppConfig): def ready(self): """This function is called whenever the report app is loaded.""" - from InvenTree.ready import (canAppAccessDatabase, isInMainThread, isPluginRegistryLoaded) @@ -190,7 +189,6 @@ class ReportConfig(AppConfig): def create_default_return_order_reports(self): """Create database entries for the default ReturnOrderReport templates""" - try: from report.models import ReturnOrderReport except Exception: # pragma: no cover @@ -210,7 +208,6 @@ class ReportConfig(AppConfig): def create_default_stock_location_reports(self): """Create database entries for the default StockLocationReport templates""" - try: from report.models import StockLocationReport except Exception: # pragma: no cover diff --git a/InvenTree/report/helpers.py b/InvenTree/report/helpers.py index 40d5c99665..58c0952082 100644 --- a/InvenTree/report/helpers.py +++ b/InvenTree/report/helpers.py @@ -9,7 +9,6 @@ logger = logging.getLogger('inventree') def report_page_size_options(): """Returns a list of page size options for PDF reports.""" - return [ ('A4', _('A4')), ('Legal', _('Legal')), @@ -19,7 +18,6 @@ def report_page_size_options(): def report_page_size_default(): """Returns the default page size for PDF reports.""" - from common.models import InvenTreeSetting try: diff --git a/InvenTree/report/models.py b/InvenTree/report/models.py index 4e6e1da7e1..6f327af973 100644 --- a/InvenTree/report/models.py +++ b/InvenTree/report/models.py @@ -103,7 +103,6 @@ class ReportBase(models.Model): def __init__(self, *args, **kwargs): """Initialize the particular report instance""" - super().__init__(*args, **kwargs) self._meta.get_field('page_size').choices = report.helpers.report_page_size_options() @@ -126,7 +125,6 @@ class ReportBase(models.Model): def rename_file(self, filename): """Function for renaming uploaded file""" - filename = os.path.basename(filename) path = os.path.join('report', 'report_template', self.getSubdir(), filename) @@ -226,7 +224,6 @@ class ReportTemplateBase(MetadataMixin, ReportBase): def get_report_size(self): """Return the printable page size for this report""" - try: page_size_default = common.models.InvenTreeSetting.get_setting('REPORT_DEFAULT_PAGE_SIZE', 'A4') except Exception: @@ -352,7 +349,6 @@ class TestReport(ReportTemplateBase): - Second, any 'non required' tests - Finally, any test results which do not match a test """ - keys = [] for test in stock_item.part.getTestTemplates(required=True): @@ -564,7 +560,6 @@ class ReturnOrderReport(ReportTemplateBase): def get_context_data(self, request): """Return custom context data for the ReturnOrderReport template""" - order = self.object_to_print return { @@ -580,7 +575,6 @@ class ReturnOrderReport(ReportTemplateBase): def rename_snippet(instance, filename): """Function to rename a report snippet once uploaded""" - filename = os.path.basename(filename) path = os.path.join('report', 'snippets', filename) @@ -619,7 +613,6 @@ class ReportSnippet(models.Model): def rename_asset(instance, filename): """Function to rename an asset file when uploaded""" - filename = os.path.basename(filename) path = os.path.join('report', 'assets', filename) diff --git a/InvenTree/report/serializers.py b/InvenTree/report/serializers.py index 5c15b14e0e..2e690b6791 100644 --- a/InvenTree/report/serializers.py +++ b/InvenTree/report/serializers.py @@ -16,7 +16,6 @@ class ReportSerializerBase(InvenTreeModelSerializer): @staticmethod def report_fields(): """Generic serializer fields for a report template""" - return [ 'pk', 'name', diff --git a/InvenTree/report/templatetags/report.py b/InvenTree/report/templatetags/report.py index 1d6775521f..28a96f0d44 100644 --- a/InvenTree/report/templatetags/report.py +++ b/InvenTree/report/templatetags/report.py @@ -30,7 +30,6 @@ def getindex(container: list, index: int): container: A python list object index: The index to retrieve from the list """ - # Index *must* be an integer try: index = int(index) @@ -110,7 +109,6 @@ def uploaded_image(filename, replace_missing=True, replacement_file='blank_image Returns: A fully qualified path to the image """ - if type(filename) is SafeString: # Prepend an empty string to enforce 'stringiness' filename = '' + filename @@ -154,7 +152,6 @@ def uploaded_image(filename, replace_missing=True, replacement_file='blank_image @register.simple_tag() def encode_svg_image(filename): """Return a base64-encoded svg image data string""" - if type(filename) is SafeString: # Prepend an empty string to enforce 'stringiness' filename = '' + filename @@ -190,7 +187,6 @@ def part_image(part: Part): Raises: TypeError if provided part is not a Part instance """ - if type(part) is Part: img = part.image.name @@ -227,7 +223,6 @@ def company_image(company): Raises: TypeError if provided company is not a Company instance """ - if type(company) is Company: img = company.image.name else: @@ -243,7 +238,6 @@ def logo_image(**kwargs): - If a custom logo has been provided, return a path to that logo - Otherwise, return a path to the default InvenTree logo """ - # If in debug mode, return URL to the image, not a local file debug_mode = InvenTreeSetting.get_setting('REPORT_DEBUG_MODE') @@ -294,7 +288,6 @@ def divide(x, y): @register.simple_tag def render_currency(money, **kwargs): """Render a currency / Money object""" - return InvenTree.helpers_model.render_currency(money, **kwargs) @@ -307,7 +300,6 @@ def render_html_text(text: str, **kwargs): italic: Boolean, whether italic (or not) heading: str, heading level e.g. 'h3' """ - tags = [] if kwargs.get('bold', False): diff --git a/InvenTree/report/tests.py b/InvenTree/report/tests.py index 321e323c91..e92fc980dc 100644 --- a/InvenTree/report/tests.py +++ b/InvenTree/report/tests.py @@ -31,7 +31,6 @@ class ReportTagTest(TestCase): def test_getindex(self): """Tests for the 'getindex' template tag""" - fn = report_tags.getindex data = [1, 2, 3, 4, 5, 6] @@ -45,7 +44,6 @@ class ReportTagTest(TestCase): def test_getkey(self): """Tests for the 'getkey' template tag""" - data = { 'hello': 'world', 'foo': 'bar', @@ -58,7 +56,6 @@ class ReportTagTest(TestCase): def test_asset(self): """Tests for asset files""" - # Test that an error is raised if the file does not exist for b in [True, False]: self.debug_mode(b) @@ -87,7 +84,6 @@ class ReportTagTest(TestCase): def test_uploaded_image(self): """Tests for retrieving uploaded images""" - # Test for a missing image for b in [True, False]: self.debug_mode(b) @@ -132,19 +128,16 @@ class ReportTagTest(TestCase): def test_part_image(self): """Unit tests for the 'part_image' tag""" - with self.assertRaises(TypeError): report_tags.part_image(None) def test_company_image(self): """Unit tests for the 'company_image' tag""" - with self.assertRaises(TypeError): report_tags.company_image(None) def test_logo_image(self): """Unit tests for the 'logo_image' tag""" - # By default, should return the core InvenTree logo for b in [True, False]: self.debug_mode(b) @@ -153,7 +146,6 @@ class ReportTagTest(TestCase): def test_maths_tags(self): """Simple tests for mathematical operator tags""" - self.assertEqual(report_tags.add(1, 2), 3) self.assertEqual(report_tags.subtract(10, 4.2), 5.8) self.assertEqual(report_tags.multiply(2.3, 4), 9.2) @@ -165,7 +157,6 @@ class BarcodeTagTest(TestCase): def test_barcode(self): """Test the barcode generation tag""" - barcode = barcode_tags.barcode("12345") self.assertTrue(isinstance(barcode, str)) @@ -178,7 +169,6 @@ class BarcodeTagTest(TestCase): def test_qrcode(self): """Test the qrcode generation tag""" - # Test with default settings qrcode = barcode_tags.qrcode("hello world") self.assertTrue(isinstance(qrcode, str)) diff --git a/InvenTree/stock/admin.py b/InvenTree/stock/admin.py index 43e2187df6..939e074369 100644 --- a/InvenTree/stock/admin.py +++ b/InvenTree/stock/admin.py @@ -150,7 +150,6 @@ class StockItemResource(InvenTreeResource): def dehydrate_purchase_price(self, item): """Render purchase pric as float""" - if item.purchase_price is not None: return float(item.purchase_price.amount) diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index 3212e5e09e..581b7417ce 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -234,7 +234,6 @@ class StockLocationFilter(rest_filters.FilterSet): def filter_has_location_type(self, queryset, name, value): """Filter by whether or not the location has a location type""" - if str2bool(value): return queryset.exclude(location_type=None) else: @@ -256,7 +255,6 @@ class StockLocationList(APIDownloadMixin, ListCreateAPI): def download_queryset(self, queryset, export_format): """Download the filtered queryset as a data file""" - dataset = LocationResource().export(queryset=queryset) filedata = dataset.export(export_format) filename = f"InvenTree_Locations.{export_format}" @@ -265,7 +263,6 @@ class StockLocationList(APIDownloadMixin, ListCreateAPI): def get_queryset(self, *args, **kwargs): """Return annotated queryset for the StockLocationList endpoint""" - queryset = super().get_queryset(*args, **kwargs) queryset = StockSerializers.LocationSerializer.annotate_queryset(queryset) return queryset @@ -477,14 +474,12 @@ class StockFilter(rest_filters.FilterSet): def filter_status(self, queryset, name, value): """Filter by integer status code""" - return queryset.filter(status=value) allocated = rest_filters.BooleanFilter(label='Is Allocated', method='filter_allocated') def filter_allocated(self, queryset, name, value): """Filter by whether or not the stock item is 'allocated'""" - if str2bool(value): # Filter StockItem with either build allocations or sales order allocations return queryset.filter(Q(sales_order_allocations__isnull=False) | Q(allocations__isnull=False)) @@ -496,7 +491,6 @@ class StockFilter(rest_filters.FilterSet): def filter_expired(self, queryset, name, value): """Filter by whether or not the stock item has expired""" - if not common.settings.stock_expiry_enabled(): return queryset @@ -509,7 +503,6 @@ class StockFilter(rest_filters.FilterSet): def filter_external(self, queryset, name, value): """Filter by whether or not the stock item is located in an external location""" - if str2bool(value): return queryset.filter(location__external=True) else: @@ -643,7 +636,6 @@ class StockFilter(rest_filters.FilterSet): def filter_ancestor(self, queryset, name, ancestor): """Filter based on ancestor stock item""" - return queryset.filter( parent__in=ancestor.get_descendants(include_self=True) ) @@ -1426,7 +1418,6 @@ class LocationDetail(CustomRetrieveUpdateDestroyAPI): def get_serializer(self, *args, **kwargs): """Add extra context to serializer based on provided query parameters""" - try: params = self.request.query_params @@ -1440,7 +1431,6 @@ class LocationDetail(CustomRetrieveUpdateDestroyAPI): def get_queryset(self, *args, **kwargs): """Return annotated queryset for the StockLocationList endpoint""" - queryset = super().get_queryset(*args, **kwargs) queryset = StockSerializers.LocationSerializer.annotate_queryset(queryset) return queryset diff --git a/InvenTree/stock/filters.py b/InvenTree/stock/filters.py index d2bb7a2b67..0d5207c801 100644 --- a/InvenTree/stock/filters.py +++ b/InvenTree/stock/filters.py @@ -12,7 +12,6 @@ def annotate_location_items(filter: Q = None): - Includes items in subcategories also - Requires subquery to perform annotation """ - # Construct a subquery to provide all items in this location and any sublocations subquery = stock.models.StockItem.objects.exclude(location=None).filter( location__tree_id=OuterRef('tree_id'), diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index ad94690519..9bf0d69778 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -310,7 +310,6 @@ def generate_batch_code(): Also, this function is exposed to the ValidationMixin plugin class, allowing custom plugins to be used to generate new batch code values """ - # First, check if any plugins can generate batch codes from plugin.registry import registry @@ -346,7 +345,6 @@ def default_delete_on_deplete(): Prior to 2022-12-24, this field was set to True by default. Now, there is a user-configurable setting to govern default behaviour. """ - try: return common.models.InvenTreeSetting.get_setting('STOCK_DELETE_DEPLETED_DEFAULT', True) except (IntegrityError, OperationalError): @@ -416,7 +414,6 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo This is used for efficient numerical sorting """ - serial = str(getattr(self, 'serial', '')).strip() from plugin.registry import registry @@ -505,7 +502,6 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo - Unique serial number requirement - Adds a transaction note when the item is first created. """ - self.validate_unique() self.clean() @@ -601,7 +597,6 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo - Validation is performed by custom plugins. - By default, no validation checks are performed """ - from plugin.registry import registry for plugin in registry.with_mixin('validation'): @@ -622,7 +617,6 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo - The location is not structural - Quantity must be 1 if the StockItem has a serial number """ - if self.location is not None and self.location.structural: raise ValidationError( {'location': _("Stock items cannot be located into structural stock locations!")}) @@ -1167,7 +1161,6 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo def sales_order_allocation_count(self, active=True): """Return the total quantity allocated to SalesOrders.""" - query = self.get_sales_order_allocations(active=active) query = query.aggregate(q=Coalesce(Sum('quantity'), Decimal(0))) diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py index 647f96b789..21453536d0 100644 --- a/InvenTree/stock/serializers.py +++ b/InvenTree/stock/serializers.py @@ -225,7 +225,6 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeTagModelSerializer): def validate_part(self, part): """Ensure the provided Part instance is valid""" - if part.virtual: raise ValidationError(_("Stock item cannot be created for virtual parts")) @@ -240,7 +239,6 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeTagModelSerializer): @staticmethod def annotate_queryset(queryset): """Add some extra annotations to the queryset, performing database queries as efficiently as possible.""" - queryset = queryset.prefetch_related( 'location', 'sales_order', @@ -591,7 +589,6 @@ class ConvertStockItemSerializer(serializers.Serializer): def validate_part(self, part): """Ensure that the provided part is a valid option for the stock item""" - stock_item = self.context['item'] valid_options = stock_item.part.get_conversion_options() @@ -605,7 +602,6 @@ class ConvertStockItemSerializer(serializers.Serializer): - If a SupplierPart is assigned, we cannot convert! """ - data = super().validate(data) stock_item = self.context['item'] @@ -653,7 +649,6 @@ class ReturnStockItemSerializer(serializers.Serializer): def save(self): """Save the serialzier to return the item into stock""" - item = self.context['item'] request = self.context['request'] @@ -691,7 +686,6 @@ class StockChangeStatusSerializer(serializers.Serializer): def validate_items(self, items): """Validate the selected stock items""" - if len(items) == 0: raise ValidationError(_("No stock items selected")) @@ -712,7 +706,6 @@ class StockChangeStatusSerializer(serializers.Serializer): @transaction.atomic def save(self): """Save the serializer to change the status of the selected stock items""" - data = self.validated_data items = data['items'] @@ -787,7 +780,6 @@ class StockLocationTypeSerializer(InvenTree.serializers.InvenTreeModelSerializer @staticmethod def annotate_queryset(queryset): """Add location count to each location type.""" - return queryset.annotate( location_count=Count("stock_locations") ) @@ -844,7 +836,6 @@ class LocationSerializer(InvenTree.serializers.InvenTreeTagModelSerializer): def __init__(self, *args, **kwargs): """Optionally add or remove extra fields""" - path_detail = kwargs.pop('path_detail', False) super().__init__(*args, **kwargs) @@ -855,7 +846,6 @@ class LocationSerializer(InvenTree.serializers.InvenTreeTagModelSerializer): @staticmethod def annotate_queryset(queryset): """Annotate extra information to the queryset""" - # Annotate the number of stock items which exist in this category (including subcategories) queryset = queryset.annotate( items=stock.filters.annotate_location_items() diff --git a/InvenTree/stock/test_api.py b/InvenTree/stock/test_api.py index 16ef993c52..98b16882d5 100644 --- a/InvenTree/stock/test_api.py +++ b/InvenTree/stock/test_api.py @@ -240,7 +240,6 @@ class StockLocationTest(StockAPITestCase): - Stock items cannot be located to structural locations - Check that stock location change to structural fails if items located into it """ - # Create our structural stock location structural_location = StockLocation.objects.create( name='Structural stock location', @@ -348,7 +347,6 @@ class StockLocationTypeTest(StockAPITestCase): def test_list(self): """Test that the list endpoint works as expected.""" - location_types = [ StockLocationType.objects.create(name="Type 1", description="Type 1 desc", icon="fas fa-box"), StockLocationType.objects.create(name="Type 2", description="Type 2 desc", icon="fas fa-box"), @@ -397,7 +395,6 @@ class StockItemListTest(StockAPITestCase): def test_top_level_filtering(self): """Test filtering against "top level" stock location""" - # No filters, should return *all* items response = self.get(self.list_url, {}, expected_code=200) self.assertEqual(len(response.data), StockItem.objects.count()) @@ -789,7 +786,6 @@ class StockItemTest(StockAPITestCase): def test_stock_item_create_withsupplierpart(self): """Test creation of a StockItem via the API, including SupplierPart data.""" - # POST with non-existent supplier part response = self.post( self.list_url, @@ -1140,7 +1136,6 @@ class StockItemTest(StockAPITestCase): def test_return_from_customer(self): """Test that we can return a StockItem from a customer, via the API""" - # Assign item to customer item = StockItem.objects.get(pk=521) customer = company.models.Company.objects.get(pk=4) @@ -1179,7 +1174,6 @@ class StockItemTest(StockAPITestCase): def test_convert_to_variant(self): """Test that we can convert a StockItem to a variant part via the API""" - category = part.models.PartCategory.objects.get(pk=3) # First, construct a set of template / variant parts @@ -1242,7 +1236,6 @@ class StockItemTest(StockAPITestCase): def test_set_status(self): """Test API endpoint for setting StockItem status""" - url = reverse('api-stock-change-status') prt = Part.objects.first() @@ -1512,7 +1505,6 @@ class StockTestResultTest(StockAPITestCase): def test_bulk_delete(self): """Test that the BulkDelete endpoint works for this model""" - n = StockItemTestResult.objects.count() tests = [] @@ -1687,7 +1679,6 @@ class StockMergeTest(StockAPITestCase): @classmethod def setUpTestData(cls): """Setup for all tests.""" - super().setUpTestData() cls.part = part.models.Part.objects.get(pk=25) @@ -1877,7 +1868,6 @@ class StockMetadataAPITest(InvenTreeAPITestCase): def metatester(self, apikey, model): """Generic tester""" - modeldata = model.objects.first() # Useless test unless a model object is found @@ -1906,7 +1896,6 @@ class StockMetadataAPITest(InvenTreeAPITestCase): def test_metadata(self): """Test all endpoints""" - for apikey, model in { 'api-location-metadata': StockLocation, 'api-stock-test-result-metadata': StockItemTestResult, diff --git a/InvenTree/stock/test_migrations.py b/InvenTree/stock/test_migrations.py index 51e00c8093..e153344e8f 100644 --- a/InvenTree/stock/test_migrations.py +++ b/InvenTree/stock/test_migrations.py @@ -13,7 +13,6 @@ class TestSerialNumberMigration(MigratorTestCase): def prepare(self): """Create initial data for this migration""" - Part = self.old_state.apps.get_model('part', 'part') StockItem = self.old_state.apps.get_model('stock', 'stockitem') @@ -53,7 +52,6 @@ class TestSerialNumberMigration(MigratorTestCase): def test_migrations(self): """Test that the migrations have been applied correctly""" - StockItem = self.new_state.apps.get_model('stock', 'stockitem') # Check that the serial number integer conversion has been applied correctly @@ -77,7 +75,6 @@ class TestScheduledForDeletionMigration(MigratorTestCase): def prepare(self): """Create some initial stock items""" - Part = self.old_state.apps.get_model('part', 'part') StockItem = self.old_state.apps.get_model('stock', 'stockitem') @@ -124,7 +121,6 @@ class TestScheduledForDeletionMigration(MigratorTestCase): def test_migration(self): """Test that all stock items were actually removed""" - StockItem = self.new_state.apps.get_model('stock', 'stockitem') # All the "scheduled for deletion" items have been removed diff --git a/InvenTree/stock/test_views.py b/InvenTree/stock/test_views.py index be2c58733c..38a7afee01 100644 --- a/InvenTree/stock/test_views.py +++ b/InvenTree/stock/test_views.py @@ -39,7 +39,6 @@ class StockDetailTest(StockViewTestCase): def test_basic_info(self): """Test that basic stock item info is rendered""" - url = reverse('stock-item-detail', kwargs={'pk': 1}) response = self.client.get(url) diff --git a/InvenTree/stock/tests.py b/InvenTree/stock/tests.py index 06ae817ce9..6843ee8bce 100644 --- a/InvenTree/stock/tests.py +++ b/InvenTree/stock/tests.py @@ -55,7 +55,6 @@ class StockTest(StockTestBase): def test_pathstring(self): """Check that pathstring updates occur as expected""" - a = StockLocation.objects.create(name="A") b = StockLocation.objects.create(name="B", parent=a) c = StockLocation.objects.create(name="C", parent=b) @@ -131,7 +130,6 @@ class StockTest(StockTestBase): def test_link(self): """Test the link URL field validation""" - item = StockItem.objects.get(pk=1) # Check that invalid URLs fail @@ -178,7 +176,6 @@ class StockTest(StockTestBase): def test_serial_numbers(self): """Test serial number uniqueness""" - # Ensure that 'global uniqueness' setting is enabled InvenTreeSetting.set_setting('SERIAL_NUMBER_GLOBALLY_UNIQUE', True, self.user) @@ -284,7 +281,6 @@ class StockTest(StockTestBase): def test_parent_locations(self): """Test parent.""" - # Ensure pathstring gets updated self.drawer3.save() @@ -318,7 +314,6 @@ class StockTest(StockTestBase): def test_items(self): """Test has_items.""" - # Drawer 3 should have three stock items self.assertEqual(self.drawer3.stock_items.count(), 18) self.assertEqual(self.drawer3.item_count, 18) @@ -495,7 +490,6 @@ class StockTest(StockTestBase): def test_return_from_customer(self): """Test removing previous allocated stock from customer""" - it = StockItem.objects.get(pk=2) # First establish total stock for this part @@ -940,7 +934,6 @@ class StockBarcodeTest(StockTestBase): def test_stock_item_barcode_basics(self): """Simple tests for the StockItem barcode integration""" - item = StockItem.objects.get(pk=1) self.assertEqual(StockItem.barcode_model_type(), 'stockitem') @@ -957,7 +950,6 @@ class StockBarcodeTest(StockTestBase): def test_location_barcode_basics(self): """Simple tests for the StockLocation barcode integration""" - self.assertEqual(StockLocation.barcode_model_type(), 'stocklocation') loc = StockLocation.objects.get(pk=1) @@ -985,7 +977,6 @@ class VariantTest(StockTestBase): def test_serial_numbers(self): """Test serial number functionality for variant / template parts.""" - InvenTreeSetting.set_setting('SERIAL_NUMBER_GLOBALLY_UNIQUE', False, self.user) chair = Part.objects.get(pk=10000) diff --git a/InvenTree/users/apps.py b/InvenTree/users/apps.py index 1f6e503fc6..bab4c03865 100644 --- a/InvenTree/users/apps.py +++ b/InvenTree/users/apps.py @@ -18,7 +18,6 @@ class UsersConfig(AppConfig): def ready(self): """Called when the 'users' app is loaded at runtime""" - # skip loading if plugin registry is not loaded or we run in a background thread if not isPluginRegistryLoaded() or not isInMainThread(): return diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index 7f92a1f62c..0d2f94448a 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -259,7 +259,6 @@ class RuleSet(models.Model): @classmethod def check_table_permission(cls, user, table, permission): """Check if the provided user has the specified permission against the table.""" - # Superuser knows no bounds if user.is_superuser: return True @@ -531,7 +530,6 @@ def clear_user_role_cache(user): Args: user: The User object to be expunged from the cache """ - for role in RuleSet.RULESET_MODELS.keys(): for perm in ['add', 'change', 'view', 'delete']: key = f"role_{user}_{role}_{perm}" @@ -540,7 +538,6 @@ def clear_user_role_cache(user): def get_user_roles(user): """Return all roles available to a given user""" - roles = set() for group in user.groups.all(): @@ -738,7 +735,6 @@ class Owner(models.Model): def is_user_allowed(self, user, include_group: bool = False): """Check if user is allowed to access something owned by this owner.""" - user_owner = Owner.get_owner(user) return user_owner in self.get_related_owners(include_group=include_group) @@ -761,7 +757,6 @@ def delete_owner(sender, instance, **kwargs): @receiver(post_save, sender=get_user_model(), dispatch_uid='clear_user_cache') def clear_user_cache(sender, instance, **kwargs): """Callback function when a user object is saved""" - clear_user_role_cache(instance) diff --git a/InvenTree/users/test_api.py b/InvenTree/users/test_api.py index 017655646f..d22a2ada7b 100644 --- a/InvenTree/users/test_api.py +++ b/InvenTree/users/test_api.py @@ -11,7 +11,6 @@ class UserAPITests(InvenTreeAPITestCase): def test_user_api(self): """Tests for User API endpoints""" - response = self.get( reverse('api-user-list'), expected_code=200 @@ -36,7 +35,6 @@ class UserAPITests(InvenTreeAPITestCase): def test_group_api(self): """Tests for the Group API endpoints""" - response = self.get( reverse('api-group-list'), expected_code=200, diff --git a/docs/docs/hooks.py b/docs/docs/hooks.py index b51141e254..fdd6f97464 100644 --- a/docs/docs/hooks.py +++ b/docs/docs/hooks.py @@ -11,7 +11,6 @@ import requests def fetch_rtd_versions(): """Get a list of RTD docs versions to build the version selector""" - print("Fetching documentation versions from ReadTheDocs") versions = [] @@ -94,7 +93,6 @@ def get_release_data(): - First look to see if 'releases.json' file exists - If data does not exist in this file, request via the github API """ - json_file = os.path.join(os.path.dirname(__file__), 'releases.json') releases = [] @@ -153,7 +151,6 @@ def on_config(config, *args, **kwargs): We can use these to determine (at run time) where we are hosting """ - rtd = os.environ.get('READTHEDOCS', False) # Check for 'versions.json' file diff --git a/docs/main.py b/docs/main.py index 1ba7a2965e..8f7f59c36d 100644 --- a/docs/main.py +++ b/docs/main.py @@ -9,7 +9,6 @@ def define_env(env): @env.macro def listimages(subdir): """Return a listing of all asset files in the provided subdir""" - here = os.path.dirname(__file__) directory = os.path.join(here, 'docs', 'assets', 'images', subdir) diff --git a/tasks.py b/tasks.py index c7d5b1d01d..8bd97d6ad3 100644 --- a/tasks.py +++ b/tasks.py @@ -98,7 +98,6 @@ def yarn(c, cmd, pty: bool = False): cmd: Yarn command to run. pty (bool, optional): Run an interactive session. Defaults to False. """ - path = managePyDir().parent.joinpath('src').joinpath('frontend') c.run(f'cd "{path}" && {cmd}', pty=pty) @@ -246,7 +245,6 @@ def translate_stats(c): The file generated from this is needed for the UI. """ - # Recompile the translation files (.mo) # We do not run 'invoke translate' here, as that will touch the source (.po) files too! try: @@ -281,7 +279,6 @@ def translate(c): @task def backup(c): """Backup the database and media files.""" - print("Backing up InvenTree database...") manage(c, "dbbackup --noinput --clean --compress") print("Backing up InvenTree media files...") @@ -291,7 +288,6 @@ def backup(c): @task def restore(c): """Restore the database and media files.""" - print("Restoring InvenTree database...") manage(c, "dbrestore --noinput --uncompress") print("Restoring InvenTree media files...") @@ -339,7 +335,6 @@ def update(c, skip_backup=False, frontend: bool = False): - clean_settings - translate_stats """ - # Ensure required components are installed install(c) @@ -690,7 +685,6 @@ def test(c, disable_pty=False, runtest='', migrations=False, report=False, cover @task(help={'dev': 'Set up development environment at the end'}) def setup_test(c, ignore_update=False, dev=False, path="inventree-demo-dataset"): """Setup a testing environment.""" - from InvenTree.InvenTree.config import get_media_dir if not ignore_update: @@ -790,7 +784,6 @@ def frontend_compile(c): Args: c: Context variable """ - frontend_install(c) frontend_trans(c) frontend_build(c) @@ -863,7 +856,6 @@ def frontend_download(c, ref=None, tag=None, file=None, repo="InvenTree/inventre 3. invoke frontend-download --file /home/vscode/Downloads/frontend-build.zip This will extract your zip file and place the contents at the correct destination """ - import functools import subprocess from tempfile import NamedTemporaryFile