From 446243f2b5846b9668053b3f803f166f5f3207e5 Mon Sep 17 00:00:00 2001 From: Matthias Mair <66015116+matmair@users.noreply.github.com> Date: Tue, 24 Aug 2021 00:29:50 +0200 Subject: [PATCH 01/67] Added templates --- .github/ISSUE_TEMPLATE/bug_report.md | 31 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 26 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..a0fba2b3d9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: bug, question +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Deployment Method** +Docker +Bare Metal + +**Version Information** +You can get this by going to the "About InvenTree" section in the upper right corner and cicking on to the "copy version information" diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..2a6042c4f2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,26 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FR]" +labels: enhancement +assignees: '' + +--- + +**Is your feature request the result of a bug?.** +Please link it here. + +**Problem** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Suggested solution** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Examples of other systems** +Show how other software handles your FR if you have examples. + +**Do you want to develop this?** +If so please describe breifly how you would like to implement it (so we can give advice) and if you have experience in the needed technology (you do not need to be a pro - this is just as a information for us). From 18297cd2fb7522174af2ec5b7f56c9c9f8a1775f Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 24 Aug 2021 22:28:42 +1000 Subject: [PATCH 02/67] Update bug_report.md Slight tweaks to bug report template --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index a0fba2b3d9..1a75b97af0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,7 +1,7 @@ --- name: Bug report -about: Create a report to help us improve -title: "[BUG]" +about: Create a bug report to help us improve InvenTree +title: "[BUG] Enter bug description" labels: bug, question assignees: '' From 28878b4b0dab2b641c5f101962c6387c5b05ab54 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 24 Aug 2021 22:30:01 +1000 Subject: [PATCH 03/67] Update feature_request.md Slight tweaks --- .github/ISSUE_TEMPLATE/feature_request.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 2a6042c4f2..ca9ff88a58 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,11 +7,11 @@ assignees: '' --- -**Is your feature request the result of a bug?.** +**Is your feature request the result of a bug?** Please link it here. **Problem** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] +A clear and concise description of what the problem is. e.g. I'm always frustrated when [...] **Suggested solution** A clear and concise description of what you want to happen. @@ -23,4 +23,4 @@ A clear and concise description of any alternative solutions or features you've Show how other software handles your FR if you have examples. **Do you want to develop this?** -If so please describe breifly how you would like to implement it (so we can give advice) and if you have experience in the needed technology (you do not need to be a pro - this is just as a information for us). +If so please describe briefly how you would like to implement it (so we can give advice) and if you have experience in the needed technology (you do not need to be a pro - this is just as a information for us). From 2923589c4a30fa79ef5a9ac899e1aef84da35fe3 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 25 Aug 2021 12:02:25 +1000 Subject: [PATCH 04/67] Fix sortName for purchase order line item table --- InvenTree/order/templates/order/purchase_order_detail.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html index e4647ce756..52355f1266 100644 --- a/InvenTree/order/templates/order/purchase_order_detail.html +++ b/InvenTree/order/templates/order/purchase_order_detail.html @@ -294,7 +294,7 @@ $("#po-table").inventreeTable({ { field: 'part', sortable: true, - sortName: 'part__part__name', + sortName: 'part_name', title: '{% trans "Part" %}', switchable: false, formatter: function(value, row, index, field) { @@ -314,7 +314,7 @@ $("#po-table").inventreeTable({ }, { sortable: true, - sortName: 'part__SKU', + sortName: 'SKU', field: 'supplier_part_detail.SKU', title: '{% trans "SKU" %}', formatter: function(value, row, index, field) { @@ -327,7 +327,7 @@ $("#po-table").inventreeTable({ }, { sortable: true, - sortName: 'part__MPN', + sortName: 'MPN', field: 'supplier_part_detail.manufacturer_part_detail.MPN', title: '{% trans "MPN" %}', formatter: function(value, row, index, field) { @@ -358,6 +358,7 @@ $("#po-table").inventreeTable({ { sortable: true, field: 'purchase_price', + sortName: 'price', title: '{% trans "Unit Price" %}', formatter: function(value, row) { return row.purchase_price_string || row.purchase_price; From c9756d30bda40d8979657f15de931fee5d973a76 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 25 Aug 2021 12:04:15 +1000 Subject: [PATCH 05/67] Add a custom OrderingFilter class Needs further work --- InvenTree/InvenTree/filters.py | 34 ++++++++++++++++++++++++++++++++++ InvenTree/order/api.py | 19 ++++++++++--------- 2 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 InvenTree/InvenTree/filters.py diff --git a/InvenTree/InvenTree/filters.py b/InvenTree/InvenTree/filters.py new file mode 100644 index 0000000000..b7ee52fb5a --- /dev/null +++ b/InvenTree/InvenTree/filters.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from rest_framework.filters import OrderingFilter + + +class InvenTreeOrderingFilter(OrderingFilter): + """ + Custom OrderingFilter class which allows aliased filtering of related fields. + + To use, simply specify this filter in the "filter_backends" section. + + Then, you can specify aliasing for ordering fields (or use ordering_fields as normal), e.g. + + filter_backends = [ + InvenTreeOrderingFilter, + ] + + ordering_fields = [ + 'name', + 'quantity', + ('part__SKU', 'SKU') + ] + + Here, ordering by "SKU" will actually order by the "SKU" field on the related part field + + """ + + def get_ordering(self, request, queryset, view): + + ordering = super().get_ordering(request, queryset, view) + + print("ORDERING:", ordering) + return ordering \ No newline at end of file diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index a834989fd9..b4c0022604 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -7,11 +7,12 @@ from __future__ import unicode_literals from django.conf.urls import url, include -from django_filters.rest_framework import DjangoFilterBackend +from django_filters import rest_framework as rest_filters from rest_framework import generics from rest_framework import filters, status from rest_framework.response import Response +from InvenTree.filters import InvenTreeOrderingFilter from InvenTree.helpers import str2bool from InvenTree.api import AttachmentMixin from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus @@ -144,7 +145,7 @@ class POList(generics.ListCreateAPIView): return queryset filter_backends = [ - DjangoFilterBackend, + rest_filters.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter, ] @@ -226,9 +227,9 @@ class POLineItemList(generics.ListCreateAPIView): return self.serializer_class(*args, **kwargs) filter_backends = [ - DjangoFilterBackend, + rest_filters.DjangoFilterBackend, filters.SearchFilter, - filters.OrderingFilter + InvenTreeOrderingFilter ] ordering_fields = [ @@ -272,7 +273,7 @@ class SOAttachmentList(generics.ListCreateAPIView, AttachmentMixin): serializer_class = SOAttachmentSerializer filter_backends = [ - DjangoFilterBackend, + rest_filters.DjangoFilterBackend, ] filter_fields = [ @@ -396,7 +397,7 @@ class SOList(generics.ListCreateAPIView): return queryset filter_backends = [ - DjangoFilterBackend, + rest_filters.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter, ] @@ -495,7 +496,7 @@ class SOLineItemList(generics.ListCreateAPIView): return queryset filter_backends = [ - DjangoFilterBackend, + rest_filters.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter ] @@ -580,7 +581,7 @@ class SOAllocationList(generics.ListCreateAPIView): return queryset filter_backends = [ - DjangoFilterBackend, + rest_filters.DjangoFilterBackend, ] # Default filterable fields @@ -598,7 +599,7 @@ class POAttachmentList(generics.ListCreateAPIView, AttachmentMixin): serializer_class = POAttachmentSerializer filter_backends = [ - DjangoFilterBackend, + rest_filters.DjangoFilterBackend, ] filter_fields = [ From 44ab487b6262aaeb1e0f80b4d1fdc6918d6f03a7 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 25 Aug 2021 12:05:41 +1000 Subject: [PATCH 06/67] Fix for file upload bug --- InvenTree/InvenTree/serializers.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/InvenTree/InvenTree/serializers.py b/InvenTree/InvenTree/serializers.py index 4cea0a218c..c6e9464e2b 100644 --- a/InvenTree/InvenTree/serializers.py +++ b/InvenTree/InvenTree/serializers.py @@ -94,9 +94,6 @@ class InvenTreeModelSerializer(serializers.ModelSerializer): # If instance is None, we are creating a new instance if instance is None and data is not empty: - - # Required to side-step immutability of a QueryDict - data = data.copy() # Add missing fields which have default values ModelClass = self.Meta.model From dcc8acb49aac5b8333332b25efdc3c855e8ce05a Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 25 Aug 2021 14:12:26 +1000 Subject: [PATCH 07/67] Data must be copied in a particular way --- InvenTree/InvenTree/serializers.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/InvenTree/InvenTree/serializers.py b/InvenTree/InvenTree/serializers.py index c6e9464e2b..0d21550f00 100644 --- a/InvenTree/InvenTree/serializers.py +++ b/InvenTree/InvenTree/serializers.py @@ -10,6 +10,8 @@ import os from decimal import Decimal +from collections import OrderedDict + from django.conf import settings from django.contrib.auth.models import User from django.core.exceptions import ValidationError as DjangoValidationError @@ -95,6 +97,14 @@ class InvenTreeModelSerializer(serializers.ModelSerializer): # If instance is None, we are creating a new instance if instance is None and data is not empty: + if data is None: + data = OrderedDict() + else: + new_data = OrderedDict() + new_data.update(data) + + data = new_data + # Add missing fields which have default values ModelClass = self.Meta.model From 9c9407b1ab9edc64df7de0e64a03dafeeac54252 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 25 Aug 2021 14:48:45 +1000 Subject: [PATCH 08/67] Add unit test for catching bug - Turns out that in an image was uploaded with more than ~2000 vertical pixels it would crash - Smaller images worked fine? --- InvenTree/stock/fixtures/test_image.bmp | Bin 0 -> 4638678 bytes InvenTree/stock/test_api.py | 36 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 InvenTree/stock/fixtures/test_image.bmp diff --git a/InvenTree/stock/fixtures/test_image.bmp b/InvenTree/stock/fixtures/test_image.bmp new file mode 100644 index 0000000000000000000000000000000000000000..56394f5cb1871f4266e97ffabd4775c175824b31 GIT binary patch literal 4638678 zcmeFxF|O>&aR5*na9|}~!-QIa;(-Ha-oq?~3vs|e&^ib>FyLH<0|&DN$*tXHf#))H%t^`4$nggHH8{rK-I z)*NezwZ?jXQ+qt(?=#jb)*NezwZ?jX(|$hU?=#jb)*NezwZ?jXNnVfm`;7I9HOE?F zt+C!;uK5vvpRr!C=2%OtHP-t}xjf?UGuA8C9BYZS#(IC5*GK$)#(Kq?V=b}PSntn2 z!Snryzt32&SaYl;)*9-+%77WS*a~Ua{s_ORP24`&$aj zBmO>Py<*L=mRM`7_qR;eNBn)pdc~S!EwR>E?@tNM???Q7#(Kq?V=b}PSnp|#{MYFb z>ly16YmT+V`tjfQx3nIQ`1_3YiZ#btVy&^>-?Do?;_oxoE7lxqiM7Uhe@pW9h`-NR zuUK=eCDt12{Vmt|5r3btUa{s_ORP24`&-J(BmO>Py<*L=mRM`7_qWW~NBn)pdc~S! zEz@8A@0aP1@!lNgP17%*|4qOA^5avi`0tOOeth>I0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PGKA_}x$SpOVCqB0%8pEbvo}wcq`nlXo2f0wo2^wUYm=k|OY%7BJU- z)BpUECs0zrTr2s{Dk%cLX@SpN`xyj&nm~RA3^5QOFe32NjFYTD{#^Y(Bmdg=68LQh zWQ~(yt*k(*kF`G!OFMu-d4a5PG7Oa!h`BcWPvHOpzjlGFaWd?X6^OYu{7>Nk0>5^F ztZ_2zkQIozHvCWF00O^ufvj;d?2r}6XRZxL^K%|hIk%rvTvWeOfvj;doPJgym3zN( zZo>uS00Nsp);QTty!k0BkjlMZIk%f-V;g}ZfvjLS%FmU{mQvDl!#Xdvq$wpS@XuRPO!CxeXVP0|;yaS>t3o@#d$jKq~is<=k$Tjco*u1hU4-(b-+U zWd%~X_bcbtRV-d6a4nEEPOfihc$yVR<=(HHTSJL>g+Qx7);MXs@ASQ_Kq~is<=jpS z!vg~S0$Jmv|K{C}tUxOFe&yVD<=`uU5rM36G7`gQZ&o0cd%tpS!v*930-HeAIN46T z`6(-q%DrDXx0_{S8-XK%tZ{O5cGquNfmH7O%DHtFisuP0W(88Y_bcbt zP$FI-&?=BMPFn9feJ?AJ%DrDXx6{J#fIz=M);Q_EdAB1gkjlMZIk#Oo_)1_zAZwhA z#PHdh6-edYubkU(0XcxcCXh8wwi9oD$_k`%?^n+4X4%+A;7A~AoE)9q^;=dTm3zN( zZe7LVWdhd%S>xpTmWHQUfmH7O%DFX^h*t=-3S^Cw*85K1%L=4&?^n+4v@kp%&@YfR zPWo@&?Z^tGa_?8pZC4Jy5*QK48Yd$$eD-DqQn~jl=QdnG4j`}zWQ~*U#G9Y80;$~l zm2vSjzSH-z0;$~lm2*2S3=atO3uKLx{+o9@vI42x`;~Lsm4mMYMg+3P$w&;Jy;*@& z?)}QS4Hu9D2y6mb<77MW=BKPcD))Zn+-{bQZ3K=4vc}2L*vSt=G~60 zKq~is<=l4V;46U^t%n?Tk$*-pIqDJzi5ycTDv&i!TJJl3 zFDsDBykTp)W6K{UX3Z!!HSI+Hb+1N(lNFZyR9G%_uTUH>Id%tpSUB%*M0@ngr3Z!!HSI+IUFgzg8FOW4(`fuLt$O@!#?^n)k zR}Q`s7!k-CCnGU@_GSfAx%VsQHe5gsAg~E!jg#%fo1d}*soeXObGunKwh=fI$Qma{ zXLtRU6-edYubf*~v3QxlwLsQ5xxS_0X;vVWd%tpS4JG0g0<8jBsCK_kQKvZkCO01dar<#>vsyUB6`oQn~jl=hjs$UM6rYkTp)OZ)td%6-edY zubf*$iFk!Tt3cK`X}$0Cy{teg_kQKvP7A{W0{sG6l`jE0D^)Upcqa!tj7V zzd+VF>A!imBP)=~yBmoQ%Zq*_#ze<=(HH+i(FnfWRh@HBPn@Z+^-O zq;l_9&h2K|*hb(;AZwf)o!#|YRv?vozjAI}#o}cG*8*ANSNCCU7l~HBPQ?X?U6yNafzIoLfVQc!fZ#K-M^E zz3=qBtUxOFe&yUw3&R5f{Q_Czr2ppKj;uf`_kQKvcIDtJff0eMaWWFaXKz*@m3zN( zZo>uS00Nsp);QTty!k0BkjlMZIk%f-V;g}ZfvjLS%FmU{mQvDl!#Xdvq$wpS@XuRPO!CxeXVP0|;yaS>t3o@#d$jKq~is<=k$Tjco*u1hU4-(b-+U zWd%~X_bcbtRV-d6a4nEEPOfihc$yVR<=(HHTSJL>g+Qx7);MXs@ASQ_Kq~is<=jpS z!vg~S0$Jmv|K{C}tUxOFe&yVD<=`uU5rM36G7`gQZ&o0cd%tpS!v*930-HeAIN46T z`6(-q%DrDXx0_{S8-XK%tZ{O5cGquNfmH7O%DHtFisuP0W(88Y_bcbt zP$FI-&?=BMPFn9feJ?AJ%DrDXx6{J#fIz=M);Q_EdAB1gkjlMZIk#Oo_)1_zAZwhA z#PHdh6-edYubkU(0XcxcCXh8wwi9oD$_k`%?^n+4X4%+A;7A~AoE)9q^;=dTm3zN( zZe7LVWdhd%S>xpTmWHQUfmH7O%DFX^h*t=-3S^Cw*85K1%L=4&?^n+4v@kp%&@YfR zPWo@&?Z^tGa_?8pZC4Jy5*QK48Yd$$eD-DqQn~jl=QdnG4j`}zWQ~*U#G9Y80;$~l zm2vSjzSH-z0;$~lm2*2S3=atO3uKLx{+o9@vI42x`;~Lsm4mMYMg+3P$w&;Jy;*@& z?)}QS4Hu9D2y6mb<77MW=BKPcD))Zn+-{bQZ3K=4vc}2L*vSt=G~60 zKq~is<=l4V;46U^t%n?Tk$*-pIqDJzi5ycTDv&i!TJJl3 zFDsDBykTp)W6K{UX3Z!!HSI+Hb+1N(lNFZyR9G%_uTUH>Id%tpSUB%*M0@ngr3Z!!HSI+IUFgzg8FOW4(`fuLt$O@!#?^n)k zR}Q`s7!k-CCnGU@_GSfAx%VsQHe5gsAg~E!jg#%fo1d}*soeXObGunKwh=fI$Qma{ zXLtRU6-edY|Nr*OAjgvB)}cG|7Q7bk+I#Ti<7Z6_MA!B03`(M;^9Btg>!w7WU=V2= zZ~x>!2>hAAUtRjRAFJx*TA@ma&E3Byat^TZ_UB0R6axP|Q(N*xRVUZtRYGj;{xy+v zfQ>iqF9d!eaHh88iKk}4rKcmJBmIl#u7Q-V)SJgFhzlnPayT$5A@vAO%#M9u*=-kcJA zV&X{+0jE@`>g1ZFN{G$fzb0}Hu<_=U;1d&1Y6v)`LRBZ%BvnFe?*28AbAXLErv#sv zcv3^aDHWPh|S%h&1fQ6AQbWKg z6{fn>Rh?XuR0*-U``1Ly0XE*85`1FfNeuy~RH*9Y znxsmI&E3Byat^TZ=9J(Q6HjUgIHf{WC)XrZLTv8-HIZ|GjW?$RpO|=3L%=B&syewQ zsS;vy_pgbZ18lrGCHTa|lNtg}sZiC)HA$5ao4bEaU)jn0Qh{z$q1~I=LpP5@K`r zuZf%kY`i%o_{7AM8UjwKP}RvbNtFQc zKi8xM1Dp~pu)_!m0jE@`>f{=XO336&<9qEoW(L}of7PuTz5G=;xgX0#2#0sS_I^5hp%vrc;8avF+=}DHS$#Vk4w)uJV*SB{8E2kA;2~g=Tbnwu5n^&M)-t%m5cq|FNp5ZG#72n8 z!C1@qCDS~HfJts`>cmEf$-!95;QB(~7Xl`^wW$*uAtncd3cmEf$-y840uXpjz@|=YguF({ix7Z-2?3iru@Pc&Fvx%a1YQ%csS_I^uTkCp5%L-( zFG2tUCIoEi#72n8!5{+y5O__%rcP{xyhh235P*OQ0h>Cp5n^&M$bbL@UK6mXlN=%c z+4|q>fbb#&8WPBwGdwR*Y=S^NfsMUZPd!z^)2*o+Sq6b(0`yw(0mQGT*Wwwo0Roi- zHs0}#o+~rPZU|%(*m%b`dd|)j`yfzBVB;O%=(#du?1n%#fsJ>3qvz~gu@3^31UBCB zjh-tr#%>5?6WDmiH+s&_75gAiNnqn0-{`qAW9)`NHi3=T(J)Vl>|24@r|A< zGsbQRWE0qU$2WS;&K3J0P)T6p9pC7=GGpw9KsJGmcYLGg>|C)A0+j?d-tmo|D>KG! z2xJr3c*i$-&dwG4AW%tQ;~n4VxiVwyhCnufjdy&b=j>du4+510Hs0}#o+~rPZU|%( z*m%b`dd|)j`yfzBVB;O%=(#du?1n%#fsJ>3qvz~gu@3^31UBCBjh-tr#%>5?6WDmi zH+s&_75gAiNnqn0-{`qAW9)`NHi3=T(J)Vl>|24@r|A|C)A0+j?d-tmo|D>KG!2xJr3c*i$-&dwG4 zAW%tQ;~n4VxiVwyhCnufjdy&b=j>du4+510Hs0}#o+~rPZU|%(*m%b`dd|)j`yfzB zVB;O%=(#du?1n%#fsJ>3qvz~gu@3^31UBCBjh-tr#%>5?6WDmiH+s&_75gAiNnqn0 z-{`qAW9)`NHi3=T(J)Vl>|24@r|A|C)A0+j?d-tmo|D>KG!2xJr3c*i$-&dwG4AW%tQ;~n4VxiVwy zhCnufjdy&b=j>du4+510Hs0}#o+~rPZU|%(*m%b`dd|)j`yfzBVB;O%=(#du?1n%# zfsJ>3qvz~gu@3^31UBCBjh-tr#%>5?6WDmiH+s&_75gAiNnqn0-{`qAW9)`NHi3=T(J)Vl>|24@r|A|C)A0+j?d-tmo|D>KG!2xJr3c*i$-&dwG4AW%tQ;~n4VxiVwyhCnufjdy&b=j>du z4+510Hs0}#o+~rPZU|%(*m%b`dd|)j`yfzBVB;O%=(#du?1n%#fsJ>3qvz~gu@3^3 z1UBCBjh-tr#%>5?6WDmiH+s&_75gAiNnqn0-{`qAW9)`NHi3=T(J)Vl>|24 z@r|A|C)A0+j?d-tmo| zD>KG!2xJr3c*i$-&dwG4AW%tQ;~n4VxiVwyhCnufjdy&b=j>du4+510Hs0}#o+~rP zZU|%(*m%b`dd|)j`yfzBVB;O%=(#du?1n%#fsJ>3qvz~gu@3^31UBCBjh-tr#%>5? z6WDmiH+s&_75gAiNnqn0-{`qAW9)`NHi3=T(J)Vl>|24@r|A|C)A0+j?d-tmo|D>KG!2xJr3c*i$- z&dwG4AW%tQ;~n4VxiVwyhCnufjdy&b=j>du4+510Hs0}#o+~rPZU|%(*m%b`dd|)j z`yfzBVB;O%=(#du?1n%#fsJ>3qvz~gu@3^31UBCBjh-tr#%>5?6WDmiH+s&_75gAi zNnqn0-{`qAW9)`NHi3=T(J)Vl>|24@r|A268M+wzh77I2M9m_ z0;vRKwf3)6*(`$q1R(G$0ebCMH2eVq5NJw(UTb=A$c6v}ekDM!{fdS^KmY2 zsU*n|jUT2!00Iz@RFY%}X8{2SKtMx4Qc02_8b3^d00bZ)sU*n|&H@4ufPjX8q>>~< zG=7)@0SG`qQc02_oCO3R009jFNhL{!X#6k*0uX?Jq>>~c(fDBs z1RwwbNhL{!a261N00cAyB$Xr?qVdBN2tWV=l1h>c;Vd8k0SIUaNGeG(MB|4k5P$## zB$Xr?!dXB70uay;kW`Xnh{g|7AOHafNGeG(gtLGE1R$UxAgLtD5RIQdP5H;*|Ni8c zXAsydk9D>Nirmk`OpFwYAY@W zOx{C4Qc02_am>~<;+PLDfT6bHa=_$01SFLt84|~QXaNkh6_*1h?;#+mB*~CC=0gi$sI9mh zFnJFFNhL{!#4#UQ07GrX<)C!(zuW#-itX)kNhL{!ymj!u-y2#0Lv8)<^KAKxwzc-} zmbfLABpDKiZ)gDwwH23x(#iB%Dt<{NNruGP9$ElHZN=rFbTYk`ieFMmk|A-nhZew4 zTX8ukolLK#;+IsCWJsLtp#?D1R$LBBCqKPbs^G=)mZBe@l1h>cd4c`QSB4hAP+MOf zYPX-YtcaGPpJYfXNirlZs6z{2sI9mhlumA05iLd2Yo$TIUQVxFBTO97vLafFehyXg zsU;Z_m$9J*Fw|CD4oWAttcaGP>9x|JUoWTEt`Q~9uQwi33_zL`%`np-MirBtzmdHnaeS+KS6T>ExCb(NZ+MRvPr{<@DM$!o&eB zE25?7=TIe|T9P4g85>#vLv6+7pmcJ}ifAdCUMmgy^>TXc8e!sqmKD)b^mC|^Pc6xi zxQq=gfT6bHa!@+CWks|UO|O*({dzgQc8xG`K+B3~Df&57$)}cNNLLC#AR$~ z0SvVjmxI#DEi0m>XnL(Q=-12XwQGcl16o!@OVQ7vN5iLbOhbsBhk_?H<*w6wPYAY@WrITA$L`%{1T4~U)m(y$42ondi ztcaGPpF@>=YDtE~Wo&2x47C-PgVM<@E25=ndaX3**URa(YlMjdT2@3$(a)huKD8u6 z;xab00EXI%%R%YnmKD)bG`&_D^y}sH+BL$&0WB+{rRe8SC7)W7A#oWSS^z_B#pR%M za?6TnDVkm@4f^$RdhHrv;((SF(NgqtsFF`D$&k2=4K0A7w&HS7I=N*3*Ghway_{aVMwmFDWks|U z{T!;~Q%f==E@MLrV5qIQ9F$IOSrIKo(`%(czg|wST_a2!(6S<0ihd4N@~I^m5|^=| z1u)cBTn*e&?HNwOJEi0m>=;u%+ zpIVY3aTyz007GrX<)CzO%Zg|znqDgn`t@>p?HXa?fR+`}QuK4El20wkkhqKuEr6l6 z;&M^W-aX`z8Xes(RRLQ57WJp}bh8Dn3TX8uko!qh_T8gIE zN`rpAoL;*|m^h$iMYI(C9IE6~OEM%bV?zsIsI9mhlumA05iLd2Yo$TIUQVxFBTO97 zvLafFehyXgsU;Z_m$9J*Fw|CD4oWAttcaGP>9x|JUoWTEt`Q~9uQwi33_zL`%`np-MirBtzmdHnaeS+KS6T>ExCb(NZ+MRvPr{ z<@DM$!o&eBE25?7=TIe|T9P4g85>#vLv6+7pmcJ}ifAdCUMmgy^>TXc8e!sqmKD)b z^mC|^Pc6xixQq=gfT6bHa!@+CWks|UO|O*({dzgQc8xG`K+B3~Df&57$)}cNNLLC#AR$~0SvVjmxI#DEi0m>XnL(Q=-12XwQGcl16o!@OVQ7vN5iLbOhx&YKN&ZR9$X~7+S^z_B{pI0y`FYEVXes(h zgrw5=|DhGPi(ads{&r`}ifAdCUVEESkN4;`k8}7=Mazn4Df&4NzSq8srQxsWHN!*s zRZ`1}Xes(R0($M&B)b2BUUNU8|JK*CB3g?6Z|bA}_s{|uYHMf|TUJC%(L)1t+8SB_ zLv5YsH{walifAc%L|ncxF|+`N+WO{18~&zcMYI&%FdXkB4=sS9w%(nw{aY<7qNQm2 zD5wMuEr6l6RHtq>r)5R76m53!adL(hz))LpIVhdnvLafFEXwiZBj~QA3Lv8uz z>6OBk712`ktK;eT($E4JYO7|HQuK(pd}Cs00SvYE&51VrP0NaCDY{`e z-bo%>07GrPJ7fE|T2@3$(e_bL2^?AgLv5)}-E2%$duF7Qj$jnaN1p)v_X5icUP#+EqgfV5qIytb}cESrIKohaI8Y;GqRD)RtR% zM0#3QL`%`41M?pMxn)JP6#d0v%=pRB z0vKv*M&=F|Ei0m>=mY)K2SW>BsI8$XY*`U4MGppZ^^Pg+()OVK0Z@{Nh11u)dsHz(TgH!UlorRav?cqe&i0SvYE?u_lExCb(Nc8jF=s9xS^z_BWhNtW zSIdfMDLU~`YgY{|fT6Z(vl6zwWks|U9d?9ngNGKtP+M;45$S1J5iLcF4$Obd&;l50 z%Rf)A6t=90mZD!BPsf*r7Qj$j9TWD2=9U%FQuG&xG2_ z9}F#kp|*ymuw_NG6g@ORr>&s{Fx1w0ej}c=tcaGPN5thD6GIDNsI6~KwBc`BRzyqD z4a4zH^3VbpYU|w@+rQPaB3g>JkAh0z&;l50OLgjIb6QqJOVMTrA17yM0SvVjmxI#D zEi0m>=+a}(Tt2h_hT6(ZM&hogB3k~F2#HmHLttnD47K$)C)?<^Eh~-hHRps4Er6l6 zoRf5+jb6Ke$E$xLFth-M+WON84tPed4LC$E3quQFs4cJT$kfnlGL#JZAuzN6hT0;v z5P-lp1cnyCP+Q-eD8GRK1cnyCP+Q~`0ucCyz|aC1YU`U5wV&48L~ literal 0 HcmV?d00001 diff --git a/InvenTree/stock/test_api.py b/InvenTree/stock/test_api.py index 4822eeaeab..619b4444d7 100644 --- a/InvenTree/stock/test_api.py +++ b/InvenTree/stock/test_api.py @@ -5,6 +5,8 @@ Unit testing for the Stock API # -*- coding: utf-8 -*- from __future__ import unicode_literals +import os + from datetime import datetime, timedelta from django.urls import reverse @@ -666,3 +668,37 @@ class StockTestResultTest(StockAPITestCase): test = response.data[0] self.assertEqual(test['value'], '150kPa') self.assertEqual(test['user'], self.user.pk) + + def test_post_bitmap(self): + """ + 2021-08-25 + + For some (unknown) reason, prior to fix https://github.com/inventree/InvenTree/pull/2018 + uploading a bitmap image would result in a failure. + + This test has been added to ensure that there is no regression. + + As a bonus this also tests the file-upload component + """ + + here = os.path.dirname(__file__) + + image_file = os.path.join(here, 'fixtures', 'test_image.bmp') + + with open(image_file, 'rb') as bitmap: + + data = { + 'stock_item': 105, + 'test': 'Checked Steam Valve', + 'result': False, + 'value': '150kPa', + 'notes': 'I guess there was just too much pressure?', + "attachment": bitmap, + } + + response = self.client.post(self.get_url(), data) + + self.assertEqual(response.status_code, 201) + + # Check that an attachment has been uploaded + self.assertIsNotNone(response.data['attachment']) From 4b8ef2ad626f6518548743b65a7c773bd445ac03 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 25 Aug 2021 17:46:42 +1000 Subject: [PATCH 09/67] Implements custom filtering back end --- InvenTree/InvenTree/filters.py | 39 ++++++++++++++++++++++++---------- InvenTree/order/api.py | 12 ++++++++--- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/InvenTree/InvenTree/filters.py b/InvenTree/InvenTree/filters.py index b7ee52fb5a..cd1b769646 100644 --- a/InvenTree/InvenTree/filters.py +++ b/InvenTree/InvenTree/filters.py @@ -10,25 +10,42 @@ class InvenTreeOrderingFilter(OrderingFilter): To use, simply specify this filter in the "filter_backends" section. - Then, you can specify aliasing for ordering fields (or use ordering_fields as normal), e.g. - filter_backends = [ InvenTreeOrderingFilter, ] - ordering_fields = [ - 'name', - 'quantity', - ('part__SKU', 'SKU') - ] - - Here, ordering by "SKU" will actually order by the "SKU" field on the related part field + Then, specify a ordering_field_aliases attribute: + ordering_field_alises = { + 'name': 'part__part__name', + 'SKU': 'part__SKU', + } """ def get_ordering(self, request, queryset, view): ordering = super().get_ordering(request, queryset, view) - print("ORDERING:", ordering) - return ordering \ No newline at end of file + aliases = getattr(view, 'ordering_field_aliases', None) + + # Attempt to map ordering fields based on provided aliases + if ordering is not None and aliases is not None: + """ + Ordering fields should be mapped to separate fields + """ + + for idx, field in enumerate(ordering): + + reverse = False + + if field.startswith('-'): + field = field[1:] + reverse = True + + if field in aliases: + ordering[idx] = aliases[field] + + if reverse: + ordering[idx] = '-' + ordering[idx] + + return ordering diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index b4c0022604..e728b19226 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -232,10 +232,16 @@ class POLineItemList(generics.ListCreateAPIView): InvenTreeOrderingFilter ] + ordering_field_aliases = { + 'MPN': 'part__manufacturer_part__MPN', + 'SKU': 'part__SKU', + 'part_name': 'part__part__name', + } + ordering_fields = [ - 'part__part__name', - 'part__MPN', - 'part__SKU', + 'part_name', + 'MPN', + 'SKU', 'reference', 'quantity', 'received', From 51992a92c19a66e3ab080825e6663fcecdae6235 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 25 Aug 2021 18:00:32 +1000 Subject: [PATCH 10/67] Change name of purchaseorder line item table - Was conflicting with purchaseorder table - Saved column selections were being overridden --- .../order/templates/order/purchase_order_detail.html | 11 ++++++----- InvenTree/templates/InvenTree/settings/settings.html | 2 -- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html index e4647ce756..80d5513efa 100644 --- a/InvenTree/order/templates/order/purchase_order_detail.html +++ b/InvenTree/order/templates/order/purchase_order_detail.html @@ -28,7 +28,7 @@ {% endif %} - +
@@ -208,13 +208,13 @@ $('#new-po-line').click(function() { {% endif %} function reloadTable() { - $("#po-table").bootstrapTable("refresh"); + $("#po-line-table").bootstrapTable("refresh"); } function setupCallbacks() { // Setup callbacks for the line buttons - var table = $("#po-table"); + var table = $("#po-line-table"); {% if order.status == PurchaseOrderStatus.PENDING %} table.find(".button-line-edit").click(function() { @@ -273,9 +273,9 @@ function setupCallbacks() { } -$("#po-table").inventreeTable({ +$("#po-line-table").inventreeTable({ onPostBody: setupCallbacks, - name: 'purchaseorder', + name: 'purchaseorderlines', sidePagination: 'server', formatNoMatches: function() { return "{% trans 'No line items found' %}"; }, queryParams: { @@ -365,6 +365,7 @@ $("#po-table").inventreeTable({ }, { sortable: true, + field: 'total_price', title: '{% trans "Total price" %}', formatter: function(value, row) { var total = row.purchase_price * row.quantity; diff --git a/InvenTree/templates/InvenTree/settings/settings.html b/InvenTree/templates/InvenTree/settings/settings.html index c28ef016e6..dd946d9055 100644 --- a/InvenTree/templates/InvenTree/settings/settings.html +++ b/InvenTree/templates/InvenTree/settings/settings.html @@ -185,8 +185,6 @@ $('#cat-param-table').inventreeTable({ function loadTemplateTable(pk) { - console.log('refresh:', pk); - // Enable the buttons $('#new-cat-param').removeAttr('disabled'); From 8660f13ef5b49c27b98213f763875364d2553e25 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 26 Aug 2021 07:50:19 +1000 Subject: [PATCH 11/67] Allow sorting by purchase price (unit price) --- InvenTree/order/api.py | 7 ++++--- InvenTree/order/templates/order/purchase_order_detail.html | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index e728b19226..cb33b3f680 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -239,12 +239,13 @@ class POLineItemList(generics.ListCreateAPIView): } ordering_fields = [ - 'part_name', 'MPN', - 'SKU', - 'reference', + 'part_name', + 'purchase_price', 'quantity', 'received', + 'reference', + 'SKU', ] search_fields = [ diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html index 52355f1266..2048599825 100644 --- a/InvenTree/order/templates/order/purchase_order_detail.html +++ b/InvenTree/order/templates/order/purchase_order_detail.html @@ -358,7 +358,6 @@ $("#po-table").inventreeTable({ { sortable: true, field: 'purchase_price', - sortName: 'price', title: '{% trans "Unit Price" %}', formatter: function(value, row) { return row.purchase_price_string || row.purchase_price; From 212a7eeed12d3fe04192c4128d79156e77af8730 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 26 Aug 2021 07:59:47 +1000 Subject: [PATCH 12/67] Disable filtering for total_price (as this is not a database field!) --- InvenTree/order/templates/order/purchase_order_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html index 2048599825..df22e60a20 100644 --- a/InvenTree/order/templates/order/purchase_order_detail.html +++ b/InvenTree/order/templates/order/purchase_order_detail.html @@ -364,7 +364,7 @@ $("#po-table").inventreeTable({ } }, { - sortable: true, + sortable: false, title: '{% trans "Total price" %}', formatter: function(value, row) { var total = row.purchase_price * row.quantity; From bad246bca6f0cbc666a9d47e0d5b6d1afee1f94a Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 26 Aug 2021 08:24:31 +1000 Subject: [PATCH 13/67] Fixes for ordering of stock table --- .../order/purchase_order_detail.html | 2 +- InvenTree/stock/api.py | 14 +++++++++++-- InvenTree/templates/js/translated/stock.js | 20 +++++++++++++++---- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html index df22e60a20..ef7a8e8703 100644 --- a/InvenTree/order/templates/order/purchase_order_detail.html +++ b/InvenTree/order/templates/order/purchase_order_detail.html @@ -383,7 +383,7 @@ $("#po-table").inventreeTable({ } }, { - sortable: true, + sortable: false, field: 'received', switchable: false, title: '{% trans "Received" %}', diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index cf58c7d4d9..7ff5c55bbe 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -43,6 +43,7 @@ from .serializers import StockItemTestResultSerializer from InvenTree.views import TreeSerializer from InvenTree.helpers import str2bool, isNull from InvenTree.api import AttachmentMixin +from InvenTree.filters import InvenTreeOrderingFilter from decimal import Decimal, InvalidOperation @@ -882,10 +883,16 @@ class StockList(generics.ListCreateAPIView): filter_backends = [ DjangoFilterBackend, filters.SearchFilter, - filters.OrderingFilter, + InvenTreeOrderingFilter, ] + ordering_field_aliases = { + 'SKU': 'supplier_part__SKU', + } + ordering_fields = [ + 'batch', + 'location', 'part__name', 'part__IPN', 'updated', @@ -893,10 +900,13 @@ class StockList(generics.ListCreateAPIView): 'expiry_date', 'quantity', 'status', + 'SKU', ] ordering = [ - 'part__name' + 'part__name', + 'quantity', + 'location', ] search_fields = [ diff --git a/InvenTree/templates/js/translated/stock.js b/InvenTree/templates/js/translated/stock.js index 585cac1310..99c3824cac 100644 --- a/InvenTree/templates/js/translated/stock.js +++ b/InvenTree/templates/js/translated/stock.js @@ -921,8 +921,10 @@ function loadStockTable(table, options) { return renderLink(text, link); } - }, - { + }); + + col = { + field: 'supplier_part', title: '{% trans "Supplier Part" %}', visible: params['supplier_part_detail'] || false, @@ -944,15 +946,25 @@ function loadStockTable(table, options) { return renderLink(text, link); } - }); + }; + + if (!options.params.ordering) { + col.sortable = true; + col.sortName = 'SKU'; + } + + columns.push(col); col = { field: 'purchase_price_string', title: '{% trans "Purchase Price" %}', }; + if (!options.params.ordering) { - col['sortable'] = true; + col.sortable = true; + col.sortName = 'purchase_price'; }; + columns.push(col); columns.push({ From ac8a0be74abed45e91b6e4c742695454d887f608 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 26 Aug 2021 08:48:19 +1000 Subject: [PATCH 14/67] Enable sorting by total_price --- InvenTree/order/api.py | 17 ++++++++++++++ InvenTree/order/serializers.py | 23 ++++++++++++++++++- .../order/purchase_order_detail.html | 3 ++- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index cb33b3f680..eb8ba22ad0 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -215,6 +215,14 @@ class POLineItemList(generics.ListCreateAPIView): queryset = PurchaseOrderLineItem.objects.all() serializer_class = POLineItemSerializer + def get_queryset(self, *args, **kwargs): + + queryset = super().get_queryset(*args, **kwargs) + + queryset = POLineItemSerializer.annotate_queryset(queryset) + + return queryset + def get_serializer(self, *args, **kwargs): try: @@ -246,6 +254,7 @@ class POLineItemList(generics.ListCreateAPIView): 'received', 'reference', 'SKU', + 'total_price', ] search_fields = [ @@ -270,6 +279,14 @@ class POLineItemDetail(generics.RetrieveUpdateDestroyAPIView): queryset = PurchaseOrderLineItem.objects.all() serializer_class = POLineItemSerializer + def get_queryset(self): + + queryset = super().get_queryset() + + queryset = POLineItemSerializer.annotate_queryset(queryset) + + return queryset + class SOAttachmentList(generics.ListCreateAPIView, AttachmentMixin): """ diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py index e97d19250a..fe23bd2a17 100644 --- a/InvenTree/order/serializers.py +++ b/InvenTree/order/serializers.py @@ -7,8 +7,9 @@ from __future__ import unicode_literals from django.utils.translation import ugettext_lazy as _ +from django.db import models from django.db.models import Case, When, Value -from django.db.models import BooleanField +from django.db.models import BooleanField, ExpressionWrapper, F from rest_framework import serializers from sql_util.utils import SubqueryCount @@ -109,6 +110,23 @@ class POSerializer(InvenTreeModelSerializer): class POLineItemSerializer(InvenTreeModelSerializer): + @staticmethod + def annotate_queryset(queryset): + """ + Add some extra annotations to this queryset: + + - Total price = purchase_price * quantity + """ + + queryset = queryset.annotate( + total_price=ExpressionWrapper( + F('purchase_price') * F('quantity'), + output_field=models.DecimalField() + ) + ) + + return queryset + def __init__(self, *args, **kwargs): part_detail = kwargs.pop('part_detail', False) @@ -123,6 +141,8 @@ class POLineItemSerializer(InvenTreeModelSerializer): quantity = serializers.FloatField(default=1) received = serializers.FloatField(default=0) + total_price = serializers.FloatField(read_only=True) + part_detail = PartBriefSerializer(source='get_base_part', many=False, read_only=True) supplier_part_detail = SupplierPartSerializer(source='part', many=False, read_only=True) @@ -158,6 +178,7 @@ class POLineItemSerializer(InvenTreeModelSerializer): 'purchase_price_string', 'destination', 'destination_detail', + 'total_price', ] diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html index ef7a8e8703..d5f8dd048a 100644 --- a/InvenTree/order/templates/order/purchase_order_detail.html +++ b/InvenTree/order/templates/order/purchase_order_detail.html @@ -364,7 +364,8 @@ $("#po-table").inventreeTable({ } }, { - sortable: false, + field: 'total_price', + sortable: true, title: '{% trans "Total price" %}', formatter: function(value, row) { var total = row.purchase_price * row.quantity; From ec88415f3dbdff79a6c6647c1399ec65a3656032 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 26 Aug 2021 21:54:25 +1000 Subject: [PATCH 15/67] Add "units" to PartBriefSerializer --- InvenTree/InvenTree/version.py | 4 ++++ InvenTree/part/serializers.py | 1 + 2 files changed, 5 insertions(+) diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py index 5e7453a80b..b98d825e37 100644 --- a/InvenTree/InvenTree/version.py +++ b/InvenTree/InvenTree/version.py @@ -15,6 +15,10 @@ INVENTREE_API_VERSION = 10 """ Increment this API version number whenever there is a significant change to the API that any clients need to know about +v11 -> 2021-08-26 + - Adds "units" field to PartBriefSerializer + - This allows units to be introspected from the "part_detail" field in the StockItem serializer + v10 -> 2021-08-23 - Adds "purchase_price_currency" to StockItem serializer - Adds "purchase_price_string" to StockItem serializer diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index c2d515cf32..060faf8b0d 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -206,6 +206,7 @@ class PartBriefSerializer(InvenTreeModelSerializer): 'stock', 'trackable', 'virtual', + 'units', ] From e5de69cd96c301308b5620822cbaaa766f449e74 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 26 Aug 2021 22:13:13 +1000 Subject: [PATCH 16/67] Update version.py Actually bump the API version --- InvenTree/InvenTree/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py index b98d825e37..b53cacff79 100644 --- a/InvenTree/InvenTree/version.py +++ b/InvenTree/InvenTree/version.py @@ -10,7 +10,7 @@ import common.models INVENTREE_SW_VERSION = "0.5.0 pre" -INVENTREE_API_VERSION = 10 +INVENTREE_API_VERSION = 11 """ Increment this API version number whenever there is a significant change to the API that any clients need to know about From 3adf30a00c041b4451df0a36f17f0f3ceaecd5aa Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 26 Aug 2021 22:50:50 +1000 Subject: [PATCH 17/67] Simple fix for category parameter settings --- InvenTree/templates/InvenTree/settings/settings.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/InvenTree/templates/InvenTree/settings/settings.html b/InvenTree/templates/InvenTree/settings/settings.html index dd946d9055..beb7f5eb04 100644 --- a/InvenTree/templates/InvenTree/settings/settings.html +++ b/InvenTree/templates/InvenTree/settings/settings.html @@ -208,7 +208,11 @@ $("#new-cat-param").click(function() { launchModalForm(`/part/category/${pk}/parameters/new/`, { success: function() { - $("#cat-param-table").bootstrapTable('refresh'); + $("#cat-param-table").bootstrapTable('refresh', { + query: { + category: pk, + } + }); }, }); }); From 62d877ba5429c4db589b7efe26d32ec229c9de50 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 28 Aug 2021 16:40:06 +1000 Subject: [PATCH 18/67] Adds script to pull down "rendered" versions of javascript files --- ci/.gitignore | 1 + ci/pull_js_files.py | 111 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 ci/.gitignore create mode 100644 ci/pull_js_files.py diff --git a/ci/.gitignore b/ci/.gitignore new file mode 100644 index 0000000000..b8ba69cb91 --- /dev/null +++ b/ci/.gitignore @@ -0,0 +1 @@ +js_tmp/ \ No newline at end of file diff --git a/ci/pull_js_files.py b/ci/pull_js_files.py new file mode 100644 index 0000000000..b69268e0e2 --- /dev/null +++ b/ci/pull_js_files.py @@ -0,0 +1,111 @@ +""" +Pull 'rendered' copies of the templated JS files down from the InvenTree server. + +These files can then be used for linting and unit testing +""" + +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import sys +import re +import os +import json +import pathlib +import argparse +import requests +from requests.auth import HTTPBasicAuth + + +here = os.path.abspath(os.path.dirname(__file__)) +js_template_dir = os.path.abspath(os.path.join(here, '..', 'InvenTree', 'templates', 'js')) + +js_tmp_dir = os.path.join(here, 'js_tmp') + +def get_token(server, username, password): + + url = os.path.join( + server, + 'api', + 'user', + 'token', + ) + + auth = HTTPBasicAuth(username, password) + + response = requests.get(url, auth=auth, allow_redirects=False) + + data = json.loads(response.text) + + return data['token'] + + +def download_file(url, filename, token): + """ + Download a single javascript file + """ + + print(f"Downloading '{url}'") + + headers = { + 'AUTHORIZATION': f'Token {token}' + } + + response = requests.get( + url, + allow_redirects=False, + headers=headers + ) + + output_file = os.path.join( + js_tmp_dir, + filename, + ) + + with open(output_file, 'wb') as output: + output.write(response.content) + + +def download_js_files(subdir, url, token): + """ + Returns a flattened list of all javascript files + """ + + d = os.path.join(js_template_dir, subdir) + + files = pathlib.Path(d).rglob('*.js') + + for filename in files: + js = os.path.basename(filename) + + js_url = os.path.join(url, js) + + download_file(js_url, js, token) + +if __name__ == '__main__': + + parser = argparse.ArgumentParser("Download JavaScript files") + + parser.add_argument('-s', '--server', help='InvenTree server', action='store') + parser.add_argument('-u', '--username', help='Username', action='store') + parser.add_argument('-p', '--password', help='password', action='store') + + args = parser.parse_args() + + if not os.path.exists(js_tmp_dir): + os.mkdir(js_tmp_dir) + + auth = HTTPBasicAuth(args.username, args.password) + + # Get an auth token from the server + token = get_token(args.server, args.username, args.password) + + # Dynamic javascript files + dynamic_url = os.path.join(args.server, 'js', 'dynamic') + + download_js_files('dynamic', dynamic_url, token) + + # Translated JS files + i18n_url = os.path.join(args.server, 'js', 'i18n') + + download_js_files("translated", i18n_url, token) \ No newline at end of file From 880a701881f668e2bbef169507103472dd04f63f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 28 Aug 2021 16:55:59 +1000 Subject: [PATCH 19/67] eslint configuration file --- ci/.eslintrc.yml | 251 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 ci/.eslintrc.yml diff --git a/ci/.eslintrc.yml b/ci/.eslintrc.yml new file mode 100644 index 0000000000..3040a512c2 --- /dev/null +++ b/ci/.eslintrc.yml @@ -0,0 +1,251 @@ +env: + commonjs: true + es6: true + browser: true + es2021: true + jquery: true +extends: 'eslint:recommended' +parserOptions: + ecmaVersion: 12 +rules: + accessor-pairs: error + array-bracket-newline: 'off' + array-bracket-spacing: + - error + - never + array-callback-return: error + array-element-newline: 'off' + arrow-body-style: error + arrow-parens: + - error + - as-needed + arrow-spacing: 'off' + block-scoped-var: 'off' + block-spacing: 'off' + brace-style: 'off' + camelcase: 'off' + capitalized-comments: 'off' + class-methods-use-this: error + comma-dangle: 'off' + comma-spacing: 'off' + comma-style: + - error + - last + complexity: 'off' + computed-property-spacing: + - error + - never + consistent-return: 'off' + consistent-this: 'off' + curly: 'off' + default-case: 'off' + default-case-last: 'off' + default-param-last: error + dot-location: error + dot-notation: 'off' + eol-last: 'off' + eqeqeq: 'off' + func-call-spacing: error + func-name-matching: error + func-names: 'off' + func-style: + - error + - declaration + function-call-argument-newline: 'off' + function-paren-newline: 'off' + generator-star-spacing: error + grouped-accessor-pairs: error + guard-for-in: 'off' + id-denylist: error + id-length: 'off' + id-match: error + implicit-arrow-linebreak: + - error + - beside + indent: 'off' + init-declarations: error + jsx-quotes: error + key-spacing: 'off' + keyword-spacing: 'off' + line-comment-position: 'off' + linebreak-style: + - error + - unix + lines-around-comment: 'off' + lines-between-class-members: error + max-classes-per-file: error + max-depth: error + max-len: 'off' + max-lines: 'off' + max-lines-per-function: 'off' + max-nested-callbacks: error + max-params: 'off' + max-statements: 'off' + max-statements-per-line: 'off' + multiline-comment-style: 'off' + new-cap: error + new-parens: error + newline-per-chained-call: 'off' + no-alert: 'off' + no-array-constructor: 'off' + no-await-in-loop: error + no-bitwise: error + no-caller: error + no-confusing-arrow: error + no-console: 'off' + no-constructor-return: error + no-continue: 'off' + no-div-regex: error + no-duplicate-imports: error + no-else-return: 'off' + no-empty-function: 'off' + no-eq-null: 'off' + no-eval: error + no-extend-native: error + no-extra-bind: error + no-extra-label: error + no-extra-parens: 'off' + no-floating-decimal: error + no-implicit-coercion: + - error + - boolean: false + disallowTemplateShorthand: false + number: false + string: false + no-implicit-globals: 'off' + no-implied-eval: error + no-inline-comments: 'off' + no-inner-declarations: + - error + - functions + no-invalid-this: error + no-iterator: error + no-label-var: error + no-labels: error + no-lone-blocks: error + no-lonely-if: error + no-loop-func: 'off' + no-loss-of-precision: error + no-magic-numbers: 'off' + no-mixed-operators: + - error + - allowSamePrecedence: true + no-multi-assign: error + no-multi-spaces: + - error + - ignoreEOLComments: true + no-multi-str: error + no-multiple-empty-lines: 'off' + no-negated-condition: 'off' + no-nested-ternary: error + no-new: error + no-new-func: error + no-new-object: error + no-new-wrappers: error + no-nonoctal-decimal-escape: error + no-octal-escape: error + no-param-reassign: 'off' + no-plusplus: 'off' + no-promise-executor-return: error + no-proto: error + no-restricted-exports: error + no-restricted-globals: error + no-restricted-imports: error + no-restricted-properties: error + no-restricted-syntax: error + no-return-assign: error + no-return-await: error + no-script-url: error + no-self-compare: error + no-sequences: 'off' + no-shadow: 'off' + no-tabs: error + no-template-curly-in-string: error + no-ternary: 'off' + no-throw-literal: error + no-trailing-spaces: 'off' + no-undef-init: error + no-undefined: 'off' + no-underscore-dangle: 'off' + no-unmodified-loop-condition: error + no-unneeded-ternary: error + no-unreachable-loop: error + no-unsafe-optional-chaining: error + no-unused-expressions: 'off' + no-use-before-define: 'off' + no-useless-backreference: error + no-useless-call: error + no-useless-computed-key: error + no-useless-concat: error + no-useless-constructor: error + no-useless-rename: error + no-useless-return: error + no-var: 'off' + no-void: error + no-warning-comments: 'off' + no-whitespace-before-property: error + nonblock-statement-body-position: error + object-curly-newline: error + object-curly-spacing: 'off' + object-property-newline: 'off' + object-shorthand: 'off' + one-var: 'off' + one-var-declaration-per-line: error + operator-assignment: + - error + - always + operator-linebreak: error + padded-blocks: 'off' + padding-line-between-statements: error + prefer-arrow-callback: 'off' + prefer-const: 'off' + prefer-destructuring: 'off' + prefer-exponentiation-operator: error + prefer-named-capture-group: 'off' + prefer-numeric-literals: error + prefer-object-spread: error + prefer-promise-reject-errors: error + prefer-regex-literals: error + prefer-rest-params: error + prefer-spread: 'off' + prefer-template: 'off' + quote-props: 'off' + quotes: 'off' + radix: 'off' + require-atomic-updates: error + require-await: error + require-unicode-regexp: 'off' + rest-spread-spacing: error + semi: 'off' + semi-spacing: + - error + - after: true + before: false + semi-style: + - error + - last + sort-imports: error + sort-keys: 'off' + sort-vars: error + space-before-blocks: 'off' + space-before-function-paren: 'off' + space-in-parens: 'off' + space-infix-ops: 'off' + space-unary-ops: 'off' + spaced-comment: 'off' + strict: 'off' + switch-colon-spacing: error + symbol-description: error + template-curly-spacing: + - error + - never + template-tag-spacing: error + unicode-bom: + - error + - never + vars-on-top: 'off' + wrap-regex: error + yield-star-spacing: error + yoda: + - error + - never From f57a31c9b512e85cb513e6aeff7381d396722f6e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 28 Aug 2021 20:46:51 +1000 Subject: [PATCH 20/67] Add a test framework script to pull down rendered javascript files - Use the testing framework so we don't need to spin up a server --- ci/.eslintrc.yml => .eslintrc.yml | 0 .gitignore | 3 + InvenTree/InvenTree/ci_render_js.py | 93 +++++++++++++++++++++++ ci/.gitignore | 1 - ci/pull_js_files.py | 111 ---------------------------- 5 files changed, 96 insertions(+), 112 deletions(-) rename ci/.eslintrc.yml => .eslintrc.yml (100%) create mode 100644 InvenTree/InvenTree/ci_render_js.py delete mode 100644 ci/.gitignore delete mode 100644 ci/pull_js_files.py diff --git a/ci/.eslintrc.yml b/.eslintrc.yml similarity index 100% rename from ci/.eslintrc.yml rename to .eslintrc.yml diff --git a/.gitignore b/.gitignore index f3fa0ac8c1..6f253ee8ea 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,9 @@ secret_key.txt .coverage htmlcov/ +# Temporary javascript files (used for testing) +js_tmp/ + # Development files dev/ diff --git a/InvenTree/InvenTree/ci_render_js.py b/InvenTree/InvenTree/ci_render_js.py new file mode 100644 index 0000000000..c45caf604d --- /dev/null +++ b/InvenTree/InvenTree/ci_render_js.py @@ -0,0 +1,93 @@ +""" +Pull rendered copies of the templated +""" + +from django.http import response +from django.test import TestCase, testcases +from django.contrib.auth import get_user_model + +import os +import pathlib + + +class RenderJavascriptFiles(TestCase): + """ + A unit test to "render" javascript files. + + The server renders templated javascript files, + we need the fully-rendered files for linting and static tests. + """ + + def setUp(self): + + user = get_user_model() + + self.user = user.objects.create_user( + username='testuser', + password='testpassword', + email='user@gmail.com', + ) + + self.client.login(username='testuser', password='testpassword') + + def download_file(self, filename, prefix): + + url = os.path.join(prefix, filename) + + response = self.client.get(url) + + here = os.path.abspath(os.path.dirname(__file__)) + + output_dir = os.path.join( + here, + '..', + '..', + 'js_tmp', + ) + + output_dir = os.path.abspath(output_dir) + + if not os.path.exists(output_dir): + os.mkdir(output_dir) + + output_file = os.path.join( + output_dir, + filename, + ) + + with open(output_file, 'wb') as output: + output.write(response.content) + + def download_files(self, subdir, prefix): + here = os.path.abspath(os.path.dirname(__file__)) + + js_template_dir = os.path.join( + here, + '..', + 'templates', + 'js', + ) + + directory = os.path.join(js_template_dir, subdir) + + directory = os.path.abspath(directory) + + js_files = pathlib.Path(directory).rglob('*.js') + + for f in js_files: + js = os.path.basename(f) + + self.download_file(js, prefix) + + def test_render_files(self): + """ + Look for all javascript files + """ + + self.download_files('translated', '/js/i18n') + self.download_files('dynamic', '/js/dynamic') + + + + + diff --git a/ci/.gitignore b/ci/.gitignore deleted file mode 100644 index b8ba69cb91..0000000000 --- a/ci/.gitignore +++ /dev/null @@ -1 +0,0 @@ -js_tmp/ \ No newline at end of file diff --git a/ci/pull_js_files.py b/ci/pull_js_files.py deleted file mode 100644 index b69268e0e2..0000000000 --- a/ci/pull_js_files.py +++ /dev/null @@ -1,111 +0,0 @@ -""" -Pull 'rendered' copies of the templated JS files down from the InvenTree server. - -These files can then be used for linting and unit testing -""" - -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -import sys -import re -import os -import json -import pathlib -import argparse -import requests -from requests.auth import HTTPBasicAuth - - -here = os.path.abspath(os.path.dirname(__file__)) -js_template_dir = os.path.abspath(os.path.join(here, '..', 'InvenTree', 'templates', 'js')) - -js_tmp_dir = os.path.join(here, 'js_tmp') - -def get_token(server, username, password): - - url = os.path.join( - server, - 'api', - 'user', - 'token', - ) - - auth = HTTPBasicAuth(username, password) - - response = requests.get(url, auth=auth, allow_redirects=False) - - data = json.loads(response.text) - - return data['token'] - - -def download_file(url, filename, token): - """ - Download a single javascript file - """ - - print(f"Downloading '{url}'") - - headers = { - 'AUTHORIZATION': f'Token {token}' - } - - response = requests.get( - url, - allow_redirects=False, - headers=headers - ) - - output_file = os.path.join( - js_tmp_dir, - filename, - ) - - with open(output_file, 'wb') as output: - output.write(response.content) - - -def download_js_files(subdir, url, token): - """ - Returns a flattened list of all javascript files - """ - - d = os.path.join(js_template_dir, subdir) - - files = pathlib.Path(d).rglob('*.js') - - for filename in files: - js = os.path.basename(filename) - - js_url = os.path.join(url, js) - - download_file(js_url, js, token) - -if __name__ == '__main__': - - parser = argparse.ArgumentParser("Download JavaScript files") - - parser.add_argument('-s', '--server', help='InvenTree server', action='store') - parser.add_argument('-u', '--username', help='Username', action='store') - parser.add_argument('-p', '--password', help='password', action='store') - - args = parser.parse_args() - - if not os.path.exists(js_tmp_dir): - os.mkdir(js_tmp_dir) - - auth = HTTPBasicAuth(args.username, args.password) - - # Get an auth token from the server - token = get_token(args.server, args.username, args.password) - - # Dynamic javascript files - dynamic_url = os.path.join(args.server, 'js', 'dynamic') - - download_js_files('dynamic', dynamic_url, token) - - # Translated JS files - i18n_url = os.path.join(args.server, 'js', 'i18n') - - download_js_files("translated", i18n_url, token) \ No newline at end of file From d0ccf8647de2741206dea4af408f5999b850fd1b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 28 Aug 2021 20:59:41 +1000 Subject: [PATCH 21/67] Add js linting to github workflow --- .github/workflows/javascript.yaml | 12 ++++++++++-- InvenTree/InvenTree/ci_render_js.py | 15 +++++++++++---- tasks.py | 9 +++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/.github/workflows/javascript.yaml b/.github/workflows/javascript.yaml index 908a87e31c..d59098da75 100644 --- a/.github/workflows/javascript.yaml +++ b/.github/workflows/javascript.yaml @@ -18,11 +18,19 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + INVENTREE_DB_ENGINE: sqlite3 + INVENTREE_DB_NAME: inventree steps: + - name: Install node.js + uses: actions/setup-node@v2 + - run: npm install - name: Checkout Code uses: actions/checkout@v2 - - name: Check Files + - name: Check Templated Files run: | cd ci python check_js_templates.py - \ No newline at end of file + - name: Lint Javascript Files + run: | + invoke render-js-files + npx eslint js_tmp/*.js \ No newline at end of file diff --git a/InvenTree/InvenTree/ci_render_js.py b/InvenTree/InvenTree/ci_render_js.py index c45caf604d..62e3fc4667 100644 --- a/InvenTree/InvenTree/ci_render_js.py +++ b/InvenTree/InvenTree/ci_render_js.py @@ -74,20 +74,27 @@ class RenderJavascriptFiles(TestCase): js_files = pathlib.Path(directory).rglob('*.js') + n = 0 + for f in js_files: js = os.path.basename(f) self.download_file(js, prefix) + n += 1 + + return n + def test_render_files(self): """ Look for all javascript files """ - self.download_files('translated', '/js/i18n') - self.download_files('dynamic', '/js/dynamic') - - + n = 0 + print("Rendering javascript files...") + n += self.download_files('translated', '/js/i18n') + n += self.download_files('dynamic', '/js/dynamic') + print(f"Rendered {n} javascript files.") diff --git a/tasks.py b/tasks.py index 3de0241c07..1abbf23bc6 100644 --- a/tasks.py +++ b/tasks.py @@ -457,3 +457,12 @@ def server(c, address="127.0.0.1:8000"): """ manage(c, "runserver {address}".format(address=address), pty=True) + + +@task +def render_js_files(c): + """ + Render templated javascript files (used for static testing). + """ + + manage(c, "test InvenTree.ci_render_js") From 09a7a7d2e48011336eb3457dee06acb6ccdfc6b2 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 28 Aug 2021 21:03:09 +1000 Subject: [PATCH 22/67] Install required files --- .github/workflows/javascript.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/javascript.yaml b/.github/workflows/javascript.yaml index d59098da75..6adb42e3e4 100644 --- a/.github/workflows/javascript.yaml +++ b/.github/workflows/javascript.yaml @@ -26,6 +26,16 @@ jobs: - run: npm install - name: Checkout Code uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install gettext + pip3 install invoke + invoke install - name: Check Templated Files run: | cd ci From f7c515b889c521dbdb63579addc6f71ecca850d3 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 28 Aug 2021 21:05:55 +1000 Subject: [PATCH 23/67] add "invoke static" step --- .github/workflows/javascript.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/javascript.yaml b/.github/workflows/javascript.yaml index 6adb42e3e4..caf82505f0 100644 --- a/.github/workflows/javascript.yaml +++ b/.github/workflows/javascript.yaml @@ -36,6 +36,7 @@ jobs: sudo apt-get install gettext pip3 install invoke invoke install + invoke static - name: Check Templated Files run: | cd ci From e85ddf3579528223fa7f1903debe4615045c89d5 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 28 Aug 2021 21:10:31 +1000 Subject: [PATCH 24/67] Add required env vars --- .github/workflows/javascript.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/javascript.yaml b/.github/workflows/javascript.yaml index caf82505f0..110f5ffbe1 100644 --- a/.github/workflows/javascript.yaml +++ b/.github/workflows/javascript.yaml @@ -20,6 +20,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} INVENTREE_DB_ENGINE: sqlite3 INVENTREE_DB_NAME: inventree + INVENTREE_MEDIA_ROOT: ./media + INVENTREE_STATIC_ROOT: ./static steps: - name: Install node.js uses: actions/setup-node@v2 From 0620e656a060a066b152392de1ce5dc6d1307cf7 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 28 Aug 2021 22:12:41 +1000 Subject: [PATCH 25/67] Fix linting errors or tables.js --- .eslintrc.yml | 1 + InvenTree/templates/js/dynamic/inventree.js | 65 ++++----------------- InvenTree/templates/js/dynamic/settings.js | 4 +- InvenTree/templates/js/translated/tables.js | 28 ++++----- 4 files changed, 28 insertions(+), 70 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 3040a512c2..58952de736 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -7,6 +7,7 @@ env: extends: 'eslint:recommended' parserOptions: ecmaVersion: 12 + sourceType: module rules: accessor-pairs: error array-bracket-newline: 'off' diff --git a/InvenTree/templates/js/dynamic/inventree.js b/InvenTree/templates/js/dynamic/inventree.js index acfd57762c..a93d6efc0f 100644 --- a/InvenTree/templates/js/dynamic/inventree.js +++ b/InvenTree/templates/js/dynamic/inventree.js @@ -1,6 +1,6 @@ {% load inventree_extras %} -function attachClipboard(selector, containerselector, textElement) { +export function attachClipboard(selector, containerselector, textElement) { // set container if (containerselector){ containerselector = document.getElementById(containerselector); @@ -28,7 +28,7 @@ function attachClipboard(selector, containerselector, textElement) { } -function inventreeDocReady() { +export function inventreeDocReady() { /* Run this function when the HTML document is loaded. * This will be called for every page that extends "base.html" */ @@ -132,7 +132,7 @@ function inventreeDocReady() { }); } -function isFileTransfer(transfer) { +export function isFileTransfer(transfer) { /* Determine if a transfer (e.g. drag-and-drop) is a file transfer */ @@ -140,27 +140,7 @@ function isFileTransfer(transfer) { } -function isOnlineTransfer(transfer) { - /* Determine if a drag-and-drop transfer is from another website. - * e.g. dragged from another browser window - */ - - return transfer.items.length > 0; -} - - -function getImageUrlFromTransfer(transfer) { - /* Extract external image URL from a drag-and-dropped image - */ - - var url = transfer.getData('text/html').match(/src\s*=\s*"(.+?)"/)[1]; - - console.log('Image URL: ' + url); - - return url; -} - -function makeIconBadge(icon, title) { +export function makeIconBadge(icon, title) { // Construct an 'icon badge' which floats to the right of an object var html = ``; @@ -168,7 +148,8 @@ function makeIconBadge(icon, title) { return html; } -function makeIconButton(icon, cls, pk, title, options={}) { + +export function makeIconButton(icon, cls, pk, title, options={}) { // Construct an 'icon button' using the fontawesome set var classes = `btn btn-default btn-glyph ${cls}`; @@ -190,7 +171,7 @@ function makeIconButton(icon, cls, pk, title, options={}) { return html; } -function makeProgressBar(value, maximum, opts={}) { +export function makeProgressBar(value, maximum, opts={}) { /* * Render a progessbar! * @@ -258,7 +239,7 @@ function makeProgressBar(value, maximum, opts={}) { } -function enableDragAndDrop(element, url, options) { +export function enableDragAndDrop(element, url, options) { /* Enable drag-and-drop file uploading for a given element. Params: @@ -315,7 +296,7 @@ function enableDragAndDrop(element, url, options) { }); } -function imageHoverIcon(url) { +export function imageHoverIcon(url) { /* Render a small thumbnail icon for an image. * On mouseover, display a full-size version of the image */ @@ -334,7 +315,7 @@ function imageHoverIcon(url) { return html; } -function inventreeSave(name, value) { +export function inventreeSave(name, value) { /* * Save a key:value pair to local storage */ @@ -343,7 +324,7 @@ function inventreeSave(name, value) { localStorage.setItem(key, value); } -function inventreeLoad(name, defaultValue) { +export function inventreeLoad(name, defaultValue) { /* * Retrieve a key:value pair from local storage */ @@ -358,27 +339,3 @@ function inventreeLoad(name, defaultValue) { return value; } } - -function inventreeLoadInt(name) { - /* - * Retrieve a value from local storage, and attempt to cast to integer - */ - - var data = inventreeLoad(name); - - return parseInt(data, 10); -} - -function inventreeLoadFloat(name) { - - var data = inventreeLoad(name); - - return parseFloat(data); -} - -function inventreeDel(name) { - - var key = 'inventree-' + name; - - localStorage.removeItem(key); -} \ No newline at end of file diff --git a/InvenTree/templates/js/dynamic/settings.js b/InvenTree/templates/js/dynamic/settings.js index 60172ead64..21ae8b5330 100644 --- a/InvenTree/templates/js/dynamic/settings.js +++ b/InvenTree/templates/js/dynamic/settings.js @@ -3,7 +3,7 @@ {% user_settings request.user as USER_SETTINGS %} -var user_settings = { +export const user_settings = { {% for key, value in USER_SETTINGS.items %} {{ key }}: {% primitive_to_javascript value %}, {% endfor %} @@ -11,7 +11,7 @@ var user_settings = { {% global_settings as GLOBAL_SETTINGS %} -var global_settings = { +export const global_settings = { {% for key, value in GLOBAL_SETTINGS.items %} {{ key }}: {% primitive_to_javascript value %}, {% endfor %} diff --git a/InvenTree/templates/js/translated/tables.js b/InvenTree/templates/js/translated/tables.js index 88d9a5f99a..2b817f1bd9 100644 --- a/InvenTree/templates/js/translated/tables.js +++ b/InvenTree/templates/js/translated/tables.js @@ -1,30 +1,30 @@ {% load i18n %} +import { inventreeLoad, inventreeSave } from '{% url "inventree.js" %}'; -function reloadtable(table) { + +export function reloadtable(table) { $(table).bootstrapTable('refresh'); } -function editButton(url, text='Edit') { +export function editButton(url, text='{% trans "Edit" %}') { return ""; } -function deleteButton(url, text='Delete') { +export function deleteButton(url, text='{% trans "Delete" %}') { return ""; } -function renderLink(text, url, options={}) { +export function renderLink(text, url, options={}) { if (url === null || url === undefined || url === '') { return text; } var max_length = options.max_length || -1; - var remove_http = options.remove_http || false; - // Shorten the displayed length if required if ((max_length > 0) && (text.length > max_length)) { var slice_length = (max_length - 3) / 2; @@ -39,14 +39,14 @@ function renderLink(text, url, options={}) { } -function enableButtons(elements, enabled) { +export function enableButtons(elements, enabled) { for (let item of elements) { $(item).prop('disabled', !enabled); } } -function linkButtonsToSelection(table, buttons) { +export function linkButtonsToSelection(table, buttons) { /* Link a bootstrap-table object to one or more buttons. * The buttons will only be enabled if there is at least one row selected */ @@ -59,7 +59,7 @@ function linkButtonsToSelection(table, buttons) { enableButtons(buttons, table.bootstrapTable('getSelections').length > 0); // Add a callback - table.on('check.bs.table uncheck.bs.table check-some.bs.table uncheck-some.bs.table check-all.bs.table uncheck-all.bs.table', function(row) { + table.on('check.bs.table uncheck.bs.table check-some.bs.table uncheck-some.bs.table check-all.bs.table uncheck-all.bs.table', function() { enableButtons(buttons, table.bootstrapTable('getSelections').length > 0); }); } @@ -74,7 +74,7 @@ function isNumeric(n) { * Reload a table which has already been made into a bootstrap table. * New filters can be optionally provided, to change the query params. */ -function reloadTableFilters(table, filters) { +export function reloadTableFilters(table, filters) { // Simply perform a refresh if (filters == null) { @@ -88,8 +88,8 @@ function reloadTableFilters(table, filters) { // Construct a new list of filters to use for the query var params = {}; - for (var key in filters) { - params[key] = filters[key]; + for (var k in filters) { + params[k] = filters[k]; } // Original query params will override @@ -220,7 +220,7 @@ $.fn.inventreeTable = function(options) { }; // Callback when a column is changed - options.onColumnSwitch = function(field, checked) { + options.onColumnSwitch = function() { var columns = table.bootstrapTable('getVisibleColumns'); @@ -263,7 +263,7 @@ $.fn.inventreeTable = function(options) { } } -function customGroupSorter(sortName, sortOrder, sortData) { +export function customGroupSorter(sortName, sortOrder, sortData) { var order = sortOrder === 'desc' ? -1 : 1; From 140a2092c84659ef36c451cab9a27a2801194501 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 28 Aug 2021 22:18:20 +1000 Subject: [PATCH 26/67] fixes for table_filters.js --- InvenTree/templates/js/translated/table_filters.js | 4 +++- InvenTree/templates/status_codes.html | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/InvenTree/templates/js/translated/table_filters.js b/InvenTree/templates/js/translated/table_filters.js index 9e173a7b37..cb8d9047d7 100644 --- a/InvenTree/templates/js/translated/table_filters.js +++ b/InvenTree/templates/js/translated/table_filters.js @@ -2,6 +2,8 @@ {% load status_codes %} {% load inventree_extras %} +import { global_settings } from '{% url "settings.js" %}'; + {% include "status_codes.html" with label='stock' options=StockStatus.list %} {% include "status_codes.html" with label='stockHistory' options=StockHistoryCode.list %} {% include "status_codes.html" with label='build' options=BuildStatus.list %} @@ -9,7 +11,7 @@ {% include "status_codes.html" with label='salesOrder' options=SalesOrderStatus.list %} -function getAvailableTableFilters(tableKey) { +export function getAvailableTableFilters(tableKey) { tableKey = tableKey.toLowerCase(); diff --git a/InvenTree/templates/status_codes.html b/InvenTree/templates/status_codes.html index e7bc2e951c..cfca5ea77b 100644 --- a/InvenTree/templates/status_codes.html +++ b/InvenTree/templates/status_codes.html @@ -1,12 +1,13 @@ /* * Status codes for the {{ label }} model. */ -var {{ label }}Codes = { +export const {{ label }}Codes = { {% for opt in options %}'{{ opt.key }}': { key: '{{ opt.key }}', value: '{{ opt.value }}',{% if opt.color %} label: 'label-{{ opt.color }}',{% endif %} - },{% endfor %} + }, + {% endfor %} }; /* @@ -14,7 +15,7 @@ var {{ label }}Codes = { * Uses the values specified in "status_codes.py" * This function is generated by the "status_codes.html" template */ -function {{ label }}StatusDisplay(key, options={}) { +export function {{ label }}StatusDisplay(key, options={}) { key = String(key); From 24ca1c56412840694c5e38b6c637ea7ff40d27cb Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 13:06:02 +1000 Subject: [PATCH 27/67] Add "helpers.js" for common functions --- InvenTree/InvenTree/urls.py | 1 + InvenTree/templates/base.html | 1 + InvenTree/templates/js/dynamic/inventree.js | 129 +------------- InvenTree/templates/js/dynamic/settings.js | 6 +- InvenTree/templates/js/translated/helpers.js | 158 ++++++++++++++++++ .../templates/js/translated/table_filters.js | 2 +- InvenTree/templates/js/translated/tables.js | 16 +- 7 files changed, 177 insertions(+), 136 deletions(-) create mode 100644 InvenTree/templates/js/translated/helpers.js diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index 71f6388c68..7d51c6a4cf 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -111,6 +111,7 @@ translated_javascript_urls = [ url(r'^company.js', DynamicJsView.as_view(template_name='js/translated/company.js'), name='company.js'), url(r'^filters.js', DynamicJsView.as_view(template_name='js/translated/filters.js'), name='filters.js'), url(r'^forms.js', DynamicJsView.as_view(template_name='js/translated/forms.js'), name='forms.js'), + url(r'^helpers.js', DynamicJsView.as_view(template_name='js/translated/helpers.js'), name='helpers.js'), url(r'^label.js', DynamicJsView.as_view(template_name='js/translated/label.js'), name='label.js'), url(r'^model_renderers.js', DynamicJsView.as_view(template_name='js/translated/model_renderers.js'), name='model_renderers.js'), url(r'^modals.js', DynamicJsView.as_view(template_name='js/translated/modals.js'), name='modals.js'), diff --git a/InvenTree/templates/base.html b/InvenTree/templates/base.html index 6f0cbe4acd..c2316ce4b0 100644 --- a/InvenTree/templates/base.html +++ b/InvenTree/templates/base.html @@ -160,6 +160,7 @@ + diff --git a/InvenTree/templates/js/dynamic/inventree.js b/InvenTree/templates/js/dynamic/inventree.js index a93d6efc0f..9813c30207 100644 --- a/InvenTree/templates/js/dynamic/inventree.js +++ b/InvenTree/templates/js/dynamic/inventree.js @@ -1,6 +1,6 @@ {% load inventree_extras %} -export function attachClipboard(selector, containerselector, textElement) { +function attachClipboard(selector, containerselector, textElement) { // set container if (containerselector){ containerselector = document.getElementById(containerselector); @@ -28,7 +28,7 @@ export function attachClipboard(selector, containerselector, textElement) { } -export function inventreeDocReady() { +function inventreeDocReady() { /* Run this function when the HTML document is loaded. * This will be called for every page that extends "base.html" */ @@ -132,7 +132,7 @@ export function inventreeDocReady() { }); } -export function isFileTransfer(transfer) { +function isFileTransfer(transfer) { /* Determine if a transfer (e.g. drag-and-drop) is a file transfer */ @@ -140,106 +140,7 @@ export function isFileTransfer(transfer) { } -export function makeIconBadge(icon, title) { - // Construct an 'icon badge' which floats to the right of an object - - var html = ``; - - return html; -} - - -export function makeIconButton(icon, cls, pk, title, options={}) { - // Construct an 'icon button' using the fontawesome set - - var classes = `btn btn-default btn-glyph ${cls}`; - - var id = `${cls}-${pk}`; - - var html = ''; - - var extraProps = ''; - - if (options.disabled) { - extraProps += "disabled='true' "; - } - - html += ``; - - return html; -} - -export function makeProgressBar(value, maximum, opts={}) { - /* - * Render a progessbar! - * - * @param value is the current value of the progress bar - * @param maximum is the maximum value of the progress bar - */ - - var options = opts || {}; - - value = parseFloat(value); - - var percent = 100; - - // Prevent div-by-zero or null value - if (maximum && maximum > 0) { - maximum = parseFloat(maximum); - percent = parseInt(value / maximum * 100); - } - - if (percent > 100) { - percent = 100; - } - - var extraclass = ''; - - if (value > maximum) { - extraclass='progress-bar-over'; - } else if (value < maximum) { - extraclass = 'progress-bar-under'; - } - - var style = options.style || ''; - - var text = ''; - - if (style == 'percent') { - // Display e.g. "50%" - - text = `${percent}%`; - } else if (style == 'max') { - // Display just the maximum value - text = `${maximum}`; - } else if (style == 'value') { - // Display just the current value - text = `${value}`; - } else if (style == 'blank') { - // No display! - text = ''; - } else { - /* Default style - * Display e.g. "5 / 10" - */ - - text = `${value} / ${maximum}`; - } - - var id = options.id || 'progress-bar'; - - return ` -
-
-
${text}
-
- `; -} - - -export function enableDragAndDrop(element, url, options) { +function enableDragAndDrop(element, url, options) { /* Enable drag-and-drop file uploading for a given element. Params: @@ -296,26 +197,8 @@ export function enableDragAndDrop(element, url, options) { }); } -export function imageHoverIcon(url) { - /* Render a small thumbnail icon for an image. - * On mouseover, display a full-size version of the image - */ - if (!url) { - url = '/static/img/blank_image.png'; - } - - var html = ` - - - - - `; - - return html; -} - -export function inventreeSave(name, value) { +function inventreeSave(name, value) { /* * Save a key:value pair to local storage */ @@ -324,7 +207,7 @@ export function inventreeSave(name, value) { localStorage.setItem(key, value); } -export function inventreeLoad(name, defaultValue) { +function inventreeLoad(name, defaultValue) { /* * Retrieve a key:value pair from local storage */ diff --git a/InvenTree/templates/js/dynamic/settings.js b/InvenTree/templates/js/dynamic/settings.js index 21ae8b5330..49d2650bef 100644 --- a/InvenTree/templates/js/dynamic/settings.js +++ b/InvenTree/templates/js/dynamic/settings.js @@ -2,16 +2,14 @@ // InvenTree settings {% user_settings request.user as USER_SETTINGS %} - -export const user_settings = { +const user_settings = { {% for key, value in USER_SETTINGS.items %} {{ key }}: {% primitive_to_javascript value %}, {% endfor %} }; {% global_settings as GLOBAL_SETTINGS %} - -export const global_settings = { +const global_settings = { {% for key, value in GLOBAL_SETTINGS.items %} {{ key }}: {% primitive_to_javascript value %}, {% endfor %} diff --git a/InvenTree/templates/js/translated/helpers.js b/InvenTree/templates/js/translated/helpers.js new file mode 100644 index 0000000000..446df773fe --- /dev/null +++ b/InvenTree/templates/js/translated/helpers.js @@ -0,0 +1,158 @@ +/* + * A set of simple helper functions + */ + + +function blankImage() { + return `/static/img/blank_image.png`; +} + +/* Render a small thumbnail icon for an image. + * On mouseover, display a full-size version of the image + */ +function imageHoverIcon(url) { + + if (!url) { + url = blankImage(); + } + + var html = ` + + + + + `; + + return html; +} + + +// Render a select2 thumbnail image +function select2Thumbnail(image) { + if (!image) { + image = blankImage(); + } + + return ``; +} + + +function makeIconBadge(icon, title) { + // Construct an 'icon badge' which floats to the right of an object + + var html = ``; + + return html; +} + + +function makeIconButton(icon, cls, pk, title, options={}) { + // Construct an 'icon button' using the fontawesome set + + var classes = `btn btn-default btn-glyph ${cls}`; + + var id = `${cls}-${pk}`; + + var html = ''; + + var extraProps = ''; + + if (options.disabled) { + extraProps += "disabled='true' "; + } + + html += ``; + + return html; +} + + +/* + * Render a progessbar! + * + * @param value is the current value of the progress bar + * @param maximum is the maximum value of the progress bar + */ +function makeProgressBar(value, maximum, opts={}) { + + var options = opts || {}; + + value = parseFloat(value); + + var percent = 100; + + // Prevent div-by-zero or null value + if (maximum && maximum > 0) { + maximum = parseFloat(maximum); + percent = parseInt(value / maximum * 100); + } + + if (percent > 100) { + percent = 100; + } + + var extraclass = ''; + + if (value > maximum) { + extraclass='progress-bar-over'; + } else if (value < maximum) { + extraclass = 'progress-bar-under'; + } + + var style = options.style || ''; + + var text = ''; + + if (style == 'percent') { + // Display e.g. "50%" + + text = `${percent}%`; + } else if (style == 'max') { + // Display just the maximum value + text = `${maximum}`; + } else if (style == 'value') { + // Display just the current value + text = `${value}`; + } else if (style == 'blank') { + // No display! + text = ''; + } else { + /* Default style + * Display e.g. "5 / 10" + */ + + text = `${value} / ${maximum}`; + } + + var id = options.id || 'progress-bar'; + + return ` +
+
+
${text}
+
+ `; +} + + +function renderLink(text, url, options={}) { + if (url === null || url === undefined || url === '') { + return text; + } + + var max_length = options.max_length || -1; + + // Shorten the displayed length if required + if ((max_length > 0) && (text.length > max_length)) { + var slice_length = (max_length - 3) / 2; + + var text_start = text.slice(0, slice_length); + var text_end = text.slice(-slice_length); + + text = `${text_start}...${text_end}`; + } + + return '' + text + ''; +} \ No newline at end of file diff --git a/InvenTree/templates/js/translated/table_filters.js b/InvenTree/templates/js/translated/table_filters.js index cb8d9047d7..423c3c989f 100644 --- a/InvenTree/templates/js/translated/table_filters.js +++ b/InvenTree/templates/js/translated/table_filters.js @@ -11,7 +11,7 @@ import { global_settings } from '{% url "settings.js" %}'; {% include "status_codes.html" with label='salesOrder' options=SalesOrderStatus.list %} -export function getAvailableTableFilters(tableKey) { +function getAvailableTableFilters(tableKey) { tableKey = tableKey.toLowerCase(); diff --git a/InvenTree/templates/js/translated/tables.js b/InvenTree/templates/js/translated/tables.js index 2b817f1bd9..a1249d0176 100644 --- a/InvenTree/templates/js/translated/tables.js +++ b/InvenTree/templates/js/translated/tables.js @@ -3,22 +3,22 @@ import { inventreeLoad, inventreeSave } from '{% url "inventree.js" %}'; -export function reloadtable(table) { +function reloadtable(table) { $(table).bootstrapTable('refresh'); } -export function editButton(url, text='{% trans "Edit" %}') { +function editButton(url, text='{% trans "Edit" %}') { return ""; } -export function deleteButton(url, text='{% trans "Delete" %}') { +function deleteButton(url, text='{% trans "Delete" %}') { return ""; } -export function renderLink(text, url, options={}) { +function renderLink(text, url, options={}) { if (url === null || url === undefined || url === '') { return text; } @@ -39,14 +39,14 @@ export function renderLink(text, url, options={}) { } -export function enableButtons(elements, enabled) { +function enableButtons(elements, enabled) { for (let item of elements) { $(item).prop('disabled', !enabled); } } -export function linkButtonsToSelection(table, buttons) { +function linkButtonsToSelection(table, buttons) { /* Link a bootstrap-table object to one or more buttons. * The buttons will only be enabled if there is at least one row selected */ @@ -74,7 +74,7 @@ function isNumeric(n) { * Reload a table which has already been made into a bootstrap table. * New filters can be optionally provided, to change the query params. */ -export function reloadTableFilters(table, filters) { +function reloadTableFilters(table, filters) { // Simply perform a refresh if (filters == null) { @@ -263,7 +263,7 @@ $.fn.inventreeTable = function(options) { } } -export function customGroupSorter(sortName, sortOrder, sortData) { +function customGroupSorter(sortName, sortOrder, sortData) { var order = sortOrder === 'desc' ? -1 : 1; From 5d45fce240d5f2446f09a907ca40ae8bf4d15ce6 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 13:10:58 +1000 Subject: [PATCH 28/67] remove import / export calls --- InvenTree/templates/js/translated/helpers.js | 11 +++++++++++ InvenTree/templates/js/translated/part.js | 9 --------- InvenTree/templates/js/translated/table_filters.js | 2 -- InvenTree/templates/js/translated/tables.js | 2 -- InvenTree/templates/status_codes.html | 4 ++-- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/InvenTree/templates/js/translated/helpers.js b/InvenTree/templates/js/translated/helpers.js index 446df773fe..a66a634ece 100644 --- a/InvenTree/templates/js/translated/helpers.js +++ b/InvenTree/templates/js/translated/helpers.js @@ -1,3 +1,14 @@ +{% load i18n %} + +function yesNoLabel(value) { + if (value) { + return `{% trans "YES" %}`; + } else { + return `{% trans "NO" %}`; + } +} + + /* * A set of simple helper functions */ diff --git a/InvenTree/templates/js/translated/part.js b/InvenTree/templates/js/translated/part.js index 052a33ec5c..98a9be5358 100644 --- a/InvenTree/templates/js/translated/part.js +++ b/InvenTree/templates/js/translated/part.js @@ -5,15 +5,6 @@ * Requires api.js to be loaded first */ -function yesNoLabel(value) { - if (value) { - return `{% trans "YES" %}`; - } else { - return `{% trans "NO" %}`; - } -} - - function partGroups(options={}) { return { diff --git a/InvenTree/templates/js/translated/table_filters.js b/InvenTree/templates/js/translated/table_filters.js index 423c3c989f..9e173a7b37 100644 --- a/InvenTree/templates/js/translated/table_filters.js +++ b/InvenTree/templates/js/translated/table_filters.js @@ -2,8 +2,6 @@ {% load status_codes %} {% load inventree_extras %} -import { global_settings } from '{% url "settings.js" %}'; - {% include "status_codes.html" with label='stock' options=StockStatus.list %} {% include "status_codes.html" with label='stockHistory' options=StockHistoryCode.list %} {% include "status_codes.html" with label='build' options=BuildStatus.list %} diff --git a/InvenTree/templates/js/translated/tables.js b/InvenTree/templates/js/translated/tables.js index a1249d0176..c3f938f9c8 100644 --- a/InvenTree/templates/js/translated/tables.js +++ b/InvenTree/templates/js/translated/tables.js @@ -1,7 +1,5 @@ {% load i18n %} -import { inventreeLoad, inventreeSave } from '{% url "inventree.js" %}'; - function reloadtable(table) { $(table).bootstrapTable('refresh'); diff --git a/InvenTree/templates/status_codes.html b/InvenTree/templates/status_codes.html index cfca5ea77b..453a7914e2 100644 --- a/InvenTree/templates/status_codes.html +++ b/InvenTree/templates/status_codes.html @@ -1,7 +1,7 @@ /* * Status codes for the {{ label }} model. */ -export const {{ label }}Codes = { +const {{ label }}Codes = { {% for opt in options %}'{{ opt.key }}': { key: '{{ opt.key }}', value: '{{ opt.value }}',{% if opt.color %} @@ -15,7 +15,7 @@ export const {{ label }}Codes = { * Uses the values specified in "status_codes.py" * This function is generated by the "status_codes.html" template */ -export function {{ label }}StatusDisplay(key, options={}) { +function {{ label }}StatusDisplay(key, options={}) { key = String(key); From 65874447edc5c25e11a6b4e6fefd3b45133e416e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 13:13:12 +1000 Subject: [PATCH 29/67] bug fix --- InvenTree/templates/js/translated/helpers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/templates/js/translated/helpers.js b/InvenTree/templates/js/translated/helpers.js index a66a634ece..2382633e8d 100644 --- a/InvenTree/templates/js/translated/helpers.js +++ b/InvenTree/templates/js/translated/helpers.js @@ -29,8 +29,8 @@ function imageHoverIcon(url) { var html = ` - - + + `; From c846aeb60fc16cb17fbdcd98fca07ae85d6a5526 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 14:07:34 +1000 Subject: [PATCH 30/67] stock.js --- .eslintrc.yml | 4 +- InvenTree/templates/js/translated/helpers.js | 26 ++- InvenTree/templates/js/translated/stock.js | 160 ++++++++++++------ .../templates/js/translated/table_filters.js | 13 ++ InvenTree/templates/js/translated/tables.js | 22 +-- 5 files changed, 157 insertions(+), 68 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 58952de736..966a822732 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1,5 +1,5 @@ env: - commonjs: true + commonjs: false es6: true browser: true es2021: true @@ -7,7 +7,7 @@ env: extends: 'eslint:recommended' parserOptions: ecmaVersion: 12 - sourceType: module + sourceType: 'script' rules: accessor-pairs: error array-bracket-newline: 'off' diff --git a/InvenTree/templates/js/translated/helpers.js b/InvenTree/templates/js/translated/helpers.js index 2382633e8d..d65d201da3 100644 --- a/InvenTree/templates/js/translated/helpers.js +++ b/InvenTree/templates/js/translated/helpers.js @@ -1,5 +1,18 @@ {% load i18n %} +/* exported + blankImage, + deleteButton, + editButton, + imageHoverIcon, + makeIconBadge, + makeIconButton, + makeProgressBar, + renderLink, + select2Thumbnail, + yesNoLabel, +*/ + function yesNoLabel(value) { if (value) { return `{% trans "YES" %}`; @@ -9,9 +22,14 @@ function yesNoLabel(value) { } -/* - * A set of simple helper functions - */ +function editButton(url, text='{% trans "Edit" %}') { + return ""; +} + + +function deleteButton(url, text='{% trans "Delete" %}') { + return ""; +} function blankImage() { @@ -165,5 +183,5 @@ function renderLink(text, url, options={}) { text = `${text_start}...${text_end}`; } - return '' + text + ''; + return `${text}`; } \ No newline at end of file diff --git a/InvenTree/templates/js/translated/stock.js b/InvenTree/templates/js/translated/stock.js index 99c3824cac..7b7c1b6272 100644 --- a/InvenTree/templates/js/translated/stock.js +++ b/InvenTree/templates/js/translated/stock.js @@ -2,6 +2,63 @@ {% load inventree_extras %} {% load status_codes %} +/* globals + attachSelect, + attachToggle, + blankImage, + enableField, + clearField, + clearFieldOptions, + closeModal, + constructFormBody, + constructNumberInput, + createNewModal, + getFormFieldValue, + global_settings, + handleFormErrors, + imageHoverIcon, + inventreeDelete, + inventreeGet, + inventreePut, + launchModalForm, + linkButtonsToSelection, + loadTableFilters, + makeIconBadge, + makeIconButton, + makeOptionsList, + makePartIcons, + modalEnable, + modalSetContent, + modalSetTitle, + modalSubmit, + moment, + openModal, + printStockItemLabels, + printTestReports, + renderLink, + reloadFieldOptions, + scanItemsIntoLocation, + showAlertDialog, + setFieldValue, + setupFilterList, + showApiError, + stockStatusDisplay, +*/ + +/* exported + createNewStockItem, + exportStock, + loadInstalledInTable, + loadStockLocationTable, + loadStockTable, + loadStockTestResultsTable, + loadStockTrackingTable, + loadTableFilters, + locationFields, + removeStockRow, + stockStatusCodes, +*/ + function locationFields() { return { @@ -209,7 +266,7 @@ function adjustStock(action, items, options={}) { title: readonly ? '{% trans "Quantity cannot be adjusted for serialized stock" %}' : '{% trans "Specify stock quantity" %}', } ) - }; + } var buttons = `
`; @@ -283,7 +340,7 @@ function adjustStock(action, items, options={}) { confirm: true, confirmMessage: '{% trans "Confirm stock adjustment" %}', modal: modal, - onSubmit: function(fields, opts) { + onSubmit: function(fields) { // "Delete" action gets handled differently if (action == 'delete') { @@ -327,7 +384,7 @@ function adjustStock(action, items, options={}) { }); // Add in extra field data - for (field_name in extraFields) { + for (var field_name in extraFields) { data[field_name] = getFormFieldValue( field_name, fields[field_name], @@ -342,7 +399,7 @@ function adjustStock(action, items, options={}) { data, { method: 'POST', - success: function(response, status) { + success: function() { // Destroy the modal window $(modal).modal('hide'); @@ -544,7 +601,7 @@ function loadStockTestResultsTable(table, options) { { success: function(data) { // Iterate through the returned test data - data.forEach(function(item, index) { + data.forEach(function(item) { var match = false; var override = false; @@ -683,8 +740,8 @@ function loadStockTable(table, options) { var original = {}; - for (var key in params) { - original[key] = params[key]; + for (var k in params) { + original[k] = params[k]; } setupFilterList(filterKey, table, filterListElement); @@ -700,10 +757,13 @@ function loadStockTable(table, options) { grouping = options.grouping; } + var col = null; + // Explicitly disable part grouping functionality // Might be able to add this in later on, // but there is a bug which makes this crash if paginating on the server side. // Ref: https://github.com/wenzhixin/bootstrap-table/issues/3250 + // eslint-disable-next-line no-unused-vars grouping = false; var columns = [ @@ -727,22 +787,24 @@ function loadStockTable(table, options) { sortName: 'part__name', visible: params['part_detail'], switchable: params['part_detail'], - formatter: function(value, row, index, field) { + formatter: function(value, row) { var url = `/stock/item/${row.pk}/`; var thumb = row.part_detail.thumbnail; var name = row.part_detail.full_name; - html = imageHoverIcon(thumb) + renderLink(name, url); + var html = imageHoverIcon(thumb) + renderLink(name, url); html += makePartIcons(row.part_detail); return html; } }; + if (!options.params.ordering) { col['sortable'] = true; - }; + } + columns.push(col); col = { @@ -751,13 +813,15 @@ function loadStockTable(table, options) { sortName: 'part__IPN', visible: params['part_detail'], switchable: params['part_detail'], - formatter: function(value, row, index, field) { + formatter: function(value, row) { return row.part_detail.IPN; }, }; + if (!options.params.ordering) { col['sortable'] = true; - }; + } + columns.push(col); columns.push({ @@ -765,7 +829,7 @@ function loadStockTable(table, options) { title: '{% trans "Description" %}', visible: params['part_detail'], switchable: params['part_detail'], - formatter: function(value, row, index, field) { + formatter: function(value, row) { return row.part_detail.description; } }); @@ -773,7 +837,7 @@ function loadStockTable(table, options) { col = { field: 'quantity', title: '{% trans "Stock" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { var val = parseFloat(value); @@ -834,51 +898,61 @@ function loadStockTable(table, options) { return html; } }; + if (!options.params.ordering) { col['sortable'] = true; - }; + } + columns.push(col); col = { field: 'status', title: '{% trans "Status" %}', - formatter: function(value, row, index, field) { + formatter: function(value) { return stockStatusDisplay(value); }, }; + if (!options.params.ordering) { col['sortable'] = true; - }; + } + columns.push(col); col = { field: 'batch', title: '{% trans "Batch" %}', }; + if (!options.params.ordering) { col['sortable'] = true; - }; + } + columns.push(col); col = { field: 'location_detail.pathstring', title: '{% trans "Location" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { return locationDetail(row); } }; + if (!options.params.ordering) { col['sortable'] = true; - }; + } + columns.push(col); col = { field: 'stocktake_date', title: '{% trans "Stocktake" %}', }; + if (!options.params.ordering) { col['sortable'] = true; - }; + } + columns.push(col); col = { @@ -887,18 +961,22 @@ function loadStockTable(table, options) { visible: global_settings.STOCK_ENABLE_EXPIRY, switchable: global_settings.STOCK_ENABLE_EXPIRY, }; + if (!options.params.ordering) { col['sortable'] = true; - }; + } + columns.push(col); col = { field: 'updated', title: '{% trans "Last Updated" %}', }; + if (!options.params.ordering) { col['sortable'] = true; - }; + } + columns.push(col); columns.push({ @@ -963,7 +1041,7 @@ function loadStockTable(table, options) { if (!options.params.ordering) { col.sortable = true; col.sortName = 'purchase_price'; - }; + } columns.push(col); @@ -1368,8 +1446,8 @@ function loadStockLocationTable(table, options) { var original = {}; - for (var key in params) { - original[key] = params[key]; + for (var k in params) { + original[k] = params[k]; } setupFilterList(filterKey, table, filterListElement); @@ -1437,7 +1515,7 @@ function loadStockTrackingTable(table, options) { field: 'date', title: '{% trans "Date" %}', sortable: true, - formatter: function(value, row, index, field) { + formatter: function(value) { var m = moment(value); if (m.isValid()) { @@ -1453,7 +1531,7 @@ function loadStockTrackingTable(table, options) { cols.push({ field: 'label', title: '{% trans "Description" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { var html = "" + value + ""; if (row.notes) { @@ -1468,7 +1546,7 @@ function loadStockTrackingTable(table, options) { cols.push({ field: 'deltas', title: '{% trans "Details" %}', - formatter: function(details, row, index, field) { + formatter: function(details, row) { var html = ``; if (!details) { @@ -1603,7 +1681,7 @@ function loadStockTrackingTable(table, options) { cols.push({ field: 'user', title: '{% trans "User" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { if (value) { // TODO - Format the user's first and last names @@ -1741,26 +1819,6 @@ function loadInstalledInTable(table, options) { * Display a table showing the stock items which are installed in this stock item. */ - function updateCallbacks() { - // Setup callback functions when buttons are pressed - table.find('.button-install').click(function() { - var pk = $(this).attr('pk'); - - launchModalForm( - `/stock/item/${options.stock_item}/install/`, - { - data: { - part: pk, - }, - success: function() { - // Refresh entire table! - table.bootstrapTable('refresh'); - } - } - ); - }); - } - table.inventreeTable({ url: "{% url 'api-stock-list' %}", queryParams: { @@ -1802,7 +1860,7 @@ function loadInstalledInTable(table, options) { { field: 'status', title: '{% trans "Status" %}', - formatter: function(value, row) { + formatter: function(value) { return stockStatusDisplay(value); } }, diff --git a/InvenTree/templates/js/translated/table_filters.js b/InvenTree/templates/js/translated/table_filters.js index 9e173a7b37..922e4f2497 100644 --- a/InvenTree/templates/js/translated/table_filters.js +++ b/InvenTree/templates/js/translated/table_filters.js @@ -8,6 +8,19 @@ {% include "status_codes.html" with label='purchaseOrder' options=PurchaseOrderStatus.list %} {% include "status_codes.html" with label='salesOrder' options=SalesOrderStatus.list %} +/* globals + global_settings +*/ + +/* exported + buildStatusDisplay, + getAvailableTableFilters, + purchaseOrderStatusDisplay, + salesOrderStatusDisplay, + stockHistoryStatusDisplay, + stockStatusDisplay, +*/ + function getAvailableTableFilters(tableKey) { diff --git a/InvenTree/templates/js/translated/tables.js b/InvenTree/templates/js/translated/tables.js index c3f938f9c8..d5c9021806 100644 --- a/InvenTree/templates/js/translated/tables.js +++ b/InvenTree/templates/js/translated/tables.js @@ -1,21 +1,21 @@ {% load i18n %} +/* global + inventreeLoad, + inventreeSave, +*/ + +/* exported + customGroupSorter, + reloadtable, + renderLink, + reloadTableFilters, +*/ function reloadtable(table) { $(table).bootstrapTable('refresh'); } - -function editButton(url, text='{% trans "Edit" %}') { - return ""; -} - - -function deleteButton(url, text='{% trans "Delete" %}') { - return ""; -} - - function renderLink(text, url, options={}) { if (url === null || url === undefined || url === '') { return text; From 3e03b1c31d44330354d0b389cfec512c32836b0e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 14:12:35 +1000 Subject: [PATCH 31/67] report.js --- InvenTree/templates/js/dynamic/settings.js | 6 ++++- InvenTree/templates/js/translated/report.js | 30 +++++++++++++++++---- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/InvenTree/templates/js/dynamic/settings.js b/InvenTree/templates/js/dynamic/settings.js index 49d2650bef..7f606b7548 100644 --- a/InvenTree/templates/js/dynamic/settings.js +++ b/InvenTree/templates/js/dynamic/settings.js @@ -1,5 +1,9 @@ {% load inventree_extras %} -// InvenTree settings + +/* exported + user_settings, + global_settings, +*/ {% user_settings request.user as USER_SETTINGS %} const user_settings = { diff --git a/InvenTree/templates/js/translated/report.js b/InvenTree/templates/js/translated/report.js index 370978be04..ff7cef941a 100644 --- a/InvenTree/templates/js/translated/report.js +++ b/InvenTree/templates/js/translated/report.js @@ -1,5 +1,25 @@ {% load i18n %} +/* globals + attachSelect, + closeModal, + inventreeGet, + openModal, + makeOptionsList, + modalEnable, + modalSetContent, + modalSetTitle, + modalSubmit, + showAlertDialog, +*/ + +/* exported + printBomReports, + printBuildReports, + printPurchaseOrderReports, + printSalesOrderReports, + printTestReports, +*/ function selectReport(reports, items, options={}) { /** @@ -88,7 +108,7 @@ function selectReport(reports, items, options={}) { } -function printTestReports(items, options={}) { +function printTestReports(items) { /** * Print test reports for the provided stock item(s) */ @@ -142,7 +162,7 @@ function printTestReports(items, options={}) { } -function printBuildReports(builds, options={}) { +function printBuildReports(builds) { /** * Print Build report for the provided build(s) */ @@ -195,7 +215,7 @@ function printBuildReports(builds, options={}) { } -function printBomReports(parts, options={}) { +function printBomReports(parts) { /** * Print BOM reports for the provided part(s) */ @@ -249,7 +269,7 @@ function printBomReports(parts, options={}) { } -function printPurchaseOrderReports(orders, options={}) { +function printPurchaseOrderReports(orders) { /** * Print PO reports for the provided purchase order(s) */ @@ -303,7 +323,7 @@ function printPurchaseOrderReports(orders, options={}) { } -function printSalesOrderReports(orders, options={}) { +function printSalesOrderReports(orders) { /** * Print SO reports for the provided purchase order(s) */ From 7d5b85923357ef268a0ed0596c483607354b5863 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 14:39:58 +1000 Subject: [PATCH 32/67] part.js --- InvenTree/templates/js/translated/part.js | 93 +++++++++++++++++------ 1 file changed, 70 insertions(+), 23 deletions(-) diff --git a/InvenTree/templates/js/translated/part.js b/InvenTree/templates/js/translated/part.js index 98a9be5358..b0c2330360 100644 --- a/InvenTree/templates/js/translated/part.js +++ b/InvenTree/templates/js/translated/part.js @@ -1,11 +1,48 @@ {% load i18n %} {% load inventree_extras %} +/* globals + Chart, + constructForm, + global_settings, + imageHoverIcon, + inventreeGet, + inventreePut, + launchModalForm, + linkButtonsToSelection, + loadTableFilters, + makeIconBadge, + makeIconButton, + printPartLabels, + renderLink, + setFormGroupVisibility, + setupFilterList, + yesNoLabel, +*/ + +/* exported + duplicatePart, + editCategory, + editPart, + initPriceBreakSet, + loadBomChart, + loadParametricPartTable, + loadPartCategoryTable, + loadPartParameterTable, + loadPartTable, + loadPartTestTemplateTable, + loadPartVariantTable, + loadSellPricingChart, + loadSimplePartTable, + loadStockPricingChart, + toggleStar, +*/ + /* Part API functions * Requires api.js to be loaded first */ -function partGroups(options={}) { +function partGroups() { return { attributes: { @@ -36,7 +73,7 @@ function partFields(options={}) { category: { secondary: { title: '{% trans "Add Part Category" %}', - fields: function(data) { + fields: function() { var fields = categoryFields(); return fields; @@ -255,7 +292,7 @@ function categoryFields() { // Edit a PartCategory via the API -function editCategory(pk, options={}) { +function editCategory(pk) { var url = `/api/part/category/${pk}/`; @@ -270,7 +307,7 @@ function editCategory(pk, options={}) { } -function editPart(pk, options={}) { +function editPart(pk) { var url = `/api/part/${pk}/`; @@ -282,7 +319,7 @@ function editPart(pk, options={}) { constructForm(url, { fields: fields, - groups: partGroups(), + groups: groups, title: '{% trans "Edit Part" %}', reload: true, }); @@ -361,7 +398,7 @@ function toggleStar(options) { } -function makePartIcons(part, options={}) { +function makePartIcons(part) { /* Render a set of icons for the given part. */ @@ -388,7 +425,7 @@ function makePartIcons(part, options={}) { } if (part.salable) { - html += makeIconBadge('fa-dollar-sign', title='{% trans "Salable part" %}'); + html += makeIconBadge('fa-dollar-sign', '{% trans "Salable part" %}'); } if (!part.active) { @@ -428,7 +465,7 @@ function loadPartVariantTable(table, partId, options={}) { field: 'name', title: '{% trans "Name" %}', switchable: false, - formatter: function(value, row, index, field) { + formatter: function(value, row) { var html = ''; var name = ''; @@ -646,14 +683,14 @@ function loadParametricPartTable(table, options={}) { var columns = []; - for (header of table_headers) { + for (var header of table_headers) { if (header === 'part') { columns.push({ field: header, title: '{% trans "Part" %}', sortable: true, sortName: 'name', - formatter: function(value, row, index, field) { + formatter: function(value, row) { var name = ''; @@ -776,6 +813,8 @@ function loadPartTable(table, url, options={}) { var filters = {}; + var col = null; + if (!options.disableFilters) { filters = loadTableFilters("parts"); } @@ -809,16 +848,18 @@ function loadPartTable(table, url, options={}) { field: 'IPN', title: 'IPN', }; + if (!options.params.ordering) { col['sortable'] = true; - }; + } + columns.push(col); col = { field: 'name', title: '{% trans "Part" %}', switchable: false, - formatter: function(value, row, index, field) { + formatter: function(value, row) { var name = ''; @@ -844,16 +885,18 @@ function loadPartTable(table, url, options={}) { return display; } - }; + } + if (!options.params.ordering) { col['sortable'] = true; - }; + } + columns.push(col); columns.push({ field: 'description', title: '{% trans "Description" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { if (row.is_template) { value = '' + value + ''; @@ -867,7 +910,7 @@ function loadPartTable(table, url, options={}) { sortName: 'category', field: 'category_detail', title: '{% trans "Category" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { if (row.category) { return renderLink(value.pathstring, "/part/category/" + row.category + "/"); } @@ -876,16 +919,18 @@ function loadPartTable(table, url, options={}) { } } }; + if (!options.params.ordering) { col['sortable'] = true; - }; + } + columns.push(col); col = { field: 'in_stock', title: '{% trans "Stock" %}', searchable: false, - formatter: function(value, row, index, field) { + formatter: function(value, row) { var link = "stock"; if (value) { @@ -912,15 +957,17 @@ function loadPartTable(table, url, options={}) { return renderLink(value, '/part/' + row.pk + "/" + link + "/"); } }; + if (!options.params.ordering) { col['sortable'] = true; - }; + } + columns.push(col); columns.push({ field: 'link', title: '{% trans "Link" %}', - formatter: function(value, row, index, field) { + formatter: function(value) { return renderLink( value, value, { @@ -1122,8 +1169,8 @@ function loadPartTestTemplateTable(table, options) { var original = {}; - for (var key in params) { - original[key] = params[key]; + for (var k in params) { + original[k] = params[k]; } setupFilterList("parttests", table, filterListElement); @@ -1268,7 +1315,7 @@ function loadPriceBreakTable(table, options) { field: 'price', title: '{% trans "Price" %}', sortable: true, - formatter: function(value, row, index) { + formatter: function(value, row) { var html = value; html += `
` From 90a75ac7cbf86c2aabf0111c8e31d4b548a4ea9d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 14:44:44 +1000 Subject: [PATCH 33/67] nav.js --- InvenTree/templates/js/dynamic/nav.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/InvenTree/templates/js/dynamic/nav.js b/InvenTree/templates/js/dynamic/nav.js index 601c0f4dcf..0b49be23c6 100644 --- a/InvenTree/templates/js/dynamic/nav.js +++ b/InvenTree/templates/js/dynamic/nav.js @@ -1,3 +1,10 @@ +/* globals +*/ + +/* exported + attachNavCallbacks, + onPanelLoad, +*/ /* * Attach callbacks to navigation bar elements. @@ -55,7 +62,7 @@ function activatePanel(panelName, options={}) { // Iterate through the available 'select' elements until one matches panelName = null; - $('.nav-toggle').each(function(item) { + $('.nav-toggle').each(function() { var panel_name = $(this).attr('id').replace('select-', ''); if ($(`#panel-${panel_name}`).length && (panelName == null)) { @@ -83,9 +90,9 @@ function activatePanel(panelName, options={}) { $('.list-group-item').removeClass('active'); // Find the associated selector - var select = `#select-${panelName}`; + var selectElement = `#select-${panelName}`; - $(select).parent('.list-group-item').addClass('active'); + $(selectElement).parent('.list-group-item').addClass('active'); } @@ -96,7 +103,7 @@ function onPanelLoad(panel, callback) { var panelId = `#panel-${panel}`; - $(panelId).on('fadeInStarted', function(e) { + $(panelId).on('fadeInStarted', function() { // Trigger the callback callback(); From 00f012311dcc7bc231b6134aba27756a8419e844 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 14:49:40 +1000 Subject: [PATCH 34/67] inventree.js --- InvenTree/templates/js/dynamic/inventree.js | 23 +++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/InvenTree/templates/js/dynamic/inventree.js b/InvenTree/templates/js/dynamic/inventree.js index 9813c30207..2a151fcdaf 100644 --- a/InvenTree/templates/js/dynamic/inventree.js +++ b/InvenTree/templates/js/dynamic/inventree.js @@ -1,5 +1,20 @@ {% load inventree_extras %} +/* globals + ClipboardJS, + inventreeFormDataUpload, + launchModalForm, + user_settings, +*/ + +/* exported + attachClipboard, + enableDragAndDrop, + inventreeDocReady, + inventreeLoad, + inventreeSave, +*/ + function attachClipboard(selector, containerselector, textElement) { // set container if (containerselector){ @@ -8,6 +23,8 @@ function attachClipboard(selector, containerselector, textElement) { containerselector = document.body; } + var text = null; + // set text-function if (textElement){ text = function() { @@ -21,6 +38,7 @@ function attachClipboard(selector, containerselector, textElement) { } // create Clipboard + // eslint-disable-next-line no-unused-vars var cis = new ClipboardJS(selector, { text: text, container: containerselector @@ -51,12 +69,13 @@ function inventreeDocReady() { // TODO - Only indicate that a drop event will occur if a file is being dragged var transfer = event.originalEvent.dataTransfer; + // eslint-disable-next-line no-constant-condition if (true || isFileTransfer(transfer)) { $(this).addClass('dragover'); } }); - $('.dropzone').on('dragleave drop', function(event) { + $('.dropzone').on('dragleave drop', function() { $(this).removeClass('dragover'); }); @@ -154,7 +173,7 @@ function enableDragAndDrop(element, url, options) { method - HTTP method */ - data = options.data || {}; + var data = options.data || {}; $(element).on('drop', function(event) { From 50b54f096624628f495d833e703dc2fe784d9041 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 14:57:17 +1000 Subject: [PATCH 35/67] order.js --- InvenTree/templates/js/translated/order.js | 44 +++++++++++++++++----- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/InvenTree/templates/js/translated/order.js b/InvenTree/templates/js/translated/order.js index c6dd773373..371bcf974d 100644 --- a/InvenTree/templates/js/translated/order.js +++ b/InvenTree/templates/js/translated/order.js @@ -1,6 +1,33 @@ {% load i18n %} {% load inventree_extras %} +/* globals + companyFormFields, + constructForm, + createSupplierPart, + global_settings, + imageHoverIcon, + inventreeGet, + launchModalForm, + loadTableFilters, + makeIconBadge, + purchaseOrderStatusDisplay, + renderLink, + salesOrderStatusDisplay, + setupFilterList, +*/ + +/* exported + createSalesOrder, + editPurchaseOrderLineItem, + loadPurchaseOrderTable, + loadSalesOrderAllocationTable, + loadSalesOrderTable, + newPurchaseOrderFromOrderWizard, + newSupplierPartFromOrderWizard, + removeOrderRowFromOrderWizard, + removePurchaseOrderLineItem, +*/ // Create a new SalesOrder function createSalesOrder(options={}) { @@ -15,7 +42,7 @@ function createSalesOrder(options={}) { value: options.customer, secondary: { title: '{% trans "Add Customer" %}', - fields: function(data) { + fields: function() { var fields = companyFormFields(); fields.is_customer.value = true; @@ -56,7 +83,7 @@ function createPurchaseOrder(options={}) { value: options.supplier, secondary: { title: '{% trans "Add Supplier" %}', - fields: function(data) { + fields: function() { var fields = companyFormFields(); fields.is_supplier.value = true; @@ -278,7 +305,7 @@ function loadPurchaseOrderTable(table, options) { title: '{% trans "Purchase Order" %}', sortable: true, switchable: false, - formatter: function(value, row, index, field) { + formatter: function(value, row) { var prefix = global_settings.PURCHASEORDER_REFERENCE_PREFIX; @@ -300,7 +327,7 @@ function loadPurchaseOrderTable(table, options) { title: '{% trans "Supplier" %}', sortable: true, sortName: 'supplier__name', - formatter: function(value, row, index, field) { + formatter: function(value, row) { return imageHoverIcon(row.supplier_detail.image) + renderLink(row.supplier_detail.name, `/company/${row.supplier}/purchase-orders/`); } }, @@ -316,7 +343,7 @@ function loadPurchaseOrderTable(table, options) { field: 'status', title: '{% trans "Status" %}', sortable: true, - formatter: function(value, row, index, field) { + formatter: function(value, row) { return purchaseOrderStatusDisplay(row.status, row.status_text); } }, @@ -373,7 +400,7 @@ function loadSalesOrderTable(table, options) { sortable: true, field: 'reference', title: '{% trans "Sales Order" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { var prefix = global_settings.SALESORDER_REFERENCE_PREFIX; @@ -395,7 +422,7 @@ function loadSalesOrderTable(table, options) { sortName: 'customer__name', field: 'customer_detail', title: '{% trans "Customer" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { if (!row.customer_detail) { return '{% trans "Invalid Customer" %}'; @@ -418,7 +445,7 @@ function loadSalesOrderTable(table, options) { sortable: true, field: 'status', title: '{% trans "Status" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { return salesOrderStatusDisplay(row.status, row.status_text); } }, @@ -486,7 +513,6 @@ function loadSalesOrderAllocationTable(table, options={}) { field: 'order', switchable: false, title: '{% trans "Order" %}', - switchable: false, formatter: function(value, row) { var prefix = global_settings.SALESORDER_REFERENCE_PREFIX; From e0e7788af6b3ad63e6f1380f8c5e7ca091c697d8 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 15:32:01 +1000 Subject: [PATCH 36/67] More js fixes --- InvenTree/templates/js/dynamic/calendar.js | 9 ++++ InvenTree/templates/js/translated/api.js | 18 ++++++-- .../templates/js/translated/attachment.js | 10 +++++ InvenTree/templates/js/translated/filters.js | 26 ++++++----- InvenTree/templates/js/translated/modals.js | 45 ++++++++++++------- .../js/translated/model_renderers.js | 38 ++++++++++------ 6 files changed, 101 insertions(+), 45 deletions(-) diff --git a/InvenTree/templates/js/dynamic/calendar.js b/InvenTree/templates/js/dynamic/calendar.js index 861bbe1727..fd36d689f5 100644 --- a/InvenTree/templates/js/dynamic/calendar.js +++ b/InvenTree/templates/js/dynamic/calendar.js @@ -1,5 +1,14 @@ {% load i18n %} +/* globals +*/ + +/* exported + clearEvents, + endDate, + startDate, +*/ + /** * Helper functions for calendar display */ diff --git a/InvenTree/templates/js/translated/api.js b/InvenTree/templates/js/translated/api.js index 4cf07638dc..8b72e166a2 100644 --- a/InvenTree/templates/js/translated/api.js +++ b/InvenTree/templates/js/translated/api.js @@ -1,7 +1,17 @@ {% load i18n %} {% load inventree_extras %} -var jQuery = window.$; +/* globals + renderErrorMessage, + showAlertDialog, +*/ + +/* exported + inventreeGet, + inventreeDelete, + inventreeFormDataUpload, + showApiError, +*/ $.urlParam = function(name){ var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href); @@ -35,7 +45,7 @@ function inventreeGet(url, filters={}, options={}) { var csrftoken = getCookie('csrftoken'); return $.ajax({ - beforeSend: function(xhr, settings) { + beforeSend: function(xhr) { xhr.setRequestHeader('X-CSRFToken', csrftoken); }, url: url, @@ -73,7 +83,7 @@ function inventreeFormDataUpload(url, data, options={}) { var csrftoken = getCookie('csrftoken'); return $.ajax({ - beforeSend: function(xhr, settings) { + beforeSend: function(xhr) { xhr.setRequestHeader('X-CSRFToken', csrftoken); }, url: url, @@ -105,7 +115,7 @@ function inventreePut(url, data={}, options={}) { var csrftoken = getCookie('csrftoken'); return $.ajax({ - beforeSend: function(xhr, settings) { + beforeSend: function(xhr) { xhr.setRequestHeader('X-CSRFToken', csrftoken); }, url: url, diff --git a/InvenTree/templates/js/translated/attachment.js b/InvenTree/templates/js/translated/attachment.js index bffe3d9995..3264bd9413 100644 --- a/InvenTree/templates/js/translated/attachment.js +++ b/InvenTree/templates/js/translated/attachment.js @@ -1,5 +1,15 @@ {% load i18n %} +/* globals + makeIconButton, + renderLink, +*/ + +/* exported + loadAttachmentTable, + reloadAttachmentTable, +*/ + function reloadAttachmentTable() { $('#attachment-table').bootstrapTable("refresh"); diff --git a/InvenTree/templates/js/translated/filters.js b/InvenTree/templates/js/translated/filters.js index bc0dc1b958..8d70715ff0 100644 --- a/InvenTree/templates/js/translated/filters.js +++ b/InvenTree/templates/js/translated/filters.js @@ -1,5 +1,16 @@ {% load i18n %} +/* globals + getAvailableTableFilters, + inventreeLoad, + inventreeSave, + reloadTableFilters, +*/ + +/* exported + setupFilterList, +*/ + /** * Code for managing query filters / table options. * @@ -42,7 +53,7 @@ function loadTableFilters(tableKey) { var filters = {}; - filterstring.split("&").forEach(function(item, index) { + filterstring.split("&").forEach(function(item) { item = item.trim(); if (item.length > 0) { @@ -227,7 +238,7 @@ function generateFilterInput(tableKey, filterKey) { html = ``; @@ -1838,7 +1867,7 @@ function constructChoiceInput(name, parameters, options) { * be converted into a select2 input. * This will then be served custom data from the API (as required)... */ -function constructRelatedFieldInput(name, parameters, options) { +function constructRelatedFieldInput(name) { var html = ``; @@ -1851,7 +1880,7 @@ function constructRelatedFieldInput(name, parameters, options) { /* * Construct a field for file upload */ -function constructFileUploadInput(name, parameters, options) { +function constructFileUploadInput(name, parameters) { var cls = 'clearablefileinput'; @@ -1871,7 +1900,7 @@ function constructFileUploadInput(name, parameters, options) { /* * Construct a field for a date input */ -function constructDateInput(name, parameters, options) { +function constructDateInput(name, parameters) { return constructInputOptions( name, @@ -1886,7 +1915,7 @@ function constructDateInput(name, parameters, options) { * Construct a "candy" field input * No actual field data! */ -function constructCandyInput(name, parameters, options) { +function constructCandyInput(name, parameters) { return parameters.html; @@ -1901,7 +1930,7 @@ function constructCandyInput(name, parameters, options) { * - parameters: Field parameters returned by the OPTIONS method * */ -function constructHelpText(name, parameters, options) { +function constructHelpText(name, parameters) { var style = ''; From 0835fe92a05acd3eb29330b52538ba6a43acae27 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 15:54:31 +1000 Subject: [PATCH 39/67] bom.js --- InvenTree/templates/js/translated/bom.js | 42 +++++++++++++++++------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/InvenTree/templates/js/translated/bom.js b/InvenTree/templates/js/translated/bom.js index 37a3eb23b0..399d2a08f3 100644 --- a/InvenTree/templates/js/translated/bom.js +++ b/InvenTree/templates/js/translated/bom.js @@ -1,5 +1,25 @@ {% load i18n %} +/* globals + constructForm, + imageHoverIcon, + inventreeGet, + inventreePut, + launchModalForm, + loadTableFilters, + makePartIcons, + renderLink, + setupFilterList, + yesNoLabel, +*/ + +/* exported + newPartFromBomWizard, + loadBomTable, + removeRowFromBomWizard, + removeColFromBomWizard, +*/ + /* BOM management functions. * Requires follwing files to be loaded first: * - api.js @@ -28,7 +48,7 @@ function bomItemFields() { } -function reloadBomTable(table, options) { +function reloadBomTable(table) { table.bootstrapTable('refresh'); } @@ -157,7 +177,7 @@ function loadBomTable(table, options) { checkbox: true, visible: true, switchable: false, - formatter: function(value, row, index, field) { + formatter: function(value, row) { // Disable checkbox if the row is defined for a *different* part! if (row.part != options.parent_id) { return { @@ -182,7 +202,7 @@ function loadBomTable(table, options) { field: 'sub_part', title: '{% trans "Part" %}', sortable: true, - formatter: function(value, row, index, field) { + formatter: function(value, row) { var url = `/part/${row.sub_part}/`; var html = imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, url); @@ -225,7 +245,7 @@ function loadBomTable(table, options) { title: '{% trans "Quantity" %}', searchable: false, sortable: true, - formatter: function(value, row, index, field) { + formatter: function(value, row) { var text = value; // The 'value' is a text string with (potentially) multiple trailing zeros @@ -250,7 +270,7 @@ function loadBomTable(table, options) { title: '{% trans "Available" %}', searchable: false, sortable: true, - formatter: function(value, row, index, field) { + formatter: function(value, row) { var url = `/part/${row.sub_part_detail.pk}/?display=stock`; var text = value; @@ -284,7 +304,7 @@ function loadBomTable(table, options) { field: 'price_range', title: '{% trans "Supplier Cost" %}', sortable: true, - formatter: function(value, row, index, field) { + formatter: function(value) { if (value) { return value; } else { @@ -314,7 +334,7 @@ function loadBomTable(table, options) { field: 'inherited', title: '{% trans "Inherited" %}', searchable: false, - formatter: function(value, row, index, field) { + formatter: function(value, row) { // This BOM item *is* inheritable, but is defined for this BOM if (!row.inherited) { return yesNoLabel(false); @@ -334,7 +354,7 @@ function loadBomTable(table, options) { { 'field': 'can_build', 'title': '{% trans "Can Build" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { var can_build = 0; if (row.quantity > 0) { @@ -379,7 +399,7 @@ function loadBomTable(table, options) { switchable: false, field: 'pk', visible: true, - formatter: function(value, row, index, field) { + formatter: function(value, row) { if (row.part == options.parent_id) { @@ -459,7 +479,7 @@ function loadBomTable(table, options) { name: 'bom', sortable: true, search: true, - rowStyle: function(row, index) { + rowStyle: function(row) { var classes = []; @@ -532,7 +552,6 @@ function loadBomTable(table, options) { table.on('click', '.bom-delete-button', function() { var pk = $(this).attr('pk'); - var url = `/part/bom/${pk}/delete/`; constructForm(`/api/bom/${pk}/`, { method: 'DELETE', @@ -546,7 +565,6 @@ function loadBomTable(table, options) { table.on('click', '.bom-edit-button', function() { var pk = $(this).attr('pk'); - var url = `/part/bom/${pk}/edit/`; var fields = bomItemFields(); From 7d4945d302802a6d99f30562b6b8d2aac673693a Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 16:01:45 +1000 Subject: [PATCH 40/67] company.js --- InvenTree/templates/js/translated/company.js | 55 ++++++++++++++------ 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/InvenTree/templates/js/translated/company.js b/InvenTree/templates/js/translated/company.js index 9c67e77db7..6c936c157a 100644 --- a/InvenTree/templates/js/translated/company.js +++ b/InvenTree/templates/js/translated/company.js @@ -1,5 +1,28 @@ {% load i18n %} +/* globals + constructForm, + imageHoverIcon, + inventreeDelete, + loadTableFilters, + makeIconButton, + renderLink, + setupFilterList, + showQuestionDialog, +*/ + +/* exported + createCompany, + createManufacturerPart, + createSupplierPart, + deleteManufacturerParts, + editCompany, + loadCompanyTable, + loadManufacturerPartTable, + loadManufacturerPartParameterTable, + loadSupplierPartTable, +*/ + function manufacturerPartFields() { @@ -32,7 +55,7 @@ function createManufacturerPart(options={}) { fields.manufacturer.secondary = { title: '{% trans "Add Manufacturer" %}', - fields: function(data) { + fields: function() { var company_fields = companyFormFields(); company_fields.is_manufacturer.value = true; @@ -126,7 +149,7 @@ function createSupplierPart(options={}) { // Add a secondary modal for the supplier fields.supplier.secondary = { title: '{% trans "Add Supplier" %}', - fields: function(data) { + fields: function() { var company_fields = companyFormFields(); company_fields.is_supplier.value = true; @@ -185,7 +208,7 @@ function deleteSupplierPart(part, options={}) { // Returns a default form-set for creating / editing a Company object -function companyFormFields(options={}) { +function companyFormFields() { return { name: {}, @@ -228,7 +251,7 @@ function editCompany(pk, options={}) { title: '{% trans "Edit Company" %}', } ); -}; +} /* * Launches a form to create a new company. @@ -285,7 +308,7 @@ function loadCompanyTable(table, url, options={}) { title: '{% trans "Company" %}', sortable: true, switchable: false, - formatter: function(value, row, index, field) { + formatter: function(value, row) { var html = imageHoverIcon(row.image) + renderLink(value, row.url); if (row.is_customer) { @@ -310,7 +333,7 @@ function loadCompanyTable(table, url, options={}) { { field: 'website', title: '{% trans "Website" %}', - formatter: function(value, row, index, field) { + formatter: function(value) { if (value) { return renderLink(value, value); } @@ -445,7 +468,7 @@ function loadManufacturerPartTable(table, url, options) { sortable: true, field: 'part_detail.full_name', title: '{% trans "Part" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { var url = `/part/${row.part}/`; @@ -470,7 +493,7 @@ function loadManufacturerPartTable(table, url, options) { sortable: true, field: 'manufacturer', title: '{% trans "Manufacturer" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { if (value && row.manufacturer_detail) { var name = row.manufacturer_detail.name; var url = `/company/${value}/`; @@ -486,14 +509,14 @@ function loadManufacturerPartTable(table, url, options) { sortable: true, field: 'MPN', title: '{% trans "MPN" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { return renderLink(value, `/manufacturer-part/${row.pk}/`); } }, { field: 'link', title: '{% trans "Link" %}', - formatter: function(value, row, index, field) { + formatter: function(value) { if (value) { return renderLink(value, value); } else { @@ -695,7 +718,7 @@ function loadSupplierPartTable(table, url, options) { sortable: true, field: 'part_detail.full_name', title: '{% trans "Part" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { var url = `/part/${row.part}/`; @@ -720,7 +743,7 @@ function loadSupplierPartTable(table, url, options) { sortable: true, field: 'supplier', title: '{% trans "Supplier" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { if (value) { var name = row.supplier_detail.name; var url = `/company/${value}/`; @@ -736,7 +759,7 @@ function loadSupplierPartTable(table, url, options) { sortable: true, field: 'SKU', title: '{% trans "Supplier Part" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { return renderLink(value, `/supplier-part/${row.pk}/`); } }, @@ -746,7 +769,7 @@ function loadSupplierPartTable(table, url, options) { sortable: true, field: 'manufacturer_detail.name', title: '{% trans "Manufacturer" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { if (value && row.manufacturer_detail) { var name = value; var url = `/company/${row.manufacturer_detail.pk}/`; @@ -764,7 +787,7 @@ function loadSupplierPartTable(table, url, options) { sortable: true, field: 'manufacturer_part_detail.MPN', title: '{% trans "MPN" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { if (value && row.manufacturer_part) { return renderLink(value, `/manufacturer-part/${row.manufacturer_part}/`); } else { @@ -775,7 +798,7 @@ function loadSupplierPartTable(table, url, options) { { field: 'link', title: '{% trans "Link" %}', - formatter: function(value, row, index, field) { + formatter: function(value) { if (value) { return renderLink(value, value); } else { From 2112c6a9ad3c6e6bb99b9bd55fd9b1f48c33bed9 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 19:33:43 +1000 Subject: [PATCH 41/67] barcode.js --- InvenTree/templates/js/translated/barcode.js | 27 ++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/InvenTree/templates/js/translated/barcode.js b/InvenTree/templates/js/translated/barcode.js index 5f255f44d6..42aaf3c416 100644 --- a/InvenTree/templates/js/translated/barcode.js +++ b/InvenTree/templates/js/translated/barcode.js @@ -3,18 +3,23 @@ /* globals imageHoverIcon, inventreePut, + makeIconButton, modalEnable, modalSetContent, modalSetTitle, + modalSetSubmitText, modalShowSubmitButton, modalSubmit, showAlertOrCache, + showQuestionDialog, */ /* exported + barcodeCheckIn, barcodeScanDialog, linkBarcodeDialog, - barcodeCheckIn, + scanItemsIntoLocation, + unlinkBarcode, */ function makeBarcodeInput(placeholderText='', hintText='') { @@ -297,7 +302,7 @@ function barcodeScanDialog() { /* * Dialog for linking a particular barcode to a stock item. */ -function linkBarcodeDialog(stockitem, options={}) { +function linkBarcodeDialog(stockitem) { var modal = '#modal-form'; @@ -308,7 +313,7 @@ function linkBarcodeDialog(stockitem, options={}) { data: { stockitem: stockitem, }, - onScan: function(response) { + onScan: function() { $(modal).modal('hide'); location.reload(); @@ -341,7 +346,7 @@ function unlinkBarcode(stockitem) { }, { method: 'PATCH', - success: function(response, status) { + success: function() { location.reload(); }, }, @@ -355,7 +360,7 @@ function unlinkBarcode(stockitem) { /* * Display dialog to check multiple stock items in to a stock location. */ -function barcodeCheckIn(location_id, options={}) { +function barcodeCheckIn(location_id) { var modal = '#modal-form'; @@ -447,7 +452,9 @@ function barcodeCheckIn(location_id, options={}) { // Called when the 'check-in' button is pressed - var data = {location: location_id}; + var data = { + location: location_id + }; // Extract 'notes' field data.notes = $(modal + ' #notes').val(); @@ -484,7 +491,7 @@ function barcodeCheckIn(location_id, options={}) { }, onScan: function(response) { if ('stockitem' in response) { - stockitem = response.stockitem; + var stockitem = response.stockitem; var duplicate = false; @@ -542,12 +549,12 @@ function scanItemsIntoLocation(item_id_list, options={}) { function updateLocationInfo(location) { var div = $(modal + ' #header-div'); - if (stock_location && stock_location.pk) { + if (location && location.pk) { div.html(`
{% trans "Location" %}
- ${stock_location.name}
- ${stock_location.description} + ${location.name}
+ ${location.description}
`); } else { From 6177fe0c5ae07e9bb6930cb67460eea332df91d9 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 19:52:28 +1000 Subject: [PATCH 42/67] build.js --- InvenTree/templates/js/translated/api.js | 1 + InvenTree/templates/js/translated/build.js | 86 +++++++++++++--------- 2 files changed, 52 insertions(+), 35 deletions(-) diff --git a/InvenTree/templates/js/translated/api.js b/InvenTree/templates/js/translated/api.js index 8b72e166a2..6ca4b2672d 100644 --- a/InvenTree/templates/js/translated/api.js +++ b/InvenTree/templates/js/translated/api.js @@ -14,6 +14,7 @@ */ $.urlParam = function(name){ + // eslint-disable-next-line no-useless-escape var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href); if (results==null) { return null; diff --git a/InvenTree/templates/js/translated/build.js b/InvenTree/templates/js/translated/build.js index 26f3876af3..a1485c5adf 100644 --- a/InvenTree/templates/js/translated/build.js +++ b/InvenTree/templates/js/translated/build.js @@ -1,6 +1,33 @@ {% load i18n %} {% load inventree_extras %} +/* globals + buildStatusDisplay, + constructForm, + getFieldByName, + global_settings, + imageHoverIcon, + inventreeGet, + launchModalForm, + linkButtonsToSelection, + loadTableFilters, + makeIconBadge, + makeIconButton, + makePartIcons, + makeProgressBar, + renderLink, + setupFilterList, +*/ + +/* exported + editBuildOrder, + loadAllocationTable, + loadBuildOrderAllocationTable, + loadBuildOutputAllocationTable, + loadBuildPartsTable, + loadBuildTable, +*/ + function buildFormFields() { return { @@ -32,7 +59,7 @@ function buildFormFields() { } -function editBuildOrder(pk, options={}) { +function editBuildOrder(pk) { var fields = buildFormFields(); @@ -76,10 +103,10 @@ function makeBuildOutputActionButtons(output, buildInfo, lines) { var buildId = buildInfo.pk; + var outputId = 'untracked'; + if (output) { outputId = output.pk; - } else { - outputId = 'untracked'; } var panel = `#allocation-panel-${outputId}`; @@ -400,10 +427,8 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { // Callback for 'buy' button $(table).find('.button-buy').click(function() { - var pk = $(this).attr('pk'); - var idx = $(this).closest('tr').attr('data-index'); - var row = $(table).bootstrapTable('getData')[idx]; + var pk = $(this).attr('pk'); launchModalForm('{% url "order-parts" %}', { data: { @@ -573,8 +598,6 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { element.html(html); - var lineItem = row; - var subTable = $(`#${subTableId}`); subTable.bootstrapTable({ @@ -595,7 +618,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { width: '50%', field: 'quantity', title: '{% trans "Assigned Stock" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { var text = ''; var url = ''; @@ -618,7 +641,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { { field: 'location', title: '{% trans "Location" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { if (row.stock_item_detail.location) { var text = row.stock_item_detail.location_name; var url = `/stock/location/${row.stock_item_detail.location}/`; @@ -631,7 +654,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { }, { field: 'actions', - formatter: function(value, row, index, field) { + formatter: function(value, row) { /* Actions available for a particular stock item allocation: * * - Edit the allocation quantity @@ -678,7 +701,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { field: 'sub_part_detail.full_name', title: '{% trans "Required Part" %}', sortable: true, - formatter: function(value, row, index, field) { + formatter: function(value, row) { var url = `/part/${row.sub_part}/`; var thumb = row.sub_part_detail.thumbnail; var name = row.sub_part_detail.full_name; @@ -709,7 +732,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { field: 'allocated', title: '{% trans "Allocated" %}', sortable: true, - formatter: function(value, row, index, field) { + formatter: function(value, row) { var allocated = 0; if (row.allocations) { @@ -757,7 +780,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { { field: 'actions', title: '{% trans "Actions" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { // Generate action buttons for this build output var html = `
`; @@ -846,7 +869,7 @@ function loadBuildTable(table, options) { title: '{% trans "Build" %}', sortable: true, switchable: true, - formatter: function(value, row, index, field) { + formatter: function(value, row) { var prefix = global_settings.BUILDORDER_REFERENCE_PREFIX; @@ -873,7 +896,7 @@ function loadBuildTable(table, options) { title: '{% trans "Part" %}', sortable: true, sortName: 'part__name', - formatter: function(value, row, index, field) { + formatter: function(value, row) { var html = imageHoverIcon(row.part_detail.thumbnail); @@ -887,7 +910,7 @@ function loadBuildTable(table, options) { field: 'quantity', title: '{% trans "Completed" %}', sortable: true, - formatter: function(value, row, index, field) { + formatter: function(value, row) { return makeProgressBar( row.completed, row.quantity, @@ -901,7 +924,7 @@ function loadBuildTable(table, options) { field: 'status', title: '{% trans "Status" %}', sortable: true, - formatter: function(value, row, index, field) { + formatter: function(value) { return buildStatusDisplay(value); }, }, @@ -914,7 +937,7 @@ function loadBuildTable(table, options) { field: 'issued_by', title: '{% trans "Issued by" %}', sortable: true, - formatter: function(value, row, index, field) { + formatter: function(value, row) { if (value) { return row.issued_by_detail.username; @@ -929,7 +952,7 @@ function loadBuildTable(table, options) { field: 'responsible', title: '{% trans "Responsible" %}', sortable: true, - formatter: function(value, row, index, field) { + formatter: function(value, row) { if (value) { return row.responsible_detail.name; @@ -991,21 +1014,21 @@ function loadAllocationTable(table, part_id, part, url, required, button) { { field: 'stock_item_detail', title: '{% trans "Stock Item" %}', - formatter: function(value, row, index, field) { + formatter: function(value) { return '' + parseFloat(value.quantity) + ' x ' + value.part_name + ' @ ' + value.location_name; } }, { field: 'stock_item_detail.quantity', title: '{% trans "Available" %}', - formatter: function(value, row, index, field) { + formatter: function(value) { return parseFloat(value); } }, { field: 'quantity', title: '{% trans "Allocated" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { var html = parseFloat(value); var bEdit = ""; @@ -1028,7 +1051,7 @@ function loadAllocationTable(table, part_id, part, url, required, button) { }); }); - table.on('load-success.bs.table', function(data) { + table.on('load-success.bs.table', function() { // Extract table data var results = table.bootstrapTable('getData'); @@ -1106,9 +1129,6 @@ function loadBuildPartsTable(table, options={}) { $(table).find('.button-buy').click(function() { var pk = $(this).attr('pk'); - var idx = $(this).closest('tr').attr('data-index'); - var row = $(table).bootstrapTable('getData')[idx]; - launchModalForm('{% url "order-parts" %}', { data: { parts: [ @@ -1122,10 +1142,6 @@ function loadBuildPartsTable(table, options={}) { $(table).find('.button-build').click(function() { var pk = $(this).attr('pk'); - // Extract row data from the table - var idx = $(this).closest('tr').attr('data-index'); - var row = $(table).bootstrapTable('getData')[idx]; - newBuildOrder({ part: pk, parent: options.build, @@ -1139,7 +1155,7 @@ function loadBuildPartsTable(table, options={}) { title: '{% trans "Part" %}', switchable: false, sortable: true, - formatter: function(value, row, index, field) { + formatter: function(value, row) { var url = `/part/${row.sub_part}/`; var html = imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, url); @@ -1177,7 +1193,7 @@ function loadBuildPartsTable(table, options={}) { switchable: false, field: 'sub_part_detail.stock', title: '{% trans "Available" %}', - formatter: function(value, row, index, field) { + formatter: function(value, row) { return makeProgressBar( value, row.quantity * options.build_remaining, @@ -1201,7 +1217,7 @@ function loadBuildPartsTable(table, options={}) { field: 'actions', title: '{% trans "Actions" %}', switchable: false, - formatter: function(value, row, index, field) { + formatter: function(value, row) { // Generate action buttons against the part var html = `
`; @@ -1228,7 +1244,7 @@ function loadBuildPartsTable(table, options={}) { sortable: true, search: true, onPostBody: setupTableCallbacks, - rowStyle: function(row, index) { + rowStyle: function(row) { var classes = []; // Shade rows differently if they are for different parent parts From e1adef5010de5fb6000fe4b6747f433ec7f69eb9 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 20:08:18 +1000 Subject: [PATCH 43/67] typo fix --- InvenTree/templates/js/translated/forms.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/templates/js/translated/forms.js b/InvenTree/templates/js/translated/forms.js index 61a1596fc5..6d5e7cce66 100644 --- a/InvenTree/templates/js/translated/forms.js +++ b/InvenTree/templates/js/translated/forms.js @@ -467,7 +467,7 @@ function constructFormBody(fields, options) { break; } - html += constructField(name, field, options); + html += constructField(field_name, field, options); } if (options.current_group) { From 4c39607e0092e97636bc5f1b09dc90a7bad401f1 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 20:30:26 +1000 Subject: [PATCH 44/67] Brace style fixes --- .../templates/js/translated/attachment.js | 4 +++- InvenTree/templates/js/translated/build.js | 18 ++++++--------- InvenTree/templates/js/translated/company.js | 14 +++++++---- InvenTree/templates/js/translated/forms.js | 10 ++++++-- InvenTree/templates/js/translated/modals.js | 23 ++++++++----------- InvenTree/templates/js/translated/order.js | 12 +++++++--- InvenTree/templates/js/translated/part.js | 19 ++++++++++----- InvenTree/templates/js/translated/stock.js | 15 ++++-------- 8 files changed, 64 insertions(+), 51 deletions(-) diff --git a/InvenTree/templates/js/translated/attachment.js b/InvenTree/templates/js/translated/attachment.js index f8780e727a..1c4e9f2781 100644 --- a/InvenTree/templates/js/translated/attachment.js +++ b/InvenTree/templates/js/translated/attachment.js @@ -23,7 +23,9 @@ function loadAttachmentTable(url, options) { $(table).inventreeTable({ url: url, name: options.name || 'attachments', - formatNoMatches: function() { return '{% trans "No attachments found" %}'}, + formatNoMatches: function() { + return '{% trans "No attachments found" %}' + }, sortable: true, search: false, queryParams: options.filters || {}, diff --git a/InvenTree/templates/js/translated/build.js b/InvenTree/templates/js/translated/build.js index a1485c5adf..703e843d76 100644 --- a/InvenTree/templates/js/translated/build.js +++ b/InvenTree/templates/js/translated/build.js @@ -938,12 +938,9 @@ function loadBuildTable(table, options) { title: '{% trans "Issued by" %}', sortable: true, formatter: function(value, row) { - if (value) - { + if (value) { return row.issued_by_detail.username; - } - else - { + } else { return `{% trans "No user information" %}`; } } @@ -953,12 +950,9 @@ function loadBuildTable(table, options) { title: '{% trans "Responsible" %}', sortable: true, formatter: function(value, row) { - if (value) - { + if (value) { return row.responsible_detail.name; - } - else - { + } else { return '{% trans "No information" %}'; } } @@ -1009,7 +1003,9 @@ function loadAllocationTable(table, part_id, part, url, required, button) { table.bootstrapTable({ url: url, sortable: false, - formatNoMatches: function() { return '{% trans "No parts allocated for" %} ' + part; }, + formatNoMatches: function() { + return '{% trans "No parts allocated for" %} ' + part; + }, columns: [ { field: 'stock_item_detail', diff --git a/InvenTree/templates/js/translated/company.js b/InvenTree/templates/js/translated/company.js index 6c936c157a..e490f8914e 100644 --- a/InvenTree/templates/js/translated/company.js +++ b/InvenTree/templates/js/translated/company.js @@ -368,7 +368,9 @@ function loadCompanyTable(table, url, options={}) { queryParams: filters, groupBy: false, sidePagination: 'server', - formatNoMatches: function() { return "{% trans "No company information found" %}"; }, + formatNoMatches: function() { + return '{% trans "No company information found" %}'; + }, showColumns: true, name: options.pagetype || 'company', columns: columns, @@ -456,7 +458,9 @@ function loadManufacturerPartTable(table, url, options) { queryParams: filters, name: 'manufacturerparts', groupBy: false, - formatNoMatches: function() { return '{% trans "No manufacturer parts found" %}'; }, + formatNoMatches: function() { + return '{% trans "No manufacturer parts found" %}'; + }, columns: [ { checkbox: true, @@ -850,8 +854,9 @@ function loadSupplierPartTable(table, url, options) { { onSuccess: function() { $(table).bootstrapTable('refresh'); + } } - }); + ); }); $(table).find('.button-supplier-part-delete').click(function() { @@ -862,8 +867,9 @@ function loadSupplierPartTable(table, url, options) { { onSuccess: function() { $(table).bootstrapTable('refresh'); + } } - }); + ); }) } }); diff --git a/InvenTree/templates/js/translated/forms.js b/InvenTree/templates/js/translated/forms.js index 6d5e7cce66..596a6d15c9 100644 --- a/InvenTree/templates/js/translated/forms.js +++ b/InvenTree/templates/js/translated/forms.js @@ -703,7 +703,9 @@ function updateFieldValues(fields, options) { var field = fields[name] || null; - if (field == null) { continue; } + if (field == null) { + continue; + } var value = field.value; @@ -711,7 +713,9 @@ function updateFieldValues(fields, options) { value = field.default; } - if (value == null) { continue; } + if (value == null) { + continue; + } updateFieldValue(name, value, field, options); } @@ -1086,6 +1090,8 @@ function initializeRelatedFields(fields, options) { case 'choice': initializeChoiceField(field, fields, options); break; + default: + break; } } } diff --git a/InvenTree/templates/js/translated/modals.js b/InvenTree/templates/js/translated/modals.js index e398fc43fd..216dfceee8 100644 --- a/InvenTree/templates/js/translated/modals.js +++ b/InvenTree/templates/js/translated/modals.js @@ -418,12 +418,15 @@ function afterForm(response, options) { if (response.success) { showAlertOrCache("alert-success", response.success, cache); } + if (response.info) { showAlertOrCache("alert-info", response.info, cache); } + if (response.warning) { showAlertOrCache("alert-warning", response.warning, cache); } + if (response.danger) { showAlertOrCache("alert-danger", response.danger, cache); } @@ -431,14 +434,11 @@ function afterForm(response, options) { // Was a callback provided? if (options.success) { options.success(response); - } - else if (options.follow && response.url) { + } else if (options.follow && response.url) { window.location.href = response.url; - } - else if (options.redirect) { + } else if (options.redirect) { window.location.href = options.redirect; - } - else if (options.reload) { + } else if (options.reload) { location.reload(); } } @@ -883,9 +883,8 @@ function handleModalForm(url, options) { if (response.form_valid) { $(modal).modal('hide'); afterForm(response, options); - } - // Form was returned, invalid! - else { + } else { + // Form was returned, invalid! // Disable error message with option or response if (!options.hideErrorMessage && !response.hideErrorMessage) { @@ -916,14 +915,12 @@ function handleModalForm(url, options) { if (response.buttons) { attachButtons(modal, response.buttons); } - } - else { + } else { $(modal).modal('hide'); showAlertDialog('{% trans "Invalid response from server" %}', '{% trans "Form data missing from server response" %}'); } } - } - else { + } else { $(modal).modal('hide'); afterForm(response, options); } diff --git a/InvenTree/templates/js/translated/order.js b/InvenTree/templates/js/translated/order.js index 371bcf974d..82818e66f0 100644 --- a/InvenTree/templates/js/translated/order.js +++ b/InvenTree/templates/js/translated/order.js @@ -292,7 +292,9 @@ function loadPurchaseOrderTable(table, options) { groupBy: false, sidePagination: 'server', original: options.params, - formatNoMatches: function() { return '{% trans "No purchase orders found" %}'; }, + formatNoMatches: function() { + return '{% trans "No purchase orders found" %}'; + }, columns: [ { title: '', @@ -388,7 +390,9 @@ function loadSalesOrderTable(table, options) { groupBy: false, sidePagination: 'server', original: options.params, - formatNoMatches: function() { return '{% trans "No sales orders found" %}'; }, + formatNoMatches: function() { + return '{% trans "No sales orders found" %}'; + }, columns: [ { title: '', @@ -502,7 +506,9 @@ function loadSalesOrderAllocationTable(table, options={}) { search: false, paginationVAlign: 'bottom', original: options.params, - formatNoMatches: function() { return '{% trans "No sales order allocations found" %}'; }, + formatNoMatches: function() { + return '{% trans "No sales order allocations found" %}'; + }, columns: [ { field: 'pk', diff --git a/InvenTree/templates/js/translated/part.js b/InvenTree/templates/js/translated/part.js index b0c2330360..5cc8409cb2 100644 --- a/InvenTree/templates/js/translated/part.js +++ b/InvenTree/templates/js/translated/part.js @@ -539,7 +539,9 @@ function loadPartVariantTable(table, partId, options={}) { showColumns: true, original: params, queryParams: filters, - formatNoMatches: function() { return '{% trans "No variants found" %}'; }, + formatNoMatches: function() { + return '{% trans "No variants found" %}'; + }, columns: cols, treeEnable: true, rootParentId: partId, @@ -587,7 +589,9 @@ function loadPartParameterTable(table, url, options) { queryParams: filters, name: 'partparameters', groupBy: false, - formatNoMatches: function() { return '{% trans "No parameters found" %}'; }, + formatNoMatches: function() { + return '{% trans "No parameters found" %}'; + }, columns: [ { checkbox: true, @@ -726,7 +730,9 @@ function loadParametricPartTable(table, options={}) { queryParams: table_headers, groupBy: false, name: options.name || 'parametric', - formatNoMatches: function() { return '{% trans "No parts found" %}'; }, + formatNoMatches: function() { + return '{% trans "No parts found" %}'; + }, columns: columns, showColumns: true, data: table_data, @@ -913,8 +919,7 @@ function loadPartTable(table, url, options={}) { formatter: function(value, row) { if (row.category) { return renderLink(value.pathstring, "/part/category/" + row.category + "/"); - } - else { + } else { return '{% trans "No category" %}'; } } @@ -987,7 +992,9 @@ function loadPartTable(table, url, options={}) { original: params, sidePagination: 'server', pagination: 'true', - formatNoMatches: function() { return '{% trans "No parts found" %}'; }, + formatNoMatches: function() { + return '{% trans "No parts found" %}'; + }, columns: columns, showColumns: true, showCustomView: false, diff --git a/InvenTree/templates/js/translated/stock.js b/InvenTree/templates/js/translated/stock.js index 7b7c1b6272..81120d82f9 100644 --- a/InvenTree/templates/js/translated/stock.js +++ b/InvenTree/templates/js/translated/stock.js @@ -881,13 +881,9 @@ function loadStockTable(table, options) { // REJECTED if (row.status == {{ StockStatus.REJECTED }}) { html += makeIconBadge('fa-times-circle icon-red', '{% trans "Stock item has been rejected" %}'); - } - - // LOST - else if (row.status == {{ StockStatus.LOST }}) { + } else if (row.status == {{ StockStatus.LOST }}) { html += makeIconBadge('fa-question-circle','{% trans "Stock item is lost" %}'); - } - else if (row.status == {{ StockStatus.DESTROYED }}) { + } else if (row.status == {{ StockStatus.DESTROYED }}) { html += makeIconBadge('fa-skull-crossbones', '{% trans "Stock item is destroyed" %}'); } @@ -1682,13 +1678,10 @@ function loadStockTrackingTable(table, options) { field: 'user', title: '{% trans "User" %}', formatter: function(value, row) { - if (value) - { + if (value) { // TODO - Format the user's first and last names return row.user_detail.username; - } - else - { + } else { return `{% trans "No user information" %}`; } } From 3d5144b9bcfd5cf39444922385d051f6c888fe47 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 20:30:44 +1000 Subject: [PATCH 45/67] Simplify eslintrc file --- .eslintrc.yml | 248 +------------------------------------------------- .gitignore | 5 + 2 files changed, 8 insertions(+), 245 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 966a822732..a10f4abbd8 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1,252 +1,10 @@ env: commonjs: false - es6: true browser: true es2021: true jquery: true -extends: 'eslint:recommended' +extends: + - google parserOptions: ecmaVersion: 12 - sourceType: 'script' -rules: - accessor-pairs: error - array-bracket-newline: 'off' - array-bracket-spacing: - - error - - never - array-callback-return: error - array-element-newline: 'off' - arrow-body-style: error - arrow-parens: - - error - - as-needed - arrow-spacing: 'off' - block-scoped-var: 'off' - block-spacing: 'off' - brace-style: 'off' - camelcase: 'off' - capitalized-comments: 'off' - class-methods-use-this: error - comma-dangle: 'off' - comma-spacing: 'off' - comma-style: - - error - - last - complexity: 'off' - computed-property-spacing: - - error - - never - consistent-return: 'off' - consistent-this: 'off' - curly: 'off' - default-case: 'off' - default-case-last: 'off' - default-param-last: error - dot-location: error - dot-notation: 'off' - eol-last: 'off' - eqeqeq: 'off' - func-call-spacing: error - func-name-matching: error - func-names: 'off' - func-style: - - error - - declaration - function-call-argument-newline: 'off' - function-paren-newline: 'off' - generator-star-spacing: error - grouped-accessor-pairs: error - guard-for-in: 'off' - id-denylist: error - id-length: 'off' - id-match: error - implicit-arrow-linebreak: - - error - - beside - indent: 'off' - init-declarations: error - jsx-quotes: error - key-spacing: 'off' - keyword-spacing: 'off' - line-comment-position: 'off' - linebreak-style: - - error - - unix - lines-around-comment: 'off' - lines-between-class-members: error - max-classes-per-file: error - max-depth: error - max-len: 'off' - max-lines: 'off' - max-lines-per-function: 'off' - max-nested-callbacks: error - max-params: 'off' - max-statements: 'off' - max-statements-per-line: 'off' - multiline-comment-style: 'off' - new-cap: error - new-parens: error - newline-per-chained-call: 'off' - no-alert: 'off' - no-array-constructor: 'off' - no-await-in-loop: error - no-bitwise: error - no-caller: error - no-confusing-arrow: error - no-console: 'off' - no-constructor-return: error - no-continue: 'off' - no-div-regex: error - no-duplicate-imports: error - no-else-return: 'off' - no-empty-function: 'off' - no-eq-null: 'off' - no-eval: error - no-extend-native: error - no-extra-bind: error - no-extra-label: error - no-extra-parens: 'off' - no-floating-decimal: error - no-implicit-coercion: - - error - - boolean: false - disallowTemplateShorthand: false - number: false - string: false - no-implicit-globals: 'off' - no-implied-eval: error - no-inline-comments: 'off' - no-inner-declarations: - - error - - functions - no-invalid-this: error - no-iterator: error - no-label-var: error - no-labels: error - no-lone-blocks: error - no-lonely-if: error - no-loop-func: 'off' - no-loss-of-precision: error - no-magic-numbers: 'off' - no-mixed-operators: - - error - - allowSamePrecedence: true - no-multi-assign: error - no-multi-spaces: - - error - - ignoreEOLComments: true - no-multi-str: error - no-multiple-empty-lines: 'off' - no-negated-condition: 'off' - no-nested-ternary: error - no-new: error - no-new-func: error - no-new-object: error - no-new-wrappers: error - no-nonoctal-decimal-escape: error - no-octal-escape: error - no-param-reassign: 'off' - no-plusplus: 'off' - no-promise-executor-return: error - no-proto: error - no-restricted-exports: error - no-restricted-globals: error - no-restricted-imports: error - no-restricted-properties: error - no-restricted-syntax: error - no-return-assign: error - no-return-await: error - no-script-url: error - no-self-compare: error - no-sequences: 'off' - no-shadow: 'off' - no-tabs: error - no-template-curly-in-string: error - no-ternary: 'off' - no-throw-literal: error - no-trailing-spaces: 'off' - no-undef-init: error - no-undefined: 'off' - no-underscore-dangle: 'off' - no-unmodified-loop-condition: error - no-unneeded-ternary: error - no-unreachable-loop: error - no-unsafe-optional-chaining: error - no-unused-expressions: 'off' - no-use-before-define: 'off' - no-useless-backreference: error - no-useless-call: error - no-useless-computed-key: error - no-useless-concat: error - no-useless-constructor: error - no-useless-rename: error - no-useless-return: error - no-var: 'off' - no-void: error - no-warning-comments: 'off' - no-whitespace-before-property: error - nonblock-statement-body-position: error - object-curly-newline: error - object-curly-spacing: 'off' - object-property-newline: 'off' - object-shorthand: 'off' - one-var: 'off' - one-var-declaration-per-line: error - operator-assignment: - - error - - always - operator-linebreak: error - padded-blocks: 'off' - padding-line-between-statements: error - prefer-arrow-callback: 'off' - prefer-const: 'off' - prefer-destructuring: 'off' - prefer-exponentiation-operator: error - prefer-named-capture-group: 'off' - prefer-numeric-literals: error - prefer-object-spread: error - prefer-promise-reject-errors: error - prefer-regex-literals: error - prefer-rest-params: error - prefer-spread: 'off' - prefer-template: 'off' - quote-props: 'off' - quotes: 'off' - radix: 'off' - require-atomic-updates: error - require-await: error - require-unicode-regexp: 'off' - rest-spread-spacing: error - semi: 'off' - semi-spacing: - - error - - after: true - before: false - semi-style: - - error - - last - sort-imports: error - sort-keys: 'off' - sort-vars: error - space-before-blocks: 'off' - space-before-function-paren: 'off' - space-in-parens: 'off' - space-infix-ops: 'off' - space-unary-ops: 'off' - spaced-comment: 'off' - strict: 'off' - switch-colon-spacing: error - symbol-description: error - template-curly-spacing: - - error - - never - template-tag-spacing: error - unicode-bom: - - error - - never - vars-on-top: 'off' - wrap-regex: error - yield-star-spacing: error - yoda: - - error - - never +rules: {} diff --git a/.gitignore b/.gitignore index 6f253ee8ea..420524d06f 100644 --- a/.gitignore +++ b/.gitignore @@ -75,3 +75,8 @@ dev/ # Locale stats file locale_stats.json + +# node.js +package-lock.json +package.json +node_modules/ \ No newline at end of file From 9781d585b07e649ae235d01825f629575b6cc116 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 21:08:46 +1000 Subject: [PATCH 46/67] Install eslint-config-google --- .github/workflows/javascript.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/javascript.yaml b/.github/workflows/javascript.yaml index 110f5ffbe1..9b7199a191 100644 --- a/.github/workflows/javascript.yaml +++ b/.github/workflows/javascript.yaml @@ -25,7 +25,9 @@ jobs: steps: - name: Install node.js uses: actions/setup-node@v2 - - run: npm install + - run: | + npm install + npm install eslint eslint-config-google - name: Checkout Code uses: actions/checkout@v2 - name: Setup Python From fc125ca20c3293eb6a7cef86d9b4cc870975ace1 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 21:16:54 +1000 Subject: [PATCH 47/67] Fix workflow file --- .github/workflows/javascript.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/javascript.yaml b/.github/workflows/javascript.yaml index 9b7199a191..a07b516ac6 100644 --- a/.github/workflows/javascript.yaml +++ b/.github/workflows/javascript.yaml @@ -25,9 +25,7 @@ jobs: steps: - name: Install node.js uses: actions/setup-node@v2 - - run: | - npm install - npm install eslint eslint-config-google + - run: npm install - name: Checkout Code uses: actions/checkout@v2 - name: Setup Python @@ -47,5 +45,6 @@ jobs: python check_js_templates.py - name: Lint Javascript Files run: | + npm install eslint eslint-config-google invoke render-js-files npx eslint js_tmp/*.js \ No newline at end of file From 4ffcf48ab3e58a2d51f6978d26e0ecd5cdac2e2b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 21:21:26 +1000 Subject: [PATCH 48/67] 4 spaces for indent --- .eslintrc.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index a10f4abbd8..8c4ed784f1 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -7,4 +7,8 @@ extends: - google parserOptions: ecmaVersion: 12 -rules: {} +rules: { + indent: + - error + - 4 +} From 6dde353646d7c11513d9cf5dbb749cbe9f1cd608 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 21:25:43 +1000 Subject: [PATCH 49/67] Fix indent rule --- .eslintrc.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 8c4ed784f1..c6a003fe06 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -7,8 +7,6 @@ extends: - google parserOptions: ecmaVersion: 12 -rules: { +rules: indent: - - error - 4 -} From a1b7239b7eb50a871fa36ebd1abc8123cc73c18b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 21:30:10 +1000 Subject: [PATCH 50/67] indent error --- .eslintrc.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintrc.yml b/.eslintrc.yml index c6a003fe06..156526e394 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -9,4 +9,5 @@ parserOptions: ecmaVersion: 12 rules: indent: + - error - 4 From cb403a5b295efc2b679306be20069a3f0d1116d3 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 30 Aug 2021 22:28:01 +1000 Subject: [PATCH 51/67] More work --- .eslintrc.yml | 7 ++++ InvenTree/templates/js/translated/tables.js | 42 ++++++++++++++------- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 156526e394..ba0e8d6cac 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -8,6 +8,13 @@ extends: parserOptions: ecmaVersion: 12 rules: + no-var: off + guard-for-in: off + no-trailing-spaces: off + camelcase: off + padded-blocks: off + prefer-const: off + max-len: off indent: - error - 4 diff --git a/InvenTree/templates/js/translated/tables.js b/InvenTree/templates/js/translated/tables.js index d5c9021806..1fc4610f95 100644 --- a/InvenTree/templates/js/translated/tables.js +++ b/InvenTree/templates/js/translated/tables.js @@ -12,10 +12,22 @@ reloadTableFilters, */ +/** + * Reload a named table + * @param table + */ function reloadtable(table) { $(table).bootstrapTable('refresh'); } + +/** + * Render a URL for display + * @param {String} text + * @param {String} url + * @param {object} options + * @returns link text + */ function renderLink(text, url, options={}) { if (url === null || url === undefined || url === '') { return text; @@ -63,6 +75,11 @@ function linkButtonsToSelection(table, buttons) { } +/** + * Returns true if the input looks like a valid number + * @param {String} n + * @returns + */ function isNumeric(n) { return !isNaN(parseFloat(n)) && isFinite(n); } @@ -134,7 +151,6 @@ function convertQueryParameters(params, filters) { var ordering = params['sort'] || null; if (ordering) { - if (order == 'desc') { ordering = `-${ordering}`; } @@ -332,42 +348,42 @@ function customGroupSorter(sortName, sortOrder, sortData) { } // Expose default bootstrap table string literals to translation layer -(function ($) { +(function($) { 'use strict'; $.fn.bootstrapTable.locales['en-US-custom'] = { - formatLoadingMessage: function () { + formatLoadingMessage: function() { return '{% trans "Loading data" %}'; }, - formatRecordsPerPage: function (pageNumber) { + formatRecordsPerPage: function(pageNumber) { return `${pageNumber} {% trans "rows per page" %}`; }, - formatShowingRows: function (pageFrom, pageTo, totalRows) { + formatShowingRows: function(pageFrom, pageTo, totalRows) { return `{% trans "Showing" %} ${pageFrom} {% trans "to" %} ${pageTo} {% trans "of" %} ${totalRows} {% trans "rows" %}`; }, - formatSearch: function () { + formatSearch: function() { return '{% trans "Search" %}'; }, - formatNoMatches: function () { + formatNoMatches: function() { return '{% trans "No matching results" %}'; }, - formatPaginationSwitch: function () { + formatPaginationSwitch: function() { return '{% trans "Hide/Show pagination" %}'; }, - formatRefresh: function () { + formatRefresh: function() { return '{% trans "Refresh" %}'; }, - formatToggle: function () { + formatToggle: function() { return '{% trans "Toggle" %}'; }, - formatColumns: function () { + formatColumns: function() { return '{% trans "Columns" %}'; }, - formatAllRows: function () { + formatAllRows: function() { return '{% trans "All" %}'; } }; $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales['en-US-custom']); -})(jQuery); \ No newline at end of file +})(jQuery); From 5ad2eabab1570e88248877db26ab4fd654111afe Mon Sep 17 00:00:00 2001 From: Matthias Mair <66015116+matmair@users.noreply.github.com> Date: Tue, 31 Aug 2021 13:17:38 +0200 Subject: [PATCH 52/67] fix html templates --- .../part/templates/part/part_pricing.html | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/InvenTree/part/templates/part/part_pricing.html b/InvenTree/part/templates/part/part_pricing.html index be80efe1e7..232af5623d 100644 --- a/InvenTree/part/templates/part/part_pricing.html +++ b/InvenTree/part/templates/part/part_pricing.html @@ -9,11 +9,11 @@
- + - +
{% trans 'Part' %}{% trans 'Part' %} {{ part }}
{% trans 'Quantity' %}{% trans 'Quantity' %} {{ quantity }}
@@ -23,13 +23,13 @@ {% if min_total_buy_price %} - + {% if quantity > 1 %} - + @@ -49,26 +49,26 @@
{% trans 'Unit Cost' %}{% trans 'Unit Cost' %} Min: {% include "price.html" with price=min_unit_buy_price %} Max: {% include "price.html" with price=max_unit_buy_price %}
{% trans 'Total Cost' %}{% trans 'Total Cost' %} Min: {% include "price.html" with price=min_total_buy_price %} Max: {% include "price.html" with price=max_total_buy_price %}
{% if min_total_bom_price %} - + {% if quantity > 1 %} - + {% endif %} {% if min_total_bom_purchase_price %} - + {% if quantity > 1 %} - + @@ -97,11 +97,11 @@

{% trans 'Internal Price' %}

{% trans 'Unit Cost' %}{% trans 'Unit Cost' %} Min: {% include "price.html" with price=min_unit_bom_price %} Max: {% include "price.html" with price=max_unit_bom_price %}
{% trans 'Total Cost' %}{% trans 'Total Cost' %} Min: {% include "price.html" with price=min_total_bom_price %} Max: {% include "price.html" with price=max_total_bom_price %}
{% trans 'Unit Purchase Price' %}{% trans 'Unit Purchase Price' %} Min: {% include "price.html" with price=min_unit_bom_purchase_price %} Max: {% include "price.html" with price=max_unit_bom_purchase_price %}
{% trans 'Total Purchase Price' %}{% trans 'Total Purchase Price' %} Min: {% include "price.html" with price=min_total_bom_purchase_price %} Max: {% include "price.html" with price=max_total_bom_purchase_price %}
- + - +
{% trans 'Unit Cost' %}{% trans 'Unit Cost' %} {% include "price.html" with price=unit_internal_part_price %}
{% trans 'Total Cost' %}{% trans 'Total Cost' %} {% include "price.html" with price=total_internal_part_price %}
@@ -112,11 +112,11 @@

{% trans 'Sale Price' %}

- + - +
{% trans 'Unit Cost' %}{% trans 'Unit Cost' %} {% include "price.html" with price=unit_part_price %}
{% trans 'Total Cost' %}{% trans 'Total Cost' %} {% include "price.html" with price=total_part_price %}
From 2b1279e64737c29bdcaed0e95136bf9008822699 Mon Sep 17 00:00:00 2001 From: Matthias Mair <66015116+matmair@users.noreply.github.com> Date: Wed, 1 Sep 2021 08:14:08 +0200 Subject: [PATCH 53/67] now with closing tags --- .../part/templates/part/part_pricing.html | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/InvenTree/part/templates/part/part_pricing.html b/InvenTree/part/templates/part/part_pricing.html index 232af5623d..b24a89f5c9 100644 --- a/InvenTree/part/templates/part/part_pricing.html +++ b/InvenTree/part/templates/part/part_pricing.html @@ -9,11 +9,11 @@ - + - +
{% trans 'Part' %}{% trans 'Part' %} {{ part }}
{% trans 'Quantity' %}{% trans 'Quantity' %} {{ quantity }}
@@ -23,13 +23,13 @@ {% if min_total_buy_price %} - + {% if quantity > 1 %} - + @@ -49,26 +49,26 @@
{% trans 'Unit Cost' %}{% trans 'Unit Cost' %} Min: {% include "price.html" with price=min_unit_buy_price %} Max: {% include "price.html" with price=max_unit_buy_price %}
{% trans 'Total Cost' %}{% trans 'Total Cost' %} Min: {% include "price.html" with price=min_total_buy_price %} Max: {% include "price.html" with price=max_total_buy_price %}
{% if min_total_bom_price %} - + {% if quantity > 1 %} - + {% endif %} {% if min_total_bom_purchase_price %} - + {% if quantity > 1 %} - + @@ -97,11 +97,11 @@

{% trans 'Internal Price' %}

{% trans 'Unit Cost' %}{% trans 'Unit Cost' %} Min: {% include "price.html" with price=min_unit_bom_price %} Max: {% include "price.html" with price=max_unit_bom_price %}
{% trans 'Total Cost' %}{% trans 'Total Cost' %} Min: {% include "price.html" with price=min_total_bom_price %} Max: {% include "price.html" with price=max_total_bom_price %}
{% trans 'Unit Purchase Price' %}{% trans 'Unit Purchase Price' %} Min: {% include "price.html" with price=min_unit_bom_purchase_price %} Max: {% include "price.html" with price=max_unit_bom_purchase_price %}
{% trans 'Total Purchase Price' %}{% trans 'Total Purchase Price' %} Min: {% include "price.html" with price=min_total_bom_purchase_price %} Max: {% include "price.html" with price=max_total_bom_purchase_price %}
- + - +
{% trans 'Unit Cost' %}{% trans 'Unit Cost' %} {% include "price.html" with price=unit_internal_part_price %}
{% trans 'Total Cost' %}{% trans 'Total Cost' %} {% include "price.html" with price=total_internal_part_price %}
@@ -112,11 +112,11 @@

{% trans 'Sale Price' %}

- + - +
{% trans 'Unit Cost' %}{% trans 'Unit Cost' %} {% include "price.html" with price=unit_part_price %}
{% trans 'Total Cost' %}{% trans 'Total Cost' %} {% include "price.html" with price=total_part_price %}
From 6a420fd95c22d8de2d11e0d89d77defd4093b844 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 2 Sep 2021 00:17:12 +0200 Subject: [PATCH 54/67] replace bold tags --- InvenTree/build/templates/build/auto_allocate.html | 2 +- InvenTree/build/templates/build/complete.html | 2 +- InvenTree/build/templates/build/detail.html | 2 +- InvenTree/common/templates/common/edit_setting.html | 2 +- .../order/templates/order/order_wizard/match_fields.html | 2 +- InvenTree/order/templates/order/receive_parts.html | 2 +- InvenTree/order/templates/order/sales_order_ship.html | 2 +- InvenTree/order/templates/order/so_allocation_delete.html | 4 ++-- InvenTree/part/templates/part/bom.html | 2 +- InvenTree/part/templates/part/bom_duplicate.html | 2 +- .../part/templates/part/bom_upload/match_fields.html | 2 +- InvenTree/part/templates/part/bom_upload/upload_file.html | 4 ++-- InvenTree/part/templates/part/create_part.html | 2 +- .../templates/part/import_wizard/ajax_match_fields.html | 2 +- .../part/templates/part/import_wizard/match_fields.html | 2 +- InvenTree/part/templates/part/partial_delete.html | 2 +- InvenTree/part/templates/part/prices.html | 8 ++++---- InvenTree/part/templates/part/variant_part.html | 2 +- InvenTree/stock/templates/stock/item_base.html | 2 +- InvenTree/stock/templates/stock/item_delete.html | 2 +- InvenTree/stock/templates/stock/loc_link.html | 2 +- InvenTree/stock/templates/stock/location_delete.html | 4 ++-- InvenTree/stock/templates/stock/stockitem_convert.html | 4 ++-- InvenTree/templates/InvenTree/index.html | 2 +- InvenTree/templates/InvenTree/search.html | 2 +- InvenTree/templates/InvenTree/settings/navbar.html | 4 ++-- InvenTree/templates/InvenTree/settings/setting.html | 6 +++--- InvenTree/templates/navbar.html | 2 +- InvenTree/templates/registration/login.html | 2 +- InvenTree/templates/stats.html | 6 +++--- 30 files changed, 42 insertions(+), 42 deletions(-) diff --git a/InvenTree/build/templates/build/auto_allocate.html b/InvenTree/build/templates/build/auto_allocate.html index 48d1837ae0..49178ff556 100644 --- a/InvenTree/build/templates/build/auto_allocate.html +++ b/InvenTree/build/templates/build/auto_allocate.html @@ -6,7 +6,7 @@ {{ block.super }}
- {% trans "Automatically Allocate Stock" %}
+ {% trans "Automatically Allocate Stock" %}
{% trans "The following stock items will be allocated to the specified build output" %}
{% if allocations %} diff --git a/InvenTree/build/templates/build/complete.html b/InvenTree/build/templates/build/complete.html index 527c0598d4..eeedc027dd 100644 --- a/InvenTree/build/templates/build/complete.html +++ b/InvenTree/build/templates/build/complete.html @@ -9,7 +9,7 @@
{% else %}
- {% trans "Build Order is incomplete" %}
+ {% trans "Build Order is incomplete" %}
    {% if build.incomplete_count > 0 %}
  • {% trans "Incompleted build outputs remain" %}
  • diff --git a/InvenTree/build/templates/build/detail.html b/InvenTree/build/templates/build/detail.html index d6b59a060d..9ebd059428 100644 --- a/InvenTree/build/templates/build/detail.html +++ b/InvenTree/build/templates/build/detail.html @@ -222,7 +222,7 @@
{% else %}
- {% trans "Create a new build output" %}
+ {% trans "Create a new build output" %}
{% trans "No incomplete build outputs remain." %}
{% trans "Create a new build output using the button above" %}
diff --git a/InvenTree/common/templates/common/edit_setting.html b/InvenTree/common/templates/common/edit_setting.html index c74ed7d591..2ebebf75af 100644 --- a/InvenTree/common/templates/common/edit_setting.html +++ b/InvenTree/common/templates/common/edit_setting.html @@ -6,7 +6,7 @@ {{ block.super }} {% endblock %} \ No newline at end of file diff --git a/InvenTree/company/templates/company/company_base.html b/InvenTree/company/templates/company/company_base.html index c50a9490f0..e4ca64b32e 100644 --- a/InvenTree/company/templates/company/company_base.html +++ b/InvenTree/company/templates/company/company_base.html @@ -78,7 +78,7 @@ {% if company.currency %} {{ company.currency }} {% else %} - {% trans "Uses default currency" %} + {% trans "Uses default currency" %} {% endif %} diff --git a/InvenTree/company/templates/company/manufacturer_part.html b/InvenTree/company/templates/company/manufacturer_part.html index cc2dd68840..13e81aaa90 100644 --- a/InvenTree/company/templates/company/manufacturer_part.html +++ b/InvenTree/company/templates/company/manufacturer_part.html @@ -225,7 +225,7 @@ $("#multi-parameter-delete").click(function() {
    `; selections.forEach(function(item) { - text += `
  • ${item.name} - ${item.value}
  • `; + text += `
  • ${item.name} - ${item.value}
  • `; }); text += ` diff --git a/InvenTree/order/templates/order/order_wizard/select_parts.html b/InvenTree/order/templates/order/order_wizard/select_parts.html index a02113fa18..e456b234b6 100644 --- a/InvenTree/order/templates/order/order_wizard/select_parts.html +++ b/InvenTree/order/templates/order/order_wizard/select_parts.html @@ -38,7 +38,7 @@ {% include "hover_image.html" with image=part.image hover=False %} - {{ part.full_name }} {{ part.description }} + {{ part.full_name }} {{ part.description }}
diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html index 41332eeec5..6bad2bbbdf 100644 --- a/InvenTree/part/templates/part/detail.html +++ b/InvenTree/part/templates/part/detail.html @@ -18,7 +18,7 @@
{% if part.is_template %}
- {% blocktrans with full_name=part.full_name%}Showing stock for all variants of {{full_name}}{% endblocktrans %} + {% blocktrans with full_name=part.full_name%}Showing stock for all variants of {{full_name}}{% endblocktrans %}
{% endif %} {% include "stock_table.html" %} diff --git a/InvenTree/part/templates/part/part_pricing.html b/InvenTree/part/templates/part/part_pricing.html index b24a89f5c9..9ec80882af 100644 --- a/InvenTree/part/templates/part/part_pricing.html +++ b/InvenTree/part/templates/part/part_pricing.html @@ -37,7 +37,7 @@ {% else %} - {% trans 'No supplier pricing available' %} + {% trans 'No supplier pricing available' %} {% endif %} @@ -78,14 +78,14 @@ {% if part.has_complete_bom_pricing == False %} - {% trans 'Note: BOM pricing is incomplete for this part' %} + {% trans 'Note: BOM pricing is incomplete for this part' %} {% endif %} {% else %} - {% trans 'No BOM pricing available' %} + {% trans 'No BOM pricing available' %} {% endif %} diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index c4c6b863f4..69412ab2bb 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -37,7 +37,7 @@ {% else %} - {% trans 'No supplier pricing available' %} + {% trans 'No supplier pricing available' %} {% endif %} @@ -83,14 +83,14 @@ {% if part.has_complete_bom_pricing == False %} - {% trans 'Note: BOM pricing is incomplete for this part' %} + {% trans 'Note: BOM pricing is incomplete for this part' %} {% endif %} {% else %} - {% trans 'No BOM pricing available' %} + {% trans 'No BOM pricing available' %} {% endif %} @@ -179,7 +179,7 @@

{% trans 'Stock Pricing' %} - +

{% if price_history|length > 0 %}
diff --git a/InvenTree/part/templates/part/variant_part.html b/InvenTree/part/templates/part/variant_part.html index f3739b2c9c..8a6064711f 100644 --- a/InvenTree/part/templates/part/variant_part.html +++ b/InvenTree/part/templates/part/variant_part.html @@ -7,7 +7,7 @@
{% trans "Create new part variant" %}
- {% blocktrans with full_name=part.full_name %}Create a new variant of template '{{full_name}}'.{% endblocktrans %} + {% blocktrans with full_name=part.full_name %}Create a new variant of template '{{full_name}}'.{% endblocktrans %}
{% endblock %} \ No newline at end of file diff --git a/InvenTree/report/templates/report/inventree_build_order_base.html b/InvenTree/report/templates/report/inventree_build_order_base.html index 2d2d2766bb..20e6a6ffd0 100644 --- a/InvenTree/report/templates/report/inventree_build_order_base.html +++ b/InvenTree/report/templates/report/inventree_build_order_base.html @@ -128,7 +128,7 @@ content: "v{{report_revision}} - {{ date.isoformat }}"; {% if build.target_date %} {{ build.target_date }} {% else %} - Not specified + Not specified {% endif %} @@ -138,7 +138,7 @@ content: "v{{report_revision}} - {{ date.isoformat }}"; {% if build.sales_order %} {% internal_link build.sales_order.get_absolute_url build.sales_order %} {% else %} - Not specified + Not specified {% endif %} diff --git a/InvenTree/report/templates/report/inventree_test_report_base.html b/InvenTree/report/templates/report/inventree_test_report_base.html index d6d9c5644f..d702973c30 100644 --- a/InvenTree/report/templates/report/inventree_test_report_base.html +++ b/InvenTree/report/templates/report/inventree_test_report_base.html @@ -68,8 +68,8 @@ content: "{% trans 'Stock Item Test Report' %}"; {{ part.full_name }}

{{ part.description }}

-

{{ stock_item.location }}

-

Stock Item ID: {{ stock_item.pk }}

+

{{ stock_item.location }}

+

Stock Item ID: {{ stock_item.pk }}

diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html index a420da87f5..759732fe6e 100644 --- a/InvenTree/stock/templates/stock/item_base.html +++ b/InvenTree/stock/templates/stock/item_base.html @@ -300,7 +300,7 @@ {% if item.location %} {{ item.location.name }} {% else %} - {% trans "No location set" %} + {% trans "No location set" %} {% endif %} {% endif %} @@ -367,7 +367,7 @@ {% if item.supplier_part.manufacturer_part.manufacturer %} {{ item.supplier_part.manufacturer_part.manufacturer.name }} {% else %} - {% trans "No manufacturer set" %} + {% trans "No manufacturer set" %} {% endif %} @@ -414,7 +414,7 @@ {% if item.stocktake_date %} {{ item.stocktake_date }} {{ item.stocktake_user }} {% else %} - {% trans "No stocktake performed" %} + {% trans "No stocktake performed" %} {% endif %} diff --git a/InvenTree/stock/templates/stock/location_delete.html b/InvenTree/stock/templates/stock/location_delete.html index 738131ec92..22b4168173 100644 --- a/InvenTree/stock/templates/stock/location_delete.html +++ b/InvenTree/stock/templates/stock/location_delete.html @@ -20,7 +20,7 @@ the top level 'Stock' location.
    {% for loc in location.children.all %} -
  • {{ loc.name }} - {{ loc.description}}
  • +
  • {{ loc.name }} - {{ loc.description}}
  • {% endfor %}
{% endif %} @@ -36,7 +36,7 @@ If this location is deleted, these items will be moved to the top level 'Stock'
    {% for item in location.stock_items.all %} -
  • {{ item.part.full_name }} - {{ item.part.description }}{% decimal item.quantity %}
  • +
  • {{ item.part.full_name }} - {{ item.part.description }}{% decimal item.quantity %}
  • {% endfor %}
{% endif %} diff --git a/InvenTree/stock/templates/stock/stock_adjust.html b/InvenTree/stock/templates/stock/stock_adjust.html index 5ee02c7ca3..60a9ec2658 100644 --- a/InvenTree/stock/templates/stock/stock_adjust.html +++ b/InvenTree/stock/templates/stock/stock_adjust.html @@ -24,7 +24,7 @@ {% for item in stock_items %} {% include "hover_image.html" with image=item.part.image hover=True %} - {{ item.part.full_name }} {{ item.part.description }} + {{ item.part.full_name }} {{ item.part.description }} {{ item.location.pathstring }} {% decimal item.quantity %} diff --git a/InvenTree/stock/templates/stock/stockitem_convert.html b/InvenTree/stock/templates/stock/stockitem_convert.html index c75b278f74..d4e3487567 100644 --- a/InvenTree/stock/templates/stock/stockitem_convert.html +++ b/InvenTree/stock/templates/stock/stockitem_convert.html @@ -5,7 +5,7 @@
{% trans "Convert Stock Item" %}
- {% blocktrans with part=item.part %}This stock item is current an instance of {{part}}{% endblocktrans %}
+ {% blocktrans with part=item.part %}This stock item is current an instance of {{part}}{% endblocktrans %}
{% trans "It can be converted to one of the part variants listed below." %}
diff --git a/InvenTree/templates/InvenTree/search.html b/InvenTree/templates/InvenTree/search.html index e027df7a7b..af0711bc35 100644 --- a/InvenTree/templates/InvenTree/search.html +++ b/InvenTree/templates/InvenTree/search.html @@ -21,7 +21,7 @@ {% if query %} {% else %}
-

{% trans "Enter a search query" %}

+

{% trans "Enter a search query" %}

{% endif %} @@ -268,7 +268,7 @@ var text = "{% trans "Shipped to customer" %}"; return renderLink(text, `/company/${row.customer}/assigned-stock/`); } else { - return '{% trans "No stock location set" %}'; + return '{% trans "No stock location set" %}'; } } } diff --git a/InvenTree/templates/InvenTree/settings/currencies.html b/InvenTree/templates/InvenTree/settings/currencies.html index 85274e91aa..08434cc704 100644 --- a/InvenTree/templates/InvenTree/settings/currencies.html +++ b/InvenTree/templates/InvenTree/settings/currencies.html @@ -40,7 +40,7 @@ {% if rates_updated %} {{ rates_updated }} {% else %} - {% trans "Never" %} + {% trans "Never" %} {% endif %}
diff --git a/InvenTree/templates/InvenTree/settings/setting.html b/InvenTree/templates/InvenTree/settings/setting.html index 89c9989c42..de3439a647 100644 --- a/InvenTree/templates/InvenTree/settings/setting.html +++ b/InvenTree/templates/InvenTree/settings/setting.html @@ -26,7 +26,7 @@ {% if setting.value %} {{ setting.value }} {% else %} - {% trans "No value set" %} + {% trans "No value set" %} {% endif %} diff --git a/InvenTree/templates/about.html b/InvenTree/templates/about.html index 358d468ca8..5b589bf47c 100644 --- a/InvenTree/templates/about.html +++ b/InvenTree/templates/about.html @@ -87,7 +87,7 @@ - + diff --git a/InvenTree/templates/clip.html b/InvenTree/templates/clip.html index a56ece838c..d4232b02b4 100644 --- a/InvenTree/templates/clip.html +++ b/InvenTree/templates/clip.html @@ -1,5 +1,5 @@ {% load i18n %} - + \ No newline at end of file diff --git a/InvenTree/templates/modals.html b/InvenTree/templates/modals.html index 11ddc40938..ac26a5fa9a 100644 --- a/InvenTree/templates/modals.html +++ b/InvenTree/templates/modals.html @@ -14,7 +14,7 @@ - +
`; +
  • +

    ${item.MPN} - ${item.part_detail.full_name}

    +
  • `; + }); + + text += ` + +
    `; showQuestionDialog( '{% trans "Delete Manufacturer Parts" %}', @@ -426,7 +439,7 @@ function deleteManufacturerParts(selections, options={}) { if (options.onSuccess) { options.onSuccess(); } - }) + }); } } ); @@ -443,13 +456,13 @@ function loadManufacturerPartTable(table, url, options) { var params = options.params || {}; // Load filters - var filters = loadTableFilters("manufacturer-part"); + var filters = loadTableFilters('manufacturer-part'); for (var key in params) { filters[key] = params[key]; } - setupFilterList("manufacturer-part", $(table)); + setupFilterList('manufacturer-part', $(table)); $(table).inventreeTable({ url: url, @@ -505,7 +518,7 @@ function loadManufacturerPartTable(table, url, options) { return html; } else { - return "-"; + return '-'; } } }, @@ -563,8 +576,9 @@ function loadManufacturerPartTable(table, url, options) { { onSuccess: function() { $(table).bootstrapTable('refresh'); + } } - }); + ); }); $(table).find('.button-manufacturer-part-delete').click(function() { @@ -575,9 +589,10 @@ function loadManufacturerPartTable(table, url, options) { { onSuccess: function() { $(table).bootstrapTable('refresh'); + } } - }); - }) + ); + }); } }); } @@ -591,7 +606,7 @@ function loadManufacturerPartParameterTable(table, url, options) { var params = options.params || {}; // Load filters - var filters = loadTableFilters("manufacturer-part-parameters"); + var filters = loadTableFilters('manufacturer-part-parameters'); // Overwrite explicit parameters for (var key in params) { @@ -607,7 +622,9 @@ function loadManufacturerPartParameterTable(table, url, options) { queryParams: filters, name: 'manufacturerpartparameters', groupBy: false, - formatNoMatches: function() { return '{% trans "No parameters found" %}'; }, + formatNoMatches: function() { + return '{% trans "No parameters found" %}'; + }, columns: [ { checkbox: true, @@ -695,13 +712,13 @@ function loadSupplierPartTable(table, url, options) { var params = options.params || {}; // Load filters - var filters = loadTableFilters("supplier-part"); + var filters = loadTableFilters('supplier-part'); for (var key in params) { filters[key] = params[key]; } - setupFilterList("supplier-part", $(table)); + setupFilterList('supplier-part', $(table)); $(table).inventreeTable({ url: url, @@ -710,7 +727,9 @@ function loadSupplierPartTable(table, url, options) { queryParams: filters, name: 'supplierparts', groupBy: false, - formatNoMatches: function() { return '{% trans "No supplier parts found" %}'; }, + formatNoMatches: function() { + return '{% trans "No supplier parts found" %}'; + }, columns: [ { checkbox: true, @@ -755,7 +774,7 @@ function loadSupplierPartTable(table, url, options) { return html; } else { - return "-"; + return '-'; } }, }, @@ -781,7 +800,7 @@ function loadSupplierPartTable(table, url, options) { return html; } else { - return "-"; + return '-'; } } }, @@ -795,7 +814,7 @@ function loadSupplierPartTable(table, url, options) { if (value && row.manufacturer_part) { return renderLink(value, `/manufacturer-part/${row.manufacturer_part}/`); } else { - return "-"; + return '-'; } } }, @@ -870,7 +889,7 @@ function loadSupplierPartTable(table, url, options) { } } ); - }) + }); } }); -} \ No newline at end of file +} diff --git a/InvenTree/templates/js/translated/filters.js b/InvenTree/templates/js/translated/filters.js index 8d70715ff0..d7e8f45ca5 100644 --- a/InvenTree/templates/js/translated/filters.js +++ b/InvenTree/templates/js/translated/filters.js @@ -27,12 +27,12 @@ function defaultFilters() { return { - stock: "cascade=1&in_stock=1", - build: "", - parts: "cascade=1", - company: "", - salesorder: "", - purchaseorder: "", + stock: 'cascade=1&in_stock=1', + build: '', + parts: 'cascade=1', + company: '', + salesorder: '', + purchaseorder: '', }; } @@ -45,7 +45,7 @@ function defaultFilters() { */ function loadTableFilters(tableKey) { - var lookup = "table-filters-" + tableKey.toLowerCase(); + var lookup = 'table-filters-' + tableKey.toLowerCase(); var defaults = defaultFilters()[tableKey] || ''; @@ -53,7 +53,7 @@ function loadTableFilters(tableKey) { var filters = {}; - filterstring.split("&").forEach(function(item) { + filterstring.split('&').forEach(function(item) { item = item.trim(); if (item.length > 0) { @@ -78,7 +78,7 @@ function loadTableFilters(tableKey) { * @param {*} filters - object of string:string pairs */ function saveTableFilters(tableKey, filters) { - var lookup = "table-filters-" + tableKey.toLowerCase(); + var lookup = 'table-filters-' + tableKey.toLowerCase(); var strings = []; @@ -201,7 +201,7 @@ function generateAvailableFilterList(tableKey) { var html = `