From 8e962c0c59980a7382d9282d2a863ed05e148b1c Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 13 Dec 2021 08:03:19 +0100 Subject: [PATCH 01/50] add mixin to consum a single API --- .../plugin/builtin/integration/mixins.py | 72 +++++++++++++++++++ InvenTree/plugin/mixins/__init__.py | 4 +- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/InvenTree/plugin/builtin/integration/mixins.py b/InvenTree/plugin/builtin/integration/mixins.py index 3a6b558db7..70345172ce 100644 --- a/InvenTree/plugin/builtin/integration/mixins.py +++ b/InvenTree/plugin/builtin/integration/mixins.py @@ -1,4 +1,7 @@ """default mixins for IntegrationMixins""" +import json +import requests + from django.conf.urls import url, include from plugin.urls import PLUGIN_BASE @@ -167,3 +170,72 @@ class AppMixin: this plugin is always an app with this plugin """ return True + + +class APICallMixin: + """Mixin that enables easier API calls for a plugin + + 1. Add this mixin + 2. Add two global settings for the required url and token/passowrd (use `GlobalSettingsMixin`) + 3. Save the references to `API_URL_SETTING` and `API_PASSWORD_SETTING` + 4. Set `API_TOKEN` to the name required for the token / password by the external API + 5. (Optional) Override the `api_url` property method if some part of the APIs url is static + 6. (Optional) Override `api_headers` to add extra headers (by default the token/password and Content-Type are contained) + 6. Access the API in you plugin code via `api_call` + """ + API_METHOD = 'https' + API_URL_SETTING = None + API_PASSWORD_SETTING = None + + API_TOKEN = 'Bearer' + + class MixinMeta: + """meta options for this mixin""" + MIXIN_NAME = 'external API usage' + + def __init__(self): + super().__init__() + self.add_mixin('api_call', 'has_api_call', __class__) + + @property + def has_api_call(self): + """Is the mixin ready to call external APIs?""" + # TODO check if settings are set + return True + + @property + def api_url(self): + return f'{self.API_METHOD}://{self.get_globalsetting(self.API_URL_SETTING)}' + + @property + def api_headers(self): + return {self.API_TOKEN: self.get_globalsetting(self.API_PASSWORD_SETTING), 'Content-Type': 'application/json'} + + def api_build_url_args(self, arguments): + groups = [] + for key, val in arguments.items(): + groups.append(f'{key}={",".join([str(a) for a in val])}') + return f'?{"&".join(groups)}' + + def api_call(self, endpoint, method: str='GET', url_args=None, data=None, headers=None, simple_response: bool = True): + if url_args: + endpoint += self.api_build_url_args(url_args) + + if headers is None: + headers = self.api_headers + + # build kwargs for call + kwargs = { + 'url': f'{self.api_url}/{endpoint}', + 'headers': headers, + } + if data: + kwargs['data'] = json.dumps(data) + + # run command + response = requests.request(method, **kwargs) + + # return + if simple_response: + return response.json() + return response diff --git a/InvenTree/plugin/mixins/__init__.py b/InvenTree/plugin/mixins/__init__.py index feb6bc3466..d63bae097b 100644 --- a/InvenTree/plugin/mixins/__init__.py +++ b/InvenTree/plugin/mixins/__init__.py @@ -1,6 +1,6 @@ """utility class to enable simpler imports""" -from ..builtin.integration.mixins import AppMixin, GlobalSettingsMixin, UrlsMixin, NavigationMixin +from ..builtin.integration.mixins import AppMixin, GlobalSettingsMixin, UrlsMixin, NavigationMixin, APICallMixin __all__ = [ - 'AppMixin', 'GlobalSettingsMixin', 'UrlsMixin', 'NavigationMixin', + 'AppMixin', 'GlobalSettingsMixin', 'UrlsMixin', 'NavigationMixin', 'APICallMixin', ] From 251fdeb69e161d1c03fb7751fcdab61c49319471 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 13 Dec 2021 18:01:20 +0100 Subject: [PATCH 02/50] PEP fixes --- InvenTree/plugin/builtin/integration/mixins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/plugin/builtin/integration/mixins.py b/InvenTree/plugin/builtin/integration/mixins.py index 70345172ce..932588c281 100644 --- a/InvenTree/plugin/builtin/integration/mixins.py +++ b/InvenTree/plugin/builtin/integration/mixins.py @@ -174,7 +174,7 @@ class AppMixin: class APICallMixin: """Mixin that enables easier API calls for a plugin - + 1. Add this mixin 2. Add two global settings for the required url and token/passowrd (use `GlobalSettingsMixin`) 3. Save the references to `API_URL_SETTING` and `API_PASSWORD_SETTING` @@ -217,7 +217,7 @@ class APICallMixin: groups.append(f'{key}={",".join([str(a) for a in val])}') return f'?{"&".join(groups)}' - def api_call(self, endpoint, method: str='GET', url_args=None, data=None, headers=None, simple_response: bool = True): + def api_call(self, endpoint, method: str = 'GET', url_args=None, data=None, headers=None, simple_response: bool = True): if url_args: endpoint += self.api_build_url_args(url_args) From a2871ccb45beebf61d54ed30df91722646fa600d Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 13 Dec 2021 18:40:24 +0100 Subject: [PATCH 03/50] update database images before running --- .github/workflows/qc_checks.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/qc_checks.yaml b/.github/workflows/qc_checks.yaml index 929a299e93..4491ba7815 100644 --- a/.github/workflows/qc_checks.yaml +++ b/.github/workflows/qc_checks.yaml @@ -282,6 +282,7 @@ jobs: cache: 'pip' - name: Install Dependencies run: | + sudo apt-get update sudo apt-get install mysql-server libmysqlclient-dev pip3 install invoke pip3 install mysqlclient From 1e5ecb13f04213f05bd0805ce6d169c5e7187cfa Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 7 Jan 2022 21:48:17 +1100 Subject: [PATCH 04/50] Add code for triggering and responding to events --- InvenTree/InvenTree/events.py | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 InvenTree/InvenTree/events.py diff --git a/InvenTree/InvenTree/events.py b/InvenTree/InvenTree/events.py new file mode 100644 index 0000000000..75b04adb00 --- /dev/null +++ b/InvenTree/InvenTree/events.py @@ -0,0 +1,42 @@ +""" +Functions for triggering and responding to server side events +""" + +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import logging + +from InvenTree.tasks import offload_task + + +logger = logging.getLogger('inventree') + + +def trigger_event(event, *args, **kwargs): + """ + Trigger an even with optional arguments. + + This event will be stored in the database, + and the worker will respond to it later on. + """ + + logger.debug(f"Event triggered: '{event}'") + + offload_task( + 'InvenTree.events.process_event', + event, + *args, + **kwargs, + ) + +def process_event(event, *args, **kwargs): + """ + Respond to a triggered event. + + This function is run by the background worker process. + """ + + logger.info(f"Processing event '{event}'") + + # Determine if there are any plugins which are interested in responding From 8ff3bf1ad19cb35ed12975a916b5fbb768c52b1b Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 7 Jan 2022 21:53:42 +1100 Subject: [PATCH 05/50] Adds a new setting to enable event responses --- InvenTree/InvenTree/events.py | 6 ++++++ InvenTree/common/models.py | 11 ++++++++++- InvenTree/templates/InvenTree/settings/plugin.html | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/events.py b/InvenTree/InvenTree/events.py index 75b04adb00..2286f57d7b 100644 --- a/InvenTree/InvenTree/events.py +++ b/InvenTree/InvenTree/events.py @@ -7,6 +7,10 @@ from __future__ import unicode_literals import logging +from django.conf import settings + +from common.models import InvenTreeSetting + from InvenTree.tasks import offload_task @@ -40,3 +44,5 @@ def process_event(event, *args, **kwargs): logger.info(f"Processing event '{event}'") # Determine if there are any plugins which are interested in responding + if settings.PLUGIN_TESTING or InvenTreeSetting.get_setting('ENABLE_PLUGINS_EVENTS'): + pass diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index dee0eb2e8b..d361cb5bb1 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -957,6 +957,8 @@ class InvenTreeSetting(BaseInvenTreeSetting): 'default': False, 'validator': bool, }, + + # Settings for plugin mixin features 'ENABLE_PLUGINS_URL': { 'name': _('Enable URL integration'), 'description': _('Enable plugins to add URL routes'), @@ -984,7 +986,14 @@ class InvenTreeSetting(BaseInvenTreeSetting): 'default': False, 'validator': bool, 'requires_restart': True, - } + }, + 'ENABLE_PLUGINS_EVENTS': { + 'name': _('Enable event integration'), + 'description': _('Enable plugins to respond to internal events'), + 'default': False, + 'validator': bool, + 'requires_restart': True, + }, } class Meta: diff --git a/InvenTree/templates/InvenTree/settings/plugin.html b/InvenTree/templates/InvenTree/settings/plugin.html index 858d0f3ab9..7b428e161f 100644 --- a/InvenTree/templates/InvenTree/settings/plugin.html +++ b/InvenTree/templates/InvenTree/settings/plugin.html @@ -20,6 +20,7 @@ {% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_SCHEDULE" icon="fa-calendar-alt" %} + {% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_EVENTS" icon="fa-reply-all" %} {% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_URL" icon="fa-link" %} {% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_NAVIGATION" icon="fa-sitemap" %} {% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_APP" icon="fa-rocket" %} From 63eb49777a5ce289c3910ea87929702e88990b39 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 7 Jan 2022 22:22:59 +1100 Subject: [PATCH 06/50] Add mixin class to respond to internal events --- .../plugin/builtin/integration/mixins.py | 64 +++++++++++++++++++ InvenTree/plugin/mixins/__init__.py | 3 +- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/InvenTree/plugin/builtin/integration/mixins.py b/InvenTree/plugin/builtin/integration/mixins.py index c6198ed7a1..68288121e2 100644 --- a/InvenTree/plugin/builtin/integration/mixins.py +++ b/InvenTree/plugin/builtin/integration/mixins.py @@ -80,6 +80,7 @@ class ScheduleMixin: ALLOWABLE_SCHEDULE_TYPES = ['I', 'H', 'D', 'W', 'M', 'Q', 'Y'] + # Override this in subclass model SCHEDULED_TASKS = {} class MixinMeta: @@ -180,6 +181,69 @@ class ScheduleMixin: logger.warning("unregister_tasks failed, database not ready") +class EventMixin: + """ + Mixin that provides support for responding to triggered events. + + Implementing classes must provide a list of tuples, + which provide pairs of 'event':'function' + + Notes: + + Events are called by name, and based on the django signal nomenclature, + e.g. 'part.pre_save' + + Receiving functions must be prototyped to match the 'event' they receive. + + Example: + + EVENTS = [ + ('part.pre_save', 'myplugin.functions.do_stuff'), + ('build.complete', 'myplugin.functions.process_completed_build'), + ] + """ + + # Override this in subclass model + EVENTS = [] + + class MixinMeta: + MIXIN_NAME = 'Events' + + def __init__(self): + super().__init__() + self.add_mixin('events', 'has_events', __class__) + self.events = getattr(self, 'EVENTS', []) + + self.validate_events() + + @property + def has_events(self): + return bool(self.events) and len(self.events) > 0 + + def validate_events(self): + """ + Check that the provided event callbacks are valid + """ + + if not self.has_events: + raise ValueError('EVENTS not defined') + + for pair in self.events: + valid = True + + if len(pair) == 2: + event = pair[0].strip() + func = pair[1].strip() + + if len(event) == 0 or len(func) == 0: + valid = False + else: + valid = False + + if not valid: + raise ValueError("Invalid event callback: " + str(pair)) + + class UrlsMixin: """ Mixin that enables custom URLs for the plugin diff --git a/InvenTree/plugin/mixins/__init__.py b/InvenTree/plugin/mixins/__init__.py index e9c910bb9e..3df552df75 100644 --- a/InvenTree/plugin/mixins/__init__.py +++ b/InvenTree/plugin/mixins/__init__.py @@ -2,10 +2,11 @@ Utility class to enable simpler imports """ -from ..builtin.integration.mixins import AppMixin, SettingsMixin, ScheduleMixin, UrlsMixin, NavigationMixin +from ..builtin.integration.mixins import AppMixin, SettingsMixin, EventMixin, ScheduleMixin, UrlsMixin, NavigationMixin __all__ = [ 'AppMixin', + 'EventMixin', 'NavigationMixin', 'ScheduleMixin', 'SettingsMixin', From a604d85f0f83afa413061bb5cb0f14c0ab6805b2 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 7 Jan 2022 22:31:10 +1100 Subject: [PATCH 07/50] Move events.py to the plugin app --- InvenTree/{InvenTree => plugin}/events.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename InvenTree/{InvenTree => plugin}/events.py (91%) diff --git a/InvenTree/InvenTree/events.py b/InvenTree/plugin/events.py similarity index 91% rename from InvenTree/InvenTree/events.py rename to InvenTree/plugin/events.py index 2286f57d7b..703fe71d9c 100644 --- a/InvenTree/InvenTree/events.py +++ b/InvenTree/plugin/events.py @@ -19,7 +19,7 @@ logger = logging.getLogger('inventree') def trigger_event(event, *args, **kwargs): """ - Trigger an even with optional arguments. + Trigger an event with optional arguments. This event will be stored in the database, and the worker will respond to it later on. @@ -28,12 +28,13 @@ def trigger_event(event, *args, **kwargs): logger.debug(f"Event triggered: '{event}'") offload_task( - 'InvenTree.events.process_event', + 'plugin.events.process_event', event, *args, **kwargs, ) + def process_event(event, *args, **kwargs): """ Respond to a triggered event. From 04d25a60b0653304ee541192920a2c737a73ac7d Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 8 Jan 2022 09:07:27 +1100 Subject: [PATCH 08/50] Adds sample plugin which responds to triggered events - Adds some example trigger events for the "Part" model --- InvenTree/part/models.py | 12 ++++-- .../plugin/builtin/integration/mixins.py | 4 +- InvenTree/plugin/events.py | 25 +++++++++++- InvenTree/plugin/plugin.py | 12 ++++++ InvenTree/plugin/registry.py | 39 +++++++++++++++++++ .../samples/integration/event_sample.py | 29 ++++++++++++++ .../samples/integration/scheduled_task.py | 2 +- .../InvenTree/settings/plugin_settings.html | 2 +- 8 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 InvenTree/plugin/samples/integration/event_sample.py diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index ec76846a08..99d7cd02f5 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -60,6 +60,8 @@ import common.models import part.settings as part_settings +from plugin.events import trigger_event + logger = logging.getLogger("inventree") @@ -1980,10 +1982,10 @@ class Part(MPTTModel): @property def attachment_count(self): - """ Count the number of attachments for this part. + """ + Count the number of attachments for this part. If the part is a variant of a template part, include the number of attachments for the template part. - """ return self.part_attachments.count() @@ -2181,7 +2183,11 @@ def after_save_part(sender, instance: Part, created, **kwargs): Function to be executed after a Part is saved """ - if not created: + trigger_event('part.saved', part_id=instance.pk) + + if created: + trigger_event('part.created', part_id=instance.pk) + else: # Check part stock only if we are *updating* the part (not creating it) # Run this check in the background diff --git a/InvenTree/plugin/builtin/integration/mixins.py b/InvenTree/plugin/builtin/integration/mixins.py index 68288121e2..dff749af53 100644 --- a/InvenTree/plugin/builtin/integration/mixins.py +++ b/InvenTree/plugin/builtin/integration/mixins.py @@ -189,10 +189,10 @@ class EventMixin: which provide pairs of 'event':'function' Notes: - + Events are called by name, and based on the django signal nomenclature, e.g. 'part.pre_save' - + Receiving functions must be prototyped to match the 'event' they receive. Example: diff --git a/InvenTree/plugin/events.py b/InvenTree/plugin/events.py index 703fe71d9c..bd3bf7c06c 100644 --- a/InvenTree/plugin/events.py +++ b/InvenTree/plugin/events.py @@ -8,11 +8,14 @@ from __future__ import unicode_literals import logging from django.conf import settings +from django.db import transaction from common.models import InvenTreeSetting from InvenTree.tasks import offload_task +from plugin.registry import plugin_registry + logger = logging.getLogger('inventree') @@ -38,12 +41,30 @@ def trigger_event(event, *args, **kwargs): def process_event(event, *args, **kwargs): """ Respond to a triggered event. - + This function is run by the background worker process. + + This function may queue multiple functions to be handled by the background worker. """ logger.info(f"Processing event '{event}'") # Determine if there are any plugins which are interested in responding if settings.PLUGIN_TESTING or InvenTreeSetting.get_setting('ENABLE_PLUGINS_EVENTS'): - pass + + # Run atomically, to ensure that either *all* tasks are registered, or *none* + with transaction.atomic(): + for slug, callbacks in plugin_registry.mixins_events.items(): + # slug = plugin slug + # callbacks = list of (event, function) tuples + + for _event, _func in callbacks: + if _event == event: + + logger.info(f"Running task '{_func}' for plugin '{slug}'") + + offload_task( + _func, + *args, + **kwargs + ) diff --git a/InvenTree/plugin/plugin.py b/InvenTree/plugin/plugin.py index 35643b36c3..8842553ba7 100644 --- a/InvenTree/plugin/plugin.py +++ b/InvenTree/plugin/plugin.py @@ -63,3 +63,15 @@ class InvenTreePlugin(): raise error return cfg + + def is_active(self): + """ + Return True if this plugin is currently active + """ + + cfg = self.plugin_config() + + if cfg: + return cfg.active + else: + return False diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index b7e37d22ba..5e6016fc86 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -56,8 +56,10 @@ class PluginsRegistry: # integration specific self.installed_apps = [] # Holds all added plugin_paths + # mixins self.mixins_settings = {} + self.mixins_events = {} # region public plugin functions def load_plugins(self): @@ -265,6 +267,7 @@ class PluginsRegistry: logger.info(f'Found {len(plugins)} active plugins') self.activate_integration_settings(plugins) + self.activate_integration_events(plugins) self.activate_integration_schedule(plugins) self.activate_integration_app(plugins, force_reload=force_reload) @@ -275,6 +278,7 @@ class PluginsRegistry: self.deactivate_integration_app() self.deactivate_integration_schedule() + self.deactivate_integration_events() self.deactivate_integration_settings() def activate_integration_settings(self, plugins): @@ -299,6 +303,41 @@ class PluginsRegistry: # clear cache self.mixins_settings = {} + def activate_integration_events(self, plugins): + """ + Activate triggered events mixin for applicable plugins + """ + + logger.info('Activating plugin events') + + from common.models import InvenTreeSetting + + self.mixins_events = {} + + event_count = 0 + + if settings.PLUGIN_TESTING or InvenTreeSetting.get_setting('ENABLE_PLUGINS_EVENTS'): + + for slug, plugin in plugins: + + if plugin.mixin_enabled('events'): + config = plugin.plugin_config() + + # Only activate events for plugins which are enabled + if config and config.active: + self.mixins_events[slug] = plugin.events + + event_count += len(plugin.events) + + if event_count > 0: + logger.info(f"Registered callbacks for {event_count} events") + + def deactivate_integration_events(self): + """ + Deactivate events for all plugins + """ + self.mixins_events = {} + def activate_integration_schedule(self, plugins): logger.info('Activating plugin tasks') diff --git a/InvenTree/plugin/samples/integration/event_sample.py b/InvenTree/plugin/samples/integration/event_sample.py new file mode 100644 index 0000000000..48516cfa3c --- /dev/null +++ b/InvenTree/plugin/samples/integration/event_sample.py @@ -0,0 +1,29 @@ +""" +Sample plugin which responds to events +""" + +from plugin import IntegrationPluginBase +from plugin.mixins import EventMixin + + +def on_part_saved(*args, **kwargs): + """ + Simple function which responds to a triggered event + """ + + part_id = kwargs['part_id'] + print(f"func on_part_saved() - part: {part_id}") + + +class EventPlugin(EventMixin, IntegrationPluginBase): + """ + A sample plugin which provides supports for triggered events + """ + + PLUGIN_NAME = "EventPlugin" + PLUGIN_SLUG = "event" + PLUGIN_TITLE = "Triggered Events" + + EVENTS = [ + ('part.saved', 'plugin.samples.integration.event_sample.on_part_saved'), + ] diff --git a/InvenTree/plugin/samples/integration/scheduled_task.py b/InvenTree/plugin/samples/integration/scheduled_task.py index 5a8f866cd7..7a08a21287 100644 --- a/InvenTree/plugin/samples/integration/scheduled_task.py +++ b/InvenTree/plugin/samples/integration/scheduled_task.py @@ -32,7 +32,7 @@ class ScheduledTaskPlugin(ScheduleMixin, IntegrationPluginBase): 'hello': { 'func': 'plugin.samples.integration.scheduled_task.print_hello', 'schedule': 'I', - 'minutes': 5, + 'minutes': 45, }, 'world': { 'func': 'plugin.samples.integration.scheduled_task.print_hello', diff --git a/InvenTree/templates/InvenTree/settings/plugin_settings.html b/InvenTree/templates/InvenTree/settings/plugin_settings.html index 59178300ac..a670d71c34 100644 --- a/InvenTree/templates/InvenTree/settings/plugin_settings.html +++ b/InvenTree/templates/InvenTree/settings/plugin_settings.html @@ -90,7 +90,7 @@ - + From 9e2250e9b80ea1216467db371e42ce37b6430365 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 8 Jan 2022 13:19:16 +1100 Subject: [PATCH 09/50] Bug fixes for settings --- InvenTree/common/models.py | 7 ------- InvenTree/templates/InvenTree/settings/report.html | 2 +- InvenTree/templates/InvenTree/settings/stock.html | 1 - 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index d361cb5bb1..50c95966cc 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -872,13 +872,6 @@ class InvenTreeSetting(BaseInvenTreeSetting): 'validator': bool, }, - 'STOCK_GROUP_BY_PART': { - 'name': _('Group by Part'), - 'description': _('Group stock items by part reference in table views'), - 'default': True, - 'validator': bool, - }, - 'BUILDORDER_REFERENCE_PREFIX': { 'name': _('Build Order Reference Prefix'), 'description': _('Prefix value for build order reference'), diff --git a/InvenTree/templates/InvenTree/settings/report.html b/InvenTree/templates/InvenTree/settings/report.html index 54c7175508..a25ace23ce 100644 --- a/InvenTree/templates/InvenTree/settings/report.html +++ b/InvenTree/templates/InvenTree/settings/report.html @@ -12,7 +12,7 @@
{% trans "Installation path" %} {{ plugin.package_path }}
- {% include "InvenTree/settings/setting.html" with key="REPORT_ENABLE" icon="file-pdf" %} + {% include "InvenTree/settings/setting.html" with key="REPORT_ENABLE" icon="fa-file-pdf" %} {% include "InvenTree/settings/setting.html" with key="REPORT_DEFAULT_PAGE_SIZE" icon="fa-print" %} {% include "InvenTree/settings/setting.html" with key="REPORT_DEBUG_MODE" icon="fa-laptop-code" %} {% include "InvenTree/settings/setting.html" with key="REPORT_ENABLE_TEST_REPORT" icon="fa-vial" %} diff --git a/InvenTree/templates/InvenTree/settings/stock.html b/InvenTree/templates/InvenTree/settings/stock.html index d5455750d7..a3c0940c1f 100644 --- a/InvenTree/templates/InvenTree/settings/stock.html +++ b/InvenTree/templates/InvenTree/settings/stock.html @@ -11,7 +11,6 @@
- {% include "InvenTree/settings/setting.html" with key="STOCK_GROUP_BY_PART" icon="fa-layer-group" %} {% include "InvenTree/settings/setting.html" with key="STOCK_ENABLE_EXPIRY" icon="fa-stopwatch" %} {% include "InvenTree/settings/setting.html" with key="STOCK_STALE_DAYS" icon="fa-calendar" %} {% include "InvenTree/settings/setting.html" with key="STOCK_ALLOW_EXPIRED_SALE" icon="fa-truck" %} From 137a668452e802f92f4f64797b28a6bf507f1ed1 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 8 Jan 2022 13:29:13 +1100 Subject: [PATCH 10/50] Remove duplicated settings display --- .../templates/InvenTree/settings/user.html | 82 ------------------- 1 file changed, 82 deletions(-) diff --git a/InvenTree/templates/InvenTree/settings/user.html b/InvenTree/templates/InvenTree/settings/user.html index ae04568f53..9bd0dba26e 100644 --- a/InvenTree/templates/InvenTree/settings/user.html +++ b/InvenTree/templates/InvenTree/settings/user.html @@ -214,88 +214,6 @@ -
-
-

{% trans "Theme Settings" %}

-
- -
-
- {% csrf_token %} - - -
- -
- -
-
- -
-
- -
-
-

{% trans "Language Settings" %}

-
- -
-
- {% csrf_token %} - - -
- -
- -
-

{% trans "Some languages are not complete" %} - {% if ALL_LANG %} - . {% trans "Show only sufficent" %} - {% else %} - and hidden. {% trans "Show them too" %} - {% endif %} -

-
- -
-
-

{% trans "Help the translation efforts!" %}

-

{% blocktrans with link="https://crowdin.com/project/inventree" %}Native language translation of the - InvenTree web application is community contributed via crowdin. Contributions are - welcomed and encouraged.{% endblocktrans %}

-
-
-
From 77decc72edb2a074d5507d78d0ddbcf2de629cc8 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 8 Jan 2022 20:07:54 +1100 Subject: [PATCH 11/50] Extra bug fix for part variant form --- InvenTree/config_template.yaml | 9 +++++---- InvenTree/templates/js/translated/part.js | 8 +++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/InvenTree/config_template.yaml b/InvenTree/config_template.yaml index 3472a37d8e..d8f780bb36 100644 --- a/InvenTree/config_template.yaml +++ b/InvenTree/config_template.yaml @@ -14,18 +14,15 @@ database: # --- Available options: --- # ENGINE: Database engine. Selection from: - # - sqlite3 # - mysql # - postgresql + # - sqlite3 # NAME: Database name # USER: Database username (if required) # PASSWORD: Database password (if required) # HOST: Database host address (if required) # PORT: Database host port (if required) - # --- Example Configuration - sqlite3 --- - # ENGINE: sqlite3 - # NAME: '/home/inventree/database.sqlite3' # --- Example Configuration - MySQL --- #ENGINE: mysql @@ -42,6 +39,10 @@ database: #PASSWORD: inventree_password #HOST: 'localhost' #PORT: '5432' + + # --- Example Configuration - sqlite3 --- + # ENGINE: sqlite3 + # NAME: '/home/inventree/database.sqlite3' # Select default system language (default is 'en-us') language: en-us diff --git a/InvenTree/templates/js/translated/part.js b/InvenTree/templates/js/translated/part.js index 3d70b97598..020c5d1a6c 100644 --- a/InvenTree/templates/js/translated/part.js +++ b/InvenTree/templates/js/translated/part.js @@ -345,6 +345,12 @@ function editPart(pk) { // Launch form to duplicate a part function duplicatePart(pk, options={}) { + var title = '{% trans "Duplicate Part" %}'; + + if (options.variant) { + title = '{% trans "Create Part Variant" %}'; + } + // First we need all the part information inventreeGet(`/api/part/${pk}/`, {}, { @@ -372,7 +378,7 @@ function duplicatePart(pk, options={}) { method: 'POST', fields: fields, groups: partGroups(), - title: '{% trans "Duplicate Part" %}', + title: title, data: data, onSuccess: function(data) { // Follow the new part From af18d16d98407146167bbce92205c1c2045a31af Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 8 Jan 2022 20:19:18 +1100 Subject: [PATCH 12/50] Tweak admin area icon --- InvenTree/templates/navbar.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/templates/navbar.html b/InvenTree/templates/navbar.html index 0ee6bb021e..551f1e99ed 100644 --- a/InvenTree/templates/navbar.html +++ b/InvenTree/templates/navbar.html @@ -108,7 +108,7 @@