From 1d51aed7815fc1656148ede39dfc260b0e65d0a4 Mon Sep 17 00:00:00 2001 From: Joseph Muller Date: Thu, 14 Mar 2024 15:47:47 +0000 Subject: [PATCH 1/4] Use currency region as basis of exchange rates #56 --- logic.py | 6 ++-- models.py | 2 +- tests/test_logic.py | 6 +++- tests/test_models.py | 68 +++++++++++++++++++++++++++++++++----------- tests/test_views.py | 6 +++- 5 files changed, 66 insertions(+), 22 deletions(-) diff --git a/logic.py b/logic.py index 0366eb9..f2368fa 100644 --- a/logic.py +++ b/logic.py @@ -144,11 +144,13 @@ def get_base_band(level=None): return get_base_band() else: try: + default_level = models.SupportLevel.objects.get(default=True) return models.Band.objects.filter( - base=True + base=True, + level=default_level, ).latest() except models.Band.DoesNotExist: - logger.warning('No base band found') + logger.warning('No default base band found.') def latest_multiplier_for_indicator( diff --git a/models.py b/models.py index d877199..67a3fc2 100755 --- a/models.py +++ b/models.py @@ -197,7 +197,7 @@ def exchange_rate(self, base_band=None) -> Tuple[decimal.Decimal, str]: base_band = logic.get_base_band() if base_band: - base_key = base_band.country.alpha3 + base_key = base_band.currency.region else: # This is needed during initial configuration base_key = '---' diff --git a/tests/test_logic.py b/tests/test_logic.py index fddbdea..898f729 100644 --- a/tests/test_logic.py +++ b/tests/test_logic.py @@ -61,7 +61,11 @@ def test_get_base_bands(self): base_bands = logic.get_base_bands() self.assertListEqual( - [self.band_base_level_other, self.band_base], + [ + self.band_base_country_other, + self.band_base_level_other, + self.band_base + ], base_bands, ) diff --git a/tests/test_models.py b/tests/test_models.py index 9269850..74f8ce7 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -66,6 +66,10 @@ def setUpClass(cls): name='Higher', order=1, ) + cls.level_third = models.SupportLevel.objects.create( + name='Even Higher', + order=0, + ) cls.currency_base = models.Currency.objects.create( code='GBP', region='GBR', @@ -94,6 +98,15 @@ def setUpClass(cls): billing_agent=cls.agent_default, base=True, ) + cls.band_base_country_other = models.Band.objects.create( + size=cls.size_other, + country='DE', + currency=cls.currency_other, + level=cls.level_third, + fee=5000, + billing_agent=cls.agent_default, + base=True, + ) cls.band_other_one = models.Band.objects.create( size=cls.size_base, country='GB', @@ -174,9 +187,12 @@ def tearDownClass(cls): cls.supporter_one.delete() cls.band_other_two.delete() cls.band_other_one.delete() + cls.band_base_country_other.delete() + cls.band_base_level_other.delete() cls.band_base.delete() cls.currency_other.delete() cls.currency_base.delete() + cls.level_third.delete() cls.level_other.delete() cls.level_base.delete() cls.size_other.delete() @@ -224,23 +240,41 @@ def test_billing_agent_save(self): self.assertFalse(other_has_country) self.assertFalse(default_still_default) - def test_currency_exchange_rate(self): - with patch( - 'plugins.consortial_billing.logic.latest_multiplier_for_indicator' - ) as latest_multiplier: - self.currency_other.exchange_rate() - self.assertIn( - plugin_settings.RATE_INDICATOR, - latest_multiplier.call_args.args, - ) - self.assertIn( - self.currency_other.region, - latest_multiplier.call_args.args, - ) - self.assertIn( - self.currency_other.region, - latest_multiplier.call_args.args, - ) + @patch('plugins.consortial_billing.logic.latest_multiplier_for_indicator') + def test_currency_exchange_rate_with_typical_args(self, latest_multiplier): + self.currency_base.exchange_rate(base_band=self.band_base_country_other) + self.assertIn( + plugin_settings.RATE_INDICATOR, + latest_multiplier.call_args.args, + ) + # Target currency + self.assertIn( + self.currency_base.region, + latest_multiplier.call_args.args, + ) + # Base currency generated with specified base band + self.assertIn( + self.currency_other.region, + latest_multiplier.call_args.args, + ) + + @patch('plugins.consortial_billing.logic.latest_multiplier_for_indicator') + def test_currency_exchange_rate_with_no_args(self, latest_multiplier): + self.currency_other.exchange_rate() + self.assertIn( + plugin_settings.RATE_INDICATOR, + latest_multiplier.call_args.args, + ) + # Target currency + self.assertIn( + self.currency_other.region, + latest_multiplier.call_args.args, + ) + # Base currency generated with default base band + self.assertIn( + self.currency_other.region, + latest_multiplier.call_args.args, + ) def test_band_economic_disparity(self): with patch( diff --git a/tests/test_views.py b/tests/test_views.py index 59bdc7f..ed2e60d 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -52,7 +52,11 @@ def test_manager_context_essential(self, latest_multiplier): response.context['plugin'], ) self.assertListEqual( - [self.band_base_level_other, self.band_base], + [ + self.band_base_country_other, + self.band_base_level_other, + self.band_base + ], response.context['base_bands'], ) self.assertEqual( From 2cbca153079ecb52d5b266b9ef2f8345786f2e7e Mon Sep 17 00:00:00 2001 From: Joseph Muller Date: Wed, 20 Mar 2024 11:02:50 +0000 Subject: [PATCH 2/4] Condense tests #56 --- tests/test_models.py | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/tests/test_models.py b/tests/test_models.py index 74f8ce7..5f1d188 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -11,6 +11,7 @@ from django.http import HttpRequest from plugins.consortial_billing import models, plugin_settings +from plugins.consortial_billing import utils from utils.testing import helpers from press import models as press_models from cms.models import Page @@ -243,36 +244,28 @@ def test_billing_agent_save(self): @patch('plugins.consortial_billing.logic.latest_multiplier_for_indicator') def test_currency_exchange_rate_with_typical_args(self, latest_multiplier): self.currency_base.exchange_rate(base_band=self.band_base_country_other) - self.assertIn( + expected_args = ( plugin_settings.RATE_INDICATOR, - latest_multiplier.call_args.args, - ) - # Target currency - self.assertIn( - self.currency_base.region, - latest_multiplier.call_args.args, + self.currency_base.region, # Target currency + self.currency_other.region, # Specified base currency + utils.setting('missing_data_exchange_rate') ) - # Base currency generated with specified base band - self.assertIn( - self.currency_other.region, + self.assertTupleEqual( + expected_args, latest_multiplier.call_args.args, ) @patch('plugins.consortial_billing.logic.latest_multiplier_for_indicator') def test_currency_exchange_rate_with_no_args(self, latest_multiplier): self.currency_other.exchange_rate() - self.assertIn( + expected_args = ( plugin_settings.RATE_INDICATOR, - latest_multiplier.call_args.args, - ) - # Target currency - self.assertIn( - self.currency_other.region, - latest_multiplier.call_args.args, + self.currency_other.region, # Target currency + self.currency_base.region, # Default base currency + utils.setting('missing_data_exchange_rate') ) - # Base currency generated with default base band - self.assertIn( - self.currency_other.region, + self.assertTupleEqual( + expected_args, latest_multiplier.call_args.args, ) From c34b16d1ee9e00dce0447734a6859b4c0988f76e Mon Sep 17 00:00:00 2001 From: Joseph Muller Date: Wed, 20 Mar 2024 11:45:21 +0000 Subject: [PATCH 3/4] Safeguard against no default support level #56 --- logic.py | 16 +++++++++++----- tests/test_logic.py | 29 +++++++++++++++++++++++++++-- utils.py | 6 ++++++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/logic.py b/logic.py index f2368fa..8c74415 100644 --- a/logic.py +++ b/logic.py @@ -144,11 +144,17 @@ def get_base_band(level=None): return get_base_band() else: try: - default_level = models.SupportLevel.objects.get(default=True) - return models.Band.objects.filter( - base=True, - level=default_level, - ).latest() + default_level = utils.get_standard_support_level() + if default_level: + return models.Band.objects.filter( + base=True, + level=default_level, + ).latest() + else: + return models.Band.objects.filter( + base=True, + ).latest() + except models.Band.DoesNotExist: logger.warning('No default base band found.') diff --git a/tests/test_logic.py b/tests/test_logic.py index 898f729..1f1008f 100644 --- a/tests/test_logic.py +++ b/tests/test_logic.py @@ -44,19 +44,44 @@ def test_get_indicator_by_country(self): data = logic.get_indicator_by_country(self.fake_indicator, 2050) self.assertEqual(data['NLD'], 12345) - def test_get_base_band(self): - + def test_get_base_band_base_level(self): base_band = logic.get_base_band(self.level_base) self.assertEqual( self.band_base, base_band, ) + + def test_get_base_band_other_level(self): other_base_band = logic.get_base_band(self.level_other) self.assertEqual( self.band_base_level_other, other_base_band, ) + def test_get_base_band_no_args(self): + base_band = logic.get_base_band() + self.assertEqual( + self.band_base, + base_band, + ) + + def test_get_base_band_no_args_no_default_level(self): + + # Make it so there is no default level + self.level_base.default = False + self.level_base.save() + + base_band = logic.get_base_band() + + # Restore data + self.level_base.default = True + self.level_base.save() + + self.assertEqual( + self.band_base, + base_band, + ) + def test_get_base_bands(self): base_bands = logic.get_base_bands() diff --git a/utils.py b/utils.py index f6cf1be..1da172a 100644 --- a/utils.py +++ b/utils.py @@ -170,6 +170,12 @@ def get_standard_support_level(): try: return models.SupportLevel.objects.get(default=True) except models.SupportLevel.DoesNotExist: + logger.error( + 'No default support level found. ' + 'Using the support level ordered last, ' + 'but that may produce unintended results. ' + 'For best results, set a level as the default.' + ) return models.SupportLevel.objects.all().last() From e515479400bf7ee1b080a139edcff532485e8b8f Mon Sep 17 00:00:00 2001 From: Joseph Muller Date: Fri, 22 Mar 2024 11:45:36 +0000 Subject: [PATCH 4/4] Make keep_default_unique more resilient via @mauromsl #56 --- logic.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/logic.py b/logic.py index 8c74415..f75d332 100644 --- a/logic.py +++ b/logic.py @@ -256,10 +256,6 @@ def keep_default_unique(obj): :obj: an unsaved model instance with a property named 'default' """ if obj.default: - try: - other = type(obj).objects.get(default=True) - if obj != other: - other.default = False - other.save() - except type(obj).DoesNotExist: - pass + type(obj).objects.filter(default=True).exclude(pk=obj.pk).update( + default=False, + )