mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-02 11:40:58 +00:00
Client side currency conversion (#4293)
* Automatically update exchange rates when base currency is updated * Adds API endpoint with currency exchange information * Add unit testing for new endpoint * Implement javascript code for client-side conversion * Adds helper function for calculating total price of a dataset * javascript cleanup * Add functionality to sales order tables * JS linting * Update API version * Prevent auto currency updates under certain conditions
This commit is contained in:
@ -9,6 +9,7 @@ from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from django_q.tasks import async_task
|
||||
from djmoney.contrib.exchange.models import Rate
|
||||
from rest_framework import filters, permissions, serializers
|
||||
from rest_framework.exceptions import NotAcceptable, NotFound
|
||||
from rest_framework.permissions import IsAdminUser
|
||||
@ -102,6 +103,36 @@ class WebhookView(CsrfExemptMixin, APIView):
|
||||
raise NotFound()
|
||||
|
||||
|
||||
class CurrencyExchangeView(APIView):
|
||||
"""API endpoint for displaying currency information
|
||||
|
||||
TODO: Add a POST hook to refresh / update the currency exchange data
|
||||
"""
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
def get(self, request, format=None):
|
||||
"""Return information on available currency conversions"""
|
||||
|
||||
# Extract a list of all available rates
|
||||
try:
|
||||
rates = Rate.objects.all()
|
||||
except Exception:
|
||||
rates = []
|
||||
|
||||
response = {
|
||||
'base_currency': common.models.InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY', 'USD'),
|
||||
'exchange_rates': {}
|
||||
}
|
||||
|
||||
for rate in rates:
|
||||
response['exchange_rates'][rate.currency] = rate.value
|
||||
|
||||
return Response(response)
|
||||
|
||||
|
||||
class SettingsList(ListAPI):
|
||||
"""Generic ListView for settings.
|
||||
|
||||
@ -418,6 +449,11 @@ common_api_urls = [
|
||||
# Webhooks
|
||||
path('webhook/<slug:endpoint>/', WebhookView.as_view(), name='api-webhook'),
|
||||
|
||||
# Currencies
|
||||
re_path(r'^currency/', include([
|
||||
re_path(r'^exchange/', CurrencyExchangeView.as_view(), name='api-currency-exchange'),
|
||||
])),
|
||||
|
||||
# Notifications
|
||||
re_path(r'^notifications/', include([
|
||||
# Individual purchase order detail URLs
|
||||
|
@ -43,6 +43,7 @@ import build.validators
|
||||
import InvenTree.fields
|
||||
import InvenTree.helpers
|
||||
import InvenTree.ready
|
||||
import InvenTree.tasks
|
||||
import InvenTree.validators
|
||||
import order.validators
|
||||
|
||||
@ -821,6 +822,18 @@ def validate_email_domains(setting):
|
||||
raise ValidationError(_(f'Invalid domain name: {domain}'))
|
||||
|
||||
|
||||
def update_exchange_rates(setting):
|
||||
"""Update exchange rates when base currency is changed"""
|
||||
|
||||
if InvenTree.ready.isImportingData():
|
||||
return
|
||||
|
||||
if not InvenTree.ready.canAppAccessDatabase():
|
||||
return
|
||||
|
||||
InvenTree.tasks.update_exchange_rates()
|
||||
|
||||
|
||||
class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
"""An InvenTreeSetting object is a key:value pair used for storing single values (e.g. one-off settings values).
|
||||
|
||||
@ -901,9 +914,10 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
|
||||
'INVENTREE_DEFAULT_CURRENCY': {
|
||||
'name': _('Default Currency'),
|
||||
'description': _('Default currency'),
|
||||
'description': _('Select base currency for pricing caluclations'),
|
||||
'default': 'USD',
|
||||
'choices': CURRENCY_CHOICES,
|
||||
'after_save': update_exchange_rates,
|
||||
},
|
||||
|
||||
'INVENTREE_DOWNLOAD_FROM_URL': {
|
||||
|
@ -900,3 +900,15 @@ class ColorThemeTest(TestCase):
|
||||
# check valid theme
|
||||
self.assertFalse(ColorTheme.is_valid_choice(aa))
|
||||
self.assertTrue(ColorTheme.is_valid_choice(ab))
|
||||
|
||||
|
||||
class CurrencyAPITests(InvenTreeAPITestCase):
|
||||
"""Unit tests for the currency exchange API endpoints"""
|
||||
|
||||
def test_exchange_endpoint(self):
|
||||
"""Test that the currency exchange endpoint works as expected"""
|
||||
|
||||
response = self.get(reverse('api-currency-exchange'), expected_code=200)
|
||||
|
||||
self.assertIn('base_currency', response.data)
|
||||
self.assertIn('exchange_rates', response.data)
|
||||
|
Reference in New Issue
Block a user