From 83dd068fecfeddd809e821c4e6bc8175c14bea2a Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 4 May 2018 23:43:41 +1000 Subject: [PATCH 1/6] Initial commit for api.js - GET JSON call with console output - Provide filters --- InvenTree/static/script/inventree/api.js | 21 +++++++++++++++++++ InvenTree/stock/templates/stock/location.html | 4 ++++ 2 files changed, 25 insertions(+) create mode 100644 InvenTree/static/script/inventree/api.js diff --git a/InvenTree/static/script/inventree/api.js b/InvenTree/static/script/inventree/api.js new file mode 100644 index 0000000000..87157bf674 --- /dev/null +++ b/InvenTree/static/script/inventree/api.js @@ -0,0 +1,21 @@ +function inventreeGet(url, filters={}) { + $.ajax({ + url: url, + type: 'get', + data: filters, + dataType: 'json', + success: function(response) { + console.log('Success GET data at ' + url); + return response; + }, + error: function(xhr, ajaxOptions, thrownError) { + console.error('Error on GET at ' + url); + console.error(thrownError); + return {}; + } + }) +} + +function getParts(filters={}) { + return inventreeGet('/api/part/', filters); +} \ No newline at end of file diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html index 1ef14f2d82..971fe65f84 100644 --- a/InvenTree/stock/templates/stock/location.html +++ b/InvenTree/stock/templates/stock/location.html @@ -59,6 +59,8 @@ {% endblock %} {% block js_load %} +{{ block.super }} + {% endblock %} {% block js_ready %} @@ -206,4 +208,6 @@ url: "{% url 'api-stock-list' %}", }); + getParts(); + {% endblock %} From 7e4b0630b6be97ec72447607fb8a4cb0b2704416 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 4 May 2018 23:54:57 +1000 Subject: [PATCH 2/6] Part category API --- InvenTree/part/api.py | 39 ++++++++++++++++++- InvenTree/part/serializers.py | 5 ++- InvenTree/static/script/inventree/api.js | 6 +++ InvenTree/stock/templates/stock/location.html | 2 + 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 0510df69d2..2ae506ae94 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -5,13 +5,14 @@ from django_filters.rest_framework import DjangoFilterBackend from rest_framework import filters from rest_framework import generics, permissions -from django.conf.urls import url +from django.conf.urls import url, include from .models import Part, PartCategory, BomItem from .models import SupplierPart from .serializers import PartSerializer, BomItemSerializer from .serializers import SupplierPartSerializer +from .serializers import CategorySerializer from InvenTree.views import TreeSerializer @@ -21,6 +22,36 @@ class PartCategoryTree(TreeSerializer): model = PartCategory +class CategoryList(generics.ListCreateAPIView): + queryset = PartCategory.objects.all() + serializer_class = CategorySerializer + + permission_classes = [ + permissions.IsAuthenticatedOrReadOnly, + ] + + filter_backends = [ + DjangoFilterBackend, + #filters.SearchFilter, + filters.OrderingFilter, + ] + + filter_fields = [ + 'parent', + ] + + ordering_fields = [ + 'name', + ] + + ordering = 'name' + + search_fields = [ + 'name', + 'description', + ] + + class PartList(generics.ListCreateAPIView): queryset = Part.objects.all() @@ -93,11 +124,17 @@ class SupplierPartList(generics.ListAPIView): 'supplier' ] +cat_api_urls = [ + + url(r'^$', CategoryList.as_view(), name='api-part-category-list'), +] part_api_urls = [ url(r'^tree/?', PartCategoryTree.as_view(), name='api-part-tree'), + url(r'^category/', include(cat_api_urls)), + url(r'^supplier/?', SupplierPartList.as_view(), name='api-part-supplier-list'), url(r'^bom/?', BomList.as_view(), name='api-bom-list'), url(r'^.*$', PartList.as_view(), name='api-part-list'), diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index c942d0a212..061bf06145 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -5,7 +5,7 @@ from .models import SupplierPart from company.serializers import CompanyBriefSerializer -class CategoryBriefSerializer(serializers.ModelSerializer): +class CategorySerializer(serializers.ModelSerializer): url = serializers.CharField(source='get_absolute_url', read_only=True) @@ -17,6 +17,7 @@ class CategoryBriefSerializer(serializers.ModelSerializer): 'description', 'pathstring', 'url', + 'parent', ] @@ -40,7 +41,7 @@ class PartSerializer(serializers.ModelSerializer): """ url = serializers.CharField(source='get_absolute_url', read_only=True) - category = CategoryBriefSerializer(many=False, read_only=True) + category = CategorySerializer(many=False, read_only=True) class Meta: model = Part diff --git a/InvenTree/static/script/inventree/api.js b/InvenTree/static/script/inventree/api.js index 87157bf674..6f85291403 100644 --- a/InvenTree/static/script/inventree/api.js +++ b/InvenTree/static/script/inventree/api.js @@ -16,6 +16,12 @@ function inventreeGet(url, filters={}) { }) } +// Return list of parts with optional filters function getParts(filters={}) { return inventreeGet('/api/part/', filters); +} + +// Return list of part categories with optional filters +function getPartCategories(filters={}) { + return inventreeGet('/api/part/category/', filters); } \ No newline at end of file diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html index 971fe65f84..f777e070c0 100644 --- a/InvenTree/stock/templates/stock/location.html +++ b/InvenTree/stock/templates/stock/location.html @@ -210,4 +210,6 @@ getParts(); + getPartCategories({parent: 2}); + {% endblock %} From ee347c61652add845a350877b5bde399963ada78 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 5 May 2018 00:00:48 +1000 Subject: [PATCH 3/6] Further API calls - getStock - getStockLocation - getCompanies --- InvenTree/static/script/inventree/api.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/InvenTree/static/script/inventree/api.js b/InvenTree/static/script/inventree/api.js index 6f85291403..536430d4c8 100644 --- a/InvenTree/static/script/inventree/api.js +++ b/InvenTree/static/script/inventree/api.js @@ -11,7 +11,7 @@ function inventreeGet(url, filters={}) { error: function(xhr, ajaxOptions, thrownError) { console.error('Error on GET at ' + url); console.error(thrownError); - return {}; + return {error: thrownError}; } }) } @@ -24,4 +24,16 @@ function getParts(filters={}) { // Return list of part categories with optional filters function getPartCategories(filters={}) { return inventreeGet('/api/part/category/', filters); +} + +function getStock(filters={}) { + return inventreeGet('/api/stock/', filters); +} + +function getStockLocations(filters={}) { + return inventreeGet('/api/stock/location/', filters) +} + +function getCompanies(filters={}) { + return inventreeGet('/api/company/', filters); } \ No newline at end of file From 41e031d4b41d918c5fa8f4a155ed81170dcd7eb7 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 5 May 2018 00:51:17 +1000 Subject: [PATCH 4/6] Draft API endpoint RUD class - RUD = Retrieve / Update / Destroy - When issuing an Update command, the validity is checked but the model object is only saved if the POST data has "_is_final": true --- InvenTree/InvenTree/serializers.py | 17 +++++++++++++++++ InvenTree/InvenTree/settings.py | 3 ++- InvenTree/part/api.py | 14 +++++++++++++- InvenTree/stock/api.py | 4 ++-- InvenTree/stock/serializers.py | 2 +- 5 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 InvenTree/InvenTree/serializers.py diff --git a/InvenTree/InvenTree/serializers.py b/InvenTree/InvenTree/serializers.py new file mode 100644 index 0000000000..36daafad1d --- /dev/null +++ b/InvenTree/InvenTree/serializers.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from rest_framework import serializers +from rest_framework import generics +from rest_framework import mixins + +class DraftRUDView(generics.RetrieveAPIView, generics.UpdateAPIView, generics.DestroyAPIView): + + def perform_update(self, serializer): + + ctx_data = serializer._context['request'].data + + if ctx_data.get('_is_final', False) in [True, u'true', u'True', 1]: + super(generics.UpdateAPIView, self).perform_update(serializer) + else: + pass \ No newline at end of file diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 14a4496f12..a8df6c6d5e 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -92,7 +92,8 @@ TEMPLATES = [ ] REST_FRAMEWORK = { - 'EXCEPTION_HANDLER': 'InvenTree.utils.api_exception_handler', + 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler' + # 'EXCEPTION_HANDLER': 'InvenTree.utils.api_exception_handler', # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # 'PAGE_SIZE': 50, } diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 2ae506ae94..b67b1dd8fb 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django_filters.rest_framework import DjangoFilterBackend from rest_framework import filters -from rest_framework import generics, permissions +from rest_framework import generics, permissions, mixins from django.conf.urls import url, include @@ -15,6 +15,7 @@ from .serializers import SupplierPartSerializer from .serializers import CategorySerializer from InvenTree.views import TreeSerializer +from InvenTree.serializers import DraftRUDView class PartCategoryTree(TreeSerializer): @@ -52,6 +53,14 @@ class CategoryList(generics.ListCreateAPIView): ] +class PartDetail(DraftRUDView): + queryset = Part.objects.all() + serializer_class = PartSerializer + + permission_classes = [ + permissions.IsAuthenticatedOrReadOnly, + ] + class PartList(generics.ListCreateAPIView): queryset = Part.objects.all() @@ -137,5 +146,8 @@ part_api_urls = [ url(r'^supplier/?', SupplierPartList.as_view(), name='api-part-supplier-list'), url(r'^bom/?', BomList.as_view(), name='api-bom-list'), + + url(r'^(?P\d+)/', PartDetail.as_view(), name='api-part-detail'), + url(r'^.*$', PartList.as_view(), name='api-part-list'), ] diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index b754c8fc7a..cc222ba96a 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -11,14 +11,14 @@ from .serializers import StockItemSerializer, StockQuantitySerializer from .serializers import LocationSerializer from InvenTree.views import TreeSerializer - +from InvenTree.serializers import DraftRUDView class StockCategoryTree(TreeSerializer): title = 'Stock' model = StockLocation -class StockDetail(generics.RetrieveUpdateDestroyAPIView): +class StockDetail(DraftRUDView): """ get: diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py index eb3217b524..54d4dbf883 100644 --- a/InvenTree/stock/serializers.py +++ b/InvenTree/stock/serializers.py @@ -56,7 +56,7 @@ class StockItemSerializer(serializers.ModelSerializer): 'stocktake_date', 'stocktake_user', 'updated', - 'quantity', + #'quantity', ] From 11e7a34aa272a5a1f8968c67549df7c10efe1203 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 5 May 2018 01:26:58 +1000 Subject: [PATCH 5/6] AJAX function to update a model endpoint - Grabs the CSRF token cookie (required!) - If final mode, adds '_is_final' parameter to request --- InvenTree/static/script/inventree/api.js | 51 +++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/InvenTree/static/script/inventree/api.js b/InvenTree/static/script/inventree/api.js index 536430d4c8..ea7ddb00c9 100644 --- a/InvenTree/static/script/inventree/api.js +++ b/InvenTree/static/script/inventree/api.js @@ -1,7 +1,26 @@ +var jQuery = window.$; + +// using jQuery +function getCookie(name) { + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; +} + function inventreeGet(url, filters={}) { $.ajax({ url: url, - type: 'get', + type: 'GET', data: filters, dataType: 'json', success: function(response) { @@ -16,6 +35,36 @@ function inventreeGet(url, filters={}) { }) } +function inventreeUpdate(url, data, final=false) { + if (final) { + data["_is_final"] = true; + } + + // Middleware token required for data update + //var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val(); + var csrftoken = getCookie('csrftoken'); + + $.ajax({ + beforeSend: function(xhr, settings) { + xhr.setRequestHeader('X-CSRFToken', csrftoken); + }, + url: url, + type: 'put', + data: data, + dataType: 'json', + success: function(response, status) { + response['_status_code'] = status; + console.log('UPDATE object to ' + url + ' - result = ' + status); + return response; + }, + error: function(xhr, ajaxOptions, thrownError) { + console.error('Error on UPDATE to ' + url); + console.error(thrownError); + return {error: thrownError}; + } + }) +} + // Return list of parts with optional filters function getParts(filters={}) { return inventreeGet('/api/part/', filters); From 8e7e784934156b7a697bbe7e1c0abffafa49a944 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 5 May 2018 01:27:15 +1000 Subject: [PATCH 6/6] Remove test code --- InvenTree/stock/templates/stock/location.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html index f777e070c0..87e83005c5 100644 --- a/InvenTree/stock/templates/stock/location.html +++ b/InvenTree/stock/templates/stock/location.html @@ -208,8 +208,4 @@ url: "{% url 'api-stock-list' %}", }); - getParts(); - - getPartCategories({parent: 2}); - {% endblock %}