mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Add endpoint for printing labels
This commit is contained in:
parent
44e60a705e
commit
bdc7367e29
@ -92,6 +92,7 @@ settings_urls = [
|
|||||||
|
|
||||||
# Some javascript files are served 'dynamically', allowing them to pass through the Django translation layer
|
# Some javascript files are served 'dynamically', allowing them to pass through the Django translation layer
|
||||||
dynamic_javascript_urls = [
|
dynamic_javascript_urls = [
|
||||||
|
url(r'^modals.js', DynamicJsView.as_view(template_name='js/modals.js'), name='modals.js'),
|
||||||
url(r'^barcode.js', DynamicJsView.as_view(template_name='js/barcode.js'), name='barcode.js'),
|
url(r'^barcode.js', DynamicJsView.as_view(template_name='js/barcode.js'), name='barcode.js'),
|
||||||
url(r'^bom.js', DynamicJsView.as_view(template_name='js/bom.js'), name='bom.js'),
|
url(r'^bom.js', DynamicJsView.as_view(template_name='js/bom.js'), name='bom.js'),
|
||||||
url(r'^build.js', DynamicJsView.as_view(template_name='js/build.js'), name='build.js'),
|
url(r'^build.js', DynamicJsView.as_view(template_name='js/build.js'), name='build.js'),
|
||||||
@ -99,6 +100,7 @@ dynamic_javascript_urls = [
|
|||||||
url(r'^company.js', DynamicJsView.as_view(template_name='js/company.js'), name='company.js'),
|
url(r'^company.js', DynamicJsView.as_view(template_name='js/company.js'), name='company.js'),
|
||||||
url(r'^order.js', DynamicJsView.as_view(template_name='js/order.js'), name='order.js'),
|
url(r'^order.js', DynamicJsView.as_view(template_name='js/order.js'), name='order.js'),
|
||||||
url(r'^part.js', DynamicJsView.as_view(template_name='js/part.js'), name='part.js'),
|
url(r'^part.js', DynamicJsView.as_view(template_name='js/part.js'), name='part.js'),
|
||||||
|
url(r'^label.js', DynamicJsView.as_view(template_name='js/label.js'), name='label.js'),
|
||||||
url(r'^stock.js', DynamicJsView.as_view(template_name='js/stock.js'), name='stock.js'),
|
url(r'^stock.js', DynamicJsView.as_view(template_name='js/stock.js'), name='stock.js'),
|
||||||
url(r'^table_filters.js', DynamicJsView.as_view(template_name='js/table_filters.js'), name='table_filters.js'),
|
url(r'^table_filters.js', DynamicJsView.as_view(template_name='js/table_filters.js'), name='table_filters.js'),
|
||||||
]
|
]
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url, include
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
|
|
||||||
from rest_framework import generics, filters
|
from rest_framework import generics, filters
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
import InvenTree.helpers
|
import InvenTree.helpers
|
||||||
|
|
||||||
@ -35,21 +39,11 @@ class LabelListView(generics.ListAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class StockItemLabelList(LabelListView):
|
class StockItemLabelMixin:
|
||||||
"""
|
"""
|
||||||
API endpoint for viewing list of StockItemLabel objects.
|
Mixin for extracting stock items from query params
|
||||||
|
|
||||||
Filterable by:
|
|
||||||
|
|
||||||
- enabled: Filter by enabled / disabled status
|
|
||||||
- item: Filter by single stock item
|
|
||||||
- items: Filter by list of stock items
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = StockItemLabel.objects.all()
|
|
||||||
serializer_class = StockItemLabelSerializer
|
|
||||||
|
|
||||||
def get_items(self):
|
def get_items(self):
|
||||||
"""
|
"""
|
||||||
Return a list of requested stock items
|
Return a list of requested stock items
|
||||||
@ -59,8 +53,8 @@ class StockItemLabelList(LabelListView):
|
|||||||
|
|
||||||
params = self.request.query_params
|
params = self.request.query_params
|
||||||
|
|
||||||
if 'items' in params:
|
if 'items[]' in params:
|
||||||
items = params.getlist('items', [])
|
items = params.getlist('items[]', [])
|
||||||
elif 'item' in params:
|
elif 'item' in params:
|
||||||
items = [params.get('item', None)]
|
items = [params.get('item', None)]
|
||||||
|
|
||||||
@ -80,6 +74,22 @@ class StockItemLabelList(LabelListView):
|
|||||||
|
|
||||||
return valid_items
|
return valid_items
|
||||||
|
|
||||||
|
|
||||||
|
class StockItemLabelList(LabelListView, StockItemLabelMixin):
|
||||||
|
"""
|
||||||
|
API endpoint for viewing list of StockItemLabel objects.
|
||||||
|
|
||||||
|
Filterable by:
|
||||||
|
|
||||||
|
- enabled: Filter by enabled / disabled status
|
||||||
|
- item: Filter by single stock item
|
||||||
|
- items: Filter by list of stock items
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
queryset = StockItemLabel.objects.all()
|
||||||
|
serializer_class = StockItemLabelSerializer
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
def filter_queryset(self, queryset):
|
||||||
"""
|
"""
|
||||||
Filter the StockItem label queryset.
|
Filter the StockItem label queryset.
|
||||||
@ -140,6 +150,47 @@ class StockItemLabelDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
serializer_class = StockItemLabelSerializer
|
serializer_class = StockItemLabelSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class StockItemLabelPrint(generics.RetrieveAPIView, StockItemLabelMixin):
|
||||||
|
"""
|
||||||
|
API endpoint for printing a StockItemLabel object
|
||||||
|
"""
|
||||||
|
|
||||||
|
queryset = StockItemLabel.objects.all()
|
||||||
|
serializer_class = StockItemLabelSerializer
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Check if valid stock item(s) have been provided.
|
||||||
|
"""
|
||||||
|
|
||||||
|
items = self.get_items()
|
||||||
|
|
||||||
|
if len(items) == 0:
|
||||||
|
# No valid items provided, return an error message
|
||||||
|
data = {
|
||||||
|
'error': _('Must provide valid StockItem(s)'),
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response(data, status=400)
|
||||||
|
|
||||||
|
label = self.get_object()
|
||||||
|
|
||||||
|
try:
|
||||||
|
pdf = label.render(items)
|
||||||
|
except:
|
||||||
|
|
||||||
|
e = sys.exc_info()[1]
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'error': _('Error during label printing'),
|
||||||
|
'message': str(e),
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response(data, status=400)
|
||||||
|
|
||||||
|
return InvenTree.helpers.DownloadFile(pdf.getbuffer(), 'stock_item_labels.pdf', content_type='application/pdf')
|
||||||
|
|
||||||
|
|
||||||
class StockLocationLabelList(LabelListView):
|
class StockLocationLabelList(LabelListView):
|
||||||
"""
|
"""
|
||||||
API endpoint for viewiing list of StockLocationLabel objects.
|
API endpoint for viewiing list of StockLocationLabel objects.
|
||||||
@ -163,7 +214,7 @@ class StockLocationLabelList(LabelListView):
|
|||||||
|
|
||||||
params = self.request.query_params
|
params = self.request.query_params
|
||||||
|
|
||||||
if 'locations' in params:
|
if 'locations[]' in params:
|
||||||
locations = params.getlist('locations', [])
|
locations = params.getlist('locations', [])
|
||||||
elif 'location' in params:
|
elif 'location' in params:
|
||||||
locations = [params.get('location', None)]
|
locations = [params.get('location', None)]
|
||||||
@ -175,7 +226,7 @@ class StockLocationLabelList(LabelListView):
|
|||||||
|
|
||||||
for loc in locations:
|
for loc in locations:
|
||||||
try:
|
try:
|
||||||
valid_ids.append(int(item))
|
valid_ids.append(int(loc))
|
||||||
except (ValueError):
|
except (ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -249,6 +300,7 @@ label_api_urls = [
|
|||||||
url(r'stock/', include([
|
url(r'stock/', include([
|
||||||
# Detail views
|
# Detail views
|
||||||
url(r'^(?P<pk>\d+)/', include([
|
url(r'^(?P<pk>\d+)/', include([
|
||||||
|
url(r'print/?', StockItemLabelPrint.as_view(), name='api-stockitem-label-print'),
|
||||||
url(r'^.*$', StockItemLabelDetail.as_view(), name='api-stockitem-label-detail'),
|
url(r'^.*$', StockItemLabelDetail.as_view(), name='api-stockitem-label-detail'),
|
||||||
])),
|
])),
|
||||||
|
|
||||||
|
@ -112,7 +112,6 @@ InvenTree
|
|||||||
<script type='text/javascript' src="{% static 'script/inventree/api.js' %}"></script>
|
<script type='text/javascript' src="{% static 'script/inventree/api.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% static 'script/inventree/filters.js' %}"></script>
|
<script type='text/javascript' src="{% static 'script/inventree/filters.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% static 'script/inventree/tables.js' %}"></script>
|
<script type='text/javascript' src="{% static 'script/inventree/tables.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% static 'script/inventree/modals.js' %}"></script>
|
|
||||||
<script type='text/javascript' src="{% static 'script/inventree/notification.js' %}"></script>
|
<script type='text/javascript' src="{% static 'script/inventree/notification.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% static 'script/inventree/sidenav.js' %}"></script>
|
<script type='text/javascript' src="{% static 'script/inventree/sidenav.js' %}"></script>
|
||||||
|
|
||||||
@ -120,6 +119,8 @@ InvenTree
|
|||||||
<script type='text/javascript' src="{% url 'bom.js' %}"></script>
|
<script type='text/javascript' src="{% url 'bom.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% url 'company.js' %}"></script>
|
<script type='text/javascript' src="{% url 'company.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% url 'part.js' %}"></script>
|
<script type='text/javascript' src="{% url 'part.js' %}"></script>
|
||||||
|
<script type='text/javascript' src="{% url 'modals.js' %}"></script>
|
||||||
|
<script type='text/javascript' src="{% url 'label.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% url 'stock.js' %}"></script>
|
<script type='text/javascript' src="{% url 'stock.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% url 'build.js' %}"></script>
|
<script type='text/javascript' src="{% url 'build.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% url 'order.js' %}"></script>
|
<script type='text/javascript' src="{% url 'order.js' %}"></script>
|
||||||
|
52
InvenTree/templates/js/label.js
Normal file
52
InvenTree/templates/js/label.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
function selectLabel(labels, options={}) {
|
||||||
|
/**
|
||||||
|
* Present the user with the available labels,
|
||||||
|
* and allow them to select which label to print.
|
||||||
|
*
|
||||||
|
* The intent is that the available labels have been requested
|
||||||
|
* (via AJAX) from the server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var modal = options.modal || '#modal-form';
|
||||||
|
|
||||||
|
var label_list = makeOptionsList(
|
||||||
|
labels,
|
||||||
|
function(item) {
|
||||||
|
var text = item.name;
|
||||||
|
|
||||||
|
if (item.description) {
|
||||||
|
text += ` - ${item.description}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
function(item) {
|
||||||
|
return item.pk;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Construct form
|
||||||
|
var html = `
|
||||||
|
<form method='post' action='' class='js-modal-form' enctype='multipart/form-data'>
|
||||||
|
<div class='form-group'>
|
||||||
|
<label class='control-label requiredField' for='id_label'>
|
||||||
|
{% trans "Select Label" %}
|
||||||
|
</label>
|
||||||
|
<div class='controls'>
|
||||||
|
<select id='id_label' class='select form-control name='label'>
|
||||||
|
${label_list}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>`;
|
||||||
|
|
||||||
|
openModal({
|
||||||
|
modal: modal,
|
||||||
|
});
|
||||||
|
|
||||||
|
modalEnable(modal, true);
|
||||||
|
modalSetTitle(modal, '{% trans "Select Label Template" %}');
|
||||||
|
modalSetContent(modal, html);
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
function makeOption(text, value, title) {
|
function makeOption(text, value, title) {
|
||||||
/* Format an option for a select element
|
/* Format an option for a select element
|
||||||
*/
|
*/
|
||||||
@ -392,7 +394,7 @@ function renderErrorMessage(xhr) {
|
|||||||
<div class='panel panel-default'>
|
<div class='panel panel-default'>
|
||||||
<div class='panel panel-heading'>
|
<div class='panel panel-heading'>
|
||||||
<div class='panel-title'>
|
<div class='panel-title'>
|
||||||
<a data-toggle='collapse' href="#collapse-error-info">Show Error Information</a>
|
<a data-toggle='collapse' href="#collapse-error-info">{% trans "Show Error Information" %}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='panel-collapse collapse' id='collapse-error-info'>
|
<div class='panel-collapse collapse' id='collapse-error-info'>
|
||||||
@ -459,8 +461,8 @@ function showQuestionDialog(title, content, options={}) {
|
|||||||
modalSetTitle(modal, title);
|
modalSetTitle(modal, title);
|
||||||
modalSetContent(modal, content);
|
modalSetContent(modal, content);
|
||||||
|
|
||||||
var accept_text = options.accept_text || 'Accept';
|
var accept_text = options.accept_text || '{% trans "Accept" %}';
|
||||||
var cancel_text = options.cancel_text || 'Cancel';
|
var cancel_text = options.cancel_text || '{% trans "Cancel" %}';
|
||||||
|
|
||||||
$(modal).find('#modal-form-cancel').html(cancel_text);
|
$(modal).find('#modal-form-cancel').html(cancel_text);
|
||||||
$(modal).find('#modal-form-accept').html(accept_text);
|
$(modal).find('#modal-form-accept').html(accept_text);
|
||||||
@ -524,7 +526,7 @@ function openModal(options) {
|
|||||||
if (options.title) {
|
if (options.title) {
|
||||||
modalSetTitle(modal, options.title);
|
modalSetTitle(modal, options.title);
|
||||||
} else {
|
} else {
|
||||||
modalSetTitle(modal, 'Loading Data...');
|
modalSetTitle(modal, '{% trans "Loading Data" %}...');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unless the content is explicitly set, display loading message
|
// Unless the content is explicitly set, display loading message
|
||||||
@ -535,8 +537,8 @@ function openModal(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Default labels for 'Submit' and 'Close' buttons in the form
|
// Default labels for 'Submit' and 'Close' buttons in the form
|
||||||
var submit_text = options.submit_text || 'Submit';
|
var submit_text = options.submit_text || '{% trans "Submit" %}';
|
||||||
var close_text = options.close_text || 'Close';
|
var close_text = options.close_text || '{% trans "Close" %}';
|
||||||
|
|
||||||
modalSetButtonText(modal, submit_text, close_text);
|
modalSetButtonText(modal, submit_text, close_text);
|
||||||
|
|
||||||
@ -745,7 +747,7 @@ function handleModalForm(url, options) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$(modal).modal('hide');
|
$(modal).modal('hide');
|
||||||
showAlertDialog('Invalid response from server', 'Form data missing from server response');
|
showAlertDialog('{% trans "Invalid response from server" %}', '{% trans "Form data missing from server response" %}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -758,7 +760,7 @@ function handleModalForm(url, options) {
|
|||||||
// There was an error submitting form data via POST
|
// There was an error submitting form data via POST
|
||||||
|
|
||||||
$(modal).modal('hide');
|
$(modal).modal('hide');
|
||||||
showAlertDialog('Error posting form data', renderErrorMessage(xhr));
|
showAlertDialog('{% trans "Error posting form data" %}', renderErrorMessage(xhr));
|
||||||
},
|
},
|
||||||
complete: function(xhr) {
|
complete: function(xhr) {
|
||||||
//TODO
|
//TODO
|
||||||
@ -793,8 +795,8 @@ function launchModalForm(url, options = {}) {
|
|||||||
var modal = options.modal || '#modal-form';
|
var modal = options.modal || '#modal-form';
|
||||||
|
|
||||||
// Default labels for 'Submit' and 'Close' buttons in the form
|
// Default labels for 'Submit' and 'Close' buttons in the form
|
||||||
var submit_text = options.submit_text || 'Submit';
|
var submit_text = options.submit_text || '{% trans "Submit" %}';
|
||||||
var close_text = options.close_text || 'Close';
|
var close_text = options.close_text || '{% trans "Close" %}';
|
||||||
|
|
||||||
// Form the ajax request to retrieve the django form data
|
// Form the ajax request to retrieve the django form data
|
||||||
ajax_data = {
|
ajax_data = {
|
||||||
@ -842,7 +844,7 @@ function launchModalForm(url, options = {}) {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
$(modal).modal('hide');
|
$(modal).modal('hide');
|
||||||
showAlertDialog('Invalid server response', 'JSON response missing form data');
|
showAlertDialog('{% trans "Invalid server response" %}', '{% trans "JSON response missing form data" %}');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function (xhr, ajaxOptions, thrownError) {
|
error: function (xhr, ajaxOptions, thrownError) {
|
||||||
@ -852,36 +854,36 @@ function launchModalForm(url, options = {}) {
|
|||||||
if (xhr.status == 0) {
|
if (xhr.status == 0) {
|
||||||
// No response from the server
|
// No response from the server
|
||||||
showAlertDialog(
|
showAlertDialog(
|
||||||
"No Response",
|
'{% trans "No Response" %}',
|
||||||
"No response from the InvenTree server",
|
'{% trans "No response from the InvenTree server" %}',
|
||||||
);
|
);
|
||||||
} else if (xhr.status == 400) {
|
} else if (xhr.status == 400) {
|
||||||
showAlertDialog(
|
showAlertDialog(
|
||||||
"Error 400: Bad Request",
|
'{% trans "Error 400: Bad Request" %}',
|
||||||
"Server returned error code 400"
|
'{% trans "Server returned error code 400" %}',
|
||||||
);
|
);
|
||||||
} else if (xhr.status == 401) {
|
} else if (xhr.status == 401) {
|
||||||
showAlertDialog(
|
showAlertDialog(
|
||||||
"Error 401: Not Authenticated",
|
'{% trans "Error 401: Not Authenticated" %}',
|
||||||
"Authentication credentials not supplied"
|
'{% trans "Authentication credentials not supplied" %}',
|
||||||
);
|
);
|
||||||
} else if (xhr.status == 403) {
|
} else if (xhr.status == 403) {
|
||||||
showAlertDialog(
|
showAlertDialog(
|
||||||
"Error 403: Permission Denied",
|
'{% trans "Error 403: Permission Denied" %}',
|
||||||
"You do not have the required permissions to access this function"
|
'{% trans "You do not have the required permissions to access this function" %}',
|
||||||
);
|
);
|
||||||
} else if (xhr.status == 404) {
|
} else if (xhr.status == 404) {
|
||||||
showAlertDialog(
|
showAlertDialog(
|
||||||
"Error 404: Resource Not Found",
|
'{% trans "Error 404: Resource Not Found" %}',
|
||||||
"The requested resource could not be located on the server"
|
'{% trans "The requested resource could not be located on the server" %}',
|
||||||
);
|
);
|
||||||
} else if (xhr.status == 408) {
|
} else if (xhr.status == 408) {
|
||||||
showAlertDialog(
|
showAlertDialog(
|
||||||
"Error 408: Timeout",
|
'{% trans "Error 408: Timeout" %}',
|
||||||
"Connection timeout while requesting data from server"
|
'{% trans "Connection timeout while requesting data from server" %}',
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
showAlertDialog('Error requesting form data', renderErrorMessage(xhr));
|
showAlertDialog('{% trans "Error requesting form data" %}', renderErrorMessage(xhr));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Modal form error: " + xhr.status);
|
console.log("Modal form error: " + xhr.status);
|
@ -645,6 +645,39 @@ function loadStockTable(table, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Automatically link button callbacks
|
// Automatically link button callbacks
|
||||||
|
$('#multi-item-print-label').click(function() {
|
||||||
|
var selections = $('#stock-table').bootstrapTable('getSelections');
|
||||||
|
|
||||||
|
var items = [];
|
||||||
|
|
||||||
|
selections.forEach(function(item) {
|
||||||
|
items.push(item.pk);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Request available labels from the server
|
||||||
|
inventreeGet(
|
||||||
|
'{% url "api-stockitem-label-list" %}',
|
||||||
|
{
|
||||||
|
enabled: true,
|
||||||
|
items: items,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
success: function(response) {
|
||||||
|
|
||||||
|
if (response.length == 0) {
|
||||||
|
showAlertDialog(
|
||||||
|
'{% trans "No Labels Found" %}',
|
||||||
|
'{% trans "No labels found which match selected stock item(s)" %}',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var label = selectLabel(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
$('#multi-item-stocktake').click(function() {
|
$('#multi-item-stocktake').click(function() {
|
||||||
stockAdjustment('count');
|
stockAdjustment('count');
|
||||||
});
|
});
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button id='stock-options' class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{% trans "Options" %}<span class="caret"></span></button>
|
<button id='stock-options' class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{% trans "Options" %}<span class="caret"></span></button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href='#' id='multi-item-print-label' title='{% trans "Print labels" %}'><span class='fas fa-tags'></span> {% trans "Print labels" %}</a></li>
|
||||||
{% if roles.stock.change %}
|
{% if roles.stock.change %}
|
||||||
<li><a href="#" id='multi-item-add' title='{% trans "Add to selected stock items" %}'><span class='fas fa-plus-circle'></span> {% trans "Add stock" %}</a></li>
|
<li><a href="#" id='multi-item-add' title='{% trans "Add to selected stock items" %}'><span class='fas fa-plus-circle'></span> {% trans "Add stock" %}</a></li>
|
||||||
<li><a href="#" id='multi-item-remove' title='{% trans "Remove from selected stock items" %}'><span class='fas fa-minus-circle'></span> {% trans "Remove stock" %}</a></li>
|
<li><a href="#" id='multi-item-remove' title='{% trans "Remove from selected stock items" %}'><span class='fas fa-minus-circle'></span> {% trans "Remove stock" %}</a></li>
|
||||||
|
Loading…
Reference in New Issue
Block a user