mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-30 04:26:44 +00:00
Merge branch 'master' of github.com:inventree/InvenTree into exchange_rate_task
This commit is contained in:
commit
1b79ef940e
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -7,5 +7,5 @@
|
|||||||
*.yml text
|
*.yml text
|
||||||
*.yaml text
|
*.yaml text
|
||||||
*.conf text
|
*.conf text
|
||||||
*.sh text
|
*.sh text eol=lf
|
||||||
*.js text
|
*.js text
|
28
.github/workflows/javascript.yaml
vendored
Normal file
28
.github/workflows/javascript.yaml
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Check javascript template files
|
||||||
|
|
||||||
|
name: Javascript Templates
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches-ignore:
|
||||||
|
- l10*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
javascript:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Check Files
|
||||||
|
run: |
|
||||||
|
cd ci
|
||||||
|
python check_js_templates.py
|
||||||
|
|
@ -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')
|
||||||
|
@ -56,7 +56,7 @@ class InvenTreeModelMoneyField(ModelMoneyField):
|
|||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
# detect if creating migration
|
# detect if creating migration
|
||||||
if 'makemigrations' in sys.argv:
|
if 'migrate' in sys.argv or 'makemigrations' in sys.argv:
|
||||||
# remove currency information for a clean migration
|
# remove currency information for a clean migration
|
||||||
kwargs['default_currency'] = ''
|
kwargs['default_currency'] = ''
|
||||||
kwargs['currency_choices'] = []
|
kwargs['currency_choices'] = []
|
||||||
|
@ -13,7 +13,6 @@ from crispy_forms.helper import FormHelper
|
|||||||
from crispy_forms.layout import Layout, Field
|
from crispy_forms.layout import Layout, Field
|
||||||
from crispy_forms.bootstrap import PrependedText, AppendedText, PrependedAppendedText, StrictButton, Div
|
from crispy_forms.bootstrap import PrependedText, AppendedText, PrependedAppendedText, StrictButton, Div
|
||||||
|
|
||||||
from common.models import ColorTheme
|
|
||||||
from part.models import PartCategory
|
from part.models import PartCategory
|
||||||
|
|
||||||
|
|
||||||
@ -177,39 +176,6 @@ class SetPasswordForm(HelperForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ColorThemeSelectForm(forms.ModelForm):
|
|
||||||
""" Form for setting color theme """
|
|
||||||
|
|
||||||
name = forms.ChoiceField(choices=(), required=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = ColorTheme
|
|
||||||
fields = [
|
|
||||||
'name'
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(ColorThemeSelectForm, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# Populate color themes choices
|
|
||||||
self.fields['name'].choices = ColorTheme.get_color_themes_choices()
|
|
||||||
|
|
||||||
self.helper = FormHelper()
|
|
||||||
# Form rendering
|
|
||||||
self.helper.form_show_labels = False
|
|
||||||
self.helper.layout = Layout(
|
|
||||||
Div(
|
|
||||||
Div(Field('name'),
|
|
||||||
css_class='col-sm-6',
|
|
||||||
style='width: 200px;'),
|
|
||||||
Div(StrictButton(_('Apply Theme'), css_class='btn btn-primary', type='submit'),
|
|
||||||
css_class='col-sm-6',
|
|
||||||
style='width: auto;'),
|
|
||||||
css_class='row',
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SettingCategorySelectForm(forms.ModelForm):
|
class SettingCategorySelectForm(forms.ModelForm):
|
||||||
""" Form for setting category settings """
|
""" Form for setting category settings """
|
||||||
|
|
||||||
|
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")
|
@ -12,6 +12,7 @@ database setup in this file.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
@ -202,7 +203,7 @@ STATICFILES_DIRS = [
|
|||||||
|
|
||||||
# Translated Template settings
|
# Translated Template settings
|
||||||
STATICFILES_I18_PREFIX = 'i18n'
|
STATICFILES_I18_PREFIX = 'i18n'
|
||||||
STATICFILES_I18_SRC = os.path.join(BASE_DIR, 'templates', 'js')
|
STATICFILES_I18_SRC = os.path.join(BASE_DIR, 'templates', 'js', 'translated')
|
||||||
STATICFILES_I18_TRG = STATICFILES_DIRS[0] + '_' + STATICFILES_I18_PREFIX
|
STATICFILES_I18_TRG = STATICFILES_DIRS[0] + '_' + STATICFILES_I18_PREFIX
|
||||||
STATICFILES_DIRS.append(STATICFILES_I18_TRG)
|
STATICFILES_DIRS.append(STATICFILES_I18_TRG)
|
||||||
STATICFILES_I18_TRG = os.path.join(STATICFILES_I18_TRG, STATICFILES_I18_PREFIX)
|
STATICFILES_I18_TRG = os.path.join(STATICFILES_I18_TRG, STATICFILES_I18_PREFIX)
|
||||||
@ -347,10 +348,22 @@ REST_FRAMEWORK = {
|
|||||||
|
|
||||||
WSGI_APPLICATION = 'InvenTree.wsgi.application'
|
WSGI_APPLICATION = 'InvenTree.wsgi.application'
|
||||||
|
|
||||||
|
background_workers = os.environ.get('INVENTREE_BACKGROUND_WORKERS', None)
|
||||||
|
|
||||||
|
if background_workers is not None:
|
||||||
|
try:
|
||||||
|
background_workers = int(background_workers)
|
||||||
|
except ValueError:
|
||||||
|
background_workers = None
|
||||||
|
|
||||||
|
if background_workers is None:
|
||||||
|
# Sensible default?
|
||||||
|
background_workers = 4
|
||||||
|
|
||||||
# django-q configuration
|
# django-q configuration
|
||||||
Q_CLUSTER = {
|
Q_CLUSTER = {
|
||||||
'name': 'InvenTree',
|
'name': 'InvenTree',
|
||||||
'workers': 4,
|
'workers': background_workers,
|
||||||
'timeout': 90,
|
'timeout': 90,
|
||||||
'retry': 120,
|
'retry': 120,
|
||||||
'queue_limit': 50,
|
'queue_limit': 50,
|
||||||
@ -502,11 +515,24 @@ LANGUAGE_CODE = CONFIG.get('language', 'en-us')
|
|||||||
|
|
||||||
# If a new language translation is supported, it must be added here
|
# If a new language translation is supported, it must be added here
|
||||||
LANGUAGES = [
|
LANGUAGES = [
|
||||||
('en', _('English')),
|
|
||||||
('fr', _('French')),
|
|
||||||
('de', _('German')),
|
('de', _('German')),
|
||||||
|
('el', _('Greek')),
|
||||||
|
('en', _('English')),
|
||||||
|
('es', _('Spanish')),
|
||||||
|
('fr', _('French')),
|
||||||
|
('he', _('Hebrew')),
|
||||||
|
('it', _('Italian')),
|
||||||
|
('ja', _('Japanese')),
|
||||||
|
('ko', _('Korean')),
|
||||||
|
('nl', _('Dutch')),
|
||||||
|
('no', _('Norwegian')),
|
||||||
('pl', _('Polish')),
|
('pl', _('Polish')),
|
||||||
|
('ru', _('Russian')),
|
||||||
|
('sv', _('Swedish')),
|
||||||
|
('th', _('Thai')),
|
||||||
('tr', _('Turkish')),
|
('tr', _('Turkish')),
|
||||||
|
('vi', _('Vietnamese')),
|
||||||
|
('zh-cn', _('Chinese')),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Currencies available for use
|
# Currencies available for use
|
||||||
|
@ -39,11 +39,11 @@ from rest_framework.documentation import include_docs_urls
|
|||||||
from .views import auth_request
|
from .views import auth_request
|
||||||
from .views import IndexView, SearchView, DatabaseStatsView
|
from .views import IndexView, SearchView, DatabaseStatsView
|
||||||
from .views import SettingsView, EditUserView, SetPasswordView
|
from .views import SettingsView, EditUserView, SetPasswordView
|
||||||
from .views import CurrencySettingsView, CurrencyRefreshView
|
from .views import CurrencyRefreshView
|
||||||
from .views import AppearanceSelectView, SettingCategorySelectView
|
from .views import AppearanceSelectView, SettingCategorySelectView
|
||||||
from .views import DynamicJsView
|
from .views import DynamicJsView
|
||||||
|
|
||||||
from common.views import SettingEdit
|
from common.views import SettingEdit, UserSettingEdit
|
||||||
|
|
||||||
from .api import InfoView, NotFoundView
|
from .api import InfoView, NotFoundView
|
||||||
from .api import ActionPluginView
|
from .api import ActionPluginView
|
||||||
@ -79,49 +79,47 @@ apipatterns = [
|
|||||||
|
|
||||||
settings_urls = [
|
settings_urls = [
|
||||||
|
|
||||||
url(r'^user/?', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings-user'),
|
|
||||||
url(r'^appearance/?', AppearanceSelectView.as_view(), name='settings-appearance'),
|
|
||||||
url(r'^i18n/?', include('django.conf.urls.i18n')),
|
url(r'^i18n/?', include('django.conf.urls.i18n')),
|
||||||
|
|
||||||
url(r'^global/', SettingsView.as_view(template_name='InvenTree/settings/global.html'), name='settings-global'),
|
url(r'^appearance/?', AppearanceSelectView.as_view(), name='settings-appearance'),
|
||||||
url(r'^report/', SettingsView.as_view(template_name='InvenTree/settings/report.html'), name='settings-report'),
|
|
||||||
url(r'^category/', SettingCategorySelectView.as_view(), name='settings-category'),
|
|
||||||
url(r'^part/', SettingsView.as_view(template_name='InvenTree/settings/part.html'), name='settings-part'),
|
|
||||||
url(r'^stock/', SettingsView.as_view(template_name='InvenTree/settings/stock.html'), name='settings-stock'),
|
|
||||||
url(r'^build/', SettingsView.as_view(template_name='InvenTree/settings/build.html'), name='settings-build'),
|
|
||||||
url(r'^purchase-order/', SettingsView.as_view(template_name='InvenTree/settings/po.html'), name='settings-po'),
|
|
||||||
url(r'^sales-order/', SettingsView.as_view(template_name='InvenTree/settings/so.html'), name='settings-so'),
|
|
||||||
url(r'^currencies/', CurrencySettingsView.as_view(), name='settings-currencies'),
|
|
||||||
url(r'^currencies-refresh/', CurrencyRefreshView.as_view(), name='settings-currencies-refresh'),
|
url(r'^currencies-refresh/', CurrencyRefreshView.as_view(), name='settings-currencies-refresh'),
|
||||||
|
|
||||||
|
url(r'^category/', SettingCategorySelectView.as_view(), name='settings-category'),
|
||||||
|
|
||||||
|
url(r'^(?P<pk>\d+)/edit/user', UserSettingEdit.as_view(), name='user-setting-edit'),
|
||||||
url(r'^(?P<pk>\d+)/edit/', SettingEdit.as_view(), name='setting-edit'),
|
url(r'^(?P<pk>\d+)/edit/', SettingEdit.as_view(), name='setting-edit'),
|
||||||
|
|
||||||
# Catch any other urls
|
# Catch any other urls
|
||||||
url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings'),
|
url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/settings.html'), name='settings'),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Some javascript files are served 'dynamically', allowing them to pass through the Django translation layer
|
# These javascript files are served "dynamically" - i.e. rendered on demand
|
||||||
dynamic_javascript_urls = [
|
dynamic_javascript_urls = [
|
||||||
url(r'^api.js', DynamicJsView.as_view(template_name='js/api.js'), name='api.js'),
|
url(r'^inventree.js', DynamicJsView.as_view(template_name='js/dynamic/inventree.js'), name='inventree.js'),
|
||||||
url(r'^attachment.js', DynamicJsView.as_view(template_name='js/attachment.js'), name='attachment.js'),
|
url(r'^calendar.js', DynamicJsView.as_view(template_name='js/dynamic/calendar.js'), name='calendar.js'),
|
||||||
url(r'^barcode.js', DynamicJsView.as_view(template_name='js/barcode.js'), name='barcode.js'),
|
url(r'^nav.js', DynamicJsView.as_view(template_name='js/dynamic/nav.js'), name='nav.js'),
|
||||||
url(r'^bom.js', DynamicJsView.as_view(template_name='js/bom.js'), name='bom.js'),
|
url(r'^settings.js', DynamicJsView.as_view(template_name='js/dynamic/settings.js'), name='settings.js'),
|
||||||
url(r'^build.js', DynamicJsView.as_view(template_name='js/build.js'), name='build.js'),
|
]
|
||||||
url(r'^calendar.js', DynamicJsView.as_view(template_name='js/calendar.js'), name='calendar.js'),
|
|
||||||
url(r'^company.js', DynamicJsView.as_view(template_name='js/company.js'), name='company.js'),
|
# These javascript files are pased through the Django translation layer
|
||||||
url(r'^filters.js', DynamicJsView.as_view(template_name='js/filters.js'), name='filters.js'),
|
translated_javascript_urls = [
|
||||||
url(r'^forms.js', DynamicJsView.as_view(template_name='js/forms.js'), name='forms.js'),
|
url(r'^api.js', DynamicJsView.as_view(template_name='js/translated/api.js'), name='api.js'),
|
||||||
url(r'^inventree.js', DynamicJsView.as_view(template_name='js/inventree.js'), name='inventree.js'),
|
url(r'^attachment.js', DynamicJsView.as_view(template_name='js/translated/attachment.js'), name='attachment.js'),
|
||||||
url(r'^label.js', DynamicJsView.as_view(template_name='js/label.js'), name='label.js'),
|
url(r'^barcode.js', DynamicJsView.as_view(template_name='js/translated/barcode.js'), name='barcode.js'),
|
||||||
url(r'^model_renderers.js', DynamicJsView.as_view(template_name='js/model_renderers.js'), name='model_renderers.js'),
|
url(r'^bom.js', DynamicJsView.as_view(template_name='js/translated/bom.js'), name='bom.js'),
|
||||||
url(r'^modals.js', DynamicJsView.as_view(template_name='js/modals.js'), name='modals.js'),
|
url(r'^build.js', DynamicJsView.as_view(template_name='js/translated/build.js'), name='build.js'),
|
||||||
url(r'^nav.js', DynamicJsView.as_view(template_name='js/nav.js'), name='nav.js'),
|
url(r'^company.js', DynamicJsView.as_view(template_name='js/translated/company.js'), name='company.js'),
|
||||||
url(r'^order.js', DynamicJsView.as_view(template_name='js/order.js'), name='order.js'),
|
url(r'^filters.js', DynamicJsView.as_view(template_name='js/translated/filters.js'), name='filters.js'),
|
||||||
url(r'^part.js', DynamicJsView.as_view(template_name='js/part.js'), name='part.js'),
|
url(r'^forms.js', DynamicJsView.as_view(template_name='js/translated/forms.js'), name='forms.js'),
|
||||||
url(r'^report.js', DynamicJsView.as_view(template_name='js/report.js'), name='report.js'),
|
url(r'^label.js', DynamicJsView.as_view(template_name='js/translated/label.js'), name='label.js'),
|
||||||
url(r'^stock.js', DynamicJsView.as_view(template_name='js/stock.js'), name='stock.js'),
|
url(r'^model_renderers.js', DynamicJsView.as_view(template_name='js/translated/model_renderers.js'), name='model_renderers.js'),
|
||||||
url(r'^tables.js', DynamicJsView.as_view(template_name='js/tables.js'), name='tables.js'),
|
url(r'^modals.js', DynamicJsView.as_view(template_name='js/translated/modals.js'), name='modals.js'),
|
||||||
url(r'^table_filters.js', DynamicJsView.as_view(template_name='js/table_filters.js'), name='table_filters.js'),
|
url(r'^order.js', DynamicJsView.as_view(template_name='js/translated/order.js'), name='order.js'),
|
||||||
|
url(r'^part.js', DynamicJsView.as_view(template_name='js/translated/part.js'), name='part.js'),
|
||||||
|
url(r'^report.js', DynamicJsView.as_view(template_name='js/translated/report.js'), name='report.js'),
|
||||||
|
url(r'^stock.js', DynamicJsView.as_view(template_name='js/translated/stock.js'), name='stock.js'),
|
||||||
|
url(r'^tables.js', DynamicJsView.as_view(template_name='js/translated/tables.js'), name='tables.js'),
|
||||||
|
url(r'^table_filters.js', DynamicJsView.as_view(template_name='js/translated/table_filters.js'), name='table_filters.js'),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -130,7 +128,8 @@ urlpatterns = [
|
|||||||
url(r'^supplier-part/', include(supplier_part_urls)),
|
url(r'^supplier-part/', include(supplier_part_urls)),
|
||||||
|
|
||||||
# "Dynamic" javascript files which are rendered using InvenTree templating.
|
# "Dynamic" javascript files which are rendered using InvenTree templating.
|
||||||
url(r'^dynamic/', include(dynamic_javascript_urls)),
|
url(r'^js/dynamic/', include(dynamic_javascript_urls)),
|
||||||
|
url(r'^js/i18n/', include(translated_javascript_urls)),
|
||||||
|
|
||||||
url(r'^common/', include(common_urls)),
|
url(r'^common/', include(common_urls)),
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import re
|
|||||||
|
|
||||||
import common.models
|
import common.models
|
||||||
|
|
||||||
INVENTREE_SW_VERSION = "0.3.1"
|
INVENTREE_SW_VERSION = "0.5.0 pre"
|
||||||
|
|
||||||
INVENTREE_API_VERSION = 8
|
INVENTREE_API_VERSION = 8
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
|
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
|
||||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ from common.models import InvenTreeSetting, ColorTheme
|
|||||||
from users.models import check_user_role, RuleSet
|
from users.models import check_user_role, RuleSet
|
||||||
|
|
||||||
from .forms import DeleteForm, EditUserForm, SetPasswordForm
|
from .forms import DeleteForm, EditUserForm, SetPasswordForm
|
||||||
from .forms import ColorThemeSelectForm, SettingCategorySelectForm
|
from .forms import SettingCategorySelectForm
|
||||||
from .helpers import str2bool
|
from .helpers import str2bool
|
||||||
|
|
||||||
from rest_framework import views
|
from rest_framework import views
|
||||||
@ -777,7 +778,7 @@ class SettingsView(TemplateView):
|
|||||||
""" View for configuring User settings
|
""" View for configuring User settings
|
||||||
"""
|
"""
|
||||||
|
|
||||||
template_name = "InvenTree/settings.html"
|
template_name = "InvenTree/settings/settings.html"
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
|
||||||
@ -785,6 +786,20 @@ class SettingsView(TemplateView):
|
|||||||
|
|
||||||
ctx['settings'] = InvenTreeSetting.objects.all().order_by('key')
|
ctx['settings'] = InvenTreeSetting.objects.all().order_by('key')
|
||||||
|
|
||||||
|
ctx["base_currency"] = currency_code_default()
|
||||||
|
ctx["currencies"] = currency_codes
|
||||||
|
|
||||||
|
ctx["rates"] = Rate.objects.filter(backend="InvenTreeExchange")
|
||||||
|
|
||||||
|
ctx["categories"] = PartCategory.objects.all().order_by('tree_id', 'lft', 'name')
|
||||||
|
|
||||||
|
# When were the rates last updated?
|
||||||
|
try:
|
||||||
|
backend = ExchangeBackend.objects.get(name='InvenTreeExchange')
|
||||||
|
ctx["rates_updated"] = backend.last_update
|
||||||
|
except:
|
||||||
|
ctx["rates_updated"] = None
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
@ -808,43 +823,12 @@ class CurrencyRefreshView(RedirectView):
|
|||||||
# Run it
|
# Run it
|
||||||
run_task(taskname)
|
run_task(taskname)
|
||||||
|
|
||||||
return self.get(request, *args, **kwargs)
|
return redirect(reverse_lazy('settings'))
|
||||||
|
|
||||||
|
|
||||||
class CurrencySettingsView(TemplateView):
|
class AppearanceSelectView(RedirectView):
|
||||||
"""
|
|
||||||
View for configuring currency settings
|
|
||||||
"""
|
|
||||||
|
|
||||||
template_name = "InvenTree/settings/currencies.html"
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
|
|
||||||
ctx = super().get_context_data(**kwargs).copy()
|
|
||||||
|
|
||||||
ctx['settings'] = InvenTreeSetting.objects.all().order_by('key')
|
|
||||||
ctx["base_currency"] = currency_code_default()
|
|
||||||
ctx["currencies"] = currency_codes
|
|
||||||
|
|
||||||
ctx["rates"] = Rate.objects.filter(backend="InvenTreeExchange")
|
|
||||||
|
|
||||||
# When were the rates last updated?
|
|
||||||
try:
|
|
||||||
backend = ExchangeBackend.objects.get(name='InvenTreeExchange')
|
|
||||||
ctx["rates_updated"] = backend.last_update
|
|
||||||
except:
|
|
||||||
ctx["rates_updated"] = None
|
|
||||||
|
|
||||||
return ctx
|
|
||||||
|
|
||||||
|
|
||||||
class AppearanceSelectView(FormView):
|
|
||||||
""" View for selecting a color theme """
|
""" View for selecting a color theme """
|
||||||
|
|
||||||
form_class = ColorThemeSelectForm
|
|
||||||
success_url = reverse_lazy('settings-appearance')
|
|
||||||
template_name = "InvenTree/settings/appearance.html"
|
|
||||||
|
|
||||||
def get_user_theme(self):
|
def get_user_theme(self):
|
||||||
""" Get current user color theme """
|
""" Get current user color theme """
|
||||||
try:
|
try:
|
||||||
@ -854,40 +838,10 @@ class AppearanceSelectView(FormView):
|
|||||||
|
|
||||||
return user_theme
|
return user_theme
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
""" Select current user color theme as initial choice """
|
|
||||||
|
|
||||||
initial = super(AppearanceSelectView, self).get_initial()
|
|
||||||
|
|
||||||
user_theme = self.get_user_theme()
|
|
||||||
if user_theme:
|
|
||||||
initial['name'] = user_theme.name
|
|
||||||
return initial
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
""" Check if current color theme exists, else display alert box """
|
|
||||||
|
|
||||||
context = {}
|
|
||||||
|
|
||||||
form = self.get_form()
|
|
||||||
context['form'] = form
|
|
||||||
|
|
||||||
user_theme = self.get_user_theme()
|
|
||||||
if user_theme:
|
|
||||||
# Check color theme is a valid choice
|
|
||||||
if not ColorTheme.is_valid_choice(user_theme):
|
|
||||||
user_color_theme_name = user_theme.name
|
|
||||||
if not user_color_theme_name:
|
|
||||||
user_color_theme_name = 'default'
|
|
||||||
|
|
||||||
context['invalid_color_theme'] = user_color_theme_name
|
|
||||||
|
|
||||||
return self.render_to_response(context)
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
""" Save user color theme selection """
|
""" Save user color theme selection """
|
||||||
|
|
||||||
form = self.get_form()
|
theme = request.POST.get('theme', None)
|
||||||
|
|
||||||
# Get current user theme
|
# Get current user theme
|
||||||
user_theme = self.get_user_theme()
|
user_theme = self.get_user_theme()
|
||||||
@ -897,20 +851,10 @@ class AppearanceSelectView(FormView):
|
|||||||
user_theme = ColorTheme()
|
user_theme = ColorTheme()
|
||||||
user_theme.user = request.user
|
user_theme.user = request.user
|
||||||
|
|
||||||
if form.is_valid():
|
user_theme.name = theme
|
||||||
theme_selected = form.cleaned_data['name']
|
|
||||||
|
|
||||||
# Set color theme to form selection
|
|
||||||
user_theme.name = theme_selected
|
|
||||||
user_theme.save()
|
user_theme.save()
|
||||||
|
|
||||||
return self.form_valid(form)
|
return redirect(reverse_lazy('settings'))
|
||||||
else:
|
|
||||||
# Set color theme to default
|
|
||||||
user_theme.name = ColorTheme.default_color_theme[0]
|
|
||||||
user_theme.save()
|
|
||||||
|
|
||||||
return self.form_invalid(form)
|
|
||||||
|
|
||||||
|
|
||||||
class SettingCategorySelectView(FormView):
|
class SettingCategorySelectView(FormView):
|
||||||
|
@ -82,7 +82,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
var prefix = '{% settings_value "BUILDORDER_REFERENCE_PREFIX" %}';
|
var prefix = global_settings.BUILDORDER_REFERENCE_PREFIX;
|
||||||
|
|
||||||
for (var idx = 0; idx < response.length; idx++) {
|
for (var idx = 0; idx < response.length; idx++) {
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from django.contrib import admin
|
|||||||
|
|
||||||
from import_export.admin import ImportExportModelAdmin
|
from import_export.admin import ImportExportModelAdmin
|
||||||
|
|
||||||
from .models import InvenTreeSetting
|
from .models import InvenTreeSetting, InvenTreeUserSetting
|
||||||
|
|
||||||
|
|
||||||
class SettingsAdmin(ImportExportModelAdmin):
|
class SettingsAdmin(ImportExportModelAdmin):
|
||||||
@ -13,4 +13,10 @@ class SettingsAdmin(ImportExportModelAdmin):
|
|||||||
list_display = ('key', 'value')
|
list_display = ('key', 'value')
|
||||||
|
|
||||||
|
|
||||||
|
class UserSettingsAdmin(ImportExportModelAdmin):
|
||||||
|
|
||||||
|
list_display = ('key', 'value', 'user', )
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(InvenTreeSetting, SettingsAdmin)
|
admin.site.register(InvenTreeSetting, SettingsAdmin)
|
||||||
|
admin.site.register(InvenTreeUserSetting, UserSettingsAdmin)
|
||||||
|
33
InvenTree/common/migrations/0011_auto_20210722_2114.py
Normal file
33
InvenTree/common/migrations/0011_auto_20210722_2114.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Generated by Django 3.2.4 on 2021-07-22 21:14
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('common', '0010_migrate_currency_setting'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='InvenTreeUserSetting',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('value', models.CharField(blank=True, help_text='Settings value', max_length=200)),
|
||||||
|
('key', models.CharField(help_text='Settings key (must be unique - case insensitive', max_length=50)),
|
||||||
|
('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'InvenTree User Setting',
|
||||||
|
'verbose_name_plural': 'InvenTree User Settings',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='inventreeusersetting',
|
||||||
|
constraint=models.UniqueConstraint(fields=('key', 'user'), name='unique key and user'),
|
||||||
|
),
|
||||||
|
]
|
File diff suppressed because it is too large
Load Diff
@ -45,11 +45,28 @@ class SettingEdit(AjaxUpdateView):
|
|||||||
|
|
||||||
ctx['key'] = setting.key
|
ctx['key'] = setting.key
|
||||||
ctx['value'] = setting.value
|
ctx['value'] = setting.value
|
||||||
ctx['name'] = models.InvenTreeSetting.get_setting_name(setting.key)
|
ctx['name'] = self.model.get_setting_name(setting.key)
|
||||||
ctx['description'] = models.InvenTreeSetting.get_setting_description(setting.key)
|
ctx['description'] = self.model.get_setting_description(setting.key)
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
"""
|
||||||
|
Custom data to return to the client after POST success
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
setting = self.get_object()
|
||||||
|
|
||||||
|
data['pk'] = setting.pk
|
||||||
|
data['key'] = setting.key
|
||||||
|
data['value'] = setting.value
|
||||||
|
data['is_bool'] = setting.is_bool()
|
||||||
|
data['is_int'] = setting.is_int()
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
def get_form(self):
|
def get_form(self):
|
||||||
"""
|
"""
|
||||||
Override default get_form behaviour
|
Override default get_form behaviour
|
||||||
@ -69,12 +86,12 @@ class SettingEdit(AjaxUpdateView):
|
|||||||
self.object.value = str2bool(setting.value)
|
self.object.value = str2bool(setting.value)
|
||||||
form.fields['value'].value = str2bool(setting.value)
|
form.fields['value'].value = str2bool(setting.value)
|
||||||
|
|
||||||
name = models.InvenTreeSetting.get_setting_name(setting.key)
|
name = self.model.get_setting_name(setting.key)
|
||||||
|
|
||||||
if name:
|
if name:
|
||||||
form.fields['value'].label = name
|
form.fields['value'].label = name
|
||||||
|
|
||||||
description = models.InvenTreeSetting.get_setting_description(setting.key)
|
description = self.model.get_setting_description(setting.key)
|
||||||
|
|
||||||
if description:
|
if description:
|
||||||
form.fields['value'].help_text = description
|
form.fields['value'].help_text = description
|
||||||
@ -111,6 +128,18 @@ class SettingEdit(AjaxUpdateView):
|
|||||||
form.add_error('value', _('Supplied value must be a boolean'))
|
form.add_error('value', _('Supplied value must be a boolean'))
|
||||||
|
|
||||||
|
|
||||||
|
class UserSettingEdit(SettingEdit):
|
||||||
|
"""
|
||||||
|
View for editing an InvenTree key:value user settings object,
|
||||||
|
(or creating it if the key does not already exist)
|
||||||
|
"""
|
||||||
|
|
||||||
|
model = models.InvenTreeUserSetting
|
||||||
|
ajax_form_title = _('Change User Setting')
|
||||||
|
form_class = forms.SettingEditForm
|
||||||
|
ajax_template_name = "common/edit_setting.html"
|
||||||
|
|
||||||
|
|
||||||
class MultiStepFormView(SessionWizardView):
|
class MultiStepFormView(SessionWizardView):
|
||||||
""" Setup basic methods of multi-step form
|
""" Setup basic methods of multi-step form
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ Provides a JSON API for the Company app
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
|
from django_filters import rest_framework as rest_filters
|
||||||
|
|
||||||
from rest_framework import filters
|
from rest_framework import filters
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
|
|
||||||
@ -84,6 +86,23 @@ class CompanyDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class ManufacturerPartFilter(rest_filters.FilterSet):
|
||||||
|
"""
|
||||||
|
Custom API filters for the ManufacturerPart list endpoint.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ManufacturerPart
|
||||||
|
fields = [
|
||||||
|
'manufacturer',
|
||||||
|
'MPN',
|
||||||
|
'part',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Filter by 'active' status of linked part
|
||||||
|
active = rest_filters.BooleanFilter(field_name='part__active')
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerPartList(generics.ListCreateAPIView):
|
class ManufacturerPartList(generics.ListCreateAPIView):
|
||||||
""" API endpoint for list view of ManufacturerPart object
|
""" API endpoint for list view of ManufacturerPart object
|
||||||
|
|
||||||
@ -98,6 +117,7 @@ class ManufacturerPartList(generics.ListCreateAPIView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
serializer_class = ManufacturerPartSerializer
|
serializer_class = ManufacturerPartSerializer
|
||||||
|
filterset_class = ManufacturerPartFilter
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
|
|
||||||
@ -115,45 +135,12 @@ class ManufacturerPartList(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
return self.serializer_class(*args, **kwargs)
|
return self.serializer_class(*args, **kwargs)
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
|
||||||
"""
|
|
||||||
Custom filtering for the queryset.
|
|
||||||
"""
|
|
||||||
|
|
||||||
queryset = super().filter_queryset(queryset)
|
|
||||||
|
|
||||||
params = self.request.query_params
|
|
||||||
|
|
||||||
# Filter by manufacturer
|
|
||||||
manufacturer = params.get('manufacturer', None)
|
|
||||||
|
|
||||||
if manufacturer is not None:
|
|
||||||
queryset = queryset.filter(manufacturer=manufacturer)
|
|
||||||
|
|
||||||
# Filter by parent part?
|
|
||||||
part = params.get('part', None)
|
|
||||||
|
|
||||||
if part is not None:
|
|
||||||
queryset = queryset.filter(part=part)
|
|
||||||
|
|
||||||
# Filter by 'active' status of the part?
|
|
||||||
active = params.get('active', None)
|
|
||||||
|
|
||||||
if active is not None:
|
|
||||||
active = str2bool(active)
|
|
||||||
queryset = queryset.filter(part__active=active)
|
|
||||||
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
filter_backends = [
|
filter_backends = [
|
||||||
DjangoFilterBackend,
|
DjangoFilterBackend,
|
||||||
filters.SearchFilter,
|
filters.SearchFilter,
|
||||||
filters.OrderingFilter,
|
filters.OrderingFilter,
|
||||||
]
|
]
|
||||||
|
|
||||||
filter_fields = [
|
|
||||||
]
|
|
||||||
|
|
||||||
search_fields = [
|
search_fields = [
|
||||||
'manufacturer__name',
|
'manufacturer__name',
|
||||||
'description',
|
'description',
|
||||||
|
@ -198,9 +198,8 @@
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
{% settings_value "INVENTREE_DOWNLOAD_FROM_URL" as allow_download %}
|
if (global_settings.INVENTREE_DOWNLOAD_FROM_URL) {
|
||||||
|
|
||||||
{% if allow_download %}
|
|
||||||
$('#company-image-url').click(function() {
|
$('#company-image-url').click(function() {
|
||||||
launchModalForm(
|
launchModalForm(
|
||||||
'{% url "company-image-download" company.id %}',
|
'{% url "company-image-download" company.id %}',
|
||||||
@ -209,6 +208,6 @@
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
{% endif %}
|
}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -371,7 +371,7 @@
|
|||||||
requests.push(inventreeDelete(url));
|
requests.push(inventreeDelete(url));
|
||||||
});
|
});
|
||||||
|
|
||||||
$.when.apply($, requests).then(function() {
|
$.when.apply($, requests).done(function() {
|
||||||
$('#supplier-part-table').bootstrapTable('refresh');
|
$('#supplier-part-table').bootstrapTable('refresh');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ $("#supplier-part-delete").click(function() {
|
|||||||
requests.push(inventreeDelete(url));
|
requests.push(inventreeDelete(url));
|
||||||
});
|
});
|
||||||
|
|
||||||
$.when.apply($, requests).then(function() {
|
$.when.apply($, requests).done(function() {
|
||||||
reloadSupplierPartTable();
|
reloadSupplierPartTable();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -247,7 +247,7 @@ $("#multi-parameter-delete").click(function() {
|
|||||||
requests.push(inventreeDelete(url));
|
requests.push(inventreeDelete(url));
|
||||||
});
|
});
|
||||||
|
|
||||||
$.when.apply($, requests).then(function() {
|
$.when.apply($, requests).done(function() {
|
||||||
$('#parameter-table').bootstrapTable('refresh');
|
$('#parameter-table').bootstrapTable('refresh');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
7946
InvenTree/locale/el/LC_MESSAGES/django.po
Normal file
7946
InvenTree/locale/el/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
7946
InvenTree/locale/he/LC_MESSAGES/django.po
Normal file
7946
InvenTree/locale/he/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
7946
InvenTree/locale/id/LC_MESSAGES/django.po
Normal file
7946
InvenTree/locale/id/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
7946
InvenTree/locale/ko/LC_MESSAGES/django.po
Normal file
7946
InvenTree/locale/ko/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
7946
InvenTree/locale/nl/LC_MESSAGES/django.po
Normal file
7946
InvenTree/locale/nl/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
7946
InvenTree/locale/no/LC_MESSAGES/django.po
Normal file
7946
InvenTree/locale/no/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
7946
InvenTree/locale/sv/LC_MESSAGES/django.po
Normal file
7946
InvenTree/locale/sv/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
7946
InvenTree/locale/th/LC_MESSAGES/django.po
Normal file
7946
InvenTree/locale/th/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
7946
InvenTree/locale/vi/LC_MESSAGES/django.po
Normal file
7946
InvenTree/locale/vi/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -164,7 +164,7 @@ $("#edit-order").click(function() {
|
|||||||
constructForm('{% url "api-po-detail" order.pk %}', {
|
constructForm('{% url "api-po-detail" order.pk %}', {
|
||||||
fields: {
|
fields: {
|
||||||
reference: {
|
reference: {
|
||||||
prefix: "{% settings_value 'PURCHASEORDER_REFERENCE_PREFIX' %}",
|
prefix: global_settings.PURCHASEORDER_REFERENCE_PREFIX,
|
||||||
},
|
},
|
||||||
{% if order.lines.count == 0 and order.status == PurchaseOrderStatus.PENDING %}
|
{% if order.lines.count == 0 and order.status == PurchaseOrderStatus.PENDING %}
|
||||||
supplier: {
|
supplier: {
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
var prefix = '{% settings_value "PURCHASEORDER_REFERENCE_PREFIX" %}';
|
var prefix = global_settings.PURCHASEORDER_REFERENCE_PREFIX;
|
||||||
|
|
||||||
for (var idx = 0; idx < response.length; idx++) {
|
for (var idx = 0; idx < response.length; idx++) {
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ $("#edit-order").click(function() {
|
|||||||
constructForm('{% url "api-so-detail" order.pk %}', {
|
constructForm('{% url "api-so-detail" order.pk %}', {
|
||||||
fields: {
|
fields: {
|
||||||
reference: {
|
reference: {
|
||||||
prefix: "{% settings_value 'SALESORDER_REFERENCE_PREFIX' %}",
|
prefix: global_settings.SALESORDER_REFERENCE_PREFIX,
|
||||||
},
|
},
|
||||||
{% if order.lines.count == 0 and order.status == SalesOrderStatus.PENDING %}
|
{% if order.lines.count == 0 and order.status == SalesOrderStatus.PENDING %}
|
||||||
customer: {
|
customer: {
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
{
|
{
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
|
|
||||||
var prefix = '{% settings_value "SALESORDER_REFERENCE_PREFIX" %}';
|
var prefix = global_settings.SALESORDER_REFERENCE_PREFIX;
|
||||||
|
|
||||||
for (var idx = 0; idx < response.length; idx++) {
|
for (var idx = 0; idx < response.length; idx++) {
|
||||||
var order = response[idx];
|
var order = response[idx];
|
||||||
|
@ -159,7 +159,7 @@ class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
queryset = PartCategory.objects.all()
|
queryset = PartCategory.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class CategoryParameters(generics.ListAPIView):
|
class CategoryParameterList(generics.ListAPIView):
|
||||||
""" API endpoint for accessing a list of PartCategoryParameterTemplate objects.
|
""" API endpoint for accessing a list of PartCategoryParameterTemplate objects.
|
||||||
|
|
||||||
- GET: Return a list of PartCategoryParameterTemplate objects
|
- GET: Return a list of PartCategoryParameterTemplate objects
|
||||||
@ -176,30 +176,27 @@ class CategoryParameters(generics.ListAPIView):
|
|||||||
- Allow traversing all parent categories
|
- Allow traversing all parent categories
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
|
||||||
cat_id = int(self.kwargs.get('pk', None))
|
|
||||||
except TypeError:
|
|
||||||
cat_id = None
|
|
||||||
fetch_parent = str2bool(self.request.query_params.get('fetch_parent', 'true'))
|
|
||||||
|
|
||||||
queryset = super().get_queryset()
|
queryset = super().get_queryset()
|
||||||
|
|
||||||
if isinstance(cat_id, int):
|
params = self.request.query_params
|
||||||
|
|
||||||
|
category = params.get('category', None)
|
||||||
|
|
||||||
|
if category is not None:
|
||||||
try:
|
try:
|
||||||
category = PartCategory.objects.get(pk=cat_id)
|
|
||||||
except PartCategory.DoesNotExist:
|
|
||||||
# Return empty queryset
|
|
||||||
return PartCategoryParameterTemplate.objects.none()
|
|
||||||
|
|
||||||
category_list = [cat_id]
|
category = PartCategory.objects.get(pk=category)
|
||||||
|
|
||||||
|
fetch_parent = str2bool(params.get('fetch_parent', True))
|
||||||
|
|
||||||
if fetch_parent:
|
if fetch_parent:
|
||||||
parent_categories = category.get_ancestors()
|
parents = category.get_ancestors(include_self=True)
|
||||||
for parent in parent_categories:
|
queryset = queryset.filter(category__in=[cat.pk for cat in parents])
|
||||||
category_list.append(parent.pk)
|
else:
|
||||||
|
queryset = queryset.filter(category=category)
|
||||||
|
|
||||||
queryset = queryset.filter(category__in=category_list)
|
except (ValueError, PartCategory.DoesNotExist):
|
||||||
|
pass
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
@ -341,9 +338,7 @@ class PartThumbs(generics.ListAPIView):
|
|||||||
- Images may be used for multiple parts!
|
- Images may be used for multiple parts!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = self.get_queryset()
|
queryset = self.filter_queryset(self.get_queryset())
|
||||||
|
|
||||||
# TODO - We should return the thumbnails here, not the full image!
|
|
||||||
|
|
||||||
# Return the most popular parts first
|
# Return the most popular parts first
|
||||||
data = queryset.values(
|
data = queryset.values(
|
||||||
@ -352,6 +347,19 @@ class PartThumbs(generics.ListAPIView):
|
|||||||
|
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
|
filter_backends = [
|
||||||
|
filters.SearchFilter,
|
||||||
|
]
|
||||||
|
|
||||||
|
search_fields = [
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
'IPN',
|
||||||
|
'revision',
|
||||||
|
'keywords',
|
||||||
|
'category__name',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartThumbsUpdate(generics.RetrieveUpdateAPIView):
|
class PartThumbsUpdate(generics.RetrieveUpdateAPIView):
|
||||||
""" API endpoint for updating Part thumbnails"""
|
""" API endpoint for updating Part thumbnails"""
|
||||||
@ -446,6 +454,8 @@ class PartFilter(rest_filters.FilterSet):
|
|||||||
else:
|
else:
|
||||||
queryset = queryset.filter(IPN='')
|
queryset = queryset.filter(IPN='')
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
||||||
# Regex filter for name
|
# Regex filter for name
|
||||||
name_regex = rest_filters.CharFilter(label='Filter by name (regex)', field_name='name', lookup_expr='iregex')
|
name_regex = rest_filters.CharFilter(label='Filter by name (regex)', field_name='name', lookup_expr='iregex')
|
||||||
|
|
||||||
@ -995,8 +1005,9 @@ class BomList(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
# Get values for currencies
|
# Get values for currencies
|
||||||
currencies = queryset.annotate(
|
currencies = queryset.annotate(
|
||||||
|
purchase_price=F('sub_part__stock_items__purchase_price'),
|
||||||
purchase_price_currency=F('sub_part__stock_items__purchase_price_currency'),
|
purchase_price_currency=F('sub_part__stock_items__purchase_price_currency'),
|
||||||
).values('pk', 'sub_part', 'purchase_price_currency')
|
).values('pk', 'sub_part', 'purchase_price', 'purchase_price_currency')
|
||||||
|
|
||||||
def convert_price(price, currency, decimal_places=4):
|
def convert_price(price, currency, decimal_places=4):
|
||||||
""" Convert price field, returns Money field """
|
""" Convert price field, returns Money field """
|
||||||
@ -1032,7 +1043,7 @@ class BomList(generics.ListCreateAPIView):
|
|||||||
# Find associated currency (select first found)
|
# Find associated currency (select first found)
|
||||||
purchase_price_currency = None
|
purchase_price_currency = None
|
||||||
for currency_item in currencies:
|
for currency_item in currencies:
|
||||||
if currency_item['pk'] == bom_item.pk and currency_item['sub_part'] == bom_item.sub_part.pk:
|
if currency_item['pk'] == bom_item.pk and currency_item['sub_part'] == bom_item.sub_part.pk and currency_item['purchase_price']:
|
||||||
purchase_price_currency = currency_item['purchase_price_currency']
|
purchase_price_currency = currency_item['purchase_price_currency']
|
||||||
break
|
break
|
||||||
# Convert prices
|
# Convert prices
|
||||||
@ -1093,7 +1104,8 @@ part_api_urls = [
|
|||||||
|
|
||||||
# Base URL for PartCategory API endpoints
|
# Base URL for PartCategory API endpoints
|
||||||
url(r'^category/', include([
|
url(r'^category/', include([
|
||||||
url(r'^(?P<pk>\d+)/parameters/?', CategoryParameters.as_view(), name='api-part-category-parameters'),
|
url(r'^parameters/', CategoryParameterList.as_view(), name='api-part-category-parameter-list'),
|
||||||
|
|
||||||
url(r'^(?P<pk>\d+)/?', CategoryDetail.as_view(), name='api-part-category-detail'),
|
url(r'^(?P<pk>\d+)/?', CategoryDetail.as_view(), name='api-part-category-detail'),
|
||||||
url(r'^$', CategoryList.as_view(), name='api-part-category-list'),
|
url(r'^$', CategoryList.as_view(), name='api-part-category-list'),
|
||||||
])),
|
])),
|
||||||
|
@ -18,7 +18,6 @@ import common.models
|
|||||||
from common.forms import MatchItemForm
|
from common.forms import MatchItemForm
|
||||||
|
|
||||||
from .models import Part, PartCategory, PartRelated
|
from .models import Part, PartCategory, PartRelated
|
||||||
from .models import BomItem
|
|
||||||
from .models import PartParameterTemplate, PartParameter
|
from .models import PartParameterTemplate, PartParameter
|
||||||
from .models import PartCategoryParameterTemplate
|
from .models import PartCategoryParameterTemplate
|
||||||
from .models import PartSellPriceBreak, PartInternalPriceBreak
|
from .models import PartSellPriceBreak, PartInternalPriceBreak
|
||||||
@ -317,33 +316,6 @@ class EditCategoryParameterTemplateForm(HelperForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class EditBomItemForm(HelperForm):
|
|
||||||
""" Form for editing a BomItem object """
|
|
||||||
|
|
||||||
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5, label=_('Quantity'))
|
|
||||||
|
|
||||||
sub_part = PartModelChoiceField(queryset=Part.objects.all(), label=_('Sub part'))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = BomItem
|
|
||||||
fields = [
|
|
||||||
'part',
|
|
||||||
'sub_part',
|
|
||||||
'quantity',
|
|
||||||
'reference',
|
|
||||||
'overage',
|
|
||||||
'note',
|
|
||||||
'allow_variants',
|
|
||||||
'inherited',
|
|
||||||
'optional',
|
|
||||||
]
|
|
||||||
|
|
||||||
# Prevent editing of the part associated with this BomItem
|
|
||||||
widgets = {
|
|
||||||
'part': forms.HiddenInput()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PartPriceForm(forms.Form):
|
class PartPriceForm(forms.Form):
|
||||||
""" Simple form for viewing part pricing information """
|
""" Simple form for viewing part pricing information """
|
||||||
|
|
||||||
|
@ -526,11 +526,14 @@ class CategoryParameterTemplateSerializer(InvenTreeModelSerializer):
|
|||||||
parameter_template = PartParameterTemplateSerializer(many=False,
|
parameter_template = PartParameterTemplateSerializer(many=False,
|
||||||
read_only=True)
|
read_only=True)
|
||||||
|
|
||||||
|
category_detail = CategorySerializer(source='category', many=False, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PartCategoryParameterTemplate
|
model = PartCategoryParameterTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'pk',
|
'pk',
|
||||||
'category',
|
'category',
|
||||||
|
'category_detail',
|
||||||
'parameter_template',
|
'parameter_template',
|
||||||
'default_value',
|
'default_value',
|
||||||
]
|
]
|
||||||
|
@ -240,32 +240,20 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$("#cat-create").click(function() {
|
$("#cat-create").click(function() {
|
||||||
launchModalForm(
|
|
||||||
"{% url 'category-create' %}",
|
var fields = categoryFields();
|
||||||
{
|
|
||||||
follow: true,
|
|
||||||
data: {
|
|
||||||
{% if category %}
|
{% if category %}
|
||||||
category: {{ category.id }}
|
fields.parent.value = {{ category.pk }};
|
||||||
{% endif %}
|
{% endif %}
|
||||||
},
|
|
||||||
secondary: [
|
constructForm('{% url "api-part-category-list" %}', {
|
||||||
{
|
fields: fields,
|
||||||
field: 'default_location',
|
method: 'POST',
|
||||||
label: '{% trans "New Location" %}',
|
title: '{% trans "Create Part Category" %}',
|
||||||
title: '{% trans "Create new location" %}',
|
follow: true,
|
||||||
url: "{% url 'stock-location-create' %}",
|
});
|
||||||
},
|
});
|
||||||
{
|
|
||||||
field: 'parent',
|
|
||||||
label: '{% trans "New Category" %}',
|
|
||||||
title: '{% trans "Create new category" %}',
|
|
||||||
url: "{% url 'category-create' %}",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
|
||||||
|
|
||||||
$("#part-export").click(function() {
|
$("#part-export").click(function() {
|
||||||
|
|
||||||
@ -286,12 +274,6 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
},
|
},
|
||||||
secondary: [
|
secondary: [
|
||||||
{
|
|
||||||
field: 'category',
|
|
||||||
label: '{% trans "New Category" %}',
|
|
||||||
title: '{% trans "Create new Part Category" %}',
|
|
||||||
url: "{% url 'category-create' %}",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
field: 'default_location',
|
field: 'default_location',
|
||||||
label: '{% trans "New Location" %}',
|
label: '{% trans "New Location" %}',
|
||||||
@ -307,24 +289,7 @@
|
|||||||
{% if category %}
|
{% if category %}
|
||||||
$("#cat-edit").click(function () {
|
$("#cat-edit").click(function () {
|
||||||
|
|
||||||
constructForm(
|
editCategory({{ category.pk }});
|
||||||
'{% url "api-part-category-detail" category.pk %}',
|
|
||||||
{
|
|
||||||
fields: {
|
|
||||||
name: {},
|
|
||||||
description: {},
|
|
||||||
parent: {
|
|
||||||
help_text: '{% trans "Select parent category" %}',
|
|
||||||
},
|
|
||||||
default_location: {},
|
|
||||||
default_keywords: {
|
|
||||||
icon: 'fa-key',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title: '{% trans "Edit Part Category" %}',
|
|
||||||
reload: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
{% if category.parent %}
|
{% if category.parent %}
|
||||||
|
@ -416,7 +416,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Wait for *all* the requests to complete
|
// Wait for *all* the requests to complete
|
||||||
$.when.apply($, requests).then(function() {
|
$.when.apply($, requests).done(function() {
|
||||||
location.reload();
|
location.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -440,22 +440,22 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$("#bom-item-new").click(function () {
|
$("#bom-item-new").click(function () {
|
||||||
launchModalForm(
|
|
||||||
"{% url 'bom-item-create' %}?parent={{ part.id }}",
|
var fields = bomItemFields();
|
||||||
{
|
|
||||||
success: function() {
|
fields.part.value = {{ part.pk }};
|
||||||
$("#bom-table").bootstrapTable('refresh');
|
fields.sub_part.filters = {
|
||||||
},
|
active: true,
|
||||||
secondary: [
|
};
|
||||||
{
|
|
||||||
field: 'sub_part',
|
constructForm('{% url "api-bom-list" %}', {
|
||||||
label: '{% trans "New Part" %}',
|
fields: fields,
|
||||||
title: '{% trans "Create New Part" %}',
|
method: 'POST',
|
||||||
url: "{% url 'part-create' %}",
|
title: '{% trans "Create BOM Item" %}',
|
||||||
},
|
onSuccess: function() {
|
||||||
]
|
$('#bom-table').bootstrapTable('refresh');
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -832,7 +832,7 @@
|
|||||||
requests.push(inventreeDelete(url));
|
requests.push(inventreeDelete(url));
|
||||||
});
|
});
|
||||||
|
|
||||||
$.when.apply($, requests).then(function() {
|
$.when.apply($, requests).done(function() {
|
||||||
reloadSupplierPartTable();
|
reloadSupplierPartTable();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -899,7 +899,7 @@
|
|||||||
{% for line in price_history %}'{{ line.date }}',{% endfor %}
|
{% for line in price_history %}'{{ line.date }}',{% endfor %}
|
||||||
],
|
],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: '{% blocktrans %}Single Price - {{currency}}{% endblocktrans %}',
|
label: '{% blocktrans %}Purchase Unit Price - {{currency}}{% endblocktrans %}',
|
||||||
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||||
borderColor: 'rgb(255, 99, 132)',
|
borderColor: 'rgb(255, 99, 132)',
|
||||||
yAxisID: 'y',
|
yAxisID: 'y',
|
||||||
@ -911,7 +911,7 @@
|
|||||||
},
|
},
|
||||||
{% if 'price_diff' in price_history.0 %}
|
{% if 'price_diff' in price_history.0 %}
|
||||||
{
|
{
|
||||||
label: '{% blocktrans %}Single Price Difference - {{currency}}{% endblocktrans %}',
|
label: '{% blocktrans %}Unit Price-Cost Difference - {{currency}}{% endblocktrans %}',
|
||||||
backgroundColor: 'rgba(68, 157, 68, 0.2)',
|
backgroundColor: 'rgba(68, 157, 68, 0.2)',
|
||||||
borderColor: 'rgb(68, 157, 68)',
|
borderColor: 'rgb(68, 157, 68)',
|
||||||
yAxisID: 'y2',
|
yAxisID: 'y2',
|
||||||
@ -923,7 +923,7 @@
|
|||||||
hidden: true,
|
hidden: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '{% blocktrans %}Part Single Price - {{currency}}{% endblocktrans %}',
|
label: '{% blocktrans %}Supplier Unit Cost - {{currency}}{% endblocktrans %}',
|
||||||
backgroundColor: 'rgba(70, 127, 155, 0.2)',
|
backgroundColor: 'rgba(70, 127, 155, 0.2)',
|
||||||
borderColor: 'rgb(70, 127, 155)',
|
borderColor: 'rgb(70, 127, 155)',
|
||||||
yAxisID: 'y',
|
yAxisID: 'y',
|
||||||
|
@ -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>
|
||||||
|
|
||||||
@ -363,13 +415,16 @@
|
|||||||
// Callback when the image-selection modal form is displayed
|
// Callback when the image-selection modal form is displayed
|
||||||
// Populate the form with image data (requested via AJAX)
|
// Populate the form with image data (requested via AJAX)
|
||||||
|
|
||||||
$("#modal-form").find("#image-select-table").bootstrapTable({
|
$("#modal-form").find("#image-select-table").inventreeTable({
|
||||||
pagination: true,
|
|
||||||
pageSize: 25,
|
|
||||||
url: "{% url 'api-part-thumbs' %}",
|
url: "{% url 'api-part-thumbs' %}",
|
||||||
showHeader: false,
|
showHeader: false,
|
||||||
|
showColumns: false,
|
||||||
clickToSelect: true,
|
clickToSelect: true,
|
||||||
|
sidePagination: 'server',
|
||||||
singleSelect: true,
|
singleSelect: true,
|
||||||
|
formatNoMatches: function() {
|
||||||
|
return '{% trans "No matching images found" %}';
|
||||||
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
checkbox: true,
|
checkbox: true,
|
||||||
@ -377,6 +432,7 @@
|
|||||||
{
|
{
|
||||||
field: 'image',
|
field: 'image',
|
||||||
title: 'Image',
|
title: 'Image',
|
||||||
|
searchable: true,
|
||||||
formatter: function(value, row, index, field) {
|
formatter: function(value, row, index, field) {
|
||||||
return "<img src='/media/" + value + "' class='grid-image'/>"
|
return "<img src='/media/" + value + "' class='grid-image'/>"
|
||||||
}
|
}
|
||||||
@ -394,8 +450,7 @@
|
|||||||
|
|
||||||
{% if roles.part.change %}
|
{% if roles.part.change %}
|
||||||
|
|
||||||
{% settings_value "INVENTREE_DOWNLOAD_FROM_URL" as allow_download %}
|
if (global_settings.INVENTREE_DOWNLOAD_FROM_URL) {
|
||||||
{% if allow_download %}
|
|
||||||
$("#part-image-url").click(function() {
|
$("#part-image-url").click(function() {
|
||||||
launchModalForm(
|
launchModalForm(
|
||||||
'{% url "part-image-download" part.id %}',
|
'{% url "part-image-download" part.id %}',
|
||||||
@ -404,7 +459,7 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
{% endif %}
|
}
|
||||||
|
|
||||||
$("#part-image-select").click(function() {
|
$("#part-image-select").click(function() {
|
||||||
launchModalForm("{% url 'part-image-select' part.id %}",
|
launchModalForm("{% url 'part-image-select' part.id %}",
|
||||||
@ -451,4 +506,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 %}
|
@ -161,7 +161,7 @@
|
|||||||
<div class='panel-content'>
|
<div class='panel-content'>
|
||||||
<h4>{% trans 'Stock Pricing' %}
|
<h4>{% trans 'Stock Pricing' %}
|
||||||
<i class="fas fa-info-circle" title="Shows the purchase prices of stock for this part.
|
<i class="fas fa-info-circle" title="Shows the purchase prices of stock for this part.
|
||||||
The part single price is the current purchase price for that supplier part."></i>
|
The Supplier Unit Cost is the current purchase price for that supplier part."></i>
|
||||||
</h4>
|
</h4>
|
||||||
{% if price_history|length > 0 %}
|
{% if price_history|length > 0 %}
|
||||||
<div style="max-width: 99%; min-height: 300px">
|
<div style="max-width: 99%; min-height: 300px">
|
||||||
|
@ -18,7 +18,7 @@ from InvenTree import version, settings
|
|||||||
|
|
||||||
import InvenTree.helpers
|
import InvenTree.helpers
|
||||||
|
|
||||||
from common.models import InvenTreeSetting, ColorTheme
|
from common.models import InvenTreeSetting, ColorTheme, InvenTreeUserSetting
|
||||||
from common.settings import currency_code_default
|
from common.settings import currency_code_default
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
@ -69,6 +69,12 @@ def add(x, y, *args, **kwargs):
|
|||||||
return x + y
|
return x + y
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag()
|
||||||
|
def to_list(*args):
|
||||||
|
""" Return the input arguments as list """
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag()
|
@register.simple_tag()
|
||||||
def part_allocation_count(build, part, *args, **kwargs):
|
def part_allocation_count(build, part, *args, **kwargs):
|
||||||
""" Return the total number of <part> allocated to <build> """
|
""" Return the total number of <part> allocated to <build> """
|
||||||
@ -182,11 +188,12 @@ def setting_object(key, *args, **kwargs):
|
|||||||
"""
|
"""
|
||||||
Return a setting object speciifed by the given key
|
Return a setting object speciifed by the given key
|
||||||
(Or return None if the setting does not exist)
|
(Or return None if the setting does not exist)
|
||||||
|
if a user-setting was requested return that
|
||||||
"""
|
"""
|
||||||
|
|
||||||
setting = InvenTreeSetting.get_setting_object(key)
|
if 'user' in kwargs:
|
||||||
|
return InvenTreeUserSetting.get_setting_object(key, user=kwargs['user'])
|
||||||
return setting
|
return InvenTreeSetting.get_setting_object(key)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag()
|
@register.simple_tag()
|
||||||
@ -195,9 +202,29 @@ def settings_value(key, *args, **kwargs):
|
|||||||
Return a settings value specified by the given key
|
Return a settings value specified by the given key
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if 'user' in kwargs:
|
||||||
|
return InvenTreeUserSetting.get_setting(key, user=kwargs['user'])
|
||||||
return InvenTreeSetting.get_setting(key)
|
return InvenTreeSetting.get_setting(key)
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag()
|
||||||
|
def user_settings(user, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Return all USER settings as a key:value dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
return InvenTreeUserSetting.allValues(user=user)
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag()
|
||||||
|
def global_settings(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Return all GLOBAL InvenTree settings as a key:value dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
return InvenTreeSetting.allValues()
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag()
|
@register.simple_tag()
|
||||||
def get_color_theme_css(username):
|
def get_color_theme_css(username):
|
||||||
try:
|
try:
|
||||||
@ -217,6 +244,23 @@ def get_color_theme_css(username):
|
|||||||
return inventree_css_static_url
|
return inventree_css_static_url
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag()
|
||||||
|
def get_available_themes(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Return the available theme choices
|
||||||
|
"""
|
||||||
|
|
||||||
|
themes = []
|
||||||
|
|
||||||
|
for key, name in ColorTheme.get_color_themes_choices():
|
||||||
|
themes.append({
|
||||||
|
'key': key,
|
||||||
|
'name': name
|
||||||
|
})
|
||||||
|
|
||||||
|
return themes
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def keyvalue(dict, key):
|
def keyvalue(dict, key):
|
||||||
"""
|
"""
|
||||||
|
@ -243,19 +243,6 @@ class PartQRTest(PartViewTestCase):
|
|||||||
class CategoryTest(PartViewTestCase):
|
class CategoryTest(PartViewTestCase):
|
||||||
""" Tests for PartCategory related views """
|
""" Tests for PartCategory related views """
|
||||||
|
|
||||||
def test_create(self):
|
|
||||||
""" Test view for creating a new category """
|
|
||||||
response = self.client.get(reverse('category-create'), {'category': 1}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_create_invalid_parent(self):
|
|
||||||
""" test creation of a new category with an invalid parent """
|
|
||||||
response = self.client.get(reverse('category-create'), {'category': 9999}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
|
||||||
|
|
||||||
# Form should still return OK
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_set_category(self):
|
def test_set_category(self):
|
||||||
""" Test that the "SetCategory" view works """
|
""" Test that the "SetCategory" view works """
|
||||||
|
|
||||||
@ -272,22 +259,3 @@ class CategoryTest(PartViewTestCase):
|
|||||||
|
|
||||||
response = self.client.post(url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
response = self.client.post(url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
class BomItemTests(PartViewTestCase):
|
|
||||||
""" Tests for BomItem related views """
|
|
||||||
|
|
||||||
def test_create_valid_parent(self):
|
|
||||||
""" Create a BomItem for a valid part """
|
|
||||||
response = self.client.get(reverse('bom-item-create'), {'parent': 1}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_create_no_parent(self):
|
|
||||||
""" Create a BomItem without a parent """
|
|
||||||
response = self.client.get(reverse('bom-item-create'), HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_create_invalid_parent(self):
|
|
||||||
""" Create a BomItem with an invalid parent """
|
|
||||||
response = self.client.get(reverse('bom-item-create'), {'parent': 99999}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
@ -65,9 +65,6 @@ category_parameter_urls = [
|
|||||||
|
|
||||||
category_urls = [
|
category_urls = [
|
||||||
|
|
||||||
# Create a new category
|
|
||||||
url(r'^new/', views.CategoryCreate.as_view(), name='category-create'),
|
|
||||||
|
|
||||||
# Top level subcategory display
|
# Top level subcategory display
|
||||||
url(r'^subcategory/', views.PartIndex.as_view(template_name='part/subcategory.html'), name='category-index-subcategory'),
|
url(r'^subcategory/', views.PartIndex.as_view(template_name='part/subcategory.html'), name='category-index-subcategory'),
|
||||||
|
|
||||||
@ -81,10 +78,6 @@ category_urls = [
|
|||||||
]))
|
]))
|
||||||
]
|
]
|
||||||
|
|
||||||
part_bom_urls = [
|
|
||||||
url(r'^edit/?', views.BomItemEdit.as_view(), name='bom-item-edit'),
|
|
||||||
]
|
|
||||||
|
|
||||||
# URL list for part web interface
|
# URL list for part web interface
|
||||||
part_urls = [
|
part_urls = [
|
||||||
|
|
||||||
@ -95,9 +88,6 @@ part_urls = [
|
|||||||
url(r'^import/', views.PartImport.as_view(), name='part-import'),
|
url(r'^import/', views.PartImport.as_view(), name='part-import'),
|
||||||
url(r'^import-api/', views.PartImportAjax.as_view(), name='api-part-import'),
|
url(r'^import-api/', views.PartImportAjax.as_view(), name='api-part-import'),
|
||||||
|
|
||||||
# Create a new BOM item
|
|
||||||
url(r'^bom/new/?', views.BomItemCreate.as_view(), name='bom-item-create'),
|
|
||||||
|
|
||||||
# Download a BOM upload template
|
# Download a BOM upload template
|
||||||
url(r'^bom_template/?', views.BomUploadTemplate.as_view(), name='bom-upload-template'),
|
url(r'^bom_template/?', views.BomUploadTemplate.as_view(), name='bom-upload-template'),
|
||||||
|
|
||||||
@ -125,9 +115,6 @@ part_urls = [
|
|||||||
# Change category for multiple parts
|
# Change category for multiple parts
|
||||||
url(r'^set-category/?', views.PartSetCategory.as_view(), name='part-set-category'),
|
url(r'^set-category/?', views.PartSetCategory.as_view(), name='part-set-category'),
|
||||||
|
|
||||||
# Bom Items
|
|
||||||
url(r'^bom/(?P<pk>\d+)/', include(part_bom_urls)),
|
|
||||||
|
|
||||||
# Individual part using IPN as slug
|
# Individual part using IPN as slug
|
||||||
url(r'^(?P<slug>[-\w]+)/', views.PartDetailFromIPN.as_view(), name='part-detail-from-ipn'),
|
url(r'^(?P<slug>[-\w]+)/', views.PartDetailFromIPN.as_view(), name='part-detail-from-ipn'),
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ from django.db.utils import IntegrityError
|
|||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.shortcuts import HttpResponseRedirect
|
from django.shortcuts import HttpResponseRedirect
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
from django.forms.models import model_to_dict
|
from django.forms.models import model_to_dict
|
||||||
from django.forms import HiddenInput, CheckboxInput
|
from django.forms import HiddenInput, CheckboxInput
|
||||||
@ -1905,49 +1905,6 @@ class CategoryDelete(AjaxDeleteView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CategoryCreate(AjaxCreateView):
|
|
||||||
""" Create view to make a new PartCategory """
|
|
||||||
model = PartCategory
|
|
||||||
ajax_form_action = reverse_lazy('category-create')
|
|
||||||
ajax_form_title = _('Create new part category')
|
|
||||||
ajax_template_name = 'modal_form.html'
|
|
||||||
form_class = part_forms.EditCategoryForm
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
""" Add extra context data to template.
|
|
||||||
|
|
||||||
- If parent category provided, pass the category details to the template
|
|
||||||
"""
|
|
||||||
context = super(CategoryCreate, self).get_context_data(**kwargs).copy()
|
|
||||||
|
|
||||||
parent_id = self.request.GET.get('category', None)
|
|
||||||
|
|
||||||
if parent_id:
|
|
||||||
try:
|
|
||||||
context['category'] = PartCategory.objects.get(pk=parent_id)
|
|
||||||
except PartCategory.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
""" Get initial data for new PartCategory
|
|
||||||
|
|
||||||
- If parent provided, pre-fill the parent category
|
|
||||||
"""
|
|
||||||
initials = super(CategoryCreate, self).get_initial().copy()
|
|
||||||
|
|
||||||
parent_id = self.request.GET.get('category', None)
|
|
||||||
|
|
||||||
if parent_id:
|
|
||||||
try:
|
|
||||||
initials['parent'] = PartCategory.objects.get(pk=parent_id)
|
|
||||||
except PartCategory.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return initials
|
|
||||||
|
|
||||||
|
|
||||||
class CategoryParameterTemplateCreate(AjaxCreateView):
|
class CategoryParameterTemplateCreate(AjaxCreateView):
|
||||||
""" View for creating a new PartCategoryParameterTemplate """
|
""" View for creating a new PartCategoryParameterTemplate """
|
||||||
|
|
||||||
@ -2121,134 +2078,6 @@ class CategoryParameterTemplateDelete(AjaxDeleteView):
|
|||||||
return self.object
|
return self.object
|
||||||
|
|
||||||
|
|
||||||
class BomItemCreate(AjaxCreateView):
|
|
||||||
"""
|
|
||||||
Create view for making a new BomItem object
|
|
||||||
"""
|
|
||||||
|
|
||||||
model = BomItem
|
|
||||||
form_class = part_forms.EditBomItemForm
|
|
||||||
ajax_template_name = 'modal_form.html'
|
|
||||||
ajax_form_title = _('Create BOM Item')
|
|
||||||
|
|
||||||
def get_form(self):
|
|
||||||
""" Override get_form() method to reduce Part selection options.
|
|
||||||
|
|
||||||
- Do not allow part to be added to its own BOM
|
|
||||||
- Remove any Part items that are already in the BOM
|
|
||||||
"""
|
|
||||||
|
|
||||||
form = super(AjaxCreateView, self).get_form()
|
|
||||||
|
|
||||||
part_id = form['part'].value()
|
|
||||||
|
|
||||||
# Construct a queryset for the part field
|
|
||||||
part_query = Part.objects.filter(active=True)
|
|
||||||
|
|
||||||
# Construct a queryset for the sub_part field
|
|
||||||
sub_part_query = Part.objects.filter(
|
|
||||||
component=True,
|
|
||||||
active=True
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
part = Part.objects.get(id=part_id)
|
|
||||||
|
|
||||||
# Hide the 'part' field
|
|
||||||
form.fields['part'].widget = HiddenInput()
|
|
||||||
|
|
||||||
# Exclude the part from its own BOM
|
|
||||||
sub_part_query = sub_part_query.exclude(id=part.id)
|
|
||||||
|
|
||||||
# Eliminate any options that are already in the BOM!
|
|
||||||
sub_part_query = sub_part_query.exclude(id__in=[item.id for item in part.getRequiredParts()])
|
|
||||||
|
|
||||||
except (ValueError, Part.DoesNotExist):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Set the querysets for the fields
|
|
||||||
form.fields['part'].queryset = part_query
|
|
||||||
form.fields['sub_part'].queryset = sub_part_query
|
|
||||||
|
|
||||||
return form
|
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
""" Provide initial data for the BomItem:
|
|
||||||
|
|
||||||
- If 'parent' provided, set the parent part field
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Look for initial values
|
|
||||||
initials = super(BomItemCreate, self).get_initial().copy()
|
|
||||||
|
|
||||||
# Parent part for this item?
|
|
||||||
parent_id = self.request.GET.get('parent', None)
|
|
||||||
|
|
||||||
if parent_id:
|
|
||||||
try:
|
|
||||||
initials['part'] = Part.objects.get(pk=parent_id)
|
|
||||||
except Part.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return initials
|
|
||||||
|
|
||||||
|
|
||||||
class BomItemEdit(AjaxUpdateView):
|
|
||||||
""" Update view for editing BomItem """
|
|
||||||
|
|
||||||
model = BomItem
|
|
||||||
form_class = part_forms.EditBomItemForm
|
|
||||||
ajax_template_name = 'modal_form.html'
|
|
||||||
ajax_form_title = _('Edit BOM item')
|
|
||||||
|
|
||||||
def get_form(self):
|
|
||||||
""" Override get_form() method to filter part selection options
|
|
||||||
|
|
||||||
- Do not allow part to be added to its own BOM
|
|
||||||
- Remove any part items that are already in the BOM
|
|
||||||
"""
|
|
||||||
|
|
||||||
item = self.get_object()
|
|
||||||
|
|
||||||
form = super().get_form()
|
|
||||||
|
|
||||||
part_id = form['part'].value()
|
|
||||||
|
|
||||||
try:
|
|
||||||
part = Part.objects.get(pk=part_id)
|
|
||||||
|
|
||||||
# Construct a queryset
|
|
||||||
query = Part.objects.filter(component=True)
|
|
||||||
|
|
||||||
# Limit to "active" items, *unless* the currently selected item is not active
|
|
||||||
if item.sub_part.active:
|
|
||||||
query = query.filter(active=True)
|
|
||||||
|
|
||||||
# Prevent the parent part from being selected
|
|
||||||
query = query.exclude(pk=part_id)
|
|
||||||
|
|
||||||
# Eliminate any options that are already in the BOM,
|
|
||||||
# *except* for the item which is already selected
|
|
||||||
try:
|
|
||||||
sub_part_id = int(form['sub_part'].value())
|
|
||||||
except ValueError:
|
|
||||||
sub_part_id = -1
|
|
||||||
|
|
||||||
existing = [item.pk for item in part.getRequiredParts()]
|
|
||||||
|
|
||||||
if sub_part_id in existing:
|
|
||||||
existing.remove(sub_part_id)
|
|
||||||
|
|
||||||
query = query.exclude(id__in=existing)
|
|
||||||
|
|
||||||
form.fields['sub_part'].queryset = query
|
|
||||||
|
|
||||||
except (ValueError, Part.DoesNotExist):
|
|
||||||
pass
|
|
||||||
|
|
||||||
return form
|
|
||||||
|
|
||||||
|
|
||||||
class PartSalePriceBreakCreate(AjaxCreateView):
|
class PartSalePriceBreakCreate(AjaxCreateView):
|
||||||
"""
|
"""
|
||||||
View for creating a sale price break for a part
|
View for creating a sale price break for a part
|
||||||
|
@ -8,7 +8,6 @@ from __future__ import unicode_literals
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.forms.utils import ErrorDict
|
from django.forms.utils import ErrorDict
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.validators import MinValueValidator
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from mptt.fields import TreeNodeChoiceField
|
from mptt.fields import TreeNodeChoiceField
|
||||||
@ -241,14 +240,9 @@ class InstallStockForm(HelperForm):
|
|||||||
help_text=_('Stock item to install')
|
help_text=_('Stock item to install')
|
||||||
)
|
)
|
||||||
|
|
||||||
quantity_to_install = RoundingDecimalFormField(
|
to_install = forms.BooleanField(
|
||||||
max_digits=10, decimal_places=5,
|
widget=forms.HiddenInput(),
|
||||||
initial=1,
|
required=False,
|
||||||
label=_('Quantity'),
|
|
||||||
help_text=_('Stock quantity to assign'),
|
|
||||||
validators=[
|
|
||||||
MinValueValidator(0.001)
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
notes = forms.CharField(
|
notes = forms.CharField(
|
||||||
@ -261,7 +255,7 @@ class InstallStockForm(HelperForm):
|
|||||||
fields = [
|
fields = [
|
||||||
'part',
|
'part',
|
||||||
'stock_item',
|
'stock_item',
|
||||||
'quantity_to_install',
|
# 'quantity_to_install',
|
||||||
'notes',
|
'notes',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -119,6 +119,11 @@
|
|||||||
<h4>{% trans "Installed Stock Items" %}</h4>
|
<h4>{% trans "Installed Stock Items" %}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class='panel-content'>
|
<div class='panel-content'>
|
||||||
|
<div class='btn-group'>
|
||||||
|
<button type='button' class='btn btn-success' id='stock-item-install'>
|
||||||
|
<span class='fas fa-plus-circle'></span> {% trans "Install Stock Item" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<table class='table table-striped table-condensed' id='installed-table'></table>
|
<table class='table table-striped table-condensed' id='installed-table'></table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -128,6 +133,20 @@
|
|||||||
{% block js_ready %}
|
{% block js_ready %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
|
|
||||||
|
$('#stock-item-install').click(function() {
|
||||||
|
|
||||||
|
launchModalForm(
|
||||||
|
"{% url 'stock-item-install' item.pk %}",
|
||||||
|
{
|
||||||
|
data: {
|
||||||
|
'part': {{ item.part.pk }},
|
||||||
|
'install_item': true,
|
||||||
|
},
|
||||||
|
reload: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
loadInstalledInTable(
|
loadInstalledInTable(
|
||||||
$('#installed-table'),
|
$('#installed-table'),
|
||||||
{
|
{
|
||||||
|
@ -127,9 +127,11 @@
|
|||||||
<li><a href='#' id='stock-return-from-customer' title='{% trans "Return to stock" %}'><span class='fas fa-undo'></span> {% trans "Return to stock" %}</a></li>
|
<li><a href='#' id='stock-return-from-customer' title='{% trans "Return to stock" %}'><span class='fas fa-undo'></span> {% trans "Return to stock" %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if item.belongs_to %}
|
{% if item.belongs_to %}
|
||||||
<li>
|
<li><a href='#' id='stock-uninstall' title='{% trans "Uninstall stock item" %}'><span class='fas fa-unlink'></span> {% trans "Uninstall" %}</a></li>
|
||||||
<a href='#' id='stock-uninstall' title='{% trans "Uninstall stock item" %}'><span class='fas fa-unlink'></span> {% trans "Uninstall" %}</a>
|
{% else %}
|
||||||
</li>
|
{% if item.part.get_used_in %}
|
||||||
|
<li><a href='#' id='stock-install-in' title='{% trans "Install stock item" %}'><span class='fas fa-link'></span> {% trans "Install" %}</a></li>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -253,7 +255,7 @@
|
|||||||
<small>{{ previous.serial }}</small> ‹
|
<small>{{ previous.serial }}</small> ‹
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="btn" href=""><strong>{{ item.serial }}</strong></span>
|
{{ item.serial }}
|
||||||
{% if next %}
|
{% if next %}
|
||||||
<a class="btn btn-default text-sm" aria-label="{% trans 'next page' %}" href="{% url request.resolver_match.url_name next.id %}">
|
<a class="btn btn-default text-sm" aria-label="{% trans 'next page' %}" href="{% url request.resolver_match.url_name next.id %}">
|
||||||
› <small>{{ next.serial }}</small>
|
› <small>{{ next.serial }}</small>
|
||||||
@ -461,6 +463,20 @@ $("#stock-serialize").click(function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#stock-install-in').click(function() {
|
||||||
|
|
||||||
|
launchModalForm(
|
||||||
|
"{% url 'stock-item-install' item.pk %}",
|
||||||
|
{
|
||||||
|
data: {
|
||||||
|
'part': {{ item.part.pk }},
|
||||||
|
'install_in': true,
|
||||||
|
},
|
||||||
|
reload: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
$('#stock-uninstall').click(function() {
|
$('#stock-uninstall').click(function() {
|
||||||
|
|
||||||
launchModalForm(
|
launchModalForm(
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
{% block pre_form_content %}
|
{% block pre_form_content %}
|
||||||
|
|
||||||
|
{% if install_item %}
|
||||||
<p>
|
<p>
|
||||||
{% trans "Install another Stock Item into this item." %}
|
{% trans "Install another Stock Item into this item." %}
|
||||||
</p>
|
</p>
|
||||||
@ -12,6 +13,21 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li>{% trans "The Stock Item links to a Part which is in the BOM for this Stock Item" %}</li>
|
<li>{% trans "The Stock Item links to a Part which is in the BOM for this Stock Item" %}</li>
|
||||||
<li>{% trans "The Stock Item is currently in stock" %}</li>
|
<li>{% trans "The Stock Item is currently in stock" %}</li>
|
||||||
|
<li>{% trans "The Stock Item is serialized and does not belong to another item" %}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
|
{% elif install_in %}
|
||||||
|
<p>
|
||||||
|
{% trans "Install this Stock Item in another stock item." %}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{% trans "Stock items can only be installed if they meet the following criteria" %}:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>{% trans "The part associated to this Stock Item belongs to another part's BOM" %}</li>
|
||||||
|
<li>{% trans "This Stock Item is serialized and does not belong to another item" %}</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -11,6 +11,7 @@ from django.views.generic import DetailView, ListView, UpdateView
|
|||||||
from django.forms.models import model_to_dict
|
from django.forms.models import model_to_dict
|
||||||
from django.forms import HiddenInput
|
from django.forms import HiddenInput
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
|
|
||||||
@ -91,12 +92,26 @@ class StockItemDetail(InvenTreeRoleMixin, DetailView):
|
|||||||
data = super().get_context_data(**kwargs)
|
data = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
if self.object.serialized:
|
if self.object.serialized:
|
||||||
serial_elem = {int(a.serial): a for a in self.object.part.stock_items.all() if a.serialized}
|
|
||||||
serials = serial_elem.keys()
|
serial_elem = {}
|
||||||
|
|
||||||
|
try:
|
||||||
current = int(self.object.serial)
|
current = int(self.object.serial)
|
||||||
|
|
||||||
|
for item in self.object.part.stock_items.all():
|
||||||
|
|
||||||
|
if item.serialized:
|
||||||
|
try:
|
||||||
|
sn = int(item.serial)
|
||||||
|
serial_elem[sn] = item
|
||||||
|
except ValueError:
|
||||||
|
# We only support integer serial number progression
|
||||||
|
pass
|
||||||
|
|
||||||
|
serials = serial_elem.keys()
|
||||||
|
|
||||||
# previous
|
# previous
|
||||||
for nbr in range(current - 1, -1, -1):
|
for nbr in range(current - 1, min(serials), -1):
|
||||||
if nbr in serials:
|
if nbr in serials:
|
||||||
data['previous'] = serial_elem.get(nbr, None)
|
data['previous'] = serial_elem.get(nbr, None)
|
||||||
break
|
break
|
||||||
@ -107,8 +122,28 @@ class StockItemDetail(InvenTreeRoleMixin, DetailView):
|
|||||||
data['next'] = serial_elem.get(nbr, None)
|
data['next'] = serial_elem.get(nbr, None)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
# We only support integer serial number progression
|
||||||
|
pass
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
""" check if item exists else return to stock index """
|
||||||
|
|
||||||
|
stock_pk = kwargs.get('pk', None)
|
||||||
|
|
||||||
|
if stock_pk:
|
||||||
|
try:
|
||||||
|
stock_item = StockItem.objects.get(pk=stock_pk)
|
||||||
|
except StockItem.DoesNotExist:
|
||||||
|
stock_item = None
|
||||||
|
|
||||||
|
if not stock_item:
|
||||||
|
return HttpResponseRedirect(reverse('stock-index'))
|
||||||
|
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class StockItemNotes(InvenTreeRoleMixin, UpdateView):
|
class StockItemNotes(InvenTreeRoleMixin, UpdateView):
|
||||||
""" View for editing the 'notes' field of a StockItem object """
|
""" View for editing the 'notes' field of a StockItem object """
|
||||||
@ -501,36 +536,73 @@ class StockItemInstall(AjaxUpdateView):
|
|||||||
|
|
||||||
part = None
|
part = None
|
||||||
|
|
||||||
|
def get_params(self):
|
||||||
|
""" Retrieve GET parameters """
|
||||||
|
|
||||||
|
# Look at GET params
|
||||||
|
self.part_id = self.request.GET.get('part', None)
|
||||||
|
self.install_in = self.request.GET.get('install_in', False)
|
||||||
|
self.install_item = self.request.GET.get('install_item', False)
|
||||||
|
|
||||||
|
if self.part_id is None:
|
||||||
|
# Look at POST params
|
||||||
|
self.part_id = self.request.POST.get('part', None)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.part = Part.objects.get(pk=self.part_id)
|
||||||
|
except (ValueError, Part.DoesNotExist):
|
||||||
|
self.part = None
|
||||||
|
|
||||||
def get_stock_items(self):
|
def get_stock_items(self):
|
||||||
"""
|
"""
|
||||||
Return a list of stock items suitable for displaying to the user.
|
Return a list of stock items suitable for displaying to the user.
|
||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
- Items must be in stock
|
- Items must be in stock
|
||||||
|
- Items must be in BOM of stock item
|
||||||
Filters:
|
- Items must be serialized
|
||||||
- Items can be filtered by Part reference
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Filter items in stock
|
||||||
items = StockItem.objects.filter(StockItem.IN_STOCK_FILTER)
|
items = StockItem.objects.filter(StockItem.IN_STOCK_FILTER)
|
||||||
|
|
||||||
# Filter by Part association
|
# Filter serialized stock items
|
||||||
|
items = items.exclude(serial__isnull=True).exclude(serial__exact='')
|
||||||
|
|
||||||
# Look at GET params
|
if self.part:
|
||||||
part_id = self.request.GET.get('part', None)
|
# Filter for parts to install this item in
|
||||||
|
if self.install_in:
|
||||||
|
# Get parts using this part
|
||||||
|
allowed_parts = self.part.get_used_in()
|
||||||
|
# Filter
|
||||||
|
items = items.filter(part__in=allowed_parts)
|
||||||
|
|
||||||
if part_id is None:
|
# Filter for parts to install in this item
|
||||||
# Look at POST params
|
if self.install_item:
|
||||||
part_id = self.request.POST.get('part', None)
|
# Get parts used in this part's BOM
|
||||||
|
bom_items = self.part.get_bom_items()
|
||||||
try:
|
allowed_parts = [item.sub_part for item in bom_items]
|
||||||
self.part = Part.objects.get(pk=part_id)
|
# Filter
|
||||||
items = items.filter(part=self.part)
|
items = items.filter(part__in=allowed_parts)
|
||||||
except (ValueError, Part.DoesNotExist):
|
|
||||||
self.part = None
|
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
""" Retrieve parameters and update context """
|
||||||
|
|
||||||
|
ctx = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
# Get request parameters
|
||||||
|
self.get_params()
|
||||||
|
|
||||||
|
ctx.update({
|
||||||
|
'part': self.part,
|
||||||
|
'install_in': self.install_in,
|
||||||
|
'install_item': self.install_item,
|
||||||
|
})
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
|
|
||||||
initials = super().get_initial()
|
initials = super().get_initial()
|
||||||
@ -541,11 +613,16 @@ class StockItemInstall(AjaxUpdateView):
|
|||||||
if items.count() == 1:
|
if items.count() == 1:
|
||||||
item = items.first()
|
item = items.first()
|
||||||
initials['stock_item'] = item.pk
|
initials['stock_item'] = item.pk
|
||||||
initials['quantity_to_install'] = item.quantity
|
|
||||||
|
|
||||||
if self.part:
|
if self.part:
|
||||||
initials['part'] = self.part
|
initials['part'] = self.part
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Is this stock item being installed in the other stock item?
|
||||||
|
initials['to_install'] = self.install_in or not self.install_item
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
return initials
|
return initials
|
||||||
|
|
||||||
def get_form(self):
|
def get_form(self):
|
||||||
@ -558,6 +635,8 @@ class StockItemInstall(AjaxUpdateView):
|
|||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
self.get_params()
|
||||||
|
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
|
|
||||||
valid = form.is_valid()
|
valid = form.is_valid()
|
||||||
@ -567,12 +646,18 @@ class StockItemInstall(AjaxUpdateView):
|
|||||||
data = form.cleaned_data
|
data = form.cleaned_data
|
||||||
|
|
||||||
other_stock_item = data['stock_item']
|
other_stock_item = data['stock_item']
|
||||||
quantity = data['quantity_to_install']
|
# Quantity will always be 1 for serialized item
|
||||||
|
quantity = 1
|
||||||
notes = data['notes']
|
notes = data['notes']
|
||||||
|
|
||||||
# Install the other stock item into this one
|
# Get stock item
|
||||||
this_stock_item = self.get_object()
|
this_stock_item = self.get_object()
|
||||||
|
|
||||||
|
if data['to_install']:
|
||||||
|
# Install this stock item into the other stock item
|
||||||
|
other_stock_item.installStockItem(this_stock_item, quantity, request.user, notes)
|
||||||
|
else:
|
||||||
|
# Install the other stock item into this one
|
||||||
this_stock_item.installStockItem(other_stock_item, quantity, request.user, notes)
|
this_stock_item.installStockItem(other_stock_item, quantity, request.user, notes)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
@ -93,59 +93,110 @@ function addHeaderAction(label, title, icon, options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
{% if roles.part.view %}
|
{% settings_value 'HOMEPAGE_PART_STARRED' user=request.user as setting_part_starred %}
|
||||||
|
{% settings_value 'HOMEPAGE_PART_LATEST' user=request.user as setting_part_latest %}
|
||||||
|
{% settings_value 'HOMEPAGE_BOM_VALIDATION' user=request.user as setting_bom_validation %}
|
||||||
|
{% to_list setting_part_starred setting_part_latest setting_bom_validation as settings_list_part %}
|
||||||
|
|
||||||
|
{% if roles.part.view and True in settings_list_part %}
|
||||||
addHeaderTitle('{% trans "Parts" %}');
|
addHeaderTitle('{% trans "Parts" %}');
|
||||||
|
|
||||||
|
{% if setting_part_starred %}
|
||||||
addHeaderAction('starred-parts', '{% trans "Starred Parts" %}', 'fa-star');
|
addHeaderAction('starred-parts', '{% trans "Starred Parts" %}', 'fa-star');
|
||||||
addHeaderAction('latest-parts', '{% trans "Latest Parts" %}', 'fa-newspaper');
|
|
||||||
addHeaderAction('bom-validation', '{% trans "BOM Waiting Validation" %}', 'fa-times-circle');
|
|
||||||
|
|
||||||
|
|
||||||
loadSimplePartTable("#table-latest-parts", "{% url 'api-part-list' %}", {
|
|
||||||
params: {
|
|
||||||
ordering: "-creation_date",
|
|
||||||
max_results: {% settings_value "PART_RECENT_COUNT" %},
|
|
||||||
},
|
|
||||||
name: 'latest_parts',
|
|
||||||
});
|
|
||||||
|
|
||||||
loadSimplePartTable("#table-starred-parts", "{% url 'api-part-list' %}", {
|
loadSimplePartTable("#table-starred-parts", "{% url 'api-part-list' %}", {
|
||||||
params: {
|
params: {
|
||||||
"starred": true,
|
"starred": true,
|
||||||
},
|
},
|
||||||
name: 'starred_parts',
|
name: 'starred_parts',
|
||||||
});
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if setting_part_latest %}
|
||||||
|
addHeaderAction('latest-parts', '{% trans "Latest Parts" %}', 'fa-newspaper');
|
||||||
|
loadSimplePartTable("#table-latest-parts", "{% url 'api-part-list' %}", {
|
||||||
|
params: {
|
||||||
|
ordering: "-creation_date",
|
||||||
|
max_results: {% settings_value "PART_RECENT_COUNT" user=request.user %},
|
||||||
|
},
|
||||||
|
name: 'latest_parts',
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if setting_bom_validation %}
|
||||||
|
addHeaderAction('bom-validation', '{% trans "BOM Waiting Validation" %}', 'fa-times-circle');
|
||||||
loadSimplePartTable("#table-bom-validation", "{% url 'api-part-list' %}", {
|
loadSimplePartTable("#table-bom-validation", "{% url 'api-part-list' %}", {
|
||||||
params: {
|
params: {
|
||||||
"bom_valid": false,
|
"bom_valid": false,
|
||||||
},
|
},
|
||||||
name: 'bom_invalid_parts',
|
name: 'bom_invalid_parts',
|
||||||
});
|
});
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if roles.stock.view %}
|
{% settings_value 'HOMEPAGE_STOCK_RECENT' user=request.user as setting_stock_recent %}
|
||||||
addHeaderTitle('{% trans "Stock" %}');
|
{% settings_value 'HOMEPAGE_STOCK_LOW' user=request.user as setting_stock_low %}
|
||||||
addHeaderAction('recently-updated-stock', '{% trans "Recently Updated" %}', 'fa-clock');
|
{% settings_value 'HOMEPAGE_STOCK_DEPLETED' user=request.user as setting_stock_depleted %}
|
||||||
addHeaderAction('low-stock', '{% trans "Low Stock" %}', 'fa-shopping-cart');
|
{% settings_value 'HOMEPAGE_STOCK_NEEDED' user=request.user as setting_stock_needed %}
|
||||||
addHeaderAction('depleted-stock', '{% trans "Depleted Stock" %}', 'fa-times');
|
{% settings_value "STOCK_ENABLE_EXPIRY" as expiry %}
|
||||||
addHeaderAction('stock-to-build', '{% trans "Required for Build Orders" %}', 'fa-bullhorn');
|
{% if expiry %}
|
||||||
|
{% settings_value 'HOMEPAGE_STOCK_EXPIRED' user=request.user as setting_stock_expired %}
|
||||||
|
{% settings_value 'HOMEPAGE_STOCK_STALE' user=request.user as setting_stock_stale %}
|
||||||
|
{% to_list setting_stock_recent setting_stock_low setting_stock_depleted setting_stock_needed setting_stock_expired setting_stock_stale as settings_list_stock %}
|
||||||
|
{% else %}
|
||||||
|
{% to_list setting_stock_recent setting_stock_low setting_stock_depleted setting_stock_needed as settings_list_stock %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if roles.stock.view and True in settings_list_stock %}
|
||||||
|
addHeaderTitle('{% trans "Stock" %}');
|
||||||
|
|
||||||
|
{% if setting_stock_recent %}
|
||||||
|
addHeaderAction('recently-updated-stock', '{% trans "Recently Updated" %}', 'fa-clock');
|
||||||
loadStockTable($('#table-recently-updated-stock'), {
|
loadStockTable($('#table-recently-updated-stock'), {
|
||||||
params: {
|
params: {
|
||||||
part_detail: true,
|
part_detail: true,
|
||||||
ordering: "-updated",
|
ordering: "-updated",
|
||||||
max_results: {% settings_value "STOCK_RECENT_COUNT" %},
|
max_results: {% settings_value "STOCK_RECENT_COUNT" user=request.user %},
|
||||||
},
|
},
|
||||||
name: 'recently-updated-stock',
|
name: 'recently-updated-stock',
|
||||||
grouping: false,
|
grouping: false,
|
||||||
});
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if setting_stock_low %}
|
||||||
|
addHeaderAction('low-stock', '{% trans "Low Stock" %}', 'fa-shopping-cart');
|
||||||
|
loadSimplePartTable("#table-low-stock", "{% url 'api-part-list' %}", {
|
||||||
|
params: {
|
||||||
|
low_stock: true,
|
||||||
|
},
|
||||||
|
name: "low_stock_parts",
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if setting_stock_depleted %}
|
||||||
|
addHeaderAction('depleted-stock', '{% trans "Depleted Stock" %}', 'fa-times');
|
||||||
|
loadSimplePartTable("#table-depleted-stock", "{% url 'api-part-list' %}", {
|
||||||
|
params: {
|
||||||
|
depleted_stock: true,
|
||||||
|
},
|
||||||
|
name: "depleted_stock_parts",
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if setting_stock_needed %}
|
||||||
|
addHeaderAction('stock-to-build', '{% trans "Required for Build Orders" %}', 'fa-bullhorn');
|
||||||
|
loadSimplePartTable("#table-stock-to-build", "{% url 'api-part-list' %}", {
|
||||||
|
params: {
|
||||||
|
stock_to_build: true,
|
||||||
|
},
|
||||||
|
name: "to_build_parts",
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% settings_value "STOCK_ENABLE_EXPIRY" as expiry %}
|
|
||||||
{% if expiry %}
|
{% if expiry %}
|
||||||
addHeaderAction('expired-stock', '{% trans "Expired Stock" %}', 'fa-calendar-times');
|
|
||||||
addHeaderAction('stale-stock', '{% trans "Stale Stock" %}', 'fa-stopwatch');
|
|
||||||
|
|
||||||
|
{% if setting_stock_expired %}
|
||||||
|
addHeaderAction('expired-stock', '{% trans "Expired Stock" %}', 'fa-calendar-times');
|
||||||
loadStockTable($("#table-expired-stock"), {
|
loadStockTable($("#table-expired-stock"), {
|
||||||
params: {
|
params: {
|
||||||
expired: true,
|
expired: true,
|
||||||
@ -153,7 +204,10 @@ loadStockTable($("#table-expired-stock"), {
|
|||||||
part_detail: true,
|
part_detail: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if setting_stock_stale %}
|
||||||
|
addHeaderAction('stale-stock', '{% trans "Stale Stock" %}', 'fa-stopwatch');
|
||||||
loadStockTable($("#table-stale-stock"), {
|
loadStockTable($("#table-stale-stock"), {
|
||||||
params: {
|
params: {
|
||||||
stale: true,
|
stale: true,
|
||||||
@ -164,34 +218,18 @@ loadStockTable($("#table-stale-stock"), {
|
|||||||
});
|
});
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
loadSimplePartTable("#table-low-stock", "{% url 'api-part-list' %}", {
|
{% endif %}
|
||||||
params: {
|
|
||||||
low_stock: true,
|
|
||||||
},
|
|
||||||
name: "low_stock_parts",
|
|
||||||
});
|
|
||||||
|
|
||||||
loadSimplePartTable("#table-depleted-stock", "{% url 'api-part-list' %}", {
|
|
||||||
params: {
|
|
||||||
depleted_stock: true,
|
|
||||||
},
|
|
||||||
name: "depleted_stock_parts",
|
|
||||||
});
|
|
||||||
|
|
||||||
loadSimplePartTable("#table-stock-to-build", "{% url 'api-part-list' %}", {
|
|
||||||
params: {
|
|
||||||
stock_to_build: true,
|
|
||||||
},
|
|
||||||
name: "to_build_parts",
|
|
||||||
});
|
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if roles.build.view %}
|
{% settings_value 'HOMEPAGE_BUILD_PENDING' user=request.user as setting_build_pending %}
|
||||||
addHeaderTitle('{% trans "Build Orders" %}');
|
{% settings_value 'HOMEPAGE_BUILD_OVERDUE' user=request.user as setting_build_overdue %}
|
||||||
addHeaderAction('build-pending', '{% trans "Build Orders In Progress" %}', 'fa-cogs');
|
{% to_list setting_build_pending setting_build_overdue as settings_list_build %}
|
||||||
addHeaderAction('build-overdue', '{% trans "Overdue Build Orders" %}', 'fa-calendar-times');
|
|
||||||
|
|
||||||
|
{% if roles.build.view and True in settings_list_build %}
|
||||||
|
addHeaderTitle('{% trans "Build Orders" %}');
|
||||||
|
|
||||||
|
{% if setting_build_pending %}
|
||||||
|
addHeaderAction('build-pending', '{% trans "Build Orders In Progress" %}', 'fa-cogs');
|
||||||
loadBuildTable("#table-build-pending", {
|
loadBuildTable("#table-build-pending", {
|
||||||
url: "{% url 'api-build-list' %}",
|
url: "{% url 'api-build-list' %}",
|
||||||
params: {
|
params: {
|
||||||
@ -199,7 +237,10 @@ loadBuildTable("#table-build-pending", {
|
|||||||
},
|
},
|
||||||
disableFilters: true,
|
disableFilters: true,
|
||||||
});
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if setting_build_overdue %}
|
||||||
|
addHeaderAction('build-overdue', '{% trans "Overdue Build Orders" %}', 'fa-calendar-times');
|
||||||
loadBuildTable("#table-build-overdue", {
|
loadBuildTable("#table-build-overdue", {
|
||||||
url: "{% url 'api-build-list' %}",
|
url: "{% url 'api-build-list' %}",
|
||||||
params: {
|
params: {
|
||||||
@ -209,11 +250,17 @@ loadBuildTable("#table-build-overdue", {
|
|||||||
});
|
});
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if roles.purchase_order.view %}
|
{% endif %}
|
||||||
addHeaderTitle('{% trans "Purchase Orders" %}');
|
|
||||||
addHeaderAction('po-outstanding', '{% trans "Outstanding Purchase Orders" %}', 'fa-sign-in-alt');
|
|
||||||
addHeaderAction('po-overdue', '{% trans "Overdue Purchase Orders" %}', 'fa-calendar-times');
|
|
||||||
|
|
||||||
|
{% settings_value 'HOMEPAGE_PO_OUTSTANDING' user=request.user as setting_po_outstanding %}
|
||||||
|
{% settings_value 'HOMEPAGE_PO_OVERDUE' user=request.user as setting_po_overdue %}
|
||||||
|
{% to_list setting_po_outstanding setting_po_overdue as settings_list_po %}
|
||||||
|
|
||||||
|
{% if roles.purchase_order.view and True in settings_list_po %}
|
||||||
|
addHeaderTitle('{% trans "Purchase Orders" %}');
|
||||||
|
|
||||||
|
{% if setting_po_outstanding %}
|
||||||
|
addHeaderAction('po-outstanding', '{% trans "Outstanding Purchase Orders" %}', 'fa-sign-in-alt');
|
||||||
loadPurchaseOrderTable("#table-po-outstanding", {
|
loadPurchaseOrderTable("#table-po-outstanding", {
|
||||||
url: "{% url 'api-po-list' %}",
|
url: "{% url 'api-po-list' %}",
|
||||||
params: {
|
params: {
|
||||||
@ -221,7 +268,10 @@ loadPurchaseOrderTable("#table-po-outstanding", {
|
|||||||
outstanding: true,
|
outstanding: true,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if setting_po_overdue %}
|
||||||
|
addHeaderAction('po-overdue', '{% trans "Overdue Purchase Orders" %}', 'fa-calendar-times');
|
||||||
loadPurchaseOrderTable("#table-po-overdue", {
|
loadPurchaseOrderTable("#table-po-overdue", {
|
||||||
url: "{% url 'api-po-list' %}",
|
url: "{% url 'api-po-list' %}",
|
||||||
params: {
|
params: {
|
||||||
@ -229,14 +279,19 @@ loadPurchaseOrderTable("#table-po-overdue", {
|
|||||||
overdue: true,
|
overdue: true,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if roles.sales_order.view %}
|
{% settings_value 'HOMEPAGE_SO_OUTSTANDING' user=request.user as setting_so_outstanding %}
|
||||||
addHeaderTitle('{% trans "Sales Orders" %}');
|
{% settings_value 'HOMEPAGE_SO_OVERDUE' user=request.user as setting_so_overdue %}
|
||||||
addHeaderAction('so-outstanding', '{% trans "Outstanding Sales Orders" %}', 'fa-sign-out-alt');
|
{% to_list setting_so_outstanding setting_so_overdue as settings_list_so %}
|
||||||
addHeaderAction('so-overdue', '{% trans "Overdue Sales Orders" %}', 'fa-calendar-times');
|
|
||||||
|
|
||||||
|
{% if roles.sales_order.view and True in settings_list_so %}
|
||||||
|
addHeaderTitle('{% trans "Sales Orders" %}');
|
||||||
|
|
||||||
|
{% if setting_so_outstanding %}
|
||||||
|
addHeaderAction('so-outstanding', '{% trans "Outstanding Sales Orders" %}', 'fa-sign-out-alt');
|
||||||
loadSalesOrderTable("#table-so-outstanding", {
|
loadSalesOrderTable("#table-so-outstanding", {
|
||||||
url: "{% url 'api-so-list' %}",
|
url: "{% url 'api-so-list' %}",
|
||||||
params: {
|
params: {
|
||||||
@ -244,7 +299,10 @@ loadSalesOrderTable("#table-so-outstanding", {
|
|||||||
outstanding: true,
|
outstanding: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if setting_so_overdue %}
|
||||||
|
addHeaderAction('so-overdue', '{% trans "Overdue Sales Orders" %}', 'fa-calendar-times');
|
||||||
loadSalesOrderTable("#table-so-overdue", {
|
loadSalesOrderTable("#table-so-overdue", {
|
||||||
url: "{% url 'api-so-list' %}",
|
url: "{% url 'api-so-list' %}",
|
||||||
params: {
|
params: {
|
||||||
@ -252,6 +310,7 @@ loadSalesOrderTable("#table-so-overdue", {
|
|||||||
customer_detail: true,
|
customer_detail: true,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
{% extends "InvenTree/settings/settings.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load inventree_extras %}
|
|
||||||
|
|
||||||
{% block tabs %}
|
|
||||||
{% include "InvenTree/settings/tabs.html" with tab='theme' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block subtitle %}
|
|
||||||
{% trans "Theme Settings" %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block settings %}
|
|
||||||
|
|
||||||
<div class='row'>
|
|
||||||
<div class='col-sm-6'>
|
|
||||||
<h4>{% trans "Color Themes" %}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form action="{% url 'settings-appearance' %}" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
{% load crispy_forms_tags %}
|
|
||||||
{% crispy form %}
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% if invalid_color_theme %}
|
|
||||||
<div class="alert alert-danger alert-block" role="alert" style="display: inline-block;">
|
|
||||||
{% blocktrans %}
|
|
||||||
The CSS sheet "{{invalid_color_theme}}.css" for the currently selected color theme was not found.<br>
|
|
||||||
Please select another color theme :)
|
|
||||||
{% endblocktrans %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
<div class='row'>
|
|
||||||
<div class='col-sm-6'>
|
|
||||||
<h4>{% trans "Language" %}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
|
|
||||||
<input name="next" type="hidden" value="{% url 'settings-appearance' %}">
|
|
||||||
<div class="col-sm-6" style="width: 200px;"><div id="div_id_name" class="form-group"><div class="controls ">
|
|
||||||
<select name="language" class="select form-control">
|
|
||||||
{% get_current_language as LANGUAGE_CODE %}
|
|
||||||
{% get_available_languages as LANGUAGES %}
|
|
||||||
{% get_language_info_list for LANGUAGES as languages %}
|
|
||||||
{% for language in languages %}
|
|
||||||
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
|
|
||||||
{{ language.name_local }} ({{ language.code }})
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div></div></div>
|
|
||||||
<div class="col-sm-6" style="width: auto;">
|
|
||||||
<input type="submit" value="{% trans 'Set Language' %}" class="btn btn btn-primary">
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
|
20
InvenTree/templates/InvenTree/settings/barcode.html
Normal file
20
InvenTree/templates/InvenTree/settings/barcode.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{% extends "panel.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load inventree_extras %}
|
||||||
|
|
||||||
|
{% block label %}barcodes{% endblock %}
|
||||||
|
|
||||||
|
{% block heading %}
|
||||||
|
{% trans "Barcode Settings" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<table class='table table-striped table-condensed'>
|
||||||
|
{% include "InvenTree/settings/header.html" %}
|
||||||
|
<tbody>
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="BARCODE_ENABLE" icon="fa-qrcode" %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -1,16 +1,14 @@
|
|||||||
{% extends "InvenTree/settings/settings.html" %}
|
{% extends "panel.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load inventree_extras %}
|
{% load inventree_extras %}
|
||||||
|
|
||||||
{% block tabs %}
|
{% block label %}build-order{% endblock %}
|
||||||
{% include "InvenTree/settings/tabs.html" with tab='build' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block heading %}
|
||||||
{% trans "Build Order Settings" %}
|
{% trans "Build Order Settings" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block settings %}
|
{% block content %}
|
||||||
|
|
||||||
<table class='table table-striped table-condensed'>
|
<table class='table table-striped table-condensed'>
|
||||||
{% include "InvenTree/settings/header.html" %}
|
{% include "InvenTree/settings/header.html" %}
|
||||||
|
@ -1,114 +1,33 @@
|
|||||||
{% extends "InvenTree/settings/settings.html" %}
|
{% extends "panel.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block tabs %}
|
{% block label %}category{% endblock %}
|
||||||
{% include "InvenTree/settings/tabs.html" with tab='category' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block heading %}
|
||||||
{% trans "Category Settings" %}
|
{% trans "Category Settings" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block settings %}
|
{% block content %}
|
||||||
|
|
||||||
<form action="{% url 'settings-category' %}" method="post">
|
<div class='row'>
|
||||||
{% csrf_token %}
|
<form action=''>
|
||||||
{% load crispy_forms_tags %}
|
<div class='col-sm-6' style='width: 250px'>
|
||||||
<div id="category-select">
|
<div class='form-group'><div class='controls'>
|
||||||
{% crispy form %}
|
<select name='category' id='category-select'>
|
||||||
|
<!-- Will be filled by API -->
|
||||||
|
</select>
|
||||||
|
</div></div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if category %}
|
<div id='cat-param-buttons'>
|
||||||
<hr>
|
<button class='btn btn-success' id='new-cat-param' disabled=''>
|
||||||
|
<div class='fas fa-plus-circle'></div> {% trans "New Parameter" %}
|
||||||
<h4>{% trans "Category Parameter Templates" %}</h4>
|
|
||||||
|
|
||||||
<div id='param-buttons'>
|
|
||||||
<button class='btn btn-success' id='new-param'>
|
|
||||||
<span class='fas fa-plus-circle'></span> {% trans "New Parameter" %}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class='table table-striped table-condensed' id='param-table' data-toolbar='#param-buttons'>
|
<table class='table table-striped table-condensed' id='cat-param-table' data-toolbar='#cat-param-buttons'>
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block js_ready %}
|
|
||||||
{{ block.super }}
|
|
||||||
|
|
||||||
{# Convert dropdown to select2 format #}
|
|
||||||
$(document).ready(function() {
|
|
||||||
attachSelect('#category-select');
|
|
||||||
});
|
|
||||||
|
|
||||||
{% if category %}
|
|
||||||
$("#param-table").inventreeTable({
|
|
||||||
url: "{% url 'api-part-category-parameters' pk=category.pk %}",
|
|
||||||
queryParams: {
|
|
||||||
ordering: 'name',
|
|
||||||
},
|
|
||||||
formatNoMatches: function() { return '{% trans "No category parameter templates found" %}'; },
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
field: 'pk',
|
|
||||||
title: 'ID',
|
|
||||||
visible: false,
|
|
||||||
switchable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'parameter_template.name',
|
|
||||||
title: '{% trans "Parameter Template" %}',
|
|
||||||
sortable: 'true',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'default_value',
|
|
||||||
title: '{% trans "Default Value" %}',
|
|
||||||
sortable: 'true',
|
|
||||||
formatter: function(value, row, index, field) {
|
|
||||||
var bEdit = "<button title='{% trans "Edit Template" %}' class='template-edit btn btn-default btn-glyph' type='button' pk='" + row.pk + "'><span class='fas fa-edit'></span></button>";
|
|
||||||
var bDel = "<button title='{% trans "Delete Template" %}' class='template-delete btn btn-default btn-glyph' type='button' pk='" + row.pk + "'><span class='fas fa-trash-alt icon-red'></span></button>";
|
|
||||||
|
|
||||||
var html = value
|
|
||||||
html += "<div class='btn-group float-right' role='group'>" + bEdit + bDel + "</div>";
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#new-param").click(function() {
|
|
||||||
launchModalForm("{% url 'category-param-template-create' category.pk %}", {
|
|
||||||
success: function() {
|
|
||||||
$("#param-table").bootstrapTable('refresh');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#param-table").on('click', '.template-edit', function() {
|
|
||||||
var button = $(this);
|
|
||||||
|
|
||||||
var url = "/part/category/{{ category.pk }}/parameters/" + button.attr('pk') + "/edit/";
|
|
||||||
|
|
||||||
launchModalForm(url, {
|
|
||||||
success: function() {
|
|
||||||
$("#param-table").bootstrapTable('refresh');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#param-table").on('click', '.template-delete', function() {
|
|
||||||
var button = $(this);
|
|
||||||
|
|
||||||
var url = "/part/category/{{ category.pk }}/parameters/" + button.attr('pk') + "/delete/";
|
|
||||||
|
|
||||||
launchModalForm(url, {
|
|
||||||
success: function() {
|
|
||||||
$("#param-table").bootstrapTable('refresh');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
{% extends "InvenTree/settings/settings.html" %}
|
{% extends "panel.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load inventree_extras %}
|
{% load inventree_extras %}
|
||||||
|
|
||||||
{% block tabs %}
|
{% block label %}currencies{% endblock %}
|
||||||
{% include "InvenTree/settings/tabs.html" with tab='currencies' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block heading %}
|
||||||
{% trans "Currency Settings" %}
|
{% trans "Currency Settings" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block settings %}
|
{% block content %}
|
||||||
|
|
||||||
<table class='table table-striped table-condensed'>
|
<table class='table table-striped table-condensed'>
|
||||||
{% include "InvenTree/settings/header.html" %}
|
{% include "InvenTree/settings/header.html" %}
|
||||||
@ -56,7 +54,3 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js_ready %}
|
|
||||||
{{ block.super }}
|
|
||||||
{% endblock %}
|
|
@ -1,16 +1,15 @@
|
|||||||
{% extends "InvenTree/settings/settings.html" %}
|
{% extends "panel.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load inventree_extras %}
|
{% load inventree_extras %}
|
||||||
|
|
||||||
{% block tabs %}
|
{% block label %}server{% endblock %}
|
||||||
{% include "InvenTree/settings/tabs.html" with tab='global' %}
|
|
||||||
|
|
||||||
|
{% block heading %}
|
||||||
|
{% trans "Server Settings" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block content %}
|
||||||
{% trans "Global InvenTree Settings" %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block settings %}
|
|
||||||
|
|
||||||
<table class='table table-striped table-condensed'>
|
<table class='table table-striped table-condensed'>
|
||||||
{% include "InvenTree/settings/header.html" %}
|
{% include "InvenTree/settings/header.html" %}
|
||||||
@ -23,19 +22,4 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h4>{% trans "Barcode Settings" %}</h4>
|
|
||||||
<table class='table table-striped table-condensed'>
|
|
||||||
{% include "InvenTree/settings/header.html" %}
|
|
||||||
<tbody>
|
|
||||||
{% include "InvenTree/settings/setting.html" with key="BARCODE_ENABLE" icon="fa-qrcode" %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h4>{% trans "Search Settings" %}</h4>
|
|
||||||
<table class='table table-striped table-condensed'>
|
|
||||||
<tbody>
|
|
||||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_RESULTS" icon="fa-search" %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
109
InvenTree/templates/InvenTree/settings/navbar.html
Normal file
109
InvenTree/templates/InvenTree/settings/navbar.html
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<ul class='list-group'>
|
||||||
|
|
||||||
|
<li class='list-group-item'>
|
||||||
|
<a href='#' id='item-menu-toggle'>
|
||||||
|
<span class='menu-tab-icon fas fa-expand-arrows-alt'></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item'>
|
||||||
|
<b>{% trans "User Settings" %}</b>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item' title='{% trans "Account" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-account'>
|
||||||
|
<span class='fas fa-user'></span> {% trans "Account" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item' title='{% trans "Home Page" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-user-home'>
|
||||||
|
<span class='fas fa-home'></span> {% trans "Home Page" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item' title='{% trans "Search" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-user-search'>
|
||||||
|
<span class='fas fa-search'></span> {% trans "Search" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<li class='list-group-item' title='{% trans "Settings" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-user-settings'>
|
||||||
|
<span class='fas fa-cog'></span> {% trans "Settings" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
-->
|
||||||
|
|
||||||
|
{% if user.is_staff %}
|
||||||
|
|
||||||
|
<li class='list-group-item'>
|
||||||
|
<b>{% trans "InvenTree Settings" %}</b>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item' title='{% trans "Server" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-server'>
|
||||||
|
<span class='fas fa-server'></span> {% trans "Server" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item' title='{% trans "Barcodes" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-barcodes'>
|
||||||
|
<span class='fas fa-qrcode'></span> {% trans "Barcodes" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item' title='{% trans "Currencies" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-currencies'>
|
||||||
|
<span class='fas fa-dollar-sign'></span> {% trans "Currencies" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item' title='{% trans "Reporting" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-reporting'>
|
||||||
|
<span class='fas fa-file-pdf'></span> {% trans "Reporting" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item' title='{% trans "Parts" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-parts'>
|
||||||
|
<span class='fas fa-shapes'></span> {% trans "Parts" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item' title='{% trans "Categories" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-category'>
|
||||||
|
<span class='fas fa-shapes'></span> {% trans "Categories" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item' title='{% trans "Stock" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-stock'>
|
||||||
|
<span class='fas fa-boxes'></span> {% trans "Stock" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item' title='{% trans "Build Orders" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-build-order'>
|
||||||
|
<span class='fas fa-tools'></span> {% trans "Build Orders" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item' title='{% trans "Purchase Orders" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-purchase-order'>
|
||||||
|
<span class='fas fa-shopping-cart'></span> {% trans "Purchase Orders" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class='list-group-item' title='{% trans "Sales Orders" %}'>
|
||||||
|
<a href='#' class='nav-toggle' id='select-sales-order'>
|
||||||
|
<span class='fas fa-truck'></span> {% trans "Sales Orders" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</ul>
|
@ -1,15 +1,13 @@
|
|||||||
{% extends "InvenTree/settings/settings.html" %}
|
{% extends "panel.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block tabs %}
|
{% block label %}parts{% endblock %}
|
||||||
{% include "InvenTree/settings/tabs.html" with tab='part' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block heading %}
|
||||||
{% trans "Part Settings" %}
|
{% trans "Part Settings" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block settings %}
|
{% block content %}
|
||||||
|
|
||||||
<h4>{% trans "Part Options" %}</h4>
|
<h4>{% trans "Part Options" %}</h4>
|
||||||
|
|
||||||
@ -22,7 +20,6 @@
|
|||||||
{% include "InvenTree/settings/setting.html" with key="PART_SHOW_QUANTITY_IN_FORMS" icon="fa-hashtag" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_SHOW_QUANTITY_IN_FORMS" icon="fa-hashtag" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_SHOW_PRICE_IN_FORMS" icon="fa-dollar-sign" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_SHOW_PRICE_IN_FORMS" icon="fa-dollar-sign" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_SHOW_RELATED" icon="fa-random" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_SHOW_RELATED" icon="fa-random" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_RECENT_COUNT" icon="fa-clock" %}
|
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_CREATE_INITIAL" icon="fa-boxes" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_CREATE_INITIAL" icon="fa-boxes" %}
|
||||||
<tr><td colspan='5'></td></tr>
|
<tr><td colspan='5'></td></tr>
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_TEMPLATE" icon="fa-clone" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_TEMPLATE" icon="fa-clone" %}
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
{% extends "InvenTree/settings/settings.html" %}
|
{% extends "panel.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block tabs %}
|
{% block label %}purchase-order{% endblock %}
|
||||||
{% include "InvenTree/settings/tabs.html" with tab='po' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block heading %}
|
||||||
{% trans "Purchase Order Settings" %}
|
{% trans "Purchase Order Settings" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block settings %}
|
{% block content %}
|
||||||
<table class='table table-striped table-condensed'>
|
<table class='table table-striped table-condensed'>
|
||||||
{% include "InvenTree/settings/header.html" %}
|
{% include "InvenTree/settings/header.html" %}
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
{% extends "InvenTree/settings/settings.html" %}
|
{% extends "panel.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load inventree_extras %}
|
{% load inventree_extras %}
|
||||||
|
|
||||||
{% block tabs %}
|
{% block label %}reporting{% endblock %}
|
||||||
{% include "InvenTree/settings/tabs.html" with tab='report' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block heading %}
|
||||||
{% trans "Report Settings" %}
|
{% trans "Report Settings" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block settings %}
|
{% block content %}
|
||||||
|
|
||||||
<table class='table table-striped table-condensed'>
|
<table class='table table-striped table-condensed'>
|
||||||
{% include "InvenTree/settings/header.html" %}
|
{% include "InvenTree/settings/header.html" %}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
{% load inventree_extras %}
|
{% load inventree_extras %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% if user_setting %}
|
||||||
|
{% setting_object key user=request.user as setting %}
|
||||||
|
{% else %}
|
||||||
{% setting_object key as setting %}
|
{% setting_object key as setting %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{% if icon %}
|
{% if icon %}
|
||||||
@ -12,23 +17,28 @@
|
|||||||
<td>
|
<td>
|
||||||
{% if setting.is_bool %}
|
{% if setting.is_bool %}
|
||||||
<div>
|
<div>
|
||||||
<input fieldname='{{ setting.key }}' class='slidey' type='checkbox' data-offstyle='warning' data-onstyle='success' data-size='small' data-toggle='toggle' disabled autocomplete='off' {% if setting.as_bool %}checked=''{% endif %}>
|
<input fieldname='{{ setting.key.upper }}' id='setting-value-{{ setting.key.upper }}' type='checkbox' disabled='' {% if setting.as_bool %}checked=''{% endif %}>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
<div id='setting-{{ setting.pk }}'>
|
||||||
|
<b>
|
||||||
|
<span id='setting-value-{{ setting.key.upper }}' fieldname='{{ setting.key.upper }}'>
|
||||||
{% if setting.value %}
|
{% if setting.value %}
|
||||||
<i><b>
|
{{ setting.value }}
|
||||||
{{ setting.value }}</b> {{ setting.units }}
|
|
||||||
</i>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<i>{% trans "No value set" %}</i>
|
<i>{% trans "No value set" %}</i>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</b>
|
||||||
|
{{ setting.units }}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td>
|
<td>
|
||||||
{% trans setting.description %}
|
{% trans setting.description %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class='btn-group float-right'>
|
<div class='btn-group float-right'>
|
||||||
<button class='btn btn-default btn-glyph btn-edit-setting' pk='{{ setting.pk }}' setting='{{ key }}' title='{% trans "Edit setting" %}'>
|
<button class='btn btn-default btn-glyph btn-edit-setting' pk='{{ setting.pk }}' setting='{{ setting.key.upper }}' title='{% trans "Edit setting" %}' {% if user_setting %}user='{{request.user.id}}'{% endif %}>
|
||||||
<span class='fas fa-edit icon-green'></span>
|
<span class='fas fa-edit icon-green'></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,30 +8,31 @@
|
|||||||
{% inventree_title %} | {% trans "Settings" %}
|
{% inventree_title %} | {% trans "Settings" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block menubar %}
|
||||||
|
{% include "InvenTree/settings/navbar.html" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class='settings-container'>
|
|
||||||
|
|
||||||
<h3>InvenTree {% trans "Settings" %}</h3>
|
{% include "InvenTree/settings/user.html" %}
|
||||||
<hr>
|
{% include "InvenTree/settings/user_settings.html" %}
|
||||||
|
{% include "InvenTree/settings/user_homepage.html" %}
|
||||||
|
{% include "InvenTree/settings/user_search.html" %}
|
||||||
|
|
||||||
<div class='settings-nav'>
|
{% if user.is_staff %}
|
||||||
{% block tabs %}
|
|
||||||
{% include "InvenTree/settings/tabs.html" %}
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='settings-content'>
|
{% include "InvenTree/settings/global.html" %}
|
||||||
<h3>
|
{% include "InvenTree/settings/barcode.html" %}
|
||||||
{% block subtitle %}
|
{% include "InvenTree/settings/currencies.html" %}
|
||||||
SUBTITLE GOES HERE
|
{% include "InvenTree/settings/report.html" %}
|
||||||
{% endblock %}
|
{% include "InvenTree/settings/part.html" %}
|
||||||
</h3>
|
{% include "InvenTree/settings/category.html" %}
|
||||||
<hr>
|
{% include "InvenTree/settings/stock.html" %}
|
||||||
{% block settings %}
|
{% include "InvenTree/settings/build.html" %}
|
||||||
{% endblock %}
|
{% include "InvenTree/settings/po.html" %}
|
||||||
</div>
|
{% include "InvenTree/settings/so.html" %}
|
||||||
|
|
||||||
</div>
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@ -45,13 +46,209 @@
|
|||||||
$('table').find('.btn-edit-setting').click(function() {
|
$('table').find('.btn-edit-setting').click(function() {
|
||||||
var setting = $(this).attr('setting');
|
var setting = $(this).attr('setting');
|
||||||
var pk = $(this).attr('pk');
|
var pk = $(this).attr('pk');
|
||||||
|
var url = `/settings/${pk}/edit/`;
|
||||||
|
|
||||||
|
if ($(this).attr('user')){
|
||||||
|
url += `user/`;
|
||||||
|
}
|
||||||
|
|
||||||
launchModalForm(
|
launchModalForm(
|
||||||
`/settings/${pk}/edit/`,
|
url,
|
||||||
|
{
|
||||||
|
success: function(response) {
|
||||||
|
|
||||||
|
if (response.is_bool) {
|
||||||
|
var enabled = response.value.toLowerCase() == 'true';
|
||||||
|
$(`#setting-value-${setting}`).prop('checked', enabled);
|
||||||
|
} else {
|
||||||
|
$(`#setting-value-${setting}`).html(response.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#edit-user").on('click', function() {
|
||||||
|
launchModalForm(
|
||||||
|
"{% url 'edit-user' %}",
|
||||||
{
|
{
|
||||||
reload: true,
|
reload: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#edit-password").on('click', function() {
|
||||||
|
launchModalForm(
|
||||||
|
"{% url 'set-password' %}",
|
||||||
|
{
|
||||||
|
reload: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$('#category-select').select2({
|
||||||
|
placeholder: '',
|
||||||
|
width: '100%',
|
||||||
|
ajax: {
|
||||||
|
url: '{% url "api-part-category-list" %}',
|
||||||
|
dataType: 'json',
|
||||||
|
delay: 250,
|
||||||
|
cache: false,
|
||||||
|
data: function(params) {
|
||||||
|
if (!params.page) {
|
||||||
|
offset = 0;
|
||||||
|
} else {
|
||||||
|
offset = (params.page - 1) * 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
search: params.term,
|
||||||
|
offset: offset,
|
||||||
|
limit: 25,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
processResults: function(response) {
|
||||||
|
var data = [];
|
||||||
|
|
||||||
|
var more = false;
|
||||||
|
|
||||||
|
if ('count' in response && 'results' in response) {
|
||||||
|
// Response is paginated
|
||||||
|
data = response.results;
|
||||||
|
|
||||||
|
// Any more data available?
|
||||||
|
if (response.next) {
|
||||||
|
more = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Non-paginated response
|
||||||
|
data = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each 'row' must have the 'id' attribute
|
||||||
|
for (var idx = 0; idx < data.length; idx++) {
|
||||||
|
data[idx].id = data[idx].pk;
|
||||||
|
data[idx].text = data[idx].pathstring;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ref: https://select2.org/data-sources/formats
|
||||||
|
var results = {
|
||||||
|
results: data,
|
||||||
|
pagination: {
|
||||||
|
more: more,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#cat-param-table').inventreeTable({
|
||||||
|
formatNoMatches: function() { return '{% trans "No category parameter templates found" %}'; },
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
field: 'pk',
|
||||||
|
title: 'ID',
|
||||||
|
visible: false,
|
||||||
|
switchable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'parameter_template.name',
|
||||||
|
title: '{% trans "Parameter Template" %}',
|
||||||
|
sortable: 'true',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'category_detail.pathstring',
|
||||||
|
title: '{% trans "Category" %}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'default_value',
|
||||||
|
title: '{% trans "Default Value" %}',
|
||||||
|
sortable: 'true',
|
||||||
|
formatter: function(value, row, index, field) {
|
||||||
|
var bEdit = "<button title='{% trans "Edit Template" %}' class='template-edit btn btn-default btn-glyph' type='button' pk='" + row.pk + "'><span class='fas fa-edit'></span></button>";
|
||||||
|
var bDel = "<button title='{% trans "Delete Template" %}' class='template-delete btn btn-default btn-glyph' type='button' pk='" + row.pk + "'><span class='fas fa-trash-alt icon-red'></span></button>";
|
||||||
|
|
||||||
|
var html = value
|
||||||
|
html += "<div class='btn-group float-right' role='group'>" + bEdit + bDel + "</div>";
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
function loadTemplateTable(pk) {
|
||||||
|
|
||||||
|
console.log('refresh:', pk);
|
||||||
|
|
||||||
|
// Enable the buttons
|
||||||
|
$('#new-cat-param').removeAttr('disabled');
|
||||||
|
|
||||||
|
// Load the parameter table
|
||||||
|
$("#cat-param-table").bootstrapTable('refresh', {
|
||||||
|
query: {
|
||||||
|
category: pk,
|
||||||
|
},
|
||||||
|
url: '{% url "api-part-category-parameter-list" %}',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$('body').on('change', '#category-select', function() {
|
||||||
|
var pk = $(this).val();
|
||||||
|
loadTemplateTable(pk);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#new-cat-param").click(function() {
|
||||||
|
|
||||||
|
var pk = $('#category-select').val();
|
||||||
|
|
||||||
|
launchModalForm(`/part/category/${pk}/parameters/new/`, {
|
||||||
|
success: function() {
|
||||||
|
$("#cat-param-table").bootstrapTable('refresh');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#cat-param-table").on('click', '.template-edit', function() {
|
||||||
|
|
||||||
|
var category = $('#category-select').val();
|
||||||
|
var pk = $(this).attr('pk');
|
||||||
|
|
||||||
|
var url = `/part/category/${category}/parameters/${pk}/edit/`;
|
||||||
|
|
||||||
|
launchModalForm(url, {
|
||||||
|
success: function() {
|
||||||
|
$("#cat-param-table").bootstrapTable('refresh');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#cat-param-table").on('click', '.template-delete', function() {
|
||||||
|
|
||||||
|
var category = $('#category-select').val();
|
||||||
|
var pk = $(this).attr('pk');
|
||||||
|
|
||||||
|
var url = `/part/category/${category}/parameters/${pk}/delete/`;
|
||||||
|
|
||||||
|
launchModalForm(url, {
|
||||||
|
success: function() {
|
||||||
|
$("#cat-param-table").bootstrapTable('refresh');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
enableNavbar({
|
||||||
|
label: 'settings',
|
||||||
|
toggleId: '#item-menu-toggle',
|
||||||
|
});
|
||||||
|
|
||||||
|
attachNavCallbacks({
|
||||||
|
name: 'settings',
|
||||||
|
default: 'account'
|
||||||
|
});
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
{% extends "InvenTree/settings/settings.html" %}
|
{% extends "panel.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block tabs %}
|
{% block label %}sales-order{% endblock %}
|
||||||
{% include "InvenTree/settings/tabs.html" with tab='so' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block heading %}
|
||||||
{% trans "Sales Order Settings" %}
|
{% trans "Sales Order Settings" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block settings %}
|
{% block content %}
|
||||||
|
|
||||||
<table class='table table-striped table-condensed'>
|
<table class='table table-striped table-condensed'>
|
||||||
{% include "InvenTree/settings/header.html" %}
|
{% include "InvenTree/settings/header.html" %}
|
||||||
|
@ -1,22 +1,18 @@
|
|||||||
{% extends "InvenTree/settings/settings.html" %}
|
{% extends "panel.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block tabs %}
|
{% block label %}stock{% endblock %}
|
||||||
{% include "InvenTree/settings/tabs.html" with tab='stock' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block heading %}
|
||||||
{% trans "Stock Settings" %}
|
{% trans "Stock Settings" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block settings %}
|
{% block content %}
|
||||||
<h4>{% trans "Stock Options" %}</h4>
|
|
||||||
|
|
||||||
<table class='table table-striped table-condensed'>
|
<table class='table table-striped table-condensed'>
|
||||||
{% include "InvenTree/settings/header.html" %}
|
{% include "InvenTree/settings/header.html" %}
|
||||||
<tbody>
|
<tbody>
|
||||||
{% include "InvenTree/settings/setting.html" with key="STOCK_GROUP_BY_PART" icon="fa-layer-group" %}
|
{% include "InvenTree/settings/setting.html" with key="STOCK_GROUP_BY_PART" icon="fa-layer-group" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="STOCK_RECENT_COUNT" icon="fa-clock" %}
|
|
||||||
{% include "InvenTree/settings/setting.html" with key="STOCK_ENABLE_EXPIRY" icon="fa-stopwatch" %}
|
{% 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_STALE_DAYS" icon="fa-calendar" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="STOCK_ALLOW_EXPIRED_SALE" icon="fa-truck" %}
|
{% include "InvenTree/settings/setting.html" with key="STOCK_ALLOW_EXPIRED_SALE" icon="fa-truck" %}
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
{% load i18n %}
|
|
||||||
|
|
||||||
<h4><span class='fas fa-user'></span> {% trans "User Settings" %}</h4>
|
|
||||||
<ul class='nav nav-pills nav-stacked'>
|
|
||||||
<li{% ifequal tab 'user' %} class='active'{% endifequal %}>
|
|
||||||
<a href="{% url 'settings-user' %}"><span class='fas fa-user'></span> {% trans "Account" %}</a>
|
|
||||||
</li>
|
|
||||||
<li{% ifequal tab 'theme' %} class='active'{% endifequal %}>
|
|
||||||
<a href="{% url 'settings-appearance' %}"><span class='fas fa-fill'></span> {% trans "Appearance" %}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
{% if user.is_staff %}
|
|
||||||
<h4><span class='fas fa-cogs'></span> {% trans "InvenTree Settings" %}</h4>
|
|
||||||
<ul class='nav nav-pills nav-stacked'>
|
|
||||||
<li {% if tab == 'global' %} class='active' {% endif %}>
|
|
||||||
<a href='{% url "settings-global" %}'><span class='fas fa-globe'></span> {% trans "Global" %}</a>
|
|
||||||
</li>
|
|
||||||
<li {% if tab == 'currencies' %} class='active'{% endif %}>
|
|
||||||
<a href="{% url 'settings-currencies' %}"><span class='fas fa-dollar-sign'></span> {% trans "Currencies" %}</a>
|
|
||||||
</li>
|
|
||||||
<li {% if tab == 'report' %} class='active' {% endif %}>
|
|
||||||
<a href='{% url "settings-report" %}'><span class='fas fa-file-pdf'></span> {% trans "Report" %}</a>
|
|
||||||
</li>
|
|
||||||
<li{% ifequal tab 'category' %} class='active'{% endifequal %}>
|
|
||||||
<a href="{% url 'settings-category' %}"><span class='fa fa-sitemap'></span> {% trans "Categories" %}</a>
|
|
||||||
</li>
|
|
||||||
<li{% ifequal tab 'part' %} class='active'{% endifequal %}>
|
|
||||||
<a href="{% url 'settings-part' %}"><span class='fas fa-shapes'></span> {% trans "Parts" %}</a>
|
|
||||||
</li>
|
|
||||||
<li {% if tab == 'stock' %} class='active'{% endif %}>
|
|
||||||
<a href='{% url 'settings-stock' %}'><span class='fas fa-boxes'></span> {% trans "Stock" %}</a>
|
|
||||||
</li>
|
|
||||||
<li {% if tab == 'build' %} class='active'{% endif %}>
|
|
||||||
<a href="{% url 'settings-build' %}"><span class='fas fa-tools'></span> {% trans "Build Orders" %}</a>
|
|
||||||
</li>
|
|
||||||
<li {% if tab == 'po' %} class='active'{% endif %}>
|
|
||||||
<a href="{% url 'settings-po' %}"><span class='fas fa-shopping-cart'></span> {% trans "Purchase Orders" %}</a>
|
|
||||||
</li>
|
|
||||||
<li {% if tab == 'so' %} class='active'{% endif %}>
|
|
||||||
<a href="{% url 'settings-so' %}"><span class='fas fa-truck'></span> {% trans "Sales Orders" %}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
@ -1,19 +1,15 @@
|
|||||||
{% extends "InvenTree/settings/settings.html" %}
|
{% extends "panel.html" %}
|
||||||
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load inventree_extras %}
|
||||||
|
|
||||||
{% block tabs %}
|
{% block label %}account{% endblock %}
|
||||||
{% include "InvenTree/settings/tabs.html" with tab='user' %}
|
|
||||||
|
{% block heading %}
|
||||||
|
{% trans "Account Settings" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block subtitle %}
|
{% block content %}
|
||||||
{% trans "User Settings" %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block settings %}
|
|
||||||
|
|
||||||
<div class='container'>
|
|
||||||
<h4>{% trans "User Information" %}</h4>
|
|
||||||
<div class='btn-group' style='float: right;'>
|
<div class='btn-group' style='float: right;'>
|
||||||
<div class='btn btn-primary' type='button' id='edit-user' title='{% trans "Edit User Information" %}'>
|
<div class='btn btn-primary' type='button' id='edit-user' title='{% trans "Edit User Information" %}'>
|
||||||
<span class='fas fa-user-cog'></span> {% trans "Edit" %}
|
<span class='fas fa-user-cog'></span> {% trans "Edit" %}
|
||||||
@ -41,29 +37,59 @@
|
|||||||
<td>{{ user.email }}</td>
|
<td>{{ user.email }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<div class='panel-heading'>
|
||||||
|
<h4>{% trans "Theme Settings" %}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='row'>
|
||||||
|
|
||||||
|
<form action='{% url "settings-appearance" %}' method='post'>
|
||||||
|
{% csrf_token %}
|
||||||
|
<input name='next' type='hidden' value='{% url "settings" %}'>
|
||||||
|
<div class="col-sm-6" style="width: 200px;">
|
||||||
|
<div id="div_id_themes" class="form-group">
|
||||||
|
<div class="controls ">
|
||||||
|
<select name='theme' class='select form-control'>
|
||||||
|
{% get_available_themes as themes %}
|
||||||
|
{% for theme in themes %}
|
||||||
|
<option value='{{ theme.key }}'>{{ theme.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6" style="width: auto;">
|
||||||
|
<input type="submit" value="{% trans 'Set Theme' %}" class="btn btn btn-primary">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='panel-heading'>
|
||||||
|
<h4>{% trans "Language Settings" %}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<form action="{% url 'set_language' %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input name="next" type="hidden" value="{% url 'settings' %}">
|
||||||
|
<div class="col-sm-6" style="width: 200px;"><div id="div_id_language" class="form-group"><div class="controls ">
|
||||||
|
<select name="language" class="select form-control">
|
||||||
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
|
{% get_available_languages as LANGUAGES %}
|
||||||
|
{% get_language_info_list for LANGUAGES as languages %}
|
||||||
|
{% for language in languages %}
|
||||||
|
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
|
||||||
|
{{ language.name_local }} ({{ language.code }})
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div></div></div>
|
||||||
|
<div class="col-sm-6" style="width: auto;">
|
||||||
|
<input type="submit" value="{% trans 'Set Language' %}" class="btn btn btn-primary">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js_ready %}
|
|
||||||
{{ block.super }}
|
|
||||||
|
|
||||||
$("#edit-user").on('click', function() {
|
|
||||||
launchModalForm(
|
|
||||||
"{% url 'edit-user' %}",
|
|
||||||
{
|
|
||||||
reload: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#edit-password").on('click', function() {
|
|
||||||
launchModalForm(
|
|
||||||
"{% url 'set-password' %}",
|
|
||||||
{
|
|
||||||
reload: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
{% endblock %}
|
|
42
InvenTree/templates/InvenTree/settings/user_homepage.html
Normal file
42
InvenTree/templates/InvenTree/settings/user_homepage.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{% extends "panel.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
{% load inventree_extras %}
|
||||||
|
|
||||||
|
{% block label %}user-home{% endblock %}
|
||||||
|
|
||||||
|
{% block heading %}
|
||||||
|
{% trans "Home Page Settings" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class='row'>
|
||||||
|
<table class='table table-striped table-condensed'>
|
||||||
|
{% include "InvenTree/settings/header.html" %}
|
||||||
|
<tbody>
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_PART_STARRED" icon='fa-star' user_setting=True %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_PART_LATEST" icon='fa-history' user_setting=True %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="PART_RECENT_COUNT" icon="fa-clock" user_setting=True %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_BOM_VALIDATION" user_setting=True %}
|
||||||
|
<tr><td colspan='5'></td></tr>
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_STOCK_RECENT" icon='fa-history' user_setting=True %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="STOCK_RECENT_COUNT" icon="fa-clock" user_setting=True %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_STOCK_LOW" user_setting=True %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_STOCK_DEPLETED" user_setting=True %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_STOCK_NEEDED" user_setting=True %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_STOCK_EXPIRED" icon='fa-calendar-alt' user_setting=True %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_STOCK_STALE" icon='fa-calendar-alt' user_setting=True %}
|
||||||
|
<tr><td colspan='5'></td></tr>
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_BUILD_PENDING" user_setting=True %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_BUILD_OVERDUE" user_setting=True %}
|
||||||
|
<tr><td colspan='5'></td></tr>
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_PO_OUTSTANDING" user_setting=True %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_PO_OVERDUE" user_setting=True %}
|
||||||
|
<tr><td colspan='5'></td></tr>
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_SO_OUTSTANDING" user_setting=True %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_SO_OVERDUE" user_setting=True %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
23
InvenTree/templates/InvenTree/settings/user_search.html
Normal file
23
InvenTree/templates/InvenTree/settings/user_search.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{% extends "panel.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
{% load inventree_extras %}
|
||||||
|
|
||||||
|
{% block label %}user-search{% endblock %}
|
||||||
|
|
||||||
|
{% block heading %}
|
||||||
|
{% trans "Search Settings" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class='row'>
|
||||||
|
<table class='table table-striped table-condensed'>
|
||||||
|
{% include "InvenTree/settings/header.html" %}
|
||||||
|
<tbody>
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_RESULTS" user_setting=True icon='fa-search' %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
21
InvenTree/templates/InvenTree/settings/user_settings.html
Normal file
21
InvenTree/templates/InvenTree/settings/user_settings.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{% extends "panel.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
{% load inventree_extras %}
|
||||||
|
|
||||||
|
{% block label %}user-settings{% endblock %}
|
||||||
|
|
||||||
|
{% block heading %}
|
||||||
|
{% trans "User Settings" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class='row'>
|
||||||
|
<table class='table table-striped table-condensed'>
|
||||||
|
{% include "InvenTree/settings/header.html" %}
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -145,19 +145,22 @@
|
|||||||
<script type='text/javascript' src="{% static 'script/inventree/notification.js' %}"></script>
|
<script type='text/javascript' src="{% static 'script/inventree/notification.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% static 'script/inventree/sidenav.js' %}"></script>
|
<script type='text/javascript' src="{% static 'script/inventree/sidenav.js' %}"></script>
|
||||||
|
|
||||||
<!-- translated -->
|
<!-- dynamic javascript templates -->
|
||||||
<script type='text/javascript' src="{% i18n_static 'inventree.js' %}"></script>
|
<script type='text/javascript' src="{% url 'inventree.js' %}"></script>
|
||||||
|
<script type='text/javascript' src="{% url 'calendar.js' %}"></script>
|
||||||
|
<script type='text/javascript' src="{% url 'nav.js' %}"></script>
|
||||||
|
<script type='text/javascript' src="{% url 'settings.js' %}"></script>
|
||||||
|
|
||||||
|
<!-- translated javascript templates-->
|
||||||
<script type='text/javascript' src="{% i18n_static 'api.js' %}"></script>
|
<script type='text/javascript' src="{% i18n_static 'api.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'attachment.js' %}"></script>
|
<script type='text/javascript' src="{% i18n_static 'attachment.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'barcode.js' %}"></script>
|
<script type='text/javascript' src="{% i18n_static 'barcode.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'bom.js' %}"></script>
|
<script type='text/javascript' src="{% i18n_static 'bom.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'build.js' %}"></script>
|
<script type='text/javascript' src="{% i18n_static 'build.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'calendar.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% i18n_static 'company.js' %}"></script>
|
<script type='text/javascript' src="{% i18n_static 'company.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'filters.js' %}"></script>
|
<script type='text/javascript' src="{% i18n_static 'filters.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'forms.js' %}"></script>
|
<script type='text/javascript' src="{% i18n_static 'forms.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'label.js' %}"></script>
|
<script type='text/javascript' src="{% i18n_static 'label.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'nav.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% i18n_static 'modals.js' %}"></script>
|
<script type='text/javascript' src="{% i18n_static 'modals.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'model_renderers.js' %}"></script>
|
<script type='text/javascript' src="{% i18n_static 'model_renderers.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'order.js' %}"></script>
|
<script type='text/javascript' src="{% i18n_static 'order.js' %}"></script>
|
||||||
|
@ -91,7 +91,7 @@ function inventreeDocReady() {
|
|||||||
url: '/api/part/',
|
url: '/api/part/',
|
||||||
data: {
|
data: {
|
||||||
search: request.term,
|
search: request.term,
|
||||||
limit: {% settings_value 'SEARCH_PREVIEW_RESULTS' %},
|
limit: user_settings.SEARCH_PREVIEW_RESULTS,
|
||||||
offset: 0
|
offset: 0
|
||||||
},
|
},
|
||||||
success: function (data) {
|
success: function (data) {
|
17
InvenTree/templates/js/dynamic/settings.js
Normal file
17
InvenTree/templates/js/dynamic/settings.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{% load inventree_extras %}
|
||||||
|
// InvenTree settings
|
||||||
|
|
||||||
|
{% user_settings request.user as USER_SETTINGS %}
|
||||||
|
{% global_settings as GLOBAL_SETTINGS %}
|
||||||
|
|
||||||
|
var user_settings = {
|
||||||
|
{% for setting in USER_SETTINGS %}
|
||||||
|
{{ setting.key }}: {{ setting.value }},
|
||||||
|
{% endfor %}
|
||||||
|
};
|
||||||
|
|
||||||
|
var global_settings = {
|
||||||
|
{% for setting in GLOBAL_SETTINGS %}
|
||||||
|
{{ setting.key }}: {{ setting.value }},
|
||||||
|
{% endfor %}
|
||||||
|
};
|
@ -147,8 +147,7 @@ function inventreeDelete(url, options={}) {
|
|||||||
|
|
||||||
options.method = 'DELETE';
|
options.method = 'DELETE';
|
||||||
|
|
||||||
inventreePut(url, {}, options);
|
return inventreePut(url, {}, options);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -8,6 +8,26 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
function bomItemFields() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
part: {
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
sub_part: {
|
||||||
|
},
|
||||||
|
quantity: {},
|
||||||
|
reference: {},
|
||||||
|
overage: {},
|
||||||
|
note: {},
|
||||||
|
allow_variants: {},
|
||||||
|
inherited: {},
|
||||||
|
optional: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function reloadBomTable(table, options) {
|
function reloadBomTable(table, options) {
|
||||||
|
|
||||||
table.bootstrapTable('refresh');
|
table.bootstrapTable('refresh');
|
||||||
@ -262,13 +282,13 @@ function loadBomTable(table, options) {
|
|||||||
cols.push(
|
cols.push(
|
||||||
{
|
{
|
||||||
field: 'price_range',
|
field: 'price_range',
|
||||||
title: '{% trans "Buy Price" %}',
|
title: '{% trans "Supplier Cost" %}',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
formatter: function(value, row, index, field) {
|
formatter: function(value, row, index, field) {
|
||||||
if (value) {
|
if (value) {
|
||||||
return value;
|
return value;
|
||||||
} else {
|
} else {
|
||||||
return "<span class='warning-msg'>{% trans 'No pricing available' %}</span>";
|
return "<span class='warning-msg'>{% trans 'No supplier pricing available' %}</span>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -528,14 +548,15 @@ function loadBomTable(table, options) {
|
|||||||
var pk = $(this).attr('pk');
|
var pk = $(this).attr('pk');
|
||||||
var url = `/part/bom/${pk}/edit/`;
|
var url = `/part/bom/${pk}/edit/`;
|
||||||
|
|
||||||
launchModalForm(
|
var fields = bomItemFields();
|
||||||
url,
|
|
||||||
{
|
constructForm(`/api/bom/${pk}/`, {
|
||||||
success: function() {
|
fields: fields,
|
||||||
|
title: '{% trans "Edit BOM Item" %}',
|
||||||
|
onSuccess: function() {
|
||||||
reloadBomTable(table);
|
reloadBomTable(table);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
table.on('click', '.bom-validate-button', function() {
|
table.on('click', '.bom-validate-button', function() {
|
@ -5,7 +5,7 @@
|
|||||||
function buildFormFields() {
|
function buildFormFields() {
|
||||||
return {
|
return {
|
||||||
reference: {
|
reference: {
|
||||||
prefix: "{% settings_value 'BUILDORDER_REFERENCE_PREFIX' %}",
|
prefix: global_settings.BUILDORDER_REFERENCE_PREFIX,
|
||||||
},
|
},
|
||||||
title: {},
|
title: {},
|
||||||
part: {},
|
part: {},
|
||||||
@ -232,7 +232,7 @@ function loadBuildOrderAllocationTable(table, options={}) {
|
|||||||
switchable: false,
|
switchable: false,
|
||||||
title: '{% trans "Build Order" %}',
|
title: '{% trans "Build Order" %}',
|
||||||
formatter: function(value, row) {
|
formatter: function(value, row) {
|
||||||
var prefix = "{% settings_value 'BUILDORDER_REFERENCE_PREFIX' %}";
|
var prefix = global_settings.BUILDORDER_REFERENCE_PREFIX;
|
||||||
|
|
||||||
var ref = `${prefix}${row.build_detail.reference}`;
|
var ref = `${prefix}${row.build_detail.reference}`;
|
||||||
|
|
||||||
@ -848,7 +848,7 @@ function loadBuildTable(table, options) {
|
|||||||
switchable: true,
|
switchable: true,
|
||||||
formatter: function(value, row, index, field) {
|
formatter: function(value, row, index, field) {
|
||||||
|
|
||||||
var prefix = "{% settings_value 'BUILDORDER_REFERENCE_PREFIX' %}";
|
var prefix = global_settings.BUILDORDER_REFERENCE_PREFIX;
|
||||||
|
|
||||||
if (prefix) {
|
if (prefix) {
|
||||||
value = `${prefix}${value}`;
|
value = `${prefix}${value}`;
|
@ -349,7 +349,7 @@ function deleteManufacturerParts(selections, options={}) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Wait for all the requests to complete
|
// Wait for all the requests to complete
|
||||||
$.when.apply($, requests).then(function() {
|
$.when.apply($, requests).done(function() {
|
||||||
|
|
||||||
if (options.onSuccess) {
|
if (options.onSuccess) {
|
||||||
options.onSuccess();
|
options.onSuccess();
|
@ -265,6 +265,8 @@ function setupFilterList(tableKey, table, target) {
|
|||||||
// One blank slate, please
|
// One blank slate, please
|
||||||
element.empty();
|
element.empty();
|
||||||
|
|
||||||
|
element.append(`<button id='reload-${tableKey}' title='{% trans "Reload data" %}' class='btn btn-default filter-tag'><span class='fas fa-redo-alt'></span></button>`);
|
||||||
|
|
||||||
element.append(`<button id='${add}' title='{% trans "Add new filter" %}' class='btn btn-default filter-tag'><span class='fas fa-filter'></span></button>`);
|
element.append(`<button id='${add}' title='{% trans "Add new filter" %}' class='btn btn-default filter-tag'><span class='fas fa-filter'></span></button>`);
|
||||||
|
|
||||||
if (Object.keys(filters).length > 0) {
|
if (Object.keys(filters).length > 0) {
|
||||||
@ -279,6 +281,11 @@ function setupFilterList(tableKey, table, target) {
|
|||||||
element.append(`<div title='${description}' class='filter-tag'>${title} = ${value}<span ${tag}='${key}' class='close'>x</span></div>`);
|
element.append(`<div title='${description}' class='filter-tag'>${title} = ${value}<span ${tag}='${key}' class='close'>x</span></div>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Callback for reloading the table
|
||||||
|
element.find(`#reload-${tableKey}`).click(function() {
|
||||||
|
$(table).bootstrapTable('refresh');
|
||||||
|
});
|
||||||
|
|
||||||
// Add a callback for adding a new filter
|
// Add a callback for adding a new filter
|
||||||
element.find(`#${add}`).click(function clicked() {
|
element.find(`#${add}`).click(function clicked() {
|
||||||
|
|
@ -9,7 +9,7 @@ function createSalesOrder(options={}) {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
fields: {
|
fields: {
|
||||||
reference: {
|
reference: {
|
||||||
prefix: '{% settings_value "SALESORDER_REFERENCE_PREFIX" %}',
|
prefix: global_settings.SALESORDER_REFERENCE_PREFIX,
|
||||||
},
|
},
|
||||||
customer: {
|
customer: {
|
||||||
value: options.customer,
|
value: options.customer,
|
||||||
@ -40,7 +40,7 @@ function createPurchaseOrder(options={}) {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
fields: {
|
fields: {
|
||||||
reference: {
|
reference: {
|
||||||
prefix: "{% settings_value 'PURCHASEORDER_REFERENCE_PREFIX' %}",
|
prefix: global_settings.PURCHASEORDER_REFERENCE_PREFIX,
|
||||||
},
|
},
|
||||||
supplier: {
|
supplier: {
|
||||||
value: options.supplier,
|
value: options.supplier,
|
||||||
@ -214,7 +214,7 @@ function loadPurchaseOrderTable(table, options) {
|
|||||||
switchable: false,
|
switchable: false,
|
||||||
formatter: function(value, row, index, field) {
|
formatter: function(value, row, index, field) {
|
||||||
|
|
||||||
var prefix = "{% settings_value 'PURCHASEORDER_REFERENCE_PREFIX' %}";
|
var prefix = global_settings.PURCHASEORDER_REFERENCE_PREFIX;
|
||||||
|
|
||||||
if (prefix) {
|
if (prefix) {
|
||||||
value = `${prefix}${value}`;
|
value = `${prefix}${value}`;
|
||||||
@ -309,7 +309,7 @@ function loadSalesOrderTable(table, options) {
|
|||||||
title: '{% trans "Sales Order" %}',
|
title: '{% trans "Sales Order" %}',
|
||||||
formatter: function(value, row, index, field) {
|
formatter: function(value, row, index, field) {
|
||||||
|
|
||||||
var prefix = "{% settings_value 'SALESORDER_REFERENCE_PREFIX' %}";
|
var prefix = global_settings.SALESORDER_REFERENCE_PREFIX;
|
||||||
|
|
||||||
if (prefix) {
|
if (prefix) {
|
||||||
value = `${prefix}${value}`;
|
value = `${prefix}${value}`;
|
||||||
@ -423,7 +423,7 @@ function loadSalesOrderAllocationTable(table, options={}) {
|
|||||||
switchable: false,
|
switchable: false,
|
||||||
formatter: function(value, row) {
|
formatter: function(value, row) {
|
||||||
|
|
||||||
var prefix = "{% settings_value 'SALESORDER_REFERENCE_PREFIX' %}";
|
var prefix = global_settings.SALESORDER_REFERENCE_PREFIX;
|
||||||
|
|
||||||
var ref = `${prefix}${row.order_detail.reference}`;
|
var ref = `${prefix}${row.order_detail.reference}`;
|
||||||
|
|
@ -14,6 +14,37 @@ function yesNoLabel(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function categoryFields() {
|
||||||
|
return {
|
||||||
|
parent: {
|
||||||
|
help_text: '{% trans "Parent part category" %}',
|
||||||
|
},
|
||||||
|
name: {},
|
||||||
|
description: {},
|
||||||
|
default_location: {},
|
||||||
|
default_keywords: {
|
||||||
|
icon: 'fa-key',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Edit a PartCategory via the API
|
||||||
|
function editCategory(pk, options={}) {
|
||||||
|
|
||||||
|
var url = `/api/part/category/${pk}/`;
|
||||||
|
|
||||||
|
var fields = categoryFields();
|
||||||
|
|
||||||
|
constructForm(url, {
|
||||||
|
fields: fields,
|
||||||
|
title: '{% trans "Edit Part Category" %}',
|
||||||
|
reload: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function editPart(pk, options={}) {
|
function editPart(pk, options={}) {
|
||||||
|
|
||||||
var url = `/api/part/${pk}/`;
|
var url = `/api/part/${pk}/`;
|
@ -6,8 +6,6 @@
|
|||||||
* Requires api.js to be loaded first
|
* Requires api.js to be loaded first
|
||||||
*/
|
*/
|
||||||
|
|
||||||
{% settings_value 'BARCODE_ENABLE' as barcodes %}
|
|
||||||
|
|
||||||
function stockStatusCodes() {
|
function stockStatusCodes() {
|
||||||
return [
|
return [
|
||||||
{% for code in StockStatus.list %}
|
{% for code in StockStatus.list %}
|
||||||
@ -287,7 +285,7 @@ function adjustStock(action, items, options={}) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Wait for *all* the requests to complete
|
// Wait for *all* the requests to complete
|
||||||
$.when.apply($, requests).then(function() {
|
$.when.apply($, requests).done(function() {
|
||||||
// Destroy the modal window
|
// Destroy the modal window
|
||||||
$(modal).modal('hide');
|
$(modal).modal('hide');
|
||||||
|
|
||||||
@ -704,8 +702,7 @@ function loadStockTable(table, options) {
|
|||||||
name: 'stock',
|
name: 'stock',
|
||||||
original: original,
|
original: original,
|
||||||
showColumns: true,
|
showColumns: true,
|
||||||
{% settings_value 'STOCK_GROUP_BY_PART' as group_by_part %}
|
{% if False %}
|
||||||
{% if group_by_part %}
|
|
||||||
groupByField: options.groupByField || 'part',
|
groupByField: options.groupByField || 'part',
|
||||||
groupBy: grouping,
|
groupBy: grouping,
|
||||||
groupByFormatter: function(field, id, data) {
|
groupByFormatter: function(field, id, data) {
|
||||||
@ -1011,14 +1008,13 @@ function loadStockTable(table, options) {
|
|||||||
title: '{% trans "Stocktake" %}',
|
title: '{% trans "Stocktake" %}',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
},
|
},
|
||||||
{% settings_value "STOCK_ENABLE_EXPIRY" as expiry %}
|
|
||||||
{% if expiry %}
|
|
||||||
{
|
{
|
||||||
field: 'expiry_date',
|
field: 'expiry_date',
|
||||||
title: '{% trans "Expiry Date" %}',
|
title: '{% trans "Expiry Date" %}',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
visible: global_settings.STOCK_ENABLE_EXPIRY,
|
||||||
|
switchable: global_settings.STOCK_ENABLE_EXPIRY,
|
||||||
},
|
},
|
||||||
{% endif %}
|
|
||||||
{
|
{
|
||||||
field: 'updated',
|
field: 'updated',
|
||||||
title: '{% trans "Last Updated" %}',
|
title: '{% trans "Last Updated" %}',
|
||||||
@ -1037,7 +1033,7 @@ function loadStockTable(table, options) {
|
|||||||
|
|
||||||
if (row.purchase_order_reference) {
|
if (row.purchase_order_reference) {
|
||||||
|
|
||||||
var prefix = '{% settings_value "PURCHASEORDER_REFERENCE_PREFIX" %}';
|
var prefix = global_settings.PURCHASEORDER_REFERENCE_PREFIX;
|
||||||
|
|
||||||
text = prefix + row.purchase_order_reference;
|
text = prefix + row.purchase_order_reference;
|
||||||
}
|
}
|
||||||
@ -1090,15 +1086,18 @@ function loadStockTable(table, options) {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var buttons = [
|
||||||
|
'#stock-print-options',
|
||||||
|
'#stock-options',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (global_settings.BARCODE_ENABLE) {
|
||||||
|
buttons.push('#stock-barcode-options');
|
||||||
|
}
|
||||||
|
|
||||||
linkButtonsToSelection(
|
linkButtonsToSelection(
|
||||||
table,
|
table,
|
||||||
[
|
buttons,
|
||||||
'#stock-print-options',
|
|
||||||
{% if barcodes %}
|
|
||||||
'#stock-barcode-options',
|
|
||||||
{% endif %}
|
|
||||||
'#stock-options',
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@ -1138,7 +1137,7 @@ function loadStockTable(table, options) {
|
|||||||
printTestReports(items);
|
printTestReports(items);
|
||||||
})
|
})
|
||||||
|
|
||||||
{% if barcodes %}
|
if (global_settings.BARCODE_ENABLE) {
|
||||||
$('#multi-item-barcode-scan-into-location').click(function() {
|
$('#multi-item-barcode-scan-into-location').click(function() {
|
||||||
var selections = $('#stock-table').bootstrapTable('getSelections');
|
var selections = $('#stock-table').bootstrapTable('getSelections');
|
||||||
|
|
||||||
@ -1150,7 +1149,7 @@ function loadStockTable(table, options) {
|
|||||||
|
|
||||||
scanItemsIntoLocation(items);
|
scanItemsIntoLocation(items);
|
||||||
});
|
});
|
||||||
{% endif %}
|
}
|
||||||
|
|
||||||
$('#multi-item-stocktake').click(function() {
|
$('#multi-item-stocktake').click(function() {
|
||||||
stockAdjustment('count');
|
stockAdjustment('count');
|
||||||
@ -1265,7 +1264,7 @@ function loadStockTable(table, options) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
$.when.apply($, requests).then(function() {
|
$.when.apply($, requests).done(function() {
|
||||||
$("#stock-table").bootstrapTable('refresh');
|
$("#stock-table").bootstrapTable('refresh');
|
||||||
});
|
});
|
||||||
})
|
})
|
@ -121,7 +121,8 @@ function getAvailableTableFilters(tableKey) {
|
|||||||
|
|
||||||
// Filters for the "Stock" table
|
// Filters for the "Stock" table
|
||||||
if (tableKey == 'stock') {
|
if (tableKey == 'stock') {
|
||||||
return {
|
|
||||||
|
var filters = {
|
||||||
active: {
|
active: {
|
||||||
type: 'bool',
|
type: 'bool',
|
||||||
title: '{% trans "Active parts" %}',
|
title: '{% trans "Active parts" %}',
|
||||||
@ -147,19 +148,6 @@ function getAvailableTableFilters(tableKey) {
|
|||||||
title: '{% trans "Depleted" %}',
|
title: '{% trans "Depleted" %}',
|
||||||
description: '{% trans "Show stock items which are depleted" %}',
|
description: '{% trans "Show stock items which are depleted" %}',
|
||||||
},
|
},
|
||||||
{% settings_value "STOCK_ENABLE_EXPIRY" as expiry %}
|
|
||||||
{% if expiry %}
|
|
||||||
expired: {
|
|
||||||
type: 'bool',
|
|
||||||
title: '{% trans "Expired" %}',
|
|
||||||
description: '{% trans "Show stock items which have expired" %}',
|
|
||||||
},
|
|
||||||
stale: {
|
|
||||||
type: 'bool',
|
|
||||||
title: '{% trans "Stale" %}',
|
|
||||||
description: '{% trans "Show stock which is close to expiring" %}',
|
|
||||||
},
|
|
||||||
{% endif %}
|
|
||||||
in_stock: {
|
in_stock: {
|
||||||
type: 'bool',
|
type: 'bool',
|
||||||
title: '{% trans "In Stock" %}',
|
title: '{% trans "In Stock" %}',
|
||||||
@ -216,6 +204,23 @@ function getAvailableTableFilters(tableKey) {
|
|||||||
description: '{% trans "Show stock items which have a purchase price set" %}',
|
description: '{% trans "Show stock items which have a purchase price set" %}',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Optional filters if stock expiry functionality is enabled
|
||||||
|
if (global_settings.STOCK_ENABLE_EXPIRY) {
|
||||||
|
filters.expired = {
|
||||||
|
type: 'bool',
|
||||||
|
title: '{% trans "Expired" %}',
|
||||||
|
description: '{% trans "Show stock items which have expired" %}',
|
||||||
|
};
|
||||||
|
|
||||||
|
filters.stale = {
|
||||||
|
type: 'bool',
|
||||||
|
title: '{% trans "Stale" %}',
|
||||||
|
description: '{% trans "Show stock which is close to expiring" %}',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filters for the 'stock test' table
|
// Filters for the 'stock test' table
|
@ -187,7 +187,7 @@ $.fn.inventreeTable = function(options) {
|
|||||||
if (!options.disablePagination) {
|
if (!options.disablePagination) {
|
||||||
options.pagination = true;
|
options.pagination = true;
|
||||||
options.paginationVAlign = options.paginationVAlign || 'both';
|
options.paginationVAlign = options.paginationVAlign || 'both';
|
||||||
options.pageSize = inventreeLoad(varName, 25);
|
options.pageSize = options.pageSize || inventreeLoad(varName, 25);
|
||||||
options.pageList = [25, 50, 100, 250, 'all'];
|
options.pageList = [25, 50, 100, 250, 'all'];
|
||||||
options.totalField = 'count';
|
options.totalField = 'count';
|
||||||
options.dataField = 'results';
|
options.dataField = 'results';
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user