2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-08-06 03:51:34 +00:00

[plugin] Mandatory plugins (#10094)

* Add setting for "mandatory" plugins

* Add 'is_active' method to PluginConfig model

* Check against plugin config object by priority

* Prevent plugin from reporting its own 'active' status

* Refactor get_plugin_class for LabelPrint endpoint

* Fix typo

* Mark internal plugin methods as "final"

- Prevent plugins from overriding them

* Enhanced checks for bad actor plugins

* Enhanced unit test for plugin install via API

* Playwright tests for plugin errors

* Test that builtin mandatory plugins are always activated

* Force mandatory plugins to be marked as active on load

* API unit tests

* Unit testing for plugin filtering

* Updated playwright tests

- Force one extra plugin to be mandatory in configuration

* Adjust unit tests

* Updated docs

* Tweak unit test

* Another unit test fix

* Fix with_mixin

- Checking active status first is expensive...

* Make with_mixin call much more efficient

- Pre-load the PluginConfig objects
- Additional unit tests
- Ensure fixed query count

* Fix the 'is_package' method for PluginConfig

* Tweak unit test

* Make api_info endpoint more efficient

- with_mixin is now very quick

* Run just single test

* Disable CI test

* Revert changes to CI pipeline

* Fix typo

* Debug for test

* Style fix

* Additional checks

* Ensure reload

* Ensure plugin registry is ready before running unit tests

* Fix typo

* Add debug statements

* Additional debug output

* Debug logging for MySQL

* Ensure config objects are created?

* Ensure plugin registry is reloaded before running tests

* Remove intentional failure

* Reset debug level

* Fix CI pipeline

* Fix

* Fix test mixins

* Fix test class

* Further updates

* Adjust info view

* Test refactoring

* Fix recursion issue in machine registry

* Force cache behavior

* Reduce API query limits in testing

* Handle potential error case in with_mixin

* remove custom query time code

* Prevent override of is_mandatory()

* Prevent unnecessary reloads

* Tweak unit tests

* Tweak mandatory active save

* Tweak unit test

* Enhanced unit testing

* Exclude lines from coverage

* (final)? cleanup

* Prevent recursive reloads

---------

Co-authored-by: Matthias Mair <code@mjmair.com>
This commit is contained in:
Oliver
2025-07-31 08:26:24 +10:00
committed by GitHub
parent b89a7c45d6
commit b8ea75b2b4
34 changed files with 993 additions and 255 deletions

View File

@@ -10,7 +10,7 @@ This page serves as a short introductory guide for plugin beginners. It should b
We strongly recommend that you use the [Plugin Creator](./creator.md) tool when first scaffolding your new plugin. This tool will help you to create a basic plugin structure, and will also provide you with a set of example files which can be used as a starting point for your own plugin development.
### Determine Requirements
## Determine Requirements
Before starting, you should have a clear understanding of what you want your plugin to do. In particular, consider the functionality provided by the available [plugin mixins](./index.md#plugin-mixins), and whether your plugin can be built using these mixins.
@@ -47,8 +47,122 @@ from plugin.mixins import APICallMixin, SettingsMixin, ScheduleMixin, BarcodeMix
- Use GitHub actions to test your plugin regularly (you can [schedule actions](https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule)) against the 'latest' [docker-build](https://hub.docker.com/r/inventree/inventree) of InvenTree
- If you use the AppMixin pin your plugin against the stable branch of InvenTree, your migrations might get messed up otherwise
### Packaging
## Plugin Code Structure
### Plugin Base Class
Custom plugins must inherit from the [InvenTreePlugin class]({{ sourcefile("src/backend/InvenTree/plugin/plugin.py") }}). Any plugins installed via the methods outlined above will be "discovered" when the InvenTree server launches.
### Imports
As the code base is evolving import paths might change. Therefore we provide stable import targets for important python APIs.
Please read all release notes and watch out for warnings - we generally provide backports for depreciated interfaces for at least one minor release.
#### Plugins
General classes and mechanisms are provided under the `plugin` [namespaces]({{ sourcefile("src/backend/InvenTree/plugin/__init__.py") }}). These include:
```python
# Management objects
registry # Object that manages all plugin states and integrations
# Base classes
InvenTreePlugin # Base class for all plugins
# Errors
MixinImplementationError # Is raised if a mixin is implemented wrong (default not overwritten for example)
MixinNotImplementedError # Is raised if a mixin was not implemented (core mechanisms are missing from the plugin)
```
#### Mixins
Plugin functionality is split between multiple "mixin" classes - each of which provides a specific set of features or behaviors that can be integrated into a plugin. These mixins are designed to be used in conjunction with the `InvenTreePlugin` base class, allowing developers to easily extend the functionality of their plugins. All public APIs that should be used are exposed under `plugin.mixins`. These include all built-in mixins and notification methods. An up-to-date reference can be found in the source code [can be found here]({{ sourcefile("src/backend/InvenTree/plugin/mixins/__init__.py") }}).
Refer to the [mixin documentation](#plugin-mixins) for a list of available mixins, and their usage.
#### Models and other internal InvenTree APIs
!!! warning "Danger Zone"
The APIs outside of the `plugin` namespace are not structured for public usage and require a more in-depth knowledge of the Django framework. Please ask in GitHub discussions of the `InvenTree` org if you are not sure you are using something the intended way.
We do not provide stable interfaces to models or any other internal python APIs. If you need to integrate into these parts please make yourself familiar with the codebase. We follow general Django patterns and only stray from them in limited, special cases.
If you need to react to state changes please use the [EventMixin](./mixins/event.md).
### Plugin Options
Some metadata options can be defined as constants in the plugins class.
``` python
NAME = '' # Used as a general reference to the plugin
SLUG = None # Used in URLs, setting-names etc. when a unique slug as a reference is needed -> the plugin name is used if not set
TITLE = None # A nice human friendly name for the plugin -> used in titles, as plugin name etc.
AUTHOR = None # Author of the plugin, git commit information is used if not present
PUBLISH_DATE = None # Publishing date of the plugin, git commit information is used if not present
WEBSITE = None # Website for the plugin, developer etc. -> is shown in plugin overview if set
VERSION = None # Version of the plugin
MIN_VERSION = None # Lowest InvenTree version number that is supported by the plugin
MAX_VERSION = None # Highest InvenTree version number that is supported by the plugin
```
Refer to the [sample plugins]({{ sourcedir("src/backend/InvenTree/plugin/samples") }}) for further examples.
### Plugin Config
A *PluginConfig* database entry will be created for each plugin "discovered" when the server launches. This configuration entry is used to determine if a particular plugin is enabled.
The configuration entries must be enabled via the [InvenTree admin interface](../settings/admin.md).
!!! warning "Disabled by Default"
Newly discovered plugins are disabled by default, and must be manually enabled (in the admin interface) by a user with staff privileges.
## Plugin Mixins
Common use cases are covered by pre-supplied modules in the form of *mixins* (similar to how [Django]({% include "django.html" %}/topics/class-based-views/mixins/) does it). Each mixin enables the integration into a specific area of InvenTree. Sometimes it also enhances the plugin with helper functions to supply often used functions out-of-the-box.
Supported mixin classes are:
| Mixin | Description |
| --- | --- |
| [ActionMixin](./mixins/action.md) | Run custom actions |
| [APICallMixin](./mixins/api.md) | Perform calls to external APIs |
| [AppMixin](./mixins/app.md) | Integrate additional database tables |
| [BarcodeMixin](./mixins/barcode.md) | Support custom barcode actions |
| [CurrencyExchangeMixin](./mixins/currency.md) | Custom interfaces for currency exchange rates |
| [DataExport](./mixins/export.md) | Customize data export functionality |
| [EventMixin](./mixins/event.md) | Respond to events |
| [LabelPrintingMixin](./mixins/label.md) | Custom label printing support |
| [LocateMixin](./mixins/locate.md) | Locate and identify stock items |
| [NavigationMixin](./mixins/navigation.md) | Add custom pages to the web interface |
| [NotificationMixin](./mixins/notification.md) | Send custom notifications in response to system events |
| [ReportMixin](./mixins/report.md) | Add custom context data to reports |
| [ScheduleMixin](./mixins/schedule.md) | Schedule periodic tasks |
| [SettingsMixin](./mixins/settings.md) | Integrate user configurable settings |
| [UserInterfaceMixin](./mixins/ui.md) | Add custom user interface features |
| [UrlsMixin](./mixins/urls.md) | Respond to custom URL endpoints |
| [ValidationMixin](./mixins/validation.md) | Provide custom validation of database models |
## Plugin Concepts
### Backend vs Frontend Code
InvenTree plugins can contain both backend and frontend code. The backend code is written in Python, and is used to implement server-side functionality, such as database models, API endpoints, and background tasks.
The frontend code is written in JavaScript (or TypeScript), and is used to implement user interface components, such as custom UI panels.
You can [read more about frontend integration](./frontend.md) to learn how to integrate custom UI components into the InvenTree web interface.
## Static Files
If your plugin requires static files (e.g. CSS, JavaScript, images), these should be placed in the top level `static` directory within the distributed plugin package. These files will be automatically collected by InvenTree when the plugin is installed, and copied to an appropriate location.
These files will be available to the InvenTree web interface, and can be accessed via the URL `/static/plugins/<plugin_name>/<filename>`. Static files are served by the [proxy server](../start/processes.md#proxy-server).
For example, if the plugin is named `my_plugin`, and contains a file `CustomPanel.js`, it can be accessed via the URL `/static/plugins/my_plugin/CustomPanel.js`.
### Packaging
!!! tip "Package-Discovery can be tricky"
Most problems with packaging stem from problems with discovery. [This guide](https://setuptools.pypa.io/en/latest/userguide/package_discovery.html#automatic-discovery) by the PyPA contains a lot of information about discovery during packaging. These mechanisms generally apply to most discovery processes in InvenTree and the wider Django ecosystem.

View File

@@ -10,7 +10,7 @@ Plugins can be added from multiple sources:
- Plugins can be installed using the PIP Python package manager
- Plugins can be placed in the external [plugins directory](../start/config.md#plugin-options)
- InvenTree built-in plugins are located within the InvenTree source code
- InvenTree [built-in](./builtin/index.md) plugins are located within the InvenTree source code
For further information, read more about [installing plugins](./install.md).
@@ -18,123 +18,45 @@ For further information, read more about [installing plugins](./install.md).
Plugin behaviour can be controlled via the InvenTree configuration options. Refer to the [configuration guide](../start/config.md#plugin-options) for the available plugin configuration options.
### Backend vs Frontend Code
## Developing a Plugin
InvenTree plugins can contain both backend and frontend code. The backend code is written in Python, and is used to implement server-side functionality, such as database models, API endpoints, and background tasks.
If you are interested in developing a custom plugin for InvenTree, refer to the [plugin development guide](./develop.md). This guide provides an overview of the plugin architecture, and how to create a new plugin.
The frontend code is written in JavaScript (or TypeScript), and is used to implement user interface components, such as custom UI panels.
You can [read more about frontend integration](./frontend.md) to learn how to integrate custom UI components into the InvenTree web interface.
### Creating a Plugin
### Plugin Creator
To assist in creating a new plugin, we provide a [plugin creator command line tool](./creator.md). This allows developers to quickly scaffold a new InvenTree plugin, and provides a basic structure to build upon.
### Plugin Walkthrough
### Basic Plugin Walkthrough
Check out our [plugin development walkthrough](./walkthrough.md) to learn how to create an example plugin. This guide will take you through the steps to add a new part panel that displays an image carousel from images attached to the selected part.
Check out our [basic plugin walkthrough](../plugins/walkthrough.md) to learn how to create an example plugin. This guide will take you through the steps to add a new part panel that displays an image carousel from images attached to the selected part.
## Available Plugins
## Plugin Code Structure
InvenTree plugins can be provided from a variety of sources, including built-in plugins, sample plugins, mandatory plugins, and third-party plugins.
### Plugin Base Class
### Built-in Plugins
Custom plugins must inherit from the [InvenTreePlugin class]({{ sourcefile("src/backend/InvenTree/plugin/plugin.py") }}). Any plugins installed via the methods outlined above will be "discovered" when the InvenTree server launches.
InvenTree comes with a number of built-in plugins that provide additional functionality. These plugins are included in the InvenTree source code, and can be enabled or disabled via the configuration options.
### Imports
Refer to the [built-in plugins documentation](./builtin/index.md) for more information on the available built-in plugins.
As the code base is evolving import paths might change. Therefore we provide stable import targets for important python APIs.
Please read all release notes and watch out for warnings - we generally provide backports for depreciated interfaces for at least one minor release.
### Sample Plugins
#### Plugins
If the InvenTree server is running in [debug mode](../start/config.md#debug-mode), an additional set of *sample* plugins are available. These plugins are intended to demonstrate some of the available capabilities provided by the InvenTree plugin architecture, and can be used as a starting point for developing your own plugins.
General classes and mechanisms are provided under the `plugin` [namespaces]({{ sourcefile("src/backend/InvenTree/plugin/__init__.py") }}). These include:
!!! info "Debug Mode Only"
Sample plugins are only available when the InvenTree server is running in debug mode. This is typically used during development, and is not recommended for production environments.
```python
# Management objects
registry # Object that manages all plugin states and integrations
### Third Party Plugins
# Base classes
InvenTreePlugin # Base class for all plugins
A list of known third-party InvenTree extensions is provided [on our website](https://inventree.org/extend/integrate/) If you have an extension that should be listed here, contact the InvenTree team on [GitHub](https://github.com/inventree/). Refer to the [InvenTree website](https://inventree.org/plugins.html) for a (non exhaustive) list of plugins that are available for InvenTree. This includes both official and third-party plugins.
# Errors
MixinImplementationError # Is raised if a mixin is implemented wrong (default not overwritten for example)
MixinNotImplementedError # Is raised if a mixin was not implemented (core mechanisms are missing from the plugin)
```
## Mandatory Plugins
#### Mixins
Some plugins are mandatory for InvenTree to function correctly. These plugins are included in the InvenTree source code, and cannot be disabled. They provide essential functionality that is required for the core InvenTree features to work.
Plugin functionality is split between multiple "mixin" classes - each of which provides a specific set of features or behaviors that can be integrated into a plugin. These mixins are designed to be used in conjunction with the `InvenTreePlugin` base class, allowing developers to easily extend the functionality of their plugins. All public APIs that should be used are exposed under `plugin.mixins`. These include all built-in mixins and notification methods. An up-to-date reference can be found in the source code [can be found here]({{ sourcefile("src/backend/InvenTree/plugin/mixins/__init__.py") }}).
### Mandatory Third-Party Plugins
Refer to the [mixin documentation](#plugin-mixins) for a list of available mixins, and their usage.
It may be desirable to mark a third-party plugin as mandatory, meaning that once installed, it is automatically enabled and cannot be disabled. This is useful in situations where a particular plugin is required for crucial functionality and it it imperative that it cannot be disabled by user interaction.
#### Models and other internal InvenTree APIs
!!! warning "Danger Zone"
The APIs outside of the `plugin` namespace are not structured for public usage and require a more in-depth knowledge of the Django framework. Please ask in GitHub discussions of the `InvenTree` org if you are not sure you are using something the intended way.
We do not provide stable interfaces to models or any other internal python APIs. If you need to integrate into these parts please make yourself familiar with the codebase. We follow general Django patterns and only stray from them in limited, special cases.
If you need to react to state changes please use the [EventMixin](./mixins/event.md).
### Plugin Options
Some metadata options can be defined as constants in the plugins class.
``` python
NAME = '' # Used as a general reference to the plugin
SLUG = None # Used in URLs, setting-names etc. when a unique slug as a reference is needed -> the plugin name is used if not set
TITLE = None # A nice human friendly name for the plugin -> used in titles, as plugin name etc.
AUTHOR = None # Author of the plugin, git commit information is used if not present
PUBLISH_DATE = None # Publishing date of the plugin, git commit information is used if not present
WEBSITE = None # Website for the plugin, developer etc. -> is shown in plugin overview if set
VERSION = None # Version of the plugin
MIN_VERSION = None # Lowest InvenTree version number that is supported by the plugin
MAX_VERSION = None # Highest InvenTree version number that is supported by the plugin
```
Refer to the [sample plugins]({{ sourcedir("src/backend/InvenTree/plugin/samples") }}) for further examples.
### Plugin Config
A *PluginConfig* database entry will be created for each plugin "discovered" when the server launches. This configuration entry is used to determine if a particular plugin is enabled.
The configuration entries must be enabled via the [InvenTree admin interface](../settings/admin.md).
!!! warning "Disabled by Default"
Newly discovered plugins are disabled by default, and must be manually enabled (in the admin interface) by a user with staff privileges.
## Plugin Mixins
Common use cases are covered by pre-supplied modules in the form of *mixins* (similar to how [Django]({% include "django.html" %}/topics/class-based-views/mixins/) does it). Each mixin enables the integration into a specific area of InvenTree. Sometimes it also enhances the plugin with helper functions to supply often used functions out-of-the-box.
Supported mixin classes are:
| Mixin | Description |
| --- | --- |
| [ActionMixin](./mixins/action.md) | Run custom actions |
| [APICallMixin](./mixins/api.md) | Perform calls to external APIs |
| [AppMixin](./mixins/app.md) | Integrate additional database tables |
| [BarcodeMixin](./mixins/barcode.md) | Support custom barcode actions |
| [CurrencyExchangeMixin](./mixins/currency.md) | Custom interfaces for currency exchange rates |
| [DataExport](./mixins/export.md) | Customize data export functionality |
| [EventMixin](./mixins/event.md) | Respond to events |
| [LabelPrintingMixin](./mixins/label.md) | Custom label printing support |
| [LocateMixin](./mixins/locate.md) | Locate and identify stock items |
| [NavigationMixin](./mixins/navigation.md) | Add custom pages to the web interface |
| [NotificationMixin](./mixins/notification.md) | Send custom notifications in response to system events |
| [ReportMixin](./mixins/report.md) | Add custom context data to reports |
| [ScheduleMixin](./mixins/schedule.md) | Schedule periodic tasks |
| [SettingsMixin](./mixins/settings.md) | Integrate user configurable settings |
| [UserInterfaceMixin](./mixins/ui.md) | Add custom user interface features |
| [UrlsMixin](./mixins/urls.md) | Respond to custom URL endpoints |
| [ValidationMixin](./mixins/validation.md) | Provide custom validation of database models |
## Static Files
If your plugin requires static files (e.g. CSS, JavaScript, images), these should be placed in the top level `static` directory within the distributed plugin package. These files will be automatically collected by InvenTree when the plugin is installed, and copied to an appropriate location.
These files will be available to the InvenTree web interface, and can be accessed via the URL `/static/plugins/<plugin_name>/<filename>`. Static files are served by the [proxy server](../start/processes.md#proxy-server).
For example, if the plugin is named `my_plugin`, and contains a file `CustomPanel.js`, it can be accessed via the URL `/static/plugins/my_plugin/CustomPanel.js`.
In such as case, the plugin(s) should be marked as "mandatory" at run-time in the [configuration file](../start/config.md#plugin-options). This will ensure that these plugins are always enabled, and cannot be disabled by the user.

View File

@@ -1,11 +0,0 @@
---
title: Third Party Integrations
---
## Third Party Integrations
A list of known third-party InvenTree extensions is provided [on our website](https://inventree.org/extend/integrate/) If you have an extension that should be listed here, contact the InvenTree team on [GitHub](https://github.com/inventree/).
## Available Plugins
Refer to the [InvenTree website](https://inventree.org/plugins.html) for a (non exhaustive) list of plugins that are available for InvenTree. This includes both official and third-party plugins.

View File

@@ -463,6 +463,9 @@ The following [plugin](../plugins/index.md) configuration options are available:
| INVENTREE_PLUGIN_NOINSTALL | plugin_noinstall | Disable Plugin installation via API - only use plugins.txt file | False |
| INVENTREE_PLUGIN_FILE | plugins_plugin_file | Location of plugin installation file | *Not specified* |
| INVENTREE_PLUGIN_DIR | plugins_plugin_dir | Location of external plugin directory | *Not specified* |
| INVENTREE_PLUGINS_MANDATORY | plugins_mandatory | List of [plugins which are considered mandatory](../plugins/index.md#mandatory-third-party-plugins) | *Not specified* |
| INVENTREE_PLUGIN_DEV_SLUG | plugin_dev.slug | Specify plugin to run in [development mode](../plugins/creator.md#backend-configuration) | *Not specified* |
| INVENTREE_PLUGIN_DEV_HOST | plugin_dev.host | Specify host for development mode plugin | http://localhost:5174 |
## Override Global Settings

View File

@@ -210,7 +210,7 @@ nav:
- Plugins:
- Overview: plugins/index.md
- Installation: plugins/install.md
- Developing a Plugin: plugins/how_to.md
- Developing a Plugin: plugins/develop.md
- Frontend Integration: plugins/frontend.md
- Plugin Creator: plugins/creator.md
- Plugin Walkthrough: plugins/walkthrough.md
@@ -265,7 +265,6 @@ nav:
- Slack Notifications: plugins/builtin/slack_notification.md
- UI Notifications: plugins/builtin/ui_notification.md
- Currency Exchange: plugins/builtin/currency_exchange.md
- Third-Party: plugins/integrate.md
# Plugins
plugins: