Merge remote-tracking branch 'matmair/webp-support' into webp-support

# Conflicts:
#	InvenTree/InvenTree/tests.py
This commit is contained in:
Oliver 2022-05-18 11:13:26 +10:00
commit 2cfa4c0a16
33 changed files with 29937 additions and 29453 deletions

View File

@ -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

View File

@ -353,7 +353,7 @@ TEMPLATES = [
]
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
'EXCEPTION_HANDLER': 'InvenTree.exceptions.exception_handler',
'DATETIME_FORMAT': '%Y-%m-%d %H:%M',
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',

View File

@ -1,5 +1,7 @@
import json
from test import support
import os
from unittest import mock
from django.test import TestCase, override_settings
import django.core.exceptions as django_exceptions
@ -449,17 +451,20 @@ class TestSettings(TestCase):
def setUp(self) -> None:
self.user_mdl = get_user_model()
self.env = support.EnvironmentVarGuard()
# Create a user for auth
user = get_user_model()
self.user = user.objects.create_superuser('testuser1', 'test1@testing.com', '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
with self.env:
with self.in_env_context(envs):
settings.USER_ADDED = False
registry.reload_plugins()
@ -475,25 +480,28 @@ class TestSettings(TestCase):
self.assertEqual(user_count(), 1)
# 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)
# enough set
self.env.set('INVENTREE_ADMIN_USER', 'admin') # set username
self.env.set('INVENTREE_ADMIN_EMAIL', 'info@example.com') # set email
self.env.set('INVENTREE_ADMIN_PASSWORD', 'password123') # set password
self.run_reload()
self.run_reload({
'INVENTREE_ADMIN_USER': 'admin', # set username
'INVENTREE_ADMIN_EMAIL': 'info@example.com', # set email
'INVENTREE_ADMIN_PASSWORD': 'password123' # set password
})
self.assertEqual(user_count(), 2)
# create user manually
self.user_mdl.objects.create_user('testuser', 'test@testing.com', 'password')
self.assertEqual(user_count(), 3)
# check it will not be created again
self.env.set('INVENTREE_ADMIN_USER', 'testuser')
self.env.set('INVENTREE_ADMIN_EMAIL', 'test@testing.com')
self.env.set('INVENTREE_ADMIN_PASSWORD', 'password')
self.run_reload()
self.run_reload({
'INVENTREE_ADMIN_USER': 'testuser',
'INVENTREE_ADMIN_EMAIL': 'test@testing.com',
'INVENTREE_ADMIN_PASSWORD': 'password',
})
self.assertEqual(user_count(), 3)
# make sure to clean up
@ -520,8 +528,7 @@ class TestSettings(TestCase):
self.assertIn('InvenTree/InvenTree/config.yaml', config.get_config_file())
# with env set
with self.env:
self.env.set('INVENTREE_CONFIG_FILE', 'my_special_conf.yaml')
with self.in_env_context({'INVENTREE_CONFIG_FILE': 'my_special_conf.yaml'}):
self.assertIn('InvenTree/InvenTree/my_special_conf.yaml', config.get_config_file())
def test_helpers_plugin_file(self):
@ -529,8 +536,7 @@ class TestSettings(TestCase):
self.assertIn('InvenTree/InvenTree/plugins.txt', config.get_plugin_file())
# with env set
with self.env:
self.env.set('INVENTREE_PLUGIN_FILE', 'my_special_plugins.txt')
with self.in_env_context({'INVENTREE_PLUGIN_FILE': 'my_special_plugins.txt'}):
self.assertIn('my_special_plugins.txt', config.get_plugin_file())
def test_helpers_setting(self):
@ -539,8 +545,7 @@ class TestSettings(TestCase):
self.assertEqual(config.get_setting(TEST_ENV_NAME, None, '123!'), '123!')
# with env set
with self.env:
self.env.set(TEST_ENV_NAME, '321')
with self.in_env_context({TEST_ENV_NAME: '321'}):
self.assertEqual(config.get_setting(TEST_ENV_NAME, None), '321')

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

View File

@ -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(),
),
]

View File

@ -1269,12 +1269,6 @@ class SalesOrderAllocation(models.Model):
def get_api_url():
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):
"""
Validate the SalesOrderAllocation object:

View File

@ -1284,14 +1284,18 @@ class SalesOrderShipmentAllocationSerializer(serializers.Serializer):
with transaction.atomic():
for entry in items:
# Create a new SalesOrderAllocation
order.models.SalesOrderAllocation.objects.create(
allocation = order.models.SalesOrderAllocation(
line=entry.get('line_item'),
item=entry.get('stock_item'),
quantity=entry.get('quantity'),
shipment=shipment,
)
allocation.full_clean()
allocation.save()
class SalesOrderExtraLineSerializer(AbstractExtraLineSerializer, InvenTreeModelSerializer):
""" Serializer for a SalesOrderExtraLine object """

View File

@ -225,6 +225,20 @@ function showApiError(xhr, url) {
default:
title = '{% trans "Unhandled Error Code" %}';
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;
}

View File

@ -8,11 +8,10 @@
![CI](https://github.com/inventree/inventree/actions/workflows/qc_checks.yaml/badge.svg)
![Docker Build](https://github.com/inventree/inventree/actions/workflows/docker_latest.yaml/badge.svg)
![Coveralls](https://img.shields.io/coveralls/github/inventree/InvenTree)
[![Coveralls](https://img.shields.io/coveralls/github/inventree/InvenTree)](https://coveralls.io/github/inventree/InvenTree)
[![Crowdin](https://badges.crowdin.net/inventree/localized.svg)](https://crowdin.com/project/inventree)
![Lines of code](https://img.shields.io/tokei/lines/github/inventree/InvenTree)
![GitHub commit activity](https://img.shields.io/github/commit-activity/m/inventree/inventree)
![PyPI - Downloads](https://img.shields.io/pypi/dm/inventree)
[![Docker Pulls](https://img.shields.io/docker/pulls/inventree/inventree)](https://hub.docker.com/r/inventree/inventree)
![GitHub Org's stars](https://img.shields.io/github/stars/inventree?style=social)