Simplify exhange rate backend

This commit is contained in:
Oliver Walters 2021-05-27 15:45:38 +10:00
parent 62320e46d5
commit af1904b6e4
10 changed files with 47 additions and 134 deletions

View File

@ -1,120 +1,29 @@
from django.conf import settings as inventree_settings from django.conf import settings as inventree_settings
from djmoney.contrib.exchange.backends.base import BaseExchangeBackend from djmoney.contrib.exchange.backends.base import SimpleExchangeBackend
from djmoney.contrib.exchange.models import Rate
from common.models import InvenTreeSetting
def get_exchange_rate_backend(): class InvenTreeExchange(SimpleExchangeBackend):
""" Return the exchange rate backend set by user """
custom = InvenTreeSetting.get_setting('CUSTOM_EXCHANGE_RATES', False)
if custom:
return InvenTreeManualExchangeBackend()
else:
return ExchangeRateHostBackend()
class InvenTreeManualExchangeBackend(BaseExchangeBackend):
""" """
Backend for manually updating currency exchange rates Backend for automatically updating currency exchange rates.
See the documentation for django-money: https://github.com/django-money/django-money Uses the exchangerate.host service API
Specifically: https://github.com/django-money/django-money/tree/master/djmoney/contrib/exchange/backends
""" """
name = 'inventree' name = "InvenTreeExchange"
url = None
custom_rates = True
base_currency = None
currencies = []
def update_default_currency(self):
""" Update to base currency """
self.base_currency = InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY', 'USD')
def __init__(self, url=None):
""" Overrides init to update url, base currency and currencies """
self.url = url
self.update_default_currency()
# Update name
self.name = self.name + '-' + self.base_currency.lower()
self.currencies = inventree_settings.CURRENCIES
super().__init__()
def get_rates(self, **kwargs):
""" Returns a mapping <currency>: <rate> """
return kwargs.get('rates', {})
def get_stored_rates(self):
""" Returns stored rate for specified backend and base currency """
stored_rates = {}
stored_rates_obj = Rate.objects.all().prefetch_related('backend')
for rate in stored_rates_obj:
# Find match for backend and base currency
if rate.backend.name == self.name and rate.backend.base_currency == self.base_currency:
# print(f'{rate.currency} | {rate.value} | {rate.backend} | {rate.backend.base_currency}')
stored_rates[rate.currency] = rate.value
return stored_rates
class ExchangeRateHostBackend(InvenTreeManualExchangeBackend):
"""
Backend for https://exchangerate.host/
"""
name = "exchangerate.host"
def __init__(self): def __init__(self):
self.url = "https://api.exchangerate.host/latest" self.url = "https://api.exchangerate.host/latest"
self.custom_rates = False super().__init__()
super().__init__(url=self.url)
def get_params(self): def get_params(self):
# No API key is required # No API key is required
return {} return {
}
def update_rates(self, base_currency=None): def update_rates(self, base_currency=inventree_settings.BASE_CURRENCY):
""" Override update_rates method using currencies found in the settings
"""
if base_currency: symbols = ','.join(inventree_settings.CURRENCIES)
self.base_currency = base_currency
else:
self.update_default_currency()
symbols = ','.join(self.currencies)
super().update_rates(base_currency=self.base_currency, symbols=symbols) super().update_rates(base=base_currency, symbols=symbols)
def get_rates(self, **params):
""" Returns a mapping <currency>: <rate> """
# Set base currency
params.update(base=self.base_currency)
response = self.get_response(**params)
try:
return self.parse_json(response)['rates']
except KeyError:
# API response did not contain any rate
pass
return {}

View File

@ -19,6 +19,8 @@ import shutil
import sys import sys
from datetime import datetime from datetime import datetime
import moneyed
import yaml import yaml
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -513,11 +515,20 @@ CURRENCIES = CONFIG.get(
], ],
) )
DEFAULT_CURRENCY = get_setting( # Check that each provided currency is supported
'INVENTREE_DEFAULT_CURRENCY', for currency in CURRENCIES:
CONFIG.get('default_currency', 'USD') if currency not in moneyed.CURRENCIES:
print(f"Currency code '{currency}' is not supported")
sys.exit(1)
BASE_CURRENCY = get_setting(
'INVENTREE_BASE_CURRENCY',
CONFIG.get('base_currency', 'USD')
) )
# Custom currency exchange backend
EXCHANGE_BACKEND = 'InvenTree.exchange.InvenTreeExchange'
# Extract email settings from the config file # Extract email settings from the config file
email_config = CONFIG.get('email', {}) email_config = CONFIG.get('email', {})

View File

@ -167,16 +167,16 @@ def update_exchange_rates():
""" """
try: try:
import common.models from InvenTree.exchange import InvenTreeExchange
from InvenTree.exchange import ExchangeRateHostBackend from django.conf import settings
except AppRegistryNotReady: except AppRegistryNotReady:
# Apps not yet loaded! # Apps not yet loaded!
return return
backend = ExchangeRateHostBackend() backend = InvenTreeExchange()
print(f"Updating exchange rates from {backend.url}") print(f"Updating exchange rates from {backend.url}")
base = common.models.InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY') base = settings.BASE_CURRENCY
print(f"Using base currency '{base}'") print(f"Using base currency '{base}'")

View File

@ -41,7 +41,6 @@ from .views import IndexView, SearchView, DatabaseStatsView
from .views import SettingsView, EditUserView, SetPasswordView from .views import SettingsView, EditUserView, SetPasswordView
from .views import AppearanceSelectView, SettingCategorySelectView from .views import AppearanceSelectView, SettingCategorySelectView
from .views import DynamicJsView from .views import DynamicJsView
from .views import CurrencySettingsView
from common.views import SettingEdit from common.views import SettingEdit
@ -91,7 +90,7 @@ settings_urls = [
url(r'^build/?', SettingsView.as_view(template_name='InvenTree/settings/build.html'), name='settings-build'), url(r'^build/?', SettingsView.as_view(template_name='InvenTree/settings/build.html'), name='settings-build'),
url(r'^purchase-order/?', SettingsView.as_view(template_name='InvenTree/settings/po.html'), name='settings-po'), url(r'^purchase-order/?', SettingsView.as_view(template_name='InvenTree/settings/po.html'), name='settings-po'),
url(r'^sales-order/?', SettingsView.as_view(template_name='InvenTree/settings/so.html'), name='settings-so'), url(r'^sales-order/?', SettingsView.as_view(template_name='InvenTree/settings/so.html'), name='settings-so'),
url(r'^currencies/?', CurrencySettingsView.as_view(), name='settings-currencies'), url(r'^currencies/?', SettingsView.as_view(template_name='InvenTree/settings/currencies.html'), name='settings-currencies'),
url(r'^(?P<pk>\d+)/edit/', SettingEdit.as_view(), name='setting-edit'), url(r'^(?P<pk>\d+)/edit/', SettingEdit.as_view(), name='setting-edit'),

View File

@ -13,6 +13,8 @@ from djmoney.forms.fields import MoneyField
from InvenTree.forms import HelperForm from InvenTree.forms import HelperForm
from InvenTree.helpers import clean_decimal from InvenTree.helpers import clean_decimal
from common.settings import currency_code_default
from .files import FileManager from .files import FileManager
from .models import InvenTreeSetting from .models import InvenTreeSetting
@ -182,7 +184,7 @@ class MatchItem(forms.Form):
if 'price' in col_guess.lower(): if 'price' in col_guess.lower():
self.fields[field_name] = MoneyField( self.fields[field_name] = MoneyField(
label=_(col_guess), label=_(col_guess),
default_currency=InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY'), default_currency=currency_code_default(),
decimal_places=5, decimal_places=5,
max_digits=19, max_digits=19,
required=False, required=False,

View File

@ -19,6 +19,8 @@ from djmoney.models.fields import MoneyField
from djmoney.contrib.exchange.models import convert_money from djmoney.contrib.exchange.models import convert_money
from djmoney.contrib.exchange.exceptions import MissingRate from djmoney.contrib.exchange.exceptions import MissingRate
from common.settings import currency_code_default
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.validators import MinValueValidator, URLValidator from django.core.validators import MinValueValidator, URLValidator
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -80,20 +82,6 @@ class InvenTreeSetting(models.Model):
'default': '', 'default': '',
}, },
'INVENTREE_DEFAULT_CURRENCY': {
'name': _('Default Currency'),
'description': _('Default currency'),
'default': 'USD',
'choices': djmoney.settings.CURRENCY_CHOICES,
},
'CUSTOM_EXCHANGE_RATES': {
'name': _('Custom Exchange Rates'),
'description': _('Enable custom exchange rates'),
'validator': bool,
'default': False,
},
'INVENTREE_DOWNLOAD_FROM_URL': { 'INVENTREE_DOWNLOAD_FROM_URL': {
'name': _('Download from URL'), 'name': _('Download from URL'),
'description': _('Allow download of remote images and files from external URL'), 'description': _('Allow download of remote images and files from external URL'),
@ -766,7 +754,7 @@ def get_price(instance, quantity, moq=True, multiples=True, currency=None):
if currency is None: if currency is None:
# Default currency selection # Default currency selection
currency = InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY') currency = currency_code_default()
pb_min = None pb_min = None
for pb in instance.price_breaks.all(): for pb in instance.price_breaks.all():

View File

@ -7,7 +7,8 @@ from __future__ import unicode_literals
from moneyed import CURRENCIES from moneyed import CURRENCIES
from common.models import InvenTreeSetting import common.models
from django.conf import settings
def currency_code_default(): def currency_code_default():
@ -15,7 +16,7 @@ def currency_code_default():
Returns the default currency code (or USD if not specified) Returns the default currency code (or USD if not specified)
""" """
code = InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY') code = settings.BASE_CURRENCY
if code not in CURRENCIES: if code not in CURRENCIES:
code = 'USD' code = 'USD'
@ -28,4 +29,4 @@ def stock_expiry_enabled():
Returns True if the stock expiry feature is enabled Returns True if the stock expiry feature is enabled
""" """
return InvenTreeSetting.get_setting('STOCK_ENABLE_EXPIRY') return common.models.InvenTreeSetting.get_setting('STOCK_ENABLE_EXPIRY')

View File

@ -52,6 +52,9 @@ language: en-us
# Use the environment variable INVENTREE_TIMEZONE # Use the environment variable INVENTREE_TIMEZONE
timezone: UTC timezone: UTC
# Base currency code
base_currency: USD
# List of currencies supported by default. # List of currencies supported by default.
# Add other currencies here to allow use in InvenTree # Add other currencies here to allow use in InvenTree
currencies: currencies:

View File

@ -2675,7 +2675,7 @@ class PartSalePriceBreakCreate(AjaxCreateView):
initials['part'] = self.get_part() initials['part'] = self.get_part()
default_currency = InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY') default_currency = settings.BASE_CURRENCY
currency = CURRENCIES.get(default_currency, None) currency = CURRENCIES.get(default_currency, None)
if currency is not None: if currency is not None:

View File

@ -15,6 +15,9 @@
<li {% if tab == 'global' %} class='active' {% endif %}> <li {% if tab == 'global' %} class='active' {% endif %}>
<a href='{% url "settings-global" %}'><span class='fas fa-globe'></span> {% trans "Global" %}</a> <a href='{% url "settings-global" %}'><span class='fas fa-globe'></span> {% trans "Global" %}</a>
</li> </li>
<li {% if tab == 'currencies' %} class='active'{% endif %}>
<a href="{% url 'settings-currencies' %}"><span class='fas fa-dollar-sign'></span> {% trans "Currencies" %}</a>
</li>
<li {% if tab == 'report' %} class='active' {% endif %}> <li {% if tab == 'report' %} class='active' {% endif %}>
<a href='{% url "settings-report" %}'><span class='fas fa-file-pdf'></span> {% trans "Report" %}</a> <a href='{% url "settings-report" %}'><span class='fas fa-file-pdf'></span> {% trans "Report" %}</a>
</li> </li>
@ -36,8 +39,5 @@
<li {% if tab == 'so' %} class='active'{% endif %}> <li {% if tab == 'so' %} class='active'{% endif %}>
<a href="{% url 'settings-so' %}"><span class='fas fa-truck'></span> {% trans "Sales Orders" %}</a> <a href="{% url 'settings-so' %}"><span class='fas fa-truck'></span> {% trans "Sales Orders" %}</a>
</li> </li>
<li {% if tab == 'currencies' %} class='active'{% endif %}>
<a href="{% url 'settings-currencies' %}"><span class='fas fa-dollar-sign'></span> {% trans "Currencies" %}</a>
</li>
</ul> </ul>
{% endif %} {% endif %}