mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-15 11:35:41 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
@ -1,4 +1,5 @@
|
|||||||
from common.settings import currency_code_default, currency_codes
|
from common.settings import currency_code_default, currency_codes
|
||||||
|
from urllib.error import HTTPError, URLError
|
||||||
|
|
||||||
from djmoney.contrib.exchange.backends.base import SimpleExchangeBackend
|
from djmoney.contrib.exchange.backends.base import SimpleExchangeBackend
|
||||||
|
|
||||||
@ -26,4 +27,8 @@ class InvenTreeExchange(SimpleExchangeBackend):
|
|||||||
|
|
||||||
symbols = ','.join(currency_codes())
|
symbols = ','.join(currency_codes())
|
||||||
|
|
||||||
|
try:
|
||||||
super().update_rates(base=base_currency, symbols=symbols)
|
super().update_rates(base=base_currency, symbols=symbols)
|
||||||
|
# catch connection errors
|
||||||
|
except (HTTPError, URLError):
|
||||||
|
print('Encountered connection error while updating')
|
||||||
|
38
InvenTree/InvenTree/management/commands/clean_settings.py
Normal file
38
InvenTree/InvenTree/management/commands/clean_settings.py
Normal file
@ -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")
|
@ -20,6 +20,7 @@ from djmoney.contrib.exchange.models import convert_money
|
|||||||
from djmoney.contrib.exchange.exceptions import MissingRate
|
from djmoney.contrib.exchange.exceptions import MissingRate
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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.validators import MinValueValidator, URLValidator
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
@ -58,6 +59,7 @@ class BaseInvenTreeSetting(models.Model):
|
|||||||
|
|
||||||
# Query the database
|
# Query the database
|
||||||
for setting in results:
|
for setting in results:
|
||||||
|
if setting.key:
|
||||||
settings.append({
|
settings.append({
|
||||||
"key": setting.key.upper(),
|
"key": setting.key.upper(),
|
||||||
"value": setting.value
|
"value": setting.value
|
||||||
@ -93,7 +95,7 @@ class BaseInvenTreeSetting(models.Model):
|
|||||||
|
|
||||||
# Wrap strings with quotes
|
# Wrap strings with quotes
|
||||||
else:
|
else:
|
||||||
value = f"'{value}'"
|
value = format_html("'{}'", value)
|
||||||
|
|
||||||
setting["value"] = value
|
setting["value"] = value
|
||||||
|
|
||||||
|
@ -10,31 +10,22 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class='panel panel-default panel-inventree'>
|
<div class="panel panel-default panel-inventree">
|
||||||
|
<!-- Default panel contents -->
|
||||||
|
<div class="panel-heading"><h3>{{ part.full_name }}</h3></div>
|
||||||
|
<div class="panel-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
{% include "part/part_thumb.html" %}
|
{% include "part/part_thumb.html" %}
|
||||||
<div class="media-body">
|
<div class="media-body">
|
||||||
<h3>
|
|
||||||
{{ part.full_name }}
|
|
||||||
{% if user.is_staff and roles.part.change %}
|
|
||||||
<a href="{% url 'admin:part_part_change' part.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
|
|
||||||
{% endif %}
|
|
||||||
{% if not part.active %}
|
|
||||||
<div class='label label-large label-large-red'>
|
|
||||||
{% trans 'Inactive' %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</h3>
|
|
||||||
{% if part.description %}
|
|
||||||
<p><em>{{ part.description }}</em></p>
|
|
||||||
{% endif %}
|
|
||||||
<p>
|
<p>
|
||||||
<div id='part-properties' class='btn-group' role='group'>
|
<h3>
|
||||||
{% if part.virtual %}
|
<!-- Admin View -->
|
||||||
<span class='fas fa-ghost' title='{% trans "Part is virtual (not a physical part)" %}'></span>
|
{% if user.is_staff and roles.part.change %}
|
||||||
|
<a href="{% url 'admin:part_part_change' part.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a> 
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<!-- Properties -->
|
||||||
|
<div id='part-properties' class='btn-group' role='group'>
|
||||||
{% if part.is_template %}
|
{% if part.is_template %}
|
||||||
<span class='fas fa-clone' title='{% trans "Part is a template part (variants can be made from this part)" %}'></span>
|
<span class='fas fa-clone' title='{% trans "Part is a template part (variants can be made from this part)" %}'></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -54,6 +45,23 @@
|
|||||||
<span class='fas fa-dollar-sign' title='{% trans "Part can be sold to customers" %}'></span>
|
<span class='fas fa-dollar-sign' title='{% trans "Part can be sold to customers" %}'></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Part active -->
|
||||||
|
{% if not part.active %}
|
||||||
|
 
|
||||||
|
<div class='label label-large label-large-red'>
|
||||||
|
<span class='fas fa-skull-crossbones' title='{% trans "Part is virtual (not a physical part)" %}'></span>
|
||||||
|
{% trans 'Inactive' %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<!-- Part virtual -->
|
||||||
|
{% if part.virtual and part.active %}
|
||||||
|
 
|
||||||
|
<div class='label label-large label-large-yellow'>
|
||||||
|
<span class='fas fa-ghost' title='{% trans "Part is virtual (not a physical part)" %}'></span>
|
||||||
|
{% trans 'Virtual' %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</h3>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class='btn-group action-buttons' role='group'>
|
<div class='btn-group action-buttons' role='group'>
|
||||||
@ -122,51 +130,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<table class='table table-condensed'>
|
|
||||||
<col width='25'>
|
|
||||||
{% if part.keywords %}
|
|
||||||
<tr>
|
|
||||||
<td><span class='fas fa-key'></span></td>
|
|
||||||
<td>{% trans "Keywords" %}</td>
|
|
||||||
<td>{{ part.keywords }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
{% if part.link %}
|
|
||||||
<tr>
|
|
||||||
<td><span class='fas fa-link'></span></td>
|
|
||||||
<td>{% trans "External Link" %}</td>
|
|
||||||
<td><a href="{{ part.link }}">{{ part.link }}</a></td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
<tr>
|
|
||||||
<td><span class='fas fa-calendar-alt'></span></td>
|
|
||||||
<td>{% trans "Creation Date" %}</td>
|
|
||||||
<td>
|
|
||||||
{{ part.creation_date }}
|
|
||||||
{% if part.creation_user %}
|
|
||||||
<span class='badge'>{{ part.creation_user }}</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% if part.trackable and part.getLatestSerialNumber %}
|
|
||||||
<tr>
|
|
||||||
<td><span class='fas fa-hashtag'></span></td>
|
|
||||||
<td>{% trans "Latest Serial Number" %}</td>
|
|
||||||
<td>{{ part.getLatestSerialNumber }}{% include "clip.html"%}</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='info-messages'>
|
<div class='info-messages'>
|
||||||
{% if part.virtual %}
|
|
||||||
<div class='alert alert-warning alert-block'>
|
|
||||||
{% trans "This is a virtual part" %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if part.variant_of %}
|
{% if part.variant_of %}
|
||||||
<div class='alert alert-info alert-block'>
|
<div class='alert alert-info alert-block' style='padding: 10px;'>
|
||||||
{% object_link 'part-detail' part.variant_of.id part.variant_of.full_name as link %}
|
{% object_link 'part-detail' part.variant_of.id part.variant_of.full_name as link %}
|
||||||
{% blocktrans %}This part is a variant of {{link}}{% endblocktrans %}
|
{% blocktrans %}This part is a variant of {{link}}{% endblocktrans %}
|
||||||
</div>
|
</div>
|
||||||
@ -174,13 +142,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<table class="table table-striped">
|
<table class='table table-condensed table-striped'>
|
||||||
<col width='25'>
|
<col width='25'>
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-boxes'></span></td>
|
<td><h4><span class='fas fa-boxes'></span></h4></td>
|
||||||
<td>
|
<td><h4>{% trans "Available Stock" %}</h4></td>
|
||||||
<h4>{% trans "Available Stock" %}</h4>
|
|
||||||
</td>
|
|
||||||
<td><h4>{% decimal available %}{% if part.units %} {{ part.units }}{% endif %}</h4></td>
|
<td><h4>{% decimal available %}{% if part.units %} {{ part.units }}{% endif %}</h4></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -220,9 +186,9 @@
|
|||||||
{% if not part.is_template %}
|
{% if not part.is_template %}
|
||||||
{% if part.assembly %}
|
{% if part.assembly %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-tools'></span></td>
|
<td><h4><span class='fas fa-tools'></span></h4></td>
|
||||||
<td colspan='2'>
|
<td colspan='2'>
|
||||||
<strong>{% trans "Build Status" %}</strong>
|
<h4>{% trans "Build Status" %}</h4>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -242,6 +208,92 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<!-- Details show/hide button -->
|
||||||
|
<button id="toggle-part-details" class="btn btn-primary" data-toggle="collapse" data-target="#collapsible-part-details" value="show">
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="collapse" id="collapsible-part-details">
|
||||||
|
<div class="card card-body">
|
||||||
|
<!-- Details Table -->
|
||||||
|
<table class="table table-striped">
|
||||||
|
<col width='25'>
|
||||||
|
{% if part.IPN %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-tag'></span></td>
|
||||||
|
<td>{% trans "IPN" %}</td>
|
||||||
|
<td>{{ part.IPN }}{% include "clip.html"%}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-shapes'></span></td>
|
||||||
|
<td>{% trans "Name" %}</td>
|
||||||
|
<td>{{ part.name }}{% include "clip.html"%}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-info-circle'></span></td>
|
||||||
|
<td>{% trans "Description" %}</td>
|
||||||
|
<td>{{ part.description }}{% include "clip.html"%}</td>
|
||||||
|
</tr>
|
||||||
|
{% if part.revision %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-code-branch'></span></td>
|
||||||
|
<td>{% trans "Revision" %}</td>
|
||||||
|
<td>{{ part.revision }}{% include "clip.html"%}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% if part.keywords %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-key'></span></td>
|
||||||
|
<td>{% trans "Keywords" %}</td>
|
||||||
|
<td>{{ part.keywords }}{% include "clip.html"%}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% if part.link %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-link'></span></td>
|
||||||
|
<td>{% trans "External Link" %}</td>
|
||||||
|
<td><a href="{{ part.link }}">{{ part.link }}</a>{% include "clip.html"%}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-calendar-alt'></span></td>
|
||||||
|
<td>{% trans "Creation Date" %}</td>
|
||||||
|
<td>
|
||||||
|
{{ part.creation_date }}
|
||||||
|
{% if part.creation_user %}
|
||||||
|
<span class='badge'>{{ part.creation_user }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% if part.trackable and part.getLatestSerialNumber %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-hashtag'></span></td>
|
||||||
|
<td>{% trans "Latest Serial Number" %}</td>
|
||||||
|
<td>{{ part.getLatestSerialNumber }}{% include "clip.html"%}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% if part.default_location %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-search-location'></span></td>
|
||||||
|
<td>{% trans "Default Location" %}</td>
|
||||||
|
<td>{{ part.default_location }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% if part.default_supplier %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-building'></span></td>
|
||||||
|
<td>{% trans "Default Supplier" %}</td>
|
||||||
|
<td>{{ part.default_supplier }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -450,4 +502,42 @@
|
|||||||
});
|
});
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
$("#toggle-part-details").click(function() {
|
||||||
|
if (this.value == 'show') {
|
||||||
|
this.innerHTML = '<span class="fas fa-chevron-up"></span> {% trans "Hide Part Details" %}';
|
||||||
|
this.value = 'hide';
|
||||||
|
// Store state of part details section
|
||||||
|
localStorage.setItem("part-details-show", true);
|
||||||
|
} else {
|
||||||
|
this.innerHTML = '<span class="fas fa-chevron-down"></span> {% 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 = '<span class="fas fa-chevron-up"></span> {% trans "Hide Part Details" %}';
|
||||||
|
toggle.value = 'hide';
|
||||||
|
} else {
|
||||||
|
// Get toggle
|
||||||
|
toggle = document.getElementById('toggle-part-details');
|
||||||
|
// Change state of toggle
|
||||||
|
toggle.innerHTML = '<span class="fas fa-chevron-down"></span> {% trans "Show Part Details" %}';
|
||||||
|
toggle.value = 'show';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -6,12 +6,12 @@
|
|||||||
|
|
||||||
var user_settings = {
|
var user_settings = {
|
||||||
{% for setting in USER_SETTINGS %}
|
{% for setting in USER_SETTINGS %}
|
||||||
{{ setting.key }}: {{ setting.value|safe }},
|
{{ setting.key }}: {{ setting.value }},
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
};
|
};
|
||||||
|
|
||||||
var global_settings = {
|
var global_settings = {
|
||||||
{% for setting in GLOBAL_SETTINGS %}
|
{% for setting in GLOBAL_SETTINGS %}
|
||||||
{{ setting.key }}: {{ setting.value|safe }},
|
{{ setting.key }}: {{ setting.value }},
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
};
|
};
|
10
tasks.py
10
tasks.py
@ -137,6 +137,14 @@ def rebuild(c):
|
|||||||
|
|
||||||
manage(c, "rebuild_models")
|
manage(c, "rebuild_models")
|
||||||
|
|
||||||
|
@task
|
||||||
|
def clean_settings(c):
|
||||||
|
"""
|
||||||
|
Clean the setting tables of old settings
|
||||||
|
"""
|
||||||
|
|
||||||
|
manage(c, "clean_settings")
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def migrate(c):
|
def migrate(c):
|
||||||
"""
|
"""
|
||||||
@ -167,7 +175,7 @@ def static(c):
|
|||||||
manage(c, "collectstatic --no-input")
|
manage(c, "collectstatic --no-input")
|
||||||
|
|
||||||
|
|
||||||
@task(pre=[install, migrate, static])
|
@task(pre=[install, migrate, static, clean_settings])
|
||||||
def update(c):
|
def update(c):
|
||||||
"""
|
"""
|
||||||
Update InvenTree installation.
|
Update InvenTree installation.
|
||||||
|
Reference in New Issue
Block a user