mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 21:15:41 +00:00
Merge remote-tracking branch 'inventree/master' into order-parts-wizard
# Conflicts: # InvenTree/order/serializers.py # InvenTree/templates/js/translated/model_renderers.js
This commit is contained in:
@ -7,11 +7,11 @@ from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.urls import include, path, re_path
|
||||
from django.http import JsonResponse
|
||||
from django.db.models import Q, F, Count, Min, Max, Avg
|
||||
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.response import Response
|
||||
@ -383,7 +383,7 @@ class PartTestTemplateList(generics.ListCreateAPIView):
|
||||
required = params.get('required', None)
|
||||
|
||||
if required is not None:
|
||||
queryset = queryset.filter(required=required)
|
||||
queryset = queryset.filter(required=str2bool(required))
|
||||
|
||||
return queryset
|
||||
|
||||
@ -1916,100 +1916,100 @@ class BomItemSubstituteDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
part_api_urls = [
|
||||
|
||||
# Base URL for PartCategory API endpoints
|
||||
url(r'^category/', include([
|
||||
url(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'^category/', include([
|
||||
re_path(r'^tree/', CategoryTree.as_view(), name='api-part-category-tree'),
|
||||
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'),
|
||||
url(r'^$', CategoryList.as_view(), name='api-part-category-list'),
|
||||
re_path(r'^(?P<pk>\d+)/?', CategoryDetail.as_view(), name='api-part-category-detail'),
|
||||
path('', CategoryList.as_view(), name='api-part-category-list'),
|
||||
])),
|
||||
|
||||
# Base URL for PartTestTemplate API endpoints
|
||||
url(r'^test-template/', include([
|
||||
url(r'^(?P<pk>\d+)/', PartTestTemplateDetail.as_view(), name='api-part-test-template-detail'),
|
||||
url(r'^$', PartTestTemplateList.as_view(), name='api-part-test-template-list'),
|
||||
re_path(r'^test-template/', include([
|
||||
re_path(r'^(?P<pk>\d+)/', PartTestTemplateDetail.as_view(), name='api-part-test-template-detail'),
|
||||
path('', PartTestTemplateList.as_view(), name='api-part-test-template-list'),
|
||||
])),
|
||||
|
||||
# Base URL for PartAttachment API endpoints
|
||||
url(r'^attachment/', include([
|
||||
url(r'^(?P<pk>\d+)/', PartAttachmentDetail.as_view(), name='api-part-attachment-detail'),
|
||||
url(r'^$', PartAttachmentList.as_view(), name='api-part-attachment-list'),
|
||||
re_path(r'^attachment/', include([
|
||||
re_path(r'^(?P<pk>\d+)/', PartAttachmentDetail.as_view(), name='api-part-attachment-detail'),
|
||||
path('', PartAttachmentList.as_view(), name='api-part-attachment-list'),
|
||||
])),
|
||||
|
||||
# Base URL for part sale pricing
|
||||
url(r'^sale-price/', include([
|
||||
url(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'^sale-price/', include([
|
||||
re_path(r'^(?P<pk>\d+)/', PartSalePriceDetail.as_view(), name='api-part-sale-price-detail'),
|
||||
re_path(r'^.*$', PartSalePriceList.as_view(), name='api-part-sale-price-list'),
|
||||
])),
|
||||
|
||||
# Base URL for part internal pricing
|
||||
url(r'^internal-price/', include([
|
||||
url(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'^internal-price/', include([
|
||||
re_path(r'^(?P<pk>\d+)/', PartInternalPriceDetail.as_view(), name='api-part-internal-price-detail'),
|
||||
re_path(r'^.*$', PartInternalPriceList.as_view(), name='api-part-internal-price-list'),
|
||||
])),
|
||||
|
||||
# Base URL for PartRelated API endpoints
|
||||
url(r'^related/', include([
|
||||
url(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'^related/', include([
|
||||
re_path(r'^(?P<pk>\d+)/', PartRelatedDetail.as_view(), name='api-part-related-detail'),
|
||||
re_path(r'^.*$', PartRelatedList.as_view(), name='api-part-related-list'),
|
||||
])),
|
||||
|
||||
# Base URL for PartParameter API endpoints
|
||||
url(r'^parameter/', include([
|
||||
url(r'^template/$', PartParameterTemplateList.as_view(), name='api-part-parameter-template-list'),
|
||||
re_path(r'^parameter/', include([
|
||||
path('template/', PartParameterTemplateList.as_view(), name='api-part-parameter-template-list'),
|
||||
|
||||
url(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'^(?P<pk>\d+)/', PartParameterDetail.as_view(), name='api-part-parameter-detail'),
|
||||
re_path(r'^.*$', PartParameterList.as_view(), name='api-part-parameter-list'),
|
||||
])),
|
||||
|
||||
url(r'^thumbs/', include([
|
||||
url(r'^$', PartThumbs.as_view(), name='api-part-thumbs'),
|
||||
url(r'^(?P<pk>\d+)/?', PartThumbsUpdate.as_view(), name='api-part-thumbs-update'),
|
||||
re_path(r'^thumbs/', include([
|
||||
path('', PartThumbs.as_view(), name='api-part-thumbs'),
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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 = [
|
||||
|
||||
url(r'^substitute/', include([
|
||||
re_path(r'^substitute/', include([
|
||||
|
||||
# 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
|
||||
url(r'^.*$', BomItemSubstituteList.as_view(), name='api-bom-substitute-list'),
|
||||
re_path(r'^.*$', BomItemSubstituteList.as_view(), name='api-bom-substitute-list'),
|
||||
])),
|
||||
|
||||
# BOM Item Detail
|
||||
url(r'^(?P<pk>\d+)/', include([
|
||||
url(r'^validate/?', BomItemValidate.as_view(), name='api-bom-item-validate'),
|
||||
url(r'^.*$', BomDetail.as_view(), name='api-bom-item-detail'),
|
||||
re_path(r'^(?P<pk>\d+)/', include([
|
||||
re_path(r'^validate/?', BomItemValidate.as_view(), name='api-bom-item-validate'),
|
||||
re_path(r'^.*$', BomDetail.as_view(), name='api-bom-item-detail'),
|
||||
])),
|
||||
|
||||
# API endpoint URLs for importing BOM data
|
||||
url(r'^import/upload/', BomImportUpload.as_view(), name='api-bom-import-upload'),
|
||||
url(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/upload/', BomImportUpload.as_view(), name='api-bom-import-upload'),
|
||||
re_path(r'^import/extract/', BomImportExtract.as_view(), name='api-bom-import-extract'),
|
||||
re_path(r'^import/submit/', BomImportSubmit.as_view(), name='api-bom-import-submit'),
|
||||
|
||||
# Catch-all
|
||||
url(r'^.*$', BomList.as_view(), name='api-bom-list'),
|
||||
re_path(r'^.*$', BomList.as_view(), name='api-bom-list'),
|
||||
]
|
||||
|
@ -6,7 +6,7 @@ Django Forms for interacting with Part objects
|
||||
from __future__ import unicode_literals
|
||||
|
||||
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
|
||||
|
||||
|
@ -2510,7 +2510,7 @@ def validate_template_name(name):
|
||||
Prevent illegal characters in "name" field for PartParameterTemplate
|
||||
"""
|
||||
|
||||
for c in "!@#$%^&*()<>{}[].,?/\|~`_+-=\'\"":
|
||||
for c in "!@#$%^&*()<>{}[].,?/\\|~`_+-=\'\"":
|
||||
if c in str(name):
|
||||
raise ValidationError(_(f"Illegal character in template name ({c})"))
|
||||
|
||||
@ -2740,8 +2740,8 @@ class BomItem(models.Model, DataImportMixin):
|
||||
if not p.active:
|
||||
continue
|
||||
|
||||
# Trackable parts cannot be 'auto allocated'
|
||||
if p.trackable:
|
||||
# Trackable status must be the same as the sub_part
|
||||
if p.trackable != self.sub_part.trackable:
|
||||
continue
|
||||
|
||||
valid_parts.append(p)
|
||||
|
@ -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.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 sql_util.utils import SubqueryCount, SubquerySum
|
||||
|
@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import InvenTree.helpers
|
||||
import InvenTree.tasks
|
||||
|
@ -12,13 +12,14 @@ import logging
|
||||
|
||||
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 import template
|
||||
from django.urls import reverse
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.templatetags.static import StaticNode
|
||||
from django.templatetags.static import StaticNode, static
|
||||
from django.core.files.storage import default_storage
|
||||
|
||||
from InvenTree import version, settings
|
||||
|
||||
@ -160,10 +161,11 @@ def inventree_in_debug_mode(*args, **kwargs):
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def inventree_demo_mode(*args, **kwargs):
|
||||
""" Return True if the server is running in DEMO mode """
|
||||
|
||||
return djangosettings.DEMO_MODE
|
||||
def inventree_show_about(user, *args, **kwargs):
|
||||
""" Return True if the about modal should be shown """
|
||||
if InvenTreeSetting.get_setting('INVENTREE_RESTRICT_ABOUT') and not user.is_superuser:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
@ -220,8 +222,13 @@ def python_version(*args, **kwargs):
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def inventree_version(*args, **kwargs):
|
||||
def inventree_version(shortstring=False, *args, **kwargs):
|
||||
""" Return InvenTree version string """
|
||||
if shortstring:
|
||||
return _("{title} v{version}".format(
|
||||
title=version.inventreeInstanceTitle(),
|
||||
version=version.inventreeVersion()
|
||||
))
|
||||
return version.inventreeVersion()
|
||||
|
||||
|
||||
@ -512,6 +519,22 @@ def mail_configured():
|
||||
return bool(settings.EMAIL_HOST)
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def inventree_customize(reference, *args, **kwargs):
|
||||
""" Return customization values for the user interface """
|
||||
|
||||
return djangosettings.CUSTOMIZE.get(reference, '')
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def inventree_logo(*args, **kwargs):
|
||||
""" Return the path to the logo-file """
|
||||
|
||||
if settings.CUSTOM_LOGO:
|
||||
return default_storage.url(settings.CUSTOM_LOGO)
|
||||
return static('img/inventree.png')
|
||||
|
||||
|
||||
class I18nStaticNode(StaticNode):
|
||||
"""
|
||||
custom StaticNode
|
||||
|
@ -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
|
||||
|
||||
|
||||
part_parameter_urls = [
|
||||
url(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'),
|
||||
url(r'^template/(?P<pk>\d+)/delete/', views.PartParameterTemplateDelete.as_view(), name='part-param-template-edit'),
|
||||
re_path(r'^template/new/', views.PartParameterTemplateCreate.as_view(), name='part-param-template-create'),
|
||||
re_path(r'^template/(?P<pk>\d+)/edit/', views.PartParameterTemplateEdit.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 = [
|
||||
url(r'^delete/?', views.PartDelete.as_view(), name='part-delete'),
|
||||
url(r'^bom-download/?', views.BomDownload.as_view(), name='bom-download'),
|
||||
re_path(r'^delete/?', views.PartDelete.as_view(), name='part-delete'),
|
||||
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
|
||||
url(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-select/?', views.PartImageSelect.as_view(), name='part-image-select'),
|
||||
re_path(r'^thumb-download/', views.PartImageDownloadFromURL.as_view(), name='part-image-download'),
|
||||
|
||||
# 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 = [
|
||||
url(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'),
|
||||
url(r'^(?P<pid>\d+)/delete/', views.CategoryParameterTemplateDelete.as_view(), name='category-param-template-delete'),
|
||||
re_path(r'^new/', views.CategoryParameterTemplateCreate.as_view(), name='category-param-template-create'),
|
||||
re_path(r'^(?P<pid>\d+)/edit/', views.CategoryParameterTemplateEdit.as_view(), name='category-param-template-edit'),
|
||||
re_path(r'^(?P<pid>\d+)/delete/', views.CategoryParameterTemplateDelete.as_view(), name='category-param-template-delete'),
|
||||
]
|
||||
|
||||
category_urls = [
|
||||
|
||||
# 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
|
||||
url(r'(?P<pk>\d+)/', include([
|
||||
url(r'^delete/', views.CategoryDelete.as_view(), name='category-delete'),
|
||||
url(r'^parameters/', include(category_parameter_urls)),
|
||||
re_path(r'(?P<pk>\d+)/', include([
|
||||
re_path(r'^delete/', views.CategoryDelete.as_view(), name='category-delete'),
|
||||
re_path(r'^parameters/', include(category_parameter_urls)),
|
||||
|
||||
# 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 = [
|
||||
|
||||
# Upload a part
|
||||
url(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/', views.PartImport.as_view(), name='part-import'),
|
||||
re_path(r'^import-api/', views.PartImportAjax.as_view(), name='api-part-import'),
|
||||
|
||||
# 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
|
||||
url(r'^(?P<pk>\d+)/', include(part_detail_urls)),
|
||||
re_path(r'^(?P<pk>\d+)/', include(part_detail_urls)),
|
||||
|
||||
# Part category
|
||||
url(r'^category/', include(category_urls)),
|
||||
re_path(r'^category/', include(category_urls)),
|
||||
|
||||
# Part parameters
|
||||
url(r'^parameter/', include(part_parameter_urls)),
|
||||
re_path(r'^parameter/', include(part_parameter_urls)),
|
||||
|
||||
# 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
|
||||
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)
|
||||
url(r'^.*$', views.PartIndex.as_view(), name='part-index'),
|
||||
re_path(r'^.*$', views.PartIndex.as_view(), name='part-index'),
|
||||
]
|
||||
|
Reference in New Issue
Block a user