mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 19:46:46 +00:00
Currency API Updates (#4300)
* Adds an API endpoint for manually updating / refreshing currency data from the server * Update currency rates manually from the settings page * Add 'last updated' information to the currency exchange backend * Load currency exchange data via API (on setings page) * Bump API version * Table cleanup
This commit is contained in:
parent
3869d98b32
commit
ce3dabedb6
@ -2,11 +2,15 @@
|
|||||||
|
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 92
|
INVENTREE_API_VERSION = 93
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Increment this API version number whenever there is a significant change to the API that any clients need to know about
|
Increment this API version number whenever there is a significant change to the API that any clients need to know about
|
||||||
|
|
||||||
|
v93 -> 2023-02-03 : https://github.com/inventree/InvenTree/pull/4300
|
||||||
|
- Adds extra information to the currency exchange endpoint
|
||||||
|
- Adds API endpoint for manually updating exchange rates
|
||||||
|
|
||||||
v92 -> 2023-02-02 : https://github.com/inventree/InvenTree/pull/4293
|
v92 -> 2023-02-02 : https://github.com/inventree/InvenTree/pull/4293
|
||||||
- Adds API endpoint for currency exchange information
|
- Adds API endpoint for currency exchange information
|
||||||
|
|
||||||
|
@ -31,8 +31,8 @@ from stock.urls import stock_urls
|
|||||||
from users.api import user_urls
|
from users.api import user_urls
|
||||||
|
|
||||||
from .api import InfoView, NotFoundView
|
from .api import InfoView, NotFoundView
|
||||||
from .views import (AboutView, AppearanceSelectView, CurrencyRefreshView,
|
from .views import (AboutView, AppearanceSelectView, CustomConnectionsView,
|
||||||
CustomConnectionsView, CustomEmailView, CustomLoginView,
|
CustomEmailView, CustomLoginView,
|
||||||
CustomPasswordResetFromKeyView,
|
CustomPasswordResetFromKeyView,
|
||||||
CustomSessionDeleteOtherView, CustomSessionDeleteView,
|
CustomSessionDeleteOtherView, CustomSessionDeleteView,
|
||||||
CustomTwoFactorRemove, DatabaseStatsView, DynamicJsView,
|
CustomTwoFactorRemove, DatabaseStatsView, DynamicJsView,
|
||||||
@ -73,7 +73,6 @@ settings_urls = [
|
|||||||
re_path(r'^i18n/?', include('django.conf.urls.i18n')),
|
re_path(r'^i18n/?', include('django.conf.urls.i18n')),
|
||||||
|
|
||||||
re_path(r'^appearance/?', AppearanceSelectView.as_view(), name='settings-appearance'),
|
re_path(r'^appearance/?', AppearanceSelectView.as_view(), name='settings-appearance'),
|
||||||
re_path(r'^currencies-refresh/', CurrencyRefreshView.as_view(), name='settings-currencies-refresh'),
|
|
||||||
|
|
||||||
# Catch any other urls
|
# Catch any other urls
|
||||||
re_path(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/settings.html'), name='settings'),
|
re_path(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/settings.html'), name='settings'),
|
||||||
|
@ -652,20 +652,6 @@ class CustomLoginView(LoginView):
|
|||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class CurrencyRefreshView(RedirectView):
|
|
||||||
"""POST endpoint to refresh / update exchange rates."""
|
|
||||||
|
|
||||||
url = reverse_lazy("settings-currencies")
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
"""On a POST request we will attempt to refresh the exchange rates."""
|
|
||||||
from InvenTree.tasks import offload_task, update_exchange_rates
|
|
||||||
|
|
||||||
offload_task(update_exchange_rates, force_sync=True)
|
|
||||||
|
|
||||||
return redirect(reverse_lazy('settings'))
|
|
||||||
|
|
||||||
|
|
||||||
class AppearanceSelectView(RedirectView):
|
class AppearanceSelectView(RedirectView):
|
||||||
"""View for selecting a color theme."""
|
"""View for selecting a color theme."""
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ from django.views.decorators.csrf import csrf_exempt
|
|||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from django_q.tasks import async_task
|
from django_q.tasks import async_task
|
||||||
from djmoney.contrib.exchange.models import Rate
|
from djmoney.contrib.exchange.models import ExchangeBackend, Rate
|
||||||
from rest_framework import filters, permissions, serializers
|
from rest_framework import filters, permissions, serializers
|
||||||
from rest_framework.exceptions import NotAcceptable, NotFound
|
from rest_framework.exceptions import NotAcceptable, NotFound
|
||||||
from rest_framework.permissions import IsAdminUser
|
from rest_framework.permissions import IsAdminUser
|
||||||
@ -104,10 +104,7 @@ class WebhookView(CsrfExemptMixin, APIView):
|
|||||||
|
|
||||||
|
|
||||||
class CurrencyExchangeView(APIView):
|
class CurrencyExchangeView(APIView):
|
||||||
"""API endpoint for displaying currency information
|
"""API endpoint for displaying currency information"""
|
||||||
|
|
||||||
TODO: Add a POST hook to refresh / update the currency exchange data
|
|
||||||
"""
|
|
||||||
|
|
||||||
permission_classes = [
|
permission_classes = [
|
||||||
permissions.IsAuthenticated,
|
permissions.IsAuthenticated,
|
||||||
@ -122,9 +119,17 @@ class CurrencyExchangeView(APIView):
|
|||||||
except Exception:
|
except Exception:
|
||||||
rates = []
|
rates = []
|
||||||
|
|
||||||
|
# Information on last update
|
||||||
|
try:
|
||||||
|
backend = ExchangeBackend.objects.get(name='InvenTreeExchange')
|
||||||
|
updated = backend.last_update
|
||||||
|
except Exception:
|
||||||
|
updated = None
|
||||||
|
|
||||||
response = {
|
response = {
|
||||||
'base_currency': common.models.InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY', 'USD'),
|
'base_currency': common.models.InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY', 'USD'),
|
||||||
'exchange_rates': {}
|
'exchange_rates': {},
|
||||||
|
'updated': updated,
|
||||||
}
|
}
|
||||||
|
|
||||||
for rate in rates:
|
for rate in rates:
|
||||||
@ -133,6 +138,29 @@ class CurrencyExchangeView(APIView):
|
|||||||
return Response(response)
|
return Response(response)
|
||||||
|
|
||||||
|
|
||||||
|
class CurrencyRefreshView(APIView):
|
||||||
|
"""API endpoint for manually refreshing currency exchange rates.
|
||||||
|
|
||||||
|
User must be a 'staff' user to access this endpoint
|
||||||
|
"""
|
||||||
|
|
||||||
|
permission_classes = [
|
||||||
|
permissions.IsAuthenticated,
|
||||||
|
permissions.IsAdminUser,
|
||||||
|
]
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
"""Performing a POST request will update currency exchange rates"""
|
||||||
|
|
||||||
|
from InvenTree.tasks import update_exchange_rates
|
||||||
|
|
||||||
|
update_exchange_rates
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'success': 'Exchange rates updated',
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class SettingsList(ListAPI):
|
class SettingsList(ListAPI):
|
||||||
"""Generic ListView for settings.
|
"""Generic ListView for settings.
|
||||||
|
|
||||||
@ -452,6 +480,7 @@ common_api_urls = [
|
|||||||
# Currencies
|
# Currencies
|
||||||
re_path(r'^currency/', include([
|
re_path(r'^currency/', include([
|
||||||
re_path(r'^exchange/', CurrencyExchangeView.as_view(), name='api-currency-exchange'),
|
re_path(r'^exchange/', CurrencyExchangeView.as_view(), name='api-currency-exchange'),
|
||||||
|
re_path(r'^refresh/', CurrencyRefreshView.as_view(), name='api-currency-refresh'),
|
||||||
])),
|
])),
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<div class='panel-content'>
|
<div class='panel-content'>
|
||||||
<table class='table table-striped table-condensed'>
|
<table class='table table-striped table-condensed'>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="INVENTREE_DEFAULT_CURRENCY" icon="fa-globe" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_INTERNAL_PRICE" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_INTERNAL_PRICE" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_BOM_USE_INTERNAL_PRICE" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_BOM_USE_INTERNAL_PRICE" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="PRICING_DECIMAL_PLACES" %}
|
{% include "InvenTree/settings/setting.html" with key="PRICING_DECIMAL_PLACES" %}
|
||||||
@ -29,54 +30,27 @@
|
|||||||
|
|
||||||
<div class='panel-heading'>
|
<div class='panel-heading'>
|
||||||
<div class='d-flex flex-wrap'>
|
<div class='d-flex flex-wrap'>
|
||||||
<h4>{% trans "Currency Settings" %}</h4>
|
<h4>{% trans "Exchange Rates" %}</h4>
|
||||||
{% include "spacer.html" %}
|
{% include "spacer.html" %}
|
||||||
<div class='btn-group' role='group'>
|
<div class='btn-group' role='group'>
|
||||||
<form action='{% url "settings-currencies-refresh" %}' method='post'>
|
<button type='button' id='btn-update-rates' class='btn btn-primary float-right'>
|
||||||
<div id='refresh-rates-form'>
|
<span class='fas fa-sync-alt'></span> {% trans "Update Now" %}
|
||||||
{% csrf_token %}
|
</button>
|
||||||
<button type='submit' id='update-rates' class='btn btn-primary float-right'>{% trans "Update Now" %}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='panel-content'>
|
<div class='panel-content'>
|
||||||
{% if rates_updated %}
|
{% if rates_updated %}
|
||||||
<div class='alert alert-block alert-info'>
|
<div class='alert alert-block alert-info'>
|
||||||
{% trans "Last Update" %} - {{ rates_updated }}
|
{% trans "Last Update" %} - {{ rates_updated }}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class='alert alert-block alert-warning'>
|
<div class='alert alert-block alert-warning'>
|
||||||
{% trans "Last Update" %} - {% trans "Never" %}
|
{% trans "Last Update" %} - {% trans "Never" %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<table class='table table-striped table-condensed'>
|
<table class='table table-striped table-condensed' id='exchange-rate-table'></table>
|
||||||
<tbody>
|
|
||||||
{% include "InvenTree/settings/setting.html" with key="INVENTREE_DEFAULT_CURRENCY" icon="fa-globe" %}
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<th>{% trans "Base Currency" %}</th>
|
|
||||||
<th>{{ base_currency }}</th>
|
|
||||||
<th colspan='2'></th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<th>{% trans "Exchange Rates" %}</th>
|
|
||||||
<th>{% trans "Currency" %}</th>
|
|
||||||
<th>{% trans "Rate" %}</th>
|
|
||||||
</tr>
|
|
||||||
{% for rate in rates %}
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td>{{ rate.currency }}</td>
|
|
||||||
<td>{{ rate.value }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock panel_content %}
|
{% endblock panel_content %}
|
||||||
|
@ -151,6 +151,52 @@ $("#edit-password").on('click', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#btn-update-rates').click(function() {
|
||||||
|
inventreePut(
|
||||||
|
'{% url "api-currency-refresh" %}',
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
success: function(data) {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#exchange-rate-table').inventreeTable({
|
||||||
|
url: '{% url "api-currency-exchange" %}',
|
||||||
|
search: false,
|
||||||
|
showColumns: false,
|
||||||
|
sortable: true,
|
||||||
|
sidePagination: 'client',
|
||||||
|
onLoadSuccess: function(response) {
|
||||||
|
var data = response.exchange_rates || {};
|
||||||
|
|
||||||
|
var rows = [];
|
||||||
|
|
||||||
|
for (var currency in data) {
|
||||||
|
rows.push({
|
||||||
|
'currency': currency,
|
||||||
|
'rate': data[currency],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#exchange-rate-table').bootstrapTable('load', rows);
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
field: 'currency',
|
||||||
|
sortable: true,
|
||||||
|
title: '{% trans "Currency" %}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'rate',
|
||||||
|
sortable: true,
|
||||||
|
title: '{% trans "Rate" %}',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
$('#category-select').select2({
|
$('#category-select').select2({
|
||||||
placeholder: '',
|
placeholder: '',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user