remove unneeded branches

fixes SIM102
This commit is contained in:
Matthias Mair 2024-08-20 01:17:35 +02:00
parent 372e3d417e
commit f74d41bc07
No known key found for this signature in database
GPG Key ID: A593429DDA23B66A
24 changed files with 289 additions and 258 deletions

View File

@ -115,9 +115,8 @@ def exception_handler(exc, context):
log_error(context['request'].path) log_error(context['request'].path)
if response is not None:
# Convert errors returned under the label '__all__' to 'non_field_errors' # Convert errors returned under the label '__all__' to 'non_field_errors'
if '__all__' in response.data: if response is not None and '__all__' in response.data:
response.data['non_field_errors'] = response.data['__all__'] response.data['non_field_errors'] = response.data['__all__']
del response.data['__all__'] del response.data['__all__']

View File

@ -184,8 +184,11 @@ def getSplashScreen(custom=True):
"""Return the InvenTree splash screen, or a custom splash if available.""" """Return the InvenTree splash screen, or a custom splash if available."""
static_storage = StaticFilesStorage() static_storage = StaticFilesStorage()
if custom and settings.CUSTOM_SPLASH: if (
if static_storage.exists(settings.CUSTOM_SPLASH): custom
and settings.CUSTOM_SPLASH
and static_storage.exists(settings.CUSTOM_SPLASH)
):
return static_storage.url(settings.CUSTOM_SPLASH) return static_storage.url(settings.CUSTOM_SPLASH)
# No custom splash screen # No custom splash screen

View File

@ -34,8 +34,7 @@ class Command(BaseCommand):
if x and x.path: if x and x.path:
img_paths.append(x.path) img_paths.append(x.path)
if len(img_paths) > 0: if len(img_paths) > 0 and all(os.path.exists(path) for path in img_paths):
if all(os.path.exists(path) for path in img_paths):
# All images exist - skip further work # All images exist - skip further work
return return

View File

@ -28,8 +28,10 @@ def get_token_from_request(request):
if auth_header := request.headers.get(k, None): if auth_header := request.headers.get(k, None):
auth_header = auth_header.strip().lower().split() auth_header = auth_header.strip().lower().split()
if len(auth_header) > 1: if (
if auth_header[0].strip().lower().replace(':', '') in token_keys: len(auth_header) > 1
and auth_header[0].strip().lower().replace(':', '') in token_keys
):
token = auth_header[1] token = auth_header[1]
return token return token

View File

@ -63,8 +63,11 @@ def init_sentry(dsn, sample_rate, tags):
def report_exception(exc): def report_exception(exc):
"""Report an exception to sentry.io.""" """Report an exception to sentry.io."""
if settings.SENTRY_ENABLED and settings.SENTRY_DSN: if (
if not any(isinstance(exc, e) for e in sentry_ignore_errors()): settings.SENTRY_ENABLED
and settings.SENTRY_DSN
and not any(isinstance(exc, e) for e in sentry_ignore_errors())
):
logger.info('Reporting exception to sentry.io: %s', exc) logger.info('Reporting exception to sentry.io: %s', exc)
try: try:

View File

@ -50,9 +50,8 @@ def check_provider(provider):
if not app: if not app:
return False return False
if allauth.app_settings.SITES_ENABLED:
# At least one matching site must be specified # At least one matching site must be specified
if not app.sites.exists(): if allauth.app_settings.SITES_ENABLED and not app.sites.exists():
logger.error('SocialApp %s has no sites configured', app) logger.error('SocialApp %s has no sites configured', app)
return False return False

View File

@ -136,12 +136,11 @@ def inventree_in_debug_mode(*args, **kwargs):
@register.simple_tag() @register.simple_tag()
def inventree_show_about(user, *args, **kwargs): def inventree_show_about(user, *args, **kwargs):
"""Return True if the about modal should be shown.""" """Return True if the about modal should be shown."""
if get_global_setting('INVENTREE_RESTRICT_ABOUT'):
# Return False if the user is not a superuser, or no user information is provided # Return False if the user is not a superuser, or no user information is provided
if not user or not user.is_superuser: return not (
return False (get_global_setting('INVENTREE_RESTRICT_ABOUT') and not user)
or not user.is_superuser
return True )
@register.simple_tag() @register.simple_tag()

View File

@ -69,9 +69,8 @@ class AllowedURLValidator(validators.URLValidator):
# Determine if 'strict' URL validation is required (i.e. if the URL must have a schema prefix) # Determine if 'strict' URL validation is required (i.e. if the URL must have a schema prefix)
strict_urls = get_global_setting('INVENTREE_STRICT_URLS', cache=False) strict_urls = get_global_setting('INVENTREE_STRICT_URLS', cache=False)
if not strict_urls:
# Allow URLs which do not have a provided schema # Allow URLs which do not have a provided schema
if '://' not in value: if not strict_urls and '://' not in value:
# Validate as if it were http # Validate as if it were http
value = 'http://' + value value = 'http://' + value

View File

@ -426,18 +426,19 @@ class SetPasswordView(AjaxUpdateView):
old_password = request.POST.get('old_password', '') old_password = request.POST.get('old_password', '')
user = self.request.user user = self.request.user
if valid:
# Passwords must match # Passwords must match
if valid and p1 != p2:
if p1 != p2:
error = _('Password fields must match') error = _('Password fields must match')
form.add_error('enter_password', error) form.add_error('enter_password', error)
form.add_error('confirm_password', error) form.add_error('confirm_password', error)
valid = False valid = False
if valid:
# Old password must be correct # Old password must be correct
if user.has_usable_password() and not user.check_password(old_password): if (
valid
and user.has_usable_password()
and not user.check_password(old_password)
):
form.add_error('old_password', _('Wrong password provided')) form.add_error('old_password', _('Wrong password provided'))
valid = False valid = False

View File

@ -444,8 +444,7 @@ class SettingsTest(InvenTreeTestCase):
# Any fields marked as 'boolean' must have a default value specified # Any fields marked as 'boolean' must have a default value specified
setting = InvenTreeSetting.get_setting_object(key) setting = InvenTreeSetting.get_setting_object(key)
if setting.is_bool(): if setting.is_bool() and setting.default_value not in [True, False]:
if setting.default_value not in [True, False]:
raise ValueError( raise ValueError(
f'Non-boolean default value specified for {key}' f'Non-boolean default value specified for {key}'
) # pragma: no cover ) # pragma: no cover

View File

@ -730,8 +730,11 @@ class SupplierPart(
raise ValidationError({'pack_quantity': e.messages}) raise ValidationError({'pack_quantity': e.messages})
# Ensure that the linked manufacturer_part points to the same part! # Ensure that the linked manufacturer_part points to the same part!
if self.manufacturer_part and self.part: if (
if self.manufacturer_part.part != self.part: self.manufacturer_part
and self.part
and self.manufacturer_part.part != self.part
):
raise ValidationError({ raise ValidationError({
'manufacturer_part': _( 'manufacturer_part': _(
'Linked manufacturer part must reference the same base part' 'Linked manufacturer part must reference the same base part'
@ -1049,8 +1052,12 @@ class SupplierPriceBreak(common.models.PriceBreak):
) )
def after_save_supplier_price(sender, instance, created, **kwargs): def after_save_supplier_price(sender, instance, created, **kwargs):
"""Callback function when a SupplierPriceBreak is created or updated.""" """Callback function when a SupplierPriceBreak is created or updated."""
if InvenTree.ready.canAppAccessDatabase() and not InvenTree.ready.isImportingData(): if (
if instance.part and instance.part.part: InvenTree.ready.canAppAccessDatabase()
and not InvenTree.ready.isImportingData()
and instance.part
and instance.part.part
):
instance.part.part.schedule_pricing_update(create=True) instance.part.part.schedule_pricing_update(create=True)
@ -1061,6 +1068,10 @@ def after_save_supplier_price(sender, instance, created, **kwargs):
) )
def after_delete_supplier_price(sender, instance, **kwargs): def after_delete_supplier_price(sender, instance, **kwargs):
"""Callback function when a SupplierPriceBreak is deleted.""" """Callback function when a SupplierPriceBreak is deleted."""
if InvenTree.ready.canAppAccessDatabase() and not InvenTree.ready.isImportingData(): if (
if instance.part and instance.part.part: InvenTree.ready.canAppAccessDatabase()
and not InvenTree.ready.isImportingData()
and instance.part
and instance.part.part
):
instance.part.part.schedule_pricing_update(create=False) instance.part.part.schedule_pricing_update(create=False)

View File

@ -1350,10 +1350,8 @@ class OrderCalendarExport(ICalFeed):
# No login yet - check in headers # No login yet - check in headers
if 'authorization' in request.headers: if 'authorization' in request.headers:
auth = request.headers['authorization'].split() auth = request.headers['authorization'].split()
if len(auth) == 2:
# NOTE: We are only support basic authentication for now. # NOTE: We are only support basic authentication for now.
# if len(auth) == 2 and auth[0].lower() == 'basic':
if auth[0].lower() == 'basic':
uname, passwd = base64.b64decode(auth[1]).decode('ascii').split(':') uname, passwd = base64.b64decode(auth[1]).decode('ascii').split(':')
user = authenticate(username=uname, password=passwd) user = authenticate(username=uname, password=passwd)
if user is not None and user.is_active: if user is not None and user.is_active:

View File

@ -232,16 +232,17 @@ class Order(
super().clean() super().clean()
# Check if a responsible owner is required for this order type # Check if a responsible owner is required for this order type
if self.REQUIRE_RESPONSIBLE_SETTING: if (
if get_global_setting(self.REQUIRE_RESPONSIBLE_SETTING, backup_value=False): self.REQUIRE_RESPONSIBLE_SETTING
if not self.responsible: and get_global_setting(self.REQUIRE_RESPONSIBLE_SETTING, backup_value=False)
and not self.responsible
):
raise ValidationError({ raise ValidationError({
'responsible': _('Responsible user or group must be specified') 'responsible': _('Responsible user or group must be specified')
}) })
# Check that the referenced 'contact' matches the correct 'company' # Check that the referenced 'contact' matches the correct 'company'
if self.company and self.contact: if self.company and self.contact and self.contact.company != self.company:
if self.contact.company != self.company:
raise ValidationError({ raise ValidationError({
'contact': _('Contact does not match selected company') 'contact': _('Contact does not match selected company')
}) })
@ -783,9 +784,8 @@ class PurchaseOrder(TotalPriceMixin, Order):
# Extract optional packaging field # Extract optional packaging field
packaging = kwargs.get('packaging', None) packaging = kwargs.get('packaging', None)
if not packaging:
# Default to the packaging field for the linked supplier part # Default to the packaging field for the linked supplier part
if line.part: if not packaging and line.part:
packaging = line.part.packaging packaging = line.part.packaging
# Extract optional barcode field # Extract optional barcode field
@ -866,8 +866,9 @@ class PurchaseOrder(TotalPriceMixin, Order):
line.save() line.save()
# Has this order been completed? # Has this order been completed?
if len(self.pending_line_items()) == 0: if len(self.pending_line_items()) == 0 and get_global_setting(
if get_global_setting('PURCHASEORDER_AUTO_COMPLETE', True): 'PURCHASEORDER_AUTO_COMPLETE', True
):
self.received_by = user self.received_by = user
self.complete_order() # This will save the model self.complete_order() # This will save the model
@ -1455,9 +1456,12 @@ class PurchaseOrderLineItem(OrderLineItem):
""" """
super().clean() super().clean()
if self.order.supplier and self.part:
# Supplier part *must* point to the same supplier! # Supplier part *must* point to the same supplier!
if self.part.supplier != self.order.supplier: if (
self.order.supplier
and self.part
and self.part.supplier != self.order.supplier
):
raise ValidationError({'part': _('Supplier part must match supplier')}) raise ValidationError({'part': _('Supplier part must match supplier')})
def __str__(self): def __str__(self):

View File

@ -91,8 +91,9 @@ def ExportBom(
bom_items.append(item) bom_items.append(item)
if cascade and item.sub_part.assembly: if (
if max_levels is None or level < max_levels: cascade and item.sub_part.assembly and max_levels is None
) or level < max_levels:
add_items(item.sub_part.bom_items.all().order_by('id'), level + 1) add_items(item.sub_part.bom_items.all().order_by('id'), level + 1)
top_level_items = part.get_bom_items().order_by('id') top_level_items = part.get_bom_items().order_by('id')

View File

@ -515,8 +515,10 @@ class Part(
if self.active: if self.active:
raise ValidationError(_('Cannot delete this part as it is still active')) raise ValidationError(_('Cannot delete this part as it is still active'))
if not get_global_setting('PART_ALLOW_DELETE_FROM_ASSEMBLY', cache=False): if (
if BomItem.objects.filter(sub_part=self).exists(): not get_global_setting('PART_ALLOW_DELETE_FROM_ASSEMBLY', cache=False)
and BomItem.objects.filter(sub_part=self).exists()
):
raise ValidationError( raise ValidationError(
_('Cannot delete this part as it is used in an assembly') _('Cannot delete this part as it is used in an assembly')
) )
@ -711,12 +713,11 @@ class Part(
'revision': _('Revision code must be specified') 'revision': _('Revision code must be specified')
}) })
if get_global_setting('PART_REVISION_ASSEMBLY_ONLY'): if (
if not self.assembly or not self.revision_of.assembly: get_global_setting('PART_REVISION_ASSEMBLY_ONLY') and not self.assembly
) or not self.revision_of.assembly:
raise ValidationError({ raise ValidationError({
'revision_of': _( 'revision_of': _('Revisions are only allowed for assembly parts')
'Revisions are only allowed for assembly parts'
)
}) })
# Cannot have a revision of a "template" part # Cannot have a revision of a "template" part
@ -2753,12 +2754,14 @@ class PartPricing(common.models.MetaMixin):
sub_part_min = self.convert(sub_part_pricing.overall_min) sub_part_min = self.convert(sub_part_pricing.overall_min)
sub_part_max = self.convert(sub_part_pricing.overall_max) sub_part_max = self.convert(sub_part_pricing.overall_max)
if sub_part_min is not None: if (
if bom_item_min is None or sub_part_min < bom_item_min: sub_part_min is not None and bom_item_min is None
) or sub_part_min < bom_item_min:
bom_item_min = sub_part_min bom_item_min = sub_part_min
if sub_part_max is not None: if (
if bom_item_max is None or sub_part_max > bom_item_max: sub_part_max is not None and bom_item_max is None
) or sub_part_max > bom_item_max:
bom_item_max = sub_part_max bom_item_max = sub_part_max
# Update cumulative totals # Update cumulative totals
@ -2962,12 +2965,10 @@ class PartPricing(common.models.MetaMixin):
v_min = self.convert(v.pricing.overall_min) v_min = self.convert(v.pricing.overall_min)
v_max = self.convert(v.pricing.overall_max) v_max = self.convert(v.pricing.overall_max)
if v_min is not None: if (v_min is not None and variant_min is None) or v_min < variant_min:
if variant_min is None or v_min < variant_min:
variant_min = v_min variant_min = v_min
if v_max is not None: if (v_max is not None and variant_max is None) or v_max > variant_max:
if variant_max is None or v_max > variant_max:
variant_max = v_max variant_max = v_max
if self.variant_cost_min != variant_min or self.variant_cost_max != variant_max: if self.variant_cost_min != variant_min or self.variant_cost_max != variant_max:
@ -3800,8 +3801,11 @@ def post_save_part_parameter_template(sender, instance, created, **kwargs):
"""Callback function when a PartParameterTemplate is created or saved.""" """Callback function when a PartParameterTemplate is created or saved."""
import part.tasks as part_tasks import part.tasks as part_tasks
if InvenTree.ready.canAppAccessDatabase() and not InvenTree.ready.isImportingData(): if (
if not created: InvenTree.ready.canAppAccessDatabase()
and not InvenTree.ready.isImportingData()
and not created
):
# Schedule a background task to rebuild the parameters against this template # Schedule a background task to rebuild the parameters against this template
InvenTree.tasks.offload_task( InvenTree.tasks.offload_task(
part_tasks.rebuild_parameters, instance.pk, force_async=True part_tasks.rebuild_parameters, instance.pk, force_async=True
@ -3922,10 +3926,13 @@ class PartParameter(InvenTree.models.InvenTreeMetadataModel):
except ValueError: except ValueError:
self.data_numeric = None self.data_numeric = None
if self.data_numeric is not None and type(self.data_numeric) is float:
# Prevent out of range numbers, etc # Prevent out of range numbers, etc
# Ref: https://github.com/inventree/InvenTree/issues/7593 # Ref: https://github.com/inventree/InvenTree/issues/7593
if math.isnan(self.data_numeric) or math.isinf(self.data_numeric): if (
self.data_numeric is not None
and type(self.data_numeric) is float
and math.isnan(self.data_numeric)
) or math.isinf(self.data_numeric):
self.data_numeric = None self.data_numeric = None
part = models.ForeignKey( part = models.ForeignKey(
@ -4509,8 +4516,11 @@ def update_bom_build_lines(sender, instance, created, **kwargs):
def update_pricing_after_edit(sender, instance, created, **kwargs): def update_pricing_after_edit(sender, instance, created, **kwargs):
"""Callback function when a part price break is created or updated.""" """Callback function when a part price break is created or updated."""
# Update part pricing *unless* we are importing data # Update part pricing *unless* we are importing data
if InvenTree.ready.canAppAccessDatabase() and not InvenTree.ready.isImportingData(): if (
if instance.part: InvenTree.ready.canAppAccessDatabase()
and not InvenTree.ready.isImportingData()
and instance.part
):
instance.part.schedule_pricing_update(create=True) instance.part.schedule_pricing_update(create=True)
@ -4526,8 +4536,11 @@ def update_pricing_after_edit(sender, instance, created, **kwargs):
def update_pricing_after_delete(sender, instance, **kwargs): def update_pricing_after_delete(sender, instance, **kwargs):
"""Callback function when a part price break is deleted.""" """Callback function when a part price break is deleted."""
# Update part pricing *unless* we are importing data # Update part pricing *unless* we are importing data
if InvenTree.ready.canAppAccessDatabase() and not InvenTree.ready.isImportingData(): if (
if instance.part: InvenTree.ready.canAppAccessDatabase()
and not InvenTree.ready.isImportingData()
and instance.part
):
instance.part.schedule_pricing_update(create=False) instance.part.schedule_pricing_update(create=False)

View File

@ -563,8 +563,11 @@ class BarcodeSOAllocate(BarcodeView):
sales_order = kwargs['sales_order'] sales_order = kwargs['sales_order']
shipment = self.get_shipment(**kwargs) shipment = self.get_shipment(**kwargs)
if stock_item is not None and line_item is not None: if (
if stock_item.part != line_item.part: stock_item is not None
and line_item is not None
and stock_item.part != line_item.part
):
result['error'] = _('Stock item does not match line item') result['error'] = _('Stock item does not match line item')
raise ValidationError(result) raise ValidationError(result)
@ -587,8 +590,11 @@ class BarcodeSOAllocate(BarcodeView):
'quantity': quantity, 'quantity': quantity,
} }
if stock_item is not None and quantity is not None: if (
if stock_item.unallocated_quantity() < quantity: stock_item is not None
and quantity is not None
and stock_item.unallocated_quantity() < quantity
):
response['error'] = _('Insufficient stock available') response['error'] = _('Insufficient stock available')
raise ValidationError(response) raise ValidationError(response)

View File

@ -477,8 +477,9 @@ class SupplierBarcodeMixin(BarcodeMixin):
location := supplier_part.part.get_default_location() location := supplier_part.part.get_default_location()
): ):
pass pass
elif StockLocation.objects.count() <= 1: elif StockLocation.objects.count() <= 1 and not (
if not (location := StockLocation.objects.first()): location := StockLocation.objects.first()
):
no_stock_locations = True no_stock_locations = True
response = { response = {

View File

@ -195,14 +195,12 @@ class PluginsRegistry:
for plugin in self.plugins.values(): for plugin in self.plugins.values():
if plugin.mixin_enabled(mixin): if plugin.mixin_enabled(mixin):
if active is not None:
# Filter by 'active' status of plugin # Filter by 'active' status of plugin
if active != plugin.is_active(): if active is not None and active != plugin.is_active():
continue continue
if builtin is not None:
# Filter by 'builtin' status of plugin # Filter by 'builtin' status of plugin
if builtin != plugin.is_builtin: if builtin is not None and builtin != plugin.is_builtin:
continue continue
result.append(plugin) result.append(plugin)
@ -396,9 +394,8 @@ class PluginsRegistry:
collected_plugins.append(item) collected_plugins.append(item)
# From this point any plugins are considered "external" and only loaded if plugins are explicitly enabled # From this point any plugins are considered "external" and only loaded if plugins are explicitly enabled
if settings.PLUGINS_ENABLED:
# Check if not running in testing mode and apps should be loaded from hooks # Check if not running in testing mode and apps should be loaded from hooks
if (not settings.PLUGIN_TESTING) or ( if (settings.PLUGINS_ENABLED and (not settings.PLUGIN_TESTING)) or (
settings.PLUGIN_TESTING and settings.PLUGIN_TESTING_SETUP settings.PLUGIN_TESTING and settings.PLUGIN_TESTING_SETUP
): ):
# Collect plugins from setup entry points # Collect plugins from setup entry points

View File

@ -66,17 +66,19 @@ class SampleValidatorPlugin(SettingsMixin, ValidationMixin, InvenTreePlugin):
# Print debug message to console (intentional) # Print debug message to console (intentional)
print('Validating model instance:', instance.__class__, f'<{instance.pk}>') print('Validating model instance:', instance.__class__, f'<{instance.pk}>')
if isinstance(instance, part.models.BomItem): if (
if self.get_setting('BOM_ITEM_INTEGER'): isinstance(instance, part.models.BomItem)
if float(instance.quantity) != int(instance.quantity): and self.get_setting('BOM_ITEM_INTEGER')
self.raise_error({ and float(instance.quantity) != int(instance.quantity)
'quantity': 'Bom item quantity must be an integer' ):
}) self.raise_error({'quantity': 'Bom item quantity must be an integer'})
if isinstance(instance, part.models.Part):
# If the part description is being updated, prevent it from being reduced in length # If the part description is being updated, prevent it from being reduced in length
if (
if deltas and 'description' in deltas: isinstance(instance, part.models.Part)
and deltas
and 'description' in deltas
):
old_desc = deltas['description']['old'] old_desc = deltas['description']['old']
new_desc = deltas['description']['new'] new_desc = deltas['description']['new']
@ -126,13 +128,11 @@ class SampleValidatorPlugin(SettingsMixin, ValidationMixin, InvenTreePlugin):
These examples are silly, but serve to demonstrate how the feature could be used These examples are silly, but serve to demonstrate how the feature could be used
""" """
if self.get_setting('SERIAL_MUST_BE_PALINDROME'): if self.get_setting('SERIAL_MUST_BE_PALINDROME') and serial != serial[::-1]:
if serial != serial[::-1]:
self.raise_error('Serial must be a palindrome') self.raise_error('Serial must be a palindrome')
if self.get_setting('SERIAL_MUST_MATCH_PART'):
# Serial must start with the same letter as the linked part, for some reason # Serial must start with the same letter as the linked part, for some reason
if serial[0] != part.name[0]: if self.get_setting('SERIAL_MUST_MATCH_PART') and serial[0] != part.name[0]:
self.raise_error('Serial number must start with same letter as part') self.raise_error('Serial number must start with same letter as part')
def validate_batch_code(self, batch_code: str, item): def validate_batch_code(self, batch_code: str, item):

View File

@ -700,17 +700,15 @@ class StockItem(
# The 'supplier_part' field must point to the same part! # The 'supplier_part' field must point to the same part!
try: try:
if self.supplier_part is not None: if self.supplier_part is not None and self.supplier_part.part != self.part:
if self.supplier_part.part != self.part:
raise ValidationError({ raise ValidationError({
'supplier_part': _( 'supplier_part': _(
f"Part type ('{self.supplier_part.part}') must be {self.part}" f"Part type ('{self.supplier_part.part}') must be {self.part}"
) )
}) })
if self.part is not None:
# A part with a serial number MUST have the quantity set to 1 # A part with a serial number MUST have the quantity set to 1
if self.serial: if self.part is not None and self.serial:
if self.quantity > 1: if self.quantity > 1:
raise ValidationError({ raise ValidationError({
'quantity': _( 'quantity': _(

View File

@ -795,14 +795,11 @@ class InstallStockItemSerializer(serializers.Serializer):
parent_item = self.context['item'] parent_item = self.context['item']
parent_part = parent_item.part parent_part = parent_item.part
# Check if the selected part is in the Bill of Materials of the parent item
if get_global_setting( if get_global_setting(
'STOCK_ENFORCE_BOM_INSTALLATION', backup_value=True, cache=False 'STOCK_ENFORCE_BOM_INSTALLATION', backup_value=True, cache=False
): ) and not parent_part.check_if_part_in_bom(stock_item.part):
# Check if the selected part is in the Bill of Materials of the parent item raise ValidationError(_('Selected part is not in the Bill of Materials'))
if not parent_part.check_if_part_in_bom(stock_item.part):
raise ValidationError(
_('Selected part is not in the Bill of Materials')
)
return stock_item return stock_item

View File

@ -95,9 +95,8 @@ class OwnerList(ListAPI):
if not search_match: if not search_match:
continue continue
if is_active is not None:
# Skip any users which do not match the required *is_active* value # Skip any users which do not match the required *is_active* value
if ( if is_active is not None and (
result.owner_type.name == 'user' result.owner_type.name == 'user'
and result.owner_id not in matching_user_ids and result.owner_id not in matching_user_ids
): ):

View File

@ -34,8 +34,9 @@ logger = logging.getLogger('inventree')
# string representation of a user # string representation of a user
def user_model_str(self): def user_model_str(self):
"""Function to override the default Django User __str__.""" """Function to override the default Django User __str__."""
if get_global_setting('DISPLAY_FULL_NAMES', cache=True): if (
if self.first_name or self.last_name: get_global_setting('DISPLAY_FULL_NAMES', cache=True) and self.first_name
) or self.last_name:
return f'{self.first_name} {self.last_name}' return f'{self.first_name} {self.last_name}'
return self.username return self.username
@ -421,8 +422,9 @@ class RuleSet(models.Model):
# Work out which roles touch the given table # Work out which roles touch the given table
for role in cls.RULESET_NAMES: for role in cls.RULESET_NAMES:
if table in cls.get_ruleset_models()[role]: if table in cls.get_ruleset_models()[role] and check_user_role(
if check_user_role(user, role, permission): user, role, permission
):
return True return True
# Check for children models which inherits from parent role # Check for children models which inherits from parent role
@ -430,9 +432,8 @@ class RuleSet(models.Model):
# Get child model name # Get child model name
parent_child_string = f'{parent}_{child}' parent_child_string = f'{parent}_{child}'
if parent_child_string == table:
# Check if parent role has change permission # Check if parent role has change permission
if check_user_role(user, parent, 'change'): if parent_child_string == table and check_user_role(user, parent, 'change'):
return True return True
# Print message instead of throwing an error # Print message instead of throwing an error

View File

@ -628,8 +628,10 @@ def export_records(
model_name = entry.get('model', None) model_name = entry.get('model', None)
# Ignore any temporary settings (start with underscore) # Ignore any temporary settings (start with underscore)
if model_name in ['common.inventreesetting', 'common.inventreeusersetting']: if model_name in [
if entry['fields'].get('key', '').startswith('_'): 'common.inventreesetting',
'common.inventreeusersetting',
] and entry['fields'].get('key', '').startswith('_'):
continue continue
if model_name == 'auth.group': if model_name == 'auth.group':