Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Camera QR scanner #2779

Merged
merged 11 commits into from Apr 30, 2022
4 changes: 4 additions & 0 deletions InvenTree/InvenTree/settings.py
Expand Up @@ -311,6 +311,10 @@ def _is_true(x):
INSTALLED_APPS.append('debug_toolbar')
MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')

# Allow secure http developer server in debug mode
if DEBUG:
INSTALLED_APPS.append('sslserver')

# InvenTree URL configuration

# Base URL for admin pages (default="admin")
Expand Down
99 changes: 99 additions & 0 deletions InvenTree/InvenTree/static/script/qr-scanner-worker.min.js

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions InvenTree/InvenTree/static/script/qr-scanner.umd.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions InvenTree/templates/base.html
Expand Up @@ -160,6 +160,7 @@

<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 InvenTree -->
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
Expand Down
52 changes: 52 additions & 0 deletions InvenTree/templates/js/translated/barcode.js
Expand Up @@ -31,6 +31,9 @@ function makeBarcodeInput(placeholderText='', hintText='') {
hintText = hintText || '{% trans "Enter barcode data" %}';

var html = `
<div id='barcode_scan_video_container' class='text-center' style='height: 240px; display: none;'>
<video id='barcode_scan_video' disablepictureinpicture playsinline height='240' style='object-fit: fill;'></video>
</div>
<div class='form-group'>
<label class='control-label' for='barcode'>{% trans "Barcode" %}</label>
<div class='controls'>
Expand All @@ -39,6 +42,7 @@ function makeBarcodeInput(placeholderText='', hintText='') {
<span class='fas fa-qrcode'></span>
</span>
<input id='barcode' class='textinput textInput form-control' type='text' name='barcode' placeholder='${placeholderText}'>
<button id='barcode_scan_btn' class='btn btn-secondary' onclick='onBarcodeScanClicked()' style='display: none;'><span class='fas fa-camera'></span></button>
</div>
<div id='hint_barcode_data' class='help-block'>${hintText}</div>
</div>
Expand All @@ -48,6 +52,45 @@ function makeBarcodeInput(placeholderText='', hintText='') {
return html;
}

qrScanner = null;

function startQrScanner()
{
$('#barcode_scan_video_container').show();
qrScanner.start();
}

function stopQrScanner()
{
if (qrScanner != null) qrScanner.stop();
$('#barcode_scan_video_container').hide();
}

function onBarcodeScanClicked(e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rkalman you need to export this function in the exported section - so just add
onBarcodeScanClicked
in line 22 of this file

if ($('#barcode_scan_video_container').is(':visible') == false) startQrScanner(); else stopQrScanner();
}

function onCameraAvailable(hasCamera, options) {
if ( hasCamera == true ) {
// Camera is only acccessible if page is served over secure connection
if ( window.isSecureContext == true ) {
qrScanner = new QrScanner(document.getElementById('barcode_scan_video'), result => onBarcodeScanCompleted(result, options), {
highlightScanRegion: true,
highlightCodeOutline: true,
});
$('#barcode_scan_btn').show();
}
}
}

function onBarcodeScanCompleted(result, options)
{
if (result.data == '') return;
console.log('decoded qr code:', result.data);
stopQrScanner();
postBarcodeData(result.data, options);
}

function makeNotesField(options={}) {

var tooltip = options.tooltip || '{% trans "Enter optional notes for stock transfer" %}';
Expand Down Expand Up @@ -186,6 +229,9 @@ function barcodeDialog(title, options={}) {
$(modal).on('shown.bs.modal', function() {
$(modal + ' .modal-form-content').scrollTop(0);

// Check for qr-scanner camera
QrScanner.hasCamera().then( hasCamera => onCameraAvailable(hasCamera, options) );

var barcode = $(modal + ' #barcode');

// Handle 'enter' key on barcode
Expand Down Expand Up @@ -220,6 +266,12 @@ function barcodeDialog(title, options={}) {

});

$(modal).on('hidden.bs.modal', function() {
stopQrScanner();
if (qrScanner != null) qrScanner.destroy();
qrScanner = null;
});

modalSetTitle(modal, title);

if (options.onSubmit) {
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Expand Up @@ -24,6 +24,7 @@ django-mptt==0.11.0 # Modified Preorder Tree Traversal
django-redis>=5.0.0
django-q==1.3.4 # Background task scheduling
django-sql-utils==0.5.0 # Advanced query annotation / aggregation
django-sslserver==0.22 # Secure HTTP development server
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this package added?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pages have to be served over https in order to access the camera, so I needed a simple way to spin up a development server.

django-stdimage==5.1.1 # Advanced ImageField management
django-test-migrations==1.1.0 # Unit testing for database migrations
django-user-sessions==1.7.1 # user sessions in DB
Expand Down