mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
commit
82433db1ea
@ -20,9 +20,6 @@ from .version import inventreeVersion, inventreeInstanceName
|
|||||||
|
|
||||||
from plugins import plugins as inventree_plugins
|
from plugins import plugins as inventree_plugins
|
||||||
|
|
||||||
# Load barcode plugins
|
|
||||||
print("Loading barcode plugins")
|
|
||||||
barcode_plugins = inventree_plugins.load_barcode_plugins()
|
|
||||||
|
|
||||||
print("Loading action plugins")
|
print("Loading action plugins")
|
||||||
action_plugins = inventree_plugins.load_action_plugins()
|
action_plugins = inventree_plugins.load_action_plugins()
|
||||||
@ -100,66 +97,3 @@ class ActionPluginView(APIView):
|
|||||||
'error': _("No matching action found"),
|
'error': _("No matching action found"),
|
||||||
"action": action,
|
"action": action,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class BarcodePluginView(APIView):
|
|
||||||
"""
|
|
||||||
Endpoint for handling barcode scan requests.
|
|
||||||
|
|
||||||
Barcode data are decoded by the client application,
|
|
||||||
and sent to this endpoint (as a JSON object) for validation.
|
|
||||||
|
|
||||||
A barcode could follow the internal InvenTree barcode format,
|
|
||||||
or it could match to a third-party barcode format (e.g. Digikey).
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
permission_classes = [
|
|
||||||
permissions.IsAuthenticated,
|
|
||||||
]
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
|
|
||||||
response = {}
|
|
||||||
|
|
||||||
barcode_data = request.data.get('barcode', None)
|
|
||||||
|
|
||||||
print("Barcode data:")
|
|
||||||
print(barcode_data)
|
|
||||||
|
|
||||||
if barcode_data is None:
|
|
||||||
response['error'] = _('No barcode data provided')
|
|
||||||
else:
|
|
||||||
# Look for a barcode plugin that knows how to handle the data
|
|
||||||
for plugin_class in barcode_plugins:
|
|
||||||
|
|
||||||
# Instantiate the plugin with the provided plugin data
|
|
||||||
plugin = plugin_class(barcode_data)
|
|
||||||
|
|
||||||
if plugin.validate():
|
|
||||||
|
|
||||||
# Plugin should return a dict response
|
|
||||||
response = plugin.decode()
|
|
||||||
|
|
||||||
if type(response) is dict:
|
|
||||||
if 'success' not in response.keys() and 'error' not in response.keys():
|
|
||||||
response['success'] = _('Barcode successfully decoded')
|
|
||||||
else:
|
|
||||||
response = {
|
|
||||||
'error': _('Barcode plugin returned incorrect response')
|
|
||||||
}
|
|
||||||
|
|
||||||
response['plugin'] = plugin.plugin_name()
|
|
||||||
response['hash'] = plugin.hash()
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
if 'error' not in response and 'success' not in response:
|
|
||||||
response = {
|
|
||||||
'error': _('Unknown barcode format'),
|
|
||||||
}
|
|
||||||
|
|
||||||
# Include the original barcode data
|
|
||||||
response['barcode_data'] = barcode_data
|
|
||||||
|
|
||||||
return Response(response)
|
|
||||||
|
43
InvenTree/InvenTree/plugins.py
Normal file
43
InvenTree/InvenTree/plugins.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
import importlib
|
||||||
|
import pkgutil
|
||||||
|
|
||||||
|
|
||||||
|
def iter_namespace(pkg):
|
||||||
|
|
||||||
|
return pkgutil.iter_modules(pkg.__path__, pkg.__name__ + ".")
|
||||||
|
|
||||||
|
|
||||||
|
def get_modules(pkg):
|
||||||
|
# Return all modules in a given package
|
||||||
|
return [importlib.import_module(name) for finder, name, ispkg in iter_namespace(pkg)]
|
||||||
|
|
||||||
|
|
||||||
|
def get_classes(module):
|
||||||
|
# Return all classes in a given module
|
||||||
|
return inspect.getmembers(module, inspect.isclass)
|
||||||
|
|
||||||
|
|
||||||
|
def get_plugins(pkg, baseclass):
|
||||||
|
"""
|
||||||
|
Return a list of all modules under a given package.
|
||||||
|
|
||||||
|
- Modules must be a subclass of the provided 'baseclass'
|
||||||
|
- Modules must have a non-empty PLUGIN_NAME parameter
|
||||||
|
"""
|
||||||
|
|
||||||
|
plugins = []
|
||||||
|
|
||||||
|
modules = get_modules(pkg)
|
||||||
|
|
||||||
|
# Iterate through each module in the package
|
||||||
|
for mod in modules:
|
||||||
|
# Iterate through each class in the module
|
||||||
|
for item in get_classes(mod):
|
||||||
|
plugin = item[1]
|
||||||
|
if issubclass(plugin, baseclass) and plugin.PLUGIN_NAME:
|
||||||
|
plugins.append(plugin)
|
||||||
|
|
||||||
|
return plugins
|
@ -82,12 +82,25 @@
|
|||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar-barcode-li {
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar-nav > li {
|
.navbar-nav > li {
|
||||||
border-left: 1px solid;
|
border-left: 1px solid;
|
||||||
border-right: 1px solid;
|
border-right: 1px solid;
|
||||||
border-color: #eee;
|
border-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar-form {
|
||||||
|
padding-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#barcode-scan {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.icon-header {
|
.icon-header {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
@ -259,6 +272,16 @@
|
|||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-buttons {
|
||||||
|
display: inline-block
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu .open{
|
||||||
|
z-index: 1000;
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
/* Styles for table buttons and filtering */
|
/* Styles for table buttons and filtering */
|
||||||
.button-toolbar .btn {
|
.button-toolbar .btn {
|
||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
@ -455,6 +478,7 @@
|
|||||||
|
|
||||||
.media-body {
|
.media-body {
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation {
|
.navigation {
|
||||||
|
@ -166,14 +166,28 @@ function modalSetContent(modal, content='') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function modalSetSubmitText(modal, text) {
|
||||||
|
if (text) {
|
||||||
|
$(modal).find('#modal-form-submit').html(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function modalSetCloseText(modal, text) {
|
||||||
|
if (text) {
|
||||||
|
$(modal).find('#modal-form-close').html(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function modalSetButtonText(modal, submit_text, close_text) {
|
function modalSetButtonText(modal, submit_text, close_text) {
|
||||||
/* Set the button text for a modal form
|
/* Set the button text for a modal form
|
||||||
*
|
*
|
||||||
* submit_text - text for the form submit button
|
* submit_text - text for the form submit button
|
||||||
* close_text - text for the form dismiss button
|
* close_text - text for the form dismiss button
|
||||||
*/
|
*/
|
||||||
$(modal).find("#modal-form-submit").html(submit_text);
|
modalSetSubmitText(modal, submit_text);
|
||||||
$(modal).find("#modal-form-close").html(close_text);
|
modalSetCloseText(modal, close_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -442,7 +456,8 @@ function attachSecondaryModal(modal, options) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
var select = '#id_' + options.field;
|
var select = '#id_' + options.field;
|
||||||
var option = new Option(response.text, response.pk, true, true)
|
|
||||||
|
var option = new Option(response.text, response.pk, true, true);
|
||||||
|
|
||||||
$(modal).find(select).append(option).trigger('change');
|
$(modal).find(select).append(option).trigger('change');
|
||||||
}
|
}
|
||||||
|
@ -79,30 +79,3 @@ class APITests(APITestCase):
|
|||||||
self.assertIn('instance', data)
|
self.assertIn('instance', data)
|
||||||
|
|
||||||
self.assertEquals('InvenTree', data['server'])
|
self.assertEquals('InvenTree', data['server'])
|
||||||
|
|
||||||
def test_barcode_fail(self):
|
|
||||||
# Test barcode endpoint without auth
|
|
||||||
response = self.client.post(reverse('api-barcode-plugin'), format='json')
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
|
||||||
|
|
||||||
def test_barcode(self):
|
|
||||||
""" Test the barcode endpoint """
|
|
||||||
|
|
||||||
self.tokenAuth()
|
|
||||||
|
|
||||||
url = reverse('api-barcode-plugin')
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'barcode': {
|
|
||||||
'asdlaksdfalsdkfsa;fdlkasd;'
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
response = self.client.post(url, format='json', data=data)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
||||||
|
|
||||||
self.assertIn('error', response.data)
|
|
||||||
self.assertIn('barcode_data', response.data)
|
|
||||||
self.assertEqual(response.data['error'], 'Unknown barcode format')
|
|
||||||
|
@ -20,6 +20,7 @@ from stock.urls import stock_urls
|
|||||||
from build.urls import build_urls
|
from build.urls import build_urls
|
||||||
from order.urls import order_urls
|
from order.urls import order_urls
|
||||||
|
|
||||||
|
from barcode.api import barcode_api_urls
|
||||||
from common.api import common_api_urls
|
from common.api import common_api_urls
|
||||||
from part.api import part_api_urls, bom_api_urls
|
from part.api import part_api_urls, bom_api_urls
|
||||||
from company.api import company_api_urls
|
from company.api import company_api_urls
|
||||||
@ -37,13 +38,15 @@ from .views import IndexView, SearchView, DatabaseStatsView
|
|||||||
from .views import SettingsView, EditUserView, SetPasswordView
|
from .views import SettingsView, EditUserView, SetPasswordView
|
||||||
from .views import DynamicJsView
|
from .views import DynamicJsView
|
||||||
|
|
||||||
from .api import InfoView, BarcodePluginView, ActionPluginView
|
from .api import InfoView
|
||||||
|
from .api import ActionPluginView
|
||||||
|
|
||||||
from users.urls import user_urls
|
from users.urls import user_urls
|
||||||
|
|
||||||
admin.site.site_header = "InvenTree Admin"
|
admin.site.site_header = "InvenTree Admin"
|
||||||
|
|
||||||
apipatterns = [
|
apipatterns = [
|
||||||
|
url(r'^barcode/', include(barcode_api_urls)),
|
||||||
url(r'^common/', include(common_api_urls)),
|
url(r'^common/', include(common_api_urls)),
|
||||||
url(r'^part/', include(part_api_urls)),
|
url(r'^part/', include(part_api_urls)),
|
||||||
url(r'^bom/', include(bom_api_urls)),
|
url(r'^bom/', include(bom_api_urls)),
|
||||||
@ -56,7 +59,6 @@ apipatterns = [
|
|||||||
url(r'^user/', include(user_urls)),
|
url(r'^user/', include(user_urls)),
|
||||||
|
|
||||||
# Plugin endpoints
|
# Plugin endpoints
|
||||||
url(r'^barcode/', BarcodePluginView.as_view(), name='api-barcode-plugin'),
|
|
||||||
url(r'^action/', ActionPluginView.as_view(), name='api-action-plugin'),
|
url(r'^action/', ActionPluginView.as_view(), name='api-action-plugin'),
|
||||||
|
|
||||||
# InvenTree information endpoint
|
# InvenTree information endpoint
|
||||||
@ -74,7 +76,9 @@ settings_urls = [
|
|||||||
url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings'),
|
url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Some javascript files are served 'dynamically', allowing them to pass through the Django translation layer
|
||||||
dynamic_javascript_urls = [
|
dynamic_javascript_urls = [
|
||||||
|
url(r'^barcode.js', DynamicJsView.as_view(template_name='js/barcode.html'), name='barcode.js'),
|
||||||
url(r'^part.js', DynamicJsView.as_view(template_name='js/part.html'), name='part.js'),
|
url(r'^part.js', DynamicJsView.as_view(template_name='js/part.html'), name='part.js'),
|
||||||
url(r'^stock.js', DynamicJsView.as_view(template_name='js/stock.html'), name='stock.js'),
|
url(r'^stock.js', DynamicJsView.as_view(template_name='js/stock.html'), name='stock.js'),
|
||||||
url(r'^build.js', DynamicJsView.as_view(template_name='js/build.html'), name='build.js'),
|
url(r'^build.js', DynamicJsView.as_view(template_name='js/build.html'), name='build.js'),
|
||||||
|
240
InvenTree/barcode/api.py
Normal file
240
InvenTree/barcode/api.py
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.conf.urls import url
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from rest_framework.exceptions import ValidationError
|
||||||
|
from rest_framework import permissions
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from stock.models import StockItem
|
||||||
|
from stock.serializers import StockItemSerializer
|
||||||
|
|
||||||
|
from barcode.barcode import load_barcode_plugins, hash_barcode
|
||||||
|
|
||||||
|
|
||||||
|
class BarcodeScan(APIView):
|
||||||
|
"""
|
||||||
|
Endpoint for handling generic barcode scan requests.
|
||||||
|
|
||||||
|
Barcode data are decoded by the client application,
|
||||||
|
and sent to this endpoint (as a JSON object) for validation.
|
||||||
|
|
||||||
|
A barcode could follow the internal InvenTree barcode format,
|
||||||
|
or it could match to a third-party barcode format (e.g. Digikey).
|
||||||
|
|
||||||
|
When a barcode is sent to the server, the following parameters must be provided:
|
||||||
|
|
||||||
|
- barcode: The raw barcode data
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
Third-party barcode formats may be supported using 'plugins'
|
||||||
|
(more information to follow)
|
||||||
|
|
||||||
|
hashing:
|
||||||
|
Barcode hashes are calculated using MD5
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
permission_classes = [
|
||||||
|
permissions.IsAuthenticated,
|
||||||
|
]
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Respond to a barcode POST request
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = request.data
|
||||||
|
|
||||||
|
if 'barcode' not in data:
|
||||||
|
raise ValidationError({'barcode': _('Must provide barcode_data parameter')})
|
||||||
|
|
||||||
|
plugins = load_barcode_plugins()
|
||||||
|
|
||||||
|
barcode_data = data.get('barcode')
|
||||||
|
|
||||||
|
# Look for a barcode plugin which knows how to deal with this barcode
|
||||||
|
plugin = None
|
||||||
|
|
||||||
|
for plugin_class in plugins:
|
||||||
|
plugin_instance = plugin_class(barcode_data)
|
||||||
|
|
||||||
|
if plugin_instance.validate():
|
||||||
|
plugin = plugin_instance
|
||||||
|
break
|
||||||
|
|
||||||
|
match_found = False
|
||||||
|
response = {}
|
||||||
|
|
||||||
|
response['barcode_data'] = barcode_data
|
||||||
|
|
||||||
|
# A plugin has been found!
|
||||||
|
if plugin is not None:
|
||||||
|
|
||||||
|
# Try to associate with a stock item
|
||||||
|
item = plugin.getStockItem()
|
||||||
|
|
||||||
|
if item is None:
|
||||||
|
item = plugin.getStockItemByHash()
|
||||||
|
|
||||||
|
if item is not None:
|
||||||
|
response['stockitem'] = plugin.renderStockItem(item)
|
||||||
|
response['url'] = reverse('stock-item-detail', kwargs={'pk': item.id})
|
||||||
|
match_found = True
|
||||||
|
|
||||||
|
# Try to associate with a stock location
|
||||||
|
loc = plugin.getStockLocation()
|
||||||
|
|
||||||
|
if loc is not None:
|
||||||
|
response['stocklocation'] = plugin.renderStockLocation(loc)
|
||||||
|
response['url'] = reverse('location-detail', kwargs={'pk': loc.id})
|
||||||
|
match_found = True
|
||||||
|
|
||||||
|
# Try to associate with a part
|
||||||
|
part = plugin.getPart()
|
||||||
|
|
||||||
|
if part is not None:
|
||||||
|
response['part'] = plugin.renderPart(part)
|
||||||
|
response['url'] = reverse('part-detail', kwargs={'pk': part.id})
|
||||||
|
match_found = True
|
||||||
|
|
||||||
|
response['hash'] = plugin.hash()
|
||||||
|
response['plugin'] = plugin.name
|
||||||
|
|
||||||
|
# No plugin is found!
|
||||||
|
# However, the hash of the barcode may still be associated with a StockItem!
|
||||||
|
else:
|
||||||
|
hash = hash_barcode(barcode_data)
|
||||||
|
|
||||||
|
response['hash'] = hash
|
||||||
|
response['plugin'] = None
|
||||||
|
|
||||||
|
# Try to look for a matching StockItem
|
||||||
|
try:
|
||||||
|
item = StockItem.objects.get(uid=hash)
|
||||||
|
serializer = StockItemSerializer(item, part_detail=True, location_detail=True, supplier_part_detail=True)
|
||||||
|
response['stockitem'] = serializer.data
|
||||||
|
response['url'] = reverse('stock-item-detail', kwargs={'pk': item.id})
|
||||||
|
match_found = True
|
||||||
|
except StockItem.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not match_found:
|
||||||
|
response['error'] = _('No match found for barcode data')
|
||||||
|
else:
|
||||||
|
response['success'] = _('Match found for barcode data')
|
||||||
|
|
||||||
|
return Response(response)
|
||||||
|
|
||||||
|
|
||||||
|
class BarcodeAssign(APIView):
|
||||||
|
"""
|
||||||
|
Endpoint for assigning a barcode to a stock item.
|
||||||
|
|
||||||
|
- This only works if the barcode is not already associated with an object in the database
|
||||||
|
- If the barcode does not match an object, then the barcode hash is assigned to the StockItem
|
||||||
|
"""
|
||||||
|
|
||||||
|
permission_classes = [
|
||||||
|
permissions.IsAuthenticated
|
||||||
|
]
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
data = request.data
|
||||||
|
|
||||||
|
if 'barcode' not in data:
|
||||||
|
raise ValidationError({'barcode': _('Must provide barcode_data parameter')})
|
||||||
|
|
||||||
|
if 'stockitem' not in data:
|
||||||
|
raise ValidationError({'stockitem': _('Must provide stockitem parameter')})
|
||||||
|
|
||||||
|
barcode_data = data['barcode']
|
||||||
|
|
||||||
|
try:
|
||||||
|
item = StockItem.objects.get(pk=data['stockitem'])
|
||||||
|
except (ValueError, StockItem.DoesNotExist):
|
||||||
|
raise ValidationError({'stockitem': _('No matching stock item found')})
|
||||||
|
|
||||||
|
plugins = load_barcode_plugins()
|
||||||
|
|
||||||
|
plugin = None
|
||||||
|
|
||||||
|
for plugin_class in plugins:
|
||||||
|
plugin_instance = plugin_class(barcode_data)
|
||||||
|
|
||||||
|
if plugin_instance.validate():
|
||||||
|
plugin = plugin_instance
|
||||||
|
break
|
||||||
|
|
||||||
|
match_found = False
|
||||||
|
|
||||||
|
response = {}
|
||||||
|
|
||||||
|
response['barcode_data'] = barcode_data
|
||||||
|
|
||||||
|
# Matching plugin was found
|
||||||
|
if plugin is not None:
|
||||||
|
|
||||||
|
hash = plugin.hash()
|
||||||
|
response['hash'] = hash
|
||||||
|
response['plugin'] = plugin.name
|
||||||
|
|
||||||
|
# Ensure that the barcode does not already match a database entry
|
||||||
|
|
||||||
|
if plugin.getStockItem() is not None:
|
||||||
|
match_found = True
|
||||||
|
response['error'] = _('Barcode already matches StockItem object')
|
||||||
|
|
||||||
|
if plugin.getStockLocation() is not None:
|
||||||
|
match_found = True
|
||||||
|
response['error'] = _('Barcode already matches StockLocation object')
|
||||||
|
|
||||||
|
if plugin.getPart() is not None:
|
||||||
|
match_found = True
|
||||||
|
response['error'] = _('Barcode already matches Part object')
|
||||||
|
|
||||||
|
if not match_found:
|
||||||
|
item = plugin.getStockItemByHash()
|
||||||
|
|
||||||
|
if item is not None:
|
||||||
|
response['error'] = _('Barcode hash already matches StockItem object')
|
||||||
|
match_found = True
|
||||||
|
|
||||||
|
else:
|
||||||
|
hash = hash_barcode(barcode_data)
|
||||||
|
|
||||||
|
response['hash'] = hash
|
||||||
|
response['plugin'] = None
|
||||||
|
|
||||||
|
# Lookup stock item by hash
|
||||||
|
try:
|
||||||
|
item = StockItem.objects.get(uid=hash)
|
||||||
|
response['error'] = _('Barcode hash already matches StockItem object')
|
||||||
|
match_found = True
|
||||||
|
except StockItem.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not match_found:
|
||||||
|
response['success'] = _('Barcode associated with StockItem')
|
||||||
|
|
||||||
|
# Save the barcode hash
|
||||||
|
item.uid = response['hash']
|
||||||
|
item.save()
|
||||||
|
|
||||||
|
serializer = StockItemSerializer(item, part_detail=True, location_detail=True, supplier_part_detail=True)
|
||||||
|
response['stockitem'] = serializer.data
|
||||||
|
|
||||||
|
return Response(response)
|
||||||
|
|
||||||
|
|
||||||
|
barcode_api_urls = [
|
||||||
|
|
||||||
|
url(r'^link/$', BarcodeAssign.as_view(), name='api-barcode-link'),
|
||||||
|
|
||||||
|
# Catch-all performs barcode 'scan'
|
||||||
|
url(r'^.*$', BarcodeScan.as_view(), name='api-barcode-scan'),
|
||||||
|
]
|
147
InvenTree/barcode/barcode.py
Normal file
147
InvenTree/barcode/barcode.py
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
from InvenTree import plugins as InvenTreePlugins
|
||||||
|
from barcode import plugins as BarcodePlugins
|
||||||
|
|
||||||
|
from stock.models import StockItem
|
||||||
|
from stock.serializers import StockItemSerializer, LocationSerializer
|
||||||
|
from part.serializers import PartSerializer
|
||||||
|
|
||||||
|
|
||||||
|
def hash_barcode(barcode_data):
|
||||||
|
"""
|
||||||
|
Calculate an MD5 hash of barcode data
|
||||||
|
"""
|
||||||
|
|
||||||
|
hash = hashlib.md5(str(barcode_data).encode())
|
||||||
|
return str(hash.hexdigest())
|
||||||
|
|
||||||
|
|
||||||
|
class BarcodePlugin:
|
||||||
|
"""
|
||||||
|
Base class for barcode handling.
|
||||||
|
Custom barcode plugins should extend this class as necessary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Override the barcode plugin name for each sub-class
|
||||||
|
PLUGIN_NAME = ""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.PLUGIN_NAME
|
||||||
|
|
||||||
|
def __init__(self, barcode_data):
|
||||||
|
"""
|
||||||
|
Initialize the BarcodePlugin instance
|
||||||
|
|
||||||
|
Args:
|
||||||
|
barcode_data - The raw barcode data
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.data = barcode_data
|
||||||
|
|
||||||
|
def getStockItem(self):
|
||||||
|
"""
|
||||||
|
Attempt to retrieve a StockItem associated with this barcode.
|
||||||
|
Default implementation returns None
|
||||||
|
"""
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getStockItemByHash(self):
|
||||||
|
"""
|
||||||
|
Attempt to retrieve a StockItem associated with this barcode,
|
||||||
|
based on the barcode hash.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
item = StockItem.objects.get(uid=self.hash())
|
||||||
|
return item
|
||||||
|
except StockItem.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def renderStockItem(self, item):
|
||||||
|
"""
|
||||||
|
Render a stock item to JSON response
|
||||||
|
"""
|
||||||
|
|
||||||
|
serializer = StockItemSerializer(item, part_detail=True, location_detail=True, supplier_part_detail=True)
|
||||||
|
return serializer.data
|
||||||
|
|
||||||
|
def getStockLocation(self):
|
||||||
|
"""
|
||||||
|
Attempt to retrieve a StockLocation associated with this barcode.
|
||||||
|
Default implementation returns None
|
||||||
|
"""
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def renderStockLocation(self, loc):
|
||||||
|
"""
|
||||||
|
Render a stock location to a JSON response
|
||||||
|
"""
|
||||||
|
|
||||||
|
serializer = LocationSerializer(loc)
|
||||||
|
return serializer.data
|
||||||
|
|
||||||
|
def getPart(self):
|
||||||
|
"""
|
||||||
|
Attempt to retrieve a Part associated with this barcode.
|
||||||
|
Default implementation returns None
|
||||||
|
"""
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def renderPart(self, part):
|
||||||
|
"""
|
||||||
|
Render a part to JSON response
|
||||||
|
"""
|
||||||
|
|
||||||
|
serializer = PartSerializer(part)
|
||||||
|
return serializer.data
|
||||||
|
|
||||||
|
def hash(self):
|
||||||
|
"""
|
||||||
|
Calculate a hash for the barcode data.
|
||||||
|
This is supposed to uniquely identify the barcode contents,
|
||||||
|
at least within the bardcode sub-type.
|
||||||
|
|
||||||
|
The default implementation simply returns an MD5 hash of the barcode data,
|
||||||
|
encoded to a string.
|
||||||
|
|
||||||
|
This may be sufficient for most applications, but can obviously be overridden
|
||||||
|
by a subclass.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
return hash_barcode(self.data)
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
"""
|
||||||
|
Default implementation returns False
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def load_barcode_plugins(debug=False):
|
||||||
|
"""
|
||||||
|
Function to load all barcode plugins
|
||||||
|
"""
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
print("Loading barcode plugins")
|
||||||
|
|
||||||
|
plugins = InvenTreePlugins.get_plugins(BarcodePlugins, BarcodePlugin)
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
if len(plugins) > 0:
|
||||||
|
print("Discovered {n} plugins:".format(n=len(plugins)))
|
||||||
|
|
||||||
|
for p in plugins:
|
||||||
|
print(" - {p}".format(p=p.PLUGIN_NAME))
|
||||||
|
else:
|
||||||
|
print("No barcode plugins found")
|
||||||
|
|
||||||
|
return plugins
|
19
InvenTree/barcode/plugins/digikey_barcode.py
Normal file
19
InvenTree/barcode/plugins/digikey_barcode.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
DigiKey barcode decoding
|
||||||
|
"""
|
||||||
|
|
||||||
|
from barcode.barcode import BarcodePlugin
|
||||||
|
|
||||||
|
|
||||||
|
class DigikeyBarcodePlugin(BarcodePlugin):
|
||||||
|
|
||||||
|
PLUGIN_NAME = "DigikeyBarcode"
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
"""
|
||||||
|
TODO: Validation of Digikey barcodes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return False
|
108
InvenTree/barcode/plugins/inventree_barcode.py
Normal file
108
InvenTree/barcode/plugins/inventree_barcode.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
"""
|
||||||
|
The InvenTreeBarcodePlugin validates barcodes generated by InvenTree itself.
|
||||||
|
It can be used as a template for developing third-party barcode plugins.
|
||||||
|
|
||||||
|
The data format is very simple, and maps directly to database objects,
|
||||||
|
via the "id" parameter.
|
||||||
|
|
||||||
|
Parsing an InvenTree barcode simply involves validating that the
|
||||||
|
references model objects actually exist in the database.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from barcode.barcode import BarcodePlugin
|
||||||
|
|
||||||
|
from stock.models import StockItem, StockLocation
|
||||||
|
from part.models import Part
|
||||||
|
|
||||||
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class InvenTreeBarcodePlugin(BarcodePlugin):
|
||||||
|
|
||||||
|
PLUGIN_NAME = "InvenTreeBarcode"
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
"""
|
||||||
|
An "InvenTree" barcode must be a jsonnable-dict with the following tags:
|
||||||
|
|
||||||
|
{
|
||||||
|
'tool': 'InvenTree',
|
||||||
|
'version': <anything>
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The data must either be dict or be able to dictified
|
||||||
|
if type(self.data) is dict:
|
||||||
|
pass
|
||||||
|
elif type(self.data) is str:
|
||||||
|
try:
|
||||||
|
self.data = json.loads(self.data)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for key in ['tool', 'version']:
|
||||||
|
if key not in self.data.keys():
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self.data['tool'] == 'InvenTree':
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getStockItem(self):
|
||||||
|
|
||||||
|
for k in self.data.keys():
|
||||||
|
if k.lower() == 'stockitem':
|
||||||
|
try:
|
||||||
|
pk = self.data[k]['id']
|
||||||
|
except (AttributeError, KeyError):
|
||||||
|
raise ValidationError({k: "id parameter not supplied"})
|
||||||
|
|
||||||
|
try:
|
||||||
|
item = StockItem.objects.get(pk=pk)
|
||||||
|
return item
|
||||||
|
except (ValueError, StockItem.DoesNotExist):
|
||||||
|
raise ValidationError({k, "Stock item does not exist"})
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getStockLocation(self):
|
||||||
|
|
||||||
|
for k in self.data.keys():
|
||||||
|
if k.lower() == 'stocklocation':
|
||||||
|
try:
|
||||||
|
pk = self.data[k]['id']
|
||||||
|
except (AttributeError, KeyError):
|
||||||
|
raise ValidationError({k: "id parameter not supplied"})
|
||||||
|
|
||||||
|
try:
|
||||||
|
loc = StockLocation.objects.get(pk=pk)
|
||||||
|
return loc
|
||||||
|
except (ValueError, StockLocation.DoesNotExist):
|
||||||
|
raise ValidationError({k, "Stock location does not exist"})
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getPart(self):
|
||||||
|
|
||||||
|
for k in self.data.keys():
|
||||||
|
if k.lower() == 'part':
|
||||||
|
try:
|
||||||
|
pk = self.data[k]['id']
|
||||||
|
except (AttributeError, KeyError):
|
||||||
|
raise ValidationError({k, 'id parameter not supplied'})
|
||||||
|
|
||||||
|
try:
|
||||||
|
part = Part.objects.get(pk=pk)
|
||||||
|
return part
|
||||||
|
except (ValueError, Part.DoesNotExist):
|
||||||
|
raise ValidationError({k, 'Part does not exist'})
|
||||||
|
|
||||||
|
return None
|
118
InvenTree/barcode/tests.py
Normal file
118
InvenTree/barcode/tests.py
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
Unit tests for Barcode endpoints
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from stock.models import StockItem
|
||||||
|
|
||||||
|
|
||||||
|
class BarcodeAPITest(APITestCase):
|
||||||
|
|
||||||
|
fixtures = [
|
||||||
|
'category',
|
||||||
|
'part',
|
||||||
|
'location',
|
||||||
|
'stock'
|
||||||
|
]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# Create a user for auth
|
||||||
|
User = get_user_model()
|
||||||
|
User.objects.create_user('testuser', 'test@testing.com', 'password')
|
||||||
|
|
||||||
|
self.client.login(username='testuser', password='password')
|
||||||
|
|
||||||
|
self.scan_url = reverse('api-barcode-scan')
|
||||||
|
self.assign_url = reverse('api-barcode-link')
|
||||||
|
|
||||||
|
def postBarcode(self, url, barcode):
|
||||||
|
|
||||||
|
return self.client.post(url, format='json', data={'barcode': str(barcode)})
|
||||||
|
|
||||||
|
def test_invalid(self):
|
||||||
|
|
||||||
|
response = self.client.post(self.scan_url, format='json', data={})
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_empty(self):
|
||||||
|
|
||||||
|
response = self.postBarcode(self.scan_url, '')
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
data = response.data
|
||||||
|
self.assertIn('error', data)
|
||||||
|
|
||||||
|
self.assertIn('barcode_data', data)
|
||||||
|
self.assertIn('hash', data)
|
||||||
|
self.assertIn('plugin', data)
|
||||||
|
self.assertIsNone(data['plugin'])
|
||||||
|
|
||||||
|
def test_barcode_generation(self):
|
||||||
|
|
||||||
|
item = StockItem.objects.get(pk=522)
|
||||||
|
|
||||||
|
response = self.postBarcode(self.scan_url, item.format_barcode())
|
||||||
|
data = response.data
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
self.assertIn('stockitem', data)
|
||||||
|
|
||||||
|
pk = data['stockitem']['pk']
|
||||||
|
|
||||||
|
self.assertEqual(pk, item.pk)
|
||||||
|
|
||||||
|
def test_association(self):
|
||||||
|
"""
|
||||||
|
Test that a barcode can be associated with a StockItem
|
||||||
|
"""
|
||||||
|
|
||||||
|
item = StockItem.objects.get(pk=522)
|
||||||
|
|
||||||
|
self.assertEqual(len(item.uid), 0)
|
||||||
|
|
||||||
|
barcode_data = 'A-TEST-BARCODE-STRING'
|
||||||
|
|
||||||
|
response = self.client.post(
|
||||||
|
self.assign_url, format='json',
|
||||||
|
data={
|
||||||
|
'barcode': barcode_data,
|
||||||
|
'stockitem': item.pk
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
data = response.data
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
self.assertIn('success', data)
|
||||||
|
|
||||||
|
hash = data['hash']
|
||||||
|
|
||||||
|
# Read the item out from the database again
|
||||||
|
item = StockItem.objects.get(pk=522)
|
||||||
|
|
||||||
|
self.assertEqual(hash, item.uid)
|
||||||
|
|
||||||
|
# Ensure that the same UID cannot be assigned to a different stock item!
|
||||||
|
response = self.client.post(
|
||||||
|
self.assign_url, format='json',
|
||||||
|
data={
|
||||||
|
'barcode': barcode_data,
|
||||||
|
'stockitem': 521
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
data = response.data
|
||||||
|
|
||||||
|
self.assertIn('error', data)
|
||||||
|
self.assertNotIn('success', data)
|
Binary file not shown.
@ -6,7 +6,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-06-09 06:17+0000\n"
|
"POT-Creation-Date: 2020-06-12 00:43+0000\n"
|
||||||
"PO-Revision-Date: 2020-05-03 11:32+0200\n"
|
"PO-Revision-Date: 2020-05-03 11:32+0200\n"
|
||||||
"Last-Translator: Christian Schlüter <chschlue@gmail.com>\n"
|
"Last-Translator: Christian Schlüter <chschlue@gmail.com>\n"
|
||||||
"Language-Team: C <kde-i18n-doc@kde.org>\n"
|
"Language-Team: C <kde-i18n-doc@kde.org>\n"
|
||||||
@ -17,30 +17,14 @@ msgstr ""
|
|||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Generator: Lokalize 19.12.0\n"
|
"X-Generator: Lokalize 19.12.0\n"
|
||||||
|
|
||||||
#: InvenTree/api.py:86
|
#: InvenTree/api.py:83
|
||||||
msgid "No action specified"
|
msgid "No action specified"
|
||||||
msgstr "Keine Aktion angegeben"
|
msgstr "Keine Aktion angegeben"
|
||||||
|
|
||||||
#: InvenTree/api.py:100
|
#: InvenTree/api.py:97
|
||||||
msgid "No matching action found"
|
msgid "No matching action found"
|
||||||
msgstr "Keine passende Aktion gefunden"
|
msgstr "Keine passende Aktion gefunden"
|
||||||
|
|
||||||
#: InvenTree/api.py:131
|
|
||||||
msgid "No barcode data provided"
|
|
||||||
msgstr "Keine Strichcodedaten bereitgestellt"
|
|
||||||
|
|
||||||
#: InvenTree/api.py:146
|
|
||||||
msgid "Barcode successfully decoded"
|
|
||||||
msgstr "Strichcode erfolgreich dekodiert"
|
|
||||||
|
|
||||||
#: InvenTree/api.py:149
|
|
||||||
msgid "Barcode plugin returned incorrect response"
|
|
||||||
msgstr "Ungültige Antwort vom Strichcode-Plugin"
|
|
||||||
|
|
||||||
#: InvenTree/api.py:159
|
|
||||||
msgid "Unknown barcode format"
|
|
||||||
msgstr "Unbekanntes Strichcode-Format"
|
|
||||||
|
|
||||||
#: InvenTree/forms.py:101 build/forms.py:37
|
#: InvenTree/forms.py:101 build/forms.py:37
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Bestätigen"
|
msgstr "Bestätigen"
|
||||||
@ -225,6 +209,50 @@ msgstr "Überschuss muss eine Ganzzahl oder ein Prozentwert sein"
|
|||||||
msgid "Database Statistics"
|
msgid "Database Statistics"
|
||||||
msgstr "Datenbankstatistiken"
|
msgstr "Datenbankstatistiken"
|
||||||
|
|
||||||
|
#: barcode/api.py:53 barcode/api.py:150
|
||||||
|
msgid "Must provide barcode_data parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:126
|
||||||
|
msgid "No match found for barcode data"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:128
|
||||||
|
msgid "Match found for barcode data"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:153
|
||||||
|
msgid "Must provide stockitem parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:160
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "No matching action found"
|
||||||
|
msgid "No matching stock item found"
|
||||||
|
msgstr "Keine passende Aktion gefunden"
|
||||||
|
|
||||||
|
#: barcode/api.py:190
|
||||||
|
msgid "Barcode already matches StockItem object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:194
|
||||||
|
msgid "Barcode already matches StockLocation object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:198
|
||||||
|
msgid "Barcode already matches Part object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:204 barcode/api.py:216
|
||||||
|
msgid "Barcode hash already matches StockItem object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:222
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Create new Stock Item"
|
||||||
|
msgid "Barcode associated with StockItem"
|
||||||
|
msgstr "Neues Lagerobjekt hinzufügen"
|
||||||
|
|
||||||
#: build/forms.py:58
|
#: build/forms.py:58
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Location Details"
|
#| msgid "Location Details"
|
||||||
@ -333,7 +361,7 @@ msgstr "Chargennummer für diese Bau-Ausgabe"
|
|||||||
#: company/templates/company/supplier_part_base.html:60
|
#: company/templates/company/supplier_part_base.html:60
|
||||||
#: company/templates/company/supplier_part_detail.html:24
|
#: company/templates/company/supplier_part_detail.html:24
|
||||||
#: part/templates/part/detail.html:74 part/templates/part/part_base.html:88
|
#: part/templates/part/detail.html:74 part/templates/part/part_base.html:88
|
||||||
#: stock/models.py:368 stock/templates/stock/item_base.html:221
|
#: stock/models.py:368 stock/templates/stock/item_base.html:230
|
||||||
msgid "External Link"
|
msgid "External Link"
|
||||||
msgstr "Externer Link"
|
msgstr "Externer Link"
|
||||||
|
|
||||||
@ -414,7 +442,7 @@ msgstr "Neues Lagerobjekt"
|
|||||||
#: build/templates/build/allocate.html:161
|
#: build/templates/build/allocate.html:161
|
||||||
#: order/templates/order/sales_order_detail.html:68
|
#: order/templates/order/sales_order_detail.html:68
|
||||||
#: order/templates/order/sales_order_detail.html:150 stock/models.py:362
|
#: order/templates/order/sales_order_detail.html:150 stock/models.py:362
|
||||||
#: stock/templates/stock/item_base.html:180
|
#: stock/templates/stock/item_base.html:189
|
||||||
msgid "Serial Number"
|
msgid "Serial Number"
|
||||||
msgstr "Seriennummer"
|
msgstr "Seriennummer"
|
||||||
|
|
||||||
@ -431,7 +459,7 @@ msgstr "Seriennummer"
|
|||||||
#: part/templates/part/allocation.html:49
|
#: part/templates/part/allocation.html:49
|
||||||
#: stock/templates/stock/item_base.html:26
|
#: stock/templates/stock/item_base.html:26
|
||||||
#: stock/templates/stock/item_base.html:32
|
#: stock/templates/stock/item_base.html:32
|
||||||
#: stock/templates/stock/item_base.html:186
|
#: stock/templates/stock/item_base.html:195
|
||||||
#: stock/templates/stock/stock_adjust.html:18 templates/js/bom.html:172
|
#: stock/templates/stock/stock_adjust.html:18 templates/js/bom.html:172
|
||||||
#: templates/js/build.html:52 templates/js/stock.html:653
|
#: templates/js/build.html:52 templates/js/stock.html:653
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
@ -439,7 +467,7 @@ msgstr "Anzahl"
|
|||||||
|
|
||||||
#: build/templates/build/allocate.html:177
|
#: build/templates/build/allocate.html:177
|
||||||
#: build/templates/build/auto_allocate.html:20
|
#: build/templates/build/auto_allocate.html:20
|
||||||
#: stock/templates/stock/item_base.html:162
|
#: stock/templates/stock/item_base.html:171
|
||||||
#: stock/templates/stock/stock_adjust.html:17 templates/js/stock.html:493
|
#: stock/templates/stock/stock_adjust.html:17 templates/js/stock.html:493
|
||||||
msgid "Location"
|
msgid "Location"
|
||||||
msgstr "Standort"
|
msgstr "Standort"
|
||||||
@ -521,7 +549,7 @@ msgstr "Keine Lagerobjekt gefunden, die diesem Bau zugewiesen werden können"
|
|||||||
#: build/templates/build/build_base.html:8
|
#: build/templates/build/build_base.html:8
|
||||||
#: build/templates/build/build_base.html:34
|
#: build/templates/build/build_base.html:34
|
||||||
#: build/templates/build/complete.html:6
|
#: build/templates/build/complete.html:6
|
||||||
#: stock/templates/stock/item_base.html:200 templates/js/build.html:33
|
#: stock/templates/stock/item_base.html:209 templates/js/build.html:33
|
||||||
#: templates/navbar.html:12
|
#: templates/navbar.html:12
|
||||||
msgid "Build"
|
msgid "Build"
|
||||||
msgstr "Bau"
|
msgstr "Bau"
|
||||||
@ -541,7 +569,7 @@ msgstr "Bau-Status"
|
|||||||
#: build/templates/build/build_base.html:80
|
#: build/templates/build/build_base.html:80
|
||||||
#: build/templates/build/detail.html:42
|
#: build/templates/build/detail.html:42
|
||||||
#: order/templates/order/receive_parts.html:24
|
#: order/templates/order/receive_parts.html:24
|
||||||
#: stock/templates/stock/item_base.html:253 templates/js/build.html:57
|
#: stock/templates/stock/item_base.html:262 templates/js/build.html:57
|
||||||
#: templates/js/order.html:162 templates/js/order.html:235
|
#: templates/js/order.html:162 templates/js/order.html:235
|
||||||
#: templates/js/stock.html:480
|
#: templates/js/stock.html:480
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
@ -553,7 +581,7 @@ msgstr "Status"
|
|||||||
#: order/templates/order/sales_order_notes.html:10
|
#: order/templates/order/sales_order_notes.html:10
|
||||||
#: order/templates/order/sales_order_ship.html:25
|
#: order/templates/order/sales_order_ship.html:25
|
||||||
#: part/templates/part/allocation.html:27
|
#: part/templates/part/allocation.html:27
|
||||||
#: stock/templates/stock/item_base.html:150 templates/js/order.html:209
|
#: stock/templates/stock/item_base.html:159 templates/js/order.html:209
|
||||||
msgid "Sales Order"
|
msgid "Sales Order"
|
||||||
msgstr "Bestellung"
|
msgstr "Bestellung"
|
||||||
|
|
||||||
@ -622,7 +650,7 @@ msgid "Stock can be taken from any available location."
|
|||||||
msgstr "Bestand kann jedem verfügbaren Lagerort entnommen werden."
|
msgstr "Bestand kann jedem verfügbaren Lagerort entnommen werden."
|
||||||
|
|
||||||
#: build/templates/build/detail.html:48
|
#: build/templates/build/detail.html:48
|
||||||
#: stock/templates/stock/item_base.html:193 templates/js/stock.html:488
|
#: stock/templates/stock/item_base.html:202 templates/js/stock.html:488
|
||||||
msgid "Batch"
|
msgid "Batch"
|
||||||
msgstr "Los"
|
msgstr "Los"
|
||||||
|
|
||||||
@ -976,14 +1004,14 @@ msgstr "Hersteller"
|
|||||||
#: company/templates/company/supplier_part_detail.html:21 order/models.py:148
|
#: company/templates/company/supplier_part_detail.html:21 order/models.py:148
|
||||||
#: order/templates/order/order_base.html:74
|
#: order/templates/order/order_base.html:74
|
||||||
#: order/templates/order/order_wizard/select_pos.html:30
|
#: order/templates/order/order_wizard/select_pos.html:30
|
||||||
#: stock/templates/stock/item_base.html:228 templates/js/company.html:52
|
#: stock/templates/stock/item_base.html:237 templates/js/company.html:52
|
||||||
#: templates/js/company.html:134 templates/js/order.html:144
|
#: templates/js/company.html:134 templates/js/order.html:144
|
||||||
msgid "Supplier"
|
msgid "Supplier"
|
||||||
msgstr "Zulieferer"
|
msgstr "Zulieferer"
|
||||||
|
|
||||||
#: company/templates/company/detail.html:26 order/models.py:314
|
#: company/templates/company/detail.html:26 order/models.py:314
|
||||||
#: order/templates/order/sales_order_base.html:73 stock/models.py:357
|
#: order/templates/order/sales_order_base.html:73 stock/models.py:357
|
||||||
#: stock/models.py:358 stock/templates/stock/item_base.html:137
|
#: stock/models.py:358 stock/templates/stock/item_base.html:146
|
||||||
#: templates/js/company.html:44 templates/js/order.html:217
|
#: templates/js/company.html:44 templates/js/order.html:217
|
||||||
msgid "Customer"
|
msgid "Customer"
|
||||||
msgstr "Kunde"
|
msgstr "Kunde"
|
||||||
@ -1098,7 +1126,7 @@ msgstr "Neuer Auftrag"
|
|||||||
|
|
||||||
#: company/templates/company/supplier_part_base.html:6
|
#: company/templates/company/supplier_part_base.html:6
|
||||||
#: company/templates/company/supplier_part_base.html:19 stock/models.py:331
|
#: company/templates/company/supplier_part_base.html:19 stock/models.py:331
|
||||||
#: stock/templates/stock/item_base.html:233 templates/js/company.html:150
|
#: stock/templates/stock/item_base.html:242 templates/js/company.html:150
|
||||||
msgid "Supplier Part"
|
msgid "Supplier Part"
|
||||||
msgstr "Zulieferer-Teil"
|
msgstr "Zulieferer-Teil"
|
||||||
|
|
||||||
@ -1385,7 +1413,7 @@ msgstr "Position - Notizen"
|
|||||||
|
|
||||||
#: order/models.py:466 order/templates/order/order_base.html:9
|
#: order/models.py:466 order/templates/order/order_base.html:9
|
||||||
#: order/templates/order/order_base.html:23
|
#: order/templates/order/order_base.html:23
|
||||||
#: stock/templates/stock/item_base.html:207 templates/js/order.html:136
|
#: stock/templates/stock/item_base.html:216 templates/js/order.html:136
|
||||||
msgid "Purchase Order"
|
msgid "Purchase Order"
|
||||||
msgstr "Kaufvertrag"
|
msgstr "Kaufvertrag"
|
||||||
|
|
||||||
@ -2114,14 +2142,14 @@ msgstr "Bestellung"
|
|||||||
#: part/templates/part/allocation.html:45
|
#: part/templates/part/allocation.html:45
|
||||||
#: stock/templates/stock/item_base.html:8
|
#: stock/templates/stock/item_base.html:8
|
||||||
#: stock/templates/stock/item_base.html:58
|
#: stock/templates/stock/item_base.html:58
|
||||||
#: stock/templates/stock/item_base.html:215
|
#: stock/templates/stock/item_base.html:224
|
||||||
#: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106
|
#: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106
|
||||||
#: templates/js/stock.html:623
|
#: templates/js/stock.html:623
|
||||||
msgid "Stock Item"
|
msgid "Stock Item"
|
||||||
msgstr "Lagerobjekt"
|
msgstr "Lagerobjekt"
|
||||||
|
|
||||||
#: part/templates/part/allocation.html:20
|
#: part/templates/part/allocation.html:20
|
||||||
#: stock/templates/stock/item_base.html:156
|
#: stock/templates/stock/item_base.html:165
|
||||||
msgid "Build Order"
|
msgid "Build Order"
|
||||||
msgstr "Bauauftrag"
|
msgstr "Bauauftrag"
|
||||||
|
|
||||||
@ -2539,7 +2567,7 @@ msgstr "Benutzt in"
|
|||||||
msgid "Tracking"
|
msgid "Tracking"
|
||||||
msgstr "Tracking"
|
msgstr "Tracking"
|
||||||
|
|
||||||
#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:259
|
#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:268
|
||||||
msgid "Tests"
|
msgid "Tests"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2738,22 +2766,6 @@ msgstr "BOM-Position beaarbeiten"
|
|||||||
msgid "Confim BOM item deletion"
|
msgid "Confim BOM item deletion"
|
||||||
msgstr "Löschung von BOM-Position bestätigen"
|
msgstr "Löschung von BOM-Position bestätigen"
|
||||||
|
|
||||||
#: plugins/barcode/inventree.py:70
|
|
||||||
msgid "Part does not exist"
|
|
||||||
msgstr "Teil existiert nicht"
|
|
||||||
|
|
||||||
#: plugins/barcode/inventree.py:79
|
|
||||||
msgid "StockLocation does not exist"
|
|
||||||
msgstr "Lagerort existiert nicht"
|
|
||||||
|
|
||||||
#: plugins/barcode/inventree.py:89
|
|
||||||
msgid "StockItem does not exist"
|
|
||||||
msgstr "Lagerobjekt existiert nicht"
|
|
||||||
|
|
||||||
#: plugins/barcode/inventree.py:92
|
|
||||||
msgid "No matching data"
|
|
||||||
msgstr "Keine passenden Daten"
|
|
||||||
|
|
||||||
#: report/models.py:167
|
#: report/models.py:167
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Template part"
|
#| msgid "Template part"
|
||||||
@ -2834,7 +2846,7 @@ msgstr "Teil kann nicht zu sich selbst gehören"
|
|||||||
msgid "Parent Stock Item"
|
msgid "Parent Stock Item"
|
||||||
msgstr "Eltern-Lagerobjekt"
|
msgstr "Eltern-Lagerobjekt"
|
||||||
|
|
||||||
#: stock/models.py:322 stock/templates/stock/item_base.html:129
|
#: stock/models.py:322 stock/templates/stock/item_base.html:138
|
||||||
msgid "Base Part"
|
msgid "Base Part"
|
||||||
msgstr "Basisteil"
|
msgstr "Basisteil"
|
||||||
|
|
||||||
@ -3066,99 +3078,132 @@ msgstr ""
|
|||||||
"Dieses Lagerobjekt wird automatisch gelöscht wenn der Lagerbestand "
|
"Dieses Lagerobjekt wird automatisch gelöscht wenn der Lagerbestand "
|
||||||
"aufgebraucht ist."
|
"aufgebraucht ist."
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:74
|
#: stock/templates/stock/item_base.html:78
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Add stock"
|
#| msgid "Source Location"
|
||||||
msgid "Add to stock"
|
msgid "Barcode actions"
|
||||||
msgstr "Bestand hinzufügen"
|
msgstr "Quell-Standort"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:77
|
#: stock/templates/stock/item_base.html:80
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Remove From Stock"
|
#| msgid "Part QR Code"
|
||||||
msgid "Take from stock"
|
msgid "Show QR Code"
|
||||||
msgstr "Aus Lagerbestand entfernen"
|
msgstr "Teil-QR-Code"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:80 templates/stock_table.html:14
|
#: stock/templates/stock/item_base.html:81
|
||||||
|
msgid "Print Label"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:83 templates/js/barcode.html:263
|
||||||
|
#: templates/js/barcode.html:268
|
||||||
|
msgid "Unlink Barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:85
|
||||||
|
msgid "Link Barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:92
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Confirm stock adjustment"
|
||||||
|
msgid "Stock adjustment actions"
|
||||||
|
msgstr "Bestands-Anpassung bestätigen"
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:95 templates/stock_table.html:14
|
||||||
msgid "Count stock"
|
msgid "Count stock"
|
||||||
msgstr "Bestand zählen"
|
msgstr "Bestand zählen"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:84
|
#: stock/templates/stock/item_base.html:96 templates/stock_table.html:12
|
||||||
#, fuzzy
|
msgid "Add stock"
|
||||||
#| msgid "Serialize Stock"
|
msgstr "Bestand hinzufügen"
|
||||||
msgid "Serialize stock"
|
|
||||||
msgstr "Lagerbestand erfassen"
|
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:90 stock/views.py:232
|
#: stock/templates/stock/item_base.html:97 templates/stock_table.html:13
|
||||||
#, fuzzy
|
msgid "Remove stock"
|
||||||
#| msgid "Item assigned to customer?"
|
msgstr "Bestand entfernen"
|
||||||
msgid "Assign to Customer"
|
|
||||||
msgstr "Ist dieses Objekt einem Kunden zugeteilt?"
|
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:94
|
#: stock/templates/stock/item_base.html:99
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Order stock"
|
#| msgid "Order stock"
|
||||||
msgid "Transfer stock"
|
msgid "Transfer stock"
|
||||||
msgstr "Bestand bestellen"
|
msgstr "Bestand bestellen"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:97
|
#: stock/templates/stock/item_base.html:105
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Stock Locations"
|
||||||
|
msgid "Stock actions"
|
||||||
|
msgstr "Lagerobjekt-Standorte"
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:108
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Serialize Stock"
|
||||||
|
msgid "Serialize stock"
|
||||||
|
msgstr "Lagerbestand erfassen"
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:111
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Item assigned to customer?"
|
||||||
|
msgid "Assign to customer"
|
||||||
|
msgstr "Ist dieses Objekt einem Kunden zugeteilt?"
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:114
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Count stock items"
|
||||||
|
msgid "Convert to variant"
|
||||||
|
msgstr "Lagerobjekte zählen"
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:116
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Count stock items"
|
#| msgid "Count stock items"
|
||||||
msgid "Duplicate stock item"
|
msgid "Duplicate stock item"
|
||||||
msgstr "Lagerobjekte zählen"
|
msgstr "Lagerobjekte zählen"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:102
|
#: stock/templates/stock/item_base.html:117
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Count stock items"
|
|
||||||
msgid "Convert stock to variant"
|
|
||||||
msgstr "Lagerobjekte zählen"
|
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:107
|
|
||||||
msgid "Generate test report"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:111
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Edit Stock Item"
|
#| msgid "Edit Stock Item"
|
||||||
msgid "Edit stock item"
|
msgid "Edit stock item"
|
||||||
msgstr "Lagerobjekt bearbeiten"
|
msgstr "Lagerobjekt bearbeiten"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:115
|
#: stock/templates/stock/item_base.html:119
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Delete Stock Item"
|
#| msgid "Delete Stock Item"
|
||||||
msgid "Delete stock item"
|
msgid "Delete stock item"
|
||||||
msgstr "Lagerobjekt löschen"
|
msgstr "Lagerobjekt löschen"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:124
|
#: stock/templates/stock/item_base.html:124
|
||||||
|
msgid "Generate test report"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:133
|
||||||
msgid "Stock Item Details"
|
msgid "Stock Item Details"
|
||||||
msgstr "Lagerbestands-Details"
|
msgstr "Lagerbestands-Details"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:144
|
#: stock/templates/stock/item_base.html:153
|
||||||
msgid "Belongs To"
|
msgid "Belongs To"
|
||||||
msgstr "Gehört zu"
|
msgstr "Gehört zu"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:166
|
#: stock/templates/stock/item_base.html:175
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "No stock location set"
|
#| msgid "No stock location set"
|
||||||
msgid "No location set"
|
msgid "No location set"
|
||||||
msgstr "Kein Lagerort gesetzt"
|
msgstr "Kein Lagerort gesetzt"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:173
|
#: stock/templates/stock/item_base.html:182
|
||||||
msgid "Unique Identifier"
|
msgid "Unique Identifier"
|
||||||
msgstr "Eindeutiger Bezeichner"
|
msgstr "Eindeutiger Bezeichner"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:214
|
#: stock/templates/stock/item_base.html:223
|
||||||
msgid "Parent Item"
|
msgid "Parent Item"
|
||||||
msgstr "Elternposition"
|
msgstr "Elternposition"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:239
|
#: stock/templates/stock/item_base.html:248
|
||||||
msgid "Last Updated"
|
msgid "Last Updated"
|
||||||
msgstr "Zuletzt aktualisiert"
|
msgstr "Zuletzt aktualisiert"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:244
|
#: stock/templates/stock/item_base.html:253
|
||||||
msgid "Last Stocktake"
|
msgid "Last Stocktake"
|
||||||
msgstr "Letzte Inventur"
|
msgstr "Letzte Inventur"
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:248
|
#: stock/templates/stock/item_base.html:257
|
||||||
msgid "No stocktake performed"
|
msgid "No stocktake performed"
|
||||||
msgstr "Keine Inventur ausgeführt"
|
msgstr "Keine Inventur ausgeführt"
|
||||||
|
|
||||||
@ -3289,6 +3334,12 @@ msgstr "Lagerobjekt bearbeiten"
|
|||||||
msgid "Delete Stock Item Attachment"
|
msgid "Delete Stock Item Attachment"
|
||||||
msgstr "Teilanhang löschen"
|
msgstr "Teilanhang löschen"
|
||||||
|
|
||||||
|
#: stock/views.py:232
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Item assigned to customer?"
|
||||||
|
msgid "Assign to Customer"
|
||||||
|
msgstr "Ist dieses Objekt einem Kunden zugeteilt?"
|
||||||
|
|
||||||
#: stock/views.py:270
|
#: stock/views.py:270
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Delete Template"
|
#| msgid "Delete Template"
|
||||||
@ -3424,8 +3475,10 @@ msgid "Create new Stock Item"
|
|||||||
msgstr "Neues Lagerobjekt hinzufügen"
|
msgstr "Neues Lagerobjekt hinzufügen"
|
||||||
|
|
||||||
#: stock/views.py:1167
|
#: stock/views.py:1167
|
||||||
msgid "Copy Stock Item"
|
#, fuzzy
|
||||||
msgstr "Lagerobjekt kopieren"
|
#| msgid "Count stock items"
|
||||||
|
msgid "Duplicate Stock Item"
|
||||||
|
msgstr "Lagerobjekte zählen"
|
||||||
|
|
||||||
#: stock/views.py:1240
|
#: stock/views.py:1240
|
||||||
msgid "Invalid quantity"
|
msgid "Invalid quantity"
|
||||||
@ -3550,6 +3603,49 @@ msgstr ""
|
|||||||
msgid "Delete attachment"
|
msgid "Delete attachment"
|
||||||
msgstr "Anhang löschen"
|
msgstr "Anhang löschen"
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:28
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "No barcode data provided"
|
||||||
|
msgid "Scan barcode data here using wedge scanner"
|
||||||
|
msgstr "Keine Strichcodedaten bereitgestellt"
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:34
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Source Location"
|
||||||
|
msgid "Barcode"
|
||||||
|
msgstr "Quell-Standort"
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:42
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "No barcode data provided"
|
||||||
|
msgid "Enter barcode data"
|
||||||
|
msgstr "Keine Strichcodedaten bereitgestellt"
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:140
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "No barcode data provided"
|
||||||
|
msgid "Scan barcode data below"
|
||||||
|
msgstr "Keine Strichcodedaten bereitgestellt"
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:195 templates/js/barcode.html:243
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Unknown barcode format"
|
||||||
|
msgid "Unknown response from server"
|
||||||
|
msgstr "Unbekanntes Strichcode-Format"
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:198 templates/js/barcode.html:247
|
||||||
|
msgid "Invalid server response"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:265
|
||||||
|
msgid ""
|
||||||
|
"This will remove the association between this stock item and the barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:271
|
||||||
|
msgid "Unlink"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: templates/js/bom.html:143
|
#: templates/js/bom.html:143
|
||||||
msgid "Open subassembly"
|
msgid "Open subassembly"
|
||||||
msgstr "Unterbaugruppe öffnen"
|
msgstr "Unterbaugruppe öffnen"
|
||||||
@ -3693,8 +3789,10 @@ msgid "NO RESULT"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/js/stock.html:58
|
#: templates/js/stock.html:58
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Edit Sales Order"
|
||||||
msgid "Add test result"
|
msgid "Add test result"
|
||||||
msgstr ""
|
msgstr "Auftrag bearbeiten"
|
||||||
|
|
||||||
#: templates/js/stock.html:76
|
#: templates/js/stock.html:76
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
@ -3832,42 +3930,38 @@ msgstr "Kaufen"
|
|||||||
msgid "Sell"
|
msgid "Sell"
|
||||||
msgstr "Verkaufen"
|
msgstr "Verkaufen"
|
||||||
|
|
||||||
#: templates/navbar.html:36
|
#: templates/navbar.html:32
|
||||||
|
msgid "Scan Barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/navbar.html:41
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr "Admin"
|
||||||
|
|
||||||
#: templates/navbar.html:39
|
#: templates/navbar.html:44
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr "Einstellungen"
|
msgstr "Einstellungen"
|
||||||
|
|
||||||
#: templates/navbar.html:40
|
#: templates/navbar.html:45
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr "Ausloggen"
|
msgstr "Ausloggen"
|
||||||
|
|
||||||
#: templates/navbar.html:42
|
#: templates/navbar.html:47
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "Einloggen"
|
msgstr "Einloggen"
|
||||||
|
|
||||||
#: templates/navbar.html:45
|
#: templates/navbar.html:50
|
||||||
msgid "About InvenTree"
|
msgid "About InvenTree"
|
||||||
msgstr "Über InvenBaum"
|
msgstr "Über InvenBaum"
|
||||||
|
|
||||||
#: templates/navbar.html:46
|
#: templates/navbar.html:51
|
||||||
msgid "Statistics"
|
msgid "Statistics"
|
||||||
msgstr "Statistiken"
|
msgstr "Statistiken"
|
||||||
|
|
||||||
#: templates/search_form.html:6
|
#: templates/search_form.html:6 templates/search_form.html:8
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Suche"
|
msgstr "Suche"
|
||||||
|
|
||||||
#: templates/stock_table.html:12
|
|
||||||
msgid "Add stock"
|
|
||||||
msgstr "Bestand hinzufügen"
|
|
||||||
|
|
||||||
#: templates/stock_table.html:13
|
|
||||||
msgid "Remove stock"
|
|
||||||
msgstr "Bestand entfernen"
|
|
||||||
|
|
||||||
#: templates/stock_table.html:15
|
#: templates/stock_table.html:15
|
||||||
msgid "Move stock"
|
msgid "Move stock"
|
||||||
msgstr "Bestand bewegen"
|
msgstr "Bestand bewegen"
|
||||||
@ -3880,6 +3974,37 @@ msgstr "Bestand bestellen"
|
|||||||
msgid "Delete Stock"
|
msgid "Delete Stock"
|
||||||
msgstr "Bestand löschen"
|
msgstr "Bestand löschen"
|
||||||
|
|
||||||
|
#~ msgid "Barcode successfully decoded"
|
||||||
|
#~ msgstr "Strichcode erfolgreich dekodiert"
|
||||||
|
|
||||||
|
#~ msgid "Barcode plugin returned incorrect response"
|
||||||
|
#~ msgstr "Ungültige Antwort vom Strichcode-Plugin"
|
||||||
|
|
||||||
|
#~ msgid "Part does not exist"
|
||||||
|
#~ msgstr "Teil existiert nicht"
|
||||||
|
|
||||||
|
#~ msgid "StockLocation does not exist"
|
||||||
|
#~ msgstr "Lagerort existiert nicht"
|
||||||
|
|
||||||
|
#~ msgid "StockItem does not exist"
|
||||||
|
#~ msgstr "Lagerobjekt existiert nicht"
|
||||||
|
|
||||||
|
#~ msgid "No matching data"
|
||||||
|
#~ msgstr "Keine passenden Daten"
|
||||||
|
|
||||||
|
#, fuzzy
|
||||||
|
#~| msgid "Add stock"
|
||||||
|
#~ msgid "Add to stock"
|
||||||
|
#~ msgstr "Bestand hinzufügen"
|
||||||
|
|
||||||
|
#, fuzzy
|
||||||
|
#~| msgid "Remove From Stock"
|
||||||
|
#~ msgid "Take from stock"
|
||||||
|
#~ msgstr "Aus Lagerbestand entfernen"
|
||||||
|
|
||||||
|
#~ msgid "Copy Stock Item"
|
||||||
|
#~ msgstr "Lagerobjekt kopieren"
|
||||||
|
|
||||||
#~ msgid "Part cannot be a variant of another part if it is already a template"
|
#~ msgid "Part cannot be a variant of another part if it is already a template"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "Teil kann keine Variante eines anderen Teils sein wenn es bereits eine "
|
#~ "Teil kann keine Variante eines anderen Teils sein wenn es bereits eine "
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-06-09 06:17+0000\n"
|
"POT-Creation-Date: 2020-06-12 00:43+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -18,30 +18,14 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: InvenTree/api.py:86
|
#: InvenTree/api.py:83
|
||||||
msgid "No action specified"
|
msgid "No action specified"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: InvenTree/api.py:100
|
#: InvenTree/api.py:97
|
||||||
msgid "No matching action found"
|
msgid "No matching action found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: InvenTree/api.py:131
|
|
||||||
msgid "No barcode data provided"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: InvenTree/api.py:146
|
|
||||||
msgid "Barcode successfully decoded"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: InvenTree/api.py:149
|
|
||||||
msgid "Barcode plugin returned incorrect response"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: InvenTree/api.py:159
|
|
||||||
msgid "Unknown barcode format"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: InvenTree/forms.py:101 build/forms.py:37
|
#: InvenTree/forms.py:101 build/forms.py:37
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -218,6 +202,46 @@ msgstr ""
|
|||||||
msgid "Database Statistics"
|
msgid "Database Statistics"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:53 barcode/api.py:150
|
||||||
|
msgid "Must provide barcode_data parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:126
|
||||||
|
msgid "No match found for barcode data"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:128
|
||||||
|
msgid "Match found for barcode data"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:153
|
||||||
|
msgid "Must provide stockitem parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:160
|
||||||
|
msgid "No matching stock item found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:190
|
||||||
|
msgid "Barcode already matches StockItem object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:194
|
||||||
|
msgid "Barcode already matches StockLocation object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:198
|
||||||
|
msgid "Barcode already matches Part object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:204 barcode/api.py:216
|
||||||
|
msgid "Barcode hash already matches StockItem object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:222
|
||||||
|
msgid "Barcode associated with StockItem"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: build/forms.py:58
|
#: build/forms.py:58
|
||||||
msgid "Location of completed parts"
|
msgid "Location of completed parts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -318,7 +342,7 @@ msgstr ""
|
|||||||
#: company/templates/company/supplier_part_base.html:60
|
#: company/templates/company/supplier_part_base.html:60
|
||||||
#: company/templates/company/supplier_part_detail.html:24
|
#: company/templates/company/supplier_part_detail.html:24
|
||||||
#: part/templates/part/detail.html:74 part/templates/part/part_base.html:88
|
#: part/templates/part/detail.html:74 part/templates/part/part_base.html:88
|
||||||
#: stock/models.py:368 stock/templates/stock/item_base.html:221
|
#: stock/models.py:368 stock/templates/stock/item_base.html:230
|
||||||
msgid "External Link"
|
msgid "External Link"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -398,7 +422,7 @@ msgstr ""
|
|||||||
#: build/templates/build/allocate.html:161
|
#: build/templates/build/allocate.html:161
|
||||||
#: order/templates/order/sales_order_detail.html:68
|
#: order/templates/order/sales_order_detail.html:68
|
||||||
#: order/templates/order/sales_order_detail.html:150 stock/models.py:362
|
#: order/templates/order/sales_order_detail.html:150 stock/models.py:362
|
||||||
#: stock/templates/stock/item_base.html:180
|
#: stock/templates/stock/item_base.html:189
|
||||||
msgid "Serial Number"
|
msgid "Serial Number"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -415,7 +439,7 @@ msgstr ""
|
|||||||
#: part/templates/part/allocation.html:49
|
#: part/templates/part/allocation.html:49
|
||||||
#: stock/templates/stock/item_base.html:26
|
#: stock/templates/stock/item_base.html:26
|
||||||
#: stock/templates/stock/item_base.html:32
|
#: stock/templates/stock/item_base.html:32
|
||||||
#: stock/templates/stock/item_base.html:186
|
#: stock/templates/stock/item_base.html:195
|
||||||
#: stock/templates/stock/stock_adjust.html:18 templates/js/bom.html:172
|
#: stock/templates/stock/stock_adjust.html:18 templates/js/bom.html:172
|
||||||
#: templates/js/build.html:52 templates/js/stock.html:653
|
#: templates/js/build.html:52 templates/js/stock.html:653
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
@ -423,7 +447,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: build/templates/build/allocate.html:177
|
#: build/templates/build/allocate.html:177
|
||||||
#: build/templates/build/auto_allocate.html:20
|
#: build/templates/build/auto_allocate.html:20
|
||||||
#: stock/templates/stock/item_base.html:162
|
#: stock/templates/stock/item_base.html:171
|
||||||
#: stock/templates/stock/stock_adjust.html:17 templates/js/stock.html:493
|
#: stock/templates/stock/stock_adjust.html:17 templates/js/stock.html:493
|
||||||
msgid "Location"
|
msgid "Location"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -504,7 +528,7 @@ msgstr ""
|
|||||||
#: build/templates/build/build_base.html:8
|
#: build/templates/build/build_base.html:8
|
||||||
#: build/templates/build/build_base.html:34
|
#: build/templates/build/build_base.html:34
|
||||||
#: build/templates/build/complete.html:6
|
#: build/templates/build/complete.html:6
|
||||||
#: stock/templates/stock/item_base.html:200 templates/js/build.html:33
|
#: stock/templates/stock/item_base.html:209 templates/js/build.html:33
|
||||||
#: templates/navbar.html:12
|
#: templates/navbar.html:12
|
||||||
msgid "Build"
|
msgid "Build"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -524,7 +548,7 @@ msgstr ""
|
|||||||
#: build/templates/build/build_base.html:80
|
#: build/templates/build/build_base.html:80
|
||||||
#: build/templates/build/detail.html:42
|
#: build/templates/build/detail.html:42
|
||||||
#: order/templates/order/receive_parts.html:24
|
#: order/templates/order/receive_parts.html:24
|
||||||
#: stock/templates/stock/item_base.html:253 templates/js/build.html:57
|
#: stock/templates/stock/item_base.html:262 templates/js/build.html:57
|
||||||
#: templates/js/order.html:162 templates/js/order.html:235
|
#: templates/js/order.html:162 templates/js/order.html:235
|
||||||
#: templates/js/stock.html:480
|
#: templates/js/stock.html:480
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
@ -536,7 +560,7 @@ msgstr ""
|
|||||||
#: order/templates/order/sales_order_notes.html:10
|
#: order/templates/order/sales_order_notes.html:10
|
||||||
#: order/templates/order/sales_order_ship.html:25
|
#: order/templates/order/sales_order_ship.html:25
|
||||||
#: part/templates/part/allocation.html:27
|
#: part/templates/part/allocation.html:27
|
||||||
#: stock/templates/stock/item_base.html:150 templates/js/order.html:209
|
#: stock/templates/stock/item_base.html:159 templates/js/order.html:209
|
||||||
msgid "Sales Order"
|
msgid "Sales Order"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -603,7 +627,7 @@ msgid "Stock can be taken from any available location."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/templates/build/detail.html:48
|
#: build/templates/build/detail.html:48
|
||||||
#: stock/templates/stock/item_base.html:193 templates/js/stock.html:488
|
#: stock/templates/stock/item_base.html:202 templates/js/stock.html:488
|
||||||
msgid "Batch"
|
msgid "Batch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -950,14 +974,14 @@ msgstr ""
|
|||||||
#: company/templates/company/supplier_part_detail.html:21 order/models.py:148
|
#: company/templates/company/supplier_part_detail.html:21 order/models.py:148
|
||||||
#: order/templates/order/order_base.html:74
|
#: order/templates/order/order_base.html:74
|
||||||
#: order/templates/order/order_wizard/select_pos.html:30
|
#: order/templates/order/order_wizard/select_pos.html:30
|
||||||
#: stock/templates/stock/item_base.html:228 templates/js/company.html:52
|
#: stock/templates/stock/item_base.html:237 templates/js/company.html:52
|
||||||
#: templates/js/company.html:134 templates/js/order.html:144
|
#: templates/js/company.html:134 templates/js/order.html:144
|
||||||
msgid "Supplier"
|
msgid "Supplier"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: company/templates/company/detail.html:26 order/models.py:314
|
#: company/templates/company/detail.html:26 order/models.py:314
|
||||||
#: order/templates/order/sales_order_base.html:73 stock/models.py:357
|
#: order/templates/order/sales_order_base.html:73 stock/models.py:357
|
||||||
#: stock/models.py:358 stock/templates/stock/item_base.html:137
|
#: stock/models.py:358 stock/templates/stock/item_base.html:146
|
||||||
#: templates/js/company.html:44 templates/js/order.html:217
|
#: templates/js/company.html:44 templates/js/order.html:217
|
||||||
msgid "Customer"
|
msgid "Customer"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -1071,7 +1095,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: company/templates/company/supplier_part_base.html:6
|
#: company/templates/company/supplier_part_base.html:6
|
||||||
#: company/templates/company/supplier_part_base.html:19 stock/models.py:331
|
#: company/templates/company/supplier_part_base.html:19 stock/models.py:331
|
||||||
#: stock/templates/stock/item_base.html:233 templates/js/company.html:150
|
#: stock/templates/stock/item_base.html:242 templates/js/company.html:150
|
||||||
msgid "Supplier Part"
|
msgid "Supplier Part"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1354,7 +1378,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: order/models.py:466 order/templates/order/order_base.html:9
|
#: order/models.py:466 order/templates/order/order_base.html:9
|
||||||
#: order/templates/order/order_base.html:23
|
#: order/templates/order/order_base.html:23
|
||||||
#: stock/templates/stock/item_base.html:207 templates/js/order.html:136
|
#: stock/templates/stock/item_base.html:216 templates/js/order.html:136
|
||||||
msgid "Purchase Order"
|
msgid "Purchase Order"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2052,14 +2076,14 @@ msgstr ""
|
|||||||
#: part/templates/part/allocation.html:45
|
#: part/templates/part/allocation.html:45
|
||||||
#: stock/templates/stock/item_base.html:8
|
#: stock/templates/stock/item_base.html:8
|
||||||
#: stock/templates/stock/item_base.html:58
|
#: stock/templates/stock/item_base.html:58
|
||||||
#: stock/templates/stock/item_base.html:215
|
#: stock/templates/stock/item_base.html:224
|
||||||
#: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106
|
#: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106
|
||||||
#: templates/js/stock.html:623
|
#: templates/js/stock.html:623
|
||||||
msgid "Stock Item"
|
msgid "Stock Item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/templates/part/allocation.html:20
|
#: part/templates/part/allocation.html:20
|
||||||
#: stock/templates/stock/item_base.html:156
|
#: stock/templates/stock/item_base.html:165
|
||||||
msgid "Build Order"
|
msgid "Build Order"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2453,7 +2477,7 @@ msgstr ""
|
|||||||
msgid "Tracking"
|
msgid "Tracking"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:259
|
#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:268
|
||||||
msgid "Tests"
|
msgid "Tests"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2646,22 +2670,6 @@ msgstr ""
|
|||||||
msgid "Confim BOM item deletion"
|
msgid "Confim BOM item deletion"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: plugins/barcode/inventree.py:70
|
|
||||||
msgid "Part does not exist"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: plugins/barcode/inventree.py:79
|
|
||||||
msgid "StockLocation does not exist"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: plugins/barcode/inventree.py:89
|
|
||||||
msgid "StockItem does not exist"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: plugins/barcode/inventree.py:92
|
|
||||||
msgid "No matching data"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: report/models.py:167
|
#: report/models.py:167
|
||||||
msgid "Template name"
|
msgid "Template name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2731,7 +2739,7 @@ msgstr ""
|
|||||||
msgid "Parent Stock Item"
|
msgid "Parent Stock Item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/models.py:322 stock/templates/stock/item_base.html:129
|
#: stock/models.py:322 stock/templates/stock/item_base.html:138
|
||||||
msgid "Base Part"
|
msgid "Base Part"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2937,79 +2945,108 @@ msgid ""
|
|||||||
"This stock item will be automatically deleted when all stock is depleted."
|
"This stock item will be automatically deleted when all stock is depleted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:74
|
#: stock/templates/stock/item_base.html:78
|
||||||
msgid "Add to stock"
|
msgid "Barcode actions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:77
|
#: stock/templates/stock/item_base.html:80
|
||||||
msgid "Take from stock"
|
msgid "Show QR Code"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:80 templates/stock_table.html:14
|
#: stock/templates/stock/item_base.html:81
|
||||||
|
msgid "Print Label"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:83 templates/js/barcode.html:263
|
||||||
|
#: templates/js/barcode.html:268
|
||||||
|
msgid "Unlink Barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:85
|
||||||
|
msgid "Link Barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:92
|
||||||
|
msgid "Stock adjustment actions"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:95 templates/stock_table.html:14
|
||||||
msgid "Count stock"
|
msgid "Count stock"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:84
|
#: stock/templates/stock/item_base.html:96 templates/stock_table.html:12
|
||||||
msgid "Serialize stock"
|
msgid "Add stock"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:90 stock/views.py:232
|
#: stock/templates/stock/item_base.html:97 templates/stock_table.html:13
|
||||||
msgid "Assign to Customer"
|
msgid "Remove stock"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:94
|
#: stock/templates/stock/item_base.html:99
|
||||||
msgid "Transfer stock"
|
msgid "Transfer stock"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:97
|
#: stock/templates/stock/item_base.html:105
|
||||||
msgid "Duplicate stock item"
|
msgid "Stock actions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:102
|
#: stock/templates/stock/item_base.html:108
|
||||||
msgid "Convert stock to variant"
|
msgid "Serialize stock"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:107
|
|
||||||
msgid "Generate test report"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:111
|
#: stock/templates/stock/item_base.html:111
|
||||||
|
msgid "Assign to customer"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:114
|
||||||
|
msgid "Convert to variant"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:116
|
||||||
|
msgid "Duplicate stock item"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:117
|
||||||
msgid "Edit stock item"
|
msgid "Edit stock item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:115
|
#: stock/templates/stock/item_base.html:119
|
||||||
msgid "Delete stock item"
|
msgid "Delete stock item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:124
|
#: stock/templates/stock/item_base.html:124
|
||||||
|
msgid "Generate test report"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:133
|
||||||
msgid "Stock Item Details"
|
msgid "Stock Item Details"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:144
|
#: stock/templates/stock/item_base.html:153
|
||||||
msgid "Belongs To"
|
msgid "Belongs To"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:166
|
#: stock/templates/stock/item_base.html:175
|
||||||
msgid "No location set"
|
msgid "No location set"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:173
|
#: stock/templates/stock/item_base.html:182
|
||||||
msgid "Unique Identifier"
|
msgid "Unique Identifier"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:214
|
#: stock/templates/stock/item_base.html:223
|
||||||
msgid "Parent Item"
|
msgid "Parent Item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:239
|
#: stock/templates/stock/item_base.html:248
|
||||||
msgid "Last Updated"
|
msgid "Last Updated"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:244
|
#: stock/templates/stock/item_base.html:253
|
||||||
msgid "Last Stocktake"
|
msgid "Last Stocktake"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:248
|
#: stock/templates/stock/item_base.html:257
|
||||||
msgid "No stocktake performed"
|
msgid "No stocktake performed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3128,6 +3165,10 @@ msgstr ""
|
|||||||
msgid "Delete Stock Item Attachment"
|
msgid "Delete Stock Item Attachment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/views.py:232
|
||||||
|
msgid "Assign to Customer"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:270
|
#: stock/views.py:270
|
||||||
msgid "Delete All Test Data"
|
msgid "Delete All Test Data"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3251,7 +3292,7 @@ msgid "Create new Stock Item"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:1167
|
#: stock/views.py:1167
|
||||||
msgid "Copy Stock Item"
|
msgid "Duplicate Stock Item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:1240
|
#: stock/views.py:1240
|
||||||
@ -3375,6 +3416,39 @@ msgstr ""
|
|||||||
msgid "Delete attachment"
|
msgid "Delete attachment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:28
|
||||||
|
msgid "Scan barcode data here using wedge scanner"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:34
|
||||||
|
msgid "Barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:42
|
||||||
|
msgid "Enter barcode data"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:140
|
||||||
|
msgid "Scan barcode data below"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:195 templates/js/barcode.html:243
|
||||||
|
msgid "Unknown response from server"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:198 templates/js/barcode.html:247
|
||||||
|
msgid "Invalid server response"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:265
|
||||||
|
msgid ""
|
||||||
|
"This will remove the association between this stock item and the barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:271
|
||||||
|
msgid "Unlink"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: templates/js/bom.html:143
|
#: templates/js/bom.html:143
|
||||||
msgid "Open subassembly"
|
msgid "Open subassembly"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3635,42 +3709,38 @@ msgstr ""
|
|||||||
msgid "Sell"
|
msgid "Sell"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/navbar.html:36
|
#: templates/navbar.html:32
|
||||||
|
msgid "Scan Barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/navbar.html:41
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/navbar.html:39
|
#: templates/navbar.html:44
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/navbar.html:40
|
#: templates/navbar.html:45
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/navbar.html:42
|
#: templates/navbar.html:47
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/navbar.html:45
|
#: templates/navbar.html:50
|
||||||
msgid "About InvenTree"
|
msgid "About InvenTree"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/navbar.html:46
|
#: templates/navbar.html:51
|
||||||
msgid "Statistics"
|
msgid "Statistics"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/search_form.html:6
|
#: templates/search_form.html:6 templates/search_form.html:8
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/stock_table.html:12
|
|
||||||
msgid "Add stock"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: templates/stock_table.html:13
|
|
||||||
msgid "Remove stock"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: templates/stock_table.html:15
|
#: templates/stock_table.html:15
|
||||||
msgid "Move stock"
|
msgid "Move stock"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-06-09 06:17+0000\n"
|
"POT-Creation-Date: 2020-06-12 00:43+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -18,30 +18,14 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: InvenTree/api.py:86
|
#: InvenTree/api.py:83
|
||||||
msgid "No action specified"
|
msgid "No action specified"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: InvenTree/api.py:100
|
#: InvenTree/api.py:97
|
||||||
msgid "No matching action found"
|
msgid "No matching action found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: InvenTree/api.py:131
|
|
||||||
msgid "No barcode data provided"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: InvenTree/api.py:146
|
|
||||||
msgid "Barcode successfully decoded"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: InvenTree/api.py:149
|
|
||||||
msgid "Barcode plugin returned incorrect response"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: InvenTree/api.py:159
|
|
||||||
msgid "Unknown barcode format"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: InvenTree/forms.py:101 build/forms.py:37
|
#: InvenTree/forms.py:101 build/forms.py:37
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -218,6 +202,46 @@ msgstr ""
|
|||||||
msgid "Database Statistics"
|
msgid "Database Statistics"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:53 barcode/api.py:150
|
||||||
|
msgid "Must provide barcode_data parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:126
|
||||||
|
msgid "No match found for barcode data"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:128
|
||||||
|
msgid "Match found for barcode data"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:153
|
||||||
|
msgid "Must provide stockitem parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:160
|
||||||
|
msgid "No matching stock item found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:190
|
||||||
|
msgid "Barcode already matches StockItem object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:194
|
||||||
|
msgid "Barcode already matches StockLocation object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:198
|
||||||
|
msgid "Barcode already matches Part object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:204 barcode/api.py:216
|
||||||
|
msgid "Barcode hash already matches StockItem object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: barcode/api.py:222
|
||||||
|
msgid "Barcode associated with StockItem"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: build/forms.py:58
|
#: build/forms.py:58
|
||||||
msgid "Location of completed parts"
|
msgid "Location of completed parts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -318,7 +342,7 @@ msgstr ""
|
|||||||
#: company/templates/company/supplier_part_base.html:60
|
#: company/templates/company/supplier_part_base.html:60
|
||||||
#: company/templates/company/supplier_part_detail.html:24
|
#: company/templates/company/supplier_part_detail.html:24
|
||||||
#: part/templates/part/detail.html:74 part/templates/part/part_base.html:88
|
#: part/templates/part/detail.html:74 part/templates/part/part_base.html:88
|
||||||
#: stock/models.py:368 stock/templates/stock/item_base.html:221
|
#: stock/models.py:368 stock/templates/stock/item_base.html:230
|
||||||
msgid "External Link"
|
msgid "External Link"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -398,7 +422,7 @@ msgstr ""
|
|||||||
#: build/templates/build/allocate.html:161
|
#: build/templates/build/allocate.html:161
|
||||||
#: order/templates/order/sales_order_detail.html:68
|
#: order/templates/order/sales_order_detail.html:68
|
||||||
#: order/templates/order/sales_order_detail.html:150 stock/models.py:362
|
#: order/templates/order/sales_order_detail.html:150 stock/models.py:362
|
||||||
#: stock/templates/stock/item_base.html:180
|
#: stock/templates/stock/item_base.html:189
|
||||||
msgid "Serial Number"
|
msgid "Serial Number"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -415,7 +439,7 @@ msgstr ""
|
|||||||
#: part/templates/part/allocation.html:49
|
#: part/templates/part/allocation.html:49
|
||||||
#: stock/templates/stock/item_base.html:26
|
#: stock/templates/stock/item_base.html:26
|
||||||
#: stock/templates/stock/item_base.html:32
|
#: stock/templates/stock/item_base.html:32
|
||||||
#: stock/templates/stock/item_base.html:186
|
#: stock/templates/stock/item_base.html:195
|
||||||
#: stock/templates/stock/stock_adjust.html:18 templates/js/bom.html:172
|
#: stock/templates/stock/stock_adjust.html:18 templates/js/bom.html:172
|
||||||
#: templates/js/build.html:52 templates/js/stock.html:653
|
#: templates/js/build.html:52 templates/js/stock.html:653
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
@ -423,7 +447,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: build/templates/build/allocate.html:177
|
#: build/templates/build/allocate.html:177
|
||||||
#: build/templates/build/auto_allocate.html:20
|
#: build/templates/build/auto_allocate.html:20
|
||||||
#: stock/templates/stock/item_base.html:162
|
#: stock/templates/stock/item_base.html:171
|
||||||
#: stock/templates/stock/stock_adjust.html:17 templates/js/stock.html:493
|
#: stock/templates/stock/stock_adjust.html:17 templates/js/stock.html:493
|
||||||
msgid "Location"
|
msgid "Location"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -504,7 +528,7 @@ msgstr ""
|
|||||||
#: build/templates/build/build_base.html:8
|
#: build/templates/build/build_base.html:8
|
||||||
#: build/templates/build/build_base.html:34
|
#: build/templates/build/build_base.html:34
|
||||||
#: build/templates/build/complete.html:6
|
#: build/templates/build/complete.html:6
|
||||||
#: stock/templates/stock/item_base.html:200 templates/js/build.html:33
|
#: stock/templates/stock/item_base.html:209 templates/js/build.html:33
|
||||||
#: templates/navbar.html:12
|
#: templates/navbar.html:12
|
||||||
msgid "Build"
|
msgid "Build"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -524,7 +548,7 @@ msgstr ""
|
|||||||
#: build/templates/build/build_base.html:80
|
#: build/templates/build/build_base.html:80
|
||||||
#: build/templates/build/detail.html:42
|
#: build/templates/build/detail.html:42
|
||||||
#: order/templates/order/receive_parts.html:24
|
#: order/templates/order/receive_parts.html:24
|
||||||
#: stock/templates/stock/item_base.html:253 templates/js/build.html:57
|
#: stock/templates/stock/item_base.html:262 templates/js/build.html:57
|
||||||
#: templates/js/order.html:162 templates/js/order.html:235
|
#: templates/js/order.html:162 templates/js/order.html:235
|
||||||
#: templates/js/stock.html:480
|
#: templates/js/stock.html:480
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
@ -536,7 +560,7 @@ msgstr ""
|
|||||||
#: order/templates/order/sales_order_notes.html:10
|
#: order/templates/order/sales_order_notes.html:10
|
||||||
#: order/templates/order/sales_order_ship.html:25
|
#: order/templates/order/sales_order_ship.html:25
|
||||||
#: part/templates/part/allocation.html:27
|
#: part/templates/part/allocation.html:27
|
||||||
#: stock/templates/stock/item_base.html:150 templates/js/order.html:209
|
#: stock/templates/stock/item_base.html:159 templates/js/order.html:209
|
||||||
msgid "Sales Order"
|
msgid "Sales Order"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -603,7 +627,7 @@ msgid "Stock can be taken from any available location."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: build/templates/build/detail.html:48
|
#: build/templates/build/detail.html:48
|
||||||
#: stock/templates/stock/item_base.html:193 templates/js/stock.html:488
|
#: stock/templates/stock/item_base.html:202 templates/js/stock.html:488
|
||||||
msgid "Batch"
|
msgid "Batch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -950,14 +974,14 @@ msgstr ""
|
|||||||
#: company/templates/company/supplier_part_detail.html:21 order/models.py:148
|
#: company/templates/company/supplier_part_detail.html:21 order/models.py:148
|
||||||
#: order/templates/order/order_base.html:74
|
#: order/templates/order/order_base.html:74
|
||||||
#: order/templates/order/order_wizard/select_pos.html:30
|
#: order/templates/order/order_wizard/select_pos.html:30
|
||||||
#: stock/templates/stock/item_base.html:228 templates/js/company.html:52
|
#: stock/templates/stock/item_base.html:237 templates/js/company.html:52
|
||||||
#: templates/js/company.html:134 templates/js/order.html:144
|
#: templates/js/company.html:134 templates/js/order.html:144
|
||||||
msgid "Supplier"
|
msgid "Supplier"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: company/templates/company/detail.html:26 order/models.py:314
|
#: company/templates/company/detail.html:26 order/models.py:314
|
||||||
#: order/templates/order/sales_order_base.html:73 stock/models.py:357
|
#: order/templates/order/sales_order_base.html:73 stock/models.py:357
|
||||||
#: stock/models.py:358 stock/templates/stock/item_base.html:137
|
#: stock/models.py:358 stock/templates/stock/item_base.html:146
|
||||||
#: templates/js/company.html:44 templates/js/order.html:217
|
#: templates/js/company.html:44 templates/js/order.html:217
|
||||||
msgid "Customer"
|
msgid "Customer"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -1071,7 +1095,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: company/templates/company/supplier_part_base.html:6
|
#: company/templates/company/supplier_part_base.html:6
|
||||||
#: company/templates/company/supplier_part_base.html:19 stock/models.py:331
|
#: company/templates/company/supplier_part_base.html:19 stock/models.py:331
|
||||||
#: stock/templates/stock/item_base.html:233 templates/js/company.html:150
|
#: stock/templates/stock/item_base.html:242 templates/js/company.html:150
|
||||||
msgid "Supplier Part"
|
msgid "Supplier Part"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1354,7 +1378,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: order/models.py:466 order/templates/order/order_base.html:9
|
#: order/models.py:466 order/templates/order/order_base.html:9
|
||||||
#: order/templates/order/order_base.html:23
|
#: order/templates/order/order_base.html:23
|
||||||
#: stock/templates/stock/item_base.html:207 templates/js/order.html:136
|
#: stock/templates/stock/item_base.html:216 templates/js/order.html:136
|
||||||
msgid "Purchase Order"
|
msgid "Purchase Order"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2052,14 +2076,14 @@ msgstr ""
|
|||||||
#: part/templates/part/allocation.html:45
|
#: part/templates/part/allocation.html:45
|
||||||
#: stock/templates/stock/item_base.html:8
|
#: stock/templates/stock/item_base.html:8
|
||||||
#: stock/templates/stock/item_base.html:58
|
#: stock/templates/stock/item_base.html:58
|
||||||
#: stock/templates/stock/item_base.html:215
|
#: stock/templates/stock/item_base.html:224
|
||||||
#: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106
|
#: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106
|
||||||
#: templates/js/stock.html:623
|
#: templates/js/stock.html:623
|
||||||
msgid "Stock Item"
|
msgid "Stock Item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/templates/part/allocation.html:20
|
#: part/templates/part/allocation.html:20
|
||||||
#: stock/templates/stock/item_base.html:156
|
#: stock/templates/stock/item_base.html:165
|
||||||
msgid "Build Order"
|
msgid "Build Order"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2453,7 +2477,7 @@ msgstr ""
|
|||||||
msgid "Tracking"
|
msgid "Tracking"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:259
|
#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:268
|
||||||
msgid "Tests"
|
msgid "Tests"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2646,22 +2670,6 @@ msgstr ""
|
|||||||
msgid "Confim BOM item deletion"
|
msgid "Confim BOM item deletion"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: plugins/barcode/inventree.py:70
|
|
||||||
msgid "Part does not exist"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: plugins/barcode/inventree.py:79
|
|
||||||
msgid "StockLocation does not exist"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: plugins/barcode/inventree.py:89
|
|
||||||
msgid "StockItem does not exist"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: plugins/barcode/inventree.py:92
|
|
||||||
msgid "No matching data"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: report/models.py:167
|
#: report/models.py:167
|
||||||
msgid "Template name"
|
msgid "Template name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2731,7 +2739,7 @@ msgstr ""
|
|||||||
msgid "Parent Stock Item"
|
msgid "Parent Stock Item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/models.py:322 stock/templates/stock/item_base.html:129
|
#: stock/models.py:322 stock/templates/stock/item_base.html:138
|
||||||
msgid "Base Part"
|
msgid "Base Part"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2937,79 +2945,108 @@ msgid ""
|
|||||||
"This stock item will be automatically deleted when all stock is depleted."
|
"This stock item will be automatically deleted when all stock is depleted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:74
|
#: stock/templates/stock/item_base.html:78
|
||||||
msgid "Add to stock"
|
msgid "Barcode actions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:77
|
#: stock/templates/stock/item_base.html:80
|
||||||
msgid "Take from stock"
|
msgid "Show QR Code"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:80 templates/stock_table.html:14
|
#: stock/templates/stock/item_base.html:81
|
||||||
|
msgid "Print Label"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:83 templates/js/barcode.html:263
|
||||||
|
#: templates/js/barcode.html:268
|
||||||
|
msgid "Unlink Barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:85
|
||||||
|
msgid "Link Barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:92
|
||||||
|
msgid "Stock adjustment actions"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:95 templates/stock_table.html:14
|
||||||
msgid "Count stock"
|
msgid "Count stock"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:84
|
#: stock/templates/stock/item_base.html:96 templates/stock_table.html:12
|
||||||
msgid "Serialize stock"
|
msgid "Add stock"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:90 stock/views.py:232
|
#: stock/templates/stock/item_base.html:97 templates/stock_table.html:13
|
||||||
msgid "Assign to Customer"
|
msgid "Remove stock"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:94
|
#: stock/templates/stock/item_base.html:99
|
||||||
msgid "Transfer stock"
|
msgid "Transfer stock"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:97
|
#: stock/templates/stock/item_base.html:105
|
||||||
msgid "Duplicate stock item"
|
msgid "Stock actions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:102
|
#: stock/templates/stock/item_base.html:108
|
||||||
msgid "Convert stock to variant"
|
msgid "Serialize stock"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:107
|
|
||||||
msgid "Generate test report"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:111
|
#: stock/templates/stock/item_base.html:111
|
||||||
|
msgid "Assign to customer"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:114
|
||||||
|
msgid "Convert to variant"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:116
|
||||||
|
msgid "Duplicate stock item"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:117
|
||||||
msgid "Edit stock item"
|
msgid "Edit stock item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:115
|
#: stock/templates/stock/item_base.html:119
|
||||||
msgid "Delete stock item"
|
msgid "Delete stock item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:124
|
#: stock/templates/stock/item_base.html:124
|
||||||
|
msgid "Generate test report"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/templates/stock/item_base.html:133
|
||||||
msgid "Stock Item Details"
|
msgid "Stock Item Details"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:144
|
#: stock/templates/stock/item_base.html:153
|
||||||
msgid "Belongs To"
|
msgid "Belongs To"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:166
|
#: stock/templates/stock/item_base.html:175
|
||||||
msgid "No location set"
|
msgid "No location set"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:173
|
#: stock/templates/stock/item_base.html:182
|
||||||
msgid "Unique Identifier"
|
msgid "Unique Identifier"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:214
|
#: stock/templates/stock/item_base.html:223
|
||||||
msgid "Parent Item"
|
msgid "Parent Item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:239
|
#: stock/templates/stock/item_base.html:248
|
||||||
msgid "Last Updated"
|
msgid "Last Updated"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:244
|
#: stock/templates/stock/item_base.html:253
|
||||||
msgid "Last Stocktake"
|
msgid "Last Stocktake"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/templates/stock/item_base.html:248
|
#: stock/templates/stock/item_base.html:257
|
||||||
msgid "No stocktake performed"
|
msgid "No stocktake performed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3128,6 +3165,10 @@ msgstr ""
|
|||||||
msgid "Delete Stock Item Attachment"
|
msgid "Delete Stock Item Attachment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: stock/views.py:232
|
||||||
|
msgid "Assign to Customer"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:270
|
#: stock/views.py:270
|
||||||
msgid "Delete All Test Data"
|
msgid "Delete All Test Data"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3251,7 +3292,7 @@ msgid "Create new Stock Item"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:1167
|
#: stock/views.py:1167
|
||||||
msgid "Copy Stock Item"
|
msgid "Duplicate Stock Item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: stock/views.py:1240
|
#: stock/views.py:1240
|
||||||
@ -3375,6 +3416,39 @@ msgstr ""
|
|||||||
msgid "Delete attachment"
|
msgid "Delete attachment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:28
|
||||||
|
msgid "Scan barcode data here using wedge scanner"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:34
|
||||||
|
msgid "Barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:42
|
||||||
|
msgid "Enter barcode data"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:140
|
||||||
|
msgid "Scan barcode data below"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:195 templates/js/barcode.html:243
|
||||||
|
msgid "Unknown response from server"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:198 templates/js/barcode.html:247
|
||||||
|
msgid "Invalid server response"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:265
|
||||||
|
msgid ""
|
||||||
|
"This will remove the association between this stock item and the barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/js/barcode.html:271
|
||||||
|
msgid "Unlink"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: templates/js/bom.html:143
|
#: templates/js/bom.html:143
|
||||||
msgid "Open subassembly"
|
msgid "Open subassembly"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3635,42 +3709,38 @@ msgstr ""
|
|||||||
msgid "Sell"
|
msgid "Sell"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/navbar.html:36
|
#: templates/navbar.html:32
|
||||||
|
msgid "Scan Barcode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/navbar.html:41
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/navbar.html:39
|
#: templates/navbar.html:44
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/navbar.html:40
|
#: templates/navbar.html:45
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/navbar.html:42
|
#: templates/navbar.html:47
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/navbar.html:45
|
#: templates/navbar.html:50
|
||||||
msgid "About InvenTree"
|
msgid "About InvenTree"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/navbar.html:46
|
#: templates/navbar.html:51
|
||||||
msgid "Statistics"
|
msgid "Statistics"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/search_form.html:6
|
#: templates/search_form.html:6 templates/search_form.html:8
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/stock_table.html:12
|
|
||||||
msgid "Add stock"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: templates/stock_table.html:13
|
|
||||||
msgid "Remove stock"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: templates/stock_table.html:15
|
#: templates/stock_table.html:15
|
||||||
msgid "Move stock"
|
msgid "Move stock"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -35,44 +35,42 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</h3>
|
</h3>
|
||||||
<p><i>{{ part.description }}</i></p>
|
<p><i>{{ part.description }}</i></p>
|
||||||
<p>
|
<div class='btn-group action-buttons' role='group'>
|
||||||
<div class='btn-row'>
|
<button type='button' class='btn btn-default' id='toggle-starred' title='{% trans "Star this part" %}'>
|
||||||
<div class='btn-group action-buttons'>
|
<span id='part-star-icon' class='fas fa-star {% if starred %}icon-yellow{% endif %}'/>
|
||||||
<button type='button' class='btn btn-default' id='toggle-starred' title='{% trans "Star this part" %}'>
|
</button>
|
||||||
<span id='part-star-icon' class='fas fa-star {% if starred %}icon-yellow{% endif %}'/>
|
<!-- Barcode actions menu -->
|
||||||
</button>
|
<div class='dropdown dropdown-buttons'>
|
||||||
{% if part.is_template == False %}
|
<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>
|
||||||
{% include "qr_button.html" %}
|
<ul class='dropdown-menu' role='menu'>
|
||||||
{% if part.active %}
|
<li><a href='#' id='show-qr-code'><span class='fas fa-qrcode'></span> {% trans "Show QR Code" %}</a></li>
|
||||||
<button type='button' class='btn btn-default' id='price-button' title='{% trans "Show pricing information" %}'>
|
<li class='disabled'><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
|
||||||
<span id='part-price-icon' class='fas fa-dollar-sign'/>
|
</ul>
|
||||||
</button>
|
|
||||||
{% if not part.virtual %}
|
|
||||||
<button type='button' class='btn btn-default' id='part-count' title='Count part stock'>
|
|
||||||
<span class='fas fa-clipboard-list'/>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if part.purchaseable %}
|
|
||||||
<button type='button' class='btn btn-default' id='part-order' title='Order part'>
|
|
||||||
<span id='part-order-icon' class='fas fa-shopping-cart'/>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
<button type='button' class='btn btn-default' id='part-duplicate' title='Duplicate part'>
|
|
||||||
<span id='part-duplicate-icon' class='fas fa-copy'/>
|
|
||||||
</button>
|
|
||||||
<button type='button' class='btn btn-default' id='part-edit' title='Edit part'>
|
|
||||||
<span id='part-edit-icon' class='fas fa-edit icon-blue'/>
|
|
||||||
</button>
|
|
||||||
{% if not part.active %}
|
|
||||||
<button type='button' class='btn btn-default' id='part-delete' title='Delete part'>
|
|
||||||
<span id='part-delete-icon' class='fas fa-trash-alt icon-red'/>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</p>
|
{% if part.active %}
|
||||||
|
<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'/>
|
||||||
|
</button>
|
||||||
|
<button type='button' class='btn btn-default' id='part-count' title='Count part stock'>
|
||||||
|
<span class='fas fa-clipboard-list'/>
|
||||||
|
</button>
|
||||||
|
{% if part.purchaseable %}
|
||||||
|
<button type='button' class='btn btn-default' id='part-order' title='Order part'>
|
||||||
|
<span id='part-order-icon' class='fas fa-shopping-cart'/>
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
<div class='dropdown dropdown-buttons'>
|
||||||
|
<button id='part-actions' title='{% trans "Part actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'> <span class='fas fa-shapes'></span> <span class='caret'></span></button>
|
||||||
|
<ul class='dropdown-menu' role='menu'>
|
||||||
|
<li><a href='#' id='part-duplicate'><span class='fas fa-copy'></span> {% trans "Duplicate part" %}</a></li>
|
||||||
|
<li><a href='#' id='part-edit'><span class='fas fa-edit icon-blue'></span> {% trans "Edit part" %}</a></li>
|
||||||
|
{% if not part.active %}
|
||||||
|
<li><a href='#' id='part-delete'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete part" %}</a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<table class='table table-condensed'>
|
<table class='table table-condensed'>
|
||||||
<col width='25'>
|
<col width='25'>
|
||||||
{% if part.IPN %}
|
{% if part.IPN %}
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
from stock.serializers import StockItemSerializer, LocationSerializer
|
|
||||||
from part.serializers import PartSerializer
|
|
||||||
|
|
||||||
import plugins.plugin as plugin
|
|
||||||
|
|
||||||
|
|
||||||
class BarcodePlugin(plugin.InvenTreePlugin):
|
|
||||||
"""
|
|
||||||
The BarcodePlugin class is the base class for any barcode plugin.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, barcode_data):
|
|
||||||
plugin.InvenTreePlugin.__init__(self)
|
|
||||||
|
|
||||||
self.data = barcode_data
|
|
||||||
|
|
||||||
def hash(self):
|
|
||||||
"""
|
|
||||||
Calculate a hash for the barcode data.
|
|
||||||
This is supposed to uniquely identify the barcode contents,
|
|
||||||
at least within the bardcode sub-type.
|
|
||||||
|
|
||||||
The default implementation simply returns an MD5 hash of the barcode data,
|
|
||||||
encoded to a string.
|
|
||||||
|
|
||||||
This may be sufficient for most applications, but can obviously be overridden
|
|
||||||
by a subclass.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
hash = hashlib.md5(str(self.data).encode())
|
|
||||||
return str(hash.hexdigest())
|
|
||||||
|
|
||||||
def validate(self):
|
|
||||||
"""
|
|
||||||
Default implementation returns False
|
|
||||||
"""
|
|
||||||
return False
|
|
||||||
|
|
||||||
def decode(self):
|
|
||||||
"""
|
|
||||||
Decode the barcode, and craft a response
|
|
||||||
"""
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def render_part(self, part):
|
|
||||||
"""
|
|
||||||
Render a Part object to JSON
|
|
||||||
Use the existing serializer to do this.
|
|
||||||
"""
|
|
||||||
|
|
||||||
serializer = PartSerializer(part)
|
|
||||||
|
|
||||||
return serializer.data
|
|
||||||
|
|
||||||
def render_stock_location(self, loc):
|
|
||||||
"""
|
|
||||||
Render a StockLocation object to JSON
|
|
||||||
Use the existing serializer to do this.
|
|
||||||
"""
|
|
||||||
|
|
||||||
serializer = LocationSerializer(loc)
|
|
||||||
|
|
||||||
return serializer.data
|
|
||||||
|
|
||||||
def render_stock_item(self, item):
|
|
||||||
"""
|
|
||||||
Render a StockItem object to JSON.
|
|
||||||
Use the existing serializer to do this
|
|
||||||
"""
|
|
||||||
|
|
||||||
serializer = StockItemSerializer(item, part_detail=True, location_detail=True, supplier_part_detail=True)
|
|
||||||
|
|
||||||
return serializer.data
|
|
@ -1,8 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from . import barcode
|
|
||||||
|
|
||||||
|
|
||||||
class DigikeyBarcodePlugin(barcode.BarcodePlugin):
|
|
||||||
|
|
||||||
PLUGIN_NAME = "DigikeyBarcodePlugin"
|
|
@ -1,94 +0,0 @@
|
|||||||
"""
|
|
||||||
The InvenTreeBarcodePlugin validates barcodes generated by InvenTree itself.
|
|
||||||
It can be used as a template for developing third-party barcode plugins.
|
|
||||||
|
|
||||||
The data format is very simple, and maps directly to database objects,
|
|
||||||
via the "id" parameter.
|
|
||||||
|
|
||||||
Parsing an InvenTree barcode simply involves validating that the
|
|
||||||
references model objects actually exist in the database.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
from . import barcode
|
|
||||||
|
|
||||||
from stock.models import StockItem, StockLocation
|
|
||||||
from part.models import Part
|
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
|
|
||||||
|
|
||||||
class InvenTreeBarcodePlugin(barcode.BarcodePlugin):
|
|
||||||
|
|
||||||
PLUGIN_NAME = "InvenTreeBarcodePlugin"
|
|
||||||
|
|
||||||
def validate(self):
|
|
||||||
"""
|
|
||||||
An "InvenTree" barcode must include the following tags:
|
|
||||||
|
|
||||||
{
|
|
||||||
'tool': 'InvenTree',
|
|
||||||
'version': <anything>
|
|
||||||
}
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# The data must either be dict or be able to dictified
|
|
||||||
if type(self.data) is dict:
|
|
||||||
pass
|
|
||||||
elif type(self.data) is str:
|
|
||||||
try:
|
|
||||||
self.data = json.loads(self.data)
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
for key in ['tool', 'version']:
|
|
||||||
if key not in self.data.keys():
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not self.data['tool'] == 'InvenTree':
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def decode(self):
|
|
||||||
|
|
||||||
response = {}
|
|
||||||
|
|
||||||
if 'part' in self.data.keys():
|
|
||||||
id = self.data['part'].get('id', None)
|
|
||||||
|
|
||||||
try:
|
|
||||||
part = Part.objects.get(id=id)
|
|
||||||
response['part'] = self.render_part(part)
|
|
||||||
except (ValueError, Part.DoesNotExist):
|
|
||||||
response['error'] = _('Part does not exist')
|
|
||||||
|
|
||||||
elif 'stocklocation' in self.data.keys():
|
|
||||||
id = self.data['stocklocation'].get('id', None)
|
|
||||||
|
|
||||||
try:
|
|
||||||
loc = StockLocation.objects.get(id=id)
|
|
||||||
response['stocklocation'] = self.render_stock_location(loc)
|
|
||||||
except (ValueError, StockLocation.DoesNotExist):
|
|
||||||
response['error'] = _('StockLocation does not exist')
|
|
||||||
|
|
||||||
elif 'stockitem' in self.data.keys():
|
|
||||||
|
|
||||||
id = self.data['stockitem'].get('id', None)
|
|
||||||
|
|
||||||
try:
|
|
||||||
item = StockItem.objects.get(id=id)
|
|
||||||
response['stockitem'] = self.render_stock_item(item)
|
|
||||||
except (ValueError, StockItem.DoesNotExist):
|
|
||||||
response['error'] = _('StockItem does not exist')
|
|
||||||
|
|
||||||
else:
|
|
||||||
response['error'] = _('No matching data')
|
|
||||||
|
|
||||||
return response
|
|
@ -4,10 +4,6 @@ import inspect
|
|||||||
import importlib
|
import importlib
|
||||||
import pkgutil
|
import pkgutil
|
||||||
|
|
||||||
# Barcode plugins
|
|
||||||
import plugins.barcode as barcode
|
|
||||||
from plugins.barcode.barcode import BarcodePlugin
|
|
||||||
|
|
||||||
# Action plugins
|
# Action plugins
|
||||||
import plugins.action as action
|
import plugins.action as action
|
||||||
from plugins.action.action import ActionPlugin
|
from plugins.action.action import ActionPlugin
|
||||||
@ -51,24 +47,6 @@ def get_plugins(pkg, baseclass):
|
|||||||
return plugins
|
return plugins
|
||||||
|
|
||||||
|
|
||||||
def load_barcode_plugins():
|
|
||||||
"""
|
|
||||||
Return a list of all registered barcode plugins
|
|
||||||
"""
|
|
||||||
|
|
||||||
print("Loading barcode plugins")
|
|
||||||
|
|
||||||
plugins = get_plugins(barcode, BarcodePlugin)
|
|
||||||
|
|
||||||
if len(plugins) > 0:
|
|
||||||
print("Discovered {n} barcode plugins:".format(n=len(plugins)))
|
|
||||||
|
|
||||||
for bp in plugins:
|
|
||||||
print(" - {bp}".format(bp=bp.PLUGIN_NAME))
|
|
||||||
|
|
||||||
return plugins
|
|
||||||
|
|
||||||
|
|
||||||
def load_action_plugins():
|
def load_action_plugins():
|
||||||
"""
|
"""
|
||||||
Return a list of all registered action plugins
|
Return a list of all registered action plugins
|
||||||
|
@ -67,55 +67,64 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<div class='btn-group action-buttons'>
|
<div class='btn-group' role='group'>
|
||||||
{% include "qr_button.html" %}
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='btn-group action-buttons' role='group'>
|
||||||
|
<!-- Barcode actions menu -->
|
||||||
|
<div class='dropdown dropdown-buttons'>
|
||||||
|
<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>
|
||||||
|
<ul class='dropdown-menu' role='menu'>
|
||||||
|
<li><a href='#' id='show-qr-code'><span class='fas fa-qrcode'></span> {% trans "Show QR Code" %}</a></li>
|
||||||
|
<li class='disabled'><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
|
||||||
|
{% if item.uid %}
|
||||||
|
<li><a href='#' id='unlink-barcode'><span class='fas fa-unlink'></span> {% trans "Unlink Barcode" %}</a></li>
|
||||||
|
{% else %}
|
||||||
|
<li><a href='#' id='link-barcode'><span class='fas fa-link'></span> {% trans "Link Barcode" %}</a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
{% if item.in_stock %}
|
{% if item.in_stock %}
|
||||||
{% if not item.serialized %}
|
<!-- Stock adjustment menu -->
|
||||||
<button type='button' class='btn btn-default' id='stock-add' title='{% trans "Add to stock" %}'>
|
<div class='dropdown dropdown-buttons'>
|
||||||
<span class='fas fa-plus-circle icon-green'/>
|
<button id='stock-options' title='{% trans "Stock adjustment actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-boxes'></span> <span class='caret'></span></button>
|
||||||
</button>
|
<ul class='dropdown-menu' role='menu'>
|
||||||
<button type='button' class='btn btn-default' id='stock-remove' title='{% trans "Take from stock" %}'>
|
{% if not item.serialized %}
|
||||||
<span class='fas fa-minus-circle icon-red''/>
|
<li><a href='#' id='stock-count' title='{% trans "Count stock" %}'><span class='fas fa-clipboard-list'></span> {% trans "Count stock" %}</a></li>
|
||||||
</button>
|
<li><a href='#' id='stock-add' title='{% trans "Add stock" %}'><span class='fas fa-plus-circle icon-green'></span> {% trans "Add stock" %}</a></li>
|
||||||
<button type='button' class='btn btn-default' id='stock-count' title='{% trans "Count stock" %}'>
|
<li><a href='#' id='stock-remove' title='{% trans "Remove stock" %}'><span class='fas fa-minus-circle icon-red'></span> {% trans "Remove stock" %}</a></li>
|
||||||
<span class='fas fa-clipboard-list'/>
|
{% endif %}
|
||||||
</button>
|
<li><a href='#' id='stock-move' title='{% trans "Transfer stock" %}'><span class='fas fa-exchange-alt icon-blue'></span> {% trans "Transfer stock" %}</a></li>
|
||||||
{% if item.part.trackable %}
|
</ul>
|
||||||
<button type='button' class='btn btn-default' id='stock-serialize' title='{% trans "Serialize stock" %}'>
|
</div>
|
||||||
<span class='fas fa-hashtag'/>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% if item.part.salable %}
|
|
||||||
<button type='button' class='btn btn-default' id='stock-assign-to-customer' title='{% trans "Assign to Customer" %}'>
|
|
||||||
<span class='fas fa-user-tie'/>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
<button type='button' class='btn btn-default' id='stock-move' title='{% trans "Transfer stock" %}'>
|
|
||||||
<span class='fas fa-exchange-alt icon-blue'/>
|
|
||||||
</button>
|
|
||||||
<button type='button' class='btn btn-default' id='stock-duplicate' title='{% trans "Duplicate stock item" %}'>
|
|
||||||
<span class='fas fa-copy'/>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if item.part.has_variants %}
|
|
||||||
<button type='button' class='btn btn-default' id='stock-convert' title='{% trans "Convert stock to variant" %}'>
|
|
||||||
<span class='fas fa-screwdriver'/>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<!-- Edit stock item -->
|
||||||
|
<div class='dropdown dropdown-buttons'>
|
||||||
|
<button id='stock-edit-actions' title='{% trans "Stock actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-tools'></span> <span class='caret'></span></button>
|
||||||
|
<ul class='dropdown-menu' role='menu'>
|
||||||
|
{% if item.part.trackable and not item.serialized %}
|
||||||
|
<li><a href='#' id='stock-serialize' title='{% trans "Serialize stock" %}'><span class='fas fa-hashtag'></span> {% trans "Serialize stock" %}</a> </li>
|
||||||
|
{% endif %}
|
||||||
|
{% if item.part.salable and not item.customer %}
|
||||||
|
<li><a href='#' id='stock-assign-to-customer' title='{% trans "Assign to customer" %}'><span class='fas fa-user-tie'></span> {% trans "Assign to customer" %}</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if item.part.has_variants %}
|
||||||
|
<li><a href='#' id='stock-convert' title='{% trans "Convert to variant" %}'><span class='fas fa-screwdriver'></span> {% trans "Convert to variant" %}</a></li>
|
||||||
|
{% endif %}
|
||||||
|
<li><a href='#' id='stock-duplicate' title='{% trans "Duplicate stock item" %}'><span class='fas fa-copy'></span> {% trans "Duplicate stock item" %}</a></li>
|
||||||
|
<li><a href='#' id='stock-edit' title='{% trans "Edit stock item" %}'><span class='fas fa-edit icon-blue'></span> {% trans "Edit stock item" %}</a></li>
|
||||||
|
{% if item.can_delete %}
|
||||||
|
<li><a href='#' id='stock-delete' title='{% trans "Delete stock item" %}'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete stock item" %}</a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
{% if item.part.has_test_report_templates %}
|
{% if item.part.has_test_report_templates %}
|
||||||
<button type='button' class='btn btn-default' id='stock-test-report' title='{% trans "Generate test report" %}'>
|
<button type='button' class='btn btn-default' id='stock-test-report' title='{% trans "Generate test report" %}'>
|
||||||
<span class='fas fa-tasks'/>
|
<span class='fas fa-tasks'/>
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button type='button' class='btn btn-default' id='stock-edit' title='{% trans "Edit stock item" %}'>
|
|
||||||
<span class='fas fa-edit icon-blue'/>
|
|
||||||
</button>
|
|
||||||
{% if item.can_delete %}
|
|
||||||
<button type='button' class='btn btn-default' id='stock-delete' title='{% trans "Delete stock item" %}'>
|
|
||||||
<span class='fas fa-trash-alt icon-red'/>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -131,6 +140,19 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
|
|||||||
<a href="{% url 'part-stock' item.part.id %}">{{ item.part.full_name }}
|
<a href="{% url 'part-stock' item.part.id %}">{{ item.part.full_name }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% if item.serialized %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-hashtag'></span></td>
|
||||||
|
<td>{% trans "Serial Number" %}</td>
|
||||||
|
<td>{{ item.serial }}</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>{% trans "Quantity" %}</td>
|
||||||
|
<td>{% decimal item.quantity %} {% if item.part.units %}{{ item.part.units }}{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
{% if item.customer %}
|
{% if item.customer %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-user-tie'></span></td>
|
<td><span class='fas fa-user-tie'></span></td>
|
||||||
@ -174,19 +196,6 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
|
|||||||
<td>{{ item.uid }}</td>
|
<td>{{ item.uid }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if item.serialized %}
|
|
||||||
<tr>
|
|
||||||
<td><span class='fas fa-hashtag'></span></td>
|
|
||||||
<td>{% trans "Serial Number" %}</td>
|
|
||||||
<td>{{ item.serial }}</td>
|
|
||||||
</tr>
|
|
||||||
{% else %}
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td>{% trans "Quantity" %}</td>
|
|
||||||
<td>{% decimal item.quantity %} {% if item.part.units %}{{ item.part.units }}{% endif %}</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
{% if item.batch %}
|
{% if item.batch %}
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
@ -330,6 +339,14 @@ $("#show-qr-code").click(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#link-barcode").click(function() {
|
||||||
|
linkBarcodeDialog({{ item.id }});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#unlink-barcode").click(function() {
|
||||||
|
unlinkBarcode({{ item.id }});
|
||||||
|
});
|
||||||
|
|
||||||
{% if item.in_stock %}
|
{% if item.in_stock %}
|
||||||
|
|
||||||
{% if item.part.salable %}
|
{% if item.part.salable %}
|
||||||
@ -385,10 +402,11 @@ $('#stock-add').click(function() {
|
|||||||
|
|
||||||
$("#stock-delete").click(function () {
|
$("#stock-delete").click(function () {
|
||||||
launchModalForm(
|
launchModalForm(
|
||||||
"{% url 'stock-item-delete' item.id %}",
|
"{% url 'stock-item-delete' item.id %}",
|
||||||
{
|
{
|
||||||
redirect: "{% url 'part-stock' item.part.id %}"
|
redirect: "{% url 'part-stock' item.part.id %}"
|
||||||
});
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -12,25 +12,36 @@
|
|||||||
<h3>{% trans "Stock" %}</h3>
|
<h3>{% trans "Stock" %}</h3>
|
||||||
<p>{% trans "All stock items" %}</p>
|
<p>{% trans "All stock items" %}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p>
|
<div class='btn-group action-buttons' role='group'>
|
||||||
<div class='btn-group action-buttons'>
|
<button class='btn btn-default' id='location-create' title='{% trans "Create new stock location" %}'>
|
||||||
<button class='btn btn-default' id='location-create' title='{% trans "Create new stock location" %}'>
|
<span class='fas fa-plus-circle icon-green'/>
|
||||||
<span class='fas fa-plus-circle icon-green'/>
|
</button>
|
||||||
</button>
|
<!-- Barcode actions menu -->
|
||||||
{% if location %}
|
{% if location %}
|
||||||
{% include "qr_button.html" %}
|
<div class='dropdown dropdown-buttons'>
|
||||||
<button class='btn btn-default' id='location-count' title='{% trans "Count stock items" %}'>
|
<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>
|
||||||
<span class='fas fa-clipboard-list'/>
|
<ul class='dropdown-menu' role='menu'>
|
||||||
</button>
|
<li><a href='#' id='show-qr-code'><span class='fas fa-qrcode'></span> {% trans "Show QR Code" %}</a></li>
|
||||||
<button class='btn btn-default btn-glyph' id='location-edit' title='{% trans "Edit stock location" %}'>
|
<li class='disabled'><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
|
||||||
<span class='fas fa-edit icon-blue'/>
|
<li><a href='#' id='barcode-check-in'><span class='fas fa-arrow-right'></span> {% trans "Check-in Items" %}</a></li>
|
||||||
</button>
|
</ul>
|
||||||
<button class='btn btn-default btn-glyph' id='location-delete' title='{% trans "Delete stock location" %}'>
|
|
||||||
<span class='fas fa-trash-alt icon-red'/>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</p>
|
<div class='dropdown dropdown-buttons'>
|
||||||
|
<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>
|
||||||
|
<ul class='dropdown-menu' role='menu'>
|
||||||
|
<li><a href='#' id='location-count'><span class='fas fa-clipboard-list'></span>
|
||||||
|
{% trans "Count stock" %}</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class='dropdown dropdown-buttons'>
|
||||||
|
<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>
|
||||||
|
<li><a href='#' id='location-delete'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete location" %}</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='col-sm-6'>
|
<div class='col-sm-6'>
|
||||||
{% if location %}
|
{% if location %}
|
||||||
@ -109,6 +120,12 @@
|
|||||||
inventreeDel('show-part-locs');
|
inventreeDel('show-part-locs');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
{% if location %}
|
||||||
|
$("#barcode-check-in").click(function() {
|
||||||
|
barcodeCheckIn({{ location.id }});
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
$("#stock-export").click(function() {
|
$("#stock-export").click(function() {
|
||||||
launchModalForm("{% url 'stock-export-options' %}", {
|
launchModalForm("{% url 'stock-export-options' %}", {
|
||||||
submit_text: "Export",
|
submit_text: "Export",
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
{% extends "modal_delete_form.html" %}
|
{% extends "modal_delete_form.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
{% load inventree_extras %}
|
||||||
|
|
||||||
{% block pre_form_content %}
|
{% block pre_form_content %}
|
||||||
Are you sure you want to delete stock location '{{ location.name }}'?
|
{% trans "Are you sure you want to delete this stock location?" %}
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
@ -33,7 +36,7 @@ If this location is deleted, these items will be moved to the top level 'Stock'
|
|||||||
|
|
||||||
<ul class='list-group'>
|
<ul class='list-group'>
|
||||||
{% for item in location.stock_items.all %}
|
{% for item in location.stock_items.all %}
|
||||||
<li class='list-group-item'><b>{{ item.part.full_name }}</b> - <i>{{ item.part.description }}</i><span class='badge'>{{ item.quantity }}</span></li>
|
<li class='list-group-item'><b>{{ item.part.full_name }}</b> - <i>{{ item.part.description }}</i><span class='badge'>{% decimal item.quantity %}</span></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1164,7 +1164,7 @@ class StockItemCreate(AjaxCreateView):
|
|||||||
try:
|
try:
|
||||||
original = StockItem.objects.get(pk=item_to_copy)
|
original = StockItem.objects.get(pk=item_to_copy)
|
||||||
initials = model_to_dict(original)
|
initials = model_to_dict(original)
|
||||||
self.ajax_form_title = _("Copy Stock Item")
|
self.ajax_form_title = _("Duplicate Stock Item")
|
||||||
except StockItem.DoesNotExist:
|
except StockItem.DoesNotExist:
|
||||||
initials = super(StockItemCreate, self).get_initial().copy()
|
initials = super(StockItemCreate, self).get_initial().copy()
|
||||||
|
|
||||||
|
@ -108,6 +108,7 @@ InvenTree
|
|||||||
<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>
|
||||||
|
|
||||||
|
<script type='text/javascript' src="{% url 'barcode.js' %}"></script>
|
||||||
<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>
|
||||||
@ -132,6 +133,11 @@ $(document).ready(function () {
|
|||||||
inventreeDocReady();
|
inventreeDocReady();
|
||||||
|
|
||||||
showCachedAlerts();
|
showCachedAlerts();
|
||||||
|
|
||||||
|
$('#barcode-scan').click(function() {
|
||||||
|
barcodeScanDialog();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
514
InvenTree/templates/js/barcode.html
Normal file
514
InvenTree/templates/js/barcode.html
Normal file
@ -0,0 +1,514 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
function makeBarcodeInput(placeholderText='') {
|
||||||
|
/*
|
||||||
|
* Generate HTML for a barcode input
|
||||||
|
*/
|
||||||
|
|
||||||
|
placeholderText = placeholderText || '{% trans "Scan barcode data here using wedge scanner" %}';
|
||||||
|
|
||||||
|
var html = `
|
||||||
|
<div class='form-group'>
|
||||||
|
<label class='control-label' for='barcode'>{% trans "Barcode" %}</label>
|
||||||
|
<div class='controls'>
|
||||||
|
<div class='input-group'>
|
||||||
|
<span class='input-group-addon'>
|
||||||
|
<span class='fas fa-qrcode'></span>
|
||||||
|
</span>
|
||||||
|
<input id='barcode' class='textinput textInput form-control' type='text' name='barcode' placeholder='${placeholderText}'>
|
||||||
|
</div>
|
||||||
|
<div id='hint_barcode_data' class='help-block'>{% trans "Enter barcode data" %}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function showBarcodeMessage(modal, message, style='danger') {
|
||||||
|
|
||||||
|
var html = `<div class='alert alert-block alert-${style}'>`;
|
||||||
|
|
||||||
|
html += message;
|
||||||
|
|
||||||
|
html += "</div>";
|
||||||
|
|
||||||
|
$(modal + ' #barcode-error-message').html(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function showInvalidResponseError(modal, response, status) {
|
||||||
|
showBarcodeMessage(modal, `{% trans "Invalid server response" %}<br>{% trans "Status" %}: '${status}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function clearBarcodeError(modal, message) {
|
||||||
|
|
||||||
|
$(modal + ' #barcode-error-message').html('');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function enableBarcodeInput(modal, enabled=true) {
|
||||||
|
|
||||||
|
var barcode = $(modal + ' #barcode');
|
||||||
|
|
||||||
|
barcode.prop('disabled', !enabled);
|
||||||
|
|
||||||
|
modalEnable(modal, enabled);
|
||||||
|
|
||||||
|
barcode.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBarcodeData(modal) {
|
||||||
|
|
||||||
|
modal = modal || '#modal-form';
|
||||||
|
|
||||||
|
var el = $(modal + ' #barcode');
|
||||||
|
|
||||||
|
var barcode = el.val();
|
||||||
|
|
||||||
|
el.val('');
|
||||||
|
el.focus();
|
||||||
|
|
||||||
|
return barcode.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function barcodeDialog(title, options={}) {
|
||||||
|
/*
|
||||||
|
* Handle a barcode display dialog.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var modal = '#modal-form';
|
||||||
|
|
||||||
|
function sendBarcode() {
|
||||||
|
var barcode = getBarcodeData(modal);
|
||||||
|
|
||||||
|
if (barcode && barcode.length > 0) {
|
||||||
|
|
||||||
|
if (options.onScan) {
|
||||||
|
options.onScan(barcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(modal).on('shown.bs.modal', function() {
|
||||||
|
$(modal + ' .modal-form-content').scrollTop(0);
|
||||||
|
|
||||||
|
var barcode = $(modal + ' #barcode');
|
||||||
|
|
||||||
|
// Handle 'enter' key on barcode
|
||||||
|
barcode.keyup(function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (event.which == 10 || event.which == 13) {
|
||||||
|
sendBarcode();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ensure the barcode field has focus
|
||||||
|
barcode.focus();
|
||||||
|
|
||||||
|
var form = $(modal).find('.js-modal-form');
|
||||||
|
|
||||||
|
// Override form submission
|
||||||
|
form.submit(function() {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Callback for when the "submit" button is pressed on the modal
|
||||||
|
modalSubmit(modal, function() {
|
||||||
|
if (options.onSubmit) {
|
||||||
|
options.onSubmit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.onShow) {
|
||||||
|
options.onShow();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
modalSetTitle(modal, title);
|
||||||
|
|
||||||
|
if (options.onSubmit) {
|
||||||
|
modalShowSubmitButton(modal, true);
|
||||||
|
} else {
|
||||||
|
modalShowSubmitButton(modal, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = '';
|
||||||
|
|
||||||
|
content += `<div class='alert alert-info alert-block'>{% trans "Scan barcode data below" %}</div>`;
|
||||||
|
|
||||||
|
content += `<div id='barcode-error-message'></div>`;
|
||||||
|
content += `<form class='js-modal-form' method='post'>`;
|
||||||
|
|
||||||
|
// Optional content before barcode input
|
||||||
|
content += `<div class='container' id='barcode-header'>`;
|
||||||
|
content += options.headerContent || '';
|
||||||
|
content += `</div>`;
|
||||||
|
|
||||||
|
content += makeBarcodeInput();
|
||||||
|
|
||||||
|
if (options.extraFields) {
|
||||||
|
content += options.extraFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
content += `</form>`;
|
||||||
|
|
||||||
|
// Optional content after barcode input
|
||||||
|
content += `<div class='container' id='barcode-footer'>`;
|
||||||
|
content += options.footerContent || '';
|
||||||
|
content += '</div>';
|
||||||
|
|
||||||
|
modalSetContent(modal, content);
|
||||||
|
|
||||||
|
$(modal).modal({
|
||||||
|
backdrop: 'static',
|
||||||
|
keyboard: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.preShow) {
|
||||||
|
options.preShow();
|
||||||
|
}
|
||||||
|
|
||||||
|
$(modal).modal('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function barcodeScanDialog() {
|
||||||
|
/*
|
||||||
|
* Perform a barcode scan,
|
||||||
|
* and (potentially) redirect the browser
|
||||||
|
*/
|
||||||
|
|
||||||
|
var modal = '#modal-form';
|
||||||
|
|
||||||
|
barcodeDialog(
|
||||||
|
"Scan Barcode",
|
||||||
|
{
|
||||||
|
onScan: function(barcode) {
|
||||||
|
enableBarcodeInput(modal, false);
|
||||||
|
inventreePut(
|
||||||
|
'/api/barcode/',
|
||||||
|
{
|
||||||
|
barcode: barcode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
success: function(response, status) {
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dialog for linking a particular barcode to a stock item.
|
||||||
|
*/
|
||||||
|
function linkBarcodeDialog(stockitem, options={}) {
|
||||||
|
|
||||||
|
var modal = '#modal-form';
|
||||||
|
|
||||||
|
barcodeDialog(
|
||||||
|
"{% trans 'Link Barcode to Stock Item' %}",
|
||||||
|
{
|
||||||
|
onScan: function(barcode) {
|
||||||
|
enableBarcodeInput(modal, false);
|
||||||
|
inventreePut(
|
||||||
|
'/api/barcode/link/',
|
||||||
|
{
|
||||||
|
barcode: barcode,
|
||||||
|
stockitem: stockitem,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
success: function(response, status) {
|
||||||
|
|
||||||
|
enableBarcodeInput(modal, true);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove barcode association from a device.
|
||||||
|
*/
|
||||||
|
function unlinkBarcode(stockitem) {
|
||||||
|
|
||||||
|
var html = `<b>{% trans "Unlink Barcode" %}</b><br>`;
|
||||||
|
|
||||||
|
html += "{% trans 'This will remove the association between this stock item and the barcode' %}";
|
||||||
|
|
||||||
|
showQuestionDialog(
|
||||||
|
"{% trans 'Unlink Barcode' %}",
|
||||||
|
html,
|
||||||
|
{
|
||||||
|
accept_text: "{% trans 'Unlink' %}",
|
||||||
|
accept: function() {
|
||||||
|
inventreePut(
|
||||||
|
`/api/stock/${stockitem}/`,
|
||||||
|
{
|
||||||
|
// Clear the UID field
|
||||||
|
uid: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'PATCH',
|
||||||
|
success: function(response, status) {
|
||||||
|
location.reload();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Display dialog to check multiple stock items in to a stock location.
|
||||||
|
*/
|
||||||
|
function barcodeCheckIn(location_id, options={}) {
|
||||||
|
|
||||||
|
var modal = '#modal-form';
|
||||||
|
|
||||||
|
// List of items we are going to checkin
|
||||||
|
var items = [];
|
||||||
|
|
||||||
|
|
||||||
|
function reloadTable() {
|
||||||
|
|
||||||
|
modalEnable(modal, false);
|
||||||
|
|
||||||
|
// Remove click listeners
|
||||||
|
$(modal + ' .button-item-remove').unbind('click');
|
||||||
|
|
||||||
|
var table = $(modal + ' #items-table-div');
|
||||||
|
|
||||||
|
var html = `
|
||||||
|
<table class='table table-condensed table-striped' id='items-table'>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Part" %}</th>
|
||||||
|
<th>{% trans "Location" %}</th>
|
||||||
|
<th>{% trans "Quantity" %}</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>`;
|
||||||
|
|
||||||
|
items.forEach(function(item) {
|
||||||
|
html += `
|
||||||
|
<tr pk='${item.pk}'>
|
||||||
|
<td>${imageHoverIcon(item.part_detail.thumbnail)} ${item.part_detail.name}</td>
|
||||||
|
<td>${item.location_detail.name}</td>
|
||||||
|
<td>${item.quantity}</td>
|
||||||
|
<td>${makeIconButton('fa-times-circle icon-red', 'button-item-remove', item.pk, '{% trans "Remove stock item" %}')}</td>
|
||||||
|
</tr>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
html += `
|
||||||
|
</tbody>
|
||||||
|
</table>`;
|
||||||
|
|
||||||
|
table.html(html);
|
||||||
|
|
||||||
|
modalEnable(modal, items.length > 0);
|
||||||
|
|
||||||
|
$(modal + ' #barcode').focus();
|
||||||
|
|
||||||
|
$(modal + ' .button-item-remove').unbind('click').on('mouseup', function() {
|
||||||
|
var pk = $(this).attr('pk');
|
||||||
|
|
||||||
|
var match = false;
|
||||||
|
|
||||||
|
for (var ii = 0; ii < items.length; ii++) {
|
||||||
|
if (pk.toString() == items[ii].pk.toString()) {
|
||||||
|
items.splice(ii, 1);
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
reloadTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var table = `<div class='container' id='items-table-div' style='width: 80%; float: left;'></div>`;
|
||||||
|
|
||||||
|
// Extra form fields
|
||||||
|
var extra = `
|
||||||
|
<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(
|
||||||
|
"{% trans "Check Stock Items into Location" %}",
|
||||||
|
{
|
||||||
|
headerContent: table,
|
||||||
|
preShow: function() {
|
||||||
|
modalSetSubmitText(modal, '{% trans "Check In" %}');
|
||||||
|
modalEnable(modal, false);
|
||||||
|
reloadTable();
|
||||||
|
},
|
||||||
|
onShow: function() {
|
||||||
|
},
|
||||||
|
extraFields: extra,
|
||||||
|
onSubmit: function() {
|
||||||
|
|
||||||
|
|
||||||
|
// Called when the 'check-in' button is pressed
|
||||||
|
|
||||||
|
var data = {location: location_id};
|
||||||
|
|
||||||
|
// Extract 'notes' field
|
||||||
|
data.notes = $(modal + ' #notes').val();
|
||||||
|
|
||||||
|
var entries = [];
|
||||||
|
|
||||||
|
items.forEach(function(item) {
|
||||||
|
entries.push({
|
||||||
|
pk: item.pk,
|
||||||
|
quantity: item.quantity,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
data.items = entries;
|
||||||
|
|
||||||
|
inventreePut(
|
||||||
|
'{% url 'api-stock-transfer' %}',
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
success: function(response, status) {
|
||||||
|
// Hide the modal
|
||||||
|
$(modal).modal('hide');
|
||||||
|
if (status == 'success' && 'success' in response) {
|
||||||
|
|
||||||
|
showAlertOrCache('alert-success', response.success, true);
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
showAlertOrCache('alert-success', 'Error transferring stock', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onScan: function(barcode) {
|
||||||
|
enableBarcodeInput(modal, false);
|
||||||
|
inventreePut(
|
||||||
|
'/api/barcode/',
|
||||||
|
{
|
||||||
|
barcode: barcode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
error: function() {
|
||||||
|
enableBarcodeInput(modal, true);
|
||||||
|
showBarcodeMessage(modal, '{% trans "Server error" %}');
|
||||||
|
},
|
||||||
|
success: function(response, status) {
|
||||||
|
|
||||||
|
enableBarcodeInput(modal, true);
|
||||||
|
|
||||||
|
if (status == 'success') {
|
||||||
|
if ('stockitem' in response) {
|
||||||
|
stockitem = response.stockitem;
|
||||||
|
|
||||||
|
var duplicate = false;
|
||||||
|
|
||||||
|
items.forEach(function(item) {
|
||||||
|
if (item.pk == stockitem.pk) {
|
||||||
|
duplicate = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (duplicate) {
|
||||||
|
showBarcodeMessage(modal, "{% trans "Stock Item already scanned" %}", "warning");
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (stockitem.location == location_id) {
|
||||||
|
showBarcodeMessage(modal, "{% trans "Stock Item already in this location" %}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this stock item to the list
|
||||||
|
items.push(stockitem);
|
||||||
|
|
||||||
|
showBarcodeMessage(modal, "{% trans "Added stock item" %}", "success");
|
||||||
|
|
||||||
|
reloadTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Barcode does not match a stock item
|
||||||
|
showBarcodeMessage(modal, "{% trans "Barcode does not match Stock Item" %}", "warning");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showInvalidResponseError(modal, response, status);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
@ -28,6 +28,11 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
{% include "search_form.html" %}
|
{% include "search_form.html" %}
|
||||||
|
<li class ='navbar-barcode-li nav navbar-nav'>
|
||||||
|
<button id='barcode-scan' class='btn btn-default' title='{% trans "Scan Barcode" %}'>
|
||||||
|
<span class='fas fa-qrcode'></span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
<li class='dropdown'>
|
<li class='dropdown'>
|
||||||
<a class='dropdown-toggle' data-toggle='dropdown' href="#"><span class="fas fa-user"></span> <b>{{ user.get_username }}</b></a>
|
<a class='dropdown-toggle' data-toggle='dropdown' href="#"><span class="fas fa-user"></span> <b>{{ user.get_username }}</b></a>
|
||||||
<ul class='dropdown-menu'>
|
<ul class='dropdown-menu'>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class='container' style='width: 80%;'>
|
<div class='container' style='width: 80%;'>
|
||||||
{% if qr_data %}
|
{% if qr_data %}
|
||||||
<div class='qr-container'>
|
<div class='qr-container'>
|
||||||
<img class='qr-code' src="{% qr_url_from_text qr_data size='m' image_format='png' error_correction='q' %}" alt="QR Code">
|
<img class='qr-code' src="{% qr_url_from_text qr_data size='m' image_format='png' error_correction='m' %}" alt="QR Code">
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<b>Error:</b><br>
|
<b>Error:</b><br>
|
||||||
|
@ -5,5 +5,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" name='search' class="form-control" placeholder="{% trans 'Search' %}"{% if query_text %} value="{{ query }}"{% endif %}>
|
<input type="text" name='search' class="form-control" placeholder="{% trans 'Search' %}"{% if query_text %} value="{{ query }}"{% endif %}>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" id='search-submit' class="btn btn-default"><span class='fas fa-search'></span></button>
|
<button type="submit" id='search-submit' class="btn btn-default" title='{% trans "Search" %}'>
|
||||||
|
<span class='fas fa-search'></span>
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
4
Makefile
4
Makefile
@ -51,12 +51,12 @@ style:
|
|||||||
# Run unit tests
|
# Run unit tests
|
||||||
test:
|
test:
|
||||||
cd InvenTree && python3 manage.py check
|
cd InvenTree && python3 manage.py check
|
||||||
cd InvenTree && python3 manage.py test build common company order part report stock InvenTree
|
cd InvenTree && python3 manage.py test barcode build common company order part report stock InvenTree
|
||||||
|
|
||||||
# Run code coverage
|
# Run code coverage
|
||||||
coverage:
|
coverage:
|
||||||
cd InvenTree && python3 manage.py check
|
cd InvenTree && python3 manage.py check
|
||||||
coverage run InvenTree/manage.py test build common company order part report stock InvenTree
|
coverage run InvenTree/manage.py test barcode build common company order part report stock InvenTree
|
||||||
coverage html
|
coverage html
|
||||||
|
|
||||||
# Install packages required to generate code docs
|
# Install packages required to generate code docs
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
ignore =
|
ignore =
|
||||||
# - W293 - blank lines contain whitespace
|
# - W293 - blank lines contain whitespace
|
||||||
W293,
|
W293,
|
||||||
|
W605,
|
||||||
# - E501 - line too long (82 characters)
|
# - E501 - line too long (82 characters)
|
||||||
E501, E722,
|
E501, E722,
|
||||||
# - C901 - function is too complex
|
# - C901 - function is too complex
|
||||||
|
Loading…
Reference in New Issue
Block a user