mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge branch 'inventree:master' into fr-2986-shipment-page-action
This commit is contained in:
commit
ac35bd8073
14
.github/workflows/qc_checks.yaml
vendored
14
.github/workflows/qc_checks.yaml
vendored
@ -182,9 +182,9 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gettext
|
||||
python -m pip install -U pip
|
||||
pip3 install invoke
|
||||
invoke install
|
||||
invoke static
|
||||
invoke update
|
||||
- name: Coverage Tests
|
||||
run: |
|
||||
invoke coverage
|
||||
@ -245,11 +245,12 @@ jobs:
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install libpq-dev
|
||||
sudo apt-get install libpq-dev gettext
|
||||
python -m pip install -U pip
|
||||
pip3 install invoke
|
||||
pip3 install psycopg2
|
||||
pip3 install django-redis>=5.0.0
|
||||
invoke install
|
||||
invoke update
|
||||
- name: Run Tests
|
||||
run: invoke test
|
||||
- name: Data Import Export
|
||||
@ -302,10 +303,11 @@ jobs:
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install libmysqlclient-dev
|
||||
sudo apt-get install libmysqlclient-dev gettext
|
||||
python -m pip install -U pip
|
||||
pip3 install invoke
|
||||
pip3 install mysqlclient
|
||||
invoke install
|
||||
invoke update
|
||||
- name: Run Tests
|
||||
run: invoke test
|
||||
- name: Data Import Export
|
||||
|
@ -32,6 +32,10 @@ class MiddlewareTests(TestCase):
|
||||
# logout
|
||||
self.client.logout()
|
||||
|
||||
# check that static files go through
|
||||
# TODO @matmair reenable this check
|
||||
# self.check_path('/static/css/inventree.css', 302)
|
||||
|
||||
# check that account things go through
|
||||
self.check_path(reverse('account_login'))
|
||||
|
||||
|
@ -1429,6 +1429,13 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
|
||||
'validator': bool,
|
||||
},
|
||||
|
||||
'SEARCH_HIDE_INACTIVE_PARTS': {
|
||||
'name': _("Hide Inactive Parts"),
|
||||
'description': _('Excluded inactive parts from search preview window'),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
|
||||
'SEARCH_PREVIEW_SHOW_CATEGORIES': {
|
||||
'name': _('Search Categories'),
|
||||
'description': _('Display part categories in search preview window'),
|
||||
@ -1443,6 +1450,13 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
|
||||
'validator': bool,
|
||||
},
|
||||
|
||||
'SEARCH_PREVIEW_HIDE_UNAVAILABLE_STOCK': {
|
||||
'name': _('Hide Unavailable Stock Items'),
|
||||
'description': _('Exclude stock items which are not available from the search preview window'),
|
||||
'validator': bool,
|
||||
'default': False,
|
||||
},
|
||||
|
||||
'SEARCH_PREVIEW_SHOW_LOCATIONS': {
|
||||
'name': _('Search Locations'),
|
||||
'description': _('Display stock locations in search preview window'),
|
||||
@ -1464,6 +1478,13 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
|
||||
'validator': bool,
|
||||
},
|
||||
|
||||
'SEARCH_PREVIEW_EXCLUDE_INACTIVE_PURCHASE_ORDERS': {
|
||||
'name': _('Exclude Inactive Purchase Orders'),
|
||||
'description': _('Exclude inactive purchase orders from search preview window'),
|
||||
'default': True,
|
||||
'validator': bool,
|
||||
},
|
||||
|
||||
'SEARCH_PREVIEW_SHOW_SALES_ORDERS': {
|
||||
'name': _('Search Sales Orders'),
|
||||
'description': _('Display sales orders in search preview window'),
|
||||
@ -1471,6 +1492,13 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
|
||||
'validator': bool,
|
||||
},
|
||||
|
||||
'SEARCH_PREVIEW_EXCLUDE_INACTIVE_SALES_ORDERS': {
|
||||
'name': _('Exclude Inactive Sales Orders'),
|
||||
'description': _('Exclude inactive sales orders from search preview window'),
|
||||
'validator': bool,
|
||||
'default': True,
|
||||
},
|
||||
|
||||
'SEARCH_PREVIEW_RESULTS': {
|
||||
'name': _('Search Preview Results'),
|
||||
'description': _('Number of results to show in each section of the search preview window'),
|
||||
@ -1478,13 +1506,6 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
|
||||
'validator': [int, MinValueValidator(1)]
|
||||
},
|
||||
|
||||
'SEARCH_HIDE_INACTIVE_PARTS': {
|
||||
'name': _("Hide Inactive Parts"),
|
||||
'description': _('Hide inactive parts in search preview window'),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
|
||||
'PART_SHOW_QUANTITY_IN_FORMS': {
|
||||
'name': _('Show Quantity in Forms'),
|
||||
'description': _('Display available part quantity in some forms'),
|
||||
@ -1701,6 +1722,9 @@ class ColorTheme(models.Model):
|
||||
@classmethod
|
||||
def get_color_themes_choices(cls):
|
||||
""" Get all color themes from static folder """
|
||||
if settings.TESTING and not os.path.exists(settings.STATIC_COLOR_THEMES_DIR):
|
||||
logger.error('Theme directory does not exsist')
|
||||
return []
|
||||
|
||||
# Get files list from css/color-themes/ folder
|
||||
files_list = []
|
||||
|
@ -12,7 +12,7 @@ from InvenTree.helpers import str2bool
|
||||
from plugin.models import NotificationUserSetting, PluginConfig
|
||||
from plugin import registry
|
||||
|
||||
from .models import InvenTreeSetting, InvenTreeUserSetting, WebhookEndpoint, WebhookMessage, NotificationEntry
|
||||
from .models import InvenTreeSetting, InvenTreeUserSetting, WebhookEndpoint, WebhookMessage, NotificationEntry, ColorTheme
|
||||
from .api import WebhookView
|
||||
|
||||
CONTENT_TYPE_JSON = 'application/json'
|
||||
@ -163,10 +163,19 @@ class SettingsTest(TestCase):
|
||||
"""
|
||||
|
||||
for key, setting in InvenTreeSetting.SETTINGS.items():
|
||||
self.run_settings_check(key, setting)
|
||||
|
||||
try:
|
||||
self.run_settings_check(key, setting)
|
||||
except Exception as exc:
|
||||
print(f"run_settings_check failed for global setting '{key}'")
|
||||
raise exc
|
||||
|
||||
for key, setting in InvenTreeUserSetting.SETTINGS.items():
|
||||
self.run_settings_check(key, setting)
|
||||
try:
|
||||
self.run_settings_check(key, setting)
|
||||
except Exception as exc:
|
||||
print(f"run_settings_check failed for user setting '{key}'")
|
||||
raise exc
|
||||
|
||||
def test_defaults(self):
|
||||
"""
|
||||
@ -707,3 +716,35 @@ class LoadingTest(TestCase):
|
||||
|
||||
# now it should be false again
|
||||
self.assertFalse(common.models.InvenTreeSetting.get_setting('SERVER_RESTART_REQUIRED'))
|
||||
|
||||
|
||||
class ColorThemeTest(TestCase):
|
||||
"""Tests for ColorTheme"""
|
||||
|
||||
def test_choices(self):
|
||||
"""Test that default choices are returned"""
|
||||
result = ColorTheme.get_color_themes_choices()
|
||||
|
||||
# skip
|
||||
if not result:
|
||||
return
|
||||
self.assertIn(('default', 'Default'), result)
|
||||
|
||||
def test_valid_choice(self):
|
||||
"""Check that is_valid_choice works correctly"""
|
||||
result = ColorTheme.get_color_themes_choices()
|
||||
|
||||
# skip
|
||||
if not result:
|
||||
return
|
||||
|
||||
# check wrong reference
|
||||
self.assertFalse(ColorTheme.is_valid_choice('abcdd'))
|
||||
|
||||
# create themes
|
||||
aa = ColorTheme.objects.create(user='aa', name='testname')
|
||||
ab = ColorTheme.objects.create(user='ab', name='darker')
|
||||
|
||||
# check valid theme
|
||||
self.assertFalse(ColorTheme.is_valid_choice(aa))
|
||||
self.assertTrue(ColorTheme.is_valid_choice(ab))
|
||||
|
@ -2,6 +2,7 @@ import os
|
||||
import shutil
|
||||
import logging
|
||||
import hashlib
|
||||
import warnings
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.conf import settings
|
||||
@ -42,6 +43,15 @@ class LabelConfig(AppConfig):
|
||||
"""
|
||||
Create all default templates
|
||||
"""
|
||||
# Test if models are ready
|
||||
try:
|
||||
from .models import StockLocationLabel
|
||||
assert bool(StockLocationLabel is not None)
|
||||
except AppRegistryNotReady:
|
||||
# Database might not yet be ready
|
||||
warnings.warn('Database was not ready for creating labels')
|
||||
return
|
||||
|
||||
self.create_stock_item_labels()
|
||||
self.create_stock_location_labels()
|
||||
self.create_part_labels()
|
||||
@ -52,11 +62,7 @@ class LabelConfig(AppConfig):
|
||||
if they do not already exist
|
||||
"""
|
||||
|
||||
try:
|
||||
from .models import StockItemLabel
|
||||
except AppRegistryNotReady: # pragma: no cover
|
||||
# Database might not by ready yet
|
||||
return
|
||||
from .models import StockItemLabel
|
||||
|
||||
src_dir = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
@ -139,11 +145,7 @@ class LabelConfig(AppConfig):
|
||||
if they do not already exist
|
||||
"""
|
||||
|
||||
try:
|
||||
from .models import StockLocationLabel
|
||||
except AppRegistryNotReady: # pragma: no cover
|
||||
# Database might not yet be ready
|
||||
return
|
||||
from .models import StockLocationLabel
|
||||
|
||||
src_dir = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
@ -233,11 +235,7 @@ class LabelConfig(AppConfig):
|
||||
if they do not already exist.
|
||||
"""
|
||||
|
||||
try:
|
||||
from .models import PartLabel
|
||||
except AppRegistryNotReady: # pragma: no cover
|
||||
# Database might not yet be ready
|
||||
return
|
||||
from .models import PartLabel
|
||||
|
||||
src_dir = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
|
@ -4,12 +4,14 @@ import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.apps import apps
|
||||
from django.urls import reverse
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from InvenTree.helpers import validateFilterString
|
||||
from InvenTree.api_tester import InvenTreeAPITestCase
|
||||
|
||||
from .models import StockItemLabel, StockLocationLabel
|
||||
from .models import StockItemLabel, StockLocationLabel, PartLabel
|
||||
from part.models import Part
|
||||
from stock.models import StockItem
|
||||
|
||||
|
||||
@ -82,3 +84,13 @@ class LabelTest(InvenTreeAPITestCase):
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
validateFilterString(bad_filter_string, model=StockItem)
|
||||
|
||||
def test_label_rendering(self):
|
||||
"""Test label rendering"""
|
||||
|
||||
labels = PartLabel.objects.all()
|
||||
part = Part.objects.first()
|
||||
|
||||
for label in labels:
|
||||
url = reverse('api-part-label-print', kwargs={'pk': label.pk})
|
||||
self.get(f'{url}?parts={part.pk}', expected_code=200)
|
||||
|
@ -83,7 +83,7 @@ def render_date(context, date_object):
|
||||
|
||||
user = context.get('user', None)
|
||||
|
||||
if user:
|
||||
if user and user.is_authenticated:
|
||||
# User is specified - look for their date display preference
|
||||
user_date_format = InvenTreeUserSetting.get_setting('DATE_DISPLAY_FORMAT', user=user)
|
||||
else:
|
||||
@ -329,7 +329,7 @@ def settings_value(key, *args, **kwargs):
|
||||
"""
|
||||
|
||||
if 'user' in kwargs:
|
||||
if not kwargs['user']:
|
||||
if not kwargs['user'] or (kwargs['user'] and kwargs['user'].is_authenticated is False):
|
||||
return InvenTreeUserSetting.get_setting(key)
|
||||
return InvenTreeUserSetting.get_setting(key, user=kwargs['user'])
|
||||
|
||||
|
@ -15,16 +15,19 @@
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tbody>
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_SHOW_PARTS" user_setting=True icon='fa-shapes' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_HIDE_INACTIVE_PARTS" user_setting=True icon='fa-eye-slash' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_SHOW_CATEGORIES" user_setting=True icon='fa-sitemap' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_SHOW_STOCK" user_setting=True icon='fa-boxes' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_HIDE_UNAVAILABLE_STOCK" user_setting=True icon='fa-eye-slash' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_SHOW_LOCATIONS" user_setting=True icon='fa-sitemap' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_SHOW_COMPANIES" user_setting=True icon='fa-building' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_SHOW_PURCHASE_ORDERS" user_setting=True icon='fa-shopping-cart' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_EXCLUDE_INACTIVE_PURCHASE_ORDERS" user_setting=True icon='fa-eye-slash' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_SHOW_SALES_ORDERS" user_setting=True icon='fa-truck' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_EXCLUDE_INACTIVE_SALES_ORDERS" user_setting=True icon='fa-eye-slash' %}
|
||||
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_RESULTS" user_setting=True icon='fa-search' %}
|
||||
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_HIDE_INACTIVE_PARTS" user_setting=True icon='fa-eye-slash' %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -122,14 +122,22 @@ function updateSearch() {
|
||||
|
||||
if (user_settings.SEARCH_PREVIEW_SHOW_STOCK) {
|
||||
// Search for matching stock items
|
||||
|
||||
var filters = {
|
||||
part_detail: true,
|
||||
location_detail: true,
|
||||
};
|
||||
|
||||
if (user_settings.SEARCH_PREVIEW_HIDE_UNAVAILABLE_STOCK) {
|
||||
// Only show 'in stock' items in the preview windoww
|
||||
filters.in_stock = true;
|
||||
}
|
||||
|
||||
addSearchQuery(
|
||||
'stock',
|
||||
'{% trans "Stock Items" %}',
|
||||
'{% url "api-stock-list" %}',
|
||||
{
|
||||
part_detail: true,
|
||||
location_detail: true,
|
||||
},
|
||||
filters,
|
||||
renderStockItem,
|
||||
{
|
||||
url: '/stock/item',
|
||||
@ -167,15 +175,21 @@ function updateSearch() {
|
||||
}
|
||||
|
||||
if (user_settings.SEARCH_PREVIEW_SHOW_PURCHASE_ORDERS) {
|
||||
|
||||
var filters = {
|
||||
supplier_detail: true,
|
||||
};
|
||||
|
||||
if (user_settings.SEARCH_PREVIEW_EXCLUDE_INACTIVE_PURCHASE_ORDERS) {
|
||||
filters.outstanding = true;
|
||||
}
|
||||
|
||||
// Search for matching purchase orders
|
||||
addSearchQuery(
|
||||
'purchaseorder',
|
||||
'{% trans "Purchase Orders" %}',
|
||||
'{% url "api-po-list" %}',
|
||||
{
|
||||
supplier_detail: true,
|
||||
outstanding: true,
|
||||
},
|
||||
filters,
|
||||
renderPurchaseOrder,
|
||||
{
|
||||
url: '/order/purchase-order',
|
||||
@ -184,15 +198,22 @@ function updateSearch() {
|
||||
}
|
||||
|
||||
if (user_settings.SEARCH_PREVIEW_SHOW_SALES_ORDERS) {
|
||||
|
||||
var filters = {
|
||||
customer_detail: true,
|
||||
};
|
||||
|
||||
// Hide inactive (not "outstanding" orders)
|
||||
if (user_settings.SEARCH_PREVIEW_EXCLUDE_INACTIVE_SALES_ORDERS) {
|
||||
filters.outstanding = true;
|
||||
}
|
||||
|
||||
// Search for matching sales orders
|
||||
addSearchQuery(
|
||||
'salesorder',
|
||||
'{% trans "Sales Orders" %}',
|
||||
'{% url "api-so-list" %}',
|
||||
{
|
||||
customer_detail: true,
|
||||
outstanding: true,
|
||||
},
|
||||
filters,
|
||||
renderSalesOrder,
|
||||
{
|
||||
url: '/order/sales-order',
|
||||
|
Loading…
Reference in New Issue
Block a user