Adds an endpoint for calling the plugin code to "locate" something

This commit is contained in:
Oliver Walters 2022-05-09 23:42:28 +10:00
parent 9b7c26ec9c
commit 57f3efe758
5 changed files with 135 additions and 2 deletions

View File

@ -13,13 +13,18 @@ from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters
from rest_framework import permissions
from rest_framework.exceptions import ParseError, NotFound
from rest_framework.response import Response
from rest_framework.views import APIView
from InvenTree.tasks import offload_task
from .views import AjaxView
from .version import inventreeVersion, inventreeApiVersion, inventreeInstanceName
from .status import is_worker_running
from stock.models import StockItem, StockLocation
from plugin import registry
@ -114,7 +119,75 @@ class ActionPluginView(APIView):
return Response(plugin.get_response())
# If we got to here, no matching action was found
return Response({
raise NotFound({
'error': _("No matching action found"),
"action": action,
})
class LocatePluginView(APIView):
"""
Endpoint for using a custom plugin to identify or 'locate' a stock item or location
"""
permission_classes = [
permissions.IsAuthenticated,
]
def post(self, request, *args, **kwargs):
# Which plugin to we wish to use?
plugin = request.data.get('plugin', None)
if not plugin:
raise ParseError("'plugin' field must be supplied")
# Check that the plugin exists, and supports the 'locate' mixin
plugins = registry.with_mixin('locate')
if plugin not in [p.slug for p in plugins]:
raise ParseError(f"Plugin '{plugin}' is not installed, or does not support the location mixin")
# StockItem to identify
item_pk= request.data.get('item', None)
# StockLocation to identify
location_pk = request.data.get('location', None)
if not item_pk and not location_pk:
raise ParseError("Must supply either 'item' or 'location' parameter")
data = {
"success": "Identification plugin activated",
"plugin": plugin,
}
# StockItem takes priority
if item_pk:
try:
item = StockItem.objects.get(pk=item_pk)
offload_task('plugin.registry.call_function', plugin, 'locate_stock_item', item_pk)
data['item'] = item_pk
return Response(data)
except StockItem.DoesNotExist:
raise NotFound("StockItem matching PK '{item}' not found")
elif location_pk:
try:
location = StockItem.objects.get(pk=location_pk)
offload_task('plugin.registry.call_function', plugin, 'locate_stock_location', location_pk)
data['location'] = location_pk
return Response(data)
except StockLocation.DoesNotExist:
raise NotFound("StockLocation matching PK {'location'} not found")
else:
raise NotFound()

View File

@ -45,7 +45,7 @@ from .views import DynamicJsView
from .views import NotificationsView
from .api import InfoView, NotFoundView
from .api import ActionPluginView
from .api import ActionPluginView, LocatePluginView
from users.api import user_urls
@ -75,6 +75,7 @@ apipatterns += [
# Plugin endpoints
re_path(r'^action/', ActionPluginView.as_view(), name='api-action-plugin'),
re_path(r'^locate/', LocatePluginView.as_view(), name='api-locate-plugin'),
# Webhook enpoint
path('', include(common_api_urls)),

View File

@ -473,6 +473,8 @@ class LocateMixin:
Note: A custom implemenation could always change this behaviour
"""
logger.info(f"LocateMixin: Attempting to locate StockItem pk={item_pk}")
from stock.models import StockItem
try:
@ -482,6 +484,7 @@ class LocateMixin:
self.locate_stock_location(item.location.pk)
except StockItem.DoesNotExist:
logger.warning("LocateMixin: StockItem pk={item_pk} not found")
pass
def locate_stock_location(self, location_pk):

View File

@ -520,6 +520,14 @@ $("#barcode-scan-into-location").click(function() {
});
});
{% if plugins_enabled %}
$('#locate-item-button').click(function() {
locateItemOrLocation({
item: {{ item.pk }},
});
});
{% endif %}
function itemAdjust(action) {
inventreeGet(

View File

@ -7,6 +7,7 @@
/* exported
installPlugin,
locateItemOrLocation
*/
function installPlugin() {
@ -24,3 +25,50 @@ function installPlugin() {
}
});
}
function locateItemOrLocation(options={}) {
if (!options.item && !options.location) {
console.error("locateItemOrLocation: Either 'item' or 'location' must be provided!");
return;
}
function performLocate(plugin) {
inventreePut(
'{% url "api-locate-plugin" %}',
{
plugin: plugin,
item: options.item,
location: options.location,
},
{
method: 'POST',
},
);
}
// Request the list of available 'locate' plugins
inventreeGet(
'{% url "api-plugin-list" %}',
{
mixin: 'locate',
},
{
success: function(plugins) {
// No 'locate' plugins are available!
if (plugins.length == 0) {
console.warn("No 'locate' plugins are available");
} else if (plugins.length == 1) {
// Only a single locate plugin is available
performLocate(plugins[0].key);
} else {
// More than 1 location plugin available
// Select from a list
}
}
},
);
}