mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-30 12:36:45 +00:00
Merge branch 'inventree:master' into plugin-2037
This commit is contained in:
commit
d1e1be4253
@ -108,6 +108,13 @@ class InvenTreeMetadata(SimpleMetadata):
|
|||||||
|
|
||||||
model_fields = model_meta.get_field_info(model_class)
|
model_fields = model_meta.get_field_info(model_class)
|
||||||
|
|
||||||
|
model_default_func = getattr(model_class, 'api_defaults', None)
|
||||||
|
|
||||||
|
if model_default_func:
|
||||||
|
model_default_values = model_class.api_defaults(self.request)
|
||||||
|
else:
|
||||||
|
model_default_values = {}
|
||||||
|
|
||||||
# Iterate through simple fields
|
# Iterate through simple fields
|
||||||
for name, field in model_fields.fields.items():
|
for name, field in model_fields.fields.items():
|
||||||
|
|
||||||
@ -123,6 +130,9 @@ class InvenTreeMetadata(SimpleMetadata):
|
|||||||
|
|
||||||
serializer_info[name]['default'] = default
|
serializer_info[name]['default'] = default
|
||||||
|
|
||||||
|
elif name in model_default_values:
|
||||||
|
serializer_info[name]['default'] = model_default_values[name]
|
||||||
|
|
||||||
# Iterate through relations
|
# Iterate through relations
|
||||||
for name, relation in model_fields.relations.items():
|
for name, relation in model_fields.relations.items():
|
||||||
|
|
||||||
@ -141,6 +151,9 @@ class InvenTreeMetadata(SimpleMetadata):
|
|||||||
if 'help_text' not in serializer_info[name] and hasattr(relation.model_field, 'help_text'):
|
if 'help_text' not in serializer_info[name] and hasattr(relation.model_field, 'help_text'):
|
||||||
serializer_info[name]['help_text'] = relation.model_field.help_text
|
serializer_info[name]['help_text'] = relation.model_field.help_text
|
||||||
|
|
||||||
|
if name in model_default_values:
|
||||||
|
serializer_info[name]['default'] = model_default_values[name]
|
||||||
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -34,8 +34,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.login-header {
|
.login-header {
|
||||||
padding-right: 30px;
|
margin-right: 5px;
|
||||||
margin-right: 30px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-container input {
|
.login-container input {
|
||||||
@ -125,18 +124,16 @@
|
|||||||
align-content: center;
|
align-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-container {
|
|
||||||
width: 100%;
|
|
||||||
align-content: center;
|
|
||||||
object-fit: fill;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
border-bottom: 1px solid #ccc;
|
border-bottom: 1px solid #ccc;
|
||||||
background-color: var(--secondary-color);
|
background-color: var(--secondary-color);
|
||||||
box-shadow: 0px 5px 5px rgb(0 0 0 / 5%);
|
box-shadow: 0px 5px 5px rgb(0 0 0 / 5%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inventree-navbar-menu {
|
||||||
|
position: absolute !important;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar-brand {
|
.navbar-brand {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
@ -545,6 +542,7 @@
|
|||||||
.inventree-body {
|
.inventree-body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inventree-pre-content {
|
.inventree-pre-content {
|
||||||
@ -835,7 +833,7 @@ input[type="submit"] {
|
|||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
box-shadow: 2px 2px #DDD;
|
box-shadow: 2px 2px #DDD;
|
||||||
margin-bottom: 20px;
|
margin-bottom: .75rem;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ def get_next_build_number():
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if Build.objects.count() == 0:
|
if Build.objects.count() == 0:
|
||||||
return
|
return '0001'
|
||||||
|
|
||||||
build = Build.objects.exclude(reference=None).last()
|
build = Build.objects.exclude(reference=None).last()
|
||||||
|
|
||||||
@ -107,6 +107,21 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def api_defaults(cls, request):
|
||||||
|
"""
|
||||||
|
Return default values for this model when issuing an API OPTIONS request
|
||||||
|
"""
|
||||||
|
|
||||||
|
defaults = {
|
||||||
|
'reference': get_next_build_number(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if request and request.user:
|
||||||
|
defaults['issued_by'] = request.user.pk
|
||||||
|
|
||||||
|
return defaults
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|
||||||
self.rebuild_reference_field()
|
self.rebuild_reference_field()
|
||||||
|
@ -37,7 +37,7 @@ def get_next_po_number():
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if PurchaseOrder.objects.count() == 0:
|
if PurchaseOrder.objects.count() == 0:
|
||||||
return
|
return '0001'
|
||||||
|
|
||||||
order = PurchaseOrder.objects.exclude(reference=None).last()
|
order = PurchaseOrder.objects.exclude(reference=None).last()
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ def get_next_so_number():
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if SalesOrder.objects.count() == 0:
|
if SalesOrder.objects.count() == 0:
|
||||||
return
|
return '0001'
|
||||||
|
|
||||||
order = SalesOrder.objects.exclude(reference=None).last()
|
order = SalesOrder.objects.exclude(reference=None).last()
|
||||||
|
|
||||||
|
@ -53,6 +53,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Stock adjustment menu -->
|
<!-- Stock adjustment menu -->
|
||||||
<!-- Check permissions and owner -->
|
<!-- Check permissions and owner -->
|
||||||
|
|
||||||
|
{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %}
|
||||||
|
{% if owner_control.value == "True" %}
|
||||||
|
{% authorized_owners item.owner as owners %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if owner_control.value == "False" or owner_control.value == "True" and user in owners or user.is_superuser %}
|
{% if owner_control.value == "False" or owner_control.value == "True" and user in owners or user.is_superuser %}
|
||||||
{% if roles.stock.change and not item.is_building %}
|
{% if roles.stock.change and not item.is_building %}
|
||||||
<div class='btn-group'>
|
<div class='btn-group'>
|
||||||
|
@ -212,26 +212,37 @@
|
|||||||
{% trans "Select language" %}
|
{% trans "Select language" %}
|
||||||
</label>
|
</label>
|
||||||
<div class='form-group input-group mb-3'>
|
<div class='form-group input-group mb-3'>
|
||||||
<select name="language" class="select form-control">
|
<select name="language" class="select form-control w-25">
|
||||||
{% get_current_language as LANGUAGE_CODE %}
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
{% get_available_languages as LANGUAGES %}
|
{% get_available_languages as LANGUAGES %}
|
||||||
{% get_language_info_list for LANGUAGES as languages %}
|
{% get_language_info_list for LANGUAGES as languages %}
|
||||||
|
{% if 'alllang' in request.GET %}{% define True as ALL_LANG %}{% endif %}
|
||||||
{% for language in languages %}
|
{% for language in languages %}
|
||||||
{% define language.code as lang_code %}
|
{% define language.code as lang_code %}
|
||||||
{% define locale_stats|keyvalue:lang_code as lang_translated %}
|
{% define locale_stats|keyvalue:lang_code as lang_translated %}
|
||||||
|
{% if lang_translated > 10 or lang_code == 'en' or lang_code == LANGUAGE_CODE %}{% define True as use_lang %}{% else %}{% define False as use_lang %}{% endif %}
|
||||||
|
{% if ALL_LANG or use_lang %}
|
||||||
<option value="{{ lang_code }}"{% if lang_code == LANGUAGE_CODE %} selected{% endif %}>
|
<option value="{{ lang_code }}"{% if lang_code == LANGUAGE_CODE %} selected{% endif %}>
|
||||||
{{ language.name_local }} ({{ lang_code }})
|
{{ language.name_local }} ({{ lang_code }})
|
||||||
{% if lang_translated %}
|
{% if lang_translated %}
|
||||||
{% blocktrans %}{{ lang_translated }}% translated{% endblocktrans %}
|
{% blocktrans %}{{ lang_translated }}% translated{% endblocktrans %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% trans 'No translations available' %}
|
{% if lang_code == 'en' %}-{% else %}{% trans 'No translations available' %}{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</option>
|
</option>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<div class='input-group-append'>
|
<div class='input-group-append'>
|
||||||
<input type="submit" value="{% trans 'Set Language' %}" class="btn btn btn-primary">
|
<input type="submit" value="{% trans 'Set Language' %}" class="btn btn btn-primary">
|
||||||
</div>
|
</div>
|
||||||
|
<p>{% trans "Some languages are not complete" %}
|
||||||
|
{% if ALL_LANG %}
|
||||||
|
. <a href="{% url 'settings' %}">{% trans "Show only sufficent" %}</a>
|
||||||
|
{% else %}
|
||||||
|
and hidden. <a href="?alllang">{% trans "Show them too" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,12 +22,12 @@
|
|||||||
<a href="https://github.com/inventree/InvenTree/releases">{% inventree_version %}</a>{% include "clip.html" %}
|
<a href="https://github.com/inventree/InvenTree/releases">{% inventree_version %}</a>{% include "clip.html" %}
|
||||||
{% inventree_is_development as dev %}
|
{% inventree_is_development as dev %}
|
||||||
{% if dev %}
|
{% if dev %}
|
||||||
<span class='badge rounded-pill bg-primary'>{% trans "Development Version" %}</span>
|
<span class='badge badge-right rounded-pill bg-primary'>{% trans "Development Version" %}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if up_to_date %}
|
{% if up_to_date %}
|
||||||
<span class='badge rounded-pill bg-success'>{% trans "Up to Date" %}</span>
|
<span class='badge badge-right rounded-pill bg-success'>{% trans "Up to Date" %}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class='badge rounded-pill bg-info'>{% trans "Update Available" %}</span>
|
<span class='badge badge-right rounded-pill bg-info'>{% trans "Update Available" %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
@ -71,8 +71,11 @@
|
|||||||
{% include "spacer.html" %}
|
{% include "spacer.html" %}
|
||||||
<span class='float-right'><h3>{% inventree_title %}</h3></span>
|
<span class='float-right'><h3>{% inventree_title %}</h3></span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='container-fluid'>
|
||||||
<hr>
|
<hr>
|
||||||
<div class='container-fluid'>{% block content %}{% endblock %}</div>
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,6 +32,7 @@ for a account and sign in below:{% endblocktrans %}</p>
|
|||||||
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
|
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<hr>
|
||||||
<div class="btn-group float-right" role="group">
|
<div class="btn-group float-right" role="group">
|
||||||
<button class="btn btn-success" type="submit">{% trans "Sign In" %}</button>
|
<button class="btn btn-success" type="submit">{% trans "Sign In" %}</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
{% if redirect_field_value %}
|
{% if redirect_field_value %}
|
||||||
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
|
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<hr>
|
||||||
<div class='btn-group float-right' role='group'>
|
<div class='btn-group float-right' role='group'>
|
||||||
<a type='button' class='btn btn-secondary' href='{% url "index" %}'><span class='fas fa-undo-alt'></span> {% trans "Back to Site" %}</a>
|
<a type='button' class='btn btn-secondary' href='{% url "index" %}'><span class='fas fa-undo-alt'></span> {% trans "Back to Site" %}</a>
|
||||||
<button type="submit" class="btn btn-danger btn-block">{% trans 'Sign Out' %}</button>
|
<button type="submit" class="btn btn-danger btn-block">{% trans 'Sign Out' %}</button>
|
||||||
|
@ -83,7 +83,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<main class='col ps-md-2 pt-2'>
|
<main class='col ps-md-2 pt-2 pe-2'>
|
||||||
|
|
||||||
{% block alerts %}
|
{% block alerts %}
|
||||||
<div class='notification-area' id='alerts'>
|
<div class='notification-area' id='alerts'>
|
||||||
@ -190,6 +190,18 @@ $(document).ready(function () {
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
moment.locale('{{ request.LANGUAGE_CODE }}');
|
moment.locale('{{ request.LANGUAGE_CODE }}');
|
||||||
|
|
||||||
|
// Account notifications
|
||||||
|
{% if messages %}
|
||||||
|
{% for message in messages %}
|
||||||
|
showMessage(
|
||||||
|
'{{ message }}',
|
||||||
|
{
|
||||||
|
style: 'info',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -43,11 +43,18 @@ function buildFormFields() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
sales_order: {
|
sales_order: {
|
||||||
|
icon: 'fa-truck',
|
||||||
},
|
},
|
||||||
batch: {},
|
batch: {},
|
||||||
target_date: {},
|
target_date: {
|
||||||
take_from: {},
|
icon: 'fa-calendar-alt',
|
||||||
destination: {},
|
},
|
||||||
|
take_from: {
|
||||||
|
icon: 'fa-sitemap',
|
||||||
|
},
|
||||||
|
destination: {
|
||||||
|
icon: 'fa-sitemap',
|
||||||
|
},
|
||||||
link: {
|
link: {
|
||||||
icon: 'fa-link',
|
icon: 'fa-link',
|
||||||
},
|
},
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
renderStockLocation,
|
renderStockLocation,
|
||||||
renderSupplierPart,
|
renderSupplierPart,
|
||||||
renderUser,
|
renderUser,
|
||||||
showAlertDialog,
|
|
||||||
showAlertOrCache,
|
showAlertOrCache,
|
||||||
showApiError,
|
showApiError,
|
||||||
*/
|
*/
|
||||||
@ -347,10 +346,12 @@ function constructForm(url, options) {
|
|||||||
constructCreateForm(OPTIONS.actions.POST, options);
|
constructCreateForm(OPTIONS.actions.POST, options);
|
||||||
} else {
|
} else {
|
||||||
// User does not have permission to POST to the endpoint
|
// User does not have permission to POST to the endpoint
|
||||||
showAlertDialog(
|
showMessage('{% trans "Action Prohibited" %}', {
|
||||||
'{% trans "Action Prohibited" %}',
|
style: 'danger',
|
||||||
'{% trans "Create operation not allowed" %}'
|
details: '{% trans "Create operation not allowed" %}',
|
||||||
);
|
icon: 'fas fa-user-times',
|
||||||
|
});
|
||||||
|
|
||||||
console.log(`'POST action unavailable at ${url}`);
|
console.log(`'POST action unavailable at ${url}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -360,10 +361,12 @@ function constructForm(url, options) {
|
|||||||
constructChangeForm(OPTIONS.actions.PUT, options);
|
constructChangeForm(OPTIONS.actions.PUT, options);
|
||||||
} else {
|
} else {
|
||||||
// User does not have permission to PUT/PATCH to the endpoint
|
// User does not have permission to PUT/PATCH to the endpoint
|
||||||
showAlertDialog(
|
showMessage('{% trans "Action Prohibited" %}', {
|
||||||
'{% trans "Action Prohibited" %}',
|
style: 'danger',
|
||||||
'{% trans "Update operation not allowed" %}'
|
details: '{% trans "Update operation not allowed" %}',
|
||||||
);
|
icon: 'fas fa-user-times',
|
||||||
|
});
|
||||||
|
|
||||||
console.log(`${options.method} action unavailable at ${url}`);
|
console.log(`${options.method} action unavailable at ${url}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -372,10 +375,12 @@ function constructForm(url, options) {
|
|||||||
constructDeleteForm(OPTIONS.actions.DELETE, options);
|
constructDeleteForm(OPTIONS.actions.DELETE, options);
|
||||||
} else {
|
} else {
|
||||||
// User does not have permission to DELETE to the endpoint
|
// User does not have permission to DELETE to the endpoint
|
||||||
showAlertDialog(
|
showMessage('{% trans "Action Prohibited" %}', {
|
||||||
'{% trans "Action Prohibited" %}',
|
style: 'danger',
|
||||||
'{% trans "Delete operation not allowed" %}'
|
details: '{% trans "Delete operation not allowed" %}',
|
||||||
);
|
icon: 'fas fa-user-times',
|
||||||
|
});
|
||||||
|
|
||||||
console.log(`DELETE action unavailable at ${url}`);
|
console.log(`DELETE action unavailable at ${url}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -384,10 +389,12 @@ function constructForm(url, options) {
|
|||||||
// TODO?
|
// TODO?
|
||||||
} else {
|
} else {
|
||||||
// User does not have permission to GET to the endpoint
|
// User does not have permission to GET to the endpoint
|
||||||
showAlertDialog(
|
showMessage('{% trans "Action Prohibited" %}', {
|
||||||
'{% trans "Action Prohibited" %}',
|
style: 'danger',
|
||||||
'{% trans "View operation not allowed" %}'
|
details: '{% trans "View operation not allowed" %}',
|
||||||
);
|
icon: 'fas fa-user-times',
|
||||||
|
});
|
||||||
|
|
||||||
console.log(`GET action unavailable at ${url}`);
|
console.log(`GET action unavailable at ${url}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -12,9 +12,6 @@
|
|||||||
<div class="navbar-header clearfix content-heading">
|
<div class="navbar-header clearfix content-heading">
|
||||||
<a class="navbar-brand" id='logo' href="{% url 'index' %}" style="padding-top: 7px; padding-bottom: 5px;"><img src="{% static 'img/inventree.png' %}" width="32" height="32" style="display:block; margin: auto;"/></a>
|
<a class="navbar-brand" id='logo' href="{% url 'index' %}" style="padding-top: 7px; padding-bottom: 5px;"><img src="{% static 'img/inventree.png' %}" width="32" height="32" style="display:block; margin: auto;"/></a>
|
||||||
</div>
|
</div>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-objects" aria-label="Toggle navigation">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="navbar-collapse collapse" id="navbar-objects">
|
<div class="navbar-collapse collapse" id="navbar-objects">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
{% if roles.part.view %}
|
{% if roles.part.view %}
|
||||||
@ -84,19 +81,24 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% include "search_form.html" %}
|
{% include "search_form.html" %}
|
||||||
<ul class='navbar-nav'>
|
<ul class='navbar-nav flex-row'>
|
||||||
{% if barcodes %}
|
{% if barcodes %}
|
||||||
<li id='navbar-barcode-li'>
|
<li class='nav-item' id='navbar-barcode-li'>
|
||||||
<button id='barcode-scan' class='btn btn-secondary' title='{% trans "Scan Barcode" %}'>
|
<button id='barcode-scan' class='btn btn-secondary' title='{% trans "Scan Barcode" %}'>
|
||||||
<span class='fas fa-qrcode'></span>
|
<span class='fas fa-qrcode'></span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<li class='nav-item' id='navbar-barcode-li'>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-objects" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
<li class='nav-item dropdown'>
|
<li class='nav-item dropdown'>
|
||||||
<a class='nav-link dropdown-toggle' href='#' id='userMenuDropdown' role='button' data-bs-toggle='dropdown'>
|
<a class='nav-link dropdown-toggle' href='#' id='userMenuDropdown' role='button' data-bs-toggle='dropdown'>
|
||||||
<span class='fas fa-user'></span> <strong>{{ user.get_username }}</strong>
|
<span class='fas fa-user'></span> <strong>{{ user.get_username }}</strong>
|
||||||
</a>
|
</a>
|
||||||
<ul class='dropdown-menu dropdown-menu-end'>
|
<ul class='dropdown-menu dropdown-menu-end inventree-navbar-menu'>
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
{% if user.is_staff %}
|
{% if user.is_staff %}
|
||||||
<li><a class='dropdown-item' href="/admin/"><span class="fas fa-user"></span> {% trans "Admin" %}</a></li>
|
<li><a class='dropdown-item' href="/admin/"><span class="fas fa-user"></span> {% trans "Admin" %}</a></li>
|
||||||
@ -130,7 +132,6 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div class='container' style='width: 80%;'>
|
<div class='container' style='width: 80%;'>
|
||||||
{% if qr_data %}
|
{% if qr_data %}
|
||||||
<div class='qr-container'>
|
<div class='d-flex justify-content-center'>
|
||||||
<img src="{% qrcode qr_data %}">
|
<img src="{% qrcode qr_data %}">
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -7,15 +7,16 @@ from django.core.exceptions import ObjectDoesNotExist
|
|||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url, include
|
||||||
|
|
||||||
from rest_framework import generics, permissions
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
|
|
||||||
|
from rest_framework import filters, generics, permissions
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
from .serializers import UserSerializer, OwnerSerializer
|
from users.models import RuleSet, Owner, check_user_role
|
||||||
|
from users.serializers import UserSerializer, OwnerSerializer
|
||||||
from .models import RuleSet, Owner, check_user_role
|
|
||||||
|
|
||||||
|
|
||||||
class OwnerList(generics.ListAPIView):
|
class OwnerList(generics.ListAPIView):
|
||||||
@ -26,6 +27,37 @@ class OwnerList(generics.ListAPIView):
|
|||||||
queryset = Owner.objects.all()
|
queryset = Owner.objects.all()
|
||||||
serializer_class = OwnerSerializer
|
serializer_class = OwnerSerializer
|
||||||
|
|
||||||
|
def filter_queryset(self, queryset):
|
||||||
|
"""
|
||||||
|
Implement text search for the "owner" model.
|
||||||
|
|
||||||
|
Note that an "owner" can be either a group, or a user,
|
||||||
|
so we cannot do a direct text search.
|
||||||
|
|
||||||
|
A "hack" here is to post-process the queryset and simply
|
||||||
|
remove any values which do not match.
|
||||||
|
|
||||||
|
It is not necessarily "efficient" to do it this way,
|
||||||
|
but until we determine a better way, this is what we have...
|
||||||
|
"""
|
||||||
|
|
||||||
|
search_term = str(self.request.query_params.get('search', '')).lower()
|
||||||
|
|
||||||
|
queryset = super().filter_queryset(queryset)
|
||||||
|
|
||||||
|
if not search_term:
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
results = []
|
||||||
|
|
||||||
|
# Extract search term f
|
||||||
|
|
||||||
|
for result in queryset.all():
|
||||||
|
if search_term in result.name().lower():
|
||||||
|
results.append(result)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
class OwnerDetail(generics.RetrieveAPIView):
|
class OwnerDetail(generics.RetrieveAPIView):
|
||||||
"""
|
"""
|
||||||
@ -96,6 +128,17 @@ class UserList(generics.ListAPIView):
|
|||||||
serializer_class = UserSerializer
|
serializer_class = UserSerializer
|
||||||
permission_classes = (permissions.IsAuthenticated,)
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
|
|
||||||
|
filter_backends = [
|
||||||
|
DjangoFilterBackend,
|
||||||
|
filters.SearchFilter,
|
||||||
|
]
|
||||||
|
|
||||||
|
search_fields = [
|
||||||
|
'first_name',
|
||||||
|
'last_name',
|
||||||
|
'username',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class GetAuthToken(APIView):
|
class GetAuthToken(APIView):
|
||||||
""" Return authentication token for an authenticated user. """
|
""" Return authentication token for an authenticated user. """
|
||||||
|
Loading…
x
Reference in New Issue
Block a user