diff --git a/app/apps/currencies/exchange_rates/fetcher.py b/app/apps/currencies/exchange_rates/fetcher.py index dd8ad0b..382dc98 100644 --- a/app/apps/currencies/exchange_rates/fetcher.py +++ b/app/apps/currencies/exchange_rates/fetcher.py @@ -203,21 +203,63 @@ class ExchangeRateFetcher: if provider.rates_inverted: # If rates are inverted, we need to swap currencies - ExchangeRate.objects.create( - from_currency=to_currency, - to_currency=from_currency, - rate=rate, - date=timezone.now(), - ) + if service.singleton: + # Try to get the last automatically created exchange rate + exchange_rate = ( + ExchangeRate.objects.filter( + automatic=True, + from_currency=to_currency, + to_currency=from_currency, + ) + .order_by("-date") + .first() + ) + else: + exchange_rate = None + + if not exchange_rate: + ExchangeRate.objects.create( + automatic=True, + from_currency=to_currency, + to_currency=from_currency, + rate=rate, + date=timezone.now(), + ) + else: + exchange_rate.rate = rate + exchange_rate.date = timezone.now() + exchange_rate.save() + processed_pairs.add((to_currency.id, from_currency.id)) else: # If rates are not inverted, we can use them as is - ExchangeRate.objects.create( - from_currency=from_currency, - to_currency=to_currency, - rate=rate, - date=timezone.now(), - ) + if service.singleton: + # Try to get the last automatically created exchange rate + exchange_rate = ( + ExchangeRate.objects.filter( + automatic=True, + from_currency=from_currency, + to_currency=to_currency, + ) + .order_by("-date") + .first() + ) + else: + exchange_rate = None + + if not exchange_rate: + ExchangeRate.objects.create( + automatic=True, + from_currency=from_currency, + to_currency=to_currency, + rate=rate, + date=timezone.now(), + ) + else: + exchange_rate.rate = rate + exchange_rate.date = timezone.now() + exchange_rate.save() + processed_pairs.add((from_currency.id, to_currency.id)) service.last_fetch = timezone.now() diff --git a/app/apps/currencies/forms.py b/app/apps/currencies/forms.py index 317bd0d..eade665 100644 --- a/app/apps/currencies/forms.py +++ b/app/apps/currencies/forms.py @@ -114,6 +114,7 @@ class ExchangeRateServiceForm(forms.ModelForm): "fetch_interval", "target_currencies", "target_accounts", + "singleton", ] def __init__(self, *args, **kwargs): @@ -126,6 +127,7 @@ class ExchangeRateServiceForm(forms.ModelForm): "name", "service_type", Switch("is_active"), + Switch("singleton"), "api_key", Row( Column("interval_type", css_class="form-group col-md-6"), diff --git a/app/apps/currencies/migrations/0015_exchangerate_automatic_exchangerateservice_singleton.py b/app/apps/currencies/migrations/0015_exchangerate_automatic_exchangerateservice_singleton.py new file mode 100644 index 0000000..7f96041 --- /dev/null +++ b/app/apps/currencies/migrations/0015_exchangerate_automatic_exchangerateservice_singleton.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.4 on 2025-08-08 02:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('currencies', '0014_alter_currency_options'), + ] + + operations = [ + migrations.AddField( + model_name='exchangerate', + name='automatic', + field=models.BooleanField(default=False, verbose_name='Automatic'), + ), + migrations.AddField( + model_name='exchangerateservice', + name='singleton', + field=models.BooleanField(default=False, help_text='Create one exchange rate and keep updating it. Avoids database clutter.', verbose_name='Single exchange rate'), + ), + ] diff --git a/app/apps/currencies/models.py b/app/apps/currencies/models.py index 234af36..6ef94fa 100644 --- a/app/apps/currencies/models.py +++ b/app/apps/currencies/models.py @@ -70,6 +70,8 @@ class ExchangeRate(models.Model): ) date = models.DateTimeField(verbose_name=_("Date and Time")) + automatic = models.BooleanField(verbose_name=_("Automatic"), default=False) + class Meta: verbose_name = _("Exchange Rate") verbose_name_plural = _("Exchange Rates") @@ -148,6 +150,14 @@ class ExchangeRateService(models.Model): blank=True, ) + singleton = models.BooleanField( + verbose_name=_("Single exchange rate"), + default=False, + help_text=_( + "Create one exchange rate and keep updating it. Avoids database clutter." + ), + ) + class Meta: verbose_name = _("Exchange Rate Service") verbose_name_plural = _("Exchange Rate Services")