mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #1260 from SchrodingersGat/barcode-scanner
Barcode scanner
This commit is contained in:
commit
8c7621d4bc
@ -90,7 +90,7 @@ class BarcodeScan(APIView):
|
|||||||
|
|
||||||
if loc is not None:
|
if loc is not None:
|
||||||
response['stocklocation'] = plugin.renderStockLocation(loc)
|
response['stocklocation'] = plugin.renderStockLocation(loc)
|
||||||
response['url'] = reverse('location-detail', kwargs={'pk': loc.id})
|
response['url'] = reverse('stock-location-detail', kwargs={'pk': loc.id})
|
||||||
match_found = True
|
match_found = True
|
||||||
|
|
||||||
# Try to associate with a part
|
# Try to associate with a part
|
||||||
|
@ -71,6 +71,13 @@ class InvenTreeSetting(models.Model):
|
|||||||
'choices': djmoney.settings.CURRENCY_CHOICES,
|
'choices': djmoney.settings.CURRENCY_CHOICES,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'BARCODE_ENABLE': {
|
||||||
|
'name': _('Barcode Support'),
|
||||||
|
'description': _('Enable barcode scanner support'),
|
||||||
|
'default': True,
|
||||||
|
'validator': bool,
|
||||||
|
},
|
||||||
|
|
||||||
'PART_IPN_REGEX': {
|
'PART_IPN_REGEX': {
|
||||||
'name': _('IPN Regex'),
|
'name': _('IPN Regex'),
|
||||||
'description': _('Regular expression pattern for matching Part IPN')
|
'description': _('Regular expression pattern for matching Part IPN')
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -44,6 +44,8 @@
|
|||||||
<span id='part-star-icon' class='fas fa-star {% if starred %}icon-yellow{% endif %}'/>
|
<span id='part-star-icon' class='fas fa-star {% if starred %}icon-yellow{% endif %}'/>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{% settings_value 'BARCODE_ENABLE' as barcodes %}
|
||||||
|
{% if barcodes %}
|
||||||
<!-- Barcode actions menu -->
|
<!-- Barcode actions menu -->
|
||||||
<div class='btn-group'>
|
<div class='btn-group'>
|
||||||
<button id='barcode-options' title='{% trans "Barcode actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-qrcode'></span> <span class='caret'></span></button>
|
<button id='barcode-options' title='{% trans "Barcode actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-qrcode'></span> <span class='caret'></span></button>
|
||||||
@ -52,6 +54,7 @@
|
|||||||
<li><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
|
<li><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% if part.active %}
|
{% if part.active %}
|
||||||
<button type='button' class='btn btn-default' id='price-button' title='{% trans "Show pricing information" %}'>
|
<button type='button' class='btn btn-default' id='price-button' title='{% trans "Show pricing information" %}'>
|
||||||
<span id='part-price-icon' class='fas fa-dollar-sign'/>
|
<span id='part-price-icon' class='fas fa-dollar-sign'/>
|
||||||
|
@ -121,12 +121,17 @@ class StockAdjust(APIView):
|
|||||||
- StockAdd: add stock items
|
- StockAdd: add stock items
|
||||||
- StockRemove: remove stock items
|
- StockRemove: remove stock items
|
||||||
- StockTransfer: transfer stock items
|
- StockTransfer: transfer stock items
|
||||||
|
|
||||||
|
# TODO - This needs serious refactoring!!!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
permission_classes = [
|
permission_classes = [
|
||||||
permissions.IsAuthenticated,
|
permissions.IsAuthenticated,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
allow_missing_quantity = False
|
||||||
|
|
||||||
def get_items(self, request):
|
def get_items(self, request):
|
||||||
"""
|
"""
|
||||||
Return a list of items posted to the endpoint.
|
Return a list of items posted to the endpoint.
|
||||||
@ -157,10 +162,13 @@ class StockAdjust(APIView):
|
|||||||
except (ValueError, StockItem.DoesNotExist):
|
except (ValueError, StockItem.DoesNotExist):
|
||||||
raise ValidationError({'pk': 'Each entry must contain a valid pk field'})
|
raise ValidationError({'pk': 'Each entry must contain a valid pk field'})
|
||||||
|
|
||||||
|
if self.allow_missing_quantity and 'quantity' not in entry:
|
||||||
|
entry['quantity'] = item.quantity
|
||||||
|
|
||||||
try:
|
try:
|
||||||
quantity = Decimal(str(entry.get('quantity', None)))
|
quantity = Decimal(str(entry.get('quantity', None)))
|
||||||
except (ValueError, TypeError, InvalidOperation):
|
except (ValueError, TypeError, InvalidOperation):
|
||||||
raise ValidationError({'quantity': 'Each entry must contain a valid quantity field'})
|
raise ValidationError({'quantity': "Each entry must contain a valid quantity value"})
|
||||||
|
|
||||||
if quantity < 0:
|
if quantity < 0:
|
||||||
raise ValidationError({'quantity': 'Quantity field must not be less than zero'})
|
raise ValidationError({'quantity': 'Quantity field must not be less than zero'})
|
||||||
@ -234,6 +242,8 @@ class StockTransfer(StockAdjust):
|
|||||||
API endpoint for performing stock movements
|
API endpoint for performing stock movements
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
allow_missing_quantity = True
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
||||||
self.get_items(request)
|
self.get_items(request)
|
||||||
|
@ -120,6 +120,8 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='btn-group action-buttons' role='group'>
|
<div class='btn-group action-buttons' role='group'>
|
||||||
|
{% settings_value 'BARCODE_ENABLE' as barcodes %}
|
||||||
|
{% if barcodes %}
|
||||||
<!-- Barcode actions menu -->
|
<!-- Barcode actions menu -->
|
||||||
<div class='btn-group'>
|
<div class='btn-group'>
|
||||||
<button id='barcode-options' title='{% trans "Barcode actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-qrcode'></span> <span class='caret'></span></button>
|
<button id='barcode-options' title='{% trans "Barcode actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-qrcode'></span> <span class='caret'></span></button>
|
||||||
@ -127,24 +129,26 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
|
|||||||
<li><a href='#' id='show-qr-code'><span class='fas fa-qrcode'></span> {% trans "Show QR Code" %}</a></li>
|
<li><a href='#' id='show-qr-code'><span class='fas fa-qrcode'></span> {% trans "Show QR Code" %}</a></li>
|
||||||
{% if roles.stock.change %}
|
{% if roles.stock.change %}
|
||||||
{% if item.uid %}
|
{% if item.uid %}
|
||||||
<li><a href='#' id='unlink-barcode'><span class='fas fa-unlink'></span> {% trans "Unlink Barcode" %}</a></li>
|
<li><a href='#' id='barcode-unlink'><span class='fas fa-unlink'></span> {% trans "Unlink Barcode" %}</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li><a href='#' id='link-barcode'><span class='fas fa-link'></span> {% trans "Link Barcode" %}</a></li>
|
<li><a href='#' id='barcode-link'><span class='fas fa-link'></span> {% trans "Link Barcode" %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
<li><a href='#' id='barcode-scan-into-location'><span class='fas fa-sitemap'></span> {% trans "Scan to Location" %}</a></li>
|
||||||
</ul>
|
{% endif %}
|
||||||
</div>
|
</ul>
|
||||||
<!-- Document / label menu -->
|
</div>
|
||||||
{% if item.has_labels or item.has_test_reports %}
|
{% endif %}
|
||||||
<div class='btn-group'>
|
<!-- Document / label menu -->
|
||||||
<button id='document-options' title='{% trans "Printing actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-print'></span> <span class='caret'></span></button>
|
{% if item.has_labels or item.has_test_reports %}
|
||||||
<ul class='dropdown-menu' role='menu'>
|
<div class='btn-group'>
|
||||||
{% if item.has_labels %}
|
<button id='document-options' title='{% trans "Printing actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-print'></span> <span class='caret'></span></button>
|
||||||
<li><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
|
<ul class='dropdown-menu' role='menu'>
|
||||||
{% endif %}
|
{% if item.has_labels %}
|
||||||
{% if item.has_test_reports %}
|
<li><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
|
||||||
<li><a href='#' id='stock-test-report'><span class='fas fa-file-pdf'></span> {% trans "Test Report" %}</a></li>
|
{% endif %}
|
||||||
{% endif %}
|
{% if item.has_test_reports %}
|
||||||
|
<li><a href='#' id='stock-test-report'><span class='fas fa-file-pdf'></span> {% trans "Test Report" %}</a></li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -447,14 +451,18 @@ $("#show-qr-code").click(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#link-barcode").click(function() {
|
$("#barcode-link").click(function() {
|
||||||
linkBarcodeDialog({{ item.id }});
|
linkBarcodeDialog({{ item.id }});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#unlink-barcode").click(function() {
|
$("#barcode-unlink").click(function() {
|
||||||
unlinkBarcode({{ item.id }});
|
unlinkBarcode({{ item.id }});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#barcode-scan-into-location").click(function() {
|
||||||
|
scanItemsIntoLocation([{{ item.id }}]);
|
||||||
|
});
|
||||||
|
|
||||||
{% if item.in_stock %}
|
{% if item.in_stock %}
|
||||||
|
|
||||||
$("#stock-assign-to-customer").click(function() {
|
$("#stock-assign-to-customer").click(function() {
|
||||||
|
@ -37,6 +37,8 @@
|
|||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% settings_value 'BARCODE_ENABLE' as barcodes %}
|
||||||
|
{% if barcodes %}
|
||||||
<!-- Barcode actions menu -->
|
<!-- Barcode actions menu -->
|
||||||
{% if location %}
|
{% if location %}
|
||||||
<div class='btn-group'>
|
<div class='btn-group'>
|
||||||
@ -47,29 +49,30 @@
|
|||||||
<li><a href='#' id='barcode-check-in'><span class='fas fa-arrow-right'></span> {% trans "Check-in Items" %}</a></li>
|
<li><a href='#' id='barcode-check-in'><span class='fas fa-arrow-right'></span> {% trans "Check-in Items" %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<!-- Check permissions and owner -->
|
{% endif %}
|
||||||
{% if owner_control.value == "False" or owner_control.value == "True" and user in owners or user.is_superuser %}
|
<!-- Check permissions and owner -->
|
||||||
{% if roles.stock.change %}
|
{% if owner_control.value == "False" or owner_control.value == "True" and user in owners or user.is_superuser %}
|
||||||
<div class='btn-group'>
|
{% if roles.stock.change %}
|
||||||
<button id='stock-actions' title='{% trans "Stock actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-boxes'></span> <span class='caret'></span></button>
|
<div class='btn-group'>
|
||||||
<ul class='dropdown-menu' role='menu'>
|
<button id='stock-actions' title='{% trans "Stock actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-boxes'></span> <span class='caret'></span></button>
|
||||||
<li><a href='#' id='location-count'><span class='fas fa-clipboard-list'></span>
|
<ul class='dropdown-menu' role='menu'>
|
||||||
{% trans "Count stock" %}</a></li>
|
<li><a href='#' id='location-count'><span class='fas fa-clipboard-list'></span>
|
||||||
</ul>
|
{% trans "Count stock" %}</a></li>
|
||||||
</div>
|
</ul>
|
||||||
{% endif %}
|
</div>
|
||||||
{% if roles.stock_location.change %}
|
|
||||||
<div class='btn-group'>
|
|
||||||
<button id='location-actions' title='{% trans "Location actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle="dropdown"><span class='fas fa-sitemap'></span> <span class='caret'></span></button>
|
|
||||||
<ul class='dropdown-menu' role='menu'>
|
|
||||||
<li><a href='#' id='location-edit'><span class='fas fa-edit icon-green'></span> {% trans "Edit location" %}</a></li>
|
|
||||||
{% if roles.stock.delete %}
|
|
||||||
<li><a href='#' id='location-delete'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete location" %}</a></li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if roles.stock_location.change %}
|
||||||
|
<div class='btn-group'>
|
||||||
|
<button id='location-actions' title='{% trans "Location actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle="dropdown"><span class='fas fa-sitemap'></span> <span class='caret'></span></button>
|
||||||
|
<ul class='dropdown-menu' role='menu'>
|
||||||
|
<li><a href='#' id='location-edit'><span class='fas fa-edit icon-green'></span> {% trans "Edit location" %}</a></li>
|
||||||
|
{% if roles.stock.delete %}
|
||||||
|
<li><a href='#' id='location-delete'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete location" %}</a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,4 +21,12 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<h4>{% trans "Barcode Settings" %}</h4>
|
||||||
|
<table class='table table-striped table-condensed'>
|
||||||
|
{% include "InvenTree/settings/header.html" %}
|
||||||
|
<tbody>
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="BARCODE_ENABLE" icon="fa-qrcode" %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,12 +1,14 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
function makeBarcodeInput(placeholderText='') {
|
function makeBarcodeInput(placeholderText='', hintText='') {
|
||||||
/*
|
/*
|
||||||
* Generate HTML for a barcode input
|
* Generate HTML for a barcode input
|
||||||
*/
|
*/
|
||||||
|
|
||||||
placeholderText = placeholderText || '{% trans "Scan barcode data here using wedge scanner" %}';
|
placeholderText = placeholderText || '{% trans "Scan barcode data here using wedge scanner" %}';
|
||||||
|
|
||||||
|
hintText = hintText || '{% trans "Enter barcode data" %}';
|
||||||
|
|
||||||
var html = `
|
var html = `
|
||||||
<div class='form-group'>
|
<div class='form-group'>
|
||||||
<label class='control-label' for='barcode'>{% trans "Barcode" %}</label>
|
<label class='control-label' for='barcode'>{% trans "Barcode" %}</label>
|
||||||
@ -17,7 +19,7 @@ function makeBarcodeInput(placeholderText='') {
|
|||||||
</span>
|
</span>
|
||||||
<input id='barcode' class='textinput textInput form-control' type='text' name='barcode' placeholder='${placeholderText}'>
|
<input id='barcode' class='textinput textInput form-control' type='text' name='barcode' placeholder='${placeholderText}'>
|
||||||
</div>
|
</div>
|
||||||
<div id='hint_barcode_data' class='help-block'>{% trans "Enter barcode data" %}</div>
|
<div id='hint_barcode_data' class='help-block'>${hintText}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@ -25,6 +27,81 @@ function makeBarcodeInput(placeholderText='') {
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeNotesField(options={}) {
|
||||||
|
|
||||||
|
var tooltip = options.tooltip || '{% trans "Enter optional notes for stock transfer" %}';
|
||||||
|
var placeholder = options.placeholder || '{% trans "Enter notes" %}';
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class='form-group'>
|
||||||
|
<label class='control-label' for='notes'>{% trans "Notes" %}</label>
|
||||||
|
<div class='controls'>
|
||||||
|
<div class='input-group'>
|
||||||
|
<span class='input-group-addon'>
|
||||||
|
<span class='fas fa-sticky-note'></span>
|
||||||
|
</span>
|
||||||
|
<input id='notes' class='textinput textInput form-control' type='text' name='notes' placeholder='${placeholder}'>
|
||||||
|
</div>
|
||||||
|
<div id='hint_notes' class='help_block'>${tooltip}</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* POST data to the server, and handle standard responses.
|
||||||
|
*/
|
||||||
|
function postBarcodeData(barcode_data, options={}) {
|
||||||
|
|
||||||
|
var modal = options.modal || '#modal-form';
|
||||||
|
|
||||||
|
var url = options.url || '/api/barcode/';
|
||||||
|
|
||||||
|
var data = options.data || {};
|
||||||
|
|
||||||
|
data.barcode = barcode_data;
|
||||||
|
|
||||||
|
inventreePut(
|
||||||
|
url,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
error: function() {
|
||||||
|
enableBarcodeInput(modal, true);
|
||||||
|
showBarcodeMessage(modal, '{% trans "Server error" %}');
|
||||||
|
},
|
||||||
|
success: function(response, status) {
|
||||||
|
modalEnable(modal, false);
|
||||||
|
enableBarcodeInput(modal, true);
|
||||||
|
|
||||||
|
if (status == 'success') {
|
||||||
|
|
||||||
|
if ('success' in response) {
|
||||||
|
if (options.onScan) {
|
||||||
|
options.onScan(response);
|
||||||
|
}
|
||||||
|
} else if ('error' in response) {
|
||||||
|
showBarcodeMessage(
|
||||||
|
modal,
|
||||||
|
response.error,
|
||||||
|
'warning'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
showBarcodeMessage(
|
||||||
|
modal,
|
||||||
|
'{% trans "Unknown response from server" %}',
|
||||||
|
'warning'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Invalid response returned from server
|
||||||
|
showInvalidResponseError(modal, response, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function showBarcodeMessage(modal, message, style='danger') {
|
function showBarcodeMessage(modal, message, style='danger') {
|
||||||
|
|
||||||
@ -43,12 +120,6 @@ function showInvalidResponseError(modal, response, status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function clearBarcodeError(modal, message) {
|
|
||||||
|
|
||||||
$(modal + ' #barcode-error-message').html('');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function enableBarcodeInput(modal, enabled=true) {
|
function enableBarcodeInput(modal, enabled=true) {
|
||||||
|
|
||||||
var barcode = $(modal + ' #barcode');
|
var barcode = $(modal + ' #barcode');
|
||||||
@ -87,9 +158,7 @@ function barcodeDialog(title, options={}) {
|
|||||||
|
|
||||||
if (barcode && barcode.length > 0) {
|
if (barcode && barcode.length > 0) {
|
||||||
|
|
||||||
if (options.onScan) {
|
postBarcodeData(barcode, options);
|
||||||
options.onScan(barcode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,40 +258,20 @@ function barcodeScanDialog() {
|
|||||||
barcodeDialog(
|
barcodeDialog(
|
||||||
"Scan Barcode",
|
"Scan Barcode",
|
||||||
{
|
{
|
||||||
onScan: function(barcode) {
|
onScan: function(response) {
|
||||||
enableBarcodeInput(modal, false);
|
if ('url' in response) {
|
||||||
inventreePut(
|
$(modal).modal('hide');
|
||||||
'/api/barcode/',
|
|
||||||
{
|
// Redirect to the URL!
|
||||||
barcode: barcode,
|
window.location.href = response.url;
|
||||||
},
|
} else {
|
||||||
{
|
showBarcodeMessage(
|
||||||
method: 'POST',
|
modal,
|
||||||
success: function(response, status) {
|
'{% trans "No URL in response" %}',
|
||||||
|
'warning'
|
||||||
enableBarcodeInput(modal, true);
|
);
|
||||||
|
}
|
||||||
if (status == 'success') {
|
}
|
||||||
|
|
||||||
if ('success' in response) {
|
|
||||||
if ('url' in response) {
|
|
||||||
// Redirect to the URL!
|
|
||||||
$(modal).modal('hide');
|
|
||||||
window.location.href = response.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if ('error' in response) {
|
|
||||||
showBarcodeMessage(modal, response.error, 'warning');
|
|
||||||
} else {
|
|
||||||
showBarcodeMessage(modal, "{% trans 'Unknown response from server' %}", 'warning');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showInvalidResponseError(modal, response, status);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -238,37 +287,14 @@ function linkBarcodeDialog(stockitem, options={}) {
|
|||||||
barcodeDialog(
|
barcodeDialog(
|
||||||
"{% trans 'Link Barcode to Stock Item' %}",
|
"{% trans 'Link Barcode to Stock Item' %}",
|
||||||
{
|
{
|
||||||
onScan: function(barcode) {
|
url: '/api/barcode/link/',
|
||||||
enableBarcodeInput(modal, false);
|
data: {
|
||||||
inventreePut(
|
stockitem: stockitem,
|
||||||
'/api/barcode/link/',
|
},
|
||||||
{
|
onScan: function(response) {
|
||||||
barcode: barcode,
|
|
||||||
stockitem: stockitem,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
success: function(response, status) {
|
|
||||||
|
|
||||||
enableBarcodeInput(modal, true);
|
$(modal).modal('hide');
|
||||||
|
location.reload();
|
||||||
if (status == 'success') {
|
|
||||||
|
|
||||||
if ('success' in response) {
|
|
||||||
$(modal).modal('hide');
|
|
||||||
location.reload();
|
|
||||||
} else if ('error' in response) {
|
|
||||||
showBarcodeMessage(modal, response.error, 'warning');
|
|
||||||
} else {
|
|
||||||
showBarcodeMessage(modal, "{% trans 'Unknown response from server' %}", warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
showInvalidResponseError(modal, response, status);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -386,22 +412,10 @@ function barcodeCheckIn(location_id, options={}) {
|
|||||||
var table = `<div class='container' id='items-table-div' style='width: 80%; float: left;'></div>`;
|
var table = `<div class='container' id='items-table-div' style='width: 80%; float: left;'></div>`;
|
||||||
|
|
||||||
// Extra form fields
|
// Extra form fields
|
||||||
var extra = `
|
var extra = makeNotesField();
|
||||||
<div class='form-group'>
|
|
||||||
<label class='control-label' for='notes'>{% trans "Notes" %}</label>
|
|
||||||
<div class='controls'>
|
|
||||||
<div class='input-group'>
|
|
||||||
<span class='input-group-addon'>
|
|
||||||
<span class='fas fa-sticky-note'></span>
|
|
||||||
</span>
|
|
||||||
<input id='notes' class='textinput textInput form-control' type='text' name='notes' placeholder='{% trans "Enter notes" %}'>
|
|
||||||
</div>
|
|
||||||
<div id='hint_notes' class='help_block'>{% trans "Enter optional notes for stock transfer" %}</div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
barcodeDialog(
|
barcodeDialog(
|
||||||
"{% trans "Check Stock Items into Location" %}",
|
'{% trans "Check Stock Items into Location" %}',
|
||||||
{
|
{
|
||||||
headerContent: table,
|
headerContent: table,
|
||||||
preShow: function() {
|
preShow: function() {
|
||||||
@ -414,7 +428,6 @@ function barcodeCheckIn(location_id, options={}) {
|
|||||||
extraFields: extra,
|
extraFields: extra,
|
||||||
onSubmit: function() {
|
onSubmit: function() {
|
||||||
|
|
||||||
|
|
||||||
// Called when the 'check-in' button is pressed
|
// Called when the 'check-in' button is pressed
|
||||||
|
|
||||||
var data = {location: location_id};
|
var data = {location: location_id};
|
||||||
@ -434,7 +447,7 @@ function barcodeCheckIn(location_id, options={}) {
|
|||||||
data.items = entries;
|
data.items = entries;
|
||||||
|
|
||||||
inventreePut(
|
inventreePut(
|
||||||
'{% url 'api-stock-transfer' %}',
|
"{% url 'api-stock-transfer' %}",
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -446,69 +459,154 @@ function barcodeCheckIn(location_id, options={}) {
|
|||||||
showAlertOrCache('alert-success', response.success, true);
|
showAlertOrCache('alert-success', response.success, true);
|
||||||
location.reload();
|
location.reload();
|
||||||
} else {
|
} else {
|
||||||
showAlertOrCache('alert-success', 'Error transferring stock', false);
|
showAlertOrCache('alert-success', '{% trans "Error transferring stock" %}', false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onScan: function(barcode) {
|
onScan: function(response) {
|
||||||
enableBarcodeInput(modal, false);
|
if ('stockitem' in response) {
|
||||||
inventreePut(
|
stockitem = response.stockitem;
|
||||||
'/api/barcode/',
|
|
||||||
{
|
|
||||||
barcode: barcode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
error: function() {
|
|
||||||
enableBarcodeInput(modal, true);
|
|
||||||
showBarcodeMessage(modal, '{% trans "Server error" %}');
|
|
||||||
},
|
|
||||||
success: function(response, status) {
|
|
||||||
|
|
||||||
enableBarcodeInput(modal, true);
|
var duplicate = false;
|
||||||
|
|
||||||
if (status == 'success') {
|
items.forEach(function(item) {
|
||||||
if ('stockitem' in response) {
|
if (item.pk == stockitem.pk) {
|
||||||
stockitem = response.stockitem;
|
duplicate = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var duplicate = false;
|
if (duplicate) {
|
||||||
|
showBarcodeMessage(modal, '{% trans "Stock Item already scanned" %}', "warning");
|
||||||
|
} else {
|
||||||
|
|
||||||
items.forEach(function(item) {
|
if (stockitem.location == location_id) {
|
||||||
if (item.pk == stockitem.pk) {
|
showBarcodeMessage(modal, '{% trans "Stock Item already in this location" %}');
|
||||||
duplicate = true;
|
return;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (duplicate) {
|
// Add this stock item to the list
|
||||||
showBarcodeMessage(modal, "{% trans "Stock Item already scanned" %}", "warning");
|
items.push(stockitem);
|
||||||
} else {
|
|
||||||
|
|
||||||
if (stockitem.location == location_id) {
|
showBarcodeMessage(modal, '{% trans "Added stock item" %}', "success");
|
||||||
showBarcodeMessage(modal, "{% trans "Stock Item already in this location" %}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add this stock item to the list
|
reloadTable();
|
||||||
items.push(stockitem);
|
}
|
||||||
|
|
||||||
showBarcodeMessage(modal, "{% trans "Added stock item" %}", "success");
|
} else {
|
||||||
|
// Barcode does not match a stock item
|
||||||
reloadTable();
|
showBarcodeMessage(modal, '{% trans "Barcode does not match Stock Item" %}', "warning");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
// Barcode does not match a stock item
|
|
||||||
showBarcodeMessage(modal, "{% trans "Barcode does not match Stock Item" %}", "warning");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showInvalidResponseError(modal, response, status);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Display dialog to check a single stock item into a stock location
|
||||||
|
*/
|
||||||
|
function scanItemsIntoLocation(item_id_list, options={}) {
|
||||||
|
|
||||||
|
var modal = options.modal || '#modal-form';
|
||||||
|
|
||||||
|
var stock_location = null;
|
||||||
|
|
||||||
|
// Extra form fields
|
||||||
|
var extra = makeNotesField();
|
||||||
|
|
||||||
|
// Header contentfor
|
||||||
|
var header = `
|
||||||
|
<div id='header-div'>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
function updateLocationInfo(location) {
|
||||||
|
var div = $(modal + ' #header-div');
|
||||||
|
|
||||||
|
if (stock_location && stock_location.pk) {
|
||||||
|
div.html(`
|
||||||
|
<div class='alert alert-block alert-info'>
|
||||||
|
<b>{% trans "Location" %}</b></br>
|
||||||
|
${stock_location.name}<br>
|
||||||
|
<i>${stock_location.description}</i>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
} else {
|
||||||
|
div.html('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
barcodeDialog(
|
||||||
|
'{% trans "Check Into Location" %}',
|
||||||
|
{
|
||||||
|
headerContent: header,
|
||||||
|
extraFields: extra,
|
||||||
|
preShow: function() {
|
||||||
|
modalSetSubmitText(modal, '{% trans "Check In" %}');
|
||||||
|
modalEnable(modal, false);
|
||||||
|
},
|
||||||
|
onShow: function() {
|
||||||
|
},
|
||||||
|
onSubmit: function() {
|
||||||
|
// Called when the 'check-in' button is pressed
|
||||||
|
if (!stock_location) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = [];
|
||||||
|
|
||||||
|
item_id_list.forEach(function(pk) {
|
||||||
|
items.push({
|
||||||
|
pk: pk,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
location: stock_location.pk,
|
||||||
|
notes: $(modal + ' #notes').val(),
|
||||||
|
items: items,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send API request
|
||||||
|
inventreePut(
|
||||||
|
'{% url "api-stock-transfer" %}',
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
success: function(response, status) {
|
||||||
|
// First hide the modal
|
||||||
|
$(modal).modal('hide');
|
||||||
|
|
||||||
|
if (status == 'success' && 'success' in response) {
|
||||||
|
showAlertOrCache('alert-success', response.success, true);
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
showAlertOrCache('alert-danger', '{% trans "Error transferring stock" %}', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onScan: function(response) {
|
||||||
|
updateLocationInfo(null);
|
||||||
|
if ('stocklocation' in response) {
|
||||||
|
// Barcode corresponds to a StockLocation
|
||||||
|
stock_location = response.stocklocation;
|
||||||
|
|
||||||
|
updateLocationInfo(stock_location);
|
||||||
|
modalEnable(modal, true);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Barcode does *NOT* correspond to a StockLocation
|
||||||
|
showBarcodeMessage(
|
||||||
|
modal,
|
||||||
|
'{% trans "Barcode does not match a valid location" %}',
|
||||||
|
"warning",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
* Requires api.js to be loaded first
|
* Requires api.js to be loaded first
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
{% settings_value 'BARCODE_ENABLE' as barcodes %}
|
||||||
|
|
||||||
function stockStatusCodes() {
|
function stockStatusCodes() {
|
||||||
return [
|
return [
|
||||||
@ -635,6 +636,9 @@ function loadStockTable(table, options) {
|
|||||||
table,
|
table,
|
||||||
[
|
[
|
||||||
'#stock-print-options',
|
'#stock-print-options',
|
||||||
|
{% if barcodes %}
|
||||||
|
'#stock-barcode-options',
|
||||||
|
{% endif %}
|
||||||
'#stock-options',
|
'#stock-options',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@ -700,6 +704,20 @@ function loadStockTable(table, options) {
|
|||||||
printTestReports(items);
|
printTestReports(items);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
{% if barcodes %}
|
||||||
|
$('#multi-item-barcode-scan-into-location').click(function() {
|
||||||
|
var selections = $('#stock-table').bootstrapTable('getSelections');
|
||||||
|
|
||||||
|
var items = [];
|
||||||
|
|
||||||
|
selections.forEach(function(item) {
|
||||||
|
items.push(item.pk);
|
||||||
|
})
|
||||||
|
|
||||||
|
scanItemsIntoLocation(items);
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
$('#multi-item-stocktake').click(function() {
|
$('#multi-item-stocktake').click(function() {
|
||||||
stockAdjustment('count');
|
stockAdjustment('count');
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load inventree_extras %}
|
{% load inventree_extras %}
|
||||||
|
|
||||||
|
{% settings_value 'BARCODE_ENABLE' as barcodes %}
|
||||||
|
|
||||||
{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %}
|
{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %}
|
||||||
{% if owner_control.value == "True" %}
|
{% if owner_control.value == "True" %}
|
||||||
{% authorized_owners location.owner as owners %}
|
{% authorized_owners location.owner as owners %}
|
||||||
@ -19,6 +21,17 @@
|
|||||||
<span class='fas fa-plus-circle'></span>
|
<span class='fas fa-plus-circle'></span>
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if barcodes %}
|
||||||
|
<!-- Barcode actions menu -->
|
||||||
|
<div class='btn-group'>
|
||||||
|
<button id='stock-barcode-options' class='btn btn-primary dropdown-toggle' type='button' data-toggle='dropdown' title='{% trans "Barcode Actions" %}'>
|
||||||
|
<span class='fas fa-qrcode'></span> <span class='caret'></span>
|
||||||
|
</button>
|
||||||
|
<ul class='dropdown-menu'>
|
||||||
|
<li><a href='#' id='multi-item-barcode-scan-into-location' title='{% trans "Scan to Location" %}'><span class='fas fa-sitemap'></span> {% trans "Scan to Location" %}</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<div class='btn-group'>
|
<div class='btn-group'>
|
||||||
<button id='stock-print-options' class='btn btn-primary dropdown-toggle' type='button' data-toggle="dropdown" title='{% trans "Printing Actions" %}'>
|
<button id='stock-print-options' class='btn btn-primary dropdown-toggle' type='button' data-toggle="dropdown" title='{% trans "Printing Actions" %}'>
|
||||||
<span class='fas fa-print'></span> <span class='caret'></span>
|
<span class='fas fa-print'></span> <span class='caret'></span>
|
||||||
|
Loading…
Reference in New Issue
Block a user