diff --git a/InvenTree/InvenTree/exchange.py b/InvenTree/InvenTree/exchange.py index c75a827cc7..9981e52ff7 100644 --- a/InvenTree/InvenTree/exchange.py +++ b/InvenTree/InvenTree/exchange.py @@ -1,4 +1,5 @@ from common.settings import currency_code_default, currency_codes +from urllib.error import HTTPError, URLError from djmoney.contrib.exchange.backends.base import SimpleExchangeBackend @@ -26,4 +27,8 @@ class InvenTreeExchange(SimpleExchangeBackend): symbols = ','.join(currency_codes()) - super().update_rates(base=base_currency, symbols=symbols) + try: + super().update_rates(base=base_currency, symbols=symbols) + # catch connection errors + except (HTTPError, URLError): + print('Encountered connection error while updating') diff --git a/InvenTree/InvenTree/management/commands/clean_settings.py b/InvenTree/InvenTree/management/commands/clean_settings.py new file mode 100644 index 0000000000..e0fd09e6c7 --- /dev/null +++ b/InvenTree/InvenTree/management/commands/clean_settings.py @@ -0,0 +1,38 @@ +""" +Custom management command to cleanup old settings that are not defined anymore +""" + +from django.core.management.base import BaseCommand + + +class Command(BaseCommand): + """ + Cleanup old (undefined) settings in the database + """ + + def handle(self, *args, **kwargs): + + print("Collecting settings") + from common.models import InvenTreeSetting, InvenTreeUserSetting + + # general settings + db_settings = InvenTreeSetting.objects.all() + model_settings = InvenTreeSetting.GLOBAL_SETTINGS + + # check if key exist and delete if not + for setting in db_settings: + if setting.key not in model_settings: + setting.delete() + print(f"deleted setting '{setting.key}'") + + # user settings + db_settings = InvenTreeUserSetting.objects.all() + model_settings = InvenTreeUserSetting.GLOBAL_SETTINGS + + # check if key exist and delete if not + for setting in db_settings: + if setting.key not in model_settings: + setting.delete() + print(f"deleted user setting '{setting.key}'") + + print("checked all settings") diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index 54dc21c1b5..5d75a4dd74 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -20,6 +20,7 @@ from djmoney.contrib.exchange.models import convert_money from djmoney.contrib.exchange.exceptions import MissingRate from django.utils.translation import ugettext_lazy as _ +from django.utils.html import format_html from django.core.validators import MinValueValidator, URLValidator from django.core.exceptions import ValidationError @@ -58,12 +59,13 @@ class BaseInvenTreeSetting(models.Model): # Query the database for setting in results: - settings.append({ - "key": setting.key.upper(), - "value": setting.value - }) + if setting.key: + settings.append({ + "key": setting.key.upper(), + "value": setting.value + }) - keys.add(setting.key.upper()) + keys.add(setting.key.upper()) # Specify any "default" values which are not in the database for key in cls.GLOBAL_SETTINGS.keys(): @@ -90,10 +92,10 @@ class BaseInvenTreeSetting(models.Model): # Numerical values remain the same elif cls.validator_is_int(validator): pass - + # Wrap strings with quotes else: - value = f"'{value}'" + value = format_html("'{}'", value) setting["value"] = value diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index d7b196917d..5191399f0a 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -10,132 +10,253 @@ {% block content %} -
+
+ +

{{ part.full_name }}

+
+
+
+ {% include "part/part_thumb.html" %} +
+

+

+ + {% if user.is_staff and roles.part.change %} +   + {% endif %} + +
+ {% if part.is_template %} + + {% endif %} + {% if part.assembly %} + + {% endif %} + {% if part.component %} + + {% endif %} + {% if part.trackable %} + + {% endif %} + {% if part.purchaseable %} + + {% endif %} + {% if part.salable %} + + {% endif %} +
+ + {% if not part.active %} +   +
+ + {% trans 'Inactive' %} +
+ {% endif %} + + {% if part.virtual and part.active %} +   +
+ + {% trans 'Virtual' %} +
+ {% endif %} +

+

-
-
- {% include "part/part_thumb.html" %} -
-

- {{ part.full_name }} - {% if user.is_staff and roles.part.change %} - - {% endif %} - {% if not part.active %} -
- {% trans 'Inactive' %} -
- {% endif %} -

- {% if part.description %} -

{{ part.description }}

- {% endif %} -

-

- {% if part.virtual %} - +
+ + + {% if barcodes %} + + {% endif %} - {% if part.is_template %} - - {% endif %} - {% if part.assembly %} - - {% endif %} - {% if part.component %} - - {% endif %} - {% if part.trackable %} - + {% if part.active %} + + {% if roles.stock.change %} + {% endif %} {% if part.purchaseable %} - - {% endif %} - {% if part.salable %} - - {% endif %} -
-

- -
- - - {% if barcodes %} - - - {% endif %} - {% if part.active %} - - {% if roles.stock.change %} -
- - + {% endif %} + {% endif %} + {% endif %} + + {% if roles.part.add or roles.part.change or roles.part.delete %} +
+ + +
+ {% endif %}
- {% endif %} - {% if part.purchaseable %} - {% if roles.purchase_order.add %} - - {% endif %} - {% endif %} - {% endif %} - - {% if roles.part.add or roles.part.change or roles.part.delete %} -
- - +
+
+
+ {% if part.variant_of %} +
+ {% object_link 'part-detail' part.variant_of.id part.variant_of.full_name as link %} + {% blocktrans %}This part is a variant of {{link}}{% endblocktrans %}
{% endif %}
- + +
+
+ + + + + + + + + + + + {% if on_order > 0 %} + + + + + + {% endif %} + {% if required_build_order_quantity > 0 %} + + + + + {% endif %} + {% if required_sales_order_quantity > 0 %} + + + + + {% endif %} + {% if allocated > 0 %} + + + + + + {% endif %} + + {% if not part.is_template %} + {% if part.assembly %} + + + + + + + + + + {% if quantity_being_built > 0 %} + + + + + + {% endif %} + {% endif %} + {% endif %} +

{% trans "Available Stock" %}

{% decimal available %}{% if part.units %} {{ part.units }}{% endif %}

{% trans "In Stock" %}{% include "part/stock_count.html" %}
{% trans "On Order" %}{% decimal on_order %}
{% trans "Required for Build Orders" %}{% decimal required_build_order_quantity %} +
{% trans "Required for Sales Orders" %}{% decimal required_sales_order_quantity %} +
{% trans "Allocated to Orders" %}{% decimal allocated %}

+

{% trans "Build Status" %}

+
{% trans "Can Build" %}{% decimal part.can_build %}
{% trans "Building" %}{% decimal quantity_being_built %}
+
+
+
+ +

+ + +

+ +
+
+ + + {% if part.IPN %} + + + + + + {% endif %} + + + + + + + + + + + {% if part.revision %} + + + + + + {% endif %} {% if part.keywords %} - + {% endif %} {% if part.link %} - + {% endif %} @@ -154,95 +275,26 @@ - {% endif %} + {% endif %} + {% if part.default_location %} + + + + + + {% endif %} + {% if part.default_supplier %} + + + + + + {% endif %}
{% trans "IPN" %}{{ part.IPN }}{% include "clip.html"%}
{% trans "Name" %}{{ part.name }}{% include "clip.html"%}
{% trans "Description" %}{{ part.description }}{% include "clip.html"%}
{% trans "Revision" %}{{ part.revision }}{% include "clip.html"%}
{% trans "Keywords" %}{{ part.keywords }}{{ part.keywords }}{% include "clip.html"%}
{% trans "External Link" %}{{ part.link }}{{ part.link }}{% include "clip.html"%}
{% trans "Latest Serial Number" %} {{ part.getLatestSerialNumber }}{% include "clip.html"%}
{% trans "Default Location" %}{{ part.default_location }}
{% trans "Default Supplier" %}{{ part.default_supplier }}
-
- -
- {% if part.virtual %} -
- {% trans "This is a virtual part" %} -
- {% endif %} - {% if part.variant_of %} -
- {% object_link 'part-detail' part.variant_of.id part.variant_of.full_name as link %} - {% blocktrans %}This part is a variant of {{link}}{% endblocktrans %} -
- {% endif %} -
-
-
- - - - - - - - - - - - - {% if on_order > 0 %} - - - - - - {% endif %} - {% if required_build_order_quantity > 0 %} - - - - - {% endif %} - {% if required_sales_order_quantity > 0 %} - - - - - {% endif %} - {% if allocated > 0 %} - - - - - - {% endif %} - - {% if not part.is_template %} - {% if part.assembly %} - - - - - - - - - - {% if quantity_being_built > 0 %} - - - - - - {% endif %} - {% endif %} - {% endif %} -
-

{% trans "Available Stock" %}

-

{% decimal available %}{% if part.units %} {{ part.units }}{% endif %}

{% trans "In Stock" %}{% include "part/stock_count.html" %}
{% trans "On Order" %}{% decimal on_order %}
{% trans "Required for Build Orders" %}{% decimal required_build_order_quantity %} -
{% trans "Required for Sales Orders" %}{% decimal required_sales_order_quantity %} -
{% trans "Allocated to Orders" %}{% decimal allocated %}
- {% trans "Build Status" %} -
{% trans "Can Build" %}{% decimal part.can_build %}
{% trans "Building" %}{% decimal quantity_being_built %}
-
+
{% block page_content %} @@ -450,4 +502,42 @@ }); {% endif %} + $("#toggle-part-details").click(function() { + if (this.value == 'show') { + this.innerHTML = ' {% trans "Hide Part Details" %}'; + this.value = 'hide'; + // Store state of part details section + localStorage.setItem("part-details-show", true); + } else { + this.innerHTML = ' {% trans "Show Part Details" %}'; + this.value = 'show'; + // Store state of part details section + localStorage.setItem("part-details-show", false); + } + }); + + // Load part details section + window.onload = function() { + details_show = localStorage.getItem("part-details-show") + + if (details_show === 'true') { + console.log(details_show) + // Get collapsible details section + details = document.getElementById('collapsible-part-details'); + // Add "show" class + details.classList.add("in"); + // Get toggle + toggle = document.getElementById('toggle-part-details'); + // Change state of toggle + toggle.innerHTML = ' {% trans "Hide Part Details" %}'; + toggle.value = 'hide'; + } else { + // Get toggle + toggle = document.getElementById('toggle-part-details'); + // Change state of toggle + toggle.innerHTML = ' {% trans "Show Part Details" %}'; + toggle.value = 'show'; + } + } + {% endblock %} \ No newline at end of file diff --git a/InvenTree/templates/js/dynamic/settings.js b/InvenTree/templates/js/dynamic/settings.js index 4cc824ed6c..ad4e297c4a 100644 --- a/InvenTree/templates/js/dynamic/settings.js +++ b/InvenTree/templates/js/dynamic/settings.js @@ -6,12 +6,12 @@ var user_settings = { {% for setting in USER_SETTINGS %} - {{ setting.key }}: {{ setting.value|safe }}, + {{ setting.key }}: {{ setting.value }}, {% endfor %} }; var global_settings = { {% for setting in GLOBAL_SETTINGS %} - {{ setting.key }}: {{ setting.value|safe }}, + {{ setting.key }}: {{ setting.value }}, {% endfor %} }; \ No newline at end of file diff --git a/tasks.py b/tasks.py index b78a135b08..a9168f4649 100644 --- a/tasks.py +++ b/tasks.py @@ -137,6 +137,14 @@ def rebuild(c): manage(c, "rebuild_models") +@task +def clean_settings(c): + """ + Clean the setting tables of old settings + """ + + manage(c, "clean_settings") + @task def migrate(c): """ @@ -167,7 +175,7 @@ def static(c): manage(c, "collectstatic --no-input") -@task(pre=[install, migrate, static]) +@task(pre=[install, migrate, static, clean_settings]) def update(c): """ Update InvenTree installation.