Merge pull request #2913 from matmair/codebase-cleanup

Codebase cleanup to python 3.9 / django 3.2
This commit is contained in:
Oliver 2022-05-02 09:25:52 +10:00 committed by GitHub
commit ef52be82d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 478 additions and 456 deletions

View File

@ -44,6 +44,31 @@ The HEAD of the "stable" branch represents the latest stable release code.
- When approved, the branch is merged back *into* stable, with an incremented PATCH number (e.g. 0.4.1 -> 0.4.2) - When approved, the branch is merged back *into* stable, with an incremented PATCH number (e.g. 0.4.1 -> 0.4.2)
- The bugfix *must* also be cherry picked into the *master* branch. - The bugfix *must* also be cherry picked into the *master* branch.
## Environment
#### Target version
We are currently targeting:
| Name | Minimum version |
|---|---|
| Python | 3.9 |
| Django | 3.2 |
### Auto creating updates
The following tools can be used to auto-upgrade syntax that was depreciated in new versions:
```bash
pip install pyupgrade
pip install django-upgrade
```
To update the codebase run the following script.
```bash
pyupgrade `find . -name "*.py"`
django-upgrade --target-version 3.2 `find . -name "*.py"`
```
### Credits
If you add any new dependencies / libraries, they need to be added to [the docs](https://github.com/inventree/inventree-docs/blob/master/docs/credits.md). Please try to do that as timely as possible.
## Migration Files ## Migration Files
Any required migration files **must** be included in the commit, or the pull-request will be rejected. If you change the underlying database schema, make sure you run `invoke migrate` and commit the migration files before submitting the PR. Any required migration files **must** be included in the commit, or the pull-request will be rejected. If you change the underlying database schema, make sure you run `invoke migrate` and commit the migration files before submitting the PR.

View File

@ -5,7 +5,7 @@ Main JSON interface views
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.conf import settings from django.conf import settings
from django.http import JsonResponse from django.http import JsonResponse

View File

@ -6,7 +6,7 @@ import sys
from .validators import allowable_url_schemes from .validators import allowable_url_schemes
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.forms.fields import URLField as FormURLField from django.forms.fields import URLField as FormURLField
from django.db import models as models from django.db import models as models

View File

@ -7,7 +7,7 @@ from __future__ import unicode_literals
from urllib.parse import urlencode from urllib.parse import urlencode
import logging import logging
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django import forms from django import forms
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from django.conf import settings from django.conf import settings
@ -319,7 +319,7 @@ class CustomSocialAccountAdapter(RegistratonMixin, DefaultSocialAccountAdapter):
redirect_url = reverse('two-factor-authenticate') redirect_url = reverse('two-factor-authenticate')
# Add GET parameters to the URL if they exist. # Add GET parameters to the URL if they exist.
if request.GET: if request.GET:
redirect_url += u'?' + urlencode(request.GET) redirect_url += '?' + urlencode(request.GET)
raise ImmediateHttpResponse( raise ImmediateHttpResponse(
response=HttpResponseRedirect(redirect_url) response=HttpResponseRedirect(redirect_url)

View File

@ -13,7 +13,7 @@ from decimal import Decimal, InvalidOperation
from wsgiref.util import FileWrapper from wsgiref.util import FileWrapper
from django.http import StreamingHttpResponse from django.http import StreamingHttpResponse
from django.core.exceptions import ValidationError, FieldError from django.core.exceptions import ValidationError, FieldError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
@ -432,7 +432,7 @@ def extract_serial_numbers(serials, expected_quantity, next_number: int):
next_number += 1 next_number += 1
# Split input string by whitespace or comma (,) characters # Split input string by whitespace or comma (,) characters
groups = re.split("[\s,]+", serials) groups = re.split(r"[\s,]+", serials)
numbers = [] numbers = []
errors = [] errors = []

View File

@ -1,7 +1,7 @@
from django.shortcuts import HttpResponseRedirect from django.shortcuts import HttpResponseRedirect
from django.urls import reverse_lazy, Resolver404 from django.urls import reverse_lazy, Resolver404
from django.shortcuts import redirect from django.shortcuts import redirect
from django.conf.urls import include, url from django.urls import include, re_path
from django.conf import settings from django.conf import settings
from django.contrib.auth.middleware import PersistentRemoteUserMiddleware from django.contrib.auth.middleware import PersistentRemoteUserMiddleware
@ -85,14 +85,14 @@ class AuthRequiredMiddleware(object):
if path not in urls and not path.startswith('/api/'): if path not in urls and not path.startswith('/api/'):
# Save the 'next' parameter to pass through to the login view # Save the 'next' parameter to pass through to the login view
return redirect('%s?next=%s' % (reverse_lazy('account_login'), request.path)) return redirect('{}?next={}'.format(reverse_lazy('account_login'), request.path))
response = self.get_response(request) response = self.get_response(request)
return response return response
url_matcher = url('', include(frontendpatterns)) url_matcher = re_path('', include(frontendpatterns))
class Check2FAMiddleware(BaseRequire2FAMiddleware): class Check2FAMiddleware(BaseRequire2FAMiddleware):

View File

@ -15,7 +15,7 @@ from collections import OrderedDict
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.db import models from django.db import models
from djmoney.contrib.django_rest_framework.fields import MoneyField from djmoney.contrib.django_rest_framework.fields import MoneyField

View File

@ -3,7 +3,7 @@ Provides system status functionality checks.
""" """
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.utils import timezone from django.utils import timezone
import logging import logging

View File

@ -1,4 +1,4 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
class StatusCode: class StatusCode:

View File

@ -1,4 +1,3 @@
import json import json
from test.support import EnvironmentVarGuard from test.support import EnvironmentVarGuard
@ -186,7 +185,7 @@ class TestDownloadFile(TestCase):
def test_download(self): def test_download(self):
helpers.DownloadFile("hello world", "out.txt") helpers.DownloadFile("hello world", "out.txt")
helpers.DownloadFile(bytes("hello world".encode("utf8")), "out.bin") helpers.DownloadFile(bytes(b"hello world"), "out.bin")
class TestMPTT(TestCase): class TestMPTT(TestCase):

View File

@ -4,8 +4,7 @@ Top-level URL lookup for InvenTree application.
Passes URL lookup downstream to each app as required. Passes URL lookup downstream to each app as required.
""" """
from django.conf.urls import url, include from django.urls import include, path, re_path
from django.urls import path
from django.contrib import admin from django.contrib import admin
from company.urls import company_urls from company.urls import company_urls
@ -56,144 +55,144 @@ apipatterns = []
if settings.PLUGINS_ENABLED: if settings.PLUGINS_ENABLED:
apipatterns.append( apipatterns.append(
url(r'^plugin/', include(plugin_api_urls)) re_path(r'^plugin/', include(plugin_api_urls))
) )
apipatterns += [ apipatterns += [
url(r'^barcode/', include(barcode_api_urls)), re_path(r'^barcode/', include(barcode_api_urls)),
url(r'^settings/', include(settings_api_urls)), re_path(r'^settings/', include(settings_api_urls)),
url(r'^part/', include(part_api_urls)), re_path(r'^part/', include(part_api_urls)),
url(r'^bom/', include(bom_api_urls)), re_path(r'^bom/', include(bom_api_urls)),
url(r'^company/', include(company_api_urls)), re_path(r'^company/', include(company_api_urls)),
url(r'^stock/', include(stock_api_urls)), re_path(r'^stock/', include(stock_api_urls)),
url(r'^build/', include(build_api_urls)), re_path(r'^build/', include(build_api_urls)),
url(r'^order/', include(order_api_urls)), re_path(r'^order/', include(order_api_urls)),
url(r'^label/', include(label_api_urls)), re_path(r'^label/', include(label_api_urls)),
url(r'^report/', include(report_api_urls)), re_path(r'^report/', include(report_api_urls)),
# User URLs # User URLs
url(r'^user/', include(user_urls)), re_path(r'^user/', include(user_urls)),
# Plugin endpoints # Plugin endpoints
url(r'^action/', ActionPluginView.as_view(), name='api-action-plugin'), re_path(r'^action/', ActionPluginView.as_view(), name='api-action-plugin'),
# Webhook enpoint # Webhook enpoint
path('', include(common_api_urls)), path('', include(common_api_urls)),
# InvenTree information endpoint # InvenTree information endpoint
url(r'^$', InfoView.as_view(), name='api-inventree-info'), path('', InfoView.as_view(), name='api-inventree-info'),
# Unknown endpoint # Unknown endpoint
url(r'^.*$', NotFoundView.as_view(), name='api-404'), re_path(r'^.*$', NotFoundView.as_view(), name='api-404'),
] ]
settings_urls = [ settings_urls = [
url(r'^i18n/?', include('django.conf.urls.i18n')), re_path(r'^i18n/?', include('django.conf.urls.i18n')),
url(r'^appearance/?', AppearanceSelectView.as_view(), name='settings-appearance'), re_path(r'^appearance/?', AppearanceSelectView.as_view(), name='settings-appearance'),
url(r'^currencies-refresh/', CurrencyRefreshView.as_view(), name='settings-currencies-refresh'), re_path(r'^currencies-refresh/', CurrencyRefreshView.as_view(), name='settings-currencies-refresh'),
url(r'^category/', SettingCategorySelectView.as_view(), name='settings-category'), re_path(r'^category/', SettingCategorySelectView.as_view(), name='settings-category'),
# Catch any other urls # Catch any other urls
url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/settings.html'), name='settings'), re_path(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/settings.html'), name='settings'),
] ]
notifications_urls = [ notifications_urls = [
# Catch any other urls # Catch any other urls
url(r'^.*$', NotificationsView.as_view(), name='notifications'), re_path(r'^.*$', NotificationsView.as_view(), name='notifications'),
] ]
# These javascript files are served "dynamically" - i.e. rendered on demand # These javascript files are served "dynamically" - i.e. rendered on demand
dynamic_javascript_urls = [ dynamic_javascript_urls = [
url(r'^calendar.js', DynamicJsView.as_view(template_name='js/dynamic/calendar.js'), name='calendar.js'), re_path(r'^calendar.js', DynamicJsView.as_view(template_name='js/dynamic/calendar.js'), name='calendar.js'),
url(r'^nav.js', DynamicJsView.as_view(template_name='js/dynamic/nav.js'), name='nav.js'), re_path(r'^nav.js', DynamicJsView.as_view(template_name='js/dynamic/nav.js'), name='nav.js'),
url(r'^settings.js', DynamicJsView.as_view(template_name='js/dynamic/settings.js'), name='settings.js'), re_path(r'^settings.js', DynamicJsView.as_view(template_name='js/dynamic/settings.js'), name='settings.js'),
] ]
# These javascript files are pased through the Django translation layer # These javascript files are pased through the Django translation layer
translated_javascript_urls = [ translated_javascript_urls = [
url(r'^api.js', DynamicJsView.as_view(template_name='js/translated/api.js'), name='api.js'), re_path(r'^api.js', DynamicJsView.as_view(template_name='js/translated/api.js'), name='api.js'),
url(r'^attachment.js', DynamicJsView.as_view(template_name='js/translated/attachment.js'), name='attachment.js'), re_path(r'^attachment.js', DynamicJsView.as_view(template_name='js/translated/attachment.js'), name='attachment.js'),
url(r'^barcode.js', DynamicJsView.as_view(template_name='js/translated/barcode.js'), name='barcode.js'), re_path(r'^barcode.js', DynamicJsView.as_view(template_name='js/translated/barcode.js'), name='barcode.js'),
url(r'^bom.js', DynamicJsView.as_view(template_name='js/translated/bom.js'), name='bom.js'), re_path(r'^bom.js', DynamicJsView.as_view(template_name='js/translated/bom.js'), name='bom.js'),
url(r'^build.js', DynamicJsView.as_view(template_name='js/translated/build.js'), name='build.js'), re_path(r'^build.js', DynamicJsView.as_view(template_name='js/translated/build.js'), name='build.js'),
url(r'^company.js', DynamicJsView.as_view(template_name='js/translated/company.js'), name='company.js'), re_path(r'^company.js', DynamicJsView.as_view(template_name='js/translated/company.js'), name='company.js'),
url(r'^filters.js', DynamicJsView.as_view(template_name='js/translated/filters.js'), name='filters.js'), re_path(r'^filters.js', DynamicJsView.as_view(template_name='js/translated/filters.js'), name='filters.js'),
url(r'^forms.js', DynamicJsView.as_view(template_name='js/translated/forms.js'), name='forms.js'), re_path(r'^forms.js', DynamicJsView.as_view(template_name='js/translated/forms.js'), name='forms.js'),
url(r'^helpers.js', DynamicJsView.as_view(template_name='js/translated/helpers.js'), name='helpers.js'), re_path(r'^helpers.js', DynamicJsView.as_view(template_name='js/translated/helpers.js'), name='helpers.js'),
url(r'^label.js', DynamicJsView.as_view(template_name='js/translated/label.js'), name='label.js'), re_path(r'^label.js', DynamicJsView.as_view(template_name='js/translated/label.js'), name='label.js'),
url(r'^model_renderers.js', DynamicJsView.as_view(template_name='js/translated/model_renderers.js'), name='model_renderers.js'), re_path(r'^model_renderers.js', DynamicJsView.as_view(template_name='js/translated/model_renderers.js'), name='model_renderers.js'),
url(r'^modals.js', DynamicJsView.as_view(template_name='js/translated/modals.js'), name='modals.js'), re_path(r'^modals.js', DynamicJsView.as_view(template_name='js/translated/modals.js'), name='modals.js'),
url(r'^order.js', DynamicJsView.as_view(template_name='js/translated/order.js'), name='order.js'), re_path(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'), re_path(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'), re_path(r'^report.js', DynamicJsView.as_view(template_name='js/translated/report.js'), name='report.js'),
url(r'^search.js', DynamicJsView.as_view(template_name='js/translated/search.js'), name='search.js'), re_path(r'^search.js', DynamicJsView.as_view(template_name='js/translated/search.js'), name='search.js'),
url(r'^stock.js', DynamicJsView.as_view(template_name='js/translated/stock.js'), name='stock.js'), re_path(r'^stock.js', DynamicJsView.as_view(template_name='js/translated/stock.js'), name='stock.js'),
url(r'^plugin.js', DynamicJsView.as_view(template_name='js/translated/plugin.js'), name='plugin.js'), re_path(r'^plugin.js', DynamicJsView.as_view(template_name='js/translated/plugin.js'), name='plugin.js'),
url(r'^tables.js', DynamicJsView.as_view(template_name='js/translated/tables.js'), name='tables.js'), re_path(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'), re_path(r'^table_filters.js', DynamicJsView.as_view(template_name='js/translated/table_filters.js'), name='table_filters.js'),
url(r'^notification.js', DynamicJsView.as_view(template_name='js/translated/notification.js'), name='notification.js'), re_path(r'^notification.js', DynamicJsView.as_view(template_name='js/translated/notification.js'), name='notification.js'),
] ]
backendpatterns = [ backendpatterns = [
# "Dynamic" javascript files which are rendered using InvenTree templating. # "Dynamic" javascript files which are rendered using InvenTree templating.
url(r'^js/dynamic/', include(dynamic_javascript_urls)), re_path(r'^js/dynamic/', include(dynamic_javascript_urls)),
url(r'^js/i18n/', include(translated_javascript_urls)), re_path(r'^js/i18n/', include(translated_javascript_urls)),
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')), re_path(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^auth/?', auth_request), re_path(r'^auth/?', auth_request),
url(r'^api/', include(apipatterns)), re_path(r'^api/', include(apipatterns)),
url(r'^api-doc/', include_docs_urls(title='InvenTree API')), re_path(r'^api-doc/', include_docs_urls(title='InvenTree API')),
# 3rd party endpoints # 3rd party endpoints
url(r'^markdownx/', include('markdownx.urls')), re_path(r'^markdownx/', include('markdownx.urls')),
] ]
frontendpatterns = [ frontendpatterns = [
url(r'^part/', include(part_urls)), re_path(r'^part/', include(part_urls)),
url(r'^manufacturer-part/', include(manufacturer_part_urls)), re_path(r'^manufacturer-part/', include(manufacturer_part_urls)),
url(r'^supplier-part/', include(supplier_part_urls)), re_path(r'^supplier-part/', include(supplier_part_urls)),
url(r'^common/', include(common_urls)), re_path(r'^common/', include(common_urls)),
url(r'^stock/', include(stock_urls)), re_path(r'^stock/', include(stock_urls)),
url(r'^company/', include(company_urls)), re_path(r'^company/', include(company_urls)),
url(r'^order/', include(order_urls)), re_path(r'^order/', include(order_urls)),
url(r'^build/', include(build_urls)), re_path(r'^build/', include(build_urls)),
url(r'^settings/', include(settings_urls)), re_path(r'^settings/', include(settings_urls)),
url(r'^notifications/', include(notifications_urls)), re_path(r'^notifications/', include(notifications_urls)),
url(r'^edit-user/', EditUserView.as_view(), name='edit-user'), re_path(r'^edit-user/', EditUserView.as_view(), name='edit-user'),
url(r'^set-password/', SetPasswordView.as_view(), name='set-password'), re_path(r'^set-password/', SetPasswordView.as_view(), name='set-password'),
url(r'^index/', IndexView.as_view(), name='index'), re_path(r'^index/', IndexView.as_view(), name='index'),
url(r'^search/', SearchView.as_view(), name='search'), re_path(r'^search/', SearchView.as_view(), name='search'),
url(r'^stats/', DatabaseStatsView.as_view(), name='stats'), re_path(r'^stats/', DatabaseStatsView.as_view(), name='stats'),
# admin sites # admin sites
url(f'^{settings.INVENTREE_ADMIN_URL}/error_log/', include('error_report.urls')), re_path(f'^{settings.INVENTREE_ADMIN_URL}/error_log/', include('error_report.urls')),
url(f'^{settings.INVENTREE_ADMIN_URL}/shell/', include('django_admin_shell.urls')), re_path(f'^{settings.INVENTREE_ADMIN_URL}/shell/', include('django_admin_shell.urls')),
url(f'^{settings.INVENTREE_ADMIN_URL}/', admin.site.urls, name='inventree-admin'), re_path(f'^{settings.INVENTREE_ADMIN_URL}/', admin.site.urls, name='inventree-admin'),
# DB user sessions # DB user sessions
url(r'^accounts/sessions/other/delete/$', view=CustomSessionDeleteOtherView.as_view(), name='session_delete_other', ), path('accounts/sessions/other/delete/', view=CustomSessionDeleteOtherView.as_view(), name='session_delete_other', ),
url(r'^accounts/sessions/(?P<pk>\w+)/delete/$', view=CustomSessionDeleteView.as_view(), name='session_delete', ), re_path(r'^accounts/sessions/(?P<pk>\w+)/delete/$', view=CustomSessionDeleteView.as_view(), name='session_delete', ),
# Single Sign On / allauth # Single Sign On / allauth
# overrides of urlpatterns # overrides of urlpatterns
url(r'^accounts/email/', CustomEmailView.as_view(), name='account_email'), re_path(r'^accounts/email/', CustomEmailView.as_view(), name='account_email'),
url(r'^accounts/social/connections/', CustomConnectionsView.as_view(), name='socialaccount_connections'), re_path(r'^accounts/social/connections/', CustomConnectionsView.as_view(), name='socialaccount_connections'),
url(r"^accounts/password/reset/key/(?P<uidb36>[0-9A-Za-z]+)-(?P<key>.+)/$", CustomPasswordResetFromKeyView.as_view(), name="account_reset_password_from_key"), re_path(r"^accounts/password/reset/key/(?P<uidb36>[0-9A-Za-z]+)-(?P<key>.+)/$", CustomPasswordResetFromKeyView.as_view(), name="account_reset_password_from_key"),
url(r'^accounts/', include('allauth_2fa.urls')), # MFA support re_path(r'^accounts/', include('allauth_2fa.urls')), # MFA support
url(r'^accounts/', include('allauth.urls')), # included urlpatterns re_path(r'^accounts/', include('allauth.urls')), # included urlpatterns
] ]
# Append custom plugin URLs (if plugin support is enabled) # Append custom plugin URLs (if plugin support is enabled)
@ -201,8 +200,8 @@ if settings.PLUGINS_ENABLED:
frontendpatterns.append(get_plugin_urls()) frontendpatterns.append(get_plugin_urls())
urlpatterns = [ urlpatterns = [
url('', include(frontendpatterns)), re_path('', include(frontendpatterns)),
url('', include(backendpatterns)), re_path('', include(backendpatterns)),
] ]
# Server running in "DEBUG" mode? # Server running in "DEBUG" mode?
@ -221,4 +220,4 @@ if settings.DEBUG:
] + urlpatterns ] + urlpatterns
# Send any unknown URLs to the parts page # Send any unknown URLs to the parts page
urlpatterns += [url(r'^.*$', RedirectView.as_view(url='/index/', permanent=False), name='index')] urlpatterns += [re_path(r'^.*$', RedirectView.as_view(url='/index/', permanent=False), name='index')]

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.urls import reverse from django.urls import reverse
from django.conf.urls import url from django.urls import path, re_path
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from rest_framework import permissions from rest_framework import permissions
@ -240,8 +240,8 @@ class BarcodeAssign(APIView):
barcode_api_urls = [ barcode_api_urls = [
url(r'^link/$', BarcodeAssign.as_view(), name='api-barcode-link'), path('link/', BarcodeAssign.as_view(), name='api-barcode-link'),
# Catch-all performs barcode 'scan' # Catch-all performs barcode 'scan'
url(r'^.*$', BarcodeScan.as_view(), name='api-barcode-scan'), re_path(r'^.*$', BarcodeScan.as_view(), name='api-barcode-scan'),
] ]

View File

@ -5,7 +5,7 @@ JSON API for the Build app
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import url, include from django.urls import include, re_path
from rest_framework import filters, generics from rest_framework import filters, generics
@ -508,29 +508,29 @@ class BuildAttachmentDetail(generics.RetrieveUpdateDestroyAPIView, AttachmentMix
build_api_urls = [ build_api_urls = [
# Attachments # Attachments
url(r'^attachment/', include([ re_path(r'^attachment/', include([
url(r'^(?P<pk>\d+)/', BuildAttachmentDetail.as_view(), name='api-build-attachment-detail'), re_path(r'^(?P<pk>\d+)/', BuildAttachmentDetail.as_view(), name='api-build-attachment-detail'),
url(r'^.*$', BuildAttachmentList.as_view(), name='api-build-attachment-list'), re_path(r'^.*$', BuildAttachmentList.as_view(), name='api-build-attachment-list'),
])), ])),
# Build Items # Build Items
url(r'^item/', include([ re_path(r'^item/', include([
url(r'^(?P<pk>\d+)/', BuildItemDetail.as_view(), name='api-build-item-detail'), re_path(r'^(?P<pk>\d+)/', BuildItemDetail.as_view(), name='api-build-item-detail'),
url(r'^.*$', BuildItemList.as_view(), name='api-build-item-list'), re_path(r'^.*$', BuildItemList.as_view(), name='api-build-item-list'),
])), ])),
# Build Detail # Build Detail
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'^allocate/', BuildAllocate.as_view(), name='api-build-allocate'), re_path(r'^allocate/', BuildAllocate.as_view(), name='api-build-allocate'),
url(r'^auto-allocate/', BuildAutoAllocate.as_view(), name='api-build-auto-allocate'), re_path(r'^auto-allocate/', BuildAutoAllocate.as_view(), name='api-build-auto-allocate'),
url(r'^complete/', BuildOutputComplete.as_view(), name='api-build-output-complete'), re_path(r'^complete/', BuildOutputComplete.as_view(), name='api-build-output-complete'),
url(r'^create-output/', BuildOutputCreate.as_view(), name='api-build-output-create'), re_path(r'^create-output/', BuildOutputCreate.as_view(), name='api-build-output-create'),
url(r'^delete-outputs/', BuildOutputDelete.as_view(), name='api-build-output-delete'), re_path(r'^delete-outputs/', BuildOutputDelete.as_view(), name='api-build-output-delete'),
url(r'^finish/', BuildFinish.as_view(), name='api-build-finish'), re_path(r'^finish/', BuildFinish.as_view(), name='api-build-finish'),
url(r'^unallocate/', BuildUnallocate.as_view(), name='api-build-unallocate'), re_path(r'^unallocate/', BuildUnallocate.as_view(), name='api-build-unallocate'),
url(r'^.*$', BuildDetail.as_view(), name='api-build-detail'), re_path(r'^.*$', BuildDetail.as_view(), name='api-build-detail'),
])), ])),
# Build List # Build List
url(r'^.*$', BuildList.as_view(), name='api-build-list'), re_path(r'^.*$', BuildList.as_view(), name='api-build-list'),
] ]

View File

@ -6,7 +6,7 @@ Django Forms for interacting with Build objects
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django import forms from django import forms
from InvenTree.forms import HelperForm from InvenTree.forms import HelperForm

View File

@ -18,7 +18,7 @@ from django.db.models.functions import Coalesce
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch.dispatcher import receiver from django.dispatch.dispatcher import receiver
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from markdownx.models import MarkdownxField from markdownx.models import MarkdownxField
@ -53,7 +53,7 @@ def get_next_build_number():
build = Build.objects.exclude(reference=None).last() build = Build.objects.exclude(reference=None).last()
attempts = set([build.reference]) attempts = {build.reference}
reference = build.reference reference = build.reference

View File

@ -7,7 +7,7 @@ from __future__ import unicode_literals
from django.db import transaction from django.db import transaction
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.db.models import Case, When, Value from django.db.models import Case, When, Value
from django.db.models import BooleanField from django.db.models import BooleanField

View File

@ -4,7 +4,7 @@ from __future__ import unicode_literals
from decimal import Decimal from decimal import Decimal
import logging import logging
from django.utils.translation import ugettext_lazy as _ 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 allauth.account.models import EmailAddress from allauth.account.models import EmailAddress

View File

@ -2,20 +2,20 @@
URL lookup for Build app URL lookup for Build app
""" """
from django.conf.urls import url, include from django.urls import include, re_path
from . import views from . import views
build_detail_urls = [ build_detail_urls = [
url(r'^cancel/', views.BuildCancel.as_view(), name='build-cancel'), re_path(r'^cancel/', views.BuildCancel.as_view(), name='build-cancel'),
url(r'^delete/', views.BuildDelete.as_view(), name='build-delete'), re_path(r'^delete/', views.BuildDelete.as_view(), name='build-delete'),
url(r'^.*$', views.BuildDetail.as_view(), name='build-detail'), re_path(r'^.*$', views.BuildDetail.as_view(), name='build-detail'),
] ]
build_urls = [ build_urls = [
url(r'^(?P<pk>\d+)/', include(build_detail_urls)), re_path(r'^(?P<pk>\d+)/', include(build_detail_urls)),
url(r'.*$', views.BuildIndex.as_view(), name='build-index'), re_path(r'.*$', views.BuildIndex.as_view(), name='build-index'),
] ]

View File

@ -5,7 +5,7 @@ Django views for interacting with Build objects
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, ListView from django.views.generic import DetailView, ListView
from .models import Build from .models import Build

View File

@ -11,7 +11,7 @@ from django.http.response import HttpResponse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.urls import path from django.urls import path
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.conf.urls import url, include from django.urls import include, re_path
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
@ -336,21 +336,21 @@ class NotificationReadAll(generics.RetrieveAPIView):
settings_api_urls = [ settings_api_urls = [
# User settings # User settings
url(r'^user/', include([ re_path(r'^user/', include([
# User Settings Detail # User Settings Detail
url(r'^(?P<pk>\d+)/', UserSettingsDetail.as_view(), name='api-user-setting-detail'), re_path(r'^(?P<pk>\d+)/', UserSettingsDetail.as_view(), name='api-user-setting-detail'),
# User Settings List # User Settings List
url(r'^.*$', UserSettingsList.as_view(), name='api-user-setting-list'), re_path(r'^.*$', UserSettingsList.as_view(), name='api-user-setting-list'),
])), ])),
# Global settings # Global settings
url(r'^global/', include([ re_path(r'^global/', include([
# Global Settings Detail # Global Settings Detail
url(r'^(?P<pk>\d+)/', GlobalSettingsDetail.as_view(), name='api-global-setting-detail'), re_path(r'^(?P<pk>\d+)/', GlobalSettingsDetail.as_view(), name='api-global-setting-detail'),
# Global Settings List # Global Settings List
url(r'^.*$', GlobalSettingsList.as_view(), name='api-global-setting-list'), re_path(r'^.*$', GlobalSettingsList.as_view(), name='api-global-setting-list'),
])), ])),
] ]
@ -359,18 +359,18 @@ common_api_urls = [
path('webhook/<slug:endpoint>/', WebhookView.as_view(), name='api-webhook'), path('webhook/<slug:endpoint>/', WebhookView.as_view(), name='api-webhook'),
# Notifications # Notifications
url(r'^notifications/', include([ re_path(r'^notifications/', include([
# Individual purchase order detail URLs # Individual purchase order detail URLs
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'^read/', NotificationRead.as_view(), name='api-notifications-read'), re_path(r'^read/', NotificationRead.as_view(), name='api-notifications-read'),
url(r'^unread/', NotificationUnread.as_view(), name='api-notifications-unread'), re_path(r'^unread/', NotificationUnread.as_view(), name='api-notifications-unread'),
url(r'.*$', NotificationDetail.as_view(), name='api-notifications-detail'), re_path(r'.*$', NotificationDetail.as_view(), name='api-notifications-detail'),
])), ])),
# Read all # Read all
url(r'^readall/', NotificationReadAll.as_view(), name='api-notifications-readall'), re_path(r'^readall/', NotificationReadAll.as_view(), name='api-notifications-readall'),
# Notification messages list # Notification messages list
url(r'^.*$', NotificationList.as_view(), name='api-notifications-list'), re_path(r'^.*$', NotificationList.as_view(), name='api-notifications-list'),
])), ])),
] ]

View File

@ -33,7 +33,7 @@ from djmoney.contrib.exchange.exceptions import MissingRate
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.core.validators import MinValueValidator, URLValidator from django.core.validators import MinValueValidator, URLValidator
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.test import TestCase from django.test import TestCase

View File

@ -7,7 +7,7 @@ from __future__ import unicode_literals
import os import os
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.conf import settings from django.conf import settings
from django.core.files.storage import FileSystemStorage from django.core.files.storage import FileSystemStorage

View File

@ -11,7 +11,7 @@ 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
from django.conf.urls import url, include from django.urls import include, re_path
from django.db.models import Q from django.db.models import Q
from InvenTree.helpers import str2bool from InvenTree.helpers import str2bool
@ -390,42 +390,42 @@ class SupplierPriceBreakDetail(generics.RetrieveUpdateDestroyAPIView):
manufacturer_part_api_urls = [ manufacturer_part_api_urls = [
url(r'^parameter/', include([ re_path(r'^parameter/', include([
url(r'^(?P<pk>\d+)/', ManufacturerPartParameterDetail.as_view(), name='api-manufacturer-part-parameter-detail'), re_path(r'^(?P<pk>\d+)/', ManufacturerPartParameterDetail.as_view(), name='api-manufacturer-part-parameter-detail'),
# Catch anything else # Catch anything else
url(r'^.*$', ManufacturerPartParameterList.as_view(), name='api-manufacturer-part-parameter-list'), re_path(r'^.*$', ManufacturerPartParameterList.as_view(), name='api-manufacturer-part-parameter-list'),
])), ])),
url(r'^(?P<pk>\d+)/?', ManufacturerPartDetail.as_view(), name='api-manufacturer-part-detail'), re_path(r'^(?P<pk>\d+)/?', ManufacturerPartDetail.as_view(), name='api-manufacturer-part-detail'),
# Catch anything else # Catch anything else
url(r'^.*$', ManufacturerPartList.as_view(), name='api-manufacturer-part-list'), re_path(r'^.*$', ManufacturerPartList.as_view(), name='api-manufacturer-part-list'),
] ]
supplier_part_api_urls = [ supplier_part_api_urls = [
url(r'^(?P<pk>\d+)/?', SupplierPartDetail.as_view(), name='api-supplier-part-detail'), re_path(r'^(?P<pk>\d+)/?', SupplierPartDetail.as_view(), name='api-supplier-part-detail'),
# Catch anything else # Catch anything else
url(r'^.*$', SupplierPartList.as_view(), name='api-supplier-part-list'), re_path(r'^.*$', SupplierPartList.as_view(), name='api-supplier-part-list'),
] ]
company_api_urls = [ company_api_urls = [
url(r'^part/manufacturer/', include(manufacturer_part_api_urls)), re_path(r'^part/manufacturer/', include(manufacturer_part_api_urls)),
url(r'^part/', include(supplier_part_api_urls)), re_path(r'^part/', include(supplier_part_api_urls)),
# Supplier price breaks # Supplier price breaks
url(r'^price-break/', include([ re_path(r'^price-break/', include([
url(r'^(?P<pk>\d+)/?', SupplierPriceBreakDetail.as_view(), name='api-part-supplier-price-detail'), re_path(r'^(?P<pk>\d+)/?', SupplierPriceBreakDetail.as_view(), name='api-part-supplier-price-detail'),
url(r'^.*$', SupplierPriceBreakList.as_view(), name='api-part-supplier-price-list'), re_path(r'^.*$', SupplierPriceBreakList.as_view(), name='api-part-supplier-price-list'),
])), ])),
url(r'^(?P<pk>\d+)/?', CompanyDetail.as_view(), name='api-company-detail'), re_path(r'^(?P<pk>\d+)/?', CompanyDetail.as_view(), name='api-company-detail'),
url(r'^.*$', CompanyList.as_view(), name='api-company-list'), re_path(r'^.*$', CompanyList.as_view(), name='api-company-list'),
] ]

View File

@ -8,7 +8,7 @@ from __future__ import unicode_literals
from InvenTree.forms import HelperForm from InvenTree.forms import HelperForm
from InvenTree.fields import RoundingDecimalFormField from InvenTree.fields import RoundingDecimalFormField
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
import django.forms import django.forms
from .models import Company from .models import Company

View File

@ -7,7 +7,7 @@ from __future__ import unicode_literals
import os import os
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError

View File

@ -2,7 +2,7 @@
JSON serializers for Company app JSON serializers for Company app
""" """
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers

View File

@ -2,37 +2,37 @@
URL lookup for Company app URL lookup for Company app
""" """
from django.conf.urls import url, include from django.urls import include, re_path
from . import views from . import views
company_detail_urls = [ company_detail_urls = [
url(r'^thumb-download/', views.CompanyImageDownloadFromURL.as_view(), name='company-image-download'), re_path(r'^thumb-download/', views.CompanyImageDownloadFromURL.as_view(), name='company-image-download'),
# Any other URL # Any other URL
url(r'^.*$', views.CompanyDetail.as_view(), name='company-detail'), re_path(r'^.*$', views.CompanyDetail.as_view(), name='company-detail'),
] ]
company_urls = [ company_urls = [
url(r'^(?P<pk>\d+)/', include(company_detail_urls)), re_path(r'^(?P<pk>\d+)/', include(company_detail_urls)),
url(r'suppliers/', views.CompanyIndex.as_view(), name='supplier-index'), re_path(r'suppliers/', views.CompanyIndex.as_view(), name='supplier-index'),
url(r'manufacturers/', views.CompanyIndex.as_view(), name='manufacturer-index'), re_path(r'manufacturers/', views.CompanyIndex.as_view(), name='manufacturer-index'),
url(r'customers/', views.CompanyIndex.as_view(), name='customer-index'), re_path(r'customers/', views.CompanyIndex.as_view(), name='customer-index'),
# Redirect any other patterns to the 'company' index which displays all companies # Redirect any other patterns to the 'company' index which displays all companies
url(r'^.*$', views.CompanyIndex.as_view(), name='company-index'), re_path(r'^.*$', views.CompanyIndex.as_view(), name='company-index'),
] ]
manufacturer_part_urls = [ manufacturer_part_urls = [
url(r'^(?P<pk>\d+)/', views.ManufacturerPartDetail.as_view(template_name='company/manufacturer_part.html'), name='manufacturer-part-detail'), re_path(r'^(?P<pk>\d+)/', views.ManufacturerPartDetail.as_view(template_name='company/manufacturer_part.html'), name='manufacturer-part-detail'),
] ]
supplier_part_urls = [ supplier_part_urls = [
url(r'^(?P<pk>\d+)/', views.SupplierPartDetail.as_view(template_name='company/supplier_part.html'), name='supplier-part-detail'), re_path(r'^(?P<pk>\d+)/', views.SupplierPartDetail.as_view(template_name='company/supplier_part.html'), name='supplier-part-detail'),
] ]

View File

@ -6,7 +6,7 @@ Django views for interacting with Company app
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, ListView from django.views.generic import DetailView, ListView
from django.urls import reverse from django.urls import reverse

View File

@ -4,10 +4,10 @@ from __future__ import unicode_literals
from io import BytesIO from io import BytesIO
from PIL import Image from PIL import Image
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.conf import settings from django.conf import settings
from django.conf.urls import url, include from django.urls import include, re_path
from django.core.exceptions import ValidationError, FieldError from django.core.exceptions import ValidationError, FieldError
from django.http import HttpResponse, JsonResponse from django.http import HttpResponse, JsonResponse
@ -579,38 +579,38 @@ class PartLabelPrint(generics.RetrieveAPIView, PartLabelMixin, LabelPrintMixin):
label_api_urls = [ label_api_urls = [
# Stock item labels # Stock item labels
url(r'stock/', include([ re_path(r'stock/', include([
# Detail views # Detail views
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'print/?', StockItemLabelPrint.as_view(), name='api-stockitem-label-print'), re_path(r'print/?', StockItemLabelPrint.as_view(), name='api-stockitem-label-print'),
url(r'^.*$', StockItemLabelDetail.as_view(), name='api-stockitem-label-detail'), re_path(r'^.*$', StockItemLabelDetail.as_view(), name='api-stockitem-label-detail'),
])), ])),
# List view # List view
url(r'^.*$', StockItemLabelList.as_view(), name='api-stockitem-label-list'), re_path(r'^.*$', StockItemLabelList.as_view(), name='api-stockitem-label-list'),
])), ])),
# Stock location labels # Stock location labels
url(r'location/', include([ re_path(r'location/', include([
# Detail views # Detail views
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'print/?', StockLocationLabelPrint.as_view(), name='api-stocklocation-label-print'), re_path(r'print/?', StockLocationLabelPrint.as_view(), name='api-stocklocation-label-print'),
url(r'^.*$', StockLocationLabelDetail.as_view(), name='api-stocklocation-label-detail'), re_path(r'^.*$', StockLocationLabelDetail.as_view(), name='api-stocklocation-label-detail'),
])), ])),
# List view # List view
url(r'^.*$', StockLocationLabelList.as_view(), name='api-stocklocation-label-list'), re_path(r'^.*$', StockLocationLabelList.as_view(), name='api-stocklocation-label-list'),
])), ])),
# Part labels # Part labels
url(r'^part/', include([ re_path(r'^part/', include([
# Detail views # Detail views
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'^print/', PartLabelPrint.as_view(), name='api-part-label-print'), re_path(r'^print/', PartLabelPrint.as_view(), name='api-part-label-print'),
url(r'^.*$', PartLabelDetail.as_view(), name='api-part-label-detail'), re_path(r'^.*$', PartLabelDetail.as_view(), name='api-part-label-detail'),
])), ])),
# List view # List view
url(r'^.*$', PartLabelList.as_view(), name='api-part-label-list'), re_path(r'^.*$', PartLabelList.as_view(), name='api-part-label-list'),
])), ])),
] ]

View File

@ -5,7 +5,7 @@ JSON API for the Order app
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import url, include from django.urls import include, path, re_path
from django.db.models import Q, F from django.db.models import Q, F
from django_filters import rest_framework as rest_filters from django_filters import rest_framework as rest_filters
@ -1096,78 +1096,78 @@ class PurchaseOrderAttachmentDetail(generics.RetrieveUpdateDestroyAPIView, Attac
order_api_urls = [ order_api_urls = [
# API endpoints for purchase orders # API endpoints for purchase orders
url(r'^po/', include([ re_path(r'^po/', include([
# Purchase order attachments # Purchase order attachments
url(r'attachment/', include([ re_path(r'attachment/', include([
url(r'^(?P<pk>\d+)/$', PurchaseOrderAttachmentDetail.as_view(), name='api-po-attachment-detail'), path('<int:pk>/', PurchaseOrderAttachmentDetail.as_view(), name='api-po-attachment-detail'),
url(r'^.*$', PurchaseOrderAttachmentList.as_view(), name='api-po-attachment-list'), re_path(r'^.*$', PurchaseOrderAttachmentList.as_view(), name='api-po-attachment-list'),
])), ])),
# Individual purchase order detail URLs # Individual purchase order detail URLs
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'^receive/', PurchaseOrderReceive.as_view(), name='api-po-receive'), re_path(r'^receive/', PurchaseOrderReceive.as_view(), name='api-po-receive'),
url(r'.*$', PurchaseOrderDetail.as_view(), name='api-po-detail'), re_path(r'.*$', PurchaseOrderDetail.as_view(), name='api-po-detail'),
])), ])),
# Purchase order list # Purchase order list
url(r'^.*$', PurchaseOrderList.as_view(), name='api-po-list'), re_path(r'^.*$', PurchaseOrderList.as_view(), name='api-po-list'),
])), ])),
# API endpoints for purchase order line items # API endpoints for purchase order line items
url(r'^po-line/', include([ re_path(r'^po-line/', include([
url(r'^(?P<pk>\d+)/$', PurchaseOrderLineItemDetail.as_view(), name='api-po-line-detail'), path('<int:pk>/', PurchaseOrderLineItemDetail.as_view(), name='api-po-line-detail'),
url(r'^.*$', PurchaseOrderLineItemList.as_view(), name='api-po-line-list'), re_path(r'^.*$', PurchaseOrderLineItemList.as_view(), name='api-po-line-list'),
])), ])),
# API endpoints for purchase order extra line # API endpoints for purchase order extra line
url(r'^po-extra-line/', include([ re_path(r'^po-extra-line/', include([
url(r'^(?P<pk>\d+)/$', PurchaseOrderExtraLineDetail.as_view(), name='api-po-extra-line-detail'), path('<int:pk>/', PurchaseOrderExtraLineDetail.as_view(), name='api-po-extra-line-detail'),
url(r'^$', PurchaseOrderExtraLineList.as_view(), name='api-po-extra-line-list'), path('', PurchaseOrderExtraLineList.as_view(), name='api-po-extra-line-list'),
])), ])),
# API endpoints for sales ordesr # API endpoints for sales ordesr
url(r'^so/', include([ re_path(r'^so/', include([
url(r'attachment/', include([ re_path(r'attachment/', include([
url(r'^(?P<pk>\d+)/$', SalesOrderAttachmentDetail.as_view(), name='api-so-attachment-detail'), path('<int:pk>/', SalesOrderAttachmentDetail.as_view(), name='api-so-attachment-detail'),
url(r'^.*$', SalesOrderAttachmentList.as_view(), name='api-so-attachment-list'), re_path(r'^.*$', SalesOrderAttachmentList.as_view(), name='api-so-attachment-list'),
])), ])),
url(r'^shipment/', include([ re_path(r'^shipment/', include([
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'^ship/$', SalesOrderShipmentComplete.as_view(), name='api-so-shipment-ship'), path('ship/', SalesOrderShipmentComplete.as_view(), name='api-so-shipment-ship'),
url(r'^.*$', SalesOrderShipmentDetail.as_view(), name='api-so-shipment-detail'), re_path(r'^.*$', SalesOrderShipmentDetail.as_view(), name='api-so-shipment-detail'),
])), ])),
url(r'^.*$', SalesOrderShipmentList.as_view(), name='api-so-shipment-list'), re_path(r'^.*$', SalesOrderShipmentList.as_view(), name='api-so-shipment-list'),
])), ])),
# Sales order detail view # Sales order detail view
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'^complete/', SalesOrderComplete.as_view(), name='api-so-complete'), re_path(r'^complete/', SalesOrderComplete.as_view(), name='api-so-complete'),
url(r'^allocate/', SalesOrderAllocate.as_view(), name='api-so-allocate'), re_path(r'^allocate/', SalesOrderAllocate.as_view(), name='api-so-allocate'),
url(r'^allocate-serials/', SalesOrderAllocateSerials.as_view(), name='api-so-allocate-serials'), re_path(r'^allocate-serials/', SalesOrderAllocateSerials.as_view(), name='api-so-allocate-serials'),
url(r'^.*$', SalesOrderDetail.as_view(), name='api-so-detail'), re_path(r'^.*$', SalesOrderDetail.as_view(), name='api-so-detail'),
])), ])),
# Sales order list view # Sales order list view
url(r'^.*$', SalesOrderList.as_view(), name='api-so-list'), re_path(r'^.*$', SalesOrderList.as_view(), name='api-so-list'),
])), ])),
# API endpoints for sales order line items # API endpoints for sales order line items
url(r'^so-line/', include([ re_path(r'^so-line/', include([
url(r'^(?P<pk>\d+)/$', SalesOrderLineItemDetail.as_view(), name='api-so-line-detail'), path('<int:pk>/', SalesOrderLineItemDetail.as_view(), name='api-so-line-detail'),
url(r'^$', SalesOrderLineItemList.as_view(), name='api-so-line-list'), path('', SalesOrderLineItemList.as_view(), name='api-so-line-list'),
])), ])),
# API endpoints for sales order extra line # API endpoints for sales order extra line
url(r'^so-extra-line/', include([ re_path(r'^so-extra-line/', include([
url(r'^(?P<pk>\d+)/$', SalesOrderExtraLineDetail.as_view(), name='api-so-extra-line-detail'), path('<int:pk>/', SalesOrderExtraLineDetail.as_view(), name='api-so-extra-line-detail'),
url(r'^$', SalesOrderExtraLineList.as_view(), name='api-so-extra-line-list'), path('', SalesOrderExtraLineList.as_view(), name='api-so-extra-line-list'),
])), ])),
# API endpoints for sales order allocations # API endpoints for sales order allocations
url(r'^so-allocation/', include([ re_path(r'^so-allocation/', include([
url(r'^(?P<pk>\d+)/$', SalesOrderAllocationDetail.as_view(), name='api-so-allocation-detail'), path('<int:pk>/', SalesOrderAllocationDetail.as_view(), name='api-so-allocation-detail'),
url(r'^.*$', SalesOrderAllocationList.as_view(), name='api-so-allocation-list'), re_path(r'^.*$', SalesOrderAllocationList.as_view(), name='api-so-allocation-list'),
])), ])),
] ]

View File

@ -6,7 +6,7 @@ Django Forms for interacting with Order objects
from __future__ import unicode_literals from __future__ import unicode_literals
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from InvenTree.forms import HelperForm from InvenTree.forms import HelperForm
from InvenTree.fields import InvenTreeMoneyField from InvenTree.fields import InvenTreeMoneyField

View File

@ -33,7 +33,7 @@ def calculate_shipped_quantity(apps, schema_editor):
part=item.part part=item.part
) )
q = sum([item.quantity for item in items]) q = sum(item.quantity for item in items)
item.shipped = q item.shipped = q

View File

@ -17,7 +17,7 @@ from django.core.validators import MinValueValidator
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from markdownx.models import MarkdownxField from markdownx.models import MarkdownxField
from mptt.models import TreeForeignKey from mptt.models import TreeForeignKey
@ -49,7 +49,7 @@ def get_next_po_number():
order = PurchaseOrder.objects.exclude(reference=None).last() order = PurchaseOrder.objects.exclude(reference=None).last()
attempts = set([order.reference]) attempts = {order.reference}
reference = order.reference reference = order.reference
@ -78,7 +78,7 @@ def get_next_so_number():
order = SalesOrder.objects.exclude(reference=None).last() order = SalesOrder.objects.exclude(reference=None).last()
attempts = set([order.reference]) attempts = {order.reference}
reference = order.reference reference = order.reference
@ -161,10 +161,10 @@ class Order(ReferenceIndexingMixin):
# gather name reference # gather name reference
price_ref = 'sale_price' if isinstance(self, SalesOrder) else 'purchase_price' price_ref = 'sale_price' if isinstance(self, SalesOrder) else 'purchase_price'
# order items # order items
total += sum([a.quantity * convert_money(getattr(a, price_ref), target_currency) for a in self.lines.all() if getattr(a, price_ref)]) total += sum(a.quantity * convert_money(getattr(a, price_ref), target_currency) for a in self.lines.all() if getattr(a, price_ref))
# extra lines # extra lines
total += sum([a.quantity * convert_money(a.price, target_currency) for a in self.extra_lines.all() if a.price]) total += sum(a.quantity * convert_money(a.price, target_currency) for a in self.extra_lines.all() if a.price)
# set decimal-places # set decimal-places
total.decimal_places = 4 total.decimal_places = 4

View File

@ -7,7 +7,7 @@ from __future__ import unicode_literals
from decimal import Decimal from decimal import Decimal
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
from django.db import models, transaction from django.db import models, transaction

View File

@ -5,50 +5,50 @@ URL lookup for the Order app. Provides URL endpoints for:
- Detail view of Purchase Orders - Detail view of Purchase Orders
""" """
from django.conf.urls import url, include from django.urls import include, re_path
from . import views from . import views
purchase_order_detail_urls = [ purchase_order_detail_urls = [
url(r'^cancel/', views.PurchaseOrderCancel.as_view(), name='po-cancel'), re_path(r'^cancel/', views.PurchaseOrderCancel.as_view(), name='po-cancel'),
url(r'^issue/', views.PurchaseOrderIssue.as_view(), name='po-issue'), re_path(r'^issue/', views.PurchaseOrderIssue.as_view(), name='po-issue'),
url(r'^complete/', views.PurchaseOrderComplete.as_view(), name='po-complete'), re_path(r'^complete/', views.PurchaseOrderComplete.as_view(), name='po-complete'),
url(r'^upload/', views.PurchaseOrderUpload.as_view(), name='po-upload'), re_path(r'^upload/', views.PurchaseOrderUpload.as_view(), name='po-upload'),
url(r'^export/', views.PurchaseOrderExport.as_view(), name='po-export'), re_path(r'^export/', views.PurchaseOrderExport.as_view(), name='po-export'),
url(r'^.*$', views.PurchaseOrderDetail.as_view(), name='po-detail'), re_path(r'^.*$', views.PurchaseOrderDetail.as_view(), name='po-detail'),
] ]
purchase_order_urls = [ purchase_order_urls = [
url(r'^order-parts/', views.OrderParts.as_view(), name='order-parts'), re_path(r'^order-parts/', views.OrderParts.as_view(), name='order-parts'),
url(r'^pricing/', views.LineItemPricing.as_view(), name='line-pricing'), re_path(r'^pricing/', views.LineItemPricing.as_view(), name='line-pricing'),
# Display detail view for a single purchase order # Display detail view for a single purchase order
url(r'^(?P<pk>\d+)/', include(purchase_order_detail_urls)), re_path(r'^(?P<pk>\d+)/', include(purchase_order_detail_urls)),
# Display complete list of purchase orders # Display complete list of purchase orders
url(r'^.*$', views.PurchaseOrderIndex.as_view(), name='po-index'), re_path(r'^.*$', views.PurchaseOrderIndex.as_view(), name='po-index'),
] ]
sales_order_detail_urls = [ sales_order_detail_urls = [
url(r'^cancel/', views.SalesOrderCancel.as_view(), name='so-cancel'), re_path(r'^cancel/', views.SalesOrderCancel.as_view(), name='so-cancel'),
url(r'^export/', views.SalesOrderExport.as_view(), name='so-export'), re_path(r'^export/', views.SalesOrderExport.as_view(), name='so-export'),
url(r'^.*$', views.SalesOrderDetail.as_view(), name='so-detail'), re_path(r'^.*$', views.SalesOrderDetail.as_view(), name='so-detail'),
] ]
sales_order_urls = [ sales_order_urls = [
# Display detail view for a single SalesOrder # Display detail view for a single SalesOrder
url(r'^(?P<pk>\d+)/', include(sales_order_detail_urls)), re_path(r'^(?P<pk>\d+)/', include(sales_order_detail_urls)),
# Display list of all sales orders # Display list of all sales orders
url(r'^.*$', views.SalesOrderIndex.as_view(), name='so-index'), re_path(r'^.*$', views.SalesOrderIndex.as_view(), name='so-index'),
] ]
order_urls = [ order_urls = [
url(r'^purchase-order/', include(purchase_order_urls)), re_path(r'^purchase-order/', include(purchase_order_urls)),
url(r'^sales-order/', include(sales_order_urls)), re_path(r'^sales-order/', include(sales_order_urls)),
] ]

View File

@ -11,7 +11,7 @@ from django.http.response import JsonResponse
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.urls import reverse from django.urls import reverse
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, ListView from django.views.generic import DetailView, ListView
from django.forms import HiddenInput, IntegerField from django.forms import HiddenInput, IntegerField

View File

@ -7,11 +7,11 @@ from __future__ import unicode_literals
import datetime import datetime
from django.conf.urls import url, include from django.urls import include, path, re_path
from django.http import JsonResponse from django.http import JsonResponse
from django.db.models import Q, F, Count, Min, Max, Avg from django.db.models import Q, F, Count, Min, Max, Avg
from django.db import transaction from django.db import transaction
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import status from rest_framework import status
from rest_framework.response import Response from rest_framework.response import Response
@ -1916,100 +1916,100 @@ class BomItemSubstituteDetail(generics.RetrieveUpdateDestroyAPIView):
part_api_urls = [ part_api_urls = [
# Base URL for PartCategory API endpoints # Base URL for PartCategory API endpoints
url(r'^category/', include([ re_path(r'^category/', include([
url(r'^tree/', CategoryTree.as_view(), name='api-part-category-tree'), re_path(r'^tree/', CategoryTree.as_view(), name='api-part-category-tree'),
url(r'^parameters/', CategoryParameterList.as_view(), name='api-part-category-parameter-list'), re_path(r'^parameters/', CategoryParameterList.as_view(), name='api-part-category-parameter-list'),
url(r'^(?P<pk>\d+)/?', CategoryDetail.as_view(), name='api-part-category-detail'), re_path(r'^(?P<pk>\d+)/?', CategoryDetail.as_view(), name='api-part-category-detail'),
url(r'^$', CategoryList.as_view(), name='api-part-category-list'), path('', CategoryList.as_view(), name='api-part-category-list'),
])), ])),
# Base URL for PartTestTemplate API endpoints # Base URL for PartTestTemplate API endpoints
url(r'^test-template/', include([ re_path(r'^test-template/', include([
url(r'^(?P<pk>\d+)/', PartTestTemplateDetail.as_view(), name='api-part-test-template-detail'), re_path(r'^(?P<pk>\d+)/', PartTestTemplateDetail.as_view(), name='api-part-test-template-detail'),
url(r'^$', PartTestTemplateList.as_view(), name='api-part-test-template-list'), path('', PartTestTemplateList.as_view(), name='api-part-test-template-list'),
])), ])),
# Base URL for PartAttachment API endpoints # Base URL for PartAttachment API endpoints
url(r'^attachment/', include([ re_path(r'^attachment/', include([
url(r'^(?P<pk>\d+)/', PartAttachmentDetail.as_view(), name='api-part-attachment-detail'), re_path(r'^(?P<pk>\d+)/', PartAttachmentDetail.as_view(), name='api-part-attachment-detail'),
url(r'^$', PartAttachmentList.as_view(), name='api-part-attachment-list'), path('', PartAttachmentList.as_view(), name='api-part-attachment-list'),
])), ])),
# Base URL for part sale pricing # Base URL for part sale pricing
url(r'^sale-price/', include([ re_path(r'^sale-price/', include([
url(r'^(?P<pk>\d+)/', PartSalePriceDetail.as_view(), name='api-part-sale-price-detail'), re_path(r'^(?P<pk>\d+)/', PartSalePriceDetail.as_view(), name='api-part-sale-price-detail'),
url(r'^.*$', PartSalePriceList.as_view(), name='api-part-sale-price-list'), re_path(r'^.*$', PartSalePriceList.as_view(), name='api-part-sale-price-list'),
])), ])),
# Base URL for part internal pricing # Base URL for part internal pricing
url(r'^internal-price/', include([ re_path(r'^internal-price/', include([
url(r'^(?P<pk>\d+)/', PartInternalPriceDetail.as_view(), name='api-part-internal-price-detail'), re_path(r'^(?P<pk>\d+)/', PartInternalPriceDetail.as_view(), name='api-part-internal-price-detail'),
url(r'^.*$', PartInternalPriceList.as_view(), name='api-part-internal-price-list'), re_path(r'^.*$', PartInternalPriceList.as_view(), name='api-part-internal-price-list'),
])), ])),
# Base URL for PartRelated API endpoints # Base URL for PartRelated API endpoints
url(r'^related/', include([ re_path(r'^related/', include([
url(r'^(?P<pk>\d+)/', PartRelatedDetail.as_view(), name='api-part-related-detail'), re_path(r'^(?P<pk>\d+)/', PartRelatedDetail.as_view(), name='api-part-related-detail'),
url(r'^.*$', PartRelatedList.as_view(), name='api-part-related-list'), re_path(r'^.*$', PartRelatedList.as_view(), name='api-part-related-list'),
])), ])),
# Base URL for PartParameter API endpoints # Base URL for PartParameter API endpoints
url(r'^parameter/', include([ re_path(r'^parameter/', include([
url(r'^template/$', PartParameterTemplateList.as_view(), name='api-part-parameter-template-list'), path('template/', PartParameterTemplateList.as_view(), name='api-part-parameter-template-list'),
url(r'^(?P<pk>\d+)/', PartParameterDetail.as_view(), name='api-part-parameter-detail'), re_path(r'^(?P<pk>\d+)/', PartParameterDetail.as_view(), name='api-part-parameter-detail'),
url(r'^.*$', PartParameterList.as_view(), name='api-part-parameter-list'), re_path(r'^.*$', PartParameterList.as_view(), name='api-part-parameter-list'),
])), ])),
url(r'^thumbs/', include([ re_path(r'^thumbs/', include([
url(r'^$', PartThumbs.as_view(), name='api-part-thumbs'), path('', PartThumbs.as_view(), name='api-part-thumbs'),
url(r'^(?P<pk>\d+)/?', PartThumbsUpdate.as_view(), name='api-part-thumbs-update'), re_path(r'^(?P<pk>\d+)/?', PartThumbsUpdate.as_view(), name='api-part-thumbs-update'),
])), ])),
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
# Endpoint for extra serial number information # Endpoint for extra serial number information
url(r'^serial-numbers/', PartSerialNumberDetail.as_view(), name='api-part-serial-number-detail'), re_path(r'^serial-numbers/', PartSerialNumberDetail.as_view(), name='api-part-serial-number-detail'),
# Endpoint for future scheduling information # Endpoint for future scheduling information
url(r'^scheduling/', PartScheduling.as_view(), name='api-part-scheduling'), re_path(r'^scheduling/', PartScheduling.as_view(), name='api-part-scheduling'),
# Endpoint for duplicating a BOM for the specific Part # Endpoint for duplicating a BOM for the specific Part
url(r'^bom-copy/', PartCopyBOM.as_view(), name='api-part-bom-copy'), re_path(r'^bom-copy/', PartCopyBOM.as_view(), name='api-part-bom-copy'),
# Endpoint for validating a BOM for the specific Part # Endpoint for validating a BOM for the specific Part
url(r'^bom-validate/', PartValidateBOM.as_view(), name='api-part-bom-validate'), re_path(r'^bom-validate/', PartValidateBOM.as_view(), name='api-part-bom-validate'),
# Part detail endpoint # Part detail endpoint
url(r'^.*$', PartDetail.as_view(), name='api-part-detail'), re_path(r'^.*$', PartDetail.as_view(), name='api-part-detail'),
])), ])),
url(r'^.*$', PartList.as_view(), name='api-part-list'), re_path(r'^.*$', PartList.as_view(), name='api-part-list'),
] ]
bom_api_urls = [ bom_api_urls = [
url(r'^substitute/', include([ re_path(r'^substitute/', include([
# Detail view # Detail view
url(r'^(?P<pk>\d+)/', BomItemSubstituteDetail.as_view(), name='api-bom-substitute-detail'), re_path(r'^(?P<pk>\d+)/', BomItemSubstituteDetail.as_view(), name='api-bom-substitute-detail'),
# Catch all # Catch all
url(r'^.*$', BomItemSubstituteList.as_view(), name='api-bom-substitute-list'), re_path(r'^.*$', BomItemSubstituteList.as_view(), name='api-bom-substitute-list'),
])), ])),
# BOM Item Detail # BOM Item Detail
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'^validate/?', BomItemValidate.as_view(), name='api-bom-item-validate'), re_path(r'^validate/?', BomItemValidate.as_view(), name='api-bom-item-validate'),
url(r'^.*$', BomDetail.as_view(), name='api-bom-item-detail'), re_path(r'^.*$', BomDetail.as_view(), name='api-bom-item-detail'),
])), ])),
# API endpoint URLs for importing BOM data # API endpoint URLs for importing BOM data
url(r'^import/upload/', BomImportUpload.as_view(), name='api-bom-import-upload'), re_path(r'^import/upload/', BomImportUpload.as_view(), name='api-bom-import-upload'),
url(r'^import/extract/', BomImportExtract.as_view(), name='api-bom-import-extract'), re_path(r'^import/extract/', BomImportExtract.as_view(), name='api-bom-import-extract'),
url(r'^import/submit/', BomImportSubmit.as_view(), name='api-bom-import-submit'), re_path(r'^import/submit/', BomImportSubmit.as_view(), name='api-bom-import-submit'),
# Catch-all # Catch-all
url(r'^.*$', BomList.as_view(), name='api-bom-list'), re_path(r'^.*$', BomList.as_view(), name='api-bom-list'),
] ]

View File

@ -6,7 +6,7 @@ Django Forms for interacting with Part objects
from __future__ import unicode_literals from __future__ import unicode_literals
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from mptt.fields import TreeNodeChoiceField from mptt.fields import TreeNodeChoiceField

View File

@ -2510,7 +2510,7 @@ def validate_template_name(name):
Prevent illegal characters in "name" field for PartParameterTemplate Prevent illegal characters in "name" field for PartParameterTemplate
""" """
for c in "!@#$%^&*()<>{}[].,?/\|~`_+-=\'\"": for c in "!@#$%^&*()<>{}[].,?/\\|~`_+-=\'\"":
if c in str(name): if c in str(name):
raise ValidationError(_(f"Illegal character in template name ({c})")) raise ValidationError(_(f"Illegal character in template name ({c})"))

View File

@ -11,7 +11,7 @@ from django.db.models import ExpressionWrapper, F, Q, Func
from django.db.models import Subquery, OuterRef, FloatField from django.db.models import Subquery, OuterRef, FloatField
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from sql_util.utils import SubqueryCount, SubquerySum from sql_util.utils import SubqueryCount, SubquerySum

View File

@ -3,7 +3,7 @@ from __future__ import unicode_literals
import logging import logging
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
import InvenTree.helpers import InvenTree.helpers
import InvenTree.tasks import InvenTree.tasks

View File

@ -12,7 +12,7 @@ import logging
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.conf import settings as djangosettings from django.conf import settings as djangosettings
from django import template from django import template

View File

@ -8,53 +8,53 @@ URL lookup for Part app. Provides URL endpoints for:
""" """
from django.conf.urls import url, include from django.urls import include, re_path
from . import views from . import views
part_parameter_urls = [ part_parameter_urls = [
url(r'^template/new/', views.PartParameterTemplateCreate.as_view(), name='part-param-template-create'), re_path(r'^template/new/', views.PartParameterTemplateCreate.as_view(), name='part-param-template-create'),
url(r'^template/(?P<pk>\d+)/edit/', views.PartParameterTemplateEdit.as_view(), name='part-param-template-edit'), re_path(r'^template/(?P<pk>\d+)/edit/', views.PartParameterTemplateEdit.as_view(), name='part-param-template-edit'),
url(r'^template/(?P<pk>\d+)/delete/', views.PartParameterTemplateDelete.as_view(), name='part-param-template-edit'), re_path(r'^template/(?P<pk>\d+)/delete/', views.PartParameterTemplateDelete.as_view(), name='part-param-template-edit'),
] ]
part_detail_urls = [ part_detail_urls = [
url(r'^delete/?', views.PartDelete.as_view(), name='part-delete'), re_path(r'^delete/?', views.PartDelete.as_view(), name='part-delete'),
url(r'^bom-download/?', views.BomDownload.as_view(), name='bom-download'), re_path(r'^bom-download/?', views.BomDownload.as_view(), name='bom-download'),
url(r'^pricing/', views.PartPricing.as_view(), name='part-pricing'), re_path(r'^pricing/', views.PartPricing.as_view(), name='part-pricing'),
url(r'^bom-upload/?', views.BomUpload.as_view(), name='upload-bom'), re_path(r'^bom-upload/?', views.BomUpload.as_view(), name='upload-bom'),
url(r'^qr_code/?', views.PartQRCode.as_view(), name='part-qr'), re_path(r'^qr_code/?', views.PartQRCode.as_view(), name='part-qr'),
# Normal thumbnail with form # Normal thumbnail with form
url(r'^thumb-select/?', views.PartImageSelect.as_view(), name='part-image-select'), re_path(r'^thumb-select/?', views.PartImageSelect.as_view(), name='part-image-select'),
url(r'^thumb-download/', views.PartImageDownloadFromURL.as_view(), name='part-image-download'), re_path(r'^thumb-download/', views.PartImageDownloadFromURL.as_view(), name='part-image-download'),
# Any other URLs go to the part detail page # Any other URLs go to the part detail page
url(r'^.*$', views.PartDetail.as_view(), name='part-detail'), re_path(r'^.*$', views.PartDetail.as_view(), name='part-detail'),
] ]
category_parameter_urls = [ category_parameter_urls = [
url(r'^new/', views.CategoryParameterTemplateCreate.as_view(), name='category-param-template-create'), re_path(r'^new/', views.CategoryParameterTemplateCreate.as_view(), name='category-param-template-create'),
url(r'^(?P<pid>\d+)/edit/', views.CategoryParameterTemplateEdit.as_view(), name='category-param-template-edit'), re_path(r'^(?P<pid>\d+)/edit/', views.CategoryParameterTemplateEdit.as_view(), name='category-param-template-edit'),
url(r'^(?P<pid>\d+)/delete/', views.CategoryParameterTemplateDelete.as_view(), name='category-param-template-delete'), re_path(r'^(?P<pid>\d+)/delete/', views.CategoryParameterTemplateDelete.as_view(), name='category-param-template-delete'),
] ]
category_urls = [ category_urls = [
# Top level subcategory display # Top level subcategory display
url(r'^subcategory/', views.PartIndex.as_view(template_name='part/subcategory.html'), name='category-index-subcategory'), re_path(r'^subcategory/', views.PartIndex.as_view(template_name='part/subcategory.html'), name='category-index-subcategory'),
# Category detail views # Category detail views
url(r'(?P<pk>\d+)/', include([ re_path(r'(?P<pk>\d+)/', include([
url(r'^delete/', views.CategoryDelete.as_view(), name='category-delete'), re_path(r'^delete/', views.CategoryDelete.as_view(), name='category-delete'),
url(r'^parameters/', include(category_parameter_urls)), re_path(r'^parameters/', include(category_parameter_urls)),
# Anything else # Anything else
url(r'^.*$', views.CategoryDetail.as_view(), name='category-detail'), re_path(r'^.*$', views.CategoryDetail.as_view(), name='category-detail'),
])) ]))
] ]
@ -62,27 +62,27 @@ category_urls = [
part_urls = [ part_urls = [
# Upload a part # Upload a part
url(r'^import/', views.PartImport.as_view(), name='part-import'), re_path(r'^import/', views.PartImport.as_view(), name='part-import'),
url(r'^import-api/', views.PartImportAjax.as_view(), name='api-part-import'), re_path(r'^import-api/', views.PartImportAjax.as_view(), name='api-part-import'),
# Download a BOM upload template # Download a BOM upload template
url(r'^bom_template/?', views.BomUploadTemplate.as_view(), name='bom-upload-template'), re_path(r'^bom_template/?', views.BomUploadTemplate.as_view(), name='bom-upload-template'),
# Individual part using pk # Individual part using pk
url(r'^(?P<pk>\d+)/', include(part_detail_urls)), re_path(r'^(?P<pk>\d+)/', include(part_detail_urls)),
# Part category # Part category
url(r'^category/', include(category_urls)), re_path(r'^category/', include(category_urls)),
# Part parameters # Part parameters
url(r'^parameter/', include(part_parameter_urls)), re_path(r'^parameter/', include(part_parameter_urls)),
# Change category for multiple parts # Change category for multiple parts
url(r'^set-category/?', views.PartSetCategory.as_view(), name='part-set-category'), re_path(r'^set-category/?', views.PartSetCategory.as_view(), name='part-set-category'),
# 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'), re_path(r'^(?P<slug>[-\w]+)/', views.PartDetailFromIPN.as_view(), name='part-detail-from-ipn'),
# Top level part list (display top level parts and categories) # Top level part list (display top level parts and categories)
url(r'^.*$', views.PartIndex.as_view(), name='part-index'), re_path(r'^.*$', views.PartIndex.as_view(), name='part-index'),
] ]

View File

@ -5,7 +5,7 @@ JSON API for the plugin app
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import url, include from django.urls import include, re_path
from rest_framework import generics from rest_framework import generics
from rest_framework import status from rest_framework import status
@ -118,18 +118,18 @@ class PluginSettingDetail(generics.RetrieveUpdateAPIView):
plugin_api_urls = [ plugin_api_urls = [
# Plugin settings URLs # Plugin settings URLs
url(r'^settings/', include([ re_path(r'^settings/', include([
url(r'^(?P<pk>\d+)/', PluginSettingDetail.as_view(), name='api-plugin-setting-detail'), re_path(r'^(?P<pk>\d+)/', PluginSettingDetail.as_view(), name='api-plugin-setting-detail'),
url(r'^.*$', PluginSettingList.as_view(), name='api-plugin-setting-list'), re_path(r'^.*$', PluginSettingList.as_view(), name='api-plugin-setting-list'),
])), ])),
# Detail views for a single PluginConfig item # Detail views for a single PluginConfig item
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'^.*$', PluginDetail.as_view(), name='api-plugin-detail'), re_path(r'^.*$', PluginDetail.as_view(), name='api-plugin-detail'),
])), ])),
url(r'^install/', PluginInstall.as_view(), name='api-plugin-install'), re_path(r'^install/', PluginInstall.as_view(), name='api-plugin-install'),
# Anything else # Anything else
url(r'^.*$', PluginList.as_view(), name='api-plugin-list'), re_path(r'^.*$', PluginList.as_view(), name='api-plugin-list'),
] ]

View File

@ -5,7 +5,7 @@ import logging
from django.apps import AppConfig from django.apps import AppConfig
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from maintenance_mode.core import set_maintenance_mode from maintenance_mode.core import set_maintenance_mode

View File

@ -6,7 +6,7 @@ import logging
import json import json
import requests import requests
from django.conf.urls import url, include from django.urls import include, re_path
from django.db.utils import OperationalError, ProgrammingError from django.db.utils import OperationalError, ProgrammingError
from plugin.models import PluginConfig, PluginSetting from plugin.models import PluginConfig, PluginSetting
@ -303,7 +303,7 @@ class UrlsMixin:
Urlpatterns for this plugin Urlpatterns for this plugin
""" """
if self.has_urls: if self.has_urls:
return url(f'^{self.slug}/', include((self.urls, self.slug)), name=self.slug) return re_path(f'^{self.slug}/', include((self.urls, self.slug)), name=self.slug)
return None return None
@property @property

View File

@ -7,7 +7,7 @@ from __future__ import unicode_literals
import logging import logging
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.conf import settings from django.conf import settings
from django.db import transaction from django.db import transaction

View File

@ -11,7 +11,7 @@ import pathlib
from django.urls.base import reverse from django.urls.base import reverse
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
import plugin.plugin as plugin_base import plugin.plugin as plugin_base
from plugin.helpers import get_git_log, GitStatus from plugin.helpers import get_git_log, GitStatus

View File

@ -17,7 +17,7 @@ from importlib import reload
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
from django.db.utils import OperationalError, ProgrammingError, IntegrityError from django.db.utils import OperationalError, ProgrammingError, IntegrityError
from django.conf.urls import url, include from django.urls import include, re_path
from django.urls import clear_url_caches from django.urls import clear_url_caches
from django.contrib import admin from django.contrib import admin
from django.utils.text import slugify from django.utils.text import slugify
@ -570,12 +570,12 @@ class PluginsRegistry:
for index, a in enumerate(urlpatterns): for index, a in enumerate(urlpatterns):
if hasattr(a, 'app_name'): if hasattr(a, 'app_name'):
if a.app_name == 'admin': if a.app_name == 'admin':
urlpatterns[index] = url(r'^admin/', admin.site.urls, name='inventree-admin') urlpatterns[index] = re_path(r'^admin/', admin.site.urls, name='inventree-admin')
elif a.app_name == 'plugin': elif a.app_name == 'plugin':
urlpatterns[index] = get_plugin_urls() urlpatterns[index] = get_plugin_urls()
# replace frontendpatterns # replace frontendpatterns
global_pattern[0] = url('', include(urlpatterns)) global_pattern[0] = re_path('', include(urlpatterns))
clear_url_caches() clear_url_caches()
def _reload_apps(self, force_reload: bool = False): def _reload_apps(self, force_reload: bool = False):

View File

@ -6,8 +6,8 @@ from plugin import IntegrationPluginBase
from plugin.mixins import AppMixin, SettingsMixin, UrlsMixin, NavigationMixin from plugin.mixins import AppMixin, SettingsMixin, UrlsMixin, NavigationMixin
from django.http import HttpResponse from django.http import HttpResponse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.conf.urls import url, include from django.urls import include, re_path
class SampleIntegrationPlugin(AppMixin, SettingsMixin, UrlsMixin, NavigationMixin, IntegrationPluginBase): class SampleIntegrationPlugin(AppMixin, SettingsMixin, UrlsMixin, NavigationMixin, IntegrationPluginBase):
@ -28,13 +28,13 @@ class SampleIntegrationPlugin(AppMixin, SettingsMixin, UrlsMixin, NavigationMixi
def setup_urls(self): def setup_urls(self):
he_urls = [ he_urls = [
url(r'^he/', self.view_test, name='he'), re_path(r'^he/', self.view_test, name='he'),
url(r'^ha/', self.view_test, name='ha'), re_path(r'^ha/', self.view_test, name='ha'),
] ]
return [ return [
url(r'^hi/', self.view_test, name='hi'), re_path(r'^hi/', self.view_test, name='hi'),
url(r'^ho/', include(he_urls), name='ho'), re_path(r'^ho/', include(he_urls), name='ho'),
] ]
SETTINGS = { SETTINGS = {

View File

@ -10,7 +10,7 @@ import subprocess
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.utils import timezone from django.utils import timezone
from rest_framework import serializers from rest_framework import serializers

View File

@ -2,7 +2,7 @@
from django.test import TestCase from django.test import TestCase
from django.conf import settings from django.conf import settings
from django.conf.urls import url, include from django.urls import include, re_path
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from datetime import datetime from datetime import datetime
@ -66,7 +66,7 @@ class UrlsMixinTest(BaseMixinDefinition, TestCase):
class UrlsCls(UrlsMixin, IntegrationPluginBase): class UrlsCls(UrlsMixin, IntegrationPluginBase):
def test(): def test():
return 'ccc' return 'ccc'
URLS = [url('testpath', test, name='test'), ] URLS = [re_path('testpath', test, name='test'), ]
self.mixin = UrlsCls() self.mixin = UrlsCls()
class NoUrlsCls(UrlsMixin, IntegrationPluginBase): class NoUrlsCls(UrlsMixin, IntegrationPluginBase):
@ -81,7 +81,7 @@ class UrlsMixinTest(BaseMixinDefinition, TestCase):
self.assertEqual(self.mixin.base_url, target_url) self.assertEqual(self.mixin.base_url, target_url)
# urlpattern # urlpattern
target_pattern = url(f'^{plg_name}/', include((self.mixin.urls, plg_name)), name=plg_name) target_pattern = re_path(f'^{plg_name}/', include((self.mixin.urls, plg_name)), name=plg_name)
self.assertEqual(self.mixin.urlpatterns.reverse_dict, target_pattern.reverse_dict) self.assertEqual(self.mixin.urlpatterns.reverse_dict, target_pattern.reverse_dict)
# resolve the view # resolve the view

View File

@ -2,7 +2,7 @@
URL lookup for plugin app URL lookup for plugin app
""" """
from django.conf.urls import url, include from django.urls import include, re_path
from plugin import registry from plugin import registry
@ -21,4 +21,4 @@ def get_plugin_urls():
if plugin.mixin_enabled('urls'): if plugin.mixin_enabled('urls'):
urls.append(plugin.urlpatterns) urls.append(plugin.urlpatterns)
return url(f'^{PLUGIN_BASE}/', include((urls, 'plugin'))) return re_path(f'^{PLUGIN_BASE}/', include((urls, 'plugin')))

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.conf.urls import url, include from django.urls import include, path, re_path
from django.core.exceptions import ValidationError, FieldError from django.core.exceptions import ValidationError, FieldError
from django.http import HttpResponse from django.http import HttpResponse
@ -730,62 +730,62 @@ class SalesOrderReportPrint(generics.RetrieveAPIView, OrderReportMixin, ReportPr
report_api_urls = [ report_api_urls = [
# Purchase order reports # Purchase order reports
url(r'po/', include([ re_path(r'po/', include([
# Detail views # Detail views
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'print/', PurchaseOrderReportPrint.as_view(), name='api-po-report-print'), re_path(r'print/', PurchaseOrderReportPrint.as_view(), name='api-po-report-print'),
url(r'^$', PurchaseOrderReportDetail.as_view(), name='api-po-report-detail'), path('', PurchaseOrderReportDetail.as_view(), name='api-po-report-detail'),
])), ])),
# List view # List view
url(r'^$', PurchaseOrderReportList.as_view(), name='api-po-report-list'), path('', PurchaseOrderReportList.as_view(), name='api-po-report-list'),
])), ])),
# Sales order reports # Sales order reports
url(r'so/', include([ re_path(r'so/', include([
# Detail views # Detail views
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'print/', SalesOrderReportPrint.as_view(), name='api-so-report-print'), re_path(r'print/', SalesOrderReportPrint.as_view(), name='api-so-report-print'),
url(r'^$', SalesOrderReportDetail.as_view(), name='api-so-report-detail'), path('', SalesOrderReportDetail.as_view(), name='api-so-report-detail'),
])), ])),
url(r'^$', SalesOrderReportList.as_view(), name='api-so-report-list'), path('', SalesOrderReportList.as_view(), name='api-so-report-list'),
])), ])),
# Build reports # Build reports
url(r'build/', include([ re_path(r'build/', include([
# Detail views # Detail views
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'print/?', BuildReportPrint.as_view(), name='api-build-report-print'), re_path(r'print/?', BuildReportPrint.as_view(), name='api-build-report-print'),
url(r'^.$', BuildReportDetail.as_view(), name='api-build-report-detail'), re_path(r'^.$', BuildReportDetail.as_view(), name='api-build-report-detail'),
])), ])),
# List view # List view
url(r'^.*$', BuildReportList.as_view(), name='api-build-report-list'), re_path(r'^.*$', BuildReportList.as_view(), name='api-build-report-list'),
])), ])),
# Bill of Material reports # Bill of Material reports
url(r'bom/', include([ re_path(r'bom/', include([
# Detail views # Detail views
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'print/?', BOMReportPrint.as_view(), name='api-bom-report-print'), re_path(r'print/?', BOMReportPrint.as_view(), name='api-bom-report-print'),
url(r'^.*$', BOMReportDetail.as_view(), name='api-bom-report-detail'), re_path(r'^.*$', BOMReportDetail.as_view(), name='api-bom-report-detail'),
])), ])),
# List view # List view
url(r'^.*$', BOMReportList.as_view(), name='api-bom-report-list'), re_path(r'^.*$', BOMReportList.as_view(), name='api-bom-report-list'),
])), ])),
# Stock item test reports # Stock item test reports
url(r'test/', include([ re_path(r'test/', include([
# Detail views # Detail views
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'print/?', StockItemTestReportPrint.as_view(), name='api-stockitem-testreport-print'), re_path(r'print/?', StockItemTestReportPrint.as_view(), name='api-stockitem-testreport-print'),
url(r'^.*$', StockItemTestReportDetail.as_view(), name='api-stockitem-testreport-detail'), re_path(r'^.*$', StockItemTestReportDetail.as_view(), name='api-stockitem-testreport-detail'),
])), ])),
# List view # List view
url(r'^.*$', StockItemTestReportList.as_view(), name='api-stockitem-testreport-list'), re_path(r'^.*$', StockItemTestReportList.as_view(), name='api-stockitem-testreport-list'),
])), ])),
] ]

View File

@ -9,11 +9,11 @@ from collections import OrderedDict
from datetime import datetime, timedelta from datetime import datetime, timedelta
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
from django.conf.urls import url, include from django.urls import include, path, re_path
from django.http import JsonResponse from django.http import JsonResponse
from django.db.models import Q, F from django.db.models import Q, F
from django.db import transaction from django.db import transaction
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from django_filters import rest_framework as rest_filters from django_filters import rest_framework as rest_filters
@ -1383,47 +1383,47 @@ class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
stock_api_urls = [ stock_api_urls = [
url(r'^location/', include([ re_path(r'^location/', include([
url(r'^tree/', StockLocationTree.as_view(), name='api-location-tree'), re_path(r'^tree/', StockLocationTree.as_view(), name='api-location-tree'),
url(r'^(?P<pk>\d+)/', LocationDetail.as_view(), name='api-location-detail'), re_path(r'^(?P<pk>\d+)/', LocationDetail.as_view(), name='api-location-detail'),
url(r'^.*$', StockLocationList.as_view(), name='api-location-list'), re_path(r'^.*$', StockLocationList.as_view(), name='api-location-list'),
])), ])),
# Endpoints for bulk stock adjustment actions # Endpoints for bulk stock adjustment actions
url(r'^count/', StockCount.as_view(), name='api-stock-count'), re_path(r'^count/', StockCount.as_view(), name='api-stock-count'),
url(r'^add/', StockAdd.as_view(), name='api-stock-add'), re_path(r'^add/', StockAdd.as_view(), name='api-stock-add'),
url(r'^remove/', StockRemove.as_view(), name='api-stock-remove'), re_path(r'^remove/', StockRemove.as_view(), name='api-stock-remove'),
url(r'^transfer/', StockTransfer.as_view(), name='api-stock-transfer'), re_path(r'^transfer/', StockTransfer.as_view(), name='api-stock-transfer'),
url(r'^assign/', StockAssign.as_view(), name='api-stock-assign'), re_path(r'^assign/', StockAssign.as_view(), name='api-stock-assign'),
url(r'^merge/', StockMerge.as_view(), name='api-stock-merge'), re_path(r'^merge/', StockMerge.as_view(), name='api-stock-merge'),
# StockItemAttachment API endpoints # StockItemAttachment API endpoints
url(r'^attachment/', include([ re_path(r'^attachment/', include([
url(r'^(?P<pk>\d+)/', StockAttachmentDetail.as_view(), name='api-stock-attachment-detail'), re_path(r'^(?P<pk>\d+)/', StockAttachmentDetail.as_view(), name='api-stock-attachment-detail'),
url(r'^$', StockAttachmentList.as_view(), name='api-stock-attachment-list'), path('', StockAttachmentList.as_view(), name='api-stock-attachment-list'),
])), ])),
# StockItemTestResult API endpoints # StockItemTestResult API endpoints
url(r'^test/', include([ re_path(r'^test/', include([
url(r'^(?P<pk>\d+)/', StockItemTestResultDetail.as_view(), name='api-stock-test-result-detail'), re_path(r'^(?P<pk>\d+)/', StockItemTestResultDetail.as_view(), name='api-stock-test-result-detail'),
url(r'^.*$', StockItemTestResultList.as_view(), name='api-stock-test-result-list'), re_path(r'^.*$', StockItemTestResultList.as_view(), name='api-stock-test-result-list'),
])), ])),
# StockItemTracking API endpoints # StockItemTracking API endpoints
url(r'^track/', include([ re_path(r'^track/', include([
url(r'^(?P<pk>\d+)/', StockTrackingDetail.as_view(), name='api-stock-tracking-detail'), re_path(r'^(?P<pk>\d+)/', StockTrackingDetail.as_view(), name='api-stock-tracking-detail'),
url(r'^.*$', StockTrackingList.as_view(), name='api-stock-tracking-list'), re_path(r'^.*$', StockTrackingList.as_view(), name='api-stock-tracking-list'),
])), ])),
# Detail views for a single stock item # Detail views for a single stock item
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'^serialize/', StockItemSerialize.as_view(), name='api-stock-item-serialize'), re_path(r'^serialize/', StockItemSerialize.as_view(), name='api-stock-item-serialize'),
url(r'^install/', StockItemInstall.as_view(), name='api-stock-item-install'), re_path(r'^install/', StockItemInstall.as_view(), name='api-stock-item-install'),
url(r'^.*$', StockDetail.as_view(), name='api-stock-detail'), re_path(r'^.*$', StockDetail.as_view(), name='api-stock-detail'),
])), ])),
# Anything else # Anything else
url(r'^.*$', StockList.as_view(), name='api-stock-list'), re_path(r'^.*$', StockList.as_view(), name='api-stock-list'),
] ]

View File

@ -7,7 +7,7 @@ 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 gettext_lazy as _
from mptt.fields import TreeNodeChoiceField from mptt.fields import TreeNodeChoiceField

View File

@ -78,7 +78,7 @@ def update_history(apps, schema_editor):
tracking_type = StockHistoryCode.STOCK_REMOVE tracking_type = StockHistoryCode.STOCK_REMOVE
# Extract the number of removed items # Extract the number of removed items
result = re.search("^removed ([\d\.]+) items", title) result = re.search(r"^removed ([\d\.]+) items", title)
if result: if result:
@ -102,7 +102,7 @@ def update_history(apps, schema_editor):
elif 'moved to' in title: elif 'moved to' in title:
tracking_type = StockHistoryCode.STOCK_MOVE tracking_type = StockHistoryCode.STOCK_MOVE
result = re.search('^Moved to (.*)( - )*(.*) \(from.*$', entry.title) result = re.search(r'^Moved to (.*)( - )*(.*) \(from.*$', entry.title)
if result: if result:
# Legacy tracking entries recorded the location in multiple ways, because.. why not? # Legacy tracking entries recorded the location in multiple ways, because.. why not?
@ -157,7 +157,7 @@ def update_history(apps, schema_editor):
tracking_type = StockHistoryCode.STOCK_ADD tracking_type = StockHistoryCode.STOCK_ADD
# Extract the number of added items # Extract the number of added items
result = re.search("^added ([\d\.]+) items", title) result = re.search(r"^added ([\d\.]+) items", title)
if result: if result:

View File

@ -10,7 +10,7 @@ from datetime import datetime, timedelta
from django.db import transaction from django.db import transaction
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.db.models import Case, When, Value from django.db.models import Case, When, Value
from django.db.models import BooleanField from django.db.models import BooleanField

View File

@ -2,55 +2,55 @@
URL lookup for Stock app URL lookup for Stock app
""" """
from django.conf.urls import url, include from django.urls import include, re_path
from stock import views from stock import views
location_urls = [ location_urls = [
url(r'^(?P<pk>\d+)/', include([ re_path(r'^(?P<pk>\d+)/', include([
url(r'^delete/?', views.StockLocationDelete.as_view(), name='stock-location-delete'), re_path(r'^delete/?', views.StockLocationDelete.as_view(), name='stock-location-delete'),
url(r'^qr_code/?', views.StockLocationQRCode.as_view(), name='stock-location-qr'), re_path(r'^qr_code/?', views.StockLocationQRCode.as_view(), name='stock-location-qr'),
# Anything else # Anything else
url('^.*$', views.StockLocationDetail.as_view(), name='stock-location-detail'), re_path('^.*$', views.StockLocationDetail.as_view(), name='stock-location-detail'),
])), ])),
] ]
stock_item_detail_urls = [ stock_item_detail_urls = [
url(r'^convert/', views.StockItemConvert.as_view(), name='stock-item-convert'), re_path(r'^convert/', views.StockItemConvert.as_view(), name='stock-item-convert'),
url(r'^delete/', views.StockItemDelete.as_view(), name='stock-item-delete'), re_path(r'^delete/', views.StockItemDelete.as_view(), name='stock-item-delete'),
url(r'^qr_code/', views.StockItemQRCode.as_view(), name='stock-item-qr'), re_path(r'^qr_code/', views.StockItemQRCode.as_view(), name='stock-item-qr'),
url(r'^delete_test_data/', views.StockItemDeleteTestData.as_view(), name='stock-item-delete-test-data'), re_path(r'^delete_test_data/', views.StockItemDeleteTestData.as_view(), name='stock-item-delete-test-data'),
url(r'^return/', views.StockItemReturnToStock.as_view(), name='stock-item-return'), re_path(r'^return/', views.StockItemReturnToStock.as_view(), name='stock-item-return'),
url(r'^add_tracking/', views.StockItemTrackingCreate.as_view(), name='stock-tracking-create'), re_path(r'^add_tracking/', views.StockItemTrackingCreate.as_view(), name='stock-tracking-create'),
url('^.*$', views.StockItemDetail.as_view(), name='stock-item-detail'), re_path('^.*$', views.StockItemDetail.as_view(), name='stock-item-detail'),
] ]
stock_tracking_urls = [ stock_tracking_urls = [
# edit # edit
url(r'^(?P<pk>\d+)/edit/', views.StockItemTrackingEdit.as_view(), name='stock-tracking-edit'), re_path(r'^(?P<pk>\d+)/edit/', views.StockItemTrackingEdit.as_view(), name='stock-tracking-edit'),
# delete # delete
url(r'^(?P<pk>\d+)/delete', views.StockItemTrackingDelete.as_view(), name='stock-tracking-delete'), re_path(r'^(?P<pk>\d+)/delete', views.StockItemTrackingDelete.as_view(), name='stock-tracking-delete'),
] ]
stock_urls = [ stock_urls = [
# Stock location # Stock location
url(r'^location/', include(location_urls)), re_path(r'^location/', include(location_urls)),
url(r'^item/uninstall/', views.StockItemUninstall.as_view(), name='stock-item-uninstall'), re_path(r'^item/uninstall/', views.StockItemUninstall.as_view(), name='stock-item-uninstall'),
url(r'^track/', include(stock_tracking_urls)), re_path(r'^track/', include(stock_tracking_urls)),
# Individual stock items # Individual stock items
url(r'^item/(?P<pk>\d+)/', include(stock_item_detail_urls)), re_path(r'^item/(?P<pk>\d+)/', include(stock_item_detail_urls)),
url(r'^sublocations/', views.StockIndex.as_view(template_name='stock/sublocation.html'), name='stock-sublocations'), re_path(r'^sublocations/', views.StockIndex.as_view(template_name='stock/sublocation.html'), name='stock-sublocations'),
url(r'^.*$', views.StockIndex.as_view(), name='stock-index'), re_path(r'^.*$', views.StockIndex.as_view(), name='stock-index'),
] ]

View File

@ -15,7 +15,7 @@ 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
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from moneyed import CURRENCIES from moneyed import CURRENCIES

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.contrib import admin, messages from django.contrib import admin, messages
from django import forms from django import forms

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.conf.urls import url, include from django.urls import include, path, re_path
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
@ -174,14 +174,14 @@ class GetAuthToken(APIView):
user_urls = [ user_urls = [
url(r'roles/?$', RoleDetails.as_view(), name='api-user-roles'), re_path(r'roles/?$', RoleDetails.as_view(), name='api-user-roles'),
url(r'token/?$', GetAuthToken.as_view(), name='api-token'), re_path(r'token/?$', GetAuthToken.as_view(), name='api-token'),
url(r'^owner/', include([ re_path(r'^owner/', include([
url(r'^(?P<pk>[0-9]+)/$', OwnerDetail.as_view(), name='api-owner-detail'), path('<int:pk>/', OwnerDetail.as_view(), name='api-owner-detail'),
url(r'^.*$', OwnerList.as_view(), name='api-owner-list'), re_path(r'^.*$', OwnerList.as_view(), name='api-owner-list'),
])), ])),
url(r'^(?P<pk>[0-9]+)/?$', UserDetail.as_view(), name='user-detail'), re_path(r'^(?P<pk>[0-9]+)/?$', UserDetail.as_view(), name='user-detail'),
url(r'^$', UserList.as_view()), path('', UserList.as_view()),
] ]

View File

@ -66,7 +66,7 @@ if __name__ == '__main__':
print("Checking development branch") print("Checking development branch")
pattern = "^\d+(\.\d+)+ dev$" pattern = r"^\d+(\.\d+)+ dev$"
result = re.match(pattern, version) result = re.match(pattern, version)
@ -82,7 +82,7 @@ if __name__ == '__main__':
print("Checking release branch") print("Checking release branch")
pattern = "^\d+(\.\d+)+$" pattern = r"^\d+(\.\d+)+$"
result = re.match(pattern, version) result = re.match(pattern, version)