mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-07-13 00:02:53 +02:00
changes
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from apps.currencies.models import Currency
|
||||
from apps.currencies.models import Currency, ExchangeRate
|
||||
|
||||
|
||||
@admin.register(Currency)
|
||||
@@ -9,3 +9,6 @@ class CurrencyAdmin(admin.ModelAdmin):
|
||||
if db_field.name == "suffix" or db_field.name == "prefix":
|
||||
kwargs["strip"] = False
|
||||
return super().formfield_for_dbfield(db_field, request, **kwargs)
|
||||
|
||||
|
||||
admin.site.register(ExchangeRate)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
from crispy_forms.bootstrap import FormActions
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Layout
|
||||
from django import forms
|
||||
from django.forms import CharField
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from apps.common.widgets.crispy.submit import NoClassSubmit
|
||||
from apps.currencies.models import Currency
|
||||
|
||||
|
||||
class CurrencyForm(forms.ModelForm):
|
||||
prefix = CharField(strip=False, required=False)
|
||||
suffix = CharField(strip=False, required=False)
|
||||
|
||||
class Meta:
|
||||
model = Currency
|
||||
fields = ["name", "decimal_places", "prefix", "suffix", "code"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_tag = False
|
||||
self.helper.form_method = "post"
|
||||
self.helper.layout = Layout(
|
||||
"code", "name", "decimal_places", "prefix", "suffix"
|
||||
)
|
||||
|
||||
if self.instance and self.instance.pk:
|
||||
self.helper.layout.append(
|
||||
FormActions(
|
||||
NoClassSubmit(
|
||||
"submit", _("Update"), css_class="btn btn-outline-primary w-100"
|
||||
),
|
||||
),
|
||||
)
|
||||
else:
|
||||
self.helper.layout.append(
|
||||
FormActions(
|
||||
NoClassSubmit(
|
||||
"submit", _("Add"), css_class="btn btn-outline-primary w-100"
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
# Generated by Django 5.1.1 on 2024-10-04 03:33
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("currencies", "0003_alter_currency_decimal_places"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ExchangeRate",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"rate",
|
||||
models.DecimalField(
|
||||
decimal_places=30, max_digits=42, verbose_name="Exchange Rate"
|
||||
),
|
||||
),
|
||||
("date", models.DateTimeField(verbose_name="Date and Time")),
|
||||
(
|
||||
"from_currency",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="from_exchange_rates",
|
||||
to="currencies.currency",
|
||||
verbose_name="From Currency",
|
||||
),
|
||||
),
|
||||
(
|
||||
"to_currency",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="to_exchange_rates",
|
||||
to="currencies.currency",
|
||||
verbose_name="To Currency",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Exchange Rate",
|
||||
"verbose_name_plural": "Exchange Rates",
|
||||
"unique_together": {("from_currency", "to_currency", "date")},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -20,3 +20,30 @@ class Currency(models.Model):
|
||||
class Meta:
|
||||
verbose_name = _("Currency")
|
||||
verbose_name_plural = _("Currencies")
|
||||
|
||||
|
||||
class ExchangeRate(models.Model):
|
||||
from_currency = models.ForeignKey(
|
||||
Currency,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="from_exchange_rates",
|
||||
verbose_name=_("From Currency"),
|
||||
)
|
||||
to_currency = models.ForeignKey(
|
||||
Currency,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="to_exchange_rates",
|
||||
verbose_name=_("To Currency"),
|
||||
)
|
||||
rate = models.DecimalField(
|
||||
max_digits=42, decimal_places=30, verbose_name=_("Exchange Rate")
|
||||
)
|
||||
date = models.DateTimeField(verbose_name=_("Date and Time"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Exchange Rate")
|
||||
verbose_name_plural = _("Exchange Rates")
|
||||
unique_together = ("from_currency", "to_currency", "date")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.from_currency.code} to {self.to_currency.code} on {self.date}"
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("currencies/", views.currency_list, name="currencies_list"),
|
||||
path("currencies/add/", views.currency_add, name="currency_add"),
|
||||
path(
|
||||
"currencies/<int:pk>/edit/",
|
||||
views.currency_edit,
|
||||
name="currency_edit",
|
||||
),
|
||||
path(
|
||||
"currencies/<int:pk>/delete/",
|
||||
views.currency_delete,
|
||||
name="currency_delete",
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,42 @@
|
||||
from django.db.models import Func, F, Value, DurationField, Case, When, DecimalField, Q
|
||||
from django.db.models.functions import Abs, Extract
|
||||
from django.utils import timezone
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from apps.currencies.models import ExchangeRate
|
||||
|
||||
|
||||
def convert(amount, from_currency, to_currency, date=None):
|
||||
if from_currency == to_currency:
|
||||
return amount
|
||||
|
||||
if date is None:
|
||||
date = timezone.localtime(timezone.now())
|
||||
|
||||
try:
|
||||
exchange_rate = (
|
||||
ExchangeRate.objects.filter(
|
||||
Q(from_currency=from_currency, to_currency=to_currency)
|
||||
| Q(from_currency=to_currency, to_currency=from_currency)
|
||||
)
|
||||
.annotate(
|
||||
date_diff=Func(
|
||||
Extract(F("date") - Value(date), "epoch"), function="ABS"
|
||||
),
|
||||
effective_rate=Case(
|
||||
When(from_currency=from_currency, then=F("rate")),
|
||||
default=1 / F("rate"),
|
||||
output_field=DecimalField(),
|
||||
),
|
||||
)
|
||||
.order_by("date_diff")
|
||||
.first()
|
||||
)
|
||||
|
||||
if exchange_rate is None:
|
||||
return None
|
||||
|
||||
return amount * exchange_rate.effective_rate
|
||||
except ExchangeRate.DoesNotExist:
|
||||
return None
|
||||
@@ -1,3 +1,92 @@
|
||||
from django.shortcuts import render
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.decorators.http import require_http_methods
|
||||
|
||||
# Create your views here.
|
||||
from apps.common.decorators.htmx import only_htmx
|
||||
from apps.currencies.forms import CurrencyForm
|
||||
from apps.currencies.models import Currency
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def currency_list(request):
|
||||
currencies = Currency.objects.all().order_by("id")
|
||||
return render(request, "currencies/pages/list.html", {"currencies": currencies})
|
||||
|
||||
|
||||
@only_htmx
|
||||
@login_required
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def currency_add(request, **kwargs):
|
||||
if request.method == "POST":
|
||||
form = CurrencyForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request, _("Currency added successfully"))
|
||||
|
||||
return HttpResponse(
|
||||
status=204,
|
||||
headers={
|
||||
"HX-Location": reverse("currencies_list"),
|
||||
"HX-Trigger": "hide_offcanvas, toasts",
|
||||
},
|
||||
)
|
||||
else:
|
||||
form = CurrencyForm()
|
||||
|
||||
return render(
|
||||
request,
|
||||
"currencies/fragments/add.html",
|
||||
{"form": form},
|
||||
)
|
||||
|
||||
|
||||
@only_htmx
|
||||
@login_required
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def currency_edit(request, pk):
|
||||
currency = get_object_or_404(Currency, id=pk)
|
||||
|
||||
if request.method == "POST":
|
||||
form = CurrencyForm(request.POST, instance=currency)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request, _("Currency updated successfully"))
|
||||
|
||||
return HttpResponse(
|
||||
status=204,
|
||||
headers={
|
||||
"HX-Location": reverse("currencies_list"),
|
||||
"HX-Trigger": "hide_offcanvas, toasts",
|
||||
},
|
||||
)
|
||||
else:
|
||||
form = CurrencyForm(instance=currency)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"currencies/fragments/edit.html",
|
||||
{"form": form, "currency": currency},
|
||||
)
|
||||
|
||||
|
||||
@only_htmx
|
||||
@login_required
|
||||
@csrf_exempt
|
||||
@require_http_methods(["DELETE"])
|
||||
def currency_delete(request, pk):
|
||||
currency = get_object_or_404(Currency, id=pk)
|
||||
|
||||
currency.delete()
|
||||
|
||||
messages.success(request, _("Currency deleted successfully"))
|
||||
|
||||
return HttpResponse(
|
||||
status=204,
|
||||
headers={"HX-Location": reverse("currencies_list")},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user