mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
f2b0717d10
this really bothers me for some reason - nothing technical
241 lines
7.3 KiB
Python
241 lines
7.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from django.urls import reverse
|
|
from django.conf.urls import url
|
|
from django.utils.translation import ugettext_lazy 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 barcodes.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('stock-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'),
|
|
]
|