From 8a6932e993042f334e3106bfec0d42c7c3318cc3 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 08:39:16 +1000 Subject: [PATCH 01/15] Improve QR code display - Pretty little glyphicon button! --- InvenTree/part/templates/part/detail.html | 10 ---- InvenTree/part/templates/part/part_base.html | 46 +++++++++++++------ InvenTree/static/css/inventree.css | 11 +++++ InvenTree/stock/templates/stock/item.html | 10 ++-- InvenTree/stock/templates/stock/location.html | 9 ++-- 5 files changed, 56 insertions(+), 30 deletions(-) diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html index 789a40b166..a99839d34a 100644 --- a/InvenTree/part/templates/part/detail.html +++ b/InvenTree/part/templates/part/detail.html @@ -23,7 +23,6 @@ {% else %}
  • Activate
  • {% endif %} -
  • Show QR Code
  • @@ -126,15 +125,6 @@ {% block js_ready %} {{ block.super }} - - $("#show-qr-code").click(function() { - launchModalForm( - "{% url 'part-qr' part.id %}", - { - no_post: true, - } - ); - }); $("#duplicate-part").click(function() { launchModalForm( diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index de1ab2c6e0..fd7dbe7695 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -21,20 +21,31 @@ {% endif %}/>
    -

    {{ part.name }}{% if part.active == False %} - INACTIVE{% endif %}

    +

    + {{ part.name }} +

    {{ part.description }}

    - {% if part.IPN %} - - IPN - {{ part.IPN }} - - {% endif %} - {% if part.URL %} - - URL - {{ part.URL }} - - {% endif %} +

    +

    + +
    +

    + + {% if part.IPN %} + + + + + {% endif %} + {% if part.URL %} + + + + + {% endif %} + + +
    IPN{{ part.IPN }}
    URL{{ part.URL }}
    @@ -82,6 +93,15 @@ {% block js_ready %} {{ block.super }} + $("#show-qr-code").click(function() { + launchModalForm( + "{% url 'part-qr' part.id %}", + { + no_post: true, + } + ); + }); + $("#part-thumb").click(function() { launchModalForm( "{% url 'part-image' part.id %}", diff --git a/InvenTree/static/css/inventree.css b/InvenTree/static/css/inventree.css index 98a4e04de4..e2978f19b4 100644 --- a/InvenTree/static/css/inventree.css +++ b/InvenTree/static/css/inventree.css @@ -2,6 +2,17 @@ float: left; } +.glyphicon { + font-size: 20px; +} + +.btn-glyph { + padding-left: 6px; + padding-right: 6px; + padding-top: 3px; + padding-bottom: 2px; +} + .badge { float: right; background-color: #777; diff --git a/InvenTree/stock/templates/stock/item.html b/InvenTree/stock/templates/stock/item.html index ca38ea29ef..87fff9fbb4 100644 --- a/InvenTree/stock/templates/stock/item.html +++ b/InvenTree/stock/templates/stock/item.html @@ -6,6 +6,11 @@

    Stock Item Details

    {{ item.quantity }} × {{ item.part.name }}

    +

    +

    + +
    +

    @@ -23,9 +28,6 @@
  • Stocktake
  • {% endif %}
  • Delete stock item
  • -
    -
  • Show QR code
  • -

    @@ -145,7 +147,7 @@ }); }); - $("#item-qr-code").click(function() { + $("#show-qr-code").click(function() { launchModalForm("{% url 'stock-item-qr' item.id %}", { no_post: true, diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html index 20769669a2..8a8f0356fe 100644 --- a/InvenTree/stock/templates/stock/location.html +++ b/InvenTree/stock/templates/stock/location.html @@ -7,6 +7,11 @@ {% if location %}

    {{ location.name }}

    {{ location.description }}

    +

    +

    + +
    +

    {% else %}

    Stock

    All stock items

    @@ -23,8 +28,6 @@ {% endif %} @@ -101,7 +104,7 @@ return false; }); - $('#location-qr-code').click(function() { + $('#show-qr-code').click(function() { launchModalForm("{% url 'stock-location-qr' location.id %}", { no_post: true, From 16edcc4bd96079677982c4275c2b7c322cae8344 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 08:42:01 +1000 Subject: [PATCH 02/15] Add 'star' button to Part --- InvenTree/part/templates/part/part_base.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index fd7dbe7695..4ee5665be7 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -28,6 +28,7 @@

    +

    @@ -102,6 +103,9 @@ ); }); + $('#toggle-starred').click(function() { + }); + $("#part-thumb").click(function() { launchModalForm( "{% url 'part-image' part.id %}", From 9919bebaa2f7154cd670eaf60562cdd5b8841788 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 08:46:23 +1000 Subject: [PATCH 03/15] Add PartStart model - Links parts to users --- InvenTree/part/migrations/0016_partstar.py | 24 ++++++++++++++++++++++ InvenTree/part/models.py | 13 ++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 InvenTree/part/migrations/0016_partstar.py diff --git a/InvenTree/part/migrations/0016_partstar.py b/InvenTree/part/migrations/0016_partstar.py new file mode 100644 index 0000000000..baa5c83d5b --- /dev/null +++ b/InvenTree/part/migrations/0016_partstar.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2 on 2019-05-04 22:45 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('part', '0015_partcategory_default_location'), + ] + + operations = [ + migrations.CreateModel( + name='PartStar', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_users', to='part.Part')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_parts', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index d406382856..f20e1decf4 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -18,6 +18,7 @@ from django.urls import reverse from django.db import models from django.core.validators import MinValueValidator +from django.contrib.auth.models import User from django.db.models.signals import pre_delete from django.dispatch import receiver @@ -427,6 +428,18 @@ class PartAttachment(models.Model): return os.path.basename(self.attachment.name) +class PartStar(models.Model): + """ A PartStar object creates a relationship between a User and a Part. + + It is used to designate a Part as 'starred' (or favourited) for a given User, + so that the user can track a list of their favourite parts. + """ + + part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='starred_users') + + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='starred_parts') + + class BomItem(models.Model): """ A BomItem links a part to its component items. A part can have a BOM (bill of materials) which defines From 919f04044b78706f6909a1d43f197e97e9d85c32 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 08:48:41 +1000 Subject: [PATCH 04/15] Enforce uniqueness constraint --- .../migrations/0017_auto_20190505_0848.py | 19 +++++++++++++++++++ InvenTree/part/models.py | 3 +++ 2 files changed, 22 insertions(+) create mode 100644 InvenTree/part/migrations/0017_auto_20190505_0848.py diff --git a/InvenTree/part/migrations/0017_auto_20190505_0848.py b/InvenTree/part/migrations/0017_auto_20190505_0848.py new file mode 100644 index 0000000000..90162132f9 --- /dev/null +++ b/InvenTree/part/migrations/0017_auto_20190505_0848.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2 on 2019-05-04 22:48 + +from django.conf import settings +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('part', '0016_partstar'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='partstar', + unique_together={('part', 'user')}, + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index f20e1decf4..06b79417d1 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -439,6 +439,9 @@ class PartStar(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='starred_parts') + class Meta: + unique_together = ['part', 'user'] + class BomItem(models.Model): """ A BomItem links a part to its component items. From 86b5b8d16e8ea61d7bc6cd5ffab1f493e86cf258 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 08:50:14 +1000 Subject: [PATCH 05/15] Add PartStar model to the admin interface --- InvenTree/part/admin.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index 56bf290739..11a5b2c28a 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -2,7 +2,7 @@ from django.contrib import admin from import_export.admin import ImportExportModelAdmin from .models import PartCategory, Part -from .models import PartAttachment +from .models import PartAttachment, PartStar from .models import SupplierPart from .models import BomItem @@ -22,6 +22,11 @@ class PartAttachmentAdmin(admin.ModelAdmin): list_display = ('part', 'attachment', 'comment') +class PartStarAdmin(admin.ModelAdmin): + + list_display = ('part', 'user') + + class BomItemAdmin(ImportExportModelAdmin): list_display = ('part', 'sub_part', 'quantity') @@ -42,5 +47,6 @@ class ParameterAdmin(admin.ModelAdmin): admin.site.register(Part, PartAdmin) admin.site.register(PartCategory, PartCategoryAdmin) admin.site.register(PartAttachment, PartAttachmentAdmin) +admin.site.register(PartStar, PartStarAdmin) admin.site.register(BomItem, BomItemAdmin) admin.site.register(SupplierPart, SupplierPartAdmin) From 70f1097ea0c7ad996d8ac0b2d7e7f4482186f792 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 09:05:44 +1000 Subject: [PATCH 06/15] API endpoint for accessing part star information --- InvenTree/part/api.py | 42 +++++++++++++++++++++++++++++++++-- InvenTree/part/serializers.py | 20 ++++++++++++++++- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 27a5b24894..d248edac98 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -12,12 +12,13 @@ from rest_framework import generics, permissions from django.db.models import Q from django.conf.urls import url, include -from .models import Part, PartCategory, BomItem +from .models import Part, PartCategory, BomItem, PartStar from .models import SupplierPart, SupplierPriceBreak from .serializers import PartSerializer, BomItemSerializer from .serializers import SupplierPartSerializer, SupplierPriceBreakSerializer from .serializers import CategorySerializer +from .serializers import PartStarSerializer from InvenTree.views import TreeSerializer @@ -150,8 +151,37 @@ class PartList(generics.ListCreateAPIView): ] +class PartStarList(generics.ListCreateAPIView): + """ API endpoint for accessing a list of PartStar objects. + + - GET: Return list of PartStar objects + - POST: Create a new PartStar object + """ + + queryset = PartStar.objects.all() + serializer_class = PartStarSerializer + + permission_classes = [ + permissions.IsAuthenticatedOrReadOnly, + ] + + filter_backends = [ + DjangoFilterBackend, + filters.SearchFilter + ] + + filter_fields = [ + 'part', + 'user', + ] + + search_fields = [ + 'partname' + ] + + class BomList(generics.ListCreateAPIView): - """ API endpoing for accessing a list of BomItem objects + """ API endpoint for accessing a list of BomItem objects. - GET: Return list of BomItem objects - POST: Create a new BomItem object @@ -267,12 +297,20 @@ supplier_part_api_urls = [ url(r'^.*$', SupplierPartList.as_view(), name='api-part-supplier-list'), ] +part_star_api_urls = [ + + # Catchall + url(r'^.*$', PartStarList.as_view(), name='api-part-star-list'), +] + part_api_urls = [ url(r'^tree/?', PartCategoryTree.as_view(), name='api-part-tree'), url(r'^category/', include(cat_api_urls)), url(r'^supplier/', include(supplier_part_api_urls)), + url(r'^star/', include(part_star_api_urls)), + url(r'^price-break/?', SupplierPriceBreakList.as_view(), name='api-part-supplier-price'), url(r'^(?P\d+)/', PartDetail.as_view(), name='api-part-detail'), diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index e5c23b730a..dff50f06d7 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -4,8 +4,10 @@ JSON serializers for Part app from rest_framework import serializers -from .models import Part, PartCategory, BomItem +from .models import Part, PartStar, PartAttachment from .models import SupplierPart, SupplierPriceBreak +from .models import PartCategory +from .models import BomItem from InvenTree.serializers import InvenTreeModelSerializer @@ -75,6 +77,22 @@ class PartSerializer(serializers.ModelSerializer): ] +class PartStarSerializer(InvenTreeModelSerializer): + """ Serializer for a PartStar object """ + + partname = serializers.CharField(source='part.name', read_only=True) + username = serializers.CharField(source='user.username', read_only=True) + + class Meta: + model = PartStar + fields = [ + 'part', + 'partname', + 'user', + 'username', + ] + + class BomItemSerializer(InvenTreeModelSerializer): """ Serializer for BomItem object """ From d245e58990568c10cbcdf980299eba1574ebc454 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 09:14:12 +1000 Subject: [PATCH 07/15] Only allow PartStar creation for the currently authenticated user --- InvenTree/part/api.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index d248edac98..b309284ba0 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -6,8 +6,10 @@ Provides a JSON API for the Part app 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.serializers import ValidationError from django.db.models import Q from django.conf.urls import url, include @@ -161,6 +163,16 @@ class PartStarList(generics.ListCreateAPIView): queryset = PartStar.objects.all() serializer_class = PartStarSerializer + def create(self, request, *args, **kwargs): + + # Ensure the 'user' field is the authenticated user + user_id = request.data['user'] + + if not str(user_id) == str(request.user.id): + raise ValidationError({'user': 'Parts can only be starred for the currently authenticated user'}) + + return super(generics.ListCreateAPIView, self).create(request, *args, **kwargs) + permission_classes = [ permissions.IsAuthenticatedOrReadOnly, ] From 3bd7c2855854f17e5329609e99b2420cc6e24fb7 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 10:36:48 +1000 Subject: [PATCH 08/15] Add API endpoint for viewing and deleting a PartStar entry --- InvenTree/part/api.py | 25 ++++++++++++++++++++----- InvenTree/part/serializers.py | 1 + 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index b309284ba0..447e283327 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -153,6 +153,13 @@ class PartList(generics.ListCreateAPIView): ] +class PartStarDetail(generics.RetrieveDestroyAPIView): + """ API endpoint for viewing or removing a PartStar object """ + + queryset = PartStar.objects.all() + serializer_class = PartStarSerializer + + class PartStarList(generics.ListCreateAPIView): """ API endpoint for accessing a list of PartStar objects. @@ -165,13 +172,20 @@ class PartStarList(generics.ListCreateAPIView): def create(self, request, *args, **kwargs): - # Ensure the 'user' field is the authenticated user - user_id = request.data['user'] + # Automatically add the user information + data = request.data.copy() + data['user'] = str(request.user.id) - if not str(user_id) == str(request.user.id): - raise ValidationError({'user': 'Parts can only be starred for the currently authenticated user'}) + serializer = self.get_serializer(data=data) - return super(generics.ListCreateAPIView, self).create(request, *args, **kwargs) + print(serializer) + print(data) + print(request.user) + + serializer.is_valid(raise_exception=True) + self.perform_create(serializer) + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) permission_classes = [ permissions.IsAuthenticatedOrReadOnly, @@ -310,6 +324,7 @@ supplier_part_api_urls = [ ] part_star_api_urls = [ + url(r'^(?P\d+)/?', PartStarDetail.as_view(), name='api-part-star-detail'), # Catchall url(r'^.*$', PartStarList.as_view(), name='api-part-star-list'), diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index dff50f06d7..48839311e4 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -86,6 +86,7 @@ class PartStarSerializer(InvenTreeModelSerializer): class Meta: model = PartStar fields = [ + 'pk', 'part', 'partname', 'user', From b2e935382bae714db225dc6c10d50537149d861a Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 10:47:57 +1000 Subject: [PATCH 09/15] Override user field when creating a new PartStar - Force it to be the currently logged-in user --- InvenTree/part/api.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 447e283327..0ff95e1879 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -7,6 +7,8 @@ from __future__ import unicode_literals from django_filters.rest_framework import DjangoFilterBackend +from rest_framework import status +from rest_framework.response import Response from rest_framework import filters from rest_framework import generics, permissions from rest_framework.serializers import ValidationError @@ -172,16 +174,12 @@ class PartStarList(generics.ListCreateAPIView): def create(self, request, *args, **kwargs): - # Automatically add the user information + # Override the user field (with the logged-in user) data = request.data.copy() data['user'] = str(request.user.id) serializer = self.get_serializer(data=data) - print(serializer) - print(data) - print(request.user) - serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) From bbd164407f30cf80ba16a62649afc87c01a98cc3 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 10:54:21 +1000 Subject: [PATCH 10/15] Display 'starred' status on the Part information page --- InvenTree/part/models.py | 9 +++++++++ InvenTree/part/templates/part/part_base.html | 4 +++- InvenTree/part/views.py | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 06b79417d1..e5497faa4c 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -247,6 +247,15 @@ class Part(models.Model): return total + def isStarredBy(self, user): + """ Return True if this part has been starred by a particular user """ + + try: + PartStar.objects.get(part=self, user=user) + return True + except PartStar.DoesNotExist: + return False + def need_to_restock(self): """ Return True if this part needs to be restocked (either by purchasing or building). diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index 4ee5665be7..7381332892 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -28,7 +28,9 @@

    - +

    diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 6ba810b67d..c97808888e 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -232,6 +232,10 @@ class PartDetail(DetailView): else: context['editing_enabled'] = 0 + part = self.get_object() + + context['starred'] = part.isStarredBy(self.request.user) + return context From d2d248c72e33c3101840d69699351ef69e61463c Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 10:56:59 +1000 Subject: [PATCH 11/15] QR button code is now a template --- InvenTree/part/templates/part/part_base.html | 2 +- InvenTree/stock/templates/stock/item.html | 2 +- InvenTree/stock/templates/stock/location.html | 2 +- InvenTree/templates/qr_button.html | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 InvenTree/templates/qr_button.html diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index 7381332892..0cac700857 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -27,7 +27,7 @@

    {{ part.description }}

    - + {% include "qr_button.html" %} diff --git a/InvenTree/stock/templates/stock/item.html b/InvenTree/stock/templates/stock/item.html index 87fff9fbb4..daea4ddf24 100644 --- a/InvenTree/stock/templates/stock/item.html +++ b/InvenTree/stock/templates/stock/item.html @@ -8,7 +8,7 @@

    {{ item.quantity }} × {{ item.part.name }}

    - + {% include "qr_button.html" %}

    diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html index 8a8f0356fe..fbb46b6e18 100644 --- a/InvenTree/stock/templates/stock/location.html +++ b/InvenTree/stock/templates/stock/location.html @@ -9,7 +9,7 @@

    {{ location.description }}

    - + {% include "qr_button.html" %}

    {% else %} diff --git a/InvenTree/templates/qr_button.html b/InvenTree/templates/qr_button.html new file mode 100644 index 0000000000..7aafd834bc --- /dev/null +++ b/InvenTree/templates/qr_button.html @@ -0,0 +1 @@ + \ No newline at end of file From fc5fd5e4774ffda41023ad3b96f9de1114f35d90 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 11:18:38 +1000 Subject: [PATCH 12/15] Toggle part star status using AJAX --- .../part/templates/part/part_app_base.html | 7 +++ InvenTree/part/templates/part/part_base.html | 8 +++ InvenTree/static/script/inventree/api.js | 6 +- InvenTree/static/script/inventree/part.js | 56 +++++++++++++++++++ 4 files changed, 72 insertions(+), 5 deletions(-) diff --git a/InvenTree/part/templates/part/part_app_base.html b/InvenTree/part/templates/part/part_app_base.html index 30c9b30b6d..8c034dfa6e 100644 --- a/InvenTree/part/templates/part/part_app_base.html +++ b/InvenTree/part/templates/part/part_app_base.html @@ -1,5 +1,7 @@ {% extends "base.html" %} +{% load static %} + {% block sidenav %}
    {% endblock %} @@ -14,6 +16,11 @@ {% endblock %} +{% block js_load %} +{{ block.super }} + +{% endblock %} + {% block js_ready %} {{ block.super }} loadTree("{% url 'api-part-tree' %}", diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index 0cac700857..44e2d04d7f 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -105,6 +105,14 @@ ); }); + $("#toggle-starred").click(function() { + toggleStar({ + part: {{ part.id }}, + user: {{ user.id }}, + button: '#part-star-icon' + }); + }); + $('#toggle-starred').click(function() { }); diff --git a/InvenTree/static/script/inventree/api.js b/InvenTree/static/script/inventree/api.js index 530e816f68..27feec8b79 100644 --- a/InvenTree/static/script/inventree/api.js +++ b/InvenTree/static/script/inventree/api.js @@ -43,9 +43,6 @@ function inventreeGet(url, filters={}, options={}) { } function inventreeUpdate(url, data={}, options={}) { - if ('final' in options && options.final) { - data["_is_final"] = true; - } var method = options.method || 'PUT'; @@ -63,8 +60,7 @@ function inventreeUpdate(url, data={}, options={}) { dataType: 'json', contentType: 'application/json', success: function(response, status) { - response['_status_code'] = status; - console.log('UPDATE object to ' + url + ' - result = ' + status); + console.log(method + ' - ' + url + ' : result = ' + status); if (options.success) { options.success(response, status); } diff --git a/InvenTree/static/script/inventree/part.js b/InvenTree/static/script/inventree/part.js index 053355113b..7682d5a6c1 100644 --- a/InvenTree/static/script/inventree/part.js +++ b/InvenTree/static/script/inventree/part.js @@ -16,4 +16,60 @@ function getPartList(filters={}, options={}) { function getBomList(filters={}, options={}) { return inventreeGet('/api/bom/', filters, options); +} + +function toggleStar(options) { + /* Toggle the 'starred' status of a part. + * Performs AJAX queries and updates the display on the button. + * + * options: + * - button: ID of the button (default = '#part-star-icon') + * - part: pk of the part object + * - user: pk of the user + */ + + var url = '/api/part/star/'; + + inventreeGet( + url, + { + part: options.part, + user: options.user, + }, + { + success: function(response) { + if (response.length == 0) { + // Zero length response = star does not exist + // So let's add one! + inventreeUpdate( + url, + { + part: options.part, + user: options.user, + }, + { + method: 'POST', + success: function(response, status) { + $(options.button).removeClass('glyphicon-star-empty').addClass('glyphicon-star'); + }, + } + ); + } else { + var pk = response[0].pk; + // There IS a star (delete it!) + inventreeUpdate( + url + pk + "/", + { + }, + { + method: 'DELETE', + success: function(response, status) { + $(options.button).removeClass('glyphicon-star').addClass('glyphicon-star-empty'); + }, + } + ); + } + }, + } + ); } \ No newline at end of file From aaff92ff9c429e80261c32ce0700fd208da0db0c Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 11:21:08 +1000 Subject: [PATCH 13/15] Make the star icon yellow --- InvenTree/part/templates/part/part_base.html | 2 +- InvenTree/static/css/inventree.css | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index 44e2d04d7f..5dc7add027 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -29,7 +29,7 @@
    {% include "qr_button.html" %}

    diff --git a/InvenTree/static/css/inventree.css b/InvenTree/static/css/inventree.css index e2978f19b4..3a9c1be184 100644 --- a/InvenTree/static/css/inventree.css +++ b/InvenTree/static/css/inventree.css @@ -6,6 +6,10 @@ font-size: 20px; } +.starred-part { + color: #ffcc00; +} + .btn-glyph { padding-left: 6px; padding-right: 6px; From c75d892fd7d521aaad913552854f95375553f074 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 11:41:42 +1000 Subject: [PATCH 14/15] Display collapsible list of starred-parts on the index page --- InvenTree/InvenTree/views.py | 6 +++++- InvenTree/templates/InvenTree/index.html | 7 +++++++ InvenTree/templates/InvenTree/parts_to_build.html | 1 + InvenTree/templates/InvenTree/parts_to_order.html | 1 + InvenTree/templates/InvenTree/starred_parts.html | 15 +++++++++++++++ 5 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 InvenTree/templates/InvenTree/starred_parts.html diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 15926715f6..68f6ebb4bd 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -328,11 +328,15 @@ class IndexView(TemplateView): def get_context_data(self, **kwargs): context = super(TemplateView, self).get_context_data(**kwargs) - + + context['starred'] = [star.part for star in self.request.user.starred_parts.all()] + # Generate a list of orderable parts which have stock below their minimum values + # TODO - Is there a less expensive way to get these from the database context['to_order'] = [part for part in Part.objects.filter(purchaseable=True) if part.need_to_restock()] # Generate a list of buildable parts which have stock below their minimum values + # TODO - Is there a less expensive way to get these from the database context['to_build'] = [part for part in Part.objects.filter(buildable=True) if part.need_to_restock()] return context diff --git a/InvenTree/templates/InvenTree/index.html b/InvenTree/templates/InvenTree/index.html index 91cac4c18c..12019ca7b0 100644 --- a/InvenTree/templates/InvenTree/index.html +++ b/InvenTree/templates/InvenTree/index.html @@ -3,6 +3,8 @@ {% block content %}

    InvenTree

    +{% include "InvenTree/starred_parts.html" with collapse_id="starred" %} + {% if to_order %} {% include "InvenTree/parts_to_order.html" with collapse_id="order" %} {% endif %} @@ -19,4 +21,9 @@ {% block js_ready %} {{ block.super }} + +$("#to-build-table").bootstrapTable(); +$("#to-order-table").bootstrapTable(); +$("#starred-parts-table").bootstrapTable(); + {% endblock %} \ No newline at end of file diff --git a/InvenTree/templates/InvenTree/parts_to_build.html b/InvenTree/templates/InvenTree/parts_to_build.html index e9c1dcdee1..df9199853b 100644 --- a/InvenTree/templates/InvenTree/parts_to_build.html +++ b/InvenTree/templates/InvenTree/parts_to_build.html @@ -1,5 +1,6 @@ {% extends "collapse.html" %} {% block collapse_title %} + Parts to Build{{ to_build | length }} {% endblock %} diff --git a/InvenTree/templates/InvenTree/parts_to_order.html b/InvenTree/templates/InvenTree/parts_to_order.html index b68eda7e14..c9b18606d0 100644 --- a/InvenTree/templates/InvenTree/parts_to_order.html +++ b/InvenTree/templates/InvenTree/parts_to_order.html @@ -1,5 +1,6 @@ {% extends "collapse.html" %} {% block collapse_title %} + Parts to Order{{ to_order | length }} {% endblock %} diff --git a/InvenTree/templates/InvenTree/starred_parts.html b/InvenTree/templates/InvenTree/starred_parts.html new file mode 100644 index 0000000000..eebc251666 --- /dev/null +++ b/InvenTree/templates/InvenTree/starred_parts.html @@ -0,0 +1,15 @@ +{% extends "collapse.html" %} +{% block collapse_title %} + +Starred Parts{{ starred | length }} +{% endblock %} + +{% block collapse_heading %} +You have {{ starred | length }} favourite parts +{% endblock %} + +{% block collapse_content %} + +{% include "required_part_table.html" with parts=starred table_id="starred-parts-table" %} + +{% endblock %} \ No newline at end of file From 7987fcc7ccd56d4bded12fbb552b8a05aaca1a0f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 5 May 2019 11:44:23 +1000 Subject: [PATCH 15/15] PEP fixes --- InvenTree/InvenTree/views.py | 2 +- InvenTree/part/api.py | 1 - InvenTree/part/serializers.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 68f6ebb4bd..6118f00fd0 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -329,7 +329,7 @@ class IndexView(TemplateView): context = super(TemplateView, self).get_context_data(**kwargs) - context['starred'] = [star.part for star in self.request.user.starred_parts.all()] + context['starred'] = [star.part for star in self.request.user.starred_parts.all()] # Generate a list of orderable parts which have stock below their minimum values # TODO - Is there a less expensive way to get these from the database diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 0ff95e1879..9db2bf102f 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -11,7 +11,6 @@ from rest_framework import status from rest_framework.response import Response from rest_framework import filters from rest_framework import generics, permissions -from rest_framework.serializers import ValidationError from django.db.models import Q from django.conf.urls import url, include diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 48839311e4..847b3bb1a8 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -4,7 +4,7 @@ JSON serializers for Part app from rest_framework import serializers -from .models import Part, PartStar, PartAttachment +from .models import Part, PartStar from .models import SupplierPart, SupplierPriceBreak from .models import PartCategory from .models import BomItem