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

View File

@ -184,9 +184,12 @@ def getSplashScreen(custom=True):
"""Return the InvenTree splash screen, or a custom splash if available."""
static_storage = StaticFilesStorage()
if custom and settings.CUSTOM_SPLASH:
if static_storage.exists(settings.CUSTOM_SPLASH):
return static_storage.url(settings.CUSTOM_SPLASH)
if (
custom
and settings.CUSTOM_SPLASH
and static_storage.exists(settings.CUSTOM_SPLASH)
):
return static_storage.url(settings.CUSTOM_SPLASH)
# No custom splash screen
return static_storage.url('img/inventree_splash.jpg')

View File

@ -34,10 +34,9 @@ class Command(BaseCommand):
if x and x.path:
img_paths.append(x.path)
if len(img_paths) > 0:
if all(os.path.exists(path) for path in img_paths):
# All images exist - skip further work
return
if len(img_paths) > 0 and all(os.path.exists(path) for path in img_paths):
# All images exist - skip further work
return
logger.info("Generating thumbnail image for '%s'", img)

View File

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

View File

@ -63,11 +63,14 @@ 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()):
logger.info('Reporting exception to sentry.io: %s', exc)
if (
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)
try:
sentry_sdk.capture_exception(exc)
except Exception:
logger.warning('Failed to report exception to sentry.io')
try:
sentry_sdk.capture_exception(exc)
except Exception:
logger.warning('Failed to report exception to sentry.io')

View File

@ -50,11 +50,10 @@ def check_provider(provider):
if not app:
return False
if allauth.app_settings.SITES_ENABLED:
# At least one matching site must be specified
if not app.sites.exists():
logger.error('SocialApp %s has no sites configured', app)
return False
# At least one matching site must be specified
if allauth.app_settings.SITES_ENABLED and not app.sites.exists():
logger.error('SocialApp %s has no sites configured', app)
return False
# At this point, we assume that the provider is correctly configured
return True

View File

@ -136,12 +136,11 @@ def inventree_in_debug_mode(*args, **kwargs):
@register.simple_tag()
def inventree_show_about(user, *args, **kwargs):
"""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
if not user or not user.is_superuser:
return False
return True
# Return False if the user is not a superuser, or no user information is provided
return not (
(get_global_setting('INVENTREE_RESTRICT_ABOUT') and not user)
or not user.is_superuser
)
@register.simple_tag()

View File

@ -69,11 +69,10 @@ class AllowedURLValidator(validators.URLValidator):
# 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)
if not strict_urls:
# Allow URLs which do not have a provided schema
if '://' not in value:
# Validate as if it were http
value = 'http://' + value
# Allow URLs which do not have a provided schema
if not strict_urls and '://' not in value:
# Validate as if it were http
value = 'http://' + value
super().__call__(value)

View File

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

View File

@ -444,11 +444,10 @@ class SettingsTest(InvenTreeTestCase):
# Any fields marked as 'boolean' must have a default value specified
setting = InvenTreeSetting.get_setting_object(key)
if setting.is_bool():
if setting.default_value not in [True, False]:
raise ValueError(
f'Non-boolean default value specified for {key}'
) # pragma: no cover
if setting.is_bool() and setting.default_value not in [True, False]:
raise ValueError(
f'Non-boolean default value specified for {key}'
) # pragma: no cover
def test_global_setting_caching(self):
"""Test caching operations for the global settings class."""

View File

@ -730,13 +730,16 @@ class SupplierPart(
raise ValidationError({'pack_quantity': e.messages})
# Ensure that the linked manufacturer_part points to the same part!
if self.manufacturer_part and self.part:
if self.manufacturer_part.part != self.part:
raise ValidationError({
'manufacturer_part': _(
'Linked manufacturer part must reference the same base part'
)
})
if (
self.manufacturer_part
and self.part
and self.manufacturer_part.part != self.part
):
raise ValidationError({
'manufacturer_part': _(
'Linked manufacturer part must reference the same base part'
)
})
def save(self, *args, **kwargs):
"""Overriding save method to connect an existing ManufacturerPart."""
@ -1049,9 +1052,13 @@ class SupplierPriceBreak(common.models.PriceBreak):
)
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:
instance.part.part.schedule_pricing_update(create=True)
if (
InvenTree.ready.canAppAccessDatabase()
and not InvenTree.ready.isImportingData()
and instance.part
and instance.part.part
):
instance.part.part.schedule_pricing_update(create=True)
@receiver(
@ -1061,6 +1068,10 @@ def after_save_supplier_price(sender, instance, created, **kwargs):
)
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:
instance.part.part.schedule_pricing_update(create=False)
if (
InvenTree.ready.canAppAccessDatabase()
and not InvenTree.ready.isImportingData()
and instance.part
and instance.part.part
):
instance.part.part.schedule_pricing_update(create=False)

View File

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

View File

@ -232,19 +232,20 @@ class Order(
super().clean()
# Check if a responsible owner is required for this order type
if self.REQUIRE_RESPONSIBLE_SETTING:
if get_global_setting(self.REQUIRE_RESPONSIBLE_SETTING, backup_value=False):
if not self.responsible:
raise ValidationError({
'responsible': _('Responsible user or group must be specified')
})
if (
self.REQUIRE_RESPONSIBLE_SETTING
and get_global_setting(self.REQUIRE_RESPONSIBLE_SETTING, backup_value=False)
and not self.responsible
):
raise ValidationError({
'responsible': _('Responsible user or group must be specified')
})
# Check that the referenced 'contact' matches the correct 'company'
if self.company and self.contact:
if self.contact.company != self.company:
raise ValidationError({
'contact': _('Contact does not match selected company')
})
if self.company and self.contact and self.contact.company != self.company:
raise ValidationError({
'contact': _('Contact does not match selected company')
})
def report_context(self):
"""Generate context data for the reporting interface."""
@ -783,10 +784,9 @@ class PurchaseOrder(TotalPriceMixin, Order):
# Extract optional packaging field
packaging = kwargs.get('packaging', None)
if not packaging:
# Default to the packaging field for the linked supplier part
if line.part:
packaging = line.part.packaging
# Default to the packaging field for the linked supplier part
if not packaging and line.part:
packaging = line.part.packaging
# Extract optional barcode field
barcode = kwargs.get('barcode', None)
@ -866,10 +866,11 @@ class PurchaseOrder(TotalPriceMixin, Order):
line.save()
# Has this order been completed?
if len(self.pending_line_items()) == 0:
if get_global_setting('PURCHASEORDER_AUTO_COMPLETE', True):
self.received_by = user
self.complete_order() # This will save the model
if len(self.pending_line_items()) == 0 and get_global_setting(
'PURCHASEORDER_AUTO_COMPLETE', True
):
self.received_by = user
self.complete_order() # This will save the model
# Issue a notification to interested parties, that this order has been "updated"
notify_responsible(
@ -1455,10 +1456,13 @@ class PurchaseOrderLineItem(OrderLineItem):
"""
super().clean()
if self.order.supplier and self.part:
# Supplier part *must* point to the same supplier!
if self.part.supplier != self.order.supplier:
raise ValidationError({'part': _('Supplier part must match supplier')})
# Supplier part *must* point to the same supplier!
if (
self.order.supplier
and self.part
and self.part.supplier != self.order.supplier
):
raise ValidationError({'part': _('Supplier part must match supplier')})
def __str__(self):
"""Render a string representation of a PurchaseOrderLineItem instance."""

View File

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

View File

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

View File

@ -563,10 +563,13 @@ class BarcodeSOAllocate(BarcodeView):
sales_order = kwargs['sales_order']
shipment = self.get_shipment(**kwargs)
if stock_item is not None and line_item is not None:
if stock_item.part != line_item.part:
result['error'] = _('Stock item does not match line item')
raise ValidationError(result)
if (
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')
raise ValidationError(result)
quantity = kwargs.get('quantity', None)
@ -587,10 +590,13 @@ class BarcodeSOAllocate(BarcodeView):
'quantity': quantity,
}
if stock_item is not None and quantity is not None:
if stock_item.unallocated_quantity() < quantity:
response['error'] = _('Insufficient stock available')
raise ValidationError(response)
if (
stock_item is not None
and quantity is not None
and stock_item.unallocated_quantity() < quantity
):
response['error'] = _('Insufficient stock available')
raise ValidationError(response)
# If we have sufficient information, we can allocate the stock item
if all(x is not None for x in [line_item, sales_order, shipment, quantity]):

View File

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

View File

@ -195,15 +195,13 @@ class PluginsRegistry:
for plugin in self.plugins.values():
if plugin.mixin_enabled(mixin):
if active is not None:
# Filter by 'active' status of plugin
if active != plugin.is_active():
continue
# Filter by 'active' status of plugin
if active is not None and active != plugin.is_active():
continue
if builtin is not None:
# Filter by 'builtin' status of plugin
if builtin != plugin.is_builtin:
continue
# Filter by 'builtin' status of plugin
if builtin is not None and builtin != plugin.is_builtin:
continue
result.append(plugin)
@ -396,21 +394,20 @@ class PluginsRegistry:
collected_plugins.append(item)
# 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
if (not settings.PLUGIN_TESTING) or (
settings.PLUGIN_TESTING and settings.PLUGIN_TESTING_SETUP
):
# Collect plugins from setup entry points
for entry in get_entrypoints():
try:
plugin = entry.load()
plugin.is_package = True
plugin.package_name = getattr(entry.dist, 'name', None)
plugin._get_package_metadata()
collected_plugins.append(plugin)
except Exception as error: # pragma: no cover
handle_error(error, do_raise=False, log_name='discovery')
# Check if not running in testing mode and apps should be loaded from hooks
if (settings.PLUGINS_ENABLED and (not settings.PLUGIN_TESTING)) or (
settings.PLUGIN_TESTING and settings.PLUGIN_TESTING_SETUP
):
# Collect plugins from setup entry points
for entry in get_entrypoints():
try:
plugin = entry.load()
plugin.is_package = True
plugin.package_name = getattr(entry.dist, 'name', None)
plugin._get_package_metadata()
collected_plugins.append(plugin)
except Exception as error: # pragma: no cover
handle_error(error, do_raise=False, log_name='discovery')
# Log collected plugins
logger.info('Collected %s plugins', len(collected_plugins))

View File

@ -66,24 +66,26 @@ class SampleValidatorPlugin(SettingsMixin, ValidationMixin, InvenTreePlugin):
# Print debug message to console (intentional)
print('Validating model instance:', instance.__class__, f'<{instance.pk}>')
if isinstance(instance, part.models.BomItem):
if self.get_setting('BOM_ITEM_INTEGER'):
if float(instance.quantity) != int(instance.quantity):
self.raise_error({
'quantity': 'Bom item quantity must be an integer'
})
if (
isinstance(instance, part.models.BomItem)
and self.get_setting('BOM_ITEM_INTEGER')
and float(instance.quantity) != int(instance.quantity)
):
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 (
isinstance(instance, part.models.Part)
and deltas
and 'description' in deltas
):
old_desc = deltas['description']['old']
new_desc = deltas['description']['new']
if deltas and 'description' in deltas:
old_desc = deltas['description']['old']
new_desc = deltas['description']['new']
if len(new_desc) < len(old_desc):
self.raise_error({
'description': 'Part description cannot be shortened'
})
if len(new_desc) < len(old_desc):
self.raise_error({
'description': 'Part description cannot be shortened'
})
def validate_part_name(self, name: str, part):
"""Custom validation for Part name field.
@ -126,14 +128,12 @@ class SampleValidatorPlugin(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]:
self.raise_error('Serial must be a palindrome')
if self.get_setting('SERIAL_MUST_BE_PALINDROME') and serial != serial[::-1]:
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
if serial[0] != part.name[0]:
self.raise_error('Serial number must start with same letter as part')
# Serial must start with the same letter as the linked part, for some reason
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')
def validate_batch_code(self, batch_code: str, item):
"""Ensure that a particular batch code meets specification.

View File

@ -700,39 +700,37 @@ class StockItem(
# The 'supplier_part' field must point to the same part!
try:
if self.supplier_part is not None:
if self.supplier_part.part != self.part:
if self.supplier_part is not None and self.supplier_part.part != self.part:
raise ValidationError({
'supplier_part': _(
f"Part type ('{self.supplier_part.part}') must be {self.part}"
)
})
# A part with a serial number MUST have the quantity set to 1
if self.part is not None and self.serial:
if self.quantity > 1:
raise ValidationError({
'supplier_part': _(
f"Part type ('{self.supplier_part.part}') must be {self.part}"
'quantity': _(
'Quantity must be 1 for item with a serial number'
),
'serial': _(
'Serial number cannot be set if quantity greater than 1'
),
})
if self.quantity == 0:
self.quantity = 1
elif self.quantity > 1:
raise ValidationError({
'quantity': _(
'Quantity must be 1 for item with a serial number'
)
})
if self.part is not None:
# A part with a serial number MUST have the quantity set to 1
if self.serial:
if self.quantity > 1:
raise ValidationError({
'quantity': _(
'Quantity must be 1 for item with a serial number'
),
'serial': _(
'Serial number cannot be set if quantity greater than 1'
),
})
if self.quantity == 0:
self.quantity = 1
elif self.quantity > 1:
raise ValidationError({
'quantity': _(
'Quantity must be 1 for item with a serial number'
)
})
# Serial numbered items cannot be deleted on depletion
self.delete_on_deplete = False
# Serial numbered items cannot be deleted on depletion
self.delete_on_deplete = False
except PartModels.Part.DoesNotExist:
pass

View File

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

View File

@ -95,13 +95,12 @@ class OwnerList(ListAPI):
if not search_match:
continue
if is_active is not None:
# Skip any users which do not match the required *is_active* value
if (
result.owner_type.name == 'user'
and result.owner_id not in matching_user_ids
):
continue
# Skip any users which do not match the required *is_active* value
if is_active is not None and (
result.owner_type.name == 'user'
and result.owner_id not in matching_user_ids
):
continue
# If we get here, there is no reason *not* to include this result
results.append(result)

View File

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

View File

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