mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
commit
66d072e1c2
@ -6,10 +6,12 @@ from django.http import JsonResponse
|
|||||||
from django.utils.translation import gettext_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 rest_framework import filters, generics, permissions
|
from rest_framework import filters, permissions
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
|
|
||||||
|
from InvenTree.mixins import ListCreateAPI
|
||||||
|
|
||||||
from .status import is_worker_running
|
from .status import is_worker_running
|
||||||
from .version import (inventreeApiVersion, inventreeInstanceName,
|
from .version import (inventreeApiVersion, inventreeInstanceName,
|
||||||
inventreeVersion)
|
inventreeVersion)
|
||||||
@ -134,7 +136,7 @@ class BulkDeleteMixin:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ListCreateDestroyAPIView(BulkDeleteMixin, generics.ListCreateAPIView):
|
class ListCreateDestroyAPIView(BulkDeleteMixin, ListCreateAPI):
|
||||||
"""Custom API endpoint which provides BulkDelete functionality in addition to List and Create"""
|
"""Custom API endpoint which provides BulkDelete functionality in addition to List and Create"""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import InvenTree.status
|
|||||||
from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus,
|
from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus,
|
||||||
SalesOrderStatus, StockHistoryCode,
|
SalesOrderStatus, StockHistoryCode,
|
||||||
StockStatus)
|
StockStatus)
|
||||||
from users.models import RuleSet
|
from users.models import RuleSet, check_user_role
|
||||||
|
|
||||||
|
|
||||||
def health_status(request):
|
def health_status(request):
|
||||||
@ -83,31 +83,13 @@ def user_roles(request):
|
|||||||
roles = {
|
roles = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.is_superuser:
|
for role in RuleSet.RULESET_MODELS.keys():
|
||||||
for ruleset in RuleSet.RULESET_MODELS.keys(): # pragma: no cover
|
|
||||||
roles[ruleset] = {
|
|
||||||
'view': True,
|
|
||||||
'add': True,
|
|
||||||
'change': True,
|
|
||||||
'delete': True,
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
for group in user.groups.all():
|
|
||||||
for rule in group.rule_sets.all():
|
|
||||||
|
|
||||||
# Ensure the role name is in the dict
|
permissions = {}
|
||||||
if rule.name not in roles:
|
|
||||||
roles[rule.name] = {
|
|
||||||
'view': user.is_superuser,
|
|
||||||
'add': user.is_superuser,
|
|
||||||
'change': user.is_superuser,
|
|
||||||
'delete': user.is_superuser
|
|
||||||
}
|
|
||||||
|
|
||||||
# Roles are additive across groups
|
for perm in ['view', 'add', 'change', 'delete']:
|
||||||
roles[rule.name]['view'] |= rule.can_view
|
permissions[perm] = user.is_superuser or check_user_role(user, role, perm)
|
||||||
roles[rule.name]['add'] |= rule.can_add
|
|
||||||
roles[rule.name]['change'] |= rule.can_change
|
roles[role] = permissions
|
||||||
roles[rule.name]['delete'] |= rule.can_delete
|
|
||||||
|
|
||||||
return {'roles': roles}
|
return {'roles': roles}
|
||||||
|
@ -124,21 +124,31 @@ class EditUserForm(HelperForm):
|
|||||||
class SetPasswordForm(HelperForm):
|
class SetPasswordForm(HelperForm):
|
||||||
"""Form for setting user password."""
|
"""Form for setting user password."""
|
||||||
|
|
||||||
enter_password = forms.CharField(max_length=100,
|
enter_password = forms.CharField(
|
||||||
min_length=8,
|
max_length=100,
|
||||||
required=True,
|
min_length=8,
|
||||||
initial='',
|
required=True,
|
||||||
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
initial='',
|
||||||
label=_('Enter password'),
|
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
||||||
help_text=_('Enter new password'))
|
label=_('Enter password'),
|
||||||
|
help_text=_('Enter new password')
|
||||||
|
)
|
||||||
|
|
||||||
confirm_password = forms.CharField(max_length=100,
|
confirm_password = forms.CharField(
|
||||||
min_length=8,
|
max_length=100,
|
||||||
required=True,
|
min_length=8,
|
||||||
initial='',
|
required=True,
|
||||||
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
initial='',
|
||||||
label=_('Confirm password'),
|
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
||||||
help_text=_('Confirm new password'))
|
label=_('Confirm password'),
|
||||||
|
help_text=_('Confirm new password')
|
||||||
|
)
|
||||||
|
|
||||||
|
old_password = forms.CharField(
|
||||||
|
label=_("Old password"),
|
||||||
|
strip=False,
|
||||||
|
widget=forms.PasswordInput(attrs={'autocomplete': 'current-password', 'autofocus': True}),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
@ -146,7 +156,8 @@ class SetPasswordForm(HelperForm):
|
|||||||
model = User
|
model = User
|
||||||
fields = [
|
fields = [
|
||||||
'enter_password',
|
'enter_password',
|
||||||
'confirm_password'
|
'confirm_password',
|
||||||
|
'old_password',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
90
InvenTree/InvenTree/mixins.py
Normal file
90
InvenTree/InvenTree/mixins.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
"""Mixins for (API) views in the whole project."""
|
||||||
|
|
||||||
|
from bleach import clean
|
||||||
|
from rest_framework import generics, status
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
|
||||||
|
class CleanMixin():
|
||||||
|
"""Model mixin class which cleans inputs."""
|
||||||
|
|
||||||
|
# Define a map of fields avaialble for import
|
||||||
|
SAFE_FIELDS = {}
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
"""Override to clean data before processing it."""
|
||||||
|
serializer = self.get_serializer(data=self.clean_data(request.data))
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
self.perform_create(serializer)
|
||||||
|
headers = self.get_success_headers(serializer.data)
|
||||||
|
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||||
|
|
||||||
|
def update(self, request, *args, **kwargs):
|
||||||
|
"""Override to clean data before processing it."""
|
||||||
|
partial = kwargs.pop('partial', False)
|
||||||
|
instance = self.get_object()
|
||||||
|
serializer = self.get_serializer(instance, data=self.clean_data(request.data), partial=partial)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
self.perform_update(serializer)
|
||||||
|
|
||||||
|
if getattr(instance, '_prefetched_objects_cache', None):
|
||||||
|
# If 'prefetch_related' has been applied to a queryset, we need to
|
||||||
|
# forcibly invalidate the prefetch cache on the instance.
|
||||||
|
instance._prefetched_objects_cache = {}
|
||||||
|
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
def clean_data(self, data: dict) -> dict:
|
||||||
|
"""Clean / sanitize data.
|
||||||
|
|
||||||
|
This uses mozillas bleach under the hood to disable certain html tags by
|
||||||
|
encoding them - this leads to script tags etc. to not work.
|
||||||
|
The results can be longer then the input; might make some character combinations
|
||||||
|
`ugly`. Prevents XSS on the server-level.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (dict): Data that should be sanatized.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Profided data sanatized; still in the same order.
|
||||||
|
"""
|
||||||
|
clean_data = {}
|
||||||
|
for k, v in data.items():
|
||||||
|
if isinstance(v, str):
|
||||||
|
ret = clean(v)
|
||||||
|
elif isinstance(v, dict):
|
||||||
|
ret = self.clean_data(v)
|
||||||
|
else:
|
||||||
|
ret = v
|
||||||
|
clean_data[k] = ret
|
||||||
|
return clean_data
|
||||||
|
|
||||||
|
|
||||||
|
class ListAPI(generics.ListAPIView):
|
||||||
|
"""View for list API."""
|
||||||
|
|
||||||
|
|
||||||
|
class ListCreateAPI(CleanMixin, generics.ListCreateAPIView):
|
||||||
|
"""View for list and create API."""
|
||||||
|
|
||||||
|
|
||||||
|
class CreateAPI(CleanMixin, generics.CreateAPIView):
|
||||||
|
"""View for create API."""
|
||||||
|
|
||||||
|
|
||||||
|
class RetrieveAPI(generics.RetrieveAPIView):
|
||||||
|
"""View for retreive API."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RetrieveUpdateAPI(CleanMixin, generics.RetrieveUpdateAPIView):
|
||||||
|
"""View for retrieve and update API."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RetrieveUpdateDestroyAPI(CleanMixin, generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
"""View for retrieve, update and destroy API."""
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateAPI(CleanMixin, generics.UpdateAPIView):
|
||||||
|
"""View for update API."""
|
@ -309,6 +309,11 @@ if DEBUG_TOOLBAR_ENABLED: # pragma: no cover
|
|||||||
INSTALLED_APPS.append('debug_toolbar')
|
INSTALLED_APPS.append('debug_toolbar')
|
||||||
MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')
|
MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')
|
||||||
|
|
||||||
|
DEBUG_TOOLBAR_CONFIG = {
|
||||||
|
'RESULTS_CACHE_SIZE': 100,
|
||||||
|
'OBSERVE_REQUEST_CALLBACK': lambda x: False,
|
||||||
|
}
|
||||||
|
|
||||||
# Internal IP addresses allowed to see the debug toolbar
|
# Internal IP addresses allowed to see the debug toolbar
|
||||||
INTERNAL_IPS = [
|
INTERNAL_IPS = [
|
||||||
'127.0.0.1',
|
'127.0.0.1',
|
||||||
|
10
InvenTree/InvenTree/static/bootstrap-table/bootstrap-table.min.css
vendored
Normal file
10
InvenTree/InvenTree/static/bootstrap-table/bootstrap-table.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
10
InvenTree/InvenTree/static/bootstrap-table/bootstrap-table.min.js
vendored
Normal file
10
InvenTree/InvenTree/static/bootstrap-table/bootstrap-table.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -470,8 +470,8 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.part-thumb {
|
.part-thumb {
|
||||||
width: 200px;
|
width: 256px;
|
||||||
height: 200px;
|
height: 256px;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
|
@ -222,6 +222,29 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
var l10 = {
|
var l10 = {
|
||||||
|
code: 'bn',
|
||||||
|
week: {
|
||||||
|
dow: 0, // Sunday is the first day of the week.
|
||||||
|
doy: 6, // The week that contains Jan 1st is the first week of the year.
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
prev: 'পেছনে',
|
||||||
|
next: 'সামনে',
|
||||||
|
today: 'আজ',
|
||||||
|
month: 'মাস',
|
||||||
|
week: 'সপ্তাহ',
|
||||||
|
day: 'দিন',
|
||||||
|
list: 'তালিকা',
|
||||||
|
},
|
||||||
|
weekText: 'সপ্তাহ',
|
||||||
|
allDayText: 'সারাদিন',
|
||||||
|
moreLinkText: function(n) {
|
||||||
|
return '+অন্যান্য ' + n
|
||||||
|
},
|
||||||
|
noEventsText: 'কোনো ইভেন্ট নেই',
|
||||||
|
};
|
||||||
|
|
||||||
|
var l11 = {
|
||||||
code: 'bs',
|
code: 'bs',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -244,7 +267,7 @@
|
|||||||
noEventsText: 'Nema događaja za prikazivanje',
|
noEventsText: 'Nema događaja za prikazivanje',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l11 = {
|
var l12 = {
|
||||||
code: 'ca',
|
code: 'ca',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -265,7 +288,7 @@
|
|||||||
noEventsText: 'No hi ha esdeveniments per mostrar',
|
noEventsText: 'No hi ha esdeveniments per mostrar',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l12 = {
|
var l13 = {
|
||||||
code: 'cs',
|
code: 'cs',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -288,7 +311,7 @@
|
|||||||
noEventsText: 'Žádné akce k zobrazení',
|
noEventsText: 'Žádné akce k zobrazení',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l13 = {
|
var l14 = {
|
||||||
code: 'cy',
|
code: 'cy',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -310,7 +333,7 @@
|
|||||||
noEventsText: 'Dim digwyddiadau',
|
noEventsText: 'Dim digwyddiadau',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l14 = {
|
var l15 = {
|
||||||
code: 'da',
|
code: 'da',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -331,7 +354,12 @@
|
|||||||
noEventsText: 'Ingen arrangementer at vise',
|
noEventsText: 'Ingen arrangementer at vise',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l15 = {
|
function affix$1(buttonText) {
|
||||||
|
return (buttonText === 'Tag' || buttonText === 'Monat') ? 'r' :
|
||||||
|
buttonText === 'Jahr' ? 's' : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
var l16 = {
|
||||||
code: 'de-at',
|
code: 'de-at',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -348,14 +376,49 @@
|
|||||||
list: 'Terminübersicht',
|
list: 'Terminübersicht',
|
||||||
},
|
},
|
||||||
weekText: 'KW',
|
weekText: 'KW',
|
||||||
|
weekTextLong: 'Woche',
|
||||||
allDayText: 'Ganztägig',
|
allDayText: 'Ganztägig',
|
||||||
moreLinkText: function(n) {
|
moreLinkText: function(n) {
|
||||||
return '+ weitere ' + n
|
return '+ weitere ' + n
|
||||||
},
|
},
|
||||||
noEventsText: 'Keine Ereignisse anzuzeigen',
|
noEventsText: 'Keine Ereignisse anzuzeigen',
|
||||||
|
buttonHints: {
|
||||||
|
prev(buttonText) {
|
||||||
|
return `Vorherige${affix$1(buttonText)} ${buttonText}`
|
||||||
|
},
|
||||||
|
next(buttonText) {
|
||||||
|
return `Nächste${affix$1(buttonText)} ${buttonText}`
|
||||||
|
},
|
||||||
|
today(buttonText) {
|
||||||
|
// → Heute, Diese Woche, Dieser Monat, Dieses Jahr
|
||||||
|
if (buttonText === 'Tag') {
|
||||||
|
return 'Heute'
|
||||||
|
}
|
||||||
|
return `Diese${affix$1(buttonText)} ${buttonText}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
viewHint(buttonText) {
|
||||||
|
// → Tagesansicht, Wochenansicht, Monatsansicht, Jahresansicht
|
||||||
|
const glue = buttonText === 'Woche' ? 'n' : buttonText === 'Monat' ? 's' : 'es';
|
||||||
|
return buttonText + glue + 'ansicht'
|
||||||
|
},
|
||||||
|
navLinkHint: 'Gehe zu $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return 'Zeige ' + (eventCnt === 1 ?
|
||||||
|
'ein weiteres Ereignis' :
|
||||||
|
eventCnt + ' weitere Ereignisse')
|
||||||
|
},
|
||||||
|
closeHint: 'Schließen',
|
||||||
|
timeHint: 'Uhrzeit',
|
||||||
|
eventHint: 'Ereignis',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l16 = {
|
function affix(buttonText) {
|
||||||
|
return (buttonText === 'Tag' || buttonText === 'Monat') ? 'r' :
|
||||||
|
buttonText === 'Jahr' ? 's' : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
var l17 = {
|
||||||
code: 'de',
|
code: 'de',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -372,14 +435,44 @@
|
|||||||
list: 'Terminübersicht',
|
list: 'Terminübersicht',
|
||||||
},
|
},
|
||||||
weekText: 'KW',
|
weekText: 'KW',
|
||||||
|
weekTextLong: 'Woche',
|
||||||
allDayText: 'Ganztägig',
|
allDayText: 'Ganztägig',
|
||||||
moreLinkText: function(n) {
|
moreLinkText: function(n) {
|
||||||
return '+ weitere ' + n
|
return '+ weitere ' + n
|
||||||
},
|
},
|
||||||
noEventsText: 'Keine Ereignisse anzuzeigen',
|
noEventsText: 'Keine Ereignisse anzuzeigen',
|
||||||
|
buttonHints: {
|
||||||
|
prev(buttonText) {
|
||||||
|
return `Vorherige${affix(buttonText)} ${buttonText}`
|
||||||
|
},
|
||||||
|
next(buttonText) {
|
||||||
|
return `Nächste${affix(buttonText)} ${buttonText}`
|
||||||
|
},
|
||||||
|
today(buttonText) {
|
||||||
|
// → Heute, Diese Woche, Dieser Monat, Dieses Jahr
|
||||||
|
if (buttonText === 'Tag') {
|
||||||
|
return 'Heute'
|
||||||
|
}
|
||||||
|
return `Diese${affix(buttonText)} ${buttonText}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
viewHint(buttonText) {
|
||||||
|
// → Tagesansicht, Wochenansicht, Monatsansicht, Jahresansicht
|
||||||
|
const glue = buttonText === 'Woche' ? 'n' : buttonText === 'Monat' ? 's' : 'es';
|
||||||
|
return buttonText + glue + 'ansicht'
|
||||||
|
},
|
||||||
|
navLinkHint: 'Gehe zu $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return 'Zeige ' + (eventCnt === 1 ?
|
||||||
|
'ein weiteres Ereignis' :
|
||||||
|
eventCnt + ' weitere Ereignisse')
|
||||||
|
},
|
||||||
|
closeHint: 'Schließen',
|
||||||
|
timeHint: 'Uhrzeit',
|
||||||
|
eventHint: 'Ereignis',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l17 = {
|
var l18 = {
|
||||||
code: 'el',
|
code: 'el',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -400,31 +493,61 @@
|
|||||||
noEventsText: 'Δεν υπάρχουν γεγονότα προς εμφάνιση',
|
noEventsText: 'Δεν υπάρχουν γεγονότα προς εμφάνιση',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l18 = {
|
var l19 = {
|
||||||
code: 'en-au',
|
code: 'en-au',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
||||||
},
|
},
|
||||||
|
buttonHints: {
|
||||||
|
prev: 'Previous $0',
|
||||||
|
next: 'Next $0',
|
||||||
|
today: 'This $0',
|
||||||
|
},
|
||||||
|
viewHint: '$0 view',
|
||||||
|
navLinkHint: 'Go to $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return `Show ${eventCnt} more event${eventCnt === 1 ? '' : 's'}`
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var l19 = {
|
var l20 = {
|
||||||
code: 'en-gb',
|
code: 'en-gb',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
||||||
},
|
},
|
||||||
|
buttonHints: {
|
||||||
|
prev: 'Previous $0',
|
||||||
|
next: 'Next $0',
|
||||||
|
today: 'This $0',
|
||||||
|
},
|
||||||
|
viewHint: '$0 view',
|
||||||
|
navLinkHint: 'Go to $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return `Show ${eventCnt} more event${eventCnt === 1 ? '' : 's'}`
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var l20 = {
|
var l21 = {
|
||||||
code: 'en-nz',
|
code: 'en-nz',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
||||||
},
|
},
|
||||||
|
buttonHints: {
|
||||||
|
prev: 'Previous $0',
|
||||||
|
next: 'Next $0',
|
||||||
|
today: 'This $0',
|
||||||
|
},
|
||||||
|
viewHint: '$0 view',
|
||||||
|
navLinkHint: 'Go to $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return `Show ${eventCnt} more event${eventCnt === 1 ? '' : 's'}`
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var l21 = {
|
var l22 = {
|
||||||
code: 'eo',
|
code: 'eo',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -445,7 +568,7 @@
|
|||||||
noEventsText: 'Neniuj eventoj por montri',
|
noEventsText: 'Neniuj eventoj por montri',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l22 = {
|
var l23 = {
|
||||||
code: 'es',
|
code: 'es',
|
||||||
week: {
|
week: {
|
||||||
dow: 0, // Sunday is the first day of the week.
|
dow: 0, // Sunday is the first day of the week.
|
||||||
@ -466,7 +589,7 @@
|
|||||||
noEventsText: 'No hay eventos para mostrar',
|
noEventsText: 'No hay eventos para mostrar',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l23 = {
|
var l24 = {
|
||||||
code: 'es',
|
code: 'es',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -481,13 +604,32 @@
|
|||||||
day: 'Día',
|
day: 'Día',
|
||||||
list: 'Agenda',
|
list: 'Agenda',
|
||||||
},
|
},
|
||||||
|
buttonHints: {
|
||||||
|
prev: '$0 antes',
|
||||||
|
next: '$0 siguiente',
|
||||||
|
today(buttonText) {
|
||||||
|
return (buttonText === 'Día') ? 'Hoy' :
|
||||||
|
((buttonText === 'Semana') ? 'Esta' : 'Este') + ' ' + buttonText.toLocaleLowerCase()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
viewHint(buttonText) {
|
||||||
|
return 'Vista ' + (buttonText === 'Semana' ? 'de la' : 'del') + ' ' + buttonText.toLocaleLowerCase()
|
||||||
|
},
|
||||||
weekText: 'Sm',
|
weekText: 'Sm',
|
||||||
|
weekTextLong: 'Semana',
|
||||||
allDayText: 'Todo el día',
|
allDayText: 'Todo el día',
|
||||||
moreLinkText: 'más',
|
moreLinkText: 'más',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return `Mostrar ${eventCnt} eventos más`
|
||||||
|
},
|
||||||
noEventsText: 'No hay eventos para mostrar',
|
noEventsText: 'No hay eventos para mostrar',
|
||||||
|
navLinkHint: 'Ir al $0',
|
||||||
|
closeHint: 'Cerrar',
|
||||||
|
timeHint: 'La hora',
|
||||||
|
eventHint: 'Evento',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l24 = {
|
var l25 = {
|
||||||
code: 'et',
|
code: 'et',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -510,7 +652,7 @@
|
|||||||
noEventsText: 'Kuvamiseks puuduvad sündmused',
|
noEventsText: 'Kuvamiseks puuduvad sündmused',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l25 = {
|
var l26 = {
|
||||||
code: 'eu',
|
code: 'eu',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -531,7 +673,7 @@
|
|||||||
noEventsText: 'Ez dago ekitaldirik erakusteko',
|
noEventsText: 'Ez dago ekitaldirik erakusteko',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l26 = {
|
var l27 = {
|
||||||
code: 'fa',
|
code: 'fa',
|
||||||
week: {
|
week: {
|
||||||
dow: 6, // Saturday is the first day of the week.
|
dow: 6, // Saturday is the first day of the week.
|
||||||
@ -555,7 +697,7 @@
|
|||||||
noEventsText: 'هیچ رویدادی به نمایش',
|
noEventsText: 'هیچ رویدادی به نمایش',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l27 = {
|
var l28 = {
|
||||||
code: 'fi',
|
code: 'fi',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -576,7 +718,7 @@
|
|||||||
noEventsText: 'Ei näytettäviä tapahtumia',
|
noEventsText: 'Ei näytettäviä tapahtumia',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l28 = {
|
var l29 = {
|
||||||
code: 'fr',
|
code: 'fr',
|
||||||
buttonText: {
|
buttonText: {
|
||||||
prev: 'Précédent',
|
prev: 'Précédent',
|
||||||
@ -594,7 +736,7 @@
|
|||||||
noEventsText: 'Aucun événement à afficher',
|
noEventsText: 'Aucun événement à afficher',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l29 = {
|
var l30 = {
|
||||||
code: 'fr-ch',
|
code: 'fr-ch',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -616,7 +758,7 @@
|
|||||||
noEventsText: 'Aucun événement à afficher',
|
noEventsText: 'Aucun événement à afficher',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l30 = {
|
var l31 = {
|
||||||
code: 'fr',
|
code: 'fr',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -638,7 +780,7 @@
|
|||||||
noEventsText: 'Aucun événement à afficher',
|
noEventsText: 'Aucun événement à afficher',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l31 = {
|
var l32 = {
|
||||||
code: 'gl',
|
code: 'gl',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -659,7 +801,7 @@
|
|||||||
noEventsText: 'Non hai eventos para amosar',
|
noEventsText: 'Non hai eventos para amosar',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l32 = {
|
var l33 = {
|
||||||
code: 'he',
|
code: 'he',
|
||||||
direction: 'rtl',
|
direction: 'rtl',
|
||||||
buttonText: {
|
buttonText: {
|
||||||
@ -677,7 +819,7 @@
|
|||||||
weekText: 'שבוע',
|
weekText: 'שבוע',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l33 = {
|
var l34 = {
|
||||||
code: 'hi',
|
code: 'hi',
|
||||||
week: {
|
week: {
|
||||||
dow: 0, // Sunday is the first day of the week.
|
dow: 0, // Sunday is the first day of the week.
|
||||||
@ -700,7 +842,7 @@
|
|||||||
noEventsText: 'कोई घटनाओं को प्रदर्शित करने के लिए',
|
noEventsText: 'कोई घटनाओं को प्रदर्शित करने के लिए',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l34 = {
|
var l35 = {
|
||||||
code: 'hr',
|
code: 'hr',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -723,7 +865,7 @@
|
|||||||
noEventsText: 'Nema događaja za prikaz',
|
noEventsText: 'Nema događaja za prikaz',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l35 = {
|
var l36 = {
|
||||||
code: 'hu',
|
code: 'hu',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -736,7 +878,7 @@
|
|||||||
month: 'Hónap',
|
month: 'Hónap',
|
||||||
week: 'Hét',
|
week: 'Hét',
|
||||||
day: 'Nap',
|
day: 'Nap',
|
||||||
list: 'Napló',
|
list: 'Lista',
|
||||||
},
|
},
|
||||||
weekText: 'Hét',
|
weekText: 'Hét',
|
||||||
allDayText: 'Egész nap',
|
allDayText: 'Egész nap',
|
||||||
@ -744,7 +886,7 @@
|
|||||||
noEventsText: 'Nincs megjeleníthető esemény',
|
noEventsText: 'Nincs megjeleníthető esemény',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l36 = {
|
var l37 = {
|
||||||
code: 'hy-am',
|
code: 'hy-am',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -767,7 +909,7 @@
|
|||||||
noEventsText: 'Բացակայում է իրադարձությունը ցուցադրելու',
|
noEventsText: 'Բացակայում է իրադարձությունը ցուցադրելու',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l37 = {
|
var l38 = {
|
||||||
code: 'id',
|
code: 'id',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -788,7 +930,7 @@
|
|||||||
noEventsText: 'Tidak ada acara untuk ditampilkan',
|
noEventsText: 'Tidak ada acara untuk ditampilkan',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l38 = {
|
var l39 = {
|
||||||
code: 'is',
|
code: 'is',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -809,7 +951,7 @@
|
|||||||
noEventsText: 'Engir viðburðir til að sýna',
|
noEventsText: 'Engir viðburðir til að sýna',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l39 = {
|
var l40 = {
|
||||||
code: 'it',
|
code: 'it',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -832,7 +974,7 @@
|
|||||||
noEventsText: 'Non ci sono eventi da visualizzare',
|
noEventsText: 'Non ci sono eventi da visualizzare',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l40 = {
|
var l41 = {
|
||||||
code: 'ja',
|
code: 'ja',
|
||||||
buttonText: {
|
buttonText: {
|
||||||
prev: '前',
|
prev: '前',
|
||||||
@ -851,7 +993,7 @@
|
|||||||
noEventsText: '表示する予定はありません',
|
noEventsText: '表示する予定はありません',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l41 = {
|
var l42 = {
|
||||||
code: 'ka',
|
code: 'ka',
|
||||||
week: {
|
week: {
|
||||||
dow: 1,
|
dow: 1,
|
||||||
@ -874,7 +1016,7 @@
|
|||||||
noEventsText: 'ღონისძიებები არ არის',
|
noEventsText: 'ღონისძიებები არ არის',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l42 = {
|
var l43 = {
|
||||||
code: 'kk',
|
code: 'kk',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -897,7 +1039,29 @@
|
|||||||
noEventsText: 'Көрсету үшін оқиғалар жоқ',
|
noEventsText: 'Көрсету үшін оқиғалар жоқ',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l43 = {
|
var l44 = {
|
||||||
|
code: 'km',
|
||||||
|
week: {
|
||||||
|
dow: 1, // Monday is the first day of the week.
|
||||||
|
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
prev: 'មុន',
|
||||||
|
next: 'បន្ទាប់',
|
||||||
|
today: 'ថ្ងៃនេះ',
|
||||||
|
year: 'ឆ្នាំ',
|
||||||
|
month: 'ខែ',
|
||||||
|
week: 'សប្តាហ៍',
|
||||||
|
day: 'ថ្ងៃ',
|
||||||
|
list: 'បញ្ជី',
|
||||||
|
},
|
||||||
|
weekText: 'សប្តាហ៍',
|
||||||
|
allDayText: 'ពេញមួយថ្ងៃ',
|
||||||
|
moreLinkText: 'ច្រើនទៀត',
|
||||||
|
noEventsText: 'គ្មានព្រឹត្តិការណ៍ត្រូវបង្ហាញ',
|
||||||
|
};
|
||||||
|
|
||||||
|
var l45 = {
|
||||||
code: 'ko',
|
code: 'ko',
|
||||||
buttonText: {
|
buttonText: {
|
||||||
prev: '이전달',
|
prev: '이전달',
|
||||||
@ -914,7 +1078,29 @@
|
|||||||
noEventsText: '일정이 없습니다',
|
noEventsText: '일정이 없습니다',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l44 = {
|
var l46 = {
|
||||||
|
code: 'ku',
|
||||||
|
week: {
|
||||||
|
dow: 6, // Saturday is the first day of the week.
|
||||||
|
doy: 12, // The week that contains Jan 1st is the first week of the year.
|
||||||
|
},
|
||||||
|
direction: 'rtl',
|
||||||
|
buttonText: {
|
||||||
|
prev: 'پێشتر',
|
||||||
|
next: 'دواتر',
|
||||||
|
today: 'ئەمڕو',
|
||||||
|
month: 'مانگ',
|
||||||
|
week: 'هەفتە',
|
||||||
|
day: 'ڕۆژ',
|
||||||
|
list: 'بەرنامە',
|
||||||
|
},
|
||||||
|
weekText: 'هەفتە',
|
||||||
|
allDayText: 'هەموو ڕۆژەکە',
|
||||||
|
moreLinkText: 'زیاتر',
|
||||||
|
noEventsText: 'هیچ ڕووداوێك نیە',
|
||||||
|
};
|
||||||
|
|
||||||
|
var l47 = {
|
||||||
code: 'lb',
|
code: 'lb',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -935,7 +1121,7 @@
|
|||||||
noEventsText: 'Nee Evenementer ze affichéieren',
|
noEventsText: 'Nee Evenementer ze affichéieren',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l45 = {
|
var l48 = {
|
||||||
code: 'lt',
|
code: 'lt',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -956,7 +1142,7 @@
|
|||||||
noEventsText: 'Nėra įvykių rodyti',
|
noEventsText: 'Nėra įvykių rodyti',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l46 = {
|
var l49 = {
|
||||||
code: 'lv',
|
code: 'lv',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -979,7 +1165,7 @@
|
|||||||
noEventsText: 'Nav notikumu',
|
noEventsText: 'Nav notikumu',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l47 = {
|
var l50 = {
|
||||||
code: 'mk',
|
code: 'mk',
|
||||||
buttonText: {
|
buttonText: {
|
||||||
prev: 'претходно',
|
prev: 'претходно',
|
||||||
@ -998,7 +1184,7 @@
|
|||||||
noEventsText: 'Нема настани за прикажување',
|
noEventsText: 'Нема настани за прикажување',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l48 = {
|
var l51 = {
|
||||||
code: 'ms',
|
code: 'ms',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1021,7 +1207,7 @@
|
|||||||
noEventsText: 'Tiada peristiwa untuk dipaparkan',
|
noEventsText: 'Tiada peristiwa untuk dipaparkan',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l49 = {
|
var l52 = {
|
||||||
code: 'nb',
|
code: 'nb',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1037,12 +1223,23 @@
|
|||||||
list: 'Agenda',
|
list: 'Agenda',
|
||||||
},
|
},
|
||||||
weekText: 'Uke',
|
weekText: 'Uke',
|
||||||
|
weekTextLong: 'Uke',
|
||||||
allDayText: 'Hele dagen',
|
allDayText: 'Hele dagen',
|
||||||
moreLinkText: 'til',
|
moreLinkText: 'til',
|
||||||
noEventsText: 'Ingen hendelser å vise',
|
noEventsText: 'Ingen hendelser å vise',
|
||||||
|
buttonHints: {
|
||||||
|
prev: 'Forrige $0',
|
||||||
|
next: 'Neste $0',
|
||||||
|
today: 'Nåværende $0',
|
||||||
|
},
|
||||||
|
viewHint: '$0 visning',
|
||||||
|
navLinkHint: 'Gå til $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return `Vis ${eventCnt} flere hendelse${eventCnt === 1 ? '' : 'r'}`
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var l50 = {
|
var l53 = {
|
||||||
code: 'ne', // code for nepal
|
code: 'ne', // code for nepal
|
||||||
week: {
|
week: {
|
||||||
dow: 7, // Sunday is the first day of the week.
|
dow: 7, // Sunday is the first day of the week.
|
||||||
@ -1063,7 +1260,7 @@
|
|||||||
noEventsText: 'देखाउनको लागि कुनै घटनाहरू छैनन्',
|
noEventsText: 'देखाउनको लागि कुनै घटनाहरू छैनन्',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l51 = {
|
var l54 = {
|
||||||
code: 'nl',
|
code: 'nl',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1084,7 +1281,7 @@
|
|||||||
noEventsText: 'Geen evenementen om te laten zien',
|
noEventsText: 'Geen evenementen om te laten zien',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l52 = {
|
var l55 = {
|
||||||
code: 'nn',
|
code: 'nn',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1105,7 +1302,7 @@
|
|||||||
noEventsText: 'Ingen hendelser å vise',
|
noEventsText: 'Ingen hendelser å vise',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l53 = {
|
var l56 = {
|
||||||
code: 'pl',
|
code: 'pl',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1126,7 +1323,7 @@
|
|||||||
noEventsText: 'Brak wydarzeń do wyświetlenia',
|
noEventsText: 'Brak wydarzeń do wyświetlenia',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l54 = {
|
var l57 = {
|
||||||
code: 'pt-br',
|
code: 'pt-br',
|
||||||
buttonText: {
|
buttonText: {
|
||||||
prev: 'Anterior',
|
prev: 'Anterior',
|
||||||
@ -1145,7 +1342,7 @@
|
|||||||
noEventsText: 'Não há eventos para mostrar',
|
noEventsText: 'Não há eventos para mostrar',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l55 = {
|
var l58 = {
|
||||||
code: 'pt',
|
code: 'pt',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1166,7 +1363,7 @@
|
|||||||
noEventsText: 'Não há eventos para mostrar',
|
noEventsText: 'Não há eventos para mostrar',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l56 = {
|
var l59 = {
|
||||||
code: 'ro',
|
code: 'ro',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1189,7 +1386,7 @@
|
|||||||
noEventsText: 'Nu există evenimente de afișat',
|
noEventsText: 'Nu există evenimente de afișat',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l57 = {
|
var l60 = {
|
||||||
code: 'ru',
|
code: 'ru',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1212,7 +1409,28 @@
|
|||||||
noEventsText: 'Нет событий для отображения',
|
noEventsText: 'Нет событий для отображения',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l58 = {
|
var l61 = {
|
||||||
|
code: 'si-lk',
|
||||||
|
week: {
|
||||||
|
dow: 1, // Monday is the first day of the week.
|
||||||
|
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
prev: 'පෙර',
|
||||||
|
next: 'පසු',
|
||||||
|
today: 'අද',
|
||||||
|
month: 'මාසය',
|
||||||
|
week: 'සතිය',
|
||||||
|
day: 'දවස',
|
||||||
|
list: 'ලැයිස්තුව',
|
||||||
|
},
|
||||||
|
weekText: 'සති',
|
||||||
|
allDayText: 'සියලු',
|
||||||
|
moreLinkText: 'තවත්',
|
||||||
|
noEventsText: 'මුකුත් නැත',
|
||||||
|
};
|
||||||
|
|
||||||
|
var l62 = {
|
||||||
code: 'sk',
|
code: 'sk',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1235,7 +1453,7 @@
|
|||||||
noEventsText: 'Žiadne akcie na zobrazenie',
|
noEventsText: 'Žiadne akcie na zobrazenie',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l59 = {
|
var l63 = {
|
||||||
code: 'sl',
|
code: 'sl',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1256,7 +1474,24 @@
|
|||||||
noEventsText: 'Ni dogodkov za prikaz',
|
noEventsText: 'Ni dogodkov za prikaz',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l60 = {
|
var l64 = {
|
||||||
|
code: 'sm',
|
||||||
|
buttonText: {
|
||||||
|
prev: 'Talu ai',
|
||||||
|
next: 'Mulimuli atu',
|
||||||
|
today: 'Aso nei',
|
||||||
|
month: 'Masina',
|
||||||
|
week: 'Vaiaso',
|
||||||
|
day: 'Aso',
|
||||||
|
list: 'Faasologa',
|
||||||
|
},
|
||||||
|
weekText: 'Vaiaso',
|
||||||
|
allDayText: 'Aso atoa',
|
||||||
|
moreLinkText: 'sili atu',
|
||||||
|
noEventsText: 'Leai ni mea na tutupu',
|
||||||
|
};
|
||||||
|
|
||||||
|
var l65 = {
|
||||||
code: 'sq',
|
code: 'sq',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1279,7 +1514,7 @@
|
|||||||
noEventsText: 'Nuk ka evente për të shfaqur',
|
noEventsText: 'Nuk ka evente për të shfaqur',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l61 = {
|
var l66 = {
|
||||||
code: 'sr-cyrl',
|
code: 'sr-cyrl',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1302,7 +1537,7 @@
|
|||||||
noEventsText: 'Нема догађаја за приказ',
|
noEventsText: 'Нема догађаја за приказ',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l62 = {
|
var l67 = {
|
||||||
code: 'sr',
|
code: 'sr',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1325,7 +1560,7 @@
|
|||||||
noEventsText: 'Nеma događaja za prikaz',
|
noEventsText: 'Nеma događaja za prikaz',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l63 = {
|
var l68 = {
|
||||||
code: 'sv',
|
code: 'sv',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1340,13 +1575,56 @@
|
|||||||
day: 'Dag',
|
day: 'Dag',
|
||||||
list: 'Program',
|
list: 'Program',
|
||||||
},
|
},
|
||||||
|
buttonHints: {
|
||||||
|
prev(buttonText) {
|
||||||
|
return `Föregående ${buttonText.toLocaleLowerCase()}`
|
||||||
|
},
|
||||||
|
next(buttonText) {
|
||||||
|
return `Nästa ${buttonText.toLocaleLowerCase()}`
|
||||||
|
},
|
||||||
|
today(buttonText) {
|
||||||
|
return (buttonText === 'Program' ? 'Detta' : 'Denna') + ' ' + buttonText.toLocaleLowerCase()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
viewHint: '$0 vy',
|
||||||
|
navLinkHint: 'Gå till $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return `Visa ytterligare ${eventCnt} händelse${eventCnt === 1 ? '' : 'r'}`
|
||||||
|
},
|
||||||
weekText: 'v.',
|
weekText: 'v.',
|
||||||
|
weekTextLong: 'Vecka',
|
||||||
allDayText: 'Heldag',
|
allDayText: 'Heldag',
|
||||||
moreLinkText: 'till',
|
moreLinkText: 'till',
|
||||||
noEventsText: 'Inga händelser att visa',
|
noEventsText: 'Inga händelser att visa',
|
||||||
|
closeHint: 'Stäng',
|
||||||
|
timeHint: 'Klockan',
|
||||||
|
eventHint: 'Händelse',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l64 = {
|
var l69 = {
|
||||||
|
code: 'ta-in',
|
||||||
|
week: {
|
||||||
|
dow: 1, // Monday is the first day of the week.
|
||||||
|
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
prev: 'முந்தைய',
|
||||||
|
next: 'அடுத்தது',
|
||||||
|
today: 'இன்று',
|
||||||
|
month: 'மாதம்',
|
||||||
|
week: 'வாரம்',
|
||||||
|
day: 'நாள்',
|
||||||
|
list: 'தினசரி அட்டவணை',
|
||||||
|
},
|
||||||
|
weekText: 'வாரம்',
|
||||||
|
allDayText: 'நாள் முழுவதும்',
|
||||||
|
moreLinkText: function(n) {
|
||||||
|
return '+ மேலும் ' + n
|
||||||
|
},
|
||||||
|
noEventsText: 'காண்பிக்க நிகழ்வுகள் இல்லை',
|
||||||
|
};
|
||||||
|
|
||||||
|
var l70 = {
|
||||||
code: 'th',
|
code: 'th',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1370,7 +1648,7 @@
|
|||||||
noEventsText: 'ไม่มีกิจกรรมที่จะแสดง',
|
noEventsText: 'ไม่มีกิจกรรมที่จะแสดง',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l65 = {
|
var l71 = {
|
||||||
code: 'tr',
|
code: 'tr',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1391,7 +1669,7 @@
|
|||||||
noEventsText: 'Gösterilecek etkinlik yok',
|
noEventsText: 'Gösterilecek etkinlik yok',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l66 = {
|
var l72 = {
|
||||||
code: 'ug',
|
code: 'ug',
|
||||||
buttonText: {
|
buttonText: {
|
||||||
month: 'ئاي',
|
month: 'ئاي',
|
||||||
@ -1402,7 +1680,7 @@
|
|||||||
allDayText: 'پۈتۈن كۈن',
|
allDayText: 'پۈتۈن كۈن',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l67 = {
|
var l73 = {
|
||||||
code: 'uk',
|
code: 'uk',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1425,7 +1703,7 @@
|
|||||||
noEventsText: 'Немає подій для відображення',
|
noEventsText: 'Немає подій для відображення',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l68 = {
|
var l74 = {
|
||||||
code: 'uz',
|
code: 'uz',
|
||||||
buttonText: {
|
buttonText: {
|
||||||
month: 'Oy',
|
month: 'Oy',
|
||||||
@ -1440,7 +1718,7 @@
|
|||||||
noEventsText: "Ko'rsatish uchun voqealar yo'q",
|
noEventsText: "Ko'rsatish uchun voqealar yo'q",
|
||||||
};
|
};
|
||||||
|
|
||||||
var l69 = {
|
var l75 = {
|
||||||
code: 'vi',
|
code: 'vi',
|
||||||
week: {
|
week: {
|
||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
@ -1463,7 +1741,7 @@
|
|||||||
noEventsText: 'Không có sự kiện để hiển thị',
|
noEventsText: 'Không có sự kiện để hiển thị',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l70 = {
|
var l76 = {
|
||||||
code: 'zh-cn',
|
code: 'zh-cn',
|
||||||
week: {
|
week: {
|
||||||
// GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
|
// GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
|
||||||
@ -1487,7 +1765,7 @@
|
|||||||
noEventsText: '没有事件显示',
|
noEventsText: '没有事件显示',
|
||||||
};
|
};
|
||||||
|
|
||||||
var l71 = {
|
var l77 = {
|
||||||
code: 'zh-tw',
|
code: 'zh-tw',
|
||||||
buttonText: {
|
buttonText: {
|
||||||
prev: '上月',
|
prev: '上月',
|
||||||
@ -1507,7 +1785,7 @@
|
|||||||
/* eslint max-len: off */
|
/* eslint max-len: off */
|
||||||
|
|
||||||
var localesAll = [
|
var localesAll = [
|
||||||
l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71,
|
l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77,
|
||||||
];
|
];
|
||||||
|
|
||||||
return localesAll;
|
return localesAll;
|
||||||
|
1
InvenTree/InvenTree/static/fullcalendar/locales-all.min.js
vendored
Normal file
1
InvenTree/InvenTree/static/fullcalendar/locales-all.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
29
InvenTree/InvenTree/static/fullcalendar/locales/bn.js
Normal file
29
InvenTree/InvenTree/static/fullcalendar/locales/bn.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
FullCalendar.globalLocales.push(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var bn = {
|
||||||
|
code: 'bn',
|
||||||
|
week: {
|
||||||
|
dow: 0, // Sunday is the first day of the week.
|
||||||
|
doy: 6, // The week that contains Jan 1st is the first week of the year.
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
prev: 'পেছনে',
|
||||||
|
next: 'সামনে',
|
||||||
|
today: 'আজ',
|
||||||
|
month: 'মাস',
|
||||||
|
week: 'সপ্তাহ',
|
||||||
|
day: 'দিন',
|
||||||
|
list: 'তালিকা',
|
||||||
|
},
|
||||||
|
weekText: 'সপ্তাহ',
|
||||||
|
allDayText: 'সারাদিন',
|
||||||
|
moreLinkText: function(n) {
|
||||||
|
return '+অন্যান্য ' + n
|
||||||
|
},
|
||||||
|
noEventsText: 'কোনো ইভেন্ট নেই',
|
||||||
|
};
|
||||||
|
|
||||||
|
return bn;
|
||||||
|
|
||||||
|
}());
|
@ -1,6 +1,11 @@
|
|||||||
FullCalendar.globalLocales.push(function () {
|
FullCalendar.globalLocales.push(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
function affix(buttonText) {
|
||||||
|
return (buttonText === 'Tag' || buttonText === 'Monat') ? 'r' :
|
||||||
|
buttonText === 'Jahr' ? 's' : ''
|
||||||
|
}
|
||||||
|
|
||||||
var deAt = {
|
var deAt = {
|
||||||
code: 'de-at',
|
code: 'de-at',
|
||||||
week: {
|
week: {
|
||||||
@ -18,11 +23,41 @@ FullCalendar.globalLocales.push(function () {
|
|||||||
list: 'Terminübersicht',
|
list: 'Terminübersicht',
|
||||||
},
|
},
|
||||||
weekText: 'KW',
|
weekText: 'KW',
|
||||||
|
weekTextLong: 'Woche',
|
||||||
allDayText: 'Ganztägig',
|
allDayText: 'Ganztägig',
|
||||||
moreLinkText: function(n) {
|
moreLinkText: function(n) {
|
||||||
return '+ weitere ' + n
|
return '+ weitere ' + n
|
||||||
},
|
},
|
||||||
noEventsText: 'Keine Ereignisse anzuzeigen',
|
noEventsText: 'Keine Ereignisse anzuzeigen',
|
||||||
|
buttonHints: {
|
||||||
|
prev(buttonText) {
|
||||||
|
return `Vorherige${affix(buttonText)} ${buttonText}`
|
||||||
|
},
|
||||||
|
next(buttonText) {
|
||||||
|
return `Nächste${affix(buttonText)} ${buttonText}`
|
||||||
|
},
|
||||||
|
today(buttonText) {
|
||||||
|
// → Heute, Diese Woche, Dieser Monat, Dieses Jahr
|
||||||
|
if (buttonText === 'Tag') {
|
||||||
|
return 'Heute'
|
||||||
|
}
|
||||||
|
return `Diese${affix(buttonText)} ${buttonText}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
viewHint(buttonText) {
|
||||||
|
// → Tagesansicht, Wochenansicht, Monatsansicht, Jahresansicht
|
||||||
|
const glue = buttonText === 'Woche' ? 'n' : buttonText === 'Monat' ? 's' : 'es';
|
||||||
|
return buttonText + glue + 'ansicht'
|
||||||
|
},
|
||||||
|
navLinkHint: 'Gehe zu $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return 'Zeige ' + (eventCnt === 1 ?
|
||||||
|
'ein weiteres Ereignis' :
|
||||||
|
eventCnt + ' weitere Ereignisse')
|
||||||
|
},
|
||||||
|
closeHint: 'Schließen',
|
||||||
|
timeHint: 'Uhrzeit',
|
||||||
|
eventHint: 'Ereignis',
|
||||||
};
|
};
|
||||||
|
|
||||||
return deAt;
|
return deAt;
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
FullCalendar.globalLocales.push(function () {
|
FullCalendar.globalLocales.push(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
function affix(buttonText) {
|
||||||
|
return (buttonText === 'Tag' || buttonText === 'Monat') ? 'r' :
|
||||||
|
buttonText === 'Jahr' ? 's' : ''
|
||||||
|
}
|
||||||
|
|
||||||
var de = {
|
var de = {
|
||||||
code: 'de',
|
code: 'de',
|
||||||
week: {
|
week: {
|
||||||
@ -18,11 +23,41 @@ FullCalendar.globalLocales.push(function () {
|
|||||||
list: 'Terminübersicht',
|
list: 'Terminübersicht',
|
||||||
},
|
},
|
||||||
weekText: 'KW',
|
weekText: 'KW',
|
||||||
|
weekTextLong: 'Woche',
|
||||||
allDayText: 'Ganztägig',
|
allDayText: 'Ganztägig',
|
||||||
moreLinkText: function(n) {
|
moreLinkText: function(n) {
|
||||||
return '+ weitere ' + n
|
return '+ weitere ' + n
|
||||||
},
|
},
|
||||||
noEventsText: 'Keine Ereignisse anzuzeigen',
|
noEventsText: 'Keine Ereignisse anzuzeigen',
|
||||||
|
buttonHints: {
|
||||||
|
prev(buttonText) {
|
||||||
|
return `Vorherige${affix(buttonText)} ${buttonText}`
|
||||||
|
},
|
||||||
|
next(buttonText) {
|
||||||
|
return `Nächste${affix(buttonText)} ${buttonText}`
|
||||||
|
},
|
||||||
|
today(buttonText) {
|
||||||
|
// → Heute, Diese Woche, Dieser Monat, Dieses Jahr
|
||||||
|
if (buttonText === 'Tag') {
|
||||||
|
return 'Heute'
|
||||||
|
}
|
||||||
|
return `Diese${affix(buttonText)} ${buttonText}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
viewHint(buttonText) {
|
||||||
|
// → Tagesansicht, Wochenansicht, Monatsansicht, Jahresansicht
|
||||||
|
const glue = buttonText === 'Woche' ? 'n' : buttonText === 'Monat' ? 's' : 'es';
|
||||||
|
return buttonText + glue + 'ansicht'
|
||||||
|
},
|
||||||
|
navLinkHint: 'Gehe zu $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return 'Zeige ' + (eventCnt === 1 ?
|
||||||
|
'ein weiteres Ereignis' :
|
||||||
|
eventCnt + ' weitere Ereignisse')
|
||||||
|
},
|
||||||
|
closeHint: 'Schließen',
|
||||||
|
timeHint: 'Uhrzeit',
|
||||||
|
eventHint: 'Ereignis',
|
||||||
};
|
};
|
||||||
|
|
||||||
return de;
|
return de;
|
||||||
|
@ -7,6 +7,16 @@ FullCalendar.globalLocales.push(function () {
|
|||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
||||||
},
|
},
|
||||||
|
buttonHints: {
|
||||||
|
prev: 'Previous $0',
|
||||||
|
next: 'Next $0',
|
||||||
|
today: 'This $0',
|
||||||
|
},
|
||||||
|
viewHint: '$0 view',
|
||||||
|
navLinkHint: 'Go to $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return `Show ${eventCnt} more event${eventCnt === 1 ? '' : 's'}`
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return enAu;
|
return enAu;
|
||||||
|
@ -7,6 +7,16 @@ FullCalendar.globalLocales.push(function () {
|
|||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
||||||
},
|
},
|
||||||
|
buttonHints: {
|
||||||
|
prev: 'Previous $0',
|
||||||
|
next: 'Next $0',
|
||||||
|
today: 'This $0',
|
||||||
|
},
|
||||||
|
viewHint: '$0 view',
|
||||||
|
navLinkHint: 'Go to $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return `Show ${eventCnt} more event${eventCnt === 1 ? '' : 's'}`
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return enGb;
|
return enGb;
|
||||||
|
@ -7,6 +7,16 @@ FullCalendar.globalLocales.push(function () {
|
|||||||
dow: 1, // Monday is the first day of the week.
|
dow: 1, // Monday is the first day of the week.
|
||||||
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
||||||
},
|
},
|
||||||
|
buttonHints: {
|
||||||
|
prev: 'Previous $0',
|
||||||
|
next: 'Next $0',
|
||||||
|
today: 'This $0',
|
||||||
|
},
|
||||||
|
viewHint: '$0 view',
|
||||||
|
navLinkHint: 'Go to $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return `Show ${eventCnt} more event${eventCnt === 1 ? '' : 's'}`
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return enNz;
|
return enNz;
|
||||||
|
@ -16,10 +16,29 @@ FullCalendar.globalLocales.push(function () {
|
|||||||
day: 'Día',
|
day: 'Día',
|
||||||
list: 'Agenda',
|
list: 'Agenda',
|
||||||
},
|
},
|
||||||
|
buttonHints: {
|
||||||
|
prev: '$0 antes',
|
||||||
|
next: '$0 siguiente',
|
||||||
|
today(buttonText) {
|
||||||
|
return (buttonText === 'Día') ? 'Hoy' :
|
||||||
|
((buttonText === 'Semana') ? 'Esta' : 'Este') + ' ' + buttonText.toLocaleLowerCase()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
viewHint(buttonText) {
|
||||||
|
return 'Vista ' + (buttonText === 'Semana' ? 'de la' : 'del') + ' ' + buttonText.toLocaleLowerCase()
|
||||||
|
},
|
||||||
weekText: 'Sm',
|
weekText: 'Sm',
|
||||||
|
weekTextLong: 'Semana',
|
||||||
allDayText: 'Todo el día',
|
allDayText: 'Todo el día',
|
||||||
moreLinkText: 'más',
|
moreLinkText: 'más',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return `Mostrar ${eventCnt} eventos más`
|
||||||
|
},
|
||||||
noEventsText: 'No hay eventos para mostrar',
|
noEventsText: 'No hay eventos para mostrar',
|
||||||
|
navLinkHint: 'Ir al $0',
|
||||||
|
closeHint: 'Cerrar',
|
||||||
|
timeHint: 'La hora',
|
||||||
|
eventHint: 'Evento',
|
||||||
};
|
};
|
||||||
|
|
||||||
return es;
|
return es;
|
||||||
|
@ -14,7 +14,7 @@ FullCalendar.globalLocales.push(function () {
|
|||||||
month: 'Hónap',
|
month: 'Hónap',
|
||||||
week: 'Hét',
|
week: 'Hét',
|
||||||
day: 'Nap',
|
day: 'Nap',
|
||||||
list: 'Napló',
|
list: 'Lista',
|
||||||
},
|
},
|
||||||
weekText: 'Hét',
|
weekText: 'Hét',
|
||||||
allDayText: 'Egész nap',
|
allDayText: 'Egész nap',
|
||||||
|
28
InvenTree/InvenTree/static/fullcalendar/locales/km.js
Normal file
28
InvenTree/InvenTree/static/fullcalendar/locales/km.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
FullCalendar.globalLocales.push(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var km = {
|
||||||
|
code: 'km',
|
||||||
|
week: {
|
||||||
|
dow: 1, // Monday is the first day of the week.
|
||||||
|
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
prev: 'មុន',
|
||||||
|
next: 'បន្ទាប់',
|
||||||
|
today: 'ថ្ងៃនេះ',
|
||||||
|
year: 'ឆ្នាំ',
|
||||||
|
month: 'ខែ',
|
||||||
|
week: 'សប្តាហ៍',
|
||||||
|
day: 'ថ្ងៃ',
|
||||||
|
list: 'បញ្ជី',
|
||||||
|
},
|
||||||
|
weekText: 'សប្តាហ៍',
|
||||||
|
allDayText: 'ពេញមួយថ្ងៃ',
|
||||||
|
moreLinkText: 'ច្រើនទៀត',
|
||||||
|
noEventsText: 'គ្មានព្រឹត្តិការណ៍ត្រូវបង្ហាញ',
|
||||||
|
};
|
||||||
|
|
||||||
|
return km;
|
||||||
|
|
||||||
|
}());
|
28
InvenTree/InvenTree/static/fullcalendar/locales/ku.js
Normal file
28
InvenTree/InvenTree/static/fullcalendar/locales/ku.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
FullCalendar.globalLocales.push(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var ku = {
|
||||||
|
code: 'ku',
|
||||||
|
week: {
|
||||||
|
dow: 6, // Saturday is the first day of the week.
|
||||||
|
doy: 12, // The week that contains Jan 1st is the first week of the year.
|
||||||
|
},
|
||||||
|
direction: 'rtl',
|
||||||
|
buttonText: {
|
||||||
|
prev: 'پێشتر',
|
||||||
|
next: 'دواتر',
|
||||||
|
today: 'ئەمڕو',
|
||||||
|
month: 'مانگ',
|
||||||
|
week: 'هەفتە',
|
||||||
|
day: 'ڕۆژ',
|
||||||
|
list: 'بەرنامە',
|
||||||
|
},
|
||||||
|
weekText: 'هەفتە',
|
||||||
|
allDayText: 'هەموو ڕۆژەکە',
|
||||||
|
moreLinkText: 'زیاتر',
|
||||||
|
noEventsText: 'هیچ ڕووداوێك نیە',
|
||||||
|
};
|
||||||
|
|
||||||
|
return ku;
|
||||||
|
|
||||||
|
}());
|
@ -17,9 +17,20 @@ FullCalendar.globalLocales.push(function () {
|
|||||||
list: 'Agenda',
|
list: 'Agenda',
|
||||||
},
|
},
|
||||||
weekText: 'Uke',
|
weekText: 'Uke',
|
||||||
|
weekTextLong: 'Uke',
|
||||||
allDayText: 'Hele dagen',
|
allDayText: 'Hele dagen',
|
||||||
moreLinkText: 'til',
|
moreLinkText: 'til',
|
||||||
noEventsText: 'Ingen hendelser å vise',
|
noEventsText: 'Ingen hendelser å vise',
|
||||||
|
buttonHints: {
|
||||||
|
prev: 'Forrige $0',
|
||||||
|
next: 'Neste $0',
|
||||||
|
today: 'Nåværende $0',
|
||||||
|
},
|
||||||
|
viewHint: '$0 visning',
|
||||||
|
navLinkHint: 'Gå til $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return `Vis ${eventCnt} flere hendelse${eventCnt === 1 ? '' : 'r'}`
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return nb;
|
return nb;
|
||||||
|
27
InvenTree/InvenTree/static/fullcalendar/locales/si-lk.js
Normal file
27
InvenTree/InvenTree/static/fullcalendar/locales/si-lk.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
FullCalendar.globalLocales.push(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var siLk = {
|
||||||
|
code: 'si-lk',
|
||||||
|
week: {
|
||||||
|
dow: 1, // Monday is the first day of the week.
|
||||||
|
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
prev: 'පෙර',
|
||||||
|
next: 'පසු',
|
||||||
|
today: 'අද',
|
||||||
|
month: 'මාසය',
|
||||||
|
week: 'සතිය',
|
||||||
|
day: 'දවස',
|
||||||
|
list: 'ලැයිස්තුව',
|
||||||
|
},
|
||||||
|
weekText: 'සති',
|
||||||
|
allDayText: 'සියලු',
|
||||||
|
moreLinkText: 'තවත්',
|
||||||
|
noEventsText: 'මුකුත් නැත',
|
||||||
|
};
|
||||||
|
|
||||||
|
return siLk;
|
||||||
|
|
||||||
|
}());
|
23
InvenTree/InvenTree/static/fullcalendar/locales/sm.js
Normal file
23
InvenTree/InvenTree/static/fullcalendar/locales/sm.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
FullCalendar.globalLocales.push(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var sm = {
|
||||||
|
code: 'sm',
|
||||||
|
buttonText: {
|
||||||
|
prev: 'Talu ai',
|
||||||
|
next: 'Mulimuli atu',
|
||||||
|
today: 'Aso nei',
|
||||||
|
month: 'Masina',
|
||||||
|
week: 'Vaiaso',
|
||||||
|
day: 'Aso',
|
||||||
|
list: 'Faasologa',
|
||||||
|
},
|
||||||
|
weekText: 'Vaiaso',
|
||||||
|
allDayText: 'Aso atoa',
|
||||||
|
moreLinkText: 'sili atu',
|
||||||
|
noEventsText: 'Leai ni mea na tutupu',
|
||||||
|
};
|
||||||
|
|
||||||
|
return sm;
|
||||||
|
|
||||||
|
}());
|
@ -16,10 +16,30 @@ FullCalendar.globalLocales.push(function () {
|
|||||||
day: 'Dag',
|
day: 'Dag',
|
||||||
list: 'Program',
|
list: 'Program',
|
||||||
},
|
},
|
||||||
|
buttonHints: {
|
||||||
|
prev(buttonText) {
|
||||||
|
return `Föregående ${buttonText.toLocaleLowerCase()}`
|
||||||
|
},
|
||||||
|
next(buttonText) {
|
||||||
|
return `Nästa ${buttonText.toLocaleLowerCase()}`
|
||||||
|
},
|
||||||
|
today(buttonText) {
|
||||||
|
return (buttonText === 'Program' ? 'Detta' : 'Denna') + ' ' + buttonText.toLocaleLowerCase()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
viewHint: '$0 vy',
|
||||||
|
navLinkHint: 'Gå till $0',
|
||||||
|
moreLinkHint(eventCnt) {
|
||||||
|
return `Visa ytterligare ${eventCnt} händelse${eventCnt === 1 ? '' : 'r'}`
|
||||||
|
},
|
||||||
weekText: 'v.',
|
weekText: 'v.',
|
||||||
|
weekTextLong: 'Vecka',
|
||||||
allDayText: 'Heldag',
|
allDayText: 'Heldag',
|
||||||
moreLinkText: 'till',
|
moreLinkText: 'till',
|
||||||
noEventsText: 'Inga händelser att visa',
|
noEventsText: 'Inga händelser att visa',
|
||||||
|
closeHint: 'Stäng',
|
||||||
|
timeHint: 'Klockan',
|
||||||
|
eventHint: 'Händelse',
|
||||||
};
|
};
|
||||||
|
|
||||||
return sv;
|
return sv;
|
||||||
|
29
InvenTree/InvenTree/static/fullcalendar/locales/ta-in.js
Normal file
29
InvenTree/InvenTree/static/fullcalendar/locales/ta-in.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
FullCalendar.globalLocales.push(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var taIn = {
|
||||||
|
code: 'ta-in',
|
||||||
|
week: {
|
||||||
|
dow: 1, // Monday is the first day of the week.
|
||||||
|
doy: 4, // The week that contains Jan 4th is the first week of the year.
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
prev: 'முந்தைய',
|
||||||
|
next: 'அடுத்தது',
|
||||||
|
today: 'இன்று',
|
||||||
|
month: 'மாதம்',
|
||||||
|
week: 'வாரம்',
|
||||||
|
day: 'நாள்',
|
||||||
|
list: 'தினசரி அட்டவணை',
|
||||||
|
},
|
||||||
|
weekText: 'வாரம்',
|
||||||
|
allDayText: 'நாள் முழுவதும்',
|
||||||
|
moreLinkText: function(n) {
|
||||||
|
return '+ மேலும் ' + n
|
||||||
|
},
|
||||||
|
noEventsText: 'காண்பிக்க நிகழ்வுகள் இல்லை',
|
||||||
|
};
|
||||||
|
|
||||||
|
return taIn;
|
||||||
|
|
||||||
|
}());
|
@ -1,11 +1,12 @@
|
|||||||
|
|
||||||
/* classes attached to <body> */
|
/* classes attached to <body> */
|
||||||
|
/* TODO: make fc-event selector work when calender in shadow DOM */
|
||||||
.fc-not-allowed,
|
.fc-not-allowed,
|
||||||
.fc-not-allowed .fc-event { /* override events' custom cursors */
|
.fc-not-allowed .fc-event { /* override events' custom cursors */
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: not attached to body. attached to specific els. move */
|
||||||
.fc-unselectable {
|
.fc-unselectable {
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
@ -367,10 +368,6 @@ When it's NOT activated, the fc-button classes won't even be in the DOM.
|
|||||||
/* for most browsers, if a height isn't set on the table, can't do liquid-height within cells */
|
/* for most browsers, if a height isn't set on the table, can't do liquid-height within cells */
|
||||||
/* serves as a min-height. harmless */
|
/* serves as a min-height. harmless */
|
||||||
}
|
}
|
||||||
.fc .fc-scrollgrid-section-liquid {
|
|
||||||
height: auto
|
|
||||||
|
|
||||||
}
|
|
||||||
.fc .fc-scrollgrid-section-liquid > td {
|
.fc .fc-scrollgrid-section-liquid > td {
|
||||||
height: 100%; /* better than `auto`, for firefox */
|
height: 100%; /* better than `auto`, for firefox */
|
||||||
}
|
}
|
||||||
@ -394,9 +391,8 @@ When it's NOT activated, the fc-button classes won't even be in the DOM.
|
|||||||
.fc .fc-scrollgrid-section-sticky > * {
|
.fc .fc-scrollgrid-section-sticky > * {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
background: var(--fc-page-bg-color, #fff);
|
background: var(--fc-page-bg-color, #fff);
|
||||||
position: -webkit-sticky;
|
|
||||||
position: sticky;
|
position: sticky;
|
||||||
z-index: 2; /* TODO: var */
|
z-index: 3; /* TODO: var */
|
||||||
/* TODO: box-shadow when sticking */
|
/* TODO: box-shadow when sticking */
|
||||||
}
|
}
|
||||||
.fc .fc-scrollgrid-section-header.fc-scrollgrid-section-sticky > * {
|
.fc .fc-scrollgrid-section-header.fc-scrollgrid-section-sticky > * {
|
||||||
@ -411,7 +407,6 @@ When it's NOT activated, the fc-button classes won't even be in the DOM.
|
|||||||
margin-bottom: -1px;
|
margin-bottom: -1px;
|
||||||
}
|
}
|
||||||
.fc-sticky { /* no .fc wrap because used as child of body */
|
.fc-sticky { /* no .fc wrap because used as child of body */
|
||||||
position: -webkit-sticky;
|
|
||||||
position: sticky;
|
position: sticky;
|
||||||
}
|
}
|
||||||
.fc .fc-view-harness {
|
.fc .fc-view-harness {
|
||||||
@ -535,14 +530,17 @@ a.fc-event:hover {
|
|||||||
bottom: -20px;
|
bottom: -20px;
|
||||||
}
|
}
|
||||||
/* selecting (always TOUCH) */
|
/* selecting (always TOUCH) */
|
||||||
|
/* OR, focused by tab-index */
|
||||||
|
/* (TODO: maybe not the best focus-styling for .fc-daygrid-dot-event) */
|
||||||
/* ---------------------------------------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------------------------------------- */
|
||||||
.fc-event-selected {
|
.fc-event-selected,
|
||||||
|
.fc-event:focus {
|
||||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2)
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2)
|
||||||
|
|
||||||
/* expand hit area (subclasses should expand) */
|
/* expand hit area (subclasses should expand) */
|
||||||
|
|
||||||
}
|
}
|
||||||
.fc-event-selected:before {
|
.fc-event-selected:before, .fc-event:focus:before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
@ -551,12 +549,13 @@ a.fc-event:hover {
|
|||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
.fc-event-selected {
|
.fc-event-selected,
|
||||||
|
.fc-event:focus {
|
||||||
|
|
||||||
/* dimmer effect */
|
/* dimmer effect */
|
||||||
|
|
||||||
}
|
}
|
||||||
.fc-event-selected:after {
|
.fc-event-selected:after, .fc-event:focus:after {
|
||||||
content: "";
|
content: "";
|
||||||
background: rgba(0, 0, 0, 0.25);
|
background: rgba(0, 0, 0, 0.25);
|
||||||
background: var(--fc-event-selected-overlay-color, rgba(0, 0, 0, 0.25));
|
background: var(--fc-event-selected-overlay-color, rgba(0, 0, 0, 0.25));
|
||||||
@ -635,38 +634,33 @@ A HORIZONTAL event
|
|||||||
.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end {
|
.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end {
|
||||||
cursor: w-resize;
|
cursor: w-resize;
|
||||||
left: -4px;
|
left: -4px;
|
||||||
left: calc(var(--fc-event-resizer-thickness, 8px) / -2);
|
left: calc(-0.5 * var(--fc-event-resizer-thickness, 8px));
|
||||||
}
|
}
|
||||||
.fc-direction-ltr .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end,
|
.fc-direction-ltr .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end,
|
||||||
.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-start {
|
.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-start {
|
||||||
cursor: e-resize;
|
cursor: e-resize;
|
||||||
right: -4px;
|
right: -4px;
|
||||||
right: calc(var(--fc-event-resizer-thickness, 8px) / -2);
|
right: calc(-0.5 * var(--fc-event-resizer-thickness, 8px));
|
||||||
}
|
}
|
||||||
/* resizers for TOUCH */
|
/* resizers for TOUCH */
|
||||||
.fc-h-event.fc-event-selected .fc-event-resizer {
|
.fc-h-event.fc-event-selected .fc-event-resizer {
|
||||||
top: 50%;
|
top: 50%;
|
||||||
margin-top: -4px;
|
margin-top: -4px;
|
||||||
margin-top: calc(var(--fc-event-resizer-dot-total-width, 8px) / -2);
|
margin-top: calc(-0.5 * var(--fc-event-resizer-dot-total-width, 8px));
|
||||||
}
|
}
|
||||||
.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-start,
|
.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-start,
|
||||||
.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-end {
|
.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-end {
|
||||||
left: -4px;
|
left: -4px;
|
||||||
left: calc(var(--fc-event-resizer-dot-total-width, 8px) / -2);
|
left: calc(-0.5 * var(--fc-event-resizer-dot-total-width, 8px));
|
||||||
}
|
}
|
||||||
.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-end,
|
.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-end,
|
||||||
.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-start {
|
.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-start {
|
||||||
right: -4px;
|
right: -4px;
|
||||||
right: calc(var(--fc-event-resizer-dot-total-width, 8px) / -2);
|
right: calc(-0.5 * var(--fc-event-resizer-dot-total-width, 8px));
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--fc-daygrid-event-dot-width: 8px;
|
|
||||||
}
|
}
|
||||||
.fc .fc-popover {
|
.fc .fc-popover {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
top: 0; /* for when not positioned yet */
|
z-index: 9999;
|
||||||
box-shadow: 0 2px 6px rgba(0,0,0,.15);
|
box-shadow: 0 2px 6px rgba(0,0,0,.15);
|
||||||
}
|
}
|
||||||
.fc .fc-popover-header {
|
.fc .fc-popover-header {
|
||||||
@ -694,6 +688,11 @@ A HORIZONTAL event
|
|||||||
background: rgba(208, 208, 208, 0.3);
|
background: rgba(208, 208, 208, 0.3);
|
||||||
background: var(--fc-neutral-bg-color, rgba(208, 208, 208, 0.3));
|
background: var(--fc-neutral-bg-color, rgba(208, 208, 208, 0.3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--fc-daygrid-event-dot-width: 8px;
|
||||||
|
}
|
||||||
/* help things clear margins of inner content */
|
/* help things clear margins of inner content */
|
||||||
.fc-daygrid-day-frame,
|
.fc-daygrid-day-frame,
|
||||||
.fc-daygrid-day-events,
|
.fc-daygrid-day-events,
|
||||||
@ -814,8 +813,12 @@ A HORIZONTAL event
|
|||||||
}
|
}
|
||||||
.fc .fc-daygrid-day-bottom {
|
.fc .fc-daygrid-day-bottom {
|
||||||
font-size: .85em;
|
font-size: .85em;
|
||||||
margin: 2px 3px 0;
|
padding: 2px 3px 0
|
||||||
}
|
}
|
||||||
|
.fc .fc-daygrid-day-bottom:before {
|
||||||
|
content: "";
|
||||||
|
clear: both;
|
||||||
|
display: table; }
|
||||||
.fc .fc-daygrid-more-link {
|
.fc .fc-daygrid-more-link {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
@ -843,9 +846,6 @@ A HORIZONTAL event
|
|||||||
/* popover */
|
/* popover */
|
||||||
|
|
||||||
}
|
}
|
||||||
.fc .fc-more-popover {
|
|
||||||
z-index: 8;
|
|
||||||
}
|
|
||||||
.fc .fc-more-popover .fc-popover-body {
|
.fc .fc-more-popover .fc-popover-body {
|
||||||
min-width: 220px;
|
min-width: 220px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
@ -1139,14 +1139,14 @@ A VERTICAL event
|
|||||||
min-height: 100%; /* liquid-hack is below */
|
min-height: 100%; /* liquid-hack is below */
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.fc-liquid-hack .fc-timegrid-col-frame {
|
.fc-media-screen.fc-liquid-hack .fc-timegrid-col-frame {
|
||||||
height: auto;
|
height: auto;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
.fc-media-screen .fc-timegrid-cols {
|
.fc-media-screen .fc-timegrid-cols {
|
||||||
position: absolute; /* no z-index. children will decide and go above slots */
|
position: absolute; /* no z-index. children will decide and go above slots */
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -1165,9 +1165,6 @@ A VERTICAL event
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
.fc-media-screen .fc-timegrid-event-harness {
|
|
||||||
position: absolute; /* top/left/right/bottom will all be set by JS */
|
|
||||||
}
|
|
||||||
.fc {
|
.fc {
|
||||||
|
|
||||||
/* bg */
|
/* bg */
|
||||||
@ -1211,18 +1208,30 @@ A VERTICAL event
|
|||||||
.fc-direction-rtl .fc-timegrid-col-events {
|
.fc-direction-rtl .fc-timegrid-col-events {
|
||||||
margin: 0 2px 0 2.5%;
|
margin: 0 2px 0 2.5%;
|
||||||
}
|
}
|
||||||
|
.fc-timegrid-event-harness {
|
||||||
|
position: absolute /* top/left/right/bottom will all be set by JS */
|
||||||
|
}
|
||||||
|
.fc-timegrid-event-harness > .fc-timegrid-event {
|
||||||
|
position: absolute; /* absolute WITHIN the harness */
|
||||||
|
top: 0; /* for when not yet positioned */
|
||||||
|
bottom: 0; /* " */
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
.fc-timegrid-event-harness-inset .fc-timegrid-event,
|
.fc-timegrid-event-harness-inset .fc-timegrid-event,
|
||||||
.fc-timegrid-event.fc-event-mirror {
|
.fc-timegrid-event.fc-event-mirror,
|
||||||
|
.fc-timegrid-more-link {
|
||||||
box-shadow: 0px 0px 0px 1px #fff;
|
box-shadow: 0px 0px 0px 1px #fff;
|
||||||
box-shadow: 0px 0px 0px 1px var(--fc-page-bg-color, #fff);
|
box-shadow: 0px 0px 0px 1px var(--fc-page-bg-color, #fff);
|
||||||
}
|
}
|
||||||
.fc-timegrid-event { /* events need to be root */
|
.fc-timegrid-event,
|
||||||
|
.fc-timegrid-more-link { /* events need to be root */
|
||||||
font-size: .85em;
|
font-size: .85em;
|
||||||
|
|
||||||
font-size: var(--fc-small-font-size, .85em);
|
font-size: var(--fc-small-font-size, .85em);
|
||||||
border-radius: 3px
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.fc-timegrid-event { /* events need to be root */
|
||||||
|
margin-bottom: 1px /* give some space from bottom */
|
||||||
}
|
}
|
||||||
.fc-timegrid-event .fc-event-main {
|
.fc-timegrid-event .fc-event-main {
|
||||||
padding: 1px 1px 0;
|
padding: 1px 1px 0;
|
||||||
@ -1233,24 +1242,37 @@ A VERTICAL event
|
|||||||
font-size: var(--fc-small-font-size, .85em);
|
font-size: var(--fc-small-font-size, .85em);
|
||||||
margin-bottom: 1px;
|
margin-bottom: 1px;
|
||||||
}
|
}
|
||||||
.fc-timegrid-event-condensed .fc-event-main-frame {
|
.fc-timegrid-event-short .fc-event-main-frame {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.fc-timegrid-event-condensed .fc-event-time:after {
|
.fc-timegrid-event-short .fc-event-time:after {
|
||||||
content: '\00a0-\00a0'; /* dash surrounded by non-breaking spaces */
|
content: '\00a0-\00a0'; /* dash surrounded by non-breaking spaces */
|
||||||
}
|
}
|
||||||
.fc-timegrid-event-condensed .fc-event-title {
|
.fc-timegrid-event-short .fc-event-title {
|
||||||
font-size: .85em;
|
font-size: .85em;
|
||||||
font-size: var(--fc-small-font-size, .85em)
|
font-size: var(--fc-small-font-size, .85em)
|
||||||
}
|
}
|
||||||
.fc-media-screen .fc-timegrid-event {
|
.fc-timegrid-more-link { /* does NOT inherit from fc-timegrid-event */
|
||||||
position: absolute; /* absolute WITHIN the harness */
|
position: absolute;
|
||||||
top: 0;
|
z-index: 9999; /* hack */
|
||||||
bottom: 1px; /* stay away from bottom slot line */
|
color: inherit;
|
||||||
left: 0;
|
color: var(--fc-more-link-text-color, inherit);
|
||||||
|
background: #d0d0d0;
|
||||||
|
background: var(--fc-more-link-bg-color, #d0d0d0);
|
||||||
|
cursor: pointer;
|
||||||
|
margin-bottom: 1px; /* match space below fc-timegrid-event */
|
||||||
|
}
|
||||||
|
.fc-timegrid-more-link-inner { /* has fc-sticky */
|
||||||
|
padding: 3px 2px;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.fc-direction-ltr .fc-timegrid-more-link {
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
.fc-direction-rtl .fc-timegrid-more-link {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
.fc {
|
.fc {
|
||||||
|
|
||||||
/* line */
|
/* line */
|
||||||
@ -1336,12 +1358,28 @@ A VERTICAL event
|
|||||||
border-right: 0;
|
border-right: 0;
|
||||||
}
|
}
|
||||||
.fc .fc-list-sticky .fc-list-day > * { /* the cells */
|
.fc .fc-list-sticky .fc-list-day > * { /* the cells */
|
||||||
position: -webkit-sticky;
|
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
background: var(--fc-page-bg-color, #fff); /* for when headers are styled to be transparent and sticky */
|
background: var(--fc-page-bg-color, #fff); /* for when headers are styled to be transparent and sticky */
|
||||||
}
|
}
|
||||||
|
.fc {
|
||||||
|
|
||||||
|
/* only exists for aria reasons, hide for non-screen-readers */
|
||||||
|
|
||||||
|
}
|
||||||
|
.fc .fc-list-table thead {
|
||||||
|
position: absolute;
|
||||||
|
left: -10000px;
|
||||||
|
}
|
||||||
|
.fc {
|
||||||
|
|
||||||
|
/* the table's border-style:hidden gets confused by hidden thead. force-hide top border of first cell */
|
||||||
|
|
||||||
|
}
|
||||||
|
.fc .fc-list-table tbody > tr:first-child th {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
.fc .fc-list-table th {
|
.fc .fc-list-table th {
|
||||||
padding: 0; /* uses an inner-wrapper instead... */
|
padding: 0; /* uses an inner-wrapper instead... */
|
||||||
}
|
}
|
||||||
@ -1427,3 +1465,31 @@ A VERTICAL event
|
|||||||
color: inherit; /* natural color for navlinks */
|
color: inherit; /* natural color for navlinks */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.fc-theme-bootstrap5 a:not([href]) {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-theme-bootstrap5 .fc-list,
|
||||||
|
.fc-theme-bootstrap5 .fc-scrollgrid,
|
||||||
|
.fc-theme-bootstrap5 td,
|
||||||
|
.fc-theme-bootstrap5 th {
|
||||||
|
border: 1px solid var(--bs-gray-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-theme-bootstrap5 {
|
||||||
|
|
||||||
|
/* HACK: reapply core styles after highe-precedence border statement above */
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-theme-bootstrap5 .fc-scrollgrid {
|
||||||
|
border-right-width: 0;
|
||||||
|
border-bottom-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-theme-bootstrap5-shaded {
|
||||||
|
background-color: var(--bs-gray-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
1
InvenTree/InvenTree/static/fullcalendar/main.min.css
vendored
Normal file
1
InvenTree/InvenTree/static/fullcalendar/main.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
6
InvenTree/InvenTree/static/fullcalendar/main.min.js
vendored
Normal file
6
InvenTree/InvenTree/static/fullcalendar/main.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -107,14 +107,9 @@ function inventreeDocReady() {
|
|||||||
|
|
||||||
// Callback to launch the 'About' window
|
// Callback to launch the 'About' window
|
||||||
$('#launch-about').click(function() {
|
$('#launch-about').click(function() {
|
||||||
var modal = $('#modal-about');
|
launchModalForm(`/about/`, {
|
||||||
|
no_post: true,
|
||||||
modal.modal({
|
|
||||||
backdrop: 'static',
|
|
||||||
keyboard: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modal.modal('show');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Callback to launch the 'Database Stats' window
|
// Callback to launch the 'Database Stats' window
|
||||||
@ -126,8 +121,6 @@ function inventreeDocReady() {
|
|||||||
|
|
||||||
// Initialize clipboard-buttons
|
// Initialize clipboard-buttons
|
||||||
attachClipboard('.clip-btn');
|
attachClipboard('.clip-btn');
|
||||||
attachClipboard('.clip-btn', 'modal-about');
|
|
||||||
attachClipboard('.clip-btn-version', 'modal-about', 'about-copy-text');
|
|
||||||
|
|
||||||
// Generate brand-icons
|
// Generate brand-icons
|
||||||
$('.brand-icon').each(function(i, obj) {
|
$('.brand-icon').each(function(i, obj) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"""Unit tests for the main web views."""
|
"""Unit tests for the main web views."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
@ -42,17 +41,3 @@ class ViewTests(InvenTreeTestCase):
|
|||||||
self.assertIn("<div id='detail-panels'>", content)
|
self.assertIn("<div id='detail-panels'>", content)
|
||||||
|
|
||||||
# TODO: In future, run the javascript and ensure that the panels get created!
|
# TODO: In future, run the javascript and ensure that the panels get created!
|
||||||
|
|
||||||
def test_js_load(self):
|
|
||||||
"""Test that the required javascript files are loaded correctly."""
|
|
||||||
# Change this number as more javascript files are added to the index page
|
|
||||||
N_SCRIPT_FILES = 40
|
|
||||||
|
|
||||||
content = self.get_index_page()
|
|
||||||
|
|
||||||
# Extract all required javascript files from the index page content
|
|
||||||
script_files = re.findall("<script type='text\\/javascript' src=\"([^\"]*)\"><\\/script>", content)
|
|
||||||
|
|
||||||
self.assertEqual(len(script_files), N_SCRIPT_FILES)
|
|
||||||
|
|
||||||
# TODO: Request the javascript files from the server, and ensure they are correcty loaded
|
|
||||||
|
@ -31,7 +31,7 @@ from stock.urls import stock_urls
|
|||||||
from users.api import user_urls
|
from users.api import user_urls
|
||||||
|
|
||||||
from .api import InfoView, NotFoundView
|
from .api import InfoView, NotFoundView
|
||||||
from .views import (AppearanceSelectView, CurrencyRefreshView,
|
from .views import (AboutView, AppearanceSelectView, CurrencyRefreshView,
|
||||||
CustomConnectionsView, CustomEmailView,
|
CustomConnectionsView, CustomEmailView,
|
||||||
CustomPasswordResetFromKeyView,
|
CustomPasswordResetFromKeyView,
|
||||||
CustomSessionDeleteOtherView, CustomSessionDeleteView,
|
CustomSessionDeleteOtherView, CustomSessionDeleteView,
|
||||||
@ -150,6 +150,7 @@ frontendpatterns = [
|
|||||||
re_path(r'^notifications/', include(notifications_urls)),
|
re_path(r'^notifications/', include(notifications_urls)),
|
||||||
re_path(r'^search/', SearchView.as_view(), name='search'),
|
re_path(r'^search/', SearchView.as_view(), name='search'),
|
||||||
re_path(r'^settings/', include(settings_urls)),
|
re_path(r'^settings/', include(settings_urls)),
|
||||||
|
re_path(r'^about/', AboutView.as_view(), name='about'),
|
||||||
re_path(r'^stats/', DatabaseStatsView.as_view(), name='stats'),
|
re_path(r'^stats/', DatabaseStatsView.as_view(), name='stats'),
|
||||||
|
|
||||||
# admin sites
|
# admin sites
|
||||||
|
@ -8,8 +8,10 @@ import json
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.auth import password_validation
|
||||||
from django.contrib.auth.mixins import (LoginRequiredMixin,
|
from django.contrib.auth.mixins import (LoginRequiredMixin,
|
||||||
PermissionRequiredMixin)
|
PermissionRequiredMixin)
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
|
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
@ -540,6 +542,8 @@ class SetPasswordView(AjaxUpdateView):
|
|||||||
|
|
||||||
p1 = request.POST.get('enter_password', '')
|
p1 = request.POST.get('enter_password', '')
|
||||||
p2 = request.POST.get('confirm_password', '')
|
p2 = request.POST.get('confirm_password', '')
|
||||||
|
old_password = request.POST.get('old_password', '')
|
||||||
|
user = self.request.user
|
||||||
|
|
||||||
if valid:
|
if valid:
|
||||||
# Passwords must match
|
# Passwords must match
|
||||||
@ -548,20 +552,28 @@ class SetPasswordView(AjaxUpdateView):
|
|||||||
error = _('Password fields must match')
|
error = _('Password fields must match')
|
||||||
form.add_error('enter_password', error)
|
form.add_error('enter_password', error)
|
||||||
form.add_error('confirm_password', error)
|
form.add_error('confirm_password', error)
|
||||||
|
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
data = {
|
if valid:
|
||||||
'form_valid': valid
|
# Old password must be correct
|
||||||
}
|
|
||||||
|
if not user.check_password(old_password):
|
||||||
|
form.add_error('old_password', _('Wrong password provided'))
|
||||||
|
valid = False
|
||||||
|
|
||||||
if valid:
|
if valid:
|
||||||
user = self.request.user
|
try:
|
||||||
|
# Validate password
|
||||||
|
password_validation.validate_password(p1, user)
|
||||||
|
|
||||||
user.set_password(p1)
|
# Update the user
|
||||||
user.save()
|
user.set_password(p1)
|
||||||
|
user.save()
|
||||||
|
except ValidationError as error:
|
||||||
|
form.add_error('confirm_password', str(error))
|
||||||
|
valid = False
|
||||||
|
|
||||||
return self.renderJsonResponse(request, form, data=data)
|
return self.renderJsonResponse(request, form, data={'form_valid': valid})
|
||||||
|
|
||||||
|
|
||||||
class IndexView(TemplateView):
|
class IndexView(TemplateView):
|
||||||
@ -738,6 +750,13 @@ class DatabaseStatsView(AjaxView):
|
|||||||
ajax_form_title = _("System Information")
|
ajax_form_title = _("System Information")
|
||||||
|
|
||||||
|
|
||||||
|
class AboutView(AjaxView):
|
||||||
|
"""A view for displaying InvenTree version information"""
|
||||||
|
|
||||||
|
ajax_template_name = "about.html"
|
||||||
|
ajax_form_title = _("About InvenTree")
|
||||||
|
|
||||||
|
|
||||||
class NotificationsView(TemplateView):
|
class NotificationsView(TemplateView):
|
||||||
"""View for showing notifications."""
|
"""View for showing notifications."""
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
from django.urls import include, re_path
|
from django.urls import include, re_path
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import filters, generics
|
from rest_framework import filters
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
@ -13,6 +13,7 @@ from InvenTree.api import AttachmentMixin, APIDownloadMixin, ListCreateDestroyAP
|
|||||||
from InvenTree.helpers import str2bool, isNull, DownloadFile
|
from InvenTree.helpers import str2bool, isNull, DownloadFile
|
||||||
from InvenTree.filters import InvenTreeOrderingFilter
|
from InvenTree.filters import InvenTreeOrderingFilter
|
||||||
from InvenTree.status_codes import BuildStatus
|
from InvenTree.status_codes import BuildStatus
|
||||||
|
from InvenTree.mixins import CreateAPI, RetrieveUpdateDestroyAPI, ListCreateAPI
|
||||||
|
|
||||||
import build.admin
|
import build.admin
|
||||||
import build.serializers
|
import build.serializers
|
||||||
@ -65,7 +66,7 @@ class BuildFilter(rest_filters.FilterSet):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class BuildList(APIDownloadMixin, generics.ListCreateAPIView):
|
class BuildList(APIDownloadMixin, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of Build objects.
|
"""API endpoint for accessing a list of Build objects.
|
||||||
|
|
||||||
- GET: Return list of objects (with filters)
|
- GET: Return list of objects (with filters)
|
||||||
@ -200,7 +201,7 @@ class BuildList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
return self.serializer_class(*args, **kwargs)
|
return self.serializer_class(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class BuildDetail(generics.RetrieveUpdateDestroyAPIView):
|
class BuildDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a Build object."""
|
"""API endpoint for detail view of a Build object."""
|
||||||
|
|
||||||
queryset = Build.objects.all()
|
queryset = Build.objects.all()
|
||||||
@ -219,7 +220,7 @@ class BuildDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return super().destroy(request, *args, **kwargs)
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class BuildUnallocate(generics.CreateAPIView):
|
class BuildUnallocate(CreateAPI):
|
||||||
"""API endpoint for unallocating stock items from a build order.
|
"""API endpoint for unallocating stock items from a build order.
|
||||||
|
|
||||||
- The BuildOrder object is specified by the URL
|
- The BuildOrder object is specified by the URL
|
||||||
@ -263,7 +264,7 @@ class BuildOrderContextMixin:
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
class BuildOutputCreate(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildOutputCreate(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for creating new build output(s)."""
|
"""API endpoint for creating new build output(s)."""
|
||||||
|
|
||||||
queryset = Build.objects.none()
|
queryset = Build.objects.none()
|
||||||
@ -271,7 +272,7 @@ class BuildOutputCreate(BuildOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = build.serializers.BuildOutputCreateSerializer
|
serializer_class = build.serializers.BuildOutputCreateSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildOutputComplete(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildOutputComplete(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for completing build outputs."""
|
"""API endpoint for completing build outputs."""
|
||||||
|
|
||||||
queryset = Build.objects.none()
|
queryset = Build.objects.none()
|
||||||
@ -279,7 +280,7 @@ class BuildOutputComplete(BuildOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = build.serializers.BuildOutputCompleteSerializer
|
serializer_class = build.serializers.BuildOutputCompleteSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildOutputDelete(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildOutputDelete(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for deleting multiple build outputs."""
|
"""API endpoint for deleting multiple build outputs."""
|
||||||
|
|
||||||
def get_serializer_context(self):
|
def get_serializer_context(self):
|
||||||
@ -295,7 +296,7 @@ class BuildOutputDelete(BuildOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = build.serializers.BuildOutputDeleteSerializer
|
serializer_class = build.serializers.BuildOutputDeleteSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildFinish(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildFinish(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for marking a build as finished (completed)."""
|
"""API endpoint for marking a build as finished (completed)."""
|
||||||
|
|
||||||
queryset = Build.objects.none()
|
queryset = Build.objects.none()
|
||||||
@ -303,7 +304,7 @@ class BuildFinish(BuildOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = build.serializers.BuildCompleteSerializer
|
serializer_class = build.serializers.BuildCompleteSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildAutoAllocate(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildAutoAllocate(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for 'automatically' allocating stock against a build order.
|
"""API endpoint for 'automatically' allocating stock against a build order.
|
||||||
|
|
||||||
- Only looks at 'untracked' parts
|
- Only looks at 'untracked' parts
|
||||||
@ -317,7 +318,7 @@ class BuildAutoAllocate(BuildOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = build.serializers.BuildAutoAllocationSerializer
|
serializer_class = build.serializers.BuildAutoAllocationSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildAllocate(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildAllocate(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to allocate stock items to a build order.
|
"""API endpoint to allocate stock items to a build order.
|
||||||
|
|
||||||
- The BuildOrder object is specified by the URL
|
- The BuildOrder object is specified by the URL
|
||||||
@ -333,21 +334,21 @@ class BuildAllocate(BuildOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = build.serializers.BuildAllocationSerializer
|
serializer_class = build.serializers.BuildAllocationSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildCancel(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildCancel(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for cancelling a BuildOrder."""
|
"""API endpoint for cancelling a BuildOrder."""
|
||||||
|
|
||||||
queryset = Build.objects.all()
|
queryset = Build.objects.all()
|
||||||
serializer_class = build.serializers.BuildCancelSerializer
|
serializer_class = build.serializers.BuildCancelSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildItemDetail(generics.RetrieveUpdateDestroyAPIView):
|
class BuildItemDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a BuildItem object."""
|
"""API endpoint for detail view of a BuildItem object."""
|
||||||
|
|
||||||
queryset = BuildItem.objects.all()
|
queryset = BuildItem.objects.all()
|
||||||
serializer_class = build.serializers.BuildItemSerializer
|
serializer_class = build.serializers.BuildItemSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildItemList(generics.ListCreateAPIView):
|
class BuildItemList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of BuildItem objects.
|
"""API endpoint for accessing a list of BuildItem objects.
|
||||||
|
|
||||||
- GET: Return list of objects
|
- GET: Return list of objects
|
||||||
@ -442,7 +443,7 @@ class BuildAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class BuildAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
|
class BuildAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for a BuildOrderAttachment object."""
|
"""Detail endpoint for a BuildOrderAttachment object."""
|
||||||
|
|
||||||
queryset = BuildOrderAttachment.objects.all()
|
queryset = BuildOrderAttachment.objects.all()
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
{% block thumbnail %}
|
{% block thumbnail %}
|
||||||
<img class="part-thumb"
|
<img class="part-thumb"
|
||||||
{% if build.part.image %}
|
{% if build.part.image %}
|
||||||
src="{{ build.part.image.url }}"
|
src="{{ build.part.image.preview.url }}"
|
||||||
{% else %}
|
{% else %}
|
||||||
src="{% static 'img/blank_image.png' %}"
|
src="{% static 'img/blank_image.png' %}"
|
||||||
{% endif %}/>
|
{% endif %}/>
|
||||||
|
@ -9,7 +9,7 @@ from django.views.decorators.csrf import csrf_exempt
|
|||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from django_q.tasks import async_task
|
from django_q.tasks import async_task
|
||||||
from rest_framework import filters, generics, permissions, serializers
|
from rest_framework import filters, permissions, serializers
|
||||||
from rest_framework.exceptions import NotAcceptable, NotFound
|
from rest_framework.exceptions import NotAcceptable, NotFound
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
@ -18,6 +18,8 @@ import common.models
|
|||||||
import common.serializers
|
import common.serializers
|
||||||
from InvenTree.api import BulkDeleteMixin
|
from InvenTree.api import BulkDeleteMixin
|
||||||
from InvenTree.helpers import inheritors
|
from InvenTree.helpers import inheritors
|
||||||
|
from InvenTree.mixins import (CreateAPI, ListAPI, RetrieveAPI,
|
||||||
|
RetrieveUpdateAPI, RetrieveUpdateDestroyAPI)
|
||||||
from plugin.models import NotificationUserSetting
|
from plugin.models import NotificationUserSetting
|
||||||
from plugin.serializers import NotificationUserSettingSerializer
|
from plugin.serializers import NotificationUserSettingSerializer
|
||||||
|
|
||||||
@ -97,7 +99,7 @@ class WebhookView(CsrfExemptMixin, APIView):
|
|||||||
raise NotFound()
|
raise NotFound()
|
||||||
|
|
||||||
|
|
||||||
class SettingsList(generics.ListAPIView):
|
class SettingsList(ListAPI):
|
||||||
"""Generic ListView for settings.
|
"""Generic ListView for settings.
|
||||||
|
|
||||||
This is inheritted by all list views for settings.
|
This is inheritted by all list views for settings.
|
||||||
@ -145,7 +147,7 @@ class GlobalSettingsPermissions(permissions.BasePermission):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class GlobalSettingsDetail(generics.RetrieveUpdateAPIView):
|
class GlobalSettingsDetail(RetrieveUpdateAPI):
|
||||||
"""Detail view for an individual "global setting" object.
|
"""Detail view for an individual "global setting" object.
|
||||||
|
|
||||||
- User must have 'staff' status to view / edit
|
- User must have 'staff' status to view / edit
|
||||||
@ -203,7 +205,7 @@ class UserSettingsPermissions(permissions.BasePermission):
|
|||||||
return user == obj.user
|
return user == obj.user
|
||||||
|
|
||||||
|
|
||||||
class UserSettingsDetail(generics.RetrieveUpdateAPIView):
|
class UserSettingsDetail(RetrieveUpdateAPI):
|
||||||
"""Detail view for an individual "user setting" object.
|
"""Detail view for an individual "user setting" object.
|
||||||
|
|
||||||
- User can only view / edit settings their own settings objects
|
- User can only view / edit settings their own settings objects
|
||||||
@ -245,7 +247,7 @@ class NotificationUserSettingsList(SettingsList):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class NotificationUserSettingsDetail(generics.RetrieveUpdateAPIView):
|
class NotificationUserSettingsDetail(RetrieveUpdateAPI):
|
||||||
"""Detail view for an individual "notification user setting" object.
|
"""Detail view for an individual "notification user setting" object.
|
||||||
|
|
||||||
- User can only view / edit settings their own settings objects
|
- User can only view / edit settings their own settings objects
|
||||||
@ -259,7 +261,7 @@ class NotificationUserSettingsDetail(generics.RetrieveUpdateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NotificationList(BulkDeleteMixin, generics.ListAPIView):
|
class NotificationList(BulkDeleteMixin, ListAPI):
|
||||||
"""List view for all notifications of the current user."""
|
"""List view for all notifications of the current user."""
|
||||||
|
|
||||||
queryset = common.models.NotificationMessage.objects.all()
|
queryset = common.models.NotificationMessage.objects.all()
|
||||||
@ -310,7 +312,7 @@ class NotificationList(BulkDeleteMixin, generics.ListAPIView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class NotificationDetail(generics.RetrieveUpdateDestroyAPIView):
|
class NotificationDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail view for an individual notification object.
|
"""Detail view for an individual notification object.
|
||||||
|
|
||||||
- User can only view / delete their own notification objects
|
- User can only view / delete their own notification objects
|
||||||
@ -323,7 +325,7 @@ class NotificationDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NotificationReadEdit(generics.CreateAPIView):
|
class NotificationReadEdit(CreateAPI):
|
||||||
"""General API endpoint to manipulate read state of a notification."""
|
"""General API endpoint to manipulate read state of a notification."""
|
||||||
|
|
||||||
queryset = common.models.NotificationMessage.objects.all()
|
queryset = common.models.NotificationMessage.objects.all()
|
||||||
@ -360,7 +362,7 @@ class NotificationUnread(NotificationReadEdit):
|
|||||||
target = False
|
target = False
|
||||||
|
|
||||||
|
|
||||||
class NotificationReadAll(generics.RetrieveAPIView):
|
class NotificationReadAll(RetrieveAPI):
|
||||||
"""API endpoint to mark all notifications as read."""
|
"""API endpoint to mark all notifications as read."""
|
||||||
|
|
||||||
queryset = common.models.NotificationMessage.objects.all()
|
queryset = common.models.NotificationMessage.objects.all()
|
||||||
|
@ -1008,6 +1008,23 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
|||||||
'validator': InvenTree.validators.validate_part_name_format
|
'validator': InvenTree.validators.validate_part_name_format
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'LABEL_ENABLE': {
|
||||||
|
'name': _('Enable label printing'),
|
||||||
|
'description': _('Enable label printing from the web interface'),
|
||||||
|
'default': True,
|
||||||
|
'validator': bool,
|
||||||
|
},
|
||||||
|
|
||||||
|
'LABEL_DPI': {
|
||||||
|
'name': _('Label Image DPI'),
|
||||||
|
'description': _('DPI resolution when generating image files to supply to label printing plugins'),
|
||||||
|
'default': 300,
|
||||||
|
'validator': [
|
||||||
|
int,
|
||||||
|
MinValueValidator(100),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
'REPORT_ENABLE': {
|
'REPORT_ENABLE': {
|
||||||
'name': _('Enable Reports'),
|
'name': _('Enable Reports'),
|
||||||
'description': _('Enable generation of reports'),
|
'description': _('Enable generation of reports'),
|
||||||
@ -1389,12 +1406,6 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
|
|||||||
'default': True,
|
'default': True,
|
||||||
'validator': bool,
|
'validator': bool,
|
||||||
},
|
},
|
||||||
'LABEL_ENABLE': {
|
|
||||||
'name': _('Enable label printing'),
|
|
||||||
'description': _('Enable label printing from the web interface'),
|
|
||||||
'default': True,
|
|
||||||
'validator': bool,
|
|
||||||
},
|
|
||||||
|
|
||||||
"LABEL_INLINE": {
|
"LABEL_INLINE": {
|
||||||
'name': _('Inline label display'),
|
'name': _('Inline label display'),
|
||||||
|
@ -5,10 +5,11 @@ from django.urls import include, re_path
|
|||||||
|
|
||||||
from django_filters import rest_framework as rest_filters
|
from django_filters import rest_framework as rest_filters
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import filters, generics
|
from rest_framework import filters
|
||||||
|
|
||||||
from InvenTree.api import AttachmentMixin, ListCreateDestroyAPIView
|
from InvenTree.api import AttachmentMixin, ListCreateDestroyAPIView
|
||||||
from InvenTree.helpers import str2bool
|
from InvenTree.helpers import str2bool
|
||||||
|
from InvenTree.mixins import ListCreateAPI, RetrieveUpdateDestroyAPI
|
||||||
|
|
||||||
from .models import (Company, ManufacturerPart, ManufacturerPartAttachment,
|
from .models import (Company, ManufacturerPart, ManufacturerPartAttachment,
|
||||||
ManufacturerPartParameter, SupplierPart,
|
ManufacturerPartParameter, SupplierPart,
|
||||||
@ -20,7 +21,7 @@ from .serializers import (CompanySerializer,
|
|||||||
SupplierPriceBreakSerializer)
|
SupplierPriceBreakSerializer)
|
||||||
|
|
||||||
|
|
||||||
class CompanyList(generics.ListCreateAPIView):
|
class CompanyList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of Company objects.
|
"""API endpoint for accessing a list of Company objects.
|
||||||
|
|
||||||
Provides two methods:
|
Provides two methods:
|
||||||
@ -67,7 +68,7 @@ class CompanyList(generics.ListCreateAPIView):
|
|||||||
ordering = 'name'
|
ordering = 'name'
|
||||||
|
|
||||||
|
|
||||||
class CompanyDetail(generics.RetrieveUpdateDestroyAPIView):
|
class CompanyDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail of a single Company object."""
|
"""API endpoint for detail of a single Company object."""
|
||||||
|
|
||||||
queryset = Company.objects.all()
|
queryset = Company.objects.all()
|
||||||
@ -146,7 +147,7 @@ class ManufacturerPartList(ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerPartDetail(generics.RetrieveUpdateDestroyAPIView):
|
class ManufacturerPartDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of ManufacturerPart object.
|
"""API endpoint for detail view of ManufacturerPart object.
|
||||||
|
|
||||||
- GET: Retrieve detail view
|
- GET: Retrieve detail view
|
||||||
@ -173,7 +174,7 @@ class ManufacturerPartAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerPartAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
|
class ManufacturerPartAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpooint for ManufacturerPartAttachment model."""
|
"""Detail endpooint for ManufacturerPartAttachment model."""
|
||||||
|
|
||||||
queryset = ManufacturerPartAttachment.objects.all()
|
queryset = ManufacturerPartAttachment.objects.all()
|
||||||
@ -246,7 +247,7 @@ class ManufacturerPartParameterList(ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerPartParameterDetail(generics.RetrieveUpdateDestroyAPIView):
|
class ManufacturerPartParameterDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of ManufacturerPartParameter model."""
|
"""API endpoint for detail view of ManufacturerPartParameter model."""
|
||||||
|
|
||||||
queryset = ManufacturerPartParameter.objects.all()
|
queryset = ManufacturerPartParameter.objects.all()
|
||||||
@ -347,7 +348,7 @@ class SupplierPartList(ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SupplierPartDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SupplierPartDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of SupplierPart object.
|
"""API endpoint for detail view of SupplierPart object.
|
||||||
|
|
||||||
- GET: Retrieve detail view
|
- GET: Retrieve detail view
|
||||||
@ -362,7 +363,7 @@ class SupplierPartDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SupplierPriceBreakList(generics.ListCreateAPIView):
|
class SupplierPriceBreakList(ListCreateAPI):
|
||||||
"""API endpoint for list view of SupplierPriceBreak object.
|
"""API endpoint for list view of SupplierPriceBreak object.
|
||||||
|
|
||||||
- GET: Retrieve list of SupplierPriceBreak objects
|
- GET: Retrieve list of SupplierPriceBreak objects
|
||||||
@ -381,7 +382,7 @@ class SupplierPriceBreakList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SupplierPriceBreakDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SupplierPriceBreakDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for SupplierPriceBreak object."""
|
"""Detail endpoint for SupplierPriceBreak object."""
|
||||||
|
|
||||||
queryset = SupplierPriceBreak.objects.all()
|
queryset = SupplierPriceBreak.objects.all()
|
||||||
|
@ -127,7 +127,10 @@ class Company(models.Model):
|
|||||||
upload_to=rename_company_image,
|
upload_to=rename_company_image,
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
variations={'thumbnail': (128, 128)},
|
variations={
|
||||||
|
'thumbnail': (128, 128),
|
||||||
|
'preview': (256, 256),
|
||||||
|
},
|
||||||
delete_orphans=True,
|
delete_orphans=True,
|
||||||
verbose_name=_('Image'),
|
verbose_name=_('Image'),
|
||||||
)
|
)
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
<div class='dropzone part-thumb-container' id='company-thumb'>
|
<div class='dropzone part-thumb-container' id='company-thumb'>
|
||||||
<img class="part-thumb" id='company-image'
|
<img class="part-thumb" id='company-image'
|
||||||
{% if company.image %}
|
{% if company.image %}
|
||||||
src="{{ company.image.url }}"
|
src="{{ company.image.preview.url }}"
|
||||||
{% else %}
|
{% else %}
|
||||||
src="{% static 'img/blank_image.png' %}"
|
src="{% static 'img/blank_image.png' %}"
|
||||||
{% endif %}/>
|
{% endif %}/>
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
{% block thumbnail %}
|
{% block thumbnail %}
|
||||||
<img class='part-thumb'
|
<img class='part-thumb'
|
||||||
{% if part.part.image %}
|
{% if part.part.image %}
|
||||||
src='{{ part.part.image.url }}'
|
src='{{ part.part.image.preview.url }}'
|
||||||
{% else %}
|
{% else %}
|
||||||
src="{% static 'img/blank_image.png' %}"
|
src="{% static 'img/blank_image.png' %}"
|
||||||
{% endif %}/>
|
{% endif %}/>
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
{% block thumbnail %}
|
{% block thumbnail %}
|
||||||
<img class='part-thumb'
|
<img class='part-thumb'
|
||||||
{% if part.part.image %}
|
{% if part.part.image %}
|
||||||
src='{{ part.part.image.url }}'
|
src='{{ part.part.image.preview.url }}'
|
||||||
{% else %}
|
{% else %}
|
||||||
src="{% static 'img/blank_image.png' %}"
|
src="{% static 'img/blank_image.png' %}"
|
||||||
{% endif %}/>
|
{% endif %}/>
|
||||||
|
@ -6,11 +6,12 @@ from django.http import HttpResponse, JsonResponse
|
|||||||
from django.urls import include, re_path
|
from django.urls import include, re_path
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import filters, generics
|
from rest_framework import filters
|
||||||
from rest_framework.exceptions import NotFound
|
from rest_framework.exceptions import NotFound
|
||||||
|
|
||||||
import common.models
|
import common.models
|
||||||
import InvenTree.helpers
|
import InvenTree.helpers
|
||||||
|
from InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateDestroyAPI
|
||||||
from InvenTree.tasks import offload_task
|
from InvenTree.tasks import offload_task
|
||||||
from part.models import Part
|
from part.models import Part
|
||||||
from plugin.base.label import label as plugin_label
|
from plugin.base.label import label as plugin_label
|
||||||
@ -22,7 +23,7 @@ from .serializers import (PartLabelSerializer, StockItemLabelSerializer,
|
|||||||
StockLocationLabelSerializer)
|
StockLocationLabelSerializer)
|
||||||
|
|
||||||
|
|
||||||
class LabelListView(generics.ListAPIView):
|
class LabelListView(ListAPI):
|
||||||
"""Generic API class for label templates."""
|
"""Generic API class for label templates."""
|
||||||
|
|
||||||
filter_backends = [
|
filter_backends = [
|
||||||
@ -275,14 +276,14 @@ class StockItemLabelList(LabelListView, StockItemLabelMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class StockItemLabelDetail(generics.RetrieveUpdateDestroyAPIView):
|
class StockItemLabelDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single StockItemLabel object."""
|
"""API endpoint for a single StockItemLabel object."""
|
||||||
|
|
||||||
queryset = StockItemLabel.objects.all()
|
queryset = StockItemLabel.objects.all()
|
||||||
serializer_class = StockItemLabelSerializer
|
serializer_class = StockItemLabelSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemLabelPrint(generics.RetrieveAPIView, StockItemLabelMixin, LabelPrintMixin):
|
class StockItemLabelPrint(RetrieveAPI, StockItemLabelMixin, LabelPrintMixin):
|
||||||
"""API endpoint for printing a StockItemLabel object."""
|
"""API endpoint for printing a StockItemLabel object."""
|
||||||
|
|
||||||
queryset = StockItemLabel.objects.all()
|
queryset = StockItemLabel.objects.all()
|
||||||
@ -391,14 +392,14 @@ class StockLocationLabelList(LabelListView, StockLocationLabelMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class StockLocationLabelDetail(generics.RetrieveUpdateDestroyAPIView):
|
class StockLocationLabelDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single StockLocationLabel object."""
|
"""API endpoint for a single StockLocationLabel object."""
|
||||||
|
|
||||||
queryset = StockLocationLabel.objects.all()
|
queryset = StockLocationLabel.objects.all()
|
||||||
serializer_class = StockLocationLabelSerializer
|
serializer_class = StockLocationLabelSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockLocationLabelPrint(generics.RetrieveAPIView, StockLocationLabelMixin, LabelPrintMixin):
|
class StockLocationLabelPrint(RetrieveAPI, StockLocationLabelMixin, LabelPrintMixin):
|
||||||
"""API endpoint for printing a StockLocationLabel object."""
|
"""API endpoint for printing a StockLocationLabel object."""
|
||||||
|
|
||||||
queryset = StockLocationLabel.objects.all()
|
queryset = StockLocationLabel.objects.all()
|
||||||
@ -483,14 +484,14 @@ class PartLabelList(LabelListView, PartLabelMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class PartLabelDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartLabelDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single PartLabel object."""
|
"""API endpoint for a single PartLabel object."""
|
||||||
|
|
||||||
queryset = PartLabel.objects.all()
|
queryset = PartLabel.objects.all()
|
||||||
serializer_class = PartLabelSerializer
|
serializer_class = PartLabelSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartLabelPrint(generics.RetrieveAPIView, PartLabelMixin, LabelPrintMixin):
|
class PartLabelPrint(RetrieveAPI, PartLabelMixin, LabelPrintMixin):
|
||||||
"""API endpoint for printing a PartLabel object."""
|
"""API endpoint for printing a PartLabel object."""
|
||||||
|
|
||||||
queryset = PartLabel.objects.all()
|
queryset = PartLabel.objects.all()
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@ from django.db.models import F, Q
|
|||||||
from django.urls import include, path, re_path
|
from django.urls import include, path, re_path
|
||||||
|
|
||||||
from django_filters import rest_framework as rest_filters
|
from django_filters import rest_framework as rest_filters
|
||||||
from rest_framework import filters, generics, status
|
from rest_framework import filters, status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
import order.models as models
|
import order.models as models
|
||||||
@ -14,6 +14,8 @@ from InvenTree.api import (APIDownloadMixin, AttachmentMixin,
|
|||||||
ListCreateDestroyAPIView)
|
ListCreateDestroyAPIView)
|
||||||
from InvenTree.filters import InvenTreeOrderingFilter
|
from InvenTree.filters import InvenTreeOrderingFilter
|
||||||
from InvenTree.helpers import DownloadFile, str2bool
|
from InvenTree.helpers import DownloadFile, str2bool
|
||||||
|
from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI,
|
||||||
|
RetrieveUpdateAPI, RetrieveUpdateDestroyAPI)
|
||||||
from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus
|
from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus
|
||||||
from order.admin import (PurchaseOrderLineItemResource, PurchaseOrderResource,
|
from order.admin import (PurchaseOrderLineItemResource, PurchaseOrderResource,
|
||||||
SalesOrderResource)
|
SalesOrderResource)
|
||||||
@ -101,7 +103,7 @@ class PurchaseOrderFilter(rest_filters.FilterSet):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderList(APIDownloadMixin, generics.ListCreateAPIView):
|
class PurchaseOrderList(APIDownloadMixin, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PurchaseOrder objects.
|
"""API endpoint for accessing a list of PurchaseOrder objects.
|
||||||
|
|
||||||
- GET: Return list of PurchaseOrder objects (with filters)
|
- GET: Return list of PurchaseOrder objects (with filters)
|
||||||
@ -114,7 +116,7 @@ class PurchaseOrderList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""Save user information on create."""
|
"""Save user information on create."""
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=self.clean_data(request.data))
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
item = serializer.save()
|
item = serializer.save()
|
||||||
@ -254,7 +256,7 @@ class PurchaseOrderList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
ordering = '-creation_date'
|
ordering = '-creation_date'
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PurchaseOrderDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a PurchaseOrder object."""
|
"""API endpoint for detail view of a PurchaseOrder object."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrder.objects.all()
|
queryset = models.PurchaseOrder.objects.all()
|
||||||
@ -304,7 +306,7 @@ class PurchaseOrderContextMixin:
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderCancel(PurchaseOrderContextMixin, generics.CreateAPIView):
|
class PurchaseOrderCancel(PurchaseOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to 'cancel' a purchase order.
|
"""API endpoint to 'cancel' a purchase order.
|
||||||
|
|
||||||
The purchase order must be in a state which can be cancelled
|
The purchase order must be in a state which can be cancelled
|
||||||
@ -315,7 +317,7 @@ class PurchaseOrderCancel(PurchaseOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = serializers.PurchaseOrderCancelSerializer
|
serializer_class = serializers.PurchaseOrderCancelSerializer
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderComplete(PurchaseOrderContextMixin, generics.CreateAPIView):
|
class PurchaseOrderComplete(PurchaseOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to 'complete' a purchase order."""
|
"""API endpoint to 'complete' a purchase order."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrder.objects.all()
|
queryset = models.PurchaseOrder.objects.all()
|
||||||
@ -323,7 +325,7 @@ class PurchaseOrderComplete(PurchaseOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = serializers.PurchaseOrderCompleteSerializer
|
serializer_class = serializers.PurchaseOrderCompleteSerializer
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderIssue(PurchaseOrderContextMixin, generics.CreateAPIView):
|
class PurchaseOrderIssue(PurchaseOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to 'complete' a purchase order."""
|
"""API endpoint to 'complete' a purchase order."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrder.objects.all()
|
queryset = models.PurchaseOrder.objects.all()
|
||||||
@ -331,7 +333,7 @@ class PurchaseOrderIssue(PurchaseOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = serializers.PurchaseOrderIssueSerializer
|
serializer_class = serializers.PurchaseOrderIssueSerializer
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderMetadata(generics.RetrieveUpdateAPIView):
|
class PurchaseOrderMetadata(RetrieveUpdateAPI):
|
||||||
"""API endpoint for viewing / updating PurchaseOrder metadata."""
|
"""API endpoint for viewing / updating PurchaseOrder metadata."""
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
@ -341,7 +343,7 @@ class PurchaseOrderMetadata(generics.RetrieveUpdateAPIView):
|
|||||||
queryset = models.PurchaseOrder.objects.all()
|
queryset = models.PurchaseOrder.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderReceive(PurchaseOrderContextMixin, generics.CreateAPIView):
|
class PurchaseOrderReceive(PurchaseOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to receive stock items against a purchase order.
|
"""API endpoint to receive stock items against a purchase order.
|
||||||
|
|
||||||
- The purchase order is specified in the URL.
|
- The purchase order is specified in the URL.
|
||||||
@ -405,7 +407,7 @@ class PurchaseOrderLineItemFilter(rest_filters.FilterSet):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderLineItemList(APIDownloadMixin, generics.ListCreateAPIView):
|
class PurchaseOrderLineItemList(APIDownloadMixin, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PurchaseOrderLineItem objects.
|
"""API endpoint for accessing a list of PurchaseOrderLineItem objects.
|
||||||
|
|
||||||
- GET: Return a list of PurchaseOrder Line Item objects
|
- GET: Return a list of PurchaseOrder Line Item objects
|
||||||
@ -499,7 +501,7 @@ class PurchaseOrderLineItemList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderLineItemDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PurchaseOrderLineItemDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail API endpoint for PurchaseOrderLineItem object."""
|
"""Detail API endpoint for PurchaseOrderLineItem object."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrderLineItem.objects.all()
|
queryset = models.PurchaseOrderLineItem.objects.all()
|
||||||
@ -514,14 +516,14 @@ class PurchaseOrderLineItemDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderExtraLineList(GeneralExtraLineList, generics.ListCreateAPIView):
|
class PurchaseOrderExtraLineList(GeneralExtraLineList, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PurchaseOrderExtraLine objects."""
|
"""API endpoint for accessing a list of PurchaseOrderExtraLine objects."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrderExtraLine.objects.all()
|
queryset = models.PurchaseOrderExtraLine.objects.all()
|
||||||
serializer_class = serializers.PurchaseOrderExtraLineSerializer
|
serializer_class = serializers.PurchaseOrderExtraLineSerializer
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderExtraLineDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PurchaseOrderExtraLineDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a PurchaseOrderExtraLine object."""
|
"""API endpoint for detail view of a PurchaseOrderExtraLine object."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrderExtraLine.objects.all()
|
queryset = models.PurchaseOrderExtraLine.objects.all()
|
||||||
@ -543,14 +545,14 @@ class SalesOrderAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for SalesOrderAttachment."""
|
"""Detail endpoint for SalesOrderAttachment."""
|
||||||
|
|
||||||
queryset = models.SalesOrderAttachment.objects.all()
|
queryset = models.SalesOrderAttachment.objects.all()
|
||||||
serializer_class = serializers.SalesOrderAttachmentSerializer
|
serializer_class = serializers.SalesOrderAttachmentSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderList(APIDownloadMixin, generics.ListCreateAPIView):
|
class SalesOrderList(APIDownloadMixin, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of SalesOrder objects.
|
"""API endpoint for accessing a list of SalesOrder objects.
|
||||||
|
|
||||||
- GET: Return list of SalesOrder objects (with filters)
|
- GET: Return list of SalesOrder objects (with filters)
|
||||||
@ -562,7 +564,7 @@ class SalesOrderList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""Save user information on create."""
|
"""Save user information on create."""
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=self.clean_data(request.data))
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
item = serializer.save()
|
item = serializer.save()
|
||||||
@ -695,7 +697,7 @@ class SalesOrderList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
ordering = '-creation_date'
|
ordering = '-creation_date'
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a SalesOrder object."""
|
"""API endpoint for detail view of a SalesOrder object."""
|
||||||
|
|
||||||
queryset = models.SalesOrder.objects.all()
|
queryset = models.SalesOrder.objects.all()
|
||||||
@ -754,7 +756,7 @@ class SalesOrderLineItemFilter(rest_filters.FilterSet):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderLineItemList(generics.ListCreateAPIView):
|
class SalesOrderLineItemList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of SalesOrderLineItem objects."""
|
"""API endpoint for accessing a list of SalesOrderLineItem objects."""
|
||||||
|
|
||||||
queryset = models.SalesOrderLineItem.objects.all()
|
queryset = models.SalesOrderLineItem.objects.all()
|
||||||
@ -818,21 +820,21 @@ class SalesOrderLineItemList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderExtraLineList(GeneralExtraLineList, generics.ListCreateAPIView):
|
class SalesOrderExtraLineList(GeneralExtraLineList, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of SalesOrderExtraLine objects."""
|
"""API endpoint for accessing a list of SalesOrderExtraLine objects."""
|
||||||
|
|
||||||
queryset = models.SalesOrderExtraLine.objects.all()
|
queryset = models.SalesOrderExtraLine.objects.all()
|
||||||
serializer_class = serializers.SalesOrderExtraLineSerializer
|
serializer_class = serializers.SalesOrderExtraLineSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderExtraLineDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderExtraLineDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a SalesOrderExtraLine object."""
|
"""API endpoint for detail view of a SalesOrderExtraLine object."""
|
||||||
|
|
||||||
queryset = models.SalesOrderExtraLine.objects.all()
|
queryset = models.SalesOrderExtraLine.objects.all()
|
||||||
serializer_class = serializers.SalesOrderExtraLineSerializer
|
serializer_class = serializers.SalesOrderExtraLineSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderLineItemDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderLineItemDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a SalesOrderLineItem object."""
|
"""API endpoint for detail view of a SalesOrderLineItem object."""
|
||||||
|
|
||||||
queryset = models.SalesOrderLineItem.objects.all()
|
queryset = models.SalesOrderLineItem.objects.all()
|
||||||
@ -864,21 +866,21 @@ class SalesOrderContextMixin:
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderCancel(SalesOrderContextMixin, generics.CreateAPIView):
|
class SalesOrderCancel(SalesOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to cancel a SalesOrder"""
|
"""API endpoint to cancel a SalesOrder"""
|
||||||
|
|
||||||
queryset = models.SalesOrder.objects.all()
|
queryset = models.SalesOrder.objects.all()
|
||||||
serializer_class = serializers.SalesOrderCancelSerializer
|
serializer_class = serializers.SalesOrderCancelSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderComplete(SalesOrderContextMixin, generics.CreateAPIView):
|
class SalesOrderComplete(SalesOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for manually marking a SalesOrder as "complete"."""
|
"""API endpoint for manually marking a SalesOrder as "complete"."""
|
||||||
|
|
||||||
queryset = models.SalesOrder.objects.all()
|
queryset = models.SalesOrder.objects.all()
|
||||||
serializer_class = serializers.SalesOrderCompleteSerializer
|
serializer_class = serializers.SalesOrderCompleteSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderMetadata(generics.RetrieveUpdateAPIView):
|
class SalesOrderMetadata(RetrieveUpdateAPI):
|
||||||
"""API endpoint for viewing / updating SalesOrder metadata."""
|
"""API endpoint for viewing / updating SalesOrder metadata."""
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
@ -888,14 +890,14 @@ class SalesOrderMetadata(generics.RetrieveUpdateAPIView):
|
|||||||
queryset = models.SalesOrder.objects.all()
|
queryset = models.SalesOrder.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAllocateSerials(SalesOrderContextMixin, generics.CreateAPIView):
|
class SalesOrderAllocateSerials(SalesOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to allocation stock items against a SalesOrder, by specifying serial numbers."""
|
"""API endpoint to allocation stock items against a SalesOrder, by specifying serial numbers."""
|
||||||
|
|
||||||
queryset = models.SalesOrder.objects.none()
|
queryset = models.SalesOrder.objects.none()
|
||||||
serializer_class = serializers.SalesOrderSerialAllocationSerializer
|
serializer_class = serializers.SalesOrderSerialAllocationSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAllocate(SalesOrderContextMixin, generics.CreateAPIView):
|
class SalesOrderAllocate(SalesOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to allocate stock items against a SalesOrder.
|
"""API endpoint to allocate stock items against a SalesOrder.
|
||||||
|
|
||||||
- The SalesOrder is specified in the URL
|
- The SalesOrder is specified in the URL
|
||||||
@ -906,14 +908,14 @@ class SalesOrderAllocate(SalesOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = serializers.SalesOrderShipmentAllocationSerializer
|
serializer_class = serializers.SalesOrderShipmentAllocationSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAllocationDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderAllocationDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detali view of a SalesOrderAllocation object."""
|
"""API endpoint for detali view of a SalesOrderAllocation object."""
|
||||||
|
|
||||||
queryset = models.SalesOrderAllocation.objects.all()
|
queryset = models.SalesOrderAllocation.objects.all()
|
||||||
serializer_class = serializers.SalesOrderAllocationSerializer
|
serializer_class = serializers.SalesOrderAllocationSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAllocationList(generics.ListAPIView):
|
class SalesOrderAllocationList(ListAPI):
|
||||||
"""API endpoint for listing SalesOrderAllocation objects."""
|
"""API endpoint for listing SalesOrderAllocation objects."""
|
||||||
|
|
||||||
queryset = models.SalesOrderAllocation.objects.all()
|
queryset = models.SalesOrderAllocation.objects.all()
|
||||||
@ -1017,7 +1019,7 @@ class SalesOrderShipmentFilter(rest_filters.FilterSet):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderShipmentList(generics.ListCreateAPIView):
|
class SalesOrderShipmentList(ListCreateAPI):
|
||||||
"""API list endpoint for SalesOrderShipment model."""
|
"""API list endpoint for SalesOrderShipment model."""
|
||||||
|
|
||||||
queryset = models.SalesOrderShipment.objects.all()
|
queryset = models.SalesOrderShipment.objects.all()
|
||||||
@ -1029,14 +1031,14 @@ class SalesOrderShipmentList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderShipmentDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderShipmentDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API detail endpooint for SalesOrderShipment model."""
|
"""API detail endpooint for SalesOrderShipment model."""
|
||||||
|
|
||||||
queryset = models.SalesOrderShipment.objects.all()
|
queryset = models.SalesOrderShipment.objects.all()
|
||||||
serializer_class = serializers.SalesOrderShipmentSerializer
|
serializer_class = serializers.SalesOrderShipmentSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderShipmentComplete(generics.CreateAPIView):
|
class SalesOrderShipmentComplete(CreateAPI):
|
||||||
"""API endpoint for completing (shipping) a SalesOrderShipment."""
|
"""API endpoint for completing (shipping) a SalesOrderShipment."""
|
||||||
|
|
||||||
queryset = models.SalesOrderShipment.objects.all()
|
queryset = models.SalesOrderShipment.objects.all()
|
||||||
@ -1072,7 +1074,7 @@ class PurchaseOrderAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
|
class PurchaseOrderAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for a PurchaseOrderAttachment."""
|
"""Detail endpoint for a PurchaseOrderAttachment."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrderAttachment.objects.all()
|
queryset = models.PurchaseOrderAttachment.objects.all()
|
||||||
|
@ -14,7 +14,7 @@ from django_filters.rest_framework import DjangoFilterBackend
|
|||||||
from djmoney.contrib.exchange.exceptions import MissingRate
|
from djmoney.contrib.exchange.exceptions import MissingRate
|
||||||
from djmoney.contrib.exchange.models import convert_money
|
from djmoney.contrib.exchange.models import convert_money
|
||||||
from djmoney.money import Money
|
from djmoney.money import Money
|
||||||
from rest_framework import filters, generics, serializers, status
|
from rest_framework import filters, serializers, status
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
@ -25,6 +25,9 @@ from company.models import Company, ManufacturerPart, SupplierPart
|
|||||||
from InvenTree.api import (APIDownloadMixin, AttachmentMixin,
|
from InvenTree.api import (APIDownloadMixin, AttachmentMixin,
|
||||||
ListCreateDestroyAPIView)
|
ListCreateDestroyAPIView)
|
||||||
from InvenTree.helpers import DownloadFile, increment, isNull, str2bool
|
from InvenTree.helpers import DownloadFile, increment, isNull, str2bool
|
||||||
|
from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI, RetrieveAPI,
|
||||||
|
RetrieveUpdateAPI, RetrieveUpdateDestroyAPI,
|
||||||
|
UpdateAPI)
|
||||||
from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus,
|
from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus,
|
||||||
SalesOrderStatus)
|
SalesOrderStatus)
|
||||||
from part.admin import PartResource
|
from part.admin import PartResource
|
||||||
@ -39,7 +42,7 @@ from .models import (BomItem, BomItemSubstitute, Part, PartAttachment,
|
|||||||
PartTestTemplate)
|
PartTestTemplate)
|
||||||
|
|
||||||
|
|
||||||
class CategoryList(generics.ListCreateAPIView):
|
class CategoryList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PartCategory objects.
|
"""API endpoint for accessing a list of PartCategory objects.
|
||||||
|
|
||||||
- GET: Return a list of PartCategory objects
|
- GET: Return a list of PartCategory objects
|
||||||
@ -155,7 +158,7 @@ class CategoryList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
|
class CategoryDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a single PartCategory object."""
|
"""API endpoint for detail view of a single PartCategory object."""
|
||||||
|
|
||||||
serializer_class = part_serializers.CategorySerializer
|
serializer_class = part_serializers.CategorySerializer
|
||||||
@ -175,8 +178,11 @@ class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
"""Perform 'update' function and mark this part as 'starred' (or not)"""
|
"""Perform 'update' function and mark this part as 'starred' (or not)"""
|
||||||
if 'starred' in request.data:
|
# Clean up input data
|
||||||
starred = str2bool(request.data.get('starred', False))
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
if 'starred' in data:
|
||||||
|
starred = str2bool(data.get('starred', False))
|
||||||
|
|
||||||
self.get_object().set_starred(request.user, starred)
|
self.get_object().set_starred(request.user, starred)
|
||||||
|
|
||||||
@ -185,7 +191,7 @@ class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
class CategoryMetadata(generics.RetrieveUpdateAPIView):
|
class CategoryMetadata(RetrieveUpdateAPI):
|
||||||
"""API endpoint for viewing / updating PartCategory metadata."""
|
"""API endpoint for viewing / updating PartCategory metadata."""
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
@ -195,7 +201,7 @@ class CategoryMetadata(generics.RetrieveUpdateAPIView):
|
|||||||
queryset = PartCategory.objects.all()
|
queryset = PartCategory.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class CategoryParameterList(generics.ListCreateAPIView):
|
class CategoryParameterList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PartCategoryParameterTemplate objects.
|
"""API endpoint for accessing a list of PartCategoryParameterTemplate objects.
|
||||||
|
|
||||||
- GET: Return a list of PartCategoryParameterTemplate objects
|
- GET: Return a list of PartCategoryParameterTemplate objects
|
||||||
@ -236,14 +242,14 @@ class CategoryParameterList(generics.ListCreateAPIView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class CategoryParameterDetail(generics.RetrieveUpdateDestroyAPIView):
|
class CategoryParameterDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint fro the PartCategoryParameterTemplate model"""
|
"""Detail endpoint fro the PartCategoryParameterTemplate model"""
|
||||||
|
|
||||||
queryset = PartCategoryParameterTemplate.objects.all()
|
queryset = PartCategoryParameterTemplate.objects.all()
|
||||||
serializer_class = part_serializers.CategoryParameterTemplateSerializer
|
serializer_class = part_serializers.CategoryParameterTemplateSerializer
|
||||||
|
|
||||||
|
|
||||||
class CategoryTree(generics.ListAPIView):
|
class CategoryTree(ListAPI):
|
||||||
"""API endpoint for accessing a list of PartCategory objects ready for rendering a tree."""
|
"""API endpoint for accessing a list of PartCategory objects ready for rendering a tree."""
|
||||||
|
|
||||||
queryset = PartCategory.objects.all()
|
queryset = PartCategory.objects.all()
|
||||||
@ -258,14 +264,14 @@ class CategoryTree(generics.ListAPIView):
|
|||||||
ordering = ['level', 'name']
|
ordering = ['level', 'name']
|
||||||
|
|
||||||
|
|
||||||
class PartSalePriceDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartSalePriceDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for PartSellPriceBreak model."""
|
"""Detail endpoint for PartSellPriceBreak model."""
|
||||||
|
|
||||||
queryset = PartSellPriceBreak.objects.all()
|
queryset = PartSellPriceBreak.objects.all()
|
||||||
serializer_class = part_serializers.PartSalePriceSerializer
|
serializer_class = part_serializers.PartSalePriceSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartSalePriceList(generics.ListCreateAPIView):
|
class PartSalePriceList(ListCreateAPI):
|
||||||
"""API endpoint for list view of PartSalePriceBreak model."""
|
"""API endpoint for list view of PartSalePriceBreak model."""
|
||||||
|
|
||||||
queryset = PartSellPriceBreak.objects.all()
|
queryset = PartSellPriceBreak.objects.all()
|
||||||
@ -280,14 +286,14 @@ class PartSalePriceList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartInternalPriceDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartInternalPriceDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for PartInternalPriceBreak model."""
|
"""Detail endpoint for PartInternalPriceBreak model."""
|
||||||
|
|
||||||
queryset = PartInternalPriceBreak.objects.all()
|
queryset = PartInternalPriceBreak.objects.all()
|
||||||
serializer_class = part_serializers.PartInternalPriceSerializer
|
serializer_class = part_serializers.PartInternalPriceSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartInternalPriceList(generics.ListCreateAPIView):
|
class PartInternalPriceList(ListCreateAPI):
|
||||||
"""API endpoint for list view of PartInternalPriceBreak model."""
|
"""API endpoint for list view of PartInternalPriceBreak model."""
|
||||||
|
|
||||||
queryset = PartInternalPriceBreak.objects.all()
|
queryset = PartInternalPriceBreak.objects.all()
|
||||||
@ -318,21 +324,21 @@ class PartAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
|
class PartAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for PartAttachment model."""
|
"""Detail endpoint for PartAttachment model."""
|
||||||
|
|
||||||
queryset = PartAttachment.objects.all()
|
queryset = PartAttachment.objects.all()
|
||||||
serializer_class = part_serializers.PartAttachmentSerializer
|
serializer_class = part_serializers.PartAttachmentSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartTestTemplateDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartTestTemplateDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for PartTestTemplate model."""
|
"""Detail endpoint for PartTestTemplate model."""
|
||||||
|
|
||||||
queryset = PartTestTemplate.objects.all()
|
queryset = PartTestTemplate.objects.all()
|
||||||
serializer_class = part_serializers.PartTestTemplateSerializer
|
serializer_class = part_serializers.PartTestTemplateSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartTestTemplateList(generics.ListCreateAPIView):
|
class PartTestTemplateList(ListCreateAPI):
|
||||||
"""API endpoint for listing (and creating) a PartTestTemplate."""
|
"""API endpoint for listing (and creating) a PartTestTemplate."""
|
||||||
|
|
||||||
queryset = PartTestTemplate.objects.all()
|
queryset = PartTestTemplate.objects.all()
|
||||||
@ -372,7 +378,7 @@ class PartTestTemplateList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartThumbs(generics.ListAPIView):
|
class PartThumbs(ListAPI):
|
||||||
"""API endpoint for retrieving information on available Part thumbnails."""
|
"""API endpoint for retrieving information on available Part thumbnails."""
|
||||||
|
|
||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
@ -415,7 +421,7 @@ class PartThumbs(generics.ListAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartThumbsUpdate(generics.RetrieveUpdateAPIView):
|
class PartThumbsUpdate(RetrieveUpdateAPI):
|
||||||
"""API endpoint for updating Part thumbnails."""
|
"""API endpoint for updating Part thumbnails."""
|
||||||
|
|
||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
@ -426,7 +432,7 @@ class PartThumbsUpdate(generics.RetrieveUpdateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartScheduling(generics.RetrieveAPIView):
|
class PartScheduling(RetrieveAPI):
|
||||||
"""API endpoint for delivering "scheduling" information about a given part via the API.
|
"""API endpoint for delivering "scheduling" information about a given part via the API.
|
||||||
|
|
||||||
Returns a chronologically ordered list about future "scheduled" events,
|
Returns a chronologically ordered list about future "scheduled" events,
|
||||||
@ -560,7 +566,7 @@ class PartScheduling(generics.RetrieveAPIView):
|
|||||||
return Response(schedule)
|
return Response(schedule)
|
||||||
|
|
||||||
|
|
||||||
class PartMetadata(generics.RetrieveUpdateAPIView):
|
class PartMetadata(RetrieveUpdateAPI):
|
||||||
"""API endpoint for viewing / updating Part metadata."""
|
"""API endpoint for viewing / updating Part metadata."""
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
@ -570,7 +576,7 @@ class PartMetadata(generics.RetrieveUpdateAPIView):
|
|||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class PartSerialNumberDetail(generics.RetrieveAPIView):
|
class PartSerialNumberDetail(RetrieveAPI):
|
||||||
"""API endpoint for returning extra serial number information about a particular part."""
|
"""API endpoint for returning extra serial number information about a particular part."""
|
||||||
|
|
||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
@ -595,7 +601,7 @@ class PartSerialNumberDetail(generics.RetrieveAPIView):
|
|||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
|
|
||||||
class PartCopyBOM(generics.CreateAPIView):
|
class PartCopyBOM(CreateAPI):
|
||||||
"""API endpoint for duplicating a BOM."""
|
"""API endpoint for duplicating a BOM."""
|
||||||
|
|
||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
@ -613,7 +619,7 @@ class PartCopyBOM(generics.CreateAPIView):
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
class PartValidateBOM(generics.RetrieveUpdateAPIView):
|
class PartValidateBOM(RetrieveUpdateAPI):
|
||||||
"""API endpoint for 'validating' the BOM for a given Part."""
|
"""API endpoint for 'validating' the BOM for a given Part."""
|
||||||
|
|
||||||
class BOMValidateSerializer(serializers.ModelSerializer):
|
class BOMValidateSerializer(serializers.ModelSerializer):
|
||||||
@ -654,7 +660,10 @@ class PartValidateBOM(generics.RetrieveUpdateAPIView):
|
|||||||
|
|
||||||
partial = kwargs.pop('partial', False)
|
partial = kwargs.pop('partial', False)
|
||||||
|
|
||||||
serializer = self.get_serializer(part, data=request.data, partial=partial)
|
# Clean up input data before using it
|
||||||
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(part, data=data, partial=partial)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
part.validate_bom(request.user)
|
part.validate_bom(request.user)
|
||||||
@ -664,7 +673,7 @@ class PartValidateBOM(generics.RetrieveUpdateAPIView):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class PartDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a single Part object."""
|
"""API endpoint for detail view of a single Part object."""
|
||||||
|
|
||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
@ -721,8 +730,11 @@ class PartDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
|
|
||||||
- If the 'starred' field is provided, update the 'starred' status against current user
|
- If the 'starred' field is provided, update the 'starred' status against current user
|
||||||
"""
|
"""
|
||||||
if 'starred' in request.data:
|
# Clean input data
|
||||||
starred = str2bool(request.data.get('starred', False))
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
if 'starred' in data:
|
||||||
|
starred = str2bool(data.get('starred', False))
|
||||||
|
|
||||||
self.get_object().set_starred(request.user, starred)
|
self.get_object().set_starred(request.user, starred)
|
||||||
|
|
||||||
@ -874,7 +886,7 @@ class PartFilter(rest_filters.FilterSet):
|
|||||||
virtual = rest_filters.BooleanFilter()
|
virtual = rest_filters.BooleanFilter()
|
||||||
|
|
||||||
|
|
||||||
class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
class PartList(APIDownloadMixin, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of Part objects.
|
"""API endpoint for accessing a list of Part objects.
|
||||||
|
|
||||||
- GET: Return list of objects
|
- GET: Return list of objects
|
||||||
@ -1003,7 +1015,10 @@ class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
"""
|
"""
|
||||||
# TODO: Unit tests for this function!
|
# TODO: Unit tests for this function!
|
||||||
|
|
||||||
serializer = self.get_serializer(data=request.data)
|
# Clean up input data
|
||||||
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(data=data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
part = serializer.save()
|
part = serializer.save()
|
||||||
@ -1011,23 +1026,23 @@ class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
|
|
||||||
# Optionally copy templates from category or parent category
|
# Optionally copy templates from category or parent category
|
||||||
copy_templates = {
|
copy_templates = {
|
||||||
'main': str2bool(request.data.get('copy_category_templates', False)),
|
'main': str2bool(data.get('copy_category_templates', False)),
|
||||||
'parent': str2bool(request.data.get('copy_parent_templates', False))
|
'parent': str2bool(data.get('copy_parent_templates', False))
|
||||||
}
|
}
|
||||||
|
|
||||||
part.save(**{'add_category_templates': copy_templates})
|
part.save(**{'add_category_templates': copy_templates})
|
||||||
|
|
||||||
# Optionally copy data from another part (e.g. when duplicating)
|
# Optionally copy data from another part (e.g. when duplicating)
|
||||||
copy_from = request.data.get('copy_from', None)
|
copy_from = data.get('copy_from', None)
|
||||||
|
|
||||||
if copy_from is not None:
|
if copy_from is not None:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
original = Part.objects.get(pk=copy_from)
|
original = Part.objects.get(pk=copy_from)
|
||||||
|
|
||||||
copy_bom = str2bool(request.data.get('copy_bom', False))
|
copy_bom = str2bool(data.get('copy_bom', False))
|
||||||
copy_parameters = str2bool(request.data.get('copy_parameters', False))
|
copy_parameters = str2bool(data.get('copy_parameters', False))
|
||||||
copy_image = str2bool(request.data.get('copy_image', True))
|
copy_image = str2bool(data.get('copy_image', True))
|
||||||
|
|
||||||
# Copy image?
|
# Copy image?
|
||||||
if copy_image:
|
if copy_image:
|
||||||
@ -1046,12 +1061,12 @@ class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# Optionally create initial stock item
|
# Optionally create initial stock item
|
||||||
initial_stock = str2bool(request.data.get('initial_stock', False))
|
initial_stock = str2bool(data.get('initial_stock', False))
|
||||||
|
|
||||||
if initial_stock:
|
if initial_stock:
|
||||||
try:
|
try:
|
||||||
|
|
||||||
initial_stock_quantity = Decimal(request.data.get('initial_stock_quantity', ''))
|
initial_stock_quantity = Decimal(data.get('initial_stock_quantity', ''))
|
||||||
|
|
||||||
if initial_stock_quantity <= 0:
|
if initial_stock_quantity <= 0:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
@ -1062,7 +1077,7 @@ class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
'initial_stock_quantity': [_('Must be a valid quantity')],
|
'initial_stock_quantity': [_('Must be a valid quantity')],
|
||||||
})
|
})
|
||||||
|
|
||||||
initial_stock_location = request.data.get('initial_stock_location', None)
|
initial_stock_location = data.get('initial_stock_location', None)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
initial_stock_location = StockLocation.objects.get(pk=initial_stock_location)
|
initial_stock_location = StockLocation.objects.get(pk=initial_stock_location)
|
||||||
@ -1086,20 +1101,20 @@ class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
stock_item.save(user=request.user)
|
stock_item.save(user=request.user)
|
||||||
|
|
||||||
# Optionally add manufacturer / supplier data to the part
|
# Optionally add manufacturer / supplier data to the part
|
||||||
if part.purchaseable and str2bool(request.data.get('add_supplier_info', False)):
|
if part.purchaseable and str2bool(data.get('add_supplier_info', False)):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
manufacturer = Company.objects.get(pk=request.data.get('manufacturer', None))
|
manufacturer = Company.objects.get(pk=data.get('manufacturer', None))
|
||||||
except Exception:
|
except Exception:
|
||||||
manufacturer = None
|
manufacturer = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
supplier = Company.objects.get(pk=request.data.get('supplier', None))
|
supplier = Company.objects.get(pk=data.get('supplier', None))
|
||||||
except Exception:
|
except Exception:
|
||||||
supplier = None
|
supplier = None
|
||||||
|
|
||||||
mpn = str(request.data.get('MPN', '')).strip()
|
mpn = str(data.get('MPN', '')).strip()
|
||||||
sku = str(request.data.get('SKU', '')).strip()
|
sku = str(data.get('SKU', '')).strip()
|
||||||
|
|
||||||
# Construct a manufacturer part
|
# Construct a manufacturer part
|
||||||
if manufacturer or mpn:
|
if manufacturer or mpn:
|
||||||
@ -1347,7 +1362,7 @@ class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartRelatedList(generics.ListCreateAPIView):
|
class PartRelatedList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PartRelated objects."""
|
"""API endpoint for accessing a list of PartRelated objects."""
|
||||||
|
|
||||||
queryset = PartRelated.objects.all()
|
queryset = PartRelated.objects.all()
|
||||||
@ -1374,14 +1389,14 @@ class PartRelatedList(generics.ListCreateAPIView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class PartRelatedDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartRelatedDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for accessing detail view of a PartRelated object."""
|
"""API endpoint for accessing detail view of a PartRelated object."""
|
||||||
|
|
||||||
queryset = PartRelated.objects.all()
|
queryset = PartRelated.objects.all()
|
||||||
serializer_class = part_serializers.PartRelationSerializer
|
serializer_class = part_serializers.PartRelationSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartParameterTemplateList(generics.ListCreateAPIView):
|
class PartParameterTemplateList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PartParameterTemplate objects.
|
"""API endpoint for accessing a list of PartParameterTemplate objects.
|
||||||
|
|
||||||
- GET: Return list of PartParameterTemplate objects
|
- GET: Return list of PartParameterTemplate objects
|
||||||
@ -1441,14 +1456,14 @@ class PartParameterTemplateList(generics.ListCreateAPIView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class PartParameterTemplateDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartParameterTemplateDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for accessing the detail view for a PartParameterTemplate object"""
|
"""API endpoint for accessing the detail view for a PartParameterTemplate object"""
|
||||||
|
|
||||||
queryset = PartParameterTemplate.objects.all()
|
queryset = PartParameterTemplate.objects.all()
|
||||||
serializer_class = part_serializers.PartParameterTemplateSerializer
|
serializer_class = part_serializers.PartParameterTemplateSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartParameterList(generics.ListCreateAPIView):
|
class PartParameterList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PartParameter objects.
|
"""API endpoint for accessing a list of PartParameter objects.
|
||||||
|
|
||||||
- GET: Return list of PartParameter objects
|
- GET: Return list of PartParameter objects
|
||||||
@ -1468,7 +1483,7 @@ class PartParameterList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartParameterDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartParameterDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a single PartParameter object."""
|
"""API endpoint for detail view of a single PartParameter object."""
|
||||||
|
|
||||||
queryset = PartParameter.objects.all()
|
queryset = PartParameter.objects.all()
|
||||||
@ -1747,7 +1762,7 @@ class BomList(ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class BomImportUpload(generics.CreateAPIView):
|
class BomImportUpload(CreateAPI):
|
||||||
"""API endpoint for uploading a complete Bill of Materials.
|
"""API endpoint for uploading a complete Bill of Materials.
|
||||||
|
|
||||||
It is assumed that the BOM has been extracted from a file using the BomExtract endpoint.
|
It is assumed that the BOM has been extracted from a file using the BomExtract endpoint.
|
||||||
@ -1758,7 +1773,10 @@ class BomImportUpload(generics.CreateAPIView):
|
|||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""Custom create function to return the extracted data."""
|
"""Custom create function to return the extracted data."""
|
||||||
serializer = self.get_serializer(data=request.data)
|
# Clean up input data
|
||||||
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(data=data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
self.perform_create(serializer)
|
self.perform_create(serializer)
|
||||||
headers = self.get_success_headers(serializer.data)
|
headers = self.get_success_headers(serializer.data)
|
||||||
@ -1768,21 +1786,21 @@ class BomImportUpload(generics.CreateAPIView):
|
|||||||
return Response(data, status=status.HTTP_201_CREATED, headers=headers)
|
return Response(data, status=status.HTTP_201_CREATED, headers=headers)
|
||||||
|
|
||||||
|
|
||||||
class BomImportExtract(generics.CreateAPIView):
|
class BomImportExtract(CreateAPI):
|
||||||
"""API endpoint for extracting BOM data from a BOM file."""
|
"""API endpoint for extracting BOM data from a BOM file."""
|
||||||
|
|
||||||
queryset = Part.objects.none()
|
queryset = Part.objects.none()
|
||||||
serializer_class = part_serializers.BomImportExtractSerializer
|
serializer_class = part_serializers.BomImportExtractSerializer
|
||||||
|
|
||||||
|
|
||||||
class BomImportSubmit(generics.CreateAPIView):
|
class BomImportSubmit(CreateAPI):
|
||||||
"""API endpoint for submitting BOM data from a BOM file."""
|
"""API endpoint for submitting BOM data from a BOM file."""
|
||||||
|
|
||||||
queryset = BomItem.objects.none()
|
queryset = BomItem.objects.none()
|
||||||
serializer_class = part_serializers.BomImportSubmitSerializer
|
serializer_class = part_serializers.BomImportSubmitSerializer
|
||||||
|
|
||||||
|
|
||||||
class BomDetail(generics.RetrieveUpdateDestroyAPIView):
|
class BomDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a single BomItem object."""
|
"""API endpoint for detail view of a single BomItem object."""
|
||||||
|
|
||||||
queryset = BomItem.objects.all()
|
queryset = BomItem.objects.all()
|
||||||
@ -1798,7 +1816,7 @@ class BomDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class BomItemValidate(generics.UpdateAPIView):
|
class BomItemValidate(UpdateAPI):
|
||||||
"""API endpoint for validating a BomItem."""
|
"""API endpoint for validating a BomItem."""
|
||||||
|
|
||||||
class BomItemValidationSerializer(serializers.Serializer):
|
class BomItemValidationSerializer(serializers.Serializer):
|
||||||
@ -1812,11 +1830,13 @@ class BomItemValidate(generics.UpdateAPIView):
|
|||||||
"""Perform update request."""
|
"""Perform update request."""
|
||||||
partial = kwargs.pop('partial', False)
|
partial = kwargs.pop('partial', False)
|
||||||
|
|
||||||
valid = request.data.get('valid', False)
|
# Clean up input data
|
||||||
|
data = self.clean_data(request.data)
|
||||||
|
valid = data.get('valid', False)
|
||||||
|
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
|
|
||||||
serializer = self.get_serializer(instance, data=request.data, partial=partial)
|
serializer = self.get_serializer(instance, data=data, partial=partial)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
if type(instance) == BomItem:
|
if type(instance) == BomItem:
|
||||||
@ -1825,7 +1845,7 @@ class BomItemValidate(generics.UpdateAPIView):
|
|||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
class BomItemSubstituteList(generics.ListCreateAPIView):
|
class BomItemSubstituteList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of BomItemSubstitute objects."""
|
"""API endpoint for accessing a list of BomItemSubstitute objects."""
|
||||||
|
|
||||||
serializer_class = part_serializers.BomItemSubstituteSerializer
|
serializer_class = part_serializers.BomItemSubstituteSerializer
|
||||||
@ -1843,7 +1863,7 @@ class BomItemSubstituteList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class BomItemSubstituteDetail(generics.RetrieveUpdateDestroyAPIView):
|
class BomItemSubstituteDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a single BomItemSubstitute object."""
|
"""API endpoint for detail view of a single BomItemSubstitute object."""
|
||||||
|
|
||||||
queryset = BomItemSubstitute.objects.all()
|
queryset = BomItemSubstitute.objects.all()
|
||||||
|
@ -19,7 +19,7 @@ Relevant PRs:
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import OuterRef, Q
|
from django.db.models import F, FloatField, Func, OuterRef, Q, Subquery
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
|
|
||||||
from sql_util.utils import SubquerySum
|
from sql_util.utils import SubquerySum
|
||||||
@ -139,3 +139,22 @@ def variant_stock_query(reference: str = '', filter: Q = stock.models.StockItem.
|
|||||||
part__lft__gt=OuterRef(f'{reference}lft'),
|
part__lft__gt=OuterRef(f'{reference}lft'),
|
||||||
part__rght__lt=OuterRef(f'{reference}rght'),
|
part__rght__lt=OuterRef(f'{reference}rght'),
|
||||||
).filter(filter)
|
).filter(filter)
|
||||||
|
|
||||||
|
|
||||||
|
def annotate_variant_quantity(subquery: Q, reference: str = 'quantity'):
|
||||||
|
"""Create a subquery annotation for all variant part stock items on the given parent query
|
||||||
|
|
||||||
|
Args:
|
||||||
|
subquery: A 'variant_stock_query' Q object
|
||||||
|
reference: The relationship reference of the variant stock items from the current queryset
|
||||||
|
"""
|
||||||
|
|
||||||
|
return Coalesce(
|
||||||
|
Subquery(
|
||||||
|
subquery.annotate(
|
||||||
|
total=Func(F(reference), function='SUM', output_field=FloatField())
|
||||||
|
).values('total')
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
output_field=FloatField(),
|
||||||
|
)
|
||||||
|
@ -13,7 +13,7 @@ from django.contrib.auth.models import User
|
|||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import Q, Sum, UniqueConstraint
|
from django.db.models import ExpressionWrapper, F, Q, Sum, UniqueConstraint
|
||||||
from django.db.models.functions import Coalesce
|
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.db.utils import IntegrityError
|
from django.db.utils import IntegrityError
|
||||||
@ -34,6 +34,7 @@ from stdimage.models import StdImageField
|
|||||||
import common.models
|
import common.models
|
||||||
import InvenTree.ready
|
import InvenTree.ready
|
||||||
import InvenTree.tasks
|
import InvenTree.tasks
|
||||||
|
import part.filters as part_filters
|
||||||
import part.settings as part_settings
|
import part.settings as part_settings
|
||||||
from build import models as BuildModels
|
from build import models as BuildModels
|
||||||
from common.models import InvenTreeSetting
|
from common.models import InvenTreeSetting
|
||||||
@ -74,9 +75,9 @@ class PartCategory(MetadataMixin, InvenTreeTree):
|
|||||||
tree_id = self.tree_id
|
tree_id = self.tree_id
|
||||||
|
|
||||||
# Update each part in this category to point to the parent category
|
# Update each part in this category to point to the parent category
|
||||||
for part in self.parts.all():
|
for p in self.parts.all():
|
||||||
part.category = self.parent
|
p.category = self.parent
|
||||||
part.save()
|
p.save()
|
||||||
|
|
||||||
# Update each child category
|
# Update each child category
|
||||||
for child in self.children.all():
|
for child in self.children.all():
|
||||||
@ -221,7 +222,7 @@ class PartCategory(MetadataMixin, InvenTreeTree):
|
|||||||
|
|
||||||
if include_parents:
|
if include_parents:
|
||||||
queryset = PartCategoryStar.objects.filter(
|
queryset = PartCategoryStar.objects.filter(
|
||||||
category__pk__in=[cat.pk for cat in cats]
|
category__in=cats,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
queryset = PartCategoryStar.objects.filter(
|
queryset = PartCategoryStar.objects.filter(
|
||||||
@ -800,7 +801,10 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
upload_to=rename_part_image,
|
upload_to=rename_part_image,
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
variations={'thumbnail': (128, 128)},
|
variations={
|
||||||
|
'thumbnail': (128, 128),
|
||||||
|
'preview': (256, 256),
|
||||||
|
},
|
||||||
delete_orphans=False,
|
delete_orphans=False,
|
||||||
verbose_name=_('Image'),
|
verbose_name=_('Image'),
|
||||||
)
|
)
|
||||||
@ -968,13 +972,10 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
def requiring_build_orders(self):
|
def requiring_build_orders(self):
|
||||||
"""Return list of outstanding build orders which require this part."""
|
"""Return list of outstanding build orders which require this part."""
|
||||||
# List parts that this part is required for
|
# List parts that this part is required for
|
||||||
parts = self.get_used_in().all()
|
|
||||||
|
|
||||||
part_ids = [part.pk for part in parts]
|
|
||||||
|
|
||||||
# Now, get a list of outstanding build orders which require this part
|
# Now, get a list of outstanding build orders which require this part
|
||||||
builds = BuildModels.Build.objects.filter(
|
builds = BuildModels.Build.objects.filter(
|
||||||
part__in=part_ids,
|
part__in=self.get_used_in().all(),
|
||||||
status__in=BuildStatus.ACTIVE_CODES
|
status__in=BuildStatus.ACTIVE_CODES
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1098,7 +1099,7 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
|
|
||||||
if include_variants:
|
if include_variants:
|
||||||
queryset = queryset.filter(
|
queryset = queryset.filter(
|
||||||
part__pk__in=[part.pk for part in self.get_ancestors(include_self=True)]
|
part__in=self.get_ancestors(include_self=True),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
queryset = queryset.filter(part=self)
|
queryset = queryset.filter(part=self)
|
||||||
@ -1142,18 +1143,70 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
|
|
||||||
total = None
|
total = None
|
||||||
|
|
||||||
bom_items = self.get_bom_items().prefetch_related('sub_part__stock_items')
|
# Prefetch related tables, to reduce query expense
|
||||||
|
queryset = self.get_bom_items().prefetch_related(
|
||||||
|
'sub_part__stock_items',
|
||||||
|
'sub_part__stock_items__allocations',
|
||||||
|
'sub_part__stock_items__sales_order_allocations',
|
||||||
|
'substitutes',
|
||||||
|
'substitutes__part__stock_items',
|
||||||
|
)
|
||||||
|
|
||||||
# Calculate the minimum number of parts that can be built using each sub-part
|
# Annotate the 'available stock' for each part in the BOM
|
||||||
for item in bom_items.all():
|
ref = 'sub_part__'
|
||||||
stock = item.sub_part.available_stock
|
queryset = queryset.alias(
|
||||||
|
total_stock=part_filters.annotate_total_stock(reference=ref),
|
||||||
|
so_allocations=part_filters.annotate_sales_order_allocations(reference=ref),
|
||||||
|
bo_allocations=part_filters.annotate_build_order_allocations(reference=ref),
|
||||||
|
)
|
||||||
|
|
||||||
# If (by some chance) we get here but the BOM item quantity is invalid,
|
# Calculate the 'available stock' based on previous annotations
|
||||||
# ignore!
|
queryset = queryset.annotate(
|
||||||
if item.quantity <= 0:
|
available_stock=ExpressionWrapper(
|
||||||
continue
|
F('total_stock') - F('so_allocations') - F('bo_allocations'),
|
||||||
|
output_field=models.DecimalField(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
n = int(stock / item.quantity)
|
# Extract similar information for any 'substitute' parts
|
||||||
|
ref = 'substitutes__part__'
|
||||||
|
queryset = queryset.alias(
|
||||||
|
sub_total_stock=part_filters.annotate_total_stock(reference=ref),
|
||||||
|
sub_so_allocations=part_filters.annotate_sales_order_allocations(reference=ref),
|
||||||
|
sub_bo_allocations=part_filters.annotate_build_order_allocations(reference=ref),
|
||||||
|
)
|
||||||
|
|
||||||
|
queryset = queryset.annotate(
|
||||||
|
substitute_stock=ExpressionWrapper(
|
||||||
|
F('sub_total_stock') - F('sub_so_allocations') - F('sub_bo_allocations'),
|
||||||
|
output_field=models.DecimalField(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Extract similar information for any 'variant' parts
|
||||||
|
variant_stock_query = part_filters.variant_stock_query(reference='sub_part__')
|
||||||
|
|
||||||
|
queryset = queryset.alias(
|
||||||
|
var_total_stock=part_filters.annotate_variant_quantity(variant_stock_query, reference='quantity'),
|
||||||
|
var_bo_allocations=part_filters.annotate_variant_quantity(variant_stock_query, reference='allocations__quantity'),
|
||||||
|
var_so_allocations=part_filters.annotate_variant_quantity(variant_stock_query, reference='sales_order_allocations__quantity'),
|
||||||
|
)
|
||||||
|
|
||||||
|
queryset = queryset.annotate(
|
||||||
|
variant_stock=ExpressionWrapper(
|
||||||
|
F('var_total_stock') - F('var_bo_allocations') - F('var_so_allocations'),
|
||||||
|
output_field=models.DecimalField(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for item in queryset.all():
|
||||||
|
# Iterate through each item in the queryset, work out the limiting quantity
|
||||||
|
quantity = item.available_stock + item.substitute_stock
|
||||||
|
|
||||||
|
if item.allow_variants:
|
||||||
|
quantity += item.variant_stock
|
||||||
|
|
||||||
|
n = int(quantity / item.quantity)
|
||||||
|
|
||||||
if total is None or n < total:
|
if total is None or n < total:
|
||||||
total = n
|
total = n
|
||||||
@ -1336,11 +1389,10 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
parents = self.get_ancestors(include_self=False)
|
parents = self.get_ancestors(include_self=False)
|
||||||
|
|
||||||
# There are parents available
|
# There are parents available
|
||||||
if parents.count() > 0:
|
if parents.exists():
|
||||||
parent_ids = [p.pk for p in parents]
|
|
||||||
|
|
||||||
parent_filter = Q(
|
parent_filter = Q(
|
||||||
part__id__in=parent_ids,
|
part__in=parents,
|
||||||
inherited=True
|
inherited=True
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1425,7 +1477,7 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
@property
|
@property
|
||||||
def has_bom(self):
|
def has_bom(self):
|
||||||
"""Return True if this Part instance has any BOM items"""
|
"""Return True if this Part instance has any BOM items"""
|
||||||
return self.get_bom_items().count() > 0
|
return self.get_bom_items().exists()
|
||||||
|
|
||||||
def get_trackable_parts(self):
|
def get_trackable_parts(self):
|
||||||
"""Return a queryset of all trackable parts in the BOM for this part."""
|
"""Return a queryset of all trackable parts in the BOM for this part."""
|
||||||
@ -1440,7 +1492,7 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
|
|
||||||
This is important when building the part.
|
This is important when building the part.
|
||||||
"""
|
"""
|
||||||
return self.get_trackable_parts().count() > 0
|
return self.get_trackable_parts().exists()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bom_count(self):
|
def bom_count(self):
|
||||||
@ -1482,7 +1534,7 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
# Validate each line item, ignoring inherited ones
|
# Validate each line item, ignoring inherited ones
|
||||||
bom_items = self.get_bom_items(include_inherited=False)
|
bom_items = self.get_bom_items(include_inherited=False)
|
||||||
|
|
||||||
for item in bom_items.all():
|
for item in bom_items:
|
||||||
item.validate_hash()
|
item.validate_hash()
|
||||||
|
|
||||||
self.bom_checksum = self.get_bom_hash()
|
self.bom_checksum = self.get_bom_hash()
|
||||||
@ -1509,7 +1561,7 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
if parts is None:
|
if parts is None:
|
||||||
parts = set()
|
parts = set()
|
||||||
|
|
||||||
bom_items = self.get_bom_items().all()
|
bom_items = self.get_bom_items()
|
||||||
|
|
||||||
for bom_item in bom_items:
|
for bom_item in bom_items:
|
||||||
|
|
||||||
@ -1533,7 +1585,7 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
def has_complete_bom_pricing(self):
|
def has_complete_bom_pricing(self):
|
||||||
"""Return true if there is pricing information for each item in the BOM."""
|
"""Return true if there is pricing information for each item in the BOM."""
|
||||||
use_internal = common.models.InvenTreeSetting.get_setting('PART_BOM_USE_INTERNAL_PRICE', False)
|
use_internal = common.models.InvenTreeSetting.get_setting('PART_BOM_USE_INTERNAL_PRICE', False)
|
||||||
for item in self.get_bom_items().all().select_related('sub_part'):
|
for item in self.get_bom_items().select_related('sub_part'):
|
||||||
if item.sub_part.get_price_range(internal=use_internal) is None:
|
if item.sub_part.get_price_range(internal=use_internal) is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -1609,7 +1661,7 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
min_price = None
|
min_price = None
|
||||||
max_price = None
|
max_price = None
|
||||||
|
|
||||||
for item in self.get_bom_items().all().select_related('sub_part'):
|
for item in self.get_bom_items().select_related('sub_part'):
|
||||||
|
|
||||||
if item.sub_part.pk == self.pk:
|
if item.sub_part.pk == self.pk:
|
||||||
logger.warning(f"WARNING: BomItem ID {item.pk} contains itself in BOM")
|
logger.warning(f"WARNING: BomItem ID {item.pk} contains itself in BOM")
|
||||||
@ -1689,7 +1741,7 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
@property
|
@property
|
||||||
def has_price_breaks(self):
|
def has_price_breaks(self):
|
||||||
"""Return True if this part has sale price breaks"""
|
"""Return True if this part has sale price breaks"""
|
||||||
return self.price_breaks.count() > 0
|
return self.price_breaks.exists()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def price_breaks(self):
|
def price_breaks(self):
|
||||||
@ -1725,7 +1777,7 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
@property
|
@property
|
||||||
def has_internal_price_breaks(self):
|
def has_internal_price_breaks(self):
|
||||||
"""Return True if this Part has internal pricing information"""
|
"""Return True if this Part has internal pricing information"""
|
||||||
return self.internal_price_breaks.count() > 0
|
return self.internal_price_breaks.exists()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def internal_price_breaks(self):
|
def internal_price_breaks(self):
|
||||||
@ -1978,7 +2030,7 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
@property
|
@property
|
||||||
def has_variants(self):
|
def has_variants(self):
|
||||||
"""Check if this Part object has variants underneath it."""
|
"""Check if this Part object has variants underneath it."""
|
||||||
return self.get_all_variants().count() > 0
|
return self.get_all_variants().exists()
|
||||||
|
|
||||||
def get_all_variants(self):
|
def get_all_variants(self):
|
||||||
"""Return all Part object which exist as a variant under this part."""
|
"""Return all Part object which exist as a variant under this part."""
|
||||||
@ -1993,7 +2045,7 @@ class Part(MetadataMixin, MPTTModel):
|
|||||||
b) It has non-virtual template parts above it
|
b) It has non-virtual template parts above it
|
||||||
c) It has non-virtual sibling variants
|
c) It has non-virtual sibling variants
|
||||||
"""
|
"""
|
||||||
return self.get_conversion_options().count() > 0
|
return self.get_conversion_options().exists()
|
||||||
|
|
||||||
def get_conversion_options(self):
|
def get_conversion_options(self):
|
||||||
"""Return options for converting this part to a "variant" within the same tree.
|
"""Return options for converting this part to a "variant" within the same tree.
|
||||||
@ -2520,7 +2572,7 @@ class BomItem(DataImportMixin, models.Model):
|
|||||||
- Allow stock from all directly specified substitute parts
|
- Allow stock from all directly specified substitute parts
|
||||||
- If allow_variants is True, allow all part variants
|
- If allow_variants is True, allow all part variants
|
||||||
"""
|
"""
|
||||||
return Q(part__in=[part.pk for part in self.get_valid_parts_for_allocation()])
|
return Q(part__in=self.get_valid_parts_for_allocation())
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""Enforce 'clean' operation when saving a BomItem instance"""
|
"""Enforce 'clean' operation when saving a BomItem instance"""
|
||||||
|
@ -4,8 +4,7 @@ import imghdr
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import (ExpressionWrapper, F, FloatField, Func, Q,
|
from django.db.models import ExpressionWrapper, F, FloatField, Q
|
||||||
Subquery)
|
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
@ -251,8 +250,6 @@ class PartBriefSerializer(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
thumbnail = serializers.CharField(source='get_thumbnail_url', read_only=True)
|
thumbnail = serializers.CharField(source='get_thumbnail_url', read_only=True)
|
||||||
|
|
||||||
stock = serializers.FloatField(source='total_stock')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass defining serializer fields"""
|
"""Metaclass defining serializer fields"""
|
||||||
model = Part
|
model = Part
|
||||||
@ -270,7 +267,6 @@ class PartBriefSerializer(InvenTreeModelSerializer):
|
|||||||
'is_template',
|
'is_template',
|
||||||
'purchaseable',
|
'purchaseable',
|
||||||
'salable',
|
'salable',
|
||||||
'stock',
|
|
||||||
'trackable',
|
'trackable',
|
||||||
'virtual',
|
'virtual',
|
||||||
'units',
|
'units',
|
||||||
@ -322,14 +318,7 @@ class PartSerializer(InvenTreeModelSerializer):
|
|||||||
variant_query = part.filters.variant_stock_query()
|
variant_query = part.filters.variant_stock_query()
|
||||||
|
|
||||||
queryset = queryset.annotate(
|
queryset = queryset.annotate(
|
||||||
variant_stock=Coalesce(
|
variant_stock=part.filters.annotate_variant_quantity(variant_query, reference='quantity'),
|
||||||
Subquery(
|
|
||||||
variant_query.annotate(
|
|
||||||
total=Func(F('quantity'), function='SUM', output_field=FloatField())
|
|
||||||
).values('total')),
|
|
||||||
0,
|
|
||||||
output_field=FloatField(),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Filter to limit builds to "active"
|
# Filter to limit builds to "active"
|
||||||
@ -642,35 +631,14 @@ class BomItemSerializer(InvenTreeModelSerializer):
|
|||||||
variant_stock_query = part.filters.variant_stock_query(reference='sub_part__')
|
variant_stock_query = part.filters.variant_stock_query(reference='sub_part__')
|
||||||
|
|
||||||
queryset = queryset.alias(
|
queryset = queryset.alias(
|
||||||
variant_stock_total=Coalesce(
|
variant_stock_total=part.filters.annotate_variant_quantity(variant_stock_query, reference='quantity'),
|
||||||
Subquery(
|
variant_bo_allocations=part.filters.annotate_variant_quantity(variant_stock_query, reference='sales_order_allocations__quantity'),
|
||||||
variant_stock_query.annotate(
|
variant_so_allocations=part.filters.annotate_variant_quantity(variant_stock_query, reference='allocations__quantity'),
|
||||||
total=Func(F('quantity'), function='SUM', output_field=FloatField())
|
|
||||||
).values('total')),
|
|
||||||
0,
|
|
||||||
output_field=FloatField()
|
|
||||||
),
|
|
||||||
variant_stock_build_order_allocations=Coalesce(
|
|
||||||
Subquery(
|
|
||||||
variant_stock_query.annotate(
|
|
||||||
total=Func(F('sales_order_allocations__quantity'), function='SUM', output_field=FloatField()),
|
|
||||||
).values('total')),
|
|
||||||
0,
|
|
||||||
output_field=FloatField(),
|
|
||||||
),
|
|
||||||
variant_stock_sales_order_allocations=Coalesce(
|
|
||||||
Subquery(
|
|
||||||
variant_stock_query.annotate(
|
|
||||||
total=Func(F('allocations__quantity'), function='SUM', output_field=FloatField()),
|
|
||||||
).values('total')),
|
|
||||||
0,
|
|
||||||
output_field=FloatField(),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
queryset = queryset.annotate(
|
queryset = queryset.annotate(
|
||||||
available_variant_stock=ExpressionWrapper(
|
available_variant_stock=ExpressionWrapper(
|
||||||
F('variant_stock_total') - F('variant_stock_build_order_allocations') - F('variant_stock_sales_order_allocations'),
|
F('variant_stock_total') - F('variant_bo_allocations') - F('variant_so_allocations'),
|
||||||
output_field=FloatField(),
|
output_field=FloatField(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -690,17 +690,6 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load the BOM table data in the pricing view
|
|
||||||
{% if part.has_bom and roles.sales_order.view %}
|
|
||||||
loadBomTable($("#bom-pricing-table"), {
|
|
||||||
editable: false,
|
|
||||||
bom_url: "{% url 'api-bom-list' %}",
|
|
||||||
part_url: "{% url 'api-part-list' %}",
|
|
||||||
parent_id: {{ part.id }} ,
|
|
||||||
sub_part_detail: true,
|
|
||||||
});
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
onPanelLoad("purchase-orders", function() {
|
onPanelLoad("purchase-orders", function() {
|
||||||
loadPartPurchaseOrderTable(
|
loadPartPurchaseOrderTable(
|
||||||
"#purchase-order-table",
|
"#purchase-order-table",
|
||||||
@ -885,152 +874,164 @@
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onPanelLoad('pricing', function() {
|
||||||
|
{% default_currency as currency %}
|
||||||
|
|
||||||
{% default_currency as currency %}
|
// Load the BOM table data in the pricing view
|
||||||
|
{% if part.has_bom and roles.sales_order.view %}
|
||||||
|
loadBomTable($("#bom-pricing-table"), {
|
||||||
|
editable: false,
|
||||||
|
bom_url: "{% url 'api-bom-list' %}",
|
||||||
|
part_url: "{% url 'api-part-list' %}",
|
||||||
|
parent_id: {{ part.id }} ,
|
||||||
|
sub_part_detail: true,
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
// history graphs
|
// history graphs
|
||||||
{% if price_history %}
|
{% if price_history %}
|
||||||
var purchasepricedata = {
|
var purchasepricedata = {
|
||||||
labels: [
|
|
||||||
{% for line in price_history %}'{% render_date line.date %}',{% endfor %}
|
|
||||||
],
|
|
||||||
datasets: [{
|
|
||||||
label: '{% blocktrans %}Purchase Unit Price - {{currency}}{% endblocktrans %}',
|
|
||||||
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
|
||||||
borderColor: 'rgb(255, 99, 132)',
|
|
||||||
yAxisID: 'y',
|
|
||||||
data: [
|
|
||||||
{% for line in price_history %}{{ line.price|stringformat:".2f" }},{% endfor %}
|
|
||||||
],
|
|
||||||
borderWidth: 1,
|
|
||||||
type: 'line'
|
|
||||||
},
|
|
||||||
{% if 'price_diff' in price_history.0 %}
|
|
||||||
{
|
|
||||||
label: '{% blocktrans %}Unit Price-Cost Difference - {{currency}}{% endblocktrans %}',
|
|
||||||
backgroundColor: 'rgba(68, 157, 68, 0.2)',
|
|
||||||
borderColor: 'rgb(68, 157, 68)',
|
|
||||||
yAxisID: 'y2',
|
|
||||||
data: [
|
|
||||||
{% for line in price_history %}{{ line.price_diff|stringformat:".2f" }},{% endfor %}
|
|
||||||
],
|
|
||||||
borderWidth: 1,
|
|
||||||
type: 'line',
|
|
||||||
hidden: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '{% blocktrans %}Supplier Unit Cost - {{currency}}{% endblocktrans %}',
|
|
||||||
backgroundColor: 'rgba(70, 127, 155, 0.2)',
|
|
||||||
borderColor: 'rgb(70, 127, 155)',
|
|
||||||
yAxisID: 'y',
|
|
||||||
data: [
|
|
||||||
{% for line in price_history %}{{ line.price_part|stringformat:".2f" }},{% endfor %}
|
|
||||||
],
|
|
||||||
borderWidth: 1,
|
|
||||||
type: 'line',
|
|
||||||
hidden: true,
|
|
||||||
},
|
|
||||||
{% endif %}
|
|
||||||
{
|
|
||||||
label: '{% trans "Quantity" %}',
|
|
||||||
backgroundColor: 'rgba(255, 206, 86, 0.2)',
|
|
||||||
borderColor: 'rgb(255, 206, 86)',
|
|
||||||
yAxisID: 'y1',
|
|
||||||
data: [
|
|
||||||
{% for line in price_history %}{{ line.qty|stringformat:"f" }},{% endfor %}
|
|
||||||
],
|
|
||||||
borderWidth: 1
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
var StockPriceChart = loadStockPricingChart($('#StockPriceChart'), purchasepricedata)
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if bom_parts %}
|
|
||||||
var bom_colors = randomColor({hue: 'green', count: {{ bom_parts|length }} })
|
|
||||||
var bomdata = {
|
|
||||||
labels: [{% for line in bom_parts %}'{{ line.name }}',{% endfor %}],
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: 'Price',
|
|
||||||
data: [{% for line in bom_parts %}{{ line.min_price }},{% endfor %}],
|
|
||||||
backgroundColor: bom_colors,
|
|
||||||
},
|
|
||||||
{% if bom_pie_max %}
|
|
||||||
{
|
|
||||||
label: 'Max Price',
|
|
||||||
data: [{% for line in bom_parts %}{{ line.max_price }},{% endfor %}],
|
|
||||||
backgroundColor: bom_colors,
|
|
||||||
},
|
|
||||||
{% endif %}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
var BomChart = loadBomChart(document.getElementById('BomChart'), bomdata)
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
// Internal pricebreaks
|
|
||||||
{% settings_value "PART_INTERNAL_PRICE" as show_internal_price %}
|
|
||||||
{% if show_internal_price and roles.sales_order.view %}
|
|
||||||
initPriceBreakSet(
|
|
||||||
$('#internal-price-break-table'),
|
|
||||||
{
|
|
||||||
part_id: {{part.id}},
|
|
||||||
pb_human_name: 'internal price break',
|
|
||||||
pb_url_slug: 'internal-price',
|
|
||||||
pb_url: '{% url 'api-part-internal-price-list' %}',
|
|
||||||
pb_new_btn: $('#new-internal-price-break'),
|
|
||||||
pb_new_url: '{% url 'api-part-internal-price-list' %}',
|
|
||||||
linkedGraph: $('#InternalPriceBreakChart'),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
// Sales pricebreaks
|
|
||||||
{% if part.salable and roles.sales_order.view %}
|
|
||||||
initPriceBreakSet(
|
|
||||||
$('#price-break-table'),
|
|
||||||
{
|
|
||||||
part_id: {{part.id}},
|
|
||||||
pb_human_name: 'sale price break',
|
|
||||||
pb_url_slug: 'sale-price',
|
|
||||||
pb_url: "{% url 'api-part-sale-price-list' %}",
|
|
||||||
pb_new_btn: $('#new-price-break'),
|
|
||||||
pb_new_url: '{% url 'api-part-sale-price-list' %}',
|
|
||||||
linkedGraph: $('#SalePriceBreakChart'),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
// Sale price history
|
|
||||||
{% if sale_history %}
|
|
||||||
var salepricedata = {
|
|
||||||
labels: [
|
labels: [
|
||||||
{% for line in sale_history %}'{% render_date line.date %}',{% endfor %}
|
{% for line in price_history %}'{% render_date line.date %}',{% endfor %}
|
||||||
],
|
],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: '{% blocktrans %}Unit Price - {{currency}}{% endblocktrans %}',
|
label: '{% blocktrans %}Purchase Unit Price - {{currency}}{% endblocktrans %}',
|
||||||
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||||
borderColor: 'rgb(255, 99, 132)',
|
borderColor: 'rgb(255, 99, 132)',
|
||||||
yAxisID: 'y',
|
yAxisID: 'y',
|
||||||
data: [
|
data: [
|
||||||
{% for line in sale_history %}{{ line.price|stringformat:".2f" }},{% endfor %}
|
{% for line in price_history %}{{ line.price|stringformat:".2f" }},{% endfor %}
|
||||||
],
|
],
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
|
type: 'line'
|
||||||
},
|
},
|
||||||
|
{% if 'price_diff' in price_history.0 %}
|
||||||
|
{
|
||||||
|
label: '{% blocktrans %}Unit Price-Cost Difference - {{currency}}{% endblocktrans %}',
|
||||||
|
backgroundColor: 'rgba(68, 157, 68, 0.2)',
|
||||||
|
borderColor: 'rgb(68, 157, 68)',
|
||||||
|
yAxisID: 'y2',
|
||||||
|
data: [
|
||||||
|
{% for line in price_history %}{{ line.price_diff|stringformat:".2f" }},{% endfor %}
|
||||||
|
],
|
||||||
|
borderWidth: 1,
|
||||||
|
type: 'line',
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '{% blocktrans %}Supplier Unit Cost - {{currency}}{% endblocktrans %}',
|
||||||
|
backgroundColor: 'rgba(70, 127, 155, 0.2)',
|
||||||
|
borderColor: 'rgb(70, 127, 155)',
|
||||||
|
yAxisID: 'y',
|
||||||
|
data: [
|
||||||
|
{% for line in price_history %}{{ line.price_part|stringformat:".2f" }},{% endfor %}
|
||||||
|
],
|
||||||
|
borderWidth: 1,
|
||||||
|
type: 'line',
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
{% endif %}
|
||||||
{
|
{
|
||||||
label: '{% trans "Quantity" %}',
|
label: '{% trans "Quantity" %}',
|
||||||
backgroundColor: 'rgba(255, 206, 86, 0.2)',
|
backgroundColor: 'rgba(255, 206, 86, 0.2)',
|
||||||
borderColor: 'rgb(255, 206, 86)',
|
borderColor: 'rgb(255, 206, 86)',
|
||||||
yAxisID: 'y1',
|
yAxisID: 'y1',
|
||||||
data: [
|
data: [
|
||||||
{% for line in sale_history %}{{ line.qty|stringformat:"f" }},{% endfor %}
|
{% for line in price_history %}{{ line.qty|stringformat:"f" }},{% endfor %}
|
||||||
],
|
],
|
||||||
borderWidth: 1,
|
borderWidth: 1
|
||||||
type: 'bar',
|
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
var SalePriceChart = loadSellPricingChart($('#SalePriceChart'), salepricedata)
|
var StockPriceChart = loadStockPricingChart($('#StockPriceChart'), purchasepricedata)
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if bom_parts %}
|
||||||
|
var bom_colors = randomColor({hue: 'green', count: {{ bom_parts|length }} })
|
||||||
|
var bomdata = {
|
||||||
|
labels: [{% for line in bom_parts %}'{{ line.name }}',{% endfor %}],
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Price',
|
||||||
|
data: [{% for line in bom_parts %}{{ line.min_price }},{% endfor %}],
|
||||||
|
backgroundColor: bom_colors,
|
||||||
|
},
|
||||||
|
{% if bom_pie_max %}
|
||||||
|
{
|
||||||
|
label: 'Max Price',
|
||||||
|
data: [{% for line in bom_parts %}{{ line.max_price }},{% endfor %}],
|
||||||
|
backgroundColor: bom_colors,
|
||||||
|
},
|
||||||
|
{% endif %}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
var BomChart = loadBomChart(document.getElementById('BomChart'), bomdata)
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
// Internal pricebreaks
|
||||||
|
{% settings_value "PART_INTERNAL_PRICE" as show_internal_price %}
|
||||||
|
{% if show_internal_price and roles.sales_order.view %}
|
||||||
|
initPriceBreakSet(
|
||||||
|
$('#internal-price-break-table'),
|
||||||
|
{
|
||||||
|
part_id: {{part.id}},
|
||||||
|
pb_human_name: 'internal price break',
|
||||||
|
pb_url_slug: 'internal-price',
|
||||||
|
pb_url: '{% url 'api-part-internal-price-list' %}',
|
||||||
|
pb_new_btn: $('#new-internal-price-break'),
|
||||||
|
pb_new_url: '{% url 'api-part-internal-price-list' %}',
|
||||||
|
linkedGraph: $('#InternalPriceBreakChart'),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
// Sales pricebreaks
|
||||||
|
{% if part.salable and roles.sales_order.view %}
|
||||||
|
initPriceBreakSet(
|
||||||
|
$('#price-break-table'),
|
||||||
|
{
|
||||||
|
part_id: {{part.id}},
|
||||||
|
pb_human_name: 'sale price break',
|
||||||
|
pb_url_slug: 'sale-price',
|
||||||
|
pb_url: "{% url 'api-part-sale-price-list' %}",
|
||||||
|
pb_new_btn: $('#new-price-break'),
|
||||||
|
pb_new_url: '{% url 'api-part-sale-price-list' %}',
|
||||||
|
linkedGraph: $('#SalePriceBreakChart'),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
// Sale price history
|
||||||
|
{% if sale_history %}
|
||||||
|
var salepricedata = {
|
||||||
|
labels: [
|
||||||
|
{% for line in sale_history %}'{% render_date line.date %}',{% endfor %}
|
||||||
|
],
|
||||||
|
datasets: [{
|
||||||
|
label: '{% blocktrans %}Unit Price - {{currency}}{% endblocktrans %}',
|
||||||
|
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||||
|
borderColor: 'rgb(255, 99, 132)',
|
||||||
|
yAxisID: 'y',
|
||||||
|
data: [
|
||||||
|
{% for line in sale_history %}{{ line.price|stringformat:".2f" }},{% endfor %}
|
||||||
|
],
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '{% trans "Quantity" %}',
|
||||||
|
backgroundColor: 'rgba(255, 206, 86, 0.2)',
|
||||||
|
borderColor: 'rgb(255, 206, 86)',
|
||||||
|
yAxisID: 'y1',
|
||||||
|
data: [
|
||||||
|
{% for line in sale_history %}{{ line.qty|stringformat:"f" }},{% endfor %}
|
||||||
|
],
|
||||||
|
borderWidth: 1,
|
||||||
|
type: 'bar',
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
var SalePriceChart = loadSellPricingChart($('#SalePriceChart'), salepricedata)
|
||||||
|
{% endif %}
|
||||||
|
});
|
||||||
|
|
||||||
enableSidebar('part');
|
enableSidebar('part');
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<img class="part-thumb" id='part-image'
|
<img class="part-thumb" id='part-image'
|
||||||
{% if part.image %}
|
{% if part.image %}
|
||||||
src="{{ part.image.url }}"
|
src="{{ part.image.preview.url }}"
|
||||||
{% else %}
|
{% else %}
|
||||||
src="{% static 'img/blank_image.png' %}"
|
src="{% static 'img/blank_image.png' %}"
|
||||||
{% endif %}/>
|
{% endif %}/>
|
||||||
|
@ -4,12 +4,14 @@ from django.conf import settings
|
|||||||
from django.urls import include, re_path
|
from django.urls import include, re_path
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import filters, generics, permissions, status
|
from rest_framework import filters, permissions, status
|
||||||
from rest_framework.exceptions import NotFound
|
from rest_framework.exceptions import NotFound
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
import plugin.serializers as PluginSerializers
|
import plugin.serializers as PluginSerializers
|
||||||
from common.api import GlobalSettingsPermissions
|
from common.api import GlobalSettingsPermissions
|
||||||
|
from InvenTree.mixins import (CreateAPI, ListAPI, RetrieveUpdateAPI,
|
||||||
|
RetrieveUpdateDestroyAPI)
|
||||||
from plugin.base.action.api import ActionPluginView
|
from plugin.base.action.api import ActionPluginView
|
||||||
from plugin.base.barcodes.api import barcode_api_urls
|
from plugin.base.barcodes.api import barcode_api_urls
|
||||||
from plugin.base.locate.api import LocatePluginView
|
from plugin.base.locate.api import LocatePluginView
|
||||||
@ -17,7 +19,7 @@ from plugin.models import PluginConfig, PluginSetting
|
|||||||
from plugin.registry import registry
|
from plugin.registry import registry
|
||||||
|
|
||||||
|
|
||||||
class PluginList(generics.ListAPIView):
|
class PluginList(ListAPI):
|
||||||
"""API endpoint for list of PluginConfig objects.
|
"""API endpoint for list of PluginConfig objects.
|
||||||
|
|
||||||
- GET: Return a list of all PluginConfig objects
|
- GET: Return a list of all PluginConfig objects
|
||||||
@ -80,7 +82,7 @@ class PluginList(generics.ListAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PluginDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PluginDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API detail endpoint for PluginConfig object.
|
"""API detail endpoint for PluginConfig object.
|
||||||
|
|
||||||
get:
|
get:
|
||||||
@ -97,7 +99,7 @@ class PluginDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
serializer_class = PluginSerializers.PluginConfigSerializer
|
serializer_class = PluginSerializers.PluginConfigSerializer
|
||||||
|
|
||||||
|
|
||||||
class PluginInstall(generics.CreateAPIView):
|
class PluginInstall(CreateAPI):
|
||||||
"""Endpoint for installing a new plugin."""
|
"""Endpoint for installing a new plugin."""
|
||||||
|
|
||||||
queryset = PluginConfig.objects.none()
|
queryset = PluginConfig.objects.none()
|
||||||
@ -105,7 +107,10 @@ class PluginInstall(generics.CreateAPIView):
|
|||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""Install a plugin via the API"""
|
"""Install a plugin via the API"""
|
||||||
serializer = self.get_serializer(data=request.data)
|
# Clean up input data
|
||||||
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(data=data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
result = self.perform_create(serializer)
|
result = self.perform_create(serializer)
|
||||||
result['input'] = serializer.data
|
result['input'] = serializer.data
|
||||||
@ -117,7 +122,7 @@ class PluginInstall(generics.CreateAPIView):
|
|||||||
return serializer.save()
|
return serializer.save()
|
||||||
|
|
||||||
|
|
||||||
class PluginSettingList(generics.ListAPIView):
|
class PluginSettingList(ListAPI):
|
||||||
"""List endpoint for all plugin related settings.
|
"""List endpoint for all plugin related settings.
|
||||||
|
|
||||||
- read only
|
- read only
|
||||||
@ -141,7 +146,7 @@ class PluginSettingList(generics.ListAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PluginSettingDetail(generics.RetrieveUpdateAPIView):
|
class PluginSettingDetail(RetrieveUpdateAPI):
|
||||||
"""Detail endpoint for a plugin-specific setting.
|
"""Detail endpoint for a plugin-specific setting.
|
||||||
|
|
||||||
Note that these cannot be created or deleted via the API
|
Note that these cannot be created or deleted via the API
|
||||||
|
@ -8,6 +8,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
import pdf2image
|
import pdf2image
|
||||||
|
|
||||||
import common.notifications
|
import common.notifications
|
||||||
|
from common.models import InvenTreeSetting
|
||||||
from InvenTree.exceptions import log_error
|
from InvenTree.exceptions import log_error
|
||||||
from plugin.registry import registry
|
from plugin.registry import registry
|
||||||
|
|
||||||
@ -36,9 +37,10 @@ def print_label(plugin_slug: str, pdf_data, filename=None, label_instance=None,
|
|||||||
return
|
return
|
||||||
|
|
||||||
# In addition to providing a .pdf image, we'll also provide a .png file
|
# In addition to providing a .pdf image, we'll also provide a .png file
|
||||||
|
dpi = InvenTreeSetting.get_setting('LABEL_DPI', 300)
|
||||||
png_file = pdf2image.convert_from_bytes(
|
png_file = pdf2image.convert_from_bytes(
|
||||||
pdf_data,
|
pdf_data,
|
||||||
dpi=300,
|
dpi=dpi,
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -8,7 +8,7 @@ from django.urls import include, path, re_path
|
|||||||
from django.utils.translation import gettext_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 rest_framework import filters, generics
|
from rest_framework import filters
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
import build.models
|
import build.models
|
||||||
@ -16,6 +16,7 @@ import common.models
|
|||||||
import InvenTree.helpers
|
import InvenTree.helpers
|
||||||
import order.models
|
import order.models
|
||||||
import part.models
|
import part.models
|
||||||
|
from InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateDestroyAPI
|
||||||
from stock.models import StockItem, StockItemAttachment
|
from stock.models import StockItem, StockItemAttachment
|
||||||
|
|
||||||
from .models import (BillOfMaterialsReport, BuildReport, PurchaseOrderReport,
|
from .models import (BillOfMaterialsReport, BuildReport, PurchaseOrderReport,
|
||||||
@ -25,7 +26,7 @@ from .serializers import (BOMReportSerializer, BuildReportSerializer,
|
|||||||
SalesOrderReportSerializer, TestReportSerializer)
|
SalesOrderReportSerializer, TestReportSerializer)
|
||||||
|
|
||||||
|
|
||||||
class ReportListView(generics.ListAPIView):
|
class ReportListView(ListAPI):
|
||||||
"""Generic API class for report templates."""
|
"""Generic API class for report templates."""
|
||||||
|
|
||||||
filter_backends = [
|
filter_backends = [
|
||||||
@ -330,14 +331,14 @@ class StockItemTestReportList(ReportListView, StockItemReportMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class StockItemTestReportDetail(generics.RetrieveUpdateDestroyAPIView):
|
class StockItemTestReportDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single TestReport object."""
|
"""API endpoint for a single TestReport object."""
|
||||||
|
|
||||||
queryset = TestReport.objects.all()
|
queryset = TestReport.objects.all()
|
||||||
serializer_class = TestReportSerializer
|
serializer_class = TestReportSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemTestReportPrint(generics.RetrieveAPIView, StockItemReportMixin, ReportPrintMixin):
|
class StockItemTestReportPrint(RetrieveAPI, StockItemReportMixin, ReportPrintMixin):
|
||||||
"""API endpoint for printing a TestReport object."""
|
"""API endpoint for printing a TestReport object."""
|
||||||
|
|
||||||
queryset = TestReport.objects.all()
|
queryset = TestReport.objects.all()
|
||||||
@ -427,14 +428,14 @@ class BOMReportList(ReportListView, PartReportMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class BOMReportDetail(generics.RetrieveUpdateDestroyAPIView):
|
class BOMReportDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single BillOfMaterialReport object."""
|
"""API endpoint for a single BillOfMaterialReport object."""
|
||||||
|
|
||||||
queryset = BillOfMaterialsReport.objects.all()
|
queryset = BillOfMaterialsReport.objects.all()
|
||||||
serializer_class = BOMReportSerializer
|
serializer_class = BOMReportSerializer
|
||||||
|
|
||||||
|
|
||||||
class BOMReportPrint(generics.RetrieveAPIView, PartReportMixin, ReportPrintMixin):
|
class BOMReportPrint(RetrieveAPI, PartReportMixin, ReportPrintMixin):
|
||||||
"""API endpoint for printing a BillOfMaterialReport object."""
|
"""API endpoint for printing a BillOfMaterialReport object."""
|
||||||
|
|
||||||
queryset = BillOfMaterialsReport.objects.all()
|
queryset = BillOfMaterialsReport.objects.all()
|
||||||
@ -509,14 +510,14 @@ class BuildReportList(ReportListView, BuildReportMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class BuildReportDetail(generics.RetrieveUpdateDestroyAPIView):
|
class BuildReportDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single BuildReport object."""
|
"""API endpoint for a single BuildReport object."""
|
||||||
|
|
||||||
queryset = BuildReport.objects.all()
|
queryset = BuildReport.objects.all()
|
||||||
serializer_class = BuildReportSerializer
|
serializer_class = BuildReportSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildReportPrint(generics.RetrieveAPIView, BuildReportMixin, ReportPrintMixin):
|
class BuildReportPrint(RetrieveAPI, BuildReportMixin, ReportPrintMixin):
|
||||||
"""API endpoint for printing a BuildReport."""
|
"""API endpoint for printing a BuildReport."""
|
||||||
|
|
||||||
queryset = BuildReport.objects.all()
|
queryset = BuildReport.objects.all()
|
||||||
@ -586,14 +587,14 @@ class PurchaseOrderReportList(ReportListView, OrderReportMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderReportDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PurchaseOrderReportDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single PurchaseOrderReport object."""
|
"""API endpoint for a single PurchaseOrderReport object."""
|
||||||
|
|
||||||
queryset = PurchaseOrderReport.objects.all()
|
queryset = PurchaseOrderReport.objects.all()
|
||||||
serializer_class = PurchaseOrderReportSerializer
|
serializer_class = PurchaseOrderReportSerializer
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderReportPrint(generics.RetrieveAPIView, OrderReportMixin, ReportPrintMixin):
|
class PurchaseOrderReportPrint(RetrieveAPI, OrderReportMixin, ReportPrintMixin):
|
||||||
"""API endpoint for printing a PurchaseOrderReport object."""
|
"""API endpoint for printing a PurchaseOrderReport object."""
|
||||||
|
|
||||||
OrderModel = order.models.PurchaseOrder
|
OrderModel = order.models.PurchaseOrder
|
||||||
@ -665,14 +666,14 @@ class SalesOrderReportList(ReportListView, OrderReportMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderReportDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderReportDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single SalesOrderReport object."""
|
"""API endpoint for a single SalesOrderReport object."""
|
||||||
|
|
||||||
queryset = SalesOrderReport.objects.all()
|
queryset = SalesOrderReport.objects.all()
|
||||||
serializer_class = SalesOrderReportSerializer
|
serializer_class = SalesOrderReportSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderReportPrint(generics.RetrieveAPIView, OrderReportMixin, ReportPrintMixin):
|
class SalesOrderReportPrint(RetrieveAPI, OrderReportMixin, ReportPrintMixin):
|
||||||
"""API endpoint for printing a PurchaseOrderReport object."""
|
"""API endpoint for printing a PurchaseOrderReport object."""
|
||||||
|
|
||||||
OrderModel = order.models.SalesOrder
|
OrderModel = order.models.SalesOrder
|
||||||
|
@ -12,7 +12,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
|
|
||||||
from django_filters import rest_framework as rest_filters
|
from django_filters import rest_framework as rest_filters
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import filters, generics, status
|
from rest_framework import filters, status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
|
|
||||||
@ -27,6 +27,8 @@ from InvenTree.api import (APIDownloadMixin, AttachmentMixin,
|
|||||||
from InvenTree.filters import InvenTreeOrderingFilter
|
from InvenTree.filters import InvenTreeOrderingFilter
|
||||||
from InvenTree.helpers import (DownloadFile, extract_serial_numbers, isNull,
|
from InvenTree.helpers import (DownloadFile, extract_serial_numbers, isNull,
|
||||||
str2bool)
|
str2bool)
|
||||||
|
from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI, RetrieveAPI,
|
||||||
|
RetrieveUpdateAPI, RetrieveUpdateDestroyAPI)
|
||||||
from order.models import PurchaseOrder, SalesOrder, SalesOrderAllocation
|
from order.models import PurchaseOrder, SalesOrder, SalesOrderAllocation
|
||||||
from order.serializers import PurchaseOrderSerializer
|
from order.serializers import PurchaseOrderSerializer
|
||||||
from part.models import BomItem, Part, PartCategory
|
from part.models import BomItem, Part, PartCategory
|
||||||
@ -37,7 +39,7 @@ from stock.models import (StockItem, StockItemAttachment, StockItemTestResult,
|
|||||||
StockItemTracking, StockLocation)
|
StockItemTracking, StockLocation)
|
||||||
|
|
||||||
|
|
||||||
class StockDetail(generics.RetrieveUpdateDestroyAPIView):
|
class StockDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API detail endpoint for Stock object.
|
"""API detail endpoint for Stock object.
|
||||||
|
|
||||||
get:
|
get:
|
||||||
@ -72,13 +74,12 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
kwargs['part_detail'] = True
|
kwargs['part_detail'] = True
|
||||||
kwargs['location_detail'] = True
|
kwargs['location_detail'] = True
|
||||||
kwargs['supplier_part_detail'] = True
|
kwargs['supplier_part_detail'] = True
|
||||||
kwargs['test_detail'] = True
|
|
||||||
kwargs['context'] = self.get_serializer_context()
|
kwargs['context'] = self.get_serializer_context()
|
||||||
|
|
||||||
return self.serializer_class(*args, **kwargs)
|
return self.serializer_class(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class StockMetadata(generics.RetrieveUpdateAPIView):
|
class StockMetadata(RetrieveUpdateAPI):
|
||||||
"""API endpoint for viewing / updating StockItem metadata."""
|
"""API endpoint for viewing / updating StockItem metadata."""
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
@ -106,13 +107,13 @@ class StockItemContextMixin:
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class StockItemSerialize(StockItemContextMixin, generics.CreateAPIView):
|
class StockItemSerialize(StockItemContextMixin, CreateAPI):
|
||||||
"""API endpoint for serializing a stock item."""
|
"""API endpoint for serializing a stock item."""
|
||||||
|
|
||||||
serializer_class = StockSerializers.SerializeStockItemSerializer
|
serializer_class = StockSerializers.SerializeStockItemSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemInstall(StockItemContextMixin, generics.CreateAPIView):
|
class StockItemInstall(StockItemContextMixin, CreateAPI):
|
||||||
"""API endpoint for installing a particular stock item into this stock item.
|
"""API endpoint for installing a particular stock item into this stock item.
|
||||||
|
|
||||||
- stock_item.part must be in the BOM for this part
|
- stock_item.part must be in the BOM for this part
|
||||||
@ -123,25 +124,25 @@ class StockItemInstall(StockItemContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = StockSerializers.InstallStockItemSerializer
|
serializer_class = StockSerializers.InstallStockItemSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemUninstall(StockItemContextMixin, generics.CreateAPIView):
|
class StockItemUninstall(StockItemContextMixin, CreateAPI):
|
||||||
"""API endpoint for removing (uninstalling) items from this item."""
|
"""API endpoint for removing (uninstalling) items from this item."""
|
||||||
|
|
||||||
serializer_class = StockSerializers.UninstallStockItemSerializer
|
serializer_class = StockSerializers.UninstallStockItemSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemConvert(StockItemContextMixin, generics.CreateAPIView):
|
class StockItemConvert(StockItemContextMixin, CreateAPI):
|
||||||
"""API endpoint for converting a stock item to a variant part"""
|
"""API endpoint for converting a stock item to a variant part"""
|
||||||
|
|
||||||
serializer_class = StockSerializers.ConvertStockItemSerializer
|
serializer_class = StockSerializers.ConvertStockItemSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemReturn(StockItemContextMixin, generics.CreateAPIView):
|
class StockItemReturn(StockItemContextMixin, CreateAPI):
|
||||||
"""API endpoint for returning a stock item from a customer"""
|
"""API endpoint for returning a stock item from a customer"""
|
||||||
|
|
||||||
serializer_class = StockSerializers.ReturnStockItemSerializer
|
serializer_class = StockSerializers.ReturnStockItemSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockAdjustView(generics.CreateAPIView):
|
class StockAdjustView(CreateAPI):
|
||||||
"""A generic class for handling stocktake actions.
|
"""A generic class for handling stocktake actions.
|
||||||
|
|
||||||
Subclasses exist for:
|
Subclasses exist for:
|
||||||
@ -186,7 +187,7 @@ class StockTransfer(StockAdjustView):
|
|||||||
serializer_class = StockSerializers.StockTransferSerializer
|
serializer_class = StockSerializers.StockTransferSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockAssign(generics.CreateAPIView):
|
class StockAssign(CreateAPI):
|
||||||
"""API endpoint for assigning stock to a particular customer."""
|
"""API endpoint for assigning stock to a particular customer."""
|
||||||
|
|
||||||
queryset = StockItem.objects.all()
|
queryset = StockItem.objects.all()
|
||||||
@ -200,7 +201,7 @@ class StockAssign(generics.CreateAPIView):
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
class StockMerge(generics.CreateAPIView):
|
class StockMerge(CreateAPI):
|
||||||
"""API endpoint for merging multiple stock items."""
|
"""API endpoint for merging multiple stock items."""
|
||||||
|
|
||||||
queryset = StockItem.objects.none()
|
queryset = StockItem.objects.none()
|
||||||
@ -213,7 +214,7 @@ class StockMerge(generics.CreateAPIView):
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
class StockLocationList(generics.ListCreateAPIView):
|
class StockLocationList(ListCreateAPI):
|
||||||
"""API endpoint for list view of StockLocation objects.
|
"""API endpoint for list view of StockLocation objects.
|
||||||
|
|
||||||
- GET: Return list of StockLocation objects
|
- GET: Return list of StockLocation objects
|
||||||
@ -305,7 +306,7 @@ class StockLocationList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class StockLocationTree(generics.ListAPIView):
|
class StockLocationTree(ListAPI):
|
||||||
"""API endpoint for accessing a list of StockLocation objects, ready for rendering as a tree."""
|
"""API endpoint for accessing a list of StockLocation objects, ready for rendering as a tree."""
|
||||||
|
|
||||||
queryset = StockLocation.objects.all()
|
queryset = StockLocation.objects.all()
|
||||||
@ -502,7 +503,8 @@ class StockList(APIDownloadMixin, ListCreateDestroyAPIView):
|
|||||||
|
|
||||||
# Copy the request data, to side-step "mutability" issues
|
# Copy the request data, to side-step "mutability" issues
|
||||||
data = OrderedDict()
|
data = OrderedDict()
|
||||||
data.update(request.data)
|
# Update with cleaned input data
|
||||||
|
data.update(self.clean_data(request.data))
|
||||||
|
|
||||||
quantity = data.get('quantity', None)
|
quantity = data.get('quantity', None)
|
||||||
|
|
||||||
@ -1067,14 +1069,14 @@ class StockAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class StockAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
|
class StockAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for StockItemAttachment."""
|
"""Detail endpoint for StockItemAttachment."""
|
||||||
|
|
||||||
queryset = StockItemAttachment.objects.all()
|
queryset = StockItemAttachment.objects.all()
|
||||||
serializer_class = StockSerializers.StockItemAttachmentSerializer
|
serializer_class = StockSerializers.StockItemAttachmentSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemTestResultDetail(generics.RetrieveUpdateDestroyAPIView):
|
class StockItemTestResultDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for StockItemTestResult."""
|
"""Detail endpoint for StockItemTestResult."""
|
||||||
|
|
||||||
queryset = StockItemTestResult.objects.all()
|
queryset = StockItemTestResult.objects.all()
|
||||||
@ -1170,14 +1172,14 @@ class StockItemTestResultList(ListCreateDestroyAPIView):
|
|||||||
test_result.save()
|
test_result.save()
|
||||||
|
|
||||||
|
|
||||||
class StockTrackingDetail(generics.RetrieveAPIView):
|
class StockTrackingDetail(RetrieveAPI):
|
||||||
"""Detail API endpoint for StockItemTracking model."""
|
"""Detail API endpoint for StockItemTracking model."""
|
||||||
|
|
||||||
queryset = StockItemTracking.objects.all()
|
queryset = StockItemTracking.objects.all()
|
||||||
serializer_class = StockSerializers.StockTrackingSerializer
|
serializer_class = StockSerializers.StockTrackingSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockTrackingList(generics.ListAPIView):
|
class StockTrackingList(ListAPI):
|
||||||
"""API endpoint for list view of StockItemTracking objects.
|
"""API endpoint for list view of StockItemTracking objects.
|
||||||
|
|
||||||
StockItemTracking objects are read-only
|
StockItemTracking objects are read-only
|
||||||
@ -1276,7 +1278,10 @@ class StockTrackingList(generics.ListAPIView):
|
|||||||
Here we override the default 'create' implementation,
|
Here we override the default 'create' implementation,
|
||||||
to save the user information associated with the request object.
|
to save the user information associated with the request object.
|
||||||
"""
|
"""
|
||||||
serializer = self.get_serializer(data=request.data)
|
# Clean up input data
|
||||||
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(data=data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
# Record the user who created this Part object
|
# Record the user who created this Part object
|
||||||
@ -1314,7 +1319,7 @@ class StockTrackingList(generics.ListAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class LocationMetadata(generics.RetrieveUpdateAPIView):
|
class LocationMetadata(RetrieveUpdateAPI):
|
||||||
"""API endpoint for viewing / updating StockLocation metadata."""
|
"""API endpoint for viewing / updating StockLocation metadata."""
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
@ -1324,7 +1329,7 @@ class LocationMetadata(generics.RetrieveUpdateAPIView):
|
|||||||
queryset = StockLocation.objects.all()
|
queryset = StockLocation.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
|
class LocationDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of StockLocation object.
|
"""API endpoint for detail view of StockLocation object.
|
||||||
|
|
||||||
- GET: Return a single StockLocation object
|
- GET: Return a single StockLocation object
|
||||||
|
@ -88,6 +88,12 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""Add some extra annotations to the queryset, performing database queries as efficiently as possible."""
|
"""Add some extra annotations to the queryset, performing database queries as efficiently as possible."""
|
||||||
|
|
||||||
|
queryset = queryset.prefetch_related(
|
||||||
|
'sales_order',
|
||||||
|
'purchase_order',
|
||||||
|
)
|
||||||
|
|
||||||
# Annotate the queryset with the total allocated to sales orders
|
# Annotate the queryset with the total allocated to sales orders
|
||||||
queryset = queryset.annotate(
|
queryset = queryset.annotate(
|
||||||
allocated=Coalesce(
|
allocated=Coalesce(
|
||||||
@ -136,20 +142,14 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
|
|
||||||
location_detail = LocationBriefSerializer(source='location', many=False, read_only=True)
|
location_detail = LocationBriefSerializer(source='location', many=False, read_only=True)
|
||||||
|
|
||||||
tracking_items = serializers.IntegerField(source='tracking_info_count', read_only=True, required=False)
|
|
||||||
|
|
||||||
quantity = InvenTreeDecimalField()
|
quantity = InvenTreeDecimalField()
|
||||||
|
|
||||||
allocated = serializers.FloatField(source='allocation_count', required=False)
|
# Annotated fields
|
||||||
|
tracking_items = serializers.IntegerField(read_only=True, required=False)
|
||||||
|
allocated = serializers.FloatField(required=False)
|
||||||
expired = serializers.BooleanField(required=False, read_only=True)
|
expired = serializers.BooleanField(required=False, read_only=True)
|
||||||
|
|
||||||
stale = serializers.BooleanField(required=False, read_only=True)
|
stale = serializers.BooleanField(required=False, read_only=True)
|
||||||
|
|
||||||
# serial = serializers.CharField(required=False)
|
|
||||||
|
|
||||||
required_tests = serializers.IntegerField(source='required_test_count', read_only=True, required=False)
|
|
||||||
|
|
||||||
purchase_price = InvenTree.serializers.InvenTreeMoneySerializer(
|
purchase_price = InvenTree.serializers.InvenTreeMoneySerializer(
|
||||||
label=_('Purchase Price'),
|
label=_('Purchase Price'),
|
||||||
max_digits=19, decimal_places=4,
|
max_digits=19, decimal_places=4,
|
||||||
@ -171,7 +171,6 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
return str(obj.purchase_price) if obj.purchase_price else '-'
|
return str(obj.purchase_price) if obj.purchase_price else '-'
|
||||||
|
|
||||||
purchase_order_reference = serializers.CharField(source='purchase_order.reference', read_only=True)
|
purchase_order_reference = serializers.CharField(source='purchase_order.reference', read_only=True)
|
||||||
|
|
||||||
sales_order_reference = serializers.CharField(source='sales_order.reference', read_only=True)
|
sales_order_reference = serializers.CharField(source='sales_order.reference', read_only=True)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -179,7 +178,6 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
part_detail = kwargs.pop('part_detail', False)
|
part_detail = kwargs.pop('part_detail', False)
|
||||||
location_detail = kwargs.pop('location_detail', False)
|
location_detail = kwargs.pop('location_detail', False)
|
||||||
supplier_part_detail = kwargs.pop('supplier_part_detail', False)
|
supplier_part_detail = kwargs.pop('supplier_part_detail', False)
|
||||||
test_detail = kwargs.pop('test_detail', False)
|
|
||||||
|
|
||||||
super(StockItemSerializer, self).__init__(*args, **kwargs)
|
super(StockItemSerializer, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
@ -192,9 +190,6 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
if supplier_part_detail is not True:
|
if supplier_part_detail is not True:
|
||||||
self.fields.pop('supplier_part_detail')
|
self.fields.pop('supplier_part_detail')
|
||||||
|
|
||||||
if test_detail is not True:
|
|
||||||
self.fields.pop('required_tests')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -208,7 +203,6 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
'delete_on_deplete',
|
'delete_on_deplete',
|
||||||
'expired',
|
'expired',
|
||||||
'expiry_date',
|
'expiry_date',
|
||||||
'in_stock',
|
|
||||||
'is_building',
|
'is_building',
|
||||||
'link',
|
'link',
|
||||||
'location',
|
'location',
|
||||||
@ -222,7 +216,6 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
'purchase_order_reference',
|
'purchase_order_reference',
|
||||||
'pk',
|
'pk',
|
||||||
'quantity',
|
'quantity',
|
||||||
'required_tests',
|
|
||||||
'sales_order',
|
'sales_order',
|
||||||
'sales_order_reference',
|
'sales_order_reference',
|
||||||
'serial',
|
'serial',
|
||||||
@ -249,7 +242,6 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
'stocktake_date',
|
'stocktake_date',
|
||||||
'stocktake_user',
|
'stocktake_user',
|
||||||
'updated',
|
'updated',
|
||||||
'in_stock'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@
|
|||||||
{% endblock actions %}
|
{% endblock actions %}
|
||||||
|
|
||||||
{% block thumbnail %}
|
{% block thumbnail %}
|
||||||
<img class='part-thumb' {% if item.part.image %}src="{{ item.part.image.url }}"{% else %}src="{% static 'img/blank_image.png' %}"{% endif %}/>
|
<img class='part-thumb' {% if item.part.image %}src="{{ item.part.image.preview.url }}"{% else %}src="{% static 'img/blank_image.png' %}"{% endif %}/>
|
||||||
{% endblock thumbnail %}
|
{% endblock thumbnail %}
|
||||||
|
|
||||||
{% block details %}
|
{% block details %}
|
||||||
|
20
InvenTree/templates/InvenTree/settings/label.html
Normal file
20
InvenTree/templates/InvenTree/settings/label.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{% extends "panel.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load inventree_extras %}
|
||||||
|
|
||||||
|
{% block label %}labels{% endblock %}
|
||||||
|
|
||||||
|
{% block heading %}
|
||||||
|
{% trans "Label Settings" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<table class='table table-striped table-condensed'>
|
||||||
|
<tbody>
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="LABEL_ENABLE" icon='fa-toggle-on' %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="LABEL_DPI" icon='fa-toggle-on' %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -33,6 +33,7 @@
|
|||||||
{% include "InvenTree/settings/login.html" %}
|
{% include "InvenTree/settings/login.html" %}
|
||||||
{% include "InvenTree/settings/barcode.html" %}
|
{% include "InvenTree/settings/barcode.html" %}
|
||||||
{% include "InvenTree/settings/currencies.html" %}
|
{% include "InvenTree/settings/currencies.html" %}
|
||||||
|
{% include "InvenTree/settings/label.html" %}
|
||||||
{% include "InvenTree/settings/report.html" %}
|
{% include "InvenTree/settings/report.html" %}
|
||||||
{% include "InvenTree/settings/part.html" %}
|
{% include "InvenTree/settings/part.html" %}
|
||||||
{% include "InvenTree/settings/category.html" %}
|
{% include "InvenTree/settings/category.html" %}
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
{% include "sidebar_item.html" with label='barcodes' text=text icon="fa-qrcode" %}
|
{% include "sidebar_item.html" with label='barcodes' text=text icon="fa-qrcode" %}
|
||||||
{% trans "Currencies" as text %}
|
{% trans "Currencies" as text %}
|
||||||
{% include "sidebar_item.html" with label='currencies' text=text icon="fa-dollar-sign" %}
|
{% include "sidebar_item.html" with label='currencies' text=text icon="fa-dollar-sign" %}
|
||||||
|
{% trans "Label Printing" as text %}
|
||||||
|
{% include "sidebar_item.html" with label='labels' text=text icon='fa-tag' %}
|
||||||
{% trans "Reporting" as text %}
|
{% trans "Reporting" as text %}
|
||||||
{% include "sidebar_item.html" with label='reporting' text=text icon="fa-file-pdf" %}
|
{% include "sidebar_item.html" with label='reporting' text=text icon="fa-file-pdf" %}
|
||||||
{% trans "Parts" as text %}
|
{% trans "Parts" as text %}
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
<div class='row'>
|
<div class='row'>
|
||||||
<table class='table table-striped table-condensed'>
|
<table class='table table-striped table-condensed'>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% include "InvenTree/settings/setting.html" with key="LABEL_ENABLE" icon='fa-toggle-on' user_setting=True %}
|
|
||||||
{% include "InvenTree/settings/setting.html" with key="LABEL_INLINE" icon='fa-tag' user_setting=True %}
|
{% include "InvenTree/settings/setting.html" with key="LABEL_INLINE" icon='fa-tag' user_setting=True %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -2,108 +2,87 @@
|
|||||||
{% load inventree_extras %}
|
{% load inventree_extras %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
<div class='modal fade modal-fixed-footer' tabindex='-1' role='dialog' id='modal-about'>
|
<table class='table table-striped table-condensed'>
|
||||||
<div class='modal-dialog'>
|
<col width='25'>
|
||||||
<div class='modal-content'>
|
<tr>
|
||||||
<div class="modal-header">
|
<td><span class='fas fa-hashtag'></span></td>
|
||||||
<img src="{% static 'img/inventree.png' %}" height='40px' style='float: left; padding-right: 25px;' alt='Inventree Logo'>
|
<td>{% trans "InvenTree Version" %}</td>
|
||||||
<h4>{% trans "InvenTree Version Information" %}</h4>
|
<td>
|
||||||
<button type='button' class='btn-close' data-bs-dismiss='modal' aria-label='{% trans "Close" %}'></button>
|
<a href="https://github.com/inventree/InvenTree/releases">{% inventree_version %}</a>{% include "clip.html" %}
|
||||||
</div>
|
{% inventree_is_development as dev %}
|
||||||
<div class='modal-form-content-wrapper'>
|
{% if dev %}
|
||||||
<div class='modal-form-content'>
|
<span class='badge badge-right rounded-pill bg-primary'>{% trans "Development Version" %}</span>
|
||||||
<div>
|
{% else %}
|
||||||
<table class='table table-striped table-condensed'>
|
{% if up_to_date %}
|
||||||
<col width='25'>
|
<span class='badge badge-right rounded-pill bg-success'>{% trans "Up to Date" %}</span>
|
||||||
<tr>
|
{% else %}
|
||||||
<td><span class='fas fa-hashtag'></span></td>
|
<span class='badge badge-right rounded-pill bg-info'>{% trans "Update Available" %}</span>
|
||||||
<td>{% trans "InvenTree Version" %}</td>
|
{% endif %}
|
||||||
<td>
|
{% endif %}
|
||||||
<a href="https://github.com/inventree/InvenTree/releases">{% inventree_version %}</a>{% include "clip.html" %}
|
</td>
|
||||||
{% inventree_is_development as dev %}
|
</tr>
|
||||||
{% if dev %}
|
{% if dev %}
|
||||||
<span class='badge badge-right rounded-pill bg-primary'>{% trans "Development Version" %}</span>
|
{% inventree_commit_hash as hash %}
|
||||||
{% else %}
|
{% if hash %}
|
||||||
{% if up_to_date %}
|
<tr>
|
||||||
<span class='badge badge-right rounded-pill bg-success'>{% trans "Up to Date" %}</span>
|
<td><span class='fas fa-code-branch'></span></td>
|
||||||
{% else %}
|
<td>{% trans "Commit Hash" %}</td><td>{{ hash }}{% include "clip.html" %}</td>
|
||||||
<span class='badge badge-right rounded-pill bg-info'>{% trans "Update Available" %}</span>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% inventree_commit_date as commit_date %}
|
||||||
</td>
|
{% if commit_date %}
|
||||||
</tr>
|
<tr>
|
||||||
{% if dev %}
|
<td><span class='fas fa-calendar-alt'></span></td>
|
||||||
{% inventree_commit_hash as hash %}
|
<td>{% trans "Commit Date" %}</td><td>{% render_date commit_date %}{% include "clip.html" %}</td>
|
||||||
{% if hash %}
|
</tr>
|
||||||
<tr>
|
{% endif %}
|
||||||
<td><span class='fas fa-code-branch'></span></td>
|
{% endif %}
|
||||||
<td>{% trans "Commit Hash" %}</td><td>{{ hash }}{% include "clip.html" %}</td>
|
<tr>
|
||||||
</tr>
|
<td><span class='fas fa-book'></span></td>
|
||||||
{% endif %}
|
<td>{% trans "InvenTree Documentation" %}</td>
|
||||||
{% inventree_commit_date as commit_date %}
|
<td><a href="{% inventree_docs_url %}">{% inventree_docs_url %}</a></td>
|
||||||
{% if commit_date %}
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-calendar-alt'></span></td>
|
<td><span class='fas fa-code'></span></td>
|
||||||
<td>{% trans "Commit Date" %}</td><td>{% render_date commit_date %}{% include "clip.html" %}</td>
|
<td>{% trans "API Version" %}</td>
|
||||||
</tr>
|
<td>{% inventree_api_version %}{% include "clip.html" %}</td>
|
||||||
{% endif %}
|
</tr>
|
||||||
{% endif %}
|
<tr>
|
||||||
<tr>
|
<td><span class='fas fa-hashtag'></span></td>
|
||||||
<td><span class='fas fa-book'></span></td>
|
<td>{% trans "Python Version" %}</td>
|
||||||
<td>{% trans "InvenTree Documentation" %}</td>
|
<td>{% python_version %}</td>
|
||||||
<td><a href="{% inventree_docs_url %}">{% inventree_docs_url %}</a></td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td><span class='fas fa-hashtag'></span></td>
|
||||||
<td><span class='fas fa-code'></span></td>
|
<td>{% trans "Django Version" %}</td>
|
||||||
<td>{% trans "API Version" %}</td>
|
<td><a href="https://www.djangoproject.com/">{% django_version %}</a>{% include "clip.html" %}</td>
|
||||||
<td>{% inventree_api_version %}{% include "clip.html" %}</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td><span class='fab fa-github'></span></td>
|
||||||
<td><span class='fas fa-hashtag'></span></td>
|
<td>{% trans "View Code on GitHub" %}</td>
|
||||||
<td>{% trans "Python Version" %}</td>
|
<td><a href="{% inventree_github_url %}">{% inventree_github_url %}</a></td>
|
||||||
<td>{% python_version %}</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td><span class='fas fa-balance-scale'></span></td>
|
||||||
<td><span class='fas fa-hashtag'></span></td>
|
<td>{% trans "Credits" %}</td>
|
||||||
<td>{% trans "Django Version" %}</td>
|
<td><a href="{% inventree_credits_url %}">{% inventree_credits_url %}</a></td>
|
||||||
<td><a href="https://www.djangoproject.com/">{% django_version %}</a>{% include "clip.html" %}</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td><span class='fas fa-mobile-alt'></span></td>
|
||||||
<td><span class='fab fa-github'></span></td>
|
<td>{% trans "Mobile App" %}</td>
|
||||||
<td>{% trans "View Code on GitHub" %}</td>
|
<td><a href="{% inventree_docs_url %}/app/app">{% inventree_docs_url %}/app/app</a></td>
|
||||||
<td><a href="{% inventree_github_url %}">{% inventree_github_url %}</a></td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td><span class='fas fa-bug'></span></td>
|
||||||
<td><span class='fas fa-balance-scale'></span></td>
|
<td>{% trans "Submit Bug Report" %}</td>
|
||||||
<td>{% trans "Credits" %}</td>
|
<td><a href='{% inventree_github_url %}/issues'>{% inventree_github_url %}issues</a></td>
|
||||||
<td><a href="{% inventree_credits_url %}">{% inventree_credits_url %}</a></td>
|
</tr>
|
||||||
</tr>
|
<tr><td></td><td></td>
|
||||||
<tr>
|
<td>
|
||||||
<td><span class='fas fa-mobile-alt'></span></td>
|
<span style="display: none;" id="about-copy-text">{% include "version.html" %}</span>
|
||||||
<td>{% trans "Mobile App" %}</td>
|
<span class="float-right">
|
||||||
<td><a href="{% inventree_docs_url %}/app/app">{% inventree_docs_url %}/app/app</a></td>
|
<button class="btn clip-btn-version" type="button" data-bs-toggle='tooltip' title='{% trans "copy to clipboard" %}'><em class="fas fa-copy"></em> {% trans "copy version information" %}</button>
|
||||||
</tr>
|
</span>
|
||||||
<tr>
|
</td>
|
||||||
<td><span class='fas fa-bug'></span></td>
|
</tr>
|
||||||
<td>{% trans "Submit Bug Report" %}</td>
|
</table>
|
||||||
<td><a href='{% inventree_github_url %}/issues'>{% inventree_github_url %}issues</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr><td></td><td></td>
|
|
||||||
<td>
|
|
||||||
<span style="display: none;" id="about-copy-text">{% include "version.html" %}</span>
|
|
||||||
<span class="float-right">
|
|
||||||
<button class="btn clip-btn-version" type="button" data-bs-toggle='tooltip' title='{% trans "copy to clipboard" %}'><em class="fas fa-copy"></em> {% trans "copy version information" %}</button>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class='modal-footer'>
|
|
||||||
<button type='button' class='btn btn-outline-secondary' data-bs-dismiss='modal'>{% trans "Close" %}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
@ -84,29 +84,14 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Scripts -->
|
|
||||||
<script type="text/javascript" src="{% static 'script/jquery_3.3.1_jquery.min.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/jquery-ui/jquery-ui.min.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'bootstrap/js/bootstrap.bundle.min.js' %}"></script>
|
|
||||||
|
|
||||||
<!-- general JS -->
|
<!-- general JS -->
|
||||||
|
{% include "third_party_js.html" %}
|
||||||
|
|
||||||
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
|
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'notification.js' %}"></script>
|
<script type='text/javascript' src="{% i18n_static 'notification.js' %}"></script>
|
||||||
|
|
||||||
<!-- fontawesome -->
|
|
||||||
<script type='text/javascript' src="{% static 'fontawesome/js/solid.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'fontawesome/js/brands.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'fontawesome/js/fontawesome.js' %}"></script>
|
|
||||||
|
|
||||||
<!-- 3rd party general js -->
|
|
||||||
<script type="text/javascript" src="{% static 'fullcalendar/main.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'fullcalendar/locales-all.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'select2/js/select2.full.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/moment.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/chart.min.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/clipboard.min.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/randomColor.min.js' %}"></script>
|
|
||||||
|
|
||||||
|
|
||||||
<script type='text/javascript'>
|
<script type='text/javascript'>
|
||||||
|
|
||||||
|
@ -39,7 +39,9 @@ for a account and sign in below:{% endblocktrans %}</p>
|
|||||||
<div>{{ login_message | safe }}<hr></div>
|
<div>{{ login_message | safe }}<hr></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="btn-group float-right" role="group">
|
<div class="btn-group float-right" role="group">
|
||||||
<button class="btn btn-success" type="submit">{% trans "Sign In" %}</button>
|
<button class="btn btn-success" type="submit">
|
||||||
|
<span class='fas fa-sign-in-alt'></span> {% trans "Sign In" %}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{% if mail_conf and enable_pwd_forgot %}
|
{% if mail_conf and enable_pwd_forgot %}
|
||||||
<a class="" href="{% url 'account_reset_password' %}"><small>{% trans "Forgot Password?" %}</small></a>
|
<a class="" href="{% url 'account_reset_password' %}"><small>{% trans "Forgot Password?" %}</small></a>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
{% block head_title %}{% trans "Sign Out" %}{% endblock %}
|
{% block head_title %}{% trans "Sign Out" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{% trans "Sign Out" %}</h1>
|
<h3>{% trans "Sign Out" %}</h3>
|
||||||
|
|
||||||
<p>{% trans 'Are you sure you want to sign out?' %}</p>
|
<p>{% trans 'Are you sure you want to sign out?' %}</p>
|
||||||
|
|
||||||
@ -16,10 +16,16 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<hr>
|
<hr>
|
||||||
<div class='btn-group float-right' role='group'>
|
<div class='btn-group float-right' role='group'>
|
||||||
<a type='button' class='btn btn-secondary' href='{% url "index" %}'><span class='fas fa-undo-alt'></span> {% trans "Back to Site" %}</a>
|
<button type="submit" class="btn btn-danger btn-block">
|
||||||
<button type="submit" class="btn btn-danger btn-block">{% trans 'Sign Out' %}</button>
|
<span class='fas fa-sign-out-alt'></span> {% trans 'Sign Out' %}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<a type='button' class='btn btn-secondary' href='{% url "index" %}'>
|
||||||
|
<span class='fas fa-undo-alt'></span> {% trans "Return to Site" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
{% trans 'Authenticate' %}
|
<span class='fas fa-check-circle'></span> {% trans 'Authenticate' %}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -17,17 +17,23 @@
|
|||||||
{% trans 'Backup tokens have been generated, but are not revealed here for security reasons. Press the button below to generate new ones.' %}
|
{% trans 'Backup tokens have been generated, but are not revealed here for security reasons. Press the button below to generate new ones.' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% trans 'No tokens. Press the button below to generate some.' %}
|
{% trans 'No backup tokens are available. Press the button below to generate some.' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<br>
|
<hr>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit" class="btn btn-primary w-100">
|
<div class='btn-group float-right' role='group'>
|
||||||
{% trans 'Generate backup tokens' %}
|
<button type="submit" class="btn btn-primary w-100">
|
||||||
</button>
|
<span class='fas fa-key'></span> {% trans 'Generate Tokens' %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<br>
|
|
||||||
<a href="{% url 'settings' %}" class="btn btn-secondary w-100 btn-sm">{% trans "Back to settings" %}</a>
|
<div>
|
||||||
|
<a type='button' href="{% url 'settings' %}" class="btn btn-secondary">
|
||||||
|
<span class='fas fa-undo-alt'></span> {% trans "Return to Site" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{% extends "account/base.html" %}
|
{% extends "account/base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n crispy_forms_tags %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h3>
|
<h3>
|
||||||
@ -10,9 +10,18 @@
|
|||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit" class="btn btn-danger w-100">
|
{{ form|crispy }}
|
||||||
{% trans 'Disable Two-Factor' %}
|
<hr>
|
||||||
</button>
|
<div class='btn-group float-right' role='group'>
|
||||||
|
<button type="submit" class="btn btn-danger w-100">
|
||||||
|
<span class='fas fa-times-circle'></span> {% trans 'Disable 2FA' %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<a type='button' href="{% url 'settings' %}" class="btn btn-secondary">
|
||||||
|
<span class='fas fa-undo-alt'></span> {% trans "Return to Site" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -31,12 +31,17 @@
|
|||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary btn-block w-100">
|
<hr>
|
||||||
{% trans 'Verify' %}
|
<div class='btn-group float-right' role='group'>
|
||||||
</button>
|
<button type="submit" class="btn btn-primary btn-block w-100">
|
||||||
|
<span class='fas fa-check-circle'></span> {% trans 'Verify' %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<a href="{% url 'settings' %}" class="btn btn-secondary w-100 btn-sm mt-3">{% trans "Back to settings" %}</a>
|
<a type='button' href="{% url 'settings' %}" class="btn btn-secondary">
|
||||||
|
<span class='fas fa-undo-alt'></span> {% trans "Return to Site" %}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
{% settings_value 'REPORT_ENABLE_TEST_REPORT' as test_report_enabled %}
|
{% settings_value 'REPORT_ENABLE_TEST_REPORT' as test_report_enabled %}
|
||||||
{% settings_value "REPORT_ENABLE" as report_enabled %}
|
{% settings_value "REPORT_ENABLE" as report_enabled %}
|
||||||
{% settings_value "SERVER_RESTART_REQUIRED" as server_restart_required %}
|
{% settings_value "SERVER_RESTART_REQUIRED" as server_restart_required %}
|
||||||
{% settings_value "LABEL_ENABLE" with user=user as labels_enabled %}
|
{% settings_value "LABEL_ENABLE" as labels_enabled %}
|
||||||
{% inventree_show_about user as show_about %}
|
{% inventree_show_about user as show_about %}
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@ -39,15 +39,15 @@
|
|||||||
|
|
||||||
<!-- CSS -->
|
<!-- CSS -->
|
||||||
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
|
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'bootstrap-table/bootstrap-table.css' %}">
|
<link rel="stylesheet" href="{% static 'bootstrap-table/bootstrap-table.min.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.css' %}">
|
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.min.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/filter-control/bootstrap-table-filter-control.css' %}">
|
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/filter-control/bootstrap-table-filter-control.css' %}">
|
||||||
<link rel='stylesheet' href='{% static "treegrid/css/jquery.treegrid.css" %}'>
|
<link rel='stylesheet' href='{% static "treegrid/css/jquery.treegrid.css" %}'>
|
||||||
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.min.css' %}">
|
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.min.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.min.css' %}">
|
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.min.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'select2/css/select2.css' %}">
|
<link rel="stylesheet" href="{% static 'select2/css/select2.min.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'select2/css/select2-bootstrap-5-theme.css' %}">
|
<link rel="stylesheet" href="{% static 'select2/css/select2-bootstrap-5-theme.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'fullcalendar/main.css' %}">
|
<link rel="stylesheet" href="{% static 'fullcalendar/main.min.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'script/jquery-ui/jquery-ui.min.css' %}">
|
<link rel="stylesheet" href="{% static 'script/jquery-ui/jquery-ui.min.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'easymde/easymde.min.css' %}">
|
<link rel="stylesheet" href="{% static 'easymde/easymde.min.css' %}">
|
||||||
|
|
||||||
@ -132,83 +132,48 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% include 'modals.html' %}
|
{% include 'modals.html' %}
|
||||||
{% if show_about %}{% include 'about.html' %}{% endif %}
|
|
||||||
{% include "notifications.html" %}
|
{% include "notifications.html" %}
|
||||||
{% include "search.html" %}
|
{% include "search.html" %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Scripts -->
|
{% include "third_party_js.html" %}
|
||||||
<script type="text/javascript" src="{% static 'script/jquery_3.3.1_jquery.min.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/jquery.form.min.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/jquery-ui/jquery-ui.min.js' %}"></script>
|
|
||||||
|
|
||||||
<script type="text/javascript" src="{% static 'bootstrap/js/bootstrap.bundle.min.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/bootstrap/bootstrap-treeview.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'bootstrap-table/bootstrap-table.js' %}"></script>
|
|
||||||
|
|
||||||
<!-- jquery-treegrid -->
|
|
||||||
<script type='text/javascript' src='{% static "treegrid/js/jquery.treegrid.js" %}'></script>
|
|
||||||
<script type='text/javascript' src='{% static "treegrid/js/jquery.treegrid.bootstrap3.js" %}'></script>
|
|
||||||
|
|
||||||
<!-- boostrap-table extensions -->
|
|
||||||
<script type='text/javascript' src='{% static "bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.js" %}'></script>
|
|
||||||
<script type='text/javascript' src='{% static "bootstrap-table/extensions/filter-control/bootstrap-table-filter-control.js" %}'></script>
|
|
||||||
<script type='text/javascript' src='{% static "bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.js" %}'></script>
|
|
||||||
<script type='text/javascript' src='{% static "bootstrap-table/extensions/custom-view/bootstrap-table-custom-view.js" %}'></script>
|
|
||||||
|
|
||||||
<!-- 3rd party general js -->
|
|
||||||
<script type="text/javascript" src="{% static 'fullcalendar/main.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'fullcalendar/locales-all.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'select2/js/select2.full.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/chart.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/moment.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/chartjs-adapter-moment.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'easymde/easymde.min.js' %}"></script>
|
|
||||||
|
|
||||||
<script type='text/javascript' src="{% static 'script/clipboard.min.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/randomColor.min.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/qr-scanner.umd.min.js' %}"></script>
|
|
||||||
|
|
||||||
<!-- general JS -->
|
<!-- general JS -->
|
||||||
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
|
<script defer type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
|
||||||
|
|
||||||
<!-- dynamic javascript templates -->
|
<!-- dynamic javascript templates -->
|
||||||
<script type='text/javascript' src="{% url 'calendar.js' %}"></script>
|
<script defer type='text/javascript' src="{% url 'calendar.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% url 'nav.js' %}"></script>
|
<script defer type='text/javascript' src="{% url 'nav.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% url 'settings.js' %}"></script>
|
<script defer type='text/javascript' src="{% url 'settings.js' %}"></script>
|
||||||
|
|
||||||
<!-- translated javascript templates-->
|
<!-- translated javascript templates-->
|
||||||
<script type='text/javascript' src="{% i18n_static 'api.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'api.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'attachment.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'attachment.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'barcode.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'barcode.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'bom.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'bom.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'build.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'build.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'company.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'company.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'filters.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'filters.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'forms.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'forms.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'helpers.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'helpers.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'label.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'label.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'modals.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'modals.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'model_renderers.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'model_renderers.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'order.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'order.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'part.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'part.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'report.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'report.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'search.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'search.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'stock.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'stock.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'plugin.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'plugin.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'tables.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'tables.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'table_filters.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'table_filters.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% i18n_static 'notification.js' %}"></script>
|
<script defer type='text/javascript' src="{% i18n_static 'notification.js' %}"></script>
|
||||||
|
|
||||||
<script type='text/javascript' src="{% static 'fontawesome/js/solid.min.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'fontawesome/js/regular.min.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'fontawesome/js/brands.min.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'fontawesome/js/fontawesome.min.js' %}"></script>
|
|
||||||
|
|
||||||
{% block js_load %}
|
{% block js_load %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<script type='text/javascript'>
|
<script defer type='text/javascript'>
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
||||||
|
@ -2540,7 +2540,7 @@ function loadBuildTable(table, options) {
|
|||||||
if (value) {
|
if (value) {
|
||||||
return row.responsible_detail.name;
|
return row.responsible_detail.name;
|
||||||
} else {
|
} else {
|
||||||
return '{% trans "No information" %}';
|
return '-';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -274,6 +274,11 @@ function setupNotesField(element, url, options={}) {
|
|||||||
initialValue: initial,
|
initialValue: initial,
|
||||||
toolbar: toolbar_icons,
|
toolbar: toolbar_icons,
|
||||||
shortcuts: [],
|
shortcuts: [],
|
||||||
|
renderingConfig: {
|
||||||
|
markedOptions: {
|
||||||
|
sanitize: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
<div class='card'>
|
<div class='card'>
|
||||||
{% block details_left %}
|
{% block details_left %}
|
||||||
<div class='row'>
|
<div class='row'>
|
||||||
<div class='col' style='max-width: 220px;'>
|
<div class='col' style='max-width: 280px;'>
|
||||||
{% block thumbnail %}
|
{% block thumbnail %}
|
||||||
{% endblock thumbnail %}
|
{% endblock thumbnail %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load inventree_extras %}
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@ -65,15 +66,7 @@
|
|||||||
{% block body_scripts_general %}
|
{% block body_scripts_general %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% include "third_party_js.html" %}
|
||||||
<!-- 3rd party general js -->
|
|
||||||
<script type="text/javascript" src="{% static 'fullcalendar/main.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'fullcalendar/locales-all.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'select2/js/select2.full.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/moment.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/chart.min.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/clipboard.min.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/randomColor.min.js' %}"></script>
|
|
||||||
|
|
||||||
<!-- general JS -->
|
<!-- general JS -->
|
||||||
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
|
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
<td><span class='fas fa-envelope'></span></td>
|
<td><span class='fas fa-envelope'></span></td>
|
||||||
<td>{% trans "Email Settings" %}</td>
|
<td>{% trans "Email Settings" %}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href='{% inventree_docs_url %}/admin/email'>
|
<a href='{% inventree_docs_url %}/settings/email'>
|
||||||
<span class='badge rounded-pill bg-warning'>{% trans "Email settings not configured" %}</span>
|
<span class='badge rounded-pill bg-warning'>{% trans "Email settings not configured" %}</span>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
37
InvenTree/templates/third_party_js.html
Normal file
37
InvenTree/templates/third_party_js.html
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{% load static %}
|
||||||
|
|
||||||
|
<!-- jquery -->
|
||||||
|
<script type="text/javascript" src="{% static 'script/jquery_3.3.1_jquery.min.js' %}"></script>
|
||||||
|
<script type='text/javascript' src="{% static 'script/jquery.form.min.js' %}"></script>
|
||||||
|
<script type='text/javascript' src="{% static 'script/jquery-ui/jquery-ui.min.js' %}"></script>
|
||||||
|
|
||||||
|
<!-- Bootstrap-->
|
||||||
|
<script type="text/javascript" src="{% static 'bootstrap/js/bootstrap.bundle.min.js' %}"></script>
|
||||||
|
|
||||||
|
<!-- Bootstrap Table -->
|
||||||
|
<script defer type='text/javascript' src="{% static 'script/bootstrap/bootstrap-treeview.js' %}"></script>
|
||||||
|
<script defer type='text/javascript' src='{% static "treegrid/js/jquery.treegrid.js" %}'></script>
|
||||||
|
<script defer type='text/javascript' src='{% static "treegrid/js/jquery.treegrid.bootstrap3.js" %}'></script>
|
||||||
|
<script defer type='text/javascript' src="{% static 'bootstrap-table/bootstrap-table.min.js' %}"></script>
|
||||||
|
<script defer type='text/javascript' src='{% static "bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.min.js" %}'></script>
|
||||||
|
<script defer type='text/javascript' src='{% static "bootstrap-table/extensions/filter-control/bootstrap-table-filter-control.min.js" %}'></script>
|
||||||
|
<script defer type='text/javascript' src='{% static "bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.min.js" %}'></script>
|
||||||
|
<script defer type='text/javascript' src='{% static "bootstrap-table/extensions/custom-view/bootstrap-table-custom-view.min.js" %}'></script>
|
||||||
|
|
||||||
|
<!-- fontawesome -->
|
||||||
|
<script defer type='text/javascript' src="{% static 'fontawesome/js/solid.min.js' %}"></script>
|
||||||
|
<script defer type='text/javascript' src="{% static 'fontawesome/js/regular.min.js' %}"></script>
|
||||||
|
<script defer type='text/javascript' src="{% static 'fontawesome/js/brands.min.js' %}"></script>
|
||||||
|
<script defer type='text/javascript' src="{% static 'fontawesome/js/fontawesome.min.js' %}"></script>
|
||||||
|
|
||||||
|
<!-- 3rd party general js -->
|
||||||
|
<script defer type="text/javascript" src="{% static 'fullcalendar/main.min.js' %}"></script>
|
||||||
|
<script defer type="text/javascript" src="{% static 'fullcalendar/locales-all.min.js' %}"></script>
|
||||||
|
<script defer type="text/javascript" src="{% static 'select2/js/select2.full.min.js' %}"></script>
|
||||||
|
<script defer type='text/javascript' src="{% static 'script/moment.js' %}"></script>
|
||||||
|
<script defer type='text/javascript' src="{% static 'script/chart.js' %}"></script>
|
||||||
|
<script defer type='text/javascript' src="{% static 'script/chartjs-adapter-moment.js' %}"></script>
|
||||||
|
<script defer type='text/javascript' src="{% static 'script/clipboard.min.js' %}"></script>
|
||||||
|
<script defer type='text/javascript' src="{% static 'easymde/easymde.min.js' %}"></script>
|
||||||
|
<script defer type='text/javascript' src="{% static 'script/randomColor.min.js' %}"></script>
|
||||||
|
<script defer type='text/javascript' src="{% static 'script/qr-scanner.umd.min.js' %}"></script>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user