mirror of
https://github.com/inventree/InvenTree.git
synced 2026-05-22 09:08:56 +00:00
Merge remote-tracking branch 'matmair/webp-support' into webp-support
# Conflicts: # InvenTree/InvenTree/tests.py
This commit is contained in:
@@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
Custom exception handling for the DRF API
|
||||||
|
"""
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import traceback
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.debug import ExceptionReporter
|
||||||
|
|
||||||
|
from error_report.models import Error
|
||||||
|
|
||||||
|
from rest_framework.exceptions import ValidationError as DRFValidationError
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import serializers
|
||||||
|
import rest_framework.views as drfviews
|
||||||
|
|
||||||
|
|
||||||
|
def exception_handler(exc, context):
|
||||||
|
"""
|
||||||
|
Custom exception handler for DRF framework.
|
||||||
|
Ref: https://www.django-rest-framework.org/api-guide/exceptions/#custom-exception-handling
|
||||||
|
|
||||||
|
Catches any errors not natively handled by DRF, and re-throws as an error DRF can handle
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = None
|
||||||
|
|
||||||
|
# Catch any django validation error, and re-throw a DRF validation error
|
||||||
|
if isinstance(exc, DjangoValidationError):
|
||||||
|
exc = DRFValidationError(detail=serializers.as_serializer_error(exc))
|
||||||
|
|
||||||
|
# Default to the built-in DRF exception handler
|
||||||
|
response = drfviews.exception_handler(exc, context)
|
||||||
|
|
||||||
|
if response is None:
|
||||||
|
# DRF handler did not provide a default response for this exception
|
||||||
|
|
||||||
|
if settings.DEBUG:
|
||||||
|
error_detail = str(exc)
|
||||||
|
else:
|
||||||
|
error_detail = _("Error details can be found in the admin panel")
|
||||||
|
|
||||||
|
response_data = {
|
||||||
|
'error': type(exc).__name__,
|
||||||
|
'error_class': str(type(exc)),
|
||||||
|
'detail': error_detail,
|
||||||
|
'path': context['request'].path,
|
||||||
|
'status_code': 500,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = Response(response_data, status=500)
|
||||||
|
|
||||||
|
# Log the exception to the database, too
|
||||||
|
kind, info, data = sys.exc_info()
|
||||||
|
|
||||||
|
Error.objects.create(
|
||||||
|
kind=kind.__name__,
|
||||||
|
info=info,
|
||||||
|
data='\n'.join(traceback.format_exception(kind, info, data)),
|
||||||
|
path=context['request'].path,
|
||||||
|
html=ExceptionReporter(context['request'], kind, info, data).get_traceback_html(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if response is not None:
|
||||||
|
# Convert errors returned under the label '__all__' to 'non_field_errors'
|
||||||
|
if '__all__' in response.data:
|
||||||
|
response.data['non_field_errors'] = response.data['__all__']
|
||||||
|
del response.data['__all__']
|
||||||
|
|
||||||
|
return response
|
||||||
@@ -353,7 +353,7 @@ TEMPLATES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
|
'EXCEPTION_HANDLER': 'InvenTree.exceptions.exception_handler',
|
||||||
'DATETIME_FORMAT': '%Y-%m-%d %H:%M',
|
'DATETIME_FORMAT': '%Y-%m-%d %H:%M',
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||||
'rest_framework.authentication.BasicAuthentication',
|
'rest_framework.authentication.BasicAuthentication',
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
from test import support
|
import os
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
import django.core.exceptions as django_exceptions
|
import django.core.exceptions as django_exceptions
|
||||||
@@ -449,17 +451,20 @@ class TestSettings(TestCase):
|
|||||||
|
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
self.user_mdl = get_user_model()
|
self.user_mdl = get_user_model()
|
||||||
self.env = support.EnvironmentVarGuard()
|
|
||||||
|
|
||||||
# Create a user for auth
|
# Create a user for auth
|
||||||
user = get_user_model()
|
user = get_user_model()
|
||||||
self.user = user.objects.create_superuser('testuser1', 'test1@testing.com', 'password1')
|
self.user = user.objects.create_superuser('testuser1', 'test1@testing.com', 'password1')
|
||||||
self.client.login(username='testuser1', password='password1')
|
self.client.login(username='testuser1', password='password1')
|
||||||
|
|
||||||
def run_reload(self):
|
def in_env_context(self, envs={}):
|
||||||
|
"""Patch the env to include the given dict"""
|
||||||
|
return mock.patch.dict(os.environ, envs)
|
||||||
|
|
||||||
|
def run_reload(self, envs={}):
|
||||||
from plugin import registry
|
from plugin import registry
|
||||||
|
|
||||||
with self.env:
|
with self.in_env_context(envs):
|
||||||
settings.USER_ADDED = False
|
settings.USER_ADDED = False
|
||||||
registry.reload_plugins()
|
registry.reload_plugins()
|
||||||
|
|
||||||
@@ -475,25 +480,28 @@ class TestSettings(TestCase):
|
|||||||
self.assertEqual(user_count(), 1)
|
self.assertEqual(user_count(), 1)
|
||||||
|
|
||||||
# not enough set
|
# not enough set
|
||||||
self.env.set('INVENTREE_ADMIN_USER', 'admin') # set username
|
self.run_reload({
|
||||||
self.run_reload()
|
'INVENTREE_ADMIN_USER': 'admin'
|
||||||
|
})
|
||||||
self.assertEqual(user_count(), 1)
|
self.assertEqual(user_count(), 1)
|
||||||
|
|
||||||
# enough set
|
# enough set
|
||||||
self.env.set('INVENTREE_ADMIN_USER', 'admin') # set username
|
self.run_reload({
|
||||||
self.env.set('INVENTREE_ADMIN_EMAIL', 'info@example.com') # set email
|
'INVENTREE_ADMIN_USER': 'admin', # set username
|
||||||
self.env.set('INVENTREE_ADMIN_PASSWORD', 'password123') # set password
|
'INVENTREE_ADMIN_EMAIL': 'info@example.com', # set email
|
||||||
self.run_reload()
|
'INVENTREE_ADMIN_PASSWORD': 'password123' # set password
|
||||||
|
})
|
||||||
self.assertEqual(user_count(), 2)
|
self.assertEqual(user_count(), 2)
|
||||||
|
|
||||||
# create user manually
|
# create user manually
|
||||||
self.user_mdl.objects.create_user('testuser', 'test@testing.com', 'password')
|
self.user_mdl.objects.create_user('testuser', 'test@testing.com', 'password')
|
||||||
self.assertEqual(user_count(), 3)
|
self.assertEqual(user_count(), 3)
|
||||||
# check it will not be created again
|
# check it will not be created again
|
||||||
self.env.set('INVENTREE_ADMIN_USER', 'testuser')
|
self.run_reload({
|
||||||
self.env.set('INVENTREE_ADMIN_EMAIL', 'test@testing.com')
|
'INVENTREE_ADMIN_USER': 'testuser',
|
||||||
self.env.set('INVENTREE_ADMIN_PASSWORD', 'password')
|
'INVENTREE_ADMIN_EMAIL': 'test@testing.com',
|
||||||
self.run_reload()
|
'INVENTREE_ADMIN_PASSWORD': 'password',
|
||||||
|
})
|
||||||
self.assertEqual(user_count(), 3)
|
self.assertEqual(user_count(), 3)
|
||||||
|
|
||||||
# make sure to clean up
|
# make sure to clean up
|
||||||
@@ -520,8 +528,7 @@ class TestSettings(TestCase):
|
|||||||
self.assertIn('InvenTree/InvenTree/config.yaml', config.get_config_file())
|
self.assertIn('InvenTree/InvenTree/config.yaml', config.get_config_file())
|
||||||
|
|
||||||
# with env set
|
# with env set
|
||||||
with self.env:
|
with self.in_env_context({'INVENTREE_CONFIG_FILE': 'my_special_conf.yaml'}):
|
||||||
self.env.set('INVENTREE_CONFIG_FILE', 'my_special_conf.yaml')
|
|
||||||
self.assertIn('InvenTree/InvenTree/my_special_conf.yaml', config.get_config_file())
|
self.assertIn('InvenTree/InvenTree/my_special_conf.yaml', config.get_config_file())
|
||||||
|
|
||||||
def test_helpers_plugin_file(self):
|
def test_helpers_plugin_file(self):
|
||||||
@@ -529,8 +536,7 @@ class TestSettings(TestCase):
|
|||||||
self.assertIn('InvenTree/InvenTree/plugins.txt', config.get_plugin_file())
|
self.assertIn('InvenTree/InvenTree/plugins.txt', config.get_plugin_file())
|
||||||
|
|
||||||
# with env set
|
# with env set
|
||||||
with self.env:
|
with self.in_env_context({'INVENTREE_PLUGIN_FILE': 'my_special_plugins.txt'}):
|
||||||
self.env.set('INVENTREE_PLUGIN_FILE', 'my_special_plugins.txt')
|
|
||||||
self.assertIn('my_special_plugins.txt', config.get_plugin_file())
|
self.assertIn('my_special_plugins.txt', config.get_plugin_file())
|
||||||
|
|
||||||
def test_helpers_setting(self):
|
def test_helpers_setting(self):
|
||||||
@@ -539,8 +545,7 @@ class TestSettings(TestCase):
|
|||||||
self.assertEqual(config.get_setting(TEST_ENV_NAME, None, '123!'), '123!')
|
self.assertEqual(config.get_setting(TEST_ENV_NAME, None, '123!'), '123!')
|
||||||
|
|
||||||
# with env set
|
# with env set
|
||||||
with self.env:
|
with self.in_env_context({TEST_ENV_NAME: '321'}):
|
||||||
self.env.set(TEST_ENV_NAME, '321')
|
|
||||||
self.assertEqual(config.get_setting(TEST_ENV_NAME, None), '321')
|
self.assertEqual(config.get_setting(TEST_ENV_NAME, None), '321')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1177
-1162
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1306
-1291
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1203
-1188
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
+1175
-1160
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 3.2.13 on 2022-05-16 14:35
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('order', '0067_auto_20220516_1120'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='salesorderallocation',
|
||||||
|
unique_together=set(),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1269,12 +1269,6 @@ class SalesOrderAllocation(models.Model):
|
|||||||
def get_api_url():
|
def get_api_url():
|
||||||
return reverse('api-so-allocation-list')
|
return reverse('api-so-allocation-list')
|
||||||
|
|
||||||
class Meta:
|
|
||||||
unique_together = [
|
|
||||||
# Cannot allocate any given StockItem to the same line more than once
|
|
||||||
('line', 'item'),
|
|
||||||
]
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
"""
|
"""
|
||||||
Validate the SalesOrderAllocation object:
|
Validate the SalesOrderAllocation object:
|
||||||
|
|||||||
@@ -1284,14 +1284,18 @@ class SalesOrderShipmentAllocationSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
for entry in items:
|
for entry in items:
|
||||||
|
|
||||||
# Create a new SalesOrderAllocation
|
# Create a new SalesOrderAllocation
|
||||||
order.models.SalesOrderAllocation.objects.create(
|
allocation = order.models.SalesOrderAllocation(
|
||||||
line=entry.get('line_item'),
|
line=entry.get('line_item'),
|
||||||
item=entry.get('stock_item'),
|
item=entry.get('stock_item'),
|
||||||
quantity=entry.get('quantity'),
|
quantity=entry.get('quantity'),
|
||||||
shipment=shipment,
|
shipment=shipment,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
allocation.full_clean()
|
||||||
|
allocation.save()
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderExtraLineSerializer(AbstractExtraLineSerializer, InvenTreeModelSerializer):
|
class SalesOrderExtraLineSerializer(AbstractExtraLineSerializer, InvenTreeModelSerializer):
|
||||||
""" Serializer for a SalesOrderExtraLine object """
|
""" Serializer for a SalesOrderExtraLine object """
|
||||||
|
|||||||
@@ -225,6 +225,20 @@ function showApiError(xhr, url) {
|
|||||||
default:
|
default:
|
||||||
title = '{% trans "Unhandled Error Code" %}';
|
title = '{% trans "Unhandled Error Code" %}';
|
||||||
message = `{% trans "Error code" %}: ${xhr.status}`;
|
message = `{% trans "Error code" %}: ${xhr.status}`;
|
||||||
|
|
||||||
|
var response = xhr.responseJSON;
|
||||||
|
|
||||||
|
// The server may have provided some extra information about this error
|
||||||
|
if (response) {
|
||||||
|
if (response.error) {
|
||||||
|
title = response.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.detail) {
|
||||||
|
message = response.detail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,10 @@
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||

|
[](https://coveralls.io/github/inventree/InvenTree)
|
||||||
[](https://crowdin.com/project/inventree)
|
[](https://crowdin.com/project/inventree)
|
||||||

|

|
||||||

|

|
||||||

|
|
||||||
[](https://hub.docker.com/r/inventree/inventree)
|
[](https://hub.docker.com/r/inventree/inventree)
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
Reference in New Issue
Block a user