mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Adds an endpoint for calling the plugin code to "locate" something
This commit is contained in:
parent
9b7c26ec9c
commit
57f3efe758
@ -13,13 +13,18 @@ from django_filters.rest_framework import DjangoFilterBackend
|
|||||||
from rest_framework import filters
|
from rest_framework import filters
|
||||||
|
|
||||||
from rest_framework import permissions
|
from rest_framework import permissions
|
||||||
|
from rest_framework.exceptions import ParseError, NotFound
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from InvenTree.tasks import offload_task
|
||||||
|
|
||||||
from .views import AjaxView
|
from .views import AjaxView
|
||||||
from .version import inventreeVersion, inventreeApiVersion, inventreeInstanceName
|
from .version import inventreeVersion, inventreeApiVersion, inventreeInstanceName
|
||||||
from .status import is_worker_running
|
from .status import is_worker_running
|
||||||
|
|
||||||
|
from stock.models import StockItem, StockLocation
|
||||||
|
|
||||||
from plugin import registry
|
from plugin import registry
|
||||||
|
|
||||||
|
|
||||||
@ -114,7 +119,75 @@ class ActionPluginView(APIView):
|
|||||||
return Response(plugin.get_response())
|
return Response(plugin.get_response())
|
||||||
|
|
||||||
# If we got to here, no matching action was found
|
# If we got to here, no matching action was found
|
||||||
return Response({
|
raise NotFound({
|
||||||
'error': _("No matching action found"),
|
'error': _("No matching action found"),
|
||||||
"action": action,
|
"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()
|
||||||
|
@ -45,7 +45,7 @@ from .views import DynamicJsView
|
|||||||
from .views import NotificationsView
|
from .views import NotificationsView
|
||||||
|
|
||||||
from .api import InfoView, NotFoundView
|
from .api import InfoView, NotFoundView
|
||||||
from .api import ActionPluginView
|
from .api import ActionPluginView, LocatePluginView
|
||||||
|
|
||||||
from users.api import user_urls
|
from users.api import user_urls
|
||||||
|
|
||||||
@ -75,6 +75,7 @@ apipatterns += [
|
|||||||
|
|
||||||
# Plugin endpoints
|
# Plugin endpoints
|
||||||
re_path(r'^action/', ActionPluginView.as_view(), name='api-action-plugin'),
|
re_path(r'^action/', ActionPluginView.as_view(), name='api-action-plugin'),
|
||||||
|
re_path(r'^locate/', LocatePluginView.as_view(), name='api-locate-plugin'),
|
||||||
|
|
||||||
# Webhook enpoint
|
# Webhook enpoint
|
||||||
path('', include(common_api_urls)),
|
path('', include(common_api_urls)),
|
||||||
|
@ -473,6 +473,8 @@ class LocateMixin:
|
|||||||
Note: A custom implemenation could always change this behaviour
|
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
|
from stock.models import StockItem
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -482,6 +484,7 @@ class LocateMixin:
|
|||||||
self.locate_stock_location(item.location.pk)
|
self.locate_stock_location(item.location.pk)
|
||||||
|
|
||||||
except StockItem.DoesNotExist:
|
except StockItem.DoesNotExist:
|
||||||
|
logger.warning("LocateMixin: StockItem pk={item_pk} not found")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def locate_stock_location(self, location_pk):
|
def locate_stock_location(self, location_pk):
|
||||||
|
@ -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) {
|
function itemAdjust(action) {
|
||||||
|
|
||||||
inventreeGet(
|
inventreeGet(
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
/* exported
|
/* exported
|
||||||
installPlugin,
|
installPlugin,
|
||||||
|
locateItemOrLocation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function installPlugin() {
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user