From 6a5dc33e209fa07a493f0bb47cd5d345725756b4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 11 Mar 2022 23:32:36 +0100 Subject: [PATCH 01/59] ignore admin rendering fields for coverage --- InvenTree/common/admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/common/admin.py b/InvenTree/common/admin.py index 3ec0e32da1..91c872a642 100644 --- a/InvenTree/common/admin.py +++ b/InvenTree/common/admin.py @@ -12,7 +12,7 @@ class SettingsAdmin(ImportExportModelAdmin): list_display = ('key', 'value') - def get_readonly_fields(self, request, obj=None): + def get_readonly_fields(self, request, obj=None): # pragma: no cover """ Prevent the 'key' field being edited once the setting is created """ @@ -27,7 +27,7 @@ class UserSettingsAdmin(ImportExportModelAdmin): list_display = ('key', 'value', 'user', ) - def get_readonly_fields(self, request, obj=None): + def get_readonly_fields(self, request, obj=None): # pragma: no cover """ Prevent the 'key' field being edited once the setting is created """ From e955b75041bba10784232ebce7b8cf8beaa58657 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 11 Mar 2022 23:41:30 +0100 Subject: [PATCH 02/59] test restart flag reset --- InvenTree/common/tests.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index 1dd5bc03bc..1a51638c21 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -223,3 +223,26 @@ class NotificationTest(TestCase): self.assertFalse(NotificationEntry.check_recent('test.notification2', 1, delta)) self.assertTrue(NotificationEntry.check_recent('test.notification', 1, delta)) + + +class LoadingTest(TestCase): + """ + Tests for the common config + """ + + def test_restart_flag(self): + """ + Test that the restart flag is reset on start + """ + + import common.models + from plugin import registry + + # set flag true + common.models.InvenTreeSetting.set_setting('SERVER_RESTART_REQUIRED', False, None) + + # reload the app + registry.reload_plugins() + + # now it should be false again + self.assertFalse(common.models.InvenTreeSetting.get_setting('SERVER_RESTART_REQUIRED')) From e05ebce05872dac0d89da356e166577e75240cab Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 00:27:37 +0100 Subject: [PATCH 03/59] test allValues --- InvenTree/common/tests.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index 1a51638c21..ef3b6ea54e 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -46,6 +46,19 @@ class SettingsTest(TestCase): # Check object lookup (case insensitive) self.assertEqual(InvenTreeSetting.get_setting_object('iNvEnTrEE_inSTanCE').pk, 1) + def test_allValues(self): + """ + Make sure that the allValues functions returns correctly + """ + # define testing settings + + # check a few keys + result = InvenTreeSetting.allValues() + self.assertIn('INVENTREE_INSTANCE', result) + self.assertIn('PART_COPY_TESTS', result) + self.assertIn('STOCK_OWNERSHIP_CONTROL', result) + self.assertIn('SIGNUP_GROUP', result) + def test_required_values(self): """ - Ensure that every global setting has a name. From 00b76ace778edaabc0e87808a01af1a60be4b810 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 00:28:04 +0100 Subject: [PATCH 04/59] increase coverage of settings --- InvenTree/common/tests.py | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index ef3b6ea54e..897d28964a 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -46,6 +46,46 @@ class SettingsTest(TestCase): # Check object lookup (case insensitive) self.assertEqual(InvenTreeSetting.get_setting_object('iNvEnTrEE_inSTanCE').pk, 1) + def test_settings_functions(self): + """ + Test settings functions and properties + """ + # define settings to check + instance_ref = 'INVENTREE_INSTANCE' + instance_obj = InvenTreeSetting.get_setting_object(instance_ref) + + stale_ref = 'STOCK_STALE_DAYS' + stale_days = InvenTreeSetting.get_setting_object(stale_ref) + + report_size_obj = InvenTreeSetting.get_setting_object('REPORT_DEFAULT_PAGE_SIZE') + report_test_obj = InvenTreeSetting.get_setting_object('REPORT_ENABLE_TEST_REPORT') + + # check settings base fields + self.assertEqual(instance_obj.name, 'InvenTree Instance Name') + self.assertEqual(instance_obj.get_setting_name(instance_ref), 'InvenTree Instance Name') + self.assertEqual(instance_obj.description, 'String descriptor for the server instance') + self.assertEqual(instance_obj.get_setting_description(instance_ref), 'String descriptor for the server instance') + + # check units + self.assertEqual(instance_obj.units, '') + self.assertEqual(instance_obj.get_setting_units(instance_ref), '') + self.assertEqual(instance_obj.get_setting_units(stale_ref), 'days') + + # check is_choice + self.assertEqual(instance_obj.is_choice(), False) + self.assertEqual(report_size_obj.is_choice(), True) + + # check setting_type + self.assertEqual(instance_obj.setting_type(), 'string') + self.assertEqual(report_test_obj.setting_type(), 'boolean') + self.assertEqual(stale_days.setting_type(), 'integer') + + # check as_int + self.assertEqual(stale_days.as_int(), 0) + + # check to_native_value + self.assertEqual(stale_days.to_native_value(), 0) + def test_allValues(self): """ Make sure that the allValues functions returns correctly From 5587c04d0de8a263628665ddce8bf9da8d1b811c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 00:53:39 +0100 Subject: [PATCH 05/59] test color theme --- InvenTree/common/tests.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index 897d28964a..66fa043f9f 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -7,7 +7,7 @@ from datetime import timedelta from django.test import TestCase, Client from django.contrib.auth import get_user_model -from .models import InvenTreeSetting, WebhookEndpoint, WebhookMessage, NotificationEntry +from .models import InvenTreeSetting, WebhookEndpoint, WebhookMessage, NotificationEntry, ColorTheme from .api import WebhookView CONTENT_TYPE_JSON = 'application/json' @@ -299,3 +299,26 @@ class LoadingTest(TestCase): # now it should be false again self.assertFalse(common.models.InvenTreeSetting.get_setting('SERVER_RESTART_REQUIRED')) + + +class ColorThemeTest(TestCase): + """ + Tests for ColorTheme + """ + + def test_choices(self): + result = ColorTheme.get_color_themes_choices() + + self.assertIn(('default', 'Default'), result) + + def test_valid_choice(self): + # check wrong reference + self.assertFalse(ColorTheme.is_valid_choice('abcdd')) + + # create themes + aa = ColorTheme.objects.create(user='aa', name='testname') + ab = ColorTheme.objects.create(user='ab', name='darker') + + # check valid theme + self.assertFalse(ColorTheme.is_valid_choice(aa)) + self.assertTrue(ColorTheme.is_valid_choice(ab)) From 92398d7b398b31447cb36a7fd14ec89edc2bdc2a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 00:54:08 +0100 Subject: [PATCH 06/59] ignore in coverage --- InvenTree/common/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/common/settings.py b/InvenTree/common/settings.py index 80f80b886f..6973f9ecd3 100644 --- a/InvenTree/common/settings.py +++ b/InvenTree/common/settings.py @@ -18,12 +18,12 @@ def currency_code_default(): try: code = InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY') - except ProgrammingError: + except ProgrammingError: # pragma: no cover # database is not initialized yet code = '' if code not in CURRENCIES: - code = 'USD' + code = 'USD' # pragma: no cover return code From 4a0f710fecc100d48453446148666d898abd936b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 00:57:37 +0100 Subject: [PATCH 07/59] PEP fix --- InvenTree/common/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index 66fa043f9f..d2abceadb7 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -314,7 +314,7 @@ class ColorThemeTest(TestCase): def test_valid_choice(self): # check wrong reference self.assertFalse(ColorTheme.is_valid_choice('abcdd')) - + # create themes aa = ColorTheme.objects.create(user='aa', name='testname') ab = ColorTheme.objects.create(user='ab', name='darker') From 91b4e7167d313c9b8f364a5ad9cdeef6fc5e384f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 01:32:18 +0100 Subject: [PATCH 08/59] test that the task is running --- InvenTree/common/test_tasks.py | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 InvenTree/common/test_tasks.py diff --git a/InvenTree/common/test_tasks.py b/InvenTree/common/test_tasks.py new file mode 100644 index 0000000000..839362a466 --- /dev/null +++ b/InvenTree/common/test_tasks.py @@ -0,0 +1,35 @@ + +# -*- coding: utf-8 -*- +from django.test import TestCase +from datetime import timedelta, datetime + +from common.models import NotificationEntry +from InvenTree.tasks import offload_task + + +class TaskTest(TestCase): + """ + Tests for common tasks + """ + + def test_delete(self): + + self.assertEqual(NotificationEntry.objects.all().count(), 0) + # check empty run + offload_task('common.tasks.delete_old_notifications',) + + # add a new tasks + strat_data = datetime.now() + NotificationEntry.objects.create(key='part.notify_low_stock', uid=1) + late = NotificationEntry.objects.create(key='part.notify_low_stock', uid=2) + late.updated = (strat_data - timedelta(days=90)) + late.save() + later = NotificationEntry.objects.create(key='part.notify_low_stock', uid=3) + later.updated = (strat_data - timedelta(days=95)) + later.save() + + self.assertEqual(NotificationEntry.objects.all().count(), 3) + + # run again and check that old notifications were deleted + offload_task('common.tasks.delete_old_notifications',) + self.assertEqual(NotificationEntry.objects.all().count(), 1) From d2e8b2cc2b5633ed6eebc1fcb48f44aa48a20382 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 01:38:08 +0100 Subject: [PATCH 09/59] run just empty task - auto_now is a hard to test --- InvenTree/common/test_tasks.py | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/InvenTree/common/test_tasks.py b/InvenTree/common/test_tasks.py index 839362a466..4a3da9c028 100644 --- a/InvenTree/common/test_tasks.py +++ b/InvenTree/common/test_tasks.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from django.test import TestCase -from datetime import timedelta, datetime from common.models import NotificationEntry from InvenTree.tasks import offload_task @@ -13,23 +12,7 @@ class TaskTest(TestCase): """ def test_delete(self): - - self.assertEqual(NotificationEntry.objects.all().count(), 0) + # check empty run + self.assertEqual(NotificationEntry.objects.all().count(), 0) offload_task('common.tasks.delete_old_notifications',) - - # add a new tasks - strat_data = datetime.now() - NotificationEntry.objects.create(key='part.notify_low_stock', uid=1) - late = NotificationEntry.objects.create(key='part.notify_low_stock', uid=2) - late.updated = (strat_data - timedelta(days=90)) - late.save() - later = NotificationEntry.objects.create(key='part.notify_low_stock', uid=3) - later.updated = (strat_data - timedelta(days=95)) - later.save() - - self.assertEqual(NotificationEntry.objects.all().count(), 3) - - # run again and check that old notifications were deleted - offload_task('common.tasks.delete_old_notifications',) - self.assertEqual(NotificationEntry.objects.all().count(), 1) From 1e164fea5be8b83c46aefe89d2b0f74832131a77 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 01:45:34 +0100 Subject: [PATCH 10/59] Add API test --- InvenTree/common/tests.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index d2abceadb7..1a8473a95e 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -6,7 +6,9 @@ from datetime import timedelta from django.test import TestCase, Client from django.contrib.auth import get_user_model +from django.urls import reverse +from InvenTree.api_tester import InvenTreeAPITestCase from .models import InvenTreeSetting, WebhookEndpoint, WebhookMessage, NotificationEntry, ColorTheme from .api import WebhookView @@ -146,6 +148,14 @@ class SettingsTest(TestCase): raise ValueError(f'Non-boolean default value specified for {key}') # pragma: no cover +class SettingsApiTest(InvenTreeAPITestCase): + + def test_settings_api(self): + # test setting with choice + url = reverse('api-user-setting-list') + self.get(url, expected_code=200) + + class WebhookMessageTests(TestCase): def setUp(self): self.endpoint_def = WebhookEndpoint.objects.create() From 92279b2f8f707f51fa5312b2210c3f95105c1a0c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 01:50:05 +0100 Subject: [PATCH 11/59] remove theme test --- InvenTree/common/tests.py | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index 1a8473a95e..c4298f237e 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -9,7 +9,7 @@ from django.contrib.auth import get_user_model from django.urls import reverse from InvenTree.api_tester import InvenTreeAPITestCase -from .models import InvenTreeSetting, WebhookEndpoint, WebhookMessage, NotificationEntry, ColorTheme +from .models import InvenTreeSetting, WebhookEndpoint, WebhookMessage, NotificationEntry from .api import WebhookView CONTENT_TYPE_JSON = 'application/json' @@ -309,26 +309,3 @@ class LoadingTest(TestCase): # now it should be false again self.assertFalse(common.models.InvenTreeSetting.get_setting('SERVER_RESTART_REQUIRED')) - - -class ColorThemeTest(TestCase): - """ - Tests for ColorTheme - """ - - def test_choices(self): - result = ColorTheme.get_color_themes_choices() - - self.assertIn(('default', 'Default'), result) - - def test_valid_choice(self): - # check wrong reference - self.assertFalse(ColorTheme.is_valid_choice('abcdd')) - - # create themes - aa = ColorTheme.objects.create(user='aa', name='testname') - ab = ColorTheme.objects.create(user='ab', name='darker') - - # check valid theme - self.assertFalse(ColorTheme.is_valid_choice(aa)) - self.assertTrue(ColorTheme.is_valid_choice(ab)) From 3a2b52ae53b5ca9c258a199275330677e4fb061e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 02:18:21 +0100 Subject: [PATCH 12/59] barcode add error coverage --- InvenTree/barcodes/tests.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/InvenTree/barcodes/tests.py b/InvenTree/barcodes/tests.py index c9a063e8f0..33b171d666 100644 --- a/InvenTree/barcodes/tests.py +++ b/InvenTree/barcodes/tests.py @@ -38,8 +38,18 @@ class BarcodeAPITest(APITestCase): def test_invalid(self): + # test scan url response = self.client.post(self.scan_url, format='json', data={}) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + # test wrong assign urls + response = self.client.post(self.assign_url, format='json', data={}) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + response = self.client.post(self.assign_url, format='json', data={'barcode': '123'}) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + response = self.client.post(self.assign_url, format='json', data={'barcode': '123', 'stockitem': '123'}) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_empty(self): From a4737a1a2f04fbb8ba4cce55b6e512fd3c8166bb Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 03:09:37 +0100 Subject: [PATCH 13/59] barcode / InvenTreePugin test errors --- InvenTree/barcodes/tests.py | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/InvenTree/barcodes/tests.py b/InvenTree/barcodes/tests.py index 33b171d666..fd46438c9b 100644 --- a/InvenTree/barcodes/tests.py +++ b/InvenTree/barcodes/tests.py @@ -214,3 +214,48 @@ class BarcodeAPITest(APITestCase): self.assertIn('error', data) self.assertNotIn('success', data) + + +class TestInvenTreeBarcode(APITestCase): + + fixtures = [ + 'category', + 'part', + 'location', + 'stock' + ] + + def setUp(self): + # Create a user for auth + user = get_user_model() + user.objects.create_user('testuser', 'test@testing.com', 'password') + + self.client.login(username='testuser', password='password') + + def test_errors(self): + """ + Test that a barcode can be associated with a StockItem + """ + + def test_assert_error(barcode_data): + response = self.client.post( + reverse('api-barcode-link'), format='json', + data={ + 'barcode': barcode_data, + 'stockitem': 522 + } + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('error', response.data) + + # test with already existing stock + test_assert_error('{"stockitem": 522}') + + # test with already existing stock location + test_assert_error('{"stocklocation": 7}') + + # test with already existing part location + test_assert_error('{"part": 10004}') + + # test with hash + test_assert_error('{"blbla": 10004}') From d8c20894cedddbc454d8f040c6c051185f75953f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 03:10:24 +0100 Subject: [PATCH 14/59] add uid to stockitem --- InvenTree/stock/fixtures/stock.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/InvenTree/stock/fixtures/stock.yaml b/InvenTree/stock/fixtures/stock.yaml index 23012c3cd3..a16e83db41 100644 --- a/InvenTree/stock/fixtures/stock.yaml +++ b/InvenTree/stock/fixtures/stock.yaml @@ -248,4 +248,5 @@ lft: 0 rght: 0 expiry_date: "1990-10-10" - status: 70 \ No newline at end of file + status: 70 + uid: 1be0dfa925825c5c6c79301449e50c2d From e8ec0dc46bdf96749c34685a831b6715e9e3500b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 03:19:00 +0100 Subject: [PATCH 15/59] add test for as_choice --- InvenTree/common/tests.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index c4298f237e..d7dfcb631f 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -73,6 +73,10 @@ class SettingsTest(TestCase): self.assertEqual(instance_obj.get_setting_units(instance_ref), '') self.assertEqual(instance_obj.get_setting_units(stale_ref), 'days') + # check as_choice + self.assertEqual(instance_obj.as_choice(), 'My very first InvenTree Instance') + self.assertEqual(report_size_obj.as_choice(), 'A4') + # check is_choice self.assertEqual(instance_obj.is_choice(), False) self.assertEqual(report_size_obj.is_choice(), True) From 76fd529374651bb2d1b7d1c46c6de062194b0db9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 03:20:14 +0100 Subject: [PATCH 16/59] add test for as_bool --- InvenTree/common/tests.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index d7dfcb631f..a226e57a5d 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -89,6 +89,9 @@ class SettingsTest(TestCase): # check as_int self.assertEqual(stale_days.as_int(), 0) + # check as_bool + self.assertEqual(report_test_obj.as_bool(), True) + # check to_native_value self.assertEqual(stale_days.to_native_value(), 0) From b88a6ccdb578addbed5c1508d50fcb42bd1df284 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 03:35:20 +0100 Subject: [PATCH 17/59] test default value with as_int and fix it --- InvenTree/common/models.py | 2 +- InvenTree/common/tests.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index 2b84918e82..22afb80664 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -570,7 +570,7 @@ class BaseInvenTreeSetting(models.Model): try: value = int(self.value) except (ValueError, TypeError): - value = self.default_value() + value = self.default_value return value diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index a226e57a5d..77a4f1a12d 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -88,6 +88,7 @@ class SettingsTest(TestCase): # check as_int self.assertEqual(stale_days.as_int(), 0) + self.assertEqual(instance_obj.as_int(), 'InvenTree server') # not an int -> return default # check as_bool self.assertEqual(report_test_obj.as_bool(), True) From d8fc56cf6141323babdb1a6a3e26deac93aa431d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 03:40:27 +0100 Subject: [PATCH 18/59] ignore m2m for coverage --- InvenTree/users/admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/InvenTree/users/admin.py b/InvenTree/users/admin.py index 91fed49830..ba7638612a 100644 --- a/InvenTree/users/admin.py +++ b/InvenTree/users/admin.py @@ -49,7 +49,7 @@ class InvenTreeGroupAdminForm(forms.ModelForm): 'users', ] - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs): # pragma: no cover super().__init__(*args, **kwargs) if self.instance.pk: @@ -65,12 +65,12 @@ class InvenTreeGroupAdminForm(forms.ModelForm): help_text=_('Select which users are assigned to this group') ) - def save_m2m(self): + def save_m2m(self): # pragma: no cover # Add the users to the Group. self.instance.user_set.set(self.cleaned_data['users']) - def save(self, *args, **kwargs): + def save(self, *args, **kwargs): # pragma: no cover # Default save instance = super().save() # Save many-to-many data From 56ebb0fd0e9e2ff7dfc998d1fe4cc0106a2cc6cf Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 03:40:50 +0100 Subject: [PATCH 19/59] ignore custom admin form --- InvenTree/users/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/users/admin.py b/InvenTree/users/admin.py index ba7638612a..39a7b1c9ee 100644 --- a/InvenTree/users/admin.py +++ b/InvenTree/users/admin.py @@ -78,7 +78,7 @@ class InvenTreeGroupAdminForm(forms.ModelForm): return instance -class RoleGroupAdmin(admin.ModelAdmin): +class RoleGroupAdmin(admin.ModelAdmin): # pragma: no cover """ Custom admin interface for the Group model """ From 4eaa655a7c0e6775cb60801c79f323dc5aaedd50 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 03:43:04 +0100 Subject: [PATCH 20/59] ignore for coverage --- InvenTree/users/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index c593fb49f3..fc22d036a3 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -265,9 +265,9 @@ class RuleSet(models.Model): model=model ) - def __str__(self, debug=False): + def __str__(self, debug=False): # pragma: no cover """ Ruleset string representation """ - if debug: # pragma: no cover + if debug: # Makes debugging easier return f'{str(self.group).ljust(15)}: {self.name.title().ljust(15)} | ' \ f'v: {str(self.can_view).ljust(5)} | a: {str(self.can_add).ljust(5)} | ' \ @@ -574,7 +574,7 @@ class Owner(models.Model): return owners @staticmethod - def get_api_url(): + def get_api_url(): # pragma: no cover return reverse('api-owner-list') class Meta: From a68ee5ce0b1881ac7a2b831df7808d3f0b311635 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 03:52:50 +0100 Subject: [PATCH 21/59] fix API assertations --- InvenTree/barcodes/tests.py | 4 ++-- InvenTree/stock/fixtures/stock.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/InvenTree/barcodes/tests.py b/InvenTree/barcodes/tests.py index fd46438c9b..852f429141 100644 --- a/InvenTree/barcodes/tests.py +++ b/InvenTree/barcodes/tests.py @@ -242,14 +242,14 @@ class TestInvenTreeBarcode(APITestCase): reverse('api-barcode-link'), format='json', data={ 'barcode': barcode_data, - 'stockitem': 522 + 'stockitem': 521 } ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn('error', response.data) # test with already existing stock - test_assert_error('{"stockitem": 522}') + test_assert_error('{"stockitem": 521}') # test with already existing stock location test_assert_error('{"stocklocation": 7}') diff --git a/InvenTree/stock/fixtures/stock.yaml b/InvenTree/stock/fixtures/stock.yaml index a16e83db41..f1926be478 100644 --- a/InvenTree/stock/fixtures/stock.yaml +++ b/InvenTree/stock/fixtures/stock.yaml @@ -235,6 +235,7 @@ lft: 0 rght: 0 status: 60 + uid: 1be0dfa925825c5c6c79301449e50c2d - model: stock.stockitem pk: 522 @@ -249,4 +250,3 @@ rght: 0 expiry_date: "1990-10-10" status: 70 - uid: 1be0dfa925825c5c6c79301449e50c2d From 925ae73eaa2199bb42bba28562e29dbccdf50c15 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 19:16:03 +0100 Subject: [PATCH 22/59] fix docstring --- InvenTree/barcodes/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/barcodes/tests.py b/InvenTree/barcodes/tests.py index 852f429141..4df62fe4f8 100644 --- a/InvenTree/barcodes/tests.py +++ b/InvenTree/barcodes/tests.py @@ -234,7 +234,7 @@ class TestInvenTreeBarcode(APITestCase): def test_errors(self): """ - Test that a barcode can be associated with a StockItem + Test all possible error cases for assigment action """ def test_assert_error(barcode_data): From bd2b279390a4682d479546555890391f87655134 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 19:38:57 +0100 Subject: [PATCH 23/59] add coverage to barcode api --- InvenTree/barcodes/tests.py | 9 +++++++++ InvenTree/stock/fixtures/stock.yaml | 1 + 2 files changed, 10 insertions(+) diff --git a/InvenTree/barcodes/tests.py b/InvenTree/barcodes/tests.py index 4df62fe4f8..1f43d4aca3 100644 --- a/InvenTree/barcodes/tests.py +++ b/InvenTree/barcodes/tests.py @@ -259,3 +259,12 @@ class TestInvenTreeBarcode(APITestCase): # test with hash test_assert_error('{"blbla": 10004}') + + def test_scan(self): + """ + Test that a barcode can be scanned + """ + + response = self.client.post(reverse('api-barcode-scan'), format='json', data={'barcode': 'blbla=10004',}) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('success', response.data) diff --git a/InvenTree/stock/fixtures/stock.yaml b/InvenTree/stock/fixtures/stock.yaml index f1926be478..0f44828d8e 100644 --- a/InvenTree/stock/fixtures/stock.yaml +++ b/InvenTree/stock/fixtures/stock.yaml @@ -222,6 +222,7 @@ lft: 0 rght: 0 expiry_date: "1990-10-10" + uid: 9e5ae7fc20568ed4814c10967bba8b65 - model: stock.stockitem pk: 521 From beb170c02da91459df92dc9c22d8265b1d8c8408 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 19:42:06 +0100 Subject: [PATCH 24/59] ignore wrong lookup values for coverage --- .../barcodes/plugins/inventree_barcode.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/InvenTree/barcodes/plugins/inventree_barcode.py b/InvenTree/barcodes/plugins/inventree_barcode.py index 1b451f0286..5df71cb776 100644 --- a/InvenTree/barcodes/plugins/inventree_barcode.py +++ b/InvenTree/barcodes/plugins/inventree_barcode.py @@ -47,7 +47,7 @@ class InvenTreeBarcodePlugin(BarcodePlugin): except json.JSONDecodeError: return False else: - return False + return False # pragma: no cover # If any of the following keys are in the JSON data, # let's go ahead and assume that the code is a valid InvenTree one... @@ -70,10 +70,10 @@ class InvenTreeBarcodePlugin(BarcodePlugin): # Initially try casting to an integer try: pk = int(data) - except (TypeError, ValueError): + except (TypeError, ValueError): # pragma: no cover pk = None - if pk is None: + if pk is None: # pragma: no cover try: pk = self.data[k]['id'] except (AttributeError, KeyError): @@ -82,7 +82,7 @@ class InvenTreeBarcodePlugin(BarcodePlugin): try: item = StockItem.objects.get(pk=pk) return item - except (ValueError, StockItem.DoesNotExist): + except (ValueError, StockItem.DoesNotExist): # pragma: no cover raise ValidationError({k, "Stock item does not exist"}) return None @@ -97,10 +97,10 @@ class InvenTreeBarcodePlugin(BarcodePlugin): # First try simple integer lookup try: pk = int(self.data[k]) - except (TypeError, ValueError): + except (TypeError, ValueError): # pragma: no cover pk = None - if pk is None: + if pk is None: # pragma: no cover # Lookup by 'id' field try: pk = self.data[k]['id'] @@ -110,7 +110,7 @@ class InvenTreeBarcodePlugin(BarcodePlugin): try: loc = StockLocation.objects.get(pk=pk) return loc - except (ValueError, StockLocation.DoesNotExist): + except (ValueError, StockLocation.DoesNotExist): # pragma: no cover raise ValidationError({k, "Stock location does not exist"}) return None @@ -125,10 +125,10 @@ class InvenTreeBarcodePlugin(BarcodePlugin): # Try integer lookup first try: pk = int(self.data[k]) - except (TypeError, ValueError): + except (TypeError, ValueError): # pragma: no cover pk = None - if pk is None: + if pk is None: # pragma: no cover try: pk = self.data[k]['id'] except (AttributeError, KeyError): @@ -137,7 +137,7 @@ class InvenTreeBarcodePlugin(BarcodePlugin): try: part = Part.objects.get(pk=pk) return part - except (ValueError, Part.DoesNotExist): + except (ValueError, Part.DoesNotExist): # pragma: no cover raise ValidationError({k, 'Part does not exist'}) return None From 59a1d12534b3517a0588286b4df2609d196e10fc Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 19:44:43 +0100 Subject: [PATCH 25/59] PEP fix --- InvenTree/barcodes/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/barcodes/tests.py b/InvenTree/barcodes/tests.py index 1f43d4aca3..18d4e77d20 100644 --- a/InvenTree/barcodes/tests.py +++ b/InvenTree/barcodes/tests.py @@ -265,6 +265,6 @@ class TestInvenTreeBarcode(APITestCase): Test that a barcode can be scanned """ - response = self.client.post(reverse('api-barcode-scan'), format='json', data={'barcode': 'blbla=10004',}) + response = self.client.post(reverse('api-barcode-scan'), format='json', data={'barcode': 'blbla=10004'}) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn('success', response.data) From fa865de073fda4600a2172da667b8cd8b9e2ee15 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 19:44:51 +0100 Subject: [PATCH 26/59] add omittion --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index 41975d6985..8bf8acdf75 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,6 +20,9 @@ max-complexity = 20 [coverage:run] source = ./InvenTree +omit= + InvenTree/wsgi.py + InvenTree/ci_render_js.py [coverage:report] omit= InvenTree/wsgi.py From 4c36737b9fe84f1ea384037fe4470bba1cfb9e1b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 19:46:42 +0100 Subject: [PATCH 27/59] add empty data --- InvenTree/plugins/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/InvenTree/plugins/__init__.py b/InvenTree/plugins/__init__.py index e69de29bb2..ea758ff8c5 100644 --- a/InvenTree/plugins/__init__.py +++ b/InvenTree/plugins/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals # pragma: no cover From e68cc252947417ae105d321b12797961af1c7a9b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 19:48:04 +0100 Subject: [PATCH 28/59] ignore programming error in coverage --- InvenTree/users/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index fc22d036a3..93bed1541d 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -373,7 +373,7 @@ def update_group_roles(group, debug=False): allowed - Whether or not the action is allowed """ - if action not in ['view', 'add', 'change', 'delete']: + if action not in ['view', 'add', 'change', 'delete']: # pragma: no cover raise ValueError("Action {a} is invalid".format(a=action)) permission_string = RuleSet.get_model_permission_string(model, action) From 1e4727d7338aa3d60002e40295452bd462cb27c4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 19:48:49 +0100 Subject: [PATCH 29/59] ignore precaution for plugin programmers srewing up in coverage --- InvenTree/users/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index 93bed1541d..92bf73b57c 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -317,7 +317,7 @@ def split_permission(app, perm): """split permission string into permission and model""" permission_name, *model = perm.split('_') # handle models that have underscores - if len(model) > 1: + if len(model) > 1: # pragma: no cover app += '_' + '_'.join(model[:-1]) perm = permission_name + '_' + model[-1:][0] model = model[-1:][0] From af95779ed06ac94eca2065ee453664195decd40f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 19:49:58 +0100 Subject: [PATCH 30/59] superusers are not tested in CI --- InvenTree/InvenTree/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/context.py b/InvenTree/InvenTree/context.py index 94665f9e07..7d65cb4231 100644 --- a/InvenTree/InvenTree/context.py +++ b/InvenTree/InvenTree/context.py @@ -95,7 +95,7 @@ def user_roles(request): } if user.is_superuser: - for ruleset in RuleSet.RULESET_MODELS.keys(): + for ruleset in RuleSet.RULESET_MODELS.keys(): # pragma: no cover roles[ruleset] = { 'view': True, 'add': True, From 1c7f37e52a6962aa9578794c2d186dee94c2d857 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:06:43 +0100 Subject: [PATCH 31/59] add test for cfg file helper --- InvenTree/InvenTree/tests.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index f89a8b073d..fa9f0552b9 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -17,6 +17,7 @@ from . import helpers from . import version from . import status from . import ready +from .config import get_config_file from decimal import Decimal @@ -453,3 +454,12 @@ class TestSettings(TestCase): # make sure to clean up settings.TESTING_ENV = False + + def test_helpers_cfg_file(self): + # normal run - not configured + self.assertIn('InvenTree/InvenTree/config.yaml', get_config_file()) + + # with env set + with self.env: + self.env.set('INVENTREE_CONFIG_FILE', 'my_special_conf.yaml') + self.assertIn('InvenTree/InvenTree/my_special_conf.yaml', get_config_file()) From 646107fee0ca6d5652ce469880f66f930da70a8d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:09:54 +0100 Subject: [PATCH 32/59] omit helpers from coverage --- InvenTree/InvenTree/ci_render_js.py | 10 +++++----- InvenTree/InvenTree/wsgi.py | 8 ++++---- setup.cfg | 7 ------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/InvenTree/InvenTree/ci_render_js.py b/InvenTree/InvenTree/ci_render_js.py index e747f1a3c0..94530db096 100644 --- a/InvenTree/InvenTree/ci_render_js.py +++ b/InvenTree/InvenTree/ci_render_js.py @@ -3,14 +3,14 @@ Pull rendered copies of the templated only used for testing the js files! - This file is omited from coverage """ -from django.test import TestCase -from django.contrib.auth import get_user_model +from django.test import TestCase # pragma: no cover +from django.contrib.auth import get_user_model # pragma: no cover -import os -import pathlib +import os # pragma: no cover +import pathlib # pragma: no cover -class RenderJavascriptFiles(TestCase): +class RenderJavascriptFiles(TestCase): # pragma: no cover """ A unit test to "render" javascript files. diff --git a/InvenTree/InvenTree/wsgi.py b/InvenTree/InvenTree/wsgi.py index c6bef4d663..9c4d72edbc 100644 --- a/InvenTree/InvenTree/wsgi.py +++ b/InvenTree/InvenTree/wsgi.py @@ -7,10 +7,10 @@ For more information on this file, see https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ """ -import os +import os # pragma: no cover -from django.core.wsgi import get_wsgi_application +from django.core.wsgi import get_wsgi_application # pragma: no cover -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "InvenTree.settings") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "InvenTree.settings") # pragma: no cover -application = get_wsgi_application() +application = get_wsgi_application() # pragma: no cover diff --git a/setup.cfg b/setup.cfg index 8bf8acdf75..b4b0af8836 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,10 +20,3 @@ max-complexity = 20 [coverage:run] source = ./InvenTree -omit= - InvenTree/wsgi.py - InvenTree/ci_render_js.py -[coverage:report] -omit= - InvenTree/wsgi.py - InvenTree/ci_render_js.py From 2c22ce9f0d66d88e9b2b7891be17329510e6e863 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:18:00 +0100 Subject: [PATCH 33/59] test plugin file helper --- InvenTree/InvenTree/tests.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index fa9f0552b9..277128cd2e 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -17,7 +17,7 @@ from . import helpers from . import version from . import status from . import ready -from .config import get_config_file +from .config import get_config_file, get_plugin_file from decimal import Decimal @@ -463,3 +463,14 @@ class TestSettings(TestCase): with self.env: self.env.set('INVENTREE_CONFIG_FILE', 'my_special_conf.yaml') self.assertIn('InvenTree/InvenTree/my_special_conf.yaml', get_config_file()) + + def test_helpers_plugin_file(self): + # normal run - not configured + self.assertIn('InvenTree/InvenTree/plugins.txt', get_plugin_file()) + + # with env set + with self.env: + self.env.set('INVENTREE_PLUGIN_FILE', 'my_special_plugins.txt') + self.assertIn('my_special_plugins.txt', get_plugin_file()) + + self.assertIn('InvenTree/InvenTree/plugins.txt', get_plugin_file()) From 2286e203ca9a4b44c29bc99ffc89cc2e68e85436 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:23:18 +0100 Subject: [PATCH 34/59] test get_settings helper --- InvenTree/InvenTree/tests.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index 277128cd2e..fa6f93562e 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -17,7 +17,7 @@ from . import helpers from . import version from . import status from . import ready -from .config import get_config_file, get_plugin_file +from .config import get_config_file, get_plugin_file, get_setting from decimal import Decimal @@ -473,4 +473,12 @@ class TestSettings(TestCase): self.env.set('INVENTREE_PLUGIN_FILE', 'my_special_plugins.txt') self.assertIn('my_special_plugins.txt', get_plugin_file()) - self.assertIn('InvenTree/InvenTree/plugins.txt', get_plugin_file()) + def test_helpers_setting(self): + TEST_ENV_NAME = '123TEST' + # check that default gets returned if not present + self.assertEqual(get_setting(TEST_ENV_NAME, None, '123!'), '123!') + + # with env set + with self.env: + self.env.set(TEST_ENV_NAME, '321') + self.assertEqual(get_setting(TEST_ENV_NAME, None), '321') From 44fb356a3be3541e28b7076070dfabef8675c3be Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:24:44 +0100 Subject: [PATCH 35/59] ignore parts that can not be reached on a dev version --- InvenTree/InvenTree/version.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py index 19373e930c..160e348dbb 100644 --- a/InvenTree/InvenTree/version.py +++ b/InvenTree/InvenTree/version.py @@ -171,7 +171,7 @@ def inventreeDocsVersion(): if isInvenTreeDevelopmentVersion(): return "latest" else: - return INVENTREE_SW_VERSION + return INVENTREE_SW_VERSION # pragma: no cover def isInvenTreeUpToDate(): @@ -189,10 +189,10 @@ def isInvenTreeUpToDate(): return True # Extract "tuple" version (Python can directly compare version tuples) - latest_version = inventreeVersionTuple(latest) - inventree_version = inventreeVersionTuple() + latest_version = inventreeVersionTuple(latest) # pragma: no cover + inventree_version = inventreeVersionTuple() # pragma: no cover - return inventree_version >= latest_version + return inventree_version >= latest_version # pragma: no cover def inventreeApiVersion(): @@ -209,7 +209,7 @@ def inventreeCommitHash(): try: return str(subprocess.check_output('git rev-parse --short HEAD'.split()), 'utf-8').strip() - except: + except: # pragma: no cover return None @@ -219,5 +219,5 @@ def inventreeCommitDate(): try: d = str(subprocess.check_output('git show -s --format=%ci'.split()), 'utf-8').strip() return d.split(' ')[0] - except: + except: # pragma: no cover return None From 00637f4b6cb00ab62bd92d69dfa208ee27fe548b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:38:02 +0100 Subject: [PATCH 36/59] test instance name --- InvenTree/InvenTree/tests.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index fa6f93562e..076c485a03 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -1,4 +1,5 @@ +import imp import json from test.support import EnvironmentVarGuard @@ -25,6 +26,7 @@ import InvenTree.tasks from stock.models import StockLocation from common.settings import currency_codes +from common.models import InvenTreeSetting class ValidatorTest(TestCase): @@ -482,3 +484,26 @@ class TestSettings(TestCase): with self.env: self.env.set(TEST_ENV_NAME, '321') self.assertEqual(get_setting(TEST_ENV_NAME, None), '321') + +class TestInstanceName(TestCase): + """ + Unit tests for instance name + """ + + def setUp(self): + # Create a user for auth + user = get_user_model() + self.user = user.objects.create_superuser('testuser', 'test@testing.com', 'password') + + self.client.login(username='testuser', password='password') + + def test_instance_name(self): + + # default setting + self.assertEqual(version.inventreeInstanceTitle(), 'InvenTree') + + # set up required setting + InvenTreeSetting.set_setting("INVENTREE_INSTANCE_TITLE", True, self.user) + InvenTreeSetting.set_setting("INVENTREE_INSTANCE", "Testing title", self.user) + + self.assertEqual(version.inventreeInstanceTitle(), 'Testing title') From 5a2bfaaf190e0ecf4058f362eba517870f20522e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:39:20 +0100 Subject: [PATCH 37/59] refactor to keep namespace cleaner --- InvenTree/InvenTree/tests.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index 076c485a03..e9a008d9f8 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -18,7 +18,7 @@ from . import helpers from . import version from . import status from . import ready -from .config import get_config_file, get_plugin_file, get_setting +from . import config from decimal import Decimal @@ -459,31 +459,31 @@ class TestSettings(TestCase): def test_helpers_cfg_file(self): # normal run - not configured - self.assertIn('InvenTree/InvenTree/config.yaml', get_config_file()) + self.assertIn('InvenTree/InvenTree/config.yaml', config.get_config_file()) # with env set with self.env: self.env.set('INVENTREE_CONFIG_FILE', 'my_special_conf.yaml') - self.assertIn('InvenTree/InvenTree/my_special_conf.yaml', get_config_file()) + self.assertIn('InvenTree/InvenTree/my_special_conf.yaml', config.get_config_file()) def test_helpers_plugin_file(self): # normal run - not configured - self.assertIn('InvenTree/InvenTree/plugins.txt', get_plugin_file()) + self.assertIn('InvenTree/InvenTree/plugins.txt', config.get_plugin_file()) # with env set with self.env: self.env.set('INVENTREE_PLUGIN_FILE', 'my_special_plugins.txt') - self.assertIn('my_special_plugins.txt', get_plugin_file()) + self.assertIn('my_special_plugins.txt', config.get_plugin_file()) def test_helpers_setting(self): TEST_ENV_NAME = '123TEST' # check that default gets returned if not present - self.assertEqual(get_setting(TEST_ENV_NAME, None, '123!'), '123!') + self.assertEqual(config.get_setting(TEST_ENV_NAME, None, '123!'), '123!') # with env set with self.env: self.env.set(TEST_ENV_NAME, '321') - self.assertEqual(get_setting(TEST_ENV_NAME, None), '321') + self.assertEqual(config.get_setting(TEST_ENV_NAME, None), '321') class TestInstanceName(TestCase): """ From a28b1e97cc9ebb76bb575bf66e8eb06158d5ba28 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:44:25 +0100 Subject: [PATCH 38/59] worker will not run in debug mode --- InvenTree/InvenTree/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index 9d4039d4b1..578a303566 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -68,7 +68,7 @@ def offload_task(taskname, *args, force_sync=False, **kwargs): import importlib from InvenTree.status import is_worker_running - if is_worker_running() and not force_sync: + if is_worker_running() and not force_sync: # pragma: no cover # Running as asynchronous task try: task = AsyncTask(taskname, *args, **kwargs) From 1b9e9801444527ef833a8286acf7a284c30db690 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:44:55 +0100 Subject: [PATCH 39/59] evals error out on gh action --- InvenTree/InvenTree/tasks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index 578a303566..d089ae7157 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -94,13 +94,13 @@ def offload_task(taskname, *args, force_sync=False, **kwargs): # Retrieve function try: _func = getattr(_mod, func) - except AttributeError: + except AttributeError: # pragma: no cover # getattr does not work for local import _func = None try: if not _func: - _func = eval(func) + _func = eval(func) # pragma: no cover except NameError: logger.warning(f"WARNING: '{taskname}' not started - No function named '{func}'") return From df74bdfacd693ddd55927ca4f45c8431e9b2183c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:45:40 +0100 Subject: [PATCH 40/59] ignore unkown errors for coverage --- InvenTree/InvenTree/tasks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index d089ae7157..c156d421ab 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -248,7 +248,7 @@ def update_exchange_rates(): # Apps not yet loaded! logger.info("Could not perform 'update_exchange_rates' - App registry not ready") return - except: + except: # pragma: no cover # Other error? return @@ -257,7 +257,7 @@ def update_exchange_rates(): backend = ExchangeBackend.objects.get(name='InvenTreeExchange') except ExchangeBackend.DoesNotExist: pass - except: + except: # pragma: no cover # Some other error logger.warning("update_exchange_rates: Database not ready") return @@ -274,7 +274,7 @@ def update_exchange_rates(): # Remove any exchange rates which are not in the provided currencies Rate.objects.filter(backend="InvenTreeExchange").exclude(currency__in=currency_codes()).delete() - except Exception as e: + except Exception as e: # pragma: no cover logger.error(f"Error updating exchange rates: {e}") From 312441c2f5b7198d004f0441e5d450ee0c8f366a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:46:44 +0100 Subject: [PATCH 41/59] PEP fix --- InvenTree/InvenTree/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index e9a008d9f8..669628bdea 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -1,5 +1,4 @@ -import imp import json from test.support import EnvironmentVarGuard @@ -485,6 +484,7 @@ class TestSettings(TestCase): self.env.set(TEST_ENV_NAME, '321') self.assertEqual(config.get_setting(TEST_ENV_NAME, None), '321') + class TestInstanceName(TestCase): """ Unit tests for instance name From e0e87efb8c28af9c709f6a3bd8b4fe275aa13b12 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:53:49 +0100 Subject: [PATCH 42/59] do not cover old import --- InvenTree/plugin/registry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index 40a4a4e2d9..a79395745b 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -21,7 +21,7 @@ from django.utils.text import slugify try: from importlib import metadata -except: +except: # pragma: no cover import importlib_metadata as metadata # TODO remove when python minimum is 3.8 From 08d2e74521fa5539e7982f6ad4bf0dc05b1b14df Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:55:11 +0100 Subject: [PATCH 43/59] do not cover disabled / debugging plugins cases --- InvenTree/plugin/registry.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index a79395745b..272c211749 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -84,7 +84,7 @@ class PluginsRegistry: """ if not settings.PLUGINS_ENABLED: # Plugins not enabled, do nothing - return + return # pragma: no cover logger.info('Start loading plugins') @@ -143,7 +143,7 @@ class PluginsRegistry: if not settings.PLUGINS_ENABLED: # Plugins not enabled, do nothing - return + return # pragma: no cover logger.info('Start unloading plugins') @@ -187,7 +187,7 @@ class PluginsRegistry: if not settings.PLUGINS_ENABLED: # Plugins not enabled, do nothing - return + return # pragma: no cover self.plugin_modules = [] # clear @@ -200,7 +200,7 @@ class PluginsRegistry: # Check if not running in testing mode and apps should be loaded from hooks if (not settings.PLUGIN_TESTING) or (settings.PLUGIN_TESTING and settings.PLUGIN_TESTING_SETUP): # Collect plugins from setup entry points - for entry in metadata.entry_points().get('inventree_plugins', []): + for entry in metadata.entry_points().get('inventree_plugins', []): # pragma: no cover try: plugin = entry.load() plugin.is_package = True @@ -257,7 +257,7 @@ class PluginsRegistry: except (OperationalError, ProgrammingError) as error: # Exception if the database has not been migrated yet - check if test are running - raise if not if not settings.PLUGIN_TESTING: - raise error + raise error # pragma: no cover plugin_db_setting = None # Always activate if testing @@ -267,7 +267,7 @@ class PluginsRegistry: # option1: package, option2: file-based if (plugin.__name__ == disabled) or (plugin.__module__ == disabled): # Errors are bad so disable the plugin in the database - if not settings.PLUGIN_TESTING: + if not settings.PLUGIN_TESTING: # pragma: no cover plugin_db_setting.active = False # TODO save the error to the plugin plugin_db_setting.save(no_reload=True) From f2aeab09ec0515948b4c3f7c9e9697fe9a7d4aba Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:55:25 +0100 Subject: [PATCH 44/59] should never be reached --- InvenTree/plugin/registry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index 272c211749..68762c8ade 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -120,7 +120,7 @@ class PluginsRegistry: # We do not want to end in an endless loop retry_counter -= 1 - if retry_counter <= 0: + if retry_counter <= 0: # pragma: no cover if settings.PLUGIN_TESTING: print('[PLUGIN] Max retries, breaking loading') # TODO error for server status From aee8ca1c78905943fc40a9f32bbf7258f8de1399 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:56:16 +0100 Subject: [PATCH 45/59] do not cover safety functions for wrongly conf swarms --- InvenTree/plugin/registry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index 68762c8ade..0d38930314 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -150,7 +150,7 @@ class PluginsRegistry: # Set maintanace mode _maintenance = bool(get_maintenance_mode()) if not _maintenance: - set_maintenance_mode(True) + set_maintenance_mode(True) # pragma: no cover # remove all plugins from registry self._clean_registry() @@ -160,7 +160,7 @@ class PluginsRegistry: # remove maintenance if not _maintenance: - set_maintenance_mode(False) + set_maintenance_mode(False) # pragma: no cover logger.info('Finished unloading plugins') def reload_plugins(self): From a3d0dcb5bb20ea564d7248b4a647bc890c9b7700 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:56:36 +0100 Subject: [PATCH 46/59] only applicable in mulithread enviroments --- InvenTree/plugin/registry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index 0d38930314..a3e9d6e42c 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -170,7 +170,7 @@ class PluginsRegistry: # Do not reload whe currently loading if self.is_loading: - return + return # pragma: no cover logger.info('Start reloading plugins') From b1a3445338f60896a925150019253252dc021145 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:57:25 +0100 Subject: [PATCH 47/59] ignore coverage for points that only gets reached by deadly broken plugins --- InvenTree/plugin/registry.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index a3e9d6e42c..928f238f58 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -445,7 +445,7 @@ class PluginsRegistry: try: app_name = plugin_path.split('.')[-1] app_config = apps.get_app_config(app_name) - except LookupError: + except LookupError: # pragma: no cover # the plugin was never loaded correctly logger.debug(f'{app_name} App was not found during deregistering') break @@ -499,7 +499,7 @@ class PluginsRegistry: # remove model from admin site admin.site.unregister(model) models += [model._meta.model_name] - except LookupError: + except LookupError: # pragma: no cover # if an error occurs the app was never loaded right -> so nothing to do anymore logger.debug(f'{app_name} App was not found during deregistering') break @@ -572,7 +572,7 @@ class PluginsRegistry: try: cmd(*args, **kwargs) return True, [] - except Exception as error: + except Exception as error: # pragma: no cover handle_error(error) # endregion From 080ee2ea50a86480eb94f152b90d291b454ef0d4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 20:58:40 +0100 Subject: [PATCH 48/59] remove dead code --- InvenTree/plugin/plugin.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/InvenTree/plugin/plugin.py b/InvenTree/plugin/plugin.py index 9abbcc041e..dce0a1b14b 100644 --- a/InvenTree/plugin/plugin.py +++ b/InvenTree/plugin/plugin.py @@ -53,7 +53,7 @@ class InvenTreePluginBase(): else: return self.plugin_name() - def plugin_config(self, raise_error=False): + def plugin_config(self): """ Return the PluginConfig object associated with this plugin """ @@ -68,9 +68,6 @@ class InvenTreePluginBase(): except (OperationalError, ProgrammingError) as error: cfg = None - if raise_error: - raise error - return cfg def is_active(self): From 5f9f5555b69598e5ed887fbca5b975d8e7512015 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 21:01:16 +0100 Subject: [PATCH 49/59] ignore depreciated function in coverage --- InvenTree/plugin/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/plugin/plugin.py b/InvenTree/plugin/plugin.py index dce0a1b14b..bef07b308f 100644 --- a/InvenTree/plugin/plugin.py +++ b/InvenTree/plugin/plugin.py @@ -88,6 +88,6 @@ class InvenTreePlugin(InvenTreePluginBase): """ This is here for leagcy reasons and will be removed in the next major release """ - def __init__(self): + def __init__(self): # pragma: no cover warnings.warn("Using the InvenTreePlugin is depreceated", DeprecationWarning) super().__init__() From de73aa4ec55823dbea8e9bb9cd22865ed210a6f4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 21:05:56 +0100 Subject: [PATCH 50/59] can never be covered --- InvenTree/InvenTree/apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/apps.py b/InvenTree/InvenTree/apps.py index 80389bda95..50d4a649c8 100644 --- a/InvenTree/InvenTree/apps.py +++ b/InvenTree/InvenTree/apps.py @@ -28,7 +28,7 @@ class InvenTreeConfig(AppConfig): self.start_background_tasks() - if not isInTestMode(): + if not isInTestMode(): # pragma: no cover self.update_exchange_rates() if canAppAccessDatabase() or settings.TESTING_ENV: From fbe3bf739aa6634200ef2ebf160c175e58936c41 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 21:06:49 +0100 Subject: [PATCH 51/59] ignore exchange rate - using external api so not reliable --- InvenTree/InvenTree/apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/apps.py b/InvenTree/InvenTree/apps.py index 50d4a649c8..18df00030a 100644 --- a/InvenTree/InvenTree/apps.py +++ b/InvenTree/InvenTree/apps.py @@ -98,7 +98,7 @@ class InvenTreeConfig(AppConfig): schedule_type=Schedule.DAILY, ) - def update_exchange_rates(self): + def update_exchange_rates(self): # pragma: no cover """ Update exchange rates each time the server is started, *if*: From 3b642695a8e6070b067f343bcc083a19b41c52d7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 21:11:06 +0100 Subject: [PATCH 52/59] PEP fix --- InvenTree/plugin/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/plugin/plugin.py b/InvenTree/plugin/plugin.py index bef07b308f..b83297dcfd 100644 --- a/InvenTree/plugin/plugin.py +++ b/InvenTree/plugin/plugin.py @@ -65,7 +65,7 @@ class InvenTreePluginBase(): key=self.plugin_slug(), name=self.plugin_name(), ) - except (OperationalError, ProgrammingError) as error: + except (OperationalError, ProgrammingError): cfg = None return cfg From 33d9913f78f50287a0e6e3ce739343cde8028c14 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 21:11:22 +0100 Subject: [PATCH 53/59] do not cover programming error --- InvenTree/users/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/users/api.py b/InvenTree/users/api.py index f8be3067ae..0e62af4dbe 100644 --- a/InvenTree/users/api.py +++ b/InvenTree/users/api.py @@ -100,7 +100,7 @@ class RoleDetails(APIView): if len(permissions) > 0: roles[role] = permissions else: - roles[role] = None + roles[role] = None # pragma: no cover data = { 'user': user.pk, From 752b70c1f7e5ca63db6d58dca00f6aa068bc21d9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 14 Mar 2022 23:22:29 +0100 Subject: [PATCH 54/59] Add "last updated" field to supplier price break Add model Fixes #1697 --- .../0042_supplierpricebreak_updated.py | 18 ++++++++++++++++++ InvenTree/company/models.py | 4 +++- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 InvenTree/company/migrations/0042_supplierpricebreak_updated.py diff --git a/InvenTree/company/migrations/0042_supplierpricebreak_updated.py b/InvenTree/company/migrations/0042_supplierpricebreak_updated.py new file mode 100644 index 0000000000..cf0788fe4e --- /dev/null +++ b/InvenTree/company/migrations/0042_supplierpricebreak_updated.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2022-03-14 22:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('company', '0041_alter_company_options'), + ] + + operations = [ + migrations.AddField( + model_name='supplierpricebreak', + name='updated', + field=models.DateTimeField(auto_now=True, null=True, verbose_name='last updated'), + ), + ] diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index ef3c8aad2e..52cf1cd122 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -686,7 +686,7 @@ class SupplierPart(models.Model): return s -class SupplierPriceBreak(common.models.PriceBreak): +class SupplierPriceBreak(common.models.PriceBreak): """ Represents a quantity price break for a SupplierPart. - Suppliers can offer discounts at larger quantities - SupplierPart(s) may have zero-or-more associated SupplierPriceBreak(s) @@ -704,6 +704,8 @@ class SupplierPriceBreak(common.models.PriceBreak): part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks', verbose_name=_('Part'),) + updated = models.DateTimeField(auto_now=True, null=True, verbose_name=_('last updated')) + class Meta: unique_together = ("part", "quantity") From ef8c656b4f8b5424f621c157865c4596ee710193 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 14 Mar 2022 23:24:37 +0100 Subject: [PATCH 55/59] update API endpoint --- InvenTree/InvenTree/version.py | 5 ++++- InvenTree/company/serializers.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py index 19373e930c..7314145072 100644 --- a/InvenTree/InvenTree/version.py +++ b/InvenTree/InvenTree/version.py @@ -12,11 +12,14 @@ import common.models INVENTREE_SW_VERSION = "0.7.0 dev" # InvenTree API version -INVENTREE_API_VERSION = 30 +INVENTREE_API_VERSION = 31 """ Increment this API version number whenever there is a significant change to the API that any clients need to know about +v31 -> 2022-03-14 + - Adds "updated" field to SupplierPriceBreakList and SupplierPriceBreakDetail API endpoints + v30 -> 2022-03-09 - Adds "exclude_location" field to BuildAutoAllocation API endpoint - Allows BuildItem API endpoint to be filtered by BomItem relation diff --git a/InvenTree/company/serializers.py b/InvenTree/company/serializers.py index 6efebce6a0..736e379c8a 100644 --- a/InvenTree/company/serializers.py +++ b/InvenTree/company/serializers.py @@ -278,4 +278,5 @@ class SupplierPriceBreakSerializer(InvenTreeModelSerializer): 'quantity', 'price', 'price_currency', + 'updated', ] From 1c9a29dfb8004b618c59ddd696e2f948ac30e53b Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 14 Mar 2022 23:28:42 +0100 Subject: [PATCH 56/59] add new 'updated' field to supplier price break table --- InvenTree/company/templates/company/supplier_part.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/InvenTree/company/templates/company/supplier_part.html b/InvenTree/company/templates/company/supplier_part.html index 44e6756845..ee2e0e8a86 100644 --- a/InvenTree/company/templates/company/supplier_part.html +++ b/InvenTree/company/templates/company/supplier_part.html @@ -268,6 +268,11 @@ $('#price-break-table').inventreeTable({ return html; } }, + { + field: 'updated', + title: '{% trans "Last updated" %}', + sortable: true, + }, ] }); From f72979f2df0b56cef0bae9cf2d17be9c936abcfc Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 14 Mar 2022 23:30:37 +0100 Subject: [PATCH 57/59] add doc-text --- InvenTree/company/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index 52cf1cd122..06a44583f7 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -693,6 +693,7 @@ class SupplierPriceBreak(common.models.PriceBreak): Attributes: part: Link to a SupplierPart object that this price break applies to + updated: Automatic DateTime field that shows last time the price break was updated quantity: Quantity required for price break cost: Cost at specified quantity currency: Reference to the currency of this pricebreak (leave empty for base currency) From cba0ca26ba30884f72a81bb9b77ecde84be76c97 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 14 Mar 2022 23:34:34 +0100 Subject: [PATCH 58/59] PEP fix --- InvenTree/company/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index 06a44583f7..f72668f9f0 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -686,7 +686,7 @@ class SupplierPart(models.Model): return s -class SupplierPriceBreak(common.models.PriceBreak): +class SupplierPriceBreak(common.models.PriceBreak): """ Represents a quantity price break for a SupplierPart. - Suppliers can offer discounts at larger quantities - SupplierPart(s) may have zero-or-more associated SupplierPriceBreak(s) From e4c293f721f57537262939896479219a98482665 Mon Sep 17 00:00:00 2001 From: Matthias Mair <66015116+matmair@users.noreply.github.com> Date: Tue, 15 Mar 2022 13:43:41 +0100 Subject: [PATCH 59/59] fix formatter --- InvenTree/company/templates/company/supplier_part.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/InvenTree/company/templates/company/supplier_part.html b/InvenTree/company/templates/company/supplier_part.html index ee2e0e8a86..67902dc6f6 100644 --- a/InvenTree/company/templates/company/supplier_part.html +++ b/InvenTree/company/templates/company/supplier_part.html @@ -272,6 +272,9 @@ $('#price-break-table').inventreeTable({ field: 'updated', title: '{% trans "Last updated" %}', sortable: true, + formatter: function(value) { + return renderDate(value); + } }, ] }); @@ -354,4 +357,4 @@ $('#delete-part').click(function() { enableSidebar('supplierpart'); -{% endblock %} \ No newline at end of file +{% endblock %}