mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
QR code improvements
- Display QR codes as links to served images - The qr_code plugin caches these images in the background - Make a qr_code template to push out as a modal form - Create a QRCodeView to simplify display of QR codes - Add option to launchModalForm() to disable the 'submit' button Refactored QR code display for - StockLocation - StockItem - Part
This commit is contained in:
parent
8e65c0a120
commit
9aa1a70f18
@ -124,6 +124,19 @@ DATABASES = {
|
||||
}
|
||||
}
|
||||
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
},
|
||||
'qr-code': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
'LOCATION': 'qr-code-cache',
|
||||
'TIMEOUT': 3600
|
||||
}
|
||||
}
|
||||
|
||||
QR_CODE_CACHE_ALIAS = 'qr-code'
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
|
||||
|
@ -8,6 +8,7 @@ Passes URL lookup downstream to each app as required.
|
||||
from django.conf.urls import url, include
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth import views as auth_views
|
||||
from qr_code import urls as qr_code_urls
|
||||
|
||||
from company.urls import company_urls
|
||||
|
||||
@ -62,6 +63,8 @@ urlpatterns = [
|
||||
url(r'^logout/', auth_views.LogoutView.as_view(template_name='registration/logout.html'), name='logout'),
|
||||
url(r'^admin/', admin.site.urls, name='inventree-admin'),
|
||||
|
||||
url(r'^qr_code/', include(qr_code_urls, namespace='qr_code')),
|
||||
|
||||
url(r'^index/', IndexView.as_view(), name='index'),
|
||||
url(r'^search/', SearchView.as_view(), name='search'),
|
||||
|
||||
|
@ -12,7 +12,7 @@ from django.template.loader import render_to_string
|
||||
from django.http import JsonResponse
|
||||
|
||||
from django.views import View
|
||||
from django.views.generic import UpdateView, CreateView, DeleteView
|
||||
from django.views.generic import UpdateView, CreateView, DeleteView, DetailView
|
||||
from django.views.generic.base import TemplateView
|
||||
|
||||
from part.models import Part
|
||||
@ -144,6 +144,43 @@ class AjaxView(AjaxMixin, View):
|
||||
return self.renderJsonResponse(request)
|
||||
|
||||
|
||||
class QRCodeView(AjaxView):
|
||||
""" An 'AJAXified' view for displaying a QR code.
|
||||
|
||||
Subclasses should implement the get_qr_data(self) function.
|
||||
"""
|
||||
|
||||
ajax_template_name = "qr_code.html"
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.request = request
|
||||
self.pk = self.kwargs['pk']
|
||||
return self.renderJsonResponse(request, None, context=self.get_context_data())
|
||||
|
||||
def get_qr_data(self):
|
||||
""" Returns the text object to render to a QR code.
|
||||
The actual rendering will be handled by the template """
|
||||
|
||||
return None
|
||||
|
||||
def get_context_data(self):
|
||||
""" Get context data for passing to the rendering template.
|
||||
|
||||
Explicity passes the parameter 'qr_data'
|
||||
"""
|
||||
|
||||
context = {}
|
||||
|
||||
qr = self.get_qr_data()
|
||||
|
||||
if qr:
|
||||
context['qr_data'] = qr
|
||||
else:
|
||||
context['error_msg'] = 'Error generating QR code'
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class AjaxCreateView(AjaxMixin, CreateView):
|
||||
|
||||
""" An 'AJAXified' CreateView for creating a new object in the db
|
||||
|
@ -180,7 +180,6 @@ class Part(models.Model):
|
||||
def __str__(self):
|
||||
return "{n} - {d}".format(n=self.name, d=self.description)
|
||||
|
||||
@property
|
||||
def format_barcode(self):
|
||||
""" Return a JSON string for formatting a barcode for this Part object """
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
{% extends "part/part_base.html" %}
|
||||
{% load static %}
|
||||
{% load qr_code %}
|
||||
{% block details %}
|
||||
|
||||
{% include 'part/tabs.html' with tab='detail' %}
|
||||
@ -24,6 +23,7 @@
|
||||
{% else %}
|
||||
<li><a href="#" id='activate-part' title='Activate part'>Activate</a></li>
|
||||
{% endif %}
|
||||
<li><a href='#' id='show-qr-code' title='Generate QR Code'>Show QR Code</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</h3>
|
||||
@ -116,8 +116,6 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% qr_from_text part.format_barcode size="s" image_format="png" error_correction="L" %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js_load %}
|
||||
@ -129,6 +127,15 @@
|
||||
{% block js_ready %}
|
||||
{{ block.super }}
|
||||
|
||||
$("#show-qr-code").click(function() {
|
||||
launchModalForm(
|
||||
"{% url 'part-qr' part.id %}",
|
||||
{
|
||||
no_post: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$("#duplicate-part").click(function() {
|
||||
launchModalForm(
|
||||
"{% url 'part-create' %}",
|
||||
|
@ -22,9 +22,7 @@
|
||||
</div>
|
||||
<div class="media-body">
|
||||
<h4>{{ part.name }}{% if part.active == False %} <i>- INACTIVE</i>{% endif %}</h4>
|
||||
{% if part.description %}
|
||||
<p><i>{{ part.description }}</i></p>
|
||||
{% endif %}
|
||||
{% if part.IPN %}
|
||||
<tr>
|
||||
<td>IPN</td>
|
||||
|
@ -34,8 +34,9 @@ part_attachment_urls = [
|
||||
part_detail_urls = [
|
||||
url(r'^edit/?', views.PartEdit.as_view(), name='part-edit'),
|
||||
url(r'^delete/?', views.PartDelete.as_view(), name='part-delete'),
|
||||
url(r'^track/?', views.PartDetail.as_view(template_name='part/track.html'), name='part-track'),
|
||||
url(r'^bom-export/?', views.BomDownload.as_view(), name='bom-export'),
|
||||
|
||||
url(r'^track/?', views.PartDetail.as_view(template_name='part/track.html'), name='part-track'),
|
||||
url(r'^attachments/?', views.PartDetail.as_view(template_name='part/attachments.html'), name='part-attachments'),
|
||||
url(r'^bom/?', views.PartDetail.as_view(template_name='part/bom.html'), name='part-bom'),
|
||||
url(r'^build/?', views.PartDetail.as_view(template_name='part/build.html'), name='part-build'),
|
||||
@ -44,6 +45,8 @@ part_detail_urls = [
|
||||
url(r'^allocation/?', views.PartDetail.as_view(template_name='part/allocation.html'), name='part-allocation'),
|
||||
url(r'^suppliers/?', views.PartDetail.as_view(template_name='part/supplier.html'), name='part-suppliers'),
|
||||
|
||||
url(r'^qr_code/?', views.PartQRCode.as_view(), name='part-qr'),
|
||||
|
||||
url(r'^thumbnail/?', views.PartImage.as_view(), name='part-image'),
|
||||
|
||||
# Any other URLs go to the part detail page
|
||||
|
@ -27,6 +27,7 @@ from .forms import BomExportForm
|
||||
from .forms import EditSupplierPartForm
|
||||
|
||||
from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
||||
from InvenTree.views import QRCodeView
|
||||
|
||||
from InvenTree.helpers import DownloadFile, str2bool
|
||||
|
||||
@ -234,6 +235,21 @@ class PartDetail(DetailView):
|
||||
return context
|
||||
|
||||
|
||||
class PartQRCode(QRCodeView):
|
||||
""" View for displaying a QR code for a Part object """
|
||||
|
||||
ajax_form_title = "Part QR Code"
|
||||
|
||||
def get_qr_data(self):
|
||||
""" Generate QR code data for the Part """
|
||||
|
||||
try:
|
||||
part = Part.objects.get(id=self.pk)
|
||||
return part.format_barcode()
|
||||
except Part.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
class PartImage(AjaxUpdateView):
|
||||
""" View for uploading Part image """
|
||||
|
||||
|
@ -73,6 +73,17 @@ function afterForm(response, options) {
|
||||
}
|
||||
}
|
||||
|
||||
function modalShowSubmitButton(modal, show=true) {
|
||||
/* Show (or hide) the 'Submit' button for the given modal form
|
||||
*/
|
||||
|
||||
if (show) {
|
||||
$(modal).find('#modal-form-submit').show();
|
||||
} else {
|
||||
$(modal).find('#modal-form-submit').hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function modalEnable(modal, enable=true) {
|
||||
/* Enable (or disable) modal form elements to prevent user input
|
||||
@ -444,6 +455,14 @@ function launchModalForm(url, options = {}) {
|
||||
* an object called 'html_form'
|
||||
*
|
||||
* If the request is NOT successful, displays an appropriate error message.
|
||||
*
|
||||
* options:
|
||||
*
|
||||
* modal - Name of the modal (default = '#modal-form')
|
||||
* data - Data to pass through to the AJAX request to fill the form
|
||||
* submit_text - Text for the submit button (default = 'Submit')
|
||||
* close_text - Text for the close button (default = 'Close')
|
||||
* no_post - If true, only display form data, hide submit button, and disallow POST
|
||||
*/
|
||||
|
||||
var modal = options.modal || '#modal-form';
|
||||
@ -475,7 +494,13 @@ function launchModalForm(url, options = {}) {
|
||||
|
||||
if (response.html_form) {
|
||||
injectModalForm(modal, response.html_form);
|
||||
|
||||
if (options.no_post) {
|
||||
modalShowSubmitButton(modal, false);
|
||||
} else {
|
||||
modalShowSubmitButton(modal, true);
|
||||
handleModalForm(url, options);
|
||||
}
|
||||
|
||||
} else {
|
||||
$(modal).modal('hide');
|
||||
|
@ -36,7 +36,6 @@ class StockLocation(InvenTreeTree):
|
||||
def has_items(self):
|
||||
return self.stock_items.count() > 0
|
||||
|
||||
@property
|
||||
def format_barcode(self):
|
||||
""" Return a JSON string for formatting a barcode for this StockLocation object """
|
||||
|
||||
@ -139,7 +138,6 @@ class StockItem(models.Model):
|
||||
('part', 'serial'),
|
||||
]
|
||||
|
||||
@property
|
||||
def format_barcode(self):
|
||||
""" Return a JSON string for formatting a barcode for this StockItem.
|
||||
Can be used to perform lookup of a stockitem using barcode
|
||||
|
@ -2,8 +2,6 @@
|
||||
{% load static %}
|
||||
{% block content %}
|
||||
|
||||
{% load qr_code %}
|
||||
|
||||
<div class='row'>
|
||||
<div class='col-sm-6'>
|
||||
<h3>Stock Item Details</h3>
|
||||
@ -25,6 +23,8 @@
|
||||
<li><a href='#' id='stock-stocktake' title='Count stock'>Stocktake</a></li>
|
||||
{% endif %}
|
||||
<li><a href="#" id='stock-delete' title='Delete stock item'>Delete stock item</a></li>
|
||||
<hr>
|
||||
<li><a href="#" id='item-qr-code' title='Generate QR code'>Show QR code</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -109,9 +109,6 @@
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
<div class='col-sm-6'>
|
||||
{% qr_from_text item.format_barcode size="s" image_format="png" error_correction="L" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -148,6 +145,13 @@
|
||||
});
|
||||
});
|
||||
|
||||
$("#item-qr-code").click(function() {
|
||||
launchModalForm("{% url 'stock-item-qr' item.id %}",
|
||||
{
|
||||
no_post: true,
|
||||
});
|
||||
});
|
||||
|
||||
{% if item.in_stock %}
|
||||
$("#stock-move").click(function() {
|
||||
launchModalForm(
|
||||
|
@ -1,6 +1,5 @@
|
||||
{% extends "stock/stock_app_base.html" %}
|
||||
{% load static %}
|
||||
{% load qr_code %}
|
||||
{% block content %}
|
||||
|
||||
<div class='row'>
|
||||
@ -24,9 +23,10 @@
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" id='location-edit' title='Edit stock location'>Edit</a></li>
|
||||
<li><a href="#" id='location-delete' title='Delete stock location'>Delete</a></li>
|
||||
<hr>
|
||||
<li><a href="#" id='location-qr-code' title='Generate QR code'>Show QR code</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% qr_from_text location.format_barcode size="s" image_format="png" error_correction="L" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</h3>
|
||||
@ -101,6 +101,13 @@
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#location-qr-code').click(function() {
|
||||
launchModalForm("{% url 'stock-location-qr' location.id %}",
|
||||
{
|
||||
no_post: true,
|
||||
});
|
||||
});
|
||||
|
||||
{% endif %}
|
||||
|
||||
$('#item-create').click(function () {
|
||||
@ -170,5 +177,4 @@
|
||||
},
|
||||
url: "{% url 'api-stock-list' %}",
|
||||
});
|
||||
|
||||
{% endblock %}
|
||||
|
@ -10,6 +10,7 @@ from . import views
|
||||
stock_location_detail_urls = [
|
||||
url(r'^edit/?', views.StockLocationEdit.as_view(), name='stock-location-edit'),
|
||||
url(r'^delete/?', views.StockLocationDelete.as_view(), name='stock-location-delete'),
|
||||
url(r'^qr_code/?', views.StockLocationQRCode.as_view(), name='stock-location-qr'),
|
||||
|
||||
# Anything else
|
||||
url('^.*$', views.StockLocationDetail.as_view(), name='stock-location-detail'),
|
||||
@ -20,6 +21,7 @@ stock_item_detail_urls = [
|
||||
url(r'^delete/?', views.StockItemDelete.as_view(), name='stock-item-delete'),
|
||||
url(r'^move/?', views.StockItemMove.as_view(), name='stock-item-move'),
|
||||
url(r'^stocktake/?', views.StockItemStocktake.as_view(), name='stock-item-stocktake'),
|
||||
url(r'^qr_code/?', views.StockItemQRCode.as_view(), name='stock-item-qr'),
|
||||
|
||||
url('^.*$', views.StockItemDetail.as_view(), name='stock-item-detail'),
|
||||
]
|
||||
|
@ -10,6 +10,7 @@ from django.forms.models import model_to_dict
|
||||
from django.forms import HiddenInput
|
||||
|
||||
from InvenTree.views import AjaxUpdateView, AjaxDeleteView, AjaxCreateView
|
||||
from InvenTree.views import QRCodeView
|
||||
|
||||
from part.models import Part
|
||||
from .models import StockItem, StockLocation, StockItemTracking
|
||||
@ -75,6 +76,34 @@ class StockLocationEdit(AjaxUpdateView):
|
||||
ajax_form_title = 'Edit Stock Location'
|
||||
|
||||
|
||||
class StockLocationQRCode(QRCodeView):
|
||||
""" View for displaying a QR code for a StockLocation object """
|
||||
|
||||
ajax_form_title = "Stock Location QR code"
|
||||
|
||||
def get_qr_data(self):
|
||||
""" Generate QR code data for the StockLocation """
|
||||
try:
|
||||
loc = StockLocation.objects.get(id=self.pk)
|
||||
return loc.format_barcode()
|
||||
except StockLocation.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
class StockItemQRCode(QRCodeView):
|
||||
""" View for displaying a QR code for a StockItem object """
|
||||
|
||||
ajax_form_title = "Stock Item QR Code"
|
||||
|
||||
def get_qr_data(self):
|
||||
""" Generate QR code data for the StockItem """
|
||||
try:
|
||||
item = StockItem.objects.get(id=self.pk)
|
||||
return item.format_barcode()
|
||||
except StockItem.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
class StockItemEdit(AjaxUpdateView):
|
||||
"""
|
||||
View for editing details of a single StockItem
|
||||
|
10
InvenTree/templates/qr_code.html
Normal file
10
InvenTree/templates/qr_code.html
Normal file
@ -0,0 +1,10 @@
|
||||
{% load qr_code %}
|
||||
|
||||
<div class='container' style='width: 80%;'>
|
||||
{% if qr_data %}
|
||||
<img src="{% qr_url_from_text qr_data size='m' error_correction='q' %}" alt="QR Code">
|
||||
{% else %}
|
||||
<b>Error:</b><br>
|
||||
{{ error_msg }}
|
||||
{% endif %}
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user