diff --git a/contrib/container/Dockerfile b/contrib/container/Dockerfile index 8d618dcfc7..67921c33de 100644 --- a/contrib/container/Dockerfile +++ b/contrib/container/Dockerfile @@ -101,7 +101,7 @@ RUN ./install_build_packages.sh --no-cache --virtual .build-deps && \ # Frontend builder image: FROM prebuild AS frontend -RUN apk add --no-cache --update nodejs npm && npm install -g yarn@v1.22.22 +RUN apk add --no-cache --update nodejs npm && npm install -g yarn@v1.22.22 --ignore-scripts RUN yarn config set network-timeout 600000 -g COPY src ${INVENTREE_HOME}/src COPY tasks.py ${INVENTREE_HOME}/tasks.py @@ -145,7 +145,7 @@ RUN pip install uv==0.1.26 --no-cache-dir && pip install -r base_requirements.tx # Install nodejs / npm / yarn -RUN apk add --no-cache --update nodejs npm && npm install -g yarn@v1.22.22 +RUN apk add --no-cache --update nodejs npm && npm install -g yarn@v1.22.22 --ignore-scripts RUN yarn config set network-timeout 600000 -g # The development image requires the source code to be mounted to /home/inventree/ diff --git a/docs/extract_schema.py b/docs/extract_schema.py index 95f99d0c66..1ff883efb9 100644 --- a/docs/extract_schema.py +++ b/docs/extract_schema.py @@ -125,8 +125,8 @@ def generate_index_file(version: str): f.write(output) -def extract_refs(data: dict, components: dict) -> list: - """Extract a list of refs from the provided paths dict. +def extract_refs(data: dict, components: dict) -> dict: + """Extract a dict of refs from the provided paths dict. The refs are located like so: ::responses::content:application/json:schema:$ref diff --git a/src/backend/InvenTree/InvenTree/admin.py b/src/backend/InvenTree/InvenTree/admin.py index 1c79d07cc1..1f19996ddb 100644 --- a/src/backend/InvenTree/InvenTree/admin.py +++ b/src/backend/InvenTree/InvenTree/admin.py @@ -91,10 +91,13 @@ class InvenTreeResource(ModelResource): """ # We can automatically determine which fields might need such a conversion for field in self.Meta.model._meta.fields: - if isinstance(field, CharField): - if field.blank and not field.null: - if field.name not in self.CONVERT_NULL_FIELDS: - self.CONVERT_NULL_FIELDS.append(field.name) + if ( + isinstance(field, CharField) + and field.blank + and not field.null + and field.name not in self.CONVERT_NULL_FIELDS + ): + self.CONVERT_NULL_FIELDS.append(field.name) for field in self.CONVERT_NULL_FIELDS: if field in row and row[field] is None: diff --git a/src/backend/InvenTree/InvenTree/config.py b/src/backend/InvenTree/InvenTree/config.py index 6e5299ad3f..008da3cccb 100644 --- a/src/backend/InvenTree/InvenTree/config.py +++ b/src/backend/InvenTree/InvenTree/config.py @@ -443,10 +443,10 @@ def get_frontend_settings(debug=True): if 'environment' not in settings: settings['environment'] = 'development' if debug else 'production' - if debug and 'show_server_selector' not in settings: + if (debug and 'show_server_selector' not in settings) or len( + settings['server_list'] + ) == 0: # In debug mode, show server selector by default - settings['show_server_selector'] = True - elif len(settings['server_list']) == 0: # If no servers are specified, show server selector settings['show_server_selector'] = True diff --git a/src/backend/InvenTree/InvenTree/fields.py b/src/backend/InvenTree/InvenTree/fields.py index 3d1e6698fa..f4dc686212 100644 --- a/src/backend/InvenTree/InvenTree/fields.py +++ b/src/backend/InvenTree/InvenTree/fields.py @@ -38,10 +38,9 @@ class InvenTreeRestURLField(RestURLField): 'INVENTREE_STRICT_URLS', True, cache=False ) - if not strict_urls and data is not empty: - if '://' not in data: - # Validate as if there were a schema provided - data = 'http://' + data + if not strict_urls and data is not empty and '://' not in data: + # Validate as if there were a schema provided + data = 'http://' + data return super().run_validation(data=data) diff --git a/src/backend/InvenTree/InvenTree/models.py b/src/backend/InvenTree/InvenTree/models.py index 75c5d845d8..6317c673d2 100644 --- a/src/backend/InvenTree/InvenTree/models.py +++ b/src/backend/InvenTree/InvenTree/models.py @@ -458,9 +458,8 @@ class ReferenceIndexingMixin(models.Model): reference_int = InvenTree.helpers.extract_int(reference) - if validate: - if reference_int > models.BigIntegerField.MAX_BIGINT: - raise ValidationError({'reference': _('Reference number is too large')}) + if validate and reference_int > models.BigIntegerField.MAX_BIGINT: + raise ValidationError({'reference': _('Reference number is too large')}) return reference_int diff --git a/src/backend/InvenTree/InvenTree/serializers.py b/src/backend/InvenTree/InvenTree/serializers.py index 3b4978d15d..08c53d20e5 100644 --- a/src/backend/InvenTree/InvenTree/serializers.py +++ b/src/backend/InvenTree/InvenTree/serializers.py @@ -603,7 +603,7 @@ class DataFileUploadSerializer(serializers.Serializer): """Perform validation checks on the uploaded data file.""" self.filename = data_file.name - name, ext = os.path.splitext(data_file.name) + _name, ext = os.path.splitext(data_file.name) # Remove the leading . from the extension ext = ext[1:] diff --git a/src/backend/InvenTree/build/api.py b/src/backend/InvenTree/build/api.py index 291e0694ae..97ead63494 100644 --- a/src/backend/InvenTree/build/api.py +++ b/src/backend/InvenTree/build/api.py @@ -1,5 +1,6 @@ """JSON API for the Build app.""" +from __future__ import annotations from django.db.models import F, Q from django.urls import include, path from django.utils.translation import gettext_lazy as _ @@ -363,7 +364,7 @@ class BuildLineList(BuildLineEndpoint, ListCreateAPI): 'bom_item__reference', ] - def get_source_build(self) -> Build: + def get_source_build(self) -> Build | None: """Return the target build for the BuildLine queryset.""" try: @@ -379,7 +380,7 @@ class BuildLineList(BuildLineEndpoint, ListCreateAPI): class BuildLineDetail(BuildLineEndpoint, RetrieveUpdateDestroyAPI): """API endpoint for detail view of a BuildLine object.""" - def get_source_build(self) -> Build: + def get_source_build(self) -> Build | None: """Return the target source location for the BuildLine queryset.""" return None diff --git a/src/backend/InvenTree/build/models.py b/src/backend/InvenTree/build/models.py index 9890c1dd22..33f54db113 100644 --- a/src/backend/InvenTree/build/models.py +++ b/src/backend/InvenTree/build/models.py @@ -1467,25 +1467,23 @@ class BuildItem(InvenTree.models.InvenTreeMetadataModel): valid = self.bom_item.is_stock_item_valid(self.stock_item) # If the existing BomItem is *not* valid, try to find a match - if not valid: + if not valid and self.build and self.stock_item: + ancestors = self.stock_item.part.get_ancestors(include_self=True, ascending=True) - if self.build and self.stock_item: - ancestors = self.stock_item.part.get_ancestors(include_self=True, ascending=True) + for idx, ancestor in enumerate(ancestors): - for idx, ancestor in enumerate(ancestors): + build_line = BuildLine.objects.filter( + build=self.build, + bom_item__part=ancestor, + ) - build_line = BuildLine.objects.filter( - build=self.build, - bom_item__part=ancestor, - ) + if build_line.exists(): + line = build_line.first() - if build_line.exists(): - line = build_line.first() - - if idx == 0 or line.bom_item.allow_variants: - valid = True - self.build_line = line - break + if idx == 0 or line.bom_item.allow_variants: + valid = True + self.build_line = line + break # BomItem did not exist or could not be validated. # Search for a new one diff --git a/src/backend/InvenTree/build/test_api.py b/src/backend/InvenTree/build/test_api.py index 6f7ad83e14..131dabaf3f 100644 --- a/src/backend/InvenTree/build/test_api.py +++ b/src/backend/InvenTree/build/test_api.py @@ -374,7 +374,7 @@ class BuildTest(BuildAPITest): self.assertEqual(n_outputs, bo.output_count) # Now, create with *good* data - response = self.post( + self.post( create_url, { 'quantity': 5, @@ -444,7 +444,7 @@ class BuildTest(BuildAPITest): self.assertEqual(1, bo.complete_count) # Let's delete 2 build outputs - response = self.post( + self.post( delete_url, { 'outputs': [ @@ -479,7 +479,7 @@ class BuildTest(BuildAPITest): output.refresh_from_db() self.assertTrue(output.is_building) - response = self.post( + self.post( complete_url, { 'outputs': [ @@ -837,7 +837,7 @@ class BuildAllocationTest(BuildAPITest): si.quantity = 100 si.save() - response = self.post( + self.post( self.url, { "items": [ @@ -860,7 +860,7 @@ class BuildAllocationTest(BuildAPITest): lft=0, rght=0 ) - response = self.post( + self.post( self.url, { "items": [ diff --git a/src/backend/InvenTree/common/models.py b/src/backend/InvenTree/common/models.py index 405427c16d..514a573a38 100644 --- a/src/backend/InvenTree/common/models.py +++ b/src/backend/InvenTree/common/models.py @@ -254,7 +254,6 @@ class BaseInvenTreeSetting(models.Model): logger.exception( 'Failed to build default values for %s (%s)', str(cls), str(type(exc)) ) - pass try: cache.set(cache_key, True, timeout=3600) @@ -753,13 +752,11 @@ class BaseInvenTreeSetting(models.Model): except (OperationalError, ProgrammingError): logger.warning("Database is locked, cannot set setting '%s'", key) # Likely the DB is locked - not much we can do here - pass except Exception as exc: # Some other error logger.exception( "Error setting setting '%s' for %s: %s", key, str(cls), str(type(exc)) ) - pass key = models.CharField( max_length=50, @@ -2924,7 +2921,7 @@ class NotificationEntry(MetaMixin): @classmethod def notify(cls, key: str, uid: int): """Notify the database that a particular notification has been sent out.""" - entry, created = cls.objects.get_or_create(key=key, uid=uid) + entry, _ = cls.objects.get_or_create(key=key, uid=uid) entry.save() diff --git a/src/backend/InvenTree/generic/states/__init__.py b/src/backend/InvenTree/generic/states/__init__.py index 8d6f1e870e..8d5fdfed71 100644 --- a/src/backend/InvenTree/generic/states/__init__.py +++ b/src/backend/InvenTree/generic/states/__init__.py @@ -9,4 +9,4 @@ States can be extended with custom options for each InvenTree instance - those o from .states import StatusCode from .transition import StateTransitionMixin, TransitionMethod, storage -__all__ = [StatusCode, storage, TransitionMethod, StateTransitionMixin] +__all__ = ['StatusCode', 'storage', 'TransitionMethod', 'StateTransitionMixin'] diff --git a/src/backend/InvenTree/label/test_api.py b/src/backend/InvenTree/label/test_api.py index cbfaf0b39b..2f64aac34f 100644 --- a/src/backend/InvenTree/label/test_api.py +++ b/src/backend/InvenTree/label/test_api.py @@ -188,7 +188,7 @@ class LabelTest(InvenTreeAPITestCase): self.assertGreaterEqual(n, 1) # Delete the last report - response = self.delete( + self.delete( reverse(self.detail_url, kwargs={'pk': labels[n - 1].pk}), expected_code=204 ) @@ -208,10 +208,10 @@ class LabelTest(InvenTreeAPITestCase): url = reverse(self.print_url, kwargs={'pk': labels[0].pk}) # Try to print without providing a valid item - response = self.get(url, expected_code=400) + self.get(url, expected_code=400) # Try to print with an invalid item - response = self.get(url, {self.print_itemname: 9999}, expected_code=400) + self.get(url, {self.print_itemname: 9999}, expected_code=400) # Now print with a valid item print(f'{self.print_itemmodel = }') diff --git a/src/backend/InvenTree/machine/machine_type.py b/src/backend/InvenTree/machine/machine_type.py index 7be615a66e..35510e7209 100644 --- a/src/backend/InvenTree/machine/machine_type.py +++ b/src/backend/InvenTree/machine/machine_type.py @@ -345,7 +345,7 @@ class BaseMachineType(ClassValidationMixin, ClassProviderMixin): missing_settings: dict[MachineSetting.ConfigType, list[str]] = {} for settings, config_type in self.setting_types: - is_valid, missing = MachineSetting.check_all_settings( + _nbr, missing = MachineSetting.check_all_settings( settings_definition=settings, machine_config=self.machine_config, config_type=config_type, diff --git a/src/backend/InvenTree/order/models.py b/src/backend/InvenTree/order/models.py index 4dd6584f7c..f33fcd53ab 100644 --- a/src/backend/InvenTree/order/models.py +++ b/src/backend/InvenTree/order/models.py @@ -151,7 +151,7 @@ class TotalPriceMixin(models.Model): total += line.quantity * convert_money(line.price, target_currency) except MissingRate: # Record the error, try to press on - kind, info, data = sys.exc_info() + _1, _2, _3 = sys.exc_info() log_error('order.calculate_total_price') logger.exception("Missing exchange rate for '%s'", target_currency) diff --git a/src/backend/InvenTree/part/models.py b/src/backend/InvenTree/part/models.py index 9c4e92d3d5..b0e020efa3 100644 --- a/src/backend/InvenTree/part/models.py +++ b/src/backend/InvenTree/part/models.py @@ -3845,16 +3845,19 @@ class PartCategoryParameterTemplate(InvenTree.models.InvenTreeMetadataModel): '' if self.default_value is None else str(self.default_value.strip()) ) - if self.default_value and InvenTreeSetting.get_setting( - 'PART_PARAMETER_ENFORCE_UNITS', True, cache=False, create=False + if ( + self.default_value + and InvenTreeSetting.get_setting( + 'PART_PARAMETER_ENFORCE_UNITS', True, cache=False, create=False + ) + and self.parameter_template.units ): - if self.parameter_template.units: - try: - InvenTree.conversion.convert_physical_value( - self.default_value, self.parameter_template.units - ) - except ValidationError as e: - raise ValidationError({'default_value': e.message}) + try: + InvenTree.conversion.convert_physical_value( + self.default_value, self.parameter_template.units + ) + except ValidationError as e: + raise ValidationError({'default_value': e.message}) category = models.ForeignKey( PartCategory, diff --git a/src/backend/InvenTree/plugin/base/barcodes/mixins.py b/src/backend/InvenTree/plugin/base/barcodes/mixins.py index 58f91942f0..929a037115 100644 --- a/src/backend/InvenTree/plugin/base/barcodes/mixins.py +++ b/src/backend/InvenTree/plugin/base/barcodes/mixins.py @@ -202,9 +202,8 @@ class SupplierBarcodeMixin(BarcodeMixin): purchase_order = matching_orders.first() - if supplier and purchase_order: - if purchase_order.supplier != supplier: - return {'error': _('Purchase order does not match supplier')} + if supplier and purchase_order and purchase_order.supplier != supplier: + return {'error': _('Purchase order does not match supplier')} return self.receive_purchase_order_item( supplier_part, @@ -329,7 +328,7 @@ class SupplierBarcodeMixin(BarcodeMixin): # Check that the barcode starts with the necessary header if not barcode_data.startswith(HEADER): - return + return [] return SupplierBarcodeMixin.split_fields( barcode_data, delimiter=DELIMITER, header=HEADER, trailer=TRAILER diff --git a/src/backend/InvenTree/plugin/base/event/events.py b/src/backend/InvenTree/plugin/base/event/events.py index 752a7a8d85..eaaa1bfd96 100644 --- a/src/backend/InvenTree/plugin/base/event/events.py +++ b/src/backend/InvenTree/plugin/base/event/events.py @@ -104,7 +104,7 @@ def process_event(plugin_slug, event, *args, **kwargs): # Log the exception to the database InvenTree.exceptions.log_error(f'plugins.{plugin_slug}.process_event') # Re-throw the exception so that the background worker tries again - raise Exception + raise e def allow_table_event(table_name): diff --git a/src/backend/InvenTree/plugin/base/integration/ScheduleMixin.py b/src/backend/InvenTree/plugin/base/integration/ScheduleMixin.py index 6a60197d75..a6cad702f7 100644 --- a/src/backend/InvenTree/plugin/base/integration/ScheduleMixin.py +++ b/src/backend/InvenTree/plugin/base/integration/ScheduleMixin.py @@ -69,11 +69,10 @@ class ScheduleMixin: 'ENABLE_PLUGINS_SCHEDULE' ): for _key, plugin in plugins: - if plugin.mixin_enabled('schedule'): - if plugin.is_active(): - # Only active tasks for plugins which are enabled - plugin.register_tasks() - task_keys += plugin.get_task_names() + if plugin.mixin_enabled('schedule') and plugin.is_active(): + # Only active tasks for plugins which are enabled + plugin.register_tasks() + task_keys += plugin.get_task_names() if len(task_keys) > 0: logger.info('Activated %s scheduled tasks', len(task_keys)) diff --git a/src/backend/InvenTree/plugin/base/label/mixins.py b/src/backend/InvenTree/plugin/base/label/mixins.py index c4bffc9801..736d431d00 100644 --- a/src/backend/InvenTree/plugin/base/label/mixins.py +++ b/src/backend/InvenTree/plugin/base/label/mixins.py @@ -58,7 +58,7 @@ class LabelPrintingMixin: """ try: return label.render(request) - except Exception as e: + except Exception: log_error('label.render_to_pdf') raise ValidationError(_('Error rendering label to PDF')) @@ -71,7 +71,7 @@ class LabelPrintingMixin: """ try: return label.render_as_string(request) - except Exception as e: + except Exception: log_error('label.render_to_html') raise ValidationError(_('Error rendering label to HTML')) @@ -106,7 +106,7 @@ class LabelPrintingMixin: # Convert to png data try: return pdf2image.convert_from_bytes(pdf_data, **pdf2image_kwargs)[0] - except Exception as e: + except Exception: log_error('label.render_to_png') raise ValidationError(_('Error rendering label to PNG')) diff --git a/src/backend/InvenTree/plugin/base/locate/mixins.py b/src/backend/InvenTree/plugin/base/locate/mixins.py index 005e9a8fcd..3d2077d566 100644 --- a/src/backend/InvenTree/plugin/base/locate/mixins.py +++ b/src/backend/InvenTree/plugin/base/locate/mixins.py @@ -58,7 +58,6 @@ class LocateMixin: except StockItem.DoesNotExist: # pragma: no cover logger.warning('LocateMixin: StockItem pk={item_pk} not found') - pass def locate_stock_location(self, location_pk): """Attempt to location a particular StockLocation. diff --git a/src/backend/InvenTree/plugin/builtin/integration/currency_exchange.py b/src/backend/InvenTree/plugin/builtin/integration/currency_exchange.py index 0efc73ba01..09ae98f14d 100644 --- a/src/backend/InvenTree/plugin/builtin/integration/currency_exchange.py +++ b/src/backend/InvenTree/plugin/builtin/integration/currency_exchange.py @@ -41,7 +41,7 @@ class InvenTreeCurrencyExchange(APICallMixin, CurrencyExchangeMixin, InvenTreePl self.api_url, response.status_code, ) - return None + return {} @property def api_url(self): diff --git a/src/backend/InvenTree/plugin/helpers.py b/src/backend/InvenTree/plugin/helpers.py index f9c1321d8d..bd287bcdd8 100644 --- a/src/backend/InvenTree/plugin/helpers.py +++ b/src/backend/InvenTree/plugin/helpers.py @@ -145,7 +145,7 @@ def get_git_log(path): datetime.datetime.fromtimestamp(commit.author_time).isoformat(), commit.message.decode().split('\n')[0], ] - except KeyError as err: + except KeyError: logger.debug('No HEAD tag found in git repo at path %s', path) except NotGitRepository: pass diff --git a/src/backend/InvenTree/plugin/models.py b/src/backend/InvenTree/plugin/models.py index b77108e2e5..482b0677c1 100644 --- a/src/backend/InvenTree/plugin/models.py +++ b/src/backend/InvenTree/plugin/models.py @@ -139,11 +139,10 @@ class PluginConfig(InvenTree.models.MetadataMixin, models.Model): # Force active if builtin self.active = True - if not reload: - if self.active != self.__org_active: - if settings.PLUGIN_TESTING: - warnings.warn('A reload was triggered', stacklevel=2) - registry.reload_plugins() + if not reload and self.active != self.__org_active: + if settings.PLUGIN_TESTING: + warnings.warn('A reload was triggered', stacklevel=2) + registry.reload_plugins() @admin.display(boolean=True, description=_('Installed')) def is_installed(self) -> bool: diff --git a/src/backend/InvenTree/plugin/registry.py b/src/backend/InvenTree/plugin/registry.py index ee68b0d04f..d56ce885ed 100644 --- a/src/backend/InvenTree/plugin/registry.py +++ b/src/backend/InvenTree/plugin/registry.py @@ -95,9 +95,8 @@ class PluginsRegistry: plg = self.plugins[slug] - if active is not None: - if active != plg.is_active(): - return None + if active is not None and active != plg.is_active(): + return None return plg @@ -130,7 +129,7 @@ class PluginsRegistry: try: cfg.name = name cfg.save() - except Exception as e: + except Exception: logger.exception('Failed to update plugin name') return cfg diff --git a/src/backend/InvenTree/plugin/samples/integration/validation_sample.py b/src/backend/InvenTree/plugin/samples/integration/validation_sample.py index 0f03f464bd..c1edd4e980 100644 --- a/src/backend/InvenTree/plugin/samples/integration/validation_sample.py +++ b/src/backend/InvenTree/plugin/samples/integration/validation_sample.py @@ -142,9 +142,8 @@ class SampleValidatorPlugin(SettingsMixin, ValidationMixin, InvenTreePlugin): """ prefix = self.get_setting('BATCH_CODE_PREFIX') - if len(batch_code) > 0: - if prefix and not batch_code.startswith(prefix): - self.raise_error(f"Batch code must start with '{prefix}'") + if len(batch_code) > 0 and prefix and not batch_code.startswith(prefix): + self.raise_error(f"Batch code must start with '{prefix}'") def generate_batch_code(self): """Generate a new batch code.""" diff --git a/src/backend/InvenTree/stock/models.py b/src/backend/InvenTree/stock/models.py index 3e7e95504b..2d5cfaeaff 100644 --- a/src/backend/InvenTree/stock/models.py +++ b/src/backend/InvenTree/stock/models.py @@ -642,13 +642,10 @@ class StockItem( try: # Trackable parts must have integer values for quantity field! - if self.part.trackable: - if self.quantity != int(self.quantity): - raise ValidationError({ - 'quantity': _( - 'Quantity must be integer value for trackable parts' - ) - }) + if self.part.trackable and self.quantity != int(self.quantity): + raise ValidationError({ + 'quantity': _('Quantity must be integer value for trackable parts') + }) # Virtual parts cannot have stock items created against them if self.part.virtual: @@ -2396,17 +2393,15 @@ class StockItemTestResult(InvenTree.models.InvenTreeMetadataModel): for template in templates: if key == template.key: - if template.requires_value: - if not self.value: - raise ValidationError({ - 'value': _('Value must be provided for this test') - }) + if template.requires_value and not self.value: + raise ValidationError({ + 'value': _('Value must be provided for this test') + }) - if template.requires_attachment: - if not self.attachment: - raise ValidationError({ - 'attachment': _('Attachment must be uploaded for this test') - }) + if template.requires_attachment and not self.attachment: + raise ValidationError({ + 'attachment': _('Attachment must be uploaded for this test') + }) break diff --git a/src/frontend/package.json b/src/frontend/package.json index 428a9b2e11..ce123ad0dc 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -1,5 +1,5 @@ { - "name": "InvenTreeUI", + "name": "inventreeui", "private": true, "version": "0.1.0", "type": "module", diff --git a/src/frontend/src/components/nav/Layout.tsx b/src/frontend/src/components/nav/Layout.tsx index d2fe67864c..ae4b88df00 100644 --- a/src/frontend/src/components/nav/Layout.tsx +++ b/src/frontend/src/components/nav/Layout.tsx @@ -1,5 +1,5 @@ import { Container, Flex, Space } from '@mantine/core'; -import { Navigate, Outlet, useLocation, useNavigate } from 'react-router-dom'; +import { Navigate, Outlet, useLocation } from 'react-router-dom'; import { InvenTreeStyle } from '../../globalStyle'; import { useSessionState } from '../../states/SessionState';