def clear_restart_flag(self): """Clear the SERVER_RESTART_REQUIRED setting.""" try: import common.models if common.models.InvenTreeSetting.get_setting( 'SERVER_RESTART_REQUIRED', backup_value=False, create=False, cache=False): logger.info("Clearing SERVER_RESTART_REQUIRED flag") if not isImportingData(): common.models.InvenTreeSetting.set_setting( 'SERVER_RESTART_REQUIRED', False, None) except Exception: pass
def allow_table_event(table_name): """ Determine if an automatic event should be fired for a given table. We *do not* want events to be fired for some tables! """ if isImportingData(): # Prevent table events during the data import process return False # pragma: no cover table_name = table_name.lower().strip() # Ignore any tables which start with these prefixes ignore_prefixes = [ 'account_', 'auth_', 'authtoken_', 'django_', 'error_', 'exchange_', 'otp_', 'plugin_', 'socialaccount_', 'user_', 'users_', ] if any([table_name.startswith(prefix) for prefix in ignore_prefixes]): return False ignore_tables = [ 'common_notificationentry', 'common_webhookendpoint', 'common_webhookmessage', ] if table_name in ignore_tables: return False return True
def trigger_notifaction(obj, category=None, obj_ref='pk', **kwargs): """ Send out a notification """ targets = kwargs.get('targets', None) target_fnc = kwargs.get('target_fnc', None) target_args = kwargs.get('target_args', []) target_kwargs = kwargs.get('target_kwargs', {}) context = kwargs.get('context', {}) delivery_methods = kwargs.get('delivery_methods', None) # Check if data is importing currently if isImportingData(): return # Resolve objekt reference obj_ref_value = getattr(obj, obj_ref) # Try with some defaults if not obj_ref_value: obj_ref_value = getattr(obj, 'pk') if not obj_ref_value: obj_ref_value = getattr(obj, 'id') if not obj_ref_value: raise KeyError( f"Could not resolve an object reference for '{str(obj)}' with {obj_ref}, pk, id" ) # Check if we have notified recently... delta = timedelta(days=1) if NotificationEntry.check_recent(category, obj_ref_value, delta): logger.info( f"Notification '{category}' has recently been sent for '{str(obj)}' - SKIPPING" ) return logger.info(f"Gathering users for notification '{category}'") # Collect possible targets if not targets: targets = target_fnc(*target_args, **target_kwargs) if targets: logger.info(f"Sending notification '{category}' for '{str(obj)}'") # Collect possible methods if delivery_methods is None: delivery_methods = storage.liste else: delivery_methods = (delivery_methods - IGNORED_NOTIFICATION_CLS) for method in delivery_methods: logger.info(f"Triggering method '{method.METHOD_NAME}'") try: deliver_notification(method, obj, category, targets, context) except NotImplementedError as error: raise error except Exception as error: logger.error(error) # Set delivery flag NotificationEntry.notify(category, obj_ref_value) else: logger.info(f"No possible users for notification '{category}'")
def trigger_notification(obj, category=None, obj_ref='pk', **kwargs): """Send out a notification.""" targets = kwargs.get('targets', None) target_fnc = kwargs.get('target_fnc', None) target_args = kwargs.get('target_args', []) target_kwargs = kwargs.get('target_kwargs', {}) target_exclude = kwargs.get('target_exclude', None) context = kwargs.get('context', {}) delivery_methods = kwargs.get('delivery_methods', None) # Check if data is importing currently if isImportingData(): return # Resolve object reference obj_ref_value = getattr(obj, obj_ref) # Try with some defaults if not obj_ref_value: obj_ref_value = getattr(obj, 'pk') if not obj_ref_value: obj_ref_value = getattr(obj, 'id') if not obj_ref_value: raise KeyError( f"Could not resolve an object reference for '{str(obj)}' with {obj_ref}, pk, id" ) # Check if we have notified recently... delta = timedelta(days=1) if NotificationEntry.check_recent(category, obj_ref_value, delta): logger.info( f"Notification '{category}' has recently been sent for '{str(obj)}' - SKIPPING" ) return logger.info(f"Gathering users for notification '{category}'") if target_exclude is None: target_exclude = set() # Collect possible targets if not targets: targets = target_fnc(*target_args, **target_kwargs) # Convert list of targets to a list of users # (targets may include 'owner' or 'group' classes) target_users = set() if targets: for target in targets: if target is None: continue # User instance is provided elif isinstance(target, get_user_model()): if target not in target_exclude: target_users.add(target) # Group instance is provided elif isinstance(target, Group): for user in get_user_model().objects.filter( groups__name=target.name): if user not in target_exclude: target_users.add(user) # Owner instance (either 'user' or 'group' is provided) elif isinstance(target, Owner): for owner in target.get_related_owners(include_group=False): user = owner.owner if user not in target_exclude: target_users.add(user) # Unhandled type else: logger.error( f"Unknown target passed to trigger_notification method: {target}" ) if target_users: logger.info(f"Sending notification '{category}' for '{str(obj)}'") # Collect possible methods if delivery_methods is None: delivery_methods = storage.liste else: delivery_methods = (delivery_methods - IGNORED_NOTIFICATION_CLS) for method in delivery_methods: logger.info( f"Triggering notification method '{method.METHOD_NAME}'") try: deliver_notification(method, obj, category, target_users, context) except NotImplementedError as error: # Allow any single notification method to fail, without failing the others logger.error(error) except Exception as error: logger.error(error) # Set delivery flag NotificationEntry.notify(category, obj_ref_value) else: logger.info(f"No possible users for notification '{category}'")
def check_build_stock(build: build.models.Build): """Check the required stock for a newly created build order. Send an email out to any subscribed users if stock is low. """ # Do not notify if we are importing data if isImportingData(): return # Iterate through each of the parts required for this build lines = [] if not build: logger.error("Invalid build passed to 'build.tasks.check_build_stock'") return try: part = build.part except part_models.Part.DoesNotExist: # Note: This error may be thrown during unit testing... logger.error( "Invalid build.part passed to 'build.tasks.check_build_stock'") return for bom_item in part.get_bom_items(): sub_part = bom_item.sub_part # The 'in stock' quantity depends on whether the bom_item allows variants in_stock = sub_part.get_stock_count( include_variants=bom_item.allow_variants) allocated = sub_part.allocation_count() available = max(0, in_stock - allocated) required = Decimal(bom_item.quantity) * Decimal(build.quantity) if available < required: # There is not sufficient stock for this part lines.append({ 'link': InvenTree.helpers.construct_absolute_url( sub_part.get_absolute_url()), 'part': sub_part, 'in_stock': in_stock, 'allocated': allocated, 'available': available, 'required': required, }) if len(lines) == 0: # Nothing to do return # Are there any users subscribed to these parts? subscribers = build.part.get_subscribers() emails = EmailAddress.objects.filter(user__in=subscribers, ) if len(emails) > 0: logger.info(f"Notifying users of stock required for build {build.pk}") context = { 'link': InvenTree.helpers.construct_absolute_url(build.get_absolute_url()), 'build': build, 'part': build.part, 'lines': lines, } # Render the HTML message html_message = render_to_string( 'email/build_order_required_stock.html', context) subject = _("Stock required for build order") recipients = emails.values_list('email', flat=True) InvenTree.tasks.send_email(subject, '', recipients, html_message=html_message)