mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Client side QR Codes (#4357)
* Add JS for qrcodejs Ref: https://davidshimjs.github.io/qrcodejs/ * Simple function for rendering a QR code * Refactor QR code view for Part * Replace QR code view for SupplierPart * Refactor QR codes for stock item and stock location models * Remove base QRCodeView entirely
This commit is contained in:
parent
0f445ea6e4
commit
cde2050236
1
InvenTree/InvenTree/static/script/qrcode.min.js
vendored
Normal file
1
InvenTree/InvenTree/static/script/qrcode.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -320,44 +320,6 @@ 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):
|
||||
"""Return json with qr-code data."""
|
||||
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 AjaxUpdateView(AjaxMixin, UpdateView):
|
||||
"""An 'AJAXified' UpdateView for updating an object in the db.
|
||||
|
||||
|
@ -270,10 +270,10 @@ src="{% static 'img/blank_image.png' %}"
|
||||
{% if barcodes %}
|
||||
|
||||
$("#show-qr-code").click(function() {
|
||||
launchModalForm("{% url 'supplier-part-qr' part.pk %}",
|
||||
{
|
||||
no_post: true,
|
||||
});
|
||||
showQRDialog(
|
||||
'{% trans "Supplier Part QR Code" %}',
|
||||
'{"supplierpart": {{ part.pk }}}'
|
||||
);
|
||||
});
|
||||
|
||||
$("#barcode-link").click(function() {
|
||||
|
@ -26,7 +26,6 @@ manufacturer_part_urls = [
|
||||
|
||||
supplier_part_urls = [
|
||||
re_path(r'^(?P<pk>\d+)/', include([
|
||||
re_path('^qr_code/?', views.SupplierPartQRCode.as_view(), name='supplier-part-qr'),
|
||||
re_path('^.*$', views.SupplierPartDetail.as_view(template_name='company/supplier_part.html'), name='supplier-part-detail'),
|
||||
]))
|
||||
|
||||
|
@ -4,7 +4,7 @@ from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import DetailView, ListView
|
||||
|
||||
from InvenTree.views import InvenTreeRoleMixin, QRCodeView
|
||||
from InvenTree.views import InvenTreeRoleMixin
|
||||
from plugin.views import InvenTreePluginViewMixin
|
||||
|
||||
from .models import Company, ManufacturerPart, SupplierPart
|
||||
@ -112,18 +112,3 @@ class SupplierPartDetail(InvenTreePluginViewMixin, DetailView):
|
||||
context_object_name = 'part'
|
||||
queryset = SupplierPart.objects.all()
|
||||
permission_required = 'purchase_order.view'
|
||||
|
||||
|
||||
class SupplierPartQRCode(QRCodeView):
|
||||
"""View for displaying a QR code for a StockItem object."""
|
||||
|
||||
ajax_form_title = _("Stock Item QR Code")
|
||||
role_required = 'stock.view'
|
||||
|
||||
def get_qr_data(self):
|
||||
"""Generate QR code data for the StockItem."""
|
||||
try:
|
||||
part = SupplierPart.objects.get(id=self.pk)
|
||||
return part.format_barcode()
|
||||
except SupplierPart.DoesNotExist:
|
||||
return None
|
||||
|
@ -2118,9 +2118,6 @@ part_api_urls = [
|
||||
# BOM download
|
||||
re_path(r'^bom-download/?', views.BomDownload.as_view(), name='api-bom-download'),
|
||||
|
||||
# QR code download
|
||||
re_path(r'^qr_code/?', views.PartQRCode.as_view(), name='api-part-qr'),
|
||||
|
||||
# Old pricing endpoint
|
||||
re_path(r'^pricing2/', views.PartPricing.as_view(), name='part-pricing'),
|
||||
|
||||
|
@ -447,11 +447,9 @@
|
||||
|
||||
{% if barcodes %}
|
||||
$("#show-qr-code").click(function() {
|
||||
launchModalForm(
|
||||
"{% url 'api-part-qr' part.id %}",
|
||||
{
|
||||
no_post: true,
|
||||
}
|
||||
showQRDialog(
|
||||
'{% trans "Part QR Code" %}',
|
||||
'{"part": {{ part.pk }}}',
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -113,28 +113,3 @@ class PartDetailTest(PartViewTestCase):
|
||||
response = self.client.get(reverse('api-bom-download', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('streaming_content', dir(response))
|
||||
|
||||
|
||||
class PartQRTest(PartViewTestCase):
|
||||
"""Tests for the Part QR Code AJAX view."""
|
||||
|
||||
def test_html_redirect(self):
|
||||
"""A HTML request for a QR code should be redirected (use an AJAX request instead)"""
|
||||
response = self.client.get(reverse('api-part-qr', args=(1,)))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_valid_part(self):
|
||||
"""Test QR code response for a Part"""
|
||||
response = self.client.get(reverse('api-part-qr', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
data = str(response.content)
|
||||
|
||||
self.assertIn('Part QR Code', data)
|
||||
self.assertIn('<img src=', data)
|
||||
|
||||
def test_invalid_part(self):
|
||||
"""Test response for an invalid Part ID value"""
|
||||
response = self.client.get(reverse('api-part-qr', args=(9999,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -16,8 +16,7 @@ from common.models import InvenTreeSetting
|
||||
from common.views import FileManagementAjaxView, FileManagementFormView
|
||||
from company.models import SupplierPart
|
||||
from InvenTree.helpers import str2bool, str2int
|
||||
from InvenTree.views import (AjaxUpdateView, AjaxView, InvenTreeRoleMixin,
|
||||
QRCodeView)
|
||||
from InvenTree.views import AjaxUpdateView, AjaxView, InvenTreeRoleMixin
|
||||
from plugin.views import InvenTreePluginViewMixin
|
||||
from stock.models import StockItem, StockLocation
|
||||
|
||||
@ -372,22 +371,6 @@ class PartDetailFromIPN(PartDetail):
|
||||
return super(PartDetailFromIPN, self).get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class PartQRCode(QRCodeView):
|
||||
"""View for displaying a QR code for a Part object."""
|
||||
|
||||
ajax_form_title = _("Part QR Code")
|
||||
|
||||
role_required = 'part.view'
|
||||
|
||||
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 PartImageSelect(AjaxUpdateView):
|
||||
"""View for selecting Part image from existing images."""
|
||||
|
||||
|
@ -523,10 +523,10 @@ $('#stock-edit-status').click(function () {
|
||||
{% endif %}
|
||||
|
||||
$("#show-qr-code").click(function() {
|
||||
launchModalForm("{% url 'stock-item-qr' item.id %}",
|
||||
{
|
||||
no_post: true,
|
||||
});
|
||||
showQRDialog(
|
||||
'{% trans "Stock Item QR Code" %}',
|
||||
'{"stockitem": {{ item.pk }}}',
|
||||
);
|
||||
});
|
||||
|
||||
{% if barcodes %}
|
||||
|
@ -399,10 +399,10 @@
|
||||
|
||||
{% if barcodes %}
|
||||
$('#show-qr-code').click(function() {
|
||||
launchModalForm("{% url 'stock-location-qr' location.id %}",
|
||||
{
|
||||
no_post: true,
|
||||
});
|
||||
showQRDialog(
|
||||
'{% trans "Stock Location QR Code" %}',
|
||||
'{"stocklocation": {{ location.pk }}}'
|
||||
);
|
||||
});
|
||||
|
||||
$("#barcode-link").click(function() {
|
||||
|
@ -7,8 +7,6 @@ from stock import views
|
||||
location_urls = [
|
||||
|
||||
re_path(r'^(?P<pk>\d+)/', include([
|
||||
re_path(r'^qr_code/?', views.StockLocationQRCode.as_view(), name='stock-location-qr'),
|
||||
|
||||
# Anything else - direct to the location detail view
|
||||
re_path('^.*$', views.StockLocationDetail.as_view(), name='stock-location-detail'),
|
||||
])),
|
||||
@ -16,8 +14,6 @@ location_urls = [
|
||||
]
|
||||
|
||||
stock_item_detail_urls = [
|
||||
re_path(r'^qr_code/', views.StockItemQRCode.as_view(), name='stock-item-qr'),
|
||||
|
||||
# Anything else - direct to the item detail view
|
||||
re_path('^.*$', views.StockItemDetail.as_view(), name='stock-item-detail'),
|
||||
]
|
||||
|
@ -2,11 +2,10 @@
|
||||
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import DetailView, ListView
|
||||
|
||||
import common.settings
|
||||
from InvenTree.views import InvenTreeRoleMixin, QRCodeView
|
||||
from InvenTree.views import InvenTreeRoleMixin
|
||||
from plugin.views import InvenTreePluginViewMixin
|
||||
|
||||
from .models import StockItem, StockLocation
|
||||
@ -101,34 +100,3 @@ class StockItemDetail(InvenTreeRoleMixin, InvenTreePluginViewMixin, DetailView):
|
||||
return HttpResponseRedirect(reverse('stock-index'))
|
||||
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class StockLocationQRCode(QRCodeView):
|
||||
"""View for displaying a QR code for a StockLocation object."""
|
||||
|
||||
ajax_form_title = _("Stock Location QR code")
|
||||
|
||||
role_required = ['stock_location.view', 'stock.view']
|
||||
|
||||
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")
|
||||
role_required = 'stock.view'
|
||||
|
||||
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
|
||||
|
@ -15,6 +15,7 @@
|
||||
getFieldValue,
|
||||
reloadFieldOptions,
|
||||
showModalImage,
|
||||
showQRDialog,
|
||||
showQuestionDialog,
|
||||
showModalSpinner,
|
||||
*/
|
||||
@ -590,19 +591,18 @@ function renderErrorMessage(xhr) {
|
||||
}
|
||||
|
||||
|
||||
/* Display a modal dialog message box.
|
||||
*
|
||||
* title - Title text
|
||||
* content - HTML content of the dialog window
|
||||
*/
|
||||
function showAlertDialog(title, content, options={}) {
|
||||
/* Display a modal dialog message box.
|
||||
*
|
||||
* title - Title text
|
||||
* content - HTML content of the dialog window
|
||||
*/
|
||||
|
||||
if (options.alert_style) {
|
||||
// Wrap content in an alert block
|
||||
content = `<div class='alert alert-block alert-${options.alert_style}'>${content}</div>`;
|
||||
}
|
||||
|
||||
|
||||
var modal = createNewModal({
|
||||
title: title,
|
||||
closeText: '{% trans "Close" %}',
|
||||
@ -612,6 +612,36 @@ function showAlertDialog(title, content, options={}) {
|
||||
modalSetContent(modal, content);
|
||||
|
||||
$(modal).modal('show');
|
||||
|
||||
if (options.after_render) {
|
||||
options.after_render(modal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display a simple modal window with a QR code
|
||||
*/
|
||||
function showQRDialog(title, data, options={}) {
|
||||
|
||||
let content = `
|
||||
<div id='qrcode-container' style='margin: auto; width: 256px; padding: 25px;'>
|
||||
<div id='qrcode'></div>
|
||||
</div>`;
|
||||
|
||||
options.after_render = function(modal) {
|
||||
let qrcode = new QRCode('qrcode', {
|
||||
width: 256,
|
||||
height: 256,
|
||||
});
|
||||
qrcode.makeCode(data);
|
||||
};
|
||||
|
||||
showAlertDialog(
|
||||
title,
|
||||
content,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
@ -35,3 +35,4 @@
|
||||
<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/html5-qrcode.min.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% static 'script/qrcode.min.js' %}"></script>
|
||||
|
Loading…
Reference in New Issue
Block a user