+ CurrencyAPIClient for fresh CNY rate
+ yuan_rate_last_updated, yuan_rate_commission fields in GlobalSettings
This commit is contained in:
parent
a4f8dfc27c
commit
2e79e579f7
57
external_api/currency.py
Normal file
57
external_api/currency.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
from decimal import Decimal
|
||||
from contextlib import suppress
|
||||
from urllib.parse import urljoin
|
||||
|
||||
import requests
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class CurrencyAPIClient:
|
||||
CONVERT_ENDPOINT = 'currency/convert'
|
||||
|
||||
MAX_RETRIES = 2
|
||||
|
||||
def __init__(self, api_key: str):
|
||||
self.api_url = 'https://api.getgeoapi.com/v2/'
|
||||
self.api_key = api_key
|
||||
self.session = requests.Session()
|
||||
|
||||
def request(self, method, url, *args, **kwargs):
|
||||
params = kwargs.pop('params', {})
|
||||
params.update({"api_key": self.api_key})
|
||||
|
||||
joined_url = urljoin(self.api_url, url)
|
||||
request = requests.Request(method, joined_url, params=params, *args, **kwargs)
|
||||
|
||||
retries = 0
|
||||
while retries < self.MAX_RETRIES:
|
||||
retries += 1
|
||||
prepared = self.session.prepare_request(request)
|
||||
r = self.session.send(prepared)
|
||||
|
||||
# TODO: handle/log errors
|
||||
return r
|
||||
|
||||
def get_rate(self, currency1: str, currency2: str, amount=1):
|
||||
params = {
|
||||
'from': currency1,
|
||||
'to': currency2,
|
||||
'amount': amount,
|
||||
'format': 'json'
|
||||
}
|
||||
|
||||
r = self.request('GET', self.CONVERT_ENDPOINT, params=params)
|
||||
if not r or r.json().get('status') == 'failed':
|
||||
return None
|
||||
|
||||
with suppress(KeyError):
|
||||
rate = r.json()['rates'][currency2.upper()]['rate']
|
||||
return Decimal(rate)
|
||||
|
||||
return None
|
||||
|
||||
def get_cny_rate(self):
|
||||
return self.get_rate('cny', 'rub')
|
||||
|
||||
|
||||
client = CurrencyAPIClient(settings.CURRENCY_GETGEOIP_API_KEY)
|
||||
|
|
@ -29,6 +29,8 @@ CDEK_CLIENT_SECRET = 'lc2gmrmK5s1Kk6FhZbNqpQCaATQRlsOy'
|
|||
|
||||
POIZON_TOKEN = 'IRwNgBxb8YQ'
|
||||
|
||||
CURRENCY_GETGEOIP_API_KEY = '136a69df7e4f419c97783288068e5a2f1ef4ad6d'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = bool(int(os.environ.get("DJANGO_DEBUG") or 0))
|
||||
DISABLE_PERMISSIONS = False
|
||||
|
|
@ -198,6 +200,7 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|||
|
||||
CHECKLIST_ID_LENGTH = 10
|
||||
COMMISSION_OVER_150K = 1.1
|
||||
YUAN_RATE_UPDATE_PERIOD_MINUTES = 60
|
||||
|
||||
# Logging
|
||||
SENTRY_DSN = "https://96106e3f938badc86ecb2e502716e496@o4506163299418112.ingest.sentry.io/4506163300663296"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 4.2.2 on 2023-11-22 22:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('store', '0047_alter_checklist_gift'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='globalsettings',
|
||||
name='yuan_rate_commission',
|
||||
field=models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Наценка на курс юаня, руб'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='globalsettings',
|
||||
name='yuan_rate_last_updated',
|
||||
field=models.DateTimeField(default=None, null=True, verbose_name='Дата обновления курса CNY/RUB'),
|
||||
),
|
||||
]
|
||||
|
|
@ -23,6 +23,7 @@ from django_cleanup import cleanup
|
|||
from mptt.fields import TreeForeignKey
|
||||
from mptt.models import MPTTModel
|
||||
|
||||
from external_api.currency import client as CurrencyAPIClient
|
||||
from store.utils import create_preview, concat_not_null_values
|
||||
from utils.cache import InMemoryCache
|
||||
|
||||
|
|
@ -30,6 +31,8 @@ from utils.cache import InMemoryCache
|
|||
class GlobalSettings(models.Model):
|
||||
# currency
|
||||
yuan_rate = models.DecimalField('Курс CNY/RUB', max_digits=10, decimal_places=2, default=0)
|
||||
yuan_rate_last_updated = models.DateTimeField('Дата обновления курса CNY/RUB', null=True, default=None)
|
||||
yuan_rate_commission = models.DecimalField('Наценка на курс юаня, руб', max_digits=10, decimal_places=2, default=0)
|
||||
# Chinadelivery
|
||||
delivery_price_CN = models.DecimalField('Цена доставки по Китаю', max_digits=10, decimal_places=2, default=0)
|
||||
commission_rub = models.DecimalField('Комиссия, руб', max_digits=10, decimal_places=2, default=0)
|
||||
|
|
@ -63,6 +66,26 @@ class GlobalSettings(models.Model):
|
|||
InMemoryCache.set('GlobalSettings', obj)
|
||||
return obj
|
||||
|
||||
def get_yuan_rate(self):
|
||||
""" Get rate either from CurrencyAPI or from DB (if fresh enough) """
|
||||
if self.yuan_rate_last_updated is not None:
|
||||
diff_minutes = (timezone.now() - self.yuan_rate_last_updated).total_seconds() / 60
|
||||
else:
|
||||
diff_minutes = None
|
||||
|
||||
if diff_minutes is None or diff_minutes > settings.YUAN_RATE_UPDATE_PERIOD_MINUTES:
|
||||
# Get fresh rate from API
|
||||
rate = CurrencyAPIClient.get_cny_rate()
|
||||
if rate:
|
||||
# Save rate in DB for future usage
|
||||
self.yuan_rate = rate
|
||||
self.yuan_rate_last_updated = timezone.now()
|
||||
self.save()
|
||||
return rate
|
||||
|
||||
# Default
|
||||
return self.yuan_rate
|
||||
|
||||
|
||||
class Category(MPTTModel):
|
||||
name = models.CharField('Название', max_length=20)
|
||||
|
|
@ -271,7 +294,7 @@ class ChecklistQuerySet(models.QuerySet):
|
|||
return self.annotate(
|
||||
_yuan_rate=Case(
|
||||
When(price_snapshot_id__isnull=False, then=F('price_snapshot__yuan_rate')),
|
||||
default=GlobalSettings.load().yuan_rate
|
||||
default=GlobalSettings.load().get_yuan_rate()
|
||||
),
|
||||
_price_rub=Ceil(F('_yuan_rate') * F('price_yuan'))
|
||||
)
|
||||
|
|
@ -448,7 +471,7 @@ class Checklist(models.Model):
|
|||
if self.price_snapshot_id:
|
||||
yuan_rate = self.price_snapshot.yuan_rate
|
||||
else:
|
||||
yuan_rate = GlobalSettings.load().yuan_rate
|
||||
yuan_rate = GlobalSettings.load().get_yuan_rate()
|
||||
|
||||
return math.ceil(yuan_rate * self.price_yuan)
|
||||
|
||||
|
|
@ -483,7 +506,7 @@ class Checklist(models.Model):
|
|||
if self.price_snapshot_id:
|
||||
return self.price_snapshot.yuan_rate
|
||||
else:
|
||||
return GlobalSettings.load().yuan_rate
|
||||
return GlobalSettings.load().get_yuan_rate()
|
||||
|
||||
@property
|
||||
def delivery_price_CN(self) -> Decimal:
|
||||
|
|
|
|||
|
|
@ -206,14 +206,14 @@ class AnonymousUserChecklistSerializer(ChecklistSerializer):
|
|||
|
||||
|
||||
class GlobalSettingsSerializer(serializers.ModelSerializer):
|
||||
currency = serializers.DecimalField(source='yuan_rate', max_digits=10, decimal_places=2)
|
||||
currency = serializers.DecimalField(source='get_yuan_rate', read_only=True, max_digits=10, decimal_places=2)
|
||||
chinadelivery = serializers.DecimalField(source='delivery_price_CN', max_digits=10, decimal_places=2)
|
||||
commission = serializers.DecimalField(source='commission_rub', max_digits=10, decimal_places=2)
|
||||
pickup = serializers.CharField(source='pickup_address')
|
||||
|
||||
class Meta:
|
||||
model = GlobalSettings
|
||||
fields = ('currency', 'commission', 'chinadelivery', 'pickup', 'time_to_buy')
|
||||
fields = ('currency', 'yuan_rate_commission', 'commission', 'chinadelivery', 'pickup', 'time_to_buy')
|
||||
|
||||
|
||||
class PaymentMethodSerializer(serializers.ModelSerializer):
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ class StatisticsAPI(viewsets.GenericViewSet):
|
|||
@action(url_path='orders', detail=False, methods=['get'])
|
||||
def stats_by_orders(self, request, *args, **kwargs):
|
||||
global_settings = GlobalSettings.load()
|
||||
yuan_rate = global_settings.yuan_rate
|
||||
yuan_rate = global_settings.get_yuan_rate()
|
||||
|
||||
# Prepare query to collect the stats
|
||||
qs = self.get_queryset() \
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user