mirror of
https://github.com/inventree/InvenTree.git
synced 2025-05-11 17:53:00 +00:00
Part page loading improvements (#3185)
* Lazy load the pricing bom table when the "pricing" tab is selected * Update django-debug-toolbar configuration * Major refactoring for the 'can_build' function - Use a single annotated query to the db, rather than a for loop (which is what a caveman would use) - Query performance is greatly improved - Also refactors existing variant-part-stock subquery code, to make it re-usable * Use minified JS and CSS where possible * Render a 'preview' version of each part image - Saves load time when the image is quite large - Adds a data migration to render out the new variation * Adds 'preview' version of company images * Defer loading of javascript files Note: some cannot be deferred - jquery in particular * Crucial bugfix for user roles context - Previously was *not* being calculated correctly - A non-superuser role would most likely display pages incorrectly * Prevent loading of "about" on every page - Load dynamically when requested - Takes ~400ms! - Cuts out a lot of fat * Match displayed image size to preview image size * Utilize caching framework for accessing user "role" information - Reduces number of DB queries required by rendering framework * Remove redundant query elements * Remove 'stock' field from PartBrief serializer - A calculated field on a serializer is a *bad idea* when that calculation requires a DB hit * Query improvements for StockItem serializer - Remove calculated fields - Fix annotations * Bug fixes * Remove JS load test - Loading of JS files is now deferred, so the unit test does not work as it used to * Fix broken template for "maintenance" page * Remove thumbnail generation migrations - Already performed manually as part of ''invoke migrate" - Running as a migration causes unit test problems - Not sensible to run this as a data-migration anyway * tweak for build table
This commit is contained in:
parent
0d01ea2f2e
commit
74bec86675
@ -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}
|
||||||
|
@ -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
|
||||||
|
@ -750,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."""
|
||||||
|
|
||||||
|
@ -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 %}/>
|
||||||
|
@ -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 %}/>
|
||||||
|
@ -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 %}/>
|
||||||
|
@ -74,7 +74,6 @@ class StockDetail(RetrieveUpdateDestroyAPI):
|
|||||||
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)
|
||||||
|
@ -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 %}
|
||||||
|
@ -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,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 '-';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -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>
|
||||||
|
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>
|
@ -6,6 +6,7 @@ from django.contrib.auth import get_user_model
|
|||||||
from django.contrib.auth.models import Group, Permission
|
from django.contrib.auth.models import Group, Permission
|
||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.core.cache import cache
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q, UniqueConstraint
|
from django.db.models import Q, UniqueConstraint
|
||||||
from django.db.models.signals import post_delete, post_save
|
from django.db.models.signals import post_delete, post_save
|
||||||
@ -474,13 +475,19 @@ def update_group_roles(group, debug=False):
|
|||||||
logger.info(f"Adding permission {child_perm} to group {group.name}")
|
logger.info(f"Adding permission {child_perm} to group {group.name}")
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Group, dispatch_uid='create_missing_rule_sets')
|
def clear_user_role_cache(user):
|
||||||
def create_missing_rule_sets(sender, instance, **kwargs):
|
"""Remove user role permission information from the cache.
|
||||||
"""Called *after* a Group object is saved.
|
|
||||||
|
|
||||||
As the linked RuleSet instances are saved *before* the Group, then we can now use these RuleSet values to update the group permissions.
|
- This function is called whenever the user / group is updated
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user: The User object to be expunged from the cache
|
||||||
"""
|
"""
|
||||||
update_group_roles(instance)
|
|
||||||
|
for role in RuleSet.RULESET_MODELS.keys():
|
||||||
|
for perm in ['add', 'change', 'view', 'delete']:
|
||||||
|
key = f"role_{user}_{role}_{perm}"
|
||||||
|
cache.delete(key)
|
||||||
|
|
||||||
|
|
||||||
def check_user_role(user, role, permission):
|
def check_user_role(user, role, permission):
|
||||||
@ -491,6 +498,17 @@ def check_user_role(user, role, permission):
|
|||||||
if user.is_superuser:
|
if user.is_superuser:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# First, check the cache
|
||||||
|
key = f"role_{user}_{role}_{permission}"
|
||||||
|
|
||||||
|
result = cache.get(key)
|
||||||
|
|
||||||
|
if result is not None:
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Default for no match
|
||||||
|
result = False
|
||||||
|
|
||||||
for group in user.groups.all():
|
for group in user.groups.all():
|
||||||
|
|
||||||
for rule in group.rule_sets.all():
|
for rule in group.rule_sets.all():
|
||||||
@ -498,19 +516,24 @@ def check_user_role(user, role, permission):
|
|||||||
if rule.name == role:
|
if rule.name == role:
|
||||||
|
|
||||||
if permission == 'add' and rule.can_add:
|
if permission == 'add' and rule.can_add:
|
||||||
return True
|
result = True
|
||||||
|
break
|
||||||
|
|
||||||
if permission == 'change' and rule.can_change:
|
if permission == 'change' and rule.can_change:
|
||||||
return True
|
result = True
|
||||||
|
break
|
||||||
|
|
||||||
if permission == 'view' and rule.can_view:
|
if permission == 'view' and rule.can_view:
|
||||||
return True
|
result = True
|
||||||
|
break
|
||||||
|
|
||||||
if permission == 'delete' and rule.can_delete:
|
if permission == 'delete' and rule.can_delete:
|
||||||
return True
|
result = True
|
||||||
|
break
|
||||||
|
|
||||||
# No matching permissions found
|
# Save result to cache
|
||||||
return False
|
cache.set(key, result, timeout=3600)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class Owner(models.Model):
|
class Owner(models.Model):
|
||||||
@ -659,3 +682,22 @@ def delete_owner(sender, instance, **kwargs):
|
|||||||
"""Callback function to delete an owner instance after either a new group or user instance is deleted."""
|
"""Callback function to delete an owner instance after either a new group or user instance is deleted."""
|
||||||
owner = Owner.get_owner(instance)
|
owner = Owner.get_owner(instance)
|
||||||
owner.delete()
|
owner.delete()
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=get_user_model(), dispatch_uid='clear_user_cache')
|
||||||
|
def clear_user_cache(sender, instance, **kwargs):
|
||||||
|
"""Callback function when a user object is saved"""
|
||||||
|
|
||||||
|
clear_user_role_cache(instance)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=Group, dispatch_uid='create_missing_rule_sets')
|
||||||
|
def create_missing_rule_sets(sender, instance, **kwargs):
|
||||||
|
"""Called *after* a Group object is saved.
|
||||||
|
|
||||||
|
As the linked RuleSet instances are saved *before* the Group, then we can now use these RuleSet values to update the group permissions.
|
||||||
|
"""
|
||||||
|
update_group_roles(instance)
|
||||||
|
|
||||||
|
for user in get_user_model().objects.filter(groups__name=instance.name):
|
||||||
|
clear_user_role_cache(user)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user